github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/external/qoi.h (about)

     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 row by row, left to right, top to bottom. The decoder and
    95  encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An
    96  image is 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 2 or more consecutive QOI_OP_INDEX chunks to the
   139  same index. QOI_OP_RUN should be used instead.
   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 = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g)
   177  	db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_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  how chunks are en-/decoded. */
   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  static 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  static 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 */