使用 WIC 成像组件编码动画 gif -> 无限循环

Encoding animated gif with WIC Imaging Component -> Infinite loop

我有一个 std::vector<> 的 IWICBitmapSource 并试图创建一个动画 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?

默认情况下,循环次数为1。正如您已经发现的那样,它只显示从第一帧到最后一帧然后停止。

为了让它无限循环,你需要将循环计数设置为0,它指定动画应该无限显示。

以下是使用GDI+分两步创建无限循环动画gif的例子,您可以参考。

首先,制作多张gif图片。每个 gif 图像都有一个框架。为每个 gif 图像设置循环计数。

// 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);

其次,将创建的gif图片添加到一个文件中,组成多帧gif图片。

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");

更多参考:“Creating and Saving a Multiple-Frame Image”。

使用 Simon Mourier 评论中的链接,我想出了这个片段并且它似乎有效(对于我的单个测试用例):

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);