From 19dc63cf17456433f2d0086e5fa0f6372018bdc7 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 24 Nov 2021 11:07:17 +0100 Subject: [PATCH 001/208] Initial --- .gitignore | 1 + README.md | 22 +++ qoi.h | 509 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qoibench.c | 512 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qoiconv.c | 86 +++++++++ 5 files changed, 1130 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 qoi.h create mode 100644 qoibench.c create mode 100644 qoiconv.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba28150 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +images/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..44a334c --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# QOI - The “Quite OK Image” format for fast, lossless image compression + +Single-file MIT licensed library for C/C++ + +See [qoi.h](https://github.com/phoboslab/qoi/blob/master/qoi.h) for +the documentation. + +More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compression + +## Why? + +Compared to stb_image and stb_image_write QOI offers 20x-50x faster encoding, +3x-4x faster decoding and 20% better compression. It's also stupidly simple and +fits in about 300 lines of C. + + +## Example Usage + +- [qoiconv.c](https://github.com/phoboslab/qoi/blob/master/qoiconv.c) +converts between png <> qoi + - [qoibench.c](https://github.com/phoboslab/qoi/blob/master/qoibench.c) +a simple wrapper to benchmark stbi, libpng and qoi diff --git a/qoi.h b/qoi.h new file mode 100644 index 0000000..270656b --- /dev/null +++ b/qoi.h @@ -0,0 +1,509 @@ +/* + +QOI - The “Quite OK Image” format for fast, lossless image compression + +Dominic Szablewski - https://phoboslab.org + + +-- LICENSE: The MIT License(MIT) + +Copyright(c) 2021 Dominic Szablewski + +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. + + +-- About + +QOI encodes and decodes images in a lossless format. An encoded QOI image is +usually around 10--30% larger than a decently optimized PNG image. + +QOI outperforms simpler PNG encoders in compression ratio and performance. QOI +images are typically 20% smaller than PNGs written with stbi_image but 10% +larger than with libpng. Encoding is 25-50x faster and decoding is 3-4x faster +than stbi_image or libpng. + + +-- Synopsis + +// Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this +// library to create the implementation. + +#define QOI_IMPLEMENTATION +#include "qoi.h" + +// Load and decode a QOI image from the file system into a 32bbp RGBA buffer +int width, height; +void *rgba_pixels = qoi_read("image.qoi", &width, &height, 4); + +// Encode and store an RGBA buffer to the file system +qoi_write("image_new.qoi", rgba_pixels, width, height, 4); + + +-- Documentation + +This library provides the following functions; +- qoi_read -- read and decode a QOI file +- qoi_decode -- decode the raw bytes of a QOI image from memory +- qoi_write -- encode and write a QOI file +- qoi_encode -- encode an rgba buffer into a QOI image in memory + +See the function declaration below for the signature and more information. + +If you don't want/need the qoi_read and qoi_write functions, you can define +QOI_NO_STDIO before including this library. + +This library uses malloc() and free(). To supply your own malloc implementation +you can define QOI_MALLOC and QOI_FREE before including this library. + + +-- Data Format + +A QOI file has the following header, followed by any number of data "chunks". + +struct qoi_header_t { + char [4]; // magic bytes "qoif" + unsigned short width; // image width in pixels + unsigned short height; // image height in pixels + unsigned int size; // number of data bytes following this header +}; + +The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous +pixel value. Pixels are either encoded as + - a run of the previous pixel + - an index into a previously seen pixel + - a difference to the previous pixel value in r,g,b,a + - full r,g,b,a values + +A running array[64] of previously seen pixel values is maintained by the encoder +and decoder. Each pixel that is seen by the encoder and decoder is put into this +array at the position (r^g^b^a) % 64. In the encoder, if the pixel value at this +index matches the current pixel, this index position is written to the stream. + +Each chunk starts with a 2, 3 or 4 bit tag, followed by a number of data bits. +The bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. + +QOI_INDEX { + u8 tag : 2; // b00 + u8 idx : 6; // 6-bit index into the color index array: 0..63 +} + +QOI_RUN_8 { + u8 tag : 3; // b010 + u8 run : 5; // 5-bit run-length repeating the previous pixel: 1..32 +} + +QOI_RUN16 { + u8 tag : 3; // b011 + u16 run : 13; // 13-bit run-length repeating the previous pixel: 33..8224 +} + +QOI_DIFF8 { + u8 tag : 2; // b10 + u8 dr : 2; // 2-bit red channel difference: -1..2 + u8 dg : 2; // 2-bit green channel difference: -1..2 + u8 db : 2; // 2-bit blue channel difference: -1..2 +} + +QOI_DIFF16 { + u8 tag : 3; // b110 + u8 dr : 5; // 5-bit red channel difference: -15..16 + u8 dg : 4; // 4-bit green channel difference: -7.. 8 + u8 db : 4; // 4-bit blue channel difference: -7.. 8 +} + +QOI_DIFF24 { + u8 tag : 4; // b1110 + u8 dr : 5; // 5-bit red channel difference: -15..16 + u8 dg : 5; // 5-bit green channel difference: -15..16 + u8 db : 5; // 5-bit blue channel difference: -15..16 + u8 da : 5; // 5-bit alpha channel difference: -15..16 +} + +QOI_COLOR { + u8 tag : 4; // b1111 + u8 has_r: 1; // red byte follows + u8 has_g: 1; // green byte follows + u8 has_b: 1; // blue byte follows + u8 has_a: 1; // alpha byte follows + u8 r; // red value if has_r == 1: 0..255 + u8 g; // green value if has_g == 1: 0..255 + u8 b; // blue value if has_b == 1: 0..255 + u8 a; // alpha value if has_a == 1: 0..255 +} + +*/ + + +// ----------------------------------------------------------------------------- +// Header - Public functions + +#ifndef QOI_H +#define QOI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef QOI_NO_STDIO + +// Encode raw RGB or RGBA pixels into a QOI image write it to the file system. +// w and h denote the the width and height of the pixel data. channels must be +// either 3 for RGB data or 4 for RGBA. +// The function returns 0 on failure (invalid parameters, or fopen or malloc +// failed) or the number of bytes written on success. + +int qoi_write(const char *filename, const void *data, int w, int h, int channels); + + +// Read and decode a QOI image from the file system into either raw RGB +// (channels=3) or RGBA (channels=4) pixel data. +// The function either returns NULL on failure (invalid data, or malloc or fopen +// failed) or a pointer to the decoded pixels. On success out_w and out_h will +// be set to the width and height of the decoded image. +// The returned pixel data should be free()d after use. + +void *qoi_read(const char *filename, int *out_w, int *out_h, int channels); + +#endif // QOI_NO_STDIO + + +// Encode raw RGB or RGBA pixels into a QOI image in memory. w and h denote the +// width and height of the pixel data. channels must be either 3 for RGB data +// or 4 for RGBA. +// The function either returns NULL on failure (invalid parameters or malloc +// failed) or a pointer to the encoded data on success. On success the out_len +// is set to the size in bytes of the encoded data. +// The returned qoi data should be free()d after user. + +void *qoi_encode(const void *data, int w, int h, int channels, int *out_len); + + +// Decode a QOI image from memory into either raw RGB (channels=3) or RGBA +// (channels=4) pixel data. +// The function either returns NULL on failure (invalid parameters or malloc +// failed) or a pointer to the decoded pixels. On success out_w and out_h will +// be set to the width and height of the decoded image. +// The returned pixel data should be free()d after use. + +void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channels); + +#ifdef __cplusplus +} +#endif +#endif // QOI_H + + +// ----------------------------------------------------------------------------- +// Implementation + +#ifdef QOI_IMPLEMENTATION +#include + +#ifndef QOI_MALLOC + #define QOI_MALLOC(sz) malloc(sz) + #define QOI_FREE(p) free(p) +#endif + +#define QOI_INDEX 0x00 // 00xxxxxx +#define QOI_RUN_8 0x40 // 010xxxxx +#define QOI_RUN_16 0x60 // 011xxxxx +#define QOI_DIFF_8 0x80 // 10xxxxxx +#define QOI_DIFF_16 0xc0 // 110xxxxx +#define QOI_DIFF_24 0xe0 // 1110xxxx +#define QOI_COLOR 0xf0 // 1111xxxx + +#define QOI_MASK_2 0xc0 // 11000000 +#define QOI_MASK_3 0xe0 // 11100000 +#define QOI_MASK_4 0xf0 // 11110000 + +#define QOI_COLOR_HASH(C) (C.rgba.r ^ C.rgba.g ^ C.rgba.b ^ C.rgba.a) + +typedef union { + struct { unsigned char r, g, b, a; } rgba; + unsigned int v; +} qoi_rgba_t; + +typedef union { + char chars[4]; + unsigned int v; +} qoi_magic_t; + +typedef struct { + qoi_magic_t magic; + unsigned short width; + unsigned short height; + unsigned int size; +} qoi_header_t; + +const static qoi_magic_t qoi_magic = {.chars = {'q','o','i','f'}}; + +void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { + if ( + data == NULL || out_len == NULL || + w <= 0 || w >= (1 << 16) || + h <= 0 || h >= (1 << 16) || + channels < 3 || channels > 4 + ) { + return NULL; + } + + int max_size = w * h * (channels + 1) + sizeof(qoi_header_t) + 4; + int p = 0; + unsigned char *bytes = QOI_MALLOC(max_size); + if (!bytes) { + return NULL; + } + + *(qoi_header_t *)bytes = (qoi_header_t) { + .magic = qoi_magic, + .width = w, + .height = h, + .size = 0 // will be set at the end + }; + p += sizeof(qoi_header_t); + + const unsigned char *pixels = (const unsigned char *)data; + + qoi_rgba_t index[64] = {0}; + + int run = 0; + qoi_rgba_t px_prev = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; + qoi_rgba_t px = px_prev; + + int px_len = w * h * channels; + int px_end = px_len - channels; + for (int px_pos = 0; px_pos < px_len; px_pos += channels) { + if (channels == 4) { + px = *(qoi_rgba_t *)(pixels + px_pos); + } + else { + px.rgba.r = pixels[px_pos]; + px.rgba.g = pixels[px_pos+1]; + px.rgba.b = pixels[px_pos+2]; + } + + if (px.v == px_prev.v) { + run++; + } + + if (run > 0 && (run == 0x2020 || px.v != px_prev.v || px_pos == px_end)) { + if (run < 33) { + run -= 1; + bytes[p++] = QOI_RUN_8 | run; + } + else { + run -= 33; + bytes[p++] = QOI_RUN_16 | run >> 8; + bytes[p++] = run; + } + run = 0; + } + + if (px.v != px_prev.v) { + int index_pos = QOI_COLOR_HASH(px) % 64; + + if (index[index_pos].v == px.v) { + bytes[p++] = QOI_INDEX | index_pos; + } + else { + index[index_pos] = px; + + int vr = px.rgba.r - px_prev.rgba.r; + int vg = px.rgba.g - px_prev.rgba.g; + int vb = px.rgba.b - px_prev.rgba.b; + int va = px.rgba.a - px_prev.rgba.a; + + if ( + vr > -16 && vr < 17 && vg > -16 && vg < 17 && + vb > -16 && vb < 17 && va > -16 && va < 17 + ) { + if ( + va == 0 && vr > -2 && vr < 3 && + vg > -2 && vg < 3 && vb > -2 && vb < 3 + ) { + bytes[p++] = QOI_DIFF_8 | ((vr + 1) << 4) | (vg + 1) << 2 | (vb + 1); + } + else if ( + va == 0 && vr > -16 && vr < 17 && + vg > -8 && vg < 9 && vb > -8 && vb < 9 + ) { + bytes[p++] = QOI_DIFF_16 | (vr + 15); + bytes[p++] = ((vg + 7) << 4) | (vb + 7); + } + else { + bytes[p++] = QOI_DIFF_24 | ((vr + 15) >> 1); + bytes[p++] = ((vr + 15) << 7) | ((vg + 15) << 2) | ((vb + 15) >> 3); + bytes[p++] = ((vb + 15) << 5) | (va + 15); + } + } + else { + bytes[p++] = QOI_COLOR | (vr?8:0)|(vg?4:0)|(vb?2:0)|(va?1:0); + if (vr) { bytes[p++] = px.rgba.r; } + if (vg) { bytes[p++] = px.rgba.g; } + if (vb) { bytes[p++] = px.rgba.b; } + if (va) { bytes[p++] = px.rgba.a; } + } + } + } + px_prev = px; + } + + for (int i = 0; i < 4; i++) { + bytes[p++] = 0; + } + + ((qoi_header_t *)bytes)->size = p - sizeof(qoi_header_t); + *out_len = p; + return bytes; +} + +void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channels) { + if (size < sizeof(qoi_header_t)) { + return NULL; + } + + qoi_header_t *header = (qoi_header_t *)data; + if ( + channels < 3 || channels > 4 || + !header->width || !header->height || + header->size + sizeof(qoi_header_t) != size || + header->magic.v != qoi_magic.v + ) { + return NULL; + } + + int px_len = header->width * header->height * channels; + unsigned char *pixels = QOI_MALLOC(px_len); + if (!pixels) { + return NULL; + } + + const unsigned char *bytes = (const unsigned char *)data + sizeof(qoi_header_t); + + int data_len = header->size; + qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; + qoi_rgba_t index[64] = {0}; + + int run = 0; + for (int px_pos = 0, p = 0; px_pos < px_len && p < data_len; px_pos += channels) { + if (run > 0) { + run--; + } + else { + int b1 = bytes[p++]; + + if ((b1 & QOI_MASK_2) == QOI_INDEX) { + px = index[b1 ^ QOI_INDEX]; + } + else if ((b1 & QOI_MASK_3) == QOI_RUN_8) { + run = (b1 & 0x1f); + } + else if ((b1 & QOI_MASK_3) == QOI_RUN_16) { + int b2 = bytes[p++]; + run = (((b1 & 0x1f) << 8) | (b2)) + 32; + } + else if ((b1 & QOI_MASK_2) == QOI_DIFF_8) { + px.rgba.r += ((b1 >> 4) & 0x03) - 1; + px.rgba.g += ((b1 >> 2) & 0x03) - 1; + px.rgba.b += ( b1 & 0x03) - 1; + } + else if ((b1 & QOI_MASK_3) == QOI_DIFF_16) { + int b2 = bytes[p++]; + px.rgba.r += (b1 & 0x1f) - 15; + px.rgba.g += (b2 >> 4) - 7; + px.rgba.b += (b2 & 0x0f) - 7; + } + else if ((b1 & QOI_MASK_4) == QOI_DIFF_24) { + int b2 = bytes[p++]; + int b3 = bytes[p++]; + px.rgba.r += (((b1 & 0x0f) << 1) | (b2 >> 7)) - 15; + px.rgba.g += ((b2 & 0x7c) >> 2) - 15; + px.rgba.b += (((b2 & 0x03) << 3) | ((b3 & 0xe0) >> 5)) - 15; + px.rgba.a += (b3 & 0x1f) - 15; + } + else if ((b1 & QOI_MASK_4) == QOI_COLOR) { + if (b1 & 8) { px.rgba.r = bytes[p++]; } + if (b1 & 4) { px.rgba.g = bytes[p++]; } + if (b1 & 2) { px.rgba.b = bytes[p++]; } + if (b1 & 1) { px.rgba.a = bytes[p++]; } + } + + index[QOI_COLOR_HASH(px) % 64] = px; + } + + if (channels == 4) { + *(qoi_rgba_t*)(pixels + px_pos) = px; + } + else { + pixels[px_pos] = px.rgba.r; + pixels[px_pos+1] = px.rgba.g; + pixels[px_pos+2] = px.rgba.b; + } + } + + *out_w = header->width; + *out_h = header->height; + return pixels; +} + +#ifndef QOI_NO_STDIO +#include + +int qoi_write(const char *filename, const void *data, int w, int h, int channels) { + int size; + void *encoded = qoi_encode(data, w, h, channels, &size); + if (!encoded) { + return 0; + } + + FILE *f = fopen(filename, "wb"); + if (!f) { + QOI_FREE(encoded); + return 0; + } + + fwrite(encoded, 1, size, f); + fclose(f); + QOI_FREE(encoded); + return size; +} + +void *qoi_read(const char *filename, int *out_w, int *out_h, int channels) { + FILE *f = fopen(filename, "rb"); + if (!f) { + return NULL; + } + + fseek(f, 0, SEEK_END); + int size = ftell(f); + fseek(f, 0, SEEK_SET); + + void *data = QOI_MALLOC(size); + if (!data) { + return NULL; + } + + int bytes_read = fread(data, 1, size, f); + fclose(f); + + void *pixels = qoi_decode(data, bytes_read, out_w, out_h, channels); + QOI_FREE(data); + return pixels; +} + +#endif // QOI_NO_STDIO +#endif // QOI_IMPLEMENTATION diff --git a/qoibench.c b/qoibench.c new file mode 100644 index 0000000..ac7a4d0 --- /dev/null +++ b/qoibench.c @@ -0,0 +1,512 @@ +/* + +Simple benchmark suite for png, stbi and qoi + +Requires libpng, "stb_image.h" and "stb_image_write.h" +Compile with: + gcc qoibench.c -std=gnu99 -O3 -o qoibench + +Dominic Szablewski - https://phoboslab.org + + +-- LICENSE: The MIT License(MIT) + +Copyright(c) 2021 Dominic Szablewski + +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. + +*/ + +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_PNG +#define STBI_NO_LINEAR +#include "stb_image.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +#define QOI_IMPLEMENTATION +#include "qoi.h" + + + + +// ----------------------------------------------------------------------------- +// Cross platform high resolution timer +// From https://gist.github.com/ForeverZer0/0a4f80fc02b96e19380ebb7a3debbee5 + +#include +#if defined(__linux) + #define HAVE_POSIX_TIMER + #include + #ifdef CLOCK_MONOTONIC + #define CLOCKID CLOCK_MONOTONIC + #else + #define CLOCKID CLOCK_REALTIME + #endif +#elif defined(__APPLE__) + #define HAVE_MACH_TIMER + #include +#elif defined(_WIN32) + #define WIN32_LEAN_AND_MEAN + #include +#endif + +static uint64_t ns() { + static uint64_t is_init = 0; +#if defined(__APPLE__) + static mach_timebase_info_data_t info; + if (0 == is_init) { + mach_timebase_info(&info); + is_init = 1; + } + uint64_t now; + now = mach_absolute_time(); + now *= info.numer; + now /= info.denom; + return now; +#elif defined(__linux) + static struct timespec linux_rate; + if (0 == is_init) { + clock_getres(CLOCKID, &linux_rate); + is_init = 1; + } + uint64_t now; + struct timespec spec; + clock_gettime(CLOCKID, &spec); + now = spec.tv_sec * 1.0e9 + spec.tv_nsec; + return now; +#elif defined(_WIN32) + static LARGE_INTEGER win_frequency; + if (0 == is_init) { + QueryPerformanceFrequency(&win_frequency); + is_init = 1; + } + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return (uint64_t) ((1e9 * now.QuadPart) / win_frequency.QuadPart); +#endif +} + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define ERROR(...) printf("abort at line " TOSTRING(__LINE__) ": " __VA_ARGS__); printf("\n"); exit(1) + + +// ----------------------------------------------------------------------------- +// libpng encode/decode wrappers +// Seriously, who thought this was a good abstraction for an API to read/write +// images? + +typedef struct { + int size; + int capacity; + unsigned char *data; +} libpng_write_t; + +void libpng_encode_callback(png_structp png_ptr, png_bytep data, png_size_t length) { + libpng_write_t *write_data = (libpng_write_t*)png_get_io_ptr(png_ptr); + if (write_data->size + length >= write_data->capacity) { + ERROR("PNG write"); + } + memcpy(write_data->data + write_data->size, data, length); + write_data->size += length; +} + +void *libpng_encode(void *pixels, int w, int h, int *out_len) { + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) { + ERROR("png_create_write_struct"); + } + + png_infop info = png_create_info_struct(png); + if (!info) { + ERROR("png_create_info_struct"); + } + + if (setjmp(png_jmpbuf(png))) { + ERROR("png_jmpbuf"); + } + + // Output is 8bit depth, RGBA format. + png_set_IHDR( + png, + info, + w, h, + 8, + PNG_COLOR_TYPE_RGBA, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT + ); + + png_bytep row_pointers[h]; + for(int y = 0; y < h; y++){ + row_pointers[y] = ((unsigned char *)pixels + y * w * 4); + } + + libpng_write_t write_data = { + .size = 0, + .capacity = w * h * 4, + .data = malloc(w * h * 4) + }; + + png_set_rows(png, info, row_pointers); + png_set_write_fn(png, &write_data, libpng_encode_callback, NULL); + png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL); + + png_destroy_write_struct(&png, &info); + + *out_len = write_data.size; + return write_data.data; +} + + +typedef struct { + int pos; + int size; + unsigned char *data; +} libpng_read_t; + +void png_decode_callback(png_structp png, png_bytep data, png_size_t length) { + libpng_read_t *read_data = (libpng_read_t*)png_get_io_ptr(png); + if (read_data->pos + length > read_data->size) { + ERROR("PNG read %d bytes at pos %d (size: %d)", length, read_data->pos, read_data->size); + } + memcpy(data, read_data->data + read_data->pos, length); + read_data->pos += length; +} + +void *libpng_decode(void *data, int size, int *out_w, int *out_h) { + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) { + ERROR("png_create_read_struct"); + } + + png_infop info = png_create_info_struct(png); + if (!info) { + ERROR("png_create_info_struct"); + } + + libpng_read_t read_data = { + .pos = 0, + .size = size, + .data = data + }; + + png_set_read_fn(png, &read_data, png_decode_callback); + png_set_sig_bytes(png, 0); + png_read_info(png, info); + + png_uint_32 w, h; + int bitDepth, colorType, interlaceType; + png_get_IHDR(png, info, &w, &h, &bitDepth, &colorType, &interlaceType, NULL, NULL); + + // 16 bit -> 8 bit + png_set_strip_16(png); + + // 1, 2, 4 bit -> 8 bit + if (bitDepth < 8) { + png_set_packing(png); + } + + if (colorType & PNG_COLOR_MASK_PALETTE) { + png_set_expand(png); + } + + if (!(colorType & PNG_COLOR_MASK_COLOR)) { + png_set_gray_to_rgb(png); + } + + // set paletted or RGB images with transparency to full alpha so we get RGBA + if (png_get_valid(png, info, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png); + } + + // make sure every pixel has an alpha value + if (!(colorType & PNG_COLOR_MASK_ALPHA)) { + png_set_filler(png, 255, PNG_FILLER_AFTER); + } + + png_read_update_info(png, info); + + unsigned char* out = malloc(w * h * 4); + *out_w = w; + *out_h = h; + + // png_uint_32 rowBytes = png_get_rowbytes(png, info); + png_bytep row_pointers[h]; + for (png_uint_32 row = 0; row < h; row++ ) { + row_pointers[row] = (png_bytep)(out + (row * w * 4)); + } + + png_read_image(png, row_pointers); + png_read_end(png, info); + png_destroy_read_struct( &png, &info, NULL); + + return out; +} + + +// ----------------------------------------------------------------------------- +// stb_image encode callback + +void stbi_write_callback(void *context, void *data, int size) { + int *encoded_size = (int *)context; + *encoded_size += size; + // In theory we'd need to do another malloc(), memcpy() and free() here to + // be fair to the other decode functions... +} + + +// ----------------------------------------------------------------------------- +// function to load a whole file into memory + +void *fload(const char *path, int *out_size) { + FILE *fh = fopen(path, "rb"); + if (!fh) { + ERROR("Can't open file"); + } + + fseek(fh, 0, SEEK_END); + int size = ftell(fh); + fseek(fh, 0, SEEK_SET); + + void *buffer = malloc(size); + if (!buffer) { + ERROR("Malloc for %d bytes failed", size); + } + + if (!fread(buffer, size, 1, fh)) { + ERROR("Can't read file %s", path); + } + fclose(fh); + + *out_size = size; + return buffer; +} + + +// ----------------------------------------------------------------------------- +// benchmark runner + +typedef struct { + uint64_t size; + uint64_t encode_time; + uint64_t decode_time; +} benchmark_lib_result_t; + +typedef struct { + uint64_t px; + int w; + int h; + benchmark_lib_result_t libpng; + benchmark_lib_result_t stbi; + benchmark_lib_result_t qoi; +} benchmark_result_t; + + +// Run __VA_ARGS__ a number of times and meassure the time taken. The first +// run is ignored. +#define BENCHMARK_FN(RUNS, AVG_TIME, ...) \ + do { \ + uint64_t time = 0; \ + for (int i = 0; i <= RUNS; i++) { \ + uint64_t time_start = ns(); \ + __VA_ARGS__ \ + uint64_t time_end = ns(); \ + if (i > 0) { \ + time += time_end - time_start; \ + } \ + } \ + AVG_TIME = time / RUNS; \ + } while (0) + + +benchmark_result_t benchmark_image(const char *path, int runs) { + int encoded_png_size; + int encoded_qoi_size; + int w; + int h; + + // Load the encoded PNG, encoded QOI and raw pixels into memory + void *pixels = (void *)stbi_load(path, &w, &h, NULL, 4); + void *encoded_png = fload(path, &encoded_png_size); + void *encoded_qoi = qoi_encode(pixels, w, h, 4, &encoded_qoi_size); + + if (!pixels || !encoded_qoi || !encoded_png) { + ERROR("Error decoding %s\n", path); + } + + benchmark_result_t res = {0}; + res.px = w * h; + res.w = w; + res.h = h; + + + // Decoding + + BENCHMARK_FN(runs, res.libpng.decode_time, { + int dec_w, dec_h; + void *dec_p = libpng_decode(encoded_png, encoded_png_size, &dec_w, &dec_h); + free(dec_p); + }); + + BENCHMARK_FN(runs, res.stbi.decode_time, { + int dec_w, dec_h, dec_channels; + void *dec_p = stbi_load_from_memory(encoded_png, encoded_png_size, &dec_w, &dec_h, &dec_channels, 4); + free(dec_p); + }); + + BENCHMARK_FN(runs, res.qoi.decode_time, { + int dec_w, dec_h; + void *dec_p = qoi_decode(encoded_qoi, encoded_qoi_size, &dec_w, &dec_h, 4); + free(dec_p); + }); + + + // Encoding + + BENCHMARK_FN(runs, res.libpng.encode_time, { + int enc_size; + void *enc_p = libpng_encode(pixels, w, h, &enc_size); + res.libpng.size = enc_size; + free(enc_p); + }); + + BENCHMARK_FN(runs, res.stbi.encode_time, { + int enc_size = 0; + stbi_write_png_to_func(stbi_write_callback, &enc_size, w, h, 4, pixels, 0); + res.stbi.size = enc_size; + }); + + BENCHMARK_FN(runs, res.qoi.encode_time, { + int enc_size; + void *enc_p = qoi_encode(pixels, w, h, 4, &enc_size); + res.qoi.size = enc_size; + free(enc_p); + }); + + free(pixels); + free(encoded_png); + free(encoded_qoi); + + return res; +} + +void benchmark_print_result(const char *head, benchmark_result_t res) { + double px = res.px; + printf("## %s size: %dx%d\n", head, res.w, res.h); + printf(" decode ms encode ms decode mpps encode mpps size kb\n"); + printf( + "libpng: %8.1f %8.1f %8.2f %8.2f %8d\n", + (double)res.libpng.decode_time/1000000.0, + (double)res.libpng.encode_time/1000000.0, + (res.libpng.decode_time > 0 ? px / ((double)res.libpng.decode_time/1000.0) : 0), + (res.libpng.encode_time > 0 ? px / ((double)res.libpng.encode_time/1000.0) : 0), + res.libpng.size/1024 + ); + printf( + "stbi: %8.1f %8.1f %8.2f %8.2f %8d\n", + (double)res.stbi.decode_time/1000000.0, + (double)res.stbi.encode_time/1000000.0, + (res.stbi.decode_time > 0 ? px / ((double)res.stbi.decode_time/1000.0) : 0), + (res.stbi.encode_time > 0 ? px / ((double)res.stbi.encode_time/1000.0) : 0), + res.stbi.size/1024 + ); + printf( + "qoi: %8.1f %8.1f %8.2f %8.2f %8d\n", + (double)res.qoi.decode_time/1000000.0, + (double)res.qoi.encode_time/1000000.0, + (res.qoi.decode_time > 0 ? px / ((double)res.qoi.decode_time/1000.0) : 0), + (res.qoi.encode_time > 0 ? px / ((double)res.qoi.encode_time/1000.0) : 0), + res.qoi.size/1024 + ); + printf("\n"); +} + +int main(int argc, char **argv) { + if (argc < 3) { + printf("Usage: qoibench \n"); + printf("Example: qoibench 10 images/textures/\n"); + exit(1); + } + + float total_percentage = 0; + int total_size = 0; + + benchmark_result_t totals = {0}; + + int runs = atoi(argv[1]); + DIR *dir = opendir(argv[2]); + if (runs <=0) { + runs = 1; + } + + if (!dir) { + ERROR("Couldn't open directory %s", argv[2]); + } + + printf("## Benchmarking %s/*.png -- %d runs\n\n", argv[2], runs); + struct dirent *file; + int i; + for (i = 0; dir && (file = readdir(dir)) != NULL; i++) { + if (strcmp(file->d_name + strlen(file->d_name) - 4, ".png") != 0) { + continue; + } + + char *file_path = malloc(strlen(file->d_name) + strlen(argv[2])+8); + sprintf(file_path, "%s/%s", argv[2], file->d_name); + + benchmark_result_t res = benchmark_image(file_path, runs); + benchmark_print_result(file_path, res); + + free(file_path); + + + totals.px += res.px; + totals.libpng.encode_time += res.libpng.encode_time; + totals.libpng.decode_time += res.libpng.decode_time; + totals.libpng.size += res.libpng.size; + totals.stbi.encode_time += res.stbi.encode_time; + totals.stbi.decode_time += res.stbi.decode_time; + totals.stbi.size += res.stbi.size; + totals.qoi.encode_time += res.qoi.encode_time; + totals.qoi.decode_time += res.qoi.decode_time; + totals.qoi.size += res.qoi.size; + } + closedir(dir); + + totals.px /= i; + totals.libpng.encode_time /= i; + totals.libpng.decode_time /= i; + totals.libpng.size /= i; + totals.stbi.encode_time /= i; + totals.stbi.decode_time /= i; + totals.stbi.size /= i; + totals.qoi.encode_time /= i; + totals.qoi.decode_time /= i; + totals.qoi.size /= i; + + benchmark_print_result("Totals (AVG)", totals); + + return 0; +} diff --git a/qoiconv.c b/qoiconv.c new file mode 100644 index 0000000..a66f481 --- /dev/null +++ b/qoiconv.c @@ -0,0 +1,86 @@ +/* + +Command line tool to convert between png <> qoi format + +Requires "stb_image.h" and "stb_image_write.h" +Compile with: + gcc qoiconv.c -std=c99 -O3 -o qoiconv + +Dominic Szablewski - https://phoboslab.org + + +-- LICENSE: The MIT License(MIT) + +Copyright(c) 2021 Dominic Szablewski + +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. + +*/ + + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_PNG +#define STBI_NO_LINEAR +#include "stb_image.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +#define QOI_IMPLEMENTATION +#include "qoi.h" + + +#define STR_ENDS_WITH(S, E) (strcmp(S + strlen(S) - (sizeof(E)-1), E) == 0) + +int main(int argc, char **argv) { + if (argc < 3) { + printf("Usage: qoiconv infile outfile\n"); + printf("Examples:\n"); + printf(" qoiconv image.png image.qoi\n"); + printf(" qoiconv image.qoi image.png\n"); + exit(1); + } + + void *pixels = NULL; + int w, h; + if (STR_ENDS_WITH(argv[1], ".png")) { + pixels = (void *)stbi_load(argv[1], &w, &h, NULL, 4); + } + else if (STR_ENDS_WITH(argv[1], ".qoi")) { + pixels = qoi_read(argv[1], &w, &h, 4); + } + + if (pixels == NULL) { + printf("Couldn't load/decode %s\n", argv[1]); + exit(1); + } + + int encoded = 0; + if (STR_ENDS_WITH(argv[2], ".png")) { + encoded = stbi_write_png(argv[2], w, h, 4, pixels, 0); + } + else if (STR_ENDS_WITH(argv[2], ".qoi")) { + encoded = qoi_write(argv[2], pixels, w, h, 4); + } + + if (!encoded) { + printf("Couldn't write/encode %s\n", argv[2]); + exit(1); + } + + return 0; +} From de17b3c2c1002dddd97366fd11f51cd429957a31 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 24 Nov 2021 11:19:49 +0100 Subject: [PATCH 002/208] Fix compile instructions --- qoibench.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoibench.c b/qoibench.c index ac7a4d0..db2933e 100644 --- a/qoibench.c +++ b/qoibench.c @@ -4,7 +4,7 @@ Simple benchmark suite for png, stbi and qoi Requires libpng, "stb_image.h" and "stb_image_write.h" Compile with: - gcc qoibench.c -std=gnu99 -O3 -o qoibench + gcc qoibench.c -std=gnu99 -lpng -O3 -o qoibench Dominic Szablewski - https://phoboslab.org From e969322d0029ffdc9e2ba597f4e734b506388cec Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 24 Nov 2021 22:09:08 +0100 Subject: [PATCH 003/208] Don't run past the padding of the byte stream when decoding; re #4 --- qoi.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/qoi.h b/qoi.h index 270656b..083d532 100644 --- a/qoi.h +++ b/qoi.h @@ -145,6 +145,10 @@ QOI_COLOR { u8 a; // alpha value if has_a == 1: 0..255 } +The byte stream is padded with 4 zero bytes. Size the longest chunk we can +encounter is 5 bytes (QOI_COLOR with RGBA set), with this padding we just have +to check for an overrun once per decode loop iteration. + */ @@ -231,6 +235,7 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel #define QOI_MASK_4 0xf0 // 11110000 #define QOI_COLOR_HASH(C) (C.rgba.r ^ C.rgba.g ^ C.rgba.b ^ C.rgba.a) +#define QOI_PADDING 4 typedef union { struct { unsigned char r, g, b, a; } rgba; @@ -362,7 +367,7 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { px_prev = px; } - for (int i = 0; i < 4; i++) { + for (int i = 0; i < QOI_PADDING; i++) { bytes[p++] = 0; } @@ -394,16 +399,16 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel const unsigned char *bytes = (const unsigned char *)data + sizeof(qoi_header_t); - int data_len = header->size; + int data_len = header->size - QOI_PADDING; qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; qoi_rgba_t index[64] = {0}; int run = 0; - for (int px_pos = 0, p = 0; px_pos < px_len && p < data_len; px_pos += channels) { + for (int px_pos = 0, p = 0; px_pos < px_len; px_pos += channels) { if (run > 0) { run--; } - else { + else if (p < data_len) { int b1 = bytes[p++]; if ((b1 & QOI_MASK_2) == QOI_INDEX) { From 324c2243b2fb35f741a88d73ae10c132ccbba0b1 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 24 Nov 2021 22:28:35 +0100 Subject: [PATCH 004/208] Add warning about untrusted input; #4 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 44a334c..4722e71 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ the documentation. More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compression +⚠️ Please note that this library is not yet ready to deal with untrusted input. + ## Why? Compared to stb_image and stb_image_write QOI offers 20x-50x faster encoding, From c03edb2f2658c13b5f84ee59e3469e0b0b6eb5d1 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 25 Nov 2021 09:43:11 +0100 Subject: [PATCH 005/208] Fix naming of chunks in the data format documentation; close #9 --- qoi.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qoi.h b/qoi.h index 083d532..ab206f1 100644 --- a/qoi.h +++ b/qoi.h @@ -106,26 +106,26 @@ QOI_RUN_8 { u8 run : 5; // 5-bit run-length repeating the previous pixel: 1..32 } -QOI_RUN16 { +QOI_RUN_16 { u8 tag : 3; // b011 u16 run : 13; // 13-bit run-length repeating the previous pixel: 33..8224 } -QOI_DIFF8 { +QOI_DIFF_8 { u8 tag : 2; // b10 u8 dr : 2; // 2-bit red channel difference: -1..2 u8 dg : 2; // 2-bit green channel difference: -1..2 u8 db : 2; // 2-bit blue channel difference: -1..2 } -QOI_DIFF16 { +QOI_DIFF_16 { u8 tag : 3; // b110 u8 dr : 5; // 5-bit red channel difference: -15..16 u8 dg : 4; // 4-bit green channel difference: -7.. 8 u8 db : 4; // 4-bit blue channel difference: -7.. 8 } -QOI_DIFF24 { +QOI_DIFF_24 { u8 tag : 4; // b1110 u8 dr : 5; // 5-bit red channel difference: -15..16 u8 dg : 5; // 5-bit green channel difference: -15..16 From 81b438cb5604a9cc413f7e41f6fa29247fdcba6c Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 25 Nov 2021 13:51:05 +0100 Subject: [PATCH 006/208] Change header to big endian; make it independent from host byte order; close #10 --- qoi.h | 80 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/qoi.h b/qoi.h index ab206f1..9f2e595 100644 --- a/qoi.h +++ b/qoi.h @@ -72,13 +72,13 @@ you can define QOI_MALLOC and QOI_FREE before including this library. -- Data Format -A QOI file has the following header, followed by any number of data "chunks". +A QOI file has a 12 byte header, followed by any number of data "chunks". struct qoi_header_t { char [4]; // magic bytes "qoif" - unsigned short width; // image width in pixels - unsigned short height; // image height in pixels - unsigned int size; // number of data bytes following this header + unsigned short width; // image width in pixels (BE) + unsigned short height; // image height in pixels (BE) + unsigned int size; // number of data bytes following this header (BE) }; The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous @@ -235,27 +235,22 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel #define QOI_MASK_4 0xf0 // 11110000 #define QOI_COLOR_HASH(C) (C.rgba.r ^ C.rgba.g ^ C.rgba.b ^ C.rgba.a) +#define QOI_MAGIC \ + (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ + ((unsigned int)'i') << 8 | ((unsigned int)'f')) +#define QOI_HEADER_SIZE 12 #define QOI_PADDING 4 +#define QOI_READ_16(B, P) ((B[P++] & 0xff) << 8 | (B[P++] & 0xff)) +#define QOI_READ_32(B, P) (QOI_READ_16(B, P) << 16 | QOI_READ_16(B, P)) +#define QOI_WRITE_16(B, P, V) (B[P++] = (0xff00 & V) >> 8, B[P++] = (0xff & V)) +#define QOI_WRITE_32(B, P, V) (QOI_WRITE_16(B, P, V >> 16), QOI_WRITE_16(B, P, V)) + typedef union { struct { unsigned char r, g, b, a; } rgba; unsigned int v; } qoi_rgba_t; -typedef union { - char chars[4]; - unsigned int v; -} qoi_magic_t; - -typedef struct { - qoi_magic_t magic; - unsigned short width; - unsigned short height; - unsigned int size; -} qoi_header_t; - -const static qoi_magic_t qoi_magic = {.chars = {'q','o','i','f'}}; - void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { if ( data == NULL || out_len == NULL || @@ -266,20 +261,17 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { return NULL; } - int max_size = w * h * (channels + 1) + sizeof(qoi_header_t) + 4; + int max_size = w * h * (channels + 1) + QOI_HEADER_SIZE + QOI_PADDING; int p = 0; unsigned char *bytes = QOI_MALLOC(max_size); if (!bytes) { return NULL; } - *(qoi_header_t *)bytes = (qoi_header_t) { - .magic = qoi_magic, - .width = w, - .height = h, - .size = 0 // will be set at the end - }; - p += sizeof(qoi_header_t); + QOI_WRITE_32(bytes, p, QOI_MAGIC); + QOI_WRITE_16(bytes, p, w); + QOI_WRITE_16(bytes, p, h); + QOI_WRITE_32(bytes, p, 0); // size, will be set later const unsigned char *pixels = (const unsigned char *)data; @@ -371,44 +363,50 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { bytes[p++] = 0; } - ((qoi_header_t *)bytes)->size = p - sizeof(qoi_header_t); + int data_len = p - QOI_HEADER_SIZE; *out_len = p; + + p = 8; + QOI_WRITE_32(bytes, p, data_len); return bytes; } void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channels) { - if (size < sizeof(qoi_header_t)) { + if (channels < 3 || channels > 4 || size < QOI_HEADER_SIZE) { return NULL; } - qoi_header_t *header = (qoi_header_t *)data; + const unsigned char *bytes = (const unsigned char *)data; + int p = 0; + + int magic = QOI_READ_32(bytes, p); + int w = QOI_READ_16(bytes, p); + int h = QOI_READ_16(bytes, p); + int data_len = QOI_READ_32(bytes, p); + if ( - channels < 3 || channels > 4 || - !header->width || !header->height || - header->size + sizeof(qoi_header_t) != size || - header->magic.v != qoi_magic.v + w == 0 || h == 0 || magic != QOI_MAGIC || + size != data_len + QOI_HEADER_SIZE ) { return NULL; } - int px_len = header->width * header->height * channels; + int px_len = w * h * channels; unsigned char *pixels = QOI_MALLOC(px_len); if (!pixels) { return NULL; } - const unsigned char *bytes = (const unsigned char *)data + sizeof(qoi_header_t); - - int data_len = header->size - QOI_PADDING; qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; qoi_rgba_t index[64] = {0}; int run = 0; - for (int px_pos = 0, p = 0; px_pos < px_len; px_pos += channels) { + int chunks_len = size - QOI_PADDING; + for (int px_pos = 0; px_pos < px_len; px_pos += channels) { if (run > 0) { run--; } - else if (p < data_len) { + else if (p < chunks_len) { int b1 = bytes[p++]; if ((b1 & QOI_MASK_2) == QOI_INDEX) { @@ -460,8 +458,8 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel } } - *out_w = header->width; - *out_h = header->height; + *out_w = w; + *out_h = h; return pixels; } From 30f8a39ec8ca609d33d401d0b6d94290f3bc05a6 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 25 Nov 2021 15:14:56 +0100 Subject: [PATCH 007/208] Fix qoibench avg calculation: ignore non-png entries --- qoibench.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/qoibench.c b/qoibench.c index db2933e..cb4d1e8 100644 --- a/qoibench.c +++ b/qoibench.c @@ -467,11 +467,12 @@ int main(int argc, char **argv) { printf("## Benchmarking %s/*.png -- %d runs\n\n", argv[2], runs); struct dirent *file; - int i; - for (i = 0; dir && (file = readdir(dir)) != NULL; i++) { + int count = 0; + for (int i = 0; dir && (file = readdir(dir)) != NULL; i++) { if (strcmp(file->d_name + strlen(file->d_name) - 4, ".png") != 0) { continue; } + count++; char *file_path = malloc(strlen(file->d_name) + strlen(argv[2])+8); sprintf(file_path, "%s/%s", argv[2], file->d_name); @@ -495,16 +496,16 @@ int main(int argc, char **argv) { } closedir(dir); - totals.px /= i; - totals.libpng.encode_time /= i; - totals.libpng.decode_time /= i; - totals.libpng.size /= i; - totals.stbi.encode_time /= i; - totals.stbi.decode_time /= i; - totals.stbi.size /= i; - totals.qoi.encode_time /= i; - totals.qoi.decode_time /= i; - totals.qoi.size /= i; + totals.px /= count; + totals.libpng.encode_time /= count; + totals.libpng.decode_time /= count; + totals.libpng.size /= count; + totals.stbi.encode_time /= count; + totals.stbi.decode_time /= count; + totals.stbi.size /= count; + totals.qoi.encode_time /= count; + totals.qoi.decode_time /= count; + totals.qoi.size /= count; benchmark_print_result("Totals (AVG)", totals); From dd0b04b319573609737044d0e6cf9927786a3019 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Fri, 26 Nov 2021 13:22:00 +0100 Subject: [PATCH 008/208] Fix #22: avoid UB when reading ints from the stream; --- qoi.h | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/qoi.h b/qoi.h index 9f2e595..f12b3af 100644 --- a/qoi.h +++ b/qoi.h @@ -241,16 +241,33 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel #define QOI_HEADER_SIZE 12 #define QOI_PADDING 4 -#define QOI_READ_16(B, P) ((B[P++] & 0xff) << 8 | (B[P++] & 0xff)) -#define QOI_READ_32(B, P) (QOI_READ_16(B, P) << 16 | QOI_READ_16(B, P)) -#define QOI_WRITE_16(B, P, V) (B[P++] = (0xff00 & V) >> 8, B[P++] = (0xff & V)) -#define QOI_WRITE_32(B, P, V) (QOI_WRITE_16(B, P, V >> 16), QOI_WRITE_16(B, P, V)) - typedef union { struct { unsigned char r, g, b, a; } rgba; unsigned int v; } qoi_rgba_t; +void qoi_write_16(unsigned char *bytes, int *p, unsigned short v) { + bytes[(*p)++] = (0xff00 & v) >> 8; + bytes[(*p)++] = (0xff & v); +} + +void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { + qoi_write_16(bytes, p, (v & 0xffff0000) >> 16); + qoi_write_16(bytes, p, (v & 0xffff)); +} + +unsigned int qoi_read_16(const unsigned char *bytes, int *p) { + unsigned int a = bytes[(*p)++]; + unsigned int b = bytes[(*p)++]; + return (a << 8) | b; +} + +unsigned int qoi_read_32(const unsigned char *bytes, int *p) { + unsigned int a = qoi_read_16(bytes, p); + unsigned int b = qoi_read_16(bytes, p); + return (a << 16) | b; +} + void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { if ( data == NULL || out_len == NULL || @@ -268,10 +285,10 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { return NULL; } - QOI_WRITE_32(bytes, p, QOI_MAGIC); - QOI_WRITE_16(bytes, p, w); - QOI_WRITE_16(bytes, p, h); - QOI_WRITE_32(bytes, p, 0); // size, will be set later + qoi_write_32(bytes, &p, QOI_MAGIC); + qoi_write_16(bytes, &p, w); + qoi_write_16(bytes, &p, h); + qoi_write_32(bytes, &p, 0); // size, will be set later const unsigned char *pixels = (const unsigned char *)data; @@ -367,7 +384,7 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { *out_len = p; p = 8; - QOI_WRITE_32(bytes, p, data_len); + qoi_write_32(bytes, &p, data_len); return bytes; } @@ -379,10 +396,10 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel const unsigned char *bytes = (const unsigned char *)data; int p = 0; - int magic = QOI_READ_32(bytes, p); - int w = QOI_READ_16(bytes, p); - int h = QOI_READ_16(bytes, p); - int data_len = QOI_READ_32(bytes, p); + int magic = qoi_read_32(bytes, &p); + int w = qoi_read_16(bytes, &p); + int h = qoi_read_16(bytes, &p); + int data_len = qoi_read_32(bytes, &p); if ( w == 0 || h == 0 || magic != QOI_MAGIC || From 7b1567cc9dd6302be56691a1191540f6c1f6e4b3 Mon Sep 17 00:00:00 2001 From: Brad Fish Date: Fri, 26 Nov 2021 12:28:49 -0800 Subject: [PATCH 009/208] Close file on allocation failure --- qoi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/qoi.h b/qoi.h index f12b3af..d188208 100644 --- a/qoi.h +++ b/qoi.h @@ -514,6 +514,7 @@ void *qoi_read(const char *filename, int *out_w, int *out_h, int channels) { void *data = QOI_MALLOC(size); if (!data) { + fclose(f); return NULL; } From 344ba65a579312b1a9ee918841cd8f928b1dc28a Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Fri, 26 Nov 2021 22:51:22 +0100 Subject: [PATCH 010/208] Avoid UTF-8 --- qoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index f12b3af..e5f4d45 100644 --- a/qoi.h +++ b/qoi.h @@ -1,6 +1,6 @@ /* -QOI - The “Quite OK Image” format for fast, lossless image compression +QOI - The "Quite OK Image" format for fast, lossless image compression Dominic Szablewski - https://phoboslab.org From a902b57ede3a54f6ea01a7161aa88e6ed3d98649 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 27 Nov 2021 13:21:16 +0100 Subject: [PATCH 011/208] Announce changes in the file format --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 4722e71..1015c81 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,13 @@ More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compressi ⚠️ Please note that this library is not yet ready to deal with untrusted input. +⚠️ 2021.11.27 – the specification for QOI has changed to accomodate some +concerns with the format. These specification changes are **not yet reflected in +the code here**. If you are working on a QOI implementation, please refer to +[#37 The QOI File Format Specification](https://github.com/phoboslab/qoi/issues/37) +for the details. + + ## Why? Compared to stb_image and stb_image_write QOI offers 20x-50x faster encoding, From 697abf669640472892a0911487f8b8d35f469aa3 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 27 Nov 2021 17:23:52 +0100 Subject: [PATCH 012/208] Align the data format with new spec #37 --- qoi.h | 134 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 65 insertions(+), 69 deletions(-) diff --git a/qoi.h b/qoi.h index e5f4d45..6dd232d 100644 --- a/qoi.h +++ b/qoi.h @@ -75,10 +75,14 @@ you can define QOI_MALLOC and QOI_FREE before including this library. A QOI file has a 12 byte header, followed by any number of data "chunks". struct qoi_header_t { - char [4]; // magic bytes "qoif" - unsigned short width; // image width in pixels (BE) - unsigned short height; // image height in pixels (BE) - unsigned int size; // number of data bytes following this header (BE) + char magic[4]; // magic bytes "qoif" + uint32_t width; // image width in pixels (BE) + uint32_t height; // image height in pixels (BE) + uint8_t channels; // must be 3 (RGB) or 4 (RGBA) + uint8_t colorspace; // a bitmap 0000rgba where + // - a zero bit indicates sRGBA, + // - a one bit indicates linear (user interpreted) + // colorspace for each channel }; The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous @@ -113,24 +117,24 @@ QOI_RUN_16 { QOI_DIFF_8 { u8 tag : 2; // b10 - u8 dr : 2; // 2-bit red channel difference: -1..2 - u8 dg : 2; // 2-bit green channel difference: -1..2 - u8 db : 2; // 2-bit blue channel difference: -1..2 + u8 dr : 2; // 2-bit red channel difference: -2..1 + u8 dg : 2; // 2-bit green channel difference: -2..1 + u8 db : 2; // 2-bit blue channel difference: -2..1 } QOI_DIFF_16 { u8 tag : 3; // b110 - u8 dr : 5; // 5-bit red channel difference: -15..16 - u8 dg : 4; // 4-bit green channel difference: -7.. 8 - u8 db : 4; // 4-bit blue channel difference: -7.. 8 + u8 dr : 5; // 5-bit red channel difference: -16..15 + u8 dg : 4; // 4-bit green channel difference: -8.. 7 + u8 db : 4; // 4-bit blue channel difference: -8.. 7 } QOI_DIFF_24 { u8 tag : 4; // b1110 - u8 dr : 5; // 5-bit red channel difference: -15..16 - u8 dg : 5; // 5-bit green channel difference: -15..16 - u8 db : 5; // 5-bit blue channel difference: -15..16 - u8 da : 5; // 5-bit alpha channel difference: -15..16 + u8 dr : 5; // 5-bit red channel difference: -16..15 + u8 dg : 5; // 5-bit green channel difference: -16..15 + u8 db : 5; // 5-bit blue channel difference: -16..15 + u8 da : 5; // 5-bit alpha channel difference: -16..15 } QOI_COLOR { @@ -246,26 +250,19 @@ typedef union { unsigned int v; } qoi_rgba_t; -void qoi_write_16(unsigned char *bytes, int *p, unsigned short v) { - bytes[(*p)++] = (0xff00 & v) >> 8; - bytes[(*p)++] = (0xff & v); -} - void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { - qoi_write_16(bytes, p, (v & 0xffff0000) >> 16); - qoi_write_16(bytes, p, (v & 0xffff)); -} - -unsigned int qoi_read_16(const unsigned char *bytes, int *p) { - unsigned int a = bytes[(*p)++]; - unsigned int b = bytes[(*p)++]; - return (a << 8) | b; + bytes[(*p)++] = (0xff000000 & v) >> 24; + bytes[(*p)++] = (0x00ff0000 & v) >> 16; + bytes[(*p)++] = (0x0000ff00 & v) >> 8; + bytes[(*p)++] = (0x000000ff & v); } unsigned int qoi_read_32(const unsigned char *bytes, int *p) { - unsigned int a = qoi_read_16(bytes, p); - unsigned int b = qoi_read_16(bytes, p); - return (a << 16) | b; + unsigned int a = bytes[(*p)++]; + unsigned int b = bytes[(*p)++]; + unsigned int c = bytes[(*p)++]; + unsigned int d = bytes[(*p)++]; + return (a << 24) | (b << 16) | (c << 8) | d; } void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { @@ -286,9 +283,10 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { } qoi_write_32(bytes, &p, QOI_MAGIC); - qoi_write_16(bytes, &p, w); - qoi_write_16(bytes, &p, h); - qoi_write_32(bytes, &p, 0); // size, will be set later + qoi_write_32(bytes, &p, w); + qoi_write_32(bytes, &p, h); + bytes[p++] = channels; + bytes[p++] = 0; // TODO: accept a colorspace as a parameter to this function const unsigned char *pixels = (const unsigned char *)data; @@ -342,26 +340,26 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { int va = px.rgba.a - px_prev.rgba.a; if ( - vr > -16 && vr < 17 && vg > -16 && vg < 17 && - vb > -16 && vb < 17 && va > -16 && va < 17 + vr > -17 && vr < 16 && vg > -17 && vg < 16 && + vb > -17 && vb < 16 && va > -17 && va < 16 ) { if ( - va == 0 && vr > -2 && vr < 3 && - vg > -2 && vg < 3 && vb > -2 && vb < 3 + va == 0 && vr > -3 && vr < 2 && + vg > -3 && vg < 2 && vb > -3 && vb < 2 ) { - bytes[p++] = QOI_DIFF_8 | ((vr + 1) << 4) | (vg + 1) << 2 | (vb + 1); + bytes[p++] = QOI_DIFF_8 | ((vr + 2) << 4) | (vg + 2) << 2 | (vb + 2); } else if ( - va == 0 && vr > -16 && vr < 17 && - vg > -8 && vg < 9 && vb > -8 && vb < 9 + va == 0 && vr > -17 && vr < 16 && + vg > -9 && vg < 8 && vb > -9 && vb < 8 ) { - bytes[p++] = QOI_DIFF_16 | (vr + 15); - bytes[p++] = ((vg + 7) << 4) | (vb + 7); + bytes[p++] = QOI_DIFF_16 | (vr + 16); + bytes[p++] = ((vg + 8) << 4) | (vb + 8); } else { - bytes[p++] = QOI_DIFF_24 | ((vr + 15) >> 1); - bytes[p++] = ((vr + 15) << 7) | ((vg + 15) << 2) | ((vb + 15) >> 3); - bytes[p++] = ((vb + 15) << 5) | (va + 15); + bytes[p++] = QOI_DIFF_24 | ((vr + 16) >> 1); + bytes[p++] = ((vr + 16) << 7) | ((vg + 16) << 2) | ((vb + 16) >> 3); + bytes[p++] = ((vb + 16) << 5) | (va + 16); } } else { @@ -380,35 +378,33 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { bytes[p++] = 0; } - int data_len = p - QOI_HEADER_SIZE; *out_len = p; - - p = 8; - qoi_write_32(bytes, &p, data_len); return bytes; } void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channels) { - if (channels < 3 || channels > 4 || size < QOI_HEADER_SIZE) { + if (channels < 3 || channels > 4 || size < QOI_HEADER_SIZE + QOI_PADDING) { return NULL; } const unsigned char *bytes = (const unsigned char *)data; int p = 0; - int magic = qoi_read_32(bytes, &p); - int w = qoi_read_16(bytes, &p); - int h = qoi_read_16(bytes, &p); - int data_len = qoi_read_32(bytes, &p); + unsigned int header_magic = qoi_read_32(bytes, &p); + unsigned int header_w = qoi_read_32(bytes, &p); + unsigned int header_h = qoi_read_32(bytes, &p); + unsigned int header_channels = bytes[p++]; + unsigned int header_colorspace = bytes[p++]; if ( - w == 0 || h == 0 || magic != QOI_MAGIC || - size != data_len + QOI_HEADER_SIZE + header_w == 0 || header_h == 0 || + header_channels < 3 || header_channels > 4 || + header_magic != QOI_MAGIC ) { return NULL; } - int px_len = w * h * channels; + int px_len = header_w * header_h * channels; unsigned char *pixels = QOI_MALLOC(px_len); if (!pixels) { return NULL; @@ -437,23 +433,23 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel run = (((b1 & 0x1f) << 8) | (b2)) + 32; } else if ((b1 & QOI_MASK_2) == QOI_DIFF_8) { - px.rgba.r += ((b1 >> 4) & 0x03) - 1; - px.rgba.g += ((b1 >> 2) & 0x03) - 1; - px.rgba.b += ( b1 & 0x03) - 1; + px.rgba.r += ((b1 >> 4) & 0x03) - 2; + px.rgba.g += ((b1 >> 2) & 0x03) - 2; + px.rgba.b += ( b1 & 0x03) - 2; } else if ((b1 & QOI_MASK_3) == QOI_DIFF_16) { int b2 = bytes[p++]; - px.rgba.r += (b1 & 0x1f) - 15; - px.rgba.g += (b2 >> 4) - 7; - px.rgba.b += (b2 & 0x0f) - 7; + px.rgba.r += (b1 & 0x1f) - 16; + px.rgba.g += (b2 >> 4) - 8; + px.rgba.b += (b2 & 0x0f) - 8; } else if ((b1 & QOI_MASK_4) == QOI_DIFF_24) { int b2 = bytes[p++]; int b3 = bytes[p++]; - px.rgba.r += (((b1 & 0x0f) << 1) | (b2 >> 7)) - 15; - px.rgba.g += ((b2 & 0x7c) >> 2) - 15; - px.rgba.b += (((b2 & 0x03) << 3) | ((b3 & 0xe0) >> 5)) - 15; - px.rgba.a += (b3 & 0x1f) - 15; + px.rgba.r += (((b1 & 0x0f) << 1) | (b2 >> 7)) - 16; + px.rgba.g += ((b2 & 0x7c) >> 2) - 16; + px.rgba.b += (((b2 & 0x03) << 3) | ((b3 & 0xe0) >> 5)) - 16; + px.rgba.a += (b3 & 0x1f) - 16; } else if ((b1 & QOI_MASK_4) == QOI_COLOR) { if (b1 & 8) { px.rgba.r = bytes[p++]; } @@ -475,8 +471,8 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel } } - *out_w = w; - *out_h = h; + *out_w = header_w; + *out_h = header_h; return pixels; } From ff542c2ae656c574ffe7bfaec9967146e8f96d2a Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 27 Nov 2021 18:36:17 +0100 Subject: [PATCH 013/208] Change the API to supply/return channel count and colorspace info --- README.md | 7 ++- qoi.h | 154 ++++++++++++++++++++++++++++++++++------------------- qoibench.c | 18 +++++-- qoiconv.c | 26 ++++++--- 4 files changed, 137 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 1015c81..6ea62a7 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,14 @@ More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compressi ⚠️ Please note that this library is not yet ready to deal with untrusted input. ⚠️ 2021.11.27 – the specification for QOI has changed to accomodate some -concerns with the format. These specification changes are **not yet reflected in -the code here**. If you are working on a QOI implementation, please refer to +concerns with the format. If you are working on a QOI implementation, please +refer to [#37 The QOI File Format Specification](https://github.com/phoboslab/qoi/issues/37) for the details. +These specification changes are ~~not yet reflected in the code here~~ +reflected in qoi.h now. + ## Why? diff --git a/qoi.h b/qoi.h index 6dd232d..23e5473 100644 --- a/qoi.h +++ b/qoi.h @@ -45,12 +45,21 @@ than stbi_image or libpng. #define QOI_IMPLEMENTATION #include "qoi.h" -// Load and decode a QOI image from the file system into a 32bbp RGBA buffer -int width, height; -void *rgba_pixels = qoi_read("image.qoi", &width, &height, 4); +// Encode and store an RGBA buffer to the file system. The qoi_desc describes +// the input pixel data. +qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){ + .width = 1920, + .height = 1080, + .channels = 4, + .colorspace = QOI_SRGB +}); + +// Load and decode a QOI image from the file system into a 32bbp RGBA buffer. +// The qoi_desc struct will be filled with the width, height, number of channels +// and colorspace read from the file header. +qoi_desc desc; +void *rgba_pixels = qoi_read("image.qoi", &desc, 4); -// Encode and store an RGBA buffer to the file system -qoi_write("image_new.qoi", rgba_pixels, width, height, 4); -- Documentation @@ -166,48 +175,75 @@ to check for an overrun once per decode loop iteration. extern "C" { #endif +// A pointer to qoi_desc struct has to be supplied to all of qoi's functions. It +// describes either the input format (for qoi_write, qoi_encode), or is filled +// with the description read from the file header (for qoi_read, qoi_decode). + +// The colorspace in this qoi_desc is a bitmap with 0000rgba where a 0-bit +// indicates sRGB and a 1-bit indicates linear colorspace for each channel. You +// may use one of the predefined constants: QOI_SRGB, QOI_SRGB_LINEAR_ALPHA or +// QOI_LINEAR. The colorspace is purely informative. It will be saved to the +// file header, but does not affect en-/decoding in any way. + +#define QOI_SRGB 0x00 +#define QOI_SRGB_LINEAR_ALPHA 0x01 +#define QOI_LINEAR 0x0f + +typedef struct { + unsigned int width; + unsigned int height; + unsigned char channels; + unsigned char colorspace; +} qoi_desc; + #ifndef QOI_NO_STDIO -// Encode raw RGB or RGBA pixels into a QOI image write it to the file system. -// w and h denote the the width and height of the pixel data. channels must be -// either 3 for RGB data or 4 for RGBA. +// Encode raw RGB or RGBA pixels into a QOI image and write it to the file +// system. The qoi_desc struct must be filled with the image width, height, +// number of channels (3 = RGB, 4 = RGBA) and the colorspace. + // The function returns 0 on failure (invalid parameters, or fopen or malloc // failed) or the number of bytes written on success. -int qoi_write(const char *filename, const void *data, int w, int h, int channels); +int qoi_write(const char *filename, const void *data, const qoi_desc *desc); -// Read and decode a QOI image from the file system into either raw RGB -// (channels=3) or RGBA (channels=4) pixel data. +// Read and decode a QOI image from the file system. If channels is 0, the +// number of channels from the file header is used. If channels is 3 or 4 the +// output format will be forced into this number of channels. + // The function either returns NULL on failure (invalid data, or malloc or fopen -// failed) or a pointer to the decoded pixels. On success out_w and out_h will -// be set to the width and height of the decoded image. +// failed) or a pointer to the decoded pixels. On success, the qoi_desc struct +// will be filled with the description from the file header. + // The returned pixel data should be free()d after use. -void *qoi_read(const char *filename, int *out_w, int *out_h, int channels); +void *qoi_read(const char *filename, qoi_desc *desc, int channels); #endif // QOI_NO_STDIO -// Encode raw RGB or RGBA pixels into a QOI image in memory. w and h denote the -// width and height of the pixel data. channels must be either 3 for RGB data -// or 4 for RGBA. +// Encode raw RGB or RGBA pixels into a QOI image in memory. + // The function either returns NULL on failure (invalid parameters or malloc -// failed) or a pointer to the encoded data on success. On success the out_len +// failed) or a pointer to the encoded data on success. On success the out_len // is set to the size in bytes of the encoded data. + // The returned qoi data should be free()d after user. -void *qoi_encode(const void *data, int w, int h, int channels, int *out_len); +void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len); -// Decode a QOI image from memory into either raw RGB (channels=3) or RGBA -// (channels=4) pixel data. +// Decode a QOI image from memory. + // The function either returns NULL on failure (invalid parameters or malloc -// failed) or a pointer to the decoded pixels. On success out_w and out_h will -// be set to the width and height of the decoded image. +// failed) or a pointer to the decoded pixels. On success, the qoi_desc struct +// is filled with the description from the file header. + // The returned pixel data should be free()d after use. -void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channels); +void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); + #ifdef __cplusplus } @@ -265,17 +301,20 @@ unsigned int qoi_read_32(const unsigned char *bytes, int *p) { return (a << 24) | (b << 16) | (c << 8) | d; } -void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { +void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { if ( - data == NULL || out_len == NULL || - w <= 0 || w >= (1 << 16) || - h <= 0 || h >= (1 << 16) || - channels < 3 || channels > 4 + data == NULL || out_len == NULL || desc == NULL || + desc->width == 0 || desc->height == 0 || + desc->channels < 3 || desc->channels > 4 || + (desc->colorspace & 0xf0) != 0 ) { return NULL; } - int max_size = w * h * (channels + 1) + QOI_HEADER_SIZE + QOI_PADDING; + int max_size = + desc->width * desc->height * (desc->channels + 1) + + QOI_HEADER_SIZE + QOI_PADDING; + int p = 0; unsigned char *bytes = QOI_MALLOC(max_size); if (!bytes) { @@ -283,10 +322,11 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { } qoi_write_32(bytes, &p, QOI_MAGIC); - qoi_write_32(bytes, &p, w); - qoi_write_32(bytes, &p, h); - bytes[p++] = channels; - bytes[p++] = 0; // TODO: accept a colorspace as a parameter to this function + qoi_write_32(bytes, &p, desc->width); + qoi_write_32(bytes, &p, desc->height); + bytes[p++] = desc->channels; + bytes[p++] = desc->colorspace; + const unsigned char *pixels = (const unsigned char *)data; @@ -295,11 +335,11 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { int run = 0; qoi_rgba_t px_prev = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; qoi_rgba_t px = px_prev; - - int px_len = w * h * channels; - int px_end = px_len - channels; - for (int px_pos = 0; px_pos < px_len; px_pos += channels) { - if (channels == 4) { + + int px_len = desc->width * desc->height * desc->channels; + int px_end = px_len - desc->channels; + for (int px_pos = 0; px_pos < px_len; px_pos += desc->channels) { + if (desc->channels == 4) { px = *(qoi_rgba_t *)(pixels + px_pos); } else { @@ -382,8 +422,12 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) { return bytes; } -void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channels) { - if (channels < 3 || channels > 4 || size < QOI_HEADER_SIZE + QOI_PADDING) { +void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { + if ( + data == NULL || desc == NULL || + (channels != 0 && channels != 3 && channels != 4) || + size < QOI_HEADER_SIZE + QOI_PADDING + ) { return NULL; } @@ -391,20 +435,24 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel int p = 0; unsigned int header_magic = qoi_read_32(bytes, &p); - unsigned int header_w = qoi_read_32(bytes, &p); - unsigned int header_h = qoi_read_32(bytes, &p); - unsigned int header_channels = bytes[p++]; - unsigned int header_colorspace = bytes[p++]; + desc->width = qoi_read_32(bytes, &p); + desc->height = qoi_read_32(bytes, &p); + desc->channels = bytes[p++]; + desc->colorspace = bytes[p++]; if ( - header_w == 0 || header_h == 0 || - header_channels < 3 || header_channels > 4 || + desc->width == 0 || desc->height == 0 || + desc->channels < 3 || desc->channels > 4 || header_magic != QOI_MAGIC ) { return NULL; } - int px_len = header_w * header_h * channels; + if (channels == 0) { + channels = desc->channels; + } + + int px_len = desc->width * desc->height * channels; unsigned char *pixels = QOI_MALLOC(px_len); if (!pixels) { return NULL; @@ -471,17 +519,15 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel } } - *out_w = header_w; - *out_h = header_h; return pixels; } #ifndef QOI_NO_STDIO #include -int qoi_write(const char *filename, const void *data, int w, int h, int channels) { +int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { int size; - void *encoded = qoi_encode(data, w, h, channels, &size); + void *encoded = qoi_encode(data, desc, &size); if (!encoded) { return 0; } @@ -498,7 +544,7 @@ int qoi_write(const char *filename, const void *data, int w, int h, int channels return size; } -void *qoi_read(const char *filename, int *out_w, int *out_h, int channels) { +void *qoi_read(const char *filename, qoi_desc *desc, int channels) { FILE *f = fopen(filename, "rb"); if (!f) { return NULL; @@ -516,7 +562,7 @@ void *qoi_read(const char *filename, int *out_w, int *out_h, int channels) { int bytes_read = fread(data, 1, size, f); fclose(f); - void *pixels = qoi_decode(data, bytes_read, out_w, out_h, channels); + void *pixels = qoi_decode(data, bytes_read, desc, channels); QOI_FREE(data); return pixels; } diff --git a/qoibench.c b/qoibench.c index cb4d1e8..f48e236 100644 --- a/qoibench.c +++ b/qoibench.c @@ -350,7 +350,12 @@ benchmark_result_t benchmark_image(const char *path, int runs) { // Load the encoded PNG, encoded QOI and raw pixels into memory void *pixels = (void *)stbi_load(path, &w, &h, NULL, 4); void *encoded_png = fload(path, &encoded_png_size); - void *encoded_qoi = qoi_encode(pixels, w, h, 4, &encoded_qoi_size); + void *encoded_qoi = qoi_encode(pixels, &(qoi_desc){ + .width = w, + .height = h, + .channels = 4, + .colorspace = QOI_SRGB + }, &encoded_qoi_size); if (!pixels || !encoded_qoi || !encoded_png) { ERROR("Error decoding %s\n", path); @@ -377,8 +382,8 @@ benchmark_result_t benchmark_image(const char *path, int runs) { }); BENCHMARK_FN(runs, res.qoi.decode_time, { - int dec_w, dec_h; - void *dec_p = qoi_decode(encoded_qoi, encoded_qoi_size, &dec_w, &dec_h, 4); + qoi_desc desc; + void *dec_p = qoi_decode(encoded_qoi, encoded_qoi_size, &desc, 4); free(dec_p); }); @@ -400,7 +405,12 @@ benchmark_result_t benchmark_image(const char *path, int runs) { BENCHMARK_FN(runs, res.qoi.encode_time, { int enc_size; - void *enc_p = qoi_encode(pixels, w, h, 4, &enc_size); + void *enc_p = qoi_encode(pixels, &(qoi_desc){ + .width = w, + .height = h, + .channels = 4, + .colorspace = QOI_SRGB + }, &enc_size); res.qoi.size = enc_size; free(enc_p); }); diff --git a/qoiconv.c b/qoiconv.c index a66f481..6e7ad36 100644 --- a/qoiconv.c +++ b/qoiconv.c @@ -48,20 +48,24 @@ SOFTWARE. int main(int argc, char **argv) { if (argc < 3) { - printf("Usage: qoiconv infile outfile\n"); + printf("Usage: qoiconv \n"); printf("Examples:\n"); - printf(" qoiconv image.png image.qoi\n"); - printf(" qoiconv image.qoi image.png\n"); + printf(" qoiconv input.png output.qoi\n"); + printf(" qoiconv input.qoi output.png\n"); exit(1); } void *pixels = NULL; - int w, h; + int w, h, channels; if (STR_ENDS_WITH(argv[1], ".png")) { - pixels = (void *)stbi_load(argv[1], &w, &h, NULL, 4); + pixels = (void *)stbi_load(argv[1], &w, &h, &channels, 0); } else if (STR_ENDS_WITH(argv[1], ".qoi")) { - pixels = qoi_read(argv[1], &w, &h, 4); + qoi_desc desc; + pixels = qoi_read(argv[1], &desc, 0); + channels = desc.channels; + w = desc.width; + h = desc.height; } if (pixels == NULL) { @@ -71,10 +75,15 @@ int main(int argc, char **argv) { int encoded = 0; if (STR_ENDS_WITH(argv[2], ".png")) { - encoded = stbi_write_png(argv[2], w, h, 4, pixels, 0); + encoded = stbi_write_png(argv[2], w, h, channels, pixels, 0); } else if (STR_ENDS_WITH(argv[2], ".qoi")) { - encoded = qoi_write(argv[2], pixels, w, h, 4); + encoded = qoi_write(argv[2], pixels, &(qoi_desc){ + .width = w, + .height = h, + .channels = channels, + .colorspace = QOI_SRGB + }); } if (!encoded) { @@ -82,5 +91,6 @@ int main(int argc, char **argv) { exit(1); } + free(pixels); return 0; } From 5506399e0db25c8ca7d5faa08a02ce33e8318757 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 27 Nov 2021 18:41:02 +0100 Subject: [PATCH 014/208] Fix HEADER_SIZE for the new header --- qoi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qoi.h b/qoi.h index 23e5473..c2ce95e 100644 --- a/qoi.h +++ b/qoi.h @@ -81,7 +81,7 @@ you can define QOI_MALLOC and QOI_FREE before including this library. -- Data Format -A QOI file has a 12 byte header, followed by any number of data "chunks". +A QOI file has a 14 byte header, followed by any number of data "chunks". struct qoi_header_t { char magic[4]; // magic bytes "qoif" @@ -278,7 +278,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #define QOI_MAGIC \ (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ ((unsigned int)'i') << 8 | ((unsigned int)'f')) -#define QOI_HEADER_SIZE 12 +#define QOI_HEADER_SIZE 14 #define QOI_PADDING 4 typedef union { From 7053672d3a2fba68aca2bb0687123a81a6e42e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Sun, 28 Nov 2021 01:08:52 +0100 Subject: [PATCH 015/208] Starts to improve the documentation. --- qoi.h | 108 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 42 deletions(-) diff --git a/qoi.h b/qoi.h index 5f43c38..b87c1ca 100644 --- a/qoi.h +++ b/qoi.h @@ -109,54 +109,78 @@ index matches the current pixel, this index position is written to the stream. Each chunk starts with a 2, 3 or 4 bit tag, followed by a number of data bits. The bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. -QOI_INDEX { - u8 tag : 2; // b00 - u8 idx : 6; // 6-bit index into the color index array: 0..63 -} +The possible chunks are: -QOI_RUN_8 { - u8 tag : 3; // b010 - u8 run : 5; // 5-bit run-length repeating the previous pixel: 1..32 -} + | QOI_INDEX | + | Byte + 0 | + | 7 6 5 4 3 2 1 0 | + |-------------------------| + | 0 0 i5 i4 i3 i2 i1 i0 | -QOI_RUN_16 { - u8 tag : 3; // b011 - u16 run : 13; // 13-bit run-length repeating the previous pixel: 33..8224 -} + i5...i0 forming the 6-bit index into the color index array: 0..63 -QOI_DIFF_8 { - u8 tag : 2; // b10 - u8 dr : 2; // 2-bit red channel difference: -2..1 - u8 dg : 2; // 2-bit green channel difference: -2..1 - u8 db : 2; // 2-bit blue channel difference: -2..1 -} -QOI_DIFF_16 { - u8 tag : 3; // b110 - u8 dr : 5; // 5-bit red channel difference: -16..15 - u8 dg : 4; // 4-bit green channel difference: -8.. 7 - u8 db : 4; // 4-bit blue channel difference: -8.. 7 -} + | QOI_INDEX | + | Byte + 0 | + | 7 6 5 4 3 2 1 0 | + |-------------------------| + | 0 1 0 i4 i3 i2 i1 i0 | -QOI_DIFF_24 { - u8 tag : 4; // b1110 - u8 dr : 5; // 5-bit red channel difference: -16..15 - u8 dg : 5; // 5-bit green channel difference: -16..15 - u8 db : 5; // 5-bit blue channel difference: -16..15 - u8 da : 5; // 5-bit alpha channel difference: -16..15 -} + i4...i0 forming the 5-bit run-length repeating the previous pixel: 1..32 + + + | QOI_RUN_16 | + | Byte + 0 | Byte + 1 | + | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | + |--------------------------------|-------------------------| + | 0 1 1 r12 r11 r10 r9 r8 | r7 r6 r5 r4 r3 r2 r1 r0 | + + r12...r0 forming the 13-bit run-length repeating the previous pixel: 33..8224 + + + | QOI_DIFF_8 | + | Byte + 0 | + | 7 6 5 4 3 2 1 0 | + |-------------------------| + | 1 0 r1 r0 g1 g0 b1 b0 | + + r1...r0 forming the red channel difference between -2..1 + g1...g0 forming the green channel difference between -2..1 + b1...b0 forming the blue channel difference between -2..1 + + + | QOI_DIFF_16 | + | Byte + 0 | Byte + 1 | + | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | + |-------------------------|-------------------------| + | 1 1 0 r4 r3 r2 r1 r0 | g3 g2 g1 g0 b3 b2 b1 b0 | + + r4...r0 forming the red channel difference between -16..15 + g3...g0 forming the green channel difference between -8..7 + b3...b0 forming the blue channel difference between -8..7 + + + | QOI_DIFF_24 | + | Byte + 0 | Byte + 1 | Byte + 1 | + | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | + |-------------------------|-------------------------|-------------------------| + | 1 1 1 0 r4 r3 r2 r1 | r0 g4 g3 g2 g1 g0 b4 b3 | b2 b1 b0 a4 a3 a2 a1 a0 | + + r4...r0 forming the red channel difference between -16..15 + g4...g0 forming the green channel difference between -16..15 + b4...b0 forming the blue channel difference between -16..15 + a4...a0 forming the alpha channel difference between -16..15 + + + | QOI_COLOR | + | Byte + 0 | + | 7 6 5 4 3 2 1 0 | + |-------------------------| + | 1 1 1 1 r g b a | + + for each set bit r, g, b and a another byte follows in the order written here. If such a byte follows, + it will replace the current color channel value with the value of this byte. -QOI_COLOR { - u8 tag : 4; // b1111 - u8 has_r: 1; // red byte follows - u8 has_g: 1; // green byte follows - u8 has_b: 1; // blue byte follows - u8 has_a: 1; // alpha byte follows - u8 r; // red value if has_r == 1: 0..255 - u8 g; // green value if has_g == 1: 0..255 - u8 b; // blue value if has_b == 1: 0..255 - u8 a; // alpha value if has_a == 1: 0..255 -} The byte stream is padded with 4 zero bytes. Size the longest chunk we can encounter is 5 bytes (QOI_COLOR with RGBA set), with this padding we just have From ef9ce10bc1fd6eb5254ef24f6ac903361ec245d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Sun, 28 Nov 2021 01:17:24 +0100 Subject: [PATCH 016/208] Adds wraparound specification --- qoi.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/qoi.h b/qoi.h index b87c1ca..2467312 100644 --- a/qoi.h +++ b/qoi.h @@ -148,6 +148,9 @@ The possible chunks are: g1...g0 forming the green channel difference between -2..1 b1...b0 forming the blue channel difference between -2..1 + The difference to the current channel values are using a wraparound operation, so "1 - 2" will result in 255, while + "255 + 1" will result in 0. + | QOI_DIFF_16 | | Byte + 0 | Byte + 1 | @@ -159,6 +162,9 @@ The possible chunks are: g3...g0 forming the green channel difference between -8..7 b3...b0 forming the blue channel difference between -8..7 + The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while + "250 + 7" will result in 1. + | QOI_DIFF_24 | | Byte + 0 | Byte + 1 | Byte + 1 | @@ -166,10 +172,13 @@ The possible chunks are: |-------------------------|-------------------------|-------------------------| | 1 1 1 0 r4 r3 r2 r1 | r0 g4 g3 g2 g1 g0 b4 b3 | b2 b1 b0 a4 a3 a2 a1 a0 | - r4...r0 forming the red channel difference between -16..15 - g4...g0 forming the green channel difference between -16..15 - b4...b0 forming the blue channel difference between -16..15 - a4...a0 forming the alpha channel difference between -16..15 + r4...r0 forming the red channel difference between -16..15 + g4...g0 forming the green channel difference between -16..15 + b4...b0 forming the blue channel difference between -16..15 + a4...a0 forming the alpha channel difference between -16..15 + + The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while + "250 + 7" will result in 1. | QOI_COLOR | From 8ebd4e7b6dbcbe87fceb9b301d24f7fe30096a9d Mon Sep 17 00:00:00 2001 From: Samyak S Sarnayak Date: Sun, 28 Nov 2021 16:18:01 +0530 Subject: [PATCH 017/208] Fix a typo: "user" -> "use" --- qoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index 5f43c38..c11c840 100644 --- a/qoi.h +++ b/qoi.h @@ -229,7 +229,7 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels); // failed) or a pointer to the encoded data on success. On success the out_len // is set to the size in bytes of the encoded data. -// The returned qoi data should be free()d after user. +// The returned qoi data should be free()d after use. void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len); From 4f8f59d53efdca6c73ed95fd21ce18ad54b2e5f2 Mon Sep 17 00:00:00 2001 From: Luis Batalha Date: Sun, 28 Nov 2021 15:12:08 +0000 Subject: [PATCH 018/208] add packages section includes aur package link --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 6ea62a7..a705d69 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,8 @@ fits in about 300 lines of C. converts between png <> qoi - [qoibench.c](https://github.com/phoboslab/qoi/blob/master/qoibench.c) a simple wrapper to benchmark stbi, libpng and qoi + +## Packages + +[AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. + From 80356a5aaa1e9c3bba8f7f882e5464ccfd838a2e Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sun, 28 Nov 2021 17:36:05 +0100 Subject: [PATCH 019/208] Improve documentation, whitespace, wording --- qoi.h | 179 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 101 insertions(+), 78 deletions(-) diff --git a/qoi.h b/qoi.h index a57b2e8..c4e13a6 100644 --- a/qoi.h +++ b/qoi.h @@ -32,9 +32,8 @@ QOI encodes and decodes images in a lossless format. An encoded QOI image is usually around 10--30% larger than a decently optimized PNG image. QOI outperforms simpler PNG encoders in compression ratio and performance. QOI -images are typically 20% smaller than PNGs written with stbi_image but 10% -larger than with libpng. Encoding is 25-50x faster and decoding is 3-4x faster -than stbi_image or libpng. +images are typically 20% smaller than PNGs written with stbi_image. Encoding is +25-50x faster and decoding is 3-4x faster than stbi_image or libpng. -- Synopsis @@ -108,92 +107,107 @@ index matches the current pixel, this index position is written to the stream. Each chunk starts with a 2, 3 or 4 bit tag, followed by a number of data bits. The bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. +All values encoded in these data bits have the most significant bit (MSB) on the +left. The possible chunks are: - | QOI_INDEX | - | Byte + 0 | - | 7 6 5 4 3 2 1 0 | - |-------------------------| - | 0 0 i5 i4 i3 i2 i1 i0 | + - QOI_INDEX ------------- +| Byte[0] | +| 7 6 5 4 3 2 1 0 | +|-------+-----------------| +| 0 0 | index | - i5...i0 forming the 6-bit index into the color index array: 0..63 +2-bit tag b00 +6-bit index into the color index array: 0..63 - | QOI_INDEX | - | Byte + 0 | - | 7 6 5 4 3 2 1 0 | - |-------------------------| - | 0 1 0 i4 i3 i2 i1 i0 | + - QOI_RUN_8 ------------- +| Byte[0] | +| 7 6 5 4 3 2 1 0 | +|----------+--------------| +| 0 1 0 | run | - i4...i0 forming the 5-bit run-length repeating the previous pixel: 1..32 +3-bit tag b010 +5-bit run-length repeating the previous pixel: 1..32 - | QOI_RUN_16 | - | Byte + 0 | Byte + 1 | - | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | - |--------------------------------|-------------------------| - | 0 1 1 r12 r11 r10 r9 r8 | r7 r6 r5 r4 r3 r2 r1 r0 | + - QOI_RUN_16 -------------------------------------- +| Byte[0] | Byte[1] | +| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | +|----------+----------------------------------------| +| 0 1 1 | run | - r12...r0 forming the 13-bit run-length repeating the previous pixel: 33..8224 +3-bit tag b011 +13-bit run-length repeating the previous pixel: 33..8224 - | QOI_DIFF_8 | - | Byte + 0 | - | 7 6 5 4 3 2 1 0 | - |-------------------------| - | 1 0 r1 r0 g1 g0 b1 b0 | + - QOI_DIFF_8 ------------ +| Byte[0] | +| 7 6 5 4 3 2 1 0 | +|-------+-----+-----+-----| +| 1 0 | dr | db | bg | - r1...r0 forming the red channel difference between -2..1 - g1...g0 forming the green channel difference between -2..1 - b1...b0 forming the blue channel difference between -2..1 +2-bit tag b10 +2-bit red channel difference from the previous pixel between -2..1 +2-bit green channel difference from the previous pixel between -2..1 +2-bit blue channel difference from the previous pixel between -2..1 - The difference to the current channel values are using a wraparound operation, so "1 - 2" will result in 255, while - "255 + 1" will result in 0. +The difference to the current channel values are using a wraparound operation, +so "1 - 2" will result in 255, while "255 + 1" will result in 0. - | QOI_DIFF_16 | - | Byte + 0 | Byte + 1 | - | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | - |-------------------------|-------------------------| - | 1 1 0 r4 r3 r2 r1 r0 | g3 g2 g1 g0 b3 b2 b1 b0 | + - QOI_DIFF_16 ------------------------------------- +| Byte[0] | Byte[1] | +| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | +|----------+--------------|------------ +-----------| +| 1 1 0 | red diff | green diff | blue diff | - r4...r0 forming the red channel difference between -16..15 - g3...g0 forming the green channel difference between -8..7 - b3...b0 forming the blue channel difference between -8..7 +3-bit tag b110 +5-bit red channel difference from the previous pixel between -16..15 +4-bit green channel difference from the previous pixel between -8..7 +4-bit blue channel difference from the previous pixel between -8..7 - The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while - "250 + 7" will result in 1. +The difference to the current channel values are using a wraparound operation, +so "10 - 13" will result in 253, while "250 + 7" will result in 1. - | QOI_DIFF_24 | - | Byte + 0 | Byte + 1 | Byte + 1 | - | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | - |-------------------------|-------------------------|-------------------------| - | 1 1 1 0 r4 r3 r2 r1 | r0 g4 g3 g2 g1 g0 b4 b3 | b2 b1 b0 a4 a3 a2 a1 a0 | + - QOI_DIFF_24 --------------------------------------------------------------- +| Byte[0] | Byte[1] | Byte[2] | +| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | +|-------------+----------------+--------------+----------------+--------------| +| 1 1 1 0 | red diff | green diff | blue diff | alpha diff | - r4...r0 forming the red channel difference between -16..15 - g4...g0 forming the green channel difference between -16..15 - b4...b0 forming the blue channel difference between -16..15 - a4...a0 forming the alpha channel difference between -16..15 +4-bit tag b1110 +5-bit red channel difference from the previous pixel between -16..15 +5-bit green channel difference from the previous pixel between -16..15 +5-bit blue channel difference from the previous pixel between -16..15 +5-bit alpha channel difference from the previous pixel between -16..15 - The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while - "250 + 7" will result in 1. +The difference to the current channel values are using a wraparound operation, +so "10 - 13" will result in 253, while "250 + 7" will result in 1. - | QOI_COLOR | - | Byte + 0 | - | 7 6 5 4 3 2 1 0 | - |-------------------------| - | 1 1 1 1 r g b a | + - QOI_COLOR ------------- +| Byte[0] | +| 7 6 5 4 3 2 1 0 | +|-------------+--+--+--+--| +| 1 1 1 1 |hr|hg|hb|ha| - for each set bit r, g, b and a another byte follows in the order written here. If such a byte follows, - it will replace the current color channel value with the value of this byte. +4-bit tag b1111 +1-bit red byte follows +1-bit green byte follows +1-bit blue byte follows +1-bit alpha byte follows + +For each set bit hr, hg, hb and ha another byte follows in this order. If such a +byte follows, it will replace the current color channel value with the value of +this byte. -The byte stream is padded with 4 zero bytes. Size the longest chunk we can -encounter is 5 bytes (QOI_COLOR with RGBA set), with this padding we just have -to check for an overrun once per decode loop iteration. +The byte stream is padded at the end with 4 zero bytes. Size the longest chunk +we can encounter is 5 bytes (QOI_COLOR with RGBA set), with this padding we just +have to check for an overrun once per decode loop iteration. */ @@ -385,7 +399,10 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { run++; } - if (run > 0 && (run == 0x2020 || px.v != px_prev.v || px_pos == px_end)) { + if ( + run > 0 && + (run == 0x2020 || px.v != px_prev.v || px_pos == px_end) + ) { if (run < 33) { run -= 1; bytes[p++] = QOI_RUN_8 | run; @@ -411,32 +428,38 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { int vg = px.rgba.g - px_prev.rgba.g; int vb = px.rgba.b - px_prev.rgba.b; int va = px.rgba.a - px_prev.rgba.a; - + if ( - vr > -17 && vr < 16 && vg > -17 && vg < 16 && - vb > -17 && vb < 16 && va > -17 && va < 16 + vr > -17 && vr < 16 && + vg > -17 && vg < 16 && + vb > -17 && vb < 16 && + va > -17 && va < 16 ) { if ( - va == 0 && vr > -3 && vr < 2 && - vg > -3 && vg < 2 && vb > -3 && vb < 2 + va == 0 && + vr > -3 && vr < 2 && + vg > -3 && vg < 2 && + vb > -3 && vb < 2 ) { bytes[p++] = QOI_DIFF_8 | ((vr + 2) << 4) | (vg + 2) << 2 | (vb + 2); } else if ( - va == 0 && vr > -17 && vr < 16 && - vg > -9 && vg < 8 && vb > -9 && vb < 8 + va == 0 && + vr > -17 && vr < 16 && + vg > -9 && vg < 8 && + vb > -9 && vb < 8 ) { - bytes[p++] = QOI_DIFF_16 | (vr + 16); - bytes[p++] = ((vg + 8) << 4) | (vb + 8); + bytes[p++] = QOI_DIFF_16 | (vr + 16); + bytes[p++] = (vg + 8) << 4 | (vb + 8); } else { - bytes[p++] = QOI_DIFF_24 | ((vr + 16) >> 1); - bytes[p++] = ((vr + 16) << 7) | ((vg + 16) << 2) | ((vb + 16) >> 3); - bytes[p++] = ((vb + 16) << 5) | (va + 16); + bytes[p++] = QOI_DIFF_24 | (vr + 16) >> 1; + bytes[p++] = (vr + 16) << 7 | (vg + 16) << 2 | (vb + 16) >> 3; + bytes[p++] = (vb + 16) << 5 | (va + 16); } } else { - bytes[p++] = QOI_COLOR | (vr?8:0)|(vg?4:0)|(vb?2:0)|(va?1:0); + bytes[p++] = QOI_COLOR | (vr ? 8 : 0) | (vg ? 4 : 0) | (vb ? 2 : 0) | (va ? 1 : 0); if (vr) { bytes[p++] = px.rgba.r; } if (vg) { bytes[p++] = px.rgba.g; } if (vb) { bytes[p++] = px.rgba.b; } @@ -521,8 +544,8 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { else if ((b1 & QOI_MASK_3) == QOI_DIFF_16) { int b2 = bytes[p++]; px.rgba.r += (b1 & 0x1f) - 16; - px.rgba.g += (b2 >> 4) - 8; - px.rgba.b += (b2 & 0x0f) - 8; + px.rgba.g += (b2 >> 4) - 8; + px.rgba.b += (b2 & 0x0f) - 8; } else if ((b1 & QOI_MASK_4) == QOI_DIFF_24) { int b2 = bytes[p++]; From 9dd60534e44005aaf1b4d62626757d95f12a939b Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sun, 28 Nov 2021 17:36:47 +0100 Subject: [PATCH 020/208] Use local var for channels to speed up encoding --- qoi.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qoi.h b/qoi.h index c4e13a6..d169d68 100644 --- a/qoi.h +++ b/qoi.h @@ -385,8 +385,10 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { int px_len = desc->width * desc->height * desc->channels; int px_end = px_len - desc->channels; - for (int px_pos = 0; px_pos < px_len; px_pos += desc->channels) { - if (desc->channels == 4) { + int channels = desc->channels; + + for (int px_pos = 0; px_pos < px_len; px_pos += channels) { + if (channels == 4) { px = *(qoi_rgba_t *)(pixels + px_pos); } else { From 94974653c1929b0e12f388a4255c9dd2563eaa08 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sun, 28 Nov 2021 17:59:51 +0100 Subject: [PATCH 021/208] Lock output file before writing; close #18 --- qoi.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/qoi.h b/qoi.h index d169d68..0aec728 100644 --- a/qoi.h +++ b/qoi.h @@ -584,20 +584,21 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { #include int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { - int size; - void *encoded = qoi_encode(data, desc, &size); - if (!encoded) { + FILE *f = fopen(filename, "wb"); + if (!f) { return 0; } - FILE *f = fopen(filename, "wb"); - if (!f) { - QOI_FREE(encoded); + int size; + void *encoded = qoi_encode(data, desc, &size); + if (!encoded) { + fclose(f); return 0; - } + } fwrite(encoded, 1, size, f); fclose(f); + QOI_FREE(encoded); return size; } From fda5167d76d05de67b821c787824c8d177fd22d8 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 29 Nov 2021 11:23:33 +0100 Subject: [PATCH 022/208] Add links to Tools and Implementations --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index a705d69..706c3fa 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,22 @@ converts between png <> qoi - [qoibench.c](https://github.com/phoboslab/qoi/blob/master/qoibench.c) a simple wrapper to benchmark stbi, libpng and qoi + +## Tools + +- https://github.com/floooh/qoiview + + +## Implementations of QOI + +- https://github.com/MasterQ32/zig-qoi (Zig) +- https://github.com/steven-joruk/qoi (Rust) +- https://github.com/ChevyRay/qoi_rs (Rust) +- https://github.com/xfmoulet/qoi (Go) +- https://github.com/panzi/jsqoi (TypeScript) +- https://github.com/pfusik/qoi-ci (Ć) + + ## Packages [AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. From e9069e11a43d779b418679c7a50b2ec14f652085 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 30 Nov 2021 17:45:48 +0100 Subject: [PATCH 023/208] Add notice about the format being not yet finalized --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 706c3fa..982e100 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,9 @@ More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compressi ⚠️ Please note that this library is not yet ready to deal with untrusted input. -⚠️ 2021.11.27 – the specification for QOI has changed to accomodate some -concerns with the format. If you are working on a QOI implementation, please -refer to -[#37 The QOI File Format Specification](https://github.com/phoboslab/qoi/issues/37) -for the details. +⚠️ 2021.11.30 – the file format is not yet finalized. We're still working to fix +some smaller issues. The final specification will be announced on 2021.12.20. +Thanks for your patience! https://github.com/phoboslab/qoi/issues/48 These specification changes are ~~not yet reflected in the code here~~ reflected in qoi.h now. From cbb62ea555221dd023085564207c634779d45314 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 30 Nov 2021 22:05:03 +0100 Subject: [PATCH 024/208] Remove QOI_RUN_16, add new QOI_GDIFF_16 op --- qoi.h | 165 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 89 insertions(+), 76 deletions(-) diff --git a/qoi.h b/qoi.h index 0aec728..a16262f 100644 --- a/qoi.h +++ b/qoi.h @@ -122,24 +122,14 @@ The possible chunks are: 6-bit index into the color index array: 0..63 - - QOI_RUN_8 ------------- + - QOI_RUN --------------- | Byte[0] | | 7 6 5 4 3 2 1 0 | -|----------+--------------| -| 0 1 0 | run | +|-------+-----------------| +| 0 1 | run | -3-bit tag b010 -5-bit run-length repeating the previous pixel: 1..32 - - - - QOI_RUN_16 -------------------------------------- -| Byte[0] | Byte[1] | -| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | -|----------+----------------------------------------| -| 0 1 1 | run | - -3-bit tag b011 -13-bit run-length repeating the previous pixel: 33..8224 +2-bit tag b01 +6-bit run-length repeating the previous pixel: 1..64 - QOI_DIFF_8 ------------ @@ -160,14 +150,33 @@ so "1 - 2" will result in 255, while "255 + 1" will result in 0. - QOI_DIFF_16 ------------------------------------- | Byte[0] | Byte[1] | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | -|----------+--------------|------------ +-----------| -| 1 1 0 | red diff | green diff | blue diff | +|-------------+-----------|------------ +-----------| +| 1 1 0 0 | red diff | green diff | blue diff | -3-bit tag b110 -5-bit red channel difference from the previous pixel between -16..15 +4-bit tag b1100 +4-bit red channel difference from the previous pixel between -8..7 4-bit green channel difference from the previous pixel between -8..7 4-bit blue channel difference from the previous pixel between -8..7 +The difference to the current channel values are using a wraparound operation, +so "5 - 8" will result in 253, while "250 + 7" will result in 1. + + + - QOI_GDIFF_16 ------------------------------------ +| Byte[0] | Byte[1] | +| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | +|-------------+--------+-------------------+--------| +| 1 1 0 1 | dr-dg | green diff | db-dg | + +4-bit tag b1101 +3-bit red channel difference minus green channel difference -4..3 +6-bit green channel difference from the previous pixel -32..31 +3-bit blue channel difference minus green channel difference -4..3 + +The green channel is used to indicate the general direction of change and gets +a few more bits. dr and db base their diffs off of the green channel diff. E.g. + dr = (last_px.r - cur_px.r) - (last_px.g - cur_px.g) + The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while "250 + 7" will result in 1. @@ -309,17 +318,16 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #define QOI_FREE(p) free(p) #endif -#define QOI_INDEX 0x00 // 00xxxxxx -#define QOI_RUN_8 0x40 // 010xxxxx -#define QOI_RUN_16 0x60 // 011xxxxx -#define QOI_DIFF_8 0x80 // 10xxxxxx -#define QOI_DIFF_16 0xc0 // 110xxxxx -#define QOI_DIFF_24 0xe0 // 1110xxxx -#define QOI_COLOR 0xf0 // 1111xxxx +#define QOI_INDEX 0x00 // 00xxxxxx +#define QOI_RUN 0x40 // 01xxxxxx +#define QOI_DIFF_8 0x80 // 10xxxxxx +#define QOI_DIFF_16 0xc0 // 1100xxxx +#define QOI_GDIFF_16 0xd0 // 1101xxxx +#define QOI_DIFF_24 0xe0 // 1110xxxx +#define QOI_COLOR 0xf0 // 1111xxxx -#define QOI_MASK_2 0xc0 // 11000000 -#define QOI_MASK_3 0xe0 // 11100000 -#define QOI_MASK_4 0xf0 // 11110000 +#define QOI_MASK_2 0xc0 // 11000000 +#define QOI_MASK_4 0xf0 // 11110000 #define QOI_COLOR_HASH(C) (C.rgba.r ^ C.rgba.g ^ C.rgba.b ^ C.rgba.a) #define QOI_MAGIC \ @@ -403,17 +411,9 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { if ( run > 0 && - (run == 0x2020 || px.v != px_prev.v || px_pos == px_end) + (run == 64 || px.v != px_prev.v || px_pos == px_end) ) { - if (run < 33) { - run -= 1; - bytes[p++] = QOI_RUN_8 | run; - } - else { - run -= 33; - bytes[p++] = QOI_RUN_16 | run >> 8; - bytes[p++] = run; - } + bytes[p++] = QOI_RUN | (run - 1); run = 0; } @@ -426,39 +426,49 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { else { index[index_pos] = px; - int vr = px.rgba.r - px_prev.rgba.r; - int vg = px.rgba.g - px_prev.rgba.g; - int vb = px.rgba.b - px_prev.rgba.b; - int va = px.rgba.a - px_prev.rgba.a; - + char vr = px.rgba.r - px_prev.rgba.r; + char vg = px.rgba.g - px_prev.rgba.g; + char vb = px.rgba.b - px_prev.rgba.b; + char va = px.rgba.a - px_prev.rgba.a; + + char vg_r = vr - vg; + char vg_b = vb - vg; + if ( + va == 0 && + vr > -3 && vr < 2 && + vg > -3 && vg < 2 && + vb > -3 && vb < 2 + ) { + bytes[p++] = QOI_DIFF_8 | ((vr + 2) << 4) | (vg + 2) << 2 | (vb + 2); + } + else if ( + va == 0 && + vr > -9 && vr < 8 && + vg > -9 && vg < 8 && + vb > -9 && vb < 8 + ) { + bytes[p++] = QOI_DIFF_16 | (vr + 8); + bytes[p++] = (vg + 8) << 4 | (vb + 8); + } + else if ( + va == 0 && + vg_r > -5 && vg_r < 4 && + vg > -33 && vg < 32 && + vg_b > -5 && vg_b < 4 + ) { + bytes[p++] = QOI_GDIFF_16 | (vg_r + 4) << 1 | (vg + 32) >> 5; + bytes[p++] = (vg + 32) << 3 | (vg_b + 4); + } + else if ( vr > -17 && vr < 16 && vg > -17 && vg < 16 && vb > -17 && vb < 16 && va > -17 && va < 16 ) { - if ( - va == 0 && - vr > -3 && vr < 2 && - vg > -3 && vg < 2 && - vb > -3 && vb < 2 - ) { - bytes[p++] = QOI_DIFF_8 | ((vr + 2) << 4) | (vg + 2) << 2 | (vb + 2); - } - else if ( - va == 0 && - vr > -17 && vr < 16 && - vg > -9 && vg < 8 && - vb > -9 && vb < 8 - ) { - bytes[p++] = QOI_DIFF_16 | (vr + 16); - bytes[p++] = (vg + 8) << 4 | (vb + 8); - } - else { - bytes[p++] = QOI_DIFF_24 | (vr + 16) >> 1; - bytes[p++] = (vr + 16) << 7 | (vg + 16) << 2 | (vb + 16) >> 3; - bytes[p++] = (vb + 16) << 5 | (va + 16); - } + bytes[p++] = QOI_DIFF_24 | (vr + 16) >> 1; + bytes[p++] = (vr + 16) << 7 | (vg + 16) << 2 | (vb + 16) >> 3; + bytes[p++] = (vb + 16) << 5 | (va + 16); } else { bytes[p++] = QOI_COLOR | (vr ? 8 : 0) | (vg ? 4 : 0) | (vb ? 2 : 0) | (va ? 1 : 0); @@ -531,23 +541,26 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { if ((b1 & QOI_MASK_2) == QOI_INDEX) { px = index[b1 ^ QOI_INDEX]; } - else if ((b1 & QOI_MASK_3) == QOI_RUN_8) { - run = (b1 & 0x1f); - } - else if ((b1 & QOI_MASK_3) == QOI_RUN_16) { - int b2 = bytes[p++]; - run = (((b1 & 0x1f) << 8) | (b2)) + 32; + else if ((b1 & QOI_MASK_2) == QOI_RUN) { + run = (b1 & 0x3f); } else if ((b1 & QOI_MASK_2) == QOI_DIFF_8) { px.rgba.r += ((b1 >> 4) & 0x03) - 2; px.rgba.g += ((b1 >> 2) & 0x03) - 2; px.rgba.b += ( b1 & 0x03) - 2; } - else if ((b1 & QOI_MASK_3) == QOI_DIFF_16) { + else if ((b1 & QOI_MASK_4) == QOI_DIFF_16) { int b2 = bytes[p++]; - px.rgba.r += (b1 & 0x1f) - 16; - px.rgba.g += (b2 >> 4) - 8; - px.rgba.b += (b2 & 0x0f) - 8; + px.rgba.r += (b1 & 0x0f) - 8; + px.rgba.g += (b2 >> 4) - 8; + px.rgba.b += (b2 & 0x0f) - 8; + } + else if ((b1 & QOI_MASK_4) == QOI_GDIFF_16) { + int b2 = bytes[p++]; + int vg = ((b1 & 0x01) << 5 | (b2 & 0xf8) >> 3) - 32; + px.rgba.r += vg - 4 + ((b1 & 0x0e) >> 1); + px.rgba.g += vg; + px.rgba.b += vg - 4 + (b2 & 0x07); } else if ((b1 & QOI_MASK_4) == QOI_DIFF_24) { int b2 = bytes[p++]; From 2392a3423ca958aefd9d4b1fa7923ba4df9c760f Mon Sep 17 00:00:00 2001 From: Zakarum Date: Wed, 1 Dec 2021 17:48:53 +0300 Subject: [PATCH 025/208] Mention rapid-qoi implementation written in Rust --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 982e100..c0f6a2e 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ a simple wrapper to benchmark stbi, libpng and qoi - https://github.com/MasterQ32/zig-qoi (Zig) - https://github.com/steven-joruk/qoi (Rust) - https://github.com/ChevyRay/qoi_rs (Rust) +- https://github.com/zakarumych/rapid-qoi (Rust) - https://github.com/xfmoulet/qoi (Go) - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/pfusik/qoi-ci (Ć) From 03606a0be7bff7a734c805aea638d48ed0e5e72f Mon Sep 17 00:00:00 2001 From: Nicolas Sauzede Date: Fri, 3 Dec 2021 01:24:52 +0100 Subject: [PATCH 026/208] Fix typo in DIFF8 documentation --- qoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index 0aec728..9fd62a5 100644 --- a/qoi.h +++ b/qoi.h @@ -146,7 +146,7 @@ The possible chunks are: | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----+-----+-----| -| 1 0 | dr | db | bg | +| 1 0 | dr | dg | db | 2-bit tag b10 2-bit red channel difference from the previous pixel between -2..1 From bd7d5c07bbb2cb2b7ef423b2c3e5465af3f473f9 Mon Sep 17 00:00:00 2001 From: kodonnell Date: Sun, 5 Dec 2021 22:18:49 +1300 Subject: [PATCH 027/208] Add link in readme to kodonnell/qoi (Python) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 982e100..5bf36c3 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ a simple wrapper to benchmark stbi, libpng and qoi - https://github.com/xfmoulet/qoi (Go) - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/pfusik/qoi-ci (Ć) +- https://github.com/kodonnell/qoi (Python) ## Packages From ce32dfed6e80d6ffbcd5bf4b9993bfdd68b1bb01 Mon Sep 17 00:00:00 2001 From: Eugene Date: Sun, 5 Dec 2021 14:23:24 -0600 Subject: [PATCH 028/208] Mention QOI implementation written in C# --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 39f8176..159018c 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ a simple wrapper to benchmark stbi, libpng and qoi - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/pfusik/qoi-ci (Ć) - https://github.com/kodonnell/qoi (Python) +- https://github.com/NUlliiON/QoiSharp (C#) ## Packages From 259a3a36a0cc577c0f0d1283d77f061c1219aaff Mon Sep 17 00:00:00 2001 From: Riccardo Binetti Date: Mon, 6 Dec 2021 02:46:10 +0100 Subject: [PATCH 029/208] Add Elixir implementation (Qoix) to the implementations section --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 39f8176..643073e 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ a simple wrapper to benchmark stbi, libpng and qoi - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/pfusik/qoi-ci (Ć) - https://github.com/kodonnell/qoi (Python) +- https://github.com/rbino/qoix (Elixir) ## Packages From f45f47c9f0ecc53df2ece721e8df6164bf87e005 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 6 Dec 2021 13:35:54 +0100 Subject: [PATCH 030/208] Recursive traversal; compression ratio; grand total; options to disable some features --- qoibench.c | 351 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 231 insertions(+), 120 deletions(-) diff --git a/qoibench.c b/qoibench.c index f48e236..b60514e 100644 --- a/qoibench.c +++ b/qoibench.c @@ -195,8 +195,12 @@ void png_decode_callback(png_structp png, png_bytep data, png_size_t length) { read_data->pos += length; } +void png_warning_callback(png_structp png_ptr, png_const_charp warning_msg) { + // Ingore warnings about sRGB profiles and such. +} + void *libpng_decode(void *data, int size, int *out_w, int *out_h) { - png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, png_warning_callback); if (!png) { ERROR("png_create_read_struct"); } @@ -308,6 +312,16 @@ void *fload(const char *path, int *out_size) { // ----------------------------------------------------------------------------- // benchmark runner + +int opt_runs = 1; +int opt_nopng = 0; +int opt_nowarmup = 0; +int opt_noverify = 0; +int opt_nodecode = 0; +int opt_noencode = 0; +int opt_norecurse = 0; + + typedef struct { uint64_t size; uint64_t encode_time; @@ -315,6 +329,8 @@ typedef struct { } benchmark_lib_result_t; typedef struct { + int count; + uint64_t raw_size; uint64_t px; int w; int h; @@ -324,12 +340,59 @@ typedef struct { } benchmark_result_t; +void benchmark_print_result(benchmark_result_t res) { + res.px /= res.count; + res.raw_size /= res.count; + res.libpng.encode_time /= res.count; + res.libpng.decode_time /= res.count; + res.libpng.size /= res.count; + res.stbi.encode_time /= res.count; + res.stbi.decode_time /= res.count; + res.stbi.size /= res.count; + res.qoi.encode_time /= res.count; + res.qoi.decode_time /= res.count; + res.qoi.size /= res.count; + + double px = res.px; + printf(" decode ms encode ms decode mpps encode mpps size kb rate\n"); + if (!opt_nopng) { + printf( + "libpng: %8.1f %8.1f %8.2f %8.2f %8d %4.1f%%\n", + (double)res.libpng.decode_time/1000000.0, + (double)res.libpng.encode_time/1000000.0, + (res.libpng.decode_time > 0 ? px / ((double)res.libpng.decode_time/1000.0) : 0), + (res.libpng.encode_time > 0 ? px / ((double)res.libpng.encode_time/1000.0) : 0), + res.libpng.size/1024, + ((double)res.libpng.size/(double)res.raw_size) * 100.0 + ); + printf( + "stbi: %8.1f %8.1f %8.2f %8.2f %8d %4.1f%%\n", + (double)res.stbi.decode_time/1000000.0, + (double)res.stbi.encode_time/1000000.0, + (res.stbi.decode_time > 0 ? px / ((double)res.stbi.decode_time/1000.0) : 0), + (res.stbi.encode_time > 0 ? px / ((double)res.stbi.encode_time/1000.0) : 0), + res.stbi.size/1024, + ((double)res.stbi.size/(double)res.raw_size) * 100.0 + ); + } + printf( + "qoi: %8.1f %8.1f %8.2f %8.2f %8d %4.1f%%\n", + (double)res.qoi.decode_time/1000000.0, + (double)res.qoi.encode_time/1000000.0, + (res.qoi.decode_time > 0 ? px / ((double)res.qoi.decode_time/1000.0) : 0), + (res.qoi.encode_time > 0 ? px / ((double)res.qoi.encode_time/1000.0) : 0), + res.qoi.size/1024, + ((double)res.qoi.size/(double)res.raw_size) * 100.0 + ); + printf("\n"); +} + // Run __VA_ARGS__ a number of times and meassure the time taken. The first // run is ignored. -#define BENCHMARK_FN(RUNS, AVG_TIME, ...) \ +#define BENCHMARK_FN(NOWARMUP, RUNS, AVG_TIME, ...) \ do { \ uint64_t time = 0; \ - for (int i = 0; i <= RUNS; i++) { \ + for (int i = NOWARMUP; i <= RUNS; i++) { \ uint64_t time_start = ns(); \ __VA_ARGS__ \ uint64_t time_end = ns(); \ @@ -341,7 +404,7 @@ typedef struct { } while (0) -benchmark_result_t benchmark_image(const char *path, int runs) { +benchmark_result_t benchmark_image(const char *path) { int encoded_png_size; int encoded_qoi_size; int w; @@ -358,10 +421,22 @@ benchmark_result_t benchmark_image(const char *path, int runs) { }, &encoded_qoi_size); if (!pixels || !encoded_qoi || !encoded_png) { - ERROR("Error decoding %s\n", path); + ERROR("Error decoding %s", path); + } + + // Verify QOI Output + if (!opt_noverify) { + qoi_desc dc; + void *pixels_qoi = qoi_decode(encoded_qoi, encoded_qoi_size, &dc, 4); + if (memcmp(pixels, pixels_qoi, w * h * 4) != 0) { + ERROR("QOI roundtrip pixel missmatch for %s", path); + } + free(pixels_qoi); } benchmark_result_t res = {0}; + res.count = 1; + res.raw_size = w * h * 4; res.px = w * h; res.w = w; res.h = h; @@ -369,51 +444,59 @@ benchmark_result_t benchmark_image(const char *path, int runs) { // Decoding - BENCHMARK_FN(runs, res.libpng.decode_time, { - int dec_w, dec_h; - void *dec_p = libpng_decode(encoded_png, encoded_png_size, &dec_w, &dec_h); - free(dec_p); - }); + if (!opt_nodecode) { + if (!opt_nopng) { + BENCHMARK_FN(opt_nowarmup, opt_runs, res.libpng.decode_time, { + int dec_w, dec_h; + void *dec_p = libpng_decode(encoded_png, encoded_png_size, &dec_w, &dec_h); + free(dec_p); + }); - BENCHMARK_FN(runs, res.stbi.decode_time, { - int dec_w, dec_h, dec_channels; - void *dec_p = stbi_load_from_memory(encoded_png, encoded_png_size, &dec_w, &dec_h, &dec_channels, 4); - free(dec_p); - }); + BENCHMARK_FN(opt_nowarmup, opt_runs, res.stbi.decode_time, { + int dec_w, dec_h, dec_channels; + void *dec_p = stbi_load_from_memory(encoded_png, encoded_png_size, &dec_w, &dec_h, &dec_channels, 4); + free(dec_p); + }); + } - BENCHMARK_FN(runs, res.qoi.decode_time, { - qoi_desc desc; - void *dec_p = qoi_decode(encoded_qoi, encoded_qoi_size, &desc, 4); - free(dec_p); - }); + BENCHMARK_FN(opt_nowarmup, opt_runs, res.qoi.decode_time, { + qoi_desc desc; + void *dec_p = qoi_decode(encoded_qoi, encoded_qoi_size, &desc, 4); + free(dec_p); + }); + } // Encoding - BENCHMARK_FN(runs, res.libpng.encode_time, { - int enc_size; - void *enc_p = libpng_encode(pixels, w, h, &enc_size); - res.libpng.size = enc_size; - free(enc_p); - }); + if (!opt_noencode) { + if (!opt_nopng) { + BENCHMARK_FN(opt_nowarmup, opt_runs, res.libpng.encode_time, { + int enc_size; + void *enc_p = libpng_encode(pixels, w, h, &enc_size); + res.libpng.size = enc_size; + free(enc_p); + }); - BENCHMARK_FN(runs, res.stbi.encode_time, { - int enc_size = 0; - stbi_write_png_to_func(stbi_write_callback, &enc_size, w, h, 4, pixels, 0); - res.stbi.size = enc_size; - }); + BENCHMARK_FN(opt_nowarmup, opt_runs, res.stbi.encode_time, { + int enc_size = 0; + stbi_write_png_to_func(stbi_write_callback, &enc_size, w, h, 4, pixels, 0); + res.stbi.size = enc_size; + }); + } - BENCHMARK_FN(runs, res.qoi.encode_time, { - int enc_size; - void *enc_p = qoi_encode(pixels, &(qoi_desc){ - .width = w, - .height = h, - .channels = 4, - .colorspace = QOI_SRGB - }, &enc_size); - res.qoi.size = enc_size; - free(enc_p); - }); + BENCHMARK_FN(opt_nowarmup, opt_runs, res.qoi.encode_time, { + int enc_size; + void *enc_p = qoi_encode(pixels, &(qoi_desc){ + .width = w, + .height = h, + .channels = 4, + .colorspace = QOI_SRGB + }, &enc_size); + res.qoi.size = enc_size; + free(enc_p); + }); + } free(pixels); free(encoded_png); @@ -422,102 +505,130 @@ benchmark_result_t benchmark_image(const char *path, int runs) { return res; } -void benchmark_print_result(const char *head, benchmark_result_t res) { - double px = res.px; - printf("## %s size: %dx%d\n", head, res.w, res.h); - printf(" decode ms encode ms decode mpps encode mpps size kb\n"); - printf( - "libpng: %8.1f %8.1f %8.2f %8.2f %8d\n", - (double)res.libpng.decode_time/1000000.0, - (double)res.libpng.encode_time/1000000.0, - (res.libpng.decode_time > 0 ? px / ((double)res.libpng.decode_time/1000.0) : 0), - (res.libpng.encode_time > 0 ? px / ((double)res.libpng.encode_time/1000.0) : 0), - res.libpng.size/1024 - ); - printf( - "stbi: %8.1f %8.1f %8.2f %8.2f %8d\n", - (double)res.stbi.decode_time/1000000.0, - (double)res.stbi.encode_time/1000000.0, - (res.stbi.decode_time > 0 ? px / ((double)res.stbi.decode_time/1000.0) : 0), - (res.stbi.encode_time > 0 ? px / ((double)res.stbi.encode_time/1000.0) : 0), - res.stbi.size/1024 - ); - printf( - "qoi: %8.1f %8.1f %8.2f %8.2f %8d\n", - (double)res.qoi.decode_time/1000000.0, - (double)res.qoi.encode_time/1000000.0, - (res.qoi.decode_time > 0 ? px / ((double)res.qoi.decode_time/1000.0) : 0), - (res.qoi.encode_time > 0 ? px / ((double)res.qoi.encode_time/1000.0) : 0), - res.qoi.size/1024 - ); - printf("\n"); -} +void benchmark_directory(const char *path, benchmark_result_t *grand_total) { + DIR *dir = opendir(path); + if (!dir) { + ERROR("Couldn't open directory %s", path); + } -int main(int argc, char **argv) { - if (argc < 3) { - printf("Usage: qoibench \n"); - printf("Example: qoibench 10 images/textures/\n"); - exit(1); + struct dirent *file; + + if (!opt_norecurse) { + for (int i = 0; (file = readdir(dir)) != NULL; i++) { + if ( + file->d_type & DT_DIR && + strcmp(file->d_name, ".") != 0 && + strcmp(file->d_name, "..") != 0 + ) { + char subpath[1024]; + snprintf(subpath, 1024, "%s/%s", path, file->d_name); + benchmark_directory(subpath, grand_total); + } + } + rewinddir(dir); } float total_percentage = 0; int total_size = 0; - benchmark_result_t totals = {0}; - - int runs = atoi(argv[1]); - DIR *dir = opendir(argv[2]); - if (runs <=0) { - runs = 1; - } - - if (!dir) { - ERROR("Couldn't open directory %s", argv[2]); - } - - printf("## Benchmarking %s/*.png -- %d runs\n\n", argv[2], runs); - struct dirent *file; - int count = 0; - for (int i = 0; dir && (file = readdir(dir)) != NULL; i++) { + benchmark_result_t dir_total = {0}; + + int has_shown_heaad = 0; + for (int i = 0; (file = readdir(dir)) != NULL; i++) { if (strcmp(file->d_name + strlen(file->d_name) - 4, ".png") != 0) { continue; } - count++; - char *file_path = malloc(strlen(file->d_name) + strlen(argv[2])+8); - sprintf(file_path, "%s/%s", argv[2], file->d_name); + if (!has_shown_heaad) { + has_shown_heaad = 1; + printf("## Benchmarking %s/*.png -- %d runs\n\n", path, opt_runs); + } + + char *file_path = malloc(strlen(file->d_name) + strlen(path)+8); + sprintf(file_path, "%s/%s", path, file->d_name); - benchmark_result_t res = benchmark_image(file_path, runs); - benchmark_print_result(file_path, res); + benchmark_result_t res = benchmark_image(file_path); + + printf("## %s size: %dx%d\n", file_path, res.w, res.h); + benchmark_print_result(res); free(file_path); - - totals.px += res.px; - totals.libpng.encode_time += res.libpng.encode_time; - totals.libpng.decode_time += res.libpng.decode_time; - totals.libpng.size += res.libpng.size; - totals.stbi.encode_time += res.stbi.encode_time; - totals.stbi.decode_time += res.stbi.decode_time; - totals.stbi.size += res.stbi.size; - totals.qoi.encode_time += res.qoi.encode_time; - totals.qoi.decode_time += res.qoi.decode_time; - totals.qoi.size += res.qoi.size; + dir_total.count++; + dir_total.raw_size += res.raw_size; + dir_total.px += res.px; + dir_total.libpng.encode_time += res.libpng.encode_time; + dir_total.libpng.decode_time += res.libpng.decode_time; + dir_total.libpng.size += res.libpng.size; + dir_total.stbi.encode_time += res.stbi.encode_time; + dir_total.stbi.decode_time += res.stbi.decode_time; + dir_total.stbi.size += res.stbi.size; + dir_total.qoi.encode_time += res.qoi.encode_time; + dir_total.qoi.decode_time += res.qoi.decode_time; + dir_total.qoi.size += res.qoi.size; + + grand_total->count++; + grand_total->raw_size += res.raw_size; + grand_total->px += res.px; + grand_total->libpng.encode_time += res.libpng.encode_time; + grand_total->libpng.decode_time += res.libpng.decode_time; + grand_total->libpng.size += res.libpng.size; + grand_total->stbi.encode_time += res.stbi.encode_time; + grand_total->stbi.decode_time += res.stbi.decode_time; + grand_total->stbi.size += res.stbi.size; + grand_total->qoi.encode_time += res.qoi.encode_time; + grand_total->qoi.decode_time += res.qoi.decode_time; + grand_total->qoi.size += res.qoi.size; } closedir(dir); - totals.px /= count; - totals.libpng.encode_time /= count; - totals.libpng.decode_time /= count; - totals.libpng.size /= count; - totals.stbi.encode_time /= count; - totals.stbi.decode_time /= count; - totals.stbi.size /= count; - totals.qoi.encode_time /= count; - totals.qoi.decode_time /= count; - totals.qoi.size /= count; + if (dir_total.count > 0) { + printf("## Total for %s\n", path); + benchmark_print_result(dir_total); + } +} - benchmark_print_result("Totals (AVG)", totals); +int main(int argc, char **argv) { + if (argc < 3) { + printf("Usage: qoibench [options]\n"); + printf("Options:\n"); + printf(" --nowarmup ... don't perform a warmup run\n"); + printf(" --nopng ...... don't run png encode/decode\n"); + printf(" --noverify ... don't verify qoi roundtrip\n"); + printf(" --noencode ... don't run encoders\n"); + printf(" --nodecode ... don't run decoders\n"); + printf(" --norecurse .. don't descend into directories\n"); + printf("Examples\n"); + printf(" qoibench 10 images/textures/\n"); + printf(" qoibench 1 images/textures/ --nopng --nowarmup\n"); + exit(1); + } + + for (int i = 3; i < argc; i++) { + if (strcmp(argv[i], "--nowarmup") == 0) { opt_nowarmup = 1; } + else if (strcmp(argv[i], "--nopng") == 0) { opt_nopng = 1; } + else if (strcmp(argv[i], "--noverify") == 0) { opt_noverify = 1; } + else if (strcmp(argv[i], "--noencode") == 0) { opt_noencode = 1; } + else if (strcmp(argv[i], "--nodecode") == 0) { opt_nodecode = 1; } + else if (strcmp(argv[i], "--norecurse") == 0) { opt_norecurse = 1; } + else { ERROR("Unknown option %s", argv[i]); } + } + + opt_runs = atoi(argv[1]); + if (opt_runs <=0) { + ERROR("Invalid number of runs %d", opt_runs); + } + + benchmark_result_t grand_total = {0}; + benchmark_directory(argv[2], &grand_total); + + if (grand_total.count > 0) { + printf("# Grand total for %s\n", argv[2]); + benchmark_print_result(grand_total); + } + else { + printf("No images found in %s\n", argv[2]); + } return 0; } From 66d12eb078b16b3c41fcaf2257e829ca3f499ae1 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 6 Dec 2021 19:55:39 +0100 Subject: [PATCH 031/208] Add option to only print directory totals --- qoibench.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/qoibench.c b/qoibench.c index b60514e..ba401a4 100644 --- a/qoibench.c +++ b/qoibench.c @@ -320,6 +320,7 @@ int opt_noverify = 0; int opt_nodecode = 0; int opt_noencode = 0; int opt_norecurse = 0; +int opt_onlytotals = 0; typedef struct { @@ -411,6 +412,7 @@ benchmark_result_t benchmark_image(const char *path) { int h; // Load the encoded PNG, encoded QOI and raw pixels into memory + void *pixels = (void *)stbi_load(path, &w, &h, NULL, 4); void *encoded_png = fload(path, &encoded_png_size); void *encoded_qoi = qoi_encode(pixels, &(qoi_desc){ @@ -425,6 +427,7 @@ benchmark_result_t benchmark_image(const char *path) { } // Verify QOI Output + if (!opt_noverify) { qoi_desc dc; void *pixels_qoi = qoi_decode(encoded_qoi, encoded_qoi_size, &dc, 4); @@ -434,6 +437,8 @@ benchmark_result_t benchmark_image(const char *path) { free(pixels_qoi); } + + benchmark_result_t res = {0}; res.count = 1; res.raw_size = w * h * 4; @@ -549,8 +554,10 @@ void benchmark_directory(const char *path, benchmark_result_t *grand_total) { benchmark_result_t res = benchmark_image(file_path); - printf("## %s size: %dx%d\n", file_path, res.w, res.h); - benchmark_print_result(res); + if (!opt_onlytotals) { + printf("## %s size: %dx%d\n", file_path, res.w, res.h); + benchmark_print_result(res); + } free(file_path); @@ -598,6 +605,7 @@ int main(int argc, char **argv) { printf(" --noencode ... don't run encoders\n"); printf(" --nodecode ... don't run decoders\n"); printf(" --norecurse .. don't descend into directories\n"); + printf(" --onlytotals . don't print individual image results\n"); printf("Examples\n"); printf(" qoibench 10 images/textures/\n"); printf(" qoibench 1 images/textures/ --nopng --nowarmup\n"); @@ -611,6 +619,7 @@ int main(int argc, char **argv) { else if (strcmp(argv[i], "--noencode") == 0) { opt_noencode = 1; } else if (strcmp(argv[i], "--nodecode") == 0) { opt_nodecode = 1; } else if (strcmp(argv[i], "--norecurse") == 0) { opt_norecurse = 1; } + else if (strcmp(argv[i], "--onlytotals") == 0) { opt_onlytotals = 1; } else { ERROR("Unknown option %s", argv[i]); } } From 28954f7a9a82e4a23ec6926c26617c9657839971 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 6 Dec 2021 21:23:13 +0100 Subject: [PATCH 032/208] Remove QOI_DIFF_16 and QOI_DIFF_24; better tagging of remaining ops; better hash function --- qoi.h | 282 +++++++++++++++++++++++++--------------------------------- 1 file changed, 121 insertions(+), 161 deletions(-) diff --git a/qoi.h b/qoi.h index a16262f..8bd8126 100644 --- a/qoi.h +++ b/qoi.h @@ -102,17 +102,23 @@ pixel value. Pixels are either encoded as A running array[64] of previously seen pixel values is maintained by the encoder and decoder. Each pixel that is seen by the encoder and decoder is put into this -array at the position (r^g^b^a) % 64. In the encoder, if the pixel value at this -index matches the current pixel, this index position is written to the stream. +array at the position (r * 3 + g * 5 + b * 7) % 64. In the encoder, if the pixel +value at this index matches the current pixel, this index position is written to +the stream. -Each chunk starts with a 2, 3 or 4 bit tag, followed by a number of data bits. +Each chunk starts with a 8 or 2 bit tag, followed by a number of data bits. The bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All values encoded in these data bits have the most significant bit (MSB) on the left. +The 8-bit tags have precedence over the 2-bit tags. A decoder must check the +8-bit tags first. + + The possible chunks are: - - QOI_INDEX ------------- + + - QOI_OP_INDEX ---------- | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----------------| @@ -122,23 +128,13 @@ The possible chunks are: 6-bit index into the color index array: 0..63 - - QOI_RUN --------------- -| Byte[0] | -| 7 6 5 4 3 2 1 0 | -|-------+-----------------| -| 0 1 | run | - -2-bit tag b01 -6-bit run-length repeating the previous pixel: 1..64 - - - - QOI_DIFF_8 ------------ + - QOI_OP_DIFF ----------- | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----+-----+-----| -| 1 0 | dr | db | bg | +| 0 1 | dr | dg | db | -2-bit tag b10 +2-bit tag b01 2-bit red channel difference from the previous pixel between -2..1 2-bit green channel difference from the previous pixel between -2..1 2-bit blue channel difference from the previous pixel between -2..1 @@ -147,31 +143,16 @@ The difference to the current channel values are using a wraparound operation, so "1 - 2" will result in 255, while "255 + 1" will result in 0. - - QOI_DIFF_16 ------------------------------------- + - QOI_OP_LUMA ------------------------------------- | Byte[0] | Byte[1] | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | -|-------------+-----------|------------ +-----------| -| 1 1 0 0 | red diff | green diff | blue diff | +|-------+-----------------+-------------+-----------| +| 1 0 | green diff | dr - dg | db - dg | -4-bit tag b1100 -4-bit red channel difference from the previous pixel between -8..7 -4-bit green channel difference from the previous pixel between -8..7 -4-bit blue channel difference from the previous pixel between -8..7 - -The difference to the current channel values are using a wraparound operation, -so "5 - 8" will result in 253, while "250 + 7" will result in 1. - - - - QOI_GDIFF_16 ------------------------------------ -| Byte[0] | Byte[1] | -| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | -|-------------+--------+-------------------+--------| -| 1 1 0 1 | dr-dg | green diff | db-dg | - -4-bit tag b1101 -3-bit red channel difference minus green channel difference -4..3 +2-bit tag b10 6-bit green channel difference from the previous pixel -32..31 -3-bit blue channel difference minus green channel difference -4..3 +4-bit red channel difference minus green channel difference -8..7 +4-bit blue channel difference minus green channel difference -8..7 The green channel is used to indicate the general direction of change and gets a few more bits. dr and db base their diffs off of the green channel diff. E.g. @@ -181,42 +162,46 @@ The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while "250 + 7" will result in 1. - - QOI_DIFF_24 --------------------------------------------------------------- -| Byte[0] | Byte[1] | Byte[2] | -| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | -|-------------+----------------+--------------+----------------+--------------| -| 1 1 1 0 | red diff | green diff | blue diff | alpha diff | - -4-bit tag b1110 -5-bit red channel difference from the previous pixel between -16..15 -5-bit green channel difference from the previous pixel between -16..15 -5-bit blue channel difference from the previous pixel between -16..15 -5-bit alpha channel difference from the previous pixel between -16..15 - -The difference to the current channel values are using a wraparound operation, -so "10 - 13" will result in 253, while "250 + 7" will result in 1. - - - - QOI_COLOR ------------- + - QOI_OP_RUN ------------ | Byte[0] | | 7 6 5 4 3 2 1 0 | -|-------------+--+--+--+--| -| 1 1 1 1 |hr|hg|hb|ha| +|-------+-----------------| +| 1 1 | run | -4-bit tag b1111 -1-bit red byte follows -1-bit green byte follows -1-bit blue byte follows -1-bit alpha byte follows - -For each set bit hr, hg, hb and ha another byte follows in this order. If such a -byte follows, it will replace the current color channel value with the value of -this byte. +2-bit tag b01 +6-bit run-length repeating the previous pixel: 1..62 +Note that the run-lengths 63 and 64 (b111110 and b111111) are illegal as they +are occupied by the QOI_OP_RGB and QOI_OP_RGBA tags. -The byte stream is padded at the end with 4 zero bytes. Size the longest chunk -we can encounter is 5 bytes (QOI_COLOR with RGBA set), with this padding we just -have to check for an overrun once per decode loop iteration. + - QOI_OP_RGB ------------------------------------------ +| Byte[0] | Byte[1] | Byte[2] | Byte[3] | +| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | +|-------------------------+---------+---------+---------| +| 1 1 1 1 1 1 1 0 | red | green | blue | + +8-bit tag b11111110 +8-bit red channel value +8-bit green channel value +8-bit blue channel value + + +- QOI_OP_RGBA ---------------------------------------------------- +| Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] | +| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | +|-------------------------+---------+---------+---------+---------| +| 1 1 1 1 1 1 1 1 | red | green | blue | alpha | + +8-bit tag b11111111 +8-bit red channel value +8-bit green channel value +8-bit blue channel value +8-bit alpha channel value + + +The byte stream is padded at the end with 4 zero bytes. Size the longest legal +chunk is 5 bytes (QOI_OP_RGBA), with this padding it is possible to check for an +overrun only once per decode loop iteration. */ @@ -318,18 +303,16 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #define QOI_FREE(p) free(p) #endif -#define QOI_INDEX 0x00 // 00xxxxxx -#define QOI_RUN 0x40 // 01xxxxxx -#define QOI_DIFF_8 0x80 // 10xxxxxx -#define QOI_DIFF_16 0xc0 // 1100xxxx -#define QOI_GDIFF_16 0xd0 // 1101xxxx -#define QOI_DIFF_24 0xe0 // 1110xxxx -#define QOI_COLOR 0xf0 // 1111xxxx +#define QOI_OP_INDEX 0x00 // 00xxxxxx +#define QOI_OP_DIFF 0x40 // 01xxxxxx +#define QOI_OP_LUMA 0x80 // 10xxxxxx +#define QOI_OP_RUN 0xc0 // 11xxxxxx +#define QOI_OP_RGB 0xfe // 11111110 +#define QOI_OP_RGBA 0xff // 11111111 #define QOI_MASK_2 0xc0 // 11000000 -#define QOI_MASK_4 0xf0 // 11110000 -#define QOI_COLOR_HASH(C) (C.rgba.r ^ C.rgba.g ^ C.rgba.b ^ C.rgba.a) +#define QOI_COLOR_HASH(C) (C.rgba.r * 3 + C.rgba.g * 5 + C.rgba.b * 7) #define QOI_MAGIC \ (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ ((unsigned int)'i') << 8 | ((unsigned int)'f')) @@ -400,9 +383,9 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { px = *(qoi_rgba_t *)(pixels + px_pos); } else { - px.rgba.r = pixels[px_pos]; - px.rgba.g = pixels[px_pos+1]; - px.rgba.b = pixels[px_pos+2]; + px.rgba.r = pixels[px_pos + 0]; + px.rgba.g = pixels[px_pos + 1]; + px.rgba.b = pixels[px_pos + 2]; } if (px.v == px_prev.v) { @@ -411,9 +394,9 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { if ( run > 0 && - (run == 64 || px.v != px_prev.v || px_pos == px_end) + (run == 62 || px.v != px_prev.v || px_pos == px_end) ) { - bytes[p++] = QOI_RUN | (run - 1); + bytes[p++] = QOI_OP_RUN | (run - 1); run = 0; } @@ -421,61 +404,47 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { int index_pos = QOI_COLOR_HASH(px) % 64; if (index[index_pos].v == px.v) { - bytes[p++] = QOI_INDEX | index_pos; + bytes[p++] = QOI_OP_INDEX | index_pos; } else { index[index_pos] = px; - char vr = px.rgba.r - px_prev.rgba.r; - char vg = px.rgba.g - px_prev.rgba.g; - char vb = px.rgba.b - px_prev.rgba.b; - char va = px.rgba.a - px_prev.rgba.a; + if (px.rgba.a == px_prev.rgba.a) { + char vr = px.rgba.r - px_prev.rgba.r; + char vg = px.rgba.g - px_prev.rgba.g; + char vb = px.rgba.b - px_prev.rgba.b; - char vg_r = vr - vg; - char vg_b = vb - vg; + char vg_r = vr - vg; + char vg_b = vb - vg; - if ( - va == 0 && - vr > -3 && vr < 2 && - vg > -3 && vg < 2 && - vb > -3 && vb < 2 - ) { - bytes[p++] = QOI_DIFF_8 | ((vr + 2) << 4) | (vg + 2) << 2 | (vb + 2); - } - else if ( - va == 0 && - vr > -9 && vr < 8 && - vg > -9 && vg < 8 && - vb > -9 && vb < 8 - ) { - bytes[p++] = QOI_DIFF_16 | (vr + 8); - bytes[p++] = (vg + 8) << 4 | (vb + 8); - } - else if ( - va == 0 && - vg_r > -5 && vg_r < 4 && - vg > -33 && vg < 32 && - vg_b > -5 && vg_b < 4 - ) { - bytes[p++] = QOI_GDIFF_16 | (vg_r + 4) << 1 | (vg + 32) >> 5; - bytes[p++] = (vg + 32) << 3 | (vg_b + 4); - } - else if ( - vr > -17 && vr < 16 && - vg > -17 && vg < 16 && - vb > -17 && vb < 16 && - va > -17 && va < 16 - ) { - bytes[p++] = QOI_DIFF_24 | (vr + 16) >> 1; - bytes[p++] = (vr + 16) << 7 | (vg + 16) << 2 | (vb + 16) >> 3; - bytes[p++] = (vb + 16) << 5 | (va + 16); + if ( + vr > -3 && vr < 2 && + vg > -3 && vg < 2 && + vb > -3 && vb < 2 + ) { + bytes[p++] = QOI_OP_DIFF | ((vr + 2) << 4) | (vg + 2) << 2 | (vb + 2); + } + else if ( + vg_r > -9 && vg_r < 8 && + vg > -33 && vg < 32 && + vg_b > -9 && vg_b < 8 + ) { + bytes[p++] = QOI_OP_LUMA | (vg + 32); + bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8); + } + else { + bytes[p++] = QOI_OP_RGB; + bytes[p++] = px.rgba.r; + bytes[p++] = px.rgba.g; + bytes[p++] = px.rgba.b; + } } else { - bytes[p++] = QOI_COLOR | (vr ? 8 : 0) | (vg ? 4 : 0) | (vb ? 2 : 0) | (va ? 1 : 0); - if (vr) { bytes[p++] = px.rgba.r; } - if (vg) { bytes[p++] = px.rgba.g; } - if (vb) { bytes[p++] = px.rgba.b; } - if (va) { bytes[p++] = px.rgba.a; } + bytes[p++] = QOI_OP_RGBA; + bytes[p++] = px.rgba.r; + bytes[p++] = px.rgba.g; + bytes[p++] = px.rgba.b; + bytes[p++] = px.rgba.a; } } } @@ -538,43 +507,34 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { else if (p < chunks_len) { int b1 = bytes[p++]; - if ((b1 & QOI_MASK_2) == QOI_INDEX) { - px = index[b1 ^ QOI_INDEX]; + if (b1 == QOI_OP_RGB) { + px.rgba.r = bytes[p++]; + px.rgba.g = bytes[p++]; + px.rgba.b = bytes[p++]; } - else if ((b1 & QOI_MASK_2) == QOI_RUN) { - run = (b1 & 0x3f); + else if (b1 == QOI_OP_RGBA) { + px.rgba.r = bytes[p++]; + px.rgba.g = bytes[p++]; + px.rgba.b = bytes[p++]; + px.rgba.a = bytes[p++]; } - else if ((b1 & QOI_MASK_2) == QOI_DIFF_8) { + else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) { + px = index[b1 ^ QOI_OP_INDEX]; + } + else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) { px.rgba.r += ((b1 >> 4) & 0x03) - 2; px.rgba.g += ((b1 >> 2) & 0x03) - 2; px.rgba.b += ( b1 & 0x03) - 2; } - else if ((b1 & QOI_MASK_4) == QOI_DIFF_16) { + else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) { int b2 = bytes[p++]; - px.rgba.r += (b1 & 0x0f) - 8; - px.rgba.g += (b2 >> 4) - 8; - px.rgba.b += (b2 & 0x0f) - 8; - } - else if ((b1 & QOI_MASK_4) == QOI_GDIFF_16) { - int b2 = bytes[p++]; - int vg = ((b1 & 0x01) << 5 | (b2 & 0xf8) >> 3) - 32; - px.rgba.r += vg - 4 + ((b1 & 0x0e) >> 1); + int vg = (b1 & 0x3f) - 32; + px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f); px.rgba.g += vg; - px.rgba.b += vg - 4 + (b2 & 0x07); + px.rgba.b += vg - 8 + (b2 & 0x0f); } - else if ((b1 & QOI_MASK_4) == QOI_DIFF_24) { - int b2 = bytes[p++]; - int b3 = bytes[p++]; - px.rgba.r += (((b1 & 0x0f) << 1) | (b2 >> 7)) - 16; - px.rgba.g += ((b2 & 0x7c) >> 2) - 16; - px.rgba.b += (((b2 & 0x03) << 3) | ((b3 & 0xe0) >> 5)) - 16; - px.rgba.a += (b3 & 0x1f) - 16; - } - else if ((b1 & QOI_MASK_4) == QOI_COLOR) { - if (b1 & 8) { px.rgba.r = bytes[p++]; } - if (b1 & 4) { px.rgba.g = bytes[p++]; } - if (b1 & 2) { px.rgba.b = bytes[p++]; } - if (b1 & 1) { px.rgba.a = bytes[p++]; } + else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) { + run = (b1 & 0x3f); } index[QOI_COLOR_HASH(px) % 64] = px; @@ -584,9 +544,9 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { *(qoi_rgba_t*)(pixels + px_pos) = px; } else { - pixels[px_pos] = px.rgba.r; - pixels[px_pos+1] = px.rgba.g; - pixels[px_pos+2] = px.rgba.b; + pixels[px_pos + 0] = px.rgba.r; + pixels[px_pos + 1] = px.rgba.g; + pixels[px_pos + 2] = px.rgba.b; } } From eb292694320595abdf975f4be408e320af81159e Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 8 Dec 2021 11:25:24 +0100 Subject: [PATCH 033/208] Fix typo in documentation --- qoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index 8bd8126..2c339f4 100644 --- a/qoi.h +++ b/qoi.h @@ -168,7 +168,7 @@ so "10 - 13" will result in 253, while "250 + 7" will result in 1. |-------+-----------------| | 1 1 | run | -2-bit tag b01 +2-bit tag b11 6-bit run-length repeating the previous pixel: 1..62 Note that the run-lengths 63 and 64 (b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and QOI_OP_RGBA tags. From d6b1ec673ae4df22964646efb7f109ec92c531fc Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 8 Dec 2021 11:30:58 +0100 Subject: [PATCH 034/208] Add alpha channel to QOI_HASH --- qoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index 2c339f4..d0884c9 100644 --- a/qoi.h +++ b/qoi.h @@ -312,7 +312,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #define QOI_MASK_2 0xc0 // 11000000 -#define QOI_COLOR_HASH(C) (C.rgba.r * 3 + C.rgba.g * 5 + C.rgba.b * 7) +#define QOI_COLOR_HASH(C) (C.rgba.r * 3 + C.rgba.g * 5 + C.rgba.b * 7 + C.rgba.a * 11) #define QOI_MAGIC \ (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ ((unsigned int)'i') << 8 | ((unsigned int)'f')) From 947941fbd0a237906d04f7b0433735a52c3e9944 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 8 Dec 2021 14:14:51 +0100 Subject: [PATCH 035/208] Change colorspace header to an enum to avoid confusion --- qoi.h | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/qoi.h b/qoi.h index d0884c9..e4067ae 100644 --- a/qoi.h +++ b/qoi.h @@ -87,10 +87,7 @@ struct qoi_header_t { uint32_t width; // image width in pixels (BE) uint32_t height; // image height in pixels (BE) uint8_t channels; // must be 3 (RGB) or 4 (RGBA) - uint8_t colorspace; // a bitmap 0000rgba where - // - a zero bit indicates sRGBA, - // - a one bit indicates linear (user interpreted) - // colorspace for each channel + uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear }; The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous @@ -220,15 +217,15 @@ extern "C" { // describes either the input format (for qoi_write, qoi_encode), or is filled // with the description read from the file header (for qoi_read, qoi_decode). -// The colorspace in this qoi_desc is a bitmap with 0000rgba where a 0-bit -// indicates sRGB and a 1-bit indicates linear colorspace for each channel. You -// may use one of the predefined constants: QOI_SRGB, QOI_SRGB_LINEAR_ALPHA or -// QOI_LINEAR. The colorspace is purely informative. It will be saved to the -// file header, but does not affect en-/decoding in any way. +// The colorspace in this qoi_desc is an enum where +// 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel +// 1 = all channels are linear +// You may use the the constants QOI_SRGB or QOI_LINEAR. The colorspace is +// purely informative. It will be saved to the file header, but does not affect +// en-/decoding in any way. -#define QOI_SRGB 0x00 -#define QOI_SRGB_LINEAR_ALPHA 0x01 -#define QOI_LINEAR 0x0f +#define QOI_SRGB 0 +#define QOI_LINEAR 1 typedef struct { unsigned int width; From 6c83cf2e0c0e0ae44fd3d186b804e64390b1269b Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 8 Dec 2021 15:29:46 +0100 Subject: [PATCH 036/208] Increase padding to 8 zero-bytes --- qoi.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/qoi.h b/qoi.h index e4067ae..27b0fa7 100644 --- a/qoi.h +++ b/qoi.h @@ -80,7 +80,8 @@ you can define QOI_MALLOC and QOI_FREE before including this library. -- Data Format -A QOI file has a 14 byte header, followed by any number of data "chunks". +A QOI file has a 14 byte header, followed by any number of data "chunks" and 8 +zero bytes to mark the end of the data stream. struct qoi_header_t { char magic[4]; // magic bytes "qoif" @@ -196,9 +197,11 @@ are occupied by the QOI_OP_RGB and QOI_OP_RGBA tags. 8-bit alpha channel value -The byte stream is padded at the end with 4 zero bytes. Size the longest legal +The byte stream is padded at the end with 8 zero bytes. Since the longest legal chunk is 5 bytes (QOI_OP_RGBA), with this padding it is possible to check for an -overrun only once per decode loop iteration. +overrun only once per decode loop iteration. These 0x00 bytes also mark the end +of the data stream, as an encoder should never produce 8 consecutive zero bytes +within the stream. */ @@ -314,7 +317,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ ((unsigned int)'i') << 8 | ((unsigned int)'f')) #define QOI_HEADER_SIZE 14 -#define QOI_PADDING 4 +#define QOI_PADDING 8 typedef union { struct { unsigned char r, g, b, a; } rgba; From 92f7ebd3f83ac20988a6c97ef20158bb653ed8df Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 8 Dec 2021 15:30:56 +0100 Subject: [PATCH 037/208] Fix qoi_desc colorspace check --- qoi.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index 27b0fa7..f3f543a 100644 --- a/qoi.h +++ b/qoi.h @@ -344,7 +344,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { data == NULL || out_len == NULL || desc == NULL || desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || - (desc->colorspace & 0xf0) != 0 + desc->colorspace > 2 ) { return NULL; } @@ -480,6 +480,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { if ( desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || + desc->colorspace > 2 || header_magic != QOI_MAGIC ) { return NULL; From 6a73cc65c5fdb87d63a5b1f8c655df5c38d45392 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 8 Dec 2021 15:32:43 +0100 Subject: [PATCH 038/208] Wording, whitespace --- qoi.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/qoi.h b/qoi.h index f3f543a..a25d6cd 100644 --- a/qoi.h +++ b/qoi.h @@ -95,22 +95,21 @@ The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. Pixels are either encoded as - a run of the previous pixel - an index into a previously seen pixel - - a difference to the previous pixel value in r,g,b,a - - full r,g,b,a values + - a difference to the previous pixel value in r,g,b + - full r,g,b or r,g,b,a values A running array[64] of previously seen pixel values is maintained by the encoder and decoder. Each pixel that is seen by the encoder and decoder is put into this -array at the position (r * 3 + g * 5 + b * 7) % 64. In the encoder, if the pixel -value at this index matches the current pixel, this index position is written to -the stream. +array at the position (r * 3 + g * 5 + b * 7 + a * 11) % 64. In the encoder, if +the pixel value at this index matches the current pixel, this index position is +written to the stream as QOI_OP_INDEX. -Each chunk starts with a 8 or 2 bit tag, followed by a number of data bits. -The bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. -All values encoded in these data bits have the most significant bit (MSB) on the -left. +Each chunk starts with a 2 or 8 bit tag, followed by a number of data bits. The +bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All +values encoded in these data bits have the most significant bit on the left. -The 8-bit tags have precedence over the 2-bit tags. A decoder must check the -8-bit tags first. +The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the +presence of an 8-bit tag first. The possible chunks are: @@ -216,15 +215,16 @@ within the stream. extern "C" { #endif -// A pointer to qoi_desc struct has to be supplied to all of qoi's functions. It -// describes either the input format (for qoi_write, qoi_encode), or is filled -// with the description read from the file header (for qoi_read, qoi_decode). +// A pointer to a qoi_desc struct has to be supplied to all of qoi's functions. +// It describes either the input format (for qoi_write and qoi_encode), or is +// filled with the description read from the file header (for qoi_read and +// qoi_decode). // The colorspace in this qoi_desc is an enum where // 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel // 1 = all channels are linear -// You may use the the constants QOI_SRGB or QOI_LINEAR. The colorspace is -// purely informative. It will be saved to the file header, but does not affect +// You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely +// informative. It will be saved to the file header, but does not affect // en-/decoding in any way. #define QOI_SRGB 0 @@ -312,7 +312,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #define QOI_MASK_2 0xc0 // 11000000 -#define QOI_COLOR_HASH(C) (C.rgba.r * 3 + C.rgba.g * 5 + C.rgba.b * 7 + C.rgba.a * 11) +#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11) #define QOI_MAGIC \ (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ ((unsigned int)'i') << 8 | ((unsigned int)'f')) @@ -422,7 +422,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { vg > -3 && vg < 2 && vb > -3 && vb < 2 ) { - bytes[p++] = QOI_OP_DIFF | ((vr + 2) << 4) | (vg + 2) << 2 | (vb + 2); + bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2); } else if ( vg_r > -9 && vg_r < 8 && From 21031685199533e136bcbd6ace54c1b665884fb7 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 8 Dec 2021 15:45:18 +0100 Subject: [PATCH 039/208] Minor encoding throughput improvement --- qoi.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/qoi.h b/qoi.h index a25d6cd..07b4561 100644 --- a/qoi.h +++ b/qoi.h @@ -390,17 +390,17 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { if (px.v == px_prev.v) { run++; + if (run == 62 || px_pos == px_end) { + bytes[p++] = QOI_OP_RUN | (run - 1); + run = 0; + } } + else { + if (run > 0) { + bytes[p++] = QOI_OP_RUN | (run - 1); + run = 0; + } - if ( - run > 0 && - (run == 62 || px.v != px_prev.v || px_pos == px_end) - ) { - bytes[p++] = QOI_OP_RUN | (run - 1); - run = 0; - } - - if (px.v != px_prev.v) { int index_pos = QOI_COLOR_HASH(px) % 64; if (index[index_pos].v == px.v) { @@ -520,7 +520,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { px.rgba.a = bytes[p++]; } else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) { - px = index[b1 ^ QOI_OP_INDEX]; + px = index[b1]; } else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) { px.rgba.r += ((b1 >> 4) & 0x03) - 2; From 473e467e7b233707f4ff2d19651e9646499083d6 Mon Sep 17 00:00:00 2001 From: Amy While <26681721+elihwyma@users.noreply.github.com> Date: Wed, 8 Dec 2021 20:22:16 +0000 Subject: [PATCH 040/208] Add Swift Implementation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 39f8176..f71fc68 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ a simple wrapper to benchmark stbi, libpng and qoi - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/pfusik/qoi-ci (Ć) - https://github.com/kodonnell/qoi (Python) +- https://github.com/elihwyma/Swift-QOI (Swift) ## Packages From 075ab8fe42f3ca4a295b787c862308de01013e78 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Fri, 10 Dec 2021 20:09:52 +0100 Subject: [PATCH 041/208] Zero-initialize previous pixel color --- qoi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qoi.h b/qoi.h index 07b4561..5a5ee91 100644 --- a/qoi.h +++ b/qoi.h @@ -91,7 +91,7 @@ struct qoi_header_t { uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear }; -The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous +The decoder and encoder start with {r: 0, g: 0, b: 0, a: 0} as the previous pixel value. Pixels are either encoded as - a run of the previous pixel - an index into a previously seen pixel @@ -371,7 +371,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { qoi_rgba_t index[64] = {0}; int run = 0; - qoi_rgba_t px_prev = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; + qoi_rgba_t px_prev = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 0}}; qoi_rgba_t px = px_prev; int px_len = desc->width * desc->height * desc->channels; @@ -496,7 +496,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { return NULL; } - qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; + qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 0}}; qoi_rgba_t index[64] = {0}; int run = 0; From 0ad304d7615a57d80eb94f9eeef5097f8f8496bf Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Fri, 10 Dec 2021 20:27:35 +0100 Subject: [PATCH 042/208] Be more specific with the documentation of the file format --- qoi.h | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/qoi.h b/qoi.h index 5a5ee91..8eed580 100644 --- a/qoi.h +++ b/qoi.h @@ -81,36 +81,44 @@ you can define QOI_MALLOC and QOI_FREE before including this library. -- Data Format A QOI file has a 14 byte header, followed by any number of data "chunks" and 8 -zero bytes to mark the end of the data stream. +zero-bytes to mark the end of the data stream. struct qoi_header_t { char magic[4]; // magic bytes "qoif" uint32_t width; // image width in pixels (BE) uint32_t height; // image height in pixels (BE) - uint8_t channels; // must be 3 (RGB) or 4 (RGBA) + uint8_t channels; // 3 = RGB, 4 = RGBA uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear }; The decoder and encoder start with {r: 0, g: 0, b: 0, a: 0} as the previous pixel value. Pixels are either encoded as - a run of the previous pixel - - an index into a previously seen pixel + - an index into an array of previously seen pixels - a difference to the previous pixel value in r,g,b - full r,g,b or r,g,b,a values -A running array[64] of previously seen pixel values is maintained by the encoder -and decoder. Each pixel that is seen by the encoder and decoder is put into this -array at the position (r * 3 + g * 5 + b * 7 + a * 11) % 64. In the encoder, if -the pixel value at this index matches the current pixel, this index position is -written to the stream as QOI_OP_INDEX. +The color channels are assumed to not be premultiplied with the alpha channel +(“un-premultiplied alpha”). -Each chunk starts with a 2 or 8 bit tag, followed by a number of data bits. The +A running array[64] (zero-initialized) of previously seen pixel values is +maintained by the encoder and decoder. Each pixel that is seen by the encoder +and decoder is put into this array at the position formed by a hash function of +the color value. In the encoder, if the pixel value at the index matches the +current pixel, this index position is written to the stream as QOI_OP_INDEX. +The hash function for the index is: + + index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 + +Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All values encoded in these data bits have the most significant bit on the left. The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the presence of an 8-bit tag first. +The byte stream is padded with 8 zero-bytes at the end. + The possible chunks are: @@ -139,8 +147,10 @@ The possible chunks are: The difference to the current channel values are using a wraparound operation, so "1 - 2" will result in 255, while "255 + 1" will result in 0. +Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as +0 (b00). 1 is stored as 3 (b11). + - - QOI_OP_LUMA ------------------------------------- | Byte[0] | Byte[1] | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | |-------+-----------------+-------------+-----------| @@ -151,13 +161,18 @@ so "1 - 2" will result in 255, while "255 + 1" will result in 0. 4-bit red channel difference minus green channel difference -8..7 4-bit blue channel difference minus green channel difference -8..7 -The green channel is used to indicate the general direction of change and gets -a few more bits. dr and db base their diffs off of the green channel diff. E.g. - dr = (last_px.r - cur_px.r) - (last_px.g - cur_px.g) +The green channel is used to indicate the general direction of change and is +encoded in 6 bits. The red and green channels (dr and db) base their diffs off +of the green channel difference and are encoded in 4 bits. I.e.: + dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g) + db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g) The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while "250 + 7" will result in 1. +Values are stored as unsigned integers with a bias of 32 for the green channel +and a bias of 8 for the red and blue channel. + - QOI_OP_RUN ------------ | Byte[0] | @@ -167,8 +182,10 @@ so "10 - 13" will result in 253, while "250 + 7" will result in 1. 2-bit tag b11 6-bit run-length repeating the previous pixel: 1..62 -Note that the run-lengths 63 and 64 (b111110 and b111111) are illegal as they -are occupied by the QOI_OP_RGB and QOI_OP_RGBA tags. + +The run-length is stored with a bias of 1. Note that the run-lengths 63 and 64 +(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and +QOI_OP_RGBA tags. - QOI_OP_RGB ------------------------------------------ From c2edcd3d7a164d81ed149073602e0ac3842442a4 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 11 Dec 2021 12:54:01 +0100 Subject: [PATCH 043/208] Cosmetics --- qoi.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/qoi.h b/qoi.h index 8eed580..bee6cfb 100644 --- a/qoi.h +++ b/qoi.h @@ -123,22 +123,22 @@ The byte stream is padded with 8 zero-bytes at the end. The possible chunks are: - - QOI_OP_INDEX ---------- +.- QOI_OP_INDEX ----------. | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----------------| | 0 0 | index | - +`-------------------------` 2-bit tag b00 6-bit index into the color index array: 0..63 - - QOI_OP_DIFF ----------- +.- QOI_OP_DIFF -----------. | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----+-----+-----| | 0 1 | dr | dg | db | - +`-------------------------` 2-bit tag b01 2-bit red channel difference from the previous pixel between -2..1 2-bit green channel difference from the previous pixel between -2..1 @@ -151,11 +151,12 @@ Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as 0 (b00). 1 is stored as 3 (b11). +.- QOI_OP_LUMA -------------------------------------. | Byte[0] | Byte[1] | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | |-------+-----------------+-------------+-----------| | 1 0 | green diff | dr - dg | db - dg | - +`---------------------------------------------------` 2-bit tag b10 6-bit green channel difference from the previous pixel -32..31 4-bit red channel difference minus green channel difference -8..7 @@ -174,12 +175,12 @@ Values are stored as unsigned integers with a bias of 32 for the green channel and a bias of 8 for the red and blue channel. - - QOI_OP_RUN ------------ +.- QOI_OP_RUN ------------. | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----------------| | 1 1 | run | - +`-------------------------` 2-bit tag b11 6-bit run-length repeating the previous pixel: 1..62 @@ -188,24 +189,24 @@ The run-length is stored with a bias of 1. Note that the run-lengths 63 and 64 QOI_OP_RGBA tags. - - QOI_OP_RGB ------------------------------------------ +.- QOI_OP_RGB ------------------------------------------. | Byte[0] | Byte[1] | Byte[2] | Byte[3] | | 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | |-------------------------+---------+---------+---------| | 1 1 1 1 1 1 1 0 | red | green | blue | - +`-------------------------------------------------------` 8-bit tag b11111110 8-bit red channel value 8-bit green channel value 8-bit blue channel value -- QOI_OP_RGBA ---------------------------------------------------- +.- QOI_OP_RGBA ---------------------------------------------------. | Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] | | 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | |-------------------------+---------+---------+---------+---------| | 1 1 1 1 1 1 1 1 | red | green | blue | alpha | - +`-----------------------------------------------------------------` 8-bit tag b11111111 8-bit red channel value 8-bit green channel value From 199362ed1d2abdee9769e72eebc18653cc1543ef Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 11 Dec 2021 15:48:01 +0100 Subject: [PATCH 044/208] Add note about current specification --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 95b5d5b..37896e9 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,8 @@ More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compressi ⚠️ 2021.11.30 – the file format is not yet finalized. We're still working to fix some smaller issues. The final specification will be announced on 2021.12.20. -Thanks for your patience! https://github.com/phoboslab/qoi/issues/48 - -These specification changes are ~~not yet reflected in the code here~~ -reflected in qoi.h now. +Thanks for your patience! The WIP file format specification can be found in +[qoi.h](https://github.com/phoboslab/qoi/blob/master/qoi.h) ## Why? From e76f25a60630f82f64542ff33f4e4e033353fac2 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 11 Dec 2021 15:49:14 +0100 Subject: [PATCH 045/208] Ignore stb dependencies and build artifacts --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ba28150..9234e61 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -images/ \ No newline at end of file +images/ +stb_image.h +stb_image_write.h +qoibench +qoiconv From ba5c1711c70f1fb4394a67ff8c9f394d117cc317 Mon Sep 17 00:00:00 2001 From: 0xd34df00d <0xd34df00d@gmail.com> Date: Sat, 11 Dec 2021 11:22:41 -0500 Subject: [PATCH 046/208] Add a Haskell implementation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 37896e9..36a20b9 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ a simple wrapper to benchmark stbi, libpng and qoi - https://github.com/zakarumych/rapid-qoi (Rust) - https://github.com/xfmoulet/qoi (Go) - https://github.com/panzi/jsqoi (TypeScript) +- https://github.com/0xd34df00d/hsqoi (Haskell) - https://github.com/pfusik/qoi-ci (Ć) - https://github.com/kodonnell/qoi (Python) - https://github.com/NUlliiON/QoiSharp (C#) From 8c77fad3400a97e890d79b7fe36cfd1f2796aafd Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Sat, 11 Dec 2021 21:03:37 +0300 Subject: [PATCH 047/208] change char local vars to signed char this accomodates toolchains where char type is unsigned by default. --- qoi.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qoi.h b/qoi.h index bee6cfb..bbb0c8a 100644 --- a/qoi.h +++ b/qoi.h @@ -428,12 +428,12 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { index[index_pos] = px; if (px.rgba.a == px_prev.rgba.a) { - char vr = px.rgba.r - px_prev.rgba.r; - char vg = px.rgba.g - px_prev.rgba.g; - char vb = px.rgba.b - px_prev.rgba.b; + signed char vr = px.rgba.r - px_prev.rgba.r; + signed char vg = px.rgba.g - px_prev.rgba.g; + signed char vb = px.rgba.b - px_prev.rgba.b; - char vg_r = vr - vg; - char vg_b = vb - vg; + signed char vg_r = vr - vg; + signed char vg_b = vb - vg; if ( vr > -3 && vr < 2 && From 99fa97792f11085fef1189b0ceee17d8cf64f7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier-Fr=C3=A9d=C3=A9ric=20Moulet?= Date: Mon, 13 Dec 2021 11:18:41 +0100 Subject: [PATCH 048/208] Revert "Zero-initialize previous pixel color" This reverts commit 075ab8fe42f3ca4a295b787c862308de01013e78. Closes #30 --- qoi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qoi.h b/qoi.h index bee6cfb..1acd7d7 100644 --- a/qoi.h +++ b/qoi.h @@ -91,7 +91,7 @@ struct qoi_header_t { uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear }; -The decoder and encoder start with {r: 0, g: 0, b: 0, a: 0} as the previous +The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. Pixels are either encoded as - a run of the previous pixel - an index into an array of previously seen pixels @@ -389,7 +389,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { qoi_rgba_t index[64] = {0}; int run = 0; - qoi_rgba_t px_prev = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 0}}; + qoi_rgba_t px_prev = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; qoi_rgba_t px = px_prev; int px_len = desc->width * desc->height * desc->channels; @@ -514,7 +514,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { return NULL; } - qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 0}}; + qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; qoi_rgba_t index[64] = {0}; int run = 0; From 2aaba8da962c0662cc1a9c450383365c615c3fc3 Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Mon, 13 Dec 2021 15:56:56 +0300 Subject: [PATCH 049/208] make qoi.h build using c89 compilers. also add a QOI_ZEROARR macro, wrapping around memset by default. --- qoi.h | 93 ++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/qoi.h b/qoi.h index 1acd7d7..c602648 100644 --- a/qoi.h +++ b/qoi.h @@ -77,6 +77,9 @@ QOI_NO_STDIO before including this library. This library uses malloc() and free(). To supply your own malloc implementation you can define QOI_MALLOC and QOI_FREE before including this library. +This library uses memset() to zero arrayb. To supply your own implementation +you can define QOI_ZEROARR before including this library. + -- Data Format @@ -315,11 +318,15 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #ifdef QOI_IMPLEMENTATION #include +#include #ifndef QOI_MALLOC #define QOI_MALLOC(sz) malloc(sz) #define QOI_FREE(p) free(p) #endif +#ifndef QOI_ZEROARR +# define QOI_ZEROARR(_arr) memset((_arr),0,sizeof(_arr)) +#endif #define QOI_OP_INDEX 0x00 // 00xxxxxx #define QOI_OP_DIFF 0x40 // 01xxxxxx @@ -358,6 +365,13 @@ unsigned int qoi_read_32(const unsigned char *bytes, int *p) { } void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { + int i, max_size, p, run; + int px_len, px_end, px_pos, channels; + unsigned char *bytes; + const unsigned char *pixels; + qoi_rgba_t index[64]; + qoi_rgba_t px, px_prev; + if ( data == NULL || out_len == NULL || desc == NULL || desc->width == 0 || desc->height == 0 || @@ -367,12 +381,12 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { return NULL; } - int max_size = + max_size = desc->width * desc->height * (desc->channels + 1) + QOI_HEADER_SIZE + QOI_PADDING; - int p = 0; - unsigned char *bytes = QOI_MALLOC(max_size); + p = 0; + bytes = (unsigned char *) QOI_MALLOC(max_size); if (!bytes) { return NULL; } @@ -384,19 +398,22 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { bytes[p++] = desc->colorspace; - const unsigned char *pixels = (const unsigned char *)data; + pixels = (const unsigned char *)data; - qoi_rgba_t index[64] = {0}; + QOI_ZEROARR(index); - int run = 0; - qoi_rgba_t px_prev = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; - qoi_rgba_t px = px_prev; + run = 0; + px_prev.rgba.r = 0; + px_prev.rgba.g = 0; + px_prev.rgba.b = 0; + px_prev.rgba.a = 255; + px = px_prev; - int px_len = desc->width * desc->height * desc->channels; - int px_end = px_len - desc->channels; - int channels = desc->channels; + px_len = desc->width * desc->height * desc->channels; + px_end = px_len - desc->channels; + channels = desc->channels; - for (int px_pos = 0; px_pos < px_len; px_pos += channels) { + for (px_pos = 0; px_pos < px_len; px_pos += channels) { if (channels == 4) { px = *(qoi_rgba_t *)(pixels + px_pos); } @@ -414,12 +431,14 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { } } else { + int index_pos; + if (run > 0) { bytes[p++] = QOI_OP_RUN | (run - 1); run = 0; } - int index_pos = QOI_COLOR_HASH(px) % 64; + index_pos = QOI_COLOR_HASH(px) % 64; if (index[index_pos].v == px.v) { bytes[p++] = QOI_OP_INDEX | index_pos; @@ -469,7 +488,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { px_prev = px; } - for (int i = 0; i < QOI_PADDING; i++) { + for (i = 0; i < QOI_PADDING; i++) { bytes[p++] = 0; } @@ -478,6 +497,14 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { } void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { + const unsigned char *bytes; + unsigned int header_magic; + unsigned char *pixels; + qoi_rgba_t index[64]; + qoi_rgba_t px; + int px_len, chunks_len, px_pos; + int p = 0, run = 0; + if ( data == NULL || desc == NULL || (channels != 0 && channels != 3 && channels != 4) || @@ -486,10 +513,9 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { return NULL; } - const unsigned char *bytes = (const unsigned char *)data; - int p = 0; + bytes = (const unsigned char *)data; - unsigned int header_magic = qoi_read_32(bytes, &p); + header_magic = qoi_read_32(bytes, &p); desc->width = qoi_read_32(bytes, &p); desc->height = qoi_read_32(bytes, &p); desc->channels = bytes[p++]; @@ -508,18 +534,20 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { channels = desc->channels; } - int px_len = desc->width * desc->height * channels; - unsigned char *pixels = QOI_MALLOC(px_len); + px_len = desc->width * desc->height * channels; + pixels = (unsigned char *) QOI_MALLOC(px_len); if (!pixels) { return NULL; } - qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; - qoi_rgba_t index[64] = {0}; + QOI_ZEROARR(index); + px.rgba.r = 0; + px.rgba.g = 0; + px.rgba.b = 0; + px.rgba.a = 255; - int run = 0; - int chunks_len = size - QOI_PADDING; - for (int px_pos = 0; px_pos < px_len; px_pos += channels) { + chunks_len = size - QOI_PADDING; + for (px_pos = 0; px_pos < px_len; px_pos += channels) { if (run > 0) { run--; } @@ -577,12 +605,14 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { FILE *f = fopen(filename, "wb"); + int size; + void *encoded; + if (!f) { return 0; } - int size; - void *encoded = qoi_encode(data, desc, &size); + encoded = qoi_encode(data, desc, &size); if (!encoded) { fclose(f); return 0; @@ -597,24 +627,27 @@ int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { void *qoi_read(const char *filename, qoi_desc *desc, int channels) { FILE *f = fopen(filename, "rb"); + int size, bytes_read; + void *pixels, *data; + if (!f) { return NULL; } fseek(f, 0, SEEK_END); - int size = ftell(f); + size = ftell(f); fseek(f, 0, SEEK_SET); - void *data = QOI_MALLOC(size); + data = QOI_MALLOC(size); if (!data) { fclose(f); return NULL; } - int bytes_read = fread(data, 1, size, f); + bytes_read = fread(data, 1, size, f); fclose(f); - void *pixels = qoi_decode(data, bytes_read, desc, channels); + pixels = qoi_decode(data, bytes_read, desc, channels); QOI_FREE(data); return pixels; } From b743409e06e80854b3de8ea613e1c0a870858304 Mon Sep 17 00:00:00 2001 From: anon Date: Mon, 13 Dec 2021 15:27:11 +0000 Subject: [PATCH 050/208] Fix qoibench to feed RGB input to the encoders when the source image is RGB. Fix rate calculation by having the raw size of RGB input be w x h x 3 instead of w x h x 4. --- qoibench.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/qoibench.c b/qoibench.c index ba401a4..30868d4 100644 --- a/qoibench.c +++ b/qoibench.c @@ -131,7 +131,7 @@ void libpng_encode_callback(png_structp png_ptr, png_bytep data, png_size_t leng write_data->size += length; } -void *libpng_encode(void *pixels, int w, int h, int *out_len) { +void *libpng_encode(void *pixels, int w, int h, int channels, int *out_len) { png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png) { ERROR("png_create_write_struct"); @@ -152,7 +152,7 @@ void *libpng_encode(void *pixels, int w, int h, int *out_len) { info, w, h, 8, - PNG_COLOR_TYPE_RGBA, + (channels==3)?PNG_COLOR_TYPE_RGB:PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT @@ -160,13 +160,13 @@ void *libpng_encode(void *pixels, int w, int h, int *out_len) { png_bytep row_pointers[h]; for(int y = 0; y < h; y++){ - row_pointers[y] = ((unsigned char *)pixels + y * w * 4); + row_pointers[y] = ((unsigned char *)pixels + y * w * channels); } libpng_write_t write_data = { .size = 0, - .capacity = w * h * 4, - .data = malloc(w * h * 4) + .capacity = w * h * channels, + .data = malloc(w * h * channels) }; png_set_rows(png, info, row_pointers); @@ -410,15 +410,21 @@ benchmark_result_t benchmark_image(const char *path) { int encoded_qoi_size; int w; int h; + int channels; // Load the encoded PNG, encoded QOI and raw pixels into memory + if(!stbi_info(path, &w, &h, &channels)) { + ERROR("Error decoding header %s", path); + } - void *pixels = (void *)stbi_load(path, &w, &h, NULL, 4); + channels = (channels == 3) ? 3 : 4; + + void *pixels = (void *)stbi_load(path, &w, &h, NULL, channels); void *encoded_png = fload(path, &encoded_png_size); void *encoded_qoi = qoi_encode(pixels, &(qoi_desc){ .width = w, .height = h, - .channels = 4, + .channels = channels, .colorspace = QOI_SRGB }, &encoded_qoi_size); @@ -430,8 +436,8 @@ benchmark_result_t benchmark_image(const char *path) { if (!opt_noverify) { qoi_desc dc; - void *pixels_qoi = qoi_decode(encoded_qoi, encoded_qoi_size, &dc, 4); - if (memcmp(pixels, pixels_qoi, w * h * 4) != 0) { + void *pixels_qoi = qoi_decode(encoded_qoi, encoded_qoi_size, &dc, channels); + if (memcmp(pixels, pixels_qoi, w * h * channels) != 0) { ERROR("QOI roundtrip pixel missmatch for %s", path); } free(pixels_qoi); @@ -441,7 +447,7 @@ benchmark_result_t benchmark_image(const char *path) { benchmark_result_t res = {0}; res.count = 1; - res.raw_size = w * h * 4; + res.raw_size = w * h * channels; res.px = w * h; res.w = w; res.h = h; @@ -473,19 +479,18 @@ benchmark_result_t benchmark_image(const char *path) { // Encoding - if (!opt_noencode) { if (!opt_nopng) { BENCHMARK_FN(opt_nowarmup, opt_runs, res.libpng.encode_time, { int enc_size; - void *enc_p = libpng_encode(pixels, w, h, &enc_size); + void *enc_p = libpng_encode(pixels, w, h, channels, &enc_size); res.libpng.size = enc_size; free(enc_p); }); BENCHMARK_FN(opt_nowarmup, opt_runs, res.stbi.encode_time, { int enc_size = 0; - stbi_write_png_to_func(stbi_write_callback, &enc_size, w, h, 4, pixels, 0); + stbi_write_png_to_func(stbi_write_callback, &enc_size, w, h, channels, pixels, 0); res.stbi.size = enc_size; }); } @@ -495,7 +500,7 @@ benchmark_result_t benchmark_image(const char *path) { void *enc_p = qoi_encode(pixels, &(qoi_desc){ .width = w, .height = h, - .channels = 4, + .channels = channels, .colorspace = QOI_SRGB }, &enc_size); res.qoi.size = enc_size; From 5983658ad4b16c379e18bb4f69bf8f6ea056a7a5 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 13 Dec 2021 17:16:22 +0100 Subject: [PATCH 051/208] Whitespace, wording --- qoi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qoi.h b/qoi.h index c602648..1a74222 100644 --- a/qoi.h +++ b/qoi.h @@ -77,8 +77,8 @@ QOI_NO_STDIO before including this library. This library uses malloc() and free(). To supply your own malloc implementation you can define QOI_MALLOC and QOI_FREE before including this library. -This library uses memset() to zero arrayb. To supply your own implementation -you can define QOI_ZEROARR before including this library. +This library uses memset() to zero-initialize the index. To supply your own +implementation you can define QOI_ZEROARR before including this library. -- Data Format @@ -325,7 +325,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #define QOI_FREE(p) free(p) #endif #ifndef QOI_ZEROARR -# define QOI_ZEROARR(_arr) memset((_arr),0,sizeof(_arr)) + #define QOI_ZEROARR(_arr) memset((_arr),0,sizeof(_arr)) #endif #define QOI_OP_INDEX 0x00 // 00xxxxxx From 3973c549dc3d6e676e7350925a8a7f2ece826698 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 13 Dec 2021 17:23:04 +0100 Subject: [PATCH 052/208] Remove single line comments to conform to c89 -pedantic --- qoi.h | 94 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/qoi.h b/qoi.h index 1a74222..4a823b6 100644 --- a/qoi.h +++ b/qoi.h @@ -226,8 +226,8 @@ within the stream. */ -// ----------------------------------------------------------------------------- -// Header - Public functions +/* ----------------------------------------------------------------------------- +Header - Public functions */ #ifndef QOI_H #define QOI_H @@ -236,17 +236,17 @@ within the stream. extern "C" { #endif -// A pointer to a qoi_desc struct has to be supplied to all of qoi's functions. -// It describes either the input format (for qoi_write and qoi_encode), or is -// filled with the description read from the file header (for qoi_read and -// qoi_decode). +/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions. +It describes either the input format (for qoi_write and qoi_encode), or is +filled with the description read from the file header (for qoi_read and +qoi_decode). -// The colorspace in this qoi_desc is an enum where -// 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel -// 1 = all channels are linear -// You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely -// informative. It will be saved to the file header, but does not affect -// en-/decoding in any way. +The colorspace in this qoi_desc is an enum where + 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel + 1 = all channels are linear +You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely +informative. It will be saved to the file header, but does not affect +en-/decoding in any way. */ #define QOI_SRGB 0 #define QOI_LINEAR 1 @@ -260,49 +260,49 @@ typedef struct { #ifndef QOI_NO_STDIO -// Encode raw RGB or RGBA pixels into a QOI image and write it to the file -// system. The qoi_desc struct must be filled with the image width, height, -// number of channels (3 = RGB, 4 = RGBA) and the colorspace. +/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file +system. The qoi_desc struct must be filled with the image width, height, +number of channels (3 = RGB, 4 = RGBA) and the colorspace. -// The function returns 0 on failure (invalid parameters, or fopen or malloc -// failed) or the number of bytes written on success. +The function returns 0 on failure (invalid parameters, or fopen or malloc +failed) or the number of bytes written on success. */ int qoi_write(const char *filename, const void *data, const qoi_desc *desc); -// Read and decode a QOI image from the file system. If channels is 0, the -// number of channels from the file header is used. If channels is 3 or 4 the -// output format will be forced into this number of channels. +/* Read and decode a QOI image from the file system. If channels is 0, the +number of channels from the file header is used. If channels is 3 or 4 the +output format will be forced into this number of channels. -// The function either returns NULL on failure (invalid data, or malloc or fopen -// failed) or a pointer to the decoded pixels. On success, the qoi_desc struct -// will be filled with the description from the file header. +The function either returns NULL on failure (invalid data, or malloc or fopen +failed) or a pointer to the decoded pixels. On success, the qoi_desc struct +will be filled with the description from the file header. -// The returned pixel data should be free()d after use. +The returned pixel data should be free()d after use. */ void *qoi_read(const char *filename, qoi_desc *desc, int channels); -#endif // QOI_NO_STDIO +#endif /* QOI_NO_STDIO */ -// Encode raw RGB or RGBA pixels into a QOI image in memory. +/* Encode raw RGB or RGBA pixels into a QOI image in memory. -// The function either returns NULL on failure (invalid parameters or malloc -// failed) or a pointer to the encoded data on success. On success the out_len -// is set to the size in bytes of the encoded data. +The function either returns NULL on failure (invalid parameters or malloc +failed) or a pointer to the encoded data on success. On success the out_len +is set to the size in bytes of the encoded data. -// The returned qoi data should be free()d after use. +The returned qoi data should be free()d after use. */ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len); -// Decode a QOI image from memory. +/* Decode a QOI image from memory. -// The function either returns NULL on failure (invalid parameters or malloc -// failed) or a pointer to the decoded pixels. On success, the qoi_desc struct -// is filled with the description from the file header. +The function either returns NULL on failure (invalid parameters or malloc +failed) or a pointer to the decoded pixels. On success, the qoi_desc struct +is filled with the description from the file header. -// The returned pixel data should be free()d after use. +The returned pixel data should be free()d after use. */ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); @@ -310,11 +310,11 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #ifdef __cplusplus } #endif -#endif // QOI_H +#endif /* QOI_H */ -// ----------------------------------------------------------------------------- -// Implementation +/* ----------------------------------------------------------------------------- +Implementation */ #ifdef QOI_IMPLEMENTATION #include @@ -328,14 +328,14 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #define QOI_ZEROARR(_arr) memset((_arr),0,sizeof(_arr)) #endif -#define QOI_OP_INDEX 0x00 // 00xxxxxx -#define QOI_OP_DIFF 0x40 // 01xxxxxx -#define QOI_OP_LUMA 0x80 // 10xxxxxx -#define QOI_OP_RUN 0xc0 // 11xxxxxx -#define QOI_OP_RGB 0xfe // 11111110 -#define QOI_OP_RGBA 0xff // 11111111 +#define QOI_OP_INDEX 0x00 /* 00xxxxxx */ +#define QOI_OP_DIFF 0x40 /* 01xxxxxx */ +#define QOI_OP_LUMA 0x80 /* 10xxxxxx */ +#define QOI_OP_RUN 0xc0 /* 11xxxxxx */ +#define QOI_OP_RGB 0xfe /* 11111110 */ +#define QOI_OP_RGBA 0xff /* 11111111 */ -#define QOI_MASK_2 0xc0 // 11000000 +#define QOI_MASK_2 0xc0 /* 11000000 */ #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11) #define QOI_MAGIC \ @@ -652,5 +652,5 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { return pixels; } -#endif // QOI_NO_STDIO -#endif // QOI_IMPLEMENTATION +#endif /* QOI_NO_STDIO */ +#endif /* QOI_IMPLEMENTATION */ From 525f32cefe58b853edae0b2818c3d85f3c3b9438 Mon Sep 17 00:00:00 2001 From: anon Date: Mon, 13 Dec 2021 16:34:39 +0000 Subject: [PATCH 053/208] Convert all non-RGB and non-RGBA input to RGBA. --- qoiconv.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/qoiconv.c b/qoiconv.c index 6e7ad36..1d112f6 100644 --- a/qoiconv.c +++ b/qoiconv.c @@ -58,7 +58,17 @@ int main(int argc, char **argv) { void *pixels = NULL; int w, h, channels; if (STR_ENDS_WITH(argv[1], ".png")) { - pixels = (void *)stbi_load(argv[1], &w, &h, &channels, 0); + if(!stbi_info(argv[1], &w, &h, &channels)) { + printf("Couldn't read header %s\n", argv[1]); + exit(1); + } + if(channels < 3) {// Force all odd encodings to be RGBA + channels = 4; + pixels = (void *)stbi_load(argv[1], &w, &h, NULL, 4); + } + else { + pixels = (void *)stbi_load(argv[1], &w, &h, &channels, 0); + } } else if (STR_ENDS_WITH(argv[1], ".qoi")) { qoi_desc desc; From 91cc726583cfb4297b66555f5934b1c948c0faf9 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 13 Dec 2021 17:44:24 +0100 Subject: [PATCH 054/208] Mention Java implementation; close #59 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 36a20b9..a316594 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ a simple wrapper to benchmark stbi, libpng and qoi - https://github.com/NUlliiON/QoiSharp (C#) - https://github.com/rbino/qoix (Elixir) - https://github.com/elihwyma/Swift-QOI (Swift) +- https://github.com/saharNooby/qoi-java (Java) ## Packages From 0112e3d5552019ac2eea0d1f24bb29e8db03e478 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 14 Dec 2021 20:54:37 +0100 Subject: [PATCH 055/208] Whitespace, cosmetics --- qoi.h | 12 ++++++------ qoibench.c | 6 ++++-- qoiconv.c | 10 +++++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/qoi.h b/qoi.h index 7cc4094..dee0d6e 100644 --- a/qoi.h +++ b/qoi.h @@ -111,7 +111,7 @@ the color value. In the encoder, if the pixel value at the index matches the current pixel, this index position is written to the stream as QOI_OP_INDEX. The hash function for the index is: - index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 + index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All @@ -242,8 +242,8 @@ filled with the description read from the file header (for qoi_read and qoi_decode). The colorspace in this qoi_desc is an enum where - 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel - 1 = all channels are linear + 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel + 1 = all channels are linear You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely informative. It will be saved to the file header, but does not affect en-/decoding in any way. */ @@ -325,7 +325,7 @@ Implementation */ #define QOI_FREE(p) free(p) #endif #ifndef QOI_ZEROARR - #define QOI_ZEROARR(_arr) memset((_arr),0,sizeof(_arr)) + #define QOI_ZEROARR(a) memset((a),0,sizeof(a)) #endif #define QOI_OP_INDEX 0x00 /* 00xxxxxx */ @@ -361,7 +361,7 @@ unsigned int qoi_read_32(const unsigned char *bytes, int *p) { unsigned int b = bytes[(*p)++]; unsigned int c = bytes[(*p)++]; unsigned int d = bytes[(*p)++]; - return (a << 24) | (b << 16) | (c << 8) | d; + return a << 24 | b << 16 | c << 8 | d; } void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { @@ -430,7 +430,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { run = 0; } } - else { + else { int index_pos; if (run > 0) { diff --git a/qoibench.c b/qoibench.c index 30868d4..27f1ec3 100644 --- a/qoibench.c +++ b/qoibench.c @@ -152,7 +152,7 @@ void *libpng_encode(void *pixels, int w, int h, int channels, int *out_len) { info, w, h, 8, - (channels==3)?PNG_COLOR_TYPE_RGB:PNG_COLOR_TYPE_RGBA, + channels == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT @@ -417,7 +417,9 @@ benchmark_result_t benchmark_image(const char *path) { ERROR("Error decoding header %s", path); } - channels = (channels == 3) ? 3 : 4; + if (channels != 3) { + channels = 4; + } void *pixels = (void *)stbi_load(path, &w, &h, NULL, channels); void *encoded_png = fload(path, &encoded_png_size); diff --git a/qoiconv.c b/qoiconv.c index 1d112f6..f22513c 100644 --- a/qoiconv.c +++ b/qoiconv.c @@ -62,13 +62,13 @@ int main(int argc, char **argv) { printf("Couldn't read header %s\n", argv[1]); exit(1); } - if(channels < 3) {// Force all odd encodings to be RGBA + + // Force all odd encodings to be RGBA + if(channels != 3) { channels = 4; - pixels = (void *)stbi_load(argv[1], &w, &h, NULL, 4); - } - else { - pixels = (void *)stbi_load(argv[1], &w, &h, &channels, 0); } + + pixels = (void *)stbi_load(argv[1], &w, &h, NULL, channels); } else if (STR_ENDS_WITH(argv[1], ".qoi")) { qoi_desc desc; From ae073961581e88a2afcc5c2d66f2c34572c2026e Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 14 Dec 2021 21:34:33 +0100 Subject: [PATCH 056/208] Avoid UTF-8 in comments... again. --- qoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index dee0d6e..d473df2 100644 --- a/qoi.h +++ b/qoi.h @@ -102,7 +102,7 @@ pixel value. Pixels are either encoded as - full r,g,b or r,g,b,a values The color channels are assumed to not be premultiplied with the alpha channel -(“un-premultiplied alpha”). +("un-premultiplied alpha"). A running array[64] (zero-initialized) of previously seen pixel values is maintained by the encoder and decoder. Each pixel that is seen by the encoder From 11dbe1e6aa6593d964a2b6648d53502877af2fd1 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 16 Dec 2021 20:02:37 +0100 Subject: [PATCH 057/208] Add clang fuzzing harness. Thanks @landaire --- qoifuzz.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 qoifuzz.c diff --git a/qoifuzz.c b/qoifuzz.c new file mode 100644 index 0000000..1b5c792 --- /dev/null +++ b/qoifuzz.c @@ -0,0 +1,51 @@ +/* + +clang fuzzing harness for qoi_decode + +Compile and run with: + clang -fsanitize=address,fuzzer -g -O0 qoifuzz.c && ./a.out + +Dominic Szablewski - https://phoboslab.org + + +-- LICENSE: The MIT License(MIT) + +Copyright(c) 2021 Dominic Szablewski + +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. + +*/ + + +#define QOI_IMPLEMENTATION +#include "qoi.h" +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + int w, h; + if (size < 4) { + return 0; + } + + qoi_desc desc; + void* decoded = qoi_decode((void*)(data + 4), (int)(size - 4), &desc, *((int *)data)); + if (decoded != NULL) { + free(decoded); + } + return 0; +} From 2f255c7aff1b8689b1db9bb619364af187d1f919 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 16 Dec 2021 20:12:27 +0100 Subject: [PATCH 058/208] Enforce a limit of 400 million pixels, 2GB file size --- README.md | 18 ++++++++++++++++-- qoi.h | 22 +++++++++++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a316594..3c2e134 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ the documentation. More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compression -⚠️ Please note that this library is not yet ready to deal with untrusted input. - ⚠️ 2021.11.30 – the file format is not yet finalized. We're still working to fix some smaller issues. The final specification will be announced on 2021.12.20. Thanks for your patience! The WIP file format specification can be found in @@ -30,6 +28,22 @@ converts between png <> qoi a simple wrapper to benchmark stbi, libpng and qoi +## Limitations + +The QOI file format allows for huge images with up to 18 exa-pixels. A streaming +en-/decoder can handle these with minimal RAM requirements, assuming there is +enough storage space. + +This particular implementation of QOI however is limited to images with a +maximum size of 400 million pixels. It will safely refuse to en-/decode anything +larger than that. This is not a streaming en-/decoder. It loads the whole image +file into RAM before doing any work and is not extensively optimized for +performance (but it's still very fast). + +If this is a limitation for your use case, please look into any of the other +implementations listed below. + + ## Tools - https://github.com/floooh/qoiview diff --git a/qoi.h b/qoi.h index d473df2..1962164 100644 --- a/qoi.h +++ b/qoi.h @@ -168,8 +168,8 @@ Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as The green channel is used to indicate the general direction of change and is encoded in 6 bits. The red and green channels (dr and db) base their diffs off of the green channel difference and are encoded in 4 bits. I.e.: - dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g) - db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g) + dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g) + db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g) The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while "250 + 7" will result in 1. @@ -344,6 +344,12 @@ Implementation */ #define QOI_HEADER_SIZE 14 #define QOI_PADDING 8 +/* 2GB is the max file size that this implementation can safely handle. We guard +against anything larger than that, assuming the worst case with 5 bytes per +pixel, rounded down to a nice clean value. 400 million pixels ought to be +enough for anybody. */ +#define QOI_PIXELS_MAX ((unsigned int)400000000) + typedef union { struct { unsigned char r, g, b, a; } rgba; unsigned int v; @@ -376,7 +382,8 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { data == NULL || out_len == NULL || desc == NULL || desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || - desc->colorspace > 2 + desc->colorspace > 2 || + desc->height >= QOI_PIXELS_MAX / desc->width ) { return NULL; } @@ -502,7 +509,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { unsigned char *pixels; qoi_rgba_t index[64]; qoi_rgba_t px; - int px_len, chunks_len, px_pos; + int px_len, chunks_len, px_pos; int p = 0, run = 0; if ( @@ -525,7 +532,8 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || desc->colorspace > 2 || - header_magic != QOI_MAGIC + header_magic != QOI_MAGIC || + desc->height >= QOI_PIXELS_MAX / desc->width ) { return NULL; } @@ -636,6 +644,10 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { fseek(f, 0, SEEK_END); size = ftell(f); + if (size <= 0) { + fclose(f); + return NULL; + } fseek(f, 0, SEEK_SET); data = QOI_MALLOC(size); From bf6951036dd1132c25cc8df251d37ecd663fcb8d Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 16 Dec 2021 20:13:52 +0100 Subject: [PATCH 059/208] Fix check for valid colorspace --- qoi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qoi.h b/qoi.h index 1962164..df84228 100644 --- a/qoi.h +++ b/qoi.h @@ -382,7 +382,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { data == NULL || out_len == NULL || desc == NULL || desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || - desc->colorspace > 2 || + desc->colorspace > 1 || desc->height >= QOI_PIXELS_MAX / desc->width ) { return NULL; @@ -531,7 +531,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { if ( desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || - desc->colorspace > 2 || + desc->colorspace > 1 || header_magic != QOI_MAGIC || desc->height >= QOI_PIXELS_MAX / desc->width ) { From 3a62cabad235a327acec558535935f2b6bf313d3 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 16 Dec 2021 20:50:19 +0100 Subject: [PATCH 060/208] Change padding bytes to a unique stream-end marker; #89 --- qoi.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/qoi.h b/qoi.h index df84228..f83e5d7 100644 --- a/qoi.h +++ b/qoi.h @@ -83,8 +83,8 @@ implementation you can define QOI_ZEROARR before including this library. -- Data Format -A QOI file has a 14 byte header, followed by any number of data "chunks" and 8 -zero-bytes to mark the end of the data stream. +A QOI file has a 14 byte header, followed by any number of data "chunks" and an +8-byte end marker. struct qoi_header_t { char magic[4]; // magic bytes "qoif" @@ -120,7 +120,7 @@ values encoded in these data bits have the most significant bit on the left. The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the presence of an 8-bit tag first. -The byte stream is padded with 8 zero-bytes at the end. +The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte. The possible chunks are: @@ -135,6 +135,9 @@ The possible chunks are: 2-bit tag b00 6-bit index into the color index array: 0..63 +A valid encoder must not issue 7 or more consecutive QOI_OP_INDEX chunks to the +index 0, to avoid confusion with the 8 byte end marker. + .- QOI_OP_DIFF -----------. | Byte[0] | @@ -342,7 +345,6 @@ Implementation */ (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ ((unsigned int)'i') << 8 | ((unsigned int)'f')) #define QOI_HEADER_SIZE 14 -#define QOI_PADDING 8 /* 2GB is the max file size that this implementation can safely handle. We guard against anything larger than that, assuming the worst case with 5 bytes per @@ -355,6 +357,8 @@ typedef union { unsigned int v; } qoi_rgba_t; +static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1}; + void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { bytes[(*p)++] = (0xff000000 & v) >> 24; bytes[(*p)++] = (0x00ff0000 & v) >> 16; @@ -390,7 +394,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { max_size = desc->width * desc->height * (desc->channels + 1) + - QOI_HEADER_SIZE + QOI_PADDING; + QOI_HEADER_SIZE + sizeof(qoi_padding); p = 0; bytes = (unsigned char *) QOI_MALLOC(max_size); @@ -495,8 +499,8 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { px_prev = px; } - for (i = 0; i < QOI_PADDING; i++) { - bytes[p++] = 0; + for (i = 0; i < sizeof(qoi_padding); i++) { + bytes[p++] = qoi_padding[i]; } *out_len = p; @@ -515,7 +519,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { if ( data == NULL || desc == NULL || (channels != 0 && channels != 3 && channels != 4) || - size < QOI_HEADER_SIZE + QOI_PADDING + size < QOI_HEADER_SIZE + sizeof(qoi_padding) ) { return NULL; } @@ -554,7 +558,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { px.rgba.b = 0; px.rgba.a = 255; - chunks_len = size - QOI_PADDING; + chunks_len = size - sizeof(qoi_padding); for (px_pos = 0; px_pos < px_len; px_pos += channels) { if (run > 0) { run--; From 4ca3d3ae42638149d241f96d6a7d95b93ffa6ea0 Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Fri, 17 Dec 2021 01:12:20 +0300 Subject: [PATCH 061/208] minor fix for sign-compare warnings. --- qoi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qoi.h b/qoi.h index f83e5d7..1a95442 100644 --- a/qoi.h +++ b/qoi.h @@ -499,7 +499,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { px_prev = px; } - for (i = 0; i < sizeof(qoi_padding); i++) { + for (i = 0; i < (int)sizeof(qoi_padding); i++) { bytes[p++] = qoi_padding[i]; } @@ -519,7 +519,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { if ( data == NULL || desc == NULL || (channels != 0 && channels != 3 && channels != 4) || - size < QOI_HEADER_SIZE + sizeof(qoi_padding) + size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding) ) { return NULL; } @@ -558,7 +558,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { px.rgba.b = 0; px.rgba.a = 255; - chunks_len = size - sizeof(qoi_padding); + chunks_len = size - (int)sizeof(qoi_padding); for (px_pos = 0; px_pos < px_len; px_pos += channels) { if (run > 0) { run--; From 9dee61246f4a2a65595169068c4c23f9c6260c62 Mon Sep 17 00:00:00 2001 From: Cr4xy Date: Sun, 19 Dec 2021 10:54:49 +0100 Subject: [PATCH 062/208] Add Lua implementation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3c2e134..c3692cf 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ implementations listed below. - https://github.com/rbino/qoix (Elixir) - https://github.com/elihwyma/Swift-QOI (Swift) - https://github.com/saharNooby/qoi-java (Java) +- https://github.com/Cr4xy/lua-qoi (Lua) ## Packages From 4bc071df7811c9c42e4a98aca13ac3dd0c98a576 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sun, 19 Dec 2021 23:14:08 +0100 Subject: [PATCH 063/208] Fix bias for run-length in the documentation --- qoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index 1a95442..c9034fc 100644 --- a/qoi.h +++ b/qoi.h @@ -190,7 +190,7 @@ and a bias of 8 for the red and blue channel. 2-bit tag b11 6-bit run-length repeating the previous pixel: 1..62 -The run-length is stored with a bias of 1. Note that the run-lengths 63 and 64 +The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64 (b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and QOI_OP_RGBA tags. From b9a93782235eeb42cc7a1481501a38be2ca70a2b Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 20 Dec 2021 10:52:19 +0100 Subject: [PATCH 064/208] Remove notice about non-final spec --- README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c3692cf..336ada2 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,11 @@ -# QOI - The “Quite OK Image” format for fast, lossless image compression +# QOI - The “Quite OK Image Format” for fast, lossless image compression Single-file MIT licensed library for C/C++ See [qoi.h](https://github.com/phoboslab/qoi/blob/master/qoi.h) for -the documentation. +the documentation and format specification. -More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compression - -⚠️ 2021.11.30 – the file format is not yet finalized. We're still working to fix -some smaller issues. The final specification will be announced on 2021.12.20. -Thanks for your patience! The WIP file format specification can be found in -[qoi.h](https://github.com/phoboslab/qoi/blob/master/qoi.h) +More info at https://qoiformat.org ## Why? @@ -70,4 +65,3 @@ implementations listed below. ## Packages [AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. - From e276f589311d6c25b72252d22d606f49e0b3512b Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 20 Dec 2021 11:38:09 +0100 Subject: [PATCH 065/208] Remove obsolete padding specification; already specified above; #96 --- qoi.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/qoi.h b/qoi.h index c9034fc..ae28362 100644 --- a/qoi.h +++ b/qoi.h @@ -219,13 +219,6 @@ QOI_OP_RGBA tags. 8-bit blue channel value 8-bit alpha channel value - -The byte stream is padded at the end with 8 zero bytes. Since the longest legal -chunk is 5 bytes (QOI_OP_RGBA), with this padding it is possible to check for an -overrun only once per decode loop iteration. These 0x00 bytes also mark the end -of the data stream, as an encoder should never produce 8 consecutive zero bytes -within the stream. - */ From 0f83363f459a82c36d9c109313aa8a4156f9df5c Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Mon, 20 Dec 2021 11:40:39 +0100 Subject: [PATCH 066/208] List languages transpiled from qoi-ci. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 336ada2..44e6c66 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ implementations listed below. - https://github.com/xfmoulet/qoi (Go) - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/0xd34df00d/hsqoi (Haskell) -- https://github.com/pfusik/qoi-ci (Ć) +- https://github.com/pfusik/qoi-ci (Ć, transpiled to C, C++, C#, Java, JavaScript, Python and Swift) - https://github.com/kodonnell/qoi (Python) - https://github.com/NUlliiON/QoiSharp (C#) - https://github.com/rbino/qoix (Elixir) From 013cfa1ecddc7cd6d3236ca55d2ef7a2bd31a686 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 20 Dec 2021 11:47:04 +0100 Subject: [PATCH 067/208] Wording --- qoi.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/qoi.h b/qoi.h index ae28362..46befc6 100644 --- a/qoi.h +++ b/qoi.h @@ -28,12 +28,9 @@ SOFTWARE. -- About -QOI encodes and decodes images in a lossless format. An encoded QOI image is -usually around 10--30% larger than a decently optimized PNG image. - -QOI outperforms simpler PNG encoders in compression ratio and performance. QOI -images are typically 20% smaller than PNGs written with stbi_image. Encoding is -25-50x faster and decoding is 3-4x faster than stbi_image or libpng. +QOI encodes and decodes images in a lossless format. Compared to stb_image and +stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and +20% better compression. -- Synopsis From f752c1a9789ec2b44b977cbb196fde5ab8060997 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 20 Dec 2021 11:56:19 +0100 Subject: [PATCH 068/208] Clarify pixel order and image completeness --- qoi.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qoi.h b/qoi.h index 46befc6..491c6f3 100644 --- a/qoi.h +++ b/qoi.h @@ -91,8 +91,11 @@ struct qoi_header_t { uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear }; -The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous -pixel value. Pixels are either encoded as +Images are encoded from top to bottom, left to right. The decoder and encoder +start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An image is +complete when all pixels specified by width * height have been covered. + +Pixels are encoded as - a run of the previous pixel - an index into an array of previously seen pixels - a difference to the previous pixel value in r,g,b From 438c1e918f73911425e44588c171287b81686c87 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 20 Dec 2021 16:15:44 +0100 Subject: [PATCH 069/208] Fix typo in documentation --- qoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index 491c6f3..5e31076 100644 --- a/qoi.h +++ b/qoi.h @@ -169,7 +169,7 @@ Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as 4-bit blue channel difference minus green channel difference -8..7 The green channel is used to indicate the general direction of change and is -encoded in 6 bits. The red and green channels (dr and db) base their diffs off +encoded in 6 bits. The red and blue channels (dr and db) base their diffs off of the green channel difference and are encoded in 4 bits. I.e.: dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g) db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g) From 71ff2ac961c1424116112844f82815935c95c9f6 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 20 Dec 2021 17:26:18 +0100 Subject: [PATCH 070/208] Add plugins for GIMP, Paint.NET and XnView MP --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 44e6c66..91b10fb 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ implementations listed below. ## Tools -- https://github.com/floooh/qoiview +- https://github.com/floooh/qoiview - native QOI viewer +- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.0.0 - QOI Plugin installer for GIMP, Paint.NET and XnView MP ## Implementations of QOI From 31f6fd3ca564124689dac3a390e198c96ca4ec8d Mon Sep 17 00:00:00 2001 From: iOrange Date: Mon, 20 Dec 2021 13:31:57 -0500 Subject: [PATCH 071/208] + added mention of the very first Paint.NET file type plugin that adds the ability to Load/Save QOI images --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 91b10fb..c522bd8 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,8 @@ implementations listed below. ## Tools - https://github.com/floooh/qoiview - native QOI viewer -- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.0.0 - QOI Plugin installer for GIMP, Paint.NET and XnView MP +- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.0.0 - QOI Plugin installer for GIMP, Paint.NET and XnView MP +- https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET ## Implementations of QOI From 3cffa33c45fdec5b3a7e271d84b0efdd8a868124 Mon Sep 17 00:00:00 2001 From: superzazu Date: Mon, 20 Dec 2021 22:35:13 +0100 Subject: [PATCH 072/208] Add C bindings (SDL2) link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c522bd8..d242c2f 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ implementations listed below. - https://github.com/elihwyma/Swift-QOI (Swift) - https://github.com/saharNooby/qoi-java (Java) - https://github.com/Cr4xy/lua-qoi (Lua) +- https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) ## Packages From e0e21e92fbdc2b6f3d8754dba2cc7efbb086c056 Mon Sep 17 00:00:00 2001 From: Tiago Filipe Silva Date: Tue, 21 Dec 2021 09:41:33 +0000 Subject: [PATCH 073/208] Replace printf() with puts() The format strings are absent in these stdout prints. They can safely be replaced with calls to _puts()_ and shave also the newlines. --- qoiconv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qoiconv.c b/qoiconv.c index f22513c..79aa75c 100644 --- a/qoiconv.c +++ b/qoiconv.c @@ -48,10 +48,10 @@ SOFTWARE. int main(int argc, char **argv) { if (argc < 3) { - printf("Usage: qoiconv \n"); - printf("Examples:\n"); - printf(" qoiconv input.png output.qoi\n"); - printf(" qoiconv input.qoi output.png\n"); + puts("Usage: qoiconv "); + puts("Examples:"); + puts(" qoiconv input.png output.qoi"); + puts(" qoiconv input.qoi output.png"); exit(1); } From c194b955d868cec9a05c42b1ac8d9bf969b77138 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 21 Dec 2021 17:13:11 +0100 Subject: [PATCH 074/208] Split implementations list based on spec conformance; #104 --- README.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d242c2f..a248474 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,22 @@ implementations listed below. - https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET -## Implementations of QOI +## Implementations & Bindings of QOI + +- https://github.com/pfusik/qoi-ci (Ć, transpiled to C, C++, C#, Java, JavaScript, Python and Swift) +- https://github.com/kodonnell/qoi (Python) +- https://github.com/Cr4xy/lua-qoi (Lua) +- https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) + + +## Packages + +[AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. + + +## Implementations not yet conforming to the final specification + +These implementations are based on the pre-release version of QOI. Resulting files are not compatible with the current version. - https://github.com/MasterQ32/zig-qoi (Zig) - https://github.com/steven-joruk/qoi (Rust) @@ -55,16 +70,8 @@ implementations listed below. - https://github.com/xfmoulet/qoi (Go) - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/0xd34df00d/hsqoi (Haskell) -- https://github.com/pfusik/qoi-ci (Ć, transpiled to C, C++, C#, Java, JavaScript, Python and Swift) -- https://github.com/kodonnell/qoi (Python) - https://github.com/NUlliiON/QoiSharp (C#) - https://github.com/rbino/qoix (Elixir) - https://github.com/elihwyma/Swift-QOI (Swift) - https://github.com/saharNooby/qoi-java (Java) -- https://github.com/Cr4xy/lua-qoi (Lua) -- https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) - -## Packages - -[AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. From 63f43a9fc11c64333618d4cf400b3c3329df8c9d Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 21 Dec 2021 17:51:08 +0100 Subject: [PATCH 075/208] qoi-java is now spec conforming; close #110 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a248474..26bf102 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ implementations listed below. - https://github.com/kodonnell/qoi (Python) - https://github.com/Cr4xy/lua-qoi (Lua) - https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) +- https://github.com/saharNooby/qoi-java (Java) ## Packages @@ -73,5 +74,4 @@ These implementations are based on the pre-release version of QOI. Resulting fil - https://github.com/NUlliiON/QoiSharp (C#) - https://github.com/rbino/qoix (Elixir) - https://github.com/elihwyma/Swift-QOI (Swift) -- https://github.com/saharNooby/qoi-java (Java) From 52051a310f5a98d5f182d2ce982592565cedfb23 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 21 Dec 2021 18:05:30 +0100 Subject: [PATCH 076/208] Documentation: clarify alpha handling for certain chunk types; close #105 --- qoi.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qoi.h b/qoi.h index 5e31076..ab9d458 100644 --- a/qoi.h +++ b/qoi.h @@ -156,6 +156,8 @@ so "1 - 2" will result in 255, while "255 + 1" will result in 0. Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as 0 (b00). 1 is stored as 3 (b11). +The alpha value remains unchanged from the previous pixel. + .- QOI_OP_LUMA -------------------------------------. | Byte[0] | Byte[1] | @@ -180,6 +182,8 @@ so "10 - 13" will result in 253, while "250 + 7" will result in 1. Values are stored as unsigned integers with a bias of 32 for the green channel and a bias of 8 for the red and blue channel. +The alpha value remains unchanged from the previous pixel. + .- QOI_OP_RUN ------------. | Byte[0] | @@ -206,6 +210,8 @@ QOI_OP_RGBA tags. 8-bit green channel value 8-bit blue channel value +The alpha value remains unchanged from the previous pixel. + .- QOI_OP_RGBA ---------------------------------------------------. | Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] | From d00620275240532a4ace669fe26953cf8f28671c Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 21 Dec 2021 19:21:21 +0100 Subject: [PATCH 077/208] Mention support in SerenityOS; close #109 --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 26bf102..dfb89ad 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,11 @@ implementations listed below. - https://github.com/saharNooby/qoi-java (Java) +## QOI Support in Other Software + +- [SerenityOS](https://github.com/SerenityOS/serenity) supports decoding QOI system wide through a custom [cpp implementation in LibGfx](https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibGfx/QOILoader.h) + + ## Packages [AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. From 4d20da32823b6bdab9d02e85500962fa5553dd24 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 21 Dec 2021 19:22:43 +0100 Subject: [PATCH 078/208] Add QOI Logo --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dfb89ad..3014b96 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![QOI Logo](https://qoiformat.org/qoi-logo.svg) + # QOI - The “Quite OK Image Format” for fast, lossless image compression Single-file MIT licensed library for C/C++ From 0d2e27d3ea438a651d16f64078f4e4cd47edecee Mon Sep 17 00:00:00 2001 From: GithubPrankster <33995085+GithubPrankster@users.noreply.github.com> Date: Tue, 21 Dec 2021 17:57:07 -0300 Subject: [PATCH 079/208] Added Raylib under `QOI Support in Other Software`. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3014b96..88e2614 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ implementations listed below. ## QOI Support in Other Software - [SerenityOS](https://github.com/SerenityOS/serenity) supports decoding QOI system wide through a custom [cpp implementation in LibGfx](https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibGfx/QOILoader.h) +- [Raylib](https://github.com/raysan5/raylib) supports decoding and encoding QOI textures through its [rtextures module](https://github.com/raysan5/raylib/blob/master/src/rtextures.c) ## Packages From 17dffb408c1e84b7c2d2ab7cff91fb2836ca51ca Mon Sep 17 00:00:00 2001 From: Riccardo Binetti Date: Wed, 22 Dec 2021 02:34:48 +0100 Subject: [PATCH 080/208] Move Qoix to the implementations section Qoix is now aligned with v1.0 spec, see https://github.com/rbino/qoix/pull/2/files --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88e2614..8bc2204 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ implementations listed below. - https://github.com/Cr4xy/lua-qoi (Lua) - https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) - https://github.com/saharNooby/qoi-java (Java) +- https://github.com/rbino/qoix (Elixir) ## QOI Support in Other Software @@ -80,6 +81,5 @@ These implementations are based on the pre-release version of QOI. Resulting fil - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/0xd34df00d/hsqoi (Haskell) - https://github.com/NUlliiON/QoiSharp (C#) -- https://github.com/rbino/qoix (Elixir) - https://github.com/elihwyma/Swift-QOI (Swift) From 54e77bf164c0f642a950dcb74dcfd4bd94efcf75 Mon Sep 17 00:00:00 2001 From: Oldes Date: Wed, 22 Dec 2021 11:12:47 +0100 Subject: [PATCH 081/208] Mention support in Rebol3; close #58 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 88e2614..f7ac2ae 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ implementations listed below. - [SerenityOS](https://github.com/SerenityOS/serenity) supports decoding QOI system wide through a custom [cpp implementation in LibGfx](https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibGfx/QOILoader.h) - [Raylib](https://github.com/raysan5/raylib) supports decoding and encoding QOI textures through its [rtextures module](https://github.com/raysan5/raylib/blob/master/src/rtextures.c) +- [Rebol3](https://github.com/Oldes/Rebol3/issues/39) supports decoding and encoding QOI using a native codec ## Packages From b58a0a28c005fcf1d53630f1868127086f690678 Mon Sep 17 00:00:00 2001 From: Eugene Antonov Date: Wed, 22 Dec 2021 10:08:40 -0600 Subject: [PATCH 082/208] Move QoiSharp to the implementations section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88e2614..1ced3c9 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ implementations listed below. - https://github.com/Cr4xy/lua-qoi (Lua) - https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) - https://github.com/saharNooby/qoi-java (Java) +- https://github.com/NUlliiON/QoiSharp (C#) ## QOI Support in Other Software @@ -79,7 +80,6 @@ These implementations are based on the pre-release version of QOI. Resulting fil - https://github.com/xfmoulet/qoi (Go) - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/0xd34df00d/hsqoi (Haskell) -- https://github.com/NUlliiON/QoiSharp (C#) - https://github.com/rbino/qoix (Elixir) - https://github.com/elihwyma/Swift-QOI (Swift) From 9c720cc6823386c20d15efe7c1b82e8c7c48b771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Wed, 22 Dec 2021 19:56:52 +0100 Subject: [PATCH 083/208] Zig implementation is now spec complete. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88e2614..70e8cb6 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ implementations listed below. - https://github.com/Cr4xy/lua-qoi (Lua) - https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) - https://github.com/saharNooby/qoi-java (Java) +- https://github.com/MasterQ32/zig-qoi (Zig) ## QOI Support in Other Software @@ -72,7 +73,6 @@ implementations listed below. These implementations are based on the pre-release version of QOI. Resulting files are not compatible with the current version. -- https://github.com/MasterQ32/zig-qoi (Zig) - https://github.com/steven-joruk/qoi (Rust) - https://github.com/ChevyRay/qoi_rs (Rust) - https://github.com/zakarumych/rapid-qoi (Rust) From 45bc32524a4b5b7842c28d34809d78b6ec3d5d78 Mon Sep 17 00:00:00 2001 From: Zakarum Date: Thu, 23 Dec 2021 00:21:46 +0300 Subject: [PATCH 084/208] rapid-qoi is now spec complete --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88e2614..2e50e2b 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ implementations listed below. - https://github.com/Cr4xy/lua-qoi (Lua) - https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) - https://github.com/saharNooby/qoi-java (Java) +- https://github.com/zakarumych/rapid-qoi (Rust) ## QOI Support in Other Software @@ -75,7 +76,6 @@ These implementations are based on the pre-release version of QOI. Resulting fil - https://github.com/MasterQ32/zig-qoi (Zig) - https://github.com/steven-joruk/qoi (Rust) - https://github.com/ChevyRay/qoi_rs (Rust) -- https://github.com/zakarumych/rapid-qoi (Rust) - https://github.com/xfmoulet/qoi (Go) - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/0xd34df00d/hsqoi (Haskell) From 56c2272dbe5ee712117a468b362123e62f0f5243 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 23 Dec 2021 11:45:19 +0100 Subject: [PATCH 085/208] Clarify pixel ordering; close #120 --- qoi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qoi.h b/qoi.h index ab9d458..b733bb2 100644 --- a/qoi.h +++ b/qoi.h @@ -91,9 +91,9 @@ struct qoi_header_t { uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear }; -Images are encoded from top to bottom, left to right. The decoder and encoder -start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An image is -complete when all pixels specified by width * height have been covered. +Images are encoded row by row, left to right, top to bottom. The decoder and +encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An +image is complete when all pixels specified by width * height have been covered. Pixels are encoded as - a run of the previous pixel From 19e118d78edbe2d95a913d566bcf9675dcf74e8b Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 23 Dec 2021 11:52:24 +0100 Subject: [PATCH 086/208] Mention QOI thumbnail provider; #123 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f5586b3..bf86a29 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ implementations listed below. - https://github.com/floooh/qoiview - native QOI viewer - https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.0.0 - QOI Plugin installer for GIMP, Paint.NET and XnView MP - https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET +- https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer ## Implementations & Bindings of QOI From f0d532c2f1cd1775eeca17e65faa056d0e587f33 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 23 Dec 2021 11:58:21 +0100 Subject: [PATCH 087/208] Mention takeyourhatoff/qoi; close #126 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bf86a29..3b38aef 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ implementations listed below. - https://github.com/rbino/qoix (Elixir) - https://github.com/NUlliiON/QoiSharp (C#) - https://github.com/zakarumych/rapid-qoi (Rust) +- https://github.com/takeyourhatoff/qoi (Go) ## QOI Support in Other Software From 9f38cffd968454be88df55aad0b4e30b4d21d5a8 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 23 Dec 2021 12:00:35 +0100 Subject: [PATCH 088/208] Mention DosWorld/pasqoi; close #92 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3b38aef..dfa2098 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ implementations listed below. - https://github.com/NUlliiON/QoiSharp (C#) - https://github.com/zakarumych/rapid-qoi (Rust) - https://github.com/takeyourhatoff/qoi (Go) +- https://github.com/DosWorld/pasqoi (Pascal) ## QOI Support in Other Software From bdcaaa1fb9bf554c218e9dc8fd4720f68ba4b8c7 Mon Sep 17 00:00:00 2001 From: Amy While <26681721+elihwyma@users.noreply.github.com> Date: Thu, 23 Dec 2021 16:53:32 +0000 Subject: [PATCH 089/208] Update Swift-QOI bindings --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dfa2098..6bf77d8 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ implementations listed below. - https://github.com/zakarumych/rapid-qoi (Rust) - https://github.com/takeyourhatoff/qoi (Go) - https://github.com/DosWorld/pasqoi (Pascal) +- https://github.com/elihwyma/Swift-QOI (Swift) ## QOI Support in Other Software @@ -85,5 +86,4 @@ These implementations are based on the pre-release version of QOI. Resulting fil - https://github.com/xfmoulet/qoi (Go) - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/0xd34df00d/hsqoi (Haskell) -- https://github.com/elihwyma/Swift-QOI (Swift) From 193862433a738d5756bf1bb8dcf3dabd5f144fc0 Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Thu, 23 Dec 2021 21:24:14 +0100 Subject: [PATCH 090/208] Mention Imagine plugin. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bf77d8..f8a5242 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ implementations listed below. ## Tools - https://github.com/floooh/qoiview - native QOI viewer -- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.0.0 - QOI Plugin installer for GIMP, Paint.NET and XnView MP +- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.1.0 - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView MP - https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET - https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer From f9954f5b4bc535cdd71109354df8ebeeb18280b1 Mon Sep 17 00:00:00 2001 From: Valtteri Koskivuori Date: Thu, 23 Dec 2021 23:19:34 +0200 Subject: [PATCH 091/208] Mention support in the c-ray rendering engine --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f8a5242..fa6718c 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ implementations listed below. - [SerenityOS](https://github.com/SerenityOS/serenity) supports decoding QOI system wide through a custom [cpp implementation in LibGfx](https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibGfx/QOILoader.h) - [Raylib](https://github.com/raysan5/raylib) supports decoding and encoding QOI textures through its [rtextures module](https://github.com/raysan5/raylib/blob/master/src/rtextures.c) - [Rebol3](https://github.com/Oldes/Rebol3/issues/39) supports decoding and encoding QOI using a native codec +- [c-ray](https://github.com/vkoskiv/c-ray) supports QOI natively ## Packages From 69b6085d87110fe5ae0d4acf7d95669534b348b1 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Fri, 24 Dec 2021 00:12:53 +0100 Subject: [PATCH 092/208] Mention xfmoulet/qoi in spec confirming; close #130 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa6718c..2db239d 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ implementations listed below. - https://github.com/takeyourhatoff/qoi (Go) - https://github.com/DosWorld/pasqoi (Pascal) - https://github.com/elihwyma/Swift-QOI (Swift) +- https://github.com/xfmoulet/qoi (Go) ## QOI Support in Other Software @@ -84,7 +85,6 @@ These implementations are based on the pre-release version of QOI. Resulting fil - https://github.com/steven-joruk/qoi (Rust) - https://github.com/ChevyRay/qoi_rs (Rust) -- https://github.com/xfmoulet/qoi (Go) - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/0xd34df00d/hsqoi (Haskell) From 61306d7ecd08a971baa18a56654b03c3372da969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20B=C3=BCnzli?= Date: Fri, 24 Dec 2021 00:17:57 +0100 Subject: [PATCH 093/208] Mention qoic, an OCaml implementation. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2db239d..ef968f9 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ implementations listed below. - https://github.com/DosWorld/pasqoi (Pascal) - https://github.com/elihwyma/Swift-QOI (Swift) - https://github.com/xfmoulet/qoi (Go) +- https://erratique.ch/software/qoic (OCaml) ## QOI Support in Other Software From b060b961e8d12bf49e05f10626b806101b165f95 Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Sat, 25 Dec 2021 16:43:37 +0100 Subject: [PATCH 094/208] Style: Remove trailing whitespaces --- qoi.h | 94 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/qoi.h b/qoi.h index b733bb2..8d8747e 100644 --- a/qoi.h +++ b/qoi.h @@ -28,8 +28,8 @@ SOFTWARE. -- About -QOI encodes and decodes images in a lossless format. Compared to stb_image and -stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and +QOI encodes and decodes images in a lossless format. Compared to stb_image and +stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and 20% better compression. @@ -45,7 +45,7 @@ stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and // the input pixel data. qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){ .width = 1920, - .height = 1080, + .height = 1080, .channels = 4, .colorspace = QOI_SRGB }); @@ -74,7 +74,7 @@ QOI_NO_STDIO before including this library. This library uses malloc() and free(). To supply your own malloc implementation you can define QOI_MALLOC and QOI_FREE before including this library. -This library uses memset() to zero-initialize the index. To supply your own +This library uses memset() to zero-initialize the index. To supply your own implementation you can define QOI_ZEROARR before including this library. @@ -91,8 +91,8 @@ struct qoi_header_t { uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear }; -Images are encoded row by row, left to right, top to bottom. The decoder and -encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An +Images are encoded row by row, left to right, top to bottom. The decoder and +encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An image is complete when all pixels specified by width * height have been covered. Pixels are encoded as @@ -101,20 +101,20 @@ Pixels are encoded as - a difference to the previous pixel value in r,g,b - full r,g,b or r,g,b,a values -The color channels are assumed to not be premultiplied with the alpha channel +The color channels are assumed to not be premultiplied with the alpha channel ("un-premultiplied alpha"). -A running array[64] (zero-initialized) of previously seen pixel values is +A running array[64] (zero-initialized) of previously seen pixel values is maintained by the encoder and decoder. Each pixel that is seen by the encoder and decoder is put into this array at the position formed by a hash function of the color value. In the encoder, if the pixel value at the index matches the -current pixel, this index position is written to the stream as QOI_OP_INDEX. +current pixel, this index position is written to the stream as QOI_OP_INDEX. The hash function for the index is: index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 -Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The -bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All +Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The +bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All values encoded in these data bits have the most significant bit on the left. The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the @@ -136,10 +136,10 @@ The possible chunks are: 6-bit index into the color index array: 0..63 A valid encoder must not issue 7 or more consecutive QOI_OP_INDEX chunks to the -index 0, to avoid confusion with the 8 byte end marker. +index 0, to avoid confusion with the 8 byte end marker. -.- QOI_OP_DIFF -----------. +.- QOI_OP_DIFF -----------. | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----+-----+-----| @@ -150,16 +150,16 @@ index 0, to avoid confusion with the 8 byte end marker. 2-bit green channel difference from the previous pixel between -2..1 2-bit blue channel difference from the previous pixel between -2..1 -The difference to the current channel values are using a wraparound operation, +The difference to the current channel values are using a wraparound operation, so "1 - 2" will result in 255, while "255 + 1" will result in 0. -Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as +Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as 0 (b00). 1 is stored as 3 (b11). The alpha value remains unchanged from the previous pixel. -.- QOI_OP_LUMA -------------------------------------. +.- QOI_OP_LUMA -------------------------------------. | Byte[0] | Byte[1] | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | |-------+-----------------+-------------+-----------| @@ -170,16 +170,16 @@ The alpha value remains unchanged from the previous pixel. 4-bit red channel difference minus green channel difference -8..7 4-bit blue channel difference minus green channel difference -8..7 -The green channel is used to indicate the general direction of change and is +The green channel is used to indicate the general direction of change and is encoded in 6 bits. The red and blue channels (dr and db) base their diffs off of the green channel difference and are encoded in 4 bits. I.e.: dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g) db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g) -The difference to the current channel values are using a wraparound operation, +The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while "250 + 7" will result in 1. -Values are stored as unsigned integers with a bias of 32 for the green channel +Values are stored as unsigned integers with a bias of 32 for the green channel and a bias of 8 for the red and blue channel. The alpha value remains unchanged from the previous pixel. @@ -194,8 +194,8 @@ The alpha value remains unchanged from the previous pixel. 2-bit tag b11 6-bit run-length repeating the previous pixel: 1..62 -The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64 -(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and +The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64 +(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and QOI_OP_RGBA tags. @@ -238,15 +238,15 @@ Header - Public functions */ extern "C" { #endif -/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions. -It describes either the input format (for qoi_write and qoi_encode), or is +/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions. +It describes either the input format (for qoi_write and qoi_encode), or is filled with the description read from the file header (for qoi_read and qoi_decode). -The colorspace in this qoi_desc is an enum where +The colorspace in this qoi_desc is an enum where 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel 1 = all channels are linear -You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely +You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely informative. It will be saved to the file header, but does not affect en-/decoding in any way. */ @@ -262,11 +262,11 @@ typedef struct { #ifndef QOI_NO_STDIO -/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file -system. The qoi_desc struct must be filled with the image width, height, -number of channels (3 = RGB, 4 = RGBA) and the colorspace. +/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file +system. The qoi_desc struct must be filled with the image width, height, +number of channels (3 = RGB, 4 = RGBA) and the colorspace. -The function returns 0 on failure (invalid parameters, or fopen or malloc +The function returns 0 on failure (invalid parameters, or fopen or malloc failed) or the number of bytes written on success. */ int qoi_write(const char *filename, const void *data, const qoi_desc *desc); @@ -277,7 +277,7 @@ number of channels from the file header is used. If channels is 3 or 4 the output format will be forced into this number of channels. The function either returns NULL on failure (invalid data, or malloc or fopen -failed) or a pointer to the decoded pixels. On success, the qoi_desc struct +failed) or a pointer to the decoded pixels. On success, the qoi_desc struct will be filled with the description from the file header. The returned pixel data should be free()d after use. */ @@ -289,8 +289,8 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels); /* Encode raw RGB or RGBA pixels into a QOI image in memory. -The function either returns NULL on failure (invalid parameters or malloc -failed) or a pointer to the encoded data on success. On success the out_len +The function either returns NULL on failure (invalid parameters or malloc +failed) or a pointer to the encoded data on success. On success the out_len is set to the size in bytes of the encoded data. The returned qoi data should be free()d after use. */ @@ -300,8 +300,8 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len); /* Decode a QOI image from memory. -The function either returns NULL on failure (invalid parameters or malloc -failed) or a pointer to the decoded pixels. On success, the qoi_desc struct +The function either returns NULL on failure (invalid parameters or malloc +failed) or a pointer to the decoded pixels. On success, the qoi_desc struct is filled with the description from the file header. The returned pixel data should be free()d after use. */ @@ -346,8 +346,8 @@ Implementation */ #define QOI_HEADER_SIZE 14 /* 2GB is the max file size that this implementation can safely handle. We guard -against anything larger than that, assuming the worst case with 5 bytes per -pixel, rounded down to a nice clean value. 400 million pixels ought to be +against anything larger than that, assuming the worst case with 5 bytes per +pixel, rounded down to a nice clean value. 400 million pixels ought to be enough for anybody. */ #define QOI_PIXELS_MAX ((unsigned int)400000000) @@ -391,8 +391,8 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { return NULL; } - max_size = - desc->width * desc->height * (desc->channels + 1) + + max_size = + desc->width * desc->height * (desc->channels + 1) + QOI_HEADER_SIZE + sizeof(qoi_padding); p = 0; @@ -418,7 +418,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { px_prev.rgba.b = 0; px_prev.rgba.a = 255; px = px_prev; - + px_len = desc->width * desc->height * desc->channels; px_end = px_len - desc->channels; channels = desc->channels; @@ -466,14 +466,14 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { if ( vr > -3 && vr < 2 && - vg > -3 && vg < 2 && + vg > -3 && vg < 2 && vb > -3 && vb < 2 ) { bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2); } else if ( - vg_r > -9 && vg_r < 8 && - vg > -33 && vg < 32 && + vg_r > -9 && vg_r < 8 && + vg > -33 && vg < 32 && vg_b > -9 && vg_b < 8 ) { bytes[p++] = QOI_OP_LUMA | (vg + 32); @@ -532,7 +532,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { desc->colorspace = bytes[p++]; if ( - desc->width == 0 || desc->height == 0 || + desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || desc->colorspace > 1 || header_magic != QOI_MAGIC || @@ -598,7 +598,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { index[QOI_COLOR_HASH(px) % 64] = px; } - if (channels == 4) { + if (channels == 4) { *(qoi_rgba_t*)(pixels + px_pos) = px; } else { @@ -627,11 +627,11 @@ int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { if (!encoded) { fclose(f); return 0; - } - + } + fwrite(encoded, 1, size, f); fclose(f); - + QOI_FREE(encoded); return size; } From da1070a234c2793125e606e4e3ba355fbc25aa87 Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Sat, 25 Dec 2021 16:37:28 +0100 Subject: [PATCH 095/208] Fix missing prototypes warning Functions qoi_write_32 and qoi_read_32 should either have a prototype or be declared static. Do the latter, as theses functions are defined in a header and should only exists in the same compilation unit as the file including qoi.h and defining QOI_IMPLEMENTATION, ie not exported. By adding the static keyword in the function declaration the following command doesn't raise a missing-prototypes warning, anymore: cc -Wmissing-prototypes -DQOI_IMPLEMENTATION -c -o qoi.o -xc qoi.h --- qoi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qoi.h b/qoi.h index b733bb2..fdb3276 100644 --- a/qoi.h +++ b/qoi.h @@ -358,14 +358,14 @@ typedef union { static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1}; -void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { +static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { bytes[(*p)++] = (0xff000000 & v) >> 24; bytes[(*p)++] = (0x00ff0000 & v) >> 16; bytes[(*p)++] = (0x0000ff00 & v) >> 8; bytes[(*p)++] = (0x000000ff & v); } -unsigned int qoi_read_32(const unsigned char *bytes, int *p) { +static unsigned int qoi_read_32(const unsigned char *bytes, int *p) { unsigned int a = bytes[(*p)++]; unsigned int b = bytes[(*p)++]; unsigned int c = bytes[(*p)++]; From c3002a4d708e6b638268044e048573263f39190d Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Sat, 25 Dec 2021 22:47:44 -0500 Subject: [PATCH 096/208] spelling: head Signed-off-by: Josh Soref --- qoibench.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qoibench.c b/qoibench.c index 27f1ec3..c14fcdc 100644 --- a/qoibench.c +++ b/qoibench.c @@ -545,14 +545,14 @@ void benchmark_directory(const char *path, benchmark_result_t *grand_total) { benchmark_result_t dir_total = {0}; - int has_shown_heaad = 0; + int has_shown_head = 0; for (int i = 0; (file = readdir(dir)) != NULL; i++) { if (strcmp(file->d_name + strlen(file->d_name) - 4, ".png") != 0) { continue; } - if (!has_shown_heaad) { - has_shown_heaad = 1; + if (!has_shown_head) { + has_shown_head = 1; printf("## Benchmarking %s/*.png -- %d runs\n\n", path, opt_runs); } From 2fff02391296a916caa9b64604b7db5e41683ce5 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Sat, 25 Dec 2021 22:47:45 -0500 Subject: [PATCH 097/208] spelling: ignore Signed-off-by: Josh Soref --- qoibench.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoibench.c b/qoibench.c index c14fcdc..dcee66e 100644 --- a/qoibench.c +++ b/qoibench.c @@ -196,7 +196,7 @@ void png_decode_callback(png_structp png, png_bytep data, png_size_t length) { } void png_warning_callback(png_structp png_ptr, png_const_charp warning_msg) { - // Ingore warnings about sRGB profiles and such. + // Ignore warnings about sRGB profiles and such. } void *libpng_decode(void *data, int size, int *out_w, int *out_h) { From 446d5e700873665aa9453dd079e0cf230e5558c8 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Sat, 25 Dec 2021 22:47:45 -0500 Subject: [PATCH 098/208] spelling: measure Signed-off-by: Josh Soref --- qoibench.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoibench.c b/qoibench.c index dcee66e..f5224ea 100644 --- a/qoibench.c +++ b/qoibench.c @@ -388,7 +388,7 @@ void benchmark_print_result(benchmark_result_t res) { printf("\n"); } -// Run __VA_ARGS__ a number of times and meassure the time taken. The first +// Run __VA_ARGS__ a number of times and measure the time taken. The first // run is ignored. #define BENCHMARK_FN(NOWARMUP, RUNS, AVG_TIME, ...) \ do { \ From b3f738a20476af60a2f65c2b7ab56a1a4930176c Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Sat, 25 Dec 2021 22:47:46 -0500 Subject: [PATCH 099/208] spelling: mismatch Signed-off-by: Josh Soref --- qoibench.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoibench.c b/qoibench.c index f5224ea..2c3df68 100644 --- a/qoibench.c +++ b/qoibench.c @@ -440,7 +440,7 @@ benchmark_result_t benchmark_image(const char *path) { qoi_desc dc; void *pixels_qoi = qoi_decode(encoded_qoi, encoded_qoi_size, &dc, channels); if (memcmp(pixels, pixels_qoi, w * h * channels) != 0) { - ERROR("QOI roundtrip pixel missmatch for %s", path); + ERROR("QOI roundtrip pixel mismatch for %s", path); } free(pixels_qoi); } From 03d6e36d5d23e41996b0a9e87bd6ae41e712f70a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Sun, 26 Dec 2021 17:41:44 +0100 Subject: [PATCH 100/208] Add link to tev image viewer to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ef968f9..702c342 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ implementations listed below. - https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.1.0 - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView MP - https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET - https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer +- https://github.com/Tom94/tev - another native QOI viewer (allows pixel peeping and comparison with other image formats) ## Implementations & Bindings of QOI From 96bfc2dbeb28bc760564732cf6946e425af17ee8 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshev Date: Mon, 27 Dec 2021 14:44:40 +0300 Subject: [PATCH 101/208] Added link to SAIL in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 702c342..4778e1d 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ implementations listed below. - [Raylib](https://github.com/raysan5/raylib) supports decoding and encoding QOI textures through its [rtextures module](https://github.com/raysan5/raylib/blob/master/src/rtextures.c) - [Rebol3](https://github.com/Oldes/Rebol3/issues/39) supports decoding and encoding QOI using a native codec - [c-ray](https://github.com/vkoskiv/c-ray) supports QOI natively +- [SAIL](https://github.com/HappySeaFox/sail) image decoding library, supports decoding and encoding QOI images ## Packages From a3d43a850a2174a52d8d82e505f39c5d82e56f60 Mon Sep 17 00:00:00 2001 From: Kevin Chapelier Date: Mon, 27 Dec 2021 16:36:37 +0100 Subject: [PATCH 102/208] Add vanilla js implementation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 702c342..3d887a8 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ implementations listed below. - https://github.com/elihwyma/Swift-QOI (Swift) - https://github.com/xfmoulet/qoi (Go) - https://erratique.ch/software/qoic (OCaml) +- https://github.com/kchapelier/qoijs (JavaScript) ## QOI Support in Other Software From a810fc07622932c8d6aea5abbdf85c6cc0950d77 Mon Sep 17 00:00:00 2001 From: Arian Stolwijk Date: Tue, 28 Dec 2021 13:00:39 +0100 Subject: [PATCH 103/208] Add link to another Go implementation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 702c342..6873aa2 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ implementations listed below. - https://github.com/elihwyma/Swift-QOI (Swift) - https://github.com/xfmoulet/qoi (Go) - https://erratique.ch/software/qoic (OCaml) +- https://github.com/arian/go-qoi (Go) ## QOI Support in Other Software From 05bf89291be7d54f7b91654c749573f82573e588 Mon Sep 17 00:00:00 2001 From: Lucas Menezes Date: Thu, 30 Dec 2021 13:57:58 +0000 Subject: [PATCH 104/208] Added qoiConverter X --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 702c342..3267130 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ implementations listed below. - https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET - https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer - https://github.com/Tom94/tev - another native QOI viewer (allows pixel peeping and comparison with other image formats) +- https://apps.apple.com/us/app/qoiconverterx/id1602159820 QOI <=> PNG converter available on the Mac App Store ## Implementations & Bindings of QOI From 9ad3e1d7b4fed6ddafdbd25e089aeafb991e4dd9 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 31 Dec 2021 12:35:45 +0100 Subject: [PATCH 105/208] add a link to the Julia bindings --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 702c342..2342373 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ implementations listed below. - https://github.com/elihwyma/Swift-QOI (Swift) - https://github.com/xfmoulet/qoi (Go) - https://erratique.ch/software/qoic (OCaml) +- https://github.com/KristofferC/QOI.jl (Julia) ## QOI Support in Other Software From 48375ec75a0da30c5a82be65381ac1bb4d3f2f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Masseix?= Date: Fri, 31 Dec 2021 14:34:55 +0100 Subject: [PATCH 106/208] Add PHP implementation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 702c342..807f123 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ implementations listed below. - https://github.com/elihwyma/Swift-QOI (Swift) - https://github.com/xfmoulet/qoi (Go) - https://erratique.ch/software/qoic (OCaml) - +- https://github.com/MKCG/php-qoi (PHP) ## QOI Support in Other Software From cf918138cf91f334a67408794b29703c4e7f6c64 Mon Sep 17 00:00:00 2001 From: Dimitri Belopopsky Date: Sat, 1 Jan 2022 14:18:54 +0100 Subject: [PATCH 107/208] Add C++ implementation to the README list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 702c342..be4f195 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ implementations listed below. - https://github.com/elihwyma/Swift-QOI (Swift) - https://github.com/xfmoulet/qoi (Go) - https://erratique.ch/software/qoic (OCaml) +- https://github.com/shadowMitia/libqoi/ (C++) ## QOI Support in Other Software From a4ea2819c4bcba1d5ee1aba30c8d13676810fb35 Mon Sep 17 00:00:00 2001 From: contriteobserver Date: Sun, 2 Jan 2022 01:26:42 -0800 Subject: [PATCH 108/208] fixed gcc warnings in qoibench.c addresses issue #155 --- qoibench.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qoibench.c b/qoibench.c index 2c3df68..cca8c14 100644 --- a/qoibench.c +++ b/qoibench.c @@ -189,7 +189,7 @@ typedef struct { void png_decode_callback(png_structp png, png_bytep data, png_size_t length) { libpng_read_t *read_data = (libpng_read_t*)png_get_io_ptr(png); if (read_data->pos + length > read_data->size) { - ERROR("PNG read %d bytes at pos %d (size: %d)", length, read_data->pos, read_data->size); + ERROR("PNG read %ld bytes at pos %d (size: %d)", length, read_data->pos, read_data->size); } memcpy(data, read_data->data + read_data->pos, length); read_data->pos += length; @@ -358,7 +358,7 @@ void benchmark_print_result(benchmark_result_t res) { printf(" decode ms encode ms decode mpps encode mpps size kb rate\n"); if (!opt_nopng) { printf( - "libpng: %8.1f %8.1f %8.2f %8.2f %8d %4.1f%%\n", + "libpng: %8.1f %8.1f %8.2f %8.2f %8ld %4.1f%%\n", (double)res.libpng.decode_time/1000000.0, (double)res.libpng.encode_time/1000000.0, (res.libpng.decode_time > 0 ? px / ((double)res.libpng.decode_time/1000.0) : 0), @@ -367,7 +367,7 @@ void benchmark_print_result(benchmark_result_t res) { ((double)res.libpng.size/(double)res.raw_size) * 100.0 ); printf( - "stbi: %8.1f %8.1f %8.2f %8.2f %8d %4.1f%%\n", + "stbi: %8.1f %8.1f %8.2f %8.2f %8ld %4.1f%%\n", (double)res.stbi.decode_time/1000000.0, (double)res.stbi.encode_time/1000000.0, (res.stbi.decode_time > 0 ? px / ((double)res.stbi.decode_time/1000.0) : 0), @@ -377,7 +377,7 @@ void benchmark_print_result(benchmark_result_t res) { ); } printf( - "qoi: %8.1f %8.1f %8.2f %8.2f %8d %4.1f%%\n", + "qoi: %8.1f %8.1f %8.2f %8.2f %8ld %4.1f%%\n", (double)res.qoi.decode_time/1000000.0, (double)res.qoi.encode_time/1000000.0, (res.qoi.decode_time > 0 ? px / ((double)res.qoi.decode_time/1000.0) : 0), From 2870b54937944d2b746b296f45135f7d09801fc4 Mon Sep 17 00:00:00 2001 From: Lucas Menezes Date: Mon, 3 Jan 2022 12:48:27 +0000 Subject: [PATCH 109/208] Changed Link Changed to the most stable link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3267130..2e3bd7b 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ implementations listed below. - https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET - https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer - https://github.com/Tom94/tev - another native QOI viewer (allows pixel peeping and comparison with other image formats) -- https://apps.apple.com/us/app/qoiconverterx/id1602159820 QOI <=> PNG converter available on the Mac App Store +- https://apps.apple.com/br/app/qoiconverterx/id1602159820 QOI <=> PNG converter available on the Mac App Store ## Implementations & Bindings of QOI From 021947986752361b7da61a16e95616a62f022de1 Mon Sep 17 00:00:00 2001 From: Jan Boon Date: Tue, 4 Jan 2022 11:34:34 +0800 Subject: [PATCH 110/208] Add link to QOI Bitmap I/O Plugin for 3ds Max --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9bbc4b9..143fa6a 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ implementations listed below. - https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer - https://github.com/Tom94/tev - another native QOI viewer (allows pixel peeping and comparison with other image formats) - https://apps.apple.com/br/app/qoiconverterx/id1602159820 QOI <=> PNG converter available on the Mac App Store +- https://github.com/kaetemi/qoi-max - QOI Bitmap I/O Plugin for 3ds Max ## Implementations & Bindings of QOI From 6804a745e4b74022dccb3b465482ea8e4b252e2a Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 5 Jan 2022 16:51:38 +0100 Subject: [PATCH 111/208] Fix example in QOI_OP_LUMA documentation; close #161 --- qoi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qoi.h b/qoi.h index 7addd70..58d14ae 100644 --- a/qoi.h +++ b/qoi.h @@ -173,8 +173,8 @@ The alpha value remains unchanged from the previous pixel. The green channel is used to indicate the general direction of change and is encoded in 6 bits. The red and blue channels (dr and db) base their diffs off of the green channel difference and are encoded in 4 bits. I.e.: - dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g) - db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g) + dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g) + db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g) The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while "250 + 7" will result in 1. From 07116e8b894176c4155090f85e2442e3c989b215 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 5 Jan 2022 16:55:27 +0100 Subject: [PATCH 112/208] Clarify colorspace & channels header usage; #152 --- qoi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoi.h b/qoi.h index 58d14ae..1290164 100644 --- a/qoi.h +++ b/qoi.h @@ -248,7 +248,7 @@ The colorspace in this qoi_desc is an enum where 1 = all channels are linear You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely informative. It will be saved to the file header, but does not affect -en-/decoding in any way. */ +how chunks are en-/decoded. */ #define QOI_SRGB 0 #define QOI_LINEAR 1 From fd6f6463efc0538ae3658363e315ea182a5217fd Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Wed, 5 Jan 2022 16:57:38 +0100 Subject: [PATCH 113/208] Fix documentation for consecutive QOI_OP_INDEX chunks; close #112 --- qoi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qoi.h b/qoi.h index 1290164..988f9ed 100644 --- a/qoi.h +++ b/qoi.h @@ -135,8 +135,8 @@ The possible chunks are: 2-bit tag b00 6-bit index into the color index array: 0..63 -A valid encoder must not issue 7 or more consecutive QOI_OP_INDEX chunks to the -index 0, to avoid confusion with the 8 byte end marker. +A valid encoder must not issue 2 or more consecutive QOI_OP_INDEX chunks to the +same index. QOI_OP_RUN should be used instead. .- QOI_OP_DIFF -----------. From 98e8a0237ccc7e6e868540d61deb0cbeac870ca7 Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Thu, 6 Jan 2022 04:06:21 +0300 Subject: [PATCH 114/208] Add `qoi` Rust crate reference (aldanor/qoi-rust) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 143fa6a..0e7542e 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ implementations listed below. - https://github.com/MasterQ32/zig-qoi (Zig) - https://github.com/rbino/qoix (Elixir) - https://github.com/NUlliiON/QoiSharp (C#) +- https://github.com/aldanor/qoi-rust (Rust) - https://github.com/zakarumych/rapid-qoi (Rust) - https://github.com/takeyourhatoff/qoi (Go) - https://github.com/DosWorld/pasqoi (Pascal) @@ -93,7 +94,6 @@ implementations listed below. These implementations are based on the pre-release version of QOI. Resulting files are not compatible with the current version. -- https://github.com/steven-joruk/qoi (Rust) - https://github.com/ChevyRay/qoi_rs (Rust) - https://github.com/panzi/jsqoi (TypeScript) - https://github.com/0xd34df00d/hsqoi (Haskell) From 98d5f5187ee66b2b52822b1d54a0f9dc424a27db Mon Sep 17 00:00:00 2001 From: aquaratixc Date: Thu, 20 Jan 2022 20:13:19 +0300 Subject: [PATCH 115/208] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0e7542e..fbcb7db 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ implementations listed below. - https://github.com/KristofferC/QOI.jl (Julia) - https://github.com/shadowMitia/libqoi/ (C++) - https://github.com/MKCG/php-qoi (PHP) +- https://github.com/LightHouseSoftware/qoiformats (D) ## QOI Support in Other Software From ee79afbe8bd2544f6de11e7bb5730c077842b38a Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Fri, 28 Jan 2022 01:24:10 +0100 Subject: [PATCH 116/208] Mention rTexViewer --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fbcb7db..858b150 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ implementations listed below. - https://github.com/Tom94/tev - another native QOI viewer (allows pixel peeping and comparison with other image formats) - https://apps.apple.com/br/app/qoiconverterx/id1602159820 QOI <=> PNG converter available on the Mac App Store - https://github.com/kaetemi/qoi-max - QOI Bitmap I/O Plugin for 3ds Max +- https://raylibtech.itch.io/rtexviewer - texture viewer, supports QOI ## Implementations & Bindings of QOI From efd968caba10a4bd0c0f7c2542f0f1f09b00fafd Mon Sep 17 00:00:00 2001 From: Matt Howard Date: Sat, 29 Jan 2022 20:18:05 +0100 Subject: [PATCH 117/208] Update README.md to reference Nim version --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 858b150..7d83f2f 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ implementations listed below. - https://github.com/shadowMitia/libqoi/ (C++) - https://github.com/MKCG/php-qoi (PHP) - https://github.com/LightHouseSoftware/qoiformats (D) +- https://github.com/mhoward540/qoi-nim (Nim) ## QOI Support in Other Software From e6195209d812c76d206e43c3d5b9be82b791be6b Mon Sep 17 00:00:00 2001 From: iarwain Date: Mon, 31 Jan 2022 10:56:59 -0500 Subject: [PATCH 118/208] Update README.md Added Orx link to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7d83f2f..a6c7c97 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ implementations listed below. - [Rebol3](https://github.com/Oldes/Rebol3/issues/39) supports decoding and encoding QOI using a native codec - [c-ray](https://github.com/vkoskiv/c-ray) supports QOI natively - [SAIL](https://github.com/HappySeaFox/sail) image decoding library, supports decoding and encoding QOI images +- [Orx](https://github.com/orx/orx) 2D game engine, supports QOI natively ## Packages From 7094132132f76df0239e4954ecbd91f855c8d6a2 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sun, 6 Feb 2022 12:12:20 +0100 Subject: [PATCH 119/208] Mention rTexPacker --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6c7c97..963f542 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ implementations listed below. - https://apps.apple.com/br/app/qoiconverterx/id1602159820 QOI <=> PNG converter available on the Mac App Store - https://github.com/kaetemi/qoi-max - QOI Bitmap I/O Plugin for 3ds Max - https://raylibtech.itch.io/rtexviewer - texture viewer, supports QOI - +- https://raylibtech.itch.io/rtexpacker - texture packer, supports QOI ## Implementations & Bindings of QOI From 6cfd82d35ee67b197d432d594833eaa1fb192cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=A1=D0=B0?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= Date: Wed, 9 Feb 2022 02:35:06 +0300 Subject: [PATCH 120/208] Add link to QOI Addon for Godot Engine --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 963f542..627cd13 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ implementations listed below. - https://github.com/kaetemi/qoi-max - QOI Bitmap I/O Plugin for 3ds Max - https://raylibtech.itch.io/rtexviewer - texture viewer, supports QOI - https://raylibtech.itch.io/rtexpacker - texture packer, supports QOI +- https://github.com/DmitriySalnikov/godot_qoi - QOI GDNative Addon for Godot Engine ## Implementations & Bindings of QOI From be96074eb0b3dff79494d96a2300d2d00561e55a Mon Sep 17 00:00:00 2001 From: I <1091761+wx257osn2@users.noreply.github.com> Date: Mon, 21 Feb 2022 08:56:59 +0900 Subject: [PATCH 121/208] Add qoixx --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 963f542..c61a3ef 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ implementations listed below. - https://github.com/MKCG/php-qoi (PHP) - https://github.com/LightHouseSoftware/qoiformats (D) - https://github.com/mhoward540/qoi-nim (Nim) +- https://github.com/wx257osn2/qoixx (C++) ## QOI Support in Other Software From 606bf77678d606f08df387e44b8e40297059bf8d Mon Sep 17 00:00:00 2001 From: dan9er <67669146+dan9er@users.noreply.github.com> Date: Wed, 2 Mar 2022 04:04:04 +0000 Subject: [PATCH 122/208] Add link to farbfeld-convert-qoi tool in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c61a3ef..2906b22 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ implementations listed below. - https://github.com/kaetemi/qoi-max - QOI Bitmap I/O Plugin for 3ds Max - https://raylibtech.itch.io/rtexviewer - texture viewer, supports QOI - https://raylibtech.itch.io/rtexpacker - texture packer, supports QOI +- https://gitlab.com/dan9er/farbfeld-convert-qoi - QOI <=> farbfeld converter ## Implementations & Bindings of QOI From 73f04c2ef9a4d865f307305924e3a840e4308d12 Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Wed, 23 Mar 2022 12:18:38 +0100 Subject: [PATCH 123/208] Mention IrfanView --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c61a3ef..dc0d73c 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ implementations listed below. - [c-ray](https://github.com/vkoskiv/c-ray) supports QOI natively - [SAIL](https://github.com/HappySeaFox/sail) image decoding library, supports decoding and encoding QOI images - [Orx](https://github.com/orx/orx) 2D game engine, supports QOI natively +- [IrfanView](https://www.irfanview.com) supports decoding and encoding QOI through its Formats plugin ## Packages From 6170f9125d9c4413cd9b3bcd6cefc8d99aa3af35 Mon Sep 17 00:00:00 2001 From: Jules Maselbas <54854023+jmaselbas@users.noreply.github.com> Date: Fri, 25 Mar 2022 00:57:01 +0100 Subject: [PATCH 124/208] Update readme with other software with QOI support --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc0d73c..d2f9733 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,8 @@ implementations listed below. - [SAIL](https://github.com/HappySeaFox/sail) image decoding library, supports decoding and encoding QOI images - [Orx](https://github.com/orx/orx) 2D game engine, supports QOI natively - [IrfanView](https://www.irfanview.com) supports decoding and encoding QOI through its Formats plugin - +- [ImageMagick](https://github.com/ImageMagick/ImageMagick) supports decoding and encoding QOI, since 7.1.0-20 +- [barebox](https://barebox.org) bootloader, supports decoding QOI images for splash logo, since v2022.03.0 ## Packages From 8297ace59d30dc27fc20cc4ba123507607a675ef Mon Sep 17 00:00:00 2001 From: Carlos Ballesteros Velasco Date: Fri, 25 Mar 2022 03:09:48 +0100 Subject: [PATCH 125/208] Added KorGE & KorIM support engine & library --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9526827..031c157 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ implementations listed below. - [IrfanView](https://www.irfanview.com) supports decoding and encoding QOI through its Formats plugin - [ImageMagick](https://github.com/ImageMagick/ImageMagick) supports decoding and encoding QOI, since 7.1.0-20 - [barebox](https://barebox.org) bootloader, supports decoding QOI images for splash logo, since v2022.03.0 +- [KorGE](https://korge.org) & KorIM Kotlin 2D game engine and imaging library, supports decoding and encoding QOI natively since 2.7.0 ## Packages From 09d144f89264342901250d280b5a7905748fa66f Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 28 Mar 2022 09:27:43 +0200 Subject: [PATCH 126/208] Mention DOjS --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 031c157..b528c1f 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ implementations listed below. - [ImageMagick](https://github.com/ImageMagick/ImageMagick) supports decoding and encoding QOI, since 7.1.0-20 - [barebox](https://barebox.org) bootloader, supports decoding QOI images for splash logo, since v2022.03.0 - [KorGE](https://korge.org) & KorIM Kotlin 2D game engine and imaging library, supports decoding and encoding QOI natively since 2.7.0 +- [DOjS](https://github.com/SuperIlu/DOjS) DOS JavaScript Canvas implementation supports loading QOI files ## Packages From 56be991260a9a585b067551a69f47e84b7512737 Mon Sep 17 00:00:00 2001 From: Alexandru M Stan Date: Tue, 5 Apr 2022 02:16:00 -0700 Subject: [PATCH 127/208] QOI on an FPGA (Verilog)! --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b528c1f..d444db1 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ implementations listed below. - https://github.com/LightHouseSoftware/qoiformats (D) - https://github.com/mhoward540/qoi-nim (Nim) - https://github.com/wx257osn2/qoixx (C++) +* https://github.com/amstan/qoi-fpga (FPGA: verilog) ## QOI Support in Other Software From fc0eef8e545e751f29920b82799fef5bf1a92507 Mon Sep 17 00:00:00 2001 From: Tiefseetauchner <48085877+Tiefseetauchner@users.noreply.github.com> Date: Thu, 7 Apr 2022 19:10:27 +0200 Subject: [PATCH 128/208] Added lr-paint processing qoi capable drawing program I made LR-Paint in three days to proof (mainly to myself) how simple it is to implement QOI So I wrote it in Processing Like any madman would It's great trust me And this is totally serious, I want the abomination of Processing code I made to be in the QOI Readme yes yes --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b528c1f..49c0ae3 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ implementations listed below. - https://github.com/LightHouseSoftware/qoiformats (D) - https://github.com/mhoward540/qoi-nim (Nim) - https://github.com/wx257osn2/qoixx (C++) +- https://github.com/Tiefseetauchner/lr-paint (Processing) ## QOI Support in Other Software From 7506300a3ef0d28a4783a88331756fbf819e42c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Musab=20K=C4=B1l=C4=B1=C3=A7?= Date: Sat, 9 Apr 2022 17:17:36 +0300 Subject: [PATCH 129/208] Add musabkilic/qoi-decoder to implementations --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b528c1f..5995f08 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ implementations listed below. - https://github.com/LightHouseSoftware/qoiformats (D) - https://github.com/mhoward540/qoi-nim (Nim) - https://github.com/wx257osn2/qoixx (C++) - +- https://github.com/musabkilic/qoi-decoder (Python) ## QOI Support in Other Software From 773915aefd1a2a19a714d72a235b1692c36533c5 Mon Sep 17 00:00:00 2001 From: Matheus Pedroni <34845106+mathpn@users.noreply.github.com> Date: Sat, 9 Apr 2022 21:56:13 -0300 Subject: [PATCH 130/208] include py-qoi in readme include a new native python implementation of encoder + decoder following QOI format specifications --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5995f08..85e4901 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ implementations listed below. - https://github.com/mhoward540/qoi-nim (Nim) - https://github.com/wx257osn2/qoixx (C++) - https://github.com/musabkilic/qoi-decoder (Python) +- https://github.com/mathpn/py-qoi (Python) ## QOI Support in Other Software From 1296ad81799b6f477a1a0816383d1eca2b484b09 Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Mon, 11 Apr 2022 21:46:56 +0200 Subject: [PATCH 131/208] New release of qoi-ci --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5995f08..de3d458 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ implementations listed below. ## Tools - https://github.com/floooh/qoiview - native QOI viewer -- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.1.0 - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView MP +- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.1.1 - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView MP - https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET - https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer - https://github.com/Tom94/tev - another native QOI viewer (allows pixel peeping and comparison with other image formats) From 59e0575c493dfebc5215f940c42b82cb8a1515f7 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 11 Apr 2022 22:35:39 +0200 Subject: [PATCH 132/208] Remove obsolete, unused vars; close #56 --- qoibench.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/qoibench.c b/qoibench.c index cca8c14..81fed6d 100644 --- a/qoibench.c +++ b/qoibench.c @@ -540,9 +540,6 @@ void benchmark_directory(const char *path, benchmark_result_t *grand_total) { rewinddir(dir); } - float total_percentage = 0; - int total_size = 0; - benchmark_result_t dir_total = {0}; int has_shown_head = 0; From e42b0b3022071b7edabd4669c2ae9b9bdc1b7404 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 11 Apr 2022 23:19:04 +0200 Subject: [PATCH 133/208] Load/store RGBA separately instead of using a typecast; close #197 This should fix problems on big-endian machines and with ILP64 --- qoi.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/qoi.h b/qoi.h index 988f9ed..31d4ce5 100644 --- a/qoi.h +++ b/qoi.h @@ -424,13 +424,12 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { channels = desc->channels; for (px_pos = 0; px_pos < px_len; px_pos += channels) { + px.rgba.r = pixels[px_pos + 0]; + px.rgba.g = pixels[px_pos + 1]; + px.rgba.b = pixels[px_pos + 2]; + if (channels == 4) { - px = *(qoi_rgba_t *)(pixels + px_pos); - } - else { - px.rgba.r = pixels[px_pos + 0]; - px.rgba.g = pixels[px_pos + 1]; - px.rgba.b = pixels[px_pos + 2]; + px.rgba.a = pixels[px_pos + 3]; } if (px.v == px_prev.v) { @@ -598,13 +597,12 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { index[QOI_COLOR_HASH(px) % 64] = px; } + pixels[px_pos + 0] = px.rgba.r; + pixels[px_pos + 1] = px.rgba.g; + pixels[px_pos + 2] = px.rgba.b; + if (channels == 4) { - *(qoi_rgba_t*)(pixels + px_pos) = px; - } - else { - pixels[px_pos + 0] = px.rgba.r; - pixels[px_pos + 1] = px.rgba.g; - pixels[px_pos + 2] = px.rgba.b; + pixels[px_pos + 3] = px.rgba.a; } } From cc97aaed08a0d87a46f93a75a8c1ce7e0785d88d Mon Sep 17 00:00:00 2001 From: J_F Date: Tue, 12 Apr 2022 21:42:58 +0200 Subject: [PATCH 134/208] Add R-package QOI to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1b5a8c4..5935f3f 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ implementations listed below. - https://github.com/amstan/qoi-fpga (FPGA: verilog) - https://github.com/musabkilic/qoi-decoder (Python) - https://github.com/mathpn/py-qoi (Python) +- https://github.com/JohannesFriedrich/qoi4R (R) ## QOI Support in Other Software From e3612650c01ad53d69b8efb204e20fb4e019cf70 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshev Date: Fri, 15 Apr 2022 11:18:01 -0700 Subject: [PATCH 135/208] Update link to SAIL library --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5935f3f..d3f7282 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ implementations listed below. - [Raylib](https://github.com/raysan5/raylib) supports decoding and encoding QOI textures through its [rtextures module](https://github.com/raysan5/raylib/blob/master/src/rtextures.c) - [Rebol3](https://github.com/Oldes/Rebol3/issues/39) supports decoding and encoding QOI using a native codec - [c-ray](https://github.com/vkoskiv/c-ray) supports QOI natively -- [SAIL](https://github.com/HappySeaFox/sail) image decoding library, supports decoding and encoding QOI images +- [SAIL](https://sail.software) image decoding library, supports decoding and encoding QOI images - [Orx](https://github.com/orx/orx) 2D game engine, supports QOI natively - [IrfanView](https://www.irfanview.com) supports decoding and encoding QOI through its Formats plugin - [ImageMagick](https://github.com/ImageMagick/ImageMagick) supports decoding and encoding QOI, since 7.1.0-20 From 682273b1013cfde19b014d8716da2287cf878c8d Mon Sep 17 00:00:00 2001 From: shraiwi <40672030+shraiwi@users.noreply.github.com> Date: Sat, 16 Apr 2022 20:38:36 -0500 Subject: [PATCH 136/208] Add C streaming decoder to readme.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5935f3f..70ef0f6 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ implementations listed below. - https://github.com/musabkilic/qoi-decoder (Python) - https://github.com/mathpn/py-qoi (Python) - https://github.com/JohannesFriedrich/qoi4R (R) - +- https://github.com/shraiwi/mini-qoi (C, streaming decoder) ## QOI Support in Other Software From 339e11e2fd474b2f359b7b378d08b8a5ed444b6a Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Thu, 21 Apr 2022 21:25:59 +0200 Subject: [PATCH 137/208] Add info about versioning and contributing --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 307d07a..71421bd 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,20 @@ If this is a limitation for your use case, please look into any of the other implementations listed below. +## Improvements, New Versions and Contributing + +The QOI format has been finalized. It was a conscious decision to **not** have a +version number in the file header. If you have a working QOI implementation today, +you can rest assured that it will be compatible with all QOI files tomorrow. + +There are a lot of interesting ideas for a successor of QOI, but none of these will +be implemented here. That doesn't mean you shouldn't experiment with QOI, but please +be aware that pull requests that change the format will not be accepted. + +Likewise, pull requests for performance improvements will probably not be accepted +either, as this "reference implementation" tries to be as easy to read as possible. + + ## Tools - https://github.com/floooh/qoiview - native QOI viewer From 8308c4c1079461b4b96df0408179537ef09d3272 Mon Sep 17 00:00:00 2001 From: Luka S Date: Fri, 22 Apr 2022 20:29:47 +0100 Subject: [PATCH 138/208] Add 'dqoi' to the implementations list on README 'dqoi' is my implementation of QOI for Dart and Flutter, based off the C code included here, and @LowLevelJavaScript's implementation. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 71421bd..2d00c78 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/pfusik/qoi-ci (Ć, transpiled to C, C++, C#, Java, JavaScript, Python and Swift) - https://github.com/kodonnell/qoi (Python) +- https://github.com/JaffaKetchup/dqoi (Dart) - https://github.com/Cr4xy/lua-qoi (Lua) - https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) - https://github.com/saharNooby/qoi-java (Java) From 70894be9aa6ecc6afb0e1d63fbc817d4a1fa43f1 Mon Sep 17 00:00:00 2001 From: Luka S Date: Mon, 25 Apr 2022 11:54:00 +0100 Subject: [PATCH 139/208] Specified framework support Flutter is the application framework that uses Dart. 'dqoi' provides full support for Flutter apps with custom image painters. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d00c78..89ea460 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/pfusik/qoi-ci (Ć, transpiled to C, C++, C#, Java, JavaScript, Python and Swift) - https://github.com/kodonnell/qoi (Python) -- https://github.com/JaffaKetchup/dqoi (Dart) +- https://github.com/JaffaKetchup/dqoi (Dart, with Flutter support) - https://github.com/Cr4xy/lua-qoi (Lua) - https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) - https://github.com/saharNooby/qoi-java (Java) From 2e58276f201389f1a1afcec5ca946ec33cfbbe1b Mon Sep 17 00:00:00 2001 From: 10maurycy10 <10maurycy10@gmail.com> Date: Tue, 3 May 2022 14:26:19 -0700 Subject: [PATCH 140/208] Add yet another implementaion --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 89ea460..9de0c54 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/mathpn/py-qoi (Python) - https://github.com/JohannesFriedrich/qoi4R (R) - https://github.com/shraiwi/mini-qoi (C, streaming decoder) +- https://github.com/10maurycy10/libqoi/ (Rust) ## QOI Support in Other Software From a4f498b23c11f9c1ebdb695f814f2cf5a9b3a267 Mon Sep 17 00:00:00 2001 From: 0xd34df00d <0xd34df00d@gmail.com> Date: Sat, 7 May 2022 16:22:03 -0400 Subject: [PATCH 141/208] Haskell implementation synced with the spec --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 9de0c54..7f133af 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/JohannesFriedrich/qoi4R (R) - https://github.com/shraiwi/mini-qoi (C, streaming decoder) - https://github.com/10maurycy10/libqoi/ (Rust) +- https://github.com/0xd34df00d/hsqoi (Haskell) ## QOI Support in Other Software @@ -129,5 +130,3 @@ These implementations are based on the pre-release version of QOI. Resulting fil - https://github.com/ChevyRay/qoi_rs (Rust) - https://github.com/panzi/jsqoi (TypeScript) -- https://github.com/0xd34df00d/hsqoi (Haskell) - From b9d1e9c3ebbca4ce417281bf16a4cbeb7e38671e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Dan=C3=B8?= Date: Thu, 12 May 2022 09:52:51 +0200 Subject: [PATCH 142/208] feat; init makefile iteration --- Makefile | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..df89774 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +CC := gcc +CFLAGS_BENCH := -std=gnu99 -O3 +LFLAGS_BENCH := -lpng +CFLAGS_CONV := -std=c99 -O3 + +TARGET_BENCH := qoibench +TARGET_CONV := qoiconv + +all := $(TARGET) + +$(TARGET_BENCH):$(TARGET_BENCH).c $(LFLAGS_BENCH) + $(CC) $(CFLAGS_BENCH) $(TARGET_BENCH).c -o $(TARGET_BENCH) $(LFLAGS_BENCH) + +$() +$(TARGET_CONV):$(TARGET_CONV).c + $(CC) $(CFLAGS_CONV) $(TARGET_CONV).c -o $(TARGET_CONV) + +.PHONY: clean +clean: + $(RM) $(TARGET) $(OBJS) $(DEPS) +-include $(DEPS) From 9374bd61aec7f4006aad63ddba4766aa04588a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Dan=C3=B8?= Date: Thu, 12 May 2022 10:07:44 +0200 Subject: [PATCH 143/208] feat; working makefile --- Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index df89774..2e7a0e9 100644 --- a/Makefile +++ b/Makefile @@ -6,16 +6,18 @@ CFLAGS_CONV := -std=c99 -O3 TARGET_BENCH := qoibench TARGET_CONV := qoiconv -all := $(TARGET) +all: $(TARGET_BENCH) $(TARGET_CONV) + +bench: $(TARGET_BENCH) $(TARGET_BENCH):$(TARGET_BENCH).c $(LFLAGS_BENCH) $(CC) $(CFLAGS_BENCH) $(TARGET_BENCH).c -o $(TARGET_BENCH) $(LFLAGS_BENCH) -$() +conv: $(TARGET_CONV) $(TARGET_CONV):$(TARGET_CONV).c $(CC) $(CFLAGS_CONV) $(TARGET_CONV).c -o $(TARGET_CONV) .PHONY: clean clean: - $(RM) $(TARGET) $(OBJS) $(DEPS) + $(RM) $(TARGET_BENCH) $(TARGET_CONV) $(OBJS) $(DEPS) -include $(DEPS) From 06d032339b8fbbd6abe38dab53f48c78a5094f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Dan=C3=B8?= Date: Thu, 12 May 2022 14:08:51 +0200 Subject: [PATCH 144/208] CC:= -> CC?= and removal of dependency cleaning --- Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 2e7a0e9..0d5c534 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CC := gcc +CC ?= gcc CFLAGS_BENCH := -std=gnu99 -O3 LFLAGS_BENCH := -lpng CFLAGS_CONV := -std=c99 -O3 @@ -19,5 +19,4 @@ $(TARGET_CONV):$(TARGET_CONV).c .PHONY: clean clean: - $(RM) $(TARGET_BENCH) $(TARGET_CONV) $(OBJS) $(DEPS) --include $(DEPS) + $(RM) $(TARGET_BENCH) $(TARGET_CONV) From 520434351901e0b2d424e716cf0c35f617c0fece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Dan=C3=B8?= Date: Thu, 12 May 2022 14:53:17 +0200 Subject: [PATCH 145/208] Replaced remaining := with ?= to allow CLI parameters --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 0d5c534..b570e8b 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ CC ?= gcc -CFLAGS_BENCH := -std=gnu99 -O3 -LFLAGS_BENCH := -lpng -CFLAGS_CONV := -std=c99 -O3 +CFLAGS_BENCH ?= -std=gnu99 -O3 +LFLAGS_BENCH ?= -lpng +CFLAGS_CONV ?= -std=c99 -O3 -TARGET_BENCH := qoibench -TARGET_CONV := qoiconv +TARGET_BENCH ?= qoibench +TARGET_CONV ?= qoiconv all: $(TARGET_BENCH) $(TARGET_CONV) From d1875b5ac885fdfaaa5d64b6fedfdce5f45bbd64 Mon Sep 17 00:00:00 2001 From: 418Coffee <63408077+418Coffee@users.noreply.github.com> Date: Sat, 21 May 2022 21:16:20 +0200 Subject: [PATCH 146/208] Add V Implementation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7f133af..7b130ef 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/shraiwi/mini-qoi (C, streaming decoder) - https://github.com/10maurycy10/libqoi/ (Rust) - https://github.com/0xd34df00d/hsqoi (Haskell) +- https://github.com/418Coffee/qoi-v (V) ## QOI Support in Other Software From 6ba3c1b42ebd93e311839606d9ef8c888bcb5a79 Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Tue, 24 May 2022 08:52:18 +0200 Subject: [PATCH 147/208] Mention XnView MP --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7b130ef..25ebf7c 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [barebox](https://barebox.org) bootloader, supports decoding QOI images for splash logo, since v2022.03.0 - [KorGE](https://korge.org) & KorIM Kotlin 2D game engine and imaging library, supports decoding and encoding QOI natively since 2.7.0 - [DOjS](https://github.com/SuperIlu/DOjS) DOS JavaScript Canvas implementation supports loading QOI files +- [XnView MP](https://www.xnview.com/en/xnviewmp/) supports decoding QOI since 1.00 ## Packages From 6005c73e0a7fdb33cfdcb82c162980b09dc14d44 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 7 Jun 2022 09:37:35 +0200 Subject: [PATCH 148/208] Add PureBasic implementation to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 25ebf7c..0d6dfb1 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/10maurycy10/libqoi/ (Rust) - https://github.com/0xd34df00d/hsqoi (Haskell) - https://github.com/418Coffee/qoi-v (V) +- https://github.com/Imagine-Programming/QoiImagePlugin (PureBasic) ## QOI Support in Other Software From bce1a069a8dc2b7aec13a3d0f09eeb9ff8fe3809 Mon Sep 17 00:00:00 2001 From: Luis Alfredo Figueroa Bracamontes <92luisalfredo@protonmail.com> Date: Sat, 11 Jun 2022 12:58:41 -0500 Subject: [PATCH 149/208] Adding links to required libreries Adding the URLs to the repo of the required libraries to make even easier to use this program --- qoiconv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qoiconv.c b/qoiconv.c index 79aa75c..501ed83 100644 --- a/qoiconv.c +++ b/qoiconv.c @@ -2,7 +2,10 @@ Command line tool to convert between png <> qoi format -Requires "stb_image.h" and "stb_image_write.h" +Requires: + -"stb_image.h" (https://github.com/nothings/stb/blob/master/stb_image.h) + -"stb_image_write.h" (https://github.com/nothings/stb/blob/master/stb_image_write.h) + Compile with: gcc qoiconv.c -std=c99 -O3 -o qoiconv From 553eae423acba5e35fb036da4473925b4d54e94a Mon Sep 17 00:00:00 2001 From: Luis Alfredo Figueroa Bracamontes <92luisalfredo@protonmail.com> Date: Sat, 11 Jun 2022 13:26:59 -0500 Subject: [PATCH 150/208] Adding qoi.h dependency Adding the text to indicate that "qoi.h" is required to use this file --- qoiconv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qoiconv.c b/qoiconv.c index 501ed83..f5fa93b 100644 --- a/qoiconv.c +++ b/qoiconv.c @@ -5,6 +5,7 @@ Command line tool to convert between png <> qoi format Requires: -"stb_image.h" (https://github.com/nothings/stb/blob/master/stb_image.h) -"stb_image_write.h" (https://github.com/nothings/stb/blob/master/stb_image_write.h) + -"qoi.h" (https://github.com/phoboslab/qoi/blob/master/qoi.h) Compile with: gcc qoiconv.c -std=c99 -O3 -o qoiconv From c1c46c8a769462a301d6430af11c7e5c3d5003b9 Mon Sep 17 00:00:00 2001 From: Fabien Chouteau Date: Sun, 12 Jun 2022 08:35:13 +0200 Subject: [PATCH 151/208] Add link to Ada/SPARK implementation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0d6dfb1..70663db 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/0xd34df00d/hsqoi (Haskell) - https://github.com/418Coffee/qoi-v (V) - https://github.com/Imagine-Programming/QoiImagePlugin (PureBasic) +- https://github.com/Fabien-Chouteau/qoi-spark (Ada/SPARK formally proven) ## QOI Support in Other Software From fc4bec90995d270d28e782cadbbfd65ccb805361 Mon Sep 17 00:00:00 2001 From: Damian Gaweda Date: Sun, 12 Jun 2022 20:18:57 +0200 Subject: [PATCH 152/208] Add AmigaOS support to README Add a link to a datatype plugin that adds QOI support to AmigaOS. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 70663db..faf70ee 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ either, as this "reference implementation" tries to be as easy to read as possib ## QOI Support in Other Software +- [Amiga OS QOI datatype](https://github.com/dgaw/qoi-datatype) adds support for decoding QOI images to the Amiga operating system. - [SerenityOS](https://github.com/SerenityOS/serenity) supports decoding QOI system wide through a custom [cpp implementation in LibGfx](https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibGfx/QOILoader.h) - [Raylib](https://github.com/raysan5/raylib) supports decoding and encoding QOI textures through its [rtextures module](https://github.com/raysan5/raylib/blob/master/src/rtextures.c) - [Rebol3](https://github.com/Oldes/Rebol3/issues/39) supports decoding and encoding QOI using a native codec From 14c22321ff3132098ad0f36d9423ff9100f672be Mon Sep 17 00:00:00 2001 From: "xiaozhuai, Weihang Ding" <798047000@qq.com> Date: Tue, 14 Jun 2022 17:36:49 +0800 Subject: [PATCH 153/208] Add Jetbrains' plugin url --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index faf70ee..8d4ad23 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://raylibtech.itch.io/rtexpacker - texture packer, supports QOI - https://github.com/DmitriySalnikov/godot_qoi - QOI GDNative Addon for Godot Engine - https://gitlab.com/dan9er/farbfeld-convert-qoi - QOI <=> farbfeld converter +- https://github.com/xiaozhuai/jetbrains-qoi - [QOI Support](https://plugins.jetbrains.com/plugin/19352-qoi-support) for Jetbrains' IDE. ## Implementations & Bindings of QOI From 9434e96f9b10b1d80c852dbf3915e667261ac53c Mon Sep 17 00:00:00 2001 From: polluks2 <74630735+polluks2@users.noreply.github.com> Date: Sat, 18 Jun 2022 14:39:05 +0200 Subject: [PATCH 154/208] Fixed broken makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b570e8b..4d21d41 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ all: $(TARGET_BENCH) $(TARGET_CONV) bench: $(TARGET_BENCH) -$(TARGET_BENCH):$(TARGET_BENCH).c $(LFLAGS_BENCH) +$(TARGET_BENCH):$(TARGET_BENCH).c $(CC) $(CFLAGS_BENCH) $(TARGET_BENCH).c -o $(TARGET_BENCH) $(LFLAGS_BENCH) conv: $(TARGET_CONV) From ef929be770f701853798584748d7636fc04fe28c Mon Sep 17 00:00:00 2001 From: mzgreen Date: Mon, 20 Jun 2022 14:33:20 +0200 Subject: [PATCH 155/208] Add Kotlin Multiplatform implementation to the README list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8d4ad23..d8d5994 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/418Coffee/qoi-v (V) - https://github.com/Imagine-Programming/QoiImagePlugin (PureBasic) - https://github.com/Fabien-Chouteau/qoi-spark (Ada/SPARK formally proven) +- https://github.com/mzgreen/qoi-kotlin (Kotlin Multiplatform) ## QOI Support in Other Software From 948a86f5073cd2f5b909c104d64437e925aead75 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 20 Jun 2022 20:28:55 +0200 Subject: [PATCH 156/208] Added unity-qoi to Readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d8d5994..bbff33d 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://raylibtech.itch.io/rtexpacker - texture packer, supports QOI - https://github.com/DmitriySalnikov/godot_qoi - QOI GDNative Addon for Godot Engine - https://gitlab.com/dan9er/farbfeld-convert-qoi - QOI <=> farbfeld converter +- https://github.com/Ben1138/unity-qoi - QOI support for the Unity3D Game Engine - https://github.com/xiaozhuai/jetbrains-qoi - [QOI Support](https://plugins.jetbrains.com/plugin/19352-qoi-support) for Jetbrains' IDE. From 376d39cc677aebdc84a41931f8806a85aed38153 Mon Sep 17 00:00:00 2001 From: Aftersol <42478428+Aftersol@users.noreply.github.com> Date: Thu, 28 Jul 2022 01:28:28 -0700 Subject: [PATCH 157/208] add my own project link to README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bbff33d..8f6680e 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/Imagine-Programming/QoiImagePlugin (PureBasic) - https://github.com/Fabien-Chouteau/qoi-spark (Ada/SPARK formally proven) - https://github.com/mzgreen/qoi-kotlin (Kotlin Multiplatform) +- https://github.com/Aftersol/Simplified-QOI-Codec (C99, streaming encoder and decoder, freestanding) ## QOI Support in Other Software @@ -127,6 +128,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [DOjS](https://github.com/SuperIlu/DOjS) DOS JavaScript Canvas implementation supports loading QOI files - [XnView MP](https://www.xnview.com/en/xnviewmp/) supports decoding QOI since 1.00 + ## Packages [AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. From 583cdd311e2d5c07d8b46cf26d00977a93bf01d9 Mon Sep 17 00:00:00 2001 From: Aftersol <42478428+Aftersol@users.noreply.github.com> Date: Thu, 28 Jul 2022 01:45:28 -0700 Subject: [PATCH 158/208] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8f6680e..1318cc8 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,6 @@ either, as this "reference implementation" tries to be as easy to read as possib - [DOjS](https://github.com/SuperIlu/DOjS) DOS JavaScript Canvas implementation supports loading QOI files - [XnView MP](https://www.xnview.com/en/xnviewmp/) supports decoding QOI since 1.00 - ## Packages [AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. From 32ac6c3c0fdeada0f0c186d819b7103b82a3f9c4 Mon Sep 17 00:00:00 2001 From: ryuukk <44361234+ryuukk@users.noreply.github.com> Date: Fri, 29 Jul 2022 16:23:23 +0200 Subject: [PATCH 159/208] Add gamut D library --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1318cc8..f2dd4be 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - https://github.com/Fabien-Chouteau/qoi-spark (Ada/SPARK formally proven) - https://github.com/mzgreen/qoi-kotlin (Kotlin Multiplatform) - https://github.com/Aftersol/Simplified-QOI-Codec (C99, streaming encoder and decoder, freestanding) +- https://github.com/AuburnSounds/gamut (D) ## QOI Support in Other Software From 370be5c080e0ff974231f4a19437e547b83dfb68 Mon Sep 17 00:00:00 2001 From: AmCh-Q Date: Tue, 2 Aug 2022 06:36:17 -0400 Subject: [PATCH 160/208] Fix small typo in qoibench.c --- qoibench.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoibench.c b/qoibench.c index 81fed6d..1f093fd 100644 --- a/qoibench.c +++ b/qoibench.c @@ -431,7 +431,7 @@ benchmark_result_t benchmark_image(const char *path) { }, &encoded_qoi_size); if (!pixels || !encoded_qoi || !encoded_png) { - ERROR("Error decoding %s", path); + ERROR("Error encoding %s", path); } // Verify QOI Output From b64209287f3dda5326836408120de085bb404fbe Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Wed, 3 Aug 2022 20:17:24 +0200 Subject: [PATCH 161/208] New release of qoi-ci --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f2dd4be..0e12700 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Tools - https://github.com/floooh/qoiview - native QOI viewer -- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.1.1 - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView MP +- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.1.2 - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView - https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET - https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer - https://github.com/Tom94/tev - another native QOI viewer (allows pixel peeping and comparison with other image formats) From a4e7750d680ab08af99336888dbfdf6aa6bab9e4 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Fri, 5 Aug 2022 23:39:12 +0200 Subject: [PATCH 162/208] Add SPDX License Identifier, remove license text; close #168 --- qoi.h | 28 ++++------------------------ qoibench.c | 27 ++++----------------------- qoiconv.c | 27 ++++----------------------- qoifuzz.c | 27 ++++----------------------- 4 files changed, 16 insertions(+), 93 deletions(-) diff --git a/qoi.h b/qoi.h index 31d4ce5..6734ac4 100644 --- a/qoi.h +++ b/qoi.h @@ -1,31 +1,11 @@ /* +Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org +SPDX-License-Identifier: MIT + + QOI - The "Quite OK Image" format for fast, lossless image compression -Dominic Szablewski - https://phoboslab.org - - --- LICENSE: The MIT License(MIT) - -Copyright(c) 2021 Dominic Szablewski - -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. - - -- About QOI encodes and decodes images in a lossless format. Compared to stb_image and diff --git a/qoibench.c b/qoibench.c index 1f093fd..05762c6 100644 --- a/qoibench.c +++ b/qoibench.c @@ -1,34 +1,15 @@ /* +Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org +SPDX-License-Identifier: MIT + + Simple benchmark suite for png, stbi and qoi Requires libpng, "stb_image.h" and "stb_image_write.h" Compile with: gcc qoibench.c -std=gnu99 -lpng -O3 -o qoibench -Dominic Szablewski - https://phoboslab.org - - --- LICENSE: The MIT License(MIT) - -Copyright(c) 2021 Dominic Szablewski - -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. - */ #include diff --git a/qoiconv.c b/qoiconv.c index f5fa93b..caef2ee 100644 --- a/qoiconv.c +++ b/qoiconv.c @@ -1,5 +1,9 @@ /* +Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org +SPDX-License-Identifier: MIT + + Command line tool to convert between png <> qoi format Requires: @@ -10,29 +14,6 @@ Requires: Compile with: gcc qoiconv.c -std=c99 -O3 -o qoiconv -Dominic Szablewski - https://phoboslab.org - - --- LICENSE: The MIT License(MIT) - -Copyright(c) 2021 Dominic Szablewski - -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. - */ diff --git a/qoifuzz.c b/qoifuzz.c index 1b5c792..69954d1 100644 --- a/qoifuzz.c +++ b/qoifuzz.c @@ -1,33 +1,14 @@ /* +Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org +SPDX-License-Identifier: MIT + + clang fuzzing harness for qoi_decode Compile and run with: clang -fsanitize=address,fuzzer -g -O0 qoifuzz.c && ./a.out -Dominic Szablewski - https://phoboslab.org - - --- LICENSE: The MIT License(MIT) - -Copyright(c) 2021 Dominic Szablewski - -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. - */ From bcb2fb5e48be3c931479a1a2e77f48123d6e96ce Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Fri, 5 Aug 2022 23:44:51 +0200 Subject: [PATCH 163/208] Add note about MIME type; close #167 --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 0e12700..651cece 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,16 @@ converts between png <> qoi a simple wrapper to benchmark stbi, libpng and qoi +## MIME Type, File Extension + +The recommended MIME type for QOI images is `image/qoi`. While QOI is not yet +officially registered with IANA, I believe QOI has found enough adoption to +prevent any future image format from choosing the same name, thus making a +MIME type collision highly unlikely ([see #167](https://github.com/phoboslab/qoi/issues/167)) + +Recommended file extension for QOI images is `.qoi` + + ## Limitations The QOI file format allows for huge images with up to 18 exa-pixels. A streaming From a488d3be0f5811dbf38f85d575c86015acbe8f60 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Fri, 5 Aug 2022 23:45:59 +0200 Subject: [PATCH 164/208] Mention Debian & Ubuntu packages; add link to Repology --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 651cece..dc59c1c 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,11 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Packages -[AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. +- [AUR](https://aur.archlinux.org/pkgbase/qoi-git/) - system-wide qoi.h, qoiconv and qoibench install as split packages. +- [Debian](https://packages.debian.org/bookworm/source/qoi) - packages for binaries and qoi.h +- [Ubuntu](https://launchpad.net/ubuntu/+source/qoi) - packages for binaries and qoi.h + +Packages for other systems [tracked at Repology](https://repology.org/project/qoi/versions). ## Implementations not yet conforming to the final specification From 948a53e04c2aa828d8e35c149f1df38803a6c7d0 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 6 Aug 2022 00:02:20 +0200 Subject: [PATCH 165/208] Link QOI plugins to releases overview page --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dc59c1c..dd729c1 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,8 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Tools - https://github.com/floooh/qoiview - native QOI viewer -- https://github.com/pfusik/qoi-ci/releases/tag/qoi-ci-1.1.2 - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView -- https://github.com/iOrange/QoiFileTypeNet/releases/tag/v0.2 - QOI Plugin for Paint.NET +- https://github.com/pfusik/qoi-ci/releases - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView +- https://github.com/iOrange/QoiFileTypeNet/releases - QOI Plugin for Paint.NET - https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer - https://github.com/Tom94/tev - another native QOI viewer (allows pixel peeping and comparison with other image formats) - https://apps.apple.com/br/app/qoiconverterx/id1602159820 QOI <=> PNG converter available on the Mac App Store From 76583cc18db1e0c549c65dc8701eb76a095d9add Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 6 Aug 2022 00:04:49 +0200 Subject: [PATCH 166/208] Wording --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dd729c1..dae0c51 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,9 @@ a simple wrapper to benchmark stbi, libpng and qoi The recommended MIME type for QOI images is `image/qoi`. While QOI is not yet officially registered with IANA, I believe QOI has found enough adoption to prevent any future image format from choosing the same name, thus making a -MIME type collision highly unlikely ([see #167](https://github.com/phoboslab/qoi/issues/167)) +MIME type collision highly unlikely ([see #167](https://github.com/phoboslab/qoi/issues/167)). -Recommended file extension for QOI images is `.qoi` +The recommended file extension for QOI images is `.qoi` ## Limitations From f177c193e0044dd1647d0d936d3846dd0e39a57a Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 6 Aug 2022 00:05:21 +0200 Subject: [PATCH 167/208] Attempt to make list of tools and implementation a bit easier to read --- README.md | 128 +++++++++++++++++++++++++++--------------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index dae0c51..4188830 100644 --- a/README.md +++ b/README.md @@ -67,77 +67,77 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Tools -- https://github.com/floooh/qoiview - native QOI viewer -- https://github.com/pfusik/qoi-ci/releases - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView -- https://github.com/iOrange/QoiFileTypeNet/releases - QOI Plugin for Paint.NET -- https://github.com/iOrange/QOIThumbnailProvider - Add thumbnails for QOI images in Windows Explorer -- https://github.com/Tom94/tev - another native QOI viewer (allows pixel peeping and comparison with other image formats) -- https://apps.apple.com/br/app/qoiconverterx/id1602159820 QOI <=> PNG converter available on the Mac App Store -- https://github.com/kaetemi/qoi-max - QOI Bitmap I/O Plugin for 3ds Max -- https://raylibtech.itch.io/rtexviewer - texture viewer, supports QOI -- https://raylibtech.itch.io/rtexpacker - texture packer, supports QOI -- https://github.com/DmitriySalnikov/godot_qoi - QOI GDNative Addon for Godot Engine -- https://gitlab.com/dan9er/farbfeld-convert-qoi - QOI <=> farbfeld converter -- https://github.com/Ben1138/unity-qoi - QOI support for the Unity3D Game Engine -- https://github.com/xiaozhuai/jetbrains-qoi - [QOI Support](https://plugins.jetbrains.com/plugin/19352-qoi-support) for Jetbrains' IDE. +- [floooh/qoiview](https://github.com/floooh/qoiview) - native QOI viewer +- [pfusik/qoi-ci](https://github.com/pfusik/qoi-ci/releases) - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView +- [iOrange/QoiFileTypeNet](https://github.com/iOrange/QoiFileTypeNet/releases) - QOI Plugin for Paint.NET +- [iOrange/QOIThumbnailProvider](https://github.com/iOrange/QOIThumbnailProvider) - Add thumbnails for QOI images in Windows Explorer +- [Tom94/tev](https://github.com/Tom94/tev) - another native QOI viewer (allows pixel peeping and comparison with other image formats) +- [qoiconverterx](https://apps.apple.com/br/app/qoiconverterx/id1602159820) QOI <=> PNG converter available on the Mac App Store +- [kaetemi/qoi-ma](https://github.com/kaetemi/qoi-max) - QOI Bitmap I/O Plugin for 3ds Max +- [rtexviewer](https://raylibtech.itch.io/rtexviewer) - texture viewer, supports QOI +- [rtexpacker](https://raylibtech.itch.io/rtexpacker) - texture packer, supports QOI +- [DmitriySalnikov/godot_qoi](https://github.com/DmitriySalnikov/godot_qoi) - QOI GDNative Addon for Godot Engine +- [dan9er/farbfeld-convert-qoi](https://gitlab.com/dan9er/farbfeld-convert-qoi) - QOI <=> farbfeld converter +- [Ben1138/unity-qo](https://github.com/Ben1138/unity-qoi) - QOI support for the Unity3D Game Engine +- [xiaozhuai/jetbrains-qo](https://github.com/xiaozhuai/jetbrains-qoi) - [QOI Support](https://plugins.jetbrains.com/plugin/19352-qoi-support) for Jetbrains' IDE. ## Implementations & Bindings of QOI -- https://github.com/pfusik/qoi-ci (Ć, transpiled to C, C++, C#, Java, JavaScript, Python and Swift) -- https://github.com/kodonnell/qoi (Python) -- https://github.com/JaffaKetchup/dqoi (Dart, with Flutter support) -- https://github.com/Cr4xy/lua-qoi (Lua) -- https://github.com/superzazu/SDL_QOI (C, SDL2 bindings) -- https://github.com/saharNooby/qoi-java (Java) -- https://github.com/MasterQ32/zig-qoi (Zig) -- https://github.com/rbino/qoix (Elixir) -- https://github.com/NUlliiON/QoiSharp (C#) -- https://github.com/aldanor/qoi-rust (Rust) -- https://github.com/zakarumych/rapid-qoi (Rust) -- https://github.com/takeyourhatoff/qoi (Go) -- https://github.com/DosWorld/pasqoi (Pascal) -- https://github.com/elihwyma/Swift-QOI (Swift) -- https://github.com/xfmoulet/qoi (Go) -- https://erratique.ch/software/qoic (OCaml) -- https://github.com/arian/go-qoi (Go) -- https://github.com/kchapelier/qoijs (JavaScript) -- https://github.com/KristofferC/QOI.jl (Julia) -- https://github.com/shadowMitia/libqoi/ (C++) -- https://github.com/MKCG/php-qoi (PHP) -- https://github.com/LightHouseSoftware/qoiformats (D) -- https://github.com/mhoward540/qoi-nim (Nim) -- https://github.com/wx257osn2/qoixx (C++) -- https://github.com/Tiefseetauchner/lr-paint (Processing) -- https://github.com/amstan/qoi-fpga (FPGA: verilog) -- https://github.com/musabkilic/qoi-decoder (Python) -- https://github.com/mathpn/py-qoi (Python) -- https://github.com/JohannesFriedrich/qoi4R (R) -- https://github.com/shraiwi/mini-qoi (C, streaming decoder) -- https://github.com/10maurycy10/libqoi/ (Rust) -- https://github.com/0xd34df00d/hsqoi (Haskell) -- https://github.com/418Coffee/qoi-v (V) -- https://github.com/Imagine-Programming/QoiImagePlugin (PureBasic) -- https://github.com/Fabien-Chouteau/qoi-spark (Ada/SPARK formally proven) -- https://github.com/mzgreen/qoi-kotlin (Kotlin Multiplatform) -- https://github.com/Aftersol/Simplified-QOI-Codec (C99, streaming encoder and decoder, freestanding) -- https://github.com/AuburnSounds/gamut (D) +- [pfusik/qoi-ci](https://github.com/pfusik/qoi-ci) - Ć, transpiled to C, C++, C#, Java, JavaScript, Python and Swift +- [kodonnell/qoi](https://github.com/kodonnell/qoi) - Python +- [JaffaKetchup/dqoi](https://github.com/JaffaKetchup/dqoi) - Dart, with Flutter support +- [Cr4xy/lua-qoi](https://github.com/Cr4xy/lua-qoi) - Lua +- [superzazu/SDL_QOI](https://github.com/superzazu/SDL_QOI) - C, SDL2 bindings +- [saharNooby/qoi-java](https://github.com/saharNooby/qoi-java) - Java +- [MasterQ32/zig-qoi](https://github.com/MasterQ32/zig-qoi) - Zig +- [rbino/qoix](https://github.com/rbino/qoix) - Elixir +- [NUlliiON/QoiSharp](https://github.com/NUlliiON/QoiSharp) - C# +- [aldanor/qoi-rust](https://github.com/aldanor/qoi-rust) - Rust +- [zakarumych/rapid-qoi](https://github.com/zakarumych/rapid-qoi) - Rust +- [takeyourhatoff/qoi](https://github.com/takeyourhatoff/qoi) - Go +- [DosWorld/pasqoi](https://github.com/DosWorld/pasqoi) - Pascal +- [elihwyma/Swift-QOI](https://github.com/elihwyma/Swift-QOI) - Swift +- [xfmoulet/qoi](https://github.com/xfmoulet/qoi) - Go +- [erratique.ch/qoic](https://erratique.ch/software/qoic) - OCaml +- [arian/go-qoi](https://github.com/arian/go-qoi) - Go +- [kchapelier/qoijs](https://github.com/kchapelier/qoijs) - JavaScript +- [KristofferC/QOI.jl](https://github.com/KristofferC/QOI.jl) - Julia +- [shadowMitia/libqoi](https://github.com/shadowMitia/libqoi) - C++ +- [MKCG/php-qoi](https://github.com/MKCG/php-qoi) - PHP +- [LightHouseSoftware/qoiformats](https://github.com/LightHouseSoftware/qoiformats) - D +- [mhoward540/qoi-nim](https://github.com/mhoward540/qoi-nim) - Nim +- [wx257osn2/qoixx](https://github.com/wx257osn2/qoixx) - C++ +- [Tiefseetauchner/lr-paint](https://github.com/Tiefseetauchner/lr-paint) - Processing +- [amstan/qoi-fpga](https://github.com/amstan/qoi-fpga) - FPGA: verilog +- [musabkilic/qoi-decoder](https://github.com/musabkilic/qoi-decoder) - Python +- [mathpn/py-qoi](https://github.com/mathpn/py-qoi) - Python +- [JohannesFriedrich/qoi4R](https://github.com/JohannesFriedrich/qoi4R) - R +- [shraiwi/mini-qoi](https://github.com/shraiwi/mini-qoi) - C, streaming decoder +- [10maurycy10/libqoi/](https://github.com/10maurycy10/libqoi/) - Rust +- [0xd34df00d/hsqoi](https://github.com/0xd34df00d/hsqoi) - Haskell +- [418Coffee/qoi-v](https://github.com/418Coffee/qoi-v) - V +- [Imagine-Programming/QoiImagePlugin](https://github.com/Imagine-Programming/QoiImagePlugin) - PureBasic +- [Fabien-Chouteau/qoi-spark](https://github.com/Fabien-Chouteau/qoi-spark) - Ada/SPARK formally proven +- [mzgreen/qoi-kotlin](https://github.com/mzgreen/qoi-kotlin) - Kotlin Multiplatform +- [Aftersol/Simplified-QOI-Codec](https://github.com/Aftersol/Simplified-QOI-Codec) - C99, streaming encoder and decoder, freestanding +- [AuburnSounds/gamut](https://github.com/AuburnSounds/gamut) - D ## QOI Support in Other Software -- [Amiga OS QOI datatype](https://github.com/dgaw/qoi-datatype) adds support for decoding QOI images to the Amiga operating system. -- [SerenityOS](https://github.com/SerenityOS/serenity) supports decoding QOI system wide through a custom [cpp implementation in LibGfx](https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibGfx/QOILoader.h) -- [Raylib](https://github.com/raysan5/raylib) supports decoding and encoding QOI textures through its [rtextures module](https://github.com/raysan5/raylib/blob/master/src/rtextures.c) -- [Rebol3](https://github.com/Oldes/Rebol3/issues/39) supports decoding and encoding QOI using a native codec -- [c-ray](https://github.com/vkoskiv/c-ray) supports QOI natively -- [SAIL](https://sail.software) image decoding library, supports decoding and encoding QOI images -- [Orx](https://github.com/orx/orx) 2D game engine, supports QOI natively -- [IrfanView](https://www.irfanview.com) supports decoding and encoding QOI through its Formats plugin -- [ImageMagick](https://github.com/ImageMagick/ImageMagick) supports decoding and encoding QOI, since 7.1.0-20 -- [barebox](https://barebox.org) bootloader, supports decoding QOI images for splash logo, since v2022.03.0 -- [KorGE](https://korge.org) & KorIM Kotlin 2D game engine and imaging library, supports decoding and encoding QOI natively since 2.7.0 -- [DOjS](https://github.com/SuperIlu/DOjS) DOS JavaScript Canvas implementation supports loading QOI files -- [XnView MP](https://www.xnview.com/en/xnviewmp/) supports decoding QOI since 1.00 +- [Amiga OS QOI datatype](https://github.com/dgaw/qoi-datatype) - adds support for decoding QOI images to the Amiga operating system. +- [SerenityOS](https://github.com/SerenityOS/serenity) - supports decoding QOI system wide through a custom [cpp implementation in LibGfx](https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibGfx/QOILoader.h) +- [Raylib](https://github.com/raysan5/raylib) - supports decoding and encoding QOI textures through its [rtextures module](https://github.com/raysan5/raylib/blob/master/src/rtextures.c) +- [Rebol3](https://github.com/Oldes/Rebol3/issues/39) - supports decoding and encoding QOI using a native codec +- [c-ray](https://github.com/vkoskiv/c-ray) - supports QOI natively +- [SAIL](https://sail.software) - image decoding library, supports decoding and encoding QOI images +- [Orx](https://github.com/orx/orx) - 2D game engine, supports QOI natively +- [IrfanView](https://www.irfanview.com) - supports decoding and encoding QOI through its Formats plugin +- [ImageMagick](https://github.com/ImageMagick/ImageMagick) - supports decoding and encoding QOI, since 7.1.0-20 +- [barebox](https://barebox.org) - bootloader, supports decoding QOI images for splash logo, since v2022.03.0 +- [KorGE](https://korge.org) - & KorIM Kotlin 2D game engine and imaging library, supports decoding and encoding QOI natively since 2.7.0 +- [DOjS](https://github.com/SuperIlu/DOjS) - DOS JavaScript Canvas implementation supports loading QOI files +- [XnView MP](https://www.xnview.com/en/xnviewmp/) - supports decoding QOI since 1.00 ## Packages From 02a49e541027d4676ec4721addb85ad51d879b1b Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 6 Aug 2022 00:06:55 +0200 Subject: [PATCH 168/208] Remove outdated implementations --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index 4188830..c56ecfe 100644 --- a/README.md +++ b/README.md @@ -146,11 +146,3 @@ either, as this "reference implementation" tries to be as easy to read as possib - [Ubuntu](https://launchpad.net/ubuntu/+source/qoi) - packages for binaries and qoi.h Packages for other systems [tracked at Repology](https://repology.org/project/qoi/versions). - - -## Implementations not yet conforming to the final specification - -These implementations are based on the pre-release version of QOI. Resulting files are not compatible with the current version. - -- https://github.com/ChevyRay/qoi_rs (Rust) -- https://github.com/panzi/jsqoi (TypeScript) From e4892c7aa2202dce64745416349e0eff0958df2b Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 6 Aug 2022 00:32:41 +0200 Subject: [PATCH 169/208] Add delphi implementation; close #144 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c56ecfe..a87afec 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [mzgreen/qoi-kotlin](https://github.com/mzgreen/qoi-kotlin) - Kotlin Multiplatform - [Aftersol/Simplified-QOI-Codec](https://github.com/Aftersol/Simplified-QOI-Codec) - C99, streaming encoder and decoder, freestanding - [AuburnSounds/gamut](https://github.com/AuburnSounds/gamut) - D +- [AngusJohnson/TQoiImage](https://github.com/AngusJohnson/TQoiImage) - Delphi ## QOI Support in Other Software From aca9552827b7eb6e858d0c922be5bcb653b80672 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 6 Aug 2022 00:34:50 +0200 Subject: [PATCH 170/208] Add MacOS QuickLook plugin; close #165 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a87afec..f05f85b 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [dan9er/farbfeld-convert-qoi](https://gitlab.com/dan9er/farbfeld-convert-qoi) - QOI <=> farbfeld converter - [Ben1138/unity-qo](https://github.com/Ben1138/unity-qoi) - QOI support for the Unity3D Game Engine - [xiaozhuai/jetbrains-qo](https://github.com/xiaozhuai/jetbrains-qoi) - [QOI Support](https://plugins.jetbrains.com/plugin/19352-qoi-support) for Jetbrains' IDE. +- [serge-ivamov/QOIql](https://github.com/serge-ivamov/QOIql) - MacOS QuickLook plugin for QOI ## Implementations & Bindings of QOI From 43240dbc200dd94d8bd1b2f3ede1d27ec8f788ff Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 6 Aug 2022 00:38:15 +0200 Subject: [PATCH 171/208] Add KDE & Nemo thumbnailers; close #174 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f05f85b..b6e04d7 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ either, as this "reference implementation" tries to be as easy to read as possib - [Ben1138/unity-qo](https://github.com/Ben1138/unity-qoi) - QOI support for the Unity3D Game Engine - [xiaozhuai/jetbrains-qo](https://github.com/xiaozhuai/jetbrains-qoi) - [QOI Support](https://plugins.jetbrains.com/plugin/19352-qoi-support) for Jetbrains' IDE. - [serge-ivamov/QOIql](https://github.com/serge-ivamov/QOIql) - MacOS QuickLook plugin for QOI +- [tobozo/kde-thumbnailer-qoi](https://github.com/tobozo/kde-thumbnailer-qoi) - QOI Thumbnailer for KDE +- [walksanatora/qoi-thumbnailer-nemo](https://github.com/walksanatora/qoi-thumbnailer-nemo) - QOI Thumbnailer for Nemo ## Implementations & Bindings of QOI From 425dfe122196e621a4cc167b047e92ddb11f4829 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 6 Aug 2022 00:46:10 +0200 Subject: [PATCH 172/208] Add Java SPI implementation; close #210 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b6e04d7..1117ec6 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [Aftersol/Simplified-QOI-Codec](https://github.com/Aftersol/Simplified-QOI-Codec) - C99, streaming encoder and decoder, freestanding - [AuburnSounds/gamut](https://github.com/AuburnSounds/gamut) - D - [AngusJohnson/TQoiImage](https://github.com/AngusJohnson/TQoiImage) - Delphi +- [MarkJeronimus/qoi-java-spi](https://github.com/MarkJeronimus/qoi-java-spi) - Java SPI ## QOI Support in Other Software From 23c790ce59e1419d348dc4b1ae7306a0914edf47 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sat, 6 Aug 2022 00:49:35 +0200 Subject: [PATCH 173/208] Add Racket implementation; close #217 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1117ec6..4dfbe13 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [AuburnSounds/gamut](https://github.com/AuburnSounds/gamut) - D - [AngusJohnson/TQoiImage](https://github.com/AngusJohnson/TQoiImage) - Delphi - [MarkJeronimus/qoi-java-spi](https://github.com/MarkJeronimus/qoi-java-spi) - Java SPI +- [aumouvantsillage/qoi-racket](https://github.com/aumouvantsillage/qoi-racket) - Racket ## QOI Support in Other Software From 21c840a2e7a404aa895161315104779677a95638 Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Mon, 12 Sep 2022 12:24:33 +0200 Subject: [PATCH 174/208] Windows Explorer, Finder, GNOME plugins released in qoi-ci --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4dfbe13..42073d7 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Tools - [floooh/qoiview](https://github.com/floooh/qoiview) - native QOI viewer -- [pfusik/qoi-ci](https://github.com/pfusik/qoi-ci/releases) - QOI Plugin installer for GIMP, Imagine, Paint.NET and XnView +- [pfusik/qoi-ci](https://github.com/pfusik/qoi-ci/releases) - QOI Plugin installer for Windows Explorer, Finder, GNOME, GIMP, Imagine, Paint.NET and XnView - [iOrange/QoiFileTypeNet](https://github.com/iOrange/QoiFileTypeNet/releases) - QOI Plugin for Paint.NET - [iOrange/QOIThumbnailProvider](https://github.com/iOrange/QOIThumbnailProvider) - Add thumbnails for QOI images in Windows Explorer - [Tom94/tev](https://github.com/Tom94/tev) - another native QOI viewer (allows pixel peeping and comparison with other image formats) From 63acdd2796e99bc52d8ff9c6257fdbc07634ef8a Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Tue, 20 Sep 2022 00:02:59 -0700 Subject: [PATCH 175/208] Add timg to tool list; provides qoi image viewing in terminal. Signed-off-by: Henner Zeller --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42073d7..6fe849b 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [serge-ivamov/QOIql](https://github.com/serge-ivamov/QOIql) - MacOS QuickLook plugin for QOI - [tobozo/kde-thumbnailer-qoi](https://github.com/tobozo/kde-thumbnailer-qoi) - QOI Thumbnailer for KDE - [walksanatora/qoi-thumbnailer-nemo](https://github.com/walksanatora/qoi-thumbnailer-nemo) - QOI Thumbnailer for Nemo - +- [hzeller/timg](https://github.com/hzeller/timg) - a terminal image viewer with QOI support ## Implementations & Bindings of QOI From 07c6d6f7d737e8c5a37a92072f0580fe7e1fec4d Mon Sep 17 00:00:00 2001 From: Luis Alfredo Figueroa Bracamontes <92luisalfredo@protonmail.com> Date: Tue, 20 Sep 2022 22:22:36 -0500 Subject: [PATCH 176/208] Adding Super QOi converter to Tools Adding Super QOI converter to Tools section, with links to console and GUI version --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6fe849b..f4550bd 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,9 @@ either, as this "reference implementation" tries to be as easy to read as possib - [tobozo/kde-thumbnailer-qoi](https://github.com/tobozo/kde-thumbnailer-qoi) - QOI Thumbnailer for KDE - [walksanatora/qoi-thumbnailer-nemo](https://github.com/walksanatora/qoi-thumbnailer-nemo) - QOI Thumbnailer for Nemo - [hzeller/timg](https://github.com/hzeller/timg) - a terminal image viewer with QOI support +- [LuisAlfredo92/Super-QOI-converter](https://github.com/LuisAlfredo92/Super-QOI-converter "LuisAlfredo92/Super-QOI-converter") - A program to convert JPG, JPEG, BMP, and PNG to QOI + - [Console version](https://github.com/LuisAlfredo92/Super-QOI-converter-Console- "Console version"): Available for Linux, OSX and Windows + - [GUI version](https://github.com/LuisAlfredo92/Super-QOI-converter-GUI- "GUI version"): Available only for windows ## Implementations & Bindings of QOI From 3a0560cb779cec566a5705fd6fb790ae220a7bb8 Mon Sep 17 00:00:00 2001 From: rubikscraft Date: Sat, 24 Sep 2022 16:13:23 +0200 Subject: [PATCH 177/208] Add a streaming C qoi library and nodejs bindings --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f4550bd..1dcf231 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,8 @@ either, as this "reference implementation" tries to be as easy to read as possib - [AngusJohnson/TQoiImage](https://github.com/AngusJohnson/TQoiImage) - Delphi - [MarkJeronimus/qoi-java-spi](https://github.com/MarkJeronimus/qoi-java-spi) - Java SPI - [aumouvantsillage/qoi-racket](https://github.com/aumouvantsillage/qoi-racket) - Racket +- [rubikscraft/qoi-stream](https://github.com/rubikscraft/qoi-stream) - C99, one byte at a time streaming encoder and decoder +- [rubikscraft/qoi-img](https://github.com/rubikscraft/qoi-img) - NodeJS typescript, bindings to both [QOIxx](https://github.com/wx257osn2/qoixx) and [qoi-stream](https://github.com/rubikscraft/qoi-stream) ## QOI Support in Other Software From 5c787e41737c6f2ea0dd95ed56b536ae733b96a2 Mon Sep 17 00:00:00 2001 From: Luis Batalha Date: Wed, 16 Nov 2022 15:52:39 +0000 Subject: [PATCH 178/208] add CFLAGS as per gnu standards --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4d21d41..4dc4f9a 100644 --- a/Makefile +++ b/Makefile @@ -11,11 +11,11 @@ all: $(TARGET_BENCH) $(TARGET_CONV) bench: $(TARGET_BENCH) $(TARGET_BENCH):$(TARGET_BENCH).c - $(CC) $(CFLAGS_BENCH) $(TARGET_BENCH).c -o $(TARGET_BENCH) $(LFLAGS_BENCH) + $(CC) $(CFLAGS_BENCH) $(CFLAGS) $(TARGET_BENCH).c -o $(TARGET_BENCH) $(LFLAGS_BENCH) conv: $(TARGET_CONV) $(TARGET_CONV):$(TARGET_CONV).c - $(CC) $(CFLAGS_CONV) $(TARGET_CONV).c -o $(TARGET_CONV) + $(CC) $(CFLAGS_CONV) $(CFLAGS) $(TARGET_CONV).c -o $(TARGET_CONV) .PHONY: clean clean: From c3dcfe780bf28f4a59bca7f0a60e2e18b0acf68f Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Tue, 13 Dec 2022 21:08:09 +0100 Subject: [PATCH 179/208] Add LICENSE file; close #250 --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..aaaa5f7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Dominic Szablewski + +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. From d70233b2c691545c29eb63d957b7f0722f0deaaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maro=C5=A1=20Grego?= Date: Thu, 19 Jan 2023 21:44:26 +0100 Subject: [PATCH 180/208] Add a link to a Hare implementation to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1dcf231..e4c4138 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [aumouvantsillage/qoi-racket](https://github.com/aumouvantsillage/qoi-racket) - Racket - [rubikscraft/qoi-stream](https://github.com/rubikscraft/qoi-stream) - C99, one byte at a time streaming encoder and decoder - [rubikscraft/qoi-img](https://github.com/rubikscraft/qoi-img) - NodeJS typescript, bindings to both [QOIxx](https://github.com/wx257osn2/qoixx) and [qoi-stream](https://github.com/rubikscraft/qoi-stream) +- [grego/hare-qoi](https://git.sr.ht/~grego/hare-qoi) - Hare ## QOI Support in Other Software From dc4b97471a6bc65a2dd207391c18a1c3da544bfa Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sun, 22 Jan 2023 20:03:01 +0100 Subject: [PATCH 181/208] Mention tacentview --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e4c4138..cc38f43 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [LuisAlfredo92/Super-QOI-converter](https://github.com/LuisAlfredo92/Super-QOI-converter "LuisAlfredo92/Super-QOI-converter") - A program to convert JPG, JPEG, BMP, and PNG to QOI - [Console version](https://github.com/LuisAlfredo92/Super-QOI-converter-Console- "Console version"): Available for Linux, OSX and Windows - [GUI version](https://github.com/LuisAlfredo92/Super-QOI-converter-GUI- "GUI version"): Available only for windows +- [tacent view](https://github.com/bluescan/tacentview) - Image and texture viewer, supports QOI ## Implementations & Bindings of QOI From c0a27f808f28182f9afe18ce80c1db8252806c58 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Sun, 22 Jan 2023 20:03:08 +0100 Subject: [PATCH 182/208] Mention ZTQOI --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cc38f43..148f5a8 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [rubikscraft/qoi-stream](https://github.com/rubikscraft/qoi-stream) - C99, one byte at a time streaming encoder and decoder - [rubikscraft/qoi-img](https://github.com/rubikscraft/qoi-img) - NodeJS typescript, bindings to both [QOIxx](https://github.com/wx257osn2/qoixx) and [qoi-stream](https://github.com/rubikscraft/qoi-stream) - [grego/hare-qoi](https://git.sr.ht/~grego/hare-qoi) - Hare +- [MrNocole/ZTQOI](https://github.com/MrNocole/ZTQOI) - Objective-C ## QOI Support in Other Software From 514c259711a8233fefddbb9afe6cbf28fa5bbf78 Mon Sep 17 00:00:00 2001 From: Dominic Szablewski Date: Mon, 6 Feb 2023 13:35:58 +0100 Subject: [PATCH 183/208] Mention ffmpeg support; close #259 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 148f5a8..af1cac2 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [KorGE](https://korge.org) - & KorIM Kotlin 2D game engine and imaging library, supports decoding and encoding QOI natively since 2.7.0 - [DOjS](https://github.com/SuperIlu/DOjS) - DOS JavaScript Canvas implementation supports loading QOI files - [XnView MP](https://www.xnview.com/en/xnviewmp/) - supports decoding QOI since 1.00 +- [ffmpeg](https://ffmpeg.org/) - supports decoding and encoding QOI since 5.1 ## Packages From 6d5a7ab2fd3f754c40e940f552c3f7b94cb6e2b2 Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Thu, 9 Feb 2023 08:01:07 +0100 Subject: [PATCH 184/208] Mention qoi-ci transpiling to TypeScript --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af1cac2..51d7a6a 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Implementations & Bindings of QOI -- [pfusik/qoi-ci](https://github.com/pfusik/qoi-ci) - Ć, transpiled to C, C++, C#, Java, JavaScript, Python and Swift +- [pfusik/qoi-ci](https://github.com/pfusik/qoi-ci) - Ć, transpiling to C, C++, C#, Java, JavaScript, Python, Swift and TypeScript - [kodonnell/qoi](https://github.com/kodonnell/qoi) - Python - [JaffaKetchup/dqoi](https://github.com/JaffaKetchup/dqoi) - Dart, with Flutter support - [Cr4xy/lua-qoi](https://github.com/Cr4xy/lua-qoi) - Lua From 013c74528483a7fe5e3447ed623f6ef340414dcc Mon Sep 17 00:00:00 2001 From: LTMX Date: Fri, 24 Feb 2023 14:20:00 +0100 Subject: [PATCH 185/208] Mention LTMX/Unity.QOI in Tools section --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 51d7a6a..31f674f 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,8 @@ either, as this "reference implementation" tries to be as easy to read as possib - [rtexpacker](https://raylibtech.itch.io/rtexpacker) - texture packer, supports QOI - [DmitriySalnikov/godot_qoi](https://github.com/DmitriySalnikov/godot_qoi) - QOI GDNative Addon for Godot Engine - [dan9er/farbfeld-convert-qoi](https://gitlab.com/dan9er/farbfeld-convert-qoi) - QOI <=> farbfeld converter -- [Ben1138/unity-qo](https://github.com/Ben1138/unity-qoi) - QOI support for the Unity3D Game Engine +- [LTMX/Unity.QOI](https://github.com/LTMX/Unity.QOI) - QOI Importer and Exporter for the Unity3D Game Engine +- [Ben1138/unity-qoi](https://github.com/Ben1138/unity-qoi) - QOI Importer(only) support for the Unity3D Game Engine - [xiaozhuai/jetbrains-qo](https://github.com/xiaozhuai/jetbrains-qoi) - [QOI Support](https://plugins.jetbrains.com/plugin/19352-qoi-support) for Jetbrains' IDE. - [serge-ivamov/QOIql](https://github.com/serge-ivamov/QOIql) - MacOS QuickLook plugin for QOI - [tobozo/kde-thumbnailer-qoi](https://github.com/tobozo/kde-thumbnailer-qoi) - QOI Thumbnailer for KDE From 040f8a15e555d25ad6aa2b03af796a511b4fadfb Mon Sep 17 00:00:00 2001 From: Kevin M Date: Thu, 2 Mar 2023 00:49:34 -0800 Subject: [PATCH 186/208] add JPEGView to end of Other Software list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 31f674f..6644e17 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [DOjS](https://github.com/SuperIlu/DOjS) - DOS JavaScript Canvas implementation supports loading QOI files - [XnView MP](https://www.xnview.com/en/xnviewmp/) - supports decoding QOI since 1.00 - [ffmpeg](https://ffmpeg.org/) - supports decoding and encoding QOI since 5.1 +- [JPEGView](https://github.com/sylikc/jpegview) - lightweight Windows image viewer, supports decoding and encoding of QOI natively, since 1.1.44 ## Packages From 1ae0c194923a08c92a80e2445f71d74bce9c3d20 Mon Sep 17 00:00:00 2001 From: Bibek Panthi Date: Sun, 30 Apr 2023 19:14:36 +0545 Subject: [PATCH 187/208] Add link to Common Lisp implementation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6644e17..942bf29 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [rubikscraft/qoi-img](https://github.com/rubikscraft/qoi-img) - NodeJS typescript, bindings to both [QOIxx](https://github.com/wx257osn2/qoixx) and [qoi-stream](https://github.com/rubikscraft/qoi-stream) - [grego/hare-qoi](https://git.sr.ht/~grego/hare-qoi) - Hare - [MrNocole/ZTQOI](https://github.com/MrNocole/ZTQOI) - Objective-C +- [bpanthi977/qoi](https://github.com/bpanthi977/qoi) - Common Lisp ## QOI Support in Other Software From 76e37890733162e6b9529c60a83ec81ba18bb34b Mon Sep 17 00:00:00 2001 From: Aftersol <42478428+Aftersol@users.noreply.github.com> Date: Wed, 17 May 2023 13:32:51 -0700 Subject: [PATCH 188/208] Update README.md small change to my link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6644e17..48d6b28 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [Imagine-Programming/QoiImagePlugin](https://github.com/Imagine-Programming/QoiImagePlugin) - PureBasic - [Fabien-Chouteau/qoi-spark](https://github.com/Fabien-Chouteau/qoi-spark) - Ada/SPARK formally proven - [mzgreen/qoi-kotlin](https://github.com/mzgreen/qoi-kotlin) - Kotlin Multiplatform -- [Aftersol/Simplified-QOI-Codec](https://github.com/Aftersol/Simplified-QOI-Codec) - C99, streaming encoder and decoder, freestanding +- [Aftersol/Simplified-QOI-Codec](https://github.com/Aftersol/Simplified-QOI-Codec) - C99, encoder and decoder, freestanding - [AuburnSounds/gamut](https://github.com/AuburnSounds/gamut) - D - [AngusJohnson/TQoiImage](https://github.com/AngusJohnson/TQoiImage) - Delphi - [MarkJeronimus/qoi-java-spi](https://github.com/MarkJeronimus/qoi-java-spi) - Java SPI From 00dfdc8b5c941287341b9dbc9e65a0d3c4fd4aa4 Mon Sep 17 00:00:00 2001 From: NRK Date: Thu, 15 Jun 2023 14:32:33 +0600 Subject: [PATCH 189/208] Error check qoi_write() more strictly simply checking the return value of fwrite() wouldn't be enough since stdio is typically buffered. and so force a flush and check for errors via ferror(). --- qoi.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qoi.h b/qoi.h index 6734ac4..eb01232 100644 --- a/qoi.h +++ b/qoi.h @@ -594,7 +594,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { FILE *f = fopen(filename, "wb"); - int size; + int size, err; void *encoded; if (!f) { @@ -608,10 +608,12 @@ int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { } fwrite(encoded, 1, size, f); + fflush(f); + err = ferror(f); fclose(f); QOI_FREE(encoded); - return size; + return err ? 0 : size; } void *qoi_read(const char *filename, qoi_desc *desc, int channels) { From 36190eb07dc5d85f408d998d1884eb69573adf68 Mon Sep 17 00:00:00 2001 From: NRK Date: Thu, 15 Jun 2023 14:43:24 +0600 Subject: [PATCH 190/208] Error check qoi_read() more strictly fseek is not guaranteed to succeed and can fail, e.g when trying to seek a pipe. the first fseek() is not error checked since if it failed, ftell would return 0. also check for fread errors too before calling qoi_decode(). --- qoi.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qoi.h b/qoi.h index eb01232..f2800b0 100644 --- a/qoi.h +++ b/qoi.h @@ -627,11 +627,10 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { fseek(f, 0, SEEK_END); size = ftell(f); - if (size <= 0) { + if (size <= 0 || fseek(f, 0, SEEK_SET) != 0) { fclose(f); return NULL; } - fseek(f, 0, SEEK_SET); data = QOI_MALLOC(size); if (!data) { @@ -641,8 +640,7 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { bytes_read = fread(data, 1, size, f); fclose(f); - - pixels = qoi_decode(data, bytes_read, desc, channels); + pixels = (bytes_read != size) ? NULL : qoi_decode(data, bytes_read, desc, channels); QOI_FREE(data); return pixels; } From 706ed3eb68a4ad848310d98aa5f35294dd1cee64 Mon Sep 17 00:00:00 2001 From: NRK Date: Fri, 16 Jun 2023 17:17:47 +0600 Subject: [PATCH 191/208] Less hardcoded benchmarking code this allows people to more easily plugin their implementation for benchmarking purposes. --- qoibench.c | 114 ++++++++++++++++++++++------------------------------- 1 file changed, 48 insertions(+), 66 deletions(-) diff --git a/qoibench.c b/qoibench.c index 05762c6..e447c5a 100644 --- a/qoibench.c +++ b/qoibench.c @@ -303,6 +303,18 @@ int opt_noencode = 0; int opt_norecurse = 0; int opt_onlytotals = 0; +enum { + LIBPNG, + STBI, + QOI, + BENCH_COUNT /* must be the last element */ +}; +static const char *const lib_names[BENCH_COUNT] = { + // NOTE: pad with spaces so everything lines up properly + [LIBPNG] = "libpng: ", + [STBI] = "stbi: ", + [QOI] = "qoi: ", +}; typedef struct { uint64_t size; @@ -316,56 +328,34 @@ typedef struct { uint64_t px; int w; int h; - benchmark_lib_result_t libpng; - benchmark_lib_result_t stbi; - benchmark_lib_result_t qoi; + benchmark_lib_result_t libs[BENCH_COUNT]; } benchmark_result_t; void benchmark_print_result(benchmark_result_t res) { res.px /= res.count; res.raw_size /= res.count; - res.libpng.encode_time /= res.count; - res.libpng.decode_time /= res.count; - res.libpng.size /= res.count; - res.stbi.encode_time /= res.count; - res.stbi.decode_time /= res.count; - res.stbi.size /= res.count; - res.qoi.encode_time /= res.count; - res.qoi.decode_time /= res.count; - res.qoi.size /= res.count; double px = res.px; - printf(" decode ms encode ms decode mpps encode mpps size kb rate\n"); - if (!opt_nopng) { + printf(" decode ms encode ms decode mpps encode mpps size kb rate\n"); + for (int i = 0; i < BENCH_COUNT; ++i) { + if (opt_nopng && (i == LIBPNG || i == STBI)) { + continue; + } + res.libs[i].encode_time /= res.count; + res.libs[i].decode_time /= res.count; + res.libs[i].size /= res.count; printf( - "libpng: %8.1f %8.1f %8.2f %8.2f %8ld %4.1f%%\n", - (double)res.libpng.decode_time/1000000.0, - (double)res.libpng.encode_time/1000000.0, - (res.libpng.decode_time > 0 ? px / ((double)res.libpng.decode_time/1000.0) : 0), - (res.libpng.encode_time > 0 ? px / ((double)res.libpng.encode_time/1000.0) : 0), - res.libpng.size/1024, - ((double)res.libpng.size/(double)res.raw_size) * 100.0 - ); - printf( - "stbi: %8.1f %8.1f %8.2f %8.2f %8ld %4.1f%%\n", - (double)res.stbi.decode_time/1000000.0, - (double)res.stbi.encode_time/1000000.0, - (res.stbi.decode_time > 0 ? px / ((double)res.stbi.decode_time/1000.0) : 0), - (res.stbi.encode_time > 0 ? px / ((double)res.stbi.encode_time/1000.0) : 0), - res.stbi.size/1024, - ((double)res.stbi.size/(double)res.raw_size) * 100.0 + "%s %8.1f %8.1f %8.2f %8.2f %8ld %4.1f%%\n", + lib_names[i], + (double)res.libs[i].decode_time/1000000.0, + (double)res.libs[i].encode_time/1000000.0, + (res.libs[i].decode_time > 0 ? px / ((double)res.libs[i].decode_time/1000.0) : 0), + (res.libs[i].encode_time > 0 ? px / ((double)res.libs[i].encode_time/1000.0) : 0), + res.libs[i].size/1024, + ((double)res.libs[i].size/(double)res.raw_size) * 100.0 ); } - printf( - "qoi: %8.1f %8.1f %8.2f %8.2f %8ld %4.1f%%\n", - (double)res.qoi.decode_time/1000000.0, - (double)res.qoi.encode_time/1000000.0, - (res.qoi.decode_time > 0 ? px / ((double)res.qoi.decode_time/1000.0) : 0), - (res.qoi.encode_time > 0 ? px / ((double)res.qoi.encode_time/1000.0) : 0), - res.qoi.size/1024, - ((double)res.qoi.size/(double)res.raw_size) * 100.0 - ); printf("\n"); } @@ -440,20 +430,20 @@ benchmark_result_t benchmark_image(const char *path) { if (!opt_nodecode) { if (!opt_nopng) { - BENCHMARK_FN(opt_nowarmup, opt_runs, res.libpng.decode_time, { + BENCHMARK_FN(opt_nowarmup, opt_runs, res.libs[LIBPNG].decode_time, { int dec_w, dec_h; void *dec_p = libpng_decode(encoded_png, encoded_png_size, &dec_w, &dec_h); free(dec_p); }); - BENCHMARK_FN(opt_nowarmup, opt_runs, res.stbi.decode_time, { + BENCHMARK_FN(opt_nowarmup, opt_runs, res.libs[STBI].decode_time, { int dec_w, dec_h, dec_channels; void *dec_p = stbi_load_from_memory(encoded_png, encoded_png_size, &dec_w, &dec_h, &dec_channels, 4); free(dec_p); }); } - BENCHMARK_FN(opt_nowarmup, opt_runs, res.qoi.decode_time, { + BENCHMARK_FN(opt_nowarmup, opt_runs, res.libs[QOI].decode_time, { qoi_desc desc; void *dec_p = qoi_decode(encoded_qoi, encoded_qoi_size, &desc, 4); free(dec_p); @@ -464,21 +454,21 @@ benchmark_result_t benchmark_image(const char *path) { // Encoding if (!opt_noencode) { if (!opt_nopng) { - BENCHMARK_FN(opt_nowarmup, opt_runs, res.libpng.encode_time, { + BENCHMARK_FN(opt_nowarmup, opt_runs, res.libs[LIBPNG].encode_time, { int enc_size; void *enc_p = libpng_encode(pixels, w, h, channels, &enc_size); - res.libpng.size = enc_size; + res.libs[LIBPNG].size = enc_size; free(enc_p); }); - BENCHMARK_FN(opt_nowarmup, opt_runs, res.stbi.encode_time, { + BENCHMARK_FN(opt_nowarmup, opt_runs, res.libs[STBI].encode_time, { int enc_size = 0; stbi_write_png_to_func(stbi_write_callback, &enc_size, w, h, channels, pixels, 0); - res.stbi.size = enc_size; + res.libs[STBI].size = enc_size; }); } - BENCHMARK_FN(opt_nowarmup, opt_runs, res.qoi.encode_time, { + BENCHMARK_FN(opt_nowarmup, opt_runs, res.libs[QOI].encode_time, { int enc_size; void *enc_p = qoi_encode(pixels, &(qoi_desc){ .width = w, @@ -486,7 +476,7 @@ benchmark_result_t benchmark_image(const char *path) { .channels = channels, .colorspace = QOI_SRGB }, &enc_size); - res.qoi.size = enc_size; + res.libs[QOI].size = enc_size; free(enc_p); }); } @@ -549,28 +539,20 @@ void benchmark_directory(const char *path, benchmark_result_t *grand_total) { dir_total.count++; dir_total.raw_size += res.raw_size; dir_total.px += res.px; - dir_total.libpng.encode_time += res.libpng.encode_time; - dir_total.libpng.decode_time += res.libpng.decode_time; - dir_total.libpng.size += res.libpng.size; - dir_total.stbi.encode_time += res.stbi.encode_time; - dir_total.stbi.decode_time += res.stbi.decode_time; - dir_total.stbi.size += res.stbi.size; - dir_total.qoi.encode_time += res.qoi.encode_time; - dir_total.qoi.decode_time += res.qoi.decode_time; - dir_total.qoi.size += res.qoi.size; + for (int i = 0; i < BENCH_COUNT; ++i) { + dir_total.libs[i].encode_time += res.libs[i].encode_time; + dir_total.libs[i].decode_time += res.libs[i].decode_time; + dir_total.libs[i].size += res.libs[i].size; + } grand_total->count++; grand_total->raw_size += res.raw_size; grand_total->px += res.px; - grand_total->libpng.encode_time += res.libpng.encode_time; - grand_total->libpng.decode_time += res.libpng.decode_time; - grand_total->libpng.size += res.libpng.size; - grand_total->stbi.encode_time += res.stbi.encode_time; - grand_total->stbi.decode_time += res.stbi.decode_time; - grand_total->stbi.size += res.stbi.size; - grand_total->qoi.encode_time += res.qoi.encode_time; - grand_total->qoi.decode_time += res.qoi.decode_time; - grand_total->qoi.size += res.qoi.size; + for (int i = 0; i < BENCH_COUNT; ++i) { + grand_total->libs[i].encode_time += res.libs[i].encode_time; + grand_total->libs[i].decode_time += res.libs[i].decode_time; + grand_total->libs[i].size += res.libs[i].size; + } } closedir(dir); From 1527109c284fa94c3157e3f34407c5151dfd78ab Mon Sep 17 00:00:00 2001 From: Victor Forsiuk Date: Mon, 17 Jul 2023 21:33:38 +0300 Subject: [PATCH 192/208] Mention QOI support in darktable --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6b83fe7..398159c 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [XnView MP](https://www.xnview.com/en/xnviewmp/) - supports decoding QOI since 1.00 - [ffmpeg](https://ffmpeg.org/) - supports decoding and encoding QOI since 5.1 - [JPEGView](https://github.com/sylikc/jpegview) - lightweight Windows image viewer, supports decoding and encoding of QOI natively, since 1.1.44 +- [darktable](https://github.com/darktable-org/darktable) - photography workflow application and raw developer, supports decoding since 4.4.0 ## Packages From 4adab9d4e0a48155d231db7ab311414cd418ce17 Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Wed, 9 Aug 2023 14:28:11 +0200 Subject: [PATCH 193/208] qoi-ci renamed to qoi-fu --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 398159c..aeaedc2 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Implementations & Bindings of QOI -- [pfusik/qoi-ci](https://github.com/pfusik/qoi-ci) - Ć, transpiling to C, C++, C#, Java, JavaScript, Python, Swift and TypeScript +- [pfusik/qoi-fu](https://github.com/pfusik/qoi-fu) - Fusion, transpiling to C, C++, C#, Java, JavaScript, Python, Swift and TypeScript - [kodonnell/qoi](https://github.com/kodonnell/qoi) - Python - [JaffaKetchup/dqoi](https://github.com/JaffaKetchup/dqoi) - Dart, with Flutter support - [Cr4xy/lua-qoi](https://github.com/Cr4xy/lua-qoi) - Lua From a06ba04fa4434d7abb2d38287a545fa08ab128cf Mon Sep 17 00:00:00 2001 From: Ernest Gupik Date: Fri, 11 Aug 2023 23:33:22 +0200 Subject: [PATCH 194/208] Mention QOI support in KDE --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aeaedc2..706dc3c 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [ffmpeg](https://ffmpeg.org/) - supports decoding and encoding QOI since 5.1 - [JPEGView](https://github.com/sylikc/jpegview) - lightweight Windows image viewer, supports decoding and encoding of QOI natively, since 1.1.44 - [darktable](https://github.com/darktable-org/darktable) - photography workflow application and raw developer, supports decoding since 4.4.0 +- [KDE](https://kde.org) - supports decoding QOI images. Implemented in [KImageFormats](https://invent.kde.org/frameworks/kimageformats) ## Packages From 4231ace0452244ce04dfc550036b5df52e89acdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Sat, 19 Aug 2023 11:28:21 +0200 Subject: [PATCH 195/208] Add link to pam2qoi --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 706dc3c..838ae4f 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [grego/hare-qoi](https://git.sr.ht/~grego/hare-qoi) - Hare - [MrNocole/ZTQOI](https://github.com/MrNocole/ZTQOI) - Objective-C - [bpanthi977/qoi](https://github.com/bpanthi977/qoi) - Common Lisp +- [Floessie/pam2qoi](https://github.com/Floessie/pam2qoi) - C++ ## QOI Support in Other Software From 972a28c9551a60666c10492118c0b82306f3757e Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Fri, 25 Aug 2023 09:27:50 +0200 Subject: [PATCH 196/208] qoi-fu transpiles to D --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 838ae4f..95977a8 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Tools - [floooh/qoiview](https://github.com/floooh/qoiview) - native QOI viewer -- [pfusik/qoi-ci](https://github.com/pfusik/qoi-ci/releases) - QOI Plugin installer for Windows Explorer, Finder, GNOME, GIMP, Imagine, Paint.NET and XnView +- [pfusik/qoi-fu](https://github.com/pfusik/qoi-fu/releases) - QOI Plugin installer for Windows Explorer, Finder, GNOME, GIMP, Imagine, Paint.NET and XnView - [iOrange/QoiFileTypeNet](https://github.com/iOrange/QoiFileTypeNet/releases) - QOI Plugin for Paint.NET - [iOrange/QOIThumbnailProvider](https://github.com/iOrange/QOIThumbnailProvider) - Add thumbnails for QOI images in Windows Explorer - [Tom94/tev](https://github.com/Tom94/tev) - another native QOI viewer (allows pixel peeping and comparison with other image formats) @@ -92,7 +92,7 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Implementations & Bindings of QOI -- [pfusik/qoi-fu](https://github.com/pfusik/qoi-fu) - Fusion, transpiling to C, C++, C#, Java, JavaScript, Python, Swift and TypeScript +- [pfusik/qoi-fu](https://github.com/pfusik/qoi-fu) - Fusion, transpiling to C, C++, C#, D, Java, JavaScript, Python, Swift and TypeScript - [kodonnell/qoi](https://github.com/kodonnell/qoi) - Python - [JaffaKetchup/dqoi](https://github.com/JaffaKetchup/dqoi) - Dart, with Flutter support - [Cr4xy/lua-qoi](https://github.com/Cr4xy/lua-qoi) - Lua From 855cd4c61e2cb4e3e916262cd8b83e846d80c4cf Mon Sep 17 00:00:00 2001 From: Specky Date: Sun, 27 Aug 2023 22:56:32 +0200 Subject: [PATCH 197/208] Add link to spwn-qoi --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 95977a8..9b709af 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [MrNocole/ZTQOI](https://github.com/MrNocole/ZTQOI) - Objective-C - [bpanthi977/qoi](https://github.com/bpanthi977/qoi) - Common Lisp - [Floessie/pam2qoi](https://github.com/Floessie/pam2qoi) - C++ +- [SpeckyYT/spwn-qoi](https://github.com/SpeckyYT/spwn-qoi) - SPWN ## QOI Support in Other Software From dbd68f185f32ffe9b0cf0ae170c95ae7b97b9be3 Mon Sep 17 00:00:00 2001 From: Ernest Gupik Date: Thu, 31 Aug 2023 17:36:53 +0200 Subject: [PATCH 198/208] KDE now has encoding support as well --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b709af..7b7d136 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [ffmpeg](https://ffmpeg.org/) - supports decoding and encoding QOI since 5.1 - [JPEGView](https://github.com/sylikc/jpegview) - lightweight Windows image viewer, supports decoding and encoding of QOI natively, since 1.1.44 - [darktable](https://github.com/darktable-org/darktable) - photography workflow application and raw developer, supports decoding since 4.4.0 -- [KDE](https://kde.org) - supports decoding QOI images. Implemented in [KImageFormats](https://invent.kde.org/frameworks/kimageformats) +- [KDE](https://kde.org) - supports decoding and encoding QOI images. Implemented in [KImageFormats](https://invent.kde.org/frameworks/kimageformats) ## Packages From 52d9ad5024291a0b83a1344a39a1cb595d20f76c Mon Sep 17 00:00:00 2001 From: colemanrgb <115294966+colemanrgb@users.noreply.github.com> Date: Sat, 2 Sep 2023 12:03:35 +0100 Subject: [PATCH 199/208] Add link to decoding and encoding QOI files on RISC OS --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7b7d136..e37c842 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [Console version](https://github.com/LuisAlfredo92/Super-QOI-converter-Console- "Console version"): Available for Linux, OSX and Windows - [GUI version](https://github.com/LuisAlfredo92/Super-QOI-converter-GUI- "GUI version"): Available only for windows - [tacent view](https://github.com/bluescan/tacentview) - Image and texture viewer, supports QOI +- [colemanrgb/qoi2spr](https://github.com/colemanrgb/qoi2spr) - A variety of applications for decoding and encoding of QOI images on [RISC OS](https://www.riscosopen.org/) ## Implementations & Bindings of QOI From d23d8f3feaf10b33c983678939b849b9af03329b Mon Sep 17 00:00:00 2001 From: n00bmind Date: Sun, 3 Sep 2023 23:15:36 +0100 Subject: [PATCH 200/208] + Add link to Jai implementation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7b7d136..8b9e10a 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [bpanthi977/qoi](https://github.com/bpanthi977/qoi) - Common Lisp - [Floessie/pam2qoi](https://github.com/Floessie/pam2qoi) - C++ - [SpeckyYT/spwn-qoi](https://github.com/SpeckyYT/spwn-qoi) - SPWN +- [n00bmind/qoi.jai](https://github.com/n00bmind/qoi.jai) - Jai ## QOI Support in Other Software From dcfec1a44da17800c4f9ee33d34931790fd2faa0 Mon Sep 17 00:00:00 2001 From: n00bmind Date: Sun, 10 Sep 2023 19:19:34 +0100 Subject: [PATCH 201/208] ~ Update link to n00bmind/qoi --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc0bc6c..5f97c70 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [bpanthi977/qoi](https://github.com/bpanthi977/qoi) - Common Lisp - [Floessie/pam2qoi](https://github.com/Floessie/pam2qoi) - C++ - [SpeckyYT/spwn-qoi](https://github.com/SpeckyYT/spwn-qoi) - SPWN -- [n00bmind/qoi.jai](https://github.com/n00bmind/qoi.jai) - Jai +- [n00bmind/qoi](https://github.com/n00bmind/qoi) - Jai ## QOI Support in Other Software From 26130106d92c5c4f10633e2c28d6b4ad2db6553f Mon Sep 17 00:00:00 2001 From: Vincent Torri Date: Tue, 17 Oct 2023 17:31:26 +0200 Subject: [PATCH 202/208] Mention QOI support in the EFL --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5f97c70..9ae67cb 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [JPEGView](https://github.com/sylikc/jpegview) - lightweight Windows image viewer, supports decoding and encoding of QOI natively, since 1.1.44 - [darktable](https://github.com/darktable-org/darktable) - photography workflow application and raw developer, supports decoding since 4.4.0 - [KDE](https://kde.org) - supports decoding and encoding QOI images. Implemented in [KImageFormats](https://invent.kde.org/frameworks/kimageformats) +- [EFL](https://www.enlightenment.org) - supports decoding and encoding QOI images since 1.27. ## Packages From 6e949186e63f5bc6febd114b4ae6393a155ae1f7 Mon Sep 17 00:00:00 2001 From: Phil Ashby Date: Thu, 19 Oct 2023 11:12:49 +0100 Subject: [PATCH 203/208] Added Swingland implemenation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9ae67cb..0c21976 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [darktable](https://github.com/darktable-org/darktable) - photography workflow application and raw developer, supports decoding since 4.4.0 - [KDE](https://kde.org) - supports decoding and encoding QOI images. Implemented in [KImageFormats](https://invent.kde.org/frameworks/kimageformats) - [EFL](https://www.enlightenment.org) - supports decoding and encoding QOI images since 1.27. +- [Swingland](https://git.sr.ht/~phlash/swingland) - supports QOI decoding/loading via the `ImageIO` API of this Java Swing reimplemenation for Wayland ## Packages From abde91410ea6e4a9d9a0d816f22a0e8c0b5434ad Mon Sep 17 00:00:00 2001 From: Luis Alfredo Figueroa Bracamontes <92luisalfredo@protonmail.com> Date: Fri, 1 Dec 2023 19:46:19 -0600 Subject: [PATCH 204/208] Update README.md Adding SixLabors/ImageSharp --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0c21976..393ee69 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [Floessie/pam2qoi](https://github.com/Floessie/pam2qoi) - C++ - [SpeckyYT/spwn-qoi](https://github.com/SpeckyYT/spwn-qoi) - SPWN - [n00bmind/qoi](https://github.com/n00bmind/qoi) - Jai +- [SixLabors/ImageSharp](https://github.com/SixLabors/ImageSharp) - C# image proccesing library ## QOI Support in Other Software From 35054beeb9002ed620a65d8d1f60486c67a2db76 Mon Sep 17 00:00:00 2001 From: Gautier de Montmollin Date: Sat, 9 Dec 2023 14:22:00 +0100 Subject: [PATCH 205/208] Added implementation of QOI in Ada The link refers to the Generic Image Decoder (GID) library. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 393ee69..9e49f12 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [SpeckyYT/spwn-qoi](https://github.com/SpeckyYT/spwn-qoi) - SPWN - [n00bmind/qoi](https://github.com/n00bmind/qoi) - Jai - [SixLabors/ImageSharp](https://github.com/SixLabors/ImageSharp) - C# image proccesing library +- [zertovitch/gid](https://github.com/zertovitch/gid) - Ada ## QOI Support in Other Software From bc8242a28df08143b7fd25a531d72924867cb2a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Finnur=20=C3=9E=C3=B3risson?= <153488719+finnurthorisson@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:26:13 +0000 Subject: [PATCH 206/208] Add Lua LIL --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9e49f12..1aa2df3 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [n00bmind/qoi](https://github.com/n00bmind/qoi) - Jai - [SixLabors/ImageSharp](https://github.com/SixLabors/ImageSharp) - C# image proccesing library - [zertovitch/gid](https://github.com/zertovitch/gid) - Ada +- [nazrin/lil](https://codeberg.org/nazrin/lil) - Lua image library ## QOI Support in Other Software From e28e20ea83ccf22ea32356047e60b3a9e77a620a Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Wed, 17 Jan 2024 16:57:07 +0100 Subject: [PATCH 207/208] Mention Imagine --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1aa2df3..12211bd 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ either, as this "reference implementation" tries to be as easy to read as possib ## Tools - [floooh/qoiview](https://github.com/floooh/qoiview) - native QOI viewer -- [pfusik/qoi-fu](https://github.com/pfusik/qoi-fu/releases) - QOI Plugin installer for Windows Explorer, Finder, GNOME, GIMP, Imagine, Paint.NET and XnView +- [pfusik/qoi-fu](https://github.com/pfusik/qoi-fu/releases) - QOI Plugin installer for Windows Explorer, Finder, GNOME, GIMP, Paint.NET and XnView - [iOrange/QoiFileTypeNet](https://github.com/iOrange/QoiFileTypeNet/releases) - QOI Plugin for Paint.NET - [iOrange/QOIThumbnailProvider](https://github.com/iOrange/QOIThumbnailProvider) - Add thumbnails for QOI images in Windows Explorer - [Tom94/tev](https://github.com/Tom94/tev) - another native QOI viewer (allows pixel peeping and comparison with other image formats) @@ -167,6 +167,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [KDE](https://kde.org) - supports decoding and encoding QOI images. Implemented in [KImageFormats](https://invent.kde.org/frameworks/kimageformats) - [EFL](https://www.enlightenment.org) - supports decoding and encoding QOI images since 1.27. - [Swingland](https://git.sr.ht/~phlash/swingland) - supports QOI decoding/loading via the `ImageIO` API of this Java Swing reimplemenation for Wayland +- [Imagine](https://www.nyam.pe.kr/dev/imagine/) - supports decoding and encoding QOI images since 1.3.9 ## Packages From aeb22ad89899688af3c0aa5a22b17693896d8df7 Mon Sep 17 00:00:00 2001 From: RomanPro100 <99795209+RomanPro100@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:36:31 +0300 Subject: [PATCH 208/208] Add Uiua to "QOI Support in Other Software" --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 12211bd..3bb9dcb 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,7 @@ either, as this "reference implementation" tries to be as easy to read as possib - [EFL](https://www.enlightenment.org) - supports decoding and encoding QOI images since 1.27. - [Swingland](https://git.sr.ht/~phlash/swingland) - supports QOI decoding/loading via the `ImageIO` API of this Java Swing reimplemenation for Wayland - [Imagine](https://www.nyam.pe.kr/dev/imagine/) - supports decoding and encoding QOI images since 1.3.9 +- [Uiua](https://uiua.org) - supports decoding and encoding QOI images since 0.8.0 ## Packages