File Coverage

qoi.h
Criterion Covered Total %
statement 142 190 74.7
branch 79 128 61.7
condition n/a
subroutine n/a
pod n/a
total 221 318 69.5


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