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 <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <stdarg.h>
37 
38 #include <directfb.h>
39 
40 #include <display/idirectfbsurface.h>
41 
42 #include <media/idirectfbimageprovider.h>
43 
44 #include <core/layers.h>
45 
46 #include <core/CoreSurface.h>
47 
48 #include <misc/gfx_util.h>
49 #include <direct/interface.h>
50 #include <direct/mem.h>
51 #include <direct/memcpy.h>
52 #include <misc/util.h>
53 
54 static DFBResult
55 Probe( IDirectFBImageProvider_ProbeContext *ctx );
56 
57 static DFBResult
58 Construct( IDirectFBImageProvider *thiz,
59            ... );
60 
61 #include <direct/interface_implementation.h>
62 
63 DIRECT_INTERFACE_IMPLEMENTATION( IDirectFBImageProvider, GIF )
64 
65 
66 #ifndef NODEBUG
67 #define GIFERRORMSG(...)     { fprintf( stderr, "(GIFLOADER) " __VA_ARGS__ ); \
68                                 fprintf( stderr, "\n" ); }
69 #else
70 #define GIFERRORMSG(...)
71 #endif
72 
73 #define MAXCOLORMAPSIZE 256
74 
75 #define CM_RED   0
76 #define CM_GREEN 1
77 #define CM_BLUE  2
78 
79 #define MAX_LWZ_BITS 12
80 
81 #define INTERLACE     0x40
82 #define LOCALCOLORMAP 0x80
83 
84 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
85 
86 #define LM_to_uint(a,b) (((b)<<8)|(a))
87 
88 /*
89  * private data struct of IDirectFBImageProvider_GIF
90  */
91 typedef struct {
92      IDirectFBImageProvider_data base;
93 
94      u32                 *image;
95      int                  image_width;
96      int                  image_height;
97      bool                 image_transparency;
98      u32                  image_colorkey;
99 
100      unsigned int  Width;
101      unsigned int  Height;
102      u8            ColorMap[3][MAXCOLORMAPSIZE];
103      unsigned int  BitPixel;
104      unsigned int  ColorResolution;
105      u32           Background;
106      unsigned int  AspectRatio;
107 
108 
109      int GrayScale;
110      int transparent;
111      int delayTime;
112      int inputFlag;
113      int disposal;
114 
115 
116      u8 buf[280];
117      int curbit, lastbit, done, last_byte;
118 
119 
120      int fresh;
121      int code_size, set_code_size;
122      int max_code, max_code_size;
123      int firstcode, oldcode;
124      int clear_code, end_code;
125      int table[2][(1<< MAX_LWZ_BITS)];
126      int stack[(1<<(MAX_LWZ_BITS))*2], *sp;
127 } IDirectFBImageProvider_GIF_data;
128 
129 static bool verbose       = false;
130 static bool showComment   = false;
131 static bool ZeroDataBlock = false;
132 
133 static u32* ReadGIF( IDirectFBImageProvider_GIF_data *data, int imageNumber,
134                        int *width, int *height, bool *transparency,
135                        u32 *key_rgb, bool alpha, bool headeronly);
136 
137 static bool ReadOK( IDirectFBDataBuffer *buffer, void *data, unsigned int len );
138 
139 
140 static DFBResult
141 IDirectFBImageProvider_GIF_RenderTo( IDirectFBImageProvider *thiz,
142                                      IDirectFBSurface       *destination,
143                                      const DFBRectangle     *destination_rect );
144 
145 static DFBResult
146 IDirectFBImageProvider_GIF_GetSurfaceDescription( IDirectFBImageProvider *thiz,
147                                                   DFBSurfaceDescription  *dsc );
148 
149 static DFBResult
150 IDirectFBImageProvider_GIF_GetImageDescription( IDirectFBImageProvider *thiz,
151                                                 DFBImageDescription    *dsc );
152 
153 
154 static void
IDirectFBImageProvider_GIF_Destruct(IDirectFBImageProvider * thiz)155 IDirectFBImageProvider_GIF_Destruct( IDirectFBImageProvider *thiz )
156 {
157      IDirectFBImageProvider_GIF_data *data =
158                                    (IDirectFBImageProvider_GIF_data*)thiz->priv;
159 
160      if (data->image)
161           D_FREE( data->image );
162 }
163 
164 static DFBResult
Probe(IDirectFBImageProvider_ProbeContext * ctx)165 Probe( IDirectFBImageProvider_ProbeContext *ctx )
166 {
167      if (strncmp ((char*) ctx->header, "GIF8", 4) == 0)
168           return DFB_OK;
169 
170      return DFB_UNSUPPORTED;
171 }
172 
173 static DFBResult
Construct(IDirectFBImageProvider * thiz,...)174 Construct( IDirectFBImageProvider *thiz,
175            ... )
176 {
177      IDirectFBDataBuffer *buffer;
178      CoreDFB             *core;
179      va_list              tag;
180 
181      DIRECT_ALLOCATE_INTERFACE_DATA(thiz, IDirectFBImageProvider_GIF)
182 
183      va_start( tag, thiz );
184      buffer = va_arg( tag, IDirectFBDataBuffer * );
185      core = va_arg( tag, CoreDFB * );
186      va_end( tag );
187 
188      data->base.ref = 1;
189 
190      data->base.buffer = buffer;
191      data->base.core = core;
192 
193      buffer->AddRef( buffer );
194 
195      data->GrayScale   = -1;
196      data->transparent = -1;
197      data->delayTime   = -1;
198 
199      data->image = ReadGIF( data, 1, &data->image_width, &data->image_height,
200                             &data->image_transparency, &data->image_colorkey,
201                             true, false );
202 
203      buffer->Release( buffer );
204      data->base.buffer = NULL;
205 
206      if (!data->image ||
207          (data->image_height == 0) ||
208          (data->image_width  == 0) ) {
209           DIRECT_DEALLOCATE_INTERFACE( thiz );
210           return DFB_FAILURE;
211      }
212 
213      data->base.Destruct = IDirectFBImageProvider_GIF_Destruct;
214 
215      thiz->RenderTo = IDirectFBImageProvider_GIF_RenderTo;
216      thiz->GetImageDescription = IDirectFBImageProvider_GIF_GetImageDescription;
217      thiz->GetSurfaceDescription =
218                                IDirectFBImageProvider_GIF_GetSurfaceDescription;
219 
220      return DFB_OK;
221 }
222 
223 static DFBResult
IDirectFBImageProvider_GIF_RenderTo(IDirectFBImageProvider * thiz,IDirectFBSurface * destination,const DFBRectangle * dest_rect)224 IDirectFBImageProvider_GIF_RenderTo( IDirectFBImageProvider *thiz,
225                                      IDirectFBSurface       *destination,
226                                      const DFBRectangle     *dest_rect )
227 {
228      DFBResult              ret;
229      DFBRegion              clip;
230      DFBRectangle           rect;
231      DFBSurfacePixelFormat  format;
232      IDirectFBSurface_data *dst_data;
233      CoreSurface           *dst_surface;
234 
235      DIRECT_INTERFACE_GET_DATA (IDirectFBImageProvider_GIF)
236 
237      dst_data = (IDirectFBSurface_data*) destination->priv;
238      if (!dst_data)
239           return DFB_DEAD;
240 
241      dst_surface = dst_data->surface;
242      if (!dst_surface)
243           return DFB_DESTROYED;
244 
245      dfb_region_from_rectangle( &clip, &dst_data->area.current );
246 
247      if (dest_rect) {
248           if (dest_rect->w < 1 || dest_rect->h < 1)
249                return DFB_INVARG;
250           rect = *dest_rect;
251           rect.x += dst_data->area.wanted.x;
252           rect.y += dst_data->area.wanted.y;
253      }
254      else {
255           rect = dst_data->area.wanted;
256      }
257 
258      ret = destination->GetPixelFormat( destination, &format );
259      if (ret)
260           return ret;
261 
262      /* actual loading and rendering */
263      if (dfb_rectangle_region_intersects( &rect, &clip )) {
264           CoreSurfaceBufferLock lock;
265 
266           ret = dfb_surface_lock_buffer( dst_surface, CSBR_BACK, CSAID_CPU, CSAF_WRITE, &lock );
267           if (ret)
268                return ret;
269 
270           dfb_scale_linear_32( data->image, data->image_width, data->image_height,
271                                lock.addr, lock.pitch, &rect, dst_surface, &clip );
272 
273           dfb_surface_unlock_buffer( dst_surface, &lock );
274 
275           if (data->base.render_callback) {
276                DIRenderCallbackResult r;
277 
278                rect.x = 0;
279                rect.y = 0;
280                rect.w = data->image_width;
281                rect.h = data->image_height;
282 
283                r = data->base.render_callback( &rect,
284                                                data->base.render_callback_context );
285 
286                if (r != DIRCR_OK)
287                        return DFB_INTERRUPTED;
288           }
289      }
290 
291      return DFB_OK;
292 }
293 
294 static DFBResult
IDirectFBImageProvider_GIF_GetSurfaceDescription(IDirectFBImageProvider * thiz,DFBSurfaceDescription * dsc)295 IDirectFBImageProvider_GIF_GetSurfaceDescription( IDirectFBImageProvider *thiz,
296                                                   DFBSurfaceDescription  *dsc )
297 {
298      DIRECT_INTERFACE_GET_DATA (IDirectFBImageProvider_GIF)
299 
300      dsc->flags       = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
301      dsc->width       = data->image_width;
302      dsc->height      = data->image_height;
303      dsc->pixelformat = dfb_primary_layer_pixelformat();
304 
305      return DFB_OK;
306 }
307 
308 static DFBResult
IDirectFBImageProvider_GIF_GetImageDescription(IDirectFBImageProvider * thiz,DFBImageDescription * dsc)309 IDirectFBImageProvider_GIF_GetImageDescription( IDirectFBImageProvider *thiz,
310                                                 DFBImageDescription    *dsc )
311 {
312      DIRECT_INTERFACE_GET_DATA (IDirectFBImageProvider_GIF)
313 
314      if (data->image_transparency) {
315           dsc->caps = DICAPS_COLORKEY;
316 
317           dsc->colorkey_r = (data->image_colorkey & 0xff0000) >> 16;
318           dsc->colorkey_g = (data->image_colorkey & 0x00ff00) >>  8;
319           dsc->colorkey_b = (data->image_colorkey & 0x0000ff);
320      }
321      else
322           dsc->caps = DICAPS_NONE;
323 
324      return DFB_OK;
325 }
326 
327 
328 /**********************************
329          GIF Loader Code
330  **********************************/
331 
ReadColorMap(IDirectFBDataBuffer * buffer,int number,u8 buf[3][MAXCOLORMAPSIZE])332 static int ReadColorMap( IDirectFBDataBuffer *buffer, int number,
333                          u8 buf[3][MAXCOLORMAPSIZE] )
334 {
335      int     i;
336      u8 rgb[3];
337 
338      for (i = 0; i < number; ++i) {
339           if (! ReadOK( buffer, rgb, sizeof(rgb) )) {
340                GIFERRORMSG("bad colormap" );
341                return true;
342           }
343 
344           buf[CM_RED][i]   = rgb[0] ;
345           buf[CM_GREEN][i] = rgb[1] ;
346           buf[CM_BLUE][i]  = rgb[2] ;
347      }
348      return false;
349 }
350 
GetDataBlock(IDirectFBDataBuffer * buffer,u8 * buf)351 static int GetDataBlock(IDirectFBDataBuffer *buffer, u8 *buf)
352 {
353      unsigned char count;
354 
355      if (! ReadOK( buffer, &count, 1 )) {
356           GIFERRORMSG("error in getting DataBlock size" );
357           return -1;
358      }
359      ZeroDataBlock = count == 0;
360 
361      if ((count != 0) && (! ReadOK( buffer, buf, count ))) {
362           GIFERRORMSG("error in reading DataBlock" );
363           return -1;
364      }
365 
366      return count;
367 }
368 
GetCode(IDirectFBImageProvider_GIF_data * data,int code_size,int flag)369 static int GetCode(IDirectFBImageProvider_GIF_data *data, int code_size, int flag)
370 {
371      int i, j, ret;
372      unsigned char count;
373 
374      if (flag) {
375           data->curbit = 0;
376           data->lastbit = 0;
377           data->done = false;
378           return 0;
379      }
380 
381      if ( (data->curbit+code_size) >= data->lastbit) {
382           if (data->done) {
383                if (data->curbit >= data->lastbit) {
384                     GIFERRORMSG("ran off the end of my bits" );
385                }
386              return -1;
387           }
388           data->buf[0] = data->buf[data->last_byte-2];
389           data->buf[1] = data->buf[data->last_byte-1];
390 
391           if ((count = GetDataBlock( data->base.buffer, &data->buf[2] )) == 0) {
392                data->done = true;
393           }
394 
395           data->last_byte = 2 + count;
396           data->curbit = (data->curbit - data->lastbit) + 16;
397           data->lastbit = (2+count) * 8;
398      }
399 
400      ret = 0;
401      for (i = data->curbit, j = 0; j < code_size; ++i, ++j) {
402           ret |= ((data->buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
403      }
404      data->curbit += code_size;
405 
406      return ret;
407 }
408 
DoExtension(IDirectFBImageProvider_GIF_data * data,int label)409 static int DoExtension( IDirectFBImageProvider_GIF_data *data, int label )
410 {
411      unsigned char buf[256] = { 0 };
412      char *str;
413 
414      switch (label) {
415           case 0x01:              /* Plain Text Extension */
416                str = "Plain Text Extension";
417                break;
418           case 0xff:              /* Application Extension */
419                str = "Application Extension";
420                break;
421           case 0xfe:              /* Comment Extension */
422                str = "Comment Extension";
423                while (GetDataBlock( data->base.buffer, (u8*) buf ) != 0) {
424                     if (showComment)
425                          GIFERRORMSG("gif comment: %s", buf );
426                     }
427                return false;
428           case 0xf9:              /* Graphic Control Extension */
429                str = "Graphic Control Extension";
430                (void) GetDataBlock( data->base.buffer, (u8*) buf );
431                data->disposal    = (buf[0] >> 2) & 0x7;
432                data->inputFlag   = (buf[0] >> 1) & 0x1;
433                data->delayTime   = LM_to_uint( buf[1], buf[2] );
434                if ((buf[0] & 0x1) != 0) {
435                     data->transparent = buf[3];
436                }
437                while (GetDataBlock( data->base.buffer, (u8*) buf ) != 0)
438                     ;
439                return false;
440           default:
441                str = (char*) buf;
442                snprintf(str, 256, "UNKNOWN (0x%02x)", label);
443           break;
444      }
445 
446      if (verbose)
447           GIFERRORMSG("got a '%s' extension", str );
448 
449      while (GetDataBlock( data->base.buffer, (u8*) buf ) != 0)
450           ;
451 
452      return false;
453 }
454 
LWZReadByte(IDirectFBImageProvider_GIF_data * data,int flag,int input_code_size)455 static int LWZReadByte( IDirectFBImageProvider_GIF_data *data, int flag, int input_code_size )
456 {
457      int code, incode;
458      int i;
459 
460      if (flag) {
461           data->set_code_size = input_code_size;
462           data->code_size = data->set_code_size+1;
463           data->clear_code = 1 << data->set_code_size ;
464           data->end_code = data->clear_code + 1;
465           data->max_code_size = 2*data->clear_code;
466           data->max_code = data->clear_code+2;
467 
468           GetCode(data, 0, true);
469 
470           data->fresh = true;
471 
472           for (i = 0; i < data->clear_code; ++i) {
473                data->table[0][i] = 0;
474                data->table[1][i] = i;
475           }
476           for (; i < (1<<MAX_LWZ_BITS); ++i) {
477                data->table[0][i] = data->table[1][0] = 0;
478           }
479           data->sp = data->stack;
480 
481           return 0;
482      }
483      else if (data->fresh) {
484           data->fresh = false;
485           do {
486                data->firstcode = data->oldcode = GetCode( data, data->code_size, false );
487           } while (data->firstcode == data->clear_code);
488 
489           return data->firstcode;
490      }
491 
492      if (data->sp > data->stack) {
493           return *--data->sp;
494      }
495 
496      while ((code = GetCode( data, data->code_size, false )) >= 0) {
497           if (code == data->clear_code) {
498                for (i = 0; i < data->clear_code; ++i) {
499                     data->table[0][i] = 0;
500                     data->table[1][i] = i;
501                }
502                for (; i < (1<<MAX_LWZ_BITS); ++i) {
503                     data->table[0][i] = data->table[1][i] = 0;
504                }
505                data->code_size = data->set_code_size+1;
506                data->max_code_size = 2*data->clear_code;
507                data->max_code = data->clear_code+2;
508                data->sp = data->stack;
509                data->firstcode = data->oldcode = GetCode( data, data->code_size, false );
510 
511                return data->firstcode;
512           }
513           else if (code == data->end_code) {
514                int count;
515                u8 buf[260];
516 
517                if (ZeroDataBlock) {
518                     return -2;
519                }
520 
521                while ((count = GetDataBlock( data->base.buffer, buf )) > 0)
522                     ;
523 
524                if (count != 0)
525                     GIFERRORMSG("missing EOD in data stream "
526                                 "(common occurence)");
527 
528                return -2;
529           }
530 
531           incode = code;
532 
533           if (code >= data->max_code) {
534                *data->sp++ = data->firstcode;
535                code = data->oldcode;
536           }
537 
538           while (code >= data->clear_code) {
539                *data->sp++ = data->table[1][code];
540                if (code == data->table[0][code]) {
541                     GIFERRORMSG("circular table entry BIG ERROR");
542                }
543                code = data->table[0][code];
544           }
545 
546           *data->sp++ = data->firstcode = data->table[1][code];
547 
548           if ((code = data->max_code) <(1<<MAX_LWZ_BITS)) {
549                data->table[0][code] = data->oldcode;
550                data->table[1][code] = data->firstcode;
551                ++data->max_code;
552                if ((data->max_code >= data->max_code_size)
553                    && (data->max_code_size < (1<<MAX_LWZ_BITS)))
554                {
555                     data->max_code_size *= 2;
556                     ++data->code_size;
557                }
558           }
559 
560           data->oldcode = incode;
561 
562           if (data->sp > data->stack) {
563                return *--data->sp;
564           }
565      }
566      return code;
567 }
568 
SortColors(const void * a,const void * b)569 static int SortColors (const void *a, const void *b)
570 {
571      return (*((const u8 *) a) - *((const u8 *) b));
572 }
573 
574 /*  looks for a color that is not in the colormap and ideally not
575     even close to the colors used in the colormap  */
FindColorKey(int n_colors,u8 cmap[3][MAXCOLORMAPSIZE])576 static u32 FindColorKey( int n_colors, u8 cmap[3][MAXCOLORMAPSIZE] )
577 {
578      u32   color = 0xFF000000;
579      u8    csort[MAXCOLORMAPSIZE];
580      int   i, j, index, d;
581 
582      if (n_colors < 1)
583           return color;
584 
585      D_ASSERT( n_colors <= MAXCOLORMAPSIZE );
586 
587      for (i = 0; i < 3; i++) {
588           direct_memcpy( csort, cmap[i], n_colors );
589           qsort( csort, n_colors, 1, SortColors );
590 
591           for (j = 1, index = 0, d = 0; j < n_colors; j++) {
592                if (csort[j] - csort[j-1] > d) {
593                     d = csort[j] - csort[j-1];
594                     index = j;
595                }
596           }
597           if ((csort[0] - 0x0) > d) {
598                d = csort[0] - 0x0;
599                index = n_colors;
600           }
601           if (0xFF - (csort[n_colors - 1]) > d) {
602                index = n_colors + 1;
603           }
604 
605           if (index < n_colors)
606                csort[0] = csort[index] - (d/2);
607           else if (index == n_colors)
608                csort[0] = 0x0;
609           else
610                csort[0] = 0xFF;
611 
612           color |= (csort[0] << (8 * (2 - i)));
613      }
614 
615      return color;
616 }
617 
ReadImage(IDirectFBImageProvider_GIF_data * data,int width,int height,u8 cmap[3][MAXCOLORMAPSIZE],u32 key_rgb,bool interlace,bool ignore)618 static u32* ReadImage( IDirectFBImageProvider_GIF_data *data, int width, int height,
619                        u8 cmap[3][MAXCOLORMAPSIZE], u32 key_rgb,
620                        bool interlace, bool ignore )
621 {
622      u8 c;
623      int v;
624      int xpos = 0, ypos = 0, pass = 0;
625      u32 *image;
626 
627      /*
628      **  Initialize the decompression routines
629      */
630      if (! ReadOK( data->base.buffer, &c, 1 ))
631           GIFERRORMSG("EOF / read error on image data" );
632 
633      if (LWZReadByte( data, true, c ) < 0)
634           GIFERRORMSG("error reading image" );
635 
636      /*
637      **  If this is an "uninteresting picture" ignore it.
638      */
639      if (ignore) {
640           if (verbose)
641                GIFERRORMSG("skipping image..." );
642 
643           while (LWZReadByte( data, false, c ) >= 0)
644                ;
645           return NULL;
646      }
647 
648      // FIXME: allocates four additional bytes because the scaling functions
649      //        in src/misc/gfx_util.c have an off-by-one bug which causes
650      //        segfaults on darwin/osx (not on linux)
651      if ((image = D_MALLOC(width * height * 4 + 4)) == NULL) {
652           GIFERRORMSG("couldn't alloc space for image" );
653      }
654 
655      if (verbose) {
656           GIFERRORMSG("reading %d by %d%s GIF image", width, height,
657                       interlace ? " interlaced" : "" );
658      }
659 
660      while ((v = LWZReadByte( data, false, c )) >= 0 ) {
661           u32 *dst = image + (ypos * width + xpos);
662 
663           if (v == data->transparent) {
664                *dst++ = key_rgb;
665           }
666           else {
667                *dst++ = (0xFF000000              |
668                          cmap[CM_RED][v]   << 16 |
669                          cmap[CM_GREEN][v] << 8  |
670                          cmap[CM_BLUE][v]);
671           }
672 
673           ++xpos;
674           if (xpos == width) {
675                xpos = 0;
676                if (interlace) {
677                     switch (pass) {
678                          case 0:
679                          case 1:
680                               ypos += 8;
681                               break;
682                          case 2:
683                               ypos += 4;
684                               break;
685                          case 3:
686                               ypos += 2;
687                               break;
688                     }
689 
690                     if (ypos >= height) {
691                          ++pass;
692                          switch (pass) {
693                               case 1:
694                                    ypos = 4;
695                                    break;
696                               case 2:
697                                    ypos = 2;
698                                    break;
699                               case 3:
700                                    ypos = 1;
701                               break;
702                               default:
703                                    goto fini;
704                          }
705                     }
706                }
707                else {
708                     ++ypos;
709                }
710           }
711           if (ypos >= height) {
712                break;
713           }
714      }
715 
716 fini:
717 
718      if (LWZReadByte( data, false, c ) >= 0) {
719           GIFERRORMSG("too much input data, ignoring extra...");
720      }
721      return image;
722 }
723 
724 
ReadGIF(IDirectFBImageProvider_GIF_data * data,int imageNumber,int * width,int * height,bool * transparency,u32 * key_rgb,bool alpha,bool headeronly)725 static u32* ReadGIF( IDirectFBImageProvider_GIF_data *data, int imageNumber,
726                      int *width, int *height, bool *transparency,
727                      u32 *key_rgb, bool alpha, bool headeronly)
728 {
729      u8    buf[16];
730      u8    c;
731      u8    localColorMap[3][MAXCOLORMAPSIZE];
732      u32   colorKey = 0;
733      bool  useGlobalColormap;
734      int   bitPixel;
735      int   imageCount = 0;
736      char  version[4];
737 
738      if (! ReadOK( data->base.buffer, buf, 6 )) {
739           GIFERRORMSG("error reading magic number" );
740      }
741 
742      if (strncmp( (char *)buf, "GIF", 3 ) != 0) {
743           GIFERRORMSG("not a GIF file" );
744      }
745 
746      direct_snputs( version, (char *)buf + 3, 4 );
747 
748      if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
749           GIFERRORMSG("bad version number, not '87a' or '89a'" );
750      }
751 
752      if (! ReadOK(data->base.buffer,buf,7)) {
753           GIFERRORMSG("failed to read screen descriptor" );
754      }
755 
756      data->Width           = LM_to_uint( buf[0], buf[1] );
757      data->Height          = LM_to_uint( buf[2], buf[3] );
758      data->BitPixel        = 2 << (buf[4] & 0x07);
759      data->ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
760      data->Background      = buf[5];
761      data->AspectRatio     = buf[6];
762 
763      if (BitSet(buf[4], LOCALCOLORMAP)) {    /* Global Colormap */
764           if (ReadColorMap( data->base.buffer, data->BitPixel, data->ColorMap )) {
765                GIFERRORMSG("error reading global colormap" );
766           }
767      }
768 
769      if (data->AspectRatio != 0 && data->AspectRatio != 49) {
770           /* float r = ( (float) data->AspectRatio + 15.0 ) / 64.0; */
771           GIFERRORMSG("warning - non-square pixels");
772      }
773 
774      data->transparent = -1;
775      data->delayTime   = -1;
776      data->inputFlag   = -1;
777      data->disposal    = 0;
778 
779      for (;;) {
780           if (! ReadOK( data->base.buffer, &c, 1)) {
781                GIFERRORMSG("EOF / read error on image data" );
782           }
783 
784           if (c == ';') {         /* GIF terminator */
785                if (imageCount < imageNumber) {
786                     GIFERRORMSG("only %d image%s found in file",
787                                 imageCount, imageCount>1?"s":"" );
788                }
789                return NULL;
790           }
791 
792           if (c == '!') {         /* Extension */
793                if (! ReadOK( data->base.buffer, &c, 1)) {
794                     GIFERRORMSG("OF / read error on extention function code");
795                }
796                DoExtension( data, c );
797             continue;
798           }
799 
800           if (c != ',') {         /* Not a valid start character */
801                GIFERRORMSG("bogus character 0x%02x, ignoring", (int) c );
802                continue;
803           }
804 
805           ++imageCount;
806 
807           if (! ReadOK( data->base.buffer, buf, 9 )) {
808                GIFERRORMSG("couldn't read left/top/width/height");
809           }
810 
811           *width  = LM_to_uint( buf[4], buf[5] );
812           *height = LM_to_uint( buf[6], buf[7] );
813           *transparency = (data->transparent != -1);
814 
815           if (headeronly && !(*transparency && key_rgb))
816                return NULL;
817 
818           useGlobalColormap = ! BitSet( buf[8], LOCALCOLORMAP );
819 
820           if (useGlobalColormap) {
821                if (*transparency && (key_rgb || !headeronly))
822                     colorKey = FindColorKey( data->BitPixel,
823                                              data->ColorMap );
824           }
825           else {
826                bitPixel = 2 << (buf[8] & 0x07);
827                if (ReadColorMap( data->base.buffer, bitPixel, localColorMap ))
828                     GIFERRORMSG("error reading local colormap" );
829 
830                if (*transparency && (key_rgb || !headeronly))
831                     colorKey = FindColorKey( bitPixel, localColorMap );
832           }
833 
834           if (key_rgb)
835                *key_rgb = colorKey;
836 
837           if (headeronly)
838                return NULL;
839 
840           if (alpha)
841                colorKey &= 0x00FFFFFF;
842 
843           return ReadImage( data, *width, *height,
844                             (useGlobalColormap ?
845                              data->ColorMap : localColorMap), colorKey,
846                             BitSet( buf[8], INTERLACE ),
847                             imageCount != imageNumber);
848      }
849 }
850 
851 static bool
ReadOK(IDirectFBDataBuffer * buffer,void * data,unsigned int len)852 ReadOK( IDirectFBDataBuffer *buffer, void *data, unsigned int len )
853 {
854      DFBResult ret;
855 
856      ret = buffer->WaitForData( buffer, len );
857      if (ret) {
858           DirectFBError( "(DirectFB/ImageProvider_GIF) WaitForData failed", ret );
859           return false;
860      }
861 
862      ret = buffer->GetData( buffer, len, data, NULL );
863      if (ret) {
864           DirectFBError( "(DirectFB/ImageProvider_GIF) GetData failed", ret );
865           return false;
866      }
867 
868      return true;
869 }
870 
871