diff options
author | 2025-02-07 11:27:18 -0500 | |
---|---|---|
committer | 2025-02-07 11:27:18 -0500 | |
commit | 4da7be39827ea5888ef9c97b1aadf61b0d76347c (patch) | |
tree | 15d0ff8f8bcb0e871efb1b2e65c2bc8d07b17565 /mons_image |
initial commit (lol)
Diffstat (limited to 'mons_image')
-rw-r--r-- | mons_image/CMakeLists.txt | 30 | ||||
-rw-r--r-- | mons_image/include/image.h | 19 | ||||
-rw-r--r-- | mons_image/include/qoi.h | 7 | ||||
-rw-r--r-- | mons_image/src/image.c | 6 | ||||
-rw-r--r-- | mons_image/src/qoi.c | 140 |
5 files changed, 202 insertions, 0 deletions
diff --git a/mons_image/CMakeLists.txt b/mons_image/CMakeLists.txt new file mode 100644 index 0000000..ddfea1a --- /dev/null +++ b/mons_image/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.14) +project(mons_image LANGUAGES C) +set(CMAKE_C_STANDARD 99) +set(CMAKE_EXPORT_COMPILE_COMMANDS true) +set(CMAKE_BUILD_TYPE "Debug") + +add_library(mons_image + SHARED + ./src/image.c + ./src/qoi.c +) + +target_include_directories(mons_image PUBLIC + "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/include" + "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" +) +target_compile_options(mons_image PRIVATE -coverage) +target_link_options(mons_image PRIVATE -coverage) + +include(CTest) + +function(TESTCASE NAME) + add_executable(test_${NAME} ./tests/${NAME}.c) + target_link_libraries(test_${NAME} PUBLIC mons_image) + add_test( + NAME ${NAME} + COMMAND $<TARGET_FILE:test_${NAME}> + ) +endfunction() + diff --git a/mons_image/include/image.h b/mons_image/include/image.h new file mode 100644 index 0000000..8b258aa --- /dev/null +++ b/mons_image/include/image.h @@ -0,0 +1,19 @@ +#ifndef MONS_IMAGE_H +#define MONS_IMAGE_H + +typedef struct mons_pixel { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; +} mons_pixel; + +typedef struct mons_image { + unsigned int width; + unsigned int height; + mons_pixel *data; +} mons_image; + +void mons_image_free(mons_image *image); + +#endif diff --git a/mons_image/include/qoi.h b/mons_image/include/qoi.h new file mode 100644 index 0000000..0fd7895 --- /dev/null +++ b/mons_image/include/qoi.h @@ -0,0 +1,7 @@ +#ifndef MONS_QOI_H +#define MONS_QOI_H +#include <stdio.h> + +struct mons_image mons_load_qoi(FILE *stream); + +#endif diff --git a/mons_image/src/image.c b/mons_image/src/image.c new file mode 100644 index 0000000..b8abffe --- /dev/null +++ b/mons_image/src/image.c @@ -0,0 +1,6 @@ +#include <stdlib.h> +#include "image.h" + +void mons_image_free(mons_image *image) { + free(image->data); +} diff --git a/mons_image/src/qoi.c b/mons_image/src/qoi.c new file mode 100644 index 0000000..c290c4e --- /dev/null +++ b/mons_image/src/qoi.c @@ -0,0 +1,140 @@ +#include <stdlib.h> +#include <stdio.h> +#include <byteswap.h> +#include <inttypes.h> +#include "qoi.h" +#include "image.h" + +#define MONS_QOI_RGB 3 +#define MONS_QOI_RGBA 4 +#define MONS_QOI_SRGB_LINEAR_ALPHA 0 +#define MONS_QOI_LINEAR 1 +#define MONS_QOI_HEADER_LEN 14 + +#define MONS_QOI_OP_RGB 0b11111110 +#define MONS_QOI_OP_RGBA 0b11111111 +#define MONS_QOI_OP_INDEX 0b00000000 +#define MONS_QOI_OP_DIFF 0b01000000 +#define MONS_QOI_OP_LUMA 0b10000000 +#define MONS_QOI_OP_RUN 0b11000000 + +typedef struct mons_qoi_header { + char magic[4]; + uint32_t width; + uint32_t height; + uint8_t channels; + uint8_t colorspace; +} mons_qoi_header; + +unsigned char pixel_index_hash(mons_pixel pixel) { + return (pixel.r * 3 + pixel.g * 5 + pixel.b * 7 + pixel.a * 11) % 64; +} + +mons_image mons_load_qoi(FILE *stream) { + mons_qoi_header header; + if (fread(&header, MONS_QOI_HEADER_LEN, 1, stream) != 1) { + fprintf(stderr, "Error reading QOI header"); + return (mons_image){0}; + } + header.width = __bswap_32(header.width); + header.height = __bswap_32(header.height); + mons_pixel previous = {0, 0, 0, 255}; + mons_pixel previously_seen[64] = {0}; + mons_image output = { + .width = header.width, + .height = header.height, + .data = calloc(4, header.width * header.height), + }; + for (size_t i = 0; i < header.width * header.height; i++) { + unsigned char op; + if (fread(&op, 1, 1, stream) != 1) { + fprintf(stderr, "Failed reading operation byte!"); + return (mons_image){0}; + } + switch (op) { + case MONS_QOI_OP_RGB: { + unsigned char rgb[3]; + if (fread(&rgb, 1, 3, stream) != 3) { + fprintf(stderr, "Failed reading RGB data!"); + return (mons_image){0}; + } + output.data[i] = (mons_pixel){ + .r = rgb[0], + .g = rgb[1], + .b = rgb[2], + .a = previous.a, + }; + break; + } + case MONS_QOI_OP_RGBA: { + unsigned char rgba[4]; + if (fread(&rgba, 1, 4, stream) != 4) { + fprintf(stderr, "Failed reading RGBA data!"); + return (mons_image){0}; + } + output.data[i] = (mons_pixel){ + .r = rgba[0], + .g = rgba[1], + .b = rgba[2], + .a = rgba[3], + }; + break; + } + default: + switch (op & 0b11000000) { + case MONS_QOI_OP_INDEX: { + unsigned char index = op & 0b00111111; + output.data[i] = previously_seen[index]; + break; + } + case MONS_QOI_OP_DIFF: { + char r_diff = ((op & 0b00110000) >> 4) - 2; + char g_diff = ((op & 0b00001100) >> 2) - 2; + char b_diff = (op & 0b00000011) - 2; + output.data[i] = (mons_pixel){ + .r = previous.r + r_diff, + .g = previous.g + g_diff, + .b = previous.b + b_diff, + .a = previous.a, + }; + break; + } + case MONS_QOI_OP_LUMA: { + int g_diff = (op & 0b00111111) - 32; + unsigned char second_byte; + if (fread(&second_byte, 1, 1, stream) != 1) { + fprintf(stderr, + "Failed to read second byte of luma operation!"); + return (mons_image){0}; + } + int r_diff = g_diff - 8 + (((second_byte & 0b11110000) >> 4)); + int b_diff = g_diff - 8 + (second_byte & 0b00001111); + output.data[i] = (mons_pixel){ + .r = previous.r + r_diff, + .g = previous.g + g_diff, + .b = previous.b + b_diff, + .a = previous.a, + }; + break; + } + case MONS_QOI_OP_RUN: { + unsigned char run = (op & 0b00111111) + 1; + for (int j = 0; j < run; j++) { + output.data[i++] = previous; + } + i--; + break; + } + default: + break; + } + break; + } + previous = output.data[i]; + unsigned int hash = pixel_index_hash(output.data[i]); + previously_seen[hash] = output.data[i]; + } + fclose(stream); + + return output; +} |