Browse Source

Implement olivec_copy

rexim 3 years ago
parent
commit
e682f8b132
13 changed files with 324 additions and 364 deletions
  1. 1 1
      .gitignore
  2. 4 4
      README.md
  3. BIN
      assets/tsodinPog.png
  4. 19 9
      build.sh
  5. 5 168
      examples/3d.c
  6. 3 3
      examples/gallery.c
  7. 42 0
      examples/squish.c
  8. 5 168
      examples/triangle.c
  9. 170 0
      examples/vc.c
  10. 3 1
      index.js
  11. 16 5
      olive.c
  12. 51 0
      png2c.c
  13. 5 5
      test.c

+ 1 - 1
.gitignore

@@ -1,2 +1,2 @@
 *.ppm
 *.ppm
-bin/
+build/

+ 4 - 4
README.md

@@ -14,7 +14,7 @@ Simple 2D Graphics Library for C.
 
 
 > The truly reusable code is the one that you can simply copy-paste.
 > The truly reusable code is the one that you can simply copy-paste.
 
 
-The library itself does not require any special building. You can simple copy-paste [./olive.c](./olive.c) to your project and `#include` it. But you may want to build the binary tools for this project that are located in `./bin/`:
+The library itself does not require any special building. You can simple copy-paste [./olive.c](./olive.c) to your project and `#include` it. But you may want to build the binary tools for this project that are located in `./build/`:
 
 
 ```console
 ```console
 $ ./build.sh
 $ ./build.sh
@@ -56,7 +56,7 @@ int main(void)
 ## Test the Library
 ## Test the Library
 
 
 ```console
 ```console
-$ ./bin/test
+$ ./build/test
 ```
 ```
 
 
 ### Update the test cases
 ### Update the test cases
@@ -64,13 +64,13 @@ $ ./bin/test
 If the expected behaviour of the library has changed in the way that breaks current test cases, you probably want to update them:
 If the expected behaviour of the library has changed in the way that breaks current test cases, you probably want to update them:
 
 
 ```console
 ```console
-$ ./bin/test record
+$ ./build/test record
 ```
 ```
 
 
 ## Regenerate the Gallery Above
 ## Regenerate the Gallery Above
 
 
 ```console
 ```console
-$ ./bin/gallery
+$ ./build/gallery
 ```
 ```
 
 
 ## WebAssembly Triangle Example
 ## WebAssembly Triangle Example

BIN
assets/tsodinPog.png


+ 19 - 9
build.sh

@@ -2,15 +2,25 @@
 
 
 set -xe
 set -xe
 
 
-mkdir -p ./bin/
+build_vc_example() {
+    NAME=$1
 
 
-clang -Wall -Wextra -ggdb -o ./bin/test -Ithirdparty test.c -lm
-clang -Wall -Wextra -ggdb -o ./bin/gallery -Ithirdparty -I. examples/gallery.c
+    clang -Os -fno-builtin -Wall -Wextra -Wswitch-enum --target=wasm32 --no-standard-libraries -Wl,--no-entry -Wl,--export=render -Wl,--export=init -Wl,--allow-undefined -I. -I./build/ -I./thirdparty/ -o ./build/$NAME.wasm -DPLATFORM=WASM_PLATFORM ./examples/$NAME.c
+    clang -Wall -Wextra -ggdb -I. -I./build/ -I./thirdparty/ -o ./build/$NAME.sdl -DPLATFORM=SDL_PLATFORM ./examples/$NAME.c -lm -lSDL2
+    clang -Wall -Wextra -ggdb -I. -I./build/ -I./thirdparty/ -o ./build/$NAME.term -DPLATFORM=TERM_PLATFORM ./examples/$NAME.c -lm
+}
 
 
-clang -Os -fno-builtin -Wall -Wextra -Wswitch-enum --target=wasm32 --no-standard-libraries -Wl,--no-entry -Wl,--export=render -Wl,--allow-undefined  -o ./bin/triangle.wasm -I. -DPLATFORM=WASM_PLATFORM ./examples/triangle.c
-clang -O2 -Wall -Wextra -ggdb -I. -o ./bin/triangle.sdl -DPLATFORM=SDL_PLATFORM ./examples/triangle.c -lm -lSDL2
-clang -O2 -Wall -Wextra -ggdb -I. -o ./bin/triangle.term -DPLATFORM=TERM_PLATFORM ./examples/triangle.c -lm
+mkdir -p ./build/
 
 
-clang -Os -fno-builtin -Wall -Wextra -Wswitch-enum --target=wasm32 --no-standard-libraries -Wl,--no-entry -Wl,--export=render -Wl,--allow-undefined  -o ./bin/3d.wasm -I. -DPLATFORM=WASM_PLATFORM ./examples/3d.c
-clang -O2 -Wall -Wextra -ggdb -I. -o ./bin/3d.sdl -DPLATFORM=SDL_PLATFORM ./examples/3d.c -lm -lSDL2
-clang -O2 -Wall -Wextra -ggdb -I. -o ./bin/3d.term -DPLATFORM=TERM_PLATFORM ./examples/3d.c -lm
+clang -Wall -Wextra -ggdb -o ./build/test -Ithirdparty test.c -lm &
+clang -Wall -Wextra -ggdb -o ./build/gallery -Ithirdparty -I. examples/gallery.c &
+clang -Wall -Wextra -ggdb -o ./build/png2c -Ithirdparty png2c.c -lm &
+wait
+
+mkdir -p ./build/assets/
+./build/png2c ./assets/tsodinPog.png > ./build/assets/tsodinPog.c
+
+build_vc_example triangle &
+build_vc_example 3d &
+build_vc_example squish &
+wait # TODO: the whole script must fail if one of the jobs fails

