diff options
author | Marcin Bukat <marcin.bukat@gmail.com> | 2010-10-31 12:40:49 +0000 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2010-10-31 12:40:49 +0000 |
commit | e26d0c62e07d1b1c1087a065242ed7ab1a2c522f (patch) | |
tree | b1e27ee113a94784233ca7aaf5b9d670f303b332 /apps/plugins/imageviewer/png/png.c | |
parent | c8901b3506ffb2f18798b005552c61b92b9d6a6c (diff) | |
download | rockbox-e26d0c62e07d1b1c1087a065242ed7ab1a2c522f.tar.gz rockbox-e26d0c62e07d1b1c1087a065242ed7ab1a2c522f.zip |
Fix and extend imageviewer png support. FS#11641 by me
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28413 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/imageviewer/png/png.c')
-rw-r--r-- | apps/plugins/imageviewer/png/png.c | 1413 |
1 files changed, 104 insertions, 1309 deletions
diff --git a/apps/plugins/imageviewer/png/png.c b/apps/plugins/imageviewer/png/png.c index 46430607ea..4bd988d939 100644 --- a/apps/plugins/imageviewer/png/png.c +++ b/apps/plugins/imageviewer/png/png.c | |||
@@ -12,6 +12,11 @@ | |||
12 | * Based on lodepng, a lightweight png decoder/encoder | 12 | * Based on lodepng, a lightweight png decoder/encoder |
13 | * (c) 2005-2008 Lode Vandevenne | 13 | * (c) 2005-2008 Lode Vandevenne |
14 | * | 14 | * |
15 | * Copyright (c) 2010 Marcin Bukat | ||
16 | * - pixel format conversion & transparency handling | ||
17 | * - adaptation of tinf (tiny inflate library) | ||
18 | * - code refactoring & cleanups | ||
19 | * | ||
15 | * This program is free software; you can redistribute it and/or | 20 | * This program is free software; you can redistribute it and/or |
16 | * modify it under the terms of the GNU General Public License | 21 | * modify it under the terms of the GNU General Public License |
17 | * as published by the Free Software Foundation; either version 2 | 22 | * as published by the Free Software Foundation; either version 2 |
@@ -22,1279 +27,29 @@ | |||
22 | * | 27 | * |
23 | ****************************************************************************/ | 28 | ****************************************************************************/ |
24 | 29 | ||
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" | 30 | #include "plugin.h" |
56 | #include "lcd.h" | 31 | #include "lcd.h" |
57 | #include <lib/pluginlib_bmp.h> | 32 | #include <lib/pluginlib_bmp.h> |
58 | #include "zlib.h" | 33 | #include "tinf.h" |
59 | #include "png.h" | 34 | #include "png.h" |
35 | #include "png_decoder.h" | ||
36 | #include "bmp.h" | ||
60 | 37 | ||
61 | /* ////////////////////////////////////////////////////////////////////////// */ | 38 | /* decoder context struct */ |
62 | /* LodeFlate & LodeZlib Setting structs */ | 39 | static LodePNG_Decoder decoder; |
63 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
64 | |||
65 | typedef struct LodePNG_InfoColor /*info about the color type of an image*/ | ||
66 | { | ||
67 | /*header (IHDR)*/ | ||
68 | unsigned colorType; /*color type*/ | ||
69 | unsigned bitDepth; /*bits per sample*/ | ||
70 | |||
71 | /*palette (PLTE)*/ | ||
72 | unsigned char palette[256 * 4]; /*palette in RGBARGBA... order*/ | ||
73 | size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ | ||
74 | 40 | ||
75 | /*transparent color key (tRNS)*/ | 41 | /* my memory pool (from the mp3 buffer) */ |
76 | unsigned key_defined; /*is a transparent color key given?*/ | 42 | static char print[128]; /* use a common snprintf() buffer */ |
77 | unsigned key_r; /*red component of color key*/ | ||
78 | unsigned key_g; /*green component of color key*/ | ||
79 | unsigned key_b; /*blue component of color key*/ | ||
80 | } LodePNG_InfoColor; | ||
81 | |||
82 | 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*/ | ||
83 | { | ||
84 | unsigned year; /*2 bytes*/ | ||
85 | unsigned char month; /*1-12*/ | ||
86 | unsigned char day; /*1-31*/ | ||
87 | unsigned char hour; /*0-23*/ | ||
88 | unsigned char minute; /*0-59*/ | ||
89 | unsigned char second; /*0-60 (to allow for leap seconds)*/ | ||
90 | } LodePNG_Time; | ||
91 | |||
92 | typedef struct LodePNG_InfoPng /*information about the PNG image, except pixels and sometimes except width and height*/ | ||
93 | { | ||
94 | /*header (IHDR), palette (PLTE) and transparency (tRNS)*/ | ||
95 | unsigned width; /*width of the image in pixels (ignored by encoder, but filled in by decoder)*/ | ||
96 | unsigned height; /*height of the image in pixels (ignored by encoder, but filled in by decoder)*/ | ||
97 | unsigned compressionMethod; /*compression method of the original file*/ | ||
98 | unsigned filterMethod; /*filter method of the original file*/ | ||
99 | unsigned interlaceMethod; /*interlace method of the original file*/ | ||
100 | LodePNG_InfoColor color; /*color type and bits, palette, transparency*/ | ||
101 | |||
102 | /*suggested background color (bKGD)*/ | ||
103 | unsigned background_defined; /*is a suggested background color given?*/ | ||
104 | unsigned background_r; /*red component of suggested background color*/ | ||
105 | unsigned background_g; /*green component of suggested background color*/ | ||
106 | unsigned background_b; /*blue component of suggested background color*/ | ||
107 | |||
108 | /*time chunk (tIME)*/ | ||
109 | unsigned char time_defined; /*if 0, no tIME chunk was or will be generated in the PNG image*/ | ||
110 | LodePNG_Time time; | ||
111 | |||
112 | /*phys chunk (pHYs)*/ | ||
113 | unsigned phys_defined; /*is pHYs chunk defined?*/ | ||
114 | unsigned phys_x; | ||
115 | unsigned phys_y; | ||
116 | unsigned char phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ | ||
117 | |||
118 | } LodePNG_InfoPng; | ||
119 | |||
120 | typedef struct LodePNG_InfoRaw /*contains user-chosen information about the raw image data, which is independent of the PNG image*/ | ||
121 | { | ||
122 | LodePNG_InfoColor color; | ||
123 | } LodePNG_InfoRaw; | ||
124 | |||
125 | typedef struct LodePNG_DecodeSettings | ||
126 | { | ||
127 | unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ | ||
128 | } LodePNG_DecodeSettings; | ||
129 | |||
130 | typedef struct LodePNG_Decoder | ||
131 | { | ||
132 | LodePNG_DecodeSettings settings; | ||
133 | LodePNG_InfoRaw infoRaw; | ||
134 | LodePNG_InfoPng infoPng; /*info of the PNG image obtained after decoding*/ | ||
135 | long error; | ||
136 | char error_msg[128]; | ||
137 | } LodePNG_Decoder; | ||
138 | |||
139 | #define VERSION_STRING "20080927" | ||
140 | 43 | ||
141 | /* decompressed image in the possible sizes (1,2,4,8), wasting the other */ | 44 | /* decompressed image in the possible sizes (1,2,4,8), wasting the other */ |
142 | static fb_data *disp[9]; | 45 | static fb_data *disp[9]; |
143 | /* up to here currently used by image(s) */ | ||
144 | static fb_data *disp_buf; | 46 | static fb_data *disp_buf; |
145 | 47 | ||
146 | /* my memory pool (from the mp3 buffer) */ | 48 | #if defined(HAVE_LCD_COLOR) |
147 | static char print[32]; /* use a common snprintf() buffer */ | 49 | #define resize_bitmap smooth_resize_bitmap |
148 | 50 | #else | |
149 | unsigned char *memory, *memory_max; /* inffast.c needs memory_max */ | 51 | #define resize_bitmap grey_resize_bitmap |
150 | static size_t memory_size; | 52 | #endif |
151 | |||
152 | static unsigned char *image; /* where we put the content of the file */ | ||
153 | static size_t image_size; | ||
154 | |||
155 | static fb_data *converted_image; /* the (color) converted image */ | ||
156 | static size_t converted_image_size; | ||
157 | |||
158 | static unsigned char *decoded_image; /* the decoded image */ | ||
159 | static size_t decoded_image_size; | ||
160 | |||
161 | static LodePNG_Decoder _decoder; | ||
162 | |||
163 | /* | ||
164 | The two functions below (LodePNG_decompress and LodePNG_compress) directly call the | ||
165 | LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions | ||
166 | below, is to provide the ability to let LodePNG use a different Zlib encoder by only | ||
167 | changing the two functions below, instead of changing it inside the vareous places | ||
168 | in the other LodePNG functions. | ||
169 | |||
170 | *out must be NULL and *outsize must be 0 initially, and after the function is done, | ||
171 | *out must point to the decompressed data, *outsize must be the size of it, and must | ||
172 | be the size of the useful data in bytes, not the alloc size. | ||
173 | */ | ||
174 | |||
175 | static unsigned LodePNG_decompress(unsigned char* out, size_t* outsize, const unsigned char* in, size_t insize, char *error_msg) | ||
176 | { | ||
177 | z_stream stream; | ||
178 | int err; | ||
179 | |||
180 | rb->strcpy(error_msg, ""); | ||
181 | |||
182 | stream.next_in = (Bytef*)in; | ||
183 | stream.avail_in = (uInt)insize; | ||
184 | |||
185 | stream.next_out = out; | ||
186 | stream.avail_out = (uInt)*outsize; | ||
187 | |||
188 | stream.zalloc = (alloc_func)0; | ||
189 | stream.zfree = (free_func)0; | ||
190 | |||
191 | err = inflateInit(&stream); | ||
192 | if (err != Z_OK) return err; | ||
193 | |||
194 | err = inflate(&stream, Z_FINISH); | ||
195 | if (err != Z_STREAM_END) { | ||
196 | inflateEnd(&stream); | ||
197 | if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) | ||
198 | return Z_DATA_ERROR; | ||
199 | return err; | ||
200 | } | ||
201 | *outsize = stream.total_out; | ||
202 | |||
203 | err = inflateEnd(&stream); | ||
204 | if (stream.msg != Z_NULL) | ||
205 | rb->strcpy(error_msg, stream.msg); | ||
206 | return err; | ||
207 | } | ||
208 | |||
209 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
210 | /* / Reading and writing single bits and bytes from/to stream for LodePNG / */ | ||
211 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
212 | |||
213 | static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) | ||
214 | { | ||
215 | unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); | ||
216 | (*bitpointer)++; | ||
217 | return result; | ||
218 | } | ||
219 | |||
220 | static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) | ||
221 | { | ||
222 | unsigned result = 0; | ||
223 | size_t i; | ||
224 | for (i = nbits - 1; i < nbits; i--) result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i; | ||
225 | return result; | ||
226 | } | ||
227 | |||
228 | static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) | ||
229 | { | ||
230 | /*the current bit in bitstream must be 0 for this to work*/ | ||
231 | if (bit) bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ | ||
232 | (*bitpointer)++; | ||
233 | } | ||
234 | |||
235 | static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) | ||
236 | { | ||
237 | /*the current bit in bitstream may be 0 or 1 for this to work*/ | ||
238 | if (bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); | ||
239 | else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); | ||
240 | (*bitpointer)++; | ||
241 | } | ||
242 | |||
243 | static unsigned LodePNG_read32bitInt(const unsigned char* buffer) | ||
244 | { | ||
245 | return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; | ||
246 | } | ||
247 | |||
248 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
249 | /* / PNG chunks / */ | ||
250 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
251 | |||
252 | unsigned LodePNG_chunk_length(const unsigned char* chunk) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/ | ||
253 | { | ||
254 | return LodePNG_read32bitInt(&chunk[0]); | ||
255 | } | ||
256 | |||
257 | void LodePNG_chunk_type(char type[5], const unsigned char* chunk) /*puts the 4-byte type in null terminated string*/ | ||
258 | { | ||
259 | unsigned i; | ||
260 | for (i = 0; i < 4; i++) type[i] = chunk[4 + i]; | ||
261 | type[4] = 0; /*null termination char*/ | ||
262 | } | ||
263 | |||
264 | unsigned char LodePNG_chunk_type_equals(const unsigned char* chunk, const char* type) /*check if the type is the given type*/ | ||
265 | { | ||
266 | if (type[4] != 0) return 0; | ||
267 | return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); | ||
268 | } | ||
269 | |||
270 | /*properties of PNG chunks gotten from capitalization of chunk type name, as defined by the standard*/ | ||
271 | unsigned char LodePNG_chunk_critical(const unsigned char* chunk) /*0: ancillary chunk, 1: it's one of the critical chunk types*/ | ||
272 | { | ||
273 | return((chunk[4] & 32) == 0); | ||
274 | } | ||
275 | |||
276 | unsigned char LodePNG_chunk_private(const unsigned char* chunk) /*0: public, 1: private*/ | ||
277 | { | ||
278 | return((chunk[6] & 32) != 0); | ||
279 | } | ||
280 | |||
281 | unsigned char LodePNG_chunk_safetocopy(const unsigned char* chunk) /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy*/ | ||
282 | { | ||
283 | return((chunk[7] & 32) != 0); | ||
284 | } | ||
285 | |||
286 | unsigned char* LodePNG_chunk_data(unsigned char* chunk) /*get pointer to the data of the chunk*/ | ||
287 | { | ||
288 | return &chunk[8]; | ||
289 | } | ||
290 | |||
291 | const unsigned char* LodePNG_chunk_data_const(const unsigned char* chunk) /*get pointer to the data of the chunk*/ | ||
292 | { | ||
293 | return &chunk[8]; | ||
294 | } | ||
295 | |||
296 | unsigned LodePNG_chunk_check_crc(const unsigned char* chunk) /*returns 0 if the crc is correct, error code if it's incorrect*/ | ||
297 | { | ||
298 | unsigned length = LodePNG_chunk_length(chunk); | ||
299 | unsigned CRC = LodePNG_read32bitInt(&chunk[length + 8]); | ||
300 | unsigned checksum = crc32(0L, &chunk[4], length + 4); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ | ||
301 | if (CRC != checksum) return 1; | ||
302 | else return 0; | ||
303 | } | ||
304 | |||
305 | unsigned char* LodePNG_chunk_next(unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/ | ||
306 | { | ||
307 | unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12; | ||
308 | return &chunk[total_chunk_length]; | ||
309 | } | ||
310 | |||
311 | const unsigned char* LodePNG_chunk_next_const(const unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/ | ||
312 | { | ||
313 | unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12; | ||
314 | return &chunk[total_chunk_length]; | ||
315 | } | ||
316 | |||
317 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
318 | /* / Color types and such / */ | ||
319 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
320 | |||
321 | /*return type is a LodePNG error code*/ | ||
322 | static unsigned checkColorValidity(unsigned colorType, unsigned bd) /*bd = bitDepth*/ | ||
323 | { | ||
324 | switch (colorType) | ||
325 | { | ||
326 | case 0: | ||
327 | if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/ | ||
328 | case 2: | ||
329 | if (!( bd == 8 || bd == 16)) return 37; break; /*RGB*/ | ||
330 | case 3: | ||
331 | if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/ | ||
332 | case 4: | ||
333 | if (!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/ | ||
334 | case 6: | ||
335 | if (!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/ | ||
336 | default: | ||
337 | return 31; | ||
338 | } | ||
339 | return 0; /*allowed color type / bits combination*/ | ||
340 | } | ||
341 | |||
342 | static unsigned getNumColorChannels(unsigned colorType) | ||
343 | { | ||
344 | switch (colorType) | ||
345 | { | ||
346 | case 0: | ||
347 | return 1; /*grey*/ | ||
348 | case 2: | ||
349 | return 3; /*RGB*/ | ||
350 | case 3: | ||
351 | return 1; /*palette*/ | ||
352 | case 4: | ||
353 | return 2; /*grey + alpha*/ | ||
354 | case 6: | ||
355 | return 4; /*RGBA*/ | ||
356 | } | ||
357 | return 0; /*unexisting color type*/ | ||
358 | } | ||
359 | |||
360 | static unsigned getBpp(unsigned colorType, unsigned bitDepth) | ||
361 | { | ||
362 | return getNumColorChannels(colorType) * bitDepth; /*bits per pixel is amount of channels * bits per channel*/ | ||
363 | } | ||
364 | |||
365 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
366 | |||
367 | void LodePNG_InfoColor_init(LodePNG_InfoColor* info) | ||
368 | { | ||
369 | info->key_defined = 0; | ||
370 | info->key_r = info->key_g = info->key_b = 0; | ||
371 | info->colorType = 6; | ||
372 | info->bitDepth = 8; | ||
373 | memset(info->palette, 0, 256 * 4 * sizeof(unsigned char)); | ||
374 | info->palettesize = 0; | ||
375 | } | ||
376 | |||
377 | void LodePNG_InfoColor_cleanup(LodePNG_InfoColor* info) | ||
378 | { | ||
379 | info->palettesize = 0; | ||
380 | } | ||
381 | |||
382 | unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor* info) { return getBpp(info->colorType, info->bitDepth); } /*calculate bits per pixel out of colorType and bitDepth*/ | ||
383 | unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor* info) { return info->colorType == 0 || info->colorType == 4; } | ||
384 | |||
385 | unsigned LodePNG_InfoColor_equal(const LodePNG_InfoColor* info1, const LodePNG_InfoColor* info2) | ||
386 | { | ||
387 | return info1->colorType == info2->colorType | ||
388 | && info1->bitDepth == info2->bitDepth; /*palette and color key not compared*/ | ||
389 | } | ||
390 | |||
391 | void LodePNG_InfoPng_init(LodePNG_InfoPng* info) | ||
392 | { | ||
393 | info->width = info->height = 0; | ||
394 | LodePNG_InfoColor_init(&info->color); | ||
395 | info->interlaceMethod = 0; | ||
396 | info->compressionMethod = 0; | ||
397 | info->filterMethod = 0; | ||
398 | info->background_defined = 0; | ||
399 | info->background_r = info->background_g = info->background_b = 0; | ||
400 | |||
401 | info->time_defined = 0; | ||
402 | info->phys_defined = 0; | ||
403 | } | ||
404 | |||
405 | void LodePNG_InfoPng_cleanup(LodePNG_InfoPng* info) | ||
406 | { | ||
407 | LodePNG_InfoColor_cleanup(&info->color); | ||
408 | } | ||
409 | |||
410 | unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor* dest, const LodePNG_InfoColor* source) | ||
411 | { | ||
412 | size_t i; | ||
413 | LodePNG_InfoColor_cleanup(dest); | ||
414 | *dest = *source; | ||
415 | for (i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i]; | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng* dest, const LodePNG_InfoPng* source) | ||
420 | { | ||
421 | unsigned error = 0; | ||
422 | LodePNG_InfoPng_cleanup(dest); | ||
423 | *dest = *source; | ||
424 | LodePNG_InfoColor_init(&dest->color); | ||
425 | error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error; | ||
426 | return error; | ||
427 | } | ||
428 | |||
429 | void LodePNG_InfoPng_swap(LodePNG_InfoPng* a, LodePNG_InfoPng* b) | ||
430 | { | ||
431 | LodePNG_InfoPng temp = *a; | ||
432 | *a = *b; | ||
433 | *b = temp; | ||
434 | } | ||
435 | |||
436 | void LodePNG_InfoRaw_init(LodePNG_InfoRaw* info) | ||
437 | { | ||
438 | LodePNG_InfoColor_init(&info->color); | ||
439 | } | ||
440 | |||
441 | void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw* info) | ||
442 | { | ||
443 | LodePNG_InfoColor_cleanup(&info->color); | ||
444 | } | ||
445 | |||
446 | unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw* dest, const LodePNG_InfoRaw* source) | ||
447 | { | ||
448 | unsigned error = 0; | ||
449 | LodePNG_InfoRaw_cleanup(dest); | ||
450 | *dest = *source; | ||
451 | LodePNG_InfoColor_init(&dest->color); | ||
452 | error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error; | ||
453 | return error; | ||
454 | } | ||
455 | |||
456 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
457 | |||
458 | /* | ||
459 | converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code | ||
460 | 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) | ||
461 | for < 8 bpp images, there may _not_ be padding bits at the end of scanlines. | ||
462 | */ | ||
463 | unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h) | ||
464 | { | ||
465 | size_t i, j, bp = 0; /*bitpointer, used by less-than-8-bit color types*/ | ||
466 | size_t x, y; | ||
467 | unsigned char c; | ||
468 | |||
469 | if (!running_slideshow) | ||
470 | { | ||
471 | rb->lcd_puts(0, 3, "color conversion in progress"); | ||
472 | rb->lcd_update(); | ||
473 | } | ||
474 | |||
475 | /*cases where in and out already have the same format*/ | ||
476 | if (LodePNG_InfoColor_equal(infoIn, infoOut)) | ||
477 | { | ||
478 | |||
479 | i = 0; | ||
480 | j = 0; | ||
481 | for (y = 0 ; y < h ; y++) { | ||
482 | for (x = 0 ; x < w ; x++) { | ||
483 | unsigned char r = in[i++]; | ||
484 | unsigned char g = in[i++]; | ||
485 | unsigned char b = in[i++]; | ||
486 | out[j++] = LCD_RGBPACK(r,g,b); | ||
487 | } | ||
488 | } | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | if ((infoOut->colorType == 2 || infoOut->colorType == 6) && infoOut->bitDepth == 8) | ||
493 | { | ||
494 | if (infoIn->bitDepth == 8) | ||
495 | { | ||
496 | switch (infoIn->colorType) | ||
497 | { | ||
498 | case 0: /*greyscale color*/ | ||
499 | i = 0; | ||
500 | for (y = 0 ; y < h ; y++) { | ||
501 | for (x = 0 ; x < w ; x++) { | ||
502 | c=in[i]; | ||
503 | //unsigned char r = in[i]; | ||
504 | //unsigned char g = in[i]; | ||
505 | //unsigned char b = in[i]; | ||
506 | out[i++] = LCD_RGBPACK(c,c,c); | ||
507 | } | ||
508 | } | ||
509 | break; | ||
510 | case 2: /*RGB color*/ | ||
511 | i = 0; | ||
512 | for (y = 0 ; y < h ; y++) { | ||
513 | for (x = 0 ; x < w ; x++) { | ||
514 | j = 3 * i; | ||
515 | unsigned char r = in[j]; | ||
516 | unsigned char g = in[j + 1]; | ||
517 | unsigned char b = in[j + 2]; | ||
518 | out[i++] = LCD_RGBPACK(r,g,b); | ||
519 | } | ||
520 | } | ||
521 | break; | ||
522 | case 3: /*indexed color (palette)*/ | ||
523 | i = 0; | ||
524 | for (y = 0 ; y < h ; y++) { | ||
525 | for (x = 0 ; x < w ; x++) { | ||
526 | if (in[i] >= infoIn->palettesize) return 46; | ||
527 | j = in[i] << 2; | ||
528 | unsigned char r = infoIn->palette[j]; | ||
529 | unsigned char g = infoIn->palette[j + 1]; | ||
530 | unsigned char b = infoIn->palette[j + 2]; | ||
531 | out[i++] = LCD_RGBPACK(r,g,b); | ||
532 | } | ||
533 | } | ||
534 | break; | ||
535 | case 4: /*greyscale with alpha*/ | ||
536 | i = 0; | ||
537 | for (y = 0 ; y < h ; y++) { | ||
538 | for (x = 0 ; x < w ; x++) { | ||
539 | c = in[i << 1]; | ||
540 | //unsigned char r = in[i<<1]; | ||
541 | //unsigned char g = in[i<<1]; | ||
542 | //unsigned char b = in[i<<1]; | ||
543 | out[i++] = LCD_RGBPACK(c,c,c); | ||
544 | } | ||
545 | } | ||
546 | break; | ||
547 | case 6: /*RGB with alpha*/ | ||
548 | i = 0; | ||
549 | for (y = 0 ; y < h ; y++) { | ||
550 | for (x = 0 ; x < w ; x++) { | ||
551 | j = i << 2; | ||
552 | unsigned char r = in[j]; | ||
553 | unsigned char g = in[j + 1]; | ||
554 | unsigned char b = in[j + 2]; | ||
555 | out[i++] = LCD_RGBPACK(r,g,b); | ||
556 | } | ||
557 | } | ||
558 | break; | ||
559 | default: | ||
560 | break; | ||
561 | } | ||
562 | } | ||
563 | else if (infoIn->bitDepth == 16) | ||
564 | { | ||
565 | switch (infoIn->colorType) | ||
566 | { | ||
567 | case 0: /*greyscale color*/ | ||
568 | i = 0; | ||
569 | for (y = 0 ; y < h ; y++) { | ||
570 | for (x = 0 ; x < w ; x++) { | ||
571 | c = in[i << 1]; | ||
572 | //unsigned char r = in[2 * i]; | ||
573 | //unsigned char g = in[2 * i]; | ||
574 | //unsigned char b = in[2 * i]; | ||
575 | out[i++] = LCD_RGBPACK(c,c,c); | ||
576 | } | ||
577 | } | ||
578 | break; | ||
579 | case 2: /*RGB color*/ | ||
580 | i = 0; | ||
581 | for (y = 0 ; y < h ; y++) { | ||
582 | for (x = 0 ; x < w ; x++) { | ||
583 | j = 6 * i; | ||
584 | unsigned char r = in[j]; | ||
585 | unsigned char g = in[j + 2]; | ||
586 | unsigned char b = in[j + 4]; | ||
587 | out[i++] = LCD_RGBPACK(r,g,b); | ||
588 | } | ||
589 | } | ||
590 | break; | ||
591 | case 4: /*greyscale with alpha*/ | ||
592 | i = 0; | ||
593 | for (y = 0 ; y < h ; y++) { | ||
594 | for (x = 0 ; x < w ; x++) { | ||
595 | c = in[i << 2]; | ||
596 | //unsigned char r = in[4 * i]; | ||
597 | //unsigned char g = in[4 * i]; | ||
598 | //unsigned char b = in[4 * i]; | ||
599 | out[i++] = LCD_RGBPACK(c,c,c); | ||
600 | } | ||
601 | } | ||
602 | break; | ||
603 | case 6: /*RGB with alpha*/ | ||
604 | i = 0; | ||
605 | for (y = 0 ; y < h ; y++) { | ||
606 | for (x = 0 ; x < w ; x++) { | ||
607 | j = i << 3; | ||
608 | unsigned char r = in[j]; | ||
609 | unsigned char g = in[j + 2]; | ||
610 | unsigned char b = in[j + 4]; | ||
611 | out[i++] = LCD_RGBPACK(r,g,b); | ||
612 | } | ||
613 | } | ||
614 | break; | ||
615 | default: | ||
616 | break; | ||
617 | } | ||
618 | } | ||
619 | else /*infoIn->bitDepth is less than 8 bit per channel*/ | ||
620 | { | ||
621 | switch (infoIn->colorType) | ||
622 | { | ||
623 | case 0: /*greyscale color*/ | ||
624 | i = 0; | ||
625 | for (y = 0 ; y < h ; y++) { | ||
626 | for (x = 0 ; x < w ; x++) { | ||
627 | unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth); | ||
628 | value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/ | ||
629 | unsigned char r = (unsigned char)value; | ||
630 | unsigned char g = (unsigned char)value; | ||
631 | unsigned char b = (unsigned char)value; | ||
632 | out[i++] = LCD_RGBPACK(r,g,b); | ||
633 | } | ||
634 | } | ||
635 | break; | ||
636 | case 3: /*indexed color (palette)*/ | ||
637 | i = 0; | ||
638 | for (y = 0 ; y < h ; y++) { | ||
639 | for (x = 0 ; x < w ; x++) { | ||
640 | unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth); | ||
641 | if (value >= infoIn->palettesize) return 47; | ||
642 | j = value << 2; | ||
643 | unsigned char r = infoIn->palette[j]; | ||
644 | unsigned char g = infoIn->palette[j + 1]; | ||
645 | unsigned char b = infoIn->palette[j + 2]; | ||
646 | out[i++] = LCD_RGBPACK(r,g,b); | ||
647 | } | ||
648 | } | ||
649 | break; | ||
650 | default: | ||
651 | break; | ||
652 | } | ||
653 | } | ||
654 | } | ||
655 | else if (LodePNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/ | ||
656 | { | ||
657 | if (!LodePNG_InfoColor_isGreyscaleType(infoIn)) return 62; | ||
658 | if (infoIn->bitDepth == 8) | ||
659 | { | ||
660 | switch (infoIn->colorType) | ||
661 | { | ||
662 | case 0: /*greyscale color*/ | ||
663 | i = 0; | ||
664 | for (y = 0 ; y < h ; y++) { | ||
665 | for (x = 0 ; x < w ; x++) { | ||
666 | c = in[i]; | ||
667 | //unsigned char r = in[i]; | ||
668 | //unsigned char g = in[i]; | ||
669 | //unsigned char b = in[i]; | ||
670 | out[i++] = LCD_RGBPACK(c,c,c); | ||
671 | } | ||
672 | } | ||
673 | break; | ||
674 | case 4: /*greyscale with alpha*/ | ||
675 | i = 0; | ||
676 | for (y = 0 ; y < h ; y++) { | ||
677 | for (x = 0 ; x < w ; x++) { | ||
678 | c = in[(i << 1) + 1]; | ||
679 | //unsigned char r = in[2 * i + 1]; | ||
680 | //unsigned char g = in[2 * i + 1]; | ||
681 | //unsigned char b = in[2 * i + 1]; | ||
682 | out[i++] = LCD_RGBPACK(c,c,c); | ||
683 | } | ||
684 | } | ||
685 | break; | ||
686 | default: | ||
687 | return 31; | ||
688 | } | ||
689 | } | ||
690 | else if (infoIn->bitDepth == 16) | ||
691 | { | ||
692 | switch (infoIn->colorType) | ||
693 | { | ||
694 | case 0: /*greyscale color*/ | ||
695 | i = 0; | ||
696 | for (y = 0 ; y < h ; y++) { | ||
697 | for (x = 0 ; x < w ; x++) { | ||
698 | c = in[i << 1]; | ||
699 | //unsigned char r = in[2 * i]; | ||
700 | //unsigned char g = in[2 * i]; | ||
701 | //unsigned char b = in[2 * i]; | ||
702 | out[i++] = LCD_RGBPACK(c,c,c); | ||
703 | } | ||
704 | } | ||
705 | break; | ||
706 | case 4: /*greyscale with alpha*/ | ||
707 | i = 0; | ||
708 | for (y = 0 ; y < h ; y++) { | ||
709 | for (x = 0 ; x < w ; x++) { | ||
710 | c = in[i << 2]; | ||
711 | //unsigned char r = in[4 * i]; | ||
712 | //unsigned char g = in[4 * i]; | ||
713 | //unsigned char b = in[4 * i]; | ||
714 | out[i++] = LCD_RGBPACK(c,c,c); | ||
715 | } | ||
716 | } | ||
717 | break; | ||
718 | default: | ||
719 | return 31; | ||
720 | } | ||
721 | } | ||
722 | else /*infoIn->bitDepth is less than 8 bit per channel*/ | ||
723 | { | ||
724 | if (infoIn->colorType != 0) return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/ | ||
725 | i = 0; | ||
726 | for (y = 0 ; y < h ; y++) { | ||
727 | for (x = 0 ; x < w ; x++) { | ||
728 | unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth); | ||
729 | value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/ | ||
730 | unsigned char r = (unsigned char)value; | ||
731 | unsigned char g = (unsigned char)value; | ||
732 | unsigned char b = (unsigned char)value; | ||
733 | out[i++] = LCD_RGBPACK(r,g,b); | ||
734 | } | ||
735 | } | ||
736 | } | ||
737 | } | ||
738 | else return 59; | ||
739 | |||
740 | return 0; | ||
741 | } | ||
742 | |||
743 | /*Paeth predicter, used by PNG filter type 4*/ | ||
744 | static int paethPredictor(int a, int b, int c) | ||
745 | { | ||
746 | int p = a + b - c; | ||
747 | int pa = p > a ? p - a : a - p; | ||
748 | int pb = p > b ? p - b : b - p; | ||
749 | int pc = p > c ? p - c : c - p; | ||
750 | |||
751 | if (pa <= pb && pa <= pc) return a; | ||
752 | else if (pb <= pc) return b; | ||
753 | else return c; | ||
754 | } | ||
755 | |||
756 | /*shared values used by multiple Adam7 related functions*/ | ||
757 | |||
758 | static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ | ||
759 | static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ | ||
760 | static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ | ||
761 | static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ | ||
762 | |||
763 | 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) | ||
764 | { | ||
765 | /*the passstart values have 8 values: the 8th one actually indicates the byte after the end of the 7th (= last) pass*/ | ||
766 | unsigned i; | ||
767 | |||
768 | /*calculate width and height in pixels of each pass*/ | ||
769 | for (i = 0; i < 7; i++) | ||
770 | { | ||
771 | passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; | ||
772 | passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; | ||
773 | if (passw[i] == 0) passh[i] = 0; | ||
774 | if (passh[i] == 0) passw[i] = 0; | ||
775 | } | ||
776 | |||
777 | filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; | ||
778 | for (i = 0; i < 7; i++) | ||
779 | { | ||
780 | 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)*/ | ||
781 | 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*/ | ||
782 | passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; /*only padded at end of reduced image*/ | ||
783 | } | ||
784 | } | ||
785 | |||
786 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
787 | /* / PNG Decoder / */ | ||
788 | /* ////////////////////////////////////////////////////////////////////////// */ | ||
789 | |||
790 | /*read the information from the header and store it in the LodePNG_Info. return value is error*/ | ||
791 | void LodePNG_inspect(LodePNG_Decoder* decoder, const unsigned char* in, size_t inlength) | ||
792 | { | ||
793 | if (inlength == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/ | ||
794 | if (inlength < 29) { decoder->error = 27; return; } /*error: the data length is smaller than the length of the header*/ | ||
795 | |||
796 | /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ | ||
797 | LodePNG_InfoPng_cleanup(&decoder->infoPng); | ||
798 | LodePNG_InfoPng_init(&decoder->infoPng); | ||
799 | decoder->error = 0; | ||
800 | |||
801 | 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*/ | ||
802 | 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!*/ | ||
803 | |||
804 | /*read the values given in the header*/ | ||
805 | decoder->infoPng.width = LodePNG_read32bitInt(&in[16]); | ||
806 | decoder->infoPng.height = LodePNG_read32bitInt(&in[20]); | ||
807 | decoder->infoPng.color.bitDepth = in[24]; | ||
808 | decoder->infoPng.color.colorType = in[25]; | ||
809 | decoder->infoPng.compressionMethod = in[26]; | ||
810 | decoder->infoPng.filterMethod = in[27]; | ||
811 | decoder->infoPng.interlaceMethod = in[28]; | ||
812 | |||
813 | unsigned CRC = LodePNG_read32bitInt(&in[29]); | ||
814 | unsigned checksum = crc32(0L, &in[12], 17); | ||
815 | if (CRC != checksum) { decoder->error = 57; return; } | ||
816 | |||
817 | if (decoder->infoPng.compressionMethod != 0) { decoder->error = 32; return; } /*error: only compression method 0 is allowed in the specification*/ | ||
818 | if (decoder->infoPng.filterMethod != 0) { decoder->error = 33; return; } /*error: only filter method 0 is allowed in the specification*/ | ||
819 | if (decoder->infoPng.interlaceMethod > 1) { decoder->error = 34; return; } /*error: only interlace methods 0 and 1 exist in the specification*/ | ||
820 | |||
821 | decoder->error = checkColorValidity(decoder->infoPng.color.colorType, decoder->infoPng.color.bitDepth); | ||
822 | } | ||
823 | |||
824 | static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length) | ||
825 | { | ||
826 | /* | ||
827 | For PNG filter method 0 | ||
828 | unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1) | ||
829 | precon is the previous unfiltered scanline, recon the result, scanline the current one | ||
830 | the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead | ||
831 | recon and scanline MAY be the same memory address! precon must be disjoint. | ||
832 | */ | ||
833 | |||
834 | size_t i; | ||
835 | switch (filterType) | ||
836 | { | ||
837 | case 0: | ||
838 | //for(i = 0; i < length; i++) recon[i] = scanline[i]; | ||
839 | memcpy(recon, scanline, length * sizeof(unsigned char)); | ||
840 | break; | ||
841 | case 1: | ||
842 | //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; | ||
843 | memcpy(recon, scanline, bytewidth * sizeof(unsigned char)); | ||
844 | for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; | ||
845 | break; | ||
846 | case 2: | ||
847 | if (precon) for (i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; | ||
848 | else //for(i = 0; i < length; i++) recon[i] = scanline[i]; | ||
849 | memcpy(recon, scanline, length * sizeof(unsigned char)); | ||
850 | break; | ||
851 | case 3: | ||
852 | if (precon) | ||
853 | { | ||
854 | for (i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; | ||
855 | for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); | ||
856 | } | ||
857 | else | ||
858 | { | ||
859 | //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; | ||
860 | memcpy(recon, scanline, bytewidth * sizeof(unsigned char)); | ||
861 | for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2; | ||
862 | } | ||
863 | break; | ||
864 | case 4: | ||
865 | if (precon) | ||
866 | { | ||
867 | for (i = 0; i < bytewidth; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(0, precon[i], 0)); | ||
868 | for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); | ||
869 | } | ||
870 | else | ||
871 | { | ||
872 | //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; | ||
873 | memcpy(recon, scanline, bytewidth * sizeof(unsigned char)); | ||
874 | for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0)); | ||
875 | } | ||
876 | break; | ||
877 | default: | ||
878 | return 36; /*error: unexisting filter type given*/ | ||
879 | } | ||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) | ||
884 | { | ||
885 | /* | ||
886 | For PNG filter method 0 | ||
887 | this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 it's called 7 times) | ||
888 | out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline | ||
889 | w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel | ||
890 | in and out are allowed to be the same memory address! | ||
891 | */ | ||
892 | |||
893 | unsigned y; | ||
894 | unsigned char* prevline = 0; | ||
895 | |||
896 | size_t bytewidth = (bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ | ||
897 | size_t linebytes = (w * bpp + 7) / 8; | ||
898 | |||
899 | for (y = 0; y < h; y++) | ||
900 | { | ||
901 | size_t outindex = linebytes * y; | ||
902 | size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ | ||
903 | unsigned char filterType = in[inindex]; | ||
904 | |||
905 | unsigned error = unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes); | ||
906 | if (error) return error; | ||
907 | |||
908 | prevline = &out[outindex]; | ||
909 | } | ||
910 | |||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) | ||
915 | { | ||
916 | /*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 | ||
917 | 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)*/ | ||
918 | unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; | ||
919 | unsigned i; | ||
920 | |||
921 | Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); | ||
922 | |||
923 | if (bpp >= 8) | ||
924 | { | ||
925 | for (i = 0; i < 7; i++) | ||
926 | { | ||
927 | unsigned x, y, b; | ||
928 | size_t bytewidth = bpp / 8; | ||
929 | for (y = 0; y < passh[i]; y++) | ||
930 | for (x = 0; x < passw[i]; x++) | ||
931 | { | ||
932 | size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; | ||
933 | size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; | ||
934 | for (b = 0; b < bytewidth; b++) | ||
935 | { | ||
936 | out[pixeloutstart + b] = in[pixelinstart + b]; | ||
937 | } | ||
938 | } | ||
939 | } | ||
940 | } | ||
941 | else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ | ||
942 | { | ||
943 | for (i = 0; i < 7; i++) | ||
944 | { | ||
945 | unsigned x, y, b; | ||
946 | unsigned ilinebits = bpp * passw[i]; | ||
947 | unsigned olinebits = bpp * w; | ||
948 | size_t obp, ibp; /*bit pointers (for out and in buffer)*/ | ||
949 | for (y = 0; y < passh[i]; y++) | ||
950 | for (x = 0; x < passw[i]; x++) | ||
951 | { | ||
952 | ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); | ||
953 | obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; | ||
954 | for (b = 0; b < bpp; b++) | ||
955 | { | ||
956 | unsigned char bit = readBitFromReversedStream(&ibp, in); | ||
957 | setBitOfReversedStream0(&obp, out, bit); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ | ||
958 | } | ||
959 | } | ||
960 | } | ||
961 | } | ||
962 | } | ||
963 | |||
964 | static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) | ||
965 | { | ||
966 | /* | ||
967 | 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. | ||
968 | 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 | ||
969 | also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 | ||
970 | only useful if (ilinebits - olinebits) is a value in the range 1..7 | ||
971 | */ | ||
972 | unsigned y; | ||
973 | size_t diff = ilinebits - olinebits; | ||
974 | size_t obp = 0, ibp = 0; /*bit pointers*/ | ||
975 | for (y = 0; y < h; y++) | ||
976 | { | ||
977 | size_t x; | ||
978 | for (x = 0; x < olinebits; x++) | ||
979 | { | ||
980 | unsigned char bit = readBitFromReversedStream(&ibp, in); | ||
981 | setBitOfReversedStream(&obp, out, bit); | ||
982 | } | ||
983 | ibp += diff; | ||
984 | } | ||
985 | } | ||
986 | |||
987 | /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks*/ | ||
988 | static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, const LodePNG_Decoder* decoder) /*return value is error*/ | ||
989 | { | ||
990 | /* | ||
991 | This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps: | ||
992 | *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) | ||
993 | *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace | ||
994 | NOTE: the in buffer will be overwritten with intermediate data! | ||
995 | */ | ||
996 | unsigned bpp = LodePNG_InfoColor_getBpp(&decoder->infoPng.color); | ||
997 | unsigned w = decoder->infoPng.width; | ||
998 | unsigned h = decoder->infoPng.height; | ||
999 | unsigned error = 0; | ||
1000 | if (bpp == 0) return 31; /*error: invalid colortype*/ | ||
1001 | |||
1002 | if (decoder->infoPng.interlaceMethod == 0) | ||
1003 | { | ||
1004 | if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) | ||
1005 | { | ||
1006 | error = unfilter(in, in, w, h, bpp); | ||
1007 | if (error) return error; | ||
1008 | removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); | ||
1009 | } | ||
1010 | else error = unfilter(out, in, w, h, bpp); /*we can immediatly filter into the out buffer, no other steps needed*/ | ||
1011 | } | ||
1012 | else /*interlaceMethod is 1 (Adam7)*/ | ||
1013 | { | ||
1014 | unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; | ||
1015 | unsigned i; | ||
1016 | |||
1017 | Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); | ||
1018 | |||
1019 | for (i = 0; i < 7; i++) | ||
1020 | { | ||
1021 | error = unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp); | ||
1022 | if (error) return error; | ||
1023 | 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*/ | ||
1024 | { | ||
1025 | /*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*/ | ||
1026 | removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7) / 8) * 8, passh[i]); | ||
1027 | } | ||
1028 | } | ||
1029 | |||
1030 | Adam7_deinterlace(out, in, w, h, bpp); | ||
1031 | } | ||
1032 | |||
1033 | return error; | ||
1034 | } | ||
1035 | |||
1036 | /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ | ||
1037 | static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t size, void (*pf_progress)(int current, int total)) | ||
1038 | { | ||
1039 | if (pf_progress != NULL) | ||
1040 | pf_progress(0, 100); | ||
1041 | unsigned char IEND = 0; | ||
1042 | const unsigned char* chunk; | ||
1043 | size_t i; | ||
1044 | unsigned char *idat = memory; | ||
1045 | size_t idat_size = 0; | ||
1046 | |||
1047 | /*for unknown chunk order*/ | ||
1048 | unsigned unknown = 0; | ||
1049 | unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ | ||
1050 | |||
1051 | /*provide some proper output values if error will happen*/ | ||
1052 | decoded_image_size = 0; | ||
1053 | |||
1054 | if (size == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/ | ||
1055 | |||
1056 | LodePNG_inspect(decoder, in, size); /*reads header and resets other parameters in decoder->infoPng*/ | ||
1057 | if (decoder->error) return; | ||
1058 | |||
1059 | chunk = &in[33]; /*first byte of the first chunk after the header*/ | ||
1060 | |||
1061 | 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*/ | ||
1062 | { | ||
1063 | unsigned chunkLength; | ||
1064 | const unsigned char* data; /*the data in the chunk*/ | ||
1065 | |||
1066 | 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*/ | ||
1067 | chunkLength = LodePNG_chunk_length(chunk); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ | ||
1068 | if (chunkLength > 2147483647) { decoder->error = 63; break; } | ||
1069 | 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*/ | ||
1070 | data = LodePNG_chunk_data_const(chunk); | ||
1071 | |||
1072 | /*IDAT chunk, containing compressed image data*/ | ||
1073 | if (LodePNG_chunk_type_equals(chunk, "IDAT")) | ||
1074 | { | ||
1075 | size_t oldsize = idat_size; | ||
1076 | idat_size += chunkLength; | ||
1077 | if (idat + idat_size >= image) { decoder->error = OUT_OF_MEMORY; break; } | ||
1078 | memcpy(idat+oldsize, data, chunkLength * sizeof(unsigned char)); | ||
1079 | critical_pos = 3; | ||
1080 | } | ||
1081 | /*IEND chunk*/ | ||
1082 | else if (LodePNG_chunk_type_equals(chunk, "IEND")) | ||
1083 | { | ||
1084 | IEND = 1; | ||
1085 | } | ||
1086 | /*palette chunk (PLTE)*/ | ||
1087 | else if (LodePNG_chunk_type_equals(chunk, "PLTE")) | ||
1088 | { | ||
1089 | unsigned pos = 0; | ||
1090 | decoder->infoPng.color.palettesize = chunkLength / 3; | ||
1091 | if (decoder->infoPng.color.palettesize > 256) { decoder->error = 38; break; } /*error: palette too big*/ | ||
1092 | for (i = 0; i < decoder->infoPng.color.palettesize; i++) | ||
1093 | { | ||
1094 | decoder->infoPng.color.palette[4 * i + 0] = data[pos++]; /*R*/ | ||
1095 | decoder->infoPng.color.palette[4 * i + 1] = data[pos++]; /*G*/ | ||
1096 | decoder->infoPng.color.palette[4 * i + 2] = data[pos++]; /*B*/ | ||
1097 | decoder->infoPng.color.palette[4 * i + 3] = 255; /*alpha*/ | ||
1098 | } | ||
1099 | critical_pos = 2; | ||
1100 | } | ||
1101 | /*palette transparency chunk (tRNS)*/ | ||
1102 | else if (LodePNG_chunk_type_equals(chunk, "tRNS")) | ||
1103 | { | ||
1104 | if (decoder->infoPng.color.colorType == 3) | ||
1105 | { | ||
1106 | if (chunkLength > decoder->infoPng.color.palettesize) { decoder->error = 39; break; } /*error: more alpha values given than there are palette entries*/ | ||
1107 | for (i = 0; i < chunkLength; i++) decoder->infoPng.color.palette[4 * i + 3] = data[i]; | ||
1108 | } | ||
1109 | else if (decoder->infoPng.color.colorType == 0) | ||
1110 | { | ||
1111 | if (chunkLength != 2) { decoder->error = 40; break; } /*error: this chunk must be 2 bytes for greyscale image*/ | ||
1112 | decoder->infoPng.color.key_defined = 1; | ||
1113 | decoder->infoPng.color.key_r = decoder->infoPng.color.key_g = decoder->infoPng.color.key_b = 256 * data[0] + data[1]; | ||
1114 | } | ||
1115 | else if (decoder->infoPng.color.colorType == 2) | ||
1116 | { | ||
1117 | if (chunkLength != 6) { decoder->error = 41; break; } /*error: this chunk must be 6 bytes for RGB image*/ | ||
1118 | decoder->infoPng.color.key_defined = 1; | ||
1119 | decoder->infoPng.color.key_r = 256 * data[0] + data[1]; | ||
1120 | decoder->infoPng.color.key_g = 256 * data[2] + data[3]; | ||
1121 | decoder->infoPng.color.key_b = 256 * data[4] + data[5]; | ||
1122 | } | ||
1123 | else { decoder->error = 42; break; } /*error: tRNS chunk not allowed for other color models*/ | ||
1124 | } | ||
1125 | /*background color chunk (bKGD)*/ | ||
1126 | else if (LodePNG_chunk_type_equals(chunk, "bKGD")) | ||
1127 | { | ||
1128 | if (decoder->infoPng.color.colorType == 3) | ||
1129 | { | ||
1130 | if (chunkLength != 1) { decoder->error = 43; break; } /*error: this chunk must be 1 byte for indexed color image*/ | ||
1131 | decoder->infoPng.background_defined = 1; | ||
1132 | decoder->infoPng.background_r = decoder->infoPng.color.palette[4 * data[0] + 0]; | ||
1133 | decoder->infoPng.background_g = decoder->infoPng.color.palette[4 * data[0] + 1]; | ||
1134 | decoder->infoPng.background_b = decoder->infoPng.color.palette[4 * data[0] + 2]; | ||
1135 | |||
1136 | } | ||
1137 | else if (decoder->infoPng.color.colorType == 0 || decoder->infoPng.color.colorType == 4) | ||
1138 | { | ||
1139 | if (chunkLength != 2) { decoder->error = 44; break; } /*error: this chunk must be 2 bytes for greyscale image*/ | ||
1140 | decoder->infoPng.background_defined = 1; | ||
1141 | decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_b = 256 * data[0] + data[1]; | ||
1142 | } | ||
1143 | else if (decoder->infoPng.color.colorType == 2 || decoder->infoPng.color.colorType == 6) | ||
1144 | { | ||
1145 | if (chunkLength != 6) { decoder->error = 45; break; } /*error: this chunk must be 6 bytes for greyscale image*/ | ||
1146 | decoder->infoPng.background_defined = 1; | ||
1147 | decoder->infoPng.background_r = 256 * data[0] + data[1]; | ||
1148 | decoder->infoPng.background_g = 256 * data[2] + data[3]; | ||
1149 | decoder->infoPng.background_b = 256 * data[4] + data[5]; | ||
1150 | } | ||
1151 | } | ||
1152 | else if (LodePNG_chunk_type_equals(chunk, "tIME")) | ||
1153 | { | ||
1154 | if (chunkLength != 7) { decoder->error = 73; break; } | ||
1155 | decoder->infoPng.time_defined = 1; | ||
1156 | decoder->infoPng.time.year = 256 * data[0] + data[+ 1]; | ||
1157 | decoder->infoPng.time.month = data[2]; | ||
1158 | decoder->infoPng.time.day = data[3]; | ||
1159 | decoder->infoPng.time.hour = data[4]; | ||
1160 | decoder->infoPng.time.minute = data[5]; | ||
1161 | decoder->infoPng.time.second = data[6]; | ||
1162 | } | ||
1163 | else if (LodePNG_chunk_type_equals(chunk, "pHYs")) | ||
1164 | { | ||
1165 | if (chunkLength != 9) { decoder->error = 74; break; } | ||
1166 | decoder->infoPng.phys_defined = 1; | ||
1167 | decoder->infoPng.phys_x = 16777216 * data[0] + 65536 * data[1] + 256 * data[2] + data[3]; | ||
1168 | decoder->infoPng.phys_y = 16777216 * data[4] + 65536 * data[5] + 256 * data[6] + data[7]; | ||
1169 | decoder->infoPng.phys_unit = data[8]; | ||
1170 | } | ||
1171 | else /*it's not an implemented chunk type, so ignore it: skip over the data*/ | ||
1172 | { | ||
1173 | if (LodePNG_chunk_critical(chunk)) { decoder->error = 69; break; } /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ | ||
1174 | unknown = 1; | ||
1175 | } | ||
1176 | |||
1177 | if (!unknown) /*check CRC if wanted, only on known chunk types*/ | ||
1178 | { | ||
1179 | long time = *rb->current_tick; | ||
1180 | if (LodePNG_chunk_check_crc(chunk)) { decoder->error = 57; break; } | ||
1181 | time = *rb->current_tick-time; | ||
1182 | } | ||
1183 | |||
1184 | if (!IEND) chunk = LodePNG_chunk_next_const(chunk); | ||
1185 | } | ||
1186 | |||
1187 | if (!decoder->error) | ||
1188 | { | ||
1189 | unsigned char *scanlines = idat + idat_size; | ||
1190 | size_t scanlines_size = (size_t)memory_max - idat_size; | ||
1191 | long time = *rb->current_tick; | ||
1192 | decoder->error = LodePNG_decompress(scanlines, &scanlines_size, idat, idat_size, decoder->error_msg); /*decompress with the Zlib decompressor*/ | ||
1193 | if (pf_progress) pf_progress(100, 100); | ||
1194 | time = *rb->current_tick-time; | ||
1195 | |||
1196 | if (!decoder->error) | ||
1197 | { | ||
1198 | decoded_image_size = (decoder->infoPng.height * decoder->infoPng.width * LodePNG_InfoColor_getBpp(&decoder->infoPng.color) + 7) / 8; | ||
1199 | if (decoded_image_size > memory_size) { decoder->error = OUT_OF_MEMORY; return; } | ||
1200 | decoded_image = memory_max - decoded_image_size; | ||
1201 | if (scanlines + scanlines_size >= decoded_image) { decoder->error = OUT_OF_MEMORY; return; } | ||
1202 | memset(decoded_image, 0, decoded_image_size * sizeof(unsigned char)); | ||
1203 | if (!running_slideshow) | ||
1204 | { | ||
1205 | rb->lcd_puts(0, 3, "unfiltering scanlines"); | ||
1206 | rb->lcd_update(); | ||
1207 | } | ||
1208 | decoder->error = postProcessScanlines(decoded_image, scanlines, decoder); | ||
1209 | } | ||
1210 | } | ||
1211 | } | ||
1212 | |||
1213 | void LodePNG_decode(LodePNG_Decoder* decoder, unsigned char* in, size_t insize, void (*pf_progress)(int current, int total)) | ||
1214 | { | ||
1215 | decodeGeneric(decoder, in, insize, pf_progress); | ||
1216 | if (decoder->error) return; | ||
1217 | |||
1218 | /*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"*/ | ||
1219 | if (!(decoder->infoRaw.color.colorType == 2 || decoder->infoRaw.color.colorType == 6) && !(decoder->infoRaw.color.bitDepth == 8)) { decoder->error = 56; return; } | ||
1220 | converted_image = (fb_data *)memory; | ||
1221 | converted_image_size = decoder->infoPng.width*decoder->infoPng.height; | ||
1222 | if ((unsigned char *)(converted_image + converted_image_size) >= decoded_image) { decoder->error = OUT_OF_MEMORY; } | ||
1223 | if (!decoder->error) decoder->error = LodePNG_convert(converted_image, decoded_image, &decoder->infoRaw.color, &decoder->infoPng.color, decoder->infoPng.width, decoder->infoPng.height); | ||
1224 | } | ||
1225 | |||
1226 | void LodePNG_DecodeSettings_init(LodePNG_DecodeSettings* settings) | ||
1227 | { | ||
1228 | settings->color_convert = 1; | ||
1229 | } | ||
1230 | |||
1231 | void LodePNG_Decoder_init(LodePNG_Decoder* decoder) | ||
1232 | { | ||
1233 | LodePNG_DecodeSettings_init(&decoder->settings); | ||
1234 | LodePNG_InfoRaw_init(&decoder->infoRaw); | ||
1235 | LodePNG_InfoPng_init(&decoder->infoPng); | ||
1236 | decoder->error = 1; | ||
1237 | } | ||
1238 | |||
1239 | void LodePNG_Decoder_cleanup(LodePNG_Decoder* decoder) | ||
1240 | { | ||
1241 | LodePNG_InfoRaw_cleanup(&decoder->infoRaw); | ||
1242 | LodePNG_InfoPng_cleanup(&decoder->infoPng); | ||
1243 | } | ||
1244 | |||
1245 | #define PNG_ERROR_MIN 27 | ||
1246 | #define PNG_ERROR_MAX 74 | ||
1247 | static const unsigned char *png_error_messages[PNG_ERROR_MAX-PNG_ERROR_MIN+1] = | ||
1248 | { | ||
1249 | "png file smaller than a png header", /*27*/ | ||
1250 | "incorrect png signature", /*28*/ | ||
1251 | "first chunk is not IHDR", /*29*/ | ||
1252 | "chunk length too large", /*30*/ | ||
1253 | "illegal PNG color type or bpp", /*31*/ | ||
1254 | "illegal PNG compression method", /*32*/ | ||
1255 | "illegal PNG filter method", /*33*/ | ||
1256 | "illegal PNG interlace method", /*34*/ | ||
1257 | "chunk length of a chunk is too large or the chunk too small", /*35*/ | ||
1258 | "illegal PNG filter type encountered", /*36*/ | ||
1259 | "illegal bit depth for this color type given", /*37*/ | ||
1260 | "the palette is too big (more than 256 colors)", /*38*/ | ||
1261 | "more palette alpha values given in tRNS, than there are colors in the palette", /*39*/ | ||
1262 | "tRNS chunk has wrong size for greyscale image", /*40*/ | ||
1263 | "tRNS chunk has wrong size for RGB image", /*41*/ | ||
1264 | "tRNS chunk appeared while it was not allowed for this color type", /*42*/ | ||
1265 | "bKGD chunk has wrong size for palette image", /*43*/ | ||
1266 | "bKGD chunk has wrong size for greyscale image", /*44*/ | ||
1267 | "bKGD chunk has wrong size for RGB image", /*45*/ | ||
1268 | "value encountered in indexed image is larger than the palette size", /*46*/ | ||
1269 | "value encountered in indexed image is larger than the palette size", /*47*/ | ||
1270 | "input file is empty", /*48*/ | ||
1271 | NULL, /*49*/ | ||
1272 | NULL, /*50*/ | ||
1273 | NULL, /*51*/ | ||
1274 | NULL, /*52*/ | ||
1275 | NULL, /*53*/ | ||
1276 | NULL, /*54*/ | ||
1277 | NULL, /*55*/ | ||
1278 | NULL, /*56*/ | ||
1279 | "invalid CRC", /*57*/ | ||
1280 | NULL, /*58*/ | ||
1281 | "conversion to unexisting or unsupported color type or bit depth", /*59*/ | ||
1282 | NULL, /*60*/ | ||
1283 | NULL, /*61*/ | ||
1284 | NULL, /*62*/ | ||
1285 | "png chunk too long", /*63*/ | ||
1286 | NULL, /*64*/ | ||
1287 | NULL, /*65*/ | ||
1288 | NULL, /*66*/ | ||
1289 | NULL, /*67*/ | ||
1290 | NULL, /*68*/ | ||
1291 | "unknown critical chunk", /*69*/ | ||
1292 | NULL, /*70*/ | ||
1293 | NULL, /*71*/ | ||
1294 | NULL, /*72*/ | ||
1295 | "invalid tIME chunk size", /*73*/ | ||
1296 | "invalid pHYs chunk size", /*74*/ | ||
1297 | }; | ||
1298 | 53 | ||
1299 | bool img_ext(const char *ext) | 54 | bool img_ext(const char *ext) |
1300 | { | 55 | { |
@@ -1310,16 +65,33 @@ void draw_image_rect(struct image_info *info, | |||
1310 | int x, int y, int width, int height) | 65 | int x, int y, int width, int height) |
1311 | { | 66 | { |
1312 | fb_data **pdisp = (fb_data**)info->data; | 67 | fb_data **pdisp = (fb_data**)info->data; |
1313 | rb->lcd_bitmap_part(*pdisp, info->x + x, info->y + y, info->width, | 68 | |
69 | #ifdef HAVE_LCD_COLOR | ||
70 | rb->lcd_bitmap_part(*pdisp, info->x + x, info->y + y, | ||
71 | STRIDE(SCREEN_MAIN, info->width, info->height), | ||
1314 | x + MAX(0, (LCD_WIDTH-info->width)/2), | 72 | x + MAX(0, (LCD_WIDTH-info->width)/2), |
1315 | y + MAX(0, (LCD_HEIGHT-info->height)/2), | 73 | y + MAX(0, (LCD_HEIGHT-info->height)/2), |
1316 | width, height); | 74 | width, height); |
75 | #else | ||
76 | mylcd_ub_gray_bitmap_part((const unsigned char*)*pdisp, | ||
77 | info->x + x, info->y + y, info->width, | ||
78 | x + MAX(0, (LCD_WIDTH-info->width)/2), | ||
79 | y + MAX(0, (LCD_HEIGHT-info->height)/2), | ||
80 | width, height); | ||
81 | #endif | ||
1317 | } | 82 | } |
1318 | 83 | ||
1319 | int img_mem(int ds) | 84 | int img_mem(int ds) |
1320 | { | 85 | { |
1321 | LodePNG_Decoder *decoder = &_decoder; | 86 | LodePNG_Decoder *p_decoder = &decoder; |
1322 | return (decoder->infoPng.width/ds) * (decoder->infoPng.height/ds) * FB_DATA_SZ; | 87 | |
88 | #ifdef USEGSLIB | ||
89 | return (p_decoder->infoPng.width/ds) * (p_decoder->infoPng.height/ds); | ||
90 | #else | ||
91 | return (p_decoder->infoPng.width/ds) * | ||
92 | (p_decoder->infoPng.height/ds) * | ||
93 | FB_DATA_SZ; | ||
94 | #endif | ||
1323 | } | 95 | } |
1324 | 96 | ||
1325 | int load_image(char *filename, struct image_info *info, | 97 | int load_image(char *filename, struct image_info *info, |
@@ -1328,10 +100,14 @@ int load_image(char *filename, struct image_info *info, | |||
1328 | int fd; | 100 | int fd; |
1329 | long time = 0; /* measured ticks */ | 101 | long time = 0; /* measured ticks */ |
1330 | int w, h; /* used to center output */ | 102 | int w, h; /* used to center output */ |
1331 | LodePNG_Decoder *decoder = &_decoder; | 103 | LodePNG_Decoder *p_decoder = &decoder; |
104 | |||
105 | unsigned char *memory, *memory_max; | ||
106 | static size_t memory_size, file_size; | ||
107 | static unsigned char *image; | ||
1332 | 108 | ||
109 | /* cleanup */ | ||
1333 | memset(&disp, 0, sizeof(disp)); | 110 | memset(&disp, 0, sizeof(disp)); |
1334 | LodePNG_Decoder_init(decoder); | ||
1335 | 111 | ||
1336 | /* align buffer */ | 112 | /* align buffer */ |
1337 | memory = (unsigned char *)((intptr_t)(buf + 3) & ~3); | 113 | memory = (unsigned char *)((intptr_t)(buf + 3) & ~3); |
@@ -1344,7 +120,7 @@ int load_image(char *filename, struct image_info *info, | |||
1344 | rb->splashf(HZ, "err opening %s: %d", filename, fd); | 120 | rb->splashf(HZ, "err opening %s: %d", filename, fd); |
1345 | return PLUGIN_ERROR; | 121 | return PLUGIN_ERROR; |
1346 | } | 122 | } |
1347 | image_size = rb->filesize(fd); | 123 | file_size = rb->filesize(fd); |
1348 | 124 | ||
1349 | DEBUGF("reading file '%s'\n", filename); | 125 | DEBUGF("reading file '%s'\n", filename); |
1350 | 126 | ||
@@ -1353,18 +129,19 @@ int load_image(char *filename, struct image_info *info, | |||
1353 | rb->lcd_update(); | 129 | rb->lcd_update(); |
1354 | } | 130 | } |
1355 | 131 | ||
1356 | if (image_size > memory_size) { | 132 | if (file_size > memory_size) { |
1357 | decoder->error = FILE_TOO_LARGE; | 133 | p_decoder->error = FILE_TOO_LARGE; |
1358 | rb->close(fd); | 134 | rb->close(fd); |
1359 | 135 | ||
1360 | } else { | 136 | } else { |
1361 | if (!running_slideshow) { | 137 | if (!running_slideshow) { |
1362 | rb->lcd_putsf(0, 1, "loading %zu bytes", image_size); | 138 | rb->lcd_putsf(0, 1, "loading %zu bytes", file_size); |
1363 | rb->lcd_update(); | 139 | rb->lcd_update(); |
1364 | } | 140 | } |
1365 | 141 | ||
1366 | image = memory_max - image_size; | 142 | /* load file to the end of the buffer */ |
1367 | rb->read(fd, image, image_size); | 143 | image = memory_max - file_size; |
144 | rb->read(fd, image, file_size); | ||
1368 | rb->close(fd); | 145 | rb->close(fd); |
1369 | 146 | ||
1370 | if (!running_slideshow) { | 147 | if (!running_slideshow) { |
@@ -1378,19 +155,27 @@ int load_image(char *filename, struct image_info *info, | |||
1378 | } | 155 | } |
1379 | #endif | 156 | #endif |
1380 | 157 | ||
1381 | decoder->settings.color_convert = 1; | 158 | /* Initialize decoder context struct, set buffer decoder is free |
1382 | decoder->infoRaw.color.colorType = 2; | 159 | * to use. |
1383 | decoder->infoRaw.color.bitDepth = 8; | 160 | * Decoder assumes that raw image file is loaded at the very |
161 | * end of the allocated buffer | ||
162 | */ | ||
163 | LodePNG_Decoder_init(p_decoder, memory, memory_size); | ||
1384 | 164 | ||
1385 | LodePNG_inspect(decoder, image, image_size); | 165 | /* read file header; file is loaded at the end |
166 | * of the allocated buffer | ||
167 | */ | ||
168 | LodePNG_inspect(p_decoder, image, file_size); | ||
1386 | 169 | ||
1387 | if (!decoder->error) { | 170 | if (!p_decoder->error) { |
1388 | 171 | ||
1389 | if (!running_slideshow) { | 172 | if (!running_slideshow) { |
1390 | rb->lcd_putsf(0, 2, "image %dx%d", | 173 | rb->lcd_putsf(0, 2, "image %dx%d", |
1391 | decoder->infoPng.width, decoder->infoPng.height); | 174 | p_decoder->infoPng.width, |
175 | p_decoder->infoPng.height); | ||
1392 | rb->lcd_putsf(0, 3, "decoding %d*%d", | 176 | rb->lcd_putsf(0, 3, "decoding %d*%d", |
1393 | decoder->infoPng.width, decoder->infoPng.height); | 177 | p_decoder->infoPng.width, |
178 | p_decoder->infoPng.height); | ||
1394 | rb->lcd_update(); | 179 | rb->lcd_update(); |
1395 | } | 180 | } |
1396 | 181 | ||
@@ -1398,16 +183,16 @@ int load_image(char *filename, struct image_info *info, | |||
1398 | time = *rb->current_tick; | 183 | time = *rb->current_tick; |
1399 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 184 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
1400 | rb->cpu_boost(true); | 185 | rb->cpu_boost(true); |
1401 | LodePNG_decode(decoder, image, image_size, cb_progress); | 186 | LodePNG_decode(p_decoder, image, file_size, cb_progress); |
1402 | rb->cpu_boost(false); | 187 | rb->cpu_boost(false); |
1403 | #else | 188 | #else |
1404 | LodePNG_decode(decoder, image, image_size, cb_progress); | 189 | LodePNG_decode(p_decoder, image, file_size, cb_progress); |
1405 | #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ | 190 | #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ |
1406 | time = *rb->current_tick - time; | 191 | time = *rb->current_tick - time; |
1407 | } | 192 | } |
1408 | } | 193 | } |
1409 | 194 | ||
1410 | if (!running_slideshow && !decoder->error) | 195 | if (!running_slideshow && !p_decoder->error) |
1411 | { | 196 | { |
1412 | rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); | 197 | rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); |
1413 | rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ | 198 | rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ |
@@ -1415,55 +200,59 @@ int load_image(char *filename, struct image_info *info, | |||
1415 | rb->lcd_update(); | 200 | rb->lcd_update(); |
1416 | } | 201 | } |
1417 | 202 | ||
1418 | if (decoder->error) { | 203 | if (p_decoder->error) { |
1419 | #ifdef USE_PLUG_BUF | 204 | #ifdef USE_PLUG_BUF |
1420 | if (plug_buf && (decoder->error == FILE_TOO_LARGE | 205 | if (plug_buf && (p_decoder->error == FILE_TOO_LARGE || |
1421 | || decoder->error == OUT_OF_MEMORY || decoder->error == Z_MEM_ERROR)) | 206 | p_decoder->error == OUT_OF_MEMORY || |
207 | p_decoder->error == TINF_DATA_ERROR)) | ||
1422 | return PLUGIN_OUTOFMEM; | 208 | return PLUGIN_OUTOFMEM; |
1423 | #endif | 209 | #endif |
1424 | 210 | ||
1425 | if (decoder->error >= PNG_ERROR_MIN && decoder->error <= PNG_ERROR_MAX | 211 | if (p_decoder->error >= PNG_ERROR_MIN && |
1426 | && png_error_messages[decoder->error-PNG_ERROR_MIN] != NULL) | 212 | p_decoder->error <= PNG_ERROR_MAX && |
213 | LodePNG_perror(p_decoder) != NULL) | ||
1427 | { | 214 | { |
1428 | rb->splash(HZ, png_error_messages[decoder->error-PNG_ERROR_MIN]); | 215 | rb->splash(HZ, LodePNG_perror(p_decoder)); |
1429 | } | 216 | } |
1430 | else | 217 | else |
1431 | { | 218 | { |
1432 | switch (decoder->error) { | 219 | switch (p_decoder->error) { |
1433 | case PLUGIN_ABORT: | 220 | case PLUGIN_ABORT: |
1434 | break; | 221 | break; |
1435 | case OUT_OF_MEMORY: | 222 | case OUT_OF_MEMORY: |
1436 | case Z_MEM_ERROR: | ||
1437 | rb->splash(HZ, "Out of Memory");break; | 223 | rb->splash(HZ, "Out of Memory");break; |
1438 | case FILE_TOO_LARGE: | 224 | case FILE_TOO_LARGE: |
1439 | rb->splash(HZ, "File too large");break; | 225 | rb->splash(HZ, "File too large");break; |
1440 | case Z_DATA_ERROR: | 226 | case TINF_DATA_ERROR: |
1441 | rb->splash(HZ, decoder->error_msg);break; | 227 | rb->splash(HZ, "Zlib decompressor error");break; |
1442 | default: | 228 | default: |
1443 | rb->splashf(HZ, "other error : %ld", decoder->error);break; | 229 | rb->splashf(HZ, "other error : %ld", p_decoder->error);break; |
1444 | } | 230 | } |
1445 | } | 231 | } |
1446 | 232 | ||
1447 | if (decoder->error == PLUGIN_ABORT) | 233 | if (p_decoder->error == PLUGIN_ABORT) |
1448 | return PLUGIN_ABORT; | 234 | return PLUGIN_ABORT; |
1449 | else | 235 | else |
1450 | return PLUGIN_ERROR; | 236 | return PLUGIN_ERROR; |
1451 | } | 237 | } |
1452 | 238 | ||
1453 | disp_buf = converted_image + converted_image_size; | 239 | info->x_size = p_decoder->infoPng.width; |
1454 | info->x_size = decoder->infoPng.width; | 240 | info->y_size = p_decoder->infoPng.height; |
1455 | info->y_size = decoder->infoPng.height; | 241 | |
242 | disp_buf = (fb_data *)(p_decoder->buf + p_decoder->native_img_size); | ||
243 | disp_buf = (fb_data *)ALIGN_UP((uintptr_t)disp_buf,4); | ||
1456 | *buf_size = memory_max - (unsigned char*)disp_buf; | 244 | *buf_size = memory_max - (unsigned char*)disp_buf; |
245 | |||
1457 | return PLUGIN_OK; | 246 | return PLUGIN_OK; |
1458 | } | 247 | } |
1459 | 248 | ||
1460 | int get_image(struct image_info *info, int ds) | 249 | int get_image(struct image_info *info, int ds) |
1461 | { | 250 | { |
1462 | fb_data **p_disp = &disp[ds]; /* short cut */ | 251 | fb_data **p_disp = &disp[ds]; /* short cut */ |
1463 | LodePNG_Decoder *decoder = &_decoder; | 252 | LodePNG_Decoder *p_decoder = &decoder; |
1464 | 253 | ||
1465 | info->width = decoder->infoPng.width / ds; | 254 | info->width = p_decoder->infoPng.width / ds; |
1466 | info->height = decoder->infoPng.height / ds; | 255 | info->height = p_decoder->infoPng.height / ds; |
1467 | info->data = p_disp; | 256 | info->data = p_disp; |
1468 | 257 | ||
1469 | if (*p_disp != NULL) | 258 | if (*p_disp != NULL) |
@@ -1483,34 +272,40 @@ int get_image(struct image_info *info, int ds) | |||
1483 | 272 | ||
1484 | int size = info->width * info->height; | 273 | int size = info->width * info->height; |
1485 | 274 | ||
1486 | if ((unsigned char *)(disp_buf + size) >= memory_max) { | 275 | if ((unsigned char *)(disp_buf + size) >= p_decoder->buf + p_decoder->buf_size) { |
1487 | /* have to discard the current */ | 276 | /* have to discard the current */ |
1488 | int i; | 277 | int i; |
1489 | for (i=1; i<=8; i++) | 278 | for (i=1; i<=8; i++) |
1490 | disp[i] = NULL; /* invalidate all bitmaps */ | 279 | disp[i] = NULL; /* invalidate all bitmaps */ |
280 | |||
1491 | /* start again from the beginning of the buffer */ | 281 | /* start again from the beginning of the buffer */ |
1492 | disp_buf = converted_image + converted_image_size; | 282 | disp_buf = (fb_data *)(p_decoder->buf + p_decoder->native_img_size); |
283 | disp_buf = (fb_data *)ALIGN_UP((uintptr_t)disp_buf,4); | ||
1493 | } | 284 | } |
1494 | 285 | ||
1495 | *p_disp = disp_buf; | 286 | *p_disp = disp_buf; |
287 | #ifdef USEGSLIB | ||
288 | disp_buf = (fb_data *)((unsigned char *)disp_buf + size); | ||
289 | #else | ||
1496 | disp_buf += size; | 290 | disp_buf += size; |
291 | #endif | ||
1497 | 292 | ||
1498 | bmp_src.width = decoder->infoPng.width; | 293 | bmp_src.width = p_decoder->infoPng.width; |
1499 | bmp_src.height = decoder->infoPng.height; | 294 | bmp_src.height = p_decoder->infoPng.height; |
1500 | bmp_src.data = (unsigned char *)converted_image; | 295 | bmp_src.data = (unsigned char *)p_decoder->buf; |
1501 | 296 | ||
1502 | bmp_dst.width = info->width; | 297 | bmp_dst.width = info->width; |
1503 | bmp_dst.height = info->height; | 298 | bmp_dst.height = info->height; |
1504 | bmp_dst.data = (unsigned char *)*p_disp; | 299 | bmp_dst.data = (unsigned char *)*p_disp; |
1505 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 300 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
1506 | rb->cpu_boost(true); | 301 | rb->cpu_boost(true); |
1507 | smooth_resize_bitmap(&bmp_src, &bmp_dst); | 302 | resize_bitmap(&bmp_src, &bmp_dst); |
1508 | rb->cpu_boost(false); | 303 | rb->cpu_boost(false); |
1509 | #else | 304 | #else |
1510 | smooth_resize_bitmap(&bmp_src, &bmp_dst); | 305 | resize_bitmap(&bmp_src, &bmp_dst); |
1511 | #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ | 306 | #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/ |
1512 | } else { | 307 | } else { |
1513 | *p_disp = converted_image; | 308 | *p_disp = (fb_data *)p_decoder->buf; |
1514 | } | 309 | } |
1515 | 310 | ||
1516 | return PLUGIN_OK; | 311 | return PLUGIN_OK; |