**IMPORTANT! THIS LIBRARY IS A WORK IN PROGRESS! ANYTHING CAN CHANGE AT ANY MOMENT WITHOUT ANY NOTICE! USE THIS LIBRARY AT YOUR OWN RISK!**
**IMPORTANT! THIS LIBRARY IS A WORK IN PROGRESS! ANYTHING CAN CHANGE AT ANY MOMENT WITHOUT ANY NOTICE! USE THIS LIBRARY AT YOUR OWN RISK!**
Simple graphics library that does not have any dependencies and renders everything into the given memory pixel by pixel.
Simple graphics library that does not have any dependencies and renders everything into the given memory pixel by pixel.
-Visit [https://tsoding.org/olive.c/](https://tsoding.org/olive.c/) to see some demos.
+Visit [https://tsoding.github.io/olive.c/](https://tsoding.github.io/olive.c/) to see some demos.
The library is not concerned with displaying the image. It only fills up the memory with pixels. It's up to you what to do with those pixels.
The library is not concerned with displaying the image. It only fills up the memory with pixels. It's up to you what to do with those pixels.
@@ -16,7 +20,8 @@ Olive.c is a classical [stb-style](https://github.com/nothings/stb) single heade
## Quick Example (Flag of Japan)
## Quick Example (Flag of Japan)
-<!-- TODO: get rid of the dependency on stb_image_write.h in here and replace it with PPM format -->
+> [!WARNING]
+> Always initialize your Canvas with a color that has Non-Zero Alpha Channel! A lot of functions use `olivec_blend_color()` function to blend with the Background which preserves the original Alpha of the Background. So you may easily end up with a result that is perceptually transparent if the Alpha is Zero.
*This example also uses [stb_image_write.h](https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h) to create the PNG image*
*This example also uses [stb_image_write.h](https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h) to create the PNG image*
-Even though the library does not require any special building, the tests and demos do. Just execute `build.sh` script to build everything.
+Even though the library does not require any special building, the tests and demos do. We use [nobuild](https://github.com/tsoding/nobuild) build system:
```console
```console
-$ ./build.sh
+$ clang -o nobuild nobuild.c
+$ ./nobuild
```
```
## Tests
## Tests
@@ -68,7 +74,7 @@ $ ./build/test run
If the expected behavior of the library has changed in the way that breaks current test cases, you probably want to update them:
If the expected behavior of the library has changed in the way that breaks current test cases, you probably want to update them:
```console
```console
-$ ./build/test record
+$ ./build/test update
```
```
For more info see the help:
For more info see the help:
@@ -88,16 +94,16 @@ The source code for demos is located at [demos](./demos/). Each demo is compiled
To run the SDL version of a demo do
To run the SDL version of a demo do
```console
```console
-$ ./build/<demo>.sdl
+$ ./build/demos/<demo>.sdl
```
```
To run the Terminal version of a demo do
To run the Terminal version of a demo do
```console
```console
-$ ./build/<demo>.term
+$ ./build/demos/<demo>.term
```
```
-To run the WASM versions of the demos from [https://tsoding.org/olive.c/](https://tsoding.org/olive.c/) locally do
+To run the WASM versions of the demos from [https://tsoding.github.io/olive.c/](https://tsoding.github.io/olive.c/) locally do
<p>Below is a bunch of demos written in C using this library compiled to WebAssembly. Every frame of the animations is generated pixel by pixel on CPU without using any special GPU APIs like OpenGL, Metal, etc. </p>
<p>Below is a bunch of demos written in C using this library compiled to WebAssembly. Every frame of the animations is generated pixel by pixel on CPU without using any special GPU APIs like OpenGL, Metal, etc. </p>
<p>The source code of the library and the demos is available on GitHub: <a href="https://github.com/tsoding/olive.c">https://github.com/tsoding/olive.c</a></p>
<p>The source code of the library and the demos is available on GitHub: <a href="https://github.com/tsoding/olive.c">https://github.com/tsoding/olive.c</a></p>
- <p>Rainbow triangle together with a transparent circle. Source: <a href="https://github.com/tsoding/olive.c/blob/master/demos/triangle.c">demos/triangle.c</a></p>
+ <p>Rainbow triangle together with a transparent circle. Source: <a href="https://github.com/tsoding/olive.c/blob/master/demos/triangle.c">demos/triangle.c</a></p>
+ <canvas id="app-triangle"></canvas>
+ </div>
- <h2 id="demo-3d"><a href="#demo-3d">3D</a></h2>
- <p>A bunch of 3D points projected onto your 2D screen plus a text with a builtin monospaced font. Source: <a href="https://github.com/tsoding/olive.c/blob/master/demos/3d.c">demos/3d.c</a></p>
+ <p>A bunch of 3D dots projected onto your 2D screen plus a text with a builtin monospaced font. Source: <a href="https://github.com/tsoding/olive.c/blob/master/demos/dots3d.c">demos/dots3d.c</a></p>
- Rotating rainbow triangle in 3D. Unlike <a href="#demo-3d">3D dots above</a> this is a solid shape. Source: <a href="https://github.com/tsoding/olive.c/blob/master/demos/triangle3d.c">demos/triangle3d.c</a></p>
+ <p>Rotating rainbow triangle in 3D. Unlike <a href="#demo-3d">3D dots above</a> this is a solid shape. Source: <a href="https://github.com/tsoding/olive.c/blob/master/demos/triangle3d.c">demos/triangle3d.c</a></p>
+ <p>Design by <a href="https://github.com/rexim">rexim</a>. 3D model by <a href="https://github.com/kolumb">kolumb</a>. Source: <a href="https://github.com/tsoding/olive.c/blob/master/demos/cup3d.c">demos/cup3d.c</a></p>
// TODO: maybe we can preallocate a Uint8ClampedArray on JavaScript side and just copy the canvas data there to bring width and stride to the same value?
// TODO: maybe we can preallocate a Uint8ClampedArray on JavaScript side and just copy the canvas data there to bring width and stride to the same value?
@@ -75,6 +76,18 @@ async function startDemo(elementId, wasmPath) {
OLIVECDEF void olivec_rect(Olivec_Canvas oc, int x, int y, int w, int h, uint32_t color);
OLIVECDEF void olivec_rect(Olivec_Canvas oc, int x, int y, int w, int h, uint32_t color);
OLIVECDEF void olivec_frame(Olivec_Canvas oc, int x, int y, int w, int h, size_t thiccness, uint32_t color);
OLIVECDEF void olivec_frame(Olivec_Canvas oc, int x, int y, int w, int h, size_t thiccness, uint32_t color);
OLIVECDEF void olivec_circle(Olivec_Canvas oc, int cx, int cy, int r, uint32_t color);
OLIVECDEF void olivec_circle(Olivec_Canvas oc, int cx, int cy, int r, uint32_t color);
+OLIVECDEF void olivec_ellipse(Olivec_Canvas oc, int cx, int cy, int rx, int ry, uint32_t color);
// TODO: lines with different thiccness
// TODO: lines with different thiccness
OLIVECDEF void olivec_line(Olivec_Canvas oc, int x1, int y1, int x2, int y2, uint32_t color);
OLIVECDEF void olivec_line(Olivec_Canvas oc, int x1, int y1, int x2, int y2, uint32_t color);
+OLIVECDEF bool olivec_normalize_triangle(size_t width, size_t height, int x1, int y1, int x2, int y2, int x3, int y3, int *lx, int *hx, int *ly, int *hy);
+OLIVECDEF bool olivec_barycentric(int x1, int y1, int x2, int y2, int x3, int y3, int xp, int yp, int *u1, int *u2, int *det);
OLIVECDEF void olivec_triangle(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3, uint32_t color);
OLIVECDEF void olivec_triangle(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3, uint32_t color);
-OLIVECDEF void olivec_triangle3(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3, uint32_t c1, uint32_t c2, uint32_t c3);
+OLIVECDEF void olivec_triangle3c(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3, uint32_t c1, uint32_t c2, uint32_t c3);
+OLIVECDEF void olivec_triangle3z(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3, float z1, float z2, float z3);
+OLIVECDEF void olivec_triangle3uv(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3, float tx1, float ty1, float tx2, float ty2, float tx3, float ty3, float z1, float z2, float z3, Olivec_Canvas texture);
+OLIVECDEF void olivec_triangle3uv_bilinear(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3, float tx1, float ty1, float tx2, float ty2, float tx3, float ty3, float z1, float z2, float z3, Olivec_Canvas texture);
OLIVECDEF void olivec_text(Olivec_Canvas oc, const char *text, int x, int y, Olivec_Font font, size_t size, uint32_t color);
OLIVECDEF void olivec_text(Olivec_Canvas oc, const char *text, int x, int y, Olivec_Font font, size_t size, uint32_t color);
+OLIVECDEF void olivec_sprite_blend(Olivec_Canvas oc, int x, int y, int w, int h, Olivec_Canvas sprite);
+OLIVECDEF void olivec_sprite_copy(Olivec_Canvas oc, int x, int y, int w, int h, Olivec_Canvas sprite);
+OLIVECDEF void olivec_sprite_copy_bilinear(Olivec_Canvas oc, int x, int y, int w, int h, Olivec_Canvas sprite);
+OLIVECDEF uint32_t olivec_pixel_bilinear(Olivec_Canvas sprite, int nx, int ny, int w, int h);
+
+typedef struct {
+ // Safe ranges to iterate over.
+ int x1, x2;
+ int y1, y2;
+
+ // Original uncut ranges some parts of which may be outside of the canvas boundaries.
+ int ox1, ox2;
+ int oy1, oy2;
+} Olivec_Normalized_Rect;
// The point of this function is to produce two ranges x1..x2 and y1..y2 that are guaranteed to be safe to iterate over the canvas of size pixels_width by pixels_height without any boundary checks.
// The point of this function is to produce two ranges x1..x2 and y1..y2 that are guaranteed to be safe to iterate over the canvas of size pixels_width by pixels_height without any boundary checks.
//
//
-// if (olivec_normalize_rect(x, y, w, h, WIDTH, HEIGHT, &x1, &y1, &x2, &y2)) {
-// for (int x = x1; x <= x2; ++x) {
-// for (int y = y1; y <= y2; ++y) {
+// Olivec_Normalized_Rect nr = {0};
+// if (olivec_normalize_rect(x, y, w, h, WIDTH, HEIGHT, &nr)) {
+// for (int x = nr.x1; x <= nr.x2; ++x) {
+// for (int y = nr.y1; y <= nr.y2; ++y) {
// OLIVEC_PIXEL(oc, x, y) = 0x69696969;
// OLIVEC_PIXEL(oc, x, y) = 0x69696969;
// }
// }
// }
// }
// } else {
// } else {
// // Rectangle is invisible cause it's completely out-of-bounds
// // Rectangle is invisible cause it's completely out-of-bounds
// }
// }
-OLIVECDEF bool olivec_normalize_rect(int x, int y, int w, int h, size_t pixels_width, size_t pixels_height, int *x1, int *x2, int *y1, int *y2);
+OLIVECDEF bool olivec_normalize_rect(int x, int y, int w, int h,
-OLIVECDEF void olivec_triangle3(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3,
- uint32_t c1, uint32_t c2, uint32_t c3)
+OLIVECDEF bool olivec_normalize_triangle(size_t width, size_t height, int x1, int y1, int x2, int y2, int x3, int y3, int *lx, int *hx, int *ly, int *hy)
{
{
- if (y1 > y2) {
- OLIVEC_SWAP(int, x1, x2);
- OLIVEC_SWAP(int, y1, y2);
- OLIVEC_SWAP(int, c1, c2);
- }
+ *lx = x1;
+ *hx = x1;
+ if (*lx > x2) *lx = x2;
+ if (*lx > x3) *lx = x3;
+ if (*hx < x2) *hx = x2;
+ if (*hx < x3) *hx = x3;
+ if (*lx < 0) *lx = 0;
+ if ((size_t) *lx >= width) return false;;
+ if (*hx < 0) return false;;
+ if ((size_t) *hx >= width) *hx = width-1;
+
+ *ly = y1;
+ *hy = y1;
+ if (*ly > y2) *ly = y2;
+ if (*ly > y3) *ly = y3;
+ if (*hy < y2) *hy = y2;
+ if (*hy < y3) *hy = y3;
+ if (*ly < 0) *ly = 0;
+ if ((size_t) *ly >= height) return false;;
+ if (*hy < 0) return false;;
+ if ((size_t) *hy >= height) *hy = height-1;
- if (y2 > y3) {
- OLIVEC_SWAP(int, x2, x3);
- OLIVEC_SWAP(int, y2, y3);
- OLIVEC_SWAP(int, c2, c3);
- }
-
- if (y1 > y2) {
- OLIVEC_SWAP(int, x1, x2);
- OLIVEC_SWAP(int, y1, y2);
- OLIVEC_SWAP(int, c1, c2);
- }
+ return true;
+}
- int dx12 = x2 - x1;
- int dy12 = y2 - y1;
- int dx13 = x3 - x1;
- int dy13 = y3 - y1;
-
- for (int y = y1; y <= y2; ++y) {
- // TODO: move boundary checks outside of loops in olivec_fill_triangle