Property changes on: . ___________________________________________________________________ Name: svn:ignore - build* output + build* output boot-e200 boot-e200v2 .cproject .project patch_png*.txt Property changes on: tools ___________________________________________________________________ Name: svn:ignore - bdf2bmp sh2d scramble generate_rocklatin descramble convbdf bmp2rb codepages rdf2binary mkboot player_unifont uclpack ipod_fw checkwps wavtrim voicefont rbspeexenc rbspeexdec mknkboot mktccboot mkzenboot iaudio_bl_flash.c iaudio_bl_flash.h + bdf2bmp sh2d scramble generate_rocklatin descramble convbdf bmp2rb codepages rdf2binary mkboot player_unifont uclpack ipod_fw checkwps wavtrim voicefont rbspeexenc rbspeexdec mknkboot mktccboot mkzenboot iaudio_bl_flash.c iaudio_bl_flash.h *.dSYM Property changes on: rbutil/rbutilqt ___________________________________________________________________ Name: svn:ignore - build rbutilqt Makefile *.qm release debug *.Debug *.Release tags + build rbutilqt Makefile *.qm release debug *.Debug *.Release tags rbutilqt.app Index: apps/plugins/png/png.make =================================================================== --- apps/plugins/png/png.make (revision 0) +++ apps/plugins/png/png.make (revision 0) @@ -0,0 +1,24 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id: $ +# + +PNGSRCDIR := $(APPSDIR)/plugins/png +PNGBUILDDIR := $(BUILDDIR)/apps/plugins/png + +ROCKS += $(PNGBUILDDIR)/png.rock + +PNG_SRC := $(call preprocess, $(PNGSRCDIR)/SOURCES) +PNG_OBJ := $(call c2obj, $(PNG_SRC)) + +# add source files to OTHER_SRC to get automatic dependencies +OTHER_SRC += $(PNG_SRC) + +# Use -O3 for png plugin : it gives a bigger file but very good performances +CFLAGS += -O3 + +$(PNGBUILDDIR)/png.rock: $(PNG_OBJ) Index: apps/plugins/png/SOURCES =================================================================== --- apps/plugins/png/SOURCES (revision 0) +++ apps/plugins/png/SOURCES (revision 0) @@ -0,0 +1 @@ +png.c Index: apps/plugins/png/png.c =================================================================== --- apps/plugins/png/png.c (revision 0) +++ apps/plugins/png/png.c (revision 0) @@ -0,0 +1,3148 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $id $ + * + * Copyright (C) 2009 by Christophe Gouiran + * + * Based on lodepng, a lightweight png decoder/encoder + * (c) 2005-2008 Lode Vandevenne + * + * 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. + * + ****************************************************************************/ + +/* +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. +*/ + +/* +The manual and changelog can be found in the header file "lodepng.h" +You are free to name this file lodepng.cpp or lodepng.c depending on your usage. +*/ + +#include "plugin.h" +#include "lcd.h" +#include +#include +#include +#include +#include + +PLUGIN_HEADER + +/* variable button definitions */ +#if CONFIG_KEYPAD == RECORDER_PAD +#define PNG_ZOOM_IN BUTTON_PLAY +#define PNG_ZOOM_OUT BUTTON_ON +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_NEXT BUTTON_F3 +#define PNG_PREVIOUS BUTTON_F2 +#define PNG_MENU BUTTON_OFF + +#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD +#define PNG_ZOOM_IN BUTTON_SELECT +#define PNG_ZOOM_OUT BUTTON_ON +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_NEXT BUTTON_F3 +#define PNG_PREVIOUS BUTTON_F2 +#define PNG_MENU BUTTON_OFF + +#elif CONFIG_KEYPAD == ONDIO_PAD +#define PNG_ZOOM_PRE BUTTON_MENU +#define PNG_ZOOM_IN (BUTTON_MENU | BUTTON_REL) +#define PNG_ZOOM_OUT (BUTTON_MENU | BUTTON_DOWN) +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_NEXT (BUTTON_MENU | BUTTON_RIGHT) +#define PNG_PREVIOUS (BUTTON_MENU | BUTTON_LEFT) +#define PNG_MENU BUTTON_OFF + +#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ + (CONFIG_KEYPAD == IRIVER_H300_PAD) +#define PNG_ZOOM_IN BUTTON_SELECT +#define PNG_ZOOM_OUT BUTTON_MODE +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#if (CONFIG_KEYPAD == IRIVER_H100_PAD) +#define PNG_NEXT BUTTON_ON +#define PNG_PREVIOUS BUTTON_REC +#else +#define PNG_NEXT BUTTON_REC +#define PNG_PREVIOUS BUTTON_ON +#endif +#define PNG_MENU BUTTON_OFF +#define PNG_RC_MENU BUTTON_RC_STOP + +#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \ + (CONFIG_KEYPAD == IPOD_1G2G_PAD) +#define PNG_ZOOM_IN BUTTON_SCROLL_FWD +#define PNG_ZOOM_OUT BUTTON_SCROLL_BACK +#define PNG_UP BUTTON_MENU +#define PNG_DOWN BUTTON_PLAY +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU (BUTTON_SELECT | BUTTON_MENU) +#define PNG_NEXT (BUTTON_SELECT | BUTTON_RIGHT) +#define PNG_PREVIOUS (BUTTON_SELECT | BUTTON_LEFT) + +#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD +#define PNG_ZOOM_PRE BUTTON_SELECT +#define PNG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) +#define PNG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_POWER +#define PNG_NEXT BUTTON_PLAY +#define PNG_PREVIOUS BUTTON_REC + +#elif CONFIG_KEYPAD == GIGABEAT_PAD +#define PNG_ZOOM_IN BUTTON_VOL_UP +#define PNG_ZOOM_OUT BUTTON_VOL_DOWN +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_MENU +#define PNG_NEXT (BUTTON_A | BUTTON_RIGHT) +#define PNG_PREVIOUS (BUTTON_A | BUTTON_LEFT) + +#elif CONFIG_KEYPAD == SANSA_E200_PAD +#define PNG_ZOOM_PRE BUTTON_SELECT +#define PNG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) +#define PNG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_POWER +#define PNG_SLIDE_SHOW BUTTON_REC +#define PNG_NEXT BUTTON_SCROLL_FWD +#define PNG_NEXT_REPEAT (BUTTON_SCROLL_FWD|BUTTON_REPEAT) +#define PNG_PREVIOUS BUTTON_SCROLL_BACK +#define PNG_PREVIOUS_REPEAT (BUTTON_SCROLL_BACK|BUTTON_REPEAT) + +#elif CONFIG_KEYPAD == SANSA_FUZE_PAD +#define PNG_ZOOM_PRE BUTTON_SELECT +#define PNG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) +#define PNG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU (BUTTON_HOME|BUTTON_REPEAT) +#define PNG_NEXT BUTTON_SCROLL_FWD +#define PNG_NEXT_REPEAT (BUTTON_SCROLL_FWD|BUTTON_REPEAT) +#define PNG_PREVIOUS BUTTON_SCROLL_BACK +#define PNG_PREVIOUS_REPEAT (BUTTON_SCROLL_BACK|BUTTON_REPEAT) + +#elif CONFIG_KEYPAD == SANSA_C200_PAD +#define PNG_ZOOM_PRE BUTTON_SELECT +#define PNG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) +#define PNG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_POWER +#define PNG_SLIDE_SHOW BUTTON_REC +#define PNG_NEXT BUTTON_VOL_UP +#define PNG_NEXT_REPEAT (BUTTON_VOL_UP|BUTTON_REPEAT) +#define PNG_PREVIOUS BUTTON_VOL_DOWN +#define PNG_PREVIOUS_REPEAT (BUTTON_VOL_DOWN|BUTTON_REPEAT) + +#elif CONFIG_KEYPAD == SANSA_CLIP_PAD +#define PNG_ZOOM_PRE BUTTON_SELECT +#define PNG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) +#define PNG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_POWER +#define PNG_SLIDE_SHOW BUTTON_HOME +#define PNG_NEXT BUTTON_VOL_UP +#define PNG_NEXT_REPEAT (BUTTON_VOL_UP|BUTTON_REPEAT) +#define PNG_PREVIOUS BUTTON_VOL_DOWN +#define PNG_PREVIOUS_REPEAT (BUTTON_VOL_DOWN|BUTTON_REPEAT) + +#elif CONFIG_KEYPAD == SANSA_M200_PAD +#define PNG_ZOOM_PRE BUTTON_SELECT +#define PNG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) +#define PNG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_POWER +#define PNG_SLIDE_SHOW (BUTTON_SELECT | BUTTON_UP) +#define PNG_NEXT BUTTON_VOL_UP +#define PNG_NEXT_REPEAT (BUTTON_VOL_UP|BUTTON_REPEAT) +#define PNG_PREVIOUS BUTTON_VOL_DOWN +#define PNG_PREVIOUS_REPEAT (BUTTON_VOL_DOWN|BUTTON_REPEAT) + +#elif CONFIG_KEYPAD == IRIVER_H10_PAD +#define PNG_ZOOM_PRE BUTTON_PLAY +#define PNG_ZOOM_IN (BUTTON_PLAY | BUTTON_REL) +#define PNG_ZOOM_OUT (BUTTON_PLAY | BUTTON_REPEAT) +#define PNG_UP BUTTON_SCROLL_UP +#define PNG_DOWN BUTTON_SCROLL_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_POWER +#define PNG_NEXT BUTTON_FF +#define PNG_PREVIOUS BUTTON_REW + +#elif CONFIG_KEYPAD == MROBE500_PAD + +#elif CONFIG_KEYPAD == GIGABEAT_S_PAD +#define PNG_ZOOM_IN BUTTON_VOL_UP +#define PNG_ZOOM_OUT BUTTON_VOL_DOWN +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_MENU +#define PNG_NEXT BUTTON_NEXT +#define PNG_PREVIOUS BUTTON_PREV + +#elif CONFIG_KEYPAD == MROBE100_PAD +#define PNG_ZOOM_IN BUTTON_SELECT +#define PNG_ZOOM_OUT BUTTON_PLAY +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_MENU +#define PNG_NEXT (BUTTON_DISPLAY | BUTTON_RIGHT) +#define PNG_PREVIOUS (BUTTON_DISPLAY | BUTTON_LEFT) + +#elif CONFIG_KEYPAD == IAUDIO_M3_PAD +#define PNG_ZOOM_PRE BUTTON_RC_PLAY +#define PNG_ZOOM_IN (BUTTON_RC_PLAY|BUTTON_REL) +#define PNG_ZOOM_OUT (BUTTON_RC_PLAY|BUTTON_REPEAT) +#define PNG_UP BUTTON_RC_VOL_UP +#define PNG_DOWN BUTTON_RC_VOL_DOWN +#define PNG_LEFT BUTTON_RC_REW +#define PNG_RIGHT BUTTON_RC_FF +#define PNG_MENU BUTTON_RC_REC +#define PNG_NEXT BUTTON_RC_MODE +#define PNG_PREVIOUS BUTTON_RC_MENU + +#elif CONFIG_KEYPAD == COWOND2_PAD + +#elif CONFIG_KEYPAD == IAUDIO67_PAD +#define PNG_ZOOM_IN BUTTON_VOLUP +#define PNG_ZOOM_OUT BUTTON_VOLDOWN +#define PNG_UP BUTTON_STOP +#define PNG_DOWN BUTTON_PLAY +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_MENU +#define PNG_NEXT (BUTTON_PLAY|BUTTON_VOLUP) +#define PNG_PREVIOUS (BUTTON_PLAY|BUTTON_VOLDOWN) + +#elif CONFIG_KEYPAD == CREATIVEZVM_PAD + +#define PNG_ZOOM_IN BUTTON_PLAY +#define PNG_ZOOM_OUT BUTTON_CUSTOM +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_MENU +#define PNG_NEXT BUTTON_SELECT +#define PNG_PREVIOUS BUTTON_BACK + +#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD +#define PNG_ZOOM_IN BUTTON_VOL_UP +#define PNG_ZOOM_OUT BUTTON_VOL_DOWN +#define PNG_UP BUTTON_UP +#define PNG_DOWN BUTTON_DOWN +#define PNG_LEFT BUTTON_LEFT +#define PNG_RIGHT BUTTON_RIGHT +#define PNG_MENU BUTTON_MENU +#define PNG_NEXT BUTTON_VIEW +#define PNG_PREVIOUS BUTTON_PLAYLIST + +#elif CONFIG_KEYPAD == ONDAVX747_PAD + +#else +#error No keymap defined! +#endif + +#ifdef HAVE_TOUCHSCREEN +#ifndef PNG_UP +#define PNG_UP BUTTON_TOPMIDDLE +#endif +#ifndef PNG_DOWN +#define PNG_DOWN BUTTON_BOTTOMMIDDLE +#endif +#ifndef PNG_LEFT +#define PNG_LEFT BUTTON_MIDLEFT +#endif +#ifndef PNG_RIGHT +#define PNG_RIGHT BUTTON_MIDRIGHT +#endif +#ifndef PNG_ZOOM_IN +#define PNG_ZOOM_IN BUTTON_TOPRIGHT +#endif +#ifndef PNG_ZOOM_OUT +#define PNG_ZOOM_OUT BUTTON_TOPLEFT +#endif +#ifndef PNG_MENU +#define PNG_MENU (BUTTON_CENTER|BUTTON_REL) +#endif +#ifndef PNG_NEXT +#define PNG_NEXT BUTTON_BOTTOMRIGHT +#endif +#ifndef PNG_PREVIOUS +#define PNG_PREVIOUS BUTTON_BOTTOMLEFT +#endif +#endif + +/* ////////////////////////////////////////////////////////////////////////// */ +/* LodeFlate & LodeZlib Setting structs */ +/* ////////////////////////////////////////////////////////////////////////// */ + +typedef struct LodeZlib_DecompressSettings +{ + unsigned ignoreAdler32; +} LodeZlib_DecompressSettings; + +typedef struct LodePNG_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*/ +} LodePNG_InfoColor; + +typedef struct LodePNG_Time /*LodePNG's encoder does not generate the current time. To make it add a time chunk the correct time has to be provided*/ +{ + unsigned year; /*2 bytes*/ + unsigned char month; /*1-12*/ + unsigned char day; /*1-31*/ + unsigned char hour; /*0-23*/ + unsigned char minute; /*0-59*/ + unsigned char second; /*0-60 (to allow for leap seconds)*/ +} LodePNG_Time; + +typedef struct LodePNG_InfoPng /*information about the PNG image, except pixels and sometimes except width and height*/ +{ + /*header (IHDR), palette (PLTE) and transparency (tRNS)*/ + unsigned width; /*width of the image in pixels (ignored by encoder, but filled in by decoder)*/ + unsigned height; /*height of the image in pixels (ignored by encoder, but filled in by decoder)*/ + unsigned compressionMethod; /*compression method of the original file*/ + unsigned filterMethod; /*filter method of the original file*/ + unsigned interlaceMethod; /*interlace method of the original file*/ + LodePNG_InfoColor color; /*color type and bits, palette, transparency*/ + + /*suggested background color (bKGD)*/ + unsigned background_defined; /*is a suggested background color given?*/ + unsigned background_r; /*red component of suggested background color*/ + unsigned background_g; /*green component of suggested background color*/ + unsigned background_b; /*blue component of suggested background color*/ + + /*time chunk (tIME)*/ + unsigned char time_defined; /*if 0, no tIME chunk was or will be generated in the PNG image*/ + LodePNG_Time time; + + /*phys chunk (pHYs)*/ + unsigned phys_defined; /*is pHYs chunk defined?*/ + unsigned phys_x; + unsigned phys_y; + unsigned char phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ + +} LodePNG_InfoPng; + +typedef struct LodePNG_InfoRaw /*contains user-chosen information about the raw image data, which is independent of the PNG image*/ +{ + LodePNG_InfoColor color; +} LodePNG_InfoRaw; + +typedef struct LodePNG_DecodeSettings +{ + LodeZlib_DecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ + + unsigned ignoreCrc; /*ignore CRC checksums*/ + unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ +} LodePNG_DecodeSettings; + +typedef struct LodePNG_Decoder +{ + LodePNG_DecodeSettings settings; + LodePNG_InfoRaw infoRaw; + LodePNG_InfoPng infoPng; /*info of the PNG image obtained after decoding*/ + unsigned error; + int x,y; +} LodePNG_Decoder; + + +#define VERSION_STRING "20080927" + +/* Headings */ +#define DIR_PREV 1 +#define DIR_NEXT -1 +#define DIR_NONE 0 + +#define PLUGIN_OTHER 10 /* State code for output with return. */ +#define PLUGIN_REFRESH 11 /* State code for output with return. */ +#define PLUGIN_ABORT 12 +#define OUT_OF_MEMORY 9900 +#define FILE_TOO_LARGE 9910 + +/* decompressed image in the possible sizes (1,2,4,8), wasting the other */ +static fb_data *disp[9]; +static fb_data *previous_disp; +static size_t size[9]; +static size_t previous_size; + +/* my memory pool (from the mp3 buffer) */ +static char print[128]; /* use a common snprintf() buffer */ + +static unsigned char *memory, *memory_max; +static size_t memory_size; + +static unsigned char *image; /* where we put the content of the file */ +static size_t image_size; + +#if LCD_DEPTH >= 8 +static fb_data *converted_image __attribute__ ((aligned (16))); /* the (color) converted image */ +#else +static fb_data *converted_image; /* the (color) converted image */ +#endif +static size_t converted_image_size; + +static unsigned char *decoded_image; /* the decoded image */ +static size_t decoded_image_size; + +#if LCD_DEPTH >= 8 +static fb_data *resized_image __attribute__ ((aligned (16))); /* the decoded image */ +#else +static fb_data *resized_image; /* the decoded image */ +#endif + +static struct tree_context *tree; + +/* the current full file name */ +static char np_file[MAX_PATH]; +static int curfile = 0, direction = DIR_NONE, entries = 0; + +static LodePNG_Decoder decoder; + +/* list of the jpeg files */ +static char **file_pt; +/* are we using the plugin buffer or the audio buffer? */ +bool plug_buf = false; + +/* Persistent configuration */ +#define PNG_CONFIGFILE "png.cfg" +#define PNG_SETTINGS_MINVERSION 1 +#define PNG_SETTINGS_VERSION 1 + +/* Slideshow times */ +#define SS_MIN_TIMEOUT 1 +#define SS_MAX_TIMEOUT 20 +#define SS_DEFAULT_TIMEOUT 5 + +struct png_settings +{ + int ss_timeout; +}; + +static struct png_settings png_settings = + { + SS_DEFAULT_TIMEOUT + }; +static struct png_settings old_settings; + +static struct configdata png_config[] = +{ + { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, + { .int_p = &png_settings.ss_timeout }, "Slideshow Time", NULL }, +}; + +#if LCD_DEPTH > 1 +static fb_data* old_backdrop; +#endif + +#define MAX_X_SIZE LCD_WIDTH*8 + +/* Min memory allowing us to use the plugin buffer + * and thus not stopping the music + * *Very* rough estimation: + * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes + * + 30k code size = 70 000 + * + 50k min for png = 130 000 + */ +#define MIN_MEM 130000 + +static int slideshow_enabled = false; /* run slideshow */ +static int running_slideshow = false; /* loading image because of slideshw */ +#ifndef SIMULATOR +static int immediate_ata_off = false; /* power down disk after loading */ +#endif + +static unsigned ds, ds_min, ds_max; /* downscaling and limits */ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / 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 *tree_tree2d, unsigned *tree_tree1d, + unsigned *tree_lengths, unsigned *tree_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 < *tree_numcodes * 2; n++) tree_tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ + + for(n = 0; n < *tree_numcodes; n++) /*the codes*/ + for(i = 0; i < tree_lengths[n]; i++) /*the bits for this code*/ + { + unsigned char bit = (unsigned char)((tree_tree1d[n] >> (tree_lengths[n] - i - 1)) & 1); + if(treepos > *tree_numcodes - 2) return 55; /*error 55: oversubscribed; see description in header*/ + if(tree_tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ + { + if(i + 1 == tree_lengths[n]) /*last bit*/ + { + tree_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++; + tree_tree2d[2 * treepos + bit] = nodefilled + *tree_numcodes; /*addresses encoded with numcodes added to it*/ + treepos = nodefilled; + } + } + else treepos = tree_tree2d[2 * treepos + bit] - *tree_numcodes; + } + for(n = 0; n < *tree_numcodes * 2; n++) if(tree_tree2d[n] == 32767) tree_tree2d[n] = 0; /*remove possible remaining 32767's*/ + + return 0; +} + +static unsigned HuffmanTree_makeFromLengths2(unsigned *tree_tree2d, unsigned *tree_tree1d, + unsigned *tree_lengths, unsigned *tree_maxbitlen, unsigned *tree_numcodes) /*given that numcodes, lengths and maxbitlen are already filled in correctly. return value is error.*/ +{ + static unsigned blcount[16]; + memset(blcount, 0, 16 * sizeof(unsigned)); + + static unsigned nextcode[16]; + memset(nextcode, 0, 16 * sizeof(unsigned)); + + unsigned bits, n; + + /*step 1: count number of instances of each code length*/ + for(bits = 0; bits < *tree_numcodes; bits++) blcount[tree_lengths[bits]]++; + /*step 2: generate the nextcode values*/ + for(bits = 1; bits <= *tree_maxbitlen; bits++) nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; + /*step 3: generate all the codes*/ + for(n = 0; n < *tree_numcodes; n++) if(tree_lengths[n] != 0) tree_tree1d[n] = nextcode[tree_lengths[n]]++; + + return HuffmanTree_make2DTree(tree_tree2d, tree_tree1d, + tree_lengths, tree_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 *tree_tree2d, unsigned *tree_tree1d, + unsigned *tree_lengths, unsigned *tree_maxbitlen, unsigned *tree_numcodes, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen) +{ + memcpy(tree_lengths, bitlen, numcodes * sizeof(unsigned)); + *tree_numcodes = (unsigned)numcodes; /*number of symbols*/ + *tree_maxbitlen = maxbitlen; + return HuffmanTree_makeFromLengths2(tree_tree2d, tree_tree1d, + tree_lengths, tree_maxbitlen, tree_numcodes); +} + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static unsigned generateFixedTree(unsigned *tree_tree2d, unsigned *tree_tree1d, + unsigned *tree_lengths, unsigned *tree_maxbitlen, unsigned *tree_numcodes) +{ + static unsigned bitlen[NUM_DEFLATE_CODE_SYMBOLS] = { + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + }; + + return HuffmanTree_makeFromLengths(tree_tree2d, tree_tree1d, + tree_lengths, tree_maxbitlen, tree_numcodes, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); +} + +static unsigned generateDistanceTree(unsigned *treeD_tree2d, unsigned *treeD_tree1d, + unsigned *treeD_lengths, unsigned *treeD_maxbitlen, unsigned *treeD_numcodes) +{ + static unsigned bitlen[NUM_DISTANCE_SYMBOLS] = { + 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, + 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, + }; + + /*there are 32 distance codes, but 30-31 are unused*/ + return HuffmanTree_makeFromLengths(treeD_tree2d, treeD_tree1d, + treeD_lengths, treeD_maxbitlen, treeD_numcodes, bitlen, NUM_DISTANCE_SYMBOLS, 15); +} + +/*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(unsigned *tree_tree2d, unsigned *tree_numcodes, unsigned* decoded, unsigned* result, unsigned* treepos, unsigned char bit) +{ + if((*treepos) >= *tree_numcodes) return 11; /*error: it appeared outside the codetree*/ + + (*result) = tree_tree2d[2 * (*treepos) + bit]; + (*decoded) = ((*result) < *tree_numcodes); + + if(*decoded) (*treepos) = 0; + else (*treepos) = (*result) - *tree_numcodes; + + return 0; +} + +static unsigned huffmanDecodeSymbol(unsigned int* error, const unsigned char* in, size_t* bp, + unsigned *codetree_tree2d, unsigned *codetree_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(codetree_tree2d, codetree_numcodes, &decoded, &ct, &treepos, bit); + if(*error) return 0; /*stop, an error happened*/ + if(decoded) return ct; + } +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Inflator / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static void getTreeInflateFixed(unsigned *codetree_tree2d, unsigned *codetree_tree1d, + unsigned *codetree_lengths, unsigned *codetree_maxbitlen, unsigned *codetree_numcodes, + unsigned *codetreeD_tree2d, unsigned *codetreeD_tree1d, unsigned *codetreeD_lengths, + unsigned *codetreeD_maxbitlen, unsigned *codetreeD_numcodes) +{ + /*error checking not done, this is fixed stuff, it works, it doesn't depend on the image*/ + generateFixedTree(codetree_tree2d, codetree_tree1d, codetree_lengths, codetree_maxbitlen, codetree_numcodes); + generateDistanceTree(codetreeD_tree2d, codetreeD_tree1d, codetreeD_lengths, codetreeD_maxbitlen, codetreeD_numcodes); +} + +/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ +static unsigned getTreeInflateDynamic(unsigned *codetree_tree2d, unsigned *codetree_tree1d, + unsigned *codetree_lengths, unsigned *codetree_maxbitlen, unsigned *codetree_numcodes, + unsigned *codetreeD_tree2d, unsigned *codetreeD_tree1d, unsigned *codetreeD_lengths, + unsigned *codetreeD_maxbitlen, unsigned *codetreeD_numcodes, + unsigned *codelengthcodetree_tree2d, unsigned *codelengthcodetree_tree1d, + unsigned *codelengthcodetree_lengths, unsigned *codelengthcodetree_maxbitlen, + unsigned *codelengthcodetree_numcodes, 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; + static unsigned bitlen[NUM_DEFLATE_CODE_SYMBOLS]; + static unsigned bitlenD[NUM_DISTANCE_SYMBOLS]; + static 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*/ + 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_tree2d, codelengthcodetree_tree1d, + codelengthcodetree_lengths, codelengthcodetree_maxbitlen, + codelengthcodetree_numcodes, 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, sizeof(unsigned) * NUM_DEFLATE_CODE_SYMBOLS); + memset(bitlenD, 0, sizeof(unsigned) * NUM_DISTANCE_SYMBOLS); + 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_tree2d, + 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_tree2d, codetree_tree1d, + codetree_lengths, codetree_maxbitlen, codetree_numcodes, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); + if(!error) error = HuffmanTree_makeFromLengths(codetreeD_tree2d, codetreeD_tree1d, + codetreeD_lengths, codetreeD_maxbitlen, codetreeD_numcodes, bitlenD, NUM_DISTANCE_SYMBOLS, 15); + + return error; +} + +/*inflate a block with dynamic of fixed Huffman tree*/ +static unsigned inflateHuffmanBlock(unsigned char* out, size_t *outsize, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength, unsigned btype) +{ + unsigned endreached = 0, error = 0; + static unsigned old_btype = 0; + + static unsigned codetree_tree2d[NUM_DEFLATE_CODE_SYMBOLS*2]; + static unsigned codetree_tree1d[NUM_DEFLATE_CODE_SYMBOLS]; + static unsigned codetree_lengths[NUM_DEFLATE_CODE_SYMBOLS]; + static unsigned codetree_maxbitlen = 15; + static unsigned codetree_numcodes = NUM_DEFLATE_CODE_SYMBOLS; + + static unsigned codetreeD_tree2d[NUM_DISTANCE_SYMBOLS*2]; + static unsigned codetreeD_tree1d[NUM_DISTANCE_SYMBOLS]; + static unsigned codetreeD_lengths[NUM_DISTANCE_SYMBOLS]; + static unsigned codetreeD_maxbitlen = 15; + static unsigned codetreeD_numcodes = NUM_DISTANCE_SYMBOLS; + + static unsigned codelengthcodetree_tree2d[NUM_CODE_LENGTH_CODES*2]; + static unsigned codelengthcodetree_tree1d[NUM_CODE_LENGTH_CODES]; + static unsigned codelengthcodetree_lengths[NUM_CODE_LENGTH_CODES]; + static unsigned codelengthcodetree_maxbitlen; + static unsigned codelengthcodetree_numcodes; + + if(btype == 1 && old_btype != 1) getTreeInflateFixed(codetree_tree2d, codetree_tree1d, + codetree_lengths, &codetree_maxbitlen, &codetree_numcodes, + codetreeD_tree2d, codetreeD_tree1d, codetreeD_lengths, + &codetreeD_maxbitlen, &codetreeD_numcodes); + else if(btype == 2) + { + error = getTreeInflateDynamic(codetree_tree2d, codetree_tree1d, + codetree_lengths, &codetree_maxbitlen, &codetree_numcodes, + codetreeD_tree2d, codetreeD_tree1d, codetreeD_lengths, + &codetreeD_maxbitlen, &codetreeD_numcodes, + codelengthcodetree_tree2d, codelengthcodetree_tree1d, + codelengthcodetree_lengths, &codelengthcodetree_maxbitlen, &codelengthcodetree_numcodes, + in, bp, inlength); + } + + old_btype = btype; + + + while(!endreached && !error) + { + unsigned code = huffmanDecodeSymbol(&error, in, bp, codetree_tree2d, &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) >= *outsize) { + *outsize = (*pos) + 1; /*reserve more room at once*/ + if((unsigned char *)(out + *outsize) >= memory_max) { error = OUT_OF_MEMORY; break; } /*not enough memory*/ + } + out[(*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_tree2d, &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 >= *outsize) { + *outsize = (*pos) + length; /*reserve more room at once*/ + if((unsigned char *)(out + *outsize) >= memory_max) { error = OUT_OF_MEMORY; break; } /*not enough memory*/ + } + + for(forward = 0; forward < length; forward++) + { + out[(*pos)] = out[backward]; + (*pos)++; + backward++; + if(backward >= start) backward = start - distance; + } + } + } + + return error; +} + +static unsigned inflateNoCompression(unsigned char* out, size_t *outsize, 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, 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 >= *outsize) { + *outsize = (*pos) + LEN; + if((unsigned char *)(out + *outsize) >= memory_max) { error = OUT_OF_MEMORY; return error; } /*not enough memory*/ + } + + /*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*/ + memcpy(&out[*pos], &in[p], LEN * sizeof(unsigned char)); + + *pos += LEN; + p += LEN; + + (*bp) = p * 8; + + return error; +} + +/*inflate the deflated data (cfr. deflate spec); return value is the error*/ +unsigned LodeFlate_inflate(unsigned char* out, size_t *outsize, const unsigned char* in, size_t insize, size_t inpos, void (*pf_progress)(int current, int total)) +{ + 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]); + + DEBUGF("BTYPE = %u\n", BTYPE); + + if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ + else if(BTYPE == 0) error = inflateNoCompression(out, outsize, &in[inpos], &bp, &pos, insize); /*no compression*/ + else error = inflateHuffmanBlock(out, outsize, &in[inpos], &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/ + + if (rb->button_get(false) == PNG_MENU) + return PLUGIN_ABORT; + else if (pf_progress != NULL) + pf_progress(bp >> 3, insize); + + if(error) return error; + } + + if (pf_progress != NULL) + pf_progress(100, 100); + + *outsize = pos; /*Only now we know the true size of out, resize it to that*/ + + 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 LodeZlib_read32bitInt(const unsigned char* buffer) +{ + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Zlib / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned LodeZlib_decompress(unsigned char* out, size_t* outsize, const unsigned char* in, size_t insize, const LodeZlib_DecompressSettings* settings, void (*pf_progress)(int current, int total)) +{ + 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 = LodeFlate_inflate(out, outsize, in, insize, 2, pf_progress); + if(error) return error; + + if(!settings->ignoreAdler32) + { + unsigned ADLER32 = LodeZlib_read32bitInt(&in[insize - 4]); + unsigned checksum = adler32(out, (unsigned)*outsize); + if(checksum != ADLER32) { error = 58; return error; } + } + + return error; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +void LodeZlib_DecompressSettings_init(LodeZlib_DecompressSettings* settings) +{ + settings->ignoreAdler32 = 0; +} + +const LodeZlib_DecompressSettings LodeZlib_defaultDecompressSettings = {0}; + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of Zlib related code, now comes the PNG related code that uses it// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +The two functions below (LodePNG_decompress and LodePNG_compress) directly call the +LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions +below, is to provide the ability to let LodePNG use a different Zlib encoder by only +changing the two functions below, instead of changing it inside the vareous places +in the other LodePNG functions. + +*out must be NULL and *outsize must be 0 initially, and after the function is done, +*out must point to the decompressed data, *outsize must be the size of it, and must +be the size of the useful data in bytes, not the alloc size. +*/ + +static unsigned LodePNG_decompress(unsigned char* out, size_t* outsize, const unsigned char* in, size_t insize, const LodeZlib_DecompressSettings* settings, void (*pf_progress)(int current, int total)) +{ + return LodeZlib_decompress(out, outsize, in, insize, settings, pf_progress); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / CRC32 / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned Crc32_crc_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x76dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x9b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x1db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x6b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0xf00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x86d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x3b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x4db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0xd6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0xa00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x26d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x5005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0xcb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0xbdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +/*Update a running CRC with the bytes buf[0..len-1]--the CRC should be +initialized to all 1's, and the transmitted value is the 1's complement of the +final running CRC (see the crc() routine below).*/ +static unsigned Crc32_update_crc(const unsigned char* buf, unsigned crc, size_t len) +{ + unsigned c = crc; + size_t n; + + for(n = 0; n < len; n++) + { + c = Crc32_crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); + } + return c; +} + +/*Return the CRC of the bytes buf[0..len-1].*/ +static unsigned Crc32_crc(const unsigned char* buf, size_t len) +{ + return Crc32_update_crc(buf, 0xffffffffL, len) ^ 0xffffffffL; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing single bits and bytes from/to stream for LodePNG / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +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 LodePNG_read32bitInt(const unsigned char* buffer) +{ + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG chunks / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned LodePNG_chunk_length(const unsigned char* chunk) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/ +{ + return LodePNG_read32bitInt(&chunk[0]); +} + +void LodePNG_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 LodePNG_chunk_type_equals(const unsigned char* chunk, const char* type) /*check if the type is the given type*/ +{ + if(type[4] != 0) 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 LodePNG_chunk_critical(const unsigned char* chunk) /*0: ancillary chunk, 1: it's one of the critical chunk types*/ +{ + return((chunk[4] & 32) == 0); +} + +unsigned char LodePNG_chunk_private(const unsigned char* chunk) /*0: public, 1: private*/ +{ + return((chunk[6] & 32) != 0); +} + +unsigned char LodePNG_chunk_safetocopy(const unsigned char* chunk) /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy*/ +{ + return((chunk[7] & 32) != 0); +} + +unsigned char* LodePNG_chunk_data(unsigned char* chunk) /*get pointer to the data of the chunk*/ +{ + return &chunk[8]; +} + +const unsigned char* LodePNG_chunk_data_const(const unsigned char* chunk) /*get pointer to the data of the chunk*/ +{ + return &chunk[8]; +} + +unsigned LodePNG_chunk_check_crc(const unsigned char* chunk) /*returns 0 if the crc is correct, error code if it's incorrect*/ +{ + unsigned length = LodePNG_chunk_length(chunk); + unsigned CRC = LodePNG_read32bitInt(&chunk[length + 8]); + unsigned checksum = Crc32_crc(&chunk[4], length + 4); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ + if(CRC != checksum) return 1; + else return 0; +} + +unsigned char* LodePNG_chunk_next(unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/ +{ + unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +const unsigned char* LodePNG_chunk_next_const(const unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/ +{ + unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Color types and such / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*return type is a LodePNG 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 LodePNG_InfoColor_init(LodePNG_InfoColor* info) +{ + info->key_defined = 0; + info->key_r = info->key_g = info->key_b = 0; + info->colorType = 6; + info->bitDepth = 8; + memset(info->palette, 0, 256 * 4 * sizeof(unsigned char)); + info->palettesize = 0; +} + +void LodePNG_InfoColor_cleanup(LodePNG_InfoColor* info) +{ + info->palettesize = 0; +} + +unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor* info) { return getBpp(info->colorType, info->bitDepth); } /*calculate bits per pixel out of colorType and bitDepth*/ +unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor* info) { return info->colorType == 0 || info->colorType == 4; } + +unsigned LodePNG_InfoColor_equal(const LodePNG_InfoColor* info1, const LodePNG_InfoColor* info2) +{ + return info1->colorType == info2->colorType + && info1->bitDepth == info2->bitDepth; /*palette and color key not compared*/ +} + +void LodePNG_InfoPng_init(LodePNG_InfoPng* info) +{ + info->width = info->height = 0; + LodePNG_InfoColor_init(&info->color); + info->interlaceMethod = 0; + info->compressionMethod = 0; + info->filterMethod = 0; + info->background_defined = 0; + info->background_r = info->background_g = info->background_b = 0; + + info->time_defined = 0; + info->phys_defined = 0; +} + +void LodePNG_InfoPng_cleanup(LodePNG_InfoPng* info) +{ + LodePNG_InfoColor_cleanup(&info->color); +} + +unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor* dest, const LodePNG_InfoColor* source) +{ + size_t i; + LodePNG_InfoColor_cleanup(dest); + *dest = *source; + for(i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i]; + return 0; +} + +unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng* dest, const LodePNG_InfoPng* source) +{ + unsigned error = 0; + LodePNG_InfoPng_cleanup(dest); + *dest = *source; + LodePNG_InfoColor_init(&dest->color); + error = LodePNG_InfoColor_copy(&dest->color, &source->color); if(error) return error; + return error; +} + +void LodePNG_InfoPng_swap(LodePNG_InfoPng* a, LodePNG_InfoPng* b) +{ + LodePNG_InfoPng temp = *a; + *a = *b; + *b = temp; +} + +void LodePNG_InfoRaw_init(LodePNG_InfoRaw* info) +{ + LodePNG_InfoColor_init(&info->color); +} + +void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw* info) +{ + LodePNG_InfoColor_cleanup(&info->color); +} + +unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw* dest, const LodePNG_InfoRaw* source) +{ + unsigned error = 0; + LodePNG_InfoRaw_cleanup(dest); + *dest = *source; + LodePNG_InfoColor_init(&dest->color); + error = LodePNG_InfoColor_copy(&dest->color, &source->color); if(error) return error; + return error; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG 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 (LodePNG_InfoColor_getBpp) +for < 8 bpp images, there may _not_ be padding bits at the end of scanlines. +*/ +unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h) +{ + size_t i, j, bp = 0; /*bitpointer, used by less-than-8-bit color types*/ + size_t x, y; + + DEBUGF("%ld: [ColorType/BitDepth] %d/%d => %d/%d\n", *rb->current_tick, infoIn->colorType, infoIn->bitDepth, infoOut->colorType, infoOut->bitDepth); + DEBUGF("%ld: Image_size => %dx%d\n", *rb->current_tick, w, h); + + if(!running_slideshow) + { + rb->snprintf(print, sizeof(print), "color conversion in progress"); + rb->lcd_puts(0, 3, print); + rb->lcd_update(); + } + + /*cases where in and out already have the same format*/ + if(LodePNG_InfoColor_equal(infoIn, infoOut)) + { + + i = 0; + j = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[i++]; + unsigned char g = in[i++]; + unsigned char b = in[i++]; + out[j++] = LCD_RGBPACK(r,g,b); + } + } + return 0; + } + + if((infoOut->colorType == 2 || infoOut->colorType == 6) && infoOut->bitDepth == 8) + { + if(infoIn->bitDepth == 8) + { + switch(infoIn->colorType) + { + case 0: /*greyscale color*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[i]; + unsigned char g = in[i]; + unsigned char b = in[i]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 2: /*RGB color*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[3 * i]; + unsigned char g = in[3 * i + 1]; + unsigned char b = in[3 * i + 2]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 3: /*indexed color (palette)*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + if(in[i] >= infoIn->palettesize) return 46; + unsigned char r = infoIn->palette[4 * in[i]]; + unsigned char g = infoIn->palette[4 * in[i] + 1]; + unsigned char b = infoIn->palette[4 * in[i] + 2]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 4: /*greyscale with alpha*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[2*i]; + unsigned char g = in[2*i]; + unsigned char b = in[2*i]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 6: /*RGB with alpha*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[4 * i]; + unsigned char g = in[4 * i + 1]; + unsigned char b = in[4 * i + 2]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + default: break; + } + } + else if(infoIn->bitDepth == 16) + { + switch(infoIn->colorType) + { + case 0: /*greyscale color*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[2 * i]; + unsigned char g = in[2 * i]; + unsigned char b = in[2 * i]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 2: /*RGB color*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[6 * i]; + unsigned char g = in[6 * i + 2]; + unsigned char b = in[6 * i + 4]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 4: /*greyscale with alpha*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[4 * i]; + unsigned char g = in[4 * i]; + unsigned char b = in[4 * i]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 6: /*RGB with alpha*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[8 * i]; + unsigned char g = in[8 * i + 2]; + unsigned char b = in[8 * i + 4]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + default: break; + } + } + else /*infoIn->bitDepth is less than 8 bit per channel*/ + { + switch(infoIn->colorType) + { + case 0: /*greyscale color*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth); + value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/ + unsigned char r = (unsigned char)value; + unsigned char g = (unsigned char)value; + unsigned char b = (unsigned char)value; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 3: /*indexed color (palette)*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth); + if(value >= infoIn->palettesize) return 47; + unsigned char r = infoIn->palette[4 * value]; + unsigned char g = infoIn->palette[4 * value + 1]; + unsigned char b = infoIn->palette[4 * value + 2]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + default: break; + } + } + } + else if(LodePNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/ + { + if(!LodePNG_InfoColor_isGreyscaleType(infoIn)) return 62; + if(infoIn->bitDepth == 8) + { + switch(infoIn->colorType) + { + case 0: /*greyscale color*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[i]; + unsigned char g = in[i]; + unsigned char b = in[i]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 4: /*greyscale with alpha*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[2 * i + 1]; + unsigned char g = in[2 * i + 1]; + unsigned char b = in[2 * i + 1]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + default: return 31; + } + } + else if(infoIn->bitDepth == 16) + { + switch(infoIn->colorType) + { + case 0: /*greyscale color*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[2 * i]; + unsigned char g = in[2 * i]; + unsigned char b = in[2 * i]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + break; + case 4: /*greyscale with alpha*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned char r = in[4 * i]; + unsigned char g = in[4 * i]; + unsigned char b = in[4 * i]; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + 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*/ + i = 0; + for(y = 0 ; y < h ; y++) { + for(x = 0 ; x < w ; x++) { + unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth); + value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/ + unsigned char r = (unsigned char)value; + unsigned char g = (unsigned char)value; + unsigned char b = (unsigned char)value; + out[i++] = LCD_RGBPACK(r,g,b); + } + } + } + } + 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 LodePNG_Info. return value is error*/ +void LodePNG_inspect(LodePNG_Decoder* decoder, const unsigned char* in, size_t inlength) +{ + if(inlength == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/ + if(inlength < 29) { decoder->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*/ + LodePNG_InfoPng_cleanup(&decoder->infoPng); + LodePNG_InfoPng_init(&decoder->infoPng); + decoder->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) { decoder->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') { decoder->error = 29; return; } /*error: it doesn't start with a IHDR chunk!*/ + + /*read the values given in the header*/ + decoder->infoPng.width = LodePNG_read32bitInt(&in[16]); + decoder->infoPng.height = LodePNG_read32bitInt(&in[20]); + decoder->infoPng.color.bitDepth = in[24]; + decoder->infoPng.color.colorType = in[25]; + decoder->infoPng.compressionMethod = in[26]; + decoder->infoPng.filterMethod = in[27]; + decoder->infoPng.interlaceMethod = in[28]; + + if(!decoder->settings.ignoreCrc) + { + unsigned CRC = LodePNG_read32bitInt(&in[29]); + unsigned checksum = Crc32_crc(&in[12], 17); + if(CRC != checksum) { decoder->error = 57; return; } + } + + if(decoder->infoPng.compressionMethod != 0) { decoder->error = 32; return; } /*error: only compression method 0 is allowed in the specification*/ + if(decoder->infoPng.filterMethod != 0) { decoder->error = 33; return; } /*error: only filter method 0 is allowed in the specification*/ + if(decoder->infoPng.interlaceMethod > 1) { decoder->error = 34; return; } /*error: only interlace methods 0 and 1 exist in the specification*/ + + decoder->error = checkColorValidity(decoder->infoPng.color.colorType, decoder->infoPng.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, const LodePNG_Decoder* decoder) /*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 = LodePNG_InfoColor_getBpp(&decoder->infoPng.color); + unsigned w = decoder->infoPng.width; + unsigned h = decoder->infoPng.height; + unsigned error = 0; + if(bpp == 0) return 31; /*error: invalid colortype*/ + + if(decoder->infoPng.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; +} + +/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ +static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t size, void (*pf_progress)(int current, int total)) +{ + if (pf_progress != NULL) + pf_progress(0, 100); + unsigned char IEND = 0; + const unsigned char* chunk; + size_t i; + unsigned char *idat = memory; + size_t idat_size = 0; + + /*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*/ + decoded_image_size = 0; + + if(size == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/ + + LodePNG_inspect(decoder, in, size); /*reads header and resets other parameters in decoder->infoPng*/ + if(decoder->error) return; + + chunk = &in[33]; /*first byte of the first chunk after the header*/ + + 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; + const unsigned char* data; /*the data in the chunk*/ + + if((size_t)((chunk - in) + 12) > size || chunk < in) { decoder->error = 30; break; } /*error: size of the in buffer too small to contain next chunk*/ + chunkLength = LodePNG_chunk_length(chunk); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ + if(chunkLength > 2147483647) { decoder->error = 63; break; } + if((size_t)((chunk - in) + chunkLength + 12) > size || (chunk + chunkLength + 12) < in) { decoder->error = 35; break; } /*error: size of the in buffer too small to contain next chunk*/ + data = LodePNG_chunk_data_const(chunk); + + /*IDAT chunk, containing compressed image data*/ + if(LodePNG_chunk_type_equals(chunk, "IDAT")) + { + size_t oldsize = idat_size; + idat_size += chunkLength; + if (idat + idat_size >= image) { decoder->error = OUT_OF_MEMORY; break; } + memcpy(idat+oldsize, data, chunkLength * sizeof(unsigned char)); + critical_pos = 3; + } + /*IEND chunk*/ + else if(LodePNG_chunk_type_equals(chunk, "IEND")) + { + IEND = 1; + } + /*palette chunk (PLTE)*/ + else if(LodePNG_chunk_type_equals(chunk, "PLTE")) + { + unsigned pos = 0; + decoder->infoPng.color.palettesize = chunkLength / 3; + if(decoder->infoPng.color.palettesize > 256) { decoder->error = 38; break; } /*error: palette too big*/ + for(i = 0; i < decoder->infoPng.color.palettesize; i++) + { + decoder->infoPng.color.palette[4 * i + 0] = data[pos++]; /*R*/ + decoder->infoPng.color.palette[4 * i + 1] = data[pos++]; /*G*/ + decoder->infoPng.color.palette[4 * i + 2] = data[pos++]; /*B*/ + decoder->infoPng.color.palette[4 * i + 3] = 255; /*alpha*/ + } + critical_pos = 2; + } + /*palette transparency chunk (tRNS)*/ + else if(LodePNG_chunk_type_equals(chunk, "tRNS")) + { + if(decoder->infoPng.color.colorType == 3) + { + if(chunkLength > decoder->infoPng.color.palettesize) { decoder->error = 39; break; } /*error: more alpha values given than there are palette entries*/ + for(i = 0; i < chunkLength; i++) decoder->infoPng.color.palette[4 * i + 3] = data[i]; + } + else if(decoder->infoPng.color.colorType == 0) + { + if(chunkLength != 2) { decoder->error = 40; break; } /*error: this chunk must be 2 bytes for greyscale image*/ + decoder->infoPng.color.key_defined = 1; + decoder->infoPng.color.key_r = decoder->infoPng.color.key_g = decoder->infoPng.color.key_b = 256 * data[0] + data[1]; + } + else if(decoder->infoPng.color.colorType == 2) + { + if(chunkLength != 6) { decoder->error = 41; break; } /*error: this chunk must be 6 bytes for RGB image*/ + decoder->infoPng.color.key_defined = 1; + decoder->infoPng.color.key_r = 256 * data[0] + data[1]; + decoder->infoPng.color.key_g = 256 * data[2] + data[3]; + decoder->infoPng.color.key_b = 256 * data[4] + data[5]; + } + else { decoder->error = 42; break; } /*error: tRNS chunk not allowed for other color models*/ + } + /*background color chunk (bKGD)*/ + else if(LodePNG_chunk_type_equals(chunk, "bKGD")) + { + if(decoder->infoPng.color.colorType == 3) + { + if(chunkLength != 1) { decoder->error = 43; break; } /*error: this chunk must be 1 byte for indexed color image*/ + decoder->infoPng.background_defined = 1; + decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_g = data[0]; + } + else if(decoder->infoPng.color.colorType == 0 || decoder->infoPng.color.colorType == 4) + { + if(chunkLength != 2) { decoder->error = 44; break; } /*error: this chunk must be 2 bytes for greyscale image*/ + decoder->infoPng.background_defined = 1; + decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_b = 256 * data[0] + data[1]; + } + else if(decoder->infoPng.color.colorType == 2 || decoder->infoPng.color.colorType == 6) + { + if(chunkLength != 6) { decoder->error = 45; break; } /*error: this chunk must be 6 bytes for greyscale image*/ + decoder->infoPng.background_defined = 1; + decoder->infoPng.background_r = 256 * data[0] + data[1]; + decoder->infoPng.background_g = 256 * data[2] + data[3]; + decoder->infoPng.background_b = 256 * data[4] + data[5]; + } + } + else if(LodePNG_chunk_type_equals(chunk, "tIME")) + { + if(chunkLength != 7) { decoder->error = 73; break; } + decoder->infoPng.time_defined = 1; + decoder->infoPng.time.year = 256 * data[0] + data[+ 1]; + decoder->infoPng.time.month = data[2]; + decoder->infoPng.time.day = data[3]; + decoder->infoPng.time.hour = data[4]; + decoder->infoPng.time.minute = data[5]; + decoder->infoPng.time.second = data[6]; + } + else if(LodePNG_chunk_type_equals(chunk, "pHYs")) + { + if(chunkLength != 9) { decoder->error = 74; break; } + decoder->infoPng.phys_defined = 1; + decoder->infoPng.phys_x = 16777216 * data[0] + 65536 * data[1] + 256 * data[2] + data[3]; + decoder->infoPng.phys_y = 16777216 * data[4] + 65536 * data[5] + 256 * data[6] + data[7]; + decoder->infoPng.phys_unit = data[8]; + } + else /*it's not an implemented chunk type, so ignore it: skip over the data*/ + { + if(LodePNG_chunk_critical(chunk)) { decoder->error = 69; break; } /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ + unknown = 1; + } + + if(!decoder->settings.ignoreCrc && !unknown) /*check CRC if wanted, only on known chunk types*/ + { + if(LodePNG_chunk_check_crc(chunk)) { decoder->error = 57; break; } + } + + if(!IEND) chunk = LodePNG_chunk_next_const(chunk); + } + + if(!decoder->error) + { + unsigned char *scanlines = idat + idat_size; + size_t scanlines_size = 0; + decoder->error = LodePNG_decompress(scanlines, &scanlines_size, idat, idat_size, &decoder->settings.zlibsettings, pf_progress); /*decompress with the Zlib decompressor*/ + + if(!decoder->error) + { + decoded_image_size = (decoder->infoPng.height * decoder->infoPng.width * LodePNG_InfoColor_getBpp(&decoder->infoPng.color) + 7) / 8; + if (decoded_image_size > memory_size) { decoder->error = OUT_OF_MEMORY; return; } + decoded_image = memory_max - decoded_image_size + 1; + if (scanlines + scanlines_size >= decoded_image) { decoder->error = OUT_OF_MEMORY; return; } + memset(decoded_image, 0, decoded_image_size * sizeof(unsigned char)); + decoder->error = postProcessScanlines(decoded_image, scanlines, decoder); + } + } +} + +void LodePNG_decode(LodePNG_Decoder* decoder, unsigned char* in, size_t insize, void (*pf_progress)(int current, int total)) +{ + decodeGeneric(decoder, in, insize, pf_progress); + if(decoder->error) return; + + /*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(!(decoder->infoRaw.color.colorType == 2 || decoder->infoRaw.color.colorType == 6) && !(decoder->infoRaw.color.bitDepth == 8)) { decoder->error = 56; return; } + converted_image = (fb_data *)((int)(memory + 3) & ~3); + converted_image_size = sizeof(fb_data)*decoder->infoPng.width*decoder->infoPng.height; + if ((unsigned char *)(converted_image + converted_image_size) >= decoded_image) { decoder->error = OUT_OF_MEMORY; } + if (!decoder->error) decoder->error = LodePNG_convert(converted_image, decoded_image, &decoder->infoRaw.color, &decoder->infoPng.color, decoder->infoPng.width, decoder->infoPng.height); +} + +void LodePNG_DecodeSettings_init(LodePNG_DecodeSettings* settings) +{ + settings->color_convert = 1; + settings->ignoreCrc = 0; + LodeZlib_DecompressSettings_init(&settings->zlibsettings); +} + +void LodePNG_Decoder_init(LodePNG_Decoder* decoder) +{ + LodePNG_DecodeSettings_init(&decoder->settings); + LodePNG_InfoRaw_init(&decoder->infoRaw); + LodePNG_InfoPng_init(&decoder->infoPng); + decoder->error = 1; +} + +void LodePNG_Decoder_cleanup(LodePNG_Decoder* decoder) +{ + LodePNG_InfoRaw_cleanup(&decoder->infoRaw); + LodePNG_InfoPng_cleanup(&decoder->infoPng); +} + +/* support function for qsort() */ +static int compare(const void* p1, const void* p2) +{ + return rb->strcasecmp(*((char **)p1), *((char **)p2)); +} + +bool png_ext(const char ext[]) +{ + if(!ext) + return false; + if(!rb->strcasecmp(ext,".png")) + return true; + else + return false; +} + +/*Read directory contents for scrolling. */ +void get_pic_list(void) +{ + int i; + long int str_len = 0; + char *pname; + tree = rb->tree_get_context(); + +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + file_pt = rb->plugin_get_buffer((size_t *)&image_size); +#else + file_pt = rb->plugin_get_audio_buffer((size_t *)&image_size); +#endif + + for(i = 0; i < tree->filesindir; i++) + { + if(png_ext(rb->strrchr(&tree->name_buffer[str_len],'.'))) + file_pt[entries++] = &tree->name_buffer[str_len]; + + str_len += rb->strlen(&tree->name_buffer[str_len]) + 1; + } + + rb->qsort(file_pt, entries, sizeof(char**), compare); + + /* Remove path and leave only the name.*/ + pname = rb->strrchr(np_file,'/'); + pname++; + + /* Find Selected File. */ + for(i = 0; i < entries; i++) + if(!rb->strcmp(file_pt[i], pname)) + curfile = i; +} + +int change_filename(int direct) +{ + int count = 0; + direction = direct; + + if(direct == DIR_PREV) + { + do + { + count++; + if(curfile == 0) + curfile = entries - 1; + else + curfile--; + }while(file_pt[curfile] == '\0' && count < entries); + /* we "erase" the file name if we encounter + * a non-supported file, so skip it now */ + } + else /* DIR_NEXT/DIR_NONE */ + { + do + { + count++; + if(curfile == entries - 1) + curfile = 0; + else + curfile++; + }while(file_pt[curfile] == '\0' && count < entries); + } + + if(count == entries && file_pt[curfile] == '\0') + { + rb->splash(HZ, "No supported files"); + return PLUGIN_ERROR; + } + if(rb->strlen(tree->currdir) > 1) + { + rb->strcpy(np_file, tree->currdir); + rb->strcat(np_file, "/"); + } + else + rb->strcpy(np_file, tree->currdir); + + rb->strcat(np_file, file_pt[curfile]); + + return PLUGIN_OTHER; +} + +/* switch off overlay, for handling SYS_ events */ +void cleanup(void *parameter) +{ + (void)parameter; +} + +#define VSCROLL (LCD_HEIGHT/8) +#define HSCROLL (LCD_WIDTH/10) + +#define ZOOM_IN 100 /* return codes for below function */ +#define ZOOM_OUT 101 + +int show_menu(void) /* return 1 to quit */ +{ +#if LCD_DEPTH > 1 + rb->lcd_set_backdrop(old_backdrop); +#ifdef HAVE_LCD_COLOR + rb->lcd_set_foreground(rb->global_settings->fg_color); + rb->lcd_set_background(rb->global_settings->bg_color); +#else + rb->lcd_set_foreground(LCD_BLACK); + rb->lcd_set_background(LCD_WHITE); +#endif +#endif + int result; + + enum menu_id + { + MIID_RETURN = 0, + MIID_TOGGLE_SS_MODE, + MIID_CHANGE_SS_MODE, +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + MIID_SHOW_PLAYBACK_MENU, +#endif + MIID_QUIT, + }; + + MENUITEM_STRINGLIST(menu, "Png Menu", NULL, + "Return", "Toggle Slideshow Mode", + "Change Slideshow Time", +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + "Show Playback Menu", +#endif + "Quit"); + + static const struct opt_items slideshow[2] = { + { "Disable", -1 }, + { "Enable", -1 }, + }; + + result=rb->do_menu(&menu, NULL, NULL, false); + + switch (result) + { + case MIID_RETURN: + break; + case MIID_TOGGLE_SS_MODE: + rb->set_option("Toggle Slideshow", &slideshow_enabled, INT, + slideshow , 2, NULL); + break; + case MIID_CHANGE_SS_MODE: + rb->set_int("Slideshow Time", "s", UNIT_SEC, + &png_settings.ss_timeout, NULL, 1, + SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL); + break; +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + case MIID_SHOW_PLAYBACK_MENU: + if (plug_buf) + { + playback_control(NULL); + } + else + { + rb->splash(HZ, "Cannot restart playback"); + } + break; +#endif + case MIID_QUIT: + return 1; + break; + } + +#if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE) + /* change ata spindown time based on slideshow time setting */ + immediate_ata_off = false; + rb->storage_spindown(rb->global_settings->disk_spindown); + + if (slideshow_enabled) + { + if(png_settings.ss_timeout < 10) + { + /* slideshow times < 10s keep disk spinning */ + rb->storage_spindown(0); + } + else if (!rb->mp3_is_playing()) + { + /* slideshow times > 10s and not playing: ata_off after load */ + immediate_ata_off = true; + } + } +#endif +#if LCD_DEPTH > 1 + rb->lcd_set_backdrop(NULL); + rb->lcd_set_foreground(LCD_WHITE); + rb->lcd_set_background(LCD_BLACK); +#endif + rb->lcd_clear_display(); + return 0; +} + +/* Pan the viewing window right - move image to the left and fill in + the right-hand side */ +static void pan_view_right(struct LodePNG_Decoder* decoder) +{ + int move; + + move = MIN(HSCROLL, decoder->infoPng.width/ds - decoder->x - LCD_WIDTH); + if (move > 0 && decoder->infoPng.width/ds > LCD_WIDTH) + { + decoder->x += move; + rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/, + MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2), + MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2), + MIN(LCD_WIDTH, decoder->infoPng.width/ds), + MIN(LCD_HEIGHT, decoder->infoPng.height/ds)); + rb->lcd_update(); + } +} + +/* Pan the viewing window left - move image to the right and fill in + the left-hand side */ +static void pan_view_left(struct LodePNG_Decoder* decoder) +{ + int move; + + move = MIN(HSCROLL, decoder->x); + if (move > 0) + { + decoder->x -= move; + rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/, + MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2), + MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2), + MIN(LCD_WIDTH, decoder->infoPng.width/ds), + MIN(LCD_HEIGHT, decoder->infoPng.height/ds)); + rb->lcd_update(); + } +} + + +/* Pan the viewing window up - move image down and fill in + the top */ +static void pan_view_up(struct LodePNG_Decoder* decoder) +{ + int move; + + move = MIN(VSCROLL, decoder->y); + if (move > 0) + { + decoder->y -= move; + rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/, + MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2), + MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2), + MIN(LCD_WIDTH, decoder->infoPng.width/ds), + MIN(LCD_HEIGHT, decoder->infoPng.height/ds)); + rb->lcd_update(); + } +} + +/* Pan the viewing window down - move image up and fill in + the bottom */ +static void pan_view_down(struct LodePNG_Decoder* decoder) +{ + int move; + + move = MIN(VSCROLL, decoder->infoPng.height/ds - decoder->y - LCD_HEIGHT); + if (move > 0 && decoder->infoPng.height/ds > LCD_HEIGHT) + { + decoder->y += move; + rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/, + MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2), + MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2), + MIN(LCD_WIDTH, decoder->infoPng.width/ds), + MIN(LCD_HEIGHT, decoder->infoPng.height/ds)); + rb->lcd_update(); + } +} + +/* interactively scroll around the image */ +int scroll_bmp(struct LodePNG_Decoder* decoder) +{ + int button; + int lastbutton = 0; + + while (true) + { + if (slideshow_enabled) + button = rb->button_get_w_tmo(png_settings.ss_timeout * HZ); + else button = rb->button_get(true); + + running_slideshow = false; + + switch(button) + { + case PNG_LEFT: + if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE) + return change_filename(DIR_PREV); + case PNG_LEFT | BUTTON_REPEAT: + pan_view_left(decoder); + break; + + case PNG_RIGHT: + if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE) + return change_filename(DIR_NEXT); + case PNG_RIGHT | BUTTON_REPEAT: + pan_view_right(decoder); + break; + + case PNG_UP: + case PNG_UP | BUTTON_REPEAT: + pan_view_up(decoder); + break; + + case PNG_DOWN: + case PNG_DOWN | BUTTON_REPEAT: + pan_view_down(decoder); + break; + + case BUTTON_NONE: + if (!slideshow_enabled) + break; + running_slideshow = true; + if (entries > 0) + return change_filename(DIR_NEXT); + break; + +#ifdef PNG_SLIDE_SHOW + case PNG_SLIDE_SHOW: + slideshow_enabled = !slideshow_enabled; + running_slideshow = slideshow_enabled; + break; +#endif + +#ifdef PNG_NEXT_REPEAT + case PNG_NEXT_REPEAT: +#endif + case PNG_NEXT: + if (entries > 0) + return change_filename(DIR_NEXT); + break; + +#ifdef PNG_PREVIOUS_REPEAT + case PNG_PREVIOUS_REPEAT: +#endif + case PNG_PREVIOUS: + if (entries > 0) + return change_filename(DIR_PREV); + break; + + case PNG_ZOOM_IN: +#ifdef PNG_ZOOM_PRE + if (lastbutton != PNG_ZOOM_PRE) + break; +#endif + return ZOOM_IN; + break; + + case PNG_ZOOM_OUT: +#ifdef PNG_ZOOM_PRE + if (lastbutton != PNG_ZOOM_PRE) + break; +#endif + return ZOOM_OUT; + break; + + #ifdef PNG_RC_MENU + case PNG_RC_MENU: +#endif + case PNG_MENU: + if (show_menu() == 1) + return PLUGIN_OK; + else + return PLUGIN_REFRESH; + + break; + default: + if (rb->default_event_handler_ex(button, cleanup, NULL) + == SYS_USB_CONNECTED) + return PLUGIN_USB_CONNECTED; + break; + + } /* switch */ + + if (button != BUTTON_NONE) + lastbutton = button; + } /* while (true) */ +} + +/* set the view to the given center point, limit if necessary */ +void set_view (struct LodePNG_Decoder* decoder, int cx, int cy) +{ + int x, y; + + /* plain center to available width/height */ + x = cx - MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2; + y = cy - MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2; + + /* limit against upper image size */ + x = MIN((int)(decoder->infoPng.width/ds) - LCD_WIDTH, x); + y = MIN((int)(decoder->infoPng.height/ds) - LCD_HEIGHT, y); + + /* limit against negative side */ + x = MAX(0, x); + y = MAX(0, y); + + decoder->x = x; /* set the values */ + decoder->y = y; + +} + +/* callback updating a progress meter while PNG decoding */ +void cb_progess(int current, int total) +{ + rb->yield(); /* be nice to the other threads */ + if(!running_slideshow) + { + rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-8, LCD_WIDTH, 8, total, 0, + current, HORIZONTAL); + rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8); + } + else + { + /* in slideshow mode, keep gui interference to a minimum */ + rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-4, LCD_WIDTH, 4, total, 0, + current, HORIZONTAL); + rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4); + } +} + +int pngmem(struct LodePNG_Decoder* decoder, int ds) +{ + return decoder->infoPng.width * decoder->infoPng.height/ds * sizeof(fb_data) / ds; +} + +/* how far can we zoom in without running out of memory */ +int min_downscale(struct LodePNG_Decoder* decoder, int bufsize) +{ + int downscale = 8; + + if (pngmem(decoder, 8) > bufsize) + return 0; /* error, too large, even 1:8 doesn't fit */ + + while (downscale > 1 && pngmem(decoder, downscale/2) <= bufsize) + downscale /= 2; + + return downscale; +} + +/* how far can we zoom out, to fit image into the LCD */ +unsigned max_downscale(struct LodePNG_Decoder* decoder) +{ + unsigned downscale = 1; + + while (downscale < 8 && (decoder->infoPng.width > LCD_WIDTH*downscale + || decoder->infoPng.height > LCD_HEIGHT*downscale)) + { + downscale *= 2; + } + + return downscale; +} + +/* calculate the view center based on the bitmap position */ +void get_view(struct LodePNG_Decoder* decoder, int* p_cx, int* p_cy) +{ + *p_cx = decoder->x + MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2; + *p_cy = decoder->y + MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2; +} + +/* return decoded or cached image */ +fb_data *get_image(struct LodePNG_Decoder* decoder) +{ + fb_data * p_disp = disp[ds]; /* short cut */ + + if (p_disp != NULL) + { + DEBUGF("Found an image in cache\n"); + return p_disp; /* we still have it */ + } + + if (previous_disp == NULL) { + previous_disp = converted_image; + previous_size = converted_image_size; + } + + size[ds] = decoder->infoPng.width * decoder->infoPng.height * sizeof(fb_data) / ds; + + /* assign image buffer */ + if (ds > 1) { + if(!running_slideshow) + { + rb->snprintf(print, sizeof(print), "resizing %d*%d", + decoder->infoPng.width/ds, decoder->infoPng.height/ds); + rb->lcd_puts(0, 3, print); + rb->lcd_update(); + } + static struct bitmap bmp_src, bmp_dst; + + disp[ds] = (fb_data *)((int)(previous_disp + previous_size + 3) & ~3); + + if ((unsigned char *)(disp[ds] + size[ds]) >= memory_max) { + //rb->splash(HZ, "Out of Memory"); + // Still display the original image which is already decoded in RAM + disp[ds] = NULL; + ds = 1; + return converted_image; + } else { + bmp_src.width = decoder->infoPng.width; + bmp_src.height = decoder->infoPng.height; + bmp_src.data = (unsigned char *)converted_image; + + bmp_dst.width = decoder->infoPng.width/ds; + bmp_dst.height = decoder->infoPng.height/ds; + bmp_dst.data = (unsigned char *)disp[ds]; +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); + smooth_resize_bitmap(&bmp_src, &bmp_dst); + rb->cpu_boost(false); +#else + smooth_resize_bitmap(&bmp_src, &bmp_dst); +#endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ + } + } else { + disp[ds] = converted_image; + } + + + + previous_disp = disp[ds]; + previous_size = size[ds]; + + return disp[ds]; + + +} + +/* load, decode, display the image */ +int load_and_show(char* filename) +{ + int fd; + int status; + long time=0; /* measured ticks */ + int cx=0, cy=0; /* view center */ + int w, h; /* used to center output */ + + LodePNG_Decoder_init(&decoder); + + rb->lcd_clear_display(); + + fd = rb->open(filename, O_RDONLY); + if (fd < 0) + { + rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd); + rb->splash(HZ, print); + return PLUGIN_ERROR; + } + image_size = rb->filesize(fd); + memset(&disp, 0, sizeof(disp)); + previous_disp = NULL; + previous_size = 0; + + DEBUGF("reading file '%s'\n", filename); + + if(!running_slideshow) { +#if LCD_DEPTH > 1 + rb->lcd_set_foreground(LCD_WHITE); + rb->lcd_set_background(LCD_BLACK); + rb->lcd_set_backdrop(NULL); +#endif + + rb->lcd_clear_display(); + rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1); + rb->lcd_puts(0, 0, print); + rb->lcd_update(); + } + + if (rb->button_get(false) == PNG_MENU) { + decoder.error = PLUGIN_ABORT; + rb->close(fd); + + } else if (image_size > memory_size) { + decoder.error = FILE_TOO_LARGE; + rb->close(fd); + + } else { + if(!running_slideshow) { + rb->snprintf(print, sizeof(print), "loading %lu bytes", image_size); + rb->lcd_puts(0, 1, print); + rb->lcd_update(); + } + + image = memory_max - image_size + 1; + rb->read(fd, image, image_size); + rb->close(fd); + + if(!running_slideshow) { + rb->snprintf(print, sizeof(print), "decoding image"); + rb->lcd_puts(0, 2, print); + rb->lcd_update(); + } +#ifndef SIMULATOR + else if(immediate_ata_off) { + /* running slideshow and time is long enough: power down disk */ + rb->storage_sleep(); + } +#endif + + decoder.settings.color_convert = 1; + decoder.infoRaw.color.colorType = 2; + decoder.infoRaw.color.bitDepth = 8; + + if (rb->button_get(false) == PNG_MENU) { + decoder.error = PLUGIN_ABORT; + } else { + LodePNG_inspect(&decoder, image, image_size); + } + + if (!decoder.error) { + + if(!running_slideshow) { + rb->snprintf(print, sizeof(print), "image %dx%d", decoder.infoPng.width, decoder.infoPng.height); + rb->lcd_puts(0, 2, print); + rb->lcd_update(); + } + ds_max = max_downscale(&decoder); /* check display constraint */ + + ds = ds_max; /* initials setting */ + + if(!running_slideshow) + { + rb->snprintf(print, sizeof(print), "decoding %d*%d", + decoder.infoPng.width, decoder.infoPng.height); + rb->lcd_puts(0, 3, print); + rb->lcd_update(); + } + + /* the actual decoding */ + time = *rb->current_tick; +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); + LodePNG_decode(&decoder, image, image_size, cb_progess); + rb->cpu_boost(false); +#else + LodePNG_decode(&decoder, image, image_size, cb_progess); +#endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ + + ds_min = min_downscale(&decoder, memory_max - (unsigned char*)(converted_image + converted_image_size)); /* check memory constraint */ + + if (ds_min == 0) { + // Could not resize the image + ds_min = ds = ds_max = 1; + } + } + } + + if (decoder.error == PLUGIN_ABORT || decoder.error == FILE_TOO_LARGE) { + rb->close(fd); +#ifndef SIMULATOR + if(immediate_ata_off) { + /* running slideshow and time is long enough: power down disk */ + rb->storage_sleep(); + } +#endif + } + + time = *rb->current_tick - time; + + if(!running_slideshow && !decoder.error) + { + rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); + rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ + rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print); + rb->lcd_update(); + } + + do { +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + if(plug_buf && (decoder.error == FILE_TOO_LARGE || decoder.error == OUT_OF_MEMORY)) + { + rb->lcd_setfont(FONT_SYSFIXED); + rb->lcd_clear_display(); + rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1); + rb->lcd_puts(0,0,print); + rb->lcd_puts(0,1,"Not enough plugin memory!"); + rb->lcd_puts(0,2,"Zoom In: Stop playback."); + if(entries>1) + rb->lcd_puts(0,3,"Left/Right: Skip File."); + rb->lcd_puts(0,4,"Off: Quit."); + rb->lcd_update(); + rb->lcd_setfont(FONT_UI); + + rb->button_clear_queue(); + + while (1) + { + int button = rb->button_get(true); + switch(button) + { + case PNG_ZOOM_IN: + plug_buf = false; + memory = rb->plugin_get_audio_buffer( + (size_t *)&memory_size); + memory += (entries * sizeof(char**)); + memory_size -= (entries * sizeof(char**)); + memory_max = memory + memory_size - 1; + /*try again this file, now using the audio buffer */ + return PLUGIN_OTHER; +#ifdef PNG_RC_MENU + case PNG_RC_MENU: +#endif + case PNG_MENU: + return PLUGIN_OK; + + case PNG_LEFT: + if(entries>1) + { + rb->lcd_clear_display(); + return change_filename(DIR_PREV); + } + break; + + case PNG_RIGHT: + if(entries>1) + { + rb->lcd_clear_display(); + return change_filename(DIR_NEXT); + } + break; + default: + if(rb->default_event_handler_ex(button, cleanup, NULL) + == SYS_USB_CONNECTED) + return PLUGIN_USB_CONNECTED; + + } + } + } + //else +#endif + + if (!decoder.error) resized_image = get_image(&decoder); /* decode or fetch from cache */ + if (decoder.error) { + + switch (decoder.error) { + case 10:case 11:case 64: rb->splash(HZ, "invalid huffman decoding");break; + case PLUGIN_ABORT: rb->splash(HZ, "aborted");break; + case 13:case 14:case 15:case 16:rb->splash(HZ, "invalid dynamic deflate block");break; + case 17:case 19: rb->splash(HZ, "while inflating: end of out buffer memory reached");break; + case 18: rb->splash(HZ, "while inflating: invalid distance code");break; + case 20: rb->splash(HZ, "invalid deflate block BTYPE encountered while decoding");break; + case 21:case 22:case 23: rb->splash(HZ, "inflate problem");break; + case 24: rb->splash(HZ, "invalid FCHECK in zlib header");break; + case 25: rb->splash(HZ, "invalid compression method in zlib header");break; + case 26: rb->splash(HZ, "FDICT encountered in zlib header while it's not used for PNG");break; + case 27: rb->splash(HZ, "png file smaller than a png header");break; + case 28: rb->splash(HZ, "incorrect png signature");break; + case 29: rb->splash(HZ, "first chunk is not IHDR");break; + case 30: rb->splash(HZ, "chunk length too large");break; + case 31: rb->splash(HZ, "illegal PNG color type or bpp");break; + case 32: rb->splash(HZ, "illegal PNG compression method");break; + case 33: rb->splash(HZ, "illegal PNG filter method");break; + case 34: rb->splash(HZ, "illegal PNG interlace method");break; + case 35: rb->splash(HZ, "chunk length of a chunk is too large or the chunk too small");break; + case 36: rb->splash(HZ, "illegal PNG filter type encountered");break; + case 37: rb->splash(HZ, "illegal bit depth for this color type given");break; + case 38: rb->splash(HZ, "the palette is too big (more than 256 colors)");break; + case 39: rb->splash(HZ, "more palette alpha values given in tRNS, than there are colors in the palette");break; + case 40: rb->splash(HZ, "tRNS chunk has wrong size for greyscale image");break; + case 41: rb->splash(HZ, "tRNS chunk has wrong size for RGB image");break; + case 42: rb->splash(HZ, "tRNS chunk appeared while it was not allowed for this color type");break; + case 43: rb->splash(HZ, "bKGD chunk has wrong size for palette image");break; + case 44: rb->splash(HZ, "bKGD chunk has wrong size for greyscale image");break; + case 45: rb->splash(HZ, "bKGD chunk has wrong size for RGB image");break; + case 46:case 47: rb->splash(HZ, "value encountered in indexed image is larger than the palette size");break; + case 48: rb->splash(HZ, "input file is empty");break; + case 49:case 50: rb->splash(HZ, "jumped past memory while generating dynamic huffman tree");break; + case 51:case 52: rb->splash(HZ, "jumped past memory while inflating");break; + case 53: rb->splash(HZ, "size of zlib data too small");break; + case 55:case OUT_OF_MEMORY: rb->splash(HZ, "Out of Memory");break; + case 57: rb->splash(HZ, "invalid CRC");break; + case 58: rb->splash(HZ, "invalid ADLER32");break; + case 59: rb->splash(HZ, "conversion to unexisting or unsupported color type or bit depth");break; + case 63: rb->splash(HZ, "png chunk too long");break; + case 69: rb->splash(HZ, "unknown critical chunk");break; + case 73: rb->splash(HZ, "invalid tIME chunk size");break; + case 74: rb->splash(HZ, "invalid pHYs chunk size");break; + case FILE_TOO_LARGE: rb->splash(HZ, "File too large");break; + default: rb->splash(HZ, "other error");break; + } + + if (decoder.error == PLUGIN_ABORT) { + return PLUGIN_OK; + } else if (decoder.error == OUT_OF_MEMORY && entries == 1) { + return PLUGIN_ERROR; + } else { + return change_filename(direction); + } + + } + + cx = decoder.infoPng.width/ds/2; /* center the view */ + cy = decoder.infoPng.height/ds/2; + + set_view(&decoder, cx, cy); + + if(!running_slideshow) + { + rb->snprintf(print, sizeof(print), "showing %dx%d", + decoder.infoPng.width/ds, decoder.infoPng.height/ds); + rb->lcd_puts(0, 3, print); + rb->lcd_update(); + } + + rb->lcd_clear_display(); + + rb->lcd_bitmap_part(resized_image, decoder.x, decoder.y, decoder.infoPng.width/ds /*stride*/, + MAX(0, (LCD_WIDTH - (int)decoder.infoPng.width/(int)ds) / 2), + MAX(0, (LCD_HEIGHT - (int)decoder.infoPng.height/(int)ds) / 2), + MIN(LCD_WIDTH, decoder.infoPng.width/ds), + MIN(LCD_HEIGHT, decoder.infoPng.height/ds)); + + rb->lcd_update(); + + //} + //} + + /* drawing is now finished, play around with scrolling + * until you press OFF or connect USB + */ + while (1) + { + status = scroll_bmp(&decoder); + if (status == ZOOM_IN) + { + if (ds > ds_min) + { + while(1) + { + ds /= 2; /* reduce downscaling to zoom in */ + get_view(&decoder, &cx, &cy); + cx *= 2; /* prepare the position in the new image */ + cy *= 2; + if (disp[ds] != converted_image || ds <= ds_min) break; + } + } + else + continue; + } + + if (status == ZOOM_OUT) + { + if (ds < ds_max) + { + while(1) + { + ds *= 2; /* increase downscaling to zoom out */ + get_view(&decoder, &cx, &cy); + cx /= 2; /* prepare the position in the new image */ + cy /= 2; + if (disp[ds] != converted_image || ds >= ds_max) break; + } + } + else + continue; + } + break; + } + rb->lcd_clear_display(); + } + while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED + && status != PLUGIN_OTHER); + + return status; +} + +/******************** Plugin entry point *********************/ + +enum plugin_status plugin_start(const void* parameter) +{ + int condition; +#if LCD_DEPTH > 1 + old_backdrop = rb->lcd_get_backdrop(); +#endif + + if(!parameter) return PLUGIN_ERROR; + + rb->strcpy(np_file, parameter); + get_pic_list(); + + if(!entries) return PLUGIN_ERROR; + +#if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR) + if(rb->audio_status()) { + memory = (unsigned char *)rb->plugin_get_buffer((size_t *)&memory_size); + plug_buf = true; + } else { + memory = (unsigned char *)rb->plugin_get_audio_buffer((size_t *)&memory_size); + } +#else + memory = (unsigned char *)rb->plugin_get_audio_buffer((size_t *)&memory_size); +#endif + + memory += (entries * sizeof(char**)); + memory_size -= (entries * sizeof(char**)); + memory_max = memory + memory_size - 1; + + /* should be ok to just load settings since the plugin itself has + just been loaded from disk and the drive should be spinning */ + configfile_load(PNG_CONFIGFILE, png_config, + ARRAYLEN(png_config), PNG_SETTINGS_MINVERSION); + old_settings = png_settings; + + /* Turn off backlight timeout */ + backlight_force_on(); /* backlight control in lib/helper.c */ + + do + { + condition = load_and_show(np_file); + }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED + && condition != PLUGIN_ERROR); + + if (rb->memcmp(&png_settings, &old_settings, sizeof (png_settings))) + { + /* Just in case drive has to spin, keep it from looking locked */ + rb->splash(0, "Saving Settings"); + configfile_save(PNG_CONFIGFILE, png_config, + ARRAYLEN(png_config), PNG_SETTINGS_VERSION); + } + +#if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE) + /* set back ata spindown time in case we changed it */ + rb->storage_spindown(rb->global_settings->disk_spindown); +#endif + + /* Turn on backlight timeout (revert to settings) */ + backlight_use_settings(); /* backlight control in lib/helper.c */ + + return condition; +} + Property changes on: apps/plugins/png/png.c ___________________________________________________________________ Name: svn:mime-type + text/plain Index: apps/plugins/CATEGORIES =================================================================== --- apps/plugins/CATEGORIES (revision 21533) +++ apps/plugins/CATEGORIES (working copy) @@ -58,6 +58,7 @@ pegbox,games pictureflow,demos plasma,demos +png,viewers pong,games ppmviewer,viewers properties,viewers Index: apps/plugins/SUBDIRS =================================================================== --- apps/plugins/SUBDIRS (revision 21533) +++ apps/plugins/SUBDIRS (working copy) @@ -24,6 +24,9 @@ sudoku reversi goban +#if LCD_DEPTH == 16 +png +#endif #ifndef OLYMPUS_MROBE_500 zxbox #endif Index: apps/plugins/viewers.config =================================================================== --- apps/plugins/viewers.config (revision 21533) +++ apps/plugins/viewers.config (working copy) @@ -5,6 +5,7 @@ jpg,viewers/jpeg,2 jpe,viewers/jpeg,2 jpeg,viewers/jpeg,2 +png,viewers/png,2 ucl,viewers/rockbox_flash,3 rvf,viewers/video,4 mp3,viewers/vbrfix,5