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 Header - Public functions */
232 
233 #ifndef QOI_H
234 #define QOI_H
235 
236 #ifdef __cplusplus
237 extern "C" {
238 #endif
239 
240 /* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions.
241 It describes either the input format (for qoi_write and qoi_encode), or is
242 filled with the description read from the file header (for qoi_read and
243 qoi_decode).
244 
245 The colorspace in this qoi_desc is an enum where
246  0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel
247  1 = all channels are linear
248 You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely
249 informative. It will be saved to the file header, but does not affect
250 en-/decoding in any way. */
251 
252 #define QOI_SRGB 0
253 #define QOI_LINEAR 1
254 
255 typedef struct
256 {
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 /* Read and decode a QOI image from the file system. If channels is 0, the
275 number of channels from the file header is used. If channels is 3 or 4 the
276 output format will be forced into this number of channels.
277 
278 The function either returns NULL on failure (invalid data, or malloc or fopen
279 failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
280 will be filled with the description from the file header.
281 
282 The returned pixel data should be free()d after use. */
283 
284 void* qoi_read(const char* filename, qoi_desc* desc, int channels);
285 
286 #endif /* QOI_NO_STDIO */
287 
288 /* Encode raw RGB or RGBA pixels into a QOI image in memory.
289 
290 The function either returns NULL on failure (invalid parameters or malloc
291 failed) or a pointer to the encoded data on success. On success the out_len
292 is set to the size in bytes of the encoded data.
293 
294 The returned qoi data should be free()d after use. */
295 
296 void* qoi_encode(const void* data, const qoi_desc* desc, int* out_len);
297 
298 /* Decode a QOI image from memory.
299 
300 The function either returns NULL on failure (invalid parameters or malloc
301 failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
302 is filled with the description from the file header.
303 
304 The returned pixel data should be free()d after use. */
305 
306 void* qoi_decode(const void* data, int size, qoi_desc* desc, int channels);
307 
308 #ifdef __cplusplus
309 }
310 #endif
311 #endif /* QOI_H */
312 
313 /* -----------------------------------------------------------------------------
314 Implementation */
315 
316 #ifdef QOI_IMPLEMENTATION
317 #include <stdlib.h>
318 #include <string.h>
319 
320 #ifndef QOI_MALLOC
321 #define QOI_MALLOC(sz) malloc(sz)
322 #define QOI_FREE(p) free(p)
323 #endif
324 #ifndef QOI_ZEROARR
325 #define QOI_ZEROARR(a) memset((a), 0, sizeof(a))
326 #endif
327 
328 #define QOI_OP_INDEX 0x00 /* 00xxxxxx */
329 #define QOI_OP_DIFF 0x40 /* 01xxxxxx */
330 #define QOI_OP_LUMA 0x80 /* 10xxxxxx */
331 #define QOI_OP_RUN 0xc0 /* 11xxxxxx */
332 #define QOI_OP_RGB 0xfe /* 11111110 */
333 #define QOI_OP_RGBA 0xff /* 11111111 */
334 
335 #define QOI_MASK_2 0xc0 /* 11000000 */
336 
337 #define QOI_COLOR_HASH(C) (C.rgba.r * 3 + C.rgba.g * 5 + C.rgba.b * 7 + C.rgba.a * 11)
338 #define QOI_MAGIC \
339  (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | ((unsigned int)'i') << 8 | \
340  ((unsigned int)'f'))
341 #define QOI_HEADER_SIZE 14
342 
343 /* 2GB is the max file size that this implementation can safely handle. We guard
344 against anything larger than that, assuming the worst case with 5 bytes per
345 pixel, rounded down to a nice clean value. 400 million pixels ought to be
346 enough for anybody. */
347 #define QOI_PIXELS_MAX ((unsigned int)400000000)
348 
349 typedef union
350 {
351  struct
352  {
353  unsigned char r, g, b, a;
354  } rgba;
355  unsigned int v;
356 } qoi_rgba_t;
357 
358 static const unsigned char qoi_padding[8] = { 0, 0, 0, 0, 0, 0, 0, 1 };
359 
360 void qoi_write_32(unsigned char* bytes, int* p, unsigned int v)
361 {
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 {
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;
375 }
376 
377 void* qoi_encode(const void* data, const qoi_desc* desc, int* out_len)
378 {
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;
385 
386  if (data == NULL || out_len == NULL || desc == NULL || desc->width == 0 ||
387  desc->height == 0 || desc->channels < 3 || desc->channels > 4 ||
388  desc->colorspace > 1 || desc->height >= QOI_PIXELS_MAX / desc->width)
389  {
390  return NULL;
391  }
392 
393  max_size = desc->width * desc->height * (desc->channels + 1) + QOI_HEADER_SIZE +
394  sizeof(qoi_padding);
395 
396  p = 0;
397  bytes = (unsigned char*)QOI_MALLOC(max_size);
398  if (!bytes)
399  {
400  return NULL;
401  }
402 
403  qoi_write_32(bytes, &p, QOI_MAGIC);
404  qoi_write_32(bytes, &p, desc->width);
405  qoi_write_32(bytes, &p, desc->height);
406  bytes[p++] = desc->channels;
407  bytes[p++] = desc->colorspace;
408 
409  pixels = (const unsigned char*)data;
410 
411  QOI_ZEROARR(index);
412 
413  run = 0;
414  px_prev.rgba.r = 0;
415  px_prev.rgba.g = 0;
416  px_prev.rgba.b = 0;
417  px_prev.rgba.a = 255;
418  px = px_prev;
419 
420  px_len = desc->width * desc->height * desc->channels;
421  px_end = px_len - desc->channels;
422  channels = desc->channels;
423 
424  for (px_pos = 0; px_pos < px_len; px_pos += channels)
425  {
426  if (channels == 4)
427  {
428  px = *(qoi_rgba_t*)(pixels + px_pos);
429  }
430  else
431  {
432  px.rgba.r = pixels[px_pos + 0];
433  px.rgba.g = pixels[px_pos + 1];
434  px.rgba.b = pixels[px_pos + 2];
435  }
436 
437  if (px.v == px_prev.v)
438  {
439  run++;
440  if (run == 62 || px_pos == px_end)
441  {
442  bytes[p++] = QOI_OP_RUN | (run - 1);
443  run = 0;
444  }
445  }
446  else
447  {
448  int index_pos;
449 
450  if (run > 0)
451  {
452  bytes[p++] = QOI_OP_RUN | (run - 1);
453  run = 0;
454  }
455 
456  index_pos = QOI_COLOR_HASH(px) % 64;
457 
458  if (index[index_pos].v == px.v)
459  {
460  bytes[p++] = QOI_OP_INDEX | index_pos;
461  }
462  else
463  {
464  index[index_pos] = px;
465 
466  if (px.rgba.a == px_prev.rgba.a)
467  {
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;
471 
472  signed char vg_r = vr - vg;
473  signed char vg_b = vb - vg;
474 
475  if (vr > -3 && vr < 2 && vg > -3 && vg < 2 && vb > -3 && vb < 2)
476  {
477  bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
478  }
479  else if (vg_r > -9 && vg_r < 8 && vg > -33 && vg < 32 && vg_b > -9 && vg_b < 8)
480  {
481  bytes[p++] = QOI_OP_LUMA | (vg + 32);
482  bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8);
483  }
484  else
485  {
486  bytes[p++] = QOI_OP_RGB;
487  bytes[p++] = px.rgba.r;
488  bytes[p++] = px.rgba.g;
489  bytes[p++] = px.rgba.b;
490  }
491  }
492  else
493  {
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;
499  }
500  }
501  }
502  px_prev = px;
503  }
504 
505  for (i = 0; i < (int)sizeof(qoi_padding); i++)
506  {
507  bytes[p++] = qoi_padding[i];
508  }
509 
510  *out_len = p;
511  return bytes;
512 }
513 
514 void* qoi_decode(const void* data, int size, qoi_desc* desc, int channels)
515 {
516  const unsigned char* bytes;
517  unsigned int header_magic;
518  unsigned char* pixels;
519  qoi_rgba_t index[64];
520  qoi_rgba_t px;
521  int px_len, chunks_len, px_pos;
522  int p = 0, run = 0;
523 
524  if (data == NULL || desc == NULL || (channels != 0 && channels != 3 && channels != 4) ||
525  size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding))
526  {
527  return NULL;
528  }
529 
530  bytes = (const unsigned char*)data;
531 
532  header_magic = qoi_read_32(bytes, &p);
533  desc->width = qoi_read_32(bytes, &p);
534  desc->height = qoi_read_32(bytes, &p);
535  desc->channels = bytes[p++];
536  desc->colorspace = bytes[p++];
537 
538  if (desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 ||
539  desc->colorspace > 1 || header_magic != QOI_MAGIC ||
540  desc->height >= QOI_PIXELS_MAX / desc->width)
541  {
542  return NULL;
543  }
544 
545  if (channels == 0)
546  {
547  channels = desc->channels;
548  }
549 
550  px_len = desc->width * desc->height * channels;
551  pixels = (unsigned char*)QOI_MALLOC(px_len);
552  if (!pixels)
553  {
554  return NULL;
555  }
556 
557  QOI_ZEROARR(index);
558  px.rgba.r = 0;
559  px.rgba.g = 0;
560  px.rgba.b = 0;
561  px.rgba.a = 255;
562 
563  chunks_len = size - (int)sizeof(qoi_padding);
564  for (px_pos = 0; px_pos < px_len; px_pos += channels)
565  {
566  if (run > 0)
567  {
568  run--;
569  }
570  else if (p < chunks_len)
571  {
572  int b1 = bytes[p++];
573 
574  if (b1 == QOI_OP_RGB)
575  {
576  px.rgba.r = bytes[p++];
577  px.rgba.g = bytes[p++];
578  px.rgba.b = bytes[p++];
579  }
580  else if (b1 == QOI_OP_RGBA)
581  {
582  px.rgba.r = bytes[p++];
583  px.rgba.g = bytes[p++];
584  px.rgba.b = bytes[p++];
585  px.rgba.a = bytes[p++];
586  }
587  else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX)
588  {
589  px = index[b1];
590  }
591  else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF)
592  {
593  px.rgba.r += ((b1 >> 4) & 0x03) - 2;
594  px.rgba.g += ((b1 >> 2) & 0x03) - 2;
595  px.rgba.b += (b1 & 0x03) - 2;
596  }
597  else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA)
598  {
599  int b2 = bytes[p++];
600  int vg = (b1 & 0x3f) - 32;
601  px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
602  px.rgba.g += vg;
603  px.rgba.b += vg - 8 + (b2 & 0x0f);
604  }
605  else if ((b1 & QOI_MASK_2) == QOI_OP_RUN)
606  {
607  run = (b1 & 0x3f);
608  }
609 
610  index[QOI_COLOR_HASH(px) % 64] = px;
611  }
612 
613  if (channels == 4)
614  {
615  *(qoi_rgba_t*)(pixels + px_pos) = px;
616  }
617  else
618  {
619  pixels[px_pos + 0] = px.rgba.r;
620  pixels[px_pos + 1] = px.rgba.g;
621  pixels[px_pos + 2] = px.rgba.b;
622  }
623  }
624 
625  return pixels;
626 }
627 
628 #ifndef QOI_NO_STDIO
629 #include <stdio.h>
630 
631 int qoi_write(const char* filename, const void* data, const qoi_desc* desc)
632 {
633  FILE* f = fopen(filename, "wb");
634  int size;
635  void* encoded;
636 
637  if (!f)
638  {
639  return 0;
640  }
641 
642  encoded = qoi_encode(data, desc, &size);
643  if (!encoded)
644  {
645  fclose(f);
646  return 0;
647  }
648 
649  fwrite(encoded, 1, size, f);
650  fclose(f);
651 
652  QOI_FREE(encoded);
653  return size;
654 }
655 
656 void* qoi_read(const char* filename, qoi_desc* desc, int channels)
657 {
658  FILE* f = fopen(filename, "rb");
659  int size, bytes_read;
660  void *pixels, *data;
661 
662  if (!f)
663  {
664  return NULL;
665  }
666 
667  fseek(f, 0, SEEK_END);
668  size = ftell(f);
669  if (size <= 0)
670  {
671  fclose(f);
672  return NULL;
673  }
674  fseek(f, 0, SEEK_SET);
675 
676  data = QOI_MALLOC(size);
677  if (!data)
678  {
679  fclose(f);
680  return NULL;
681  }
682 
683  bytes_read = fread(data, 1, size, f);
684  fclose(f);
685 
686  pixels = qoi_decode(data, bytes_read, desc, channels);
687  QOI_FREE(data);
688  return pixels;
689 }
690 
691 #endif /* QOI_NO_STDIO */
692 #endif /* QOI_IMPLEMENTATION */
bytes
Definition: format.h:4101
qoi_desc::width
unsigned int width
Definition: qoi.h:257
qoi_desc::colorspace
unsigned char colorspace
Definition: qoi.h:260
sol::meta_function::index
@ index
f
f
nonstd::span_lite::size
span_constexpr std::size_t size(span< T, Extent > const &spn)
Definition: span.hpp:1554
qoi_desc::channels
unsigned char channels
Definition: qoi.h:259
qoi_decode
void * qoi_decode(const void *data, int size, qoi_desc *desc, int channels)
d
d
qoi_encode
void * qoi_encode(const void *data, const qoi_desc *desc, int *out_len)
udp_client.int
int
Definition: udp_client.py:11
qoi_desc
Definition: qoi.h:255
mqtt_test.data
dictionary data
Definition: mqtt_test.py:22
qoi_desc::height
unsigned int height
Definition: qoi.h:258
qoi_read
void * qoi_read(const char *filename, qoi_desc *desc, int channels)
qoi_write
int qoi_write(const char *filename, const void *data, const qoi_desc *desc)


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon Nov 11 2024 03:23:45