Ver Fonte

Ready. Set. Go!

rexim há 3 anos atrás
commit
8963246037
9 ficheiros alterados com 302 adições e 0 exclusões
  1. 2 0
      .gitignore
  2. 20 0
      LICENSE
  3. 18 0
      README.md
  4. 5 0
      build.sh
  5. 173 0
      example.c
  6. BIN
      imgs/checker.png
  7. BIN
      imgs/circle.png
  8. BIN
      imgs/lines.png
  9. 84 0
      olive.c

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+*.ppm
+example

+ 20 - 0
LICENSE

@@ -0,0 +1,20 @@
+Copyright 2022 Alexey Kutepov <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 18 - 0
README.md

@@ -0,0 +1,18 @@
+# Olive.c
+
+Simple 2D Graphics Library for C
+
+## Quick Start
+
+```console
+$ ./build.sh
+$ ./example
+```
+
+## Gallery
+
+![checker](./imgs/checker.png)
+
+![circle](./imgs/circle.png)
+
+![lines](./imgs/lines.png)

+ 5 - 0
build.sh

@@ -0,0 +1,5 @@
+#!/bin/sh
+
+set -xe
+
+cc -Wall -Wextra -ggdb -o example example.c

+ 173 - 0
example.c

@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include "olive.c"
+
+#define WIDTH 800
+#define HEIGHT 600
+
+#define COLS (8*2)
+#define ROWS (6*2)
+#define CELL_WIDTH  (WIDTH/COLS)
+#define CELL_HEIGHT (HEIGHT/ROWS)
+
+#define BACKGROUND_COLOR 0xFF202020
+#define FOREGROUND_COLOR 0xFF2020FF
+
+static uint32_t pixels[WIDTH*HEIGHT];
+
+void swap_int(int *a, int *b)
+{
+    int t = *a;
+    *a = *b;
+    *b = t;
+}
+
+void olivec_draw_line(uint32_t *pixels, size_t pixels_width, size_t pixels_height,
+                      int x1, int y1, int x2, int y2,
+                      uint32_t color)
+{
+    int dx = x2 - x1;
+    int dy = y2 - y1;
+
+    if (dx != 0) {
+        int c = y1 - dy*x1/dx;
+
+        if (x1 > x2) swap_int(&x1, &x2);
+        for (int x = x1; x <= x2; ++x) {
+            if (0 <= x && x < (int) pixels_width) {
+                int sy1 = dy*x/dx + c;
+                int sy2 = dy*(x + 1)/dx + c;
+                if (sy1 > sy2) swap_int(&sy1, &sy2);
+                for (int y = sy1; y <= sy2; ++y) {
+                    if (0 <= y && y < (int) pixels_height) {
+                        pixels[y*pixels_width + x] = color;
+                    }
+                }
+            }
+        }
+    } else {
+        int x = x1;
+        if (0 <= x && x < (int) pixels_width) {
+            if (y1 > y2) swap_int(&y1, &y2);
+            for (int y = y1; y <= y2; ++y) {
+                if (0 <= y && y < (int) pixels_height) {
+                    pixels[y*pixels_width + x] = color;
+                }
+            }
+        }
+    }
+}
+
+bool checker_example(void)
+{
+    olivec_fill(pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
+
+    for (int y = 0; y < ROWS; ++y) {
+        for (int x = 0; x < COLS; ++x) {
+            uint32_t color = BACKGROUND_COLOR;
+            if ((x + y)%2 == 0) {
+                color = 0xFF2020FF;
+            }
+            olivec_fill_rect(pixels, WIDTH, HEIGHT, x*CELL_WIDTH, y*CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT, color);
+        }
+    }
+
+    const char *file_path = "checker.ppm";
+    Errno err = olivec_save_to_ppm_file(pixels, WIDTH, HEIGHT, file_path);
+    if (err) {
+        fprintf(stderr, "ERROR: could not save file %s: %s\n", file_path, strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+float lerpf(float a, float b, float t)
+{
+    return a + (b - a)*t;
+}
+
+bool circle_example(void)
+{
+    olivec_fill(pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
+
+    for (int y = 0; y < ROWS; ++y) {
+        for (int x = 0; x < COLS; ++x) {
+            float u = (float)x/COLS;
+            float v = (float)y/ROWS;
+            float t = (u + v)/2;
+
+            size_t radius = CELL_WIDTH;
+            if (CELL_HEIGHT < radius) radius = CELL_HEIGHT;
+
+            olivec_fill_circle(pixels, WIDTH, HEIGHT,
+                               x*CELL_WIDTH + CELL_WIDTH/2, y*CELL_HEIGHT + CELL_HEIGHT/2,
+                               (size_t) lerpf(radius/8, radius/2, t),
+                               FOREGROUND_COLOR);
+        }
+    }
+
+    const char *file_path = "circle.ppm";
+    Errno err = olivec_save_to_ppm_file(pixels, WIDTH, HEIGHT, file_path);
+    if (err) {
+        fprintf(stderr, "ERROR: could not save file %s: %s\n", file_path, strerror(errno));
+        return false;
+    }
+    return true;
+}
+
+bool lines_example(void)
+{
+    olivec_fill(pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
+
+    olivec_draw_line(pixels, WIDTH, HEIGHT,
+                     0, 0, WIDTH, HEIGHT,
+                     FOREGROUND_COLOR);
+
+    olivec_draw_line(pixels, WIDTH, HEIGHT,
+                     WIDTH, 0, 0, HEIGHT,
+                     FOREGROUND_COLOR);
+
+    olivec_draw_line(pixels, WIDTH, HEIGHT,
+                     0, 0, WIDTH/4, HEIGHT,
+                     0xFF20FF20);
+
+    olivec_draw_line(pixels, WIDTH, HEIGHT,
+                     WIDTH/4, 0, 0, HEIGHT,
+                     0xFF20FF20);
+
+    olivec_draw_line(pixels, WIDTH, HEIGHT,
+                     WIDTH, 0, WIDTH/4*3, HEIGHT,
+                     0xFF20FF20);
+
+    olivec_draw_line(pixels, WIDTH, HEIGHT,
+                     WIDTH/4*3, 0, WIDTH, HEIGHT,
+                     0xFF20FF20);
+
+    olivec_draw_line(pixels, WIDTH, HEIGHT,
+                     0, HEIGHT/2, WIDTH, HEIGHT/2,
+                     0xFFFF3030);
+
+    olivec_draw_line(pixels, WIDTH, HEIGHT,
+                     WIDTH/2, 0, WIDTH/2, HEIGHT,
+                     0xFFFF3030);
+
+    const char *file_path = "lines.ppm";
+    Errno err = olivec_save_to_ppm_file(pixels, WIDTH, HEIGHT, file_path);
+    if (err) {
+        fprintf(stderr, "ERROR: could not save file %s: %s\n", file_path, strerror(errno));
+        return false;
+    }
+    return true;
+}
+
+int main(void)
+{
+    if (!checker_example()) return -1;
+    if (!circle_example()) return -1;
+    if (!lines_example()) return -1;
+    return 0;
+}

BIN
imgs/checker.png


BIN
imgs/circle.png


BIN
imgs/lines.png


+ 84 - 0
olive.c

@@ -0,0 +1,84 @@
+#ifndef OLIVE_C_
+#define OLIVE_C_
+
+void olivec_fill(uint32_t *pixels, size_t width, size_t height, uint32_t color)
+{
+    for (size_t i = 0; i < width*height; ++i) {
+        pixels[i] = color;
+    }
+}
+
+typedef int Errno;
+
+#define return_defer(value) do { result = (value); goto defer; } while (0)
+
+Errno olivec_save_to_ppm_file(uint32_t *pixels, size_t width, size_t height, const char *file_path)
+{
+    int result = 0;
+    FILE *f = NULL;
+
+    {
+        f = fopen(file_path, "wb");
+        if (f == NULL) return_defer(errno);
+
+        fprintf(f, "P6\n%zu %zu 255\n", width, height);
+        if (ferror(f)) return_defer(errno);
+
+        for (size_t i = 0; i < width*height; ++i) {
+            uint32_t pixel = pixels[i];
+            uint8_t bytes[3] = {
+                (pixel>>(8*0))&0xFF,
+                (pixel>>(8*1))&0xFF,
+                (pixel>>(8*2))&0xFF,
+            };
+            fwrite(bytes, sizeof(bytes), 1, f);
+            if (ferror(f)) return_defer(errno);
+        }
+    }
+
+defer:
+    if (f) fclose(f);
+    return result;
+}
+
+void olivec_fill_rect(uint32_t *pixels, size_t pixels_width, size_t pixels_height,
+                      int x0, int y0, size_t w, size_t h,
+                      uint32_t color)
+{
+    for (int dy = 0; dy < (int) h; ++dy) {
+        int y = y0 + dy;
+        if (0 <= y && y < (int) pixels_height) {
+            for (int dx = 0; dx < (int) w; ++dx) {
+                int x = x0 + dx;
+                if (0 <= x && x < (int) pixels_width) {
+                    pixels[y*pixels_width + x] = color;
+                }
+            }
+        }
+    }
+}
+
+void olivec_fill_circle(uint32_t *pixels, size_t pixels_width, size_t pixels_height,
+                        int cx, int cy, int r,
+                        uint32_t color)
+{
+    int x1 = cx - r;
+    int y1 = cy - r;
+    int x2 = cx + r;
+    int y2 = cy + r;
+    for (int y = y1; y <= y2; ++y) {
+        if (0 <= y && y < (int) pixels_height) {
+            for (int x = x1; x <= x2; ++x) {
+                if (0 <= x && x < (int) pixels_width) {
+                    int dx = x - cx;
+                    int dy = y - cy;
+                    if (dx*dx + dy*dy <= r*r) {
+                        pixels[y*pixels_width + x] = color;
+                    }
+                }
+            }
+        }
+    }
+}
+
+#endif // OLIVE_C_