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