0

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?

Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78
  • *"However no animation takes place, it just shows a static image."* - What tool, application, or procedure did you use to verify this? – IInspectable Jul 17 '20 at 14:35
  • Windows photo viewer, I also uploaded it to facebook, I also tried it in Photoshop which can't even open it. – Michael Chourdakis Jul 17 '20 at 14:38
  • @IInspectable now it's working (instead of caching the Wics, I put them on creation) any idea how to make the gif loop? (Edited the q) – Michael Chourdakis Jul 17 '20 at 14:49
  • To my knowledge, looping isn't part of the core specification for animated GIFs, though I don't know whether WIC supports this non-standard extension. – IInspectable Jul 17 '20 at 15:41
  • 3
    You must write an application extension to the GIF http://www.vurdalakov.net/misc/gif/netscape-looping-application-extension in WIC it means you write metadata, exactly the reverse of this: https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/multimedia/wic/wicanimatedgif/WicAnimatedGif.cpp#L675 – Simon Mourier Jul 17 '20 at 16:12
  • I don't have writing code either, but you can look at [this](https://github.com/microsoft/DirectXTex/blob/master/Texassemble/AnimatedGif.cpp) for an example of how the metadata is read via WIC and reconstructed back into frames. – Chuck Walbourn Jul 19 '20 at 18:37

2 Answers2

1

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".

Rita Han
  • 9,574
  • 1
  • 11
  • 24
  • 1
    I asked about WIC, not GDI+. – Michael Chourdakis Jul 22 '20 at 07:12
  • @MichaelChourdakis Based on [GIF Metadata](https://learn.microsoft.com/en-us/windows/win32/wic/-wic-native-image-format-metadata-queries#gif-metadata) document of WIC, there is no such *loop count* defined. And *loop count* is not defined in GIF standard. So a workaround maybe using other valid tool to set this value or use GDI+ instead. – Rita Han Jul 22 '20 at 08:56
1

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);
Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78
Pruyque
  • 372
  • 1
  • 5