static int readExtensions(int ExtFunction, GifByteType *ExtData, GifInfo *info) { if (ExtData == NULL) return GIF_OK; if (ExtFunction == GRAPHICS_EXT_FUNC_CODE) { GraphicsControlBlock *GCB = &info->controlBlock[info->gifFilePtr->ImageCount]; if (DGifExtensionToGCB(ExtData[0], ExtData + 1, GCB) == GIF_ERROR) return GIF_ERROR; GCB->DelayTime = GCB->DelayTime > 1 ? GCB->DelayTime * 10 : DEFAULT_FRAME_DURATION_MS; } else if (ExtFunction == COMMENT_EXT_FUNC_CODE) { if (getComment(ExtData, info) == GIF_ERROR) { info->gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM; return GIF_ERROR; } } else if (ExtFunction == APPLICATION_EXT_FUNC_CODE) { char const *string = (char const *) (ExtData + 1); if (strncmp("NETSCAPE2.0", string, ExtData[0]) == 0 || strncmp("ANIMEXTS1.0", string, ExtData[0]) == 0) { if (DGifGetExtensionNext(info->gifFilePtr, &ExtData, &ExtFunction) == GIF_ERROR) return GIF_ERROR; if (ExtData[0] == 3 && ExtData[1] == 1) { info->loopCount = (uint_fast16_t) (ExtData[2] + (ExtData[3] << 8)); } } } return GIF_OK; }
static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex, GraphicsControlBlock* gcb) { int i; if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) { return GIF_ERROR; } gcb->DisposalMode = DISPOSAL_UNSPECIFIED; gcb->UserInputFlag = 0; gcb->DelayTime = 0; gcb->TransparentColor = NO_TRANSPARENT_COLOR; for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) { ExtensionBlock* ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { return DGifExtensionToGCB( ep->ByteCount, (const GifByteType*)ep->Bytes, gcb); } } return GIF_ERROR; }
void dumpGCBBlock(const ExtensionBlock &ep) { GraphicsControlBlock gcb; CHECKGIFOK(DGifExtensionToGCB(ep.ByteCount, ep.Bytes, &gcb)); _tprintf(_T("GraphicsControlBlock : Dispose:%d, Delay:%d, Transparent color:%d, Userinput:%s\n") ,gcb.DisposalMode, gcb.DelayTime, gcb.TransparentColor, boolToStr(gcb.UserInputFlag)); }
bool GifTranscoder::resizeBoxFilter(GifFileType* gifIn, GifFileType* gifOut) { ASSERT(gifIn != NULL, "gifIn cannot be NULL"); ASSERT(gifOut != NULL, "gifOut cannot be NULL"); if (gifIn->SWidth < 0 || gifIn->SHeight < 0) { LOGE("Input GIF has invalid size: %d x %d", gifIn->SWidth, gifIn->SHeight); return false; } // Output GIF will be 50% the size of the original. if (EGifPutScreenDesc(gifOut, gifIn->SWidth / 2, gifIn->SHeight / 2, gifIn->SColorResolution, gifIn->SBackGroundColor, gifIn->SColorMap) == GIF_ERROR) { LOGE("Could not write screen descriptor"); return false; } LOGD("Wrote screen descriptor"); // Index of the current image. int imageIndex = 0; // Transparent color of the current image. int transparentColor = NO_TRANSPARENT_COLOR; // Buffer for reading raw images from the input GIF. std::vector<GifByteType> srcBuffer(gifIn->SWidth * gifIn->SHeight); // Buffer for rendering images from the input GIF. std::unique_ptr<ColorARGB> renderBuffer(new ColorARGB[gifIn->SWidth * gifIn->SHeight]); // Buffer for writing new images to output GIF (one row at a time). std::unique_ptr<GifByteType> dstRowBuffer(new GifByteType[gifOut->SWidth]); // Many GIFs use DISPOSE_DO_NOT to make images draw on top of previous images. They can also // use DISPOSE_BACKGROUND to clear the last image region before drawing the next one. We need // to keep track of the disposal mode as we go along to properly render the GIF. int disposalMode = DISPOSAL_UNSPECIFIED; int prevImageDisposalMode = DISPOSAL_UNSPECIFIED; GifImageDesc prevImageDimens; // Background color (applies to entire GIF). ColorARGB bgColor = TRANSPARENT; GifRecordType recordType; do { if (DGifGetRecordType(gifIn, &recordType) == GIF_ERROR) { LOGE("Could not get record type"); return false; } LOGD("Read record type: %d", recordType); switch (recordType) { case IMAGE_DESC_RECORD_TYPE: { if (DGifGetImageDesc(gifIn) == GIF_ERROR) { LOGE("Could not read image descriptor (%d)", imageIndex); return false; } // Sanity-check the current image position. if (gifIn->Image.Left < 0 || gifIn->Image.Top < 0 || gifIn->Image.Left + gifIn->Image.Width > gifIn->SWidth || gifIn->Image.Top + gifIn->Image.Height > gifIn->SHeight) { LOGE("GIF image extends beyond logical screen"); return false; } // Write the new image descriptor. if (EGifPutImageDesc(gifOut, 0, // Left 0, // Top gifOut->SWidth, gifOut->SHeight, false, // Interlace gifIn->Image.ColorMap) == GIF_ERROR) { LOGE("Could not write image descriptor (%d)", imageIndex); return false; } // Read the image from the input GIF. The buffer is already initialized to the // size of the GIF, which is usually equal to the size of all the images inside it. // If not, the call to resize below ensures that the buffer is the right size. srcBuffer.resize(gifIn->Image.Width * gifIn->Image.Height); if (readImage(gifIn, srcBuffer.data()) == false) { LOGE("Could not read image data (%d)", imageIndex); return false; } LOGD("Read image data (%d)", imageIndex); // Render the image from the input GIF. if (renderImage(gifIn, srcBuffer.data(), imageIndex, transparentColor, renderBuffer.get(), bgColor, prevImageDimens, prevImageDisposalMode) == false) { LOGE("Could not render %d", imageIndex); return false; } LOGD("Rendered image (%d)", imageIndex); // Generate the image in the output GIF. for (int y = 0; y < gifOut->SHeight; y++) { for (int x = 0; x < gifOut->SWidth; x++) { const GifByteType dstColorIndex = computeNewColorIndex( gifIn, transparentColor, renderBuffer.get(), x, y); *(dstRowBuffer.get() + x) = dstColorIndex; } if (EGifPutLine(gifOut, dstRowBuffer.get(), gifOut->SWidth) == GIF_ERROR) { LOGE("Could not write raster data (%d)", imageIndex); return false; } } LOGD("Wrote raster data (%d)", imageIndex); // Save the disposal mode for rendering the next image. // We only support DISPOSE_DO_NOT and DISPOSE_BACKGROUND. prevImageDisposalMode = disposalMode; if (prevImageDisposalMode == DISPOSAL_UNSPECIFIED) { prevImageDisposalMode = DISPOSE_DO_NOT; } else if (prevImageDisposalMode == DISPOSE_PREVIOUS) { prevImageDisposalMode = DISPOSE_BACKGROUND; } if (prevImageDisposalMode == DISPOSE_BACKGROUND) { prevImageDimens.Left = gifIn->Image.Left; prevImageDimens.Top = gifIn->Image.Top; prevImageDimens.Width = gifIn->Image.Width; prevImageDimens.Height = gifIn->Image.Height; } if (gifOut->Image.ColorMap) { GifFreeMapObject(gifOut->Image.ColorMap); gifOut->Image.ColorMap = NULL; } imageIndex++; } break; case EXTENSION_RECORD_TYPE: { int extCode; GifByteType* ext; if (DGifGetExtension(gifIn, &extCode, &ext) == GIF_ERROR) { LOGE("Could not read extension block"); return false; } LOGD("Read extension block, code: %d", extCode); if (extCode == GRAPHICS_EXT_FUNC_CODE) { GraphicsControlBlock gcb; if (DGifExtensionToGCB(ext[0], ext + 1, &gcb) == GIF_ERROR) { LOGE("Could not interpret GCB extension"); return false; } transparentColor = gcb.TransparentColor; // This logic for setting the background color based on the first GCB // doesn't quite match the GIF spec, but empirically it seems to work and it // matches what libframesequence (Rastermill) does. if (imageIndex == 0 && gifIn->SColorMap) { if (gcb.TransparentColor == NO_TRANSPARENT_COLOR) { GifColorType bgColorIndex = gifIn->SColorMap->Colors[gifIn->SBackGroundColor]; bgColor = gifColorToColorARGB(bgColorIndex); LOGD("Set background color based on first GCB"); } } // Record the original disposal mode and then update it. disposalMode = gcb.DisposalMode; gcb.DisposalMode = DISPOSE_BACKGROUND; EGifGCBToExtension(&gcb, ext + 1); } if (EGifPutExtensionLeader(gifOut, extCode) == GIF_ERROR) { LOGE("Could not write extension leader"); return false; } if (EGifPutExtensionBlock(gifOut, ext[0], ext + 1) == GIF_ERROR) { LOGE("Could not write extension block"); return false; } LOGD("Wrote extension block"); while (ext != NULL) { if (DGifGetExtensionNext(gifIn, &ext) == GIF_ERROR) { LOGE("Could not read extension continuation"); return false; } if (ext != NULL) { LOGD("Read extension continuation"); if (EGifPutExtensionBlock(gifOut, ext[0], ext + 1) == GIF_ERROR) { LOGE("Could not write extension continuation"); return false; } LOGD("Wrote extension continuation"); } } if (EGifPutExtensionTrailer(gifOut) == GIF_ERROR) { LOGE("Could not write extension trailer"); return false; } } break; } } while (recordType != TERMINATE_RECORD_TYPE); LOGD("No more records"); return true; }
/****************************************************************************** Interpret the command line and scan the given GIF file. ******************************************************************************/ int main(int argc, char **argv) { int i, j, ExtCode, ErrorCode, CodeSize, NumFiles, Len, ImageNum = 1; bool Error, ColorMapFlag = false, EncodedFlag = false, LZCodesFlag = false, PixelFlag = false, HelpFlag = false, RawFlag = false; char *GifFileName, **FileName = NULL; GifPixelType *Line; GifRecordType RecordType; GifByteType *CodeBlock, *Extension; GifFileType *GifFile; if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &ColorMapFlag, &EncodedFlag, &LZCodesFlag, &PixelFlag, &RawFlag, &HelpFlag, &NumFiles, &FileName)) != false || (NumFiles > 1 && !HelpFlag)) { if (Error) GAPrintErrMsg(Error); else if (NumFiles > 1) GIF_MESSAGE("Error in command line parsing - one GIF file please."); GAPrintHowTo(CtrlStr); exit(EXIT_FAILURE); } if (HelpFlag) { (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); GAPrintHowTo(CtrlStr); exit(EXIT_SUCCESS); } if (NumFiles == 1) { GifFileName = *FileName; if ((GifFile = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { PrintGifError(ErrorCode); exit(EXIT_FAILURE); } } else { /* Use stdin instead: */ GifFileName = "Stdin"; if ((GifFile = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { PrintGifError(ErrorCode); exit(EXIT_FAILURE); } } /* Because we write binary data - make sure no text will be written. */ if (RawFlag) { ColorMapFlag = EncodedFlag = LZCodesFlag = PixelFlag = false; #ifdef _WIN32 _setmode(1, O_BINARY); /* Make sure it is in binary mode. */ #endif /* _WIN32 */ } else { printf("\n%s:\n\n\tScreen Size - Width = %d, Height = %d.\n", GifFileName, GifFile->SWidth, GifFile->SHeight); printf("\tColorResolution = %d, BitsPerPixel = %d, BackGround = %d, Aspect = %d.\n", GifFile->SColorResolution, GifFile->SColorMap ? GifFile->SColorMap->BitsPerPixel : 0, GifFile->SBackGroundColor, GifFile->AspectByte); if (GifFile->SColorMap) printf("\tHas Global Color Map.\n\n"); else printf("\tNo Global Color Map.\n\n"); if (ColorMapFlag && GifFile->SColorMap) { printf("\tGlobal Color Map:\n"); Len = GifFile->SColorMap->ColorCount; printf("\tSort Flag: %s\n", GifFile->SColorMap->SortFlag ? "on":"off"); for (i = 0; i < Len; i+=4) { for (j = 0; j < 4 && j < Len; j++) { printf("%3d: %02xh %02xh %02xh ", i + j, GifFile->SColorMap->Colors[i + j].Red, GifFile->SColorMap->Colors[i + j].Green, GifFile->SColorMap->Colors[i + j].Blue); } printf("\n"); } } } do { if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } switch (RecordType) { case IMAGE_DESC_RECORD_TYPE: if (DGifGetImageDesc(GifFile) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } if (!RawFlag) { printf("\nImage #%d:\n\n\tImage Size - Left = %d, Top = %d, Width = %d, Height = %d.\n", ImageNum++, GifFile->Image.Left, GifFile->Image.Top, GifFile->Image.Width, GifFile->Image.Height); printf("\tImage is %s", GifFile->Image.Interlace ? "Interlaced" : "Non Interlaced"); if (GifFile->Image.ColorMap != NULL) printf(", BitsPerPixel = %d.\n", GifFile->Image.ColorMap->BitsPerPixel); else printf(".\n"); if (GifFile->Image.ColorMap) printf("\tImage Has Color Map.\n"); else printf("\tNo Image Color Map.\n"); if (ColorMapFlag && GifFile->Image.ColorMap) { printf("\tSort Flag: %s\n", GifFile->Image.ColorMap->SortFlag ? "on":"off"); Len = 1 << GifFile->Image.ColorMap->BitsPerPixel; for (i = 0; i < Len; i+=4) { for (j = 0; j < 4 && j < Len; j++) { printf("%3d: %02xh %02xh %02xh ", i + j, GifFile->Image.ColorMap->Colors[i + j].Red, GifFile->Image.ColorMap->Colors[i + j].Green, GifFile->Image.ColorMap->Colors[i + j].Blue); } printf("\n"); } } } if (EncodedFlag) { if (DGifGetCode(GifFile, &CodeSize, &CodeBlock) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } printf("\nImage LZ compressed Codes (Code Size = %d):\n", CodeSize); PrintCodeBlock(GifFile, CodeBlock, true); while (CodeBlock != NULL) { if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } PrintCodeBlock(GifFile, CodeBlock, false); } } else if (LZCodesFlag) { PrintLZCodes(GifFile); } else if (PixelFlag) { Line = (GifPixelType *) malloc(GifFile->Image.Width * sizeof(GifPixelType)); for (i = 0; i < GifFile->Image.Height; i++) { if (DGifGetLine(GifFile, Line, GifFile->Image.Width) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } PrintPixelBlock(Line, GifFile->Image.Width, i == 0); } PrintPixelBlock(NULL, GifFile->Image.Width, false); free((char *) Line); } else if (RawFlag) { Line = (GifPixelType *) malloc(GifFile->Image.Width * sizeof(GifPixelType)); for (i = 0; i < GifFile->Image.Height; i++) { if (DGifGetLine(GifFile, Line, GifFile->Image.Width) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } fwrite(Line, 1, GifFile->Image.Width, stdout); } free((char *) Line); } else { /* Skip the image: */ if (DGifGetCode(GifFile, &CodeSize, &CodeBlock) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } while (CodeBlock != NULL) { if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } } } break; case EXTENSION_RECORD_TYPE: if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } if (!RawFlag) { putchar('\n'); switch (ExtCode) { case COMMENT_EXT_FUNC_CODE: printf("GIF89 comment"); break; case GRAPHICS_EXT_FUNC_CODE: printf("GIF89 graphics control"); break; case PLAINTEXT_EXT_FUNC_CODE: printf("GIF89 plaintext"); break; case APPLICATION_EXT_FUNC_CODE: printf("GIF89 application block"); break; default: printf("Extension record of unknown type"); break; } printf(" (Ext Code = %d [%c]):\n", ExtCode, MAKE_PRINTABLE(ExtCode)); PrintExtBlock(Extension, true); if (ExtCode == GRAPHICS_EXT_FUNC_CODE) { GraphicsControlBlock gcb; if (DGifExtensionToGCB(Extension[0], Extension+1, &gcb) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } printf("\tDisposal Mode: %d\n", gcb.DisposalMode); printf("\tUser Input Flag: %d\n", gcb.UserInputFlag); printf("\tTransparency on: %s\n", gcb.TransparentColor != -1 ? "yes" : "no"); printf("\tDelayTime: %d\n", gcb.DelayTime); printf("\tTransparent Index: %d\n", gcb.TransparentColor); } } for (;;) { if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); } if (Extension == NULL) break; PrintExtBlock(Extension, false); } break; case TERMINATE_RECORD_TYPE: break; default: /* Should be trapped by DGifGetRecordType */ break; } } while (RecordType != TERMINATE_RECORD_TYPE); if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { PrintGifError(ErrorCode); exit(EXIT_FAILURE); } if (!RawFlag) printf("\nGIF file terminated normally.\n"); return 0; }