diff options
Diffstat (limited to 'qoi')
| -rw-r--r-- | qoi/.envrc | 1 | ||||
| -rw-r--r-- | qoi/.gitignore | 5 | ||||
| -rw-r--r-- | qoi/Makefile | 53 | ||||
| -rw-r--r-- | qoi/bjarne.qoi | bin | 0 -> 705778 bytes | |||
| -rw-r--r-- | qoi/include/lcq/qoi.h | 49 | ||||
| -rw-r--r-- | qoi/mrgreen.qoi | bin | 0 -> 614 bytes | |||
| -rw-r--r-- | qoi/packages.nix | 15 | ||||
| -rw-r--r-- | qoi/src/main.c | 58 | ||||
| -rw-r--r-- | qoi/src/qoi.c | 112 |
9 files changed, 293 insertions, 0 deletions
diff --git a/qoi/.envrc b/qoi/.envrc new file mode 100644 index 0000000..c4b17d7 --- /dev/null +++ b/qoi/.envrc @@ -0,0 +1 @@ +use_flake diff --git a/qoi/.gitignore b/qoi/.gitignore new file mode 100644 index 0000000..df2b66d --- /dev/null +++ b/qoi/.gitignore @@ -0,0 +1,5 @@ +/build +/.direnv +/TAGS +/qoi +/*.a
\ No newline at end of file diff --git a/qoi/Makefile b/qoi/Makefile new file mode 100644 index 0000000..0a75636 --- /dev/null +++ b/qoi/Makefile @@ -0,0 +1,53 @@ +CC ?= gcc +AR ?= ar +override CPPFLAGS += -MMD -MP +override CFLAGS += --std=c99 -g -Ideps/ -Isrc/ -Iinclude/ -Wall -Wextra -Wpedantic -Wconversion -Wformat-security -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -Wfloat-equal -Wundef -Wpointer-arith -Wbad-function-cast -Wlogical-op -Wmissing-braces -Wcast-align -Wstrict-overflow=5 -ftrapv +override LDFLAGS += -g -lraylib + +BUILD = build_$(CC) + +SRCS := src/qoi.c +CHK_SOURCES ?= $(SRCS) +OBJECTS := $(SRCS:src/%.c=$(BUILD)/%.o) +EXE := qoi +LIB := libcolonq-qoi.a + +prefix ?= /usr/local +exec_prefix ?= $(prefix) +bindir ?= $(exec_prefix)/bin +includedir ?= $(prefix)/include +libdir ?= $(exec_prefix)/lib + +.PHONY: all clean install check-syntax + +all: $(EXE) $(LIB) + +$(EXE): $(BUILD)/main.o $(LIB) + $(CC) -o $@ $^ $(LDFLAGS) + +$(LIB): $(OBJECTS) + ar rcs $@ $^ + +$(BUILD): + mkdir $(BUILD)/ + +$(BUILD)/%.o: src/%.c | $(BUILD) + $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< + +clean: + -rm $(EXE) + -rm -r $(BUILD)/ + +TAGS: $(SRCS) + etags $^ + +install: $(EXE) $(LIB) + mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir) + install $(EXE) $(DESTDIR)$(bindir)/$(EXE) + install $(LIB) $(DESTDIR)$(libdir)/$(LIB) + cp -r include/* $(DESTDIR)$(includedir) + +check-syntax: TAGS + gcc $(CFLAGS) -fsyntax-only $(CHK_SOURCES) + +-include $(OBJECTS:.o=.d) diff --git a/qoi/bjarne.qoi b/qoi/bjarne.qoi Binary files differnew file mode 100644 index 0000000..89c5b33 --- /dev/null +++ b/qoi/bjarne.qoi diff --git a/qoi/include/lcq/qoi.h b/qoi/include/lcq/qoi.h new file mode 100644 index 0000000..b90c4b2 --- /dev/null +++ b/qoi/include/lcq/qoi.h @@ -0,0 +1,49 @@ +#ifndef LCOLONQ_QOI_H +#define LCOLONQ_QOI_H + +#include <lcq/prelude.h> + +typedef struct { + u8 r, g, b, a; +} qoi_color; +static inline qoi_color qoi_color_new(u8 r, u8 g, u8 b, u8 a) { + qoi_color ret; + ret.r = r; ret.g = g; ret.b = b; ret.a = a; + return ret; +} +static inline u8 qoi_color_hash(qoi_color c) { + return (u8) (c.r * 3 + c.g * 5 + c.b * 7 + c.a * 11) % 64; +} + +typedef enum { + QOI_CHANNELS_RGB = 3, + QOI_CHANNELS_RGBA = 4 +} qoi_channels; +typedef enum { + QOI_COLORSPACE_SRGB = 0, + QOI_COLORSPACE_LINEAR = 1 +} qoi_colorspace; +typedef struct { + u32 width, height; + u8 channels, colorspace; +} qoi_header; + +typedef struct { + u8 *buf; + i64 len; + i64 cur; + qoi_color palette[64]; +} qoi_encoder; + +typedef struct { + u8 *buf; + i64 len; + i64 cur; + qoi_color palette[64]; +} qoi_decoder; + +void qoi_decoder_new(qoi_decoder *q, u8 *buf, i64 len); +bool qoi_decode_header(qoi_decoder *q, qoi_header *h); +bool qoi_decode(qoi_decoder *q, qoi_header *h, qoi_color *pixels); + +#endif diff --git a/qoi/mrgreen.qoi b/qoi/mrgreen.qoi Binary files differnew file mode 100644 index 0000000..3960a67 --- /dev/null +++ b/qoi/mrgreen.qoi diff --git a/qoi/packages.nix b/qoi/packages.nix new file mode 100644 index 0000000..d66e00b --- /dev/null +++ b/qoi/packages.nix @@ -0,0 +1,15 @@ +pkgs: lcq: { + native = pkgs.stdenv.mkDerivation { + pname = "libcolonq-qoi"; + version = "git"; + src = ./.; + buildInputs = [ + lcq.prelude.native + pkgs.raylib + ]; + hardeningDisable = ["all"]; + installPhase = '' + make prefix=$out install + ''; + }; +} diff --git a/qoi/src/main.c b/qoi/src/main.c new file mode 100644 index 0000000..b44c682 --- /dev/null +++ b/qoi/src/main.c @@ -0,0 +1,58 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> + +#include <raylib.h> + +#include <lcq/qoi.h> + +int main(int argc, char **argv) { + FILE *f; + u8 *buf; + i64 len; + qoi_decoder q; + qoi_header h; + qoi_color *pixels; + if (argc < 2) { + fprintf(stderr, "usage: %s PATH\n", argv[0]); + return 1; + } + if (!(f = fopen(argv[1], "r"))) { + fprintf(stderr, "failed to open file: %s\n", argv[1]); + return 1; + } + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + buf = calloc((size_t) len, sizeof(u8)); + fread(buf, sizeof(u8), (size_t) len, f); + qoi_decoder_new(&q, buf, len); + if (!qoi_decode_header(&q, &h)) { + fprintf(stderr, "failed to decode header\n"); + return 1; + } + pixels = calloc(h.width * h.height, sizeof(qoi_color)); + qoi_decode(&q, &h, pixels); + + InitWindow(800, 450, "qoi test"); + { + Image im; + Texture2D tex; + im.data = pixels; + im.width = (int) h.width; + im.height = (int) h.height; + im.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + im.mipmaps = 1; + tex = LoadTextureFromImage(im); + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + DrawText("hi", 0, 0, 100, BLACK); + DrawTexture(tex, 10, 10, WHITE); + EndDrawing(); + } + CloseWindow(); + } + + return 0; +} diff --git a/qoi/src/qoi.c b/qoi/src/qoi.c new file mode 100644 index 0000000..52545c9 --- /dev/null +++ b/qoi/src/qoi.c @@ -0,0 +1,112 @@ +#include <lcq/qoi.h> + +/* encoder */ +static void write_u32_be(qoi_encoder *q) { +} + +/* decoder */ +static u32 read_u32_be(qoi_decoder *q) { + u32 ret = 0; + if (q->cur + 4 >= q->len) return 0xffffffff; + ret |= q->buf[q->cur++] << 24; + ret |= q->buf[q->cur++] << 16; + ret |= q->buf[q->cur++] << 8; + ret |= q->buf[q->cur++]; + return ret; +} +typedef enum { + QOI_OP_INVALID = -3, + QOI_OP_RGB = -2, + QOI_OP_RGBA = -1, + QOI_OP_INDEX = 0, + QOI_OP_DIFF = 1, + QOI_OP_LUMA = 2, + QOI_OP_RUN = 3 +} qoi_op; +static qoi_op cur_op(qoi_decoder *q) { + u8 v; + if (q->cur >= q->len) return QOI_OP_INVALID; + v = q->buf[q->cur]; + if ((i8) v == QOI_OP_RGB) return QOI_OP_RGB; + if ((i8) v == QOI_OP_RGBA) return QOI_OP_RGBA; + return v >> 6; +} +void qoi_decoder_new(qoi_decoder *q, u8 *buf, i64 len) { + i64 i; + q->buf = buf; + q->len = len; + q->cur = 0; + for (i = 0; i < 64; ++i) q->palette[i] = qoi_color_new(0, 0, 0, 0); +} +bool qoi_decode_header(qoi_decoder *q, qoi_header *h) { + if (q->cur + 14 > q->len) return false; + if (read_u32_be(q) != ('q' << 24 | 'o' << 16 | 'i' << 8 | 'f')) { + return false; + } + h->width = read_u32_be(q); + h->height = read_u32_be(q); + h->channels = q->buf[q->cur++]; + h->colorspace = q->buf[q->cur++]; + return true; +} +bool qoi_decode(qoi_decoder *q, qoi_header *h, qoi_color *pixels) { + i64 len = h->width * h->height; + i64 pidx = 0; + qoi_color c = qoi_color_new(0, 0, 0, 255); + u8 v = 0, w = 0; + u8 padding[8] = {0, 0, 0, 0, 0, 0, 0, 1}; + i64 i; + for (i = 0; i < 8; ++i) if (padding[i] != q->buf[q->len - 8 + i]) return false; + while (q->cur < q->len - 8 && pidx < len) { + /* we never need to range-check q->cur mid-loop, since we know the padding exists */ + switch (cur_op(q)) { + case QOI_OP_INVALID: return false; + case QOI_OP_RGB: + q->cur++; + c.r = q->buf[q->cur++]; + c.g = q->buf[q->cur++]; + c.b = q->buf[q->cur++]; + pixels[pidx++] = c; + break; + case QOI_OP_RGBA: + q->cur++; + c.r = q->buf[q->cur++]; + c.g = q->buf[q->cur++]; + c.b = q->buf[q->cur++]; + c.a = q->buf[q->cur++]; + pixels[pidx++] = c; + break; + case QOI_OP_INDEX: + v = q->buf[q->cur++] & 0x3f /* 0b111111 */; + c = q->palette[v]; + pixels[pidx++] = c; + break; + case QOI_OP_DIFF: { + v = q->buf[q->cur++]; + c.r += (u8) ((v >> 4 & 0x3) - 2); + c.g += (u8) ((v >> 2 & 0x3) - 2); + c.b += (u8) ((v & 0x3) - 2); + pixels[pidx++] = c; + break; + } + case QOI_OP_LUMA: { + v = (q->buf[q->cur++] & 0x3f) - 32; + w = q->buf[q->cur++]; + c.r += (u8) (((w >> 4) & 0xf) - 8 + v); + c.g += v; + c.b += (u8) ((w & 0xf) - 8 + v); + pixels[pidx++] = c; + break; + } + case QOI_OP_RUN: + v = (q->buf[q->cur++] & 0x3f) + 1; + for (; v > 0; v--) { + pixels[pidx++] = c; + } + break; + default: return false; + } + q->palette[qoi_color_hash(c)] = c; + } + return true; +} |
