qoi.h
Go to the documentation of this file.
1 /*
2 
3 QOI - The "Quite OK Image" format for fast, lossless image compression
4 
5 Dominic Szablewski - https://phoboslab.org
6 
7 
8 -- LICENSE: The MIT License(MIT)
9 
10 Copyright(c) 2021 Dominic Szablewski
11 
12 Permission is hereby granted, free of charge, to any person obtaining a copy of
13 this software and associated documentation files(the "Software"), to deal in
14 the Software without restriction, including without limitation the rights to
15 use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
16 of the Software, and to permit persons to whom the Software is furnished to do
17 so, subject to the following conditions :
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 SOFTWARE.
27 
28 
29 -- About
30 
31 QOI encodes and decodes images in a lossless format. Compared to stb_image and
32 stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and
33 20% better compression.
34 
35 
36 -- Synopsis
37 
38 // Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this
39 // library to create the implementation.
40 
41 #define QOI_IMPLEMENTATION
42 #include "qoi.h"
43 
44 // Encode and store an RGBA buffer to the file system. The qoi_desc describes
45 // the input pixel data.
46 qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){
47  .width = 1920,
48  .height = 1080,
49  .channels = 4,
50  .colorspace = QOI_SRGB
51 });
52 
53 // Load and decode a QOI image from the file system into a 32bbp RGBA buffer.
54 // The qoi_desc struct will be filled with the width, height, number of channels
55 // and colorspace read from the file header.
56 qoi_desc desc;
57 void *rgba_pixels = qoi_read("image.qoi", &desc, 4);
58 
59 
60 
61 -- Documentation
62 
63 This library provides the following functions;
64 - qoi_read -- read and decode a QOI file
65 - qoi_decode -- decode the raw bytes of a QOI image from memory
66 - qoi_write -- encode and write a QOI file
67 - qoi_encode -- encode an rgba buffer into a QOI image in memory
68 
69 See the function declaration below for the signature and more information.
70 
71 If you don't want/need the qoi_read and qoi_write functions, you can define
72 QOI_NO_STDIO before including this library.
73 
74 This library uses malloc() and free(). To supply your own malloc implementation
75 you can define QOI_MALLOC and QOI_FREE before including this library.
76 
77 This library uses memset() to zero-initialize the index. To supply your own
78 implementation you can define QOI_ZEROARR before including this library.
79 
80 
81 -- Data Format
82 
83 A QOI file has a 14 byte header, followed by any number of data "chunks" and an
84 8-byte end marker.
85 
86 struct qoi_header_t {
87  char magic[4]; // magic bytes "qoif"
88  uint32_t width; // image width in pixels (BE)
89  uint32_t height; // image height in pixels (BE)
90  uint8_t channels; // 3 = RGB, 4 = RGBA
91  uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
92 };
93 
94 Images are encoded from top to bottom, left to right. The decoder and encoder
95 start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An image is
96 complete when all pixels specified by width * height have been covered.
97 
98 Pixels are encoded as
99  - a run of the previous pixel
100  - an index into an array of previously seen pixels
101  - a difference to the previous pixel value in r,g,b
102  - full r,g,b or r,g,b,a values
103 
104 The color channels are assumed to not be premultiplied with the alpha channel
105 ("un-premultiplied alpha").
106 
107 A running array[64] (zero-initialized) of previously seen pixel values is
108 maintained by the encoder and decoder. Each pixel that is seen by the encoder
109 and decoder is put into this array at the position formed by a hash function of
110 the color value. In the encoder, if the pixel value at the index matches the
111 current pixel, this index position is written to the stream as QOI_OP_INDEX.
112 The hash function for the index is:
113 
114  index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64
115 
116 Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The
117 bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All
118 values encoded in these data bits have the most significant bit on the left.
119 
120 The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the
121 presence of an 8-bit tag first.
122 
123 The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte.
124 
125 
126 The possible chunks are:
127 
128 
129 .- QOI_OP_INDEX ----------.
130 | Byte[0] |
131 | 7 6 5 4 3 2 1 0 |
132 |-------+-----------------|
133 | 0 0 | index |
134 `-------------------------`
135 2-bit tag b00
136 6-bit index into the color index array: 0..63
137 
138 A valid encoder must not issue 7 or more consecutive QOI_OP_INDEX chunks to the
139 index 0, to avoid confusion with the 8 byte end marker.
140 
141 
142 .- QOI_OP_DIFF -----------.
143 | Byte[0] |
144 | 7 6 5 4 3 2 1 0 |
145 |-------+-----+-----+-----|
146 | 0 1 | dr | dg | db |
147 `-------------------------`
148 2-bit tag b01
149 2-bit red channel difference from the previous pixel between -2..1
150 2-bit green channel difference from the previous pixel between -2..1
151 2-bit blue channel difference from the previous pixel between -2..1
152 
153 The difference to the current channel values are using a wraparound operation,
154 so "1 - 2" will result in 255, while "255 + 1" will result in 0.
155 
156 Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as
157 0 (b00). 1 is stored as 3 (b11).
158 
159 The alpha value remains unchanged from the previous pixel.
160 
161 
162 .- QOI_OP_LUMA -------------------------------------.
163 | Byte[0] | Byte[1] |
164 | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |
165 |-------+-----------------+-------------+-----------|
166 | 1 0 | green diff | dr - dg | db - dg |
167 `---------------------------------------------------`
168 2-bit tag b10
169 6-bit green channel difference from the previous pixel -32..31
170 4-bit red channel difference minus green channel difference -8..7
171 4-bit blue channel difference minus green channel difference -8..7
172 
173 The green channel is used to indicate the general direction of change and is
174 encoded in 6 bits. The red and blue channels (dr and db) base their diffs off
175 of the green channel difference and are encoded in 4 bits. I.e.:
176  dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g)
177  db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g)
178 
179 The difference to the current channel values are using a wraparound operation,
180 so "10 - 13" will result in 253, while "250 + 7" will result in 1.
181 
182 Values are stored as unsigned integers with a bias of 32 for the green channel
183 and a bias of 8 for the red and blue channel.
184 
185 The alpha value remains unchanged from the previous pixel.
186 
187 
188 .- QOI_OP_RUN ------------.
189 | Byte[0] |
190 | 7 6 5 4 3 2 1 0 |
191 |-------+-----------------|
192 | 1 1 | run |
193 `-------------------------`
194 2-bit tag b11
195 6-bit run-length repeating the previous pixel: 1..62
196 
197 The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64
198 (b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and
199 QOI_OP_RGBA tags.
200 
201 
202 .- QOI_OP_RGB ------------------------------------------.
203 | Byte[0] | Byte[1] | Byte[2] | Byte[3] |
204 | 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
205 |-------------------------+---------+---------+---------|
206 | 1 1 1 1 1 1 1 0 | red | green | blue |
207 `-------------------------------------------------------`
208 8-bit tag b11111110
209 8-bit red channel value
210 8-bit green channel value
211 8-bit blue channel value
212 
213 The alpha value remains unchanged from the previous pixel.
214 
215 
216 .- QOI_OP_RGBA ---------------------------------------------------.
217 | Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] |
218 | 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
219 |-------------------------+---------+---------+---------+---------|
220 | 1 1 1 1 1 1 1 1 | red | green | blue | alpha |
221 `-----------------------------------------------------------------`
222 8-bit tag b11111111
223 8-bit red channel value
224 8-bit green channel value
225 8-bit blue channel value
226 8-bit alpha channel value
227 
228 */
229 
230 
231 /* -----------------------------------------------------------------------------
232 Header - Public functions */
233 
234 #ifndef QOI_H
235 #define QOI_H
236 
237 #ifdef __cplusplus
238 extern "C" {
239 #endif
240 
241 /* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions.
242 It describes either the input format (for qoi_write and qoi_encode), or is
243 filled with the description read from the file header (for qoi_read and
244 qoi_decode).
245 
246 The colorspace in this qoi_desc is an enum where
247  0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel
248  1 = all channels are linear
249 You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely
250 informative. It will be saved to the file header, but does not affect
251 en-/decoding in any way. */
252 
253 #define QOI_SRGB 0
254 #define QOI_LINEAR 1
255 
256 typedef struct {
257  unsigned int width;
258  unsigned int height;
259  unsigned char channels;
260  unsigned char colorspace;
261 } qoi_desc;
262 
263 #ifndef QOI_NO_STDIO
264 
265 /* Encode raw RGB or RGBA pixels into a QOI image and write it to the file
266 system. The qoi_desc struct must be filled with the image width, height,
267 number of channels (3 = RGB, 4 = RGBA) and the colorspace.
268 
269 The function returns 0 on failure (invalid parameters, or fopen or malloc
270 failed) or the number of bytes written on success. */
271 
272 int qoi_write(const char *filename, const void *data, const qoi_desc *desc);
273 
274 
275 /* Read and decode a QOI image from the file system. If channels is 0, the
276 number of channels from the file header is used. If channels is 3 or 4 the
277 output format will be forced into this number of channels.
278 
279 The function either returns NULL on failure (invalid data, or malloc or fopen
280 failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
281 will be filled with the description from the file header.
282 
283 The returned pixel data should be free()d after use. */
284 
285 void *qoi_read(const char *filename, qoi_desc *desc, int channels);
286 
287 #endif /* QOI_NO_STDIO */
288 
289 
290 /* Encode raw RGB or RGBA pixels into a QOI image in memory.
291 
292 The function either returns NULL on failure (invalid parameters or malloc
293 failed) or a pointer to the encoded data on success. On success the out_len
294 is set to the size in bytes of the encoded data.
295 
296 The returned qoi data should be free()d after use. */
297 
298 void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len);
299 
300 
301 /* Decode a QOI image from memory.
302 
303 The function either returns NULL on failure (invalid parameters or malloc
304 failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
305 is filled with the description from the file header.
306 
307 The returned pixel data should be free()d after use. */
308 
309 void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels);
310 
311 
312 #ifdef __cplusplus
313 }
314 #endif
315 #endif /* QOI_H */
316 
317 
318 /* -----------------------------------------------------------------------------
319 Implementation */
320 
321 #ifdef QOI_IMPLEMENTATION
322 #include <stdlib.h>
323 #include <string.h>
324 
325 #ifndef QOI_MALLOC
326  #define QOI_MALLOC(sz) malloc(sz)
327  #define QOI_FREE(p) free(p)
328 #endif
329 #ifndef QOI_ZEROARR
330  #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
331 #endif
332 
333 #define QOI_OP_INDEX 0x00 /* 00xxxxxx */
334 #define QOI_OP_DIFF 0x40 /* 01xxxxxx */
335 #define QOI_OP_LUMA 0x80 /* 10xxxxxx */
336 #define QOI_OP_RUN 0xc0 /* 11xxxxxx */
337 #define QOI_OP_RGB 0xfe /* 11111110 */
338 #define QOI_OP_RGBA 0xff /* 11111111 */
339 
340 #define QOI_MASK_2 0xc0 /* 11000000 */
341 
342 #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
343 #define QOI_MAGIC \
344  (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
345  ((unsigned int)'i') << 8 | ((unsigned int)'f'))
346 #define QOI_HEADER_SIZE 14
347 
348 /* 2GB is the max file size that this implementation can safely handle. We guard
349 against anything larger than that, assuming the worst case with 5 bytes per
350 pixel, rounded down to a nice clean value. 400 million pixels ought to be
351 enough for anybody. */
352 #define QOI_PIXELS_MAX ((unsigned int)400000000)
353 
354 typedef union {
355  struct { unsigned char r, g, b, a; } rgba;
356  unsigned int v;
357 } qoi_rgba_t;
358 
359 static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
360 
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);
366 }
367 
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;
374 }
375 
376 void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
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;
383 
384  if (
385  data == NULL || out_len == NULL || desc == NULL ||
386  desc->width == 0 || desc->height == 0 ||
387  desc->channels < 3 || desc->channels > 4 ||
388  desc->colorspace > 1 ||
389  desc->height >= QOI_PIXELS_MAX / desc->width
390  ) {
391  return NULL;
392  }
393 
394  max_size =
395  desc->width * desc->height * (desc->channels + 1) +
396  QOI_HEADER_SIZE + sizeof(qoi_padding);
397 
398  p = 0;
399  bytes = (unsigned char *) QOI_MALLOC(max_size);
400  if (!bytes) {
401  return NULL;
402  }
403 
404  qoi_write_32(bytes, &p, QOI_MAGIC);
405  qoi_write_32(bytes, &p, desc->width);
406  qoi_write_32(bytes, &p, desc->height);
407  bytes[p++] = desc->channels;
408  bytes[p++] = desc->colorspace;
409 
410 
411  pixels = (const unsigned char *)data;
412 
413  QOI_ZEROARR(index);
414 
415  run = 0;
416  px_prev.rgba.r = 0;
417  px_prev.rgba.g = 0;
418  px_prev.rgba.b = 0;
419  px_prev.rgba.a = 255;
420  px = px_prev;
421 
422  px_len = desc->width * desc->height * desc->channels;
423  px_end = px_len - desc->channels;
424  channels = desc->channels;
425 
426  for (px_pos = 0; px_pos < px_len; px_pos += channels) {
427  if (channels == 4) {
428  px = *(qoi_rgba_t *)(pixels + px_pos);
429  }
430  else {
431  px.rgba.r = pixels[px_pos + 0];
432  px.rgba.g = pixels[px_pos + 1];
433  px.rgba.b = pixels[px_pos + 2];
434  }
435 
436  if (px.v == px_prev.v) {
437  run++;
438  if (run == 62 || px_pos == px_end) {
439  bytes[p++] = QOI_OP_RUN | (run - 1);
440  run = 0;
441  }
442  }
443  else {
444  int index_pos;
445 
446  if (run > 0) {
447  bytes[p++] = QOI_OP_RUN | (run - 1);
448  run = 0;
449  }
450 
451  index_pos = QOI_COLOR_HASH(px) % 64;
452 
453  if (index[index_pos].v == px.v) {
454  bytes[p++] = QOI_OP_INDEX | index_pos;
455  }
456  else {
457  index[index_pos] = px;
458 
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;
463 
464  signed char vg_r = vr - vg;
465  signed char vg_b = vb - vg;
466 
467  if (
468  vr > -3 && vr < 2 &&
469  vg > -3 && vg < 2 &&
470  vb > -3 && vb < 2
471  ) {
472  bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
473  }
474  else if (
475  vg_r > -9 && vg_r < 8 &&
476  vg > -33 && vg < 32 &&
477  vg_b > -9 && vg_b < 8
478  ) {
479  bytes[p++] = QOI_OP_LUMA | (vg + 32);
480  bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8);
481  }
482  else {
483  bytes[p++] = QOI_OP_RGB;
484  bytes[p++] = px.rgba.r;
485  bytes[p++] = px.rgba.g;
486  bytes[p++] = px.rgba.b;
487  }
488  }
489  else {
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;
495  }
496  }
497  }
498  px_prev = px;
499  }
500 
501  for (i = 0; i < (int)sizeof(qoi_padding); i++) {
502  bytes[p++] = qoi_padding[i];
503  }
504 
505  *out_len = p;
506  return bytes;
507 }
508 
509 void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
510  const unsigned char *bytes;
511  unsigned int header_magic;
512  unsigned char *pixels;
513  qoi_rgba_t index[64];
514  qoi_rgba_t px;
515  int px_len, chunks_len, px_pos;
516  int p = 0, run = 0;
517 
518  if (
519  data == NULL || desc == NULL ||
520  (channels != 0 && channels != 3 && channels != 4) ||
521  size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)
522  ) {
523  return NULL;
524  }
525 
526  bytes = (const unsigned char *)data;
527 
528  header_magic = qoi_read_32(bytes, &p);
529  desc->width = qoi_read_32(bytes, &p);
530  desc->height = qoi_read_32(bytes, &p);
531  desc->channels = bytes[p++];
532  desc->colorspace = bytes[p++];
533 
534  if (
535  desc->width == 0 || desc->height == 0 ||
536  desc->channels < 3 || desc->channels > 4 ||
537  desc->colorspace > 1 ||
538  header_magic != QOI_MAGIC ||
539  desc->height >= QOI_PIXELS_MAX / desc->width
540  ) {
541  return NULL;
542  }
543 
544  if (channels == 0) {
545  channels = desc->channels;
546  }
547 
548  px_len = desc->width * desc->height * channels;
549  pixels = (unsigned char *) QOI_MALLOC(px_len);
550  if (!pixels) {
551  return NULL;
552  }
553 
554  QOI_ZEROARR(index);
555  px.rgba.r = 0;
556  px.rgba.g = 0;
557  px.rgba.b = 0;
558  px.rgba.a = 255;
559 
560  chunks_len = size - (int)sizeof(qoi_padding);
561  for (px_pos = 0; px_pos < px_len; px_pos += channels) {
562  if (run > 0) {
563  run--;
564  }
565  else if (p < chunks_len) {
566  int b1 = bytes[p++];
567 
568  if (b1 == QOI_OP_RGB) {
569  px.rgba.r = bytes[p++];
570  px.rgba.g = bytes[p++];
571  px.rgba.b = bytes[p++];
572  }
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++];
578  }
579  else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
580  px = index[b1];
581  }
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;
586  }
587  else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
588  int b2 = bytes[p++];
589  int vg = (b1 & 0x3f) - 32;
590  px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
591  px.rgba.g += vg;
592  px.rgba.b += vg - 8 + (b2 & 0x0f);
593  }
594  else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
595  run = (b1 & 0x3f);
596  }
597 
598  index[QOI_COLOR_HASH(px) % 64] = px;
599  }
600 
601  if (channels == 4) {
602  *(qoi_rgba_t*)(pixels + px_pos) = px;
603  }
604  else {
605  pixels[px_pos + 0] = px.rgba.r;
606  pixels[px_pos + 1] = px.rgba.g;
607  pixels[px_pos + 2] = px.rgba.b;
608  }
609  }
610 
611  return pixels;
612 }
613 
614 #ifndef QOI_NO_STDIO
615 #include <stdio.h>
616 
617 int qoi_write(const char *filename, const void *data, const qoi_desc *desc) {
618  FILE *f = fopen(filename, "wb");
619  int size;
620  void *encoded;
621 
622  if (!f) {
623  return 0;
624  }
625 
626  encoded = qoi_encode(data, desc, &size);
627  if (!encoded) {
628  fclose(f);
629  return 0;
630  }
631 
632  fwrite(encoded, 1, size, f);
633  fclose(f);
634 
635  QOI_FREE(encoded);
636  return size;
637 }
638 
639 void *qoi_read(const char *filename, qoi_desc *desc, int channels) {
640  FILE *f = fopen(filename, "rb");
641  int size, bytes_read;
642  void *pixels, *data;
643 
644  if (!f) {
645  return NULL;
646  }
647 
648  fseek(f, 0, SEEK_END);
649  size = ftell(f);
650  if (size <= 0) {
651  fclose(f);
652  return NULL;
653  }
654  fseek(f, 0, SEEK_SET);
655 
656  data = QOI_MALLOC(size);
657  if (!data) {
658  fclose(f);
659  return NULL;
660  }
661 
662  bytes_read = fread(data, 1, size, f);
663  fclose(f);
664 
665  pixels = qoi_decode(data, bytes_read, desc, channels);
666  QOI_FREE(data);
667  return pixels;
668 }
669 
670 #endif /* QOI_NO_STDIO */
671 #endif /* QOI_IMPLEMENTATION */
d
unsigned char colorspace
Definition: qoi.h:260
unsigned int width
Definition: qoi.h:257
f
void * qoi_encode(const void *data, const qoi_desc *desc, int *out_len)
unsigned char channels
Definition: qoi.h:259
void * qoi_decode(const void *data, int size, qoi_desc *desc, int channels)
Definition: qoi.h:256
unsigned int height
Definition: qoi.h:258
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)
Definition: span.hpp:1485
dictionary data
Definition: mqtt_test.py:22
Definition: format.h:895
Definition: format.h:2459


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon Jun 19 2023 03:01:38