Does GDI+ have standard image encoder CLSIDs?
Asked Answered
R

3

15

The GDI+ Image::Save method requires a CLSID parameter to specify the encoder to use. The documentation points to some sample code for getting the encoder associated with a particular MIME type, such as image/jpeg or image/png. However I'm balking at the thought of copying a half-page function just to support a 1-line debugging aid where I save an intermediate result out to disk.

Shouldn't there be a list of standard CLSIDs for the standard encoders? Where would I find such a list? I haven't been able to find one by searching Microsoft's include files.

Reenter answered 17/3, 2011 at 21:59 Comment(0)
P
9

There isn't one. I think they intended the codec list to be extensible and support plugins, but never got around to it. Given that they haven't made any changes to GDI+ in quite some time, they likely won't anytime soon. You could probably get away with generating your own hard coded list based on an enumeration of Gdiplus::GetImageEncoders.

That is:

image/bmp  : {557cf400-1a04-11d3-9a73-0000f81ef32e}
image/jpeg : {557cf401-1a04-11d3-9a73-0000f81ef32e} 
image/gif  : {557cf402-1a04-11d3-9a73-0000f81ef32e} 
image/tiff : {557cf405-1a04-11d3-9a73-0000f81ef32e}
image/png  : {557cf406-1a04-11d3-9a73-0000f81ef32e}

Here's the function I routinely cut&paste between projects for getting at the CLSID of the encoder. You could modify it to be a table lookup.

#include <windows.h>
#include <gdiplus.h>
#include <string>
#include <vector>

HRESULT GetGdiplusEncoderClsid(const std::wstring& format, GUID* pGuid)
{
    HRESULT hr = S_OK;
    UINT  nEncoders = 0;          // number of image encoders
    UINT  nSize = 0;              // size of the image encoder array in bytes
    std::vector<BYTE> spData;
    Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
    Gdiplus::Status status;
    bool found = false;

    if (format.empty() || !pGuid)
    {
        hr = E_INVALIDARG;
    }

    if (SUCCEEDED(hr))
    {
        *pGuid = GUID_NULL;
        status = Gdiplus::GetImageEncodersSize(&nEncoders, &nSize);

        if ((status != Gdiplus::Ok) || (nSize == 0))
        {
            hr = E_FAIL;
        }
    }

    if (SUCCEEDED(hr))
    {

        spData.resize(nSize);
        pImageCodecInfo = (Gdiplus::ImageCodecInfo*)&spData.front();
        status = Gdiplus::GetImageEncoders(nEncoders, nSize, pImageCodecInfo);

        if (status != Gdiplus::Ok)
        {
            hr = E_FAIL;
        }
    }

    if (SUCCEEDED(hr))
    {
        for (UINT j = 0; j < nEncoders && !found; j++)
        {
            if (pImageCodecInfo[j].MimeType == format)
            {
                *pGuid = pImageCodecInfo[j].Clsid;
                found = true;
            }
        }

        hr = found ? S_OK : E_FAIL;
    }

    return hr;
}


int main()
{
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    GUID guidBmp = {};
    GUID guidJpeg = {};
    GUID guidGif = {};
    GUID guidTiff = {};
    GUID guidPng = {};

    GetGdiplusEncoderClsid(L"image/bmp", &guidBmp);
    GetGdiplusEncoderClsid(L"image/jpeg", &guidJpeg);
    GetGdiplusEncoderClsid(L"image/gif", &guidGif);
    GetGdiplusEncoderClsid(L"image/tiff", &guidTiff);
    GetGdiplusEncoderClsid(L"image/png", &guidPng);

    return 0;
}
Polyclinic answered 17/3, 2011 at 22:25 Comment(5)
These CLSIDs do not exist in registry. The only codec CLSIDs I could find that actually exist in the Registry turned out to be for Windows Imaging Component (WIC) codecs, rather than GDI+ codecs. Does GDI+'s Image::Save() map the encoder CLSID it receives to an actual encoder using an hard-coded list?Om
Why does it matter if the GUIDs are in the registry?Polyclinic
If they were in the registry's CLSID key, it would mean they can be used from outside of GDI+ like ordinary COM classes (that's what a CLSID means, after all). But more importantly I'd have expected a key referencing all codecs, where codecs can be added for extensibility. Unfortunately it seems GDI+ can't be extended with new codecs, only WIC can.Om
It has been 13 years since I asked this question and you answered. I think I stopped using GDI+ shortly after. Have there been any changes in all those years? P.S. Thank you, your answer helped greatly at the time.Reenter
@MarkRansom - I am not aware of any further evolvement of GDI+. Most all new Windows APIs seems to be implemented in XAML, WinRT, Windows RunTime, UWP, etc... I've heard reports of modern codecs being supported in Windows, but it's not clear to me how those are exposed to application developers.Polyclinic
A
5

If you just want to write a PNG, this appears to work:

// image/png  : {557cf406-1a04-11d3-9a73-0000f81ef32e}
const CLSID pngEncoderClsId = { 0x557cf406, 0x1a04, 0x11d3,{ 0x9a,0x73,0x00,0x00,0xf8,0x1e,0xf3,0x2e } }; 
stat = image->Save(L"test.png", &pngEncoderClsId, NULL);

Note the reformatting of the hex values.

From: How to initialize a constant CLSID

Arciniega answered 17/10, 2015 at 0:25 Comment(1)
GetEncoderClsid(L"image/png", ...) didn't work for some reason, but the this worked just fine.Feaze
E
3

You will probably want to use ImageCodecInfo with GetImageEncodersSize() and GetImageEncoders() I'm not aware of any easier way.

EDIT: If you know specifically what you want and damn all the rest you can get away with doing something like this ...

CLSID pngClsid;
GetEncoderClsid("image/png", &pngClsid);
image.Save("imagename.png", &pngClsid, NULL);
Eyeleteer answered 17/3, 2011 at 22:10 Comment(1)
That's exactly what I tried to do, but GetEncoderClsid isn't part of GDI+ - you have to copy it from the documentation.Reenter

© 2022 - 2024 — McMap. All rights reserved.