Ian Pringle
Ian Pringle

Reputation: 364

Manipulating gifs frame by frame in C with libgd

I have a program I use to manipulate various image files. Currently it only works with static images. I want to provide support for gifs and I've been looking through the lackluster documentation but cannot find any reference to how to manipulate gifs frame by frame.

Ideally I'd like to work through the gif one frame at a time, using my other functions to manipulate each of the frames in a file and then repackage the image.

Upvotes: 1

Views: 195

Answers (1)

José Manuel Ramos
José Manuel Ramos

Reputation: 355

If you look carefully to the source code, you can easily find a weak implementation when using animated frames.

The code does not lie: when you call gdImageCreateFromGif(FILE *fdFile) or gdImageCreateFromGifPtr (int size, void *data), it calls directly to gdImageCreateFromGifCtx(gdIOCtxPtr fd), so you must read'n'search in that function where is the mess. The documentation has no info about it, because this library is very, very old.

In the source code (fd06f7f on 22 Jun) line 258 (gdImageCreateFromGifCtx(gdIOCtxPtr fd)):

for (;;) {
    int top, left;
    int width, height;

    if(!ReadOK(fd, &c, 1)) {
        return 0;
    }

    if (c == ';') { /* GIF terminator */
        goto terminated;
    }

    if(c == '!') { /* Extension */
        if(!ReadOK(fd, &c, 1)) {
            return 0;
        }

        DoExtension(fd, c, &Transparent, &ZeroDataBlock);
        continue;
    }

    if(c != ',') { /* Not a valid start character */
        continue;
    }

    /*1.4//++imageCount; */

    if(!ReadOK(fd, buf, 9)) {
        return 0;
    }

    useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);

    bitPixel = 1 << ((buf[8] & 0x07) + 1);
    left = LM_to_uint(buf[0], buf[1]);
    top = LM_to_uint(buf[2], buf[3]);
    width = LM_to_uint(buf[4], buf[5]);
    height = LM_to_uint(buf[6], buf[7]);

    if(((left + width) > screen_width) || ((top + height) > screen_height)) {
        if(VERBOSE) {
            printf("Frame is not confined to screen dimension.\n");
        }
        return 0;
    }

    if(!(im = gdImageCreate(width, height))) {
        return 0;
    }

    im->interlace = BitSet(buf[8], INTERLACE);
    if(!useGlobalColormap) {
        if(ReadColorMap(fd, bitPixel, localColorMap)) {
            gdImageDestroy(im);
            return 0;
        }

        ReadImage(im, fd, width, height, localColorMap, BitSet(buf[8], INTERLACE), &ZeroDataBlock);
    } else {
        if(!haveGlobalColormap) {
            gdImageDestroy(im);
            return 0;
        }

        ReadImage(im, fd, width, height, ColorMap, BitSet(buf[8], INTERLACE), &ZeroDataBlock);
    }

    if(Transparent != (-1)) {
        gdImageColorTransparent(im, Transparent);
    }

    goto terminated;
}

That goto finishes the loop without attempt to read another frame, even.

That's because this implementation is intended to read explicitly one frame from a gif. Only one.

I worked with this library. Has other flaws, too. The transparency for PNG files are 7 bits, not 8 bits (one of the main reasons I used it and discard it later).

You can pack animated gifs with gdImageGifAnimBegin(gdImagePtr im, FILE *outFile, int GlobalCM, int Loops)

[TL;DR]: libgd has no multiple frames option when loading. It's not intended to load animated gifs.

Upvotes: 1

Related Questions