321 #ifdef QOI_IMPLEMENTATION 326 #define QOI_MALLOC(sz) malloc(sz) 327 #define QOI_FREE(p) free(p) 330 #define QOI_ZEROARR(a) memset((a),0,sizeof(a)) 333 #define QOI_OP_INDEX 0x00 334 #define QOI_OP_DIFF 0x40 335 #define QOI_OP_LUMA 0x80 336 #define QOI_OP_RUN 0xc0 337 #define QOI_OP_RGB 0xfe 338 #define QOI_OP_RGBA 0xff 340 #define QOI_MASK_2 0xc0 342 #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11) 344 (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ 345 ((unsigned int)'i') << 8 | ((unsigned int)'f')) 346 #define QOI_HEADER_SIZE 14 352 #define QOI_PIXELS_MAX ((unsigned int)400000000) 355 struct {
unsigned char r, g, b, a; } rgba;
359 static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
361 void qoi_write_32(
unsigned char *
bytes,
int *p,
unsigned int v) {
362 bytes[(*p)++] = (0xff000000 & v) >> 24;
363 bytes[(*p)++] = (0x00ff0000 & v) >> 16;
364 bytes[(*p)++] = (0x0000ff00 & v) >> 8;
365 bytes[(*p)++] = (0x000000ff & v);
368 unsigned int qoi_read_32(
const unsigned char *
bytes,
int *p) {
369 unsigned int a = bytes[(*p)++];
370 unsigned int b = bytes[(*p)++];
371 unsigned int c = bytes[(*p)++];
372 unsigned int d = bytes[(*p)++];
373 return a << 24 | b << 16 | c << 8 | d;
377 int i, max_size, p, run;
378 int px_len, px_end, px_pos, channels;
379 unsigned char *
bytes;
380 const unsigned char *pixels;
381 qoi_rgba_t index[64];
382 qoi_rgba_t px, px_prev;
385 data == NULL || out_len == NULL || desc == NULL ||
396 QOI_HEADER_SIZE +
sizeof(qoi_padding);
399 bytes = (
unsigned char *) QOI_MALLOC(max_size);
404 qoi_write_32(bytes, &p, QOI_MAGIC);
405 qoi_write_32(bytes, &p, desc->
width);
406 qoi_write_32(bytes, &p, desc->
height);
411 pixels = (
const unsigned char *)data;
419 px_prev.rgba.a = 255;
426 for (px_pos = 0; px_pos < px_len; px_pos += channels) {
428 px = *(qoi_rgba_t *)(pixels + px_pos);
431 px.rgba.r = pixels[px_pos + 0];
432 px.rgba.g = pixels[px_pos + 1];
433 px.rgba.b = pixels[px_pos + 2];
436 if (px.v == px_prev.v) {
438 if (run == 62 || px_pos == px_end) {
439 bytes[p++] = QOI_OP_RUN | (run - 1);
447 bytes[p++] = QOI_OP_RUN | (run - 1);
451 index_pos = QOI_COLOR_HASH(px) % 64;
453 if (index[index_pos].v == px.v) {
454 bytes[p++] = QOI_OP_INDEX | index_pos;
457 index[index_pos] = px;
459 if (px.rgba.a == px_prev.rgba.a) {
460 signed char vr = px.rgba.r - px_prev.rgba.r;
461 signed char vg = px.rgba.g - px_prev.rgba.g;
462 signed char vb = px.rgba.b - px_prev.rgba.b;
464 signed char vg_r = vr - vg;
465 signed char vg_b = vb - vg;
472 bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
475 vg_r > -9 && vg_r < 8 &&
476 vg > -33 && vg < 32 &&
477 vg_b > -9 && vg_b < 8
479 bytes[p++] = QOI_OP_LUMA | (vg + 32);
480 bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8);
483 bytes[p++] = QOI_OP_RGB;
484 bytes[p++] = px.rgba.r;
485 bytes[p++] = px.rgba.g;
486 bytes[p++] = px.rgba.b;
490 bytes[p++] = QOI_OP_RGBA;
491 bytes[p++] = px.rgba.r;
492 bytes[p++] = px.rgba.g;
493 bytes[p++] = px.rgba.b;
494 bytes[p++] = px.rgba.a;
501 for (i = 0; i < (int)
sizeof(qoi_padding); i++) {
502 bytes[p++] = qoi_padding[i];
510 const unsigned char *bytes;
511 unsigned int header_magic;
512 unsigned char *pixels;
513 qoi_rgba_t index[64];
515 int px_len, chunks_len, px_pos;
519 data == NULL || desc == NULL ||
520 (channels != 0 && channels != 3 && channels != 4) ||
521 size < QOI_HEADER_SIZE + (
int)
sizeof(qoi_padding)
526 bytes = (
const unsigned char *)data;
528 header_magic = qoi_read_32(bytes, &p);
529 desc->
width = qoi_read_32(bytes, &p);
530 desc->
height = qoi_read_32(bytes, &p);
538 header_magic != QOI_MAGIC ||
549 pixels = (
unsigned char *) QOI_MALLOC(px_len);
560 chunks_len = size - (int)
sizeof(qoi_padding);
561 for (px_pos = 0; px_pos < px_len; px_pos += channels) {
565 else if (p < chunks_len) {
568 if (b1 == QOI_OP_RGB) {
569 px.rgba.r = bytes[p++];
570 px.rgba.g = bytes[p++];
571 px.rgba.b = bytes[p++];
573 else if (b1 == QOI_OP_RGBA) {
574 px.rgba.r = bytes[p++];
575 px.rgba.g = bytes[p++];
576 px.rgba.b = bytes[p++];
577 px.rgba.a = bytes[p++];
579 else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
582 else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
583 px.rgba.r += ((b1 >> 4) & 0x03) - 2;
584 px.rgba.g += ((b1 >> 2) & 0x03) - 2;
585 px.rgba.b += ( b1 & 0x03) - 2;
587 else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
589 int vg = (b1 & 0x3f) - 32;
590 px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
592 px.rgba.b += vg - 8 + (b2 & 0x0f);
594 else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
598 index[QOI_COLOR_HASH(px) % 64] = px;
602 *(qoi_rgba_t*)(pixels + px_pos) = px;
605 pixels[px_pos + 0] = px.rgba.r;
606 pixels[px_pos + 1] = px.rgba.g;
607 pixels[px_pos + 2] = px.rgba.b;
618 FILE *
f = fopen(filename,
"wb");
632 fwrite(encoded, 1, size, f);
640 FILE *f = fopen(filename,
"rb");
641 int size, bytes_read;
648 fseek(f, 0, SEEK_END);
654 fseek(f, 0, SEEK_SET);
656 data = QOI_MALLOC(size);
662 bytes_read = fread(data, 1, size, f);
665 pixels =
qoi_decode(data, bytes_read, desc, channels);
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)
void * qoi_read(const char *filename, qoi_desc *desc, int channels)
int qoi_write(const char *filename, const void *data, const qoi_desc *desc)
span_constexpr std::size_t size(span< T, Extent > const &spn)