static int EGifPutExtensionFirst(GifFileType * GifFile, int ExtCode, int ExtLen, const void * Extension) { int r = EGifPutExtensionLeader( GifFile, ExtCode ); if ( r != GIF_OK ) return r; return EGifPutExtensionBlock( GifFile, ExtLen, Extension ); }
/****************************************************************************** Put a comment into GIF file using the GIF89 comment extension block. ******************************************************************************/ int EGifPutComment(GifFileType *GifFile, const char *Comment) { unsigned int length; char *buf; length = strlen(Comment); if (length <= 255) { return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, length, Comment); } else { buf = (char *)Comment; if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) == GIF_ERROR) { return GIF_ERROR; } /* Break the comment into 255 byte sub blocks */ while (length > 255) { if (EGifPutExtensionBlock(GifFile, 255, buf) == GIF_ERROR) { return GIF_ERROR; } buf = buf + 255; length -= 255; } /* Output any partial block and the clear code. */ if (length > 0) { if (EGifPutExtensionBlock(GifFile, length, buf) == GIF_ERROR) { return GIF_ERROR; } } if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) { return GIF_ERROR; } } return GIF_OK; }
/****************************************************************************** Main sequence ******************************************************************************/ int main(int argc, char **argv) { GifFileType *GifFileIn = NULL, *GifFileOut = NULL; GifRecordType RecordType; int CodeSize, ExtCode, ErrorCode; GifByteType *CodeBlock, *Extension; /* * Command-line processing goes here. */ /* Use stdin as input (note this also read screen descriptor in: */ if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { PrintGifError(ErrorCode); exit(EXIT_FAILURE); } /* Use the stdout as output: */ if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { PrintGifError(ErrorCode); exit(EXIT_FAILURE); } /* And dump out its screen information: */ if (EGifPutScreenDesc(GifFileOut, GifFileIn->SWidth, GifFileIn->SHeight, GifFileIn->SColorResolution, GifFileIn->SBackGroundColor, GifFileIn->SColorMap) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); /* Scan the content of the input GIF file and load the image(s) in: */ do { if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); switch (RecordType) { case IMAGE_DESC_RECORD_TYPE: if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); /* Put image descriptor to out file: */ if (EGifPutImageDesc(GifFileOut, GifFileIn->Image.Left, GifFileIn->Image.Top, GifFileIn->Image.Width, GifFileIn->Image.Height, GifFileIn->Image.Interlace, GifFileIn->Image.ColorMap) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); /* Now read image itself in decoded form as we dont really */ /* care what we have there, and this is much faster. */ if (DGifGetCode(GifFileIn, &CodeSize, &CodeBlock) == GIF_ERROR || EGifPutCode(GifFileOut, CodeSize, CodeBlock) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); while (CodeBlock != NULL) { if (DGifGetCodeNext(GifFileIn, &CodeBlock) == GIF_ERROR || EGifPutCodeNext(GifFileOut, CodeBlock) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); } break; case EXTENSION_RECORD_TYPE: /* pass through extension records */ if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (EGifPutExtensionLeader(GifFileOut, ExtCode) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (EGifPutExtensionBlock(GifFileOut, Extension[0], Extension + 1) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); while (Extension != NULL) { if (DGifGetExtensionNext(GifFileIn, &Extension)==GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (Extension != NULL) if (EGifPutExtensionBlock(GifFileOut, Extension[0], Extension + 1) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); } if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); break; case TERMINATE_RECORD_TYPE: break; default: /* Should be trapped by DGifGetRecordType */ break; } } while (RecordType != TERMINATE_RECORD_TYPE); if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) { PrintGifError(ErrorCode); if (GifFileIn != NULL) { EGifCloseFile(GifFileIn, NULL); } exit(EXIT_FAILURE); } if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) { PrintGifError(ErrorCode); if (GifFileOut != NULL) { EGifCloseFile(GifFileOut, NULL); } exit(EXIT_FAILURE); } return 0; }
static int write_gif_info(const psx_image* image, image_writer_fn func, void* param, float quality, psx_image_header* header) { size_t buf_size; #if GIFLIB_MAJOR >= 5 int errorcode = 0; #endif struct gif_image_ctx* ctx = (struct gif_image_ctx*)calloc(1, sizeof(struct gif_image_ctx)); if (!ctx) { return -1; // out of memory. } ctx->writer = func; ctx->writer_param = param; #if GIFLIB_MAJOR >= 5 if ((ctx->gif = EGifOpen((void*)ctx, write_gif_from_memory, &errorcode)) == NULL) { free(ctx); return -1; } if (image->num_frames > 1) { EGifSetGifVersion(ctx->gif, true); } #else if ((ctx->gif = EGifOpen((void*)ctx, write_gif_from_memory)) == NULL) { free(ctx); return -1; } if (image->num_frames > 1) { EGifSetGifVersion("89a"); } else { EGifSetGifVersion("87a"); } #endif if (EGifPutScreenDesc(ctx->gif, image->width, image->height, 8, 0, NULL) == GIF_ERROR) { GIF_CLOSE_EFILE(ctx->gif); free(ctx); return -1; } if (image->num_frames > 1) { // add netscape2.0 application extension to an animation gif. #if GIFLIB_MAJOR >= 5 EGifPutExtensionLeader(ctx->gif, APPLICATION_EXT_FUNC_CODE); EGifPutExtensionBlock(ctx->gif, 11, "NETSCAPE2.0"); EGifPutExtensionBlock(ctx->gif, 3, "\x01\x00\x00"); EGifPutExtensionTrailer(ctx->gif); #else EGifPutExtensionFirst(ctx->gif, APPLICATION_EXT_FUNC_CODE, 11, "NETSCAPE2.0"); EGifPutExtensionLast(ctx->gif, APPLICATION_EXT_FUNC_CODE, 3, "\x01\x00\x00"); #endif } buf_size = image->width * image->height * sizeof(GifByteType); ctx->red_buf = (GifByteType*)malloc(buf_size); ctx->green_buf = (GifByteType*)malloc(buf_size); ctx->blue_buf = (GifByteType*)malloc(buf_size); ctx->output_buffer = (GifByteType*)malloc(buf_size); if (!ctx->red_buf || !ctx->green_buf || !ctx->blue_buf || !ctx->output_buffer) { GIF_CLOSE_EFILE(ctx->gif); if (ctx->red_buf) free(ctx->red_buf); if (ctx->green_buf) free(ctx->green_buf); if (ctx->blue_buf) free(ctx->blue_buf); if (ctx->output_buffer) free(ctx->output_buffer); free(ctx); return -1; } header->priv = ctx; header->width = image->width; header->height = image->height; header->pitch = image->pitch; header->depth = get_depth(image->format); header->bpp = get_bpp(image->format); header->format = (int)image->format; header->alpha = 1; header->frames = (int)image->num_frames; return 0; }
bool ParupaintPanvasInputOutput::exportGIF(ParupaintPanvas * panvas, const QString & filename, QString & errorStr) { Q_ASSERT(panvas); if(filename.isEmpty()) return (errorStr = "Enter a filename to save to.").isEmpty(); #ifndef PARUPAINT_NOGIF int error = 0; GifFileType * gif = EGifOpenFileName(filename.toStdString().c_str(), false, &error); foreach(const QImage & image, panvas->mergedImageFrames(true)){ error = 0; bool alpha = false; QImage toWrite = convertToIndexed8(image, &alpha); QVector<QRgb> colorTable = toWrite.colorTable(); ColorMapObject cmap; int numColors = 1 << BitSize(toWrite.colorCount()); numColors = 256; cmap.ColorCount = numColors; cmap.BitsPerPixel = 8; /// @todo based on numColors (or not? we did ask for Format_Indexed8, so the data is always 8-bit, right?) GifColorType* colorValues = (GifColorType*)malloc(cmap.ColorCount * sizeof(GifColorType)); cmap.Colors = colorValues; int c = 0; for(; c < toWrite.colorCount(); ++c) { //qDebug("color %d has %02X%02X%02X", c, qRed(colorTable[c]), qGreen(colorTable[c]), qBlue(colorTable[c])); colorValues[c].Red = qRed(colorTable[c]); colorValues[c].Green = qGreen(colorTable[c]); colorValues[c].Blue = qBlue(colorTable[c]); } // In case we had an actual number of colors that's not a power of 2, // fill the rest with something (black perhaps). for (; c < numColors; ++c) { colorValues[c].Red = 0; colorValues[c].Green = 0; colorValues[c].Blue = 0; } /// @todo how to specify which version, or decide based on features in use // Because of this call, libgif is not re-entrant EGifSetGifVersion(gif, true); if ((error = EGifPutScreenDesc(gif, toWrite.width(), toWrite.height(), numColors, 0, &cmap)) == GIF_ERROR) qCritical("EGifPutScreenDesc returned error %d", error); int fps = (100.0/panvas->frameRate()); char flags = 1 << 3; if(alpha) flags |= 1; char graphics_ext[] = { flags, (char)(fps % 256), (char)(fps / 256), (char)(alpha ? 0x00 : 0xff) }; EGifPutExtension(gif, GRAPHICS_EXT_FUNC_CODE, 4, graphics_ext); if ((error = EGifPutImageDesc(gif, 0, 0, toWrite.width(), toWrite.height(), 0, &cmap)) == GIF_ERROR) qCritical("EGifPutImageDesc returned error %d", error); int lc = toWrite.height(); int llen = toWrite.bytesPerLine(); for (int l = 0; l < lc; ++l) { uchar* line = toWrite.scanLine(l); if ((error = EGifPutLine(gif, (GifPixelType*)line, llen)) == GIF_ERROR) { qCritical("EGifPutLine returned error %d", error); } } if(true){ // loop forever unsigned char loopblock[3] = {1, 0, 0}; EGifPutExtensionLeader(gif, APPLICATION_EXT_FUNC_CODE); EGifPutExtensionBlock(gif, 11, "NETSCAPE2.0"); EGifPutExtensionBlock(gif, 3, loopblock); EGifPutExtensionTrailer(gif); } } EGifCloseFile(gif, &error); return true; #endif return (errorStr = "GIF export not available.").isEmpty(); }
/****************************************************************************** Interpret the command line and scan the given GIF file. ******************************************************************************/ int main(int argc, char **argv) { int NumFiles, ExtCode, CodeSize, ImageNum = 0, ImageN, HasGIFOutput, ErrorCode; bool Error, ImageNFlag = false, HelpFlag = false; GifRecordType RecordType; GifByteType *Extension, *CodeBlock; char **FileName = NULL, *ColorFileName, *TranslateFileName; GifFileType *GifFileIn = NULL, *GifFileOut = NULL; if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &SaveFlag, &TranslateFlag, &TranslateFileName, &LoadFlag, &ColorFileName, &GammaFlag, &Gamma, &ImageNFlag, &ImageN, &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 (SaveFlag + LoadFlag + GammaFlag + TranslateFlag > 1) GIF_EXIT("Can not handle more than one of -s -l, -t, or -g at the same time."); /* Default action is to dump colormaps */ if (!SaveFlag && !LoadFlag && !GammaFlag && !TranslateFlag) SaveFlag = true; if (NumFiles == 1) { if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { PrintGifError(ErrorCode); exit(EXIT_FAILURE); } } else { /* Use stdin instead: */ if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { PrintGifError(ErrorCode); exit(EXIT_FAILURE); } } if (SaveFlag) { /* We are dumping out the color map as text file to stdout: */ ColorFile = stdout; } else { if (TranslateFlag) { /* We are loading new color map from specified file: */ if ((TranslateFile = fopen(TranslateFileName, "rt")) == NULL) GIF_EXIT("Failed to open specified color translation file."); } if (LoadFlag) { /* We are loading new color map from specified file: */ if ((ColorFile = fopen(ColorFileName, "rt")) == NULL) GIF_EXIT("Failed to open specified color map file."); } } if ((HasGIFOutput = (LoadFlag || TranslateFlag || GammaFlag)) != 0) { /* Open stdout for GIF output file: */ if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { PrintGifError(ErrorCode); exit(EXIT_FAILURE); } } if (!ImageNFlag) { /* We are suppose to modify the screen color map, so do it: */ // cppcheck-suppress nullPointer GifFileIn->SColorMap = ModifyColorMap(GifFileIn->SColorMap); if (!HasGIFOutput) { /* We can quit here, as we have the color map: */ if (GifFileIn != NULL) DGifCloseFile(GifFileIn); fclose(ColorFile); exit(EXIT_SUCCESS); } } /* And dump out its new possible repositioned screen information: */ if (HasGIFOutput) if (EGifPutScreenDesc(GifFileOut, GifFileIn->SWidth, GifFileIn->SHeight, GifFileIn->SColorResolution, GifFileIn->SBackGroundColor, GifFileIn->SColorMap) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); /* Scan the content of the GIF file and load the image(s) in: */ do { if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); switch (RecordType) { case IMAGE_DESC_RECORD_TYPE: if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if ((++ImageNum == ImageN) && ImageNFlag) { /* We are suppose to modify this image color map, do it: */ GifFileIn->SColorMap =ModifyColorMap(GifFileIn->SColorMap); if (!HasGIFOutput) { /* We can quit here, as we have the color map: */ if (GifFileIn != NULL) DGifCloseFile(GifFileIn); fclose(ColorFile); exit(EXIT_SUCCESS); } } if (HasGIFOutput) if (EGifPutImageDesc(GifFileOut, GifFileIn->Image.Left, GifFileIn->Image.Top, GifFileIn->Image.Width, GifFileIn->Image.Height, GifFileIn->Image.Interlace, GifFileIn->Image.ColorMap) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (!TranslateFlag || (ImageNFlag && (ImageN != ImageNum))) { /* Now read image itself in decoded form as we don't */ /* really care what we have there, and this is much */ /* faster. */ if (DGifGetCode(GifFileIn, &CodeSize, &CodeBlock) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (HasGIFOutput) if (EGifPutCode(GifFileOut, CodeSize, CodeBlock) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); while (CodeBlock != NULL) { if (DGifGetCodeNext(GifFileIn, &CodeBlock) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (HasGIFOutput) if (EGifPutCodeNext(GifFileOut, CodeBlock) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); } } else /* we need to mung pixels intices */ { int i; register GifPixelType *cp; GifPixelType *Line = (GifPixelType *) malloc(GifFileIn->Image.Width * sizeof(GifPixelType)); for (i = 0; i < GifFileIn->Image.Height; i++) { if (DGifGetLine(GifFileIn, Line,GifFileIn->Image.Width) == GIF_ERROR) { QuitGifError(GifFileIn, GifFileOut); } /* translation step goes here */ for (cp = Line; cp < Line+GifFileIn->Image.Width; cp++) *cp = Translation[*cp]; if (EGifPutLine(GifFileOut, Line, GifFileIn->Image.Width) == GIF_ERROR) { QuitGifError(GifFileIn, GifFileOut); } } free((char *) Line); } break; case EXTENSION_RECORD_TYPE: assert(GifFileOut != NULL); /* might pacify Coverity */ /* pass through extension records */ if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (EGifPutExtensionLeader(GifFileOut, ExtCode) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (EGifPutExtensionBlock(GifFileOut, Extension[0], Extension + 1) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); while (Extension != NULL) { if (DGifGetExtensionNext(GifFileIn, &Extension)==GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (Extension != NULL) if (EGifPutExtensionBlock(GifFileOut, Extension[0], Extension + 1) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); } if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); break; case TERMINATE_RECORD_TYPE: break; default: /* Should be trapped by DGifGetRecordType. */ break; } } while (RecordType != TERMINATE_RECORD_TYPE); if (DGifCloseFile(GifFileIn) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); if (HasGIFOutput) if (EGifCloseFile(GifFileOut) == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); return 0; }
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; }