1 /*
2    (c) Copyright 2001-2009  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 <string.h>
34 #include <unistd.h>
35 #include <sys/time.h>
36 
37 #include <pthread.h>
38 
39 #include <directfb.h>
40 
41 #include <direct/types.h>
42 #include <direct/mem.h>
43 #include <direct/memcpy.h>
44 #include <direct/messages.h>
45 #include <direct/thread.h>
46 #include <direct/util.h>
47 
48 #include <idirectfb.h>
49 
50 #include <core/surface.h>
51 
52 #include <display/idirectfbsurface.h>
53 
54 #include <media/idirectfbdatabuffer.h>
55 #include <media/idirectfbvideoprovider.h>
56 
57 #include <misc/gfx_util.h>
58 
59 
60 static DFBResult Probe( IDirectFBVideoProvider_ProbeContext *ctx );
61 
62 static DFBResult Construct( IDirectFBVideoProvider *thiz,
63                             IDirectFBDataBuffer    *buffer );
64 
65 
66 #include <direct/interface_implementation.h>
67 
68 DIRECT_INTERFACE_IMPLEMENTATION( IDirectFBVideoProvider, Gif )
69 
70 /*****************************************************************************/
71 
72 #define MAXCOLORMAPSIZE 256
73 
74 #define CM_RED   0
75 #define CM_GREEN 1
76 #define CM_BLUE  2
77 
78 #define MAX_LWZ_BITS 12
79 
80 #define INTERLACE     0x40
81 #define LOCALCOLORMAP 0x80
82 
83 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
84 
85 #define LM_to_uint(a,b) (((b)<<8)|(a))
86 
87 typedef struct {
88      int                            ref;      /* reference counter */
89 
90      IDirectFBDataBuffer           *buffer;
91      DFBBoolean                     seekable;
92 
93      IDirectFBSurface              *destination;
94      IDirectFBSurface_data         *dst_data;
95      DFBRectangle                   dst_rect;
96 
97      u32                           *image;
98 
99      DirectThread                  *thread;
100      pthread_mutex_t                lock;
101      pthread_cond_t                 cond;
102 
103      DFBVideoProviderStatus         status;
104      DFBVideoProviderPlaybackFlags  flags;
105      double                         speed;
106 
107      unsigned int                   start_pos;
108 
109      char                           Version[4];
110      unsigned int                   Width;
111      unsigned int                   Height;
112      u8                             ColorMap[3][MAXCOLORMAPSIZE];
113      unsigned int                   BitPixel;
114      unsigned int                   ColorResolution;
115      u32                            Background;
116      unsigned int                   AspectRatio;
117 
118      int                            transparent;
119      unsigned int                   delayTime;
120      int                            inputFlag;
121      int                            disposal;
122 
123      u8                             buf[280];
124      int                            curbit, lastbit, done, last_byte;
125 
126      int                            fresh;
127      int                            code_size, set_code_size;
128      int                            max_code, max_code_size;
129      int                            firstcode, oldcode;
130      int                            clear_code, end_code;
131      int                            table[2][(1<< MAX_LWZ_BITS)];
132      int                            stack[(1<<(MAX_LWZ_BITS))*2], *sp;
133 
134      DVFrameCallback                callback;
135      void                          *callback_ctx;
136 } IDirectFBVideoProvider_GIF_data;
137 
138 #define GIFERRORMSG(x, ...) \
139      D_ERROR( "IDirectFBVideoProvider_GIF: " #x "!\n", ## __VA_ARGS__ )
140 
141 #define GIFDEBUGMSG(x, ...) \
142      D_DEBUG( "IDirectFBVideoProvider_GIF: " #x "!\n", ## __VA_ARGS__ )
143 
144 /*****************************************************************************/
145 
146 static int ZeroDataBlock = 0;
147 
148 static DFBResult
FetchData(IDirectFBDataBuffer * buffer,void * data,unsigned int len)149 FetchData( IDirectFBDataBuffer *buffer, void *data, unsigned int len )
150 {
151      DFBResult ret = DFB_OK;
152 
153      do {
154           unsigned int read = 0;
155 
156           ret = buffer->WaitForData( buffer, len );
157           if (ret == DFB_OK)
158                ret = buffer->GetData( buffer, len, data, &read );
159           if (ret)
160                break;
161 
162           data += read;
163           len  -= read;
164      } while (len);
165 
166      return ret;
167 }
168 
ReadColorMap(IDirectFBDataBuffer * buffer,int number,u8 buf[3][MAXCOLORMAPSIZE])169 static int ReadColorMap( IDirectFBDataBuffer *buffer, int number,
170                          u8 buf[3][MAXCOLORMAPSIZE] )
171 {
172      int  i;
173      u8   rgb[3*number];
174 
175      if (FetchData( buffer, rgb, sizeof(rgb) )) {
176           GIFERRORMSG("bad colormap");
177           return -1;
178      }
179 
180      for (i = 0; i < number; ++i) {
181           buf[CM_RED][i]   = rgb[i*3+0];
182           buf[CM_GREEN][i] = rgb[i*3+1];
183           buf[CM_BLUE][i]  = rgb[i*3+2];
184      }
185 
186      return 0;
187 }
188 
GetDataBlock(IDirectFBDataBuffer * buffer,u8 * buf)189 static int GetDataBlock(IDirectFBDataBuffer *buffer, u8 *buf)
190 {
191      unsigned char count;
192 
193      if (FetchData( buffer, &count, 1 )) {
194           GIFERRORMSG("error in getting DataBlock size");
195           return -1;
196      }
197      ZeroDataBlock = (count == 0);
198 
199      if ((count != 0) && FetchData( buffer, buf, count )) {
200           GIFERRORMSG("error in reading DataBlock");
201           return -1;
202      }
203 
204      return count;
205 }
206 
GetCode(IDirectFBVideoProvider_GIF_data * data,int code_size,int flag)207 static int GetCode(IDirectFBVideoProvider_GIF_data *data, int code_size, int flag)
208 {
209      int i, j, ret;
210      unsigned char count;
211 
212      if (flag) {
213           data->curbit = 0;
214           data->lastbit = 0;
215           data->done = false;
216           return 0;
217      }
218 
219      if ( (data->curbit+code_size) >= data->lastbit) {
220           if (data->done) {
221                if (data->curbit >= data->lastbit) {
222                     GIFERRORMSG("ran off the end of my bits");
223                }
224                return -1;
225           }
226           data->buf[0] = data->buf[data->last_byte-2];
227           data->buf[1] = data->buf[data->last_byte-1];
228 
229           if ((count = GetDataBlock( data->buffer, &data->buf[2] )) == 0) {
230                data->done = true;
231           }
232 
233           data->last_byte = 2 + count;
234           data->curbit = (data->curbit - data->lastbit) + 16;
235           data->lastbit = (2+count) * 8;
236      }
237 
238      ret = 0;
239      for (i = data->curbit, j = 0; j < code_size; ++i, ++j) {
240           ret |= ((data->buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
241      }
242      data->curbit += code_size;
243 
244      return ret;
245 }
246 
DoExtension(IDirectFBVideoProvider_GIF_data * data,int label)247 static int DoExtension( IDirectFBVideoProvider_GIF_data *data, int label )
248 {
249      unsigned char buf[256] = { 0 };
250      char *str;
251 
252      switch (label) {
253           case 0x01:              /* Plain Text Extension */
254                str = "Plain Text Extension";
255                break;
256           case 0xff:              /* Application Extension */
257                str = "Application Extension";
258                break;
259           case 0xfe:              /* Comment Extension */
260                str = "Comment Extension";
261                while (GetDataBlock( data->buffer, (u8*) buf ) != 0)
262                     GIFDEBUGMSG("gif comment: %s", buf);
263                return false;
264           case 0xf9:              /* Graphic Control Extension */
265                str = "Graphic Control Extension";
266                (void) GetDataBlock( data->buffer, (u8*) buf );
267                data->disposal  = (buf[0] >> 2) & 0x7;
268                data->inputFlag = (buf[0] >> 1) & 0x1;
269                if (LM_to_uint( buf[1], buf[2] ))
270                     data->delayTime = LM_to_uint( buf[1], buf[2] ) * 10000;
271                if ((buf[0] & 0x1) != 0)
272                     data->transparent = buf[3];
273                else
274                     data->transparent = -1;
275                while (GetDataBlock( data->buffer, (u8*) buf ) != 0)
276                     ;
277                return false;
278           default:
279                str = (char*) buf;
280                snprintf(str, 256, "UNKNOWN (0x%02x)", label);
281           break;
282      }
283 
284      GIFDEBUGMSG("got a '%s' extension", str );
285 
286      while (GetDataBlock( data->buffer, (u8*) buf ) != 0);
287 
288      return 0;
289 }
290 
LWZReadByte(IDirectFBVideoProvider_GIF_data * data,int flag,int input_code_size)291 static int LWZReadByte( IDirectFBVideoProvider_GIF_data *data, int flag, int input_code_size )
292 {
293      int code, incode;
294      int i;
295 
296      if (flag) {
297           data->set_code_size = input_code_size;
298           data->code_size = data->set_code_size+1;
299           data->clear_code = 1 << data->set_code_size ;
300           data->end_code = data->clear_code + 1;
301           data->max_code_size = 2*data->clear_code;
302           data->max_code = data->clear_code+2;
303 
304           GetCode(data, 0, true);
305 
306           data->fresh = true;
307 
308           for (i = 0; i < data->clear_code; ++i) {
309                data->table[0][i] = 0;
310                data->table[1][i] = i;
311           }
312           for (; i < (1<<MAX_LWZ_BITS); ++i) {
313                data->table[0][i] = data->table[1][0] = 0;
314           }
315           data->sp = data->stack;
316 
317           return 0;
318      }
319      else if (data->fresh) {
320           data->fresh = false;
321           do {
322                data->firstcode = data->oldcode = GetCode( data, data->code_size, false );
323           } while (data->firstcode == data->clear_code);
324 
325           return data->firstcode;
326      }
327 
328      if (data->sp > data->stack) {
329           return *--data->sp;
330      }
331 
332      while ((code = GetCode( data, data->code_size, false )) >= 0) {
333           if (code == data->clear_code) {
334                for (i = 0; i < data->clear_code; ++i) {
335                     data->table[0][i] = 0;
336                     data->table[1][i] = i;
337                }
338                for (; i < (1<<MAX_LWZ_BITS); ++i) {
339                     data->table[0][i] = data->table[1][i] = 0;
340                }
341                data->code_size = data->set_code_size+1;
342                data->max_code_size = 2*data->clear_code;
343                data->max_code = data->clear_code+2;
344                data->sp = data->stack;
345                data->firstcode = data->oldcode = GetCode( data, data->code_size, false );
346 
347                return data->firstcode;
348           }
349           else if (code == data->end_code) {
350                int count;
351                u8 buf[260];
352 
353                if (ZeroDataBlock) {
354                     return -2;
355                }
356 
357                while ((count = GetDataBlock( data->buffer, buf )) > 0)
358                     ;
359 
360                if (count != 0)
361                     GIFERRORMSG("missing EOD in data stream (common occurence)");
362 
363                return -2;
364           }
365 
366           incode = code;
367 
368           if (code >= data->max_code) {
369                *data->sp++ = data->firstcode;
370                code = data->oldcode;
371           }
372 
373           while (code >= data->clear_code) {
374                *data->sp++ = data->table[1][code];
375                if (code == data->table[0][code]) {
376                     GIFERRORMSG("circular table entry BIG ERROR");
377                }
378                code = data->table[0][code];
379           }
380 
381           *data->sp++ = data->firstcode = data->table[1][code];
382 
383           if ((code = data->max_code) <(1<<MAX_LWZ_BITS)) {
384                data->table[0][code] = data->oldcode;
385                data->table[1][code] = data->firstcode;
386                ++data->max_code;
387                if ((data->max_code >= data->max_code_size)
388                    && (data->max_code_size < (1<<MAX_LWZ_BITS)))
389                {
390                     data->max_code_size *= 2;
391                     ++data->code_size;
392                }
393           }
394 
395           data->oldcode = incode;
396 
397           if (data->sp > data->stack) {
398                return *--data->sp;
399           }
400      }
401      return code;
402 }
403 
ReadImage(IDirectFBVideoProvider_GIF_data * data,int left,int top,int width,int height,u8 cmap[3][MAXCOLORMAPSIZE],bool interlace,bool ignore)404 static int ReadImage( IDirectFBVideoProvider_GIF_data *data,
405                       int left, int top, int width, int height,
406                       u8 cmap[3][MAXCOLORMAPSIZE], bool interlace, bool ignore )
407 {
408      u8   c;
409      int  v;
410      int  xpos = 0, ypos = 0, pass = 0;
411      u32 *image, *dst;
412 
413      /*
414      **  Initialize the decompression routines
415      */
416      if (FetchData( data->buffer, &c, 1 ))
417           GIFERRORMSG("EOF / read error on image data");
418 
419      if (LWZReadByte( data, true, c ) < 0)
420           GIFERRORMSG("error reading image");
421 
422      /*
423      **  If this is an "uninteresting picture" ignore it.
424      */
425      if (ignore) {
426           GIFDEBUGMSG("skipping image...");
427 
428           while (LWZReadByte( data, false, c ) >= 0)
429                ;
430           return 0;
431      }
432 
433      switch (data->disposal) {
434           case 2:
435                GIFDEBUGMSG("restoring to background color...");
436                memset( data->image, 0, data->Width * data->Height * 4 );
437                break;
438           case 3:
439                GIFERRORMSG("restoring to previous frame is unsupported");
440                break;
441           default:
442                break;
443      }
444 
445      dst = image = data->image + (top * data->Width + left);
446 
447      GIFDEBUGMSG("reading %dx%d at %dx%d %sGIF image",
448                  width, height, left, top, interlace ? " interlaced " : "" );
449 
450      while ((v = LWZReadByte( data, false, c )) >= 0 ) {
451           if (v != data->transparent) {
452                dst[xpos] = (0xFF000000              |
453                             cmap[CM_RED][v]   << 16 |
454                             cmap[CM_GREEN][v] << 8  |
455                             cmap[CM_BLUE][v]);
456           }
457 
458           ++xpos;
459           if (xpos == width) {
460                xpos = 0;
461                if (interlace) {
462                     switch (pass) {
463                          case 0:
464                          case 1:
465                               ypos += 8;
466                               break;
467                          case 2:
468                               ypos += 4;
469                               break;
470                          case 3:
471                               ypos += 2;
472                               break;
473                     }
474 
475                     if (ypos >= height) {
476                          ++pass;
477                          switch (pass) {
478                               case 1:
479                                    ypos = 4;
480                                    break;
481                               case 2:
482                                    ypos = 2;
483                                    break;
484                               case 3:
485                                    ypos = 1;
486                               break;
487                               default:
488                                    goto fini;
489                          }
490                     }
491                }
492                else {
493                     ++ypos;
494                }
495                dst = image + ypos * data->Width;
496           }
497           if (ypos >= height) {
498                break;
499           }
500      }
501 
502 fini:
503 
504      if (LWZReadByte( data, false, c ) >= 0) {
505           GIFERRORMSG("too much input data, ignoring extra...");
506           //while (LWZReadByte( data, false, c ) >= 0);
507      }
508 
509      return 0;
510 }
511 
GIFReset(IDirectFBVideoProvider_GIF_data * data)512 static void GIFReset( IDirectFBVideoProvider_GIF_data *data )
513 {
514      data->transparent = -1;
515      data->delayTime   = 1000000; /* default: 1s */
516      data->inputFlag   = -1;
517      data->disposal    = 0;
518 
519      if (data->image)
520           memset( data->image, 0, data->Width*data->Height*4 );
521 }
522 
GIFReadHeader(IDirectFBVideoProvider_GIF_data * data)523 static DFBResult GIFReadHeader( IDirectFBVideoProvider_GIF_data *data )
524 {
525      DFBResult ret;
526      u8        buf[7];
527 
528      ret = FetchData( data->buffer, buf, 6 );
529      if (ret) {
530           GIFERRORMSG("error reading header");
531           return ret;
532      }
533 
534      if (memcmp( buf, "GIF", 3 )) {
535           GIFERRORMSG("bad magic");
536           return DFB_UNSUPPORTED;
537      }
538 
539      memcpy( data->Version, &buf[3], 3 );
540      data->Version[3] = '\0';
541 
542      ret = FetchData( data->buffer, buf, 7 );
543      if (ret) {
544           GIFERRORMSG("error reading screen descriptor");
545           return ret;
546      }
547 
548      data->Width           = LM_to_uint( buf[0], buf[1] );
549      data->Height          = LM_to_uint( buf[2], buf[3] );
550      data->BitPixel        = 2 << (buf[4] & 0x07);
551      data->ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
552      data->Background      = buf[5];
553      data->AspectRatio     = buf[6];
554      if (data->AspectRatio)
555           data->AspectRatio = ((data->AspectRatio + 15) << 8) >> 6;
556      else
557           data->AspectRatio = (data->Width << 8) / data->Height;
558 
559      if (BitSet(buf[4], LOCALCOLORMAP)) {    /* Global Colormap */
560           if (ReadColorMap( data->buffer, data->BitPixel, data->ColorMap )) {
561                GIFERRORMSG("error reading global colormap");
562                return DFB_FAILURE;
563           }
564      }
565 
566      return DFB_OK;
567 }
568 
GIFReadFrame(IDirectFBVideoProvider_GIF_data * data)569 static DFBResult GIFReadFrame( IDirectFBVideoProvider_GIF_data *data )
570 {
571      u8    buf[16], c;
572      int   top, left;
573      int   width, height;
574      u8    localColorMap[3][MAXCOLORMAPSIZE];
575      bool  useGlobalColormap;
576 
577      data->curbit = data->lastbit = data->done = data->last_byte = 0;
578 
579      data->fresh =
580      data->code_size = data->set_code_size =
581      data->max_code = data->max_code_size =
582      data->firstcode = data->oldcode =
583      data->clear_code = data->end_code = 0;
584 
585      for (;;) {
586           DFBResult ret;
587 
588           ret = FetchData( data->buffer, &c, 1);
589           if (ret) {
590                GIFERRORMSG("EOF / read error on image data" );
591                return DFB_EOF;
592           }
593 
594           if (c == ';') /* GIF terminator */
595                return DFB_EOF;
596 
597           if (c == '!') { /* Extension */
598                if (FetchData( data->buffer, &c, 1)) {
599                     GIFERRORMSG("EOF / read error on extention function code");
600                     return DFB_EOF;
601                }
602                DoExtension( data, c );
603                continue;
604           }
605 
606           if (c != ',') { /* Not a valid start character */
607                GIFERRORMSG("bogus character 0x%02x, ignoring", (int) c );
608                continue;
609           }
610 
611           ret = FetchData( data->buffer, buf, 9 );
612           if (ret) {
613                GIFERRORMSG("couldn't read left/top/width/height");
614                return ret;
615           }
616 
617           left   = LM_to_uint( buf[0], buf[1] );
618           top    = LM_to_uint( buf[2], buf[3] );
619           width  = LM_to_uint( buf[4], buf[5] );
620           height = LM_to_uint( buf[6], buf[7] );
621 
622           useGlobalColormap = !BitSet( buf[8], LOCALCOLORMAP );
623 
624           if (!useGlobalColormap) {
625                int bitPixel = 2 << (buf[8] & 0x07);
626                if (ReadColorMap( data->buffer, bitPixel, localColorMap ))
627                     GIFERRORMSG("error reading local colormap");
628           }
629 
630           if (ReadImage( data, left, top, width, height,
631                         (useGlobalColormap ?
632                          data->ColorMap : localColorMap),
633                          BitSet( buf[8], INTERLACE ), 0 )) {
634                GIFERRORMSG("error reading image");
635                return DFB_FAILURE;
636           }
637 
638           break;
639      }
640 
641      return DFB_OK;
642 }
643 
644 /*****************************************************************************/
645 
646 static void
IDirectFBVideoProvider_GIF_Destruct(IDirectFBVideoProvider * thiz)647 IDirectFBVideoProvider_GIF_Destruct( IDirectFBVideoProvider *thiz )
648 {
649      IDirectFBVideoProvider_GIF_data *data = thiz->priv;
650 
651      thiz->Stop( thiz );
652 
653      if (data->image)
654           D_FREE( data->image );
655 
656      if (data->buffer)
657           data->buffer->Release( data->buffer );
658 
659      pthread_cond_destroy( &data->cond );
660      pthread_mutex_destroy( &data->lock );
661 
662      DIRECT_DEALLOCATE_INTERFACE( thiz );
663 }
664 
665 static DirectResult
IDirectFBVideoProvider_GIF_AddRef(IDirectFBVideoProvider * thiz)666 IDirectFBVideoProvider_GIF_AddRef( IDirectFBVideoProvider *thiz )
667 {
668      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
669 
670      data->ref++;
671 
672      return DR_OK;
673 }
674 
675 static DirectResult
IDirectFBVideoProvider_GIF_Release(IDirectFBVideoProvider * thiz)676 IDirectFBVideoProvider_GIF_Release( IDirectFBVideoProvider *thiz )
677 {
678      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
679 
680      if (--data->ref == 0)
681           IDirectFBVideoProvider_GIF_Destruct( thiz );
682 
683      return DR_OK;
684 }
685 
686 static DFBResult
IDirectFBVideoProvider_GIF_GetCapabilities(IDirectFBVideoProvider * thiz,DFBVideoProviderCapabilities * caps)687 IDirectFBVideoProvider_GIF_GetCapabilities( IDirectFBVideoProvider       *thiz,
688                                             DFBVideoProviderCapabilities *caps )
689 {
690      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
691 
692      if (!caps)
693           return DFB_INVARG;
694 
695      *caps = DVCAPS_BASIC | DVCAPS_SCALE | DVCAPS_SPEED;
696 
697      return DFB_OK;
698 }
699 
700 static DFBResult
IDirectFBVideoProvider_GIF_GetSurfaceDescription(IDirectFBVideoProvider * thiz,DFBSurfaceDescription * desc)701 IDirectFBVideoProvider_GIF_GetSurfaceDescription( IDirectFBVideoProvider *thiz,
702                                                   DFBSurfaceDescription  *desc )
703 {
704      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
705 
706      if (!desc)
707           return DFB_INVARG;
708 
709      desc->flags       = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
710      desc->width       = data->Width;
711      desc->height      = data->Height;
712      desc->pixelformat = DSPF_ARGB;
713 
714      return DFB_OK;
715 }
716 
717 static DFBResult
IDirectFBVideoProvider_GIF_GetStreamDescription(IDirectFBVideoProvider * thiz,DFBStreamDescription * desc)718 IDirectFBVideoProvider_GIF_GetStreamDescription( IDirectFBVideoProvider *thiz,
719                                                  DFBStreamDescription   *desc )
720 {
721      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
722 
723      if (!desc)
724           return DFB_INVARG;
725 
726      desc->caps = DVSCAPS_VIDEO;
727 
728      snprintf( desc->video.encoding,
729                DFB_STREAM_DESC_ENCODING_LENGTH, "GIF %s", data->Version );
730      desc->video.framerate = 0;
731      desc->video.aspect    = (double)data->AspectRatio/256.0;
732      desc->video.bitrate   = 0;
733 
734      desc->title[0] = desc->author[0] =
735      desc->album[0] = desc->genre[0] = desc->comment[0] = 0;
736      desc->year = 0;
737 
738      return DFB_OK;
739 }
740 
741 static void*
GIFVideo(DirectThread * self,void * arg)742 GIFVideo( DirectThread *self, void *arg )
743 {
744      IDirectFBVideoProvider_GIF_data *data = arg;
745 
746      pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, NULL );
747 
748      while (!direct_thread_is_canceled( self )) {
749           DFBResult              ret;
750           DFBRectangle           rect;
751           DFBRegion              clip;
752           CoreSurface           *surface;
753           CoreSurfaceBufferLock  lock;
754 
755           pthread_mutex_lock( &data->lock );
756 
757           if (direct_thread_is_canceled( self )) {
758                pthread_mutex_unlock( &data->lock );
759                break;
760           }
761 
762           ret = GIFReadFrame( data );
763           if (ret) {
764                if (ret == DFB_EOF) {
765                     GIFReset( data );
766                     if (data->flags & DVPLAY_LOOPING) {
767                          data->buffer->SeekTo( data->buffer, data->start_pos );
768                     }
769                     else {
770                          data->status = DVSTATE_FINISHED;
771                          pthread_mutex_unlock( &data->lock );
772                          break;
773                     }
774                }
775                pthread_mutex_unlock( &data->lock );
776                continue;
777           }
778 
779           rect = (data->dst_rect.w == 0)
780                  ? data->dst_data->area.wanted : data->dst_rect;
781           dfb_region_from_rectangle( &clip, &data->dst_data->area.current );
782 
783           surface = data->dst_data->surface;
784           D_MAGIC_ASSERT( surface, CoreSurface );
785 
786           if (dfb_rectangle_region_intersects( &rect, &clip ) &&
787               dfb_surface_lock_buffer( surface, CSBR_BACK, CSAID_CPU, CSAF_WRITE, &lock ) == DFB_OK)
788           {
789                dfb_scale_linear_32( data->image, data->Width, data->Height,
790                                     lock.addr, lock.pitch, &rect, data->dst_data->surface, &clip );
791 
792                dfb_surface_unlock_buffer( surface, &lock );
793 
794                if (data->callback)
795                     data->callback( data->callback_ctx );
796           }
797 
798           if (!data->speed) {
799                pthread_cond_wait( &data->cond, &data->lock );
800           }
801           else {
802                struct timespec ts;
803                struct timeval  tv;
804                unsigned long   us;
805 
806                gettimeofday( &tv, NULL );
807 
808                us = data->delayTime;
809                if (data->speed != 1.0)
810                     us = ((double)us / data->speed + .5);
811                us += tv.tv_usec;
812 
813                ts.tv_sec  = tv.tv_sec + us/1000000;
814                ts.tv_nsec = (us%1000000) * 1000;
815 
816                pthread_cond_timedwait( &data->cond, &data->lock, &ts );
817           }
818 
819           pthread_mutex_unlock( &data->lock );
820      }
821 
822      return (void*)0;
823 }
824 
825 static DFBResult
IDirectFBVideoProvider_GIF_PlayTo(IDirectFBVideoProvider * thiz,IDirectFBSurface * destination,const DFBRectangle * dest_rect,DVFrameCallback callback,void * ctx)826 IDirectFBVideoProvider_GIF_PlayTo( IDirectFBVideoProvider *thiz,
827                                    IDirectFBSurface       *destination,
828                                    const DFBRectangle     *dest_rect,
829                                    DVFrameCallback         callback,
830                                    void                   *ctx )
831 {
832      IDirectFBSurface_data *dst_data;
833      DFBRectangle           rect = { 0, 0, 0, 0 };
834      DFBResult              ret;
835 
836      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
837 
838      if (!destination)
839           return DFB_INVARG;
840 
841      dst_data = destination->priv;
842      if (!dst_data || !dst_data->surface)
843           return DFB_DESTROYED;
844 
845      if (dest_rect) {
846           if (dest_rect->w < 1 || dest_rect->h < 1)
847                return DFB_INVARG;
848 
849           rect = *dest_rect;
850           rect.x += dst_data->area.wanted.x;
851           rect.y += dst_data->area.wanted.y;
852      }
853 
854      pthread_mutex_lock( &data->lock );
855 
856      if (data->status == DVSTATE_FINISHED) {
857           ret = data->buffer->SeekTo( data->buffer, data->start_pos );
858           if (ret) {
859                pthread_mutex_unlock( &data->lock );
860                return ret;
861           }
862      }
863      data->status = DVSTATE_PLAY;
864 
865      if (!data->image) {
866           data->image = D_CALLOC( 4, data->Width * data->Height );
867           if (!data->image) {
868                pthread_mutex_unlock( &data->lock );
869                return D_OOM();
870           }
871      }
872 
873      if (data->destination)
874           data->destination->Release( data->destination );
875 
876      destination->AddRef( destination );
877      data->destination = destination;
878      data->dst_data    = dst_data;
879      data->dst_rect    = rect;
880 
881      data->callback     = callback;
882      data->callback_ctx = ctx;
883 
884      if (!data->thread) {
885           data->thread = direct_thread_create( DTT_DEFAULT, GIFVideo,
886                                               (void*)data, "GIF Video" );
887      }
888 
889      pthread_mutex_unlock( &data->lock );
890 
891      return DFB_OK;
892 }
893 
894 static DFBResult
IDirectFBVideoProvider_GIF_Stop(IDirectFBVideoProvider * thiz)895 IDirectFBVideoProvider_GIF_Stop( IDirectFBVideoProvider *thiz )
896 {
897      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
898 
899      if (data->thread) {
900           direct_thread_cancel( data->thread );
901           pthread_mutex_lock( &data->lock );
902           pthread_cond_signal( &data->cond );
903           pthread_mutex_unlock( &data->lock );
904           direct_thread_join( data->thread );
905           direct_thread_destroy( data->thread );
906           data->thread = NULL;
907      }
908 
909      if (data->destination) {
910           data->destination->Release( data->destination );
911           data->destination = NULL;
912           data->dst_data    = NULL;
913      }
914 
915      data->status = DVSTATE_STOP;
916 
917      return DFB_OK;
918 }
919 
920 static DFBResult
IDirectFBVideoProvider_GIF_GetStatus(IDirectFBVideoProvider * thiz,DFBVideoProviderStatus * status)921 IDirectFBVideoProvider_GIF_GetStatus( IDirectFBVideoProvider *thiz,
922                                       DFBVideoProviderStatus *status )
923 {
924      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
925 
926      if (!status)
927           return DFB_INVARG;
928 
929      *status = data->status;
930 
931      return DFB_OK;
932 }
933 
934 static DFBResult
IDirectFBVideoProvider_GIF_SeekTo(IDirectFBVideoProvider * thiz,double seconds)935 IDirectFBVideoProvider_GIF_SeekTo( IDirectFBVideoProvider *thiz,
936                                    double                  seconds )
937 {
938      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
939 
940      if (seconds < 0.0)
941           return DFB_INVARG;
942 
943      return DFB_UNSUPPORTED;
944 }
945 
946 static DFBResult
IDirectFBVideoProvider_GIF_GetPos(IDirectFBVideoProvider * thiz,double * seconds)947 IDirectFBVideoProvider_GIF_GetPos( IDirectFBVideoProvider *thiz,
948                                    double                 *seconds )
949 {
950      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
951 
952      if (!seconds)
953           return DFB_INVARG;
954 
955      *seconds = 0.0;
956 
957      return DFB_UNSUPPORTED;
958 }
959 
960 static DFBResult
IDirectFBVideoProvider_GIF_GetLength(IDirectFBVideoProvider * thiz,double * seconds)961 IDirectFBVideoProvider_GIF_GetLength( IDirectFBVideoProvider *thiz,
962                                       double                 *seconds )
963 {
964      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
965 
966      if (!seconds)
967           return DFB_INVARG;
968 
969      *seconds = 0.0;
970 
971      return DFB_UNSUPPORTED;
972 }
973 
974 static DFBResult
IDirectFBVideoProvider_GIF_SetPlaybackFlags(IDirectFBVideoProvider * thiz,DFBVideoProviderPlaybackFlags flags)975 IDirectFBVideoProvider_GIF_SetPlaybackFlags( IDirectFBVideoProvider        *thiz,
976                                              DFBVideoProviderPlaybackFlags  flags )
977 {
978      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
979 
980      if (flags & ~DVPLAY_LOOPING)
981           return DFB_UNSUPPORTED;
982 
983      if (flags & DVPLAY_LOOPING && !data->seekable)
984           return DFB_UNSUPPORTED;
985 
986      data->flags = flags;
987 
988      return DFB_OK;
989 }
990 
991 static DFBResult
IDirectFBVideoProvider_GIF_SetSpeed(IDirectFBVideoProvider * thiz,double multiplier)992 IDirectFBVideoProvider_GIF_SetSpeed( IDirectFBVideoProvider *thiz,
993                                      double                  multiplier )
994 {
995      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
996 
997      if (multiplier < 0.0)
998           return DFB_INVARG;
999 
1000      if (data->speed != multiplier) {
1001           pthread_mutex_lock( &data->lock );
1002           data->speed = multiplier;
1003           pthread_cond_signal( &data->cond );
1004           pthread_mutex_unlock( &data->lock );
1005      }
1006 
1007      return DFB_OK;
1008 }
1009 
1010 static DFBResult
IDirectFBVideoProvider_GIF_GetSpeed(IDirectFBVideoProvider * thiz,double * multiplier)1011 IDirectFBVideoProvider_GIF_GetSpeed( IDirectFBVideoProvider *thiz,
1012                                      double                 *multiplier )
1013 {
1014      DIRECT_INTERFACE_GET_DATA( IDirectFBVideoProvider_GIF )
1015 
1016      if (!multiplier)
1017           return DFB_INVARG;
1018 
1019      *multiplier = data->speed;
1020 
1021      return DFB_OK;
1022 }
1023 
1024 /* exported symbols */
1025 static DFBResult
Probe(IDirectFBVideoProvider_ProbeContext * ctx)1026 Probe( IDirectFBVideoProvider_ProbeContext *ctx )
1027 {
1028      if (!memcmp( ctx->header, "GIF89", 5 ))
1029           return DFB_OK;
1030 
1031      return DFB_UNSUPPORTED;
1032 }
1033 
1034 static DFBResult
Construct(IDirectFBVideoProvider * thiz,IDirectFBDataBuffer * buffer)1035 Construct( IDirectFBVideoProvider *thiz,
1036            IDirectFBDataBuffer    *buffer )
1037 {
1038      DFBResult ret;
1039 
1040      DIRECT_ALLOCATE_INTERFACE_DATA( thiz, IDirectFBVideoProvider_GIF )
1041 
1042      data->ref    = 1;
1043      data->status = DVSTATE_STOP;
1044      data->buffer = buffer;
1045      data->speed  = 1.0;
1046 
1047      buffer->AddRef( buffer );
1048      data->seekable = (buffer->SeekTo( buffer, 0 ) == DFB_OK);
1049 
1050      GIFReset( data );
1051      ret = GIFReadHeader( data );
1052      if (ret) {
1053           IDirectFBVideoProvider_GIF_Destruct( thiz );
1054           return ret;
1055      }
1056 
1057      data->buffer->GetPosition( data->buffer, &data->start_pos );
1058 
1059      direct_util_recursive_pthread_mutex_init( &data->lock );
1060      pthread_cond_init( &data->cond, NULL );
1061 
1062      thiz->AddRef                = IDirectFBVideoProvider_GIF_AddRef;
1063      thiz->Release               = IDirectFBVideoProvider_GIF_Release;
1064      thiz->GetCapabilities       = IDirectFBVideoProvider_GIF_GetCapabilities;
1065      thiz->GetSurfaceDescription = IDirectFBVideoProvider_GIF_GetSurfaceDescription;
1066      thiz->GetStreamDescription  = IDirectFBVideoProvider_GIF_GetStreamDescription;
1067      thiz->PlayTo                = IDirectFBVideoProvider_GIF_PlayTo;
1068      thiz->Stop                  = IDirectFBVideoProvider_GIF_Stop;
1069      thiz->GetStatus             = IDirectFBVideoProvider_GIF_GetStatus;
1070      thiz->SeekTo                = IDirectFBVideoProvider_GIF_SeekTo;
1071      thiz->GetPos                = IDirectFBVideoProvider_GIF_GetPos;
1072      thiz->GetLength             = IDirectFBVideoProvider_GIF_GetLength;
1073      thiz->SetPlaybackFlags      = IDirectFBVideoProvider_GIF_SetPlaybackFlags;
1074      thiz->SetSpeed              = IDirectFBVideoProvider_GIF_SetSpeed;
1075      thiz->GetSpeed              = IDirectFBVideoProvider_GIF_GetSpeed;
1076 
1077      return DFB_OK;
1078 }
1079