2022-07-08 09:36:13 +00:00
|
|
|
|
#include "HGGif.h"
|
|
|
|
|
#include "../base/HGInc.h"
|
2024-08-01 05:46:17 +00:00
|
|
|
|
#include "log/log.h"
|
2022-07-08 09:36:13 +00:00
|
|
|
|
#include "gif_lib.h"
|
|
|
|
|
|
2024-08-01 05:46:17 +00:00
|
|
|
|
extern HLOG g_hLog;
|
|
|
|
|
|
2022-07-08 09:36:13 +00:00
|
|
|
|
struct HGGifReaderImpl
|
|
|
|
|
{
|
|
|
|
|
HGGifReaderImpl()
|
|
|
|
|
{
|
|
|
|
|
m_gifFile = NULL;
|
|
|
|
|
m_curIndex = 0xFFFFFFFF;
|
2022-07-12 02:21:55 +00:00
|
|
|
|
m_curInterval = 0xFFFFFFFF;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
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;
|
2022-07-12 02:21:55 +00:00
|
|
|
|
HGUInt m_curInterval;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
uint8_t* m_screenBuffer;
|
|
|
|
|
uint8_t* m_imageBuffer;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct HGGifWriterImpl
|
|
|
|
|
{
|
|
|
|
|
HGGifWriterImpl()
|
|
|
|
|
{
|
2022-07-11 03:40:57 +00:00
|
|
|
|
m_initWidth = 0;
|
|
|
|
|
m_initHeight = 0;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
m_gifFile = NULL;
|
2022-07-12 02:21:55 +00:00
|
|
|
|
m_curIndex = 0;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
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;
|
2022-07-11 08:40:21 +00:00
|
|
|
|
|
2022-07-08 09:36:13 +00:00
|
|
|
|
//关闭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)
|
2022-07-11 08:40:21 +00:00
|
|
|
|
{
|
|
|
|
|
free(sp->RasterBits);
|
|
|
|
|
sp->RasterBits = NULL;
|
|
|
|
|
}
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
|
|
|
|
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-11 08:40:21 +00:00
|
|
|
|
free(savedImages);
|
2022-07-08 09:36:13 +00:00
|
|
|
|
savedImages = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-11 03:40:57 +00:00
|
|
|
|
HGUInt m_initWidth;
|
|
|
|
|
HGUInt m_initHeight;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2022-10-12 03:35:09 +00:00
|
|
|
|
struct QuantizedColorType
|
|
|
|
|
{
|
2022-07-08 09:36:13 +00:00
|
|
|
|
GifByteType RGB[3];
|
|
|
|
|
GifByteType NewColorIndex;
|
|
|
|
|
long Count;
|
|
|
|
|
struct QuantizedColorType* Pnext;
|
2022-10-12 03:35:09 +00:00
|
|
|
|
};
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
2022-10-12 03:35:09 +00:00
|
|
|
|
struct NewColorMapType
|
|
|
|
|
{
|
2022-07-08 09:36:13 +00:00
|
|
|
|
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;
|
2022-10-12 03:35:09 +00:00
|
|
|
|
};
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-26 02:42:06 +00:00
|
|
|
|
#if defined(HG_CMP_MSC)
|
|
|
|
|
if (0 != _access(fileName, 0))
|
|
|
|
|
#else
|
|
|
|
|
if (0 != access(fileName, 0))
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
return HGBASE_ERR_FILENOTEXIST;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 10:14:16 +00:00
|
|
|
|
int err;
|
|
|
|
|
GifFileType* gifFile = DGifOpenFileName(fileName, &err);
|
2022-11-26 02:42:06 +00:00
|
|
|
|
if (NULL == gifFile)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-11-26 02:42:06 +00:00
|
|
|
|
return HGBASE_ERR_FILEERROR;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
2022-11-26 02:42:06 +00:00
|
|
|
|
|
|
|
|
|
*isGif = HGTRUE;
|
|
|
|
|
DGifCloseFile(gifFile, &err);
|
2022-07-08 09:36:13 +00:00
|
|
|
|
return HGBASE_ERR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HGResult HGAPI HGImgFmt_OpenGifReader(const HGChar* fileName, HGGifLoadInfo* info, HGGifReader* reader)
|
|
|
|
|
{
|
|
|
|
|
if (NULL == fileName || NULL == reader)
|
|
|
|
|
{
|
|
|
|
|
return HGBASE_ERR_INVALIDARG;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-25 10:00:33 +00:00
|
|
|
|
#if defined(HG_CMP_MSC)
|
|
|
|
|
if (0 != _access(fileName, 0))
|
|
|
|
|
#else
|
|
|
|
|
if (0 != access(fileName, 0))
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
return HGBASE_ERR_FILENOTEXIST;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 09:36:13 +00:00
|
|
|
|
int err;
|
|
|
|
|
GifFileType* gifFile = DGifOpenFileName(fileName, &err);
|
|
|
|
|
if (NULL == gifFile)
|
|
|
|
|
{
|
2022-11-25 10:00:33 +00:00
|
|
|
|
return HGBASE_ERR_FILEERROR;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DGifSlurp(gifFile);
|
|
|
|
|
if (gifFile->ImageCount <= 0)
|
|
|
|
|
{
|
|
|
|
|
DGifCloseFile(gifFile, &err);
|
2022-11-25 10:00:33 +00:00
|
|
|
|
return HGBASE_ERR_FILEERROR;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
info->colorResolution = gifFile->SColorResolution;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
static HGResult LoadGifImage(HGGifReaderImpl* gifReaderImpl)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2022-11-25 10:00:33 +00:00
|
|
|
|
return HGIMGFMT_ERR_FAIL;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transColor = gcb.TransparentColor;
|
|
|
|
|
//DelayTime的单位为10 ms
|
2022-07-12 02:21:55 +00:00
|
|
|
|
gifReaderImpl->m_curInterval = gcb.DelayTime * 10;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
{
|
2022-11-25 10:00:33 +00:00
|
|
|
|
return HGIMGFMT_ERR_FAIL;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
HGResult HGAPI HGImgFmt_RetrieveImageFromGifReader(HGGifReader reader, HGUInt* index, HGUInt* interval,
|
2022-07-08 09:36:13 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-26 02:42:06 +00:00
|
|
|
|
if (0 != imgOrigin && HGBASE_IMGORIGIN_TOP != imgOrigin && HGBASE_IMGORIGIN_BOTTOM != imgOrigin)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
|
|
|
|
return HGBASE_ERR_INVALIDARG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HGGifReaderImpl* gifReaderImpl = (HGGifReaderImpl*)reader;
|
2022-07-12 02:21:55 +00:00
|
|
|
|
if (gifReaderImpl->m_curIndex + 1 >= (HGUInt)gifReaderImpl->m_gifFile->ImageCount)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-07-12 02:21:55 +00:00
|
|
|
|
gifReaderImpl->m_curIndex = 0xFFFFFFFF;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
// 设置底色
|
|
|
|
|
memset(gifReaderImpl->m_screenBuffer, gifReaderImpl->m_gifFile->SBackGroundColor,
|
|
|
|
|
gifReaderImpl->m_gifFile->SWidth * gifReaderImpl->m_gifFile->SHeight);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
++gifReaderImpl->m_curIndex;
|
|
|
|
|
|
|
|
|
|
HGResult ret = LoadGifImage(gifReaderImpl);
|
2022-07-08 09:36:13 +00:00
|
|
|
|
if (HGBASE_ERR_OK != ret)
|
|
|
|
|
{
|
2022-07-12 02:21:55 +00:00
|
|
|
|
--gifReaderImpl->m_curIndex;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (0 == imgType)
|
|
|
|
|
{
|
|
|
|
|
imgType = HGBASE_IMGTYPE_RGB;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-26 02:42:06 +00:00
|
|
|
|
if (0 == imgOrigin)
|
|
|
|
|
{
|
|
|
|
|
imgOrigin = HGBASE_IMGORIGIN_TOP;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 09:36:13 +00:00
|
|
|
|
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)
|
2022-07-12 02:21:55 +00:00
|
|
|
|
*interval = gifReaderImpl->m_curInterval;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
return HGBASE_ERR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
HGResult HGAPI HGImgFmt_LoadImageFromGifReader(HGGifReader reader, HGUInt index, HGUInt* interval,
|
|
|
|
|
HGUInt imgType, HGUInt imgOrigin, HGImage* image)
|
2022-07-11 03:40:57 +00:00
|
|
|
|
{
|
2022-07-12 02:21:55 +00:00
|
|
|
|
if (NULL == reader || NULL == image)
|
2022-07-11 03:40:57 +00:00
|
|
|
|
{
|
|
|
|
|
return HGBASE_ERR_INVALIDARG;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
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)
|
2022-07-11 03:40:57 +00:00
|
|
|
|
{
|
|
|
|
|
return HGBASE_ERR_INVALIDARG;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-28 02:53:54 +00:00
|
|
|
|
if (0 != imgOrigin && HGBASE_IMGORIGIN_TOP != imgOrigin && HGBASE_IMGORIGIN_BOTTOM != imgOrigin)
|
2022-07-11 03:40:57 +00:00
|
|
|
|
{
|
2022-07-12 02:21:55 +00:00
|
|
|
|
return HGBASE_ERR_INVALIDARG;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HGGifReaderImpl* gifReaderImpl = (HGGifReaderImpl*)reader;
|
|
|
|
|
if (index >= (HGUInt)gifReaderImpl->m_gifFile->ImageCount)
|
|
|
|
|
{
|
|
|
|
|
return HGBASE_ERR_INVALIDARG;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
if (0 == imgType)
|
2022-07-11 03:40:57 +00:00
|
|
|
|
{
|
2022-07-12 02:21:55 +00:00
|
|
|
|
imgType = HGBASE_IMGTYPE_RGB;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-28 02:53:54 +00:00
|
|
|
|
if (imgOrigin == 0)
|
|
|
|
|
{
|
|
|
|
|
imgOrigin = HGBASE_IMGORIGIN_TOP;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
if (index < gifReaderImpl->m_curIndex)
|
2022-07-11 03:40:57 +00:00
|
|
|
|
{
|
2022-07-12 02:21:55 +00:00
|
|
|
|
gifReaderImpl->m_curIndex = 0xFFFFFFFF;
|
|
|
|
|
// 设置底色
|
|
|
|
|
memset(gifReaderImpl->m_screenBuffer, gifReaderImpl->m_gifFile->SBackGroundColor,
|
|
|
|
|
gifReaderImpl->m_gifFile->SWidth * gifReaderImpl->m_gifFile->SHeight);
|
|
|
|
|
}
|
2022-07-11 03:40:57 +00:00
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
while (index != gifReaderImpl->m_curIndex)
|
|
|
|
|
{
|
|
|
|
|
++gifReaderImpl->m_curIndex;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
HGResult ret = LoadGifImage(gifReaderImpl);
|
2022-07-11 03:40:57 +00:00
|
|
|
|
if (HGBASE_ERR_OK != ret)
|
|
|
|
|
{
|
2022-07-12 02:21:55 +00:00
|
|
|
|
--gifReaderImpl->m_curIndex;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
2022-07-12 02:21:55 +00:00
|
|
|
|
}
|
2022-07-11 03:40:57 +00:00
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
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;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
ret = HGBase_CloneImage(image2, imgType, imgOrigin, image);
|
|
|
|
|
HGBase_DestroyImage(image2);
|
|
|
|
|
if (HGBASE_ERR_OK != ret)
|
|
|
|
|
{
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NULL != interval)
|
|
|
|
|
*interval = gifReaderImpl->m_curInterval;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
return HGBASE_ERR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:21:55 +00:00
|
|
|
|
HGResult HGAPI HGImgFmt_LoadGifImage(const HGChar* fileName, HGGifLoadInfo* info, HGUInt* interval,
|
2022-07-08 09:36:13 +00:00
|
|
|
|
HGUInt imgType, HGUInt imgOrigin, HGImage* image)
|
|
|
|
|
{
|
|
|
|
|
if (NULL == image)
|
|
|
|
|
{
|
2022-07-12 02:21:55 +00:00
|
|
|
|
if (NULL != interval || 0 != imgType || 0 != imgOrigin)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
|
|
|
|
return HGBASE_ERR_INVALIDARG;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HGGifReader reader = NULL;
|
|
|
|
|
HGResult ret = HGImgFmt_OpenGifReader(fileName, info, &reader);
|
|
|
|
|
if (HGBASE_ERR_OK != ret)
|
|
|
|
|
{
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NULL != image)
|
|
|
|
|
{
|
2022-11-26 02:42:06 +00:00
|
|
|
|
ret = HGImgFmt_RetrieveImageFromGifReader(reader, NULL, interval, imgType, imgOrigin, image);
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HGImgFmt_CloseGifReader(reader);
|
2022-11-26 02:42:06 +00:00
|
|
|
|
return ret;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HGResult HGAPI HGImgFmt_OpenGifWriter(const HGChar* fileName, const HGGifSaveInfo* info, HGGifWriter* writer)
|
|
|
|
|
{
|
2022-07-11 03:40:57 +00:00
|
|
|
|
if (NULL == fileName || NULL == writer)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
|
|
|
|
return HGBASE_ERR_INVALIDARG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int err;
|
|
|
|
|
GifFileType* gifFile = EGifOpenFileName(fileName, false, &err);
|
|
|
|
|
if (NULL == gifFile)
|
|
|
|
|
{
|
2024-08-01 05:46:17 +00:00
|
|
|
|
ErrorLog(g_hLog, "HGImgFmt_OpenGifWriter: EGifOpenFileName fail, %s", fileName);
|
2022-07-08 09:36:13 +00:00
|
|
|
|
return HGBASE_ERR_ACCESSDENIED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-11 03:40:57 +00:00
|
|
|
|
gifFile->SWidth = 0;
|
|
|
|
|
gifFile->SHeight = 0;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
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;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
gifWriterImpl->m_initWidth = (NULL != info) ? info->width : 0;
|
|
|
|
|
gifWriterImpl->m_initHeight = (NULL != info) ? info->height : 0;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
gifWriterImpl->m_gifFile = gifFile;
|
|
|
|
|
gifWriterImpl->m_curIndex = 0;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
gifWriterImpl->m_redBuffer = NULL;
|
|
|
|
|
gifWriterImpl->m_greenBuffer = NULL;
|
|
|
|
|
gifWriterImpl->m_blueBuffer = NULL;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
|
|
|
|
*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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-05 01:48:26 +00:00
|
|
|
|
HGResult HGAPI HGImgFmt_SaveImageToGifWriter(HGGifWriter writer, HGUInt interval, HGColor bkColor, HGImage image)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2022-08-05 01:48:26 +00:00
|
|
|
|
ret = HGImgFmt_SaveImageToGifWriter(writer, interval, bkColor, imageTmp);
|
2022-07-08 09:36:13 +00:00
|
|
|
|
HGBase_DestroyImage(imageTmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HGBase_SetImageROI(image, &roi);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HGGifWriterImpl* gifWriterImpl = (HGGifWriterImpl*)writer;
|
2022-07-11 03:40:57 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 09:36:13 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2022-08-05 01:48:26 +00:00
|
|
|
|
memset(redBuffer, HG_GETCOLOR_R(bkColor), gifWidth * gifHeight);
|
|
|
|
|
memset(greenBuffer, HG_GETCOLOR_G(bkColor), gifWidth * gifHeight);
|
|
|
|
|
memset(blueBuffer, HG_GETCOLOR_B(bkColor), gifWidth * gifHeight);
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2022-08-05 01:48:26 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2022-07-08 09:36:13 +00:00
|
|
|
|
if (HGBASE_IMGTYPE_RGB == imgInfo.type || HGBASE_IMGTYPE_RGBA == imgInfo.type)
|
|
|
|
|
{
|
2022-08-05 01:48:26 +00:00
|
|
|
|
for (int row = 0; row < copyHeight; row++)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-08-05 01:48:26 +00:00
|
|
|
|
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;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
if (HGBASE_IMGORIGIN_BOTTOM == imgInfo.origin)
|
2022-08-05 01:48:26 +00:00
|
|
|
|
pData = imgData + (imgInfo.height - row - srcTop - 1) * imgInfo.widthStep;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
2022-08-05 01:48:26 +00:00
|
|
|
|
for (int col = 0; col < copyWidth; col++)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-08-05 01:48:26 +00:00
|
|
|
|
pr[col + dstLeft] = pData[(col + srcLeft) * channels];
|
|
|
|
|
pg[col + dstLeft] = pData[(col + srcLeft) * channels + 1];
|
|
|
|
|
pb[col + dstLeft] = pData[(col + srcLeft) * channels + 2];
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (HGBASE_IMGTYPE_BGR == imgInfo.type || HGBASE_IMGTYPE_BGRA == imgInfo.type)
|
|
|
|
|
{
|
2022-08-05 01:48:26 +00:00
|
|
|
|
for (int row = 0; row < copyHeight; row++)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-08-05 01:48:26 +00:00
|
|
|
|
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;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
if (HGBASE_IMGORIGIN_BOTTOM == imgInfo.origin)
|
2022-08-05 01:48:26 +00:00
|
|
|
|
pData = imgData + (imgInfo.height - row - srcTop - 1) * imgInfo.widthStep;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
2022-08-05 01:48:26 +00:00
|
|
|
|
for (int col = 0; col < copyWidth; col++)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-08-05 01:48:26 +00:00
|
|
|
|
pb[col + dstLeft] = pData[(col + srcLeft) * channels];
|
|
|
|
|
pg[col + dstLeft] = pData[(col + srcLeft) * channels + 1];
|
|
|
|
|
pr[col + dstLeft] = pData[(col + srcLeft) * channels + 2];
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
assert(HGBASE_IMGTYPE_GRAY == imgInfo.type);
|
|
|
|
|
|
2022-08-05 01:48:26 +00:00
|
|
|
|
for (int row = 0; row < copyHeight; row++)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-08-05 01:48:26 +00:00
|
|
|
|
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;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
if (HGBASE_IMGORIGIN_BOTTOM == imgInfo.origin)
|
2022-08-05 01:48:26 +00:00
|
|
|
|
pData = imgData + (imgInfo.height - row - srcTop - 1) * imgInfo.widthStep;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
2022-08-05 01:48:26 +00:00
|
|
|
|
for (int col = 0; col < copyWidth; col++)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-08-05 01:48:26 +00:00
|
|
|
|
pr[col + dstLeft] = pData[(col + srcLeft)];
|
|
|
|
|
pg[col + dstLeft] = pData[(col + srcLeft)];
|
|
|
|
|
pb[col + dstLeft] = pData[(col + srcLeft)];
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int mapSize = (1 << gifFile->SColorResolution);
|
2022-07-11 07:16:00 +00:00
|
|
|
|
ColorMapObject* colorMap = GifMakeMapObject(mapSize, NULL);
|
|
|
|
|
if (NULL == colorMap)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-11-25 10:00:33 +00:00
|
|
|
|
return HGIMGFMT_ERR_FAIL;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-11 07:16:00 +00:00
|
|
|
|
GifByteType* rasterBits = (GifPixelType*)malloc(sizeof(GifPixelType) * gifWidth * gifHeight);
|
|
|
|
|
if (NULL == rasterBits)
|
|
|
|
|
{
|
|
|
|
|
GifFreeMapObject(colorMap);
|
2022-11-26 02:42:06 +00:00
|
|
|
|
return HGBASE_ERR_OUTOFMEMORY;
|
2022-07-11 07:16:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 09:36:13 +00:00
|
|
|
|
if (GifQuantizeBuffer(gifWidth, gifHeight, &mapSize, redBuffer, greenBuffer, blueBuffer,
|
2022-07-11 07:16:00 +00:00
|
|
|
|
rasterBits, colorMap->Colors) == GIF_ERROR)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
2022-07-11 07:16:00 +00:00
|
|
|
|
free(rasterBits);
|
|
|
|
|
GifFreeMapObject(colorMap);
|
2022-11-25 10:00:33 +00:00
|
|
|
|
return HGIMGFMT_ERR_FAIL;
|
2022-07-11 07:16:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SavedImage* gifImage = GifMakeSavedImage(gifFile, NULL);
|
|
|
|
|
if (NULL == gifImage)
|
|
|
|
|
{
|
|
|
|
|
free(rasterBits);
|
|
|
|
|
GifFreeMapObject(colorMap);
|
2022-11-25 10:00:33 +00:00
|
|
|
|
return HGIMGFMT_ERR_FAIL;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gifImage->ImageDesc.Left = 0;
|
|
|
|
|
gifImage->ImageDesc.Top = 0;
|
|
|
|
|
gifImage->ImageDesc.Width = gifWidth;
|
|
|
|
|
gifImage->ImageDesc.Height = gifHeight;
|
|
|
|
|
gifImage->ImageDesc.Interlace = false;
|
2022-07-11 07:16:00 +00:00
|
|
|
|
gifImage->ImageDesc.ColorMap = colorMap;
|
|
|
|
|
gifImage->RasterBits = rasterBits;
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
|
|
|
|
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
|
2022-07-11 07:16:00 +00:00
|
|
|
|
GifAddExtensionBlock(&gifImage->ExtensionBlockCount, &gifImage->ExtensionBlocks,
|
|
|
|
|
APPLICATION_EXT_FUNC_CODE, 11, (unsigned char*)"NETSCAPE2.0");
|
2022-07-08 09:36:13 +00:00
|
|
|
|
|
2022-07-11 07:16:00 +00:00
|
|
|
|
unsigned char params[3] = { 1, 0, 0 };
|
|
|
|
|
GifAddExtensionBlock(&gifImage->ExtensionBlockCount, &gifImage->ExtensionBlocks,
|
|
|
|
|
CONTINUE_EXT_FUNC_CODE, sizeof(params), params);
|
2022-07-08 09:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++gifWriterImpl->m_curIndex;
|
|
|
|
|
return HGBASE_ERR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-05 01:48:26 +00:00
|
|
|
|
HGResult HGAPI HGImgFmt_SaveGifImage(HGImage image, const HGGifSaveInfo* info,
|
|
|
|
|
HGUInt interval, HGColor bkColor, const HGChar* fileName)
|
2022-07-08 09:36:13 +00:00
|
|
|
|
{
|
|
|
|
|
HGGifWriter writer = NULL;
|
|
|
|
|
HGResult ret = HGImgFmt_OpenGifWriter(fileName, info, &writer);
|
|
|
|
|
if (HGBASE_ERR_OK != ret)
|
|
|
|
|
{
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-05 01:48:26 +00:00
|
|
|
|
ret = HGImgFmt_SaveImageToGifWriter(writer, interval, bkColor, image);
|
2022-07-08 09:36:13 +00:00
|
|
|
|
HGImgFmt_CloseGifWriter(writer);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|