#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; } #if defined(HG_CMP_MSC) if (0 != _access(fileName, 0)) #else if (0 != access(fileName, 0)) #endif { return HGBASE_ERR_FILENOTEXIST; } int err; GifFileType* gifFile = DGifOpenFileName(fileName, &err); if (NULL == gifFile) { return HGBASE_ERR_FILEERROR; } *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; } #if defined(HG_CMP_MSC) if (0 != _access(fileName, 0)) #else if (0 != access(fileName, 0)) #endif { return HGBASE_ERR_FILENOTEXIST; } int err; GifFileType* gifFile = DGifOpenFileName(fileName, &err); if (NULL == gifFile) { return HGBASE_ERR_FILEERROR; } DGifSlurp(gifFile); if (gifFile->ImageCount <= 0) { DGifCloseFile(gifFile, &err); return HGBASE_ERR_FILEERROR; } 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 HGIMGFMT_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 HGIMGFMT_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 (0 != imgOrigin && 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; } if (0 == imgOrigin) { imgOrigin = HGBASE_IMGORIGIN_TOP; } 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) { ret = HGImgFmt_RetrieveImageFromGifReader(reader, NULL, interval, imgType, imgOrigin, image); } HGImgFmt_CloseGifReader(reader); return ret; } 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: EGifOpenFileName fail, %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 HGIMGFMT_ERR_FAIL; } GifByteType* rasterBits = (GifPixelType*)malloc(sizeof(GifPixelType) * gifWidth * gifHeight); if (NULL == rasterBits) { GifFreeMapObject(colorMap); return HGBASE_ERR_OUTOFMEMORY; } if (GifQuantizeBuffer(gifWidth, gifHeight, &mapSize, redBuffer, greenBuffer, blueBuffer, rasterBits, colorMap->Colors) == GIF_ERROR) { free(rasterBits); GifFreeMapObject(colorMap); return HGIMGFMT_ERR_FAIL; } SavedImage* gifImage = GifMakeSavedImage(gifFile, NULL); if (NULL == gifImage) { free(rasterBits); GifFreeMapObject(colorMap); return HGIMGFMT_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; }