+ 5 - 168
examples/3d.c

@@ -16,6 +16,7 @@ float cosf(float x);
 // #define HEIGHT (9*FACTOR)
 // #define HEIGHT (9*FACTOR)
 #define WIDTH 800
 #define WIDTH 800
 #define HEIGHT 600
 #define HEIGHT 600
+#define SCALE_DOWN_FACTOR 10
 #define BACKGROUND_COLOR 0xFF181818
 #define BACKGROUND_COLOR 0xFF181818
 #define GRID_COUNT 10
 #define GRID_COUNT 10
 #define GRID_PAD 0.5/GRID_COUNT
 #define GRID_PAD 0.5/GRID_COUNT
@@ -37,11 +38,13 @@ uint32_t circle_colors[] = {
 static uint32_t pixels[WIDTH*HEIGHT];
 static uint32_t pixels[WIDTH*HEIGHT];
 static float angle = 0;
 static float angle = 0;
 
 
+void init(void) {}
+
 uint32_t *render(float dt)
 uint32_t *render(float dt)
 {
 {
     angle += 0.25*PI*dt;
     angle += 0.25*PI*dt;
 
 
-    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);
 
 
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
     for (int ix = 0; ix < GRID_COUNT; ++ix) {
     for (int ix = 0; ix < GRID_COUNT; ++ix) {
@@ -84,170 +87,4 @@ uint32_t *render(float dt)
     return pixels;
     return pixels;
 }
 }
 
 
-#define WASM_PLATFORM 0
-#define SDL_PLATFORM 1
-#define TERM_PLATFORM 2
-
-#if PLATFORM == SDL_PLATFORM
-#include <stdio.h>
-#include <SDL2/SDL.h>
-
-#define return_defer(value) do { result = (value); goto defer; } while (0)
-
-int main(void)
-{
-    int result = 0;
-
-    SDL_Window *window = NULL;
-    SDL_Renderer *renderer = NULL;
-    SDL_Texture *texture = NULL;
-
-    {
-        if (SDL_Init(SDL_INIT_VIDEO) < 0) return_defer(1);
-
-        window = SDL_CreateWindow("Olivec", 0, 0, WIDTH, HEIGHT, 0);
-        if (window == NULL) return_defer(1);
-
-        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
-        if (renderer == NULL) return_defer(1);
-
-        texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
-        if (texture == NULL) return_defer(1);
-
-        Uint32 prev = SDL_GetTicks();
-        for (;;) {
-            // Compute Delta Time
-            Uint32 curr = SDL_GetTicks();
-            float dt = (curr - prev)/1000.f;
-            prev = curr;
-
-            // Flush the events
-            SDL_Event event;
-            while (SDL_PollEvent(&event)) if (event.type == SDL_QUIT) return_defer(0);
-
-            // Render the texture
-            SDL_Rect window_rect = {0, 0, WIDTH, HEIGHT};
-            uint32_t *pixels_src = render(dt);
-            void *pixels_dst;
-            int pitch;
-            if (SDL_LockTexture(texture, &window_rect, &pixels_dst, &pitch) < 0) return_defer(1);
-            for (size_t y = 0; y < HEIGHT; ++y) {
-                memcpy(pixels_dst + y*pitch, pixels_src + y*WIDTH, WIDTH*sizeof(uint32_t));
-            }
-            SDL_UnlockTexture(texture);
-
-            // Display the texture
-            if (SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0) < 0) return_defer(1);
-            if (SDL_RenderClear(renderer) < 0) return_defer(1);
-            if (SDL_RenderCopy(renderer, texture, &window_rect, &window_rect) < 0) return_defer(1);
-            SDL_RenderPresent(renderer);
-        }
-    }
-
-defer:
-    switch (result) {
-    case 0:
-        printf("OK\n");
-        break;
-    default:
-        fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError());
-    }
-    if (texture) SDL_DestroyTexture(texture);
-    if (renderer) SDL_DestroyRenderer(renderer);
-    if (window) SDL_DestroyWindow(window);
-    SDL_Quit();
-    return result;
-}
-#elif PLATFORM == TERM_PLATFORM
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <time.h>
-#include <unistd.h>
-
-#define SCALE_DOWN_FACTOR 5
-static_assert(WIDTH%SCALE_DOWN_FACTOR == 0, "WIDTH must be divisible by the SCALE_DOWN_FACTOR");
-#define SCALED_DOWN_WIDTH (WIDTH/SCALE_DOWN_FACTOR)
-static_assert(HEIGHT%SCALE_DOWN_FACTOR == 0, "HEIGHT must be divisible by the SCALE_DOWN_FACTOR");
-#define SCALED_DOWN_HEIGHT (HEIGHT/SCALE_DOWN_FACTOR)
-
-char char_canvas[SCALED_DOWN_WIDTH*SCALED_DOWN_HEIGHT];
-
-char color_to_char(uint32_t pixel)
-{
-    size_t r = OLIVEC_RED(pixel);
-    size_t g = OLIVEC_GREEN(pixel);
-    size_t b = OLIVEC_BLUE(pixel);
-    // TODO: brightness should take into account tranparency as well
-    size_t bright = r;
-    if (bright < g) bright = g;
-    if (bright < b) bright = b;
-
-    char table[] = " .:a@#";
-    size_t n = sizeof(table) - 1;
-    return table[bright*n/256];
-}
-
-uint32_t compress_pixels_chunk(Olivec_Canvas oc)
-{
-    size_t r = 0;
-    size_t g = 0;
-    size_t b = 0;
-    size_t a = 0;
-
-    for (size_t y = 0; y < oc.height; ++y) {
-        for (size_t x = 0; x < oc.width; ++x) {
-            r += OLIVEC_RED(OLIVEC_PIXEL(oc, x, y));
-            g += OLIVEC_GREEN(OLIVEC_PIXEL(oc, x, y));
-            b += OLIVEC_BLUE(OLIVEC_PIXEL(oc, x, y));
-            a += OLIVEC_ALPHA(OLIVEC_PIXEL(oc, x, y));
-        }
-    }
-
-    r /= oc.width*oc.height;
-    g /= oc.width*oc.height;
-    b /= oc.width*oc.height;
-    a /= oc.width*oc.height;
-
-    return OLIVEC_RGBA(r, g, b, a);
-}
-
-void compress_pixels(uint32_t *pixels)
-{
-    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT);
-    for (size_t y = 0; y < SCALED_DOWN_HEIGHT; ++y) {
-        for (size_t x = 0; x < SCALED_DOWN_WIDTH; ++x) {
-            Olivec_Canvas soc = olivec_subcanvas(oc, x*SCALE_DOWN_FACTOR, y*SCALE_DOWN_FACTOR, SCALE_DOWN_FACTOR, 
-SCALE_DOWN_FACTOR);
-            char_canvas[y*SCALED_DOWN_WIDTH + x] = color_to_char(compress_pixels_chunk(soc));
-        }
-    }
-}
-
-int main(void)
-{
-    for (;;) {
-        compress_pixels(render(1.f/60.f));
-        for (size_t y = 0; y < SCALED_DOWN_HEIGHT; ++y) {
-            for (size_t x = 0; x < SCALED_DOWN_WIDTH; ++x) {
-                putc(char_canvas[y*SCALED_DOWN_WIDTH + x], stdout);
-                putc(char_canvas[y*SCALED_DOWN_WIDTH + x], stdout);
-            }
-            putc('\n', stdout);
-        }
-
-        usleep(1000*1000/60);
-        printf("\033[%dA", SCALED_DOWN_HEIGHT);
-        printf("\033[%dD", SCALED_DOWN_WIDTH);
-    }
-    return 0;
-}
-
-#elif PLATFORM == WASM_PLATFORM
-// Do nothing
-#else
-#error "Unknown platform"
-#endif // SDL_PLATFORM
+#include "vc.c"

