diff options
author | AlTheKiller <AlTheKiller@svn> | 2009-09-23 01:49:50 +0000 |
---|---|---|
committer | AlTheKiller <AlTheKiller@svn> | 2009-09-23 01:49:50 +0000 |
commit | 45285e8a9300cd754a760560640b75b09f98035e (patch) | |
tree | ad9f093885ad5c98e9dd4156674e7691c22ed0a2 /guilib/AnimatedGif.cpp |
step 3/4: Move linuxport to trunk. How'd I get roped into this?
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@23097 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'guilib/AnimatedGif.cpp')
-rw-r--r-- | guilib/AnimatedGif.cpp | 648 |
1 files changed, 648 insertions, 0 deletions
diff --git a/guilib/AnimatedGif.cpp b/guilib/AnimatedGif.cpp new file mode 100644 index 0000000000..d1c5f6be2b --- /dev/null +++ b/guilib/AnimatedGif.cpp @@ -0,0 +1,648 @@ + +// **************************************************************************** +// +// WINIMAGE.CPP : Generic classes for raster images (MSWindows specialization) +// +// Content: Member definitions for: +// - class CAnimatedGif : Storage class for single images +// - class CAnimatedGifSet : Storage class for sets of images +// +// (Includes routines to Load and Save BMP files and to load GIF files into +// these classes). +// +// -------------------------------------------------------------------------- +// +// Copyright (c) 2000, Juan Soulie <jsoulie@cplusplus.com> +// +// Permission to use, copy, modify, distribute and sell this software or any +// part thereof and/or its documentation for any purpose is granted without fee +// provided that the above copyright notice and this permission notice appear +// in all copies. +// +// This software is provided "as is" without express or implied warranty of +// any kind. The author shall have no liability with respect to the +// infringement of copyrights or patents that any modification to the content +// of this file or this file itself may incur. +// +// **************************************************************************** + +#include "AnimatedGif.h" +#include "FileSystem/SpecialProtocol.h" +#include "utils/EndianSwap.h" + +#ifdef _WIN32PC +extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode); +#else +#define fopen_utf8 fopen +#endif + +#pragma pack(1) +// Error processing macro (NO-OP by default): +#define ERRORMSG(PARAM) {} + +#ifndef BI_RGB + #define BI_RGB 0L + #define BI_RLE8 1L + #define BI_RLE4 2L + #define BI_BITFIELDS 3L +#endif + +// Macros to swap data endianness +#define SWAP16(X) X=Endian_SwapLE16(X) +#define SWAP32(X) X=Endian_SwapLE32(X) + +// pre-declaration: +int LZWDecoder (char*, char*, short, int, int, int, const int); + +// **************************************************************************** +// * CAnimatedGif Member definitions * +// **************************************************************************** + +CAnimatedGif::CAnimatedGif() +{ + Height = Width = 0; + Raster = NULL; + Palette = NULL; + pbmi = NULL; + BPP = Transparent = BytesPerRow = 0; + xPos = yPos = Delay = Transparency = 0; + nLoops = 1; //default=play animation 1 time +} + +CAnimatedGif::~CAnimatedGif() +{ + delete [] pbmi; + delete [] Raster; + delete [] Palette; +} + +// Init: Allocates space for raster and palette in GDI-compatible structures. +void CAnimatedGif::Init(int iWidth, int iHeight, int iBPP, int iLoops) +{ + delete[] Raster; + Raster = NULL; + + delete[] pbmi; + pbmi = NULL; + + delete[] Palette; + Palette = NULL; + + // Standard members setup + Transparent = -1; + BytesPerRow = Width = iWidth; + Height = iHeight; + BPP = iBPP; + // Animation Extra members setup: + xPos = yPos = Delay = Transparency = 0; + nLoops = iLoops; + + if (BPP == 24) + { + BytesPerRow *= 3; + pbmi = (GUIBITMAPINFO*)new char [sizeof(GUIBITMAPINFO)]; + } + else + { + pbmi = (GUIBITMAPINFO*)new char[sizeof(GUIBITMAPINFOHEADER)]; + Palette = new COLOR[256]; + } + + BytesPerRow += (ALIGN - Width % ALIGN) % ALIGN; // Align BytesPerRow + int size = BytesPerRow * Height; + + Raster = new char [size]; + + pbmi->bmiHeader.biSize = sizeof (GUIBITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = Width; + pbmi->bmiHeader.biHeight = -Height; // negative means up-to-bottom + pbmi->bmiHeader.biPlanes = 1; + pbmi->bmiHeader.biBitCount = (BPP < 8 ? 8 : BPP); // Our raster is byte-aligned + pbmi->bmiHeader.biCompression = BI_RGB; + pbmi->bmiHeader.biSizeImage = 0; + pbmi->bmiHeader.biXPelsPerMeter = 11811; + pbmi->bmiHeader.biYPelsPerMeter = 11811; + pbmi->bmiHeader.biClrUsed = 0; + pbmi->bmiHeader.biClrImportant = 0; +} + +// operator=: copies an object's content to another +CAnimatedGif& CAnimatedGif::operator = (CAnimatedGif& rhs) +{ + Init(rhs.Width, rhs.Height, rhs.BPP); // respects virtualization + memcpy(Raster, rhs.Raster, BytesPerRow*Height); + memcpy(Palette, rhs.Palette, 256*sizeof(COLOR)); + return *this; +} + + + +CAnimatedGifSet::CAnimatedGifSet() +{ + FrameHeight = FrameWidth = 0; + nLoops = 1; //default=play animation 1 time +} + +CAnimatedGifSet::~CAnimatedGifSet() +{ + Release(); +} + +void CAnimatedGifSet::Release() +{ + FrameWidth = 0; + FrameHeight = 0; + for (int i = 0; i < (int)m_vecimg.size(); ++i) + { + CAnimatedGif* pImage = m_vecimg[i]; + delete pImage; + } + m_vecimg.erase(m_vecimg.begin(), m_vecimg.end()); + +} + +// **************************************************************************** +// * CAnimatedGifSet Member definitions * +// **************************************************************************** + +// AddImage: Adds an image object to the back of the img vector. +void CAnimatedGifSet::AddImage (CAnimatedGif* newimage) +{ + m_vecimg.push_back(newimage); +} + +int CAnimatedGifSet::GetImageCount() const +{ + return m_vecimg.size(); +} + +unsigned char CAnimatedGifSet::getbyte(FILE *fd) +{ + unsigned char uchar; + if (fread(&uchar, 1, 1, fd) == 1) + return uchar; + else + return 0; +} + +// **************************************************************************** +// * LoadGIF * +// * Load a GIF File into the CAnimatedGifSet object * +// * (c) Nov 2000, Juan Soulie <jsoulie@cplusplus.com> * +// **************************************************************************** +int CAnimatedGifSet::LoadGIF (const char * szFileName) +{ + int n; + // Global GIF variables: + int GlobalBPP; // Bits per Pixel. + COLOR * GlobalColorMap; // Global colormap (allocate) + + struct GIFGCEtag + { // GRAPHIC CONTROL EXTENSION + unsigned char BlockSize; // Block Size: 4 bytes + unsigned char PackedFields; // 3.. Packed Fields. Bits detail: + // 0: Transparent Color Flag + // 1: User Input Flag + // 2-4: Disposal Method + unsigned short Delay; // 4..5 Delay Time (1/100 seconds) + unsigned char Transparent; // 6.. Transparent Color Index + } + gifgce; + + struct GIFNetscapeTag + { + unsigned char comment[11]; //4...14 NETSCAPE2.0 + unsigned char SubBlockLength; //15 0x3 + unsigned char reserved; //16 0x1 + unsigned short iIterations ; //17..18 number of iterations (lo-hi) + } + gifnetscape; + + int GraphicExtensionFound = 0; + + // OPEN FILE + FILE *fd = fopen_utf8(_P(szFileName), "rb"); + if (!fd) + { + return 0; + } + + // *1* READ HEADERBLOCK (6bytes) (SIGNATURE + VERSION) + char szSignature[6]; // First 6 bytes (GIF87a or GIF89a) + int iRead = fread(szSignature, 1, 6, fd); + if (iRead != 6) + { + fclose(fd); + return 0; + } + if ( memcmp(szSignature, "GIF", 2) != 0) + { + fclose(fd); + return 0; + } + // *2* READ LOGICAL SCREEN DESCRIPTOR + struct GIFLSDtag + { + unsigned short ScreenWidth; // Logical Screen Width + unsigned short ScreenHeight; // Logical Screen Height + unsigned char PackedFields; // Packed Fields. Bits detail: + // 0-2: Size of Global Color Table + // 3: Sort Flag + // 4-6: Color Resolution + // 7: Global Color Table Flag + unsigned char Background; // Background Color Index + unsigned char PixelAspectRatio; // Pixel Aspect Ratio + } + giflsd; + + iRead = fread(&giflsd, 1, sizeof(giflsd), fd); + if (iRead != sizeof(giflsd)) + { + fclose(fd); + return 0; + } + // endian swap + SWAP16(giflsd.ScreenWidth); + SWAP16(giflsd.ScreenHeight); + + GlobalBPP = (giflsd.PackedFields & 0x07) + 1; + + // fill some animation data: + FrameWidth = giflsd.ScreenWidth; + FrameHeight = giflsd.ScreenHeight; + nLoops = 1; //default=play animation 1 time + + // *3* READ/GENERATE GLOBAL COLOR MAP + GlobalColorMap = new COLOR [1 << GlobalBPP]; + if (giflsd.PackedFields & 0x80) // File has global color map? + for (n = 0;n < 1 << GlobalBPP;n++) + { + GlobalColorMap[n].r = getbyte(fd); + GlobalColorMap[n].g = getbyte(fd); + GlobalColorMap[n].b = getbyte(fd); + } + + else // GIF standard says to provide an internal default Palette: + for (n = 0;n < 256;n++) + GlobalColorMap[n].r = GlobalColorMap[n].g = GlobalColorMap[n].b = n; + + // *4* NOW WE HAVE 3 POSSIBILITIES: + // 4a) Get and Extension Block (Blocks with additional information) + // 4b) Get an Image Separator (Introductor to an image) + // 4c) Get the trailer Char (End of GIF File) + do + { + int charGot = getbyte(fd); + + if (charGot == 0x21) // *A* EXTENSION BLOCK + { + unsigned char extensionType = getbyte(fd); + switch (extensionType) + { + case 0xF9: // Graphic Control Extension + { + if (fread((char*)&gifgce, 1, sizeof(gifgce), fd) == sizeof(gifgce)) + SWAP16(gifgce.Delay); + GraphicExtensionFound++; + getbyte(fd); // Block Terminator (always 0) + } + break; + + case 0xFE: // Comment Extension: Ignored + { + while (int nBlockLength = getbyte(fd)) + for (n = 0;n < nBlockLength;n++) getbyte(fd); + } + break; + + case 0x01: // PlainText Extension: Ignored + { + while (int nBlockLength = getbyte(fd)) + for (n = 0;n < nBlockLength;n++) getbyte(fd); + } + break; + + case 0xFF: // Application Extension: Ignored + { + int nBlockLength = getbyte(fd); + if (nBlockLength == 0x0b) + { + struct GIFNetscapeTag tag; + if (fread((char*)&tag, 1, sizeof(gifnetscape), fd) == sizeof(gifnetscape)) + { + SWAP16(tag.iIterations); + nLoops = tag.iIterations; + } + else + nLoops = 0; + + if (nLoops) nLoops++; + getbyte(fd); + } + else + { + do + { + for (n = 0;n < nBlockLength;n++) getbyte(fd); + } + while ((nBlockLength = getbyte(fd)) != 0); + } + } + break; + + default: // Unknown Extension: Ignored + { + // read (and ignore) data sub-blocks + while (int nBlockLength = getbyte(fd)) + for (n = 0;n < nBlockLength;n++) getbyte(fd); + } + break; + } + } + else if (charGot == 0x2c) + { // *B* IMAGE (0x2c Image Separator) + // Create a new Image Object: + CAnimatedGif* NextImage = new CAnimatedGif(); + + // Read Image Descriptor + struct GIFIDtag + { + unsigned short xPos; // Image Left Position + unsigned short yPos; // Image Top Position + unsigned short Width; // Image Width + unsigned short Height; // Image Height + unsigned char PackedFields; // Packed Fields. Bits detail: + // 0-2: Size of Local Color Table + // 3-4: (Reserved) + // 5: Sort Flag + // 6: Interlace Flag + // 7: Local Color Table Flag + } + gifid; + + memset(&gifid, 0, sizeof(gifid)); + + int LocalColorMap = 0; + if (fread((char*)&gifid, 1, sizeof(gifid), fd) == sizeof(gifid)) + { + SWAP16(gifid.xPos); + SWAP16(gifid.yPos); + SWAP16(gifid.Width); + SWAP16(gifid.Height); + + LocalColorMap = (gifid.PackedFields & 0x08) ? 1 : 0; + } + + NextImage->Init(gifid.Width, gifid.Height, LocalColorMap ? (gifid.PackedFields&7) + 1 : GlobalBPP); + + // Fill NextImage Data + NextImage->xPos = gifid.xPos; + NextImage->yPos = gifid.yPos; + if (GraphicExtensionFound) + { + NextImage->Transparent = (gifgce.PackedFields & 0x01) ? gifgce.Transparent : -1; + NextImage->Transparency = (gifgce.PackedFields & 0x1c) > 1 ? 1 : 0; + NextImage->Delay = gifgce.Delay * 10; + } + + if (NextImage->Transparent != -1) + memset(NextImage->Raster, NextImage->Transparent, NextImage->BytesPerRow * NextImage->Height); + else + memset(NextImage->Raster, giflsd.Background, NextImage->BytesPerRow * NextImage->Height); + + // Read Color Map (if descriptor says so) + size_t palSize = sizeof(COLOR)*(1 << NextImage->BPP); + bool isPalRead = false; + if (LocalColorMap && fread((char*)NextImage->Palette, 1, palSize, fd) == palSize) + isPalRead = true; + + // Copy global, if no palette + if (!isPalRead) + memcpy(NextImage->Palette, GlobalColorMap, palSize); + + short firstbyte = getbyte(fd); // 1st byte of img block (CodeSize) + + // Calculate compressed image block size + // to fix: this allocates an extra byte per block + long ImgStart, ImgEnd; + ImgEnd = ImgStart = ftell(fd); + while ((n = getbyte(fd)) != 0) fseek (fd, ImgEnd += n + 1, SEEK_SET ); + fseek (fd, ImgStart, SEEK_SET); + + // Allocate Space for Compressed Image + char * pCompressedImage = new char [ImgEnd - ImgStart + 4]; + + // Read and store Compressed Image + char * pTemp = pCompressedImage; + while (int nBlockLength = getbyte(fd)) + { + if (fread(pTemp, 1, nBlockLength, fd) != (size_t)nBlockLength) + { + // Error? + } + pTemp += nBlockLength; + } + + // Call LZW/GIF decompressor + n = LZWDecoder( + (char*) pCompressedImage, + (char*) NextImage->Raster, + firstbyte, NextImage->BytesPerRow, //NextImage->AlignedWidth, + gifid.Width, gifid.Height, + ((gifid.PackedFields & 0x40) ? 1 : 0) //Interlaced? + ); + + if (n) + AddImage(NextImage); + else + { + delete NextImage; + ERRORMSG("GIF File Corrupt"); + } + + // Some cleanup + delete[] pCompressedImage; + GraphicExtensionFound = 0; + } + else if (charGot == 0x3b) + { + // *C* TRAILER: End of GIF Info + break; // Ok. Standard End. + } + + } + while ( !feof(fd) ); + + delete[] GlobalColorMap; + fclose(fd); + if ( GetImageCount() == 0) ERRORMSG("Premature End Of File"); + return GetImageCount(); +} + +// **************************************************************************** +// * LZWDecoder (C/C++) * +// * Codec to perform LZW (GIF Variant) decompression. * +// * (c) Nov2000, Juan Soulie <jsoulie@cplusplus.com> * +// **************************************************************************** +// +// Parameter description: +// - bufIn: Input buffer containing a "de-blocked" GIF/LZW compressed image. +// - bufOut: Output buffer where result will be stored. +// - InitCodeSize: Initial CodeSize to be Used +// (GIF files include this as the first byte in a picture block) +// - AlignedWidth : Width of a row in memory (including alignment if needed) +// - Width, Height: Physical dimensions of image. +// - Interlace: 1 for Interlaced GIFs. +// +int LZWDecoder (char * bufIn, char * bufOut, + short InitCodeSize, int AlignedWidth, + int Width, int Height, const int Interlace) +{ + int n; + int row = 0, col = 0; // used to point output if Interlaced + int nPixels, maxPixels; // Output pixel counter + + short CodeSize; // Current CodeSize (size in bits of codes) + short ClearCode; // Clear code : resets decompressor + short EndCode; // End code : marks end of information + + long whichBit; // Index of next bit in bufIn + long LongCode; // Temp. var. from which Code is retrieved + short Code; // Code extracted + short PrevCode; // Previous Code + short OutCode; // Code to output + + // Translation Table: + short Prefix[4096]; // Prefix: index of another Code + unsigned char Suffix[4096]; // Suffix: terminating character + short FirstEntry; // Index of first free entry in table + short NextEntry; // Index of next free entry in table + + unsigned char OutStack[4097]; // Output buffer + int OutIndex; // Characters in OutStack + + int RowOffset; // Offset in output buffer for current row + + // Set up values that depend on InitCodeSize Parameter. + CodeSize = InitCodeSize + 1; + ClearCode = (1 << InitCodeSize); + EndCode = ClearCode + 1; + NextEntry = FirstEntry = ClearCode + 2; + + whichBit = 0; + nPixels = 0; + maxPixels = Width * Height; + RowOffset = 0; + PrevCode = 0; + + while (nPixels < maxPixels) + { + OutIndex = 0; // Reset Output Stack + + // GET NEXT CODE FROM bufIn: + // LZW compression uses code items longer than a single byte. + // For GIF Files, code sizes are variable between 9 and 12 bits + // That's why we must read data (Code) this way: + LongCode = *((long*)(bufIn + whichBit / 8)); // Get some bytes from bufIn + SWAP32(LongCode); + LongCode >>= (whichBit&7); // Discard too low bits + Code = (short)((LongCode & ((1 << CodeSize) - 1) )); // Discard too high bits + whichBit += CodeSize; // Increase Bit Offset + + // SWITCH, DIFFERENT POSIBILITIES FOR CODE: + if (Code == EndCode) // END CODE + break; // Exit LZW Decompression loop + + if (Code == ClearCode) + { + // CLEAR CODE: + CodeSize = InitCodeSize + 1; // Reset CodeSize + NextEntry = FirstEntry; // Reset Translation Table + PrevCode = Code; // Prevent next to be added to table. + continue; // restart, to get another code + } + if (Code < NextEntry) // CODE IS IN TABLE + OutCode = Code; // Set code to output. + + else + { // CODE IS NOT IN TABLE: + OutIndex++; // Keep "first" character of previous output. + OutCode = PrevCode; // Set PrevCode to be output + } + + // EXPAND OutCode IN OutStack + // - Elements up to FirstEntry are Raw-Codes and are not expanded + // - Table Prefices contain indexes to other codes + // - Table Suffices contain the raw codes to be output + while (OutCode >= FirstEntry) + { + if (OutIndex > 4096 || OutCode >= 4096) + return 0; + OutStack[OutIndex++] = Suffix[OutCode]; // Add suffix to Output Stack + OutCode = Prefix[OutCode]; // Loop with preffix + } + + // NOW OutCode IS A RAW CODE, ADD IT TO OUTPUT STACK. + if (OutIndex > 4096) + return 0; + OutStack[OutIndex++] = (unsigned char) OutCode; + + // ADD NEW ENTRY TO TABLE (PrevCode + OutCode) + // (EXCEPT IF PREVIOUS CODE WAS A CLEARCODE) + if (PrevCode != ClearCode) + { + Prefix[NextEntry] = PrevCode; + Suffix[NextEntry] = (unsigned char) OutCode; + NextEntry++; + + // Prevent Translation table overflow: + if (NextEntry >= 4096) + return 0; + + // INCREASE CodeSize IF NextEntry IS INVALID WITH CURRENT CodeSize + if (NextEntry >= (1 << CodeSize)) + { + if (CodeSize < 12) CodeSize++; + else + { + ; + } // Do nothing. Maybe next is Clear Code. + } + } + + PrevCode = Code; + + // Avoid the possibility of overflow on 'bufOut'. + if (nPixels + OutIndex > maxPixels) OutIndex = maxPixels - nPixels; + + // OUTPUT OutStack (LAST-IN FIRST-OUT ORDER) + for (n = OutIndex - 1; n >= 0; n--) + { + if (col == Width) // Check if new row. + { + if (Interlace) + { + // If interlaced:: + if ((row&7) == 0) {row += 8; if (row >= Height) row = 4;} + else if ((row&3) == 0) {row += 8; if (row >= Height) row = 2;} + else if ((row&1) == 0) {row += 4; if (row >= Height) row = 1;} + else row += 2; + } + else // If not interlaced: + row++; + + RowOffset = row * AlignedWidth; // Set new row offset + col = 0; + } + bufOut[RowOffset + col] = OutStack[n]; // Write output + col++; nPixels++; // Increase counters. + } + + } // while (main decompressor loop) + + return whichBit; +} + +// Refer to WINIMAGE.TXT for copyright and patent notices on GIF and LZW. + +#pragma pack() |