Does Gdi+ Have Standard Image Encoder Clsids

Does GDI+ have standard image encoder CLSIDs?

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

Saving a JPG from a BMP using GDI+ with a set DPI

A modified version of the function SaveFile() solved the problem:

VOID SaveFile()
{
// Initialize GDI+.
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

CLSID encoderClsid;
Status stat;
EncoderParameters encoderParameters;
ULONG quality;

Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(L"plot.bmp");
Gdiplus::REAL dpi = 72;
bitmap->SetResolution(dpi,dpi);

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

encoderParameters.Count = 1;
encoderParameters.Parameter[0].Guid = EncoderQuality;
encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;

quality = 100;
encoderParameters.Parameter[0].Value = &quality;

stat = bitmap->Save(L"plot.jpg", &encoderClsid, &encoderParameters);

if (stat == Ok)
printf("plot.jpg was saved successfully\n");
else
printf("Failure: stat = %d\n", stat);

delete bitmap;
GdiplusShutdown(gdiplusToken);
return;
}

GDI+ Image::SetPropertyItem not working as expected

You code works fine, but you must save the image back, for example like this

...
newImage->SetPropertyItem(propItem);

CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
newImage->Save(L"Test2.jpg", &clsid);
...

BOOL GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0;
UINT size = 0;
ImageCodecInfo* info = NULL;

ZeroMemory(pClsid, sizeof(CLSID));
GetImageEncodersSize(&num, &size);
if (size == 0)
return FALSE;

info = (ImageCodecInfo*)(malloc(size));
if (info == NULL)
return FALSE;

GetImageEncoders(num, size, info);
for (UINT j = 0; j < num; ++j)
{
if (!wcscmp(info[j].MimeType, format))
{
*pClsid = info[j].Clsid;
free(info);
return TRUE;
}
}

free(info);
return FALSE;
}

How to save an image to BMP/PNG/JPG with GDI+?

Seems I was using wrong GUID-s. These are correct:

gGIf: TGUID = '{557CF402-1A04-11D3-9A73-0000F81EF32E}';
gPNG: TGUID = '{557CF406-1A04-11D3-9A73-0000F81EF32E}';
gPJG: TGUID = '{557CF401-1A04-11D3-9A73-0000F81EF32E}';
gBMP: TGUID = '{557CF400-1A04-11D3-9A73-0000F81EF32E}';
gTIF: TGUID = '{557CF405-1A04-11D3-9A73-0000F81EF32E}';

Seems the previous ones are only for reading:

Program gets stuck after taking screenshot

As @xsoftie, You are hanging in the image::~image().

According to the GdiplusShutdown :

you must delete all of your GDI+ objects (or have them go out of
scope) before you call GdiplusShutdown.

Gdiplus::Bitmap bitmap(hBitmap, NULL);

bitmap are stored on the stack, and the system automatically releases them at the end of their life cycle (after {}). scoftie's method is one of feasible solutions.

Of course you can also use new to request GDI + object pointers on the heap, as this sample:

Gdiplus::Bitmap *bitmap = new Gdiplus::Bitmap(hBitmap, NULL);
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
bitmap->Save(L"D:\\Pictures\\screen.png", &pngClsid, NULL);

//...
delete bitmap;
GdiplusShutdown(gdiplusToken);

Or, put GdiplusStartup and GdiplusShutdown in the main function:

int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
screenshot();
changewallpaper();
GdiplusShutdown(gdiplusToken);
}

And in addition, When you no longer need the hScreenDC, call the DeleteDC function.

How to construct a GDI+ Bitmap object from a Device-Dependent HBITMAP

First (and this is unrelated to your main question):

When creating a bitmap for screen shot, don't use a memory dc because that creates a monochrome bitmap. That's the main reason you are getting a black and white image (on my computer I just get a black image).

Don't use GetDC(0) inside another function. Every call to GetDC match have a matching ReleaseDC to avoid resource leak.

After calling BitBlt it is good practice to select hbitmap out of dc because you are basically finished drawing on dc.

The following code will work on Windows 10

int w = 800;
int h = 600;

HDC hdc = GetDC(HWND_DESKTOP);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, w, h);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
BitBlt(memdc, 0, 0, w, h, hdc, 0, 0, SRCCOPY | CAPTUREBLT);
SelectObject(memdc, oldbmp);

Bitmap(hbitmap, NULL).Save(filename, &pngEncoder);

DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(HWND_DESKTOP, hdc);

Back to your question regarding the documentation:

Type: HPALETTE

Handle to a GDI palette used to define the bitmap colors if hbm is not a device-independent bitmap (DIB).

In addition,

Do not pass to the Bitmap::FromHBITMAP method a GDI bitmap or a GDI palette that is currently (or was previously) selected into a device context.

The code I posted obeys only one rule, that GDI bitmap is not currently selected in to a device context (but it was previously selected).

The documentation may apply to older versions of Windows. As far as I can see MFC's CImage class does not follow all these rules. New computer displays are all 24 or 32 bit, I don't know how you would get a palette for it.

To follow the documentation to the letter, you can convert DDB to DIB section, using CreateDIBSection and GetDIBits. Use the new DIB section hbitmap_dib in Bitmap::FromHBITMAP. This will satisfy all of the conditions: hbitmap is dib, it is not (and was not) selected in to a device context.

Or, Gdiplus::Bitmap has another method Bitmap::FromBITMAPINFO. If there is no palette, you can use this code instead:

HDC hdc = GetDC(HWND_DESKTOP);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, w, h);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
BitBlt(memdc, 0, 0, 800, 600, hdc, 0, 0, SRCCOPY | CAPTUREBLT);
SelectObject(memdc, oldbmp);

BITMAP bm;
GetObject(hbitmap, sizeof(bm), &bm);
int size = ((bm.bmWidth * bm.bmBitsPixel + 31) / 32) * 4 * bm.bmHeight;
BITMAPINFO info{ sizeof(info), bm.bmWidth, bm.bmHeight, 1, bm.bmBitsPixel, BI_RGB, size };
std::vector<char> bits(size);
GetDIBits(memdc, hbitmap, 0, bm.bmHeight, &bits[0], &info, DIB_RGB_COLORS);

Bitmap *bitmap = Bitmap::FromBITMAPINFO(&info, &bits[0]);
bitmap->Save(filename, &pngEncoder);
delete bitmap;

DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(HWND_DESKTOP, hdc);

Why won't my Gdiplus::Bitmap save a file

The answer was a combination of all of your answers. one of my octets was wrong in my CLSID, The output path wasn't within reach of the current user. And the input parameters to my Bitmap Constructor required that the Stride parameter(The one I shove image.step1() into) is divisible by 4. The status error code I recieved of 2 was carried over from my Bitmap Constructor



Related Topics



Leave a reply



Submit