+ 3 - 3
examples/gallery.c

@@ -28,7 +28,7 @@ static uint32_t pixels[WIDTH*HEIGHT];
 
 
 bool checker_example(void)
 bool checker_example(void)
 {
 {
-    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);
 
 
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
 
 
@@ -58,7 +58,7 @@ float lerpf(float a, float b, float t)
 
 
 bool circle_example(void)
 bool circle_example(void)
 {
 {
-    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
 
 
     for (int y = 0; y < ROWS; ++y) {
     for (int y = 0; y < ROWS; ++y) {
@@ -88,7 +88,7 @@ bool circle_example(void)
 
 
 bool lines_example(void)
 bool lines_example(void)
 {
 {
-    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);
 
 
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_line(oc, 0, 0, WIDTH, HEIGHT, FOREGROUND_COLOR);
     olivec_line(oc, 0, 0, WIDTH, HEIGHT, FOREGROUND_COLOR);

+ 42 - 0
examples/squish.c

@@ -0,0 +1,42 @@
+#define OLIVEC_IMPLEMENTATION
+#include "./olive.c"
+
+#include "./assets/tsodinPog.c"
+
+#define WIDTH 800
+#define HEIGHT 600
+#define SCALE_DOWN_FACTOR 10
+
+float sinf(float);
+
+uint32_t dst[WIDTH*HEIGHT];
+float global_time = 0;
+
+void init(void)
+{
+}
+
+#define SRC_SCALE 3
+
+uint32_t *render(float dt)
+{
+    global_time += dt;
+
+    float t = sinf(20*global_time);
+
+    olivec_fill(olivec_canvas(dst, WIDTH, HEIGHT, WIDTH), 0xFF181818);
+
+    Olivec_Canvas dst_canvas = olivec_canvas(dst, WIDTH, HEIGHT, WIDTH);
+
+    int factor = 100;
+    int w = png_width*SRC_SCALE - t*factor;
+    int h = png_height*SRC_SCALE + t*factor;
+
+    olivec_copy(
+        olivec_canvas(png, png_width, png_height, png_width),
+        olivec_subcanvas(dst_canvas, WIDTH/2 - w/2, HEIGHT - h, w, h));
+
+    return dst;
+}
+
+#include "vc.c"

+ 5 - 168
examples/triangle.c

@@ -6,6 +6,7 @@
 
 
 #define WIDTH 800
 #define WIDTH 800
 #define HEIGHT 600
 #define HEIGHT 600
+#define SCALE_DOWN_FACTOR 20
 #define BACKGROUND_COLOR 0xFF181818
 #define BACKGROUND_COLOR 0xFF181818
 #define CIRCLE_RADIUS 100
 #define CIRCLE_RADIUS 100
 #define CIRCLE_COLOR 0x99AA2020
 #define CIRCLE_COLOR 0x99AA2020
@@ -34,9 +35,11 @@ static inline void rotate_point(float *x, float *y)
     *y = sinf(dir)*mag + HEIGHT/2;
     *y = sinf(dir)*mag + HEIGHT/2;
 }
 }
 
 
+void init(void) {}
+
 uint32_t *render(float dt)
 uint32_t *render(float dt)
 {
 {
-    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);
 
 
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
 
 
@@ -75,170 +78,4 @@ uint32_t *render(float dt)
     return pixels;
     return pixels;
 }
 }
 
 
-#define WASM_PLATFORM 0
-#define SDL_PLATFORM 1
-#define TERM_PLATFORM 2
-
-#if PLATFORM == SDL_PLATFORM
-#include <stdio.h>
-#include <SDL2/SDL.h>
-
-#define return_defer(value) do { result = (value); goto defer; } while (0)
-
-int main(void)
-{
-    int result = 0;
-
-    SDL_Window *window = NULL;
-    SDL_Renderer *renderer = NULL;
-    SDL_Texture *texture = NULL;
-
-    {
-        if (SDL_Init(SDL_INIT_VIDEO) < 0) return_defer(1);
-
-        window = SDL_CreateWindow("Olivec", 0, 0, WIDTH, HEIGHT, 0);
-        if (window == NULL) return_defer(1);
-
-        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
-        if (renderer == NULL) return_defer(1);
-
-        texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
-        if (texture == NULL) return_defer(1);
-
-        Uint32 prev = SDL_GetTicks();
-        for (;;) {
-            // Compute Delta Time
-            Uint32 curr = SDL_GetTicks();
-            float dt = (curr - prev)/1000.f;
-            prev = curr;
-
-            // Flush the events
-            SDL_Event event;
-            while (SDL_PollEvent(&event)) if (event.type == SDL_QUIT) return_defer(0);
-
-            // Render the texture
-            SDL_Rect window_rect = {0, 0, WIDTH, HEIGHT};
-            uint32_t *pixels_src = render(dt);
-            void *pixels_dst;
-            int pitch;
-            if (SDL_LockTexture(texture, &window_rect, &pixels_dst, &pitch) < 0) return_defer(1);
-            for (size_t y = 0; y < HEIGHT; ++y) {
-                memcpy(pixels_dst + y*pitch, pixels_src + y*WIDTH, WIDTH*sizeof(uint32_t));
-            }
-            SDL_UnlockTexture(texture);
-
-            // Display the texture
-            if (SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0) < 0) return_defer(1);
-            if (SDL_RenderClear(renderer) < 0) return_defer(1);
-            if (SDL_RenderCopy(renderer, texture, &window_rect, &window_rect) < 0) return_defer(1);
-            SDL_RenderPresent(renderer);
-        }
-    }
-
-defer:
-    switch (result) {
-    case 0:
-        printf("OK\n");
-        break;
-    default:
-        fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError());
-    }
-    if (texture) SDL_DestroyTexture(texture);
-    if (renderer) SDL_DestroyRenderer(renderer);
-    if (window) SDL_DestroyWindow(window);
-    SDL_Quit();
-    return result;
-}
-#elif PLATFORM == TERM_PLATFORM
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <time.h>
-#include <unistd.h>
-
-#define SCALE_DOWN_FACTOR 20
-static_assert(WIDTH%SCALE_DOWN_FACTOR == 0, "WIDTH must be divisible by the SCALE_DOWN_FACTOR");
-#define SCALED_DOWN_WIDTH (WIDTH/SCALE_DOWN_FACTOR)
-static_assert(HEIGHT%SCALE_DOWN_FACTOR == 0, "HEIGHT must be divisible by the SCALE_DOWN_FACTOR");
-#define SCALED_DOWN_HEIGHT (HEIGHT/SCALE_DOWN_FACTOR)
-
-char char_canvas[SCALED_DOWN_WIDTH*SCALED_DOWN_HEIGHT];
-
-char color_to_char(uint32_t pixel)
-{
-    size_t r = OLIVEC_RED(pixel);
-    size_t g = OLIVEC_GREEN(pixel);
-    size_t b = OLIVEC_BLUE(pixel);
-    // TODO: brightness should take into account tranparency as well
-    size_t bright = r;
-    if (bright < g) bright = g;
-    if (bright < b) bright = b;
-
-    char table[] = " .:a@#";
-    size_t n = sizeof(table) - 1;
-    return table[bright*n/256];
-}
-
-uint32_t compress_pixels_chunk(Olivec_Canvas oc)
-{
-    size_t r = 0;
-    size_t g = 0;
-    size_t b = 0;
-    size_t a = 0;
-
-    for (size_t y = 0; y < oc.height; ++y) {
-        for (size_t x = 0; x < oc.width; ++x) {
-            r += OLIVEC_RED(OLIVEC_PIXEL(oc, x, y));
-            g += OLIVEC_GREEN(OLIVEC_PIXEL(oc, x, y));
-            b += OLIVEC_BLUE(OLIVEC_PIXEL(oc, x, y));
-            a += OLIVEC_ALPHA(OLIVEC_PIXEL(oc, x, y));
-        }
-    }
-
-    r /= oc.width*oc.height;
-    g /= oc.width*oc.height;
-    b /= oc.width*oc.height;
-    a /= oc.width*oc.height;
-
-    return OLIVEC_RGBA(r, g, b, a);
-}
-
-void compress_pixels(uint32_t *pixels)
-{
-    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT);
-    for (size_t y = 0; y < SCALED_DOWN_HEIGHT; ++y) {
-        for (size_t x = 0; x < SCALED_DOWN_WIDTH; ++x) {
-            Olivec_Canvas soc = olivec_subcanvas(oc, x*SCALE_DOWN_FACTOR, y*SCALE_DOWN_FACTOR, SCALE_DOWN_FACTOR, 
-SCALE_DOWN_FACTOR);
-            char_canvas[y*SCALED_DOWN_WIDTH + x] = color_to_char(compress_pixels_chunk(soc));
-        }
-    }
-}
-
-int main(void)
-{
-    for (;;) {
-        compress_pixels(render(1.f/60.f));
-        for (size_t y = 0; y < SCALED_DOWN_HEIGHT; ++y) {
-            for (size_t x = 0; x < SCALED_DOWN_WIDTH; ++x) {
-                putc(char_canvas[y*SCALED_DOWN_WIDTH + x], stdout);
-                putc(char_canvas[y*SCALED_DOWN_WIDTH + x], stdout);
-            }
-            putc('\n', stdout);
-        }
-
-        usleep(1000*1000/60);
-        printf("\033[%dA", SCALED_DOWN_HEIGHT);
-        printf("\033[%dD", SCALED_DOWN_WIDTH);
-    }
-    return 0;
-}
-
-#elif PLATFORM == WASM_PLATFORM
-// Do nothing
-#else
-#error "Unknown platform"
-#endif // SDL_PLATFORM
+#include "vc.c"

