mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-14 10:05:43 +01:00
80a7bd0b7a
Signed-off-by: SergeySlice <sergey.slice@gmail.com>
1354 lines
42 KiB
C
1354 lines
42 KiB
C
/*
|
|
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
|
|
*
|
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
|
*
|
|
* This file contains Original Code and/or Modifications of Original Code
|
|
* as defined in and that are subject to the Apple Public Source License
|
|
* Version 2.0 (the 'License'). You may not use this file except in
|
|
* compliance with the License. The rights granted to you under the License
|
|
* may not be used to create, or enable the creation or redistribution of,
|
|
* unlawful or unlicensed copies of an Apple operating system, or to
|
|
* circumvent, violate, or enable the circumvention or violation of, any
|
|
* terms of an Apple operating system software license agreement.
|
|
*
|
|
* Please obtain a copy of the License at
|
|
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
|
*
|
|
* The Original Code and all software distributed under the License are
|
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
|
* Please see the License for the specific language governing rights and
|
|
* limitations under the License.
|
|
*
|
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
|
*/
|
|
|
|
#ifndef __GNUC__
|
|
#pragma optimize("g", off)
|
|
#endif
|
|
|
|
#define TEST 0
|
|
#include "picopng.h"
|
|
|
|
#ifndef DEBUG_ALL
|
|
#define DEBUG_PNG 0
|
|
#else
|
|
#define DEBUG_PNG DEBUG_ALL
|
|
#endif
|
|
|
|
#if DEBUG_PNG == 0
|
|
#define DBG(...)
|
|
#else
|
|
#define DBG(...) DebugLog(DEBUG_PNG, __VA_ARGS__)
|
|
#endif
|
|
|
|
|
|
|
|
CONST
|
|
UINT32 pattern[28] = {0, 4, 0, 2, 0, 1, 0,
|
|
0, 0, 4, 0, 2, 0, 1,
|
|
8, 8, 4, 4, 2, 2, 1,
|
|
8, 8, 8, 4, 4, 2, 2 }; // values for the adam7 passes
|
|
|
|
|
|
/*************************************************************************************************/
|
|
|
|
typedef struct png_alloc_node
|
|
{
|
|
struct png_alloc_node *prev, *next;
|
|
void *addr;
|
|
UINT32 size;
|
|
} png_alloc_node_t;
|
|
|
|
png_alloc_node_t *png_alloc_head = NULL;
|
|
png_alloc_node_t *png_alloc_tail = NULL;
|
|
|
|
png_alloc_node_t *png_alloc_find_node(void *addr, UINT32 size)
|
|
{
|
|
png_alloc_node_t *node;
|
|
for (node = png_alloc_head; node; node = node->next)
|
|
if (node->addr == addr){
|
|
if (size && (node->size != size)) {
|
|
// Print(L"Error: Same address %p, size: %x, requested size: %x\n", node->addr, node->size, size);
|
|
}
|
|
break;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
void png_alloc_add_node(void *addr, UINT32 size)
|
|
{
|
|
png_alloc_node_t *node;
|
|
if (png_alloc_find_node(addr, size))
|
|
return;
|
|
node = AllocateZeroPool(sizeof (png_alloc_node_t));
|
|
node->addr = addr;
|
|
node->size = size;
|
|
node->prev = png_alloc_tail;
|
|
node->next = NULL;
|
|
png_alloc_tail = node;
|
|
if (node->prev)
|
|
node->prev->next = node;
|
|
if (!png_alloc_head)
|
|
png_alloc_head = node;
|
|
}
|
|
|
|
void png_alloc_remove_node(png_alloc_node_t *node)
|
|
{
|
|
if (node->prev)
|
|
node->prev->next = node->next;
|
|
if (node->next)
|
|
node->next->prev = node->prev;
|
|
if (node == png_alloc_head)
|
|
png_alloc_head = node->next;
|
|
if (node == png_alloc_tail)
|
|
png_alloc_tail = node->prev;
|
|
node->prev = node->next = node->addr = NULL;
|
|
FreePool(node);
|
|
}
|
|
|
|
void *png_alloc_malloc(UINT32 size)
|
|
{
|
|
void *addr = AllocateZeroPool(size);
|
|
png_alloc_add_node(addr, size);
|
|
return addr;
|
|
}
|
|
|
|
void *png_alloc_realloc(void *addr, UINT32 oldSize, UINT32 newSize)
|
|
{
|
|
if (!addr) {
|
|
return png_alloc_malloc(newSize);
|
|
}
|
|
if ( newSize > oldSize ) {
|
|
png_alloc_node_t *old_node = png_alloc_find_node(addr, oldSize);
|
|
if (old_node) {
|
|
void *new_addr = ReallocatePool(oldSize, newSize, addr);
|
|
old_node->addr = new_addr;
|
|
old_node->size = newSize;
|
|
return new_addr;
|
|
}
|
|
else {
|
|
png_alloc_node_t* node = png_alloc_malloc(newSize);
|
|
CopyMem(node->addr, addr, oldSize); // here, newSize is > oldSize
|
|
return node->addr;
|
|
}
|
|
} else {
|
|
return addr;
|
|
}
|
|
}
|
|
|
|
void png_alloc_free(void *addr)
|
|
{
|
|
png_alloc_node_t *node = png_alloc_find_node(addr, 0);
|
|
if (!node)
|
|
return;
|
|
png_alloc_remove_node(node);
|
|
FreePool(addr);
|
|
}
|
|
|
|
void png_alloc_free_all()
|
|
{
|
|
while (png_alloc_tail) {
|
|
void *addr = png_alloc_tail->addr;
|
|
png_alloc_remove_node(png_alloc_tail);
|
|
FreePool(addr);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
|
|
void vector32_cleanup(VECTOR_32 *p)
|
|
{
|
|
p->size = p->allocsize = 0;
|
|
if (p->data)
|
|
png_alloc_free(p->data);
|
|
p->data = NULL;
|
|
}
|
|
|
|
UINT32 vector32_resize(VECTOR_32 *p, UINT32 size)
|
|
{ // returns 1 if success, 0 if failure ==> nothing done
|
|
void *data;
|
|
if (size * sizeof (UINT32) > p->allocsize)
|
|
{
|
|
UINT32 newsize = size * sizeof (UINT32) * 2;
|
|
data = png_alloc_realloc(p->data, p->allocsize, newsize);
|
|
if (data)
|
|
{
|
|
p->allocsize = newsize;
|
|
p->data = (UINT32 *) data;
|
|
p->size = size;
|
|
} else
|
|
return 0;
|
|
}
|
|
else
|
|
p->size = size;
|
|
return 1;
|
|
}
|
|
|
|
UINT32 vector32_resizev(VECTOR_32 *p, UINT32 size, UINT32 value)
|
|
{ // resize and give all new elements the value
|
|
UINT32 oldsize = p->size, i;
|
|
if (!vector32_resize(p, size))
|
|
return 0;
|
|
for (i = oldsize; i < size; i++)
|
|
p->data[i] = value;
|
|
return 1;
|
|
}
|
|
|
|
void vector32_init(VECTOR_32 *p)
|
|
{
|
|
p->data = NULL;
|
|
p->size = p->allocsize = 0;
|
|
}
|
|
|
|
VECTOR_32 *vector32_new(UINT32 size, UINT32 value)
|
|
{
|
|
VECTOR_32 *p = png_alloc_malloc(sizeof (VECTOR_32));
|
|
vector32_init(p);
|
|
if (size && !vector32_resizev(p, size, value))
|
|
return NULL;
|
|
return p;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
|
|
void vector8_cleanup(VECTOR_8 *p)
|
|
{
|
|
p->size = p->allocsize = 0;
|
|
if (p->data)
|
|
png_alloc_free(p->data);
|
|
p->data = NULL;
|
|
}
|
|
|
|
UINT32 vector8_resize(VECTOR_8 *p, UINT32 size)
|
|
{ // returns 1 if success, 0 if failure ==> nothing done
|
|
// xxx: the use of sizeof UINT32 here seems like a bug (this descends from the lodepng vector
|
|
// compatibility functions which do the same). without this there is corruption in certain cases,
|
|
// so this was probably done to cover up allocation bug(s) in the original picopng code!
|
|
void *data;
|
|
if (size * sizeof (UINT32) > p->allocsize)
|
|
{
|
|
UINT32 newsize = size * sizeof (UINT32) * 2;
|
|
data = png_alloc_realloc(p->data, p->allocsize, newsize);
|
|
if (data)
|
|
{
|
|
p->allocsize = newsize;
|
|
p->data = (UINT8 *) data;
|
|
p->size = size;
|
|
} else
|
|
return 0; // error: not enough memory
|
|
} else
|
|
p->size = size;
|
|
return 1;
|
|
}
|
|
|
|
UINT32 vector8_resizev(VECTOR_8 *p, UINT32 size, UINT8 value)
|
|
{ // resize and give all new elements the value
|
|
UINT32 oldsize = p->size, i;
|
|
if (!vector8_resize(p, size))
|
|
return 0;
|
|
for (i = oldsize; i < size; i++)
|
|
p->data[i] = value;
|
|
return 1;
|
|
}
|
|
|
|
void vector8_init(VECTOR_8 *p)
|
|
{
|
|
p->data = NULL;
|
|
p->size = p->allocsize = 0;
|
|
}
|
|
|
|
VECTOR_8 *vector8_new(UINT32 size, UINT8 value)
|
|
{
|
|
VECTOR_8 *p = png_alloc_malloc(sizeof (VECTOR_8));
|
|
vector8_init(p);
|
|
if (size && !vector8_resizev(p, size, value))
|
|
return NULL;
|
|
return p;
|
|
}
|
|
|
|
VECTOR_8 *vector8_copy(VECTOR_8 *p)
|
|
{
|
|
VECTOR_8 *q = vector8_new(p->size, 0);
|
|
UINT32 n;
|
|
for (n = 0; n < q->size; n++)
|
|
q->data[n] = p->data[n];
|
|
return q;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
|
|
const UINT32 LENBASE[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 };
|
|
const UINT32 LENEXTRA[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 };
|
|
const UINT32 DISTBASE[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 };
|
|
const UINT32 DISTEXTRA[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 };
|
|
// code length code lengths
|
|
const UINT32 CLCL[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
|
|
|
/*************************************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
// 2D representation of a huffman tree: The one dimension is "0" or "1", the other contains all
|
|
// nodes and leaves of the tree.
|
|
VECTOR_32 *tree2d;
|
|
} HuffmanTree;
|
|
|
|
HuffmanTree *HuffmanTree_new()
|
|
{
|
|
HuffmanTree *tree = png_alloc_malloc(sizeof (HuffmanTree));
|
|
tree->tree2d = NULL;
|
|
return tree;
|
|
}
|
|
|
|
int HuffmanTree_makeFromLengths(HuffmanTree *tree, const VECTOR_32 *bitlen, UINT32 maxbitlen)
|
|
{ // make tree given the lengths
|
|
VECTOR_32 *tree2d;
|
|
UINT32 bits, n, i;
|
|
UINT32 numcodes = (UINT32) bitlen->size, treepos = 0, nodefilled = 0;
|
|
VECTOR_32 *tree1d, *blcount, *nextcode;
|
|
tree1d = vector32_new(numcodes, 0);
|
|
blcount = vector32_new(maxbitlen + 1, 0);
|
|
nextcode = vector32_new(maxbitlen + 1, 0);
|
|
for (bits = 0; bits < numcodes; bits++)
|
|
blcount->data[bitlen->data[bits]]++; // count number of instances of each code length
|
|
for (bits = 1; bits <= maxbitlen; bits++)
|
|
nextcode->data[bits] = (nextcode->data[bits - 1] + blcount->data[bits - 1]) << 1;
|
|
for (n = 0; n < numcodes; n++)
|
|
if (bitlen->data[n] != 0)
|
|
tree1d->data[n] = nextcode->data[bitlen->data[n]]++; // generate all the codes
|
|
// 0x7fff here means the tree2d isn't filled there yet
|
|
tree2d = vector32_new(numcodes * 2, 0x7fff);
|
|
tree->tree2d = tree2d;
|
|
for (n = 0; n < numcodes; n++) // the codes
|
|
for (i = 0; i < bitlen->data[n]; i++)
|
|
{ // the bits for this code
|
|
UINT32 bit = (tree1d->data[n] >> (bitlen->data[n] - i - 1)) & 1;
|
|
if (treepos > numcodes - 2)
|
|
return 55;
|
|
if (tree2d->data[2 * treepos + bit] == 0x7fff)
|
|
{
|
|
if (i + 1 == bitlen->data[n])
|
|
{
|
|
tree2d->data[2 * treepos + bit] = n;
|
|
treepos = 0;
|
|
}
|
|
else
|
|
{
|
|
tree2d->data[2 * treepos + bit] = ++nodefilled + numcodes;
|
|
treepos = nodefilled;
|
|
}
|
|
} else
|
|
treepos = tree2d->data[2 * treepos + bit] - numcodes;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int HuffmanTree_decode(const HuffmanTree *tree, BOOLEAN *decoded, UINT32 *result, UINT32 *treepos,
|
|
UINT32 bit)
|
|
{ // Decodes a symbol from the tree
|
|
const VECTOR_32 *tree2d = tree->tree2d;
|
|
UINT32 numcodes = (UINT32) tree2d->size / 2;
|
|
if (*treepos >= numcodes)
|
|
return 11;
|
|
*result = tree2d->data[2 * (*treepos) + bit];
|
|
*decoded = (*result < numcodes);
|
|
*treepos = *decoded ? 0 : *result - numcodes;
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
|
|
int Inflator_error;
|
|
|
|
UINT32 Zlib_readBitFromStream(UINT32 *bitp, const UINT8 *bits)
|
|
{
|
|
UINT32 result = (bits[*bitp >> 3] >> (*bitp & 0x7)) & 1;
|
|
(*bitp)++;
|
|
return result;
|
|
}
|
|
|
|
UINT32 Zlib_readBitsFromStream(UINT32 *bitp, const UINT8 *bits, UINT32 nbits)
|
|
{
|
|
UINT32 i, result = 0;
|
|
for (i = 0; i < nbits; i++)
|
|
result += (Zlib_readBitFromStream(bitp, bits)) << i;
|
|
return result;
|
|
}
|
|
|
|
void Inflator_generateFixedTrees(HuffmanTree *tree, HuffmanTree *treeD)
|
|
{ // get the tree of a deflated block with fixed tree
|
|
UINT32 i;
|
|
VECTOR_32 *bitlen, *bitlenD;
|
|
bitlen = vector32_new(288, 8);
|
|
bitlenD = vector32_new(32, 5);
|
|
for (i = 144; i <= 255; i++)
|
|
bitlen->data[i] = 9;
|
|
for (i = 256; i <= 279; i++)
|
|
bitlen->data[i] = 7;
|
|
HuffmanTree_makeFromLengths(tree, bitlen, 15);
|
|
HuffmanTree_makeFromLengths(treeD, bitlenD, 15);
|
|
}
|
|
|
|
UINT32 Inflator_huffmanDecodeSymbol(const UINT8 *in, UINT32 *bp, const HuffmanTree *codetree,
|
|
UINT32 inlength)
|
|
{ // decode a single symbol from given list of bits with given code tree. returns the symbol
|
|
BOOLEAN decoded = FALSE;
|
|
UINT32 ct = 0;
|
|
UINT32 treepos = 0;
|
|
for (;;) {
|
|
if ((*bp & 0x07) == 0 && (*bp >> 3) > inlength) {
|
|
Inflator_error = 10; // error: end reached without endcode
|
|
return 0;
|
|
}
|
|
Inflator_error = HuffmanTree_decode(codetree, &decoded, &ct, &treepos,
|
|
Zlib_readBitFromStream(bp, in));
|
|
if (Inflator_error)
|
|
return 0; // stop, an error happened
|
|
if (decoded)
|
|
return ct;
|
|
}
|
|
}
|
|
|
|
void Inflator_getTreeInflateDynamic(HuffmanTree *tree, HuffmanTree *treeD, const UINT8 *in,
|
|
UINT32 *bp, UINT32 inlength)
|
|
{ // get the tree of a deflated block with dynamic tree, the tree itself is also Huffman
|
|
// compressed with a known tree
|
|
UINT32 i, n;
|
|
UINT32 HLIT;
|
|
UINT32 HDIST;
|
|
UINT32 HCLEN;
|
|
UINT32 replength;
|
|
VECTOR_32 *codelengthcode;
|
|
HuffmanTree *codelengthcodetree = HuffmanTree_new(); // the code tree for code length codes
|
|
VECTOR_32 *bitlen, *bitlenD;
|
|
bitlen = vector32_new(288, 0);
|
|
bitlenD = vector32_new(32, 0);
|
|
if (*bp >> 3 >= inlength - 2)
|
|
{
|
|
Inflator_error = 49; // the bit pointer is or will go past the memory
|
|
png_alloc_free(codelengthcodetree);
|
|
return;
|
|
}
|
|
HLIT = Zlib_readBitsFromStream(bp, in, 5) + 257; // number of literal/length codes + 257
|
|
HDIST = Zlib_readBitsFromStream(bp, in, 5) + 1; // number of dist codes + 1
|
|
HCLEN = Zlib_readBitsFromStream(bp, in, 4) + 4; // number of code length codes + 4
|
|
// lengths of tree to decode the lengths of the dynamic tree
|
|
codelengthcode = vector32_new(19, 0);
|
|
for (i = 0; i < 19; i++)
|
|
codelengthcode->data[CLCL[i]] = (i < HCLEN) ? Zlib_readBitsFromStream(bp, in, 3) : 0;
|
|
|
|
Inflator_error = HuffmanTree_makeFromLengths(codelengthcodetree, codelengthcode, 7);
|
|
if (Inflator_error)
|
|
return;
|
|
|
|
for (i = 0; i < HLIT + HDIST; )
|
|
{
|
|
UINT32 code = Inflator_huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength);
|
|
if (Inflator_error)
|
|
return;
|
|
if (code <= 15)
|
|
{ // a length code
|
|
if (i < HLIT)
|
|
bitlen->data[i++] = code;
|
|
else
|
|
bitlenD->data[i++ - HLIT] = code;
|
|
} else if (code == 16)
|
|
{ // repeat previous
|
|
UINT32 value; // set value to the previous code
|
|
if (*bp >> 3 >= inlength)
|
|
{
|
|
Inflator_error = 50; // error, bit pointer jumps past memory
|
|
return;
|
|
}
|
|
replength = 3 + Zlib_readBitsFromStream(bp, in, 2);
|
|
|
|
if ((i - 1) < HLIT)
|
|
value = bitlen->data[i - 1];
|
|
else
|
|
value = bitlenD->data[i - HLIT - 1];
|
|
for (n = 0; n < replength; n++)
|
|
{ // repeat this value in the next lengths
|
|
if (i >= HLIT + HDIST)
|
|
{
|
|
Inflator_error = 13; // error: i is larger than the amount of codes
|
|
return;
|
|
}
|
|
if (i < HLIT)
|
|
bitlen->data[i++] = value;
|
|
else
|
|
bitlenD->data[i++ - HLIT] = value;
|
|
}
|
|
} else if (code == 17)
|
|
{
|
|
if (*bp >> 3 >= inlength)
|
|
{
|
|
Inflator_error = 50; // error, bit pointer jumps past memory
|
|
return;
|
|
}
|
|
replength = 3 + Zlib_readBitsFromStream(bp, in, 3);
|
|
for (n = 0; n < replength; n++)
|
|
{ // repeat this value in the next lengths
|
|
if (i >= HLIT + HDIST) {
|
|
Inflator_error = 14; // error: i is larger than the amount of codes
|
|
return;
|
|
}
|
|
if (i < HLIT)
|
|
bitlen->data[i++] = 0;
|
|
else
|
|
bitlenD->data[i++ - HLIT] = 0;
|
|
}
|
|
} else if (code == 18)
|
|
{
|
|
if (*bp >> 3 >= inlength)
|
|
{
|
|
Inflator_error = 50;
|
|
return;
|
|
}
|
|
replength = 11 + Zlib_readBitsFromStream(bp, in, 7);
|
|
for (n = 0; n < replength; n++)
|
|
{
|
|
|
|
if (i >= HLIT + HDIST)
|
|
{
|
|
Inflator_error = 15; // error: i is larger than the amount of codes
|
|
return;
|
|
}
|
|
if (i < HLIT)
|
|
bitlen->data[i++] = 0;
|
|
else
|
|
bitlenD->data[i++ - HLIT] = 0;
|
|
}
|
|
} else
|
|
{
|
|
Inflator_error = 16; // error: an nonexitent code appeared. This can never happen.
|
|
return;
|
|
}
|
|
}
|
|
if (bitlen->data[256] == 0)
|
|
{
|
|
Inflator_error = 64; // the length of the end code 256 must be larger than 0
|
|
return;
|
|
}
|
|
// now we've finally got HLIT and HDIST, so generate the code trees, and the function is done
|
|
Inflator_error = HuffmanTree_makeFromLengths(tree, bitlen, 15);
|
|
if (Inflator_error)
|
|
return;
|
|
Inflator_error = HuffmanTree_makeFromLengths(treeD, bitlenD, 15);
|
|
if (Inflator_error)
|
|
return;
|
|
}
|
|
|
|
void Inflator_inflateHuffmanBlock(VECTOR_8 *out, const UINT8 *in, UINT32 *bp, UINT32 *pos,
|
|
UINT32 inlength, UINT32 btype)
|
|
{
|
|
HuffmanTree *codetree, *codetreeD; // the code tree for Huffman codes, dist codes
|
|
codetree = HuffmanTree_new();
|
|
codetreeD = HuffmanTree_new();
|
|
if (btype == 1)
|
|
Inflator_generateFixedTrees(codetree, codetreeD);
|
|
else if (btype == 2)
|
|
{
|
|
Inflator_getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength);
|
|
if (Inflator_error)
|
|
return;
|
|
}
|
|
for (;;)
|
|
{
|
|
UINT32 code = Inflator_huffmanDecodeSymbol(in, bp, codetree, inlength);
|
|
if (Inflator_error)
|
|
return;
|
|
if (code == 256) // end code
|
|
return;
|
|
else if (code <= 255)
|
|
{ // literal symbol
|
|
if (*pos >= out->size)
|
|
vector8_resize(out, (*pos + 1) * 2); // reserve more room
|
|
out->data[(*pos)++] = (UINT8) code;
|
|
}
|
|
else if (code <= 285)
|
|
{ // length code
|
|
UINT32 length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257];
|
|
UINT32 codeD;
|
|
UINT32 dist,numextrabitsD;
|
|
UINT32 start,back;
|
|
UINT32 i;
|
|
if ((*bp >> 3) >= inlength)
|
|
{
|
|
Inflator_error = 51; // error, bit pointer will jump past memory
|
|
return;
|
|
}
|
|
length += Zlib_readBitsFromStream(bp, in, numextrabits);
|
|
codeD = Inflator_huffmanDecodeSymbol(in, bp, codetreeD, inlength);
|
|
if (Inflator_error)
|
|
return;
|
|
if (codeD > 29) {
|
|
Inflator_error = 18; // error: invalid dist code (30-31 are never used)
|
|
return;
|
|
}
|
|
dist = DISTBASE[codeD] ;
|
|
numextrabitsD = DISTEXTRA[codeD];
|
|
if ((*bp >> 3) >= inlength)
|
|
{
|
|
Inflator_error = 51; // error, bit pointer will jump past memory
|
|
return;
|
|
}
|
|
dist += Zlib_readBitsFromStream(bp, in, numextrabitsD);
|
|
start = *pos;
|
|
back = start - dist; // backwards
|
|
if (*pos + length >= out->size)
|
|
vector8_resize(out, (*pos + length) * 2); // reserve more room
|
|
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
out->data[(*pos)++] = out->data[back++];
|
|
if (back >= start)
|
|
back = start - dist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Inflator_inflateNoCompression(VECTOR_8 *out, const UINT8 *in, UINT32 *bp, UINT32 *pos,
|
|
UINT32 inlength)
|
|
{
|
|
UINT32 LEN,NLEN;
|
|
UINT32 p;
|
|
UINT32 n;
|
|
|
|
while ((*bp & 0x7) != 0)
|
|
(*bp)++; // go to first boundary of byte
|
|
p = *bp / 8;
|
|
if (p >= inlength - 4) {
|
|
Inflator_error = 52; // error, bit pointer will jump past memory
|
|
return;
|
|
}
|
|
LEN = in[p] + 256 * in[p + 1];
|
|
NLEN = in[p + 2] + 256 * in[p + 3];
|
|
|
|
p += 4;
|
|
if (LEN + NLEN != 65535) {
|
|
Inflator_error = 21; // error: NLEN is not one's complement of LEN
|
|
return;
|
|
}
|
|
if (*pos + LEN >= out->size)
|
|
vector8_resize(out, *pos + LEN);
|
|
if (p + LEN > inlength) {
|
|
Inflator_error = 23; // error: reading outside of in buffer
|
|
return;
|
|
}
|
|
|
|
for (n = 0; n < LEN; n++)
|
|
out->data[(*pos)++] = in[p++]; // read LEN bytes of literal data
|
|
*bp = p * 8;
|
|
}
|
|
|
|
void Inflator_inflate(VECTOR_8 *out, const VECTOR_8 *in, UINT32 inpos)
|
|
{
|
|
UINT32 bp = 0, pos = 0; // bit pointer and byte pointer
|
|
UINT32 BFINAL = 0;
|
|
UINT32 BTYPE;
|
|
Inflator_error = 0;
|
|
while (!BFINAL && !Inflator_error) {
|
|
if (bp >> 3 >= in->size) {
|
|
Inflator_error = 52; // error, bit pointer will jump past memory
|
|
return;
|
|
}
|
|
BFINAL = Zlib_readBitFromStream(&bp, &in->data[inpos]);
|
|
BTYPE = Zlib_readBitFromStream(&bp, &in->data[inpos]);
|
|
BTYPE += 2 * Zlib_readBitFromStream(&bp, &in->data[inpos]);
|
|
if (BTYPE == 3)
|
|
{
|
|
Inflator_error = 20; // error: invalid BTYPE
|
|
return;
|
|
}
|
|
else if (BTYPE == 0)
|
|
Inflator_inflateNoCompression(out, &in->data[inpos], &bp, &pos, in->size);
|
|
else
|
|
Inflator_inflateHuffmanBlock(out, &in->data[inpos], &bp, &pos, in->size, BTYPE);
|
|
}
|
|
if (!Inflator_error)
|
|
vector8_resize(out, pos); // Only now we know the TRUE size of out, resize it to that
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
|
|
INT32 Zlib_decompress(VECTOR_8 *out, const VECTOR_8 *in) // returns error value
|
|
{
|
|
UINT32 CM,CINFO,FDICT;
|
|
if (in->size < 2) {
|
|
return 53; // error, size of zlib data too small
|
|
}
|
|
if ((in->data[0] * 256 + in->data[1]) % 31 != 0) {
|
|
// error: 256 * in->data[0] + in->data[1] must be a multiple of 31, the FCHECK value is
|
|
// supposed to be made that way
|
|
return 24;
|
|
}
|
|
CM = in->data[0] & 15;
|
|
CINFO = (in->data[0] >> 4) & 15;
|
|
FDICT = (in->data[1] >> 5) & 1;
|
|
// DBG("compression method %d and CINFO=%d FDICT=%d\n", CM, CINFO, FDICT);
|
|
if (CM != 8 || CINFO > 7){
|
|
// DBG("Error 25 : compression method %d and CINFO=%d FDICT=%d\n", CM, CINFO, FDICT);
|
|
// error: only compression method 8: inflate with sliding window of 32k is supported by
|
|
// the PNG spec
|
|
|
|
return 25;
|
|
}
|
|
if (FDICT != 0){
|
|
// DBG("Error 26 : compression method %d and CINFO=%d FDICT=%d\n", CM, CINFO, FDICT);
|
|
// error: the specification of PNG says about the zlib stream: "The additional flags shall
|
|
// not specify a preset dictionary."
|
|
return 26;
|
|
}
|
|
Inflator_inflate(out, in, 2);
|
|
return Inflator_error; // note: adler32 checksum was skipped and ignored
|
|
}
|
|
|
|
/*************************************************************************************************/
|
|
|
|
#define PNG_SIGNATURE 0x0a1a0a0d474e5089ull
|
|
|
|
#define CHUNK_IHDR 0x52444849
|
|
#define CHUNK_IDAT 0x54414449
|
|
#define CHUNK_IEND 0x444e4549
|
|
#define CHUNK_PLTE 0x45544c50
|
|
#define CHUNK_tRNS 0x534e5274
|
|
|
|
INT32 PNG_error;
|
|
|
|
UINT32 PNG_readBitFromReversedStream(UINT32 *bitp, const UINT8 *bits)
|
|
{
|
|
UINT32 result = (bits[*bitp >> 3] >> (7 - (*bitp & 0x7))) & 1;
|
|
(*bitp)++;
|
|
return result;
|
|
}
|
|
|
|
UINT32 PNG_readBitsFromReversedStream(UINT32 *bitp, const UINT8 *bits, UINT32 nbits)
|
|
{
|
|
UINT32 i, result = 0;
|
|
for (i = nbits - 1; i < nbits; i--)
|
|
result += ((PNG_readBitFromReversedStream(bitp, bits)) << i);
|
|
return result;
|
|
}
|
|
|
|
void PNG_setBitOfReversedStream(UINT32 *bitp, UINT8 *bits, UINT32 bit)
|
|
{
|
|
bits[*bitp >> 3] |= (bit << (7 - (*bitp & 0x7)));
|
|
(*bitp)++;
|
|
}
|
|
|
|
UINT32 PNG_read32bitInt(const UINT8 *buffer)
|
|
{
|
|
return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
|
|
}
|
|
|
|
UINT32 PNG_read32bitLE(const UINT8 *buffer)
|
|
{
|
|
return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
|
|
}
|
|
|
|
INT32 PNG_checkColorValidity(UINT32 colorType, UINT32 bd) // return type is a LodePNG error code
|
|
{
|
|
if ((colorType == 2 || colorType == 4 || colorType == 6)) {
|
|
if (!(bd == 8 || bd == 16))
|
|
return 37;
|
|
else
|
|
return 0;
|
|
} else if (colorType == 0) {
|
|
if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16))
|
|
return 37;
|
|
else
|
|
return 0;
|
|
} else if (colorType == 3) {
|
|
if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8))
|
|
return 37;
|
|
else
|
|
return 0;
|
|
} else
|
|
return 31; // nonexistent color type
|
|
}
|
|
|
|
UINT32 PNG_getBpp(const PNG_INFO *info)
|
|
{
|
|
UINT32 bitDepth, colorType;
|
|
bitDepth = info->bitDepth;
|
|
colorType = info->colorType;
|
|
if (colorType == 2)
|
|
return (3 * bitDepth);
|
|
else if (colorType >= 4)
|
|
return (colorType - 2) * bitDepth;
|
|
else
|
|
return bitDepth;
|
|
}
|
|
|
|
void PNG_readPngHeader(PNG_INFO *info, const UINT8 *in, UINT32 inlength)
|
|
{ // read the information from the header and store it in the Info
|
|
if (inlength < 29) {
|
|
PNG_error = 27; // error: the data length is smaller than the length of the header
|
|
return;
|
|
}
|
|
if (*(UINT64 *) in != PNG_SIGNATURE) {
|
|
PNG_error = 28; // no PNG signature
|
|
return;
|
|
}
|
|
if (*(UINT32 *) &in[12] != CHUNK_IHDR) {
|
|
PNG_error = 29; // error: it doesn't start with a IHDR chunk!
|
|
return;
|
|
}
|
|
info->width = PNG_read32bitInt(&in[16]);
|
|
info->height = PNG_read32bitInt(&in[20]);
|
|
info->bitDepth = in[24];
|
|
info->colorType = in[25];
|
|
info->compressionMethod = in[26];
|
|
if (in[26] != 0) {
|
|
PNG_error = 32; // error: only compression method 0 is allowed in the specification
|
|
DBG("error: only compression method 0 is allowed in the specification\n");
|
|
return;
|
|
}
|
|
info->filterMethod = in[27];
|
|
if (in[27] != 0) {
|
|
PNG_error = 33; // error: only filter method 0 is allowed in the specification
|
|
DBG("error: only filter method 0 is allowed in the specification\n");
|
|
return;
|
|
}
|
|
info->interlaceMethod = in[28];
|
|
if (in[28] > 1) {
|
|
PNG_error = 34; // error: only interlace methods 0 and 1 exist in the specification
|
|
DBG("error: only interlace method 0 and 1 exist in the specification\n");
|
|
return;
|
|
}
|
|
PNG_error = PNG_checkColorValidity(info->colorType, info->bitDepth);
|
|
if (PNG_error) {
|
|
DBG("error: checkColorValidity =%d\n", PNG_error);
|
|
}
|
|
}
|
|
|
|
INT32 PNG_paethPredictor(INT32 a, INT32 b, INT32 c) // Paeth predicter, used by PNG filter type 4
|
|
{
|
|
INT32 p, pa, pb, pc;
|
|
p = a + b - c;
|
|
pa = p > a ? (p - a) : (a - p);
|
|
pb = p > b ? (p - b) : (b - p);
|
|
pc = p > c ? (p - c) : (c - p);
|
|
return (pa <= pb && pa <= pc) ? a : (pb <= pc ? b : c);
|
|
}
|
|
|
|
void PNG_unFilterScanline(UINT8 *recon, const UINT8 *scanline, const UINT8 *precon,
|
|
UINT32 bytewidth, UINT32 filterType, UINT32 length)
|
|
{
|
|
UINT32 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] = (UINT8) (scanline[i] + PNG_paethPredictor(0, precon[i], 0));
|
|
for (i = bytewidth; i < length; i++)
|
|
recon[i] = (UINT8) (scanline[i] + PNG_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] = (UINT8) (scanline[i] + PNG_paethPredictor(recon[i - bytewidth], 0, 0));
|
|
}
|
|
break;
|
|
default:
|
|
PNG_error = 36; // error: nonexistent filter type given
|
|
DBG("error: nonexistent filter type given\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PNG_adam7Pass(UINT8 *out, UINT8 *linen, UINT8 *lineo, const UINT8 *in, UINT32 w,
|
|
UINT32 passleft, UINT32 passtop, UINT32 spacex, UINT32 spacey, UINT32 passw, UINT32 passh,
|
|
UINT32 bpp)
|
|
{ // filter and reposition the pixels into the output when the image is Adam7 interlaced. This
|
|
// function can only do it after the full image is already decoded. The out buffer must have
|
|
// the correct allocated memory size already.
|
|
UINT32 bytewidth,linelength,y;
|
|
|
|
if (passw == 0)
|
|
return;
|
|
bytewidth = (bpp + 7) / 8;
|
|
linelength = 1 + ((bpp * passw + 7) / 8);
|
|
for (y = 0; y < passh; y++)
|
|
{
|
|
UINT32 i, b;
|
|
UINT8 filterType = in[y * linelength], *prevline = (y == 0) ? 0 : lineo;
|
|
UINT8 *temp;
|
|
PNG_unFilterScanline(linen, &in[y * linelength + 1], prevline, bytewidth, filterType,
|
|
(w * bpp + 7) / 8);
|
|
if (PNG_error)
|
|
return;
|
|
if (bpp >= 8)
|
|
for (i = 0; i < passw; i++)
|
|
for (b = 0; b < bytewidth; b++) // b = current byte of this pixel
|
|
out[bytewidth * w * (passtop + spacey * y) + bytewidth *
|
|
(passleft + spacex * i) + b] = linen[bytewidth * i + b];
|
|
else
|
|
for (i = 0; i < passw; i++)
|
|
{
|
|
UINT32 obp, bp;
|
|
obp = bpp * w * (passtop + spacey * y) + bpp * (passleft + spacex * i);
|
|
bp = i * bpp;
|
|
for (b = 0; b < bpp; b++)
|
|
PNG_setBitOfReversedStream(&obp, out, PNG_readBitFromReversedStream(&bp, linen));
|
|
}
|
|
temp = linen;
|
|
linen = lineo;
|
|
lineo = temp; // swap the two buffer pointers "line old" and "line new"
|
|
}
|
|
}
|
|
|
|
INT32 PNG_convert(const PNG_INFO *info, VECTOR_8 *out, const UINT8 *in)
|
|
{ // converts from any color type to 32-bit. return value = LodePNG error code
|
|
UINT32 i, c, numpixels,bp;
|
|
UINT32 bitDepth, colorType;
|
|
UINT8 *out_data;
|
|
bitDepth = info->bitDepth;
|
|
colorType = info->colorType;
|
|
numpixels = info->width * info->height;
|
|
bp = 0;
|
|
vector8_resize(out, numpixels * 4);
|
|
out_data = out->size ? out->data : 0;
|
|
if (bitDepth == 8 && colorType == 0) // greyscale
|
|
for (i = 0; i < numpixels; i++)
|
|
{
|
|
out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = in[i];
|
|
out_data[4 * i + 3] = (info->key_defined && (in[i] == info->key_r)) ? 0 : 255;
|
|
}
|
|
else if (bitDepth == 8 && colorType == 2) // RGB color
|
|
for (i = 0; i < numpixels; i++)
|
|
{
|
|
for (c = 0; c < 3; c++)
|
|
out_data[4 * i + c] = in[3 * i + c];
|
|
out_data[4 * i + 3] = (info->key_defined && (in[3 * i + 0] == info->key_r) &&
|
|
(in[3 * i + 1] == info->key_g) && (in[3 * i + 2] == info->key_b)) ? 0 : 255;
|
|
}
|
|
else if (bitDepth == 8 && colorType == 3) // indexed color (palette)
|
|
for (i = 0; i < numpixels; i++)
|
|
{
|
|
if (4U * in[i] >= info->palette->size)
|
|
return 46;
|
|
for (c = 0; c < 4; c++) // get rgb colors from the palette
|
|
out_data[4 * i + c] = info->palette->data[4 * in[i] + c];
|
|
}
|
|
else if (bitDepth == 8 && colorType == 4) // greyscale with alpha
|
|
for (i = 0; i < numpixels; i++) {
|
|
out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = in[2 * i + 0];
|
|
out_data[4 * i + 3] = in[2 * i + 1];
|
|
}
|
|
else if (bitDepth == 8 && colorType == 6)
|
|
for (i = 0; i < numpixels; i++)
|
|
for (c = 0; c < 4; c++)
|
|
out_data[4 * i + c] = in[4 * i + c]; // RGB with alpha
|
|
else if (bitDepth == 16 && colorType == 0) // greyscale
|
|
for (i = 0; i < numpixels; i++) {
|
|
out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = in[2 * i];
|
|
out_data[4 * i + 3] = (info->key_defined && (256U * in[i] + in[i + 1] == info->key_r))
|
|
? 0 : 255;
|
|
}
|
|
else if (bitDepth == 16 && colorType == 2) // RGB color
|
|
for (i = 0; i < numpixels; i++) {
|
|
for (c = 0; c < 3; c++)
|
|
out_data[4 * i + c] = in[6 * i + 2 * c];
|
|
out_data[4 * i + 3] = (info->key_defined &&
|
|
(256U * in[6 * i + 0] + in[6 * i + 1] == info->key_r) &&
|
|
(256U * in[6 * i + 2] + in[6 * i + 3] == info->key_g) &&
|
|
(256U * in[6 * i + 4] + in[6 * i + 5] == info->key_b)) ? 0 : 255;
|
|
}
|
|
else if (bitDepth == 16 && colorType == 4) // greyscale with alpha
|
|
for (i = 0; i < numpixels; i++) {
|
|
out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = in[4 * i]; // msb
|
|
out_data[4 * i + 3] = in[4 * i + 2];
|
|
}
|
|
else if (bitDepth == 16 && colorType == 6)
|
|
for (i = 0; i < numpixels; i++)
|
|
for (c = 0; c < 4; c++)
|
|
out_data[4 * i + c] = in[8 * i + 2 * c]; // RGB with alpha
|
|
else if (bitDepth < 8 && colorType == 0) // greyscale
|
|
for (i = 0; i < numpixels; i++) {
|
|
UINT32 value = (PNG_readBitsFromReversedStream(&bp, in, bitDepth) * 255) /
|
|
((1 << bitDepth) - 1); // scale value from 0 to 255
|
|
out_data[4 * i + 0] = out_data[4 * i + 1] = out_data[4 * i + 2] = (UINT8) value;
|
|
out_data[4 * i + 3] = (info->key_defined && value &&
|
|
(((1U << bitDepth) - 1U) == info->key_r) && ((1U << bitDepth) - 1U)) ? 0 : 255;
|
|
}
|
|
else if (bitDepth < 8 && colorType == 3) // palette
|
|
for (i = 0; i < numpixels; i++)
|
|
{
|
|
UINT32 value = PNG_readBitsFromReversedStream(&bp, in, bitDepth);
|
|
if (4 * value >= info->palette->size)
|
|
return 47;
|
|
for (c = 0; c < 4; c++) // get rgb colors from the palette
|
|
out_data[4 * i + c] = info->palette->data[4 * value + c];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
PNG_INFO *PNG_info_new()
|
|
{
|
|
PNG_INFO *info = png_alloc_malloc(sizeof (PNG_INFO));
|
|
UINT32 i;
|
|
for (i = 0; i < sizeof (PNG_INFO); i++)
|
|
((UINT8 *) info)[i] = 0;
|
|
info->palette = vector8_new(0, 0);
|
|
info->image = vector8_new(0, 0);
|
|
return info;
|
|
}
|
|
|
|
PNG_INFO *PNG_decode(/* const*/ UINT8 *in, UINT32 size)
|
|
{
|
|
UINT32 pos ;
|
|
VECTOR_8 *idat;
|
|
BOOLEAN IEND; //,known_type;
|
|
UINT32 bpp;
|
|
PNG_INFO *info;
|
|
VECTOR_8 *scanlines; // now the out buffer will be filled
|
|
UINT32 bytewidth, outlength ;
|
|
UINT8 *out_data;
|
|
|
|
PNG_error = 0;
|
|
|
|
if (size == 0 || in == 0)
|
|
{
|
|
PNG_error = 48; // the given data is empty
|
|
DBG("the given data is empty\n");
|
|
return NULL;
|
|
}
|
|
info = PNG_info_new();
|
|
PNG_readPngHeader(info, in, size);
|
|
if (PNG_error){
|
|
DBG("readPngHeader PNG_error=%d\n", PNG_error);
|
|
return NULL;
|
|
}
|
|
pos = 33; // first byte of the first chunk after the header
|
|
idat = NULL; // the data from idat chunks
|
|
IEND = FALSE;
|
|
// known_type = TRUE;
|
|
info->key_defined = FALSE;
|
|
// loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is
|
|
// put at the start of the in buffer
|
|
while (!IEND)
|
|
{
|
|
UINT32 i, j;
|
|
UINT32 chunkLength ;
|
|
UINT32 chunkType;
|
|
UINT32 offset = 0;
|
|
if (pos + 8 >= size)
|
|
{
|
|
PNG_error = 30; // error: size of the in buffer too small to contain next chunk
|
|
DBG("error: size of the in buffer too small to contain next chunk size=%d\n", size);
|
|
return NULL;
|
|
}
|
|
chunkLength = PNG_read32bitInt(&in[pos]); //big endian? Yes!
|
|
pos += 4;
|
|
if (chunkLength > 0x7fffffff)
|
|
{
|
|
PNG_error = 63;
|
|
return NULL;
|
|
}
|
|
if (pos + chunkLength >= size) {
|
|
PNG_error = 35; // error: size of the in buffer too small to contain next chunk
|
|
return NULL;
|
|
}
|
|
// chunkType = *(UINT32 *) &in[pos];
|
|
chunkType = PNG_read32bitLE(&in[pos]); //read as LE to compare with our constants
|
|
switch (chunkType) {
|
|
case CHUNK_IDAT:
|
|
if (idat)
|
|
{
|
|
offset = idat->size;
|
|
vector8_resize(idat, offset + chunkLength);
|
|
}
|
|
else
|
|
idat = vector8_new(chunkLength, 0);
|
|
for (i = 0; i < chunkLength; i++)
|
|
idat->data[offset + i] = in[pos + 4 + i];
|
|
pos += (4 + chunkLength);
|
|
break;
|
|
case CHUNK_IEND:
|
|
pos += 4;
|
|
IEND = TRUE;
|
|
break;
|
|
case CHUNK_PLTE:
|
|
// PLTE: palette chunk
|
|
pos += 4; // go after the 4 letters
|
|
vector8_resize(info->palette, 4 * (chunkLength / 3));
|
|
if (info->palette->size > (4 * 256))
|
|
{
|
|
PNG_error = 38; // error: palette too big
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < info->palette->size; i += 4) {
|
|
for (j = 0; j < 3; j++)
|
|
info->palette->data[i + j] = in[pos++]; // RGB
|
|
info->palette->data[i + 3] = 255; // alpha
|
|
}
|
|
break;
|
|
case CHUNK_tRNS:
|
|
// tRNS: palette transparency chunk
|
|
pos += 4; // go after the 4 letters
|
|
switch (info->colorType) {
|
|
case 0:
|
|
if (chunkLength != 2) {
|
|
PNG_error = 40; // error: this chunk must be 2 bytes for greyscale image
|
|
return NULL;
|
|
}
|
|
info->key_defined = TRUE;
|
|
info->key_r = info->key_g = info->key_b = 256 * in[pos] + in[pos + 1];
|
|
pos += 2;
|
|
break;
|
|
case 2:
|
|
if (chunkLength != 6) {
|
|
PNG_error = 41; // error: this chunk must be 6 bytes for RGB image
|
|
return NULL;
|
|
}
|
|
info->key_defined = TRUE;
|
|
info->key_r = 256 * in[pos] + in[pos + 1];
|
|
pos += 2;
|
|
info->key_g = 256 * in[pos] + in[pos + 1];
|
|
pos += 2;
|
|
info->key_b = 256 * in[pos] + in[pos + 1];
|
|
pos += 2;
|
|
break;
|
|
case 3:
|
|
if (4 * chunkLength > info->palette->size) {
|
|
PNG_error = 39; // error: more alpha values given than there are palette entries
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < chunkLength; i++)
|
|
info->palette->data[4 * i + 3] = in[pos++];
|
|
break;
|
|
default:
|
|
PNG_error = 42; // error: tRNS chunk not allowed for other color models
|
|
return NULL;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
// it's not an implemented chunk type, so ignore it: skip over the data
|
|
if (!(in[pos + 0] & 0x20))
|
|
{
|
|
// error: unknown critical chunk (5th bit of first byte of chunk type is 0)
|
|
PNG_error = 69;
|
|
return NULL;
|
|
}
|
|
pos += (chunkLength + 4); // skip 4 letters and uninterpreted data of unimplemented chunk
|
|
// known_type = FALSE;
|
|
break;
|
|
}
|
|
pos += 4; // step over CRC (which is ignored)
|
|
}
|
|
bpp = PNG_getBpp(info);
|
|
// now the out buffer will be filled
|
|
scanlines = vector8_new(((info->width * (info->height * bpp + 7)) / 8) + info->height, 0);
|
|
PNG_error = Zlib_decompress(scanlines, idat);
|
|
if (PNG_error) {
|
|
// DBG("Zlib_decompress return %d", PNG_error);
|
|
return NULL; // stop if the zlib decompressor returned an error
|
|
}
|
|
bytewidth = (bpp + 7) / 8;
|
|
outlength = (info->height * info->width * bpp + 7) / 8;
|
|
vector8_resize(info->image, outlength); // time to fill the out buffer
|
|
out_data = outlength ? info->image->data : 0;
|
|
if (info->interlaceMethod == 0)
|
|
{ // no interlace, just filter
|
|
UINT32 y, obp, bp;
|
|
UINT32 linestart, linelength;
|
|
linestart = 0;
|
|
// length in bytes of a scanline, excluding the filtertype byte
|
|
linelength = (info->width * bpp + 7) / 8;
|
|
if (bpp >= 8) // byte per byte
|
|
for (y = 0; y < info->height; y++)
|
|
{
|
|
UINT32 filterType = scanlines->data[linestart];
|
|
const UINT8 *prevline;
|
|
prevline = (y == 0) ? 0 : &out_data[(y - 1) * info->width * bytewidth];
|
|
PNG_unFilterScanline(&out_data[linestart - y], &scanlines->data[linestart + 1],
|
|
prevline, bytewidth, filterType, linelength);
|
|
if (PNG_error) {
|
|
// DBG("PNG_unFilterScanline 1 return %d", PNG_error);
|
|
return NULL;
|
|
}
|
|
linestart += (1 + linelength); // go to start of next scanline
|
|
} else
|
|
{ // less than 8 bits per pixel, so fill it up bit per bit
|
|
VECTOR_8 *templine; // only used if bpp < 8
|
|
templine = vector8_new((info->width * bpp + 7) >> 3, 0);
|
|
for (y = 0, obp = 0; y < info->height; y++)
|
|
{
|
|
UINT32 filterType = scanlines->data[linestart];
|
|
const UINT8 *prevline;
|
|
prevline = (y == 0) ? 0 : &out_data[(y - 1) * info->width * bytewidth];
|
|
PNG_unFilterScanline(templine->data, &scanlines->data[linestart + 1], prevline,
|
|
bytewidth, filterType, linelength);
|
|
if (PNG_error) {
|
|
// DBG("PNG_unFilterScanline 2 return %d", PNG_error);
|
|
return NULL;
|
|
}
|
|
for (bp = 0; bp < info->width * bpp;)
|
|
PNG_setBitOfReversedStream(&obp, out_data, PNG_readBitFromReversedStream(&bp,
|
|
templine->data));
|
|
linestart += (1 + linelength); // go to start of next scanline
|
|
}
|
|
}
|
|
} else
|
|
{ // interlaceMethod is 1 (Adam7)
|
|
INT32 i;
|
|
VECTOR_8 *scanlineo, *scanlinen; // "old" and "new" scanline
|
|
#ifndef __GNUC__
|
|
#pragma warning(disable: 4204)
|
|
#endif
|
|
UINT32 passw[7] = {
|
|
(info->width + 7) / 8, (info->width + 3) / 8, (info->width + 3) / 4,
|
|
(info->width + 1) / 4, (info->width + 1) / 2, (info->width + 0) / 2,
|
|
(info->width + 0) / 1
|
|
};
|
|
UINT32 passh[7] = {
|
|
(info->height + 7) / 8, (info->height + 7) / 8, (info->height + 3) / 8,
|
|
(info->height + 3) / 4, (info->height + 1) / 4, (info->height + 1) / 2,
|
|
(info->height + 0) / 2
|
|
};
|
|
UINT32 passstart[7]; //= { 0 };
|
|
for (i=0; i<7; i++) {
|
|
passstart[i] = 0;
|
|
}
|
|
// UINT32 pattern[28] = { 0, 4, 0, 2, 0, 1, 0, 0, 0, 4, 0, 2, 0, 1, 8, 8, 4, 4, 2, 2, 1, 8, 8,
|
|
// 8, 4, 4, 2, 2 }; // values for the adam7 passes
|
|
for (i = 0; i < 6; i++)
|
|
passstart[i + 1] = passstart[i] + passh[i] * ((passw[i] ? 1 : 0) + (passw[i] * bpp + 7) / 8);
|
|
scanlineo = vector8_new((info->width * bpp + 7) / 8, 0);
|
|
scanlinen = vector8_new((info->width * bpp + 7) / 8, 0);
|
|
for (i = 0; i < 7; i++)
|
|
PNG_adam7Pass(out_data, scanlinen->data, scanlineo->data, &scanlines->data[passstart[i]],
|
|
info->width, pattern[i], pattern[i + 7], pattern[i + 14], pattern[i + 21],
|
|
passw[i], passh[i], bpp);
|
|
}
|
|
if (info->colorType != 6 || info->bitDepth != 8)
|
|
{ // conversion needed
|
|
VECTOR_8 *copy = vector8_copy(info->image); // xxx: is this copy necessary?
|
|
PNG_error = PNG_convert(info, info->image, copy->data);
|
|
if (PNG_error) {
|
|
// DBG("PNG_convert return %d", PNG_error);
|
|
return NULL;
|
|
}
|
|
}
|
|
return info;
|
|
}
|
|
|
|
/**********************************************************************************************/
|
|
EG_IMAGE * egCreateImage(IN INTN Width, IN INTN Height, IN BOOLEAN HasAlpha)
|
|
{
|
|
EG_IMAGE *NewImage;
|
|
|
|
NewImage = (EG_IMAGE *) AllocatePool(sizeof(EG_IMAGE));
|
|
if (NewImage == NULL)
|
|
return NULL;
|
|
NewImage->PixelData = (EFI_UGA_PIXEL *) AllocatePool((UINTN)(Width * Height * sizeof(EFI_UGA_PIXEL)));
|
|
if (NewImage->PixelData == NULL) {
|
|
FreePool(NewImage);
|
|
return NULL;
|
|
}
|
|
|
|
NewImage->Width = Width;
|
|
NewImage->Height = Height;
|
|
NewImage->HasAlpha = HasAlpha;
|
|
return NewImage;
|
|
}
|
|
|
|
VOID egFreeImage(IN EG_IMAGE *Image)
|
|
{
|
|
if (Image != NULL) {
|
|
if (Image->PixelData != NULL) {
|
|
FreePool(Image->PixelData);
|
|
Image->PixelData = NULL; //FreePool will not zero pointer
|
|
}
|
|
FreePool(Image);
|
|
}
|
|
}
|
|
|
|
|
|
EG_IMAGE * egDecodePNG(IN UINT8 *FileData, IN UINTN FileDataLength, IN BOOLEAN WantAlpha)
|
|
{
|
|
EG_IMAGE *NewImage;
|
|
PNG_INFO *info;
|
|
EFI_UGA_PIXEL *Pixel;
|
|
UINT8 *Data;
|
|
INTN x, y;
|
|
|
|
// read and check header
|
|
info = PNG_decode(FileData, (UINT32)FileDataLength);
|
|
if (info == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
NewImage = egCreateImage((INTN)info->width, (INTN)info->height, WantAlpha);
|
|
if (NewImage == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//CopyMem(NewImage->PixelData, info->image->data, info->image->size);
|
|
Data = (UINT8*)info->image->data;
|
|
Pixel = (EFI_UGA_PIXEL*)NewImage->PixelData;
|
|
for (y = 0; y < NewImage->Height; y++) {
|
|
for (x = 0; x < NewImage->Width; x++) {
|
|
/* UINT8 Temp;
|
|
Temp = Pixel->Blue;
|
|
Pixel->Blue = Pixel->Red;
|
|
Pixel->Red = Temp; */
|
|
// It seems 0 is opaque and 255 is fully transparent
|
|
// Pixel->Reserved = 255 - Pixel->Reserved; */
|
|
Pixel->Red = *Data++;
|
|
Pixel->Green = *Data++;
|
|
Pixel->Blue = *Data++;
|
|
Pixel->Reserved = 255 - *Data++;
|
|
Pixel++;
|
|
// PixelD++;
|
|
}
|
|
}
|
|
png_alloc_free_all();
|
|
return NewImage;
|
|
}
|
|
|
|
|