1 /*
2 (c) Copyright 2001-2010 The world wide DirectFB Open Source Community (directfb.org)
3 (c) Copyright 2000-2004 Convergence (integrated media) GmbH
4
5 All rights reserved.
6
7 Written by Denis Oliver Kropp <dok@directfb.org>,
8 Andreas Hundt <andi@fischlustig.de>,
9 Sven Neumann <neo@directfb.org>,
10 Ville Syrjälä <syrjala@sci.fi> and
11 Claudio Ciccani <klan@users.sf.net>.
12
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the
25 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 Boston, MA 02111-1307, USA.
27 */
28
29 #include <config.h>
30
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <png.h>
36 #include <string.h>
37 #include <stdarg.h>
38
39 #include <directfb.h>
40
41 #include <display/idirectfbsurface.h>
42
43 #include <media/idirectfbimageprovider.h>
44
45 #include <core/coredefs.h>
46 #include <core/coretypes.h>
47
48 #include <core/layers.h>
49 #include <core/palette.h>
50 #include <core/surface.h>
51
52 #include <core/CoreSurface.h>
53
54 #include <misc/gfx_util.h>
55 #include <misc/util.h>
56
57 #include <gfx/clip.h>
58 #include <gfx/convert.h>
59
60 #include <direct/interface.h>
61 #include <direct/mem.h>
62 #include <direct/memcpy.h>
63 #include <direct/messages.h>
64 #include <direct/util.h>
65
66 #include "config.h"
67
68 D_DEBUG_DOMAIN( imageProviderPNG, "ImageProvider/PNG", "libPNG based image decoder" );
69
70 #if PNG_LIBPNG_VER < 10400
71 #define trans_color trans_values
72 #define trans_alpha trans
73 #endif
74
75 static DFBResult
76 Probe( IDirectFBImageProvider_ProbeContext *ctx );
77
78 static DFBResult
79 Construct( IDirectFBImageProvider *thiz,
80 ... );
81
82 #include <direct/interface_implementation.h>
83
84 DIRECT_INTERFACE_IMPLEMENTATION( IDirectFBImageProvider, PNG )
85
86
87 enum {
88 STAGE_ABORT = -2,
89 STAGE_ERROR = -1,
90 STAGE_START = 0,
91 STAGE_INFO,
92 STAGE_IMAGE,
93 STAGE_END
94 };
95
96 /*
97 * private data struct of IDirectFBImageProvider_PNG
98 */
99 typedef struct {
100 IDirectFBImageProvider_data base;
101
102 int stage;
103 int rows;
104
105 png_structp png_ptr;
106 png_infop info_ptr;
107
108 png_int_32 width;
109 png_int_32 height;
110 int bpp;
111 int color_type;
112 png_uint_32 color_key;
113 bool color_keyed;
114
115 void *image;
116 int pitch;
117 u32 palette[256];
118 DFBColor colors[256];
119 } IDirectFBImageProvider_PNG_data;
120
121
122 static DFBResult
123 IDirectFBImageProvider_PNG_RenderTo( IDirectFBImageProvider *thiz,
124 IDirectFBSurface *destination,
125 const DFBRectangle *destination_rect );
126
127 static DFBResult
128 IDirectFBImageProvider_PNG_GetSurfaceDescription( IDirectFBImageProvider *thiz,
129 DFBSurfaceDescription *dsc );
130
131 static DFBResult
132 IDirectFBImageProvider_PNG_GetImageDescription( IDirectFBImageProvider *thiz,
133 DFBImageDescription *dsc );
134
135 /* Called at the start of the progressive load, once we have image info */
136 static void
137 png_info_callback (png_structp png_read_ptr,
138 png_infop png_info_ptr);
139
140 /* Called for each row; note that you will get duplicate row numbers
141 for interlaced PNGs */
142 static void
143 png_row_callback (png_structp png_read_ptr,
144 png_bytep new_row,
145 png_uint_32 row_num,
146 int pass_num);
147
148 /* Called after reading the entire image */
149 static void
150 png_end_callback (png_structp png_read_ptr,
151 png_infop png_info_ptr);
152
153 /* Pipes data into libpng until stage is different from the one specified. */
154 static DFBResult
155 push_data_until_stage (IDirectFBImageProvider_PNG_data *data,
156 int stage,
157 int buffer_size);
158
159 /**********************************************************************************************************************/
160
161 static void
IDirectFBImageProvider_PNG_Destruct(IDirectFBImageProvider * thiz)162 IDirectFBImageProvider_PNG_Destruct( IDirectFBImageProvider *thiz )
163 {
164 IDirectFBImageProvider_PNG_data *data =
165 (IDirectFBImageProvider_PNG_data*)thiz->priv;
166
167 png_destroy_read_struct( &data->png_ptr, &data->info_ptr, NULL );
168
169 /* Deallocate image data. */
170 if (data->image)
171 D_FREE( data->image );
172 }
173
174 /**********************************************************************************************************************/
175
176 static DFBResult
Probe(IDirectFBImageProvider_ProbeContext * ctx)177 Probe( IDirectFBImageProvider_ProbeContext *ctx )
178 {
179 if (!png_sig_cmp( ctx->header, 0, 8 ))
180 return DFB_OK;
181
182 return DFB_UNSUPPORTED;
183 }
184
185 static DFBResult
Construct(IDirectFBImageProvider * thiz,...)186 Construct( IDirectFBImageProvider *thiz,
187 ... )
188 {
189 DFBResult ret = DFB_FAILURE;
190
191 IDirectFBDataBuffer *buffer;
192 CoreDFB *core;
193 va_list tag;
194
195 D_DEBUG_AT(imageProviderPNG,"%s(%d)\n",__FUNCTION__,__LINE__);
196
197 DIRECT_ALLOCATE_INTERFACE_DATA(thiz, IDirectFBImageProvider_PNG)
198
199 va_start( tag, thiz );
200 buffer = va_arg( tag, IDirectFBDataBuffer * );
201 core = va_arg( tag, CoreDFB * );
202 va_end( tag );
203
204 data->base.ref = 1;
205 data->base.buffer = buffer;
206 data->base.core = core;
207
208 /* Increase the data buffer reference counter. */
209 buffer->AddRef( buffer );
210
211 /* Create the PNG read handle. */
212 data->png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
213 NULL, NULL, NULL );
214 if (!data->png_ptr)
215 goto error;
216
217 if (setjmp( png_jmpbuf(data->png_ptr) )) {
218 D_ERROR( "ImageProvider/PNG: Error reading header!\n" );
219 goto error;
220 }
221
222 /* Create the PNG info handle. */
223 data->info_ptr = png_create_info_struct( data->png_ptr );
224 if (!data->info_ptr)
225 goto error;
226
227 /* Setup progressive image loading. */
228 png_set_progressive_read_fn( data->png_ptr, data,
229 png_info_callback,
230 png_row_callback,
231 png_end_callback );
232
233
234 /* Read until info callback is called. */
235 ret = push_data_until_stage( data, STAGE_INFO, 64 );
236 if (ret)
237 goto error;
238
239 data->base.Destruct = IDirectFBImageProvider_PNG_Destruct;
240
241 thiz->RenderTo = IDirectFBImageProvider_PNG_RenderTo;
242 thiz->GetImageDescription = IDirectFBImageProvider_PNG_GetImageDescription;
243 thiz->GetSurfaceDescription = IDirectFBImageProvider_PNG_GetSurfaceDescription;
244
245 return DFB_OK;
246
247 error:
248 if (data->png_ptr)
249 png_destroy_read_struct( &data->png_ptr, &data->info_ptr, NULL );
250
251 buffer->Release( buffer );
252
253 if (data->image)
254 D_FREE( data->image );
255
256 DIRECT_DEALLOCATE_INTERFACE(thiz);
257
258 return ret;
259 }
260
261 /**********************************************************************************************************************/
262
263 static DFBResult
IDirectFBImageProvider_PNG_RenderTo(IDirectFBImageProvider * thiz,IDirectFBSurface * destination,const DFBRectangle * dest_rect)264 IDirectFBImageProvider_PNG_RenderTo( IDirectFBImageProvider *thiz,
265 IDirectFBSurface *destination,
266 const DFBRectangle *dest_rect )
267 {
268 DFBResult ret = DFB_OK;
269 IDirectFBSurface_data *dst_data;
270 CoreSurface *dst_surface;
271 DFBRegion clip;
272 DFBRectangle rect;
273 png_infop info;
274 int x, y;
275 DFBRectangle clipped;
276
277 DIRECT_INTERFACE_GET_DATA (IDirectFBImageProvider_PNG)
278
279 D_DEBUG_AT(imageProviderPNG,"%s(%d)\n",__FUNCTION__,__LINE__);
280
281 info = data->info_ptr;
282
283 dst_data = (IDirectFBSurface_data*) destination->priv;
284 if (!dst_data)
285 return DFB_DEAD;
286
287 dst_surface = dst_data->surface;
288 if (!dst_surface)
289 return DFB_DESTROYED;
290
291 dfb_region_from_rectangle( &clip, &dst_data->area.current );
292
293 if (dest_rect) {
294 if (dest_rect->w < 1 || dest_rect->h < 1)
295 return DFB_INVARG;
296 rect = *dest_rect;
297 rect.x += dst_data->area.wanted.x;
298 rect.y += dst_data->area.wanted.y;
299 }
300 else {
301 rect = dst_data->area.wanted;
302 }
303
304 if (setjmp( png_jmpbuf(data->png_ptr) )) {
305 D_ERROR( "ImageProvider/PNG: Error during decoding!\n" );
306
307 if (data->stage < STAGE_IMAGE)
308 return DFB_FAILURE;
309
310 data->stage = STAGE_ERROR;
311 }
312
313 /* Read until image is completely decoded. */
314 if (data->stage != STAGE_ERROR) {
315 ret = push_data_until_stage( data, STAGE_END, 16384 );
316 if (ret)
317 return ret;
318 }
319
320 clipped = rect;
321
322 if (!dfb_rectangle_intersect_by_region( &clipped, &clip ))
323 return DFB_INVAREA;
324
325 /* actual rendering */
326 if (0 && // FIXME
327 rect.w == data->width && rect.h == data->height &&
328 (data->color_type == PNG_COLOR_TYPE_RGB || data->color_type == PNG_COLOR_TYPE_RGBA) &&
329 (dst_surface->config.format == DSPF_RGB32 || dst_surface->config.format == DSPF_ARGB) &&
330 !(dst_surface->config.caps & DSCAPS_PREMULTIPLIED))
331 {
332 //ret = dfb_surface_write_buffer( dst_surface, CSBR_BACK,
333 // data->image +
334 // (clipped.x - rect.x) * 4 +
335 // (clipped.y - rect.y) * data->width * 4,
336 // data->width * 4, &clipped );
337 }
338 else {
339 CoreSurfaceBufferLock lock;
340
341 int bit_depth = bit_depth = png_get_bit_depth(data->png_ptr,data->info_ptr);
342
343 ret = dfb_surface_lock_buffer( dst_surface, CSBR_BACK, CSAID_CPU, CSAF_WRITE, &lock );
344 if (ret)
345 return ret;
346
347 switch (data->color_type) {
348 case PNG_COLOR_TYPE_PALETTE:
349 if (dst_surface->config.format == DSPF_LUT8 && bit_depth == 8) {
350 /*
351 * Special indexed PNG to LUT8 loading.
352 */
353
354 /* FIXME: Limitation for LUT8 is to load complete surface only. */
355 dfb_clip_rectangle( &clip, &rect );
356 if (rect.x == 0 && rect.y == 0 &&
357 rect.w == dst_surface->config.size.w &&
358 rect.h == dst_surface->config.size.h &&
359 rect.w == data->width &&
360 rect.h == data->height)
361 {
362 for (y=0; y<data->height; y++)
363 direct_memcpy( (u8*)lock.addr + lock.pitch * y,
364 (u8*)data->image + data->pitch * y,
365 data->width );
366
367 break;
368 }
369 }
370 /* fall through */
371
372 case PNG_COLOR_TYPE_GRAY: {
373 /*
374 * Convert to ARGB and use generic loading code.
375 */
376 if (data->bpp == 16) {
377 /* in 16 bit grayscale, conversion to RGB32 is already done! */
378
379 for (x=0; x<256; x++)
380 data->palette[x] = 0xff000000 | (x << 16) | (x << 8) | x;
381
382 dfb_scale_linear_32( data->image, data->width, data->height,
383 lock.addr, lock.pitch, &rect, dst_surface, &clip );
384 break;
385 }
386
387 // FIXME: allocates four additional bytes because the scaling functions
388 // in src/misc/gfx_util.c have an off-by-one bug which causes
389 // segfaults on darwin/osx (not on linux)
390 int size = data->width * data->height * 4 + 4;
391
392 /* allocate image data */
393 void *image_argb = D_MALLOC( size );
394
395 if (!image_argb) {
396 D_ERROR( "DirectFB/ImageProvider_PNG: Could not "
397 "allocate %d bytes of system memory!\n", size );
398 ret = DFB_NOSYSTEMMEMORY;
399 }
400 else {
401 if (data->color_type == PNG_COLOR_TYPE_GRAY) {
402 int num = 1 << bit_depth;
403
404 for (x=0; x<num; x++) {
405 int value = x * 255 / (num - 1);
406
407 data->palette[x] = 0xff000000 | (value << 16) | (value << 8) | value;
408 }
409 }
410
411 switch (bit_depth) {
412 case 8:
413 for (y=0; y<data->height; y++) {
414 u8 *S = (u8*)data->image + data->pitch * y;
415 u32 *D = (u32*)((u8*)image_argb + data->width * y * 4);
416
417 for (x=0; x<data->width; x++)
418 D[x] = data->palette[ S[x] ];
419 }
420 break;
421
422 case 4:
423 for (y=0; y<data->height; y++) {
424 u8 *S = (u8*)data->image + data->pitch * y;
425 u32 *D = (u32*)((u8*)image_argb + data->width * y * 4);
426
427 for (x=0; x<data->width; x++) {
428 if (x & 1)
429 D[x] = data->palette[ S[x>>1] & 0xf ];
430 else
431 D[x] = data->palette[ S[x>>1] >> 4 ];
432 }
433 }
434 break;
435
436 case 2:
437 for (y=0; y<data->height; y++) {
438 int n = 6;
439 u8 *S = (u8*)data->image + data->pitch * y;
440 u32 *D = (u32*)((u8*)image_argb + data->width * y * 4);
441
442 for (x=0; x<data->width; x++) {
443 D[x] = data->palette[ (S[x>>2] >> n) & 3 ];
444
445 n = (n ? n - 2 : 6);
446 }
447 }
448 break;
449
450 case 1:
451 for (y=0; y<data->height; y++) {
452 int n = 7;
453 u8 *S = (u8*)data->image + data->pitch * y;
454 u32 *D = (u32*)((u8*)image_argb + data->width * y * 4);
455
456 for (x=0; x<data->width; x++) {
457 D[x] = data->palette[ (S[x>>3] >> n) & 1 ];
458
459 n = (n ? n - 1 : 7);
460 }
461 }
462 break;
463
464 default:
465 D_ERROR( "ImageProvider/PNG: Unsupported indexed bit depth %d!\n",
466 bit_depth );
467 }
468
469 dfb_scale_linear_32( image_argb, data->width, data->height,
470 lock.addr, lock.pitch, &rect, dst_surface, &clip );
471
472 D_FREE( image_argb );
473 }
474 break;
475 }
476 default:
477 /*
478 * Generic loading code.
479 */
480 dfb_scale_linear_32( data->image, data->width, data->height,
481 lock.addr, lock.pitch, &rect, dst_surface, &clip );
482 break;
483 }
484
485 dfb_surface_unlock_buffer( dst_surface, &lock );
486 }
487
488 if (data->stage != STAGE_END)
489 ret = DFB_INCOMPLETE;
490
491 return ret;
492 }
493
494 static DFBResult
IDirectFBImageProvider_PNG_GetSurfaceDescription(IDirectFBImageProvider * thiz,DFBSurfaceDescription * dsc)495 IDirectFBImageProvider_PNG_GetSurfaceDescription( IDirectFBImageProvider *thiz,
496 DFBSurfaceDescription *dsc )
497 {
498 DFBSurfacePixelFormat primary_format = dfb_primary_layer_pixelformat();
499
500 DIRECT_INTERFACE_GET_DATA (IDirectFBImageProvider_PNG)
501
502 dsc->flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
503 dsc->width = data->width;
504 dsc->height = data->height;
505
506 if (data->color_type & PNG_COLOR_MASK_ALPHA)
507 dsc->pixelformat = DFB_PIXELFORMAT_HAS_ALPHA(primary_format) ? primary_format : DSPF_ARGB;
508 else
509 dsc->pixelformat = primary_format;
510
511 if (data->color_type == PNG_COLOR_TYPE_PALETTE) {
512 dsc->flags |= DSDESC_PALETTE;
513
514 dsc->palette.entries = data->colors; /* FIXME */
515 dsc->palette.size = 256;
516 }
517
518 return DFB_OK;
519 }
520
521 static DFBResult
IDirectFBImageProvider_PNG_GetImageDescription(IDirectFBImageProvider * thiz,DFBImageDescription * dsc)522 IDirectFBImageProvider_PNG_GetImageDescription( IDirectFBImageProvider *thiz,
523 DFBImageDescription *dsc )
524 {
525 DIRECT_INTERFACE_GET_DATA(IDirectFBImageProvider_PNG)
526
527 if (!dsc)
528 return DFB_INVARG;
529
530 dsc->caps = DICAPS_NONE;
531
532 if (data->color_type & PNG_COLOR_MASK_ALPHA)
533 dsc->caps |= DICAPS_ALPHACHANNEL;
534
535 if (data->color_keyed) {
536 dsc->caps |= DICAPS_COLORKEY;
537
538 dsc->colorkey_r = (data->color_key & 0xff0000) >> 16;
539 dsc->colorkey_g = (data->color_key & 0x00ff00) >> 8;
540 dsc->colorkey_b = (data->color_key & 0x0000ff);
541 }
542
543 return DFB_OK;
544 }
545
546 /**********************************************************************************************************************/
547
548 #define MAXCOLORMAPSIZE 256
549
SortColors(const void * a,const void * b)550 static int SortColors (const void *a, const void *b)
551 {
552 return (*((const u8 *) a) - *((const u8 *) b));
553 }
554
555 /* looks for a color that is not in the colormap and ideally not
556 even close to the colors used in the colormap */
FindColorKey(int n_colors,u8 * cmap)557 static u32 FindColorKey( int n_colors, u8 *cmap )
558 {
559 u32 color = 0xFF000000;
560 u8 csort[n_colors];
561 int i, j, index, d;
562
563 if (n_colors < 1)
564 return color;
565
566 for (i = 0; i < 3; i++) {
567 direct_memcpy( csort, cmap + (n_colors * i), n_colors );
568 qsort( csort, n_colors, 1, SortColors );
569
570 for (j = 1, index = 0, d = 0; j < n_colors; j++) {
571 if (csort[j] - csort[j-1] > d) {
572 d = csort[j] - csort[j-1];
573 index = j;
574 }
575 }
576 if ((csort[0] - 0x0) > d) {
577 d = csort[0] - 0x0;
578 index = n_colors;
579 }
580 if (0xFF - (csort[n_colors - 1]) > d) {
581 index = n_colors + 1;
582 }
583
584 if (index < n_colors)
585 csort[0] = csort[index] - (d/2);
586 else if (index == n_colors)
587 csort[0] = 0x0;
588 else
589 csort[0] = 0xFF;
590
591 color |= (csort[0] << (8 * (2 - i)));
592 }
593
594 return color;
595 }
596
597 /* Called at the start of the progressive load, once we have image info */
598 static void
png_info_callback(png_structp png_read_ptr,png_infop png_info_ptr)599 png_info_callback( png_structp png_read_ptr,
600 png_infop png_info_ptr )
601 {
602 int i,ret;
603 IDirectFBImageProvider_PNG_data *data;
604
605 u32 bpp1[2] = {0, 0xff};
606 u32 bpp2[4] = {0, 0x55, 0xaa, 0xff};
607 u32 bpp4[16] = {0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
608
609 D_UNUSED_P( png_info_ptr );
610
611 D_DEBUG_AT(imageProviderPNG,"%s(%d)\n",__FUNCTION__,__LINE__);
612
613 data = png_get_progressive_ptr( png_read_ptr );
614
615 /* error stage? */
616 if (data->stage < 0)
617 return;
618
619 /* set info stage */
620 data->stage = STAGE_INFO;
621
622 ret = png_get_IHDR( data->png_ptr, data->info_ptr,
623 (png_uint_32 *)&data->width, (png_uint_32 *)&data->height, &data->bpp, &data->color_type,
624 NULL, NULL, NULL );
625
626 /* Let's not do anything with badly sized or corrupted images */
627 if ( (data->height == 0) || (data->width == 0) || (ret != 1) )
628 return;
629
630 if (png_get_valid( data->png_ptr, data->info_ptr, PNG_INFO_tRNS )) {
631 data->color_keyed = true;
632
633 /* generate color key based on palette... */
634 if (data->color_type == PNG_COLOR_TYPE_PALETTE) {
635 u32 key;
636 png_colorp palette;
637 png_bytep trans_alpha;
638 png_color_16p trans_color;
639 u8 cmap[3][MAXCOLORMAPSIZE];
640 int num_palette = 0, num_colors = 0, num_trans = 0;
641
642 D_DEBUG_AT(imageProviderPNG,"%s(%d) - num_trans %d \n",__FUNCTION__,__LINE__, num_trans);
643
644 if (png_get_PLTE(data->png_ptr,data->info_ptr,&palette,&num_palette)) {
645
646 if (png_get_tRNS(data->png_ptr,data->info_ptr,
647 &trans_alpha,&num_trans,&trans_color)) {
648 num_colors = MIN( MAXCOLORMAPSIZE,num_palette );
649
650 for (i=0; i<num_colors; i++) {
651 cmap[0][i] = palette[i].red;
652 cmap[1][i] = palette[i].green;
653 cmap[2][i] = palette[i].blue;
654 }
655
656 key = FindColorKey( num_colors, &cmap[0][0] );
657
658 for (i=0; i< num_trans; i++) {
659 if (!trans_alpha[i]) {
660 palette[i].red = (key & 0xff0000) >> 16;
661 palette[i].green = (key & 0x00ff00) >> 8;
662 palette[i].blue = (key & 0x0000ff);
663 }
664 }
665
666 data->color_key = key;
667 }
668 }
669
670 }
671 else if (data->color_type == PNG_COLOR_TYPE_GRAY) {
672 /* ...or based on trans gray value */
673 png_bytep trans_alpha;
674 png_color_16p trans_color;
675 int num_trans = 0;
676
677
678 D_DEBUG_AT(imageProviderPNG,"%s(%d)\n",__FUNCTION__,__LINE__);
679
680 if (png_get_tRNS(data->png_ptr,data->info_ptr,
681 &trans_alpha,&num_trans,&trans_color) ) {
682 switch(data->bpp) {
683 case 1:
684 data->color_key = (((bpp1[trans_color[0].gray]) << 16) |
685 ((bpp1[trans_color[0].gray]) << 8) |
686 ((bpp1[trans_color[0].gray])));
687 break;
688 case 2:
689 data->color_key = (((bpp2[trans_color[0].gray]) << 16) |
690 ((bpp2[trans_color[0].gray]) << 8) |
691 ((bpp2[trans_color[0].gray])));
692 break;
693 case 4:
694 data->color_key = (((bpp4[trans_color[0].gray]) << 16) |
695 ((bpp4[trans_color[0].gray]) << 8) |
696 ((bpp4[trans_color[0].gray])));
697 break;
698 case 8:
699 data->color_key = (((trans_color[0].gray & 0x00ff) << 16) |
700 ((trans_color[0].gray & 0x00ff) << 8) |
701 ((trans_color[0].gray & 0x00ff)));
702 break;
703 case 16:
704 default:
705 data->color_key = (((trans_color[0].gray & 0xff00) << 8) |
706 ((trans_color[0].gray & 0xff00)) |
707 ((trans_color[0].gray & 0xff00) >> 8));
708 break;
709 }
710 }
711 }
712 else {
713 /* ...or based on trans rgb value */
714 png_bytep trans_alpha;
715 png_color_16p trans_color;
716 int num_trans = 0;
717
718 D_DEBUG_AT(imageProviderPNG,"%s(%d)\n",__FUNCTION__,__LINE__);
719
720 if (png_get_tRNS(data->png_ptr,data->info_ptr,
721 &trans_alpha,&num_trans,&trans_color)) {
722 switch(data->bpp) {
723 case 1:
724 data->color_key = (((bpp1[trans_color[0].red]) << 16) |
725 ((bpp1[trans_color[0].green]) << 8) |
726 ((bpp1[trans_color[0].blue])));
727 break;
728 case 2:
729 data->color_key = (((bpp2[trans_color[0].red]) << 16) |
730 ((bpp2[trans_color[0].green]) << 8) |
731 ((bpp2[trans_color[0].blue])));
732 break;
733 case 4:
734 data->color_key = (((bpp4[trans_color[0].red]) << 16) |
735 ((bpp4[trans_color[0].green]) << 8) |
736 ((bpp4[trans_color[0].blue])));
737 break;
738 case 8:
739 data->color_key = (((trans_color[0].red & 0x00ff) << 16) |
740 ((trans_color[0].green & 0x00ff) << 8) |
741 ((trans_color[0].blue & 0x00ff)));
742 break;
743 case 16:
744 default:
745 data->color_key = (((trans_color[0].red & 0xff00) << 8) |
746 ((trans_color[0].green & 0xff00)) |
747 ((trans_color[0].blue & 0xff00) >> 8));
748 break;
749 }
750 }
751 }
752 }
753
754 switch (data->color_type) {
755 case PNG_COLOR_TYPE_PALETTE: {
756 png_colorp palette;
757 png_bytep trans_alpha;
758 png_color_16p trans_color;
759 int num_palette = 0, num_colors = 0, num_trans = 0;
760
761
762 png_get_PLTE(data->png_ptr,data->info_ptr,&palette,&num_palette);
763
764 png_get_tRNS(data->png_ptr,data->info_ptr,
765 &trans_alpha,&num_trans,&trans_color);
766
767 num_colors = MIN( MAXCOLORMAPSIZE, num_palette );
768
769 for (i=0; i < num_colors; i++) {
770 data->colors[i].a = (i < num_trans) ? trans_alpha[i] : 0xff;
771 data->colors[i].r = palette[i].red;
772 data->colors[i].g = palette[i].green;
773 data->colors[i].b = palette[i].blue;
774
775 data->palette[i] = PIXEL_ARGB( data->colors[i].a,
776 data->colors[i].r,
777 data->colors[i].g,
778 data->colors[i].b );
779 }
780
781 data->pitch = (data->width + 7) & ~7;
782 break;
783 }
784
785 case PNG_COLOR_TYPE_GRAY:
786 if (data->bpp < 16) {
787 data->pitch = data->width;
788 break;
789 }
790
791 /* fall through */
792 case PNG_COLOR_TYPE_GRAY_ALPHA:
793 png_set_gray_to_rgb( data->png_ptr );
794
795 /* fall through */
796 default:
797 data->pitch = data->width * 4;
798
799 if (!data->color_keyed)
800 png_set_strip_16( data->png_ptr ); /* if it is color keyed we will handle conversion ourselves */
801
802 #ifdef WORDS_BIGENDIAN
803 if (!(data->color_type & PNG_COLOR_MASK_ALPHA))
804 png_set_filler( data->png_ptr, 0xFF, PNG_FILLER_BEFORE );
805
806 png_set_swap_alpha( data->png_ptr );
807 #else
808 if (!(data->color_type & PNG_COLOR_MASK_ALPHA))
809 png_set_filler( data->png_ptr, 0xFF, PNG_FILLER_AFTER );
810
811 png_set_bgr( data->png_ptr );
812 #endif
813 break;
814 }
815
816 png_set_interlace_handling( data->png_ptr );
817
818 /* Update the info to reflect our transformations */
819 png_read_update_info( data->png_ptr, data->info_ptr );
820 }
821
822 /* Called for each row; note that you will get duplicate row numbers
823 for interlaced PNGs */
824 static void
png_row_callback(png_structp png_read_ptr,png_bytep new_row,png_uint_32 row_num,int pass_num)825 png_row_callback( png_structp png_read_ptr,
826 png_bytep new_row,
827 png_uint_32 row_num,
828 int pass_num )
829 {
830 IDirectFBImageProvider_PNG_data *data;
831
832 D_DEBUG_AT(imageProviderPNG,"%s(%d)\n",__FUNCTION__,__LINE__);
833
834 data = png_get_progressive_ptr( png_read_ptr );
835
836 /* error stage? */
837 if (data->stage < 0)
838 return;
839
840 D_UNUSED_P( pass_num );
841
842 /* set image decoding stage */
843 data->stage = STAGE_IMAGE;
844
845 /* check image data pointer */
846 if (!data->image) {
847 // FIXME: allocates four additional bytes because the scaling functions
848 // in src/misc/gfx_util.c have an off-by-one bug which causes
849 // segfaults on darwin/osx (not on linux)
850 int size = data->pitch * data->height + 4;
851
852 /* allocate image data */
853 data->image = D_CALLOC( 1, size );
854 if (!data->image) {
855 D_ERROR("DirectFB/ImageProvider_PNG: Could not "
856 "allocate %d bytes of system memory!\n", size);
857
858 /* set error stage */
859 data->stage = STAGE_ERROR;
860
861 return;
862 }
863 }
864
865 /* write to image data */
866 if (data->bpp == 16 && data->color_keyed) {
867 u8 *dst = (u8*)((u8*)data->image + row_num * data->pitch);
868 u8 *src = (u8*)new_row;
869
870 if (src) {
871 int src_advance = 8;
872 int src16_advance = 4;
873 int dst32_advance = 1;
874 int src16_initial_offset = 0;
875 int dst32_initial_offset = 0;
876
877 if (!(row_num % 2)) { /* even lines 0,2,4 ... */
878 switch (pass_num) {
879 case 1:
880 dst32_initial_offset = 4;
881 src16_initial_offset = 16;
882 src_advance = 64;
883 src16_advance = 32;
884 dst32_advance = 8;
885 break;
886 case 3:
887 dst32_initial_offset = 2;
888 src16_initial_offset = 8;
889 src_advance = 32;
890 src16_advance = 16;
891 dst32_advance = 4;
892 break;
893 case 5:
894 dst32_initial_offset = 1;
895 src16_initial_offset = 4;
896 src_advance = 16;
897 src16_advance = 8;
898 dst32_advance = 2;
899 break;
900 default:
901 break;
902 }
903 }
904
905
906 png_bytep trans;
907 png_color_16p trans_color;
908 int num_trans = 0;
909
910 png_get_tRNS(data->png_ptr,data->info_ptr,
911 &trans,&num_trans,&trans_color);
912
913 u16 *src16 = (u16*)src + src16_initial_offset;
914 u32 *dst32 = (u32*)dst + dst32_initial_offset;
915
916 int remaining = data->width - dst32_initial_offset;
917
918 while (remaining > 0) {
919 int keyed = 0;
920 #ifdef WORDS_BIGENDIAN
921 u16 comp_r = src16[1];
922 u16 comp_g = src16[2];
923 u16 comp_b = src16[3];
924 u32 pixel32 = src[1] << 24 | src[3] << 16 | src[5] << 8 | src[7];
925 #else
926 u16 comp_r = src16[2];
927 u16 comp_g = src16[1];
928 u16 comp_b = src16[0];
929
930 u32 pixel32 = src[6] << 24 | src[4] << 16 | src[2] << 8 | src[0];
931 #endif
932 /* is the pixel supposted to match the color key in 16 bit per channel resolution? */
933 if (((comp_r == trans_color[0].gray) && (data->color_type == PNG_COLOR_TYPE_GRAY)) ||
934 ((comp_g == trans_color[0].green) && (comp_b == trans_color[0].blue) && (comp_r == trans_color[0].red)))
935 keyed = 1;
936
937 /*
938 * if the pixel was not supposed to get keyed but the colorkey matches in the reduced
939 * color space, then toggle the least significant blue bit
940 */
941 if (!keyed && (pixel32 == (0xff000000 | data->color_key))) {
942 D_ONCE( "ImageProvider/PNG: adjusting pixel data to protect it from being keyed!\n");
943 pixel32 ^= 0x00000001;
944 }
945
946 *dst32 = pixel32;
947
948 src16 += src16_advance;
949 src += src_advance;
950 dst32 += dst32_advance;
951 remaining-= dst32_advance;
952 }
953 }
954 }
955 else
956 png_progressive_combine_row( data->png_ptr, (png_bytep)((u8*)data->image + row_num * data->pitch), new_row );
957
958 /* increase row counter, FIXME: interlaced? */
959 data->rows++;
960
961 if (data->base.render_callback) {
962 DIRenderCallbackResult r;
963 DFBRectangle rect = { 0, row_num, data->width, 1 };
964
965 r = data->base.render_callback( &rect,
966 data->base.render_callback_context );
967 if (r != DIRCR_OK)
968 data->stage = STAGE_ABORT;
969 }
970 }
971
972 /* Called after reading the entire image */
973 static void
png_end_callback(png_structp png_read_ptr,png_infop png_info_ptr)974 png_end_callback (png_structp png_read_ptr,
975 png_infop png_info_ptr)
976 {
977 IDirectFBImageProvider_PNG_data *data;
978
979 D_UNUSED_P( png_info_ptr );
980
981 D_DEBUG_AT(imageProviderPNG,"%s(%d)\n",__FUNCTION__,__LINE__);
982
983 data = png_get_progressive_ptr( png_read_ptr );
984
985 /* error stage? */
986 if (data->stage < 0)
987 return;
988
989 /* set end stage */
990 data->stage = STAGE_END;
991 }
992
993 /* Pipes data into libpng until stage is different from the one specified. */
994 static DFBResult
push_data_until_stage(IDirectFBImageProvider_PNG_data * data,int stage,int buffer_size)995 push_data_until_stage (IDirectFBImageProvider_PNG_data *data,
996 int stage,
997 int buffer_size)
998 {
999 DFBResult ret;
1000 IDirectFBDataBuffer *buffer = data->base.buffer;
1001
1002 while (data->stage < stage) {
1003 unsigned int len;
1004 unsigned char buf[buffer_size];
1005
1006 if (data->stage < 0)
1007 return DFB_FAILURE;
1008
1009 while (buffer->HasData( buffer ) == DFB_OK) {
1010 D_DEBUG_AT(imageProviderPNG, "Retrieving data (up to %d bytes)...\n", buffer_size );
1011
1012 ret = buffer->GetData( buffer, buffer_size, buf, &len );
1013 if (ret)
1014 return ret;
1015
1016 D_DEBUG_AT(imageProviderPNG, "Got %d bytes...\n", len );
1017
1018 png_process_data( data->png_ptr, data->info_ptr, buf, len );
1019
1020 D_DEBUG_AT(imageProviderPNG, "...processed %d bytes.\n", len );
1021
1022 /* are we there yet? */
1023 if (data->stage < 0 || data->stage >= stage) {
1024 switch (data->stage) {
1025 case STAGE_ABORT: return DFB_INTERRUPTED;
1026 case STAGE_ERROR: return DFB_FAILURE;
1027 default: return DFB_OK;
1028 }
1029 }
1030 }
1031
1032 D_DEBUG_AT(imageProviderPNG, "Waiting for data...\n" );
1033
1034 if (buffer->WaitForData( buffer, 1 ) == DFB_EOF)
1035 return DFB_FAILURE;
1036 }
1037
1038 return DFB_OK;
1039 }
1040