+ 170 - 0
examples/vc.c

@@ -0,0 +1,170 @@
+#define WASM_PLATFORM 0
+#define SDL_PLATFORM 1
+#define TERM_PLATFORM 2
+
+#if PLATFORM == SDL_PLATFORM
+#include <stdio.h>
+#include <SDL2/SDL.h>
+
+#define return_defer(value) do { result = (value); goto defer; } while (0)
+
+int main(void)
+{
+    int result = 0;
+
+    SDL_Window *window = NULL;
+    SDL_Renderer *renderer = NULL;
+    SDL_Texture *texture = NULL;
+
+    {
+        init();
+
+        if (SDL_Init(SDL_INIT_VIDEO) < 0) return_defer(1);
+
+        window = SDL_CreateWindow("Olivec", 0, 0, WIDTH, HEIGHT, 0);
+        if (window == NULL) return_defer(1);
+
+        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+        if (renderer == NULL) return_defer(1);
+
+        texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
+        if (texture == NULL) return_defer(1);
+
+        Uint32 prev = SDL_GetTicks();
+        for (;;) {
+            // Compute Delta Time
+            Uint32 curr = SDL_GetTicks();
+            float dt = (curr - prev)/1000.f;
+            prev = curr;
+
+            // Flush the events
+            SDL_Event event;
+            while (SDL_PollEvent(&event)) if (event.type == SDL_QUIT) return_defer(0);
+
+            // Render the texture
+            SDL_Rect window_rect = {0, 0, WIDTH, HEIGHT};
+            uint32_t *pixels_src = render(dt);
+            void *pixels_dst;
+            int pitch;
+            if (SDL_LockTexture(texture, &window_rect, &pixels_dst, &pitch) < 0) return_defer(1);
+            for (size_t y = 0; y < HEIGHT; ++y) {
+                memcpy(pixels_dst + y*pitch, pixels_src + y*WIDTH, WIDTH*sizeof(uint32_t));
+            }
+            SDL_UnlockTexture(texture);
+
+            // Display the texture
+            if (SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0) < 0) return_defer(1);
+            if (SDL_RenderClear(renderer) < 0) return_defer(1);
+            if (SDL_RenderCopy(renderer, texture, &window_rect, &window_rect) < 0) return_defer(1);
+            SDL_RenderPresent(renderer);
+        }
+    }
+
+defer:
+    switch (result) {
+    case 0:
+        printf("OK\n");
+        break;
+    default:
+        fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError());
+    }
+    if (texture) SDL_DestroyTexture(texture);
+    if (renderer) SDL_DestroyRenderer(renderer);
+    if (window) SDL_DestroyWindow(window);
+    SDL_Quit();
+    return result;
+}
+#elif PLATFORM == TERM_PLATFORM
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+
+static_assert(WIDTH%SCALE_DOWN_FACTOR == 0, "WIDTH must be divisible by the SCALE_DOWN_FACTOR");
+#define SCALED_DOWN_WIDTH (WIDTH/SCALE_DOWN_FACTOR)
+static_assert(HEIGHT%SCALE_DOWN_FACTOR == 0, "HEIGHT must be divisible by the SCALE_DOWN_FACTOR");
+#define SCALED_DOWN_HEIGHT (HEIGHT/SCALE_DOWN_FACTOR)
+
+char char_canvas[SCALED_DOWN_WIDTH*SCALED_DOWN_HEIGHT];
+
+char color_to_char(uint32_t pixel)
+{
+    size_t r = OLIVEC_RED(pixel);
+    size_t g = OLIVEC_GREEN(pixel);
+    size_t b = OLIVEC_BLUE(pixel);
+    // TODO: brightness should take into account tranparency as well
+    size_t bright = r;
+    if (bright < g) bright = g;
+    if (bright < b) bright = b;
+
+    char table[] = " .:a@#";
+    size_t n = sizeof(table) - 1;
+    return table[bright*n/256];
+}
+
+uint32_t compress_pixels_chunk(Olivec_Canvas oc)
+{
+    size_t r = 0;
+    size_t g = 0;
+    size_t b = 0;
+    size_t a = 0;
+
+    for (size_t y = 0; y < oc.height; ++y) {
+        for (size_t x = 0; x < oc.width; ++x) {
+            r += OLIVEC_RED(OLIVEC_PIXEL(oc, x, y));
+            g += OLIVEC_GREEN(OLIVEC_PIXEL(oc, x, y));
+            b += OLIVEC_BLUE(OLIVEC_PIXEL(oc, x, y));
+            a += OLIVEC_ALPHA(OLIVEC_PIXEL(oc, x, y));
+        }
+    }
+
+    r /= oc.width*oc.height;
+    g /= oc.width*oc.height;
+    b /= oc.width*oc.height;
+    a /= oc.width*oc.height;
+
+    return OLIVEC_RGBA(r, g, b, a);
+}
+
+void compress_pixels(uint32_t *pixels)
+{
+    Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);
+    for (size_t y = 0; y < SCALED_DOWN_HEIGHT; ++y) {
+        for (size_t x = 0; x < SCALED_DOWN_WIDTH; ++x) {
+            Olivec_Canvas soc = olivec_subcanvas(oc, x*SCALE_DOWN_FACTOR, y*SCALE_DOWN_FACTOR, SCALE_DOWN_FACTOR, 
+SCALE_DOWN_FACTOR);
+            char_canvas[y*SCALED_DOWN_WIDTH + x] = color_to_char(compress_pixels_chunk(soc));
+        }
+    }
+}
+
+int main(void)
+{
+    init();
+
+    for (;;) {
+        compress_pixels(render(1.f/60.f));
+        for (size_t y = 0; y < SCALED_DOWN_HEIGHT; ++y) {
+            for (size_t x = 0; x < SCALED_DOWN_WIDTH; ++x) {
+                putc(char_canvas[y*SCALED_DOWN_WIDTH + x], stdout);
+                putc(char_canvas[y*SCALED_DOWN_WIDTH + x], stdout);
+            }
+            putc('\n', stdout);
+        }
+
+        usleep(1000*1000/60);
+        printf("\033[%dA", SCALED_DOWN_HEIGHT);
+        printf("\033[%dD", SCALED_DOWN_WIDTH);
+    }
+    return 0;
+}
+
+#elif PLATFORM == WASM_PLATFORM
+// Do nothing
+#else
+#error "Unknown platform"
+#endif // SDL_PLATFORM

