code_app/modules/imgfmt/HGGif.cpp

1020 lines
29 KiB
C++

#include "HGGif.h"
#include "../base/HGInc.h"
#include "../base/HGInfo.h"
#include "gif_lib.h"
struct HGGifReaderImpl
{
HGGifReaderImpl()
{
m_gifFile = NULL;
m_curIndex = 0xFFFFFFFF;
m_curInterval = 0xFFFFFFFF;
m_screenBuffer = NULL;
m_imageBuffer = NULL;
}
~HGGifReaderImpl()
{
if (NULL != m_imageBuffer)
{
free(m_imageBuffer);
m_imageBuffer = NULL;
}
if (NULL != m_screenBuffer)
{
free(m_screenBuffer);
m_screenBuffer = NULL;
}
if (NULL != m_gifFile)
{
int err;
DGifCloseFile(m_gifFile, &err);
m_gifFile = NULL;
}
}
GifFileType* m_gifFile;
HGUInt m_curIndex;
HGUInt m_curInterval;
uint8_t* m_screenBuffer;
uint8_t* m_imageBuffer;
};
struct HGGifWriterImpl
{
HGGifWriterImpl()
{
m_initWidth = 0;
m_initHeight = 0;
m_gifFile = NULL;
m_curIndex = 0;
m_redBuffer = NULL;
m_greenBuffer = NULL;
m_blueBuffer = NULL;
}
~HGGifWriterImpl()
{
if (NULL != m_blueBuffer)
{
free(m_blueBuffer);
m_blueBuffer = NULL;
}
if (NULL != m_greenBuffer)
{
free(m_greenBuffer);
m_greenBuffer = NULL;
}
if (NULL != m_redBuffer)
{
free(m_redBuffer);
m_redBuffer = NULL;
}
if (NULL != m_gifFile)
{
int savedCount = m_gifFile->ImageCount;
SavedImage* savedImages = m_gifFile->SavedImages;
//关闭GIF并释放相关存储。
EGifSpew(m_gifFile);
for (SavedImage *sp = savedImages; sp < savedImages + savedCount; sp++)
{
if (sp->ImageDesc.ColorMap != NULL)
{
GifFreeMapObject(sp->ImageDesc.ColorMap);
sp->ImageDesc.ColorMap = NULL;
}
if (sp->RasterBits != NULL)
{
free(sp->RasterBits);
sp->RasterBits = NULL;
}
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
}
free(savedImages);
savedImages = NULL;
}
}
HGUInt m_initWidth;
HGUInt m_initHeight;
GifFileType* m_gifFile;
HGUInt m_curIndex;
uint8_t* m_redBuffer;
uint8_t* m_greenBuffer;
uint8_t* m_blueBuffer;
};
#define ABS(x) ((x) > 0 ? (x) : (-(x)))
#define COLOR_ARRAY_SIZE 32768
#define BITS_PER_PRIM_COLOR 5
#define MAX_PRIM_COLOR 0x1f
static int SortRGBAxis;
struct QuantizedColorType
{
GifByteType RGB[3];
GifByteType NewColorIndex;
long Count;
struct QuantizedColorType* Pnext;
};
struct NewColorMapType
{
GifByteType RGBMin[3], RGBWidth[3];
unsigned int NumEntries; /* # of QuantizedColorType in linked list below */
unsigned long Count; /* Total number of pixels in all the entries */
QuantizedColorType* QuantizedColors;
};
static int SortCmpRtn(const void* Entry1,
const void* Entry2) {
QuantizedColorType* entry1 = (*((QuantizedColorType**)Entry1));
QuantizedColorType* entry2 = (*((QuantizedColorType**)Entry2));
/* sort on all axes of the color space! */
int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256
+ entry1->RGB[(SortRGBAxis + 1) % 3] * 256
+ entry1->RGB[(SortRGBAxis + 2) % 3];
int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256
+ entry2->RGB[(SortRGBAxis + 1) % 3] * 256
+ entry2->RGB[(SortRGBAxis + 2) % 3];
return hash1 - hash2;
}
static int SubdivColorMap(NewColorMapType* NewColorSubdiv,
unsigned int ColorMapSize,
unsigned int* NewColorMapSize) {
unsigned int i, j, Index = 0;
QuantizedColorType* QuantizedColor, ** SortArray;
while (ColorMapSize > *NewColorMapSize) {
/* Find candidate for subdivision: */
long Sum, Count;
int MaxSize = -1;
unsigned int NumEntries, MinColor, MaxColor;
for (i = 0; i < *NewColorMapSize; i++) {
for (j = 0; j < 3; j++) {
if ((((int)NewColorSubdiv[i].RGBWidth[j]) > MaxSize) &&
(NewColorSubdiv[i].NumEntries > 1)) {
MaxSize = NewColorSubdiv[i].RGBWidth[j];
Index = i;
SortRGBAxis = j;
}
}
}
if (MaxSize == -1)
return GIF_OK;
/* Split the entry Index into two along the axis SortRGBAxis: */
/* Sort all elements in that entry along the given axis and split at
* the median. */
SortArray = (QuantizedColorType**)malloc(
sizeof(QuantizedColorType*) *
NewColorSubdiv[Index].NumEntries);
if (SortArray == NULL)
return GIF_ERROR;
for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL;
j++, QuantizedColor = QuantizedColor->Pnext)
SortArray[j] = QuantizedColor;
/*
* Because qsort isn't stable, this can produce differing
* results for the order of tuples depending on platform
* details of how qsort() is implemented.
*
* We mitigate this problem by sorting on all three axes rather
* than only the one specied by SortRGBAxis; that way the instability
* can only become an issue if there are multiple color indices
* referring to identical RGB tuples. Older versions of this
* sorted on only the one axis.
*/
qsort(SortArray, NewColorSubdiv[Index].NumEntries,
sizeof(QuantizedColorType*), SortCmpRtn);
/* Relink the sorted list into one: */
for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++)
SortArray[j]->Pnext = SortArray[j + 1];
SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL;
NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0];
free((char*)SortArray);
/* Now simply add the Counts until we have half of the Count: */
Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
NumEntries = 1;
Count = QuantizedColor->Count;
while (QuantizedColor->Pnext != NULL &&
(Sum -= QuantizedColor->Pnext->Count) >= 0 &&
QuantizedColor->Pnext->Pnext != NULL) {
QuantizedColor = QuantizedColor->Pnext;
NumEntries++;
Count += QuantizedColor->Count;
}
/* Save the values of the last color of the first half, and first
* of the second half so we can update the Bounding Boxes later.
* Also as the colors are quantized and the BBoxes are full 0..255,
* they need to be rescaled.
*/
MaxColor = QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */
/* coverity[var_deref_op] */
MinColor = QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */
MaxColor <<= (8 - BITS_PER_PRIM_COLOR);
MinColor <<= (8 - BITS_PER_PRIM_COLOR);
/* Partition right here: */
NewColorSubdiv[*NewColorMapSize].QuantizedColors =
QuantizedColor->Pnext;
QuantizedColor->Pnext = NULL;
NewColorSubdiv[*NewColorMapSize].Count = Count;
NewColorSubdiv[Index].Count -= Count;
NewColorSubdiv[*NewColorMapSize].NumEntries =
NewColorSubdiv[Index].NumEntries - NumEntries;
NewColorSubdiv[Index].NumEntries = NumEntries;
for (j = 0; j < 3; j++) {
NewColorSubdiv[*NewColorMapSize].RGBMin[j] =
NewColorSubdiv[Index].RGBMin[j];
NewColorSubdiv[*NewColorMapSize].RGBWidth[j] =
NewColorSubdiv[Index].RGBWidth[j];
}
NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor;
NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
(*NewColorMapSize)++;
}
return GIF_OK;
}
static int GifQuantizeBuffer(unsigned int Width,
unsigned int Height,
int* ColorMapSize,
GifByteType* RedInput,
GifByteType* GreenInput,
GifByteType* BlueInput,
GifByteType* OutputBuffer,
GifColorType* OutputColorMap) {
unsigned int Index, NumOfEntries;
int i, j, MaxRGBError[3];
unsigned int NewColorMapSize;
long Red, Green, Blue;
NewColorMapType NewColorSubdiv[256];
QuantizedColorType* ColorArrayEntries, * QuantizedColor;
ColorArrayEntries = (QuantizedColorType*)malloc(
sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
if (ColorArrayEntries == NULL) {
return GIF_ERROR;
}
for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR);
ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) &
MAX_PRIM_COLOR;
ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
ColorArrayEntries[i].Count = 0;
}
/* Sample the colors and their distribution: */
for (i = 0; i < (int)(Width * Height); i++) {
Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
(2 * BITS_PER_PRIM_COLOR)) +
((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
BITS_PER_PRIM_COLOR) +
(BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
ColorArrayEntries[Index].Count++;
}
/* Put all the colors in the first entry of the color map, and call the
* recursive subdivision process. */
for (i = 0; i < 256; i++) {
NewColorSubdiv[i].QuantizedColors = NULL;
NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0;
for (j = 0; j < 3; j++) {
NewColorSubdiv[i].RGBMin[j] = 0;
NewColorSubdiv[i].RGBWidth[j] = 255;
}
}
/* Find the non empty entries in the color table and chain them: */
for (i = 0; i < COLOR_ARRAY_SIZE; i++)
if (ColorArrayEntries[i].Count > 0)
break;
QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i];
NumOfEntries = 1;
while (++i < COLOR_ARRAY_SIZE)
if (ColorArrayEntries[i].Count > 0) {
QuantizedColor->Pnext = &ColorArrayEntries[i];
QuantizedColor = &ColorArrayEntries[i];
NumOfEntries++;
}
QuantizedColor->Pnext = NULL;
NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */
NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
NewColorMapSize = 1;
if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
GIF_OK) {
free((char*)ColorArrayEntries);
return GIF_ERROR;
}
if ((int)NewColorMapSize < *ColorMapSize) {
/* And clear rest of color map: */
for (i = NewColorMapSize; i < *ColorMapSize; i++)
OutputColorMap[i].Red = OutputColorMap[i].Green =
OutputColorMap[i].Blue = 0;
}
/* Average the colors in each entry to be the color to be used in the
* output color map, and plug it into the output color map itself. */
for (i = 0; i < (int)NewColorMapSize; i++) {
if ((j = NewColorSubdiv[i].NumEntries) > 0) {
QuantizedColor = NewColorSubdiv[i].QuantizedColors;
Red = Green = Blue = 0;
while (QuantizedColor) {
QuantizedColor->NewColorIndex = i;
Red += QuantizedColor->RGB[0];
Green += QuantizedColor->RGB[1];
Blue += QuantizedColor->RGB[2];
QuantizedColor = QuantizedColor->Pnext;
}
OutputColorMap[i].Red = (GifByteType)((Red << (8 - BITS_PER_PRIM_COLOR)) / j);
OutputColorMap[i].Green = (GifByteType)((Green << (8 - BITS_PER_PRIM_COLOR)) / j);
OutputColorMap[i].Blue = (GifByteType)((Blue << (8 - BITS_PER_PRIM_COLOR)) / j);
}
}
/* Finally scan the input buffer again and put the mapped index in the
* output buffer. */
MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0;
for (i = 0; i < (int)(Width * Height); i++) {
Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
(2 * BITS_PER_PRIM_COLOR)) +
((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
BITS_PER_PRIM_COLOR) +
(BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
Index = ColorArrayEntries[Index].NewColorIndex;
OutputBuffer[i] = Index;
if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i]))
MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]);
if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i]))
MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]);
if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i]))
MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]);
}
#ifdef DEBUG
fprintf(stderr,
"Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n",
MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]);
#endif /* DEBUG */
free((char*)ColorArrayEntries);
*ColorMapSize = NewColorMapSize;
return GIF_OK;
}
HGResult HGAPI HGImgFmt_CheckGifFile(const HGChar* fileName, HGBool* isGif)
{
if (NULL == fileName || NULL == isGif)
{
return HGBASE_ERR_INVALIDARG;
}
*isGif = HGFALSE;
int err;
GifFileType* gifFile = DGifOpenFileName(fileName, &err);
if (NULL != gifFile)
{
*isGif = HGTRUE;
DGifCloseFile(gifFile, &err);
}
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_OpenGifReader(const HGChar* fileName, HGGifLoadInfo* info, HGGifReader* reader)
{
if (NULL == fileName || NULL == reader)
{
return HGBASE_ERR_INVALIDARG;
}
int err;
GifFileType* gifFile = DGifOpenFileName(fileName, &err);
if (NULL == gifFile)
{
return HGBASE_ERR_ACCESSDENIED;
}
DGifSlurp(gifFile);
if (gifFile->ImageCount <= 0)
{
DGifCloseFile(gifFile, &err);
return HGBASE_ERR_FAIL;
}
uint8_t* screenBuffer = (uint8_t*)malloc(gifFile->SWidth * gifFile->SHeight);
if (NULL == screenBuffer)
{
DGifCloseFile(gifFile, &err);
return HGBASE_ERR_OUTOFMEMORY;
}
uint8_t* imageBuffer = (uint8_t *)malloc(gifFile->SWidth * gifFile->SHeight * 4);
if (NULL == imageBuffer)
{
free(screenBuffer);
screenBuffer = NULL;
DGifCloseFile(gifFile, &err);
return HGBASE_ERR_OUTOFMEMORY;
}
// 设置底色
memset(screenBuffer, gifFile->SBackGroundColor, gifFile->SWidth * gifFile->SHeight);
if (NULL != info)
{
info->width = gifFile->SWidth;
info->height = gifFile->SHeight;
info->colorResolution = gifFile->SColorResolution;
info->imageCount = gifFile->ImageCount;
}
HGGifReaderImpl* gifReaderImpl = new HGGifReaderImpl;
gifReaderImpl->m_gifFile = gifFile;
gifReaderImpl->m_screenBuffer = screenBuffer;
gifReaderImpl->m_imageBuffer = imageBuffer;
*reader = (HGGifReader)gifReaderImpl;
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_CloseGifReader(HGGifReader reader)
{
if (NULL == reader)
{
return HGBASE_ERR_INVALIDARG;
}
HGGifReaderImpl* gifReaderImpl = (HGGifReaderImpl*)reader;
delete gifReaderImpl;
return HGBASE_ERR_OK;
}
static HGResult LoadGifImage(HGGifReaderImpl* gifReaderImpl)
{
assert(NULL != gifReaderImpl);
int transColor = -1;
unsigned int loopCount = 0;
SavedImage* image = &gifReaderImpl->m_gifFile->SavedImages[gifReaderImpl->m_curIndex];
//获取间隔、TransparentColor等信息
for (ExtensionBlock* ep = image->ExtensionBlocks; ep < image->ExtensionBlocks + image->ExtensionBlockCount; ep++)
{
bool last = (ep - image->ExtensionBlocks == (image->ExtensionBlockCount - 1));
if (ep->Function == GRAPHICS_EXT_FUNC_CODE)
{
GraphicsControlBlock gcb;
if (DGifExtensionToGCB(ep->ByteCount, ep->Bytes, &gcb) == GIF_ERROR)
{
return HGBASE_ERR_FAIL;
}
transColor = gcb.TransparentColor;
//DelayTime的单位为10 ms
gifReaderImpl->m_curInterval = gcb.DelayTime * 10;
}
else if (!last
&& ep->Function == APPLICATION_EXT_FUNC_CODE
&& ep->ByteCount >= 11
&& (ep + 1)->ByteCount >= 3
&& memcmp(ep->Bytes, "NETSCAPE2.0", 11) == 0)
{
unsigned char* params = (++ep)->Bytes;
loopCount = params[1] | (params[2] << 8);
}
else
{
//其他信息暂不处理
while (!last && ep[1].Function == CONTINUE_EXT_FUNC_CODE)
{
++ep;
last = (ep - image->ExtensionBlocks == (image->ExtensionBlockCount - 1));
}
}
}
if (image->ImageDesc.Interlace)
{
//
}
ColorMapObject* colorMap = (image->ImageDesc.ColorMap ? image->ImageDesc.ColorMap : gifReaderImpl->m_gifFile->SColorMap);
if (colorMap == NULL)
{
return HGBASE_ERR_FAIL;
}
for (int h = image->ImageDesc.Top; h < image->ImageDesc.Top + image->ImageDesc.Height; ++h)
{
uint8_t* p1 = gifReaderImpl->m_screenBuffer + h * gifReaderImpl->m_gifFile->SWidth + image->ImageDesc.Left;
uint8_t* p2 = image->RasterBits + (h - image->ImageDesc.Top) * image->ImageDesc.Width;
memcpy(p1, p2, image->ImageDesc.Width);
}
for (int h = 0; h < gifReaderImpl->m_gifFile->SHeight; ++h)
{
uint8_t* p1 = gifReaderImpl->m_imageBuffer + h * gifReaderImpl->m_gifFile->SWidth * 4;
uint8_t* p2 = gifReaderImpl->m_screenBuffer + h * gifReaderImpl->m_gifFile->SWidth;
for (int w = 0; w < gifReaderImpl->m_gifFile->SWidth; ++w)
{
if (transColor != -1 && transColor == p2[w])
continue;
GifColorType* colorMapEntry = &colorMap->Colors[p2[w]];
p1[w * 4] = colorMapEntry->Red;
p1[w * 4 + 1] = colorMapEntry->Green;
p1[w * 4 + 2] = colorMapEntry->Blue;
p1[w * 4 + 3] = 0xFF;
}
}
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_RetrieveImageFromGifReader(HGGifReader reader, HGUInt* index, HGUInt* interval,
HGUInt imgType, HGUInt imgOrigin, HGImage* image)
{
if (NULL == reader || NULL == image)
{
return HGBASE_ERR_INVALIDARG;
}
if (0 != imgType && HGBASE_IMGTYPE_BINARY != imgType && HGBASE_IMGTYPE_GRAY != imgType
&& HGBASE_IMGTYPE_BGR != imgType && HGBASE_IMGTYPE_RGB != imgType
&& HGBASE_IMGTYPE_BGRA != imgType && HGBASE_IMGTYPE_RGBA != imgType)
{
return HGBASE_ERR_INVALIDARG;
}
if (HGBASE_IMGORIGIN_TOP != imgOrigin && HGBASE_IMGORIGIN_BOTTOM != imgOrigin)
{
return HGBASE_ERR_INVALIDARG;
}
HGGifReaderImpl* gifReaderImpl = (HGGifReaderImpl*)reader;
if (gifReaderImpl->m_curIndex + 1 >= (HGUInt)gifReaderImpl->m_gifFile->ImageCount)
{
gifReaderImpl->m_curIndex = 0xFFFFFFFF;
// 设置底色
memset(gifReaderImpl->m_screenBuffer, gifReaderImpl->m_gifFile->SBackGroundColor,
gifReaderImpl->m_gifFile->SWidth * gifReaderImpl->m_gifFile->SHeight);
}
++gifReaderImpl->m_curIndex;
HGResult ret = LoadGifImage(gifReaderImpl);
if (HGBASE_ERR_OK != ret)
{
--gifReaderImpl->m_curIndex;
return ret;
}
if (0 == imgType)
{
imgType = HGBASE_IMGTYPE_RGB;
}
HGImageInfo gifImageInfo;
gifImageInfo.width = gifReaderImpl->m_gifFile->SWidth;
gifImageInfo.height = gifReaderImpl->m_gifFile->SHeight;
gifImageInfo.type = HGBASE_IMGTYPE_RGBA;
gifImageInfo.widthStep = gifReaderImpl->m_gifFile->SWidth * 4;
gifImageInfo.origin = HGBASE_IMGORIGIN_TOP;
HGImage image2 = NULL;
ret = HGBase_CreateImageWithData((HGByte*)gifReaderImpl->m_imageBuffer, &gifImageInfo, &image2);
if (HGBASE_ERR_OK != ret)
{
return ret;
}
ret = HGBase_CloneImage(image2, imgType, imgOrigin, image);
HGBase_DestroyImage(image2);
if (HGBASE_ERR_OK != ret)
{
return ret;
}
if (NULL != index)
*index = gifReaderImpl->m_curIndex;
if (NULL != interval)
*interval = gifReaderImpl->m_curInterval;
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_LoadImageFromGifReader(HGGifReader reader, HGUInt index, HGUInt* interval,
HGUInt imgType, HGUInt imgOrigin, HGImage* image)
{
if (NULL == reader || NULL == image)
{
return HGBASE_ERR_INVALIDARG;
}
if (0 != imgType && HGBASE_IMGTYPE_BINARY != imgType && HGBASE_IMGTYPE_GRAY != imgType
&& HGBASE_IMGTYPE_BGR != imgType && HGBASE_IMGTYPE_RGB != imgType
&& HGBASE_IMGTYPE_BGRA != imgType && HGBASE_IMGTYPE_RGBA != imgType)
{
return HGBASE_ERR_INVALIDARG;
}
if (0 != imgOrigin && HGBASE_IMGORIGIN_TOP != imgOrigin && HGBASE_IMGORIGIN_BOTTOM != imgOrigin)
{
return HGBASE_ERR_INVALIDARG;
}
HGGifReaderImpl* gifReaderImpl = (HGGifReaderImpl*)reader;
if (index >= (HGUInt)gifReaderImpl->m_gifFile->ImageCount)
{
return HGBASE_ERR_INVALIDARG;
}
if (0 == imgType)
{
imgType = HGBASE_IMGTYPE_RGB;
}
if (imgOrigin == 0)
{
imgOrigin = HGBASE_IMGORIGIN_TOP;
}
if (index < gifReaderImpl->m_curIndex)
{
gifReaderImpl->m_curIndex = 0xFFFFFFFF;
// 设置底色
memset(gifReaderImpl->m_screenBuffer, gifReaderImpl->m_gifFile->SBackGroundColor,
gifReaderImpl->m_gifFile->SWidth * gifReaderImpl->m_gifFile->SHeight);
}
while (index != gifReaderImpl->m_curIndex)
{
++gifReaderImpl->m_curIndex;
HGResult ret = LoadGifImage(gifReaderImpl);
if (HGBASE_ERR_OK != ret)
{
--gifReaderImpl->m_curIndex;
return ret;
}
}
HGImageInfo gifImageInfo;
gifImageInfo.width = gifReaderImpl->m_gifFile->SWidth;
gifImageInfo.height = gifReaderImpl->m_gifFile->SHeight;
gifImageInfo.type = HGBASE_IMGTYPE_RGBA;
gifImageInfo.widthStep = gifReaderImpl->m_gifFile->SWidth * 4;
gifImageInfo.origin = HGBASE_IMGORIGIN_TOP;
HGImage image2 = NULL;
HGResult ret = HGBase_CreateImageWithData((HGByte*)gifReaderImpl->m_imageBuffer, &gifImageInfo, &image2);
if (HGBASE_ERR_OK != ret)
{
return ret;
}
ret = HGBase_CloneImage(image2, imgType, imgOrigin, image);
HGBase_DestroyImage(image2);
if (HGBASE_ERR_OK != ret)
{
return ret;
}
if (NULL != interval)
*interval = gifReaderImpl->m_curInterval;
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_LoadGifImage(const HGChar* fileName, HGGifLoadInfo* info, HGUInt* interval,
HGUInt imgType, HGUInt imgOrigin, HGImage* image)
{
if (NULL == image)
{
if (NULL != interval || 0 != imgType || 0 != imgOrigin)
{
return HGBASE_ERR_INVALIDARG;
}
}
HGGifReader reader = NULL;
HGResult ret = HGImgFmt_OpenGifReader(fileName, info, &reader);
if (HGBASE_ERR_OK != ret)
{
return ret;
}
if (NULL != image)
{
HGImgFmt_RetrieveImageFromGifReader(reader, NULL, interval, imgType, imgOrigin, image);
}
HGImgFmt_CloseGifReader(reader);
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_OpenGifWriter(const HGChar* fileName, const HGGifSaveInfo* info, HGGifWriter* writer)
{
if (NULL == fileName || NULL == writer)
{
return HGBASE_ERR_INVALIDARG;
}
int err;
GifFileType* gifFile = EGifOpenFileName(fileName, false, &err);
if (NULL == gifFile)
{
HGBase_WriteInfo(HGBASE_INFOTYPE_ERROR, "HGImgFmt_OpenGifWriter: Create File Faild, %s", fileName);
return HGBASE_ERR_ACCESSDENIED;
}
gifFile->SWidth = 0;
gifFile->SHeight = 0;
gifFile->SColorResolution = 8;
gifFile->SBackGroundColor = 0;
gifFile->SColorMap = NULL;
gifFile->SavedImages = NULL;
gifFile->ImageCount = 0;
gifFile->ExtensionBlockCount = 0;
gifFile->ExtensionBlocks = NULL;
HGGifWriterImpl* gifWriterImpl = new HGGifWriterImpl;
gifWriterImpl->m_initWidth = (NULL != info) ? info->width : 0;
gifWriterImpl->m_initHeight = (NULL != info) ? info->height : 0;
gifWriterImpl->m_gifFile = gifFile;
gifWriterImpl->m_curIndex = 0;
gifWriterImpl->m_redBuffer = NULL;
gifWriterImpl->m_greenBuffer = NULL;
gifWriterImpl->m_blueBuffer = NULL;
*writer = (HGGifWriter)gifWriterImpl;
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_CloseGifWriter(HGGifWriter writer)
{
if (NULL == writer)
{
return HGBASE_ERR_INVALIDARG;
}
HGGifWriterImpl* gifWriterImpl = (HGGifWriterImpl*)writer;
delete gifWriterImpl;
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_SaveImageToGifWriter(HGGifWriter writer, HGUInt interval, HGColor bkColor, HGImage image)
{
if (NULL == writer || NULL == image)
{
return HGBASE_ERR_INVALIDARG;
}
HGImageInfo imgInfo;
HGBase_GetImageInfo(image, &imgInfo);
if (HGBASE_IMGTYPE_BINARY == imgInfo.type)
{
HGImageRoi roi;
HGBase_GetImageROI(image, &roi);
HGBase_ResetImageROI(image);
HGImage imageTmp = NULL;
HGResult ret = HGBase_CloneImage(image, HGBASE_IMGTYPE_GRAY, 0, &imageTmp);
if (HGBASE_ERR_OK == ret)
{
ret = HGImgFmt_SaveImageToGifWriter(writer, interval, bkColor, imageTmp);
HGBase_DestroyImage(imageTmp);
}
HGBase_SetImageROI(image, &roi);
return ret;
}
HGGifWriterImpl* gifWriterImpl = (HGGifWriterImpl*)writer;
if (0 == gifWriterImpl->m_curIndex)
{
gifWriterImpl->m_gifFile->SWidth = (0 == gifWriterImpl->m_initWidth)
? imgInfo.width : gifWriterImpl->m_initWidth;
gifWriterImpl->m_gifFile->SHeight = (0 == gifWriterImpl->m_initHeight)
? imgInfo.height : gifWriterImpl->m_initHeight;
free(gifWriterImpl->m_redBuffer);
gifWriterImpl->m_redBuffer = (uint8_t*)malloc(gifWriterImpl->m_gifFile->SWidth * gifWriterImpl->m_gifFile->SHeight);
if (NULL == gifWriterImpl->m_redBuffer)
{
return HGBASE_ERR_OUTOFMEMORY;
}
free(gifWriterImpl->m_greenBuffer);
gifWriterImpl->m_greenBuffer = (uint8_t*)malloc(gifWriterImpl->m_gifFile->SWidth * gifWriterImpl->m_gifFile->SHeight);
if (NULL == gifWriterImpl->m_greenBuffer)
{
free(gifWriterImpl->m_redBuffer);
gifWriterImpl->m_redBuffer = NULL;
return HGBASE_ERR_OUTOFMEMORY;
}
free(gifWriterImpl->m_blueBuffer);
gifWriterImpl->m_blueBuffer = (uint8_t*)malloc(gifWriterImpl->m_gifFile->SWidth * gifWriterImpl->m_gifFile->SHeight);
if (NULL == gifWriterImpl->m_blueBuffer)
{
free(gifWriterImpl->m_greenBuffer);
gifWriterImpl->m_greenBuffer = NULL;
free(gifWriterImpl->m_redBuffer);
gifWriterImpl->m_redBuffer = NULL;
return HGBASE_ERR_OUTOFMEMORY;
}
}
GifFileType* gifFile = gifWriterImpl->m_gifFile;
GifWord gifWidth = gifWriterImpl->m_gifFile->SWidth;
GifWord gifHeight = gifWriterImpl->m_gifFile->SHeight;
uint8_t* redBuffer = gifWriterImpl->m_redBuffer;
uint8_t* greenBuffer = gifWriterImpl->m_greenBuffer;
uint8_t* blueBuffer = gifWriterImpl->m_blueBuffer;
memset(redBuffer, HG_GETCOLOR_R(bkColor), gifWidth * gifHeight);
memset(greenBuffer, HG_GETCOLOR_G(bkColor), gifWidth * gifHeight);
memset(blueBuffer, HG_GETCOLOR_B(bkColor), gifWidth * gifHeight);
HGByte* imgData = NULL;
HGBase_GetImageData(image, &imgData);
int channels = 1;
if (HGBASE_IMGTYPE_RGB == imgInfo.type || HGBASE_IMGTYPE_BGR == imgInfo.type)
channels = 3;
else if (HGBASE_IMGTYPE_RGBA == imgInfo.type || HGBASE_IMGTYPE_BGRA == imgInfo.type)
channels = 4;
int copyWidth = HGMIN((GifWord)imgInfo.width, gifWidth);
int copyHeight = HGMIN((GifWord)imgInfo.height, gifHeight);
int srcLeft = ((GifWord)imgInfo.width <= gifWidth) ? 0 : ((GifWord)imgInfo.width - gifWidth) / 2;
int srcTop = ((GifWord)imgInfo.height <= gifHeight) ? 0 : ((GifWord)imgInfo.height - gifHeight) / 2;
int dstLeft = ((GifWord)imgInfo.width <= gifWidth) ? (gifWidth - (GifWord)imgInfo.width) / 2 : 0;
int dstTop = ((GifWord)imgInfo.height <= gifHeight) ? (gifHeight - (GifWord)imgInfo.height) / 2 : 0;
if (HGBASE_IMGTYPE_RGB == imgInfo.type || HGBASE_IMGTYPE_RGBA == imgInfo.type)
{
for (int row = 0; row < copyHeight; row++)
{
uint8_t* pr = redBuffer + (row + dstTop) * gifWidth;
uint8_t* pg = greenBuffer + (row + dstTop) * gifWidth;
uint8_t* pb = blueBuffer + (row + dstTop) * gifWidth;
uint8_t* pData = imgData + (row + srcTop) * imgInfo.widthStep;
if (HGBASE_IMGORIGIN_BOTTOM == imgInfo.origin)
pData = imgData + (imgInfo.height - row - srcTop - 1) * imgInfo.widthStep;
for (int col = 0; col < copyWidth; col++)
{
pr[col + dstLeft] = pData[(col + srcLeft) * channels];
pg[col + dstLeft] = pData[(col + srcLeft) * channels + 1];
pb[col + dstLeft] = pData[(col + srcLeft) * channels + 2];
}
}
}
else if (HGBASE_IMGTYPE_BGR == imgInfo.type || HGBASE_IMGTYPE_BGRA == imgInfo.type)
{
for (int row = 0; row < copyHeight; row++)
{
uint8_t* pr = redBuffer + (row + dstTop) * gifWidth;
uint8_t* pg = greenBuffer + (row + dstTop) * gifWidth;
uint8_t* pb = blueBuffer + (row + dstTop) * gifWidth;
uint8_t* pData = imgData + (row + srcTop) * imgInfo.widthStep;
if (HGBASE_IMGORIGIN_BOTTOM == imgInfo.origin)
pData = imgData + (imgInfo.height - row - srcTop - 1) * imgInfo.widthStep;
for (int col = 0; col < copyWidth; col++)
{
pb[col + dstLeft] = pData[(col + srcLeft) * channels];
pg[col + dstLeft] = pData[(col + srcLeft) * channels + 1];
pr[col + dstLeft] = pData[(col + srcLeft) * channels + 2];
}
}
}
else
{
assert(HGBASE_IMGTYPE_GRAY == imgInfo.type);
for (int row = 0; row < copyHeight; row++)
{
uint8_t* pr = redBuffer + (row + dstTop) * gifWidth;
uint8_t* pg = greenBuffer + (row + dstTop) * gifWidth;
uint8_t* pb = blueBuffer + (row + dstTop) * gifWidth;
uint8_t* pData = imgData + (row + srcTop) * imgInfo.widthStep;
if (HGBASE_IMGORIGIN_BOTTOM == imgInfo.origin)
pData = imgData + (imgInfo.height - row - srcTop - 1) * imgInfo.widthStep;
for (int col = 0; col < copyWidth; col++)
{
pr[col + dstLeft] = pData[(col + srcLeft)];
pg[col + dstLeft] = pData[(col + srcLeft)];
pb[col + dstLeft] = pData[(col + srcLeft)];
}
}
}
int mapSize = (1 << gifFile->SColorResolution);
ColorMapObject* colorMap = GifMakeMapObject(mapSize, NULL);
if (NULL == colorMap)
{
return HGBASE_ERR_FAIL;
}
GifByteType* rasterBits = (GifPixelType*)malloc(sizeof(GifPixelType) * gifWidth * gifHeight);
if (NULL == rasterBits)
{
GifFreeMapObject(colorMap);
return HGBASE_ERR_FAIL;
}
if (GifQuantizeBuffer(gifWidth, gifHeight, &mapSize, redBuffer, greenBuffer, blueBuffer,
rasterBits, colorMap->Colors) == GIF_ERROR)
{
free(rasterBits);
GifFreeMapObject(colorMap);
return HGBASE_ERR_FAIL;
}
SavedImage* gifImage = GifMakeSavedImage(gifFile, NULL);
if (NULL == gifImage)
{
free(rasterBits);
GifFreeMapObject(colorMap);
return HGBASE_ERR_FAIL;
}
gifImage->ImageDesc.Left = 0;
gifImage->ImageDesc.Top = 0;
gifImage->ImageDesc.Width = gifWidth;
gifImage->ImageDesc.Height = gifHeight;
gifImage->ImageDesc.Interlace = false;
gifImage->ImageDesc.ColorMap = colorMap;
gifImage->RasterBits = rasterBits;
GraphicsControlBlock gcb;
gcb.DisposalMode = DISPOSAL_UNSPECIFIED;
gcb.DelayTime = interval / 10;
gcb.UserInputFlag = false;
gcb.TransparentColor = NO_TRANSPARENT_COLOR;
EGifGCBToSavedExtension(&gcb, gifFile, gifWriterImpl->m_curIndex);
//把循环次数写到第一帧对应的扩展块
if (0 == gifWriterImpl->m_curIndex)
{
//Create a Netscape 2.0 loop block
GifAddExtensionBlock(&gifImage->ExtensionBlockCount, &gifImage->ExtensionBlocks,
APPLICATION_EXT_FUNC_CODE, 11, (unsigned char*)"NETSCAPE2.0");
unsigned char params[3] = { 1, 0, 0 };
GifAddExtensionBlock(&gifImage->ExtensionBlockCount, &gifImage->ExtensionBlocks,
CONTINUE_EXT_FUNC_CODE, sizeof(params), params);
}
++gifWriterImpl->m_curIndex;
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_SaveGifImage(HGImage image, const HGGifSaveInfo* info,
HGUInt interval, HGColor bkColor, const HGChar* fileName)
{
HGGifWriter writer = NULL;
HGResult ret = HGImgFmt_OpenGifWriter(fileName, info, &writer);
if (HGBASE_ERR_OK != ret)
{
return ret;
}
ret = HGImgFmt_SaveImageToGifWriter(writer, interval, bkColor, image);
HGImgFmt_CloseGifWriter(writer);
return ret;
}