Michael Chourdakis
Michael Chourdakis

Reputation: 11158

Encoding animated gif with WIC Imaging Component -> Infinite loop

I have a std::vector<> of IWICBitmapSource and trying to create an animated gif:

void SaveGif()
{
    CComPtr<IWICBitmapEncoder> wicBitmapEncoder;
    auto hr =
        fact->CreateEncoder(
            GUID_ContainerFormatGif,
            nullptr,    // No preferred codec vendor.
            &wicBitmapEncoder
        );
    VectorStream stream;
    hr =
        wicBitmapEncoder->Initialize(
            &stream,
            WICBitmapEncoderNoCache
        );
    for (auto& gox : Gifs)
    {
        CComPtr<IWICBitmapFrameEncode> wicFrameEncode;
        hr =
            wicBitmapEncoder->CreateNewFrame(
                &wicFrameEncode,
                0
            );
        hr =
            wicFrameEncode->Initialize(bag2);
        auto g2 = GUID_WICPixelFormat24bppRGB;
        wicFrameEncode->SetPixelFormat(&g2);

        WICRect wr = {};
        UINT wi, he;
        gox.second->GetSize(&wi,&he);
        wr.Width = wi;
        wr.Height = he;
        hr = wicFrameEncode->WriteSource(gox.second, &wr);
        hr = wicFrameEncode->Commit();
    }
 // save stream to gif file
}

All functions succeed, the final file is animating, but not looping. It stops at the last frame.

How can I tell it to loop indefinitely?

Upvotes: 0

Views: 536

Answers (2)

Pruyque
Pruyque

Reputation: 382

Using the links in Simon Mourier's comment, I came up with this snippet and it seems to be working (for my single testcase):

CComPtr<IWICMetadataQueryWriter> meta; // Get a metadata writer
wicBitmapEncoder->GetMetadataQueryWriter(&meta);
PROPVARIANT app = { 0 };
PROPVARIANT data = { 0 };
const char *appext = "NETSCAPE2.0"; // application that defined the extension
struct LoopCount // the data that is needed for the extension (see Simon's links)
{
    unsigned char block_size = 3;
    unsigned char block_id = 1;
    unsigned short loopcount = 0;
} bData;

// Setup the PROPVARIANTS
PropVariantInit(&app);
PropVariantInit(&data);
data.vt = VT_UI1|VT_VECTOR;
app.vt = VT_UI1|VT_VECTOR;
app.cac.cElems = strlen(appext);
app.cac.pElems = (char *)appext;

data.cac.cElems = sizeof(LoopCount);
data.cac.pElems = (char *)&bData;

meta->SetMetadataByName(L"/appext/application", &app); // Write the metadata
meta->SetMetadataByName(L"/appext/data", &data);

Upvotes: 1

Rita Han
Rita Han

Reputation: 9700

All functions succeed, the final file is animating, but not looping. It stops at the last frame.

How can I tell it to loop indefinitely?

By default, the loop count is 1. As you already found that it only displays from the first frame to the last one then stops.

In order to make it loop indefinitely you need to set the loop count to 0 which specifies that the animation should be displayed infinitely.

The following is an example of creating an indefinitely loop animated gif in two steps using GDI+ you can refer to.

First, create multiple gif images. Every gif image has one frame. Set loop count for per gif image.

// Get the CLSID of the GIF encoder.
CLSID encoderClsid;
GetEncoderClsid(L"image/gif", &encoderClsid);

Image* singleFrameGif = new Image(fileName);

// Set the loop count.
PropertyItem* propItemLoopCount = new PropertyItem;
SHORT loopCount = 0; //A value of 0 specifies that the animation should be displayed infinitely.

propItemLoopCount->id = PropertyTagLoopCount;
propItemLoopCount->length = sizeof(loopCount);
propItemLoopCount->type = PropertyTagTypeShort;
propItemLoopCount->value = &loopCount;

singleFrameGif->SetPropertyItem(propItemLoopCount);

//Save this image to a file.
singleFrameGif->Save(newName, &encoderClsid, NULL);

Second, add created gif images to one file to make up of a multiple-frames gif image.

EncoderParameters encoderParameters;
ULONG             parameterValue;
Status            stat;

// An EncoderParameters object has an array of
// EncoderParameter objects. In this case, there is only
// one EncoderParameter object in the array.
encoderParameters.Count = 1;

// Initialize the one EncoderParameter object.
encoderParameters.Parameter[0].Guid = EncoderSaveFlag;
encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;
encoderParameters.Parameter[0].Value = &parameterValue;

// Create two image objects.
Image* multi = new Image(L"1.gif");

// Save the first page (frame).
parameterValue = EncoderValueMultiFrame;
stat = multi->Save(L"MultiFrame.gif", &encoderClsid, &encoderParameters);
if (stat == Ok)
    printf("Page 1 saved successfully.\n");

Image* page2 = new Image(L"2.gif");

// Save the second page (frame).
parameterValue = EncoderValueFrameDimensionTime;
stat = multi->SaveAdd(page2, &encoderParameters);
if (stat == Ok)
    printf("Page 2 saved successfully.\n");

// Close the multiframe file.
parameterValue = EncoderValueFlush;
stat = multi->SaveAdd(&encoderParameters);
if (stat == Ok)
    printf("File closed successfully.\n");

More reference: "Creating and Saving a Multiple-Frame Image".

Upvotes: 1

Related Questions