Add basic gif support to CBitmap (issue #238)
This commit is contained in:
parent
fcc15e049e
commit
3d5503bac6
5 changed files with 364 additions and 0 deletions
|
@ -106,6 +106,7 @@ ENDIF(WIN32)
|
||||||
|
|
||||||
FIND_PACKAGE(LibXml2 REQUIRED)
|
FIND_PACKAGE(LibXml2 REQUIRED)
|
||||||
FIND_PACKAGE(PNG REQUIRED)
|
FIND_PACKAGE(PNG REQUIRED)
|
||||||
|
FIND_PACKAGE(GIF)
|
||||||
FIND_PACKAGE(Jpeg)
|
FIND_PACKAGE(Jpeg)
|
||||||
|
|
||||||
IF(WITH_STATIC_LIBXML2)
|
IF(WITH_STATIC_LIBXML2)
|
||||||
|
|
|
@ -44,6 +44,7 @@ const uint32 DDS_HEADER = NL_MAKEFOURCC('D', 'D', 'S', ' ');
|
||||||
const uint32 DXT_HEADER = NL_MAKEFOURCC('D', 'X', 'T', '\0');
|
const uint32 DXT_HEADER = NL_MAKEFOURCC('D', 'X', 'T', '\0');
|
||||||
const uint32 PNG_HEADER = NL_MAKEFOURCC(0x89, 'P', 'N', 'G');
|
const uint32 PNG_HEADER = NL_MAKEFOURCC(0x89, 'P', 'N', 'G');
|
||||||
const uint32 JPG_HEADER = NL_MAKEFOURCC(0xff, 0xd8, 0xff, 0xe0);
|
const uint32 JPG_HEADER = NL_MAKEFOURCC(0xff, 0xd8, 0xff, 0xe0);
|
||||||
|
const uint32 GIF_HEADER = NL_MAKEFOURCC('G', 'I', 'F', '8');
|
||||||
|
|
||||||
|
|
||||||
// dwLinearSize is valid
|
// dwLinearSize is valid
|
||||||
|
@ -132,6 +133,15 @@ private :
|
||||||
uint8 readJPG( NLMISC::IStream &f );
|
uint8 readJPG( NLMISC::IStream &f );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a GIF from an IStream.
|
||||||
|
* GIF pictures are all converted to 32bit
|
||||||
|
* \param f IStream (must be a reading stream)
|
||||||
|
* \return image depth if succeed, 0 else
|
||||||
|
*/
|
||||||
|
uint8 readGIF( NLMISC::IStream &f );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change bitmap format
|
* Change bitmap format
|
||||||
*
|
*
|
||||||
|
|
|
@ -187,6 +187,12 @@ IF(JPEG_FOUND)
|
||||||
TARGET_LINK_LIBRARIES(nelmisc ${JPEG_LIBRARY})
|
TARGET_LINK_LIBRARIES(nelmisc ${JPEG_LIBRARY})
|
||||||
ENDIF(JPEG_FOUND)
|
ENDIF(JPEG_FOUND)
|
||||||
|
|
||||||
|
IF(GIF_FOUND)
|
||||||
|
INCLUDE_DIRECTORIES(${GIF_INCLUDE_DIR})
|
||||||
|
ADD_DEFINITIONS(-DUSE_GIF)
|
||||||
|
TARGET_LINK_LIBRARIES(nelmisc ${GIF_LIBRARY})
|
||||||
|
ENDIF(GIF_FOUND)
|
||||||
|
|
||||||
IF(WITH_STATIC OR WIN32)
|
IF(WITH_STATIC OR WIN32)
|
||||||
TARGET_LINK_LIBRARIES(nelmisc ${PNG_LIBRARIES})
|
TARGET_LINK_LIBRARIES(nelmisc ${PNG_LIBRARIES})
|
||||||
ELSE(WITH_STATIC OR WIN32)
|
ELSE(WITH_STATIC OR WIN32)
|
||||||
|
|
|
@ -134,6 +134,19 @@ uint8 CBitmap::load(NLMISC::IStream &f, uint mipMapSkip)
|
||||||
}
|
}
|
||||||
#endif // USE_JPEG
|
#endif // USE_JPEG
|
||||||
|
|
||||||
|
#ifdef USE_GIF
|
||||||
|
if (fileType == GIF_HEADER)
|
||||||
|
{
|
||||||
|
#ifdef NEL_ALL_BITMAP_WHITE
|
||||||
|
uint8 result = readGIF(f);
|
||||||
|
MakeWhite (*this);
|
||||||
|
return result;
|
||||||
|
#else // NEL_ALL_BITMAP_WHITE
|
||||||
|
return readGIF(f);
|
||||||
|
#endif // NEL_ALL_BITMAP_WHITE
|
||||||
|
}
|
||||||
|
#endif // USE_GIF
|
||||||
|
|
||||||
// assuming it's TGA
|
// assuming it's TGA
|
||||||
NLMISC::IStream::TSeekOrigin origin= f.begin;
|
NLMISC::IStream::TSeekOrigin origin= f.begin;
|
||||||
if(!f.seek (0, origin))
|
if(!f.seek (0, origin))
|
||||||
|
@ -3149,6 +3162,25 @@ void CBitmap::loadSize(NLMISC::IStream &f, uint32 &retWidth, uint32 &retHeight)
|
||||||
}
|
}
|
||||||
while(!eof);
|
while(!eof);
|
||||||
}
|
}
|
||||||
|
else if(fileType == GIF_HEADER)
|
||||||
|
{
|
||||||
|
// check second part of header ("7a" or "9a" in 'GIF89a')
|
||||||
|
uint16 s;
|
||||||
|
f.serial(s);
|
||||||
|
if (s != 0x6137 && s != 0x6139)
|
||||||
|
{
|
||||||
|
nlwarning("Invalid GIF header, expected GIF87a or GIF89a");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 lsWidth;
|
||||||
|
uint16 lsHeight;
|
||||||
|
f.serial(lsWidth);
|
||||||
|
f.serial(lsHeight);
|
||||||
|
|
||||||
|
retWidth = lsWidth;
|
||||||
|
retHeight = lsHeight;
|
||||||
|
}
|
||||||
// assuming it's TGA
|
// assuming it's TGA
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
315
code/nel/src/misc/bitmap_gif.cpp
Normal file
315
code/nel/src/misc/bitmap_gif.cpp
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "stdmisc.h"
|
||||||
|
#include "nel/misc/bitmap.h"
|
||||||
|
|
||||||
|
#ifdef USE_GIF
|
||||||
|
#include <gif_lib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace NLMISC
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef USE_GIF
|
||||||
|
|
||||||
|
// GIFLIB_MAJOR is defined from version 5
|
||||||
|
#ifndef GIFLIB_MAJOR
|
||||||
|
#define GIFLIB_MAJOR 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint8 GIF_TRANSPARENT_MASK = 0x01;
|
||||||
|
static uint8 GIF_DISPOSE_MASK = 0x07;
|
||||||
|
static sint8 GIF_NOT_TRANSPARENT = -1;
|
||||||
|
|
||||||
|
static uint8 GIF_DISPOSE_NONE = 0;
|
||||||
|
static uint8 GIF_DISPOSE_LEAVE = 1;
|
||||||
|
static uint8 GIF_DISPOSE_BACKGROUND = 2;
|
||||||
|
static uint8 GIF_DISPOSE_RESTORE = 3;
|
||||||
|
|
||||||
|
static NLMISC::IStream *GIFStream = NULL;
|
||||||
|
|
||||||
|
#if GIFLIB_MAJOR < 5
|
||||||
|
static uint8 INTERLACED_OFFSET[] = { 0, 4, 2, 1 };
|
||||||
|
static uint8 INTERLACED_JUMP[] = { 8, 8, 4, 2 };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int readGIFData(GifFileType *gif, GifByteType *data, int length){
|
||||||
|
NLMISC::IStream *f = static_cast<NLMISC::IStream *>(gif->UserData);
|
||||||
|
|
||||||
|
if(!f->isReading()) return 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
f->serialBuffer((uint8*) data, length);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
nlwarning("error while reading JPEG image");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------*\
|
||||||
|
readGIF
|
||||||
|
\*-------------------------------------------------------------------*/
|
||||||
|
uint8 CBitmap::readGIF( NLMISC::IStream &f )
|
||||||
|
{
|
||||||
|
if(!f.isReading()) return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
// check gif canvas dimension
|
||||||
|
uint16 ver;
|
||||||
|
uint16 width;
|
||||||
|
uint16 height;
|
||||||
|
|
||||||
|
f.serial(ver);
|
||||||
|
f.serial(width);
|
||||||
|
f.serial(height);
|
||||||
|
|
||||||
|
// limit image size as we are using 32bit pixels
|
||||||
|
// 4000x4000x4 ~ 61MiB
|
||||||
|
if (width*height > 4000*4000)
|
||||||
|
{
|
||||||
|
nlwarning("GIF image size is too big (width=%d, height=%d)", width, height);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewind for gif decoder
|
||||||
|
f.seek(-10, IStream::current);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if GIFLIB_MAJOR >= 5
|
||||||
|
sint32 errorCode;
|
||||||
|
GifFileType *gif = DGifOpen(&f, readGIFData, &errorCode);
|
||||||
|
if (gif == NULL)
|
||||||
|
{
|
||||||
|
nlwarning("failed to open gif, error=%d", errorCode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
GifFileType *gif = DGifOpen(&f, readGIFData);
|
||||||
|
if (gif == NULL)
|
||||||
|
{
|
||||||
|
nlwarning("failed to open gif, error=%d", GifLastError());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// this will read and decode all frames
|
||||||
|
sint32 ret = DGifSlurp(gif);
|
||||||
|
if (ret != GIF_OK)
|
||||||
|
{
|
||||||
|
nlwarning("failed to read gif, error=%d", ret);
|
||||||
|
#if GIFLIB_MAJOR >= 5 && GIFLIB_MINOR >= 1
|
||||||
|
DGifCloseFile(gif, &errorCode);
|
||||||
|
#else
|
||||||
|
DGifCloseFile(gif);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize target buffer
|
||||||
|
uint32 dstChannels = 4; // RGBA
|
||||||
|
resize (gif->SWidth, gif->SHeight, RGBA);
|
||||||
|
|
||||||
|
// make transparent
|
||||||
|
_Data[0].fill(0);
|
||||||
|
|
||||||
|
// make sure background color index exists in global colormap
|
||||||
|
if (gif->SColorMap && gif->SColorMap->ColorCount < gif->SBackGroundColor)
|
||||||
|
{
|
||||||
|
gif->SBackGroundColor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge all frames one by one into single target
|
||||||
|
ColorMapObject *ColorMap;
|
||||||
|
sint32 transparency = GIF_NOT_TRANSPARENT;
|
||||||
|
uint8 r, g, b, a;
|
||||||
|
uint32 offset_x, offset_y, width, height;
|
||||||
|
|
||||||
|
// disable loop as we only interested in first frame
|
||||||
|
// for (uint32 frame = 0; frame < gif->ImageCount; frame++)
|
||||||
|
{
|
||||||
|
uint32 frame = 0;
|
||||||
|
SavedImage *curFrame = &gif->SavedImages[frame];
|
||||||
|
|
||||||
|
if (curFrame->ExtensionBlockCount > 0)
|
||||||
|
{
|
||||||
|
for(uint e=0; e<curFrame->ExtensionBlockCount; e++){
|
||||||
|
ExtensionBlock *ext = &curFrame->ExtensionBlocks[e];
|
||||||
|
|
||||||
|
if (ext->Function == GRAPHICS_EXT_FUNC_CODE) {
|
||||||
|
uint8 flag = ext->Bytes[0];
|
||||||
|
//delay = (ext.Bytes[1] << 8) | ext.Bytes[2];
|
||||||
|
transparency = (flag & GIF_TRANSPARENT_MASK) ? ext->Bytes[3] : GIF_NOT_TRANSPARENT;
|
||||||
|
//dispose = ((flag >> 2) & GIF_DISPOSE_MASK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// select color map for frame
|
||||||
|
if (curFrame->ImageDesc.ColorMap)
|
||||||
|
{
|
||||||
|
ColorMap = curFrame->ImageDesc.ColorMap;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (gif->SColorMap)
|
||||||
|
{
|
||||||
|
ColorMap = gif->SColorMap;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nlwarning("GIF has no global or local color map");
|
||||||
|
ColorMap = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy frame to canvas
|
||||||
|
offset_x = curFrame->ImageDesc.Left;
|
||||||
|
offset_y = curFrame->ImageDesc.Top;
|
||||||
|
width = curFrame->ImageDesc.Width;
|
||||||
|
height = curFrame->ImageDesc.Height;
|
||||||
|
|
||||||
|
#if GIFLIB_MAJOR < 5
|
||||||
|
// giflib 4 does not handle interlaced images, so we must do it
|
||||||
|
if (curFrame->ImageDesc.Interlace)
|
||||||
|
{
|
||||||
|
uint32 srcOffset = 0;
|
||||||
|
for (uint8 pass = 0; pass < 4; pass++)
|
||||||
|
{
|
||||||
|
uint32 nextLine = INTERLACED_OFFSET[pass];
|
||||||
|
|
||||||
|
// y is destination row
|
||||||
|
for (uint32 y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
if (y != nextLine)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint32 dstOffset = (y + offset_y)*gif->SWidth*dstChannels + offset_x*dstChannels;
|
||||||
|
nextLine += INTERLACED_JUMP[pass];
|
||||||
|
|
||||||
|
for (uint32 x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
srcOffset++;
|
||||||
|
dstOffset+= dstChannels;
|
||||||
|
|
||||||
|
uint32 index = curFrame->RasterBits[srcOffset];
|
||||||
|
if (index != transparency)
|
||||||
|
{
|
||||||
|
// make sure color index is not outside colormap
|
||||||
|
if (ColorMap)
|
||||||
|
{
|
||||||
|
if (index > ColorMap->ColorCount)
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
r = ColorMap->Colors[index].Red;
|
||||||
|
g = ColorMap->Colors[index].Green;
|
||||||
|
b = ColorMap->Colors[index].Blue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// broken gif, no colormap
|
||||||
|
r = g = b = 0;
|
||||||
|
}
|
||||||
|
a = 255;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// transparent
|
||||||
|
r = g = b = a = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Data[0][dstOffset] = r;
|
||||||
|
_Data[0][dstOffset+1] = g;
|
||||||
|
_Data[0][dstOffset+2] = b;
|
||||||
|
_Data[0][dstOffset+3] = a;
|
||||||
|
} // x loop
|
||||||
|
} // y loop
|
||||||
|
} // pass loop
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
for (uint32 y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
uint32 srcOffset = y*width;
|
||||||
|
uint32 dstOffset = (y + offset_y)*gif->SWidth*dstChannels + offset_x*dstChannels;
|
||||||
|
for (uint32 x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
srcOffset++;
|
||||||
|
dstOffset+= dstChannels;
|
||||||
|
|
||||||
|
uint32 index = curFrame->RasterBits[srcOffset];
|
||||||
|
if (index != transparency)
|
||||||
|
{
|
||||||
|
// make sure color index is not outside colormap
|
||||||
|
if (ColorMap)
|
||||||
|
{
|
||||||
|
if (index > ColorMap->ColorCount)
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
r = ColorMap->Colors[index].Red;
|
||||||
|
g = ColorMap->Colors[index].Green;
|
||||||
|
b = ColorMap->Colors[index].Blue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// broken gif, no colormap
|
||||||
|
r = g = b = 0;
|
||||||
|
}
|
||||||
|
a = 255;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// transparent
|
||||||
|
r = g = b = a = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Data[0][dstOffset] = r;
|
||||||
|
_Data[0][dstOffset+1] = g;
|
||||||
|
_Data[0][dstOffset+2] = b;
|
||||||
|
_Data[0][dstOffset+3] = a;
|
||||||
|
} // x loop
|
||||||
|
} // y loop
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up after the read, and free any memory allocated
|
||||||
|
#if GIFLIB_MAJOR >= 5 && GIFLIB_MINOR >= 1
|
||||||
|
DGifCloseFile(gif, &errorCode);
|
||||||
|
#else
|
||||||
|
DGifCloseFile(gif);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//return the size of a pixel as 32bits
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
uint8 CBitmap::readGIF( NLMISC::IStream &/* f */)
|
||||||
|
{
|
||||||
|
nlwarning ("You must compile NLMISC with USE_GIF if you want gif support");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}//namespace
|
Loading…
Reference in a new issue