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
How to Find If a Variable Is Allocated in Stack or Heap
Why Do I Need to Include Both the iOStream and Fstream Headers to Open a File
Const and Non Const Template Specialization
Nested Class' Access to Enclosing Class' Private Data Members
What Are the Advantages and Disadvantages of Implementing Classes in Header Files
Understanding How Lambda Closure Type Has Deleted Default Constructor
Freopen() Equivalent for C++ Streams
Openal: How to Create Simple "Microphone Echo" Programm
What Standard Clause Mandates This Lvalue-To-Rvalue Conversion
_Glibcxx_Use_Cxx11_Abi, Gcc 4.8 and Abi Compatibility
Potential Problem in "Swapping Values of Two Variables Without Using a Third Variable"
C++ Dynamic Array Initialization with Declaration
What Is the Cin Analougus of Scanf Formatted Input
Multiple "Could Not Be Resolved" Problems Using Eclipse with Mingw