/
pixelsuft
/
tinyfoo
Обзор
Документация
Войти
/
pixelsuft
/
tinyfoo
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/upng.cpp
1 354 строки
36 KB
Pixelsuft
wip
22 июн 2025, 08:11
22 июн 2025, 08:11
19e95cd
Код
Авторство
О чём код?
/* uPNG -- derived from LodePNG version 20100808 Copyright (c) 2005-2010 Lode Vandevenne Copyright (c) 2010 Sean Middleditch This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include <lbs.hpp> #if ENABLE_UPNG #include <limits.h> #include <upng.hpp> #include <SDL3/SDL.h> #define MAKE_BYTE(b) ((b) & 0xFF) #define MAKE_DWORD(a, b, c, d) ((MAKE_BYTE(a) << 24) | (MAKE_BYTE(b) << 16) | (MAKE_BYTE(c) << 8) | MAKE_BYTE(d)) #define MAKE_DWORD_PTR(p) MAKE_DWORD((p)[0], (p)[1], (p)[2], (p)[3]) #define CHUNK_IHDR MAKE_DWORD('I', 'H', 'D', 'R') #define CHUNK_IDAT MAKE_DWORD('I', 'D', 'A', 'T') #define CHUNK_IEND MAKE_DWORD('I', 'E', 'N', 'D') #define CHUNK_PLTE MAKE_DWORD('P', 'L', 'T', 'E') #define FIRST_LENGTH_CODE_INDEX 257 #define LAST_LENGTH_CODE_INDEX 285 #define NUM_DEFLATE_CODE_SYMBOLS 288 #define NUM_DISTANCE_SYMBOLS 32 #define NUM_CODE_LENGTH_CODES 19 #define MAX_SYMBOLS 288 #define DEFLATE_CODE_BITLEN 15 #define DISTANCE_BITLEN 15 #define CODE_LENGTH_BITLEN 7 #define MAX_BIT_LENGTH 15 #define DEFLATE_CODE_BUFFER_SIZE (NUM_DEFLATE_CODE_SYMBOLS * 2) #define DISTANCE_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2) #define CODE_LENGTH_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2) #define SET_ERROR(upng, code) \ do \ { \ (upng)->error = (code); \ (upng)->error_line = __LINE__; \ } while (0) #define upng_chunk_length(chunk) MAKE_DWORD_PTR(chunk) #define upng_chunk_type(chunk) MAKE_DWORD_PTR((chunk) + 4) #define upng_chunk_critical(chunk) (((chunk)[4] & 32) == 0) typedef enum upng_state { UPNG_ERROR = -1, UPNG_DECODED = 0, UPNG_HEADER = 1, UPNG_NEW = 2 } upng_state; typedef enum upng_color { UPNG_LUM = 0, UPNG_RGB = 2, UPNG_INDX = 3, UPNG_LUMA = 4, UPNG_RGBA = 6 } upng_color; typedef struct upng_source { const unsigned char *buffer; unsigned long size; char owning; } upng_source; struct upng_t { unsigned width; unsigned height; upng_color color_type; unsigned color_depth; upng_format format; unsigned char *buffer; unsigned long size; unsigned char *palette; upng_error error; unsigned error_line; upng_state state; upng_source source; }; typedef struct huffman_tree { unsigned *tree2d; unsigned maxbitlen; unsigned numcodes; } huffman_tree; static const unsigned LENGTH_BASE[29] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}; static const unsigned LENGTH_EXTRA[29] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; static const unsigned DISTANCE_BASE[30] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; static const unsigned DISTANCE_EXTRA[30] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; static const unsigned CLCL[NUM_CODE_LENGTH_CODES] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; static const unsigned FIXED_DEFLATE_CODE_TREE[NUM_DEFLATE_CODE_SYMBOLS * 2] = { 289, 370, 290, 307, 546, 291, 561, 292, 293, 300, 294, 297, 295, 296, 0, 1, 2, 3, 298, 299, 4, 5, 6, 7, 301, 304, 302, 303, 8, 9, 10, 11, 305, 306, 12, 13, 14, 15, 308, 339, 309, 324, 310, 317, 311, 314, 312, 313, 16, 17, 18, 19, 315, 316, 20, 21, 22, 23, 318, 321, 319, 320, 24, 25, 26, 27, 322, 323, 28, 29, 30, 31, 325, 332, 326, 329, 327, 328, 32, 33, 34, 35, 330, 331, 36, 37, 38, 39, 333, 336, 334, 335, 40, 41, 42, 43, 337, 338, 44, 45, 46, 47, 340, 355, 341, 348, 342, 345, 343, 344, 48, 49, 50, 51, 346, 347, 52, 53, 54, 55, 349, 352, 350, 351, 56, 57, 58, 59, 353, 354, 60, 61, 62, 63, 356, 363, 357, 360, 358, 359, 64, 65, 66, 67, 361, 362, 68, 69, 70, 71, 364, 367, 365, 366, 72, 73, 74, 75, 368, 369, 76, 77, 78, 79, 371, 434, 372, 403, 373, 388, 374, 381, 375, 378, 376, 377, 80, 81, 82, 83, 379, 380, 84, 85, 86, 87, 382, 385, 383, 384, 88, 89, 90, 91, 386, 387, 92, 93, 94, 95, 389, 396, 390, 393, 391, 392, 96, 97, 98, 99, 394, 395, 100, 101, 102, 103, 397, 400, 398, 399, 104, 105, 106, 107, 401, 402, 108, 109, 110, 111, 404, 419, 405, 412, 406, 409, 407, 408, 112, 113, 114, 115, 410, 411, 116, 117, 118, 119, 413, 416, 414, 415, 120, 121, 122, 123, 417, 418, 124, 125, 126, 127, 420, 427, 421, 424, 422, 423, 128, 129, 130, 131, 425, 426, 132, 133, 134, 135, 428, 431, 429, 430, 136, 137, 138, 139, 432, 433, 140, 141, 142, 143, 435, 483, 436, 452, 568, 437, 438, 445, 439, 442, 440, 441, 144, 145, 146, 147, 443, 444, 148, 149, 150, 151, 446, 449, 447, 448, 152, 153, 154, 155, 450, 451, 156, 157, 158, 159, 453, 468, 454, 461, 455, 458, 456, 457, 160, 161, 162, 163, 459, 460, 164, 165, 166, 167, 462, 465, 463, 464, 168, 169, 170, 171, 466, 467, 172, 173, 174, 175, 469, 476, 470, 473, 471, 472, 176, 177, 178, 179, 474, 475, 180, 181, 182, 183, 477, 480, 478, 479, 184, 185, 186, 187, 481, 482, 188, 189, 190, 191, 484, 515, 485, 500, 486, 493, 487, 490, 488, 489, 192, 193, 194, 195, 491, 492, 196, 197, 198, 199, 494, 497, 495, 496, 200, 201, 202, 203, 498, 499, 204, 205, 206, 207, 501, 508, 502, 505, 503, 504, 208, 209, 210, 211, 506, 507, 212, 213, 214, 215, 509, 512, 510, 511, 216, 217, 218, 219, 513, 514, 220, 221, 222, 223, 516, 531, 517, 524, 518, 521, 519, 520, 224, 225, 226, 227, 522, 523, 228, 229, 230, 231, 525, 528, 526, 527, 232, 233, 234, 235, 529, 530, 236, 237, 238, 239, 532, 539, 533, 536, 534, 535, 240, 241, 242, 243, 537, 538, 244, 245, 246, 247, 540, 543, 541, 542, 248, 249, 250, 251, 544, 545, 252, 253, 254, 255, 547, 554, 548, 551, 549, 550, 256, 257, 258, 259, 552, 553, 260, 261, 262, 263, 555, 558, 556, 557, 264, 265, 266, 267, 559, 560, 268, 269, 270, 271, 562, 565, 563, 564, 272, 273, 274, 275, 566, 567, 276, 277, 278, 279, 569, 572, 570, 571, 280, 281, 282, 283, 573, 574, 284, 285, 286, 287, 0, 0}; static const unsigned FIXED_DISTANCE_TREE[NUM_DISTANCE_SYMBOLS * 2] = { 33, 48, 34, 41, 35, 38, 36, 37, 0, 1, 2, 3, 39, 40, 4, 5, 6, 7, 42, 45, 43, 44, 8, 9, 10, 11, 46, 47, 12, 13, 14, 15, 49, 56, 50, 53, 51, 52, 16, 17, 18, 19, 54, 55, 20, 21, 22, 23, 57, 60, 58, 59, 24, 25, 26, 27, 61, 62, 28, 29, 30, 31, 0, 0}; static unsigned char read_bit(unsigned long *bitpointer, const unsigned char *bitstream) { unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> ((*bitpointer) & 0x7)) & 1); (*bitpointer)++; return result; } static unsigned read_bits(unsigned long *bitpointer, const unsigned char *bitstream, unsigned long nbits) { unsigned result = 0, i; for (i = 0; i < nbits; i++) result |= ((unsigned)read_bit(bitpointer, bitstream)) << i; return result; } static void huffman_tree_init(huffman_tree *tree, unsigned *buffer, unsigned numcodes, unsigned maxbitlen) { tree->tree2d = buffer; tree->numcodes = numcodes; tree->maxbitlen = maxbitlen; } static void huffman_tree_create_lengths(upng_t *upng, huffman_tree *tree, const unsigned *bitlen) { unsigned tree1d[MAX_SYMBOLS]; unsigned blcount[MAX_BIT_LENGTH]; unsigned nextcode[MAX_BIT_LENGTH + 1]; unsigned bits, n, i; unsigned nodefilled = 0; unsigned treepos = 0; SDL_memset(blcount, 0, sizeof(blcount)); SDL_memset(nextcode, 0, sizeof(nextcode)); for (bits = 0; bits < tree->numcodes; bits++) { blcount[bitlen[bits]]++; } for (bits = 1; bits <= tree->maxbitlen; bits++) { nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; } for (n = 0; n < tree->numcodes; n++) { if (bitlen[n] != 0) { tree1d[n] = nextcode[bitlen[n]]++; } } for (n = 0; n < tree->numcodes * 2; n++) { tree->tree2d[n] = 32767; } for (n = 0; n < tree->numcodes; n++) { for (i = 0; i < bitlen[n]; i++) { unsigned char bit = (unsigned char)((tree1d[n] >> (bitlen[n] - i - 1)) & 1); if (treepos > tree->numcodes - 2) { SET_ERROR(upng, UPNG_EMALFORMED); return; } if (tree->tree2d[2 * treepos + bit] == 32767) { if (i + 1 == bitlen[n]) { tree->tree2d[2 * treepos + bit] = n; treepos = 0; } else { nodefilled++; tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; treepos = nodefilled; } } else { treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes; } } } for (n = 0; n < tree->numcodes * 2; n++) { if (tree->tree2d[n] == 32767) { tree->tree2d[n] = 0; } } } static unsigned huffman_decode_symbol(upng_t *upng, const unsigned char *in, unsigned long *bp, const huffman_tree *codetree, unsigned long inlength) { unsigned treepos = 0, ct; unsigned char bit; for (;;) { if (((*bp) & 0x07) == 0 && ((*bp) >> 3) > inlength) { SET_ERROR(upng, UPNG_EMALFORMED); return 0; } bit = read_bit(bp, in); ct = codetree->tree2d[(treepos << 1) | bit]; if (ct < codetree->numcodes) { return ct; } treepos = ct - codetree->numcodes; if (treepos >= codetree->numcodes) { SET_ERROR(upng, UPNG_EMALFORMED); return 0; } } } static void get_tree_inflate_dynamic(upng_t *upng, huffman_tree *codetree, huffman_tree *codetreeD, huffman_tree *codelengthcodetree, const unsigned char *in, unsigned long *bp, unsigned long inlength) { unsigned codelengthcode[NUM_CODE_LENGTH_CODES]; unsigned bitlen[NUM_DEFLATE_CODE_SYMBOLS]; unsigned bitlenD[NUM_DISTANCE_SYMBOLS]; unsigned n, hlit, hdist, hclen, i; if ((*bp) >> 3 >= inlength - 2) { SET_ERROR(upng, UPNG_EMALFORMED); return; } SDL_memset(bitlen, 0, sizeof(bitlen)); SDL_memset(bitlenD, 0, sizeof(bitlenD)); hlit = read_bits(bp, in, 5) + 257; hdist = read_bits(bp, in, 5) + 1; hclen = read_bits(bp, in, 4) + 4; for (i = 0; i < NUM_CODE_LENGTH_CODES; i++) { if (i < hclen) { codelengthcode[CLCL[i]] = read_bits(bp, in, 3); } else { codelengthcode[CLCL[i]] = 0; } } huffman_tree_create_lengths(upng, codelengthcodetree, codelengthcode); if (upng->error != UPNG_EOK) { return; } i = 0; while (i < hlit + hdist) { unsigned code = huffman_decode_symbol(upng, in, bp, codelengthcodetree, inlength); if (upng->error != UPNG_EOK) { break; } if (code <= 15) { if (i < hlit) { bitlen[i] = code; } else { bitlenD[i - hlit] = code; } i++; } else if (code == 16) { unsigned replength = 3; unsigned value; if ((*bp) >> 3 >= inlength) { SET_ERROR(upng, UPNG_EMALFORMED); break; } replength += read_bits(bp, in, 2); if ((i - 1) < hlit) { value = bitlen[i - 1]; } else { value = bitlenD[i - hlit - 1]; } for (n = 0; n < replength; n++) { if (i >= hlit + hdist) { SET_ERROR(upng, UPNG_EMALFORMED); break; } if (i < hlit) { bitlen[i] = value; } else { bitlenD[i - hlit] = value; } i++; } } else if (code == 17) { unsigned replength = 3; if ((*bp) >> 3 >= inlength) { SET_ERROR(upng, UPNG_EMALFORMED); break; } replength += read_bits(bp, in, 3); for (n = 0; n < replength; n++) { if (i >= hlit + hdist) { SET_ERROR(upng, UPNG_EMALFORMED); break; } if (i < hlit) { bitlen[i] = 0; } else { bitlenD[i - hlit] = 0; } i++; } } else if (code == 18) { unsigned replength = 11; if ((*bp) >> 3 >= inlength) { SET_ERROR(upng, UPNG_EMALFORMED); break; } replength += read_bits(bp, in, 7); for (n = 0; n < replength; n++) { if (i >= hlit + hdist) { SET_ERROR(upng, UPNG_EMALFORMED); break; } if (i < hlit) bitlen[i] = 0; else bitlenD[i - hlit] = 0; i++; } } else { SET_ERROR(upng, UPNG_EMALFORMED); break; } } if (upng->error == UPNG_EOK && bitlen[256] == 0) { SET_ERROR(upng, UPNG_EMALFORMED); } if (upng->error == UPNG_EOK) { huffman_tree_create_lengths(upng, codetree, bitlen); } if (upng->error == UPNG_EOK) { huffman_tree_create_lengths(upng, codetreeD, bitlenD); } } static void inflate_huffman(upng_t *upng, unsigned char *out, unsigned long outsize, const unsigned char *in, unsigned long *bp, unsigned long *pos, unsigned long inlength, unsigned btype) { unsigned codetree_buffer[DEFLATE_CODE_BUFFER_SIZE]; unsigned codetreeD_buffer[DISTANCE_BUFFER_SIZE]; unsigned done = 0; huffman_tree codetree; huffman_tree codetreeD; if (btype == 1) { huffman_tree_init(&codetree, (unsigned *)FIXED_DEFLATE_CODE_TREE, NUM_DEFLATE_CODE_SYMBOLS, DEFLATE_CODE_BITLEN); huffman_tree_init(&codetreeD, (unsigned *)FIXED_DISTANCE_TREE, NUM_DISTANCE_SYMBOLS, DISTANCE_BITLEN); } else if (btype == 2) { unsigned codelengthcodetree_buffer[CODE_LENGTH_BUFFER_SIZE]; huffman_tree codelengthcodetree; huffman_tree_init(&codetree, codetree_buffer, NUM_DEFLATE_CODE_SYMBOLS, DEFLATE_CODE_BITLEN); huffman_tree_init(&codetreeD, codetreeD_buffer, NUM_DISTANCE_SYMBOLS, DISTANCE_BITLEN); huffman_tree_init(&codelengthcodetree, codelengthcodetree_buffer, NUM_CODE_LENGTH_CODES, CODE_LENGTH_BITLEN); get_tree_inflate_dynamic(upng, &codetree, &codetreeD, &codelengthcodetree, in, bp, inlength); } while (done == 0) { unsigned code = huffman_decode_symbol(upng, in, bp, &codetree, inlength); if (upng->error != UPNG_EOK) { return; } if (code == 256) { done = 1; } else if (code <= 255) { if ((*pos) >= outsize) { SET_ERROR(upng, UPNG_EMALFORMED); return; } out[(*pos)++] = (unsigned char)(code); } else if (code >= FIRST_LENGTH_CODE_INDEX && code <= LAST_LENGTH_CODE_INDEX) { unsigned long length = LENGTH_BASE[code - FIRST_LENGTH_CODE_INDEX]; unsigned codeD, distance, numextrabitsD; unsigned long start, forward, backward, numextrabits; numextrabits = LENGTH_EXTRA[code - FIRST_LENGTH_CODE_INDEX]; if (((*bp) >> 3) >= inlength) { SET_ERROR(upng, UPNG_EMALFORMED); return; } length += read_bits(bp, in, numextrabits); codeD = huffman_decode_symbol(upng, in, bp, &codetreeD, inlength); if (upng->error != UPNG_EOK) { return; } if (codeD > 29) { SET_ERROR(upng, UPNG_EMALFORMED); return; } distance = DISTANCE_BASE[codeD]; numextrabitsD = DISTANCE_EXTRA[codeD]; if (((*bp) >> 3) >= inlength) { SET_ERROR(upng, UPNG_EMALFORMED); return; } distance += read_bits(bp, in, numextrabitsD); start = (*pos); backward = start - distance; if ((*pos) + length >= outsize) { SET_ERROR(upng, UPNG_EMALFORMED); return; } for (forward = 0; forward < length; forward++) { out[(*pos)++] = out[backward]; backward++; if (backward >= start) { backward = start - distance; } } } } } static void inflate_uncompressed(upng_t *upng, unsigned char *out, unsigned long outsize, const unsigned char *in, unsigned long *bp, unsigned long *pos, unsigned long inlength) { unsigned long p; unsigned len, nlen, n; while (((*bp) & 0x7) != 0) { (*bp)++; } p = (*bp) / 8; if (p >= inlength - 4) { SET_ERROR(upng, UPNG_EMALFORMED); return; } len = in[p] + 256 * in[p + 1]; p += 2; nlen = in[p] + 256 * in[p + 1]; p += 2; if (len + nlen != 65535) { SET_ERROR(upng, UPNG_EMALFORMED); return; } if ((*pos) + len >= outsize) { SET_ERROR(upng, UPNG_EMALFORMED); return; } if (p + len > inlength) { SET_ERROR(upng, UPNG_EMALFORMED); return; } for (n = 0; n < len; n++) { out[(*pos)++] = in[p++]; } (*bp) = p * 8; } static upng_error uz_inflate_data(upng_t *upng, unsigned char *out, unsigned long outsize, const unsigned char *in, unsigned long insize, unsigned long inpos) { unsigned long bp = 0; unsigned long pos = 0; unsigned done = 0; while (done == 0) { unsigned btype; if ((bp >> 3) >= insize) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } done = read_bit(&bp, &in[inpos]); btype = read_bit(&bp, &in[inpos]) | (read_bit(&bp, &in[inpos]) << 1); if (btype == 3) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } else if (btype == 0) { inflate_uncompressed(upng, out, outsize, &in[inpos], &bp, &pos, insize); } else { inflate_huffman(upng, out, outsize, &in[inpos], &bp, &pos, insize, btype); } if (upng->error != UPNG_EOK) { return upng->error; } } return upng->error; } static upng_error uz_inflate(upng_t *upng, unsigned char *out, unsigned long outsize, const unsigned char *in, unsigned long insize) { if (insize < 2) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } if ((in[0] * 256 + in[1]) % 31 != 0) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } if ((in[0] & 15) != 8 || ((in[0] >> 4) & 15) > 7) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } if (((in[1] >> 5) & 1) != 0) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } uz_inflate_data(upng, out, outsize, in, insize, 2); return upng->error; } static int paeth_predictor(int a, int b, int c) { int p = a + b - c; int pa = p > a ? p - a : a - p; int pb = p > b ? p - b : b - p; int pc = p > c ? p - c : c - p; if (pa <= pb && pa <= pc) return a; else if (pb <= pc) return b; else return c; } static void unfilter_scanline(upng_t *upng, unsigned char *recon, const unsigned char *scanline, const unsigned char *precon, unsigned long bytewidth, unsigned char filterType, unsigned long length) { unsigned long i; switch (filterType) { case 0: for (i = 0; i < length; i++) recon[i] = scanline[i]; break; case 1: for (i = 0; i < bytewidth; i++) recon[i] = scanline[i]; for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; break; case 2: if (precon) for (i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; else for (i = 0; i < length; i++) recon[i] = scanline[i]; break; case 3: if (precon) { for (i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); } else { for (i = 0; i < bytewidth; i++) recon[i] = scanline[i]; for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2; } break; case 4: if (precon) { for (i = 0; i < bytewidth; i++) recon[i] = (unsigned char)(scanline[i] + paeth_predictor(0, precon[i], 0)); for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paeth_predictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); } else { for (i = 0; i < bytewidth; i++) recon[i] = scanline[i]; for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paeth_predictor(recon[i - bytewidth], 0, 0)); } break; default: SET_ERROR(upng, UPNG_EMALFORMED); break; } } static void unfilter(upng_t *upng, unsigned char *out, const unsigned char *in, unsigned w, unsigned h, unsigned bpp) { unsigned y; unsigned char *prevline = 0; unsigned long bytewidth = (bpp + 7) / 8; unsigned long linebytes = (w * bpp + 7) / 8; for (y = 0; y < h; y++) { unsigned long outindex = linebytes * y; unsigned long inindex = (1 + linebytes) * y; unsigned char filterType = in[inindex]; unfilter_scanline(upng, &out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes); if (upng->error != UPNG_EOK) { return; } prevline = &out[outindex]; } } static void remove_padding_bits(unsigned char *out, const unsigned char *in, unsigned long olinebits, unsigned long ilinebits, unsigned h) { unsigned y; unsigned long diff = ilinebits - olinebits; unsigned long obp = 0, ibp = 0; for (y = 0; y < h; y++) { unsigned long x; for (x = 0; x < olinebits; x++) { unsigned char bit = (unsigned char)((in[(ibp) >> 3] >> (7 - ((ibp) & 0x7))) & 1); ibp++; if (bit == 0) out[(obp) >> 3] &= (unsigned char)(~(1 << (7 - ((obp) & 0x7)))); else out[(obp) >> 3] |= (1 << (7 - ((obp) & 0x7))); ++obp; } ibp += diff; } } static void post_process_scanlines(upng_t *upng, unsigned char *out, unsigned char *in, const upng_t *info_png) { unsigned bpp = upng_get_bpp(info_png); unsigned w = info_png->width; unsigned h = info_png->height; if (bpp == 0) { SET_ERROR(upng, UPNG_EMALFORMED); return; } if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) { unfilter(upng, in, in, w, h, bpp); if (upng->error != UPNG_EOK) { return; } remove_padding_bits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); } else { unfilter(upng, out, in, w, h, bpp); } } static upng_format determine_format(upng_t *upng) { switch (upng->color_type) { case UPNG_LUM: switch (upng->color_depth) { case 1: return UPNG_LUMINANCE1; case 2: return UPNG_LUMINANCE2; case 4: return UPNG_LUMINANCE4; case 8: return UPNG_LUMINANCE8; default: return UPNG_BADFORMAT; } case UPNG_RGB: switch (upng->color_depth) { case 8: return UPNG_RGB8; case 16: return UPNG_RGB16; default: return UPNG_BADFORMAT; } case UPNG_LUMA: switch (upng->color_depth) { case 1: return UPNG_LUMINANCE_ALPHA1; case 2: return UPNG_LUMINANCE_ALPHA2; case 4: return UPNG_LUMINANCE_ALPHA4; case 8: return UPNG_LUMINANCE_ALPHA8; default: return UPNG_BADFORMAT; } case UPNG_RGBA: switch (upng->color_depth) { case 8: return UPNG_RGBA8; case 16: return UPNG_RGBA16; default: return UPNG_BADFORMAT; } case UPNG_INDX: switch (upng->color_depth) { case 1: return UPNG_INDEX1; case 2: return UPNG_INDEX2; case 4: return UPNG_INDEX4; case 8: return UPNG_INDEX8; default: return UPNG_BADFORMAT; } default: return UPNG_BADFORMAT; } } static void upng_free_source(upng_t *upng) { if (upng->source.owning != 0) { SDL_free((void *)upng->source.buffer); } upng->source.buffer = nullptr; upng->source.size = 0; upng->source.owning = 0; } upng_error upng_header(upng_t *upng) { if (upng->error != UPNG_EOK) { return upng->error; } if (upng->state != UPNG_NEW) { return upng->error; } if (upng->source.size < 29) { SET_ERROR(upng, UPNG_ENOTPNG); return upng->error; } if (upng->source.buffer[0] != 137 || upng->source.buffer[1] != 80 || upng->source.buffer[2] != 78 || upng->source.buffer[3] != 71 || upng->source.buffer[4] != 13 || upng->source.buffer[5] != 10 || upng->source.buffer[6] != 26 || upng->source.buffer[7] != 10) { SET_ERROR(upng, UPNG_ENOTPNG); return upng->error; } if (MAKE_DWORD_PTR(upng->source.buffer + 12) != CHUNK_IHDR) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } upng->width = MAKE_DWORD_PTR(upng->source.buffer + 16); upng->height = MAKE_DWORD_PTR(upng->source.buffer + 20); upng->color_depth = upng->source.buffer[24]; upng->color_type = (upng_color)upng->source.buffer[25]; upng->format = determine_format(upng); if (upng->format == UPNG_BADFORMAT) { SET_ERROR(upng, UPNG_EUNFORMAT); return upng->error; } if (upng->source.buffer[26] != 0) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } if (upng->source.buffer[27] != 0) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } if (upng->source.buffer[28] != 0) { SET_ERROR(upng, UPNG_EUNINTERLACED); return upng->error; } upng->state = UPNG_HEADER; return upng->error; } upng_error upng_decode(upng_t *upng) { const unsigned char *chunk; unsigned char *compressed; unsigned char *inflated; unsigned char *palette = nullptr; unsigned long compressed_size = 0, compressed_index = 0; unsigned long palette_size = 0; unsigned long inflated_size; upng_error error; if (upng->error != UPNG_EOK) { return upng->error; } upng_header(upng); if (upng->error != UPNG_EOK) { return upng->error; } if (upng->state != UPNG_HEADER) { return upng->error; } if (upng->buffer != nullptr) { SDL_free(upng->buffer); upng->buffer = nullptr; upng->size = 0; } chunk = upng->source.buffer + 33; while (chunk < upng->source.buffer + upng->source.size) { unsigned long length; if ((unsigned long)(chunk - upng->source.buffer + 12) > upng->source.size) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } length = upng_chunk_length(chunk); if (length > INT_MAX) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } if ((unsigned long)(chunk - upng->source.buffer + length + 12) > upng->source.size) { SET_ERROR(upng, UPNG_EMALFORMED); return upng->error; } if (upng_chunk_type(chunk) == CHUNK_IDAT) { compressed_size += length; } else if (upng_chunk_type(chunk) == CHUNK_PLTE) { palette_size = length; } else if (upng_chunk_type(chunk) == CHUNK_IEND) { break; } else if (upng_chunk_critical(chunk)) { SET_ERROR(upng, UPNG_EUNSUPPORTED); return upng->error; } chunk += length + 12; } compressed = (unsigned char *)SDL_malloc(compressed_size); if (compressed == nullptr) { SET_ERROR(upng, UPNG_ENOMEM); return upng->error; } if (palette_size) { palette = (unsigned char *)SDL_malloc(palette_size); if (palette == nullptr) { SDL_free(compressed); SET_ERROR(upng, UPNG_ENOMEM); return upng->error; } } chunk = upng->source.buffer + 33; while (chunk < upng->source.buffer + upng->source.size) { unsigned long length; const unsigned char *data; length = upng_chunk_length(chunk); if (upng_chunk_type(chunk) == CHUNK_IDAT) { data = chunk + 8; SDL_memcpy(compressed + compressed_index, data, length); compressed_index += length; } else if (upng_chunk_type(chunk) == CHUNK_PLTE) { data = chunk + 8; SDL_memcpy(palette, data, palette_size); } else if (upng_chunk_type(chunk) == CHUNK_IEND) { break; } chunk += length + 12; } inflated_size = ((upng->width * (upng->height * upng_get_bpp(upng) + 7)) / 8) + upng->height; inflated = (unsigned char *)SDL_malloc(inflated_size); if (inflated == nullptr) { SDL_free(palette); SDL_free(compressed); SET_ERROR(upng, UPNG_ENOMEM); return upng->error; } error = uz_inflate(upng, inflated, inflated_size, compressed, compressed_size); if (error != UPNG_EOK) { SDL_free(palette); SDL_free(compressed); SDL_free(inflated); return upng->error; } SDL_free(compressed); upng->size = (upng->height * upng->width * upng_get_bpp(upng) + 7) / 8; upng->buffer = (unsigned char *)SDL_malloc(upng->size); if (upng->buffer == nullptr) { SDL_free(palette); SDL_free(inflated); upng->size = 0; SET_ERROR(upng, UPNG_ENOMEM); return upng->error; } post_process_scanlines(upng, upng->buffer, inflated, upng); SDL_free(inflated); if (upng->error != UPNG_EOK) { SDL_free(palette); SDL_free(upng->buffer); upng->buffer = nullptr; upng->size = 0; } else { upng->palette = palette; upng->state = UPNG_DECODED; } upng_free_source(upng); return upng->error; } static upng_t *upng_new(void) { upng_t *upng; upng = (upng_t *)SDL_malloc(sizeof(upng_t)); if (upng == nullptr) { return nullptr; } upng->buffer = nullptr; upng->size = 0; upng->width = upng->height = 0; upng->color_type = UPNG_RGBA; upng->color_depth = 8; upng->format = UPNG_RGBA8; upng->state = UPNG_NEW; upng->error = UPNG_EOK; upng->error_line = 0; upng->source.buffer = nullptr; upng->source.size = 0; upng->source.owning = 0; return upng; } upng_t *upng_new_from_bytes(const unsigned char *buffer, unsigned long size) { upng_t *upng = upng_new(); if (upng == nullptr) { return nullptr; } upng->source.buffer = buffer; upng->source.size = size; upng->source.owning = 0; return upng; } void upng_free(upng_t *upng) { if (upng->palette != nullptr) { SDL_free(upng->palette); } if (upng->buffer != nullptr) { SDL_free(upng->buffer); } upng_free_source(upng); SDL_free(upng); } upng_error upng_get_error(const upng_t *upng) { return upng->error; } unsigned upng_get_error_line(const upng_t *upng) { return upng->error_line; } unsigned upng_get_width(const upng_t *upng) { return upng->width; } unsigned upng_get_height(const upng_t *upng) { return upng->height; } unsigned upng_get_bpp(const upng_t *upng) { return upng_get_bitdepth(upng) * upng_get_components(upng); } unsigned upng_get_components(const upng_t *upng) { switch (upng->color_type) { case UPNG_LUM: return 1; case UPNG_RGB: return 3; case UPNG_LUMA: return 2; case UPNG_RGBA: return 4; case UPNG_INDX: return 1; default: return 0; } } unsigned upng_get_bitdepth(const upng_t *upng) { return upng->color_depth; } unsigned upng_get_pixelsize(const upng_t *upng) { unsigned bits = upng_get_bitdepth(upng) * upng_get_components(upng); bits += bits % 8; return bits; } upng_format upng_get_format(const upng_t *upng) { return upng->format; } const unsigned char *upng_get_buffer(const upng_t *upng) { return upng->buffer; } unsigned upng_get_size(const upng_t *upng) { return upng->size; } const unsigned char *upng_get_palette(const upng_t *upng) { return upng->palette; } #endif