From bf57f8e05f488f2336d32836c8da58573b575f86 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Tue, 12 May 2026 02:57:33 +0200 Subject: [PATCH] Add files via upload --- pikture.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ pikture.h | 45 +++++++++++ 2 files changed, 274 insertions(+) create mode 100644 pikture.c create mode 100644 pikture.h diff --git a/pikture.c b/pikture.c new file mode 100644 index 0000000..fb2397e --- /dev/null +++ b/pikture.c @@ -0,0 +1,229 @@ +/* +pikture.c +Native PIKTURE format encoder and decoder library. +Version: 100 +License: MIT +Made by: Nexoniarz +*/ + +#include "pikture.h" +#include +#include +#include + +#define PIKT_MAGIC "PIK!" +#define PIKT_VERSION 5 +#define PIKT_HASH(r, g, b, a) (((r) * 3 + (g) * 5 + (b) * 7 + (a) * 11) % 64) + +typedef struct { + uint8_t r, g, b, a; +} pikt_pixel; + +/* Reads a 16-bit unsigned integer in Big-Endian format */ +static uint16_t read_u16_be(FILE* f) { + uint8_t bytes[2]; + fread(bytes, 1, 2, f); + return (bytes[0] << 8) | bytes[1]; +} + +/* Writes a 16-bit unsigned integer in Big-Endian format */ +static void write_u16_be(FILE* f, uint16_t value) { + uint8_t bytes[2] = { (value >> 8) & 0xFF, value & 0xFF }; + fwrite(bytes, 1, 2, f); +} + +pikture_t* pikture_load(const char* filename) { + FILE* f = fopen(filename, "rb"); + if (!f) return NULL; + + char magic[4]; + fread(magic, 1, 4, f); + if (memcmp(magic, PIKT_MAGIC, 4) != 0) { + fclose(f); + return NULL; + } + + uint8_t version; + fread(&version, 1, 1, f); + if (version != PIKT_VERSION) { + fclose(f); + return NULL; + } + + pikture_t* img = (pikture_t*)malloc(sizeof(pikture_t)); + if (!img) { + fclose(f); + return NULL; + } + + img->width = read_u16_be(f); + img->height = read_u16_be(f); + + uint8_t depth; + fread(&depth, 1, 1, f); + img->channels = 4; + + size_t data_size = (size_t)img->width * img->height * 4; + img->pixels = (uint8_t*)malloc(data_size); + if (!img->pixels) { + free(img); + fclose(f); + return NULL; + } + + pikt_pixel index[64] = {0}; + uint8_t r = 0, g = 0, b = 0, a = 255; + + size_t ptr = 0; + while (ptr < data_size) { + int b0 = fgetc(f); + if (b0 == EOF) break; + + if (b0 == 0xFE) { + r = fgetc(f); + g = fgetc(f); + b = fgetc(f); + } else if (b0 == 0xFF) { + r = fgetc(f); + g = fgetc(f); + b = fgetc(f); + a = fgetc(f); + } else { + uint8_t match = b0 & 0xC0; + if (match == 0x00) { + pikt_pixel p = index[b0 & 0x3F]; + r = p.r; g = p.g; b = p.b; a = p.a; + } else if (match == 0x40) { + r = (uint8_t)(r + ((b0 >> 4) & 0x03) - 2); + g = (uint8_t)(g + ((b0 >> 2) & 0x03) - 2); + b = (uint8_t)(b + (b0 & 0x03) - 2); + } else if (match == 0x80) { + int b1 = fgetc(f); + int8_t dg = (b0 & 0x3F) - 32; + r = (uint8_t)(r + dg + ((b1 >> 4) & 0x0F) - 8); + g = (uint8_t)(g + dg); + b = (uint8_t)(b + dg + (b1 & 0x0F) - 8); + } else if (match == 0xC0) { + int run = (b0 & 0x3F) + 1; + for (int i = 0; i < run && ptr < data_size; i++) { + img->pixels[ptr++] = r; + img->pixels[ptr++] = g; + img->pixels[ptr++] = b; + img->pixels[ptr++] = a; + } + continue; + } + } + + uint8_t idx_pos = PIKT_HASH(r, g, b, a); + index[idx_pos].r = r; + index[idx_pos].g = g; + index[idx_pos].b = b; + index[idx_pos].a = a; + + if (ptr < data_size) { + img->pixels[ptr++] = r; + img->pixels[ptr++] = g; + img->pixels[ptr++] = b; + img->pixels[ptr++] = a; + } + } + + fclose(f); + return img; +} + +int pikture_save(const char* filename, const pikture_t* img) { + if (!img || !img->pixels) return 0; + + FILE* f = fopen(filename, "wb"); + if (!f) return 0; + + fwrite(PIKT_MAGIC, 1, 4, f); + uint8_t version = PIKT_VERSION; + fwrite(&version, 1, 1, f); + write_u16_be(f, img->width); + write_u16_be(f, img->height); + uint8_t depth = 32; + fwrite(&depth, 1, 1, f); + + pikt_pixel index[64] = {0}; + uint8_t prev_r = 0, prev_g = 0, prev_b = 0, prev_a = 255; + int run = 0; + + size_t total_pixels = (size_t)img->width * img->height; + size_t px_idx = 0; + + while (px_idx < total_pixels * 4) { + uint8_t r = img->pixels[px_idx++]; + uint8_t g = img->pixels[px_idx++]; + uint8_t b = img->pixels[px_idx++]; + uint8_t a = img->pixels[px_idx++]; + + if (r == prev_r && g == prev_g && b == prev_b && a == prev_a) { + run++; + if (run == 62) { + fputc(0xC0 | (run - 1), f); + run = 0; + } + continue; + } + + if (run > 0) { + fputc(0xC0 | (run - 1), f); + run = 0; + } + + uint8_t idx_pos = PIKT_HASH(r, g, b, a); + if (index[idx_pos].r == r && index[idx_pos].g == g && + index[idx_pos].b == b && index[idx_pos].a == a) { + fputc(0x00 | idx_pos, f); + } else { + index[idx_pos].r = r; + index[idx_pos].g = g; + index[idx_pos].b = b; + index[idx_pos].a = a; + + if (a == prev_a) { + int8_t s_dr = (int8_t)(r - prev_r); + int8_t s_dg = (int8_t)(g - prev_g); + int8_t s_db = (int8_t)(b - prev_b); + + if (s_dr >= -2 && s_dr <= 1 && s_dg >= -2 && s_dg <= 1 && s_db >= -2 && s_db <= 1) { + fputc(0x40 | ((s_dr + 2) << 4) | ((s_dg + 2) << 2) | (s_db + 2), f); + } else { + int8_t dr_dg = s_dr - s_dg; + int8_t db_dg = s_db - s_dg; + + if (s_dg >= -32 && s_dg <= 31 && dr_dg >= -8 && dr_dg <= 7 && db_dg >= -8 && db_dg <= 7) { + fputc(0x80 | (s_dg + 32), f); + fputc(((dr_dg + 8) << 4) | (db_dg + 8), f); + } else { + fputc(0xFE, f); + fputc(r, f); fputc(g, f); fputc(b, f); + } + } + } else { + fputc(0xFF, f); + fputc(r, f); fputc(g, f); fputc(b, f); fputc(a, f); + } + } + prev_r = r; prev_g = g; prev_b = b; prev_a = a; + } + + if (run > 0) { + fputc(0xC0 | (run - 1), f); + } + + fclose(f); + return 1; +} + +void pikture_free(pikture_t* img) { + if (img) { + if (img->pixels) { + free(img->pixels); + } + free(img); + } +} diff --git a/pikture.h b/pikture.h new file mode 100644 index 0000000..fc8c198 --- /dev/null +++ b/pikture.h @@ -0,0 +1,45 @@ +/* +pikture.h +Native PIKTURE format encoder and decoder library. +Version: 100 +License: MIT +Made by: Nexoniarz +*/ + +#ifndef PIKTURE_H +#define PIKTURE_H + +#include +#include + +/* Image data structure */ +typedef struct { + uint16_t width; + uint16_t height; + uint8_t channels; /* Always 4 (RGBA) */ + uint8_t* pixels; /* Raw RGBA pixel array */ +} pikture_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* * Loads a PIKTURE image from the specified file path. + * Returns a pointer to a pikture_t struct, or NULL on failure. + */ +pikture_t* pikture_load(const char* filename); + +/* * Saves a PIKTURE image to the specified file path. + * Returns 1 on success, 0 on failure. + */ +int pikture_save(const char* filename, const pikture_t* img); + +/* * Frees the memory allocated for the PIKTURE image. + */ +void pikture_free(pikture_t* img); + +#ifdef __cplusplus +} +#endif + +#endif /* PIKTURE_H */