Page 1 of 1

Handling / creating a GIF animation with MagickWand

Posted: 2011-09-06T17:01:18-07:00
by djense2000
Well, actually it's not really like creating a GIF animation, but close...
I already have a GIF animation and most of the layers have transparency and so when displayed, each frame is composed with the previous layers, the first is a white background of the size of the overall image (I hope I am explaining this right ?).

In my code, I am just trying to reconstruct those layers to have the same GIF animation, but where all layers are completely defined (they have all the same size, no transparency...etc...).

Cutting the story short, here is my code :

Code: Select all

void reconstruct(char *filename) {
    MagickBooleanType status;
    int num;
    PixelIterator * iterator, * orig_iterator;
    PixelWand ** pixels, ** orig_pixels;
    PixelWand *bg_pxl_wnd;
    size_t width;
    size_t height;
    int x,y,frame, tmp_frame;
    int sx,sy, offset_x, offset_y;
    char *identity;
    char *pointer, *end_pointer, tmp[64];

    unsigned int alpha;
    PixelPacket color;

    individual_layers = NewMagickWand();

    status = MagickReadImage(individual_layers, filename);
    if (status == MagickFalse) {
        fprintf(stderr, "Could not open file %s\n", filename);
        exit (-1);
    }

    num = MagickGetNumberImages(individual_layers);
    num_frames = num;

    width=MagickGetImageWidth(individual_layers);
    height=MagickGetImageHeight(individual_layers);

    bg_pxl_wnd = NewPixelWand();
    PixelSetColor(bg_pxl_wnd, "white");

    // reconstruct the different frames
    reconstructed_images = NewMagickWand();

    // for each frame of the final reconstructed image
    for (frame = 0; frame < num; frame ++) {
        fprintf(stderr, "reconstructing frame %d\n", frame);

        MagickNewImage(reconstructed_images, width, height, bg_pxl_wnd);
        MagickNextImage(reconstructed_images);

        MagickResetIterator(individual_layers);

        // go through all the previous frames
        for (tmp_frame = 0; tmp_frame <= frame; tmp_frame ++) {
            MagickNextImage(individual_layers);
            // find the offset of the current layer
            identity = MagickIdentifyImage(individual_layers);

            // get the offset
            pointer = strstr(identity, "Page geometry:");
            end_pointer = strstr(pointer, "\n");
            strncpy(tmp, pointer, end_pointer - pointer);
            tmp[end_pointer-pointer]='\0';

            sscanf(tmp, "Page geometry: %dx%d+%d+%d", &x,&y, &offset_x,&offset_y);

            // get the size
            pointer = strstr(identity, "Geometry:");
            end_pointer = strstr(pointer, "\n");
            strncpy(tmp, pointer, end_pointer - pointer);
            tmp[end_pointer-pointer]='\0';

            sscanf(tmp, "Geometry: %dx%d+%d+%d", &sx,&sy, &x,&y);

            iterator = NewPixelRegionIterator(reconstructed_images, offset_x, offset_y, sx, sy);
            orig_iterator = NewPixelIterator(individual_layers);
            for (y=0; y<sy; y++) {
                orig_pixels = PixelGetNextIteratorRow(orig_iterator, &sx);
                pixels = PixelGetNextIteratorRow(iterator, &sx);

                for (x=0; x<sx; x++) {
                    alpha = (unsigned int) (255*PixelGetAlpha(pixels[x]));
                    if (alpha == 0)
                        continue;

                    //fprintf(stderr, "Getting color of pixel %d,%d for reconstructing frame %d with frame %d, offset %d,%d, size %d,%d\n",
                            //x,y,frame, tmp_frame, offset_x, offset_y, sx, sy);
                    PixelGetQuantumColor(orig_pixels[x],&color);
                    PixelSetQuantumColor(pixels[x], &color);
                }
            }
            PixelSyncIterator(iterator);

            DestroyPixelIterator(iterator);
            DestroyPixelIterator(orig_iterator);
        }
    }

    //printf("Read %d layers in %s\n", MagickGetNumberImages(reconstructed_images), filename);
    //MagickDisplayImages(reconstructed_images, "");
    MagickWriteImage(reconstructed_images, "recon.gif");
}
///The problem I have at this point is that it seems that even though I am calling MagickNewImage at each loop, the result wand (reconstructed_wand) only has one image/layer in it.... ?///
(Edit) Actually, this is not correct, the MagickGetNumberImages reports the right number of images in the wand... (/edit)

Not sure I am calling the right API ? Also, if there is any comment on the rest, I am just starting with all this (parsing the string for the Identity does not seem the most efficient, but that's the best I have found...?)

Oh, and if it is important, here is the version I use :
Version: ImageMagick 6.6.2-6 2011-03-16 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2010 ImageMagick Studio LLC
Features: OpenMP

That's on Linux (Ubuntu 11.04).

Thanks

Re: Handling / creating a GIF animation with MagickWand

Posted: 2011-09-07T17:14:05-07:00
by djense2000
There was several problems....
1) create the image correctly :

MagickNewImage(reconstructed_images, width, height, bg_pxl_wnd);
MagickSetIteratorIndex(reconstructed_images, frame);
MagickSetImagePage(reconstructed_images, width, height, 0,0);

2) The call to PixelSyncIterator(iterator); should be one level higher.

3) And using MagickWriteImage is really not the easy way to check the result (I imagine, would need to set properly the GIF, durations of the frames...etc...). Instead just use :

MagickResetIterator(reconstructed_images);
MagickDisplayImages(reconstructed_images, "");