316 #ifdef QOI_IMPLEMENTATION
321 #define QOI_MALLOC(sz) malloc(sz)
322 #define QOI_FREE(p) free(p)
325 #define QOI_ZEROARR(a) memset((a), 0, sizeof(a))
328 #define QOI_OP_INDEX 0x00
329 #define QOI_OP_DIFF 0x40
330 #define QOI_OP_LUMA 0x80
331 #define QOI_OP_RUN 0xc0
332 #define QOI_OP_RGB 0xfe
333 #define QOI_OP_RGBA 0xff
335 #define QOI_MASK_2 0xc0
337 #define QOI_COLOR_HASH(C) (C.rgba.r * 3 + C.rgba.g * 5 + C.rgba.b * 7 + C.rgba.a * 11)
339 (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | ((unsigned int)'i') << 8 | \
341 #define QOI_HEADER_SIZE 14
347 #define QOI_PIXELS_MAX ((unsigned int)400000000)
353 unsigned char r, g, b, a;
358 static const unsigned char qoi_padding[8] = { 0, 0, 0, 0, 0, 0, 0, 1 };
360 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)
370 unsigned int a =
bytes[(*p)++];
371 unsigned int b =
bytes[(*p)++];
372 unsigned int c =
bytes[(*p)++];
373 unsigned int d =
bytes[(*p)++];
374 return a << 24 | b << 16 | c << 8 |
d;
379 int i, max_size, p, run;
380 int px_len, px_end, px_pos, channels;
381 unsigned char*
bytes;
382 const unsigned char* pixels;
383 qoi_rgba_t
index[64];
384 qoi_rgba_t px, px_prev;
386 if (
data == NULL || out_len == NULL || desc == NULL || desc->
width == 0 ||
397 bytes = (
unsigned char*)QOI_MALLOC(max_size);
403 qoi_write_32(
bytes, &p, QOI_MAGIC);
409 pixels = (
const unsigned char*)
data;
417 px_prev.rgba.a = 255;
424 for (px_pos = 0; px_pos < px_len; px_pos += channels)
428 px = *(qoi_rgba_t*)(pixels + px_pos);
432 px.rgba.r = pixels[px_pos + 0];
433 px.rgba.g = pixels[px_pos + 1];
434 px.rgba.b = pixels[px_pos + 2];
437 if (px.v == px_prev.v)
440 if (run == 62 || px_pos == px_end)
442 bytes[p++] = QOI_OP_RUN | (run - 1);
452 bytes[p++] = QOI_OP_RUN | (run - 1);
456 index_pos = QOI_COLOR_HASH(px) % 64;
458 if (index[index_pos].v == px.v)
460 bytes[p++] = QOI_OP_INDEX | index_pos;
464 index[index_pos] = px;
466 if (px.rgba.a == px_prev.rgba.a)
468 signed char vr = px.rgba.r - px_prev.rgba.r;
469 signed char vg = px.rgba.g - px_prev.rgba.g;
470 signed char vb = px.rgba.b - px_prev.rgba.b;
472 signed char vg_r = vr - vg;
473 signed char vg_b = vb - vg;
475 if (vr > -3 && vr < 2 && vg > -3 && vg < 2 && vb > -3 && vb < 2)
477 bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
479 else if (vg_r > -9 && vg_r < 8 && vg > -33 && vg < 32 && vg_b > -9 && vg_b < 8)
481 bytes[p++] = QOI_OP_LUMA | (vg + 32);
482 bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8);
486 bytes[p++] = QOI_OP_RGB;
487 bytes[p++] = px.rgba.r;
488 bytes[p++] = px.rgba.g;
489 bytes[p++] = px.rgba.b;
494 bytes[p++] = QOI_OP_RGBA;
495 bytes[p++] = px.rgba.r;
496 bytes[p++] = px.rgba.g;
497 bytes[p++] = px.rgba.b;
498 bytes[p++] = px.rgba.a;
505 for (i = 0; i < (
int)
sizeof(qoi_padding); i++)
507 bytes[p++] = qoi_padding[i];
516 const unsigned char*
bytes;
517 unsigned int header_magic;
518 unsigned char* pixels;
519 qoi_rgba_t
index[64];
521 int px_len, chunks_len, px_pos;
524 if (
data == NULL || desc == NULL || (channels != 0 && channels != 3 && channels != 4) ||
525 size < QOI_HEADER_SIZE + (
int)
sizeof(qoi_padding))
532 header_magic = qoi_read_32(
bytes, &p);
539 desc->
colorspace > 1 || header_magic != QOI_MAGIC ||
551 pixels = (
unsigned char*)QOI_MALLOC(px_len);
563 chunks_len =
size - (
int)
sizeof(qoi_padding);
564 for (px_pos = 0; px_pos < px_len; px_pos += channels)
570 else if (p < chunks_len)
574 if (b1 == QOI_OP_RGB)
576 px.rgba.r =
bytes[p++];
577 px.rgba.g =
bytes[p++];
578 px.rgba.b =
bytes[p++];
580 else if (b1 == QOI_OP_RGBA)
582 px.rgba.r =
bytes[p++];
583 px.rgba.g =
bytes[p++];
584 px.rgba.b =
bytes[p++];
585 px.rgba.a =
bytes[p++];
587 else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX)
591 else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF)
593 px.rgba.r += ((b1 >> 4) & 0x03) - 2;
594 px.rgba.g += ((b1 >> 2) & 0x03) - 2;
595 px.rgba.b += (b1 & 0x03) - 2;
597 else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA)
600 int vg = (b1 & 0x3f) - 32;
601 px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
603 px.rgba.b += vg - 8 + (b2 & 0x0f);
605 else if ((b1 & QOI_MASK_2) == QOI_OP_RUN)
610 index[QOI_COLOR_HASH(px) % 64] = px;
615 *(qoi_rgba_t*)(pixels + px_pos) = px;
619 pixels[px_pos + 0] = px.rgba.r;
620 pixels[px_pos + 1] = px.rgba.g;
621 pixels[px_pos + 2] = px.rgba.b;
633 FILE*
f = fopen(filename,
"wb");
649 fwrite(encoded, 1,
size, f);
658 FILE*
f = fopen(filename,
"rb");
659 int size, bytes_read;
667 fseek(f, 0, SEEK_END);
674 fseek(f, 0, SEEK_SET);
683 bytes_read = fread(
data, 1,
size, f);