Пример #1
0
int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, 
			    GifFileType *GifFile, int ImageIndex)
{
    int i;
    size_t Len;
    GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */

    if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1)
	return GIF_ERROR;

    for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
	ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
	if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
	    EGifGCBToExtension(GCB, ep->Bytes);
	    return GIF_OK;
	}
    }

    Len = EGifGCBToExtension(GCB, (GifByteType *)buf);
    if (GifAddExtensionBlock(&GifFile->SavedImages[ImageIndex].ExtensionBlockCount,
			     &GifFile->SavedImages[ImageIndex].ExtensionBlocks,
			     GRAPHICS_EXT_FUNC_CODE,
			     Len,
			     (unsigned char *)buf) == GIF_ERROR)
	return (GIF_ERROR);

    return (GIF_OK);
}
Пример #2
0
static int encode_gif_data(psx_image_header* header, psx_image_frame* frame, int idx, const ps_byte* buffer, size_t buffer_len, int* ret)
{
    int x, y;
    ColorMapObject *output_map = NULL;
    int map_size = 256;

    struct gif_image_ctx* ctx = (struct gif_image_ctx*)header->priv;

    if ((output_map = MakeMapObject(map_size, NULL)) == NULL) {
        if (ret) *ret = S_FAILURE;
        return -1;
    }

    for (y = 0; y < header->height; y++) {
        ps_byte* row = (ps_byte*)(buffer + header->pitch * y);
        for (x = 0; x < header->width; x++) {
            uint32_t rgba[4] = {0}; // r, g, b, a 
            gif_get_pixel_rgba_premultiply(header->format, row, x, rgba);
            ctx->red_buf[header->width * y + x] = rgba[0];
            ctx->green_buf[header->width * y + x] = rgba[1];
            ctx->blue_buf[header->width * y + x] = rgba[2];
        }
    }
    
    if (QuantizeBuffer(header->width, header->height, &map_size,
		ctx->red_buf, ctx->green_buf, ctx->blue_buf, ctx->output_buffer, output_map->Colors) == GIF_ERROR) {
        FreeMapObject(output_map);
        if (ret) *ret = S_FAILURE;
        return -1;
    }

    if (frame->duration > 0) {
        GifByteType extension[4];
#if GIFLIB_MAJOR >= 5
        GraphicsControlBlock gcb;
        gcb.DisposalMode = DISPOSAL_UNSPECIFIED; // FIXME: need specified ?
        gcb.UserInputFlag = false;
        gcb.DelayTime = frame->duration / 10;
        gcb.TransparentColor = -1; // FIXME: need specified ?

        EGifGCBToExtension(&gcb, extension);
#else
        int delay = frame->duration / 10;
        extension[0] = 0;
        extension[1] = LOBYTE(delay);
        extension[2] = HIBYTE(delay);
        extension[3] = (char)-1;
#endif
        if (EGifPutExtension(ctx->gif, GRAPHICS_EXT_FUNC_CODE, 4, extension) == GIF_ERROR) {
            FreeMapObject(output_map);
            if (ret) *ret = S_FAILURE;
            return -1;
        }
    }

    if (EGifPutImageDesc(ctx->gif, 0, 0, header->width, header->height, FALSE, output_map) == GIF_ERROR) {
        FreeMapObject(output_map);
        if (ret) *ret = S_FAILURE;
        return -1;
    }

    for (y = 0; y < header->height; y++) {
        EGifPutLine(ctx->gif, ctx->output_buffer + y * header->width, header->width);
    }
    FreeMapObject(output_map);
    return 0;
}
Пример #3
0
void EncodeToGifBufferWorker::Execute () {
    GifByteType
        * redBuff = (GifByteType *) _pixbuf,
        * greenBuff = (GifByteType *) _pixbuf + _width * _height,
        * blueBuff = (GifByteType *) _pixbuf + 2 * _width * _height,
        * alphaBuff = (GifByteType *) _pixbuf + 3 * _width * _height,
        * gifimgbuf = (GifByteType *) malloc(_width * _height * sizeof(GifByteType)); // the indexed image
    ColorMapObject *cmap;
    SavedImage * simg;

    if (NULL == gifimgbuf){
        SetErrorMessage("Out of memory");
        return;
    }

    cmap = GifMakeMapObject(_cmapSize, NULL);

    if (NULL == cmap){
        free(gifimgbuf);
        SetErrorMessage("Out of memory");
        return;
    }

    if (GIF_ERROR == GifQuantizeBuffer(
                _width, _height, &_colors,
               redBuff, greenBuff, blueBuff,
               gifimgbuf, cmap->Colors
        )){
        free(gifimgbuf);
        GifFreeMapObject(cmap);
        SetErrorMessage("Unable to quantize image");
        return;
    }

    int errcode;
    gifWriteCbData buffinf = {NULL, 0};
    GifFileType * gif;

    gif = EGifOpen((void *) &buffinf, gifWriteCB, &errcode);

    if (NULL == gif){
        free(gifimgbuf);
        GifFreeMapObject(cmap);
        SetErrorMessage(GifErrorString(errcode));
        return;
    }

    gif->SWidth = _width;
    gif->SHeight = _height;
    gif->SColorResolution = _cmapSize;

    simg = GifMakeSavedImage(gif, NULL);

    if (NULL == simg){
        free(gifimgbuf);
        EGifCloseFile(gif, &errcode); // will also free cmap
        SetErrorMessage("Out of memory");
        return;
    }

    simg->ImageDesc.Left = 0;
    simg->ImageDesc.Top = 0;
    simg->ImageDesc.Width = _width;
    simg->ImageDesc.Height = _height;
    simg->ImageDesc.Interlace = _interlaced;
    simg->ImageDesc.ColorMap = cmap;
    simg->RasterBits = gifimgbuf;

    // for some reason giflib sometimes creates an invalid file if the global
    // color table is not set as well
    gif->SColorMap = cmap;

    if (_trans){
        ExtensionBlock ext;
        // 1. assign transparent color index in color table
        GraphicsControlBlock gcb = {0, false, 0, _colors++};
        // 2. replace transparent pixels above threshold with this color
        remapTransparentPixels(gifimgbuf, alphaBuff, _width, _height, gcb.TransparentColor, _threshold);
        // 3. create a control block
        size_t extlen = EGifGCBToExtension(&gcb, (GifByteType *) &ext);
        if (GIF_ERROR == GifAddExtensionBlock(
                &(simg->ExtensionBlockCount),
                &(simg->ExtensionBlocks),
                GRAPHICS_EXT_FUNC_CODE,
                extlen,
                (unsigned char *) &ext)
            ) {
            EGifCloseFile(gif, &errcode);
            SetErrorMessage("Out of memory");
            return;
        }
    }


    if (GIF_ERROR == EGifSpew(gif)){
        EGifCloseFile(gif, &errcode);
        SetErrorMessage(GifErrorString(gif->Error));
        return;
    }

    _gifbuf = (char *) buffinf.buff;
    _gifbufsize = buffinf.buffsize;

    return;
}
Пример #4
0
bool MCImageEncodeGIF(MCImageIndexedBitmap *p_indexed, IO_handle p_stream, uindex_t &r_bytes_written)
{
	bool t_success = true;

	int32_t t_transparent = -1;
	uindex_t t_palette_size;
	uindex_t t_depth;


	t_depth = GifBitSize(p_indexed->palette_size);
	// GIF requires palette size to be 2^depth
	t_palette_size = 1 << t_depth;

	int t_err = 0;
	GifFileType *t_gif = nil;
	ColorMapObject *t_colormap = nil;
	
	MCGIFWriteContext t_context;
	t_context.stream = p_stream;
	t_context.byte_count = 0;

	t_success = nil != (t_gif = EGifOpen(&t_context, gif_writeFunc, &t_err));

	if (t_success)
		t_success = nil != (t_colormap = GifMakeMapObject(t_palette_size, nil));

	if (t_success)
	{
		for (uindex_t i = 0; i < p_indexed->palette_size; i++)
		{
			t_colormap->Colors[i].Red = p_indexed->palette[i].red;
			t_colormap->Colors[i].Green = p_indexed->palette[i].green;
			t_colormap->Colors[i].Blue = p_indexed->palette[i].blue;
		}
		for (uindex_t i = p_indexed->palette_size; i < t_palette_size; i++)
		{
			t_colormap->Colors[i].Red =
				t_colormap->Colors[i].Green =
				t_colormap->Colors[i].Blue = 0;
		}

		if (MCImageIndexedBitmapHasTransparency(p_indexed))
		{
			t_transparent = p_indexed->transparent_index;
			t_colormap->Colors[t_transparent].Red =
				t_colormap->Colors[t_transparent].Green = 
				t_colormap->Colors[t_transparent].Blue = 0xFF;
		}

		t_success = GIF_OK == EGifPutScreenDesc(t_gif, p_indexed->width, p_indexed->height, t_depth, 0, t_colormap);
	}

	if (t_success)
	{
		if (t_transparent != -1)
		{
			GraphicsControlBlock t_gcb;
			MCMemoryClear(&t_gcb, sizeof(t_gcb));
			t_gcb.TransparentColor = t_transparent;

			GifByteType t_extension[4];

			uindex_t t_extension_size;
			t_extension_size = EGifGCBToExtension(&t_gcb, t_extension);

			// Should always be 4 bytes
			MCAssert(t_extension_size == sizeof(t_extension));

			t_success = GIF_OK == EGifPutExtension(t_gif, GRAPHICS_EXT_FUNC_CODE, sizeof(t_extension), t_extension);
		}
	}

	if (t_success)
		t_success = GIF_OK == EGifPutImageDesc(t_gif, 0, 0, p_indexed->width, p_indexed->height, false, nil);

	for (uindex_t y = 0; t_success && y < p_indexed->height; y++)
		t_success = GIF_OK == EGifPutLine(t_gif, (uint8_t*)p_indexed->data + y * p_indexed->stride, p_indexed->width);

	int t_error_code;
	if (GIF_ERROR == EGifCloseFile(t_gif, &t_error_code))
		t_success = false;

	GifFreeMapObject(t_colormap);

	if (t_success)
		r_bytes_written = t_context.byte_count;

	return t_success;
}
Пример #5
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;
}