+ 3 - 1
index.js

@@ -17,7 +17,8 @@ function make_environment(...envs) {
     });
     });
 }
 }
 
 
-WebAssembly.instantiateStreaming(fetch('./bin/triangle.wasm'), {
+// TODO: display all the VC examples on a single page
+WebAssembly.instantiateStreaming(fetch('./build/squish.wasm'), {
     "env": make_environment({
     "env": make_environment({
         "atan2f": Math.atan2,
         "atan2f": Math.atan2,
         "cosf": Math.cos,
         "cosf": Math.cos,
@@ -43,5 +44,6 @@ WebAssembly.instantiateStreaming(fetch('./bin/triangle.wasm'), {
 
 
         window.requestAnimationFrame(loop);
         window.requestAnimationFrame(loop);
     }
     }
+    w.instance.exports.init();
     window.requestAnimationFrame(first);
     window.requestAnimationFrame(first);
 })
 })

+ 16 - 5
olive.c

@@ -33,10 +33,10 @@ static char default_font_glyphs[128][DEFAULT_FONT_HEIGHT][DEFAULT_FONT_WIDTH] =
         {0, 1, 1, 1, 0},
         {0, 1, 1, 1, 0},
     },
     },
     ['b'] = {
     ['b'] = {
-        {1, 0, 0, 0, 0},
         {1, 0, 0, 0, 0},
         {1, 0, 0, 0, 0},
         {1, 1, 1, 0, 0},
         {1, 1, 1, 0, 0},
         {1, 0, 0, 1, 0},
         {1, 0, 0, 1, 0},
+        {1, 0, 0, 1, 0},
         {1, 1, 1, 0, 0},
         {1, 1, 1, 0, 0},
     },
     },
     ['c'] = {
     ['c'] = {
@@ -99,7 +99,7 @@ typedef struct {
 #define OLIVEC_CANVAS_NULL ((Olivec_Canvas) {0})
 #define OLIVEC_CANVAS_NULL ((Olivec_Canvas) {0})
 #define OLIVEC_PIXEL(oc, x, y) (oc).pixels[(y)*(oc).stride + (x)]
 #define OLIVEC_PIXEL(oc, x, y) (oc).pixels[(y)*(oc).stride + (x)]
 
 
-OLIVECDEF Olivec_Canvas olivec_canvas(uint32_t *pixels, size_t width, size_t height);
+OLIVECDEF Olivec_Canvas olivec_canvas(uint32_t *pixels, size_t width, size_t height, size_t stride);
 OLIVECDEF Olivec_Canvas olivec_subcanvas(Olivec_Canvas oc, int x, int y, int w, int h);
 OLIVECDEF Olivec_Canvas olivec_subcanvas(Olivec_Canvas oc, int x, int y, int w, int h);
 OLIVECDEF void olivec_blend_color(uint32_t *c1, uint32_t c2);
 OLIVECDEF void olivec_blend_color(uint32_t *c1, uint32_t c2);
 OLIVECDEF void olivec_fill(Olivec_Canvas oc, uint32_t color);
 OLIVECDEF void olivec_fill(Olivec_Canvas oc, uint32_t color);
@@ -128,13 +128,13 @@ OLIVECDEF bool olivec_normalize_rect(int x, int y, int w, int h, size_t pixels_w
 
 
 #ifdef OLIVEC_IMPLEMENTATION
 #ifdef OLIVEC_IMPLEMENTATION
 
 
-OLIVECDEF Olivec_Canvas olivec_canvas(uint32_t *pixels, size_t width, size_t height)
+OLIVECDEF Olivec_Canvas olivec_canvas(uint32_t *pixels, size_t width, size_t height, size_t stride)
 {
 {
     Olivec_Canvas oc = {
     Olivec_Canvas oc = {
         .pixels = pixels,
         .pixels = pixels,
         .width  = width,
         .width  = width,
         .height = height,
         .height = height,
-        .stride = width,
+        .stride = stride,
     };
     };
     return oc;
     return oc;
 }
 }
@@ -361,9 +361,20 @@ OLIVECDEF void olivec_text(Olivec_Canvas oc, const char *text, int tx, int ty, O
     }
     }
 }
 }
 
 
+// TODO: olivec_copy does not work correctly with dst out of bounds
+void olivec_copy(Olivec_Canvas src, Olivec_Canvas dst)
+{
+    for (size_t y = 0; y < dst.height; ++y) {
+        for (size_t x = 0; x < dst.width; ++x) {
+            size_t nx = x*src.width/dst.width;
+            size_t ny = y*src.height/dst.height;
+            OLIVEC_PIXEL(dst, x, y) = OLIVEC_PIXEL(src, nx, ny);
+        }
+    }
+}
+
 #endif // OLIVEC_IMPLEMENTATION
 #endif // OLIVEC_IMPLEMENTATION
 
 
-// TODO: Copy canvas onto canvas
 // TODO: Rainbow Triangle
 // TODO: Rainbow Triangle
 // TODO: 3D triangles with z-buffering
 // TODO: 3D triangles with z-buffering
 // TODO: 3D textures
 // TODO: 3D textures

+ 51 - 0
png2c.c

@@ -0,0 +1,51 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#define STB_IMAGE_IMPLEMENTATION
+#include "./stb_image.h"
+
+const char *shift(int *argc, char ***argv)
+{
+    assert(*argc > 0);
+    const char *result = *argv[0];
+    *argc -= 1;
+    *argv += 1;
+    return result;
+}
+
+int main(int argc, char *argv[])
+{
+    shift(&argc, &argv);        // skip program name
+
+    if (argc <= 0) {
+        fprintf(stderr, "Usage: png2c <filepath.png>\n");
+        fprintf(stderr, "ERROR: expected file path\n");
+        exit(1);
+    }
+
+    const char *filepath = shift(&argc, &argv);
+
+    int x, y, n;
+    uint32_t *data = (uint32_t *)stbi_load(filepath, &x, &y, &n, 4);
+
+    if (data == NULL) {
+        fprintf(stderr, "Could not load file `%s`\n", filepath);
+        exit(1);
+    }
+
+    // TODO: inclusion guards and the array name are not customizable
+    printf("#ifndef PNG_H_\n");
+    printf("#define PNG_H_\n");
+    printf("size_t png_width = %d;\n", x);
+    printf("size_t png_height = %d;\n", y);
+    printf("uint32_t png[] = {");
+    for (size_t i = 0; i < (size_t)(x * y); ++i) {
+        printf("0x%x, ", data[i]);
+    }
+    printf("};\n");
+    printf("#endif // PNG_H_\n");
+
+    return 0;
+}

+ 5 - 5
test.c

@@ -162,7 +162,7 @@ typedef struct {
 
 
 void test_fill_rect(void)
 void test_fill_rect(void)
 {
 {
-    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT, WIDTH);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_rect(oc, WIDTH/2 - WIDTH/8, HEIGHT/2 - HEIGHT/8, WIDTH/4, HEIGHT/4, RED_COLOR);
     olivec_rect(oc, WIDTH/2 - WIDTH/8, HEIGHT/2 - HEIGHT/8, WIDTH/4, HEIGHT/4, RED_COLOR);
     olivec_rect(oc, WIDTH - 1, HEIGHT - 1, -WIDTH/2, -HEIGHT/2, GREEN_COLOR);
     olivec_rect(oc, WIDTH - 1, HEIGHT - 1, -WIDTH/2, -HEIGHT/2, GREEN_COLOR);
@@ -171,7 +171,7 @@ void test_fill_rect(void)
 
 
 void test_fill_circle(void)
 void test_fill_circle(void)
 {
 {
-    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT, WIDTH);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_circle(oc, 0, 0, WIDTH/2, RED_COLOR);
     olivec_circle(oc, 0, 0, WIDTH/2, RED_COLOR);
     olivec_circle(oc, WIDTH/2, HEIGHT/2, WIDTH/4, BLUE_COLOR);
     olivec_circle(oc, WIDTH/2, HEIGHT/2, WIDTH/4, BLUE_COLOR);
@@ -180,7 +180,7 @@ void test_fill_circle(void)
 
 
 void test_draw_line(void)
 void test_draw_line(void)
 {
 {
-    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT, WIDTH);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_line(oc, 0, 0, WIDTH, HEIGHT, RED_COLOR);
     olivec_line(oc, 0, 0, WIDTH, HEIGHT, RED_COLOR);
     olivec_line(oc, WIDTH, 0, 0, HEIGHT, BLUE_COLOR);
     olivec_line(oc, WIDTH, 0, 0, HEIGHT, BLUE_COLOR);
@@ -189,7 +189,7 @@ void test_draw_line(void)
 
 
 void test_fill_triangle(void)
 void test_fill_triangle(void)
 {
 {
-    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT, WIDTH);
 
 
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
 
 
@@ -217,7 +217,7 @@ void test_fill_triangle(void)
 
 
 void test_alpha_blending(void)
 void test_alpha_blending(void)
 {
 {
-    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT);
+    Olivec_Canvas oc = olivec_canvas(actual_pixels, WIDTH, HEIGHT, WIDTH);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_fill(oc, BACKGROUND_COLOR);
     olivec_rect(oc, 0, 0, WIDTH*3/4, HEIGHT*3/4, RED_COLOR);
     olivec_rect(oc, 0, 0, WIDTH*3/4, HEIGHT*3/4, RED_COLOR);
     olivec_rect(oc, WIDTH-1, HEIGHT-1, -WIDTH*3/4, -HEIGHT*3/4, 0x5520AA20);
     olivec_rect(oc, WIDTH-1, HEIGHT-1, -WIDTH*3/4, -HEIGHT*3/4, 0x5520AA20);