PageRenderTime 84ms CodeModel.GetById 17ms app.highlight 61ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/guilib/AnimatedGif.cpp

http://github.com/xbmc/xbmc
C++ | 655 lines | 445 code | 92 blank | 118 comment | 92 complexity | a8f4a9e1d0026ed3c75174a37a687667 MD5 | raw file
  1
  2// ****************************************************************************
  3//
  4// WINIMAGE.CPP : Generic classes for raster images (MSWindows specialization)
  5//
  6//  Content: Member definitions for:
  7//  - class CAnimatedGif             : Storage class for single images
  8//  - class CAnimatedGifSet          : Storage class for sets of images
  9//
 10//  (Includes routines to Load and Save BMP files and to load GIF files into
 11// these classes).
 12//
 13//  --------------------------------------------------------------------------
 14//
 15// Copyright (c) 2000, Juan Soulie <jsoulie@cplusplus.com>
 16//
 17// Permission to use, copy, modify, distribute and sell this software or any
 18// part thereof and/or its documentation for any purpose is granted without fee
 19// provided that the above copyright notice and this permission notice appear
 20// in all copies.
 21//
 22// This software is provided "as is" without express or implied warranty of
 23// any kind. The author shall have no liability with respect to the
 24// infringement of copyrights or patents that any modification to the content
 25// of this file or this file itself may incur.
 26//
 27// ****************************************************************************
 28
 29#include "AnimatedGif.h"
 30#include "filesystem/SpecialProtocol.h"
 31#include "utils/EndianSwap.h"
 32
 33#ifdef TARGET_WINDOWS
 34extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode);
 35#else
 36#define fopen_utf8 fopen
 37#endif
 38
 39#pragma pack(1)
 40// Error processing macro (NO-OP by default):
 41#define ERRORMSG(PARAM) {}
 42
 43#ifndef BI_RGB
 44 #define BI_RGB        0L
 45 #define BI_RLE8       1L
 46 #define BI_RLE4       2L
 47 #define BI_BITFIELDS  3L
 48#endif
 49
 50#undef ALIGN
 51#define ALIGN sizeof(int)         ///< Windows GDI expects all int-aligned
 52
 53// Macros to swap data endianness
 54#define SWAP16(X)    X=Endian_SwapLE16(X)
 55#define SWAP32(X)    X=Endian_SwapLE32(X)
 56
 57// pre-declaration:
 58int LZWDecoder (char*, char*, short, int, int, int, const int);
 59
 60// ****************************************************************************
 61// * CAnimatedGif Member definitions                                               *
 62// ****************************************************************************
 63
 64CAnimatedGif::CAnimatedGif()
 65{
 66  Height = Width = 0;
 67  Raster = NULL;
 68  Palette = NULL;
 69  pbmi = NULL;
 70  BPP = Transparent = BytesPerRow = 0;
 71  xPos = yPos = Delay = Transparency = 0;
 72  nLoops = 1; //default=play animation 1 time
 73}
 74
 75CAnimatedGif::~CAnimatedGif()
 76{
 77  delete [] pbmi;
 78  delete [] Raster;
 79  delete [] Palette;
 80}
 81
 82// Init: Allocates space for raster and palette in GDI-compatible structures.
 83void CAnimatedGif::Init(int iWidth, int iHeight, int iBPP, int iLoops)
 84{
 85  delete[] Raster;
 86  Raster = NULL;
 87
 88  delete[] pbmi;
 89  pbmi = NULL;
 90
 91  delete[] Palette;
 92  Palette = NULL;
 93
 94  // Standard members setup
 95  Transparent = -1;
 96  BytesPerRow = Width = iWidth;
 97  Height = iHeight;
 98  BPP = iBPP;
 99  // Animation Extra members setup:
100  xPos = yPos = Delay = Transparency = 0;
101  nLoops = iLoops;
102
103  if (BPP == 24)
104  {
105    BytesPerRow *= 3;
106    pbmi = (GUIBITMAPINFO*)new char [sizeof(GUIBITMAPINFO)];
107  }
108  else
109  {
110    pbmi = (GUIBITMAPINFO*)new char[sizeof(GUIBITMAPINFOHEADER)];
111    Palette = new COLOR[256];
112  }
113
114  BytesPerRow += (ALIGN - Width % ALIGN) % ALIGN; // Align BytesPerRow
115  int size = BytesPerRow * Height;
116
117  Raster = new char [size];
118
119  pbmi->bmiHeader.biSize = sizeof (GUIBITMAPINFOHEADER);
120  pbmi->bmiHeader.biWidth = Width;
121  pbmi->bmiHeader.biHeight = -Height;   // negative means up-to-bottom
122  pbmi->bmiHeader.biPlanes = 1;
123  pbmi->bmiHeader.biBitCount = (BPP < 8 ? 8 : BPP); // Our raster is byte-aligned
124  pbmi->bmiHeader.biCompression = BI_RGB;
125  pbmi->bmiHeader.biSizeImage = 0;
126  pbmi->bmiHeader.biXPelsPerMeter = 11811;
127  pbmi->bmiHeader.biYPelsPerMeter = 11811;
128  pbmi->bmiHeader.biClrUsed = 0;
129  pbmi->bmiHeader.biClrImportant = 0;
130}
131
132// operator=: copies an object's content to another
133CAnimatedGif& CAnimatedGif::operator = (CAnimatedGif& rhs)
134{
135  Init(rhs.Width, rhs.Height, rhs.BPP); // respects virtualization
136  memcpy(Raster, rhs.Raster, BytesPerRow*Height);
137  memcpy(Palette, rhs.Palette, 256*sizeof(COLOR));
138  return *this;
139}
140
141
142
143CAnimatedGifSet::CAnimatedGifSet()
144{
145  FrameHeight = FrameWidth = 0;
146  nLoops = 1; //default=play animation 1 time
147}
148
149CAnimatedGifSet::~CAnimatedGifSet()
150{
151  Release();
152}
153
154void CAnimatedGifSet::Release()
155{
156  FrameWidth = 0;
157  FrameHeight = 0;
158  for (int i = 0; i < (int)m_vecimg.size(); ++i)
159  {
160    CAnimatedGif* pImage = m_vecimg[i];
161    delete pImage;
162  }
163  m_vecimg.erase(m_vecimg.begin(), m_vecimg.end());
164
165}
166
167// ****************************************************************************
168// * CAnimatedGifSet Member definitions                                            *
169// ****************************************************************************
170
171// AddImage: Adds an image object to the back of the img vector.
172void CAnimatedGifSet::AddImage (CAnimatedGif* newimage)
173{
174  m_vecimg.push_back(newimage);
175}
176
177int CAnimatedGifSet::GetImageCount() const
178{
179  return m_vecimg.size();
180}
181
182unsigned char CAnimatedGifSet::getbyte(FILE *fd)
183{
184  unsigned char uchar;
185  if (fread(&uchar, 1, 1, fd) == 1)
186    return uchar;
187  else
188    return 0;
189}
190
191// ****************************************************************************
192// * LoadGIF                                                                  *
193// *   Load a GIF File into the CAnimatedGifSet object                             *
194// *                        (c) Nov 2000, Juan Soulie <jsoulie@cplusplus.com> *
195// ****************************************************************************
196int CAnimatedGifSet::LoadGIF (const char * szFileName)
197{
198  int n;
199  // Global GIF variables:
200  int GlobalBPP;       // Bits per Pixel.
201  COLOR * GlobalColorMap;     // Global colormap (allocate)
202
203  struct GIFGCEtag
204  {                // GRAPHIC CONTROL EXTENSION
205    unsigned char BlockSize;   // Block Size: 4 bytes
206    unsigned char PackedFields;  // 3.. Packed Fields. Bits detail:
207    //    0: Transparent Color Flag
208    //    1: User Input Flag
209    //  2-4: Disposal Method
210    unsigned short Delay;     // 4..5 Delay Time (1/100 seconds)
211    unsigned char Transparent;  // 6.. Transparent Color Index
212  }
213  gifgce;
214
215  struct GIFNetscapeTag
216  {
217    unsigned char comment[11];  //4...14  NETSCAPE2.0
218    unsigned char SubBlockLength; //15      0x3
219    unsigned char reserved;       //16      0x1
220    unsigned short iIterations ;    //17..18  number of iterations (lo-hi)
221  }
222  gifnetscape;
223
224  int GraphicExtensionFound = 0;
225
226  // OPEN FILE
227  FILE *fd = fopen_utf8(CSpecialProtocol::TranslatePath(szFileName), "rb");
228  if (!fd)
229  {
230    return 0;
231  }
232
233  // *1* READ HEADERBLOCK (6bytes) (SIGNATURE + VERSION)
234  char szSignature[6];    // First 6 bytes (GIF87a or GIF89a)
235  int iRead = fread(szSignature, 1, 6, fd);
236  if (iRead != 6)
237  {
238    fclose(fd);
239    return 0;
240  }
241  if ( memcmp(szSignature, "GIF", 2) != 0)
242  {
243    fclose(fd);
244    return 0;
245  }
246  // *2* READ LOGICAL SCREEN DESCRIPTOR
247  struct GIFLSDtag
248  {
249    unsigned short ScreenWidth;  // Logical Screen Width
250    unsigned short ScreenHeight; // Logical Screen Height
251    unsigned char PackedFields;  // Packed Fields. Bits detail:
252    //  0-2: Size of Global Color Table
253    //    3: Sort Flag
254    //  4-6: Color Resolution
255    //    7: Global Color Table Flag
256    unsigned char Background;  // Background Color Index
257    unsigned char PixelAspectRatio; // Pixel Aspect Ratio
258  }
259  giflsd;
260
261  iRead = fread(&giflsd, 1, sizeof(giflsd), fd);
262  if (iRead != sizeof(giflsd))
263  {
264    fclose(fd);
265    return 0;
266  }
267  // endian swap
268  SWAP16(giflsd.ScreenWidth);
269  SWAP16(giflsd.ScreenHeight);
270
271  GlobalBPP = (giflsd.PackedFields & 0x07) + 1;
272
273  // fill some animation data:
274  FrameWidth = giflsd.ScreenWidth;
275  FrameHeight = giflsd.ScreenHeight;
276  nLoops = 1; //default=play animation 1 time
277
278  // *3* READ/GENERATE GLOBAL COLOR MAP
279  GlobalColorMap = new COLOR [1 << GlobalBPP];
280  if (giflsd.PackedFields & 0x80) // File has global color map?
281    for (n = 0;n < 1 << GlobalBPP;n++)
282    {
283      GlobalColorMap[n].r = getbyte(fd);
284      GlobalColorMap[n].g = getbyte(fd);
285      GlobalColorMap[n].b = getbyte(fd);
286      GlobalColorMap[n].x = 0;
287    }
288
289  else // GIF standard says to provide an internal default Palette:
290    for (n = 0;n < 256;n++)
291    {
292      GlobalColorMap[n].r = GlobalColorMap[n].g = GlobalColorMap[n].b = n;
293      GlobalColorMap[n].x = 0;
294    }
295
296  // *4* NOW WE HAVE 3 POSSIBILITIES:
297  //  4a) Get and Extension Block (Blocks with additional information)
298  //  4b) Get an Image Separator (Introductor to an image)
299  //  4c) Get the trailer Char (End of GIF File)
300  do
301  {
302    int charGot = getbyte(fd);
303
304    if (charGot == 0x21)  // *A* EXTENSION BLOCK
305    {
306      unsigned char extensionType = getbyte(fd);
307      switch (extensionType)
308      {
309      case 0xF9:    // Graphic Control Extension
310        {
311          if (fread((char*)&gifgce, 1, sizeof(gifgce), fd) == sizeof(gifgce))
312            SWAP16(gifgce.Delay);
313          GraphicExtensionFound++;
314          getbyte(fd); // Block Terminator (always 0)
315        }
316        break;
317
318      case 0xFE:    // Comment Extension: Ignored
319        {
320          while (int nBlockLength = getbyte(fd))
321            for (n = 0;n < nBlockLength;n++) getbyte(fd);
322        }
323        break;
324
325      case 0x01:    // PlainText Extension: Ignored
326        {
327          while (int nBlockLength = getbyte(fd))
328            for (n = 0;n < nBlockLength;n++) getbyte(fd);
329        }
330        break;
331
332      case 0xFF:    // Application Extension: Ignored
333        {
334          int nBlockLength = getbyte(fd);
335          if (nBlockLength == 0x0b)
336          {
337            struct GIFNetscapeTag tag;
338            if (fread((char*)&tag, 1, sizeof(gifnetscape), fd) == sizeof(gifnetscape))
339            {
340              SWAP16(tag.iIterations);
341              nLoops = tag.iIterations;
342            }
343            else
344              nLoops = 0;
345
346            if (nLoops) nLoops++;
347            getbyte(fd);
348          }
349          else
350          {
351            do
352            {
353              for (n = 0;n < nBlockLength;n++) getbyte(fd);
354            }
355            while ((nBlockLength = getbyte(fd)) != 0);
356          }
357        }
358        break;
359
360       default:    // Unknown Extension: Ignored
361        {
362          // read (and ignore) data sub-blocks
363          while (int nBlockLength = getbyte(fd))
364            for (n = 0;n < nBlockLength;n++) getbyte(fd);
365        }
366        break;
367      }
368    }
369    else if (charGot == 0x2c)
370    { // *B* IMAGE (0x2c Image Separator)
371      // Create a new Image Object:
372      CAnimatedGif* NextImage = new CAnimatedGif();
373
374      // Read Image Descriptor
375      struct GIFIDtag
376      {
377        unsigned short xPos;     // Image Left Position
378        unsigned short yPos;     // Image Top Position
379        unsigned short Width;     // Image Width
380        unsigned short Height;    // Image Height
381        unsigned char PackedFields;  // Packed Fields. Bits detail:
382        //  0-2: Size of Local Color Table
383        //  3-4: (Reserved)
384        //    5: Sort Flag
385        //    6: Interlace Flag
386        //    7: Local Color Table Flag
387      }
388      gifid;
389
390      memset(&gifid, 0, sizeof(gifid));
391
392      int LocalColorMap = 0;
393      if (fread((char*)&gifid, 1, sizeof(gifid), fd) == sizeof(gifid))
394      {
395        SWAP16(gifid.xPos);
396        SWAP16(gifid.yPos);
397        SWAP16(gifid.Width);
398        SWAP16(gifid.Height);
399
400        LocalColorMap = (gifid.PackedFields & 0x08) ? 1 : 0;
401      }
402
403      NextImage->Init(gifid.Width, gifid.Height, LocalColorMap ? (gifid.PackedFields&7) + 1 : GlobalBPP);
404
405      // Fill NextImage Data
406      NextImage->xPos = gifid.xPos;
407      NextImage->yPos = gifid.yPos;
408      if (GraphicExtensionFound)
409      {
410        NextImage->Transparent = (gifgce.PackedFields & 0x01) ? gifgce.Transparent : -1;
411        NextImage->Transparency = (gifgce.PackedFields & 0x1c) > 1 ? 1 : 0;
412        NextImage->Delay = gifgce.Delay * 10;
413      }
414
415      if (NextImage->Transparent != -1)
416        memset(NextImage->Raster, NextImage->Transparent, NextImage->BytesPerRow * NextImage->Height);
417      else
418        memset(NextImage->Raster, giflsd.Background, NextImage->BytesPerRow * NextImage->Height);
419
420      // Read Color Map (if descriptor says so)
421      size_t palSize = sizeof(COLOR)*(1 << NextImage->BPP);
422      bool isPalRead = false;
423      if (LocalColorMap && fread((char*)NextImage->Palette, 1, palSize, fd) == palSize)
424        isPalRead = true;
425
426      // Copy global, if no palette
427      if (!isPalRead)
428        memcpy(NextImage->Palette, GlobalColorMap, palSize);
429
430      short firstbyte = getbyte(fd); // 1st byte of img block (CodeSize)
431
432      // Calculate compressed image block size
433      // to fix: this allocates an extra byte per block
434      long ImgStart, ImgEnd;
435      ImgEnd = ImgStart = ftell(fd);
436      while ((n = getbyte(fd)) !=  0) fseek (fd, ImgEnd += n + 1, SEEK_SET );
437      fseek (fd, ImgStart, SEEK_SET);
438
439      // Allocate Space for Compressed Image
440      char * pCompressedImage = new char [ImgEnd - ImgStart + 4];
441
442      // Read and store Compressed Image
443      char * pTemp = pCompressedImage;
444      while (int nBlockLength = getbyte(fd))
445      {
446        if (fread(pTemp, 1, nBlockLength, fd) != (size_t)nBlockLength)
447        {
448        // Error?
449        }
450        pTemp += nBlockLength;
451      }
452
453      // Call LZW/GIF decompressor
454      n = LZWDecoder(
455            (char*) pCompressedImage,
456            (char*) NextImage->Raster,
457            firstbyte, NextImage->BytesPerRow, //NextImage->AlignedWidth,
458            gifid.Width, gifid.Height,
459            ((gifid.PackedFields & 0x40) ? 1 : 0) //Interlaced?
460          );
461
462      if (n)
463        AddImage(NextImage);
464      else
465      {
466        delete NextImage;
467        ERRORMSG("GIF File Corrupt");
468      }
469
470      // Some cleanup
471      delete[] pCompressedImage;
472      GraphicExtensionFound = 0;
473    }
474    else if (charGot == 0x3b)
475    {
476      // *C* TRAILER: End of GIF Info
477      break; // Ok. Standard End.
478    }
479
480  }
481  while ( !feof(fd) );
482
483  delete[] GlobalColorMap;
484  fclose(fd);
485  if ( GetImageCount() == 0) ERRORMSG("Premature End Of File");
486  return GetImageCount();
487}
488
489// ****************************************************************************
490// * LZWDecoder (C/C++)                                                       *
491// * Codec to perform LZW (GIF Variant) decompression.                        *
492// *                         (c) Nov2000, Juan Soulie <jsoulie@cplusplus.com> *
493// ****************************************************************************
494//
495// Parameter description:
496//  - bufIn: Input buffer containing a "de-blocked" GIF/LZW compressed image.
497//  - bufOut: Output buffer where result will be stored.
498//  - InitCodeSize: Initial CodeSize to be Used
499//    (GIF files include this as the first byte in a picture block)
500//  - AlignedWidth : Width of a row in memory (including alignment if needed)
501//  - Width, Height: Physical dimensions of image.
502//  - Interlace: 1 for Interlaced GIFs.
503//
504int LZWDecoder (char * bufIn, char * bufOut,
505                short InitCodeSize, int AlignedWidth,
506                int Width, int Height, const int Interlace)
507{
508  int n;
509  int row = 0, col = 0;    // used to point output if Interlaced
510  int nPixels, maxPixels; // Output pixel counter
511
512  short CodeSize;      // Current CodeSize (size in bits of codes)
513  short ClearCode;     // Clear code : resets decompressor
514  short EndCode;      // End code : marks end of information
515
516  long whichBit;      // Index of next bit in bufIn
517  long LongCode;      // Temp. var. from which Code is retrieved
518  short Code;        // Code extracted
519  short PrevCode;      // Previous Code
520  short OutCode;      // Code to output
521
522  // Translation Table:
523  short Prefix[4096] = {};    // Prefix: index of another Code
524  unsigned char Suffix[4096] = {};    // Suffix: terminating character
525  short FirstEntry;     // Index of first free entry in table
526  short NextEntry;     // Index of next free entry in table
527
528  unsigned char OutStack[4097];   // Output buffer
529  int OutIndex;      // Characters in OutStack
530
531  int RowOffset;     // Offset in output buffer for current row
532
533  // Set up values that depend on InitCodeSize Parameter.
534  CodeSize = InitCodeSize + 1;
535  ClearCode = (1 << InitCodeSize);
536  EndCode = ClearCode + 1;
537  NextEntry = FirstEntry = ClearCode + 2;
538
539  whichBit = 0;
540  nPixels = 0;
541  maxPixels = Width * Height;
542  RowOffset = 0;
543  PrevCode = 0;
544
545  while (nPixels < maxPixels)
546  {
547    OutIndex = 0;       // Reset Output Stack
548
549    // GET NEXT CODE FROM bufIn:
550    // LZW compression uses code items longer than a single byte.
551    // For GIF Files, code sizes are variable between 9 and 12 bits
552    // That's why we must read data (Code) this way:
553    LongCode = *((long*)(bufIn + whichBit / 8));     // Get some bytes from bufIn
554    SWAP32(LongCode);
555    LongCode >>= (whichBit&7);            // Discard too low bits
556    Code = (short)((LongCode & ((1 << CodeSize) - 1) )); // Discard too high bits
557    whichBit += CodeSize;              // Increase Bit Offset
558
559    // SWITCH, DIFFERENT POSIBILITIES FOR CODE:
560    if (Code == EndCode)     // END CODE
561      break;           // Exit LZW Decompression loop
562
563    if (Code == ClearCode)
564    {
565      // CLEAR CODE:
566      CodeSize = InitCodeSize + 1; // Reset CodeSize
567      NextEntry = FirstEntry;   // Reset Translation Table
568      PrevCode = Code;       // Prevent next to be added to table.
569      continue;          // restart, to get another code
570    }
571    if (Code < NextEntry)     // CODE IS IN TABLE
572      OutCode = Code;       // Set code to output.
573
574    else
575    {               // CODE IS NOT IN TABLE:
576      OutIndex++;         // Keep "first" character of previous output.
577      OutCode = PrevCode;     // Set PrevCode to be output
578    }
579
580    // EXPAND OutCode IN OutStack
581    // - Elements up to FirstEntry are Raw-Codes and are not expanded
582    // - Table Prefices contain indexes to other codes
583    // - Table Suffices contain the raw codes to be output
584    while (OutCode >= FirstEntry)
585    {
586      if (OutIndex > 4096 || OutCode >= 4096)
587        return 0;
588      OutStack[OutIndex++] = Suffix[OutCode]; // Add suffix to Output Stack
589      OutCode = Prefix[OutCode];       // Loop with preffix
590    }
591
592    // NOW OutCode IS A RAW CODE, ADD IT TO OUTPUT STACK.
593    if (OutIndex > 4096)
594      return 0;
595    OutStack[OutIndex++] = (unsigned char) OutCode;
596
597    // ADD NEW ENTRY TO TABLE (PrevCode + OutCode)
598    // (EXCEPT IF PREVIOUS CODE WAS A CLEARCODE)
599    if (PrevCode != ClearCode)
600    {
601      Prefix[NextEntry] = PrevCode;
602      Suffix[NextEntry] = (unsigned char) OutCode;
603      NextEntry++;
604
605      // Prevent Translation table overflow:
606      if (NextEntry >= 4096)
607        return 0;
608
609      // INCREASE CodeSize IF NextEntry IS INVALID WITH CURRENT CodeSize
610      if (NextEntry >= (1 << CodeSize))
611      {
612        if (CodeSize < 12) CodeSize++;
613        else
614        {
615          ;
616        }    // Do nothing. Maybe next is Clear Code.
617      }
618    }
619
620    PrevCode = Code;
621
622    // Avoid the possibility of overflow on 'bufOut'.
623    if (nPixels + OutIndex > maxPixels) OutIndex = maxPixels - nPixels;
624
625    // OUTPUT OutStack (LAST-IN FIRST-OUT ORDER)
626    for (n = OutIndex - 1; n >= 0; n--)
627    {
628      if (col == Width)      // Check if new row.
629      {
630        if (Interlace)
631        {
632          // If interlaced::
633          if ((row&7) == 0) {row += 8; if (row >= Height) row = 4;}
634        else if ((row&3) == 0) {row += 8; if (row >= Height) row = 2;}
635        else if ((row&1) == 0) {row += 4; if (row >= Height) row = 1;}
636          else row += 2;
637        }
638        else       // If not interlaced:
639          row++;
640
641        RowOffset = row * AlignedWidth;  // Set new row offset
642        col = 0;
643      }
644      bufOut[RowOffset + col] = OutStack[n]; // Write output
645      col++; nPixels++;     // Increase counters.
646    }
647
648  } // while (main decompressor loop)
649
650  return whichBit;
651}
652
653// Refer to WINIMAGE.TXT for copyright and patent notices on GIF and LZW.
654
655#pragma pack()