diff options
author | Frank Gevaerts <frank@gevaerts.be> | 2009-07-25 21:36:11 +0000 |
---|---|---|
committer | Frank Gevaerts <frank@gevaerts.be> | 2009-07-25 21:36:11 +0000 |
commit | a175e3a42a18e3462095242700daa7229d6aa4c7 (patch) | |
tree | 4e87587d7e17850f8186a734f6211450835ad29f /apps/plugins/png/png.c | |
parent | fee24eefa9c843bac5586fb762da2d8871df3a65 (diff) | |
download | rockbox-a175e3a42a18e3462095242700daa7229d6aa4c7.tar.gz rockbox-a175e3a42a18e3462095242700daa7229d6aa4c7.zip |
Add a png viewer plugin
Author: Christophe Gouiran
Flyspray: FS#9493
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22037 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/png/png.c')
-rw-r--r-- | apps/plugins/png/png.c | 2299 |
1 files changed, 2299 insertions, 0 deletions
diff --git a/apps/plugins/png/png.c b/apps/plugins/png/png.c new file mode 100644 index 0000000000..d54d839a67 --- /dev/null +++ b/apps/plugins/png/png.c | |||
@@ -0,0 +1,2299 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$id $ | ||
9 | * | ||
10 | * Copyright (C) 2009 by Christophe Gouiran <bechris13250 -at- gmail -dot- com> | ||
11 | * | ||
12 | * Based on lodepng, a lightweight png decoder/encoder | ||
13 | * (c) 2005-2008 Lode Vandevenne | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License | ||
17 | * as published by the Free Software Foundation; either version 2 | ||
18 | * of the License, or (at your option) any later version. | ||
19 | * | ||
20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
21 | * KIND, either express or implied. | ||
22 | * | ||
23 | ****************************************************************************/ | ||
24 | |||
25 | /* | ||
26 | LodePNG version 20080927 | ||
27 | |||
28 | Copyright (c) 2005-2008 Lode Vandevenne | ||
29 | |||
30 | This software is provided 'as-is', without any express or implied | ||
31 | warranty. In no event will the authors be held liable for any damages | ||
32 | arising from the use of this software. | ||
33 | |||
34 | Permission is granted to anyone to use this software for any purpose, | ||
35 | including commercial applications, and to alter it and redistribute it | ||
36 | freely, subject to the following restrictions: | ||
37 | |||
38 | 1. The origin of this software must not be misrepresented; you must not | ||
39 | claim that you wrote the original software. If you use this software | ||
40 | in a product, an acknowledgment in the product documentation would be | ||
41 | appreciated but is not required. | ||
42 | |||
43 | 2. Altered source versions must be plainly marked as such, and must not be | ||
44 | misrepresented as being the original software. | ||
45 | |||
46 | 3. This notice may not be removed or altered from any source | ||
47 | distribution. | ||
48 | */ | ||
49 | |||
50 | /* | ||
51 | The manual and changelog can be found in the header file "lodepng.h" | ||
52 | You are free to name this file lodepng.cpp or lodepng.c depending on your usage. | ||
53 | */ | ||
54 | |||
55 | #include "plugin.h" | ||
56 | #include "lcd.h" | ||
57 | #include <lib/playback_control.h> | ||
58 | #include <lib/helper.h> | ||
59 | #include <lib/configfile.h> | ||
60 | #include <lib/grey.h> | ||
61 | #include <lib/pluginlib_bmp.h> | ||
62 | #include "zlib.h" | ||
63 | #include "png.h" | ||
64 | |||
65 | PLUGIN_HEADER | ||
66 | |||
67 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
68 | /* LodeFlate & LodeZlib Setting structs */ | ||
69 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
70 | |||
71 | typedef struct LodePNG_InfoColor /*info about the color type of an image*/ | ||
72 | { | ||
73 | /*header (IHDR)*/ | ||
74 | unsigned colorType; /*color type*/ | ||
75 | unsigned bitDepth; /*bits per sample*/ | ||
76 | |||
77 | /*palette (PLTE)*/ | ||
78 | unsigned char palette[256 * 4]; /*palette in RGBARGBA... order*/ | ||
79 | size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ | ||
80 | |||
81 | /*transparent color key (tRNS)*/ | ||
82 | unsigned key_defined; /*is a transparent color key given?*/ | ||
83 | unsigned key_r; /*red component of color key*/ | ||
84 | unsigned key_g; /*green component of color key*/ | ||
85 | unsigned key_b; /*blue component of color key*/ | ||
86 | } LodePNG_InfoColor; | ||
87 | |||
88 | 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*/ | ||
89 | { | ||
90 | unsigned year; /*2 bytes*/ | ||
91 | unsigned char month; /*1-12*/ | ||
92 | unsigned char day; /*1-31*/ | ||
93 | unsigned char hour; /*0-23*/ | ||
94 | unsigned char minute; /*0-59*/ | ||
95 | unsigned char second; /*0-60 (to allow for leap seconds)*/ | ||
96 | } LodePNG_Time; | ||
97 | |||
98 | typedef struct LodePNG_InfoPng /*information about the PNG image, except pixels and sometimes except width and height*/ | ||
99 | { | ||
100 | /*header (IHDR), palette (PLTE) and transparency (tRNS)*/ | ||
101 | unsigned width; /*width of the image in pixels (ignored by encoder, but filled in by decoder)*/ | ||
102 | unsigned height; /*height of the image in pixels (ignored by encoder, but filled in by decoder)*/ | ||
103 | unsigned compressionMethod; /*compression method of the original file*/ | ||
104 | unsigned filterMethod; /*filter method of the original file*/ | ||
105 | unsigned interlaceMethod; /*interlace method of the original file*/ | ||
106 | LodePNG_InfoColor color; /*color type and bits, palette, transparency*/ | ||
107 | |||
108 | /*suggested background color (bKGD)*/ | ||
109 | unsigned background_defined; /*is a suggested background color given?*/ | ||
110 | unsigned background_r; /*red component of suggested background color*/ | ||
111 | unsigned background_g; /*green component of suggested background color*/ | ||
112 | unsigned background_b; /*blue component of suggested background color*/ | ||
113 | |||
114 | /*time chunk (tIME)*/ | ||
115 | unsigned char time_defined; /*if 0, no tIME chunk was or will be generated in the PNG image*/ | ||
116 | LodePNG_Time time; | ||
117 | |||
118 | /*phys chunk (pHYs)*/ | ||
119 | unsigned phys_defined; /*is pHYs chunk defined?*/ | ||
120 | unsigned phys_x; | ||
121 | unsigned phys_y; | ||
122 | unsigned char phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ | ||
123 | |||
124 | } LodePNG_InfoPng; | ||
125 | |||
126 | typedef struct LodePNG_InfoRaw /*contains user-chosen information about the raw image data, which is independent of the PNG image*/ | ||
127 | { | ||
128 | LodePNG_InfoColor color; | ||
129 | } LodePNG_InfoRaw; | ||
130 | |||
131 | typedef struct LodePNG_DecodeSettings | ||
132 | { | ||
133 | unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ | ||
134 | } LodePNG_DecodeSettings; | ||
135 | |||
136 | typedef struct LodePNG_Decoder | ||
137 | { | ||
138 | LodePNG_DecodeSettings settings; | ||
139 | LodePNG_InfoRaw infoRaw; | ||
140 | LodePNG_InfoPng infoPng; /*info of the PNG image obtained after decoding*/ | ||
141 | long error; | ||
142 | char error_msg[128]; | ||
143 | int x,y; | ||
144 | } LodePNG_Decoder; | ||
145 | |||
146 | #define VERSION_STRING "20080927" | ||
147 | |||
148 | /* Headings */ | ||
149 | #define DIR_PREV 1 | ||
150 | #define DIR_NEXT -1 | ||
151 | #define DIR_NONE 0 | ||
152 | |||
153 | /* decompressed image in the possible sizes (1,2,4,8), wasting the other */ | ||
154 | static fb_data *disp[9]; | ||
155 | static fb_data *previous_disp; | ||
156 | static size_t size[9]; | ||
157 | static size_t previous_size; | ||
158 | |||
159 | /* my memory pool (from the mp3 buffer) */ | ||
160 | static char print[128]; /* use a common snprintf() buffer */ | ||
161 | |||
162 | unsigned char *memory, *memory_max; | ||
163 | static size_t memory_size; | ||
164 | |||
165 | static unsigned char *image; /* where we put the content of the file */ | ||
166 | static size_t image_size; | ||
167 | |||
168 | #if LCD_DEPTH >= 8 | ||
169 | static fb_data *converted_image __attribute__ ((aligned (16))); /* the (color) converted image */ | ||
170 | #else | ||
171 | static fb_data *converted_image; /* the (color) converted image */ | ||
172 | #endif | ||
173 | static size_t converted_image_size; | ||
174 | |||
175 | static unsigned char *decoded_image; /* the decoded image */ | ||
176 | static size_t decoded_image_size; | ||
177 | |||
178 | #if LCD_DEPTH >= 8 | ||
179 | static fb_data *resized_image __attribute__ ((aligned (16))); /* the decoded image */ | ||
180 | #else | ||
181 | static fb_data *resized_image; /* the decoded image */ | ||
182 | #endif | ||
183 | |||
184 | static struct tree_context *tree; | ||
185 | |||
186 | /* the current full file name */ | ||
187 | static char np_file[MAX_PATH]; | ||
188 | static int curfile = 0, direction = DIR_NONE, entries = 0; | ||
189 | |||
190 | static LodePNG_Decoder decoder; | ||
191 | |||
192 | /* list of the jpeg files */ | ||
193 | static char **file_pt; | ||
194 | /* are we using the plugin buffer or the audio buffer? */ | ||
195 | bool plug_buf = false; | ||
196 | |||
197 | /* Persistent configuration */ | ||
198 | #define PNG_CONFIGFILE "png.cfg" | ||
199 | #define PNG_SETTINGS_MINVERSION 1 | ||
200 | #define PNG_SETTINGS_VERSION 1 | ||
201 | |||
202 | /* Slideshow times */ | ||
203 | #define SS_MIN_TIMEOUT 1 | ||
204 | #define SS_MAX_TIMEOUT 20 | ||
205 | #define SS_DEFAULT_TIMEOUT 5 | ||
206 | |||
207 | struct png_settings | ||
208 | { | ||
209 | int ss_timeout; | ||
210 | }; | ||
211 | |||
212 | static struct png_settings png_settings = | ||
213 | { | ||
214 | SS_DEFAULT_TIMEOUT | ||
215 | }; | ||
216 | static struct png_settings old_settings; | ||
217 | |||
218 | static struct configdata png_config[] = | ||
219 | { | ||
220 | { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, | ||
221 | { .int_p = &png_settings.ss_timeout }, "Slideshow Time", NULL | ||
222 | }, | ||
223 | }; | ||
224 | |||
225 | #if LCD_DEPTH > 1 | ||
226 | static fb_data* old_backdrop; | ||
227 | #endif | ||
228 | |||
229 | #define MAX_X_SIZE LCD_WIDTH*8 | ||
230 | |||
231 | /* Min memory allowing us to use the plugin buffer | ||
232 | * and thus not stopping the music | ||
233 | * *Very* rough estimation: | ||
234 | * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes | ||
235 | * + 30k code size = 70 000 | ||
236 | * + 50k min for png = 130 000 | ||
237 | */ | ||
238 | #define MIN_MEM 130000 | ||
239 | |||
240 | static int slideshow_enabled = false; /* run slideshow */ | ||
241 | static int running_slideshow = false; /* loading image because of slideshw */ | ||
242 | #ifndef SIMULATOR | ||
243 | static int immediate_ata_off = false; /* power down disk after loading */ | ||
244 | #endif | ||
245 | |||
246 | static unsigned ds, ds_min, ds_max; /* downscaling and limits */ | ||
247 | |||
248 | /* | ||
249 | The two functions below (LodePNG_decompress and LodePNG_compress) directly call the | ||
250 | LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions | ||
251 | below, is to provide the ability to let LodePNG use a different Zlib encoder by only | ||
252 | changing the two functions below, instead of changing it inside the vareous places | ||
253 | in the other LodePNG functions. | ||
254 | |||
255 | *out must be NULL and *outsize must be 0 initially, and after the function is done, | ||
256 | *out must point to the decompressed data, *outsize must be the size of it, and must | ||
257 | be the size of the useful data in bytes, not the alloc size. | ||
258 | */ | ||
259 | |||
260 | static unsigned LodePNG_decompress(unsigned char* out, size_t* outsize, const unsigned char* in, size_t insize, char *error_msg) | ||
261 | { | ||
262 | z_stream stream; | ||
263 | int err; | ||
264 | |||
265 | error_msg = ""; | ||
266 | |||
267 | stream.next_in = (Bytef*)in; | ||
268 | stream.avail_in = (uInt)insize; | ||
269 | |||
270 | stream.next_out = out; | ||
271 | stream.avail_out = (uInt)*outsize; | ||
272 | |||
273 | stream.zalloc = (alloc_func)0; | ||
274 | stream.zfree = (free_func)0; | ||
275 | |||
276 | err = inflateInit(&stream); | ||
277 | if (err != Z_OK) return err; | ||
278 | |||
279 | err = inflate(&stream, Z_FINISH); | ||
280 | if (err != Z_STREAM_END) { | ||
281 | inflateEnd(&stream); | ||
282 | if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) | ||
283 | return Z_DATA_ERROR; | ||
284 | return err; | ||
285 | } | ||
286 | *outsize = stream.total_out; | ||
287 | |||
288 | err = inflateEnd(&stream); | ||
289 | error_msg = stream.msg; | ||
290 | return err; | ||
291 | |||
292 | } | ||
293 | |||
294 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
295 | /* / Reading and writing single bits and bytes from/to stream for LodePNG / */ | ||
296 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
297 | |||
298 | static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) | ||
299 | { | ||
300 | unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); | ||
301 | (*bitpointer)++; | ||
302 | return result; | ||
303 | } | ||
304 | |||
305 | static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) | ||
306 | { | ||
307 | unsigned result = 0; | ||
308 | size_t i; | ||
309 | for (i = nbits - 1; i < nbits; i--) result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i; | ||
310 | return result; | ||
311 | } | ||
312 | |||
313 | static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) | ||
314 | { | ||
315 | /*the current bit in bitstream must be 0 for this to work*/ | ||
316 | if (bit) bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ | ||
317 | (*bitpointer)++; | ||
318 | } | ||
319 | |||
320 | static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) | ||
321 | { | ||
322 | /*the current bit in bitstream may be 0 or 1 for this to work*/ | ||
323 | if (bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); | ||
324 | else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); | ||
325 | (*bitpointer)++; | ||
326 | } | ||
327 | |||
328 | static unsigned LodePNG_read32bitInt(const unsigned char* buffer) | ||
329 | { | ||
330 | return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; | ||
331 | } | ||
332 | |||
333 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
334 | /* / PNG chunks / */ | ||
335 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
336 | |||
337 | unsigned LodePNG_chunk_length(const unsigned char* chunk) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/ | ||
338 | { | ||
339 | return LodePNG_read32bitInt(&chunk[0]); | ||
340 | } | ||
341 | |||
342 | void LodePNG_chunk_type(char type[5], const unsigned char* chunk) /*puts the 4-byte type in null terminated string*/ | ||
343 | { | ||
344 | unsigned i; | ||
345 | for (i = 0; i < 4; i++) type[i] = chunk[4 + i]; | ||
346 | type[4] = 0; /*null termination char*/ | ||
347 | } | ||
348 | |||
349 | unsigned char LodePNG_chunk_type_equals(const unsigned char* chunk, const char* type) /*check if the type is the given type*/ | ||
350 | { | ||
351 | if (type[4] != 0) return 0; | ||
352 | return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); | ||
353 | } | ||
354 | |||
355 | /*properties of PNG chunks gotten from capitalization of chunk type name, as defined by the standard*/ | ||
356 | unsigned char LodePNG_chunk_critical(const unsigned char* chunk) /*0: ancillary chunk, 1: it's one of the critical chunk types*/ | ||
357 | { | ||
358 | return((chunk[4] & 32) == 0); | ||
359 | } | ||
360 | |||
361 | unsigned char LodePNG_chunk_private(const unsigned char* chunk) /*0: public, 1: private*/ | ||
362 | { | ||
363 | return((chunk[6] & 32) != 0); | ||
364 | } | ||
365 | |||
366 | unsigned char LodePNG_chunk_safetocopy(const unsigned char* chunk) /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy*/ | ||
367 | { | ||
368 | return((chunk[7] & 32) != 0); | ||
369 | } | ||
370 | |||
371 | unsigned char* LodePNG_chunk_data(unsigned char* chunk) /*get pointer to the data of the chunk*/ | ||
372 | { | ||
373 | return &chunk[8]; | ||
374 | } | ||
375 | |||
376 | const unsigned char* LodePNG_chunk_data_const(const unsigned char* chunk) /*get pointer to the data of the chunk*/ | ||
377 | { | ||
378 | return &chunk[8]; | ||
379 | } | ||
380 | |||
381 | unsigned LodePNG_chunk_check_crc(const unsigned char* chunk) /*returns 0 if the crc is correct, error code if it's incorrect*/ | ||
382 | { | ||
383 | unsigned length = LodePNG_chunk_length(chunk); | ||
384 | unsigned CRC = LodePNG_read32bitInt(&chunk[length + 8]); | ||
385 | unsigned checksum = crc32(0L, &chunk[4], length + 4); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ | ||
386 | if (CRC != checksum) return 1; | ||
387 | else return 0; | ||
388 | } | ||
389 | |||
390 | unsigned char* LodePNG_chunk_next(unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/ | ||
391 | { | ||
392 | unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12; | ||
393 | return &chunk[total_chunk_length]; | ||
394 | } | ||
395 | |||
396 | const unsigned char* LodePNG_chunk_next_const(const unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/ | ||
397 | { | ||
398 | unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12; | ||
399 | return &chunk[total_chunk_length]; | ||
400 | } | ||
401 | |||
402 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
403 | /* / Color types and such / */ | ||
404 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
405 | |||
406 | /*return type is a LodePNG error code*/ | ||
407 | static unsigned checkColorValidity(unsigned colorType, unsigned bd) /*bd = bitDepth*/ | ||
408 | { | ||
409 | switch (colorType) | ||
410 | { | ||
411 | case 0: | ||
412 | if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/ | ||
413 | case 2: | ||
414 | if (!( bd == 8 || bd == 16)) return 37; break; /*RGB*/ | ||
415 | case 3: | ||
416 | if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/ | ||
417 | case 4: | ||
418 | if (!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/ | ||
419 | case 6: | ||
420 | if (!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/ | ||
421 | default: | ||
422 | return 31; | ||
423 | } | ||
424 | return 0; /*allowed color type / bits combination*/ | ||
425 | } | ||
426 | |||
427 | static unsigned getNumColorChannels(unsigned colorType) | ||
428 | { | ||
429 | switch (colorType) | ||
430 | { | ||
431 | case 0: | ||
432 | return 1; /*grey*/ | ||
433 | case 2: | ||
434 | return 3; /*RGB*/ | ||
435 | case 3: | ||
436 | return 1; /*palette*/ | ||
437 | case 4: | ||
438 | return 2; /*grey + alpha*/ | ||
439 | case 6: | ||
440 | return 4; /*RGBA*/ | ||
441 | } | ||
442 | return 0; /*unexisting color type*/ | ||
443 | } | ||
444 | |||
445 | static unsigned getBpp(unsigned colorType, unsigned bitDepth) | ||
446 | { | ||
447 | return getNumColorChannels(colorType) * bitDepth; /*bits per pixel is amount of channels * bits per channel*/ | ||
448 | } | ||
449 | |||
450 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
451 | |||
452 | void LodePNG_InfoColor_init(LodePNG_InfoColor* info) | ||
453 | { | ||
454 | info->key_defined = 0; | ||
455 | info->key_r = info->key_g = info->key_b = 0; | ||
456 | info->colorType = 6; | ||
457 | info->bitDepth = 8; | ||
458 | memset(info->palette, 0, 256 * 4 * sizeof(unsigned char)); | ||
459 | info->palettesize = 0; | ||
460 | } | ||
461 | |||
462 | void LodePNG_InfoColor_cleanup(LodePNG_InfoColor* info) | ||
463 | { | ||
464 | info->palettesize = 0; | ||
465 | } | ||
466 | |||
467 | unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor* info) { return getBpp(info->colorType, info->bitDepth); } /*calculate bits per pixel out of colorType and bitDepth*/ | ||
468 | unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor* info) { return info->colorType == 0 || info->colorType == 4; } | ||
469 | |||
470 | unsigned LodePNG_InfoColor_equal(const LodePNG_InfoColor* info1, const LodePNG_InfoColor* info2) | ||
471 | { | ||
472 | return info1->colorType == info2->colorType | ||
473 | && info1->bitDepth == info2->bitDepth; /*palette and color key not compared*/ | ||
474 | } | ||
475 | |||
476 | void LodePNG_InfoPng_init(LodePNG_InfoPng* info) | ||
477 | { | ||
478 | info->width = info->height = 0; | ||
479 | LodePNG_InfoColor_init(&info->color); | ||
480 | info->interlaceMethod = 0; | ||
481 | info->compressionMethod = 0; | ||
482 | info->filterMethod = 0; | ||
483 | info->background_defined = 0; | ||
484 | info->background_r = info->background_g = info->background_b = 0; | ||
485 | |||
486 | info->time_defined = 0; | ||
487 | info->phys_defined = 0; | ||
488 | } | ||
489 | |||
490 | void LodePNG_InfoPng_cleanup(LodePNG_InfoPng* info) | ||
491 | { | ||
492 | LodePNG_InfoColor_cleanup(&info->color); | ||
493 | } | ||
494 | |||
495 | unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor* dest, const LodePNG_InfoColor* source) | ||
496 | { | ||
497 | size_t i; | ||
498 | LodePNG_InfoColor_cleanup(dest); | ||
499 | *dest = *source; | ||
500 | for (i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i]; | ||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng* dest, const LodePNG_InfoPng* source) | ||
505 | { | ||
506 | unsigned error = 0; | ||
507 | LodePNG_InfoPng_cleanup(dest); | ||
508 | *dest = *source; | ||
509 | LodePNG_InfoColor_init(&dest->color); | ||
510 | error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error; | ||
511 | return error; | ||
512 | } | ||
513 | |||
514 | void LodePNG_InfoPng_swap(LodePNG_InfoPng* a, LodePNG_InfoPng* b) | ||
515 | { | ||
516 | LodePNG_InfoPng temp = *a; | ||
517 | *a = *b; | ||
518 | *b = temp; | ||
519 | } | ||
520 | |||
521 | void LodePNG_InfoRaw_init(LodePNG_InfoRaw* info) | ||
522 | { | ||
523 | LodePNG_InfoColor_init(&info->color); | ||
524 | } | ||
525 | |||
526 | void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw* info) | ||
527 | { | ||
528 | LodePNG_InfoColor_cleanup(&info->color); | ||
529 | } | ||
530 | |||
531 | unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw* dest, const LodePNG_InfoRaw* source) | ||
532 | { | ||
533 | unsigned error = 0; | ||
534 | LodePNG_InfoRaw_cleanup(dest); | ||
535 | *dest = *source; | ||
536 | LodePNG_InfoColor_init(&dest->color); | ||
537 | error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error; | ||
538 | return error; | ||
539 | } | ||
540 | |||
541 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
542 | |||
543 | /* | ||
544 | converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code | ||
545 | 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) | ||
546 | for < 8 bpp images, there may _not_ be padding bits at the end of scanlines. | ||
547 | */ | ||
548 | unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h) | ||
549 | { | ||
550 | size_t i, j, bp = 0; /*bitpointer, used by less-than-8-bit color types*/ | ||
551 | size_t x, y; | ||
552 | unsigned char c; | ||
553 | |||
554 | if (!running_slideshow) | ||
555 | { | ||
556 | rb->snprintf(print, sizeof(print), "color conversion in progress"); | ||
557 | rb->lcd_puts(0, 3, print); | ||
558 | rb->lcd_update(); | ||
559 | } | ||
560 | |||
561 | /*cases where in and out already have the same format*/ | ||
562 | if (LodePNG_InfoColor_equal(infoIn, infoOut)) | ||
563 | { | ||
564 | |||
565 | i = 0; | ||
566 | j = 0; | ||
567 | for (y = 0 ; y < h ; y++) { | ||
568 | for (x = 0 ; x < w ; x++) { | ||
569 | unsigned char r = in[i++]; | ||
570 | unsigned char g = in[i++]; | ||
571 | unsigned char b = in[i++]; | ||
572 | out[j++] = LCD_RGBPACK(r,g,b); | ||
573 | } | ||
574 | } | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | if ((infoOut->colorType == 2 || infoOut->colorType == 6) && infoOut->bitDepth == 8) | ||
579 | { | ||
580 | if (infoIn->bitDepth == 8) | ||
581 | { | ||
582 | switch (infoIn->colorType) | ||
583 | { | ||
584 | case 0: /*greyscale color*/ | ||
585 | i = 0; | ||
586 | for (y = 0 ; y < h ; y++) { | ||
587 | for (x = 0 ; x < w ; x++) { | ||
588 | c=in[i]; | ||
589 | //unsigned char r = in[i]; | ||
590 | //unsigned char g = in[i]; | ||
591 | //unsigned char b = in[i]; | ||
592 | out[i++] = LCD_RGBPACK(c,c,c); | ||
593 | } | ||
594 | } | ||
595 | break; | ||
596 | case 2: /*RGB color*/ | ||
597 | i = 0; | ||
598 | for (y = 0 ; y < h ; y++) { | ||
599 | for (x = 0 ; x < w ; x++) { | ||
600 | j = 3 * i; | ||
601 | unsigned char r = in[j]; | ||
602 | unsigned char g = in[j + 1]; | ||
603 | unsigned char b = in[j + 2]; | ||
604 | out[i++] = LCD_RGBPACK(r,g,b); | ||
605 | } | ||
606 | } | ||
607 | break; | ||
608 | case 3: /*indexed color (palette)*/ | ||
609 | i = 0; | ||
610 | for (y = 0 ; y < h ; y++) { | ||
611 | for (x = 0 ; x < w ; x++) { | ||
612 | if (in[i] >= infoIn->palettesize) return 46; | ||
613 | j = in[i] << 2; | ||
614 | unsigned char r = infoIn->palette[j]; | ||
615 | unsigned char g = infoIn->palette[j + 1]; | ||
616 | unsigned char b = infoIn->palette[j + 2]; | ||
617 | out[i++] = LCD_RGBPACK(r,g,b); | ||
618 | } | ||
619 | } | ||
620 | break; | ||
621 | case 4: /*greyscale with alpha*/ | ||
622 | i = 0; | ||
623 | for (y = 0 ; y < h ; y++) { | ||
624 | for (x = 0 ; x < w ; x++) { | ||
625 | c = in[i << 1]; | ||
626 | //unsigned char r = in[i<<1]; | ||
627 | //unsigned char g = in[i<<1]; | ||
628 | //unsigned char b = in[i<<1]; | ||
629 | out[i++] = LCD_RGBPACK(c,c,c); | ||
630 | } | ||
631 | } | ||
632 | break; | ||
633 | case 6: /*RGB with alpha*/ | ||
634 | i = 0; | ||
635 | for (y = 0 ; y < h ; y++) { | ||
636 | for (x = 0 ; x < w ; x++) { | ||
637 | j = i << 2; | ||
638 | unsigned char r = in[j]; | ||
639 | unsigned char g = in[j + 1]; | ||
640 | unsigned char b = in[j + 2]; | ||
641 | out[i++] = LCD_RGBPACK(r,g,b); | ||
642 | } | ||
643 | } | ||
644 | break; | ||
645 | default: | ||
646 | break; | ||
647 | } | ||
648 | } | ||
649 | else if (infoIn->bitDepth == 16) | ||
650 | { | ||
651 | switch (infoIn->colorType) | ||
652 | { | ||
653 | case 0: /*greyscale color*/ | ||
654 | i = 0; | ||
655 | for (y = 0 ; y < h ; y++) { | ||
656 | for (x = 0 ; x < w ; x++) { | ||
657 | c = in[i << 1]; | ||
658 | //unsigned char r = in[2 * i]; | ||
659 | //unsigned char g = in[2 * i]; | ||
660 | //unsigned char b = in[2 * i]; | ||
661 | out[i++] = LCD_RGBPACK(c,c,c); | ||
662 | } | ||
663 | } | ||
664 | break; | ||
665 | case 2: /*RGB color*/ | ||
666 | i = 0; | ||
667 | for (y = 0 ; y < h ; y++) { | ||
668 | for (x = 0 ; x < w ; x++) { | ||
669 | j = 6 * i; | ||
670 | unsigned char r = in[j]; | ||
671 | unsigned char g = in[j + 2]; | ||
672 | unsigned char b = in[j + 4]; | ||
673 | out[i++] = LCD_RGBPACK(r,g,b); | ||
674 | } | ||
675 | } | ||
676 | break; | ||
677 | case 4: /*greyscale with alpha*/ | ||
678 | i = 0; | ||
679 | for (y = 0 ; y < h ; y++) { | ||
680 | for (x = 0 ; x < w ; x++) { | ||
681 | c = in[i << 2]; | ||
682 | //unsigned char r = in[4 * i]; | ||
683 | //unsigned char g = in[4 * i]; | ||
684 | //unsigned char b = in[4 * i]; | ||
685 | out[i++] = LCD_RGBPACK(c,c,c); | ||
686 | } | ||
687 | } | ||
688 | break; | ||
689 | case 6: /*RGB with alpha*/ | ||
690 | i = 0; | ||
691 | for (y = 0 ; y < h ; y++) { | ||
692 | for (x = 0 ; x < w ; x++) { | ||
693 | j = i << 3; | ||
694 | unsigned char r = in[j]; | ||
695 | unsigned char g = in[j + 2]; | ||
696 | unsigned char b = in[j + 4]; | ||
697 | out[i++] = LCD_RGBPACK(r,g,b); | ||
698 | } | ||
699 | } | ||
700 | break; | ||
701 | default: | ||
702 | break; | ||
703 | } | ||
704 | } | ||
705 | else /*infoIn->bitDepth is less than 8 bit per channel*/ | ||
706 | { | ||
707 | switch (infoIn->colorType) | ||
708 | { | ||
709 | case 0: /*greyscale color*/ | ||
710 | i = 0; | ||
711 | for (y = 0 ; y < h ; y++) { | ||
712 | for (x = 0 ; x < w ; x++) { | ||
713 | unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth); | ||
714 | value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/ | ||
715 | unsigned char r = (unsigned char)value; | ||
716 | unsigned char g = (unsigned char)value; | ||
717 | unsigned char b = (unsigned char)value; | ||
718 | out[i++] = LCD_RGBPACK(r,g,b); | ||
719 | } | ||
720 | } | ||
721 | break; | ||
722 | case 3: /*indexed color (palette)*/ | ||
723 | i = 0; | ||
724 | for (y = 0 ; y < h ; y++) { | ||
725 | for (x = 0 ; x < w ; x++) { | ||
726 | unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth); | ||
727 | if (value >= infoIn->palettesize) return 47; | ||
728 | j = value << 2; | ||
729 | unsigned char r = infoIn->palette[j]; | ||
730 | unsigned char g = infoIn->palette[j + 1]; | ||
731 | unsigned char b = infoIn->palette[j + 2]; | ||
732 | out[i++] = LCD_RGBPACK(r,g,b); | ||
733 | } | ||
734 | } | ||
735 | break; | ||
736 | default: | ||
737 | break; | ||
738 | } | ||
739 | } | ||
740 | } | ||
741 | else if (LodePNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/ | ||
742 | { | ||
743 | if (!LodePNG_InfoColor_isGreyscaleType(infoIn)) return 62; | ||
744 | if (infoIn->bitDepth == 8) | ||
745 | { | ||
746 | switch (infoIn->colorType) | ||
747 | { | ||
748 | case 0: /*greyscale color*/ | ||
749 | i = 0; | ||
750 | for (y = 0 ; y < h ; y++) { | ||
751 | for (x = 0 ; x < w ; x++) { | ||
752 | c = in[i]; | ||
753 | //unsigned char r = in[i]; | ||
754 | //unsigned char g = in[i]; | ||
755 | //unsigned char b = in[i]; | ||
756 | out[i++] = LCD_RGBPACK(c,c,c); | ||
757 | } | ||
758 | } | ||
759 | break; | ||
760 | case 4: /*greyscale with alpha*/ | ||
761 | i = 0; | ||
762 | for (y = 0 ; y < h ; y++) { | ||
763 | for (x = 0 ; x < w ; x++) { | ||
764 | c = in[(i << 1) + 1]; | ||
765 | //unsigned char r = in[2 * i + 1]; | ||
766 | //unsigned char g = in[2 * i + 1]; | ||
767 | //unsigned char b = in[2 * i + 1]; | ||
768 | out[i++] = LCD_RGBPACK(c,c,c); | ||
769 | } | ||
770 | } | ||
771 | break; | ||
772 | default: | ||
773 | return 31; | ||
774 | } | ||
775 | } | ||
776 | else if (infoIn->bitDepth == 16) | ||
777 | { | ||
778 | switch (infoIn->colorType) | ||
779 | { | ||
780 | case 0: /*greyscale color*/ | ||
781 | i = 0; | ||
782 | for (y = 0 ; y < h ; y++) { | ||
783 | for (x = 0 ; x < w ; x++) { | ||
784 | c = in[i << 1]; | ||
785 | //unsigned char r = in[2 * i]; | ||
786 | //unsigned char g = in[2 * i]; | ||
787 | //unsigned char b = in[2 * i]; | ||
788 | out[i++] = LCD_RGBPACK(c,c,c); | ||
789 | } | ||
790 | } | ||
791 | break; | ||
792 | case 4: /*greyscale with alpha*/ | ||
793 | i = 0; | ||
794 | for (y = 0 ; y < h ; y++) { | ||
795 | for (x = 0 ; x < w ; x++) { | ||
796 | c = in[i << 2]; | ||
797 | //unsigned char r = in[4 * i]; | ||
798 | //unsigned char g = in[4 * i]; | ||
799 | //unsigned char b = in[4 * i]; | ||
800 | out[i++] = LCD_RGBPACK(c,c,c); | ||
801 | } | ||
802 | } | ||
803 | break; | ||
804 | default: | ||
805 | return 31; | ||
806 | } | ||
807 | } | ||
808 | else /*infoIn->bitDepth is less than 8 bit per channel*/ | ||
809 | { | ||
810 | if (infoIn->colorType != 0) return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/ | ||
811 | i = 0; | ||
812 | for (y = 0 ; y < h ; y++) { | ||
813 | for (x = 0 ; x < w ; x++) { | ||
814 | unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth); | ||
815 | value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/ | ||
816 | unsigned char r = (unsigned char)value; | ||
817 | unsigned char g = (unsigned char)value; | ||
818 | unsigned char b = (unsigned char)value; | ||
819 | out[i++] = LCD_RGBPACK(r,g,b); | ||
820 | } | ||
821 | } | ||
822 | } | ||
823 | } | ||
824 | else return 59; | ||
825 | |||
826 | return 0; | ||
827 | } | ||
828 | |||
829 | /*Paeth predicter, used by PNG filter type 4*/ | ||
830 | static int paethPredictor(int a, int b, int c) | ||
831 | { | ||
832 | int p = a + b - c; | ||
833 | int pa = p > a ? p - a : a - p; | ||
834 | int pb = p > b ? p - b : b - p; | ||
835 | int pc = p > c ? p - c : c - p; | ||
836 | |||
837 | if (pa <= pb && pa <= pc) return a; | ||
838 | else if (pb <= pc) return b; | ||
839 | else return c; | ||
840 | } | ||
841 | |||
842 | /*shared values used by multiple Adam7 related functions*/ | ||
843 | |||
844 | static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ | ||
845 | static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ | ||
846 | static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ | ||
847 | static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ | ||
848 | |||
849 | 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) | ||
850 | { | ||
851 | /*the passstart values have 8 values: the 8th one actually indicates the byte after the end of the 7th (= last) pass*/ | ||
852 | unsigned i; | ||
853 | |||
854 | /*calculate width and height in pixels of each pass*/ | ||
855 | for (i = 0; i < 7; i++) | ||
856 | { | ||
857 | passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; | ||
858 | passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; | ||
859 | if (passw[i] == 0) passh[i] = 0; | ||
860 | if (passh[i] == 0) passw[i] = 0; | ||
861 | } | ||
862 | |||
863 | filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; | ||
864 | for (i = 0; i < 7; i++) | ||
865 | { | ||
866 | 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)*/ | ||
867 | 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*/ | ||
868 | passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; /*only padded at end of reduced image*/ | ||
869 | } | ||
870 | } | ||
871 | |||
872 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
873 | /* / PNG Decoder / */ | ||
874 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
875 | |||
876 | /*read the information from the header and store it in the LodePNG_Info. return value is error*/ | ||
877 | void LodePNG_inspect(LodePNG_Decoder* decoder, const unsigned char* in, size_t inlength) | ||
878 | { | ||
879 | if (inlength == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/ | ||
880 | if (inlength < 29) { decoder->error = 27; return; } /*error: the data length is smaller than the length of the header*/ | ||
881 | |||
882 | /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ | ||
883 | LodePNG_InfoPng_cleanup(&decoder->infoPng); | ||
884 | LodePNG_InfoPng_init(&decoder->infoPng); | ||
885 | decoder->error = 0; | ||
886 | |||
887 | 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*/ | ||
888 | 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!*/ | ||
889 | |||
890 | /*read the values given in the header*/ | ||
891 | decoder->infoPng.width = LodePNG_read32bitInt(&in[16]); | ||
892 | decoder->infoPng.height = LodePNG_read32bitInt(&in[20]); | ||
893 | decoder->infoPng.color.bitDepth = in[24]; | ||
894 | decoder->infoPng.color.colorType = in[25]; | ||
895 | decoder->infoPng.compressionMethod = in[26]; | ||
896 | decoder->infoPng.filterMethod = in[27]; | ||
897 | decoder->infoPng.interlaceMethod = in[28]; | ||
898 | |||
899 | unsigned CRC = LodePNG_read32bitInt(&in[29]); | ||
900 | unsigned checksum = crc32(0L, &in[12], 17); | ||
901 | if (CRC != checksum) { decoder->error = 57; return; } | ||
902 | |||
903 | if (decoder->infoPng.compressionMethod != 0) { decoder->error = 32; return; } /*error: only compression method 0 is allowed in the specification*/ | ||
904 | if (decoder->infoPng.filterMethod != 0) { decoder->error = 33; return; } /*error: only filter method 0 is allowed in the specification*/ | ||
905 | if (decoder->infoPng.interlaceMethod > 1) { decoder->error = 34; return; } /*error: only interlace methods 0 and 1 exist in the specification*/ | ||
906 | |||
907 | decoder->error = checkColorValidity(decoder->infoPng.color.colorType, decoder->infoPng.color.bitDepth); | ||
908 | } | ||
909 | |||
910 | static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length) | ||
911 | { | ||
912 | /* | ||
913 | For PNG filter method 0 | ||
914 | unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1) | ||
915 | precon is the previous unfiltered scanline, recon the result, scanline the current one | ||
916 | the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead | ||
917 | recon and scanline MAY be the same memory address! precon must be disjoint. | ||
918 | */ | ||
919 | |||
920 | size_t i; | ||
921 | switch (filterType) | ||
922 | { | ||
923 | case 0: | ||
924 | //for(i = 0; i < length; i++) recon[i] = scanline[i]; | ||
925 | memcpy(recon, scanline, length * sizeof(unsigned char)); | ||
926 | break; | ||
927 | case 1: | ||
928 | //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; | ||
929 | memcpy(recon, scanline, bytewidth * sizeof(unsigned char)); | ||
930 | for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; | ||
931 | break; | ||
932 | case 2: | ||
933 | if (precon) for (i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; | ||
934 | else //for(i = 0; i < length; i++) recon[i] = scanline[i]; | ||
935 | memcpy(recon, scanline, length * sizeof(unsigned char)); | ||
936 | break; | ||
937 | case 3: | ||
938 | if (precon) | ||
939 | { | ||
940 | for (i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; | ||
941 | for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); | ||
942 | } | ||
943 | else | ||
944 | { | ||
945 | //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; | ||
946 | memcpy(recon, scanline, bytewidth * sizeof(unsigned char)); | ||
947 | for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2; | ||
948 | } | ||
949 | break; | ||
950 | case 4: | ||
951 | if (precon) | ||
952 | { | ||
953 | for (i = 0; i < bytewidth; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(0, precon[i], 0)); | ||
954 | for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); | ||
955 | } | ||
956 | else | ||
957 | { | ||
958 | //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; | ||
959 | memcpy(recon, scanline, bytewidth * sizeof(unsigned char)); | ||
960 | for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0)); | ||
961 | } | ||
962 | break; | ||
963 | default: | ||
964 | return 36; /*error: unexisting filter type given*/ | ||
965 | } | ||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) | ||
970 | { | ||
971 | /* | ||
972 | For PNG filter method 0 | ||
973 | this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 it's called 7 times) | ||
974 | out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline | ||
975 | w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel | ||
976 | in and out are allowed to be the same memory address! | ||
977 | */ | ||
978 | |||
979 | unsigned y; | ||
980 | unsigned char* prevline = 0; | ||
981 | |||
982 | size_t bytewidth = (bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ | ||
983 | size_t linebytes = (w * bpp + 7) / 8; | ||
984 | |||
985 | for (y = 0; y < h; y++) | ||
986 | { | ||
987 | size_t outindex = linebytes * y; | ||
988 | size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ | ||
989 | unsigned char filterType = in[inindex]; | ||
990 | |||
991 | unsigned error = unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes); | ||
992 | if (error) return error; | ||
993 | |||
994 | prevline = &out[outindex]; | ||
995 | } | ||
996 | |||
997 | return 0; | ||
998 | } | ||
999 | |||
1000 | static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) | ||
1001 | { | ||
1002 | /*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 | ||
1003 | 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)*/ | ||
1004 | unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; | ||
1005 | unsigned i; | ||
1006 | |||
1007 | Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); | ||
1008 | |||
1009 | if (bpp >= 8) | ||
1010 | { | ||
1011 | for (i = 0; i < 7; i++) | ||
1012 | { | ||
1013 | unsigned x, y, b; | ||
1014 | size_t bytewidth = bpp / 8; | ||
1015 | for (y = 0; y < passh[i]; y++) | ||
1016 | for (x = 0; x < passw[i]; x++) | ||
1017 | { | ||
1018 | size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; | ||
1019 | size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; | ||
1020 | for (b = 0; b < bytewidth; b++) | ||
1021 | { | ||
1022 | out[pixeloutstart + b] = in[pixelinstart + b]; | ||
1023 | } | ||
1024 | } | ||
1025 | } | ||
1026 | } | ||
1027 | else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ | ||
1028 | { | ||
1029 | for (i = 0; i < 7; i++) | ||
1030 | { | ||
1031 | unsigned x, y, b; | ||
1032 | unsigned ilinebits = bpp * passw[i]; | ||
1033 | unsigned olinebits = bpp * w; | ||
1034 | size_t obp, ibp; /*bit pointers (for out and in buffer)*/ | ||
1035 | for (y = 0; y < passh[i]; y++) | ||
1036 | for (x = 0; x < passw[i]; x++) | ||
1037 | { | ||
1038 | ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); | ||
1039 | obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; | ||
1040 | for (b = 0; b < bpp; b++) | ||
1041 | { | ||
1042 | unsigned char bit = readBitFromReversedStream(&ibp, in); | ||
1043 | setBitOfReversedStream0(&obp, out, bit); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ | ||
1044 | } | ||
1045 | } | ||
1046 | } | ||
1047 | } | ||
1048 | } | ||
1049 | |||
1050 | static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) | ||
1051 | { | ||
1052 | /* | ||
1053 | 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. | ||
1054 | 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 | ||
1055 | also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 | ||
1056 | only useful if (ilinebits - olinebits) is a value in the range 1..7 | ||
1057 | */ | ||
1058 | unsigned y; | ||
1059 | size_t diff = ilinebits - olinebits; | ||
1060 | size_t obp = 0, ibp = 0; /*bit pointers*/ | ||
1061 | for (y = 0; y < h; y++) | ||
1062 | { | ||
1063 | size_t x; | ||
1064 | for (x = 0; x < olinebits; x++) | ||
1065 | { | ||
1066 | unsigned char bit = readBitFromReversedStream(&ibp, in); | ||
1067 | setBitOfReversedStream(&obp, out, bit); | ||
1068 | } | ||
1069 | ibp += diff; | ||
1070 | } | ||
1071 | } | ||
1072 | |||
1073 | /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks*/ | ||
1074 | static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, const LodePNG_Decoder* decoder) /*return value is error*/ | ||
1075 | { | ||
1076 | /* | ||
1077 | This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps: | ||
1078 | *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) | ||
1079 | *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace | ||
1080 | NOTE: the in buffer will be overwritten with intermediate data! | ||
1081 | */ | ||
1082 | unsigned bpp = LodePNG_InfoColor_getBpp(&decoder->infoPng.color); | ||
1083 | unsigned w = decoder->infoPng.width; | ||
1084 | unsigned h = decoder->infoPng.height; | ||
1085 | unsigned error = 0; | ||
1086 | if (bpp == 0) return 31; /*error: invalid colortype*/ | ||
1087 | |||
1088 | if (decoder->infoPng.interlaceMethod == 0) | ||
1089 | { | ||
1090 | if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) | ||
1091 | { | ||
1092 | error = unfilter(in, in, w, h, bpp); | ||
1093 | if (error) return error; | ||
1094 | removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); | ||
1095 | } | ||
1096 | else error = unfilter(out, in, w, h, bpp); /*we can immediatly filter into the out buffer, no other steps needed*/ | ||
1097 | } | ||
1098 | else /*interlaceMethod is 1 (Adam7)*/ | ||
1099 | { | ||
1100 | unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; | ||
1101 | unsigned i; | ||
1102 | |||
1103 | Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); | ||
1104 | |||
1105 | for (i = 0; i < 7; i++) | ||
1106 | { | ||
1107 | error = unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp); | ||
1108 | if (error) return error; | ||
1109 | 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*/ | ||
1110 | { | ||
1111 | /*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*/ | ||
1112 | removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7) / 8) * 8, passh[i]); | ||
1113 | } | ||
1114 | } | ||
1115 | |||
1116 | Adam7_deinterlace(out, in, w, h, bpp); | ||
1117 | } | ||
1118 | |||
1119 | return error; | ||
1120 | } | ||
1121 | |||
1122 | /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ | ||
1123 | static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t size, void (*pf_progress)(int current, int total)) | ||
1124 | { | ||
1125 | if (pf_progress != NULL) | ||
1126 | pf_progress(0, 100); | ||
1127 | unsigned char IEND = 0; | ||
1128 | const unsigned char* chunk; | ||
1129 | size_t i; | ||
1130 | unsigned char *idat = memory; | ||
1131 | size_t idat_size = 0; | ||
1132 | |||
1133 | /*for unknown chunk order*/ | ||
1134 | unsigned unknown = 0; | ||
1135 | unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ | ||
1136 | |||
1137 | /*provide some proper output values if error will happen*/ | ||
1138 | decoded_image_size = 0; | ||
1139 | |||
1140 | if (size == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/ | ||
1141 | |||
1142 | LodePNG_inspect(decoder, in, size); /*reads header and resets other parameters in decoder->infoPng*/ | ||
1143 | if (decoder->error) return; | ||
1144 | |||
1145 | chunk = &in[33]; /*first byte of the first chunk after the header*/ | ||
1146 | |||
1147 | 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*/ | ||
1148 | { | ||
1149 | unsigned chunkLength; | ||
1150 | const unsigned char* data; /*the data in the chunk*/ | ||
1151 | |||
1152 | 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*/ | ||
1153 | chunkLength = LodePNG_chunk_length(chunk); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ | ||
1154 | if (chunkLength > 2147483647) { decoder->error = 63; break; } | ||
1155 | 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*/ | ||
1156 | data = LodePNG_chunk_data_const(chunk); | ||
1157 | |||
1158 | /*IDAT chunk, containing compressed image data*/ | ||
1159 | if (LodePNG_chunk_type_equals(chunk, "IDAT")) | ||
1160 | { | ||
1161 | size_t oldsize = idat_size; | ||
1162 | idat_size += chunkLength; | ||
1163 | if (idat + idat_size >= image) { decoder->error = OUT_OF_MEMORY; break; } | ||
1164 | memcpy(idat+oldsize, data, chunkLength * sizeof(unsigned char)); | ||
1165 | critical_pos = 3; | ||
1166 | } | ||
1167 | /*IEND chunk*/ | ||
1168 | else if (LodePNG_chunk_type_equals(chunk, "IEND")) | ||
1169 | { | ||
1170 | IEND = 1; | ||
1171 | } | ||
1172 | /*palette chunk (PLTE)*/ | ||
1173 | else if (LodePNG_chunk_type_equals(chunk, "PLTE")) | ||
1174 | { | ||
1175 | unsigned pos = 0; | ||
1176 | decoder->infoPng.color.palettesize = chunkLength / 3; | ||
1177 | if (decoder->infoPng.color.palettesize > 256) { decoder->error = 38; break; } /*error: palette too big*/ | ||
1178 | for (i = 0; i < decoder->infoPng.color.palettesize; i++) | ||
1179 | { | ||
1180 | decoder->infoPng.color.palette[4 * i + 0] = data[pos++]; /*R*/ | ||
1181 | decoder->infoPng.color.palette[4 * i + 1] = data[pos++]; /*G*/ | ||
1182 | decoder->infoPng.color.palette[4 * i + 2] = data[pos++]; /*B*/ | ||
1183 | decoder->infoPng.color.palette[4 * i + 3] = 255; /*alpha*/ | ||
1184 | } | ||
1185 | critical_pos = 2; | ||
1186 | } | ||
1187 | /*palette transparency chunk (tRNS)*/ | ||
1188 | else if (LodePNG_chunk_type_equals(chunk, "tRNS")) | ||
1189 | { | ||
1190 | if (decoder->infoPng.color.colorType == 3) | ||
1191 | { | ||
1192 | if (chunkLength > decoder->infoPng.color.palettesize) { decoder->error = 39; break; } /*error: more alpha values given than there are palette entries*/ | ||
1193 | for (i = 0; i < chunkLength; i++) decoder->infoPng.color.palette[4 * i + 3] = data[i]; | ||
1194 | } | ||
1195 | else if (decoder->infoPng.color.colorType == 0) | ||
1196 | { | ||
1197 | if (chunkLength != 2) { decoder->error = 40; break; } /*error: this chunk must be 2 bytes for greyscale image*/ | ||
1198 | decoder->infoPng.color.key_defined = 1; | ||
1199 | decoder->infoPng.color.key_r = decoder->infoPng.color.key_g = decoder->infoPng.color.key_b = 256 * data[0] + data[1]; | ||
1200 | } | ||
1201 | else if (decoder->infoPng.color.colorType == 2) | ||
1202 | { | ||
1203 | if (chunkLength != 6) { decoder->error = 41; break; } /*error: this chunk must be 6 bytes for RGB image*/ | ||
1204 | decoder->infoPng.color.key_defined = 1; | ||
1205 | decoder->infoPng.color.key_r = 256 * data[0] + data[1]; | ||
1206 | decoder->infoPng.color.key_g = 256 * data[2] + data[3]; | ||
1207 | decoder->infoPng.color.key_b = 256 * data[4] + data[5]; | ||
1208 | } | ||
1209 | else { decoder->error = 42; break; } /*error: tRNS chunk not allowed for other color models*/ | ||
1210 | } | ||
1211 | /*background color chunk (bKGD)*/ | ||
1212 | else if (LodePNG_chunk_type_equals(chunk, "bKGD")) | ||
1213 | { | ||
1214 | if (decoder->infoPng.color.colorType == 3) | ||
1215 | { | ||
1216 | if (chunkLength != 1) { decoder->error = 43; break; } /*error: this chunk must be 1 byte for indexed color image*/ | ||
1217 | decoder->infoPng.background_defined = 1; | ||
1218 | decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_g = data[0]; | ||
1219 | } | ||
1220 | else if (decoder->infoPng.color.colorType == 0 || decoder->infoPng.color.colorType == 4) | ||
1221 | { | ||
1222 | if (chunkLength != 2) { decoder->error = 44; break; } /*error: this chunk must be 2 bytes for greyscale image*/ | ||
1223 | decoder->infoPng.background_defined = 1; | ||
1224 | decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_b = 256 * data[0] + data[1]; | ||
1225 | } | ||
1226 | else if (decoder->infoPng.color.colorType == 2 || decoder->infoPng.color.colorType == 6) | ||
1227 | { | ||
1228 | if (chunkLength != 6) { decoder->error = 45; break; } /*error: this chunk must be 6 bytes for greyscale image*/ | ||
1229 | decoder->infoPng.background_defined = 1; | ||
1230 | decoder->infoPng.background_r = 256 * data[0] + data[1]; | ||
1231 | decoder->infoPng.background_g = 256 * data[2] + data[3]; | ||
1232 | decoder->infoPng.background_b = 256 * data[4] + data[5]; | ||
1233 | } | ||
1234 | } | ||
1235 | else if (LodePNG_chunk_type_equals(chunk, "tIME")) | ||
1236 | { | ||
1237 | if (chunkLength != 7) { decoder->error = 73; break; } | ||
1238 | decoder->infoPng.time_defined = 1; | ||
1239 | decoder->infoPng.time.year = 256 * data[0] + data[+ 1]; | ||
1240 | decoder->infoPng.time.month = data[2]; | ||
1241 | decoder->infoPng.time.day = data[3]; | ||
1242 | decoder->infoPng.time.hour = data[4]; | ||
1243 | decoder->infoPng.time.minute = data[5]; | ||
1244 | decoder->infoPng.time.second = data[6]; | ||
1245 | } | ||
1246 | else if (LodePNG_chunk_type_equals(chunk, "pHYs")) | ||
1247 | { | ||
1248 | if (chunkLength != 9) { decoder->error = 74; break; } | ||
1249 | decoder->infoPng.phys_defined = 1; | ||
1250 | decoder->infoPng.phys_x = 16777216 * data[0] + 65536 * data[1] + 256 * data[2] + data[3]; | ||
1251 | decoder->infoPng.phys_y = 16777216 * data[4] + 65536 * data[5] + 256 * data[6] + data[7]; | ||
1252 | decoder->infoPng.phys_unit = data[8]; | ||
1253 | } | ||
1254 | else /*it's not an implemented chunk type, so ignore it: skip over the data*/ | ||
1255 | { | ||
1256 | if (LodePNG_chunk_critical(chunk)) { decoder->error = 69; break; } /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ | ||
1257 | unknown = 1; | ||
1258 | } | ||
1259 | |||
1260 | if (!unknown) /*check CRC if wanted, only on known chunk types*/ | ||
1261 | { | ||
1262 | long time = *rb->current_tick; | ||
1263 | if (LodePNG_chunk_check_crc(chunk)) { decoder->error = 57; break; } | ||
1264 | time = *rb->current_tick-time; | ||
1265 | } | ||
1266 | |||
1267 | if (!IEND) chunk = LodePNG_chunk_next_const(chunk); | ||
1268 | } | ||
1269 | |||
1270 | if (!decoder->error) | ||
1271 | { | ||
1272 | unsigned char *scanlines = idat + idat_size; | ||
1273 | size_t scanlines_size = (size_t)memory_max - idat_size + 1; | ||
1274 | long time = *rb->current_tick; | ||
1275 | decoder->error = LodePNG_decompress(scanlines, &scanlines_size, idat, idat_size, decoder->error_msg); /*decompress with the Zlib decompressor*/ | ||
1276 | if (pf_progress) pf_progress(100, 100); | ||
1277 | time = *rb->current_tick-time; | ||
1278 | |||
1279 | if (!decoder->error) | ||
1280 | { | ||
1281 | decoded_image_size = (decoder->infoPng.height * decoder->infoPng.width * LodePNG_InfoColor_getBpp(&decoder->infoPng.color) + 7) / 8; | ||
1282 | if (decoded_image_size > memory_size) { decoder->error = OUT_OF_MEMORY; return; } | ||
1283 | decoded_image = memory_max - decoded_image_size + 1; | ||
1284 | if (scanlines + scanlines_size >= decoded_image) { decoder->error = OUT_OF_MEMORY; return; } | ||
1285 | memset(decoded_image, 0, decoded_image_size * sizeof(unsigned char)); | ||
1286 | if (!running_slideshow) | ||
1287 | { | ||
1288 | rb->snprintf(print, sizeof(print), "unfiltering scanlines"); | ||
1289 | rb->lcd_puts(0, 3, print); | ||
1290 | rb->lcd_update(); | ||
1291 | } | ||
1292 | decoder->error = postProcessScanlines(decoded_image, scanlines, decoder); | ||
1293 | } | ||
1294 | } | ||
1295 | } | ||
1296 | |||
1297 | void LodePNG_decode(LodePNG_Decoder* decoder, unsigned char* in, size_t insize, void (*pf_progress)(int current, int total)) | ||
1298 | { | ||
1299 | decodeGeneric(decoder, in, insize, pf_progress); | ||
1300 | if (decoder->error) return; | ||
1301 | |||
1302 | /*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"*/ | ||
1303 | if (!(decoder->infoRaw.color.colorType == 2 || decoder->infoRaw.color.colorType == 6) && !(decoder->infoRaw.color.bitDepth == 8)) { decoder->error = 56; return; } | ||
1304 | converted_image = (fb_data *)((int)(memory + 3) & ~3); | ||
1305 | converted_image_size = FB_DATA_SZ*decoder->infoPng.width*decoder->infoPng.height; | ||
1306 | if ((unsigned char *)(converted_image + converted_image_size) >= decoded_image) { decoder->error = OUT_OF_MEMORY; } | ||
1307 | if (!decoder->error) decoder->error = LodePNG_convert(converted_image, decoded_image, &decoder->infoRaw.color, &decoder->infoPng.color, decoder->infoPng.width, decoder->infoPng.height); | ||
1308 | } | ||
1309 | |||
1310 | void LodePNG_DecodeSettings_init(LodePNG_DecodeSettings* settings) | ||
1311 | { | ||
1312 | settings->color_convert = 1; | ||
1313 | } | ||
1314 | |||
1315 | void LodePNG_Decoder_init(LodePNG_Decoder* decoder) | ||
1316 | { | ||
1317 | LodePNG_DecodeSettings_init(&decoder->settings); | ||
1318 | LodePNG_InfoRaw_init(&decoder->infoRaw); | ||
1319 | LodePNG_InfoPng_init(&decoder->infoPng); | ||
1320 | decoder->error = 1; | ||
1321 | } | ||
1322 | |||
1323 | void LodePNG_Decoder_cleanup(LodePNG_Decoder* decoder) | ||
1324 | { | ||
1325 | LodePNG_InfoRaw_cleanup(&decoder->infoRaw); | ||
1326 | LodePNG_InfoPng_cleanup(&decoder->infoPng); | ||
1327 | } | ||
1328 | |||
1329 | /* support function for qsort() */ | ||
1330 | static int compare(const void* p1, const void* p2) | ||
1331 | { | ||
1332 | return rb->strcasecmp(*((char **)p1), *((char **)p2)); | ||
1333 | } | ||
1334 | |||
1335 | bool png_ext(const char ext[]) | ||
1336 | { | ||
1337 | if (!ext) | ||
1338 | return false; | ||
1339 | if (!rb->strcasecmp(ext,".png")) | ||
1340 | return true; | ||
1341 | else | ||
1342 | return false; | ||
1343 | } | ||
1344 | |||
1345 | /*Read directory contents for scrolling. */ | ||
1346 | void get_pic_list(void) | ||
1347 | { | ||
1348 | int i; | ||
1349 | long int str_len = 0; | ||
1350 | char *pname; | ||
1351 | tree = rb->tree_get_context(); | ||
1352 | |||
1353 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
1354 | file_pt = rb->plugin_get_buffer((size_t *)&image_size); | ||
1355 | #else | ||
1356 | file_pt = rb->plugin_get_audio_buffer((size_t *)&image_size); | ||
1357 | #endif | ||
1358 | |||
1359 | for (i = 0; i < tree->filesindir; i++) | ||
1360 | { | ||
1361 | if (png_ext(rb->strrchr(&tree->name_buffer[str_len],'.'))) | ||
1362 | file_pt[entries++] = &tree->name_buffer[str_len]; | ||
1363 | |||
1364 | str_len += rb->strlen(&tree->name_buffer[str_len]) + 1; | ||
1365 | } | ||
1366 | |||
1367 | rb->qsort(file_pt, entries, sizeof(char**), compare); | ||
1368 | |||
1369 | /* Remove path and leave only the name.*/ | ||
1370 | pname = rb->strrchr(np_file,'/'); | ||
1371 | pname++; | ||
1372 | |||
1373 | /* Find Selected File. */ | ||
1374 | for (i = 0; i < entries; i++) | ||
1375 | if (!rb->strcmp(file_pt[i], pname)) | ||
1376 | curfile = i; | ||
1377 | } | ||
1378 | |||
1379 | int change_filename(int direct) | ||
1380 | { | ||
1381 | int count = 0; | ||
1382 | direction = direct; | ||
1383 | |||
1384 | if (direct == DIR_PREV) | ||
1385 | { | ||
1386 | do | ||
1387 | { | ||
1388 | count++; | ||
1389 | if (curfile == 0) | ||
1390 | curfile = entries - 1; | ||
1391 | else | ||
1392 | curfile--; | ||
1393 | }while (file_pt[curfile] == '\0' && count < entries); | ||
1394 | /* we "erase" the file name if we encounter | ||
1395 | * a non-supported file, so skip it now */ | ||
1396 | } | ||
1397 | else /* DIR_NEXT/DIR_NONE */ | ||
1398 | { | ||
1399 | do | ||
1400 | { | ||
1401 | count++; | ||
1402 | if (curfile == entries - 1) | ||
1403 | curfile = 0; | ||
1404 | else | ||
1405 | curfile++; | ||
1406 | }while (file_pt[curfile] == '\0' && count < entries); | ||
1407 | } | ||
1408 | |||
1409 | if (count == entries && file_pt[curfile] == '\0') | ||
1410 | { | ||
1411 | rb->splash(HZ, "No supported files"); | ||
1412 | return PLUGIN_ERROR; | ||
1413 | } | ||
1414 | if (rb->strlen(tree->currdir) > 1) | ||
1415 | { | ||
1416 | rb->strcpy(np_file, tree->currdir); | ||
1417 | rb->strcat(np_file, "/"); | ||
1418 | } | ||
1419 | else | ||
1420 | rb->strcpy(np_file, tree->currdir); | ||
1421 | |||
1422 | rb->strcat(np_file, file_pt[curfile]); | ||
1423 | |||
1424 | return PLUGIN_OTHER; | ||
1425 | } | ||
1426 | |||
1427 | /* switch off overlay, for handling SYS_ events */ | ||
1428 | void cleanup(void *parameter) | ||
1429 | { | ||
1430 | (void)parameter; | ||
1431 | } | ||
1432 | |||
1433 | #define VSCROLL (LCD_HEIGHT/8) | ||
1434 | #define HSCROLL (LCD_WIDTH/10) | ||
1435 | |||
1436 | #define ZOOM_IN 100 /* return codes for below function */ | ||
1437 | #define ZOOM_OUT 101 | ||
1438 | |||
1439 | int show_menu(void) /* return 1 to quit */ | ||
1440 | { | ||
1441 | #if LCD_DEPTH > 1 | ||
1442 | rb->lcd_set_backdrop(old_backdrop); | ||
1443 | #ifdef HAVE_LCD_COLOR | ||
1444 | rb->lcd_set_foreground(rb->global_settings->fg_color); | ||
1445 | rb->lcd_set_background(rb->global_settings->bg_color); | ||
1446 | #else | ||
1447 | rb->lcd_set_foreground(LCD_BLACK); | ||
1448 | rb->lcd_set_background(LCD_WHITE); | ||
1449 | #endif | ||
1450 | #endif | ||
1451 | int result; | ||
1452 | |||
1453 | enum menu_id | ||
1454 | { | ||
1455 | MIID_RETURN = 0, | ||
1456 | MIID_TOGGLE_SS_MODE, | ||
1457 | MIID_CHANGE_SS_MODE, | ||
1458 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
1459 | MIID_SHOW_PLAYBACK_MENU, | ||
1460 | #endif | ||
1461 | MIID_QUIT, | ||
1462 | }; | ||
1463 | |||
1464 | MENUITEM_STRINGLIST(menu, "Png Menu", NULL, | ||
1465 | "Return", "Toggle Slideshow Mode", | ||
1466 | "Change Slideshow Time", | ||
1467 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
1468 | "Show Playback Menu", | ||
1469 | #endif | ||
1470 | "Quit"); | ||
1471 | |||
1472 | static const struct opt_items slideshow[2] = { | ||
1473 | { "Disable", -1 }, | ||
1474 | { "Enable", -1 }, | ||
1475 | }; | ||
1476 | |||
1477 | result=rb->do_menu(&menu, NULL, NULL, false); | ||
1478 | |||
1479 | switch (result) | ||
1480 | { | ||
1481 | case MIID_RETURN: | ||
1482 | break; | ||
1483 | case MIID_TOGGLE_SS_MODE: | ||
1484 | rb->set_option("Toggle Slideshow", &slideshow_enabled, INT, | ||
1485 | slideshow , 2, NULL); | ||
1486 | break; | ||
1487 | case MIID_CHANGE_SS_MODE: | ||
1488 | rb->set_int("Slideshow Time", "s", UNIT_SEC, | ||
1489 | &png_settings.ss_timeout, NULL, 1, | ||
1490 | SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL); | ||
1491 | break; | ||
1492 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
1493 | case MIID_SHOW_PLAYBACK_MENU: | ||
1494 | if (plug_buf) | ||
1495 | { | ||
1496 | playback_control(NULL); | ||
1497 | } | ||
1498 | else | ||
1499 | { | ||
1500 | rb->splash(HZ, "Cannot restart playback"); | ||
1501 | } | ||
1502 | break; | ||
1503 | #endif | ||
1504 | case MIID_QUIT: | ||
1505 | return 1; | ||
1506 | break; | ||
1507 | } | ||
1508 | |||
1509 | #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE) | ||
1510 | /* change ata spindown time based on slideshow time setting */ | ||
1511 | immediate_ata_off = false; | ||
1512 | rb->storage_spindown(rb->global_settings->disk_spindown); | ||
1513 | |||
1514 | if (slideshow_enabled) | ||
1515 | { | ||
1516 | if (png_settings.ss_timeout < 10) | ||
1517 | { | ||
1518 | /* slideshow times < 10s keep disk spinning */ | ||
1519 | rb->storage_spindown(0); | ||
1520 | } | ||
1521 | else if (!rb->mp3_is_playing()) | ||
1522 | { | ||
1523 | /* slideshow times > 10s and not playing: ata_off after load */ | ||
1524 | immediate_ata_off = true; | ||
1525 | } | ||
1526 | } | ||
1527 | #endif | ||
1528 | #if LCD_DEPTH > 1 | ||
1529 | rb->lcd_set_backdrop(NULL); | ||
1530 | rb->lcd_set_foreground(LCD_WHITE); | ||
1531 | rb->lcd_set_background(LCD_BLACK); | ||
1532 | #endif | ||
1533 | rb->lcd_clear_display(); | ||
1534 | return 0; | ||
1535 | } | ||
1536 | |||
1537 | /* Pan the viewing window right - move image to the left and fill in | ||
1538 | the right-hand side */ | ||
1539 | static void pan_view_right(struct LodePNG_Decoder* decoder) | ||
1540 | { | ||
1541 | int move; | ||
1542 | |||
1543 | move = MIN(HSCROLL, decoder->infoPng.width/ds - decoder->x - LCD_WIDTH); | ||
1544 | if (move > 0 && decoder->infoPng.width/ds > LCD_WIDTH) | ||
1545 | { | ||
1546 | decoder->x += move; | ||
1547 | rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/, | ||
1548 | MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2), | ||
1549 | MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2), | ||
1550 | MIN(LCD_WIDTH, decoder->infoPng.width/ds), | ||
1551 | MIN(LCD_HEIGHT, decoder->infoPng.height/ds)); | ||
1552 | rb->lcd_update(); | ||
1553 | } | ||
1554 | } | ||
1555 | |||
1556 | /* Pan the viewing window left - move image to the right and fill in | ||
1557 | the left-hand side */ | ||
1558 | static void pan_view_left(struct LodePNG_Decoder* decoder) | ||
1559 | { | ||
1560 | int move; | ||
1561 | |||
1562 | move = MIN(HSCROLL, decoder->x); | ||
1563 | if (move > 0) | ||
1564 | { | ||
1565 | decoder->x -= move; | ||
1566 | rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/, | ||
1567 | MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2), | ||
1568 | MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2), | ||
1569 | MIN(LCD_WIDTH, decoder->infoPng.width/ds), | ||
1570 | MIN(LCD_HEIGHT, decoder->infoPng.height/ds)); | ||
1571 | rb->lcd_update(); | ||
1572 | } | ||
1573 | } | ||
1574 | |||
1575 | |||
1576 | /* Pan the viewing window up - move image down and fill in | ||
1577 | the top */ | ||
1578 | static void pan_view_up(struct LodePNG_Decoder* decoder) | ||
1579 | { | ||
1580 | int move; | ||
1581 | |||
1582 | move = MIN(VSCROLL, decoder->y); | ||
1583 | if (move > 0) | ||
1584 | { | ||
1585 | decoder->y -= move; | ||
1586 | rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/, | ||
1587 | MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2), | ||
1588 | MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2), | ||
1589 | MIN(LCD_WIDTH, decoder->infoPng.width/ds), | ||
1590 | MIN(LCD_HEIGHT, decoder->infoPng.height/ds)); | ||
1591 | rb->lcd_update(); | ||
1592 | } | ||
1593 | } | ||
1594 | |||
1595 | /* Pan the viewing window down - move image up and fill in | ||
1596 | the bottom */ | ||
1597 | static void pan_view_down(struct LodePNG_Decoder* decoder) | ||
1598 | { | ||
1599 | int move; | ||
1600 | |||
1601 | move = MIN(VSCROLL, decoder->infoPng.height/ds - decoder->y - LCD_HEIGHT); | ||
1602 | if (move > 0 && decoder->infoPng.height/ds > LCD_HEIGHT) | ||
1603 | { | ||
1604 | decoder->y += move; | ||
1605 | rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/, | ||
1606 | MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2), | ||
1607 | MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2), | ||
1608 | MIN(LCD_WIDTH, decoder->infoPng.width/ds), | ||
1609 | MIN(LCD_HEIGHT, decoder->infoPng.height/ds)); | ||
1610 | rb->lcd_update(); | ||
1611 | } | ||
1612 | } | ||
1613 | |||
1614 | /* interactively scroll around the image */ | ||
1615 | int scroll_bmp(struct LodePNG_Decoder* decoder) | ||
1616 | { | ||
1617 | int button; | ||
1618 | int lastbutton = 0; | ||
1619 | |||
1620 | while (true) | ||
1621 | { | ||
1622 | if (slideshow_enabled) | ||
1623 | button = rb->button_get_w_tmo(png_settings.ss_timeout * HZ); | ||
1624 | else button = rb->button_get(true); | ||
1625 | |||
1626 | running_slideshow = false; | ||
1627 | |||
1628 | switch (button) | ||
1629 | { | ||
1630 | case PNG_LEFT: | ||
1631 | if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE) | ||
1632 | return change_filename(DIR_PREV); | ||
1633 | case PNG_LEFT | BUTTON_REPEAT: | ||
1634 | pan_view_left(decoder); | ||
1635 | break; | ||
1636 | |||
1637 | case PNG_RIGHT: | ||
1638 | if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE) | ||
1639 | return change_filename(DIR_NEXT); | ||
1640 | case PNG_RIGHT | BUTTON_REPEAT: | ||
1641 | pan_view_right(decoder); | ||
1642 | break; | ||
1643 | |||
1644 | case PNG_UP: | ||
1645 | case PNG_UP | BUTTON_REPEAT: | ||
1646 | pan_view_up(decoder); | ||
1647 | break; | ||
1648 | |||
1649 | case PNG_DOWN: | ||
1650 | case PNG_DOWN | BUTTON_REPEAT: | ||
1651 | pan_view_down(decoder); | ||
1652 | break; | ||
1653 | |||
1654 | case BUTTON_NONE: | ||
1655 | if (!slideshow_enabled) | ||
1656 | break; | ||
1657 | running_slideshow = true; | ||
1658 | if (entries > 0) | ||
1659 | return change_filename(DIR_NEXT); | ||
1660 | break; | ||
1661 | |||
1662 | #ifdef PNG_SLIDE_SHOW | ||
1663 | case PNG_SLIDE_SHOW: | ||
1664 | slideshow_enabled = !slideshow_enabled; | ||
1665 | running_slideshow = slideshow_enabled; | ||
1666 | break; | ||
1667 | #endif | ||
1668 | |||
1669 | #ifdef PNG_NEXT_REPEAT | ||
1670 | case PNG_NEXT_REPEAT: | ||
1671 | #endif | ||
1672 | case PNG_NEXT: | ||
1673 | if (entries > 0) | ||
1674 | return change_filename(DIR_NEXT); | ||
1675 | break; | ||
1676 | |||
1677 | #ifdef PNG_PREVIOUS_REPEAT | ||
1678 | case PNG_PREVIOUS_REPEAT: | ||
1679 | #endif | ||
1680 | case PNG_PREVIOUS: | ||
1681 | if (entries > 0) | ||
1682 | return change_filename(DIR_PREV); | ||
1683 | break; | ||
1684 | |||
1685 | case PNG_ZOOM_IN: | ||
1686 | #ifdef PNG_ZOOM_PRE | ||
1687 | if (lastbutton != PNG_ZOOM_PRE) | ||
1688 | break; | ||
1689 | #endif | ||
1690 | return ZOOM_IN; | ||
1691 | break; | ||
1692 | |||
1693 | case PNG_ZOOM_OUT: | ||
1694 | #ifdef PNG_ZOOM_PRE | ||
1695 | if (lastbutton != PNG_ZOOM_PRE) | ||
1696 | break; | ||
1697 | #endif | ||
1698 | return ZOOM_OUT; | ||
1699 | break; | ||
1700 | |||
1701 | #ifdef PNG_RC_MENU | ||
1702 | case PNG_RC_MENU: | ||
1703 | #endif | ||
1704 | case PNG_MENU: | ||
1705 | if (show_menu() == 1) | ||
1706 | return PLUGIN_OK; | ||
1707 | else | ||
1708 | return PLUGIN_REFRESH; | ||
1709 | |||
1710 | break; | ||
1711 | default: | ||
1712 | if (rb->default_event_handler_ex(button, cleanup, NULL) | ||
1713 | == SYS_USB_CONNECTED) | ||
1714 | return PLUGIN_USB_CONNECTED; | ||
1715 | break; | ||
1716 | |||
1717 | } /* switch */ | ||
1718 | |||
1719 | if (button != BUTTON_NONE) | ||
1720 | lastbutton = button; | ||
1721 | } /* while (true) */ | ||
1722 | } | ||
1723 | |||
1724 | /* set the view to the given center point, limit if necessary */ | ||
1725 | void set_view (struct LodePNG_Decoder* decoder, int cx, int cy) | ||
1726 | { | ||
1727 | int x, y; | ||
1728 | |||
1729 | /* plain center to available width/height */ | ||
1730 | x = cx - MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2; | ||
1731 | y = cy - MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2; | ||
1732 | |||
1733 | /* limit against upper image size */ | ||
1734 | x = MIN((int)(decoder->infoPng.width/ds) - LCD_WIDTH, x); | ||
1735 | y = MIN((int)(decoder->infoPng.height/ds) - LCD_HEIGHT, y); | ||
1736 | |||
1737 | /* limit against negative side */ | ||
1738 | x = MAX(0, x); | ||
1739 | y = MAX(0, y); | ||
1740 | |||
1741 | decoder->x = x; /* set the values */ | ||
1742 | decoder->y = y; | ||
1743 | |||
1744 | } | ||
1745 | |||
1746 | /* callback updating a progress meter while PNG decoding */ | ||
1747 | void cb_progress(int current, int total) | ||
1748 | { | ||
1749 | |||
1750 | if (current & 1) rb->yield(); /* be nice to the other threads */ | ||
1751 | if (!running_slideshow) | ||
1752 | { | ||
1753 | rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-8, LCD_WIDTH, 8, total, 0, | ||
1754 | current, HORIZONTAL); | ||
1755 | rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8); | ||
1756 | } | ||
1757 | else | ||
1758 | { | ||
1759 | /* in slideshow mode, keep gui interference to a minimum */ | ||
1760 | rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-4, LCD_WIDTH, 4, total, 0, | ||
1761 | current, HORIZONTAL); | ||
1762 | rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4); | ||
1763 | } | ||
1764 | } | ||
1765 | |||
1766 | int pngmem(struct LodePNG_Decoder* decoder, int ds) | ||
1767 | { | ||
1768 | return decoder->infoPng.width * decoder->infoPng.height * FB_DATA_SZ / ds; | ||
1769 | } | ||
1770 | |||
1771 | /* how far can we zoom in without running out of memory */ | ||
1772 | int min_downscale(struct LodePNG_Decoder* decoder, int bufsize) | ||
1773 | { | ||
1774 | int downscale = 8; | ||
1775 | |||
1776 | if (pngmem(decoder, 8) > bufsize) | ||
1777 | return 0; /* error, too large, even 1:8 doesn't fit */ | ||
1778 | |||
1779 | while (downscale > 1 && pngmem(decoder, downscale/2) <= bufsize) | ||
1780 | downscale /= 2; | ||
1781 | |||
1782 | return downscale; | ||
1783 | } | ||
1784 | |||
1785 | /* how far can we zoom out, to fit image into the LCD */ | ||
1786 | unsigned max_downscale(struct LodePNG_Decoder* decoder) | ||
1787 | { | ||
1788 | unsigned downscale = 1; | ||
1789 | |||
1790 | while (downscale < 8 && (decoder->infoPng.width > LCD_WIDTH*downscale | ||
1791 | || decoder->infoPng.height > LCD_HEIGHT*downscale)) | ||
1792 | { | ||
1793 | downscale *= 2; | ||
1794 | } | ||
1795 | |||
1796 | return downscale; | ||
1797 | } | ||
1798 | |||
1799 | /* calculate the view center based on the bitmap position */ | ||
1800 | void get_view(struct LodePNG_Decoder* decoder, int* p_cx, int* p_cy) | ||
1801 | { | ||
1802 | *p_cx = decoder->x + MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2; | ||
1803 | *p_cy = decoder->y + MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2; | ||
1804 | } | ||
1805 | |||
1806 | /* return decoded or cached image */ | ||
1807 | fb_data *get_image(struct LodePNG_Decoder* decoder) | ||
1808 | { | ||
1809 | fb_data * p_disp = disp[ds]; /* short cut */ | ||
1810 | |||
1811 | if (p_disp != NULL) | ||
1812 | { | ||
1813 | DEBUGF("Found an image in cache\n"); | ||
1814 | return p_disp; /* we still have it */ | ||
1815 | } | ||
1816 | |||
1817 | if (previous_disp == NULL) { | ||
1818 | previous_disp = converted_image; | ||
1819 | previous_size = converted_image_size; | ||
1820 | } | ||
1821 | |||
1822 | size[ds] = decoder->infoPng.width * decoder->infoPng.height * FB_DATA_SZ / ds; | ||
1823 | |||
1824 | /* assign image buffer */ | ||
1825 | if (ds > 1) { | ||
1826 | if (!running_slideshow) | ||
1827 | { | ||
1828 | rb->snprintf(print, sizeof(print), "resizing %d*%d", | ||
1829 | decoder->infoPng.width/ds, decoder->infoPng.height/ds); | ||
1830 | rb->lcd_puts(0, 3, print); | ||
1831 | rb->lcd_update(); | ||
1832 | } | ||
1833 | static struct bitmap bmp_src, bmp_dst; | ||
1834 | |||
1835 | disp[ds] = (fb_data *)((int)(previous_disp + previous_size + 3) & ~3); | ||
1836 | |||
1837 | if ((unsigned char *)(disp[ds] + size[ds]) >= memory_max) { | ||
1838 | //rb->splash(HZ, "Out of Memory"); | ||
1839 | // Still display the original image which is already decoded in RAM | ||
1840 | disp[ds] = NULL; | ||
1841 | ds = 1; | ||
1842 | return converted_image; | ||
1843 | } else { | ||
1844 | bmp_src.width = decoder->infoPng.width; | ||
1845 | bmp_src.height = decoder->infoPng.height; | ||
1846 | bmp_src.data = (unsigned char *)converted_image; | ||
1847 | |||
1848 | bmp_dst.width = decoder->infoPng.width/ds; | ||
1849 | bmp_dst.height = decoder->infoPng.height/ds; | ||
1850 | bmp_dst.data = (unsigned char *)disp[ds]; | ||
1851 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
1852 | rb->cpu_boost(true); | ||
1853 | smooth_resize_bitmap(&bmp_src, &bmp_dst); | ||
1854 | rb->cpu_boost(false); | ||
1855 | #else | ||
1856 | smooth_resize_bitmap(&bmp_src, &bmp_dst); | ||
1857 | #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ | ||
1858 | } | ||
1859 | } else { | ||
1860 | disp[ds] = converted_image; | ||
1861 | } | ||
1862 | |||
1863 | |||
1864 | |||
1865 | previous_disp = disp[ds]; | ||
1866 | previous_size = size[ds]; | ||
1867 | |||
1868 | return disp[ds]; | ||
1869 | |||
1870 | |||
1871 | } | ||
1872 | |||
1873 | /* load, decode, display the image */ | ||
1874 | int load_and_show(char* filename) | ||
1875 | { | ||
1876 | int fd; | ||
1877 | int status; | ||
1878 | long time=0; /* measured ticks */ | ||
1879 | int cx=0, cy=0; /* view center */ | ||
1880 | int w, h; /* used to center output */ | ||
1881 | |||
1882 | LodePNG_Decoder_init(&decoder); | ||
1883 | |||
1884 | rb->lcd_clear_display(); | ||
1885 | |||
1886 | fd = rb->open(filename, O_RDONLY); | ||
1887 | if (fd < 0) | ||
1888 | { | ||
1889 | rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd); | ||
1890 | rb->splash(HZ, print); | ||
1891 | return PLUGIN_ERROR; | ||
1892 | } | ||
1893 | image_size = rb->filesize(fd); | ||
1894 | memset(&disp, 0, sizeof(disp)); | ||
1895 | previous_disp = NULL; | ||
1896 | previous_size = 0; | ||
1897 | |||
1898 | DEBUGF("reading file '%s'\n", filename); | ||
1899 | |||
1900 | if (!running_slideshow) { | ||
1901 | #if LCD_DEPTH > 1 | ||
1902 | rb->lcd_set_foreground(LCD_WHITE); | ||
1903 | rb->lcd_set_background(LCD_BLACK); | ||
1904 | rb->lcd_set_backdrop(NULL); | ||
1905 | #endif | ||
1906 | |||
1907 | rb->lcd_clear_display(); | ||
1908 | rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1); | ||
1909 | rb->lcd_puts(0, 0, print); | ||
1910 | rb->lcd_update(); | ||
1911 | } | ||
1912 | |||
1913 | if (rb->button_get(false) == PNG_MENU) { | ||
1914 | decoder.error = PLUGIN_ABORT; | ||
1915 | rb->close(fd); | ||
1916 | |||
1917 | } else if (image_size > memory_size) { | ||
1918 | decoder.error = FILE_TOO_LARGE; | ||
1919 | rb->close(fd); | ||
1920 | |||
1921 | } else { | ||
1922 | if (!running_slideshow) { | ||
1923 | rb->snprintf(print, sizeof(print), "loading %lu bytes", image_size); | ||
1924 | rb->lcd_puts(0, 1, print); | ||
1925 | rb->lcd_update(); | ||
1926 | } | ||
1927 | |||
1928 | image = memory_max - image_size + 1; | ||
1929 | rb->read(fd, image, image_size); | ||
1930 | rb->close(fd); | ||
1931 | |||
1932 | if (!running_slideshow) { | ||
1933 | rb->snprintf(print, sizeof(print), "decoding image"); | ||
1934 | rb->lcd_puts(0, 2, print); | ||
1935 | rb->lcd_update(); | ||
1936 | } | ||
1937 | #ifndef SIMULATOR | ||
1938 | else if (immediate_ata_off) { | ||
1939 | /* running slideshow and time is long enough: power down disk */ | ||
1940 | rb->storage_sleep(); | ||
1941 | } | ||
1942 | #endif | ||
1943 | |||
1944 | decoder.settings.color_convert = 1; | ||
1945 | decoder.infoRaw.color.colorType = 2; | ||
1946 | decoder.infoRaw.color.bitDepth = 8; | ||
1947 | |||
1948 | if (rb->button_get(false) == PNG_MENU) { | ||
1949 | decoder.error = PLUGIN_ABORT; | ||
1950 | } else { | ||
1951 | LodePNG_inspect(&decoder, image, image_size); | ||
1952 | } | ||
1953 | |||
1954 | if (!decoder.error) { | ||
1955 | |||
1956 | if (!running_slideshow) { | ||
1957 | rb->snprintf(print, sizeof(print), "image %dx%d", decoder.infoPng.width, decoder.infoPng.height); | ||
1958 | rb->lcd_puts(0, 2, print); | ||
1959 | rb->lcd_update(); | ||
1960 | } | ||
1961 | ds_max = max_downscale(&decoder); /* check display constraint */ | ||
1962 | |||
1963 | ds = ds_max; /* initials setting */ | ||
1964 | |||
1965 | if (!running_slideshow) | ||
1966 | { | ||
1967 | rb->snprintf(print, sizeof(print), "decoding %d*%d", | ||
1968 | decoder.infoPng.width, decoder.infoPng.height); | ||
1969 | rb->lcd_puts(0, 3, print); | ||
1970 | rb->lcd_update(); | ||
1971 | } | ||
1972 | |||
1973 | /* the actual decoding */ | ||
1974 | time = *rb->current_tick; | ||
1975 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
1976 | rb->cpu_boost(true); | ||
1977 | LodePNG_decode(&decoder, image, image_size, cb_progress); | ||
1978 | rb->cpu_boost(false); | ||
1979 | #else | ||
1980 | LodePNG_decode(&decoder, image, image_size, cb_progress); | ||
1981 | #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ | ||
1982 | |||
1983 | ds_min = min_downscale(&decoder, memory_max - (unsigned char*)(converted_image + converted_image_size)); /* check memory constraint */ | ||
1984 | |||
1985 | if (ds_min == 0) { | ||
1986 | // Could not resize the image | ||
1987 | ds_min = ds = ds_max = 1; | ||
1988 | } | ||
1989 | } | ||
1990 | } | ||
1991 | |||
1992 | if (decoder.error == PLUGIN_ABORT || decoder.error == FILE_TOO_LARGE) { | ||
1993 | rb->close(fd); | ||
1994 | #ifndef SIMULATOR | ||
1995 | if (immediate_ata_off) { | ||
1996 | /* running slideshow and time is long enough: power down disk */ | ||
1997 | rb->storage_sleep(); | ||
1998 | } | ||
1999 | #endif | ||
2000 | } | ||
2001 | |||
2002 | time = *rb->current_tick - time; | ||
2003 | |||
2004 | if (!running_slideshow && !decoder.error) | ||
2005 | { | ||
2006 | rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); | ||
2007 | rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ | ||
2008 | rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print); | ||
2009 | rb->lcd_update(); | ||
2010 | } | ||
2011 | |||
2012 | do { | ||
2013 | #if PLUGIN_BUFFER_SIZE >= MIN_MEM | ||
2014 | if (plug_buf && (decoder.error == FILE_TOO_LARGE || decoder.error == OUT_OF_MEMORY || decoder.error == Z_MEM_ERROR)) | ||
2015 | { | ||
2016 | rb->lcd_setfont(FONT_SYSFIXED); | ||
2017 | rb->lcd_clear_display(); | ||
2018 | rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1); | ||
2019 | rb->lcd_puts(0,0,print); | ||
2020 | rb->lcd_puts(0,1,"Not enough plugin memory!"); | ||
2021 | rb->lcd_puts(0,2,"Zoom In: Stop playback."); | ||
2022 | if (entries>1) | ||
2023 | rb->lcd_puts(0,3,"Left/Right: Skip File."); | ||
2024 | rb->lcd_puts(0,4,"Off: Quit."); | ||
2025 | rb->lcd_update(); | ||
2026 | rb->lcd_setfont(FONT_UI); | ||
2027 | |||
2028 | rb->button_clear_queue(); | ||
2029 | |||
2030 | while (1) | ||
2031 | { | ||
2032 | int button = rb->button_get(true); | ||
2033 | switch (button) | ||
2034 | { | ||
2035 | case PNG_ZOOM_IN: | ||
2036 | plug_buf = false; | ||
2037 | memory = rb->plugin_get_audio_buffer( | ||
2038 | (size_t *)&memory_size); | ||
2039 | memory += (entries * sizeof(char**)); | ||
2040 | memory_size -= (entries * sizeof(char**)); | ||
2041 | memory_max = memory + memory_size - 1; | ||
2042 | /*try again this file, now using the audio buffer */ | ||
2043 | return PLUGIN_OTHER; | ||
2044 | #ifdef PNG_RC_MENU | ||
2045 | case PNG_RC_MENU: | ||
2046 | #endif | ||
2047 | case PNG_MENU: | ||
2048 | return PLUGIN_OK; | ||
2049 | |||
2050 | case PNG_LEFT: | ||
2051 | if (entries>1) | ||
2052 | { | ||
2053 | rb->lcd_clear_display(); | ||
2054 | return change_filename(DIR_PREV); | ||
2055 | } | ||
2056 | break; | ||
2057 | |||
2058 | case PNG_RIGHT: | ||
2059 | if (entries>1) | ||
2060 | { | ||
2061 | rb->lcd_clear_display(); | ||
2062 | return change_filename(DIR_NEXT); | ||
2063 | } | ||
2064 | break; | ||
2065 | default: | ||
2066 | if (rb->default_event_handler_ex(button, cleanup, NULL) | ||
2067 | == SYS_USB_CONNECTED) | ||
2068 | return PLUGIN_USB_CONNECTED; | ||
2069 | |||
2070 | } | ||
2071 | } | ||
2072 | } | ||
2073 | //else | ||
2074 | #endif | ||
2075 | |||
2076 | if (!decoder.error) { | ||
2077 | resized_image = get_image(&decoder); /* decode or fetch from cache */ | ||
2078 | } | ||
2079 | if (decoder.error) { | ||
2080 | |||
2081 | switch (decoder.error) { | ||
2082 | case PLUGIN_ABORT: | ||
2083 | rb->splash(HZ, "aborted");break; | ||
2084 | case 27: | ||
2085 | rb->splash(HZ, "png file smaller than a png header");break; | ||
2086 | case 28: | ||
2087 | rb->splash(HZ, "incorrect png signature");break; | ||
2088 | case 29: | ||
2089 | rb->splash(HZ, "first chunk is not IHDR");break; | ||
2090 | case 30: | ||
2091 | rb->splash(HZ, "chunk length too large");break; | ||
2092 | case 31: | ||
2093 | rb->splash(HZ, "illegal PNG color type or bpp");break; | ||
2094 | case 32: | ||
2095 | rb->splash(HZ, "illegal PNG compression method");break; | ||
2096 | case 33: | ||
2097 | rb->splash(HZ, "illegal PNG filter method");break; | ||
2098 | case 34: | ||
2099 | rb->splash(HZ, "illegal PNG interlace method");break; | ||
2100 | case 35: | ||
2101 | rb->splash(HZ, "chunk length of a chunk is too large or the chunk too small");break; | ||
2102 | case 36: | ||
2103 | rb->splash(HZ, "illegal PNG filter type encountered");break; | ||
2104 | case 37: | ||
2105 | rb->splash(HZ, "illegal bit depth for this color type given");break; | ||
2106 | case 38: | ||
2107 | rb->splash(HZ, "the palette is too big (more than 256 colors)");break; | ||
2108 | case 39: | ||
2109 | rb->splash(HZ, "more palette alpha values given in tRNS, than there are colors in the palette");break; | ||
2110 | case 40: | ||
2111 | rb->splash(HZ, "tRNS chunk has wrong size for greyscale image");break; | ||
2112 | case 41: | ||
2113 | rb->splash(HZ, "tRNS chunk has wrong size for RGB image");break; | ||
2114 | case 42: | ||
2115 | rb->splash(HZ, "tRNS chunk appeared while it was not allowed for this color type");break; | ||
2116 | case 43: | ||
2117 | rb->splash(HZ, "bKGD chunk has wrong size for palette image");break; | ||
2118 | case 44: | ||
2119 | rb->splash(HZ, "bKGD chunk has wrong size for greyscale image");break; | ||
2120 | case 45: | ||
2121 | rb->splash(HZ, "bKGD chunk has wrong size for RGB image");break; | ||
2122 | case 46: | ||
2123 | case 47: | ||
2124 | rb->splash(HZ, "value encountered in indexed image is larger than the palette size");break; | ||
2125 | case 48: | ||
2126 | rb->splash(HZ, "input file is empty");break; | ||
2127 | case OUT_OF_MEMORY: | ||
2128 | case Z_MEM_ERROR: | ||
2129 | rb->splash(HZ, "Out of Memory");break; | ||
2130 | case 57: | ||
2131 | rb->splash(HZ, "invalid CRC");break; | ||
2132 | case 59: | ||
2133 | rb->splash(HZ, "conversion to unexisting or unsupported color type or bit depth");break; | ||
2134 | case 63: | ||
2135 | rb->splash(HZ, "png chunk too long");break; | ||
2136 | case 69: | ||
2137 | rb->splash(HZ, "unknown critical chunk");break; | ||
2138 | case 73: | ||
2139 | rb->splash(HZ, "invalid tIME chunk size");break; | ||
2140 | case 74: | ||
2141 | rb->splash(HZ, "invalid pHYs chunk size");break; | ||
2142 | case FILE_TOO_LARGE: | ||
2143 | rb->splash(HZ, "File too large");break; | ||
2144 | case Z_DATA_ERROR: | ||
2145 | rb->splash(HZ, decoder.error_msg);break; | ||
2146 | default: | ||
2147 | rb->splashf(HZ, "other error : %ld", decoder.error);break; | ||
2148 | } | ||
2149 | |||
2150 | if (decoder.error == PLUGIN_ABORT) { | ||
2151 | return PLUGIN_OK; | ||
2152 | } else if (decoder.error == OUT_OF_MEMORY && entries == 1) { | ||
2153 | return PLUGIN_ERROR; | ||
2154 | } else { | ||
2155 | return change_filename(direction); | ||
2156 | } | ||
2157 | |||
2158 | } | ||
2159 | |||
2160 | cx = decoder.infoPng.width/ds/2; /* center the view */ | ||
2161 | cy = decoder.infoPng.height/ds/2; | ||
2162 | |||
2163 | set_view(&decoder, cx, cy); | ||
2164 | |||
2165 | if (!running_slideshow) | ||
2166 | { | ||
2167 | rb->snprintf(print, sizeof(print), "showing %dx%d", | ||
2168 | decoder.infoPng.width/ds, decoder.infoPng.height/ds); | ||
2169 | rb->lcd_puts(0, 3, print); | ||
2170 | rb->lcd_update(); | ||
2171 | } | ||
2172 | |||
2173 | rb->lcd_clear_display(); | ||
2174 | |||
2175 | rb->lcd_bitmap_part(resized_image, decoder.x, decoder.y, decoder.infoPng.width/ds /*stride*/, | ||
2176 | MAX(0, (LCD_WIDTH - (int)decoder.infoPng.width/(int)ds) / 2), | ||
2177 | MAX(0, (LCD_HEIGHT - (int)decoder.infoPng.height/(int)ds) / 2), | ||
2178 | MIN(LCD_WIDTH, decoder.infoPng.width/ds), | ||
2179 | MIN(LCD_HEIGHT, decoder.infoPng.height/ds)); | ||
2180 | |||
2181 | rb->lcd_update(); | ||
2182 | |||
2183 | //} | ||
2184 | //} | ||
2185 | |||
2186 | /* drawing is now finished, play around with scrolling | ||
2187 | * until you press OFF or connect USB | ||
2188 | */ | ||
2189 | while (1) | ||
2190 | { | ||
2191 | status = scroll_bmp(&decoder); | ||
2192 | if (status == ZOOM_IN) | ||
2193 | { | ||
2194 | if (ds > ds_min) | ||
2195 | { | ||
2196 | while (1) | ||
2197 | { | ||
2198 | ds /= 2; /* reduce downscaling to zoom in */ | ||
2199 | get_view(&decoder, &cx, &cy); | ||
2200 | cx *= 2; /* prepare the position in the new image */ | ||
2201 | cy *= 2; | ||
2202 | if (disp[ds] != converted_image || ds <= ds_min) break; | ||
2203 | } | ||
2204 | } | ||
2205 | else | ||
2206 | continue; | ||
2207 | } | ||
2208 | |||
2209 | if (status == ZOOM_OUT) | ||
2210 | { | ||
2211 | if (ds < ds_max) | ||
2212 | { | ||
2213 | while (1) | ||
2214 | { | ||
2215 | ds *= 2; /* increase downscaling to zoom out */ | ||
2216 | get_view(&decoder, &cx, &cy); | ||
2217 | cx /= 2; /* prepare the position in the new image */ | ||
2218 | cy /= 2; | ||
2219 | if (disp[ds] != converted_image || ds >= ds_max) break; | ||
2220 | } | ||
2221 | } | ||
2222 | else | ||
2223 | continue; | ||
2224 | } | ||
2225 | break; | ||
2226 | } | ||
2227 | rb->lcd_clear_display(); | ||
2228 | } | ||
2229 | while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED | ||
2230 | && status != PLUGIN_OTHER); | ||
2231 | |||
2232 | return status; | ||
2233 | } | ||
2234 | |||
2235 | /******************** Plugin entry point *********************/ | ||
2236 | |||
2237 | enum plugin_status plugin_start(const void* parameter) | ||
2238 | { | ||
2239 | int condition; | ||
2240 | #if LCD_DEPTH > 1 | ||
2241 | old_backdrop = rb->lcd_get_backdrop(); | ||
2242 | #endif | ||
2243 | |||
2244 | if (!parameter) return PLUGIN_ERROR; | ||
2245 | |||
2246 | rb->strcpy(np_file, parameter); | ||
2247 | get_pic_list(); | ||
2248 | |||
2249 | if (!entries) return PLUGIN_ERROR; | ||
2250 | |||
2251 | #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR) | ||
2252 | if (rb->audio_status()) { | ||
2253 | memory = (unsigned char *)rb->plugin_get_buffer((size_t *)&memory_size); | ||
2254 | plug_buf = true; | ||
2255 | } else { | ||
2256 | memory = (unsigned char *)rb->plugin_get_audio_buffer((size_t *)&memory_size); | ||
2257 | } | ||
2258 | #else | ||
2259 | memory = (unsigned char *)rb->plugin_get_audio_buffer((size_t *)&memory_size); | ||
2260 | #endif | ||
2261 | |||
2262 | memory += (entries * sizeof(char**)); | ||
2263 | memory_size -= (entries * sizeof(char**)); | ||
2264 | memory_max = memory + memory_size - 1; | ||
2265 | |||
2266 | /* should be ok to just load settings since the plugin itself has | ||
2267 | just been loaded from disk and the drive should be spinning */ | ||
2268 | configfile_load(PNG_CONFIGFILE, png_config, | ||
2269 | ARRAYLEN(png_config), PNG_SETTINGS_MINVERSION); | ||
2270 | old_settings = png_settings; | ||
2271 | |||
2272 | /* Turn off backlight timeout */ | ||
2273 | backlight_force_on(); /* backlight control in lib/helper.c */ | ||
2274 | |||
2275 | do | ||
2276 | { | ||
2277 | condition = load_and_show(np_file); | ||
2278 | }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED | ||
2279 | && condition != PLUGIN_ERROR); | ||
2280 | |||
2281 | if (rb->memcmp(&png_settings, &old_settings, sizeof (png_settings))) | ||
2282 | { | ||
2283 | /* Just in case drive has to spin, keep it from looking locked */ | ||
2284 | rb->splash(0, "Saving Settings"); | ||
2285 | configfile_save(PNG_CONFIGFILE, png_config, | ||
2286 | ARRAYLEN(png_config), PNG_SETTINGS_VERSION); | ||
2287 | } | ||
2288 | |||
2289 | #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE) | ||
2290 | /* set back ata spindown time in case we changed it */ | ||
2291 | rb->storage_spindown(rb->global_settings->disk_spindown); | ||
2292 | #endif | ||
2293 | |||
2294 | /* Turn on backlight timeout (revert to settings) */ | ||
2295 | backlight_use_settings(); /* backlight control in lib/helper.c */ | ||
2296 | |||
2297 | return condition; | ||
2298 | } | ||
2299 | |||