Index: apps/recorder/png.c =================================================================== --- apps/recorder/png.c (revision 0) +++ apps/recorder/png.c (revision 0) @@ -0,0 +1,1937 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Christophe Gouiran + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* + Based on LodePNG version 20080927 + + Copyright (c) 2005-2008 Lode Vandevenne + + 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 +#include +#include + +#include "file.h" +#include "png.h" + +#define MAX_SCANLINES_SIZE (((400*(400*32+7))/8)+400) +unsigned char scanlines[MAX_SCANLINES_SIZE]; +size_t scanlines_size; + +#include "config.h" +#include "lcd.h" + +#include "debug.h" + +/* ////////////////////////////////////////////////////////////////////////// */ +/* Code Sections */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* Flate & Zlib Setting structs */ +/* ////////////////////////////////////////////////////////////////////////// */ + +typedef struct PNG_InfoColor /*info about the color type of an image*/ +{ + /*header (IHDR)*/ + unsigned colorType; /*color type*/ + unsigned bitDepth; /*bits per sample*/ + + /*palette (PLTE)*/ + unsigned char palette[256 * 4]; /*palette in RGBARGBA... order*/ + size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ + + /*transparent color key (tRNS)*/ + unsigned key_defined; /*is a transparent color key given?*/ + unsigned key_r; /*red component of color key*/ + unsigned key_g; /*green component of color key*/ + unsigned key_b; /*blue component of color key*/ +} PNG_InfoColor; + +/*header (IHDR), palette (PLTE) and transparency (tRNS)*/ +static unsigned info_width; /*width of the image in pixels (ignored by encoder, but filled in by decoder)*/ +static unsigned info_height; /*height of the image in pixels (ignored by encoder, but filled in by decoder)*/ +static unsigned info_compressionMethod; /*compression method of the original file*/ +static unsigned info_filterMethod; /*filter method of the original file*/ +static unsigned info_interlaceMethod; /*interlace method of the original file*/ +static PNG_InfoColor info_color; /*color type and bits, palette, transparency*/ + +static PNG_InfoColor raw_color; + +static unsigned color_convert = 0; /*whether to convert the PNG to the color type you want. Default: yes*/ + +static unsigned error; + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing single bits and bytes from/to stream for Deflate / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned char readBitFromStream(size_t* bitpointer, + const unsigned char* bitstream) +{ + unsigned char result = (unsigned char) ((bitstream[(*bitpointer) >> 3] + >> ((*bitpointer) & 0x7)) & 1); + (*bitpointer)++; + return result; +} + +static unsigned readBitsFromStream(size_t* bitpointer, + const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0, i; + for (i = 0; i < nbits; i++) + result += ((unsigned) readBitFromStream(bitpointer, bitstream)) << i; + return result; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflate - Huffman / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#define FIRST_LENGTH_CODE_INDEX 257 +#define LAST_LENGTH_CODE_INDEX 285 +#define NUM_DEFLATE_CODE_SYMBOLS 288 /*256 literals, the end code, some length codes, and 2 unused codes*/ +#define NUM_DISTANCE_SYMBOLS 32 /*the distance codes have their own symbols, 30 used, 2 unused*/ +#define NUM_CODE_LENGTH_CODES 19 /*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ + +static const unsigned LENGTHBASE[29] /*the base lengths represented by codes 257-285*/ += +{ 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 LENGTHEXTRA[29] /*the extra bits used by codes 257-285 (added to base length)*/ += +{ 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 DISTANCEBASE[30] /*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ += +{ 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 DISTANCEEXTRA[30] /*the extra bits of backwards distances (added to base)*/ += +{ 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] /*the order in which "code length alphabet code lengths" are stored, out of this the huffman tree of the dynamic huffman tree lengths is generated*/ += +{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +/* /////////////////////////////////////////////////////////////////////////// */ + +/*the tree representation used by the decoder. return value is error*/ +static unsigned HuffmanTree_make2DTree(unsigned *tree2d, unsigned *tree1d, + unsigned *lengths, unsigned *numcodes) +{ + unsigned nodefilled = 0; /*up to which node it is filled*/ + unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/ + unsigned n, i; + + /*convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means uninited, a value >= numcodes is an address to another bit, a value < numcodes is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as many columns as codes - 1 + a good huffmann tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. Here, the internal nodes are stored (what their 0 and 1 option point to). There is only memory for such good tree currently, if there are more nodes (due to too long length codes), error 55 will happen*/ + for (n = 0; n < *numcodes * 2; n++) + tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ + + for (n = 0; n < *numcodes; n++) + /*the codes*/ + for (i = 0; i < lengths[n]; i++) /*the bits for this code*/ + { + unsigned char bit = (unsigned char) ((tree1d[n] >> (lengths[n] - i + - 1)) & 1); + if (treepos > *numcodes - 2) + return 55; /*error 55: oversubscribed; see description in header*/ + if (tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ + { + if (i + 1 == lengths[n]) /*last bit*/ + { + tree2d[2 * treepos + bit] = n; /*put the current code in it*/ + treepos = 0; + } + else /*put address of the next step in here, first that address has to be found of course (it's just nodefilled + 1)...*/ + { + nodefilled++; + tree2d[2 * treepos + bit] = nodefilled + *numcodes; /*addresses encoded with numcodes added to it*/ + treepos = nodefilled; + } + } + else + treepos = tree2d[2 * treepos + bit] - *numcodes; + } + for (n = 0; n < *numcodes * 2; n++) + if (tree2d[n] == 32767) + tree2d[n] = 0; /*remove possible remaining 32767's*/ + + return 0; +} + +static unsigned HuffmanTree_makeFromLengths2(unsigned *tree2d, + unsigned *tree1d, unsigned *lengths, unsigned *numcodes, + unsigned *maxbitlen) /*given that numcodes, lengths and maxbitlen are already filled in correctly. return value is error.*/ +{ + unsigned blcount[] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + unsigned nextcode[] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + unsigned bits, n; + + /*step 1: count number of instances of each code length*/ + for (bits = 0; bits < *numcodes; bits++) + blcount[lengths[bits]]++; + /*step 2: generate the nextcode values*/ + for (bits = 1; bits <= *maxbitlen; bits++) + nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; + /*step 3: generate all the codes*/ + for (n = 0; n < *numcodes; n++) + if (lengths[n] != 0) + tree1d[n] = nextcode[lengths[n]]++; + + return HuffmanTree_make2DTree(tree2d, tree1d, lengths, numcodes); + +} + +/*given the code lengths (as stored in the PNG file), generate the tree as defined by Deflate. maxbitlen is the maximum bits that a code in the tree can have. return value is error.*/ +static unsigned HuffmanTree_makeFromLengths(unsigned *tree2d, unsigned *tree1d, + unsigned *lengths, unsigned *numcodes, unsigned *maxbitlen, + const unsigned* bitlen, size_t in_numcodes, unsigned in_maxbitlen) +{ + memcpy(lengths, bitlen, in_numcodes * sizeof(unsigned)); + *numcodes = (unsigned) in_numcodes; /*number of symbols*/ + *maxbitlen = in_maxbitlen; + return HuffmanTree_makeFromLengths2(tree2d, tree1d, lengths, numcodes, + maxbitlen); +} + +/*Decodes a symbol from the tree + if decoded is true, then result contains the symbol, otherwise it contains something unspecified (because the symbol isn't fully decoded yet) + bit is the bit that was just read from the stream + you have to decode a full symbol (let the decode function return true) before you can try to decode another one, otherwise the state isn't reset + return value is error.*/ +static unsigned HuffmanTree_decode(const unsigned* tree2d, + const unsigned numcodes, unsigned* decoded, unsigned* result, + unsigned* treepos, unsigned char bit) +{ + if ((*treepos) >= numcodes) + return 11; /*error: it appeared outside the codetree*/ + + (*result) = tree2d[2 * (*treepos) + bit]; + (*decoded) = ((*result) < numcodes); + + if (*decoded) + (*treepos) = 0; + else + (*treepos) = (*result) - numcodes; + + return 0; +} + +static unsigned huffmanDecodeSymbol(unsigned int* error, + const unsigned char* in, size_t* bp, const unsigned* tree2d, + const unsigned numcodes, size_t inlength) +{ + unsigned treepos = 0, decoded, ct; + for (;;) + { + unsigned char bit; + if (((*bp) & 0x07) == 0 && ((*bp) >> 3) > inlength) + { + *error = 10; + return 0; + } /*error: end of input memory reached without endcode*/ + bit = readBitFromStream(bp, in); + *error = HuffmanTree_decode(tree2d, numcodes, &decoded, &ct, &treepos, + bit); + if (*error) + return 0; /*stop, an error happened*/ + if (decoded) + return ct; + } +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Inflator / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned codelengthcodetree_2d[NUM_CODE_LENGTH_CODES*2]; +unsigned codelengthcodetree_1d[NUM_CODE_LENGTH_CODES]; +unsigned codelengthcodetree_lengths[NUM_CODE_LENGTH_CODES]; +unsigned codelengthcodetree_maxbitlen; +unsigned codelengthcodetree_numcodes; + +unsigned codetree_2d[NUM_DEFLATE_CODE_SYMBOLS*2]; +unsigned codetree_1d[NUM_DEFLATE_CODE_SYMBOLS]; +unsigned codetree_lengths[NUM_DEFLATE_CODE_SYMBOLS]; +unsigned codetree_maxbitlen; +unsigned codetree_numcodes; + +unsigned codetreeD_2d[NUM_DISTANCE_SYMBOLS*2]; +unsigned codetreeD_1d[NUM_DISTANCE_SYMBOLS]; +unsigned codetreeD_lengths[NUM_DISTANCE_SYMBOLS]; +unsigned codetreeD_maxbitlen; +unsigned codetreeD_numcodes; + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static unsigned generateFixedTree(void) +{ + unsigned bitlen[NUM_DEFLATE_CODE_SYMBOLS]; + + memset(bitlen, 8, NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + memset(bitlen + 144, 9, (255 - 114 + 1) * sizeof(unsigned)); + memset(bitlen + 256, 7, (279 - 256 + 1) * sizeof(unsigned)); + + return HuffmanTree_makeFromLengths(codetree_2d, codetree_1d, + codetree_lengths, &codetree_numcodes, &codetree_maxbitlen, bitlen, + NUM_DEFLATE_CODE_SYMBOLS, 15); + +} + +static unsigned generateDistanceTree(void) +{ + unsigned bitlen[NUM_DISTANCE_SYMBOLS]; + + /*there are 32 distance codes, but 30-31 are unused*/ + memset(bitlen, 5, NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + return HuffmanTree_makeFromLengths(codetreeD_2d, codetreeD_1d, + codetreeD_lengths, &codetreeD_numcodes, &codetreeD_maxbitlen, + bitlen, NUM_DISTANCE_SYMBOLS, 15); + +} + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static void getTreeInflateFixed(void) +{ + /*error checking not done, this is fixed stuff, it works, it doesn't depend on the image*/ + generateFixedTree(); + generateDistanceTree(); +} + +/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ +static unsigned getTreeInflateDynamic(const unsigned char* in, size_t* bp, + size_t inlength) +{ + /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ + /*C-code note: use no "return" between ctor and dtor of an uivector!*/ + unsigned error = 0; + unsigned n, HLIT, HDIST, HCLEN, i; + unsigned bitlen[NUM_DEFLATE_CODE_SYMBOLS]; + unsigned bitlenD[NUM_DISTANCE_SYMBOLS]; + unsigned codelengthcode[NUM_CODE_LENGTH_CODES]; + + if ((*bp) >> 3 >= inlength - 2) + { + return 49; + } /*the bit pointer is or will go past the memory*/ + + HLIT = readBitsFromStream(bp, in, 5) + 257; /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ + HDIST = readBitsFromStream(bp, in, 5) + 1; /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ + HCLEN = readBitsFromStream(bp, in, 4) + 4; /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ + + /*read the code length codes out of 3 * (amount of code length codes) bits*/ + + if (!error) + { + for (i = 0; i < NUM_CODE_LENGTH_CODES; i++) + { + if (i < HCLEN) + codelengthcode[CLCL[i]] = readBitsFromStream(bp, in, 3); + else + codelengthcode[CLCL[i]] = 0; /*if not, it must stay 0*/ + } + + error = HuffmanTree_makeFromLengths(codelengthcodetree_2d, + codelengthcodetree_1d, codelengthcodetree_lengths, + &codelengthcodetree_numcodes, &codelengthcodetree_maxbitlen, + codelengthcode, NUM_CODE_LENGTH_CODES, 7); + } + + if (error) + return error; + + /*now we can use this tree to read the lengths for the tree that this function will return*/ + memset(bitlen, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + memset(bitlenD, 0, NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + i = 0; + while (i < HLIT + HDIST) /*i is the current symbol we're reading in the part that contains the code lengths of lit/len codes and dist codes*/ + { + unsigned code = huffmanDecodeSymbol(&error, in, bp, + codelengthcodetree_2d, codelengthcodetree_numcodes, inlength); + if (error) + break; + + if (code <= 15) /*a length code*/ + { + if (i < HLIT) + bitlen[i] = code; + else + bitlenD[i - HLIT] = code; + i++; + } + else if (code == 16) /*repeat previous*/ + { + unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ + unsigned value; /*set value to the previous code*/ + + if ((*bp) >> 3 >= inlength) + { + error = 50; + break; + } /*error, bit pointer jumps past memory*/ + + replength += readBitsFromStream(bp, in, 2); + + if ((i - 1) < HLIT) + value = bitlen[i - 1]; + else + value = bitlenD[i - HLIT - 1]; + /*repeat this value in the next lengths*/ + for (n = 0; n < replength; n++) + { + if (i >= HLIT + HDIST) + { + error = 13; + break; + } /*error: i is larger than the amount of codes*/ + if (i < HLIT) + bitlen[i] = value; + else + bitlenD[i - HLIT] = value; + i++; + } + } + else if (code == 17) /*repeat "0" 3-10 times*/ + { + unsigned replength = 3; /*read in the bits that indicate repeat length*/ + if ((*bp) >> 3 >= inlength) + { + error = 50; + break; + } /*error, bit pointer jumps past memory*/ + + replength += readBitsFromStream(bp, in, 3); + + /*repeat this value in the next lengths*/ + for (n = 0; n < replength; n++) + { + if (i >= HLIT + HDIST) + { + error = 14; + break; + } /*error: i is larger than the amount of codes*/ + if (i < HLIT) + bitlen[i] = 0; + else + bitlenD[i - HLIT] = 0; + i++; + } + } + else if (code == 18) /*repeat "0" 11-138 times*/ + { + unsigned replength = 11; /*read in the bits that indicate repeat length*/ + if ((*bp) >> 3 >= inlength) + { + error = 50; + break; + } /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 7); + + /*repeat this value in the next lengths*/ + for (n = 0; n < replength; n++) + { + if (i >= HLIT + HDIST) + { + error = 15; + break; + } /*error: i is larger than the amount of codes*/ + if (i < HLIT) + bitlen[i] = 0; + else + bitlenD[i - HLIT] = 0; + i++; + } + } + else + { + error = 16; + break; + } /*error: somehow an unexisting code appeared. This can never happen.*/ + } + + if (!error && bitlen[256] == 0) + { + error = 64; + } /*the length of the end code 256 must be larger than 0*/ + + /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ + if (!error) + error = HuffmanTree_makeFromLengths(codetree_2d, codetree_1d, + codetree_lengths, &codetree_numcodes, &codetree_maxbitlen, + bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); + + if (!error) + error = HuffmanTree_makeFromLengths(codetreeD_2d, codetreeD_1d, + codetreeD_lengths, &codetreeD_numcodes, &codetreeD_maxbitlen, + bitlenD, NUM_DISTANCE_SYMBOLS, 15); + + return error; +} + +/*inflate a block with dynamic of fixed Huffman tree*/ +static unsigned inflateHuffmanBlock(const unsigned char* in, size_t* bp, + size_t* pos, size_t inlength, unsigned btype) +{ + unsigned endreached = 0, error = 0; + + if (btype == 1) + getTreeInflateFixed(); + else if (btype == 2) + { + error = getTreeInflateDynamic(in, bp, inlength); + } + + while (!endreached && !error) + { + unsigned code = huffmanDecodeSymbol(&error, in, bp, codetree_2d, + codetree_numcodes, inlength); + if (error) + break; /*some error happened in the above function*/ + if (code == 256) + endreached = 1; /*end code*/ + else if (code <= 255) /*literal symbol*/ + { + if ((*pos) >= scanlines_size) + scanlines_size = (*pos) + 1; /*reserve more room at once*/ + if ((*pos) >= MAX_SCANLINES_SIZE) + { + error = 9913; + break; + } /*not enough memory*/ + scanlines[(*pos)] = (unsigned char) (code); + (*pos)++; + } + else if (code >= FIRST_LENGTH_CODE_INDEX && code + <= LAST_LENGTH_CODE_INDEX) /*length code*/ + { + /*part 1: get length base*/ + size_t length = LENGTHBASE[code - FIRST_LENGTH_CODE_INDEX]; + unsigned codeD, distance, numextrabitsD; + size_t start, forward, backward, numextrabits; + + /*part 2: get extra bits and add the value of that to length*/ + numextrabits = LENGTHEXTRA[code - FIRST_LENGTH_CODE_INDEX]; + if (((*bp) >> 3) >= inlength) + { + error = 51; + break; + } /*error, bit pointer will jump past memory*/ + length += readBitsFromStream(bp, in, numextrabits); + + /*part 3: get distance code*/ + codeD = huffmanDecodeSymbol(&error, in, bp, codetreeD_2d, + codetreeD_numcodes, inlength); + if (error) + break; + if (codeD > 29) + { + error = 18; + break; + } /*error: invalid distance code (30-31 are never used)*/ + distance = DISTANCEBASE[codeD]; + + /*part 4: get extra bits from distance*/ + numextrabitsD = DISTANCEEXTRA[codeD]; + if (((*bp) >> 3) >= inlength) + { + error = 51; + break; + } /*error, bit pointer will jump past memory*/ + distance += readBitsFromStream(bp, in, numextrabitsD); + + /*part 5: fill in all the out[n] values based on the length and dist*/ + start = (*pos); + backward = start - distance; + if ((*pos) + length >= scanlines_size) + scanlines_size = (*pos) + length; /*reserve more room at once*/ + if ((*pos) + length >= MAX_SCANLINES_SIZE) + { + error = 9914; + break; + } /*not enough memory*/ + + for (forward = 0; forward < length; forward++) + { + scanlines[(*pos)] = scanlines[backward]; + (*pos)++; + backward++; + if (backward >= start) + backward = start - distance; + } + } + } + + return error; +} + +static unsigned inflateNoCompression(const unsigned char* in, size_t* bp, + size_t* pos, size_t inlength) +{ + /*go to first boundary of byte*/ + size_t p; + unsigned LEN, NLEN, n, error = 0; + while (((*bp) & 0x7) != 0) + (*bp)++; + p = (*bp) / 8; /*byte position*/ + + /*read LEN (2 bytes) and NLEN (2 bytes)*/ + if (p >= inlength - 4) + return 52; /*error, bit pointer will jump past memory*/ + LEN = in[p] + 256 * in[p + 1]; + p += 2; + NLEN = in[p] + 256 * in[p + 1]; + p += 2; + + /*check if 16-bit NLEN is really the one's complement of LEN*/ + if (LEN + NLEN != 65535) + return 21; /*error: NLEN is not one's complement of LEN*/ + + if ((*pos) + LEN >= scanlines_size) + scanlines_size = (*pos) + LEN; + + if ((*pos) + LEN >= MAX_SCANLINES_SIZE) + return 9915; + + /*read the literal data: LEN bytes are now stored in the out buffer*/ + if (p + LEN > inlength) + return 23; /*error: reading outside of in buffer*/ + for (n = 0; n < LEN; n++) + scanlines[(*pos)++] = in[p++]; + + (*bp) = p * 8; + + return error; +} + +/*inflate the deflated data (cfr. deflate spec); return value is the error*/ +unsigned inflate(const unsigned char* in, size_t insize, size_t inpos) +{ + size_t bp = 0; /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte)*/ + unsigned BFINAL = 0; + size_t pos = 0; /*byte position in the out buffer*/ + + unsigned error = 0; + + while (!BFINAL) + { + unsigned BTYPE; + if ((bp >> 3) >= insize) + return 52; /*error, bit pointer will jump past memory*/ + BFINAL = readBitFromStream(&bp, &in[inpos]); + BTYPE = 1 * readBitFromStream(&bp, &in[inpos]); + BTYPE += 2 * readBitFromStream(&bp, &in[inpos]); + + if (BTYPE == 3) + return 20; /*error: invalid BTYPE*/ + else if (BTYPE == 0) + error = inflateNoCompression(&in[inpos], &bp, &pos, insize); /*no compression*/ + else + error = inflateHuffmanBlock(&in[inpos], &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/ + if (error) + return error; + } + + if (pos > MAX_SCANLINES_SIZE) + error = 9916; /*Only now we know the true size of out, resize it to that*/ + else + scanlines_size = pos; + + return error; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Adler32 */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned update_adler32(unsigned adler, const unsigned char* data, + unsigned len) +{ + unsigned s1 = adler & 0xffff; + unsigned s2 = (adler >> 16) & 0xffff; + + while (len > 0) + { + /*at least 5550 sums can be done before the sums overflow, saving us from a lot of module divisions*/ + unsigned amount = len > 5550 ? 5550 : len; + len -= amount; + while (amount > 0) + { + s1 = (s1 + *data++); + s2 = (s2 + s1); + amount--; + } + s1 %= 65521; + s2 %= 65521; + } + + return (s2 << 16) | s1; +} + +/*Return the adler32 of the bytes data[0..len-1]*/ +static unsigned adler32(const unsigned char* data, unsigned len) +{ + return update_adler32(1L, data, len); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing single bits and bytes from/to stream for Zlib / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned Zlib_read32bitInt(const unsigned char* buffer) +{ + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Zlib / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned Zlib_decompress(const unsigned char* in, size_t insize) +{ + unsigned error = 0; + unsigned CM, CINFO, FDICT; + + if (insize < 2) + { + error = 53; + return error; + } /*error, size of zlib data too small*/ + /*read information from zlib header*/ + if ((in[0] * 256 + in[1]) % 31 != 0) + { + error = 24; + return error; + } /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ + + CM = in[0] & 15; + CINFO = (in[0] >> 4) & 15; + /*FCHECK = in[1] & 31; //FCHECK is already tested above*/ + FDICT = (in[1] >> 5) & 1; + /*FLEVEL = (in[1] >> 6) & 3; //not really important, all it does it to give a compiler warning about unused variable, we don't care what encoding setting the encoder used*/ + + if (CM != 8 || CINFO > 7) + { + error = 25; + return error; + } /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ + if (FDICT != 0) + { + error = 26; + return error; + } /*error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary."*/ + + error = inflate(in, insize, 2); + + if (error) + return error; + + unsigned ADLER32 = Zlib_read32bitInt(&in[insize - 4]); + unsigned checksum = adler32(scanlines, (unsigned) scanlines_size); + if (checksum != ADLER32) + { + error = 58; + return error; + } + + return error; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of Zlib related code, now comes the PNG related code that uses it// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing single bits and bytes from/to stream for PNG / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned char readBitFromReversedStream(size_t* bitpointer, + const unsigned char* bitstream) +{ + unsigned char result = (unsigned char) ((bitstream[(*bitpointer) >> 3] + >> (7 - ((*bitpointer) & 0x7))) & 1); + (*bitpointer)++; + return result; +} + +static unsigned readBitsFromReversedStream(size_t* bitpointer, + const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0; + size_t i; + for (i = nbits - 1; i < nbits; i--) + result += (unsigned) readBitFromReversedStream(bitpointer, bitstream) + << i; + return result; +} + +static void setBitOfReversedStream0(size_t* bitpointer, + unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream must be 0 for this to work*/ + if (bit) + bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ + (*bitpointer)++; +} + +static void setBitOfReversedStream(size_t* bitpointer, + unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream may be 0 or 1 for this to work*/ + if (bit == 0) + bitstream[(*bitpointer) >> 3] &= (unsigned char) (~(1 << (7 + - ((*bitpointer) & 0x7)))); + else + bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); + (*bitpointer)++; +} + +static unsigned PNG_read32bitInt(const unsigned char* buffer) +{ + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG chunks / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned PNG_chunk_length(const unsigned char* chunk) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/ +{ + return PNG_read32bitInt(chunk); +} + +void PNG_chunk_type(char type[5], const unsigned char* chunk) /*puts the 4-byte type in null terminated string*/ +{ + unsigned i; + for (i = 0; i < 4; i++) + type[i] = chunk[4 + i]; + type[4] = 0; /*null termination char*/ +} + +unsigned char PNG_chunk_type_equals(const unsigned char* chunk, + const char* type) /*check if the type is the given type*/ +{ + if (strlen(type) != 4) + return 0; + return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] + && chunk[7] == type[3]); +} + +/*properties of PNG chunks gotten from capitalization of chunk type name, as defined by the standard*/ +unsigned char PNG_chunk_critical(const unsigned char* chunk) /*0: ancillary chunk, 1: it's one of the critical chunk types*/ +{ + return ((chunk[4] & 32) == 0); +} + +unsigned PNG_chunk_next_const(const unsigned char* chunk, int fd, + int *pos) /*don't use on IEND chunk, as there is no next chunk then*/ +{ + unsigned total_chunk_length = PNG_chunk_length(chunk) + 12; + *pos += total_chunk_length; + return !(lseek(fd, *pos, SEEK_SET) == *pos); + +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Color types and such / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*return type is a PNG error code*/ +static unsigned checkColorValidity(unsigned colorType, unsigned bd) /*bd = bitDepth*/ +{ + switch (colorType) + { + case 0: + if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) + return 37; + break; /*grey*/ + case 2: + if (!(bd == 8 || bd == 16)) + return 37; + break; /*RGB*/ + case 3: + if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8)) + return 37; + break; /*palette*/ + case 4: + if (!(bd == 8 || bd == 16)) + return 37; + break; /*grey + alpha*/ + case 6: + if (!(bd == 8 || bd == 16)) + return 37; + break; /*RGBA*/ + default: + return 31; + } + return 0; /*allowed color type / bits combination*/ +} + +static unsigned getNumColorChannels(unsigned colorType) +{ + switch (colorType) + { + case 0: + return 1; /*grey*/ + case 2: + return 3; /*RGB*/ + case 3: + return 1; /*palette*/ + case 4: + return 2; /*grey + alpha*/ + case 6: + return 4; /*RGBA*/ + } + return 0; /*unexisting color type*/ +} + +static unsigned getBpp(unsigned colorType, unsigned bitDepth) +{ + return getNumColorChannels(colorType) * bitDepth; /*bits per pixel is amount of channels * bits per channel*/ +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +void PNG_InfoColor_init(PNG_InfoColor* info) +{ + info->key_defined = 0; + info->key_r = info->key_g = info->key_b = 0; + info->colorType = 6; + info->bitDepth = 8; + info->palettesize = 0; +} + +void PNG_InfoColor_cleanup(PNG_InfoColor* info) +{ + info->palettesize = 0; +} + +unsigned PNG_InfoColor_getBpp(const PNG_InfoColor* info) +{ + return getBpp(info->colorType, info->bitDepth); +} /*calculate bits per pixel out of colorType and bitDepth*/ +unsigned PNG_InfoColor_getChannels(const PNG_InfoColor* info) +{ + return getNumColorChannels(info->colorType); +} +unsigned PNG_InfoColor_isGreyscaleType(const PNG_InfoColor* info) +{ + return info->colorType == 0 || info->colorType == 4; +} +unsigned PNG_InfoColor_isAlphaType(const PNG_InfoColor* info) +{ + return (info->colorType & 4) != 0; +} + +unsigned PNG_InfoColor_equal(const PNG_InfoColor* info1, + const PNG_InfoColor* info2) +{ + return info1->colorType == info2->colorType && info1->bitDepth + == info2->bitDepth; /*palette and color key not compared*/ +} + +void PNG_InfoPng_init(void) +{ + info_width = info_height = 0; + PNG_InfoColor_init(&info_color); + info_interlaceMethod = 0; + info_compressionMethod = 0; + info_filterMethod = 0; +} + +void PNG_InfoPng_cleanup(void) +{ + PNG_InfoColor_cleanup(&info_color); +} + +unsigned PNG_InfoColor_copy(PNG_InfoColor* dest, const PNG_InfoColor* source) +{ + size_t i; + PNG_InfoColor_cleanup(dest); + *dest = *source; + for (i = 0; i < source->palettesize * 4; i++) + dest->palette[i] = source->palette[i]; + return 0; +} + +void PNG_InfoRaw_init(void) +{ + PNG_InfoColor_init(&raw_color); +} + +void PNG_InfoRaw_cleanup(void) +{ + PNG_InfoColor_cleanup(&raw_color); +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +/* + converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = PNG error code + the out buffer must have (w * h * bpp + 7) / 8 bytes, where bpp is the bits per pixel of the output color type (PNG_InfoColor_getBpp) + for < 8 bpp images, there may _not_ be padding bits at the end of scanlines. + */ +unsigned PNG_convert(unsigned char* out, const unsigned char* in, + PNG_InfoColor* infoOut, PNG_InfoColor* infoIn, unsigned w, unsigned h) +{ + const size_t numpixels = w * h; /*amount of pixels*/ + const unsigned OUT_BYTES = PNG_InfoColor_getBpp(infoOut) / 8; /*bytes per pixel in the output image*/ + const unsigned OUT_ALPHA = PNG_InfoColor_isAlphaType(infoOut); /*use 8-bit alpha channel*/ + size_t i, c, bp = 0; /*bitpointer, used by less-than-8-bit color types*/ + + /*cases where in and out already have the same format*/ + if (PNG_InfoColor_equal(infoIn, infoOut)) + { + size_t i, size = (w * h * PNG_InfoColor_getBpp(infoIn) + 7) / 8; + for (i = 0; i < size; i++) + out[i] = in[i]; + return 0; + } + + if ((infoOut->colorType == 2 || infoOut->colorType == 6) + && infoOut->bitDepth == 8) + { + if (infoIn->bitDepth == 8) + { + switch (infoIn->colorType) + { + case 0: /*greyscale color*/ + for (i = 0; i < numpixels; i++) + { + if (OUT_ALPHA) + out[OUT_BYTES * i + 3] = 255; + out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] + = out[OUT_BYTES * i + 2] = in[i]; + if (OUT_ALPHA && infoIn->key_defined && in[i] + == infoIn->key_r) + out[OUT_BYTES * i + 3] = 0; + } + break; + case 2: /*RGB color*/ + for (i = 0; i < numpixels; i++) + { + if (OUT_ALPHA) + out[OUT_BYTES * i + 3] = 255; + for (c = 0; c < 3; c++) + out[OUT_BYTES * i + c] = in[3 * i + c]; + if (OUT_ALPHA && infoIn->key_defined == 1 && in[3 * i + 0] + == infoIn->key_r && in[3 * i + 1] == infoIn->key_g + && in[3 * i + 2] == infoIn->key_b) + out[OUT_BYTES * i + 3] = 0; + } + break; + case 3: /*indexed color (palette)*/ + for (i = 0; i < numpixels; i++) + { + if (OUT_ALPHA) + out[OUT_BYTES * i + 3] = 255; + if (in[i] >= infoIn->palettesize) + return 46; + for (c = 0; c < OUT_BYTES; c++) + out[OUT_BYTES * i + c] = infoIn->palette[4 * in[i] + c]; /*get rgb colors from the palette*/ + } + break; + case 4: /*greyscale with alpha*/ + for (i = 0; i < numpixels; i++) + { + out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] + = out[OUT_BYTES * i + 2] = in[2 * i + 0]; + if (OUT_ALPHA) + out[OUT_BYTES * i + 3] = in[2 * i + 1]; + } + break; + case 6: /*RGB with alpha*/ + for (i = 0; i < numpixels; i++) + { + for (c = 0; c < OUT_BYTES; c++) + out[OUT_BYTES * i + c] = in[4 * i + c]; + } + break; + default: + break; + } + } + else if (infoIn->bitDepth == 16) + { + switch (infoIn->colorType) + { + case 0: /*greyscale color*/ + for (i = 0; i < numpixels; i++) + { + if (OUT_ALPHA) + out[OUT_BYTES * i + 3] = 255; + out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] + = out[OUT_BYTES * i + 2] = in[2 * i]; + if (OUT_ALPHA && infoIn->key_defined && 256U * in[i] + in[i + + 1] == infoIn->key_r) + out[OUT_BYTES * i + 3] = 0; + } + break; + case 2: /*RGB color*/ + for (i = 0; i < numpixels; i++) + { + if (OUT_ALPHA) + out[OUT_BYTES * i + 3] = 255; + for (c = 0; c < 3; c++) + out[OUT_BYTES * i + c] = in[6 * i + 2 * c]; + if (OUT_ALPHA && infoIn->key_defined && 256U + * in[6 * i + 0] + in[6 * i + 1] == infoIn->key_r + && 256U * in[6 * i + 2] + in[6 * i + 3] + == infoIn->key_g && 256U * in[6 * i + 4] + + in[6 * i + 5] == infoIn->key_b) + out[OUT_BYTES * i + 3] = 0; + } + break; + case 4: /*greyscale with alpha*/ + for (i = 0; i < numpixels; i++) + { + out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] + = out[OUT_BYTES * i + 2] = in[4 * i]; /*most significant byte*/ + if (OUT_ALPHA) + out[OUT_BYTES * i + 3] = in[4 * i + 2]; + } + break; + case 6: /*RGB with alpha*/ + for (i = 0; i < numpixels; i++) + { + for (c = 0; c < OUT_BYTES; c++) + out[OUT_BYTES * i + c] = in[8 * i + 2 * c]; + } + break; + default: + break; + } + } + else /*infoIn->bitDepth is less than 8 bit per channel*/ + { + switch (infoIn->colorType) + { + case 0: /*greyscale color*/ + for (i = 0; i < numpixels; i++) + { + unsigned value = readBitsFromReversedStream(&bp, in, + infoIn->bitDepth); + if (OUT_ALPHA) + out[OUT_BYTES * i + 3] = 255; + if (OUT_ALPHA && infoIn->key_defined && value && ((1U + << infoIn->bitDepth) - 1U) == infoIn->key_r && ((1U + << infoIn->bitDepth) - 1U)) + out[OUT_BYTES * i + 3] = 0; + value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/ + out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] + = out[OUT_BYTES * i + 2] = (unsigned char) (value); + } + break; + case 3: /*indexed color (palette)*/ + for (i = 0; i < numpixels; i++) + { + unsigned value = readBitsFromReversedStream(&bp, in, + infoIn->bitDepth); + if (OUT_ALPHA) + out[OUT_BYTES * i + 3] = 255; + if (value >= infoIn->palettesize) + return 47; + for (c = 0; c < OUT_BYTES; c++) + out[OUT_BYTES * i + c] = infoIn->palette[4 * value + c]; /*get rgb colors from the palette*/ + } + break; + default: + break; + } + } + } + else if (PNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/ + { + if (!PNG_InfoColor_isGreyscaleType(infoIn)) + return 62; + if (infoIn->bitDepth == 8) + { + switch (infoIn->colorType) + { + case 0: /*greyscale color*/ + for (i = 0; i < numpixels; i++) + { + if (OUT_ALPHA) + out[OUT_BYTES * i + 1] = 255; + out[OUT_BYTES * i] = in[i]; + if (OUT_ALPHA && infoIn->key_defined && in[i] + == infoIn->key_r) + out[OUT_BYTES * i + 1] = 0; + } + break; + case 4: /*greyscale with alpha*/ + for (i = 0; i < numpixels; i++) + { + out[OUT_BYTES * i + 0] = in[2 * i + 0]; + if (OUT_ALPHA) + out[OUT_BYTES * i + 1] = in[2 * i + 1]; + } + break; + default: + return 31; + } + } + else if (infoIn->bitDepth == 16) + { + switch (infoIn->colorType) + { + case 0: /*greyscale color*/ + for (i = 0; i < numpixels; i++) + { + if (OUT_ALPHA) + out[OUT_BYTES * i + 1] = 255; + out[OUT_BYTES * i] = in[2 * i]; + if (OUT_ALPHA && infoIn->key_defined && 256U * in[i] + in[i + + 1] == infoIn->key_r) + out[OUT_BYTES * i + 1] = 0; + } + break; + case 4: /*greyscale with alpha*/ + for (i = 0; i < numpixels; i++) + { + out[OUT_BYTES * i] = in[4 * i]; /*most significant byte*/ + if (OUT_ALPHA) + out[OUT_BYTES * i + 1] = in[4 * i + 2]; /*most significant byte*/ + } + break; + default: + return 31; + } + } + else /*infoIn->bitDepth is less than 8 bit per channel*/ + { + if (infoIn->colorType != 0) + return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/ + for (i = 0; i < numpixels; i++) + { + unsigned value = readBitsFromReversedStream(&bp, in, + infoIn->bitDepth); + if (OUT_ALPHA) + out[OUT_BYTES * i + 1] = 255; + if (OUT_ALPHA && infoIn->key_defined && value && ((1U + << infoIn->bitDepth) - 1U) == infoIn->key_r && ((1U + << infoIn->bitDepth) - 1U)) + out[OUT_BYTES * i + 1] = 0; + value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/ + out[OUT_BYTES * i] = (unsigned char) (value); + } + } + } + else + return 59; + + return 0; +} + +/*Paeth predicter, used by PNG filter type 4*/ +static int paethPredictor(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; +} + +/*shared values used by multiple Adam7 related functions*/ + +static const unsigned ADAM7_IX[7] = +{ 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ +static const unsigned ADAM7_IY[7] = +{ 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ +static const unsigned ADAM7_DX[7] = +{ 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ +static const unsigned ADAM7_DY[7] = +{ 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ + +static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], + size_t filter_passstart[8], size_t padded_passstart[8], + size_t passstart[8], unsigned w, unsigned h, unsigned bpp) +{ + /*the passstart values have 8 values: the 8th one actually indicates the byte after the end of the 7th (= last) pass*/ + unsigned i; + + /*calculate width and height in pixels of each pass*/ + for (i = 0; i < 7; i++) + { + passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; + passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; + if (passw[i] == 0) + passh[i] = 0; + if (passh[i] == 0) + passw[i] = 0; + } + + filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; + for (i = 0; i < 7; i++) + { + filter_passstart[i + 1] = filter_passstart[i] + + ((passw[i] && passh[i]) ? passh[i] * (1 + + (passw[i] * bpp + 7) / 8) : 0); /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ + padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] + * bpp + 7) / 8); /*bits padded if needed to fill full byte at end of each scanline*/ + passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; /*only padded at end of reduced image*/ + } +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Decoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*read the information from the header and store it in the PNG_Info. return value is error*/ +void PNG_inspect(int fd) +{ + unsigned char in[29]; + lseek(fd, 0, SEEK_SET); + + if (read(fd, in, 29) < 29) + { + error = 27; + return; + } /*error: the data length is smaller than the length of the header*/ + + /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ + PNG_InfoPng_cleanup(); + PNG_InfoPng_init(); + error = 0; + + if (in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] + != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) + { + error = 28; + return; + } /*error: the first 8 bytes are not the correct PNG signature*/ + if (in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') + { + error = 29; + return; + } /*error: it doesn't start with a IHDR chunk!*/ + + /*read the values given in the header*/ + info_width = PNG_read32bitInt(&in[16]); + info_height = PNG_read32bitInt(&in[20]); + info_color.bitDepth = in[24]; + info_color.colorType = in[25]; + info_compressionMethod = in[26]; + info_filterMethod = in[27]; + info_interlaceMethod = in[28]; + + if (info_compressionMethod != 0) + { + error = 32; + return; + } /*error: only compression method 0 is allowed in the specification*/ + if (info_filterMethod != 0) + { + error = 33; + return; + } /*error: only filter method 0 is allowed in the specification*/ + if (info_interlaceMethod > 1) + { + error = 34; + return; + } /*error: only interlace methods 0 and 1 exist in the specification*/ + + error = checkColorValidity(info_color.colorType, info_color.bitDepth); +} + +static unsigned unfilterScanline(unsigned char* recon, + const unsigned char* scanline, const unsigned char* precon, + size_t bytewidth, unsigned char filterType, size_t length) +{ + /* + For PNG filter method 0 + unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1) + precon is the previous unfiltered scanline, recon the result, scanline the current one + the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead + recon and scanline MAY be the same memory address! precon must be disjoint. + */ + + size_t 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] + paethPredictor(0, + precon[i], 0)); + for (i = bytewidth; i < length; i++) + recon[i] + = (unsigned char) (scanline[i] + paethPredictor(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] + paethPredictor( + recon[i - bytewidth], 0, 0)); + } + break; + default: + return 36; /*error: unexisting filter type given*/ + } + return 0; +} + +static unsigned unfilter(unsigned char* out, const unsigned char* in, + unsigned w, unsigned h, unsigned bpp) +{ + /* + For PNG filter method 0 + this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 it's called 7 times) + out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline + w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel + in and out are allowed to be the same memory address! + */ + + unsigned y; + unsigned char* prevline = 0; + + size_t bytewidth = (bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t linebytes = (w * bpp + 7) / 8; + + for (y = 0; y < h; y++) + { + size_t outindex = linebytes * y; + size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + unsigned char filterType = in[inindex]; + + unsigned error = unfilterScanline(&out[outindex], &in[inindex + 1], + prevline, bytewidth, filterType, linebytes); + if (error) + return error; + + prevline = &out[outindex]; + } + + return 0; +} + +static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, + unsigned w, unsigned h, unsigned bpp) +{ + /*Note: this function works on image buffers WITHOUT padding bits at end of scanlines with non-multiple-of-8 bit amounts, only between reduced images is padding + out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation (because that's likely a little bit faster)*/ + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, + passstart, w, h, bpp); + + if (bpp >= 8) + { + for (i = 0; i < 7; i++) + { + unsigned x, y, b; + size_t bytewidth = bpp / 8; + for (y = 0; y < passh[i]; y++) + for (x = 0; x < passw[i]; x++) + { + size_t pixelinstart = passstart[i] + (y * passw[i] + x) + * bytewidth; + size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + for (b = 0; b < bytewidth; b++) + { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } + else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ + { + for (i = 0; i < 7; i++) + { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for (y = 0; y < passh[i]; y++) + for (x = 0; x < passw[i]; x++) + { + ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + for (b = 0; b < bpp; b++) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream0(&obp, out, bit); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ + } + } + } + } +} + +static void removePaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) +{ + /* + After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers for the Adam7 code, the color convert code and the output to the user. + in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits + also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 + only useful if (ilinebits - olinebits) is a value in the range 1..7 + */ + unsigned y; + size_t diff = ilinebits - olinebits; + size_t obp = 0, ibp = 0; /*bit pointers*/ + for (y = 0; y < h; y++) + { + size_t x; + for (x = 0; x < olinebits; x++) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + ibp += diff; + } +} + +/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks*/ +static unsigned postProcessScanlines(unsigned char* out, unsigned char* in) /*return value is error*/ +{ + /* + This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps: + *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) + *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace + NOTE: the in buffer will be overwritten with intermediate data! + */ + unsigned bpp = PNG_InfoColor_getBpp(&info_color); + unsigned w = info_width; + unsigned h = info_height; + unsigned error = 0; + if (bpp == 0) + return 31; /*error: invalid colortype*/ + + if (info_interlaceMethod == 0) + { + if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) + { + error = unfilter(in, in, w, h, bpp); + if (error) + return error; + removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); + } + else + error = unfilter(out, in, w, h, bpp); /*we can immediatly filter into the out buffer, no other steps needed*/ + } + else /*interlaceMethod is 1 (Adam7)*/ + { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, + passstart, w, h, bpp); + + for (i = 0; i < 7; i++) + { + error = unfilter(&in[padded_passstart[i]], + &in[filter_passstart[i]], passw[i], passh[i], bpp); + if (error) + return error; + if (bpp < 8) /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, move bytes instead of bits or move not at all*/ + { + /*remove padding bits in scanlines; after this there still may be padding bits between the different reduced images: each reduced image still starts nicely at a byte*/ + removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], + passw[i] * bpp, ((passw[i] * bpp + 7) / 8) * 8, + passh[i]); + } + } + + Adam7_deinterlace(out, in, w, h, bpp); + } + + return error; +} + +#define MAX_IDAT_SIZE 300000 +unsigned char idat[MAX_IDAT_SIZE]; +size_t idat_size = 0; + +/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ +static void decodeGeneric(unsigned char* out, size_t* outsize, int fd, + unsigned int maxsize) +{ + unsigned char IEND = 0; + const unsigned char chunk[8]; + unsigned int pos = 0; + size_t i; + + /*for unknown chunk order*/ + unsigned unknown = 0; + unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ + + /*provide some proper output values if error will happen*/ + *outsize = 0; + + PNG_inspect(fd); /*reads header and resets other parameters in decoder->infoPng*/ + if (error) + return; + + idat_size = 0; + + lseek(fd, 33, SEEK_SET); + pos = 33; + + while (!IEND) /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer*/ + { + unsigned chunkLength; + + if (read(fd, &chunk, 8) != 8) + { + error = 30; + break; + } + chunkLength = PNG_chunk_length(chunk); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ + if (chunkLength > 2147483647) + { + error = 63; + break; + } + + /*IDAT chunk, containing compressed image data*/ + if (PNG_chunk_type_equals(chunk, "IDAT")) + { + size_t oldsize = idat_size; + idat_size = oldsize + chunkLength; + if (idat_size > MAX_IDAT_SIZE) + { + error = 9917; + break; + } /* input file too large */ + if (read(fd, &idat[oldsize], chunkLength) != chunkLength) + { + error = 30; + break; + } + critical_pos = 3; + } + /*IEND chunk*/ + else if (PNG_chunk_type_equals(chunk, "IEND")) + { + IEND = 1; + } + /*palette chunk (PLTE)*/ + else if (PNG_chunk_type_equals(chunk, "PLTE")) + { + unsigned char data[3]; + info_color.palettesize = chunkLength / 3; + if (info_color.palettesize > 256) + { + error = 38; + break; + } /*error: palette too big*/ + for (i = 0; i < info_color.palettesize; i++) + { + if (read(fd, data, 3) != 3) + { + error = 30; + break; + } + info_color.palette[4 * i + 0] = data[0]; /*R*/ + info_color.palette[4 * i + 1] = data[1]; /*G*/ + info_color.palette[4 * i + 2] = data[2]; /*B*/ + info_color.palette[4 * i + 3] = 255; /*alpha*/ + } + critical_pos = 2; + } + /*palette transparency chunk (tRNS)*/ + else if (PNG_chunk_type_equals(chunk, "tRNS")) + { + unsigned char data[6]; + if (info_color.colorType == 3) + { + if (chunkLength > info_color.palettesize) + { + error = 39; + break; + } /*error: more alpha values given than there are palette entries*/ + for (i = 0; i < chunkLength; i++) + { + if (read(fd, data, 1) != 1) + { + error = 30; + break; + } + info_color.palette[4 * i + 3] = data[0]; + } + } + else if (info_color.colorType == 0) + { + if (chunkLength != 2) + { + error = 40; + break; + } /*error: this chunk must be 2 bytes for greyscale image*/ + if (read(fd, data, 2) != 2) + { + error = 30; + break; + } + info_color.key_defined = 1; + info_color.key_r = info_color.key_g = info_color.key_b = 256 + * data[0] + data[1]; + } + else if (info_color.colorType == 2) + { + if (chunkLength != 6) + { + error = 41; + break; + } /*error: this chunk must be 6 bytes for RGB image*/ + if (read(fd, data, 6) != 6) + { + error = 30; + break; + } + info_color.key_defined = 1; + info_color.key_r = 256 * data[0] + data[1]; + info_color.key_g = 256 * data[2] + data[3]; + info_color.key_b = 256 * data[4] + data[5]; + } + else + { + error = 42; + break; + } /*error: tRNS chunk not allowed for other color models*/ + } + else /*it's not an implemented chunk type, so ignore it: skip over the data*/ + { + if (PNG_chunk_critical(chunk)) + { + error = 69; + break; + } /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ + unknown = 1; + } + + if (!IEND) + error = PNG_chunk_next_const(chunk, fd, &pos); + } + + if (!error) + { + if (!error) + error = Zlib_decompress(idat, idat_size); /*decompress with the Zlib decompressor*/ + + if (!error) + { + *outsize = (info_height * info_width * PNG_InfoColor_getBpp( + &info_color) + 7) / 8; + if (*outsize > maxsize) + { + DEBUGF("read_png_fd: Bitmap too large for buffer: " + "%ld bytes.\n", *outsize); + *outsize = 0; + error = 9918; + } + if (!error) + error = postProcessScanlines(out, scanlines); + } + } +} + +void PNG_decode(unsigned char* out, size_t* outsize, int fd, unsigned int maxsize) +{ + *outsize = 0; + decodeGeneric(out, outsize, fd, maxsize); + if (error) + return; + if (!color_convert || PNG_InfoColor_equal(&raw_color, &info_color)) + { + /*same color type, no copying or converting of data needed*/ + /*store the infoPng color settings on the infoRaw so that the infoRaw still reflects what colorType + the raw image has to the end user*/ + if (!color_convert) + { + error = PNG_InfoColor_copy(&raw_color, &info_color); + if (error) + return; + } + } + else + { + /*color conversion needed; sort of copy of the data*/ + unsigned char* data = out; + + /*TODO: check if this works according to the statement in the documentation: "The converter can convert from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/ + if (!(raw_color.colorType == 2 || raw_color.colorType == 6) + && !(raw_color.bitDepth == 8)) + { + error = 56; + return; + } + + *outsize = (info_width * info_height + * PNG_InfoColor_getBpp(&info_color) + 7) / 8; + + if (*outsize > maxsize) + { + DEBUGF("read_png_fd: Bitmap too large for buffer: " + "%ld bytes.\n", *outsize); + *outsize = 0; + error = 9918; + } + else + { + error = PNG_convert(out, data, &raw_color, &info_color, info_width, + info_height); + } + } +} + +void PNG_Decoder_init(void) +{ + PNG_InfoRaw_init(); + PNG_InfoPng_init(); + error = 1; +} + +void PNG_Decoder_cleanup(void) +{ + PNG_InfoRaw_cleanup(); + PNG_InfoPng_cleanup(); +} + +/****************************************************************************** + * read_png_file() + * + * Reads a PNG file and puts the data in rockbox format in *bitmap. + * + *****************************************************************************/ +int read_png_file(const char* filename, struct bitmap *bm, int maxsize, + int format) +{ + int fd, ret; + fd = open(filename, O_RDONLY); + + /* Exit if file opening failed */ + if (fd < 0) + { + DEBUGF("read_png_file: can't open '%s', rc: %d\n", filename, fd); + return fd * 10 - 1; + } + + ret = read_png_fd(fd, bm, maxsize, format); + close(fd); + return ret; +} + +/****************************************************************************** + * read_png_fd() + * + * Reads a PNG file in an open file descriptor and puts the data in rockbox + * format in *bitmap. + * + *****************************************************************************/ +int read_png_fd(int fd, struct bitmap *bm, int maxsize, int format) +{ + size_t dummy_size; + (void)format; + PNG_Decoder_init(); + PNG_decode(bm->data, &dummy_size, fd, (unsigned int)maxsize); + bm->width = info_width; + bm->height = info_height; + PNG_Decoder_cleanup(); + + if (error) + { + DEBUGF("read_png_fd: unexpected error while parsing the file: " + "%d.\n", error); + } + + return -error; + +} Property changes on: apps/recorder/png.c ___________________________________________________________________ Name: svn:executable + * Index: apps/recorder/png.h =================================================================== --- apps/recorder/png.h (revision 0) +++ apps/recorder/png.h (revision 0) @@ -0,0 +1,67 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Christophe Gouiran + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* + Based on LodePNG version 20080927 + + Copyright (c) 2005-2008 Lode Vandevenne + + 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. + */ + +#ifndef _PNG_H_ +#define _PNG_H_ + +#include "config.h" +#include "lcd.h" + +/********************************************************************* + * read_png_file() + * + * Reads a PNG file and puts the data in a 1-pixel-per-byte + * array. + * Returns < 0 for error, or number of bytes used from the bitmap buffer + * + **********************************************/ +int read_png_file(const char* filename, struct bitmap *bm, int maxsize, + int format); + +int read_png_fd(int fd, struct bitmap *bm, int maxsize, int format); + +#endif + Property changes on: apps/recorder/png.h ___________________________________________________________________ Name: svn:executable + * Index: apps/SOURCES =================================================================== --- apps/SOURCES (revision 18818) +++ apps/SOURCES (working copy) @@ -93,6 +93,7 @@ recorder/icons.c recorder/keyboard.c recorder/peakmeter.c +recorder/png.c #ifdef HAVE_ALBUMART recorder/albumart.c #endif