[PATCH 12/12] png: add picoPNG lib support

Jean-Christophe PLAGNIOL-VILLARD plagnioj at jcrosoft.com
Thu Sep 6 02:05:00 EDT 2012


This is an alternative to LodePNG take from http://forge.voodooprojects.org/
which is base on picoPNG C++ wrote by Lode Vandevenne. The same author as
LodePNG.

PicoPNG only support RGBA PNG8

The source code of picopng.c was just adapat to be compliant to C89 and drop
the interanal ZLIB support. Coding style untouched.

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj at jcrosoft.com>
---
 lib/Kconfig    |   19 ++
 lib/Makefile   |    3 +-
 lib/picopng.c  |  810 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/picopng.h  |   34 +++
 lib/png_pico.c |  152 +++++++++++
 5 files changed, 1017 insertions(+), 1 deletion(-)
 create mode 100644 lib/picopng.c
 create mode 100644 lib/picopng.h
 create mode 100644 lib/png_pico.c

diff --git a/lib/Kconfig b/lib/Kconfig
index 0013b5a..8f05fa4 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -51,6 +51,25 @@ config PNG
 	bool "png"
 	select ZLIB
 
+if PNG
+
+choice
+	prompt "PNG Lib"
+
+config LODEPNG
+	bool "lodePNG"
+	help
+	  Mostly PNG support
+
+config PICOPNG
+	bool "picoPNG"
+	help
+	  Support only RGBA PNG8
+
+endchoice
+
+endif
+
 endif
 
 endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 4944319..733bd8c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -37,4 +37,5 @@ obj-$(CONFIG_QSORT)	+= qsort.o
 obj-$(CONFIG_BMP)	+= bmp.o
 obj-$(CONFIG_IMAGE_RENDERER)	+= image_renderer.o graphic_utils.o
 obj-$(CONFIG_IMAGE_RENDERER)	+= image_renderer.o
-obj-$(CONFIG_PNG)	+= png.o lodepng.o
+obj-$(CONFIG_LODEPNG)	+= png_lode.o lodepng.o
+obj-$(CONFIG_PICOPNG)	+= png_pico.o picopng.o
diff --git a/lib/picopng.c b/lib/picopng.c
new file mode 100644
index 0000000..77cd81c
--- /dev/null
+++ b/lib/picopng.c
@@ -0,0 +1,810 @@
+// picoPNG version 20080503 (cleaned up and ported to c by kaitek)
+// 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 <common.h>
+#include <malloc.h>
+#include "picopng.h"
+
+/*************************************************************************************************/
+
+typedef struct png_alloc_node {
+	struct png_alloc_node *prev, *next;
+	void *addr;
+	size_t 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)
+{
+	png_alloc_node_t *node;
+	for (node = png_alloc_head; node; node = node->next)
+		if (node->addr == addr)
+			break;
+	return node;
+}
+
+void png_alloc_add_node(void *addr, size_t size)
+{
+	png_alloc_node_t *node;
+	if (png_alloc_find_node(addr))
+		return;
+	node = malloc(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;
+	free(node);
+}
+
+void *png_alloc_malloc(size_t size)
+{
+	void *addr = malloc(size);
+	png_alloc_add_node(addr, size);
+	return addr;
+}
+
+void *png_alloc_realloc(void *addr, size_t size)
+{
+	void *new_addr;
+	if (!addr)
+		return png_alloc_malloc(size);
+	new_addr = realloc(addr, size);
+	if (new_addr != addr) {
+		png_alloc_node_t *old_node;
+		old_node = png_alloc_find_node(addr);
+		png_alloc_remove_node(old_node);
+		png_alloc_add_node(new_addr, size);
+	}
+	return new_addr;
+}
+
+void png_alloc_free(void *addr)
+{
+	png_alloc_node_t *node = png_alloc_find_node(addr);
+	if (!node)
+		return;
+	png_alloc_remove_node(node);
+	free(addr);
+}
+
+void png_alloc_free_all()
+{
+	while (png_alloc_tail) {
+		void *addr = png_alloc_tail->addr;
+		png_alloc_remove_node(png_alloc_tail);
+		free(addr);
+	}
+}
+
+/*************************************************************************************************/
+
+__maybe_unused void vector32_cleanup(vector32_t *p)
+{
+	p->size = p->allocsize = 0;
+	if (p->data)
+		png_alloc_free(p->data);
+	p->data = NULL;
+}
+
+uint32_t vector32_resize(vector32_t *p, size_t size)
+{	// returns 1 if success, 0 if failure ==> nothing done
+	if (size * sizeof (uint32_t) > p->allocsize) {
+		size_t newsize = size * sizeof (uint32_t) * 2;
+		void *data = png_alloc_realloc(p->data, newsize);
+		if (data) {
+			p->allocsize = newsize;
+			p->data = (uint32_t *) data;
+			p->size = size;
+		} else
+			return 0;
+	} else
+		p->size = size;
+	return 1;
+}
+
+uint32_t vector32_resizev(vector32_t *p, size_t size, uint32_t value)
+{	// resize and give all new elements the value
+	size_t 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(vector32_t *p)
+{
+	p->data = NULL;
+	p->size = p->allocsize = 0;
+}
+
+vector32_t *vector32_new(size_t size, uint32_t value)
+{
+	vector32_t *p = png_alloc_malloc(sizeof (vector32_t));
+	vector32_init(p);
+	if (size && !vector32_resizev(p, size, value))
+		return NULL;
+	return p;
+}
+
+/*************************************************************************************************/
+
+__maybe_unused void vector8_cleanup(vector8_t *p)
+{
+	p->size = p->allocsize = 0;
+	if (p->data)
+		png_alloc_free(p->data);
+	p->data = NULL;
+}
+
+uint32_t vector8_resize(vector8_t *p, size_t size)
+{	// returns 1 if success, 0 if failure ==> nothing done
+	// xxx: the use of sizeof uint32_t 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!
+	if (size * sizeof (uint32_t) > p->allocsize) {
+		size_t newsize = size * sizeof (uint32_t) * 2;
+		void *data = png_alloc_realloc(p->data, newsize);
+		if (data) {
+			p->allocsize = newsize;
+			p->data = (uint8_t *) data;
+			p->size = size;
+		} else
+			return 0; // error: not enough memory
+	} else
+		p->size = size;
+	return 1;
+}
+
+uint32_t vector8_resizev(vector8_t *p, size_t size, uint8_t value)
+{	// resize and give all new elements the value
+	size_t 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(vector8_t *p)
+{
+	p->data = NULL;
+	p->size = p->allocsize = 0;
+}
+
+vector8_t *vector8_new(size_t size, uint8_t value)
+{
+	vector8_t *p = png_alloc_malloc(sizeof (vector8_t));
+	vector8_init(p);
+	if (size && !vector8_resizev(p, size, value))
+		return NULL;
+	return p;
+}
+
+vector8_t *vector8_copy(vector8_t *p)
+{
+	vector8_t *q = vector8_new(p->size, 0);
+	uint32_t n;
+	for (n = 0; n < q->size; n++)
+		q->data[n] = p->data[n];
+	return q;
+}
+
+/*************************************************************************************************/
+int Zlib_decompress(vector8_t *out, const vector8_t *in) // returns error value
+{
+	return picopng_zlib_decompress(out->data, out->size, in->data, in->size);
+}
+
+/*************************************************************************************************/
+
+#define PNG_SIGNATURE	0x0a1a0a0d474e5089ull
+
+#define CHUNK_IHDR		0x52444849
+#define CHUNK_IDAT		0x54414449
+#define CHUNK_IEND		0x444e4549
+#define CHUNK_PLTE		0x45544c50
+#define CHUNK_tRNS		0x534e5274
+
+int PNG_error;
+
+uint32_t PNG_readBitFromReversedStream(size_t *bitp, const uint8_t *bits)
+{
+	uint32_t result = (bits[*bitp >> 3] >> (7 - (*bitp & 0x7))) & 1;
+	(*bitp)++;
+	return result;
+}
+
+uint32_t PNG_readBitsFromReversedStream(size_t *bitp, const uint8_t *bits, uint32_t nbits)
+{
+	uint32_t i, result = 0;
+	for (i = nbits - 1; i < nbits; i--)
+		result += ((PNG_readBitFromReversedStream(bitp, bits)) << i);
+	return result;
+}
+
+void PNG_setBitOfReversedStream(size_t *bitp, uint8_t *bits, uint32_t bit)
+{
+	bits[*bitp >> 3] |= (bit << (7 - (*bitp & 0x7)));
+	(*bitp)++;
+}
+
+uint32_t PNG_read32bitInt(const uint8_t *buffer)
+{
+	return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+}
+
+int PNG_checkColorValidity(uint32_t colorType, uint32_t 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_t PNG_getBpp(const PNG_info_t *info)
+{
+	uint32_t 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_t *info, const uint8_t *in, size_t 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_t *) in != PNG_SIGNATURE) {
+		PNG_error = 28; // no PNG signature
+		return;
+	}
+	if (*(uint32_t *) &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
+		return;
+	}
+	info->filterMethod = in[27];
+	if (in[27] != 0) {
+		PNG_error = 33; // error: only filter method 0 is allowed in the specification
+		return;
+	}
+	info->interlaceMethod = in[28];
+	if (in[28] > 1) {
+		PNG_error = 34; // error: only interlace methods 0 and 1 exist in the specification
+		return;
+	}
+	PNG_error = PNG_checkColorValidity(info->colorType, info->bitDepth);
+}
+
+int PNG_paethPredictor(int a, int b, int c) // Paeth predicter, used by PNG filter type 4
+{
+	int 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_t *recon, const uint8_t *scanline, const uint8_t *precon,
+		size_t bytewidth, uint32_t filterType, size_t length)
+{
+	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] = (uint8_t) (scanline[i] + PNG_paethPredictor(0, precon[i], 0));
+			for (i = bytewidth; i < length; i++)
+				recon[i] = (uint8_t) (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_t) (scanline[i] + PNG_paethPredictor(recon[i - bytewidth], 0, 0));
+		}
+		break;
+	default:
+		PNG_error = 36; // error: nonexistent filter type given
+		return;
+	}
+}
+
+void PNG_adam7Pass(uint8_t *out, uint8_t *linen, uint8_t *lineo, const uint8_t *in, uint32_t w,
+		size_t passleft, size_t passtop, size_t spacex, size_t spacey, size_t passw, size_t passh,
+		uint32_t bpp)
+{
+	size_t bytewidth, linelength;
+	uint32_t y;
+	uint8_t *temp;
+	// 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.
+	if (passw == 0)
+		return;
+	bytewidth = (bpp + 7) / 8;
+	linelength = 1 + ((bpp * passw + 7) / 8);
+	for (y = 0; y < passh; y++) {
+		size_t i, b;
+		uint8_t filterType = in[y * linelength], *prevline = (y == 0) ? 0 : lineo;
+		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++) {
+				size_t 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"
+	}
+}
+
+int PNG_convert(const PNG_info_t *info, vector8_t *out, const uint8_t *in)
+{	// converts from any color type to 32-bit. return value = LodePNG error code
+	size_t i, c;
+	uint32_t bitDepth, colorType;
+	size_t numpixels, bp;
+	uint8_t *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_t 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_t) 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_t 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_t *PNG_info_new(void)
+{
+	PNG_info_t *info = png_alloc_malloc(sizeof (PNG_info_t));
+	uint32_t i;
+	for (i = 0; i < sizeof (PNG_info_t); i++)
+		((uint8_t *) info)[i] = 0;
+	info->palette = vector8_new(0, 0);
+	info->image = vector8_new(0, 0);
+	return info;
+}
+
+PNG_info_t *PNG_decode(const uint8_t *in, uint32_t size)
+{
+	PNG_info_t *info;
+	size_t pos;
+	vector8_t *idat;
+	bool IEND, known_type;
+	uint32_t bpp;
+	vector8_t *scanlines; // now the out buffer will be filled
+	size_t bytewidth, outlength;
+	uint8_t *out_data;
+
+	PNG_error = 0;
+
+	if (size == 0 || in == 0) {
+		PNG_error = 48; // the given data is empty
+		return NULL;
+	}
+	info = PNG_info_new();
+	PNG_readPngHeader(info, in, size);
+	if (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) {
+		size_t i, j;
+		size_t chunkLength;
+		uint32_t chunkType;
+
+		if (pos + 8 >= size) {
+			PNG_error = 30; // error: size of the in buffer too small to contain next chunk
+			return NULL;
+		}
+		chunkLength = PNG_read32bitInt(&in[pos]);
+		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_t *) &in[pos];
+		if (chunkType == CHUNK_IDAT) { // IDAT: compressed image data chunk
+			size_t offset = 0;
+			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);
+		} else if (chunkType == CHUNK_IEND) { // IEND
+			pos += 4;
+			IEND = true;
+		} else if (chunkType == 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
+			}
+		} else if (chunkType == CHUNK_tRNS) { // tRNS: palette transparency chunk
+			pos += 4; // go after the 4 letters
+			if (info->colorType == 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++];
+			} else if (info->colorType == 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;
+			} else if (info->colorType == 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;
+			} else {
+				PNG_error = 42; // error: tRNS chunk not allowed for other color models
+				return NULL;
+			}
+		} else { // it's not an implemented chunk type, so ignore it: skip over the data
+			if (!(in[pos + 0] & 32)) {
+				// 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;
+		}
+		pos += 4; // step over CRC (which is ignored)
+	}
+	bpp = PNG_getBpp(info);
+	scanlines = vector8_new(((info->width * (info->height * bpp + 7)) / 8) + info->height, 0);
+	PNG_error = Zlib_decompress(scanlines, idat);
+	if (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
+		size_t y, obp, bp;
+		size_t 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_t filterType = scanlines->data[linestart];
+				const uint8_t *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)
+					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
+			vector8_t *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_t filterType = scanlines->data[linestart];
+				const uint8_t *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)
+					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)
+		int i;
+		vector8_t *scanlineo, *scanlinen; // "old" and "new" scanline
+		size_t 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
+		};
+		size_t 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
+		};
+		size_t passstart[7] = { 0 };
+		size_t 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
+		vector8_t *copy = vector8_copy(info->image); // xxx: is this copy necessary?
+		PNG_error = PNG_convert(info, info->image, copy->data);
+	}
+	return info;
+}
+
+/*************************************************************************************************/
+
+#ifdef TEST
+
+#include <stdio.h>
+#include <sys/stat.h>
+
+int main(int argc, char **argv)
+{
+	char *fname = (argc > 1) ? argv[1] : "test.png";
+	PNG_info_t *info;
+	struct stat statbuf;
+	uint32_t insize, outsize;
+	FILE *infp, *outfp;
+	uint8_t *inbuf;
+	uint32_t n;
+
+	if (stat(fname, &statbuf) != 0) {
+		perror("stat");
+		return 1;
+	} else if (!statbuf.st_size) {
+		printf("file empty\n");
+		return 1;
+	}
+	insize = (uint32_t) statbuf.st_size;
+	inbuf = malloc(insize);
+	infp = fopen(fname, "rb");
+	if (!infp) {
+		perror("fopen");
+		return 1;
+	} else if (fread(inbuf, 1, insize, infp) != insize) {
+		perror("fread");
+		return 1;
+	}
+	fclose(infp);
+
+	printf("input file: %s (size: %d)\n", fname, insize);
+
+	info = PNG_decode(inbuf, insize);
+	free(inbuf);
+	printf("PNG_error: %d\n", PNG_error);
+	if (PNG_error != 0)
+		return 1;
+
+	printf("width: %d, height: %d\nfirst 16 bytes: ", info->width, info->height);
+	for (n = 0; n < 16; n++)
+		printf("%02x ", info->image->data[n]);
+	printf("\n");
+
+	outsize = info->width * info->height * 4;
+	printf("image size: %d\n", outsize);
+	if (outsize != info->image->size) {
+		printf("error: image size doesn't match dimensions\n");
+		return 1;
+	}
+	outfp = fopen("out.bin", "wb");
+	if (!outfp) {
+		perror("fopen");
+		return 1;
+	} else if (fwrite(info->image->data, 1, outsize, outfp) != outsize) {
+		perror("fwrite");
+		return 1;
+	}
+	fclose(outfp);
+
+#ifdef ALLOC_DEBUG
+	png_alloc_node_t *node;
+	for (node = png_alloc_head, n = 1; node; node = node->next, n++)
+		printf("node %d (%p) addr = %p, size = %ld\n", n, node, node->addr, node->size);
+#endif
+	png_alloc_free_all(); // also frees info and image data from PNG_decode
+
+	return 0;
+}
+
+#endif
diff --git a/lib/picopng.h b/lib/picopng.h
new file mode 100644
index 0000000..14c5e28
--- /dev/null
+++ b/lib/picopng.h
@@ -0,0 +1,34 @@
+#ifndef _PICOPNG_H
+#define _PICOPNG_H
+
+typedef struct {
+	uint32_t *data;
+	size_t size;
+	size_t allocsize;
+} vector32_t;
+
+typedef struct {
+	uint8_t *data;
+	size_t size;
+	size_t allocsize;
+} vector8_t;
+
+typedef struct {
+	uint32_t width, height;
+	uint32_t colorType, bitDepth;
+	uint32_t compressionMethod, filterMethod, interlaceMethod;
+	uint32_t key_r, key_g, key_b;
+	bool key_defined; // is a transparent color key given?
+	vector8_t *palette;
+	vector8_t *image;
+} PNG_info_t;
+
+PNG_info_t *PNG_decode(const uint8_t *in, uint32_t size);
+void png_alloc_free_all(void);
+
+unsigned picopng_zlib_decompress(unsigned char* out, size_t outsize,
+				 const unsigned char* in, size_t insize);
+
+extern int PNG_error;
+
+#endif
diff --git a/lib/png_pico.c b/lib/png_pico.c
new file mode 100644
index 0000000..688906a
--- /dev/null
+++ b/lib/png_pico.c
@@ -0,0 +1,152 @@
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <fb.h>
+#include <asm/byteorder.h>
+#include <init.h>
+#include <image_renderer.h>
+#include <graphic_utils.h>
+#include <linux/zlib.h>
+
+#include "picopng.h"
+
+static z_stream stream;
+static int initialized;
+
+unsigned picopng_zlib_decompress(unsigned char* out, size_t outsize,
+				 const unsigned char* in, size_t insize)
+{
+	int err;
+
+	stream.next_in = in;
+	stream.avail_in = insize;
+
+	stream.next_out = out;
+	stream.avail_out = outsize;
+
+	err = zlib_inflateReset(&stream);
+	if (err != Z_OK) {
+		printk("zlib_inflateReset error %d\n", err);
+		zlib_inflateEnd(&stream);
+		zlib_inflateInit(&stream);
+	}
+
+	err = zlib_inflate(&stream, Z_FINISH);
+	if (err != Z_STREAM_END)
+		goto err;
+	return 0;
+
+err:
+	printk("Error %d while decompressing!\n", err);
+	printk("%p(%d)->%p(%d)\n", in, insize, out, outsize);
+	return -EIO;
+}
+
+static int png_uncompress_init(void)
+{
+	stream.workspace = malloc(zlib_inflate_workspacesize());
+	if ( !stream.workspace ) {
+		initialized = 0;
+		return -ENOMEM;
+	}
+	stream.next_in = NULL;
+	stream.avail_in = 0;
+	zlib_inflateInit(&stream);
+	return 0;
+}
+
+static void png_uncompress_exit(void)
+{
+	zlib_inflateEnd(&stream);
+	vfree(stream.workspace);
+}
+
+#define DEBUG
+#ifdef DEBUG
+static void png_info_display(PNG_info_t *info)
+{
+	pr_info("width = %d\n", info->width);
+	pr_info("height = %d\n", info->height);
+}
+#else
+static void png_info_display(PNG_info_t *info)
+{
+}
+#endif
+
+static int png_renderer(struct fb_info *info, void* inbuf, int insize, void* fb,
+		    int startx, int starty, int xres, int yres, void* offscreenbuf)
+{
+	int width, height;
+	int ret;
+	unsigned sw, sh;
+	PNG_info_t *png_info;
+	void *buf;
+	int xres, yres;
+
+	xres = info->xres;
+	yres = info->yres;
+
+	ret = png_uncompress_init();
+	if (ret)
+		return ret;
+
+	/* rgba */
+	png_info = PNG_decode(inbuf, insize);
+
+	if(PNG_error) {
+		printf("error %u:\n", PNG_error);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	sw = png_info->width;
+	sh = png_info->height;
+
+	png_info_display(png_info);
+
+	if (startx < 0) {
+		startx = (xres - sw) / 2;
+		if (startx < 0)
+			startx = 0;
+	}
+
+	if (starty < 0) {
+		starty = (yres - sh) / 2;
+		if (starty < 0)
+			starty = 0;
+	}
+
+	width = min((int)sw, xres - startx);
+	height = min((int)sh, yres - starty);
+
+	buf = offscreenbuf ? offscreenbuf : fb;
+
+	rgba_blend(info, png_info->image->data, buf, height, width, startx, starty, true);
+
+	if (offscreenbuf) {
+		int fbsize;
+
+		fbsize = xres * yres * (info->bits_per_pixel >> 3);
+		memcpy(fb, offscreenbuf, fbsize);
+	}
+
+	ret = sh;
+
+err:
+	png_uncompress_exit();
+	png_alloc_free_all();
+
+	return ret;
+}
+
+static struct image_renderer png = {
+	.type = filetype_png,
+	.renderer = png_renderer,
+};
+
+static int png_init(void)
+{
+	return image_renderer_register(&png);
+}
+fs_initcall(png_init);
-- 
1.7.10.4




More information about the barebox mailing list