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