GDI+ - converting bitmap pixels to JPG format - c++

I have an array of a bitmap pixels. How can I convert them to JPG format and copy to another array? How to convert them back to bitmap from JPG pixels?

Checkout the Encoder CLSID function from here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms533843(v=vs.85).aspx
Modified original code from https://vctipsplusplus.wordpress.com/tag/image-conversion-gdi/:
int main()
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
CLSID encoderClsid;
Status stat;
Image* image = new Image(L”Bird.bmp”);
// Get the CLSID of the JPEG encoder.
GetEncoderClsid(L”image/jpeg”, &encoderClsid);
stat = image->Save(L”Bird.png”, &encoderClsid, NULL);
if(stat == Ok)
printf(“Bird.png was saved successfully\n”);
else
printf(“Failure: stat = %d\n”, stat);
delete image;
GdiplusShutdown(gdiplusToken);
return 0;
}
Just change image/jpeg to whatever format you want to convert. The details are given on the MSDN link I mentioned above. Of course to work with the pixels, you'll need to convert the JPEG to BMP

You can create a memory stream using CreateStreamOnHGlobal or SHCreateMemStream, then use the GDI+ method Image.Save to save to the stream. Reverse the process to read back in.

Related

WICConvertBitmapSource + CopyPixels results in blue image

I'm trying to use WIC to load an image into an in-memory buffer for further processing then write it back to a file when done. Specifically:
Load the image into an IWICBitmapFrameDecode.
The loaded IWICBitmapFrameDecode reports that its pixel format is GUID_WICPixelFormat24bppBGR. I want to work in 32bpp RGBA, so I call WICConvertBitmapSource.
Call CopyPixels on the converted frame to get a memory buffer.
Write the memory buffer back into an IWICBitmapFrameEncode using WritePixels.
This results in a recognizable image, but the resulting image is mostly blueish, as if the red channel is being interpreted as blue.
If I call WriteSource to write the converted frame directly, instead of writing the memory buffer, it works. If I call CopyPixels from the original unconverted frame (and update my stride and pixel formats accordingly), it works. It's only the combination of WICConvertBitmapSource plus the use of a memory buffer (CopyPixels + WritePixels) that causes the problem, but I can't figure out what I'm doing wrong.
Here's my code.
int main() {
IWICImagingFactory *pFactory;
IWICBitmapDecoder *pDecoder = NULL;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
(LPVOID*)&pFactory
);
// Load the image.
pFactory->CreateDecoderFromFilename(L"input.png", NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &pDecoder);
IWICBitmapFrameDecode *pFrame = NULL;
pDecoder->GetFrame(0, &pFrame);
// pFrame->GetPixelFormat shows that the image is 24bpp BGR.
// Convert to 32bpp RGBA for easier processing.
IWICBitmapSource *pConvertedFrame = NULL;
WICConvertBitmapSource(GUID_WICPixelFormat32bppRGBA, pFrame, &pConvertedFrame);
// Copy the 32bpp RGBA image to a buffer for further processing.
UINT width, height;
pConvertedFrame->GetSize(&width, &height);
const unsigned bytesPerPixel = 4;
const unsigned stride = width * bytesPerPixel;
const unsigned bitmapSize = width * height * bytesPerPixel;
BYTE *buffer = new BYTE[bitmapSize];
pConvertedFrame->CopyPixels(nullptr, stride, bitmapSize, buffer);
// Insert image buffer processing here. (Not currently implemented.)
// Create an encoder to turn the buffer back into an image file.
IWICBitmapEncoder *pEncoder = NULL;
pFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &pEncoder);
IStream *pStream = NULL;
SHCreateStreamOnFileEx(L"output.png", STGM_WRITE | STGM_CREATE, FILE_ATTRIBUTE_NORMAL, true, NULL, &pStream);
pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
IWICBitmapFrameEncode *pFrameEncode = NULL;
pEncoder->CreateNewFrame(&pFrameEncode, NULL);
pFrameEncode->Initialize(NULL);
WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat32bppRGBA;
pFrameEncode->SetPixelFormat(&pixelFormat);
pFrameEncode->SetSize(width, height);
pFrameEncode->WritePixels(height, stride, bitmapSize, buffer);
pFrameEncode->Commit();
pEncoder->Commit();
pStream->Commit(STGC_DEFAULT);
return 0;
}
The PNG encoder only supports GUID_WICPixelFormat32bppBGRA (BGR) for 32bpp as specified in PNG Native Codec official documentation. When you call it with GUID_WICPixelFormat32bppRGBA, it will not do channel switching. The pervert will just use your pixels as they were BGR, not RGB, and will not tell you there's a problem.
I don't know what you're trying to do, but in your example, you could just replace GUID_WICPixelFormat32bppRGBA by GUID_WICPixelFormat32bppBGRA in the call to WICConvertBitmapSource (and also replace the definition of the last pixelFormat variable to make sure your source code is correct, but it doesn't change anything).
PS: you can use Wic to save files, not need to create stream using another API, see my answer here: Capture screen using DirectX

save jpeg from hBitmap to BYTE array [duplicate]

I've got a HBITMAP that I want to save into a JPEG/PNG stream or array of bytes. The problem is that I'm using mingw as my compiler so I can't use CImage.. which would have made my life easier.
I can get the pixels from the bitmap without any problems, but I have no idea how to get access to them in JPEG/PNG-format.
Where do I start?
If you have access DirectX library you may use IStream to convert your image to JPEG
http://msdn.microsoft.com/en-us/library/windows/desktop/aa380034(v=vs.85).aspx
or if you have GDI+ something like this might work
Gdiplus::Bitmap bmp(hbmpImage,(HPALETTE)0);
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
bmp.Save(L"D:\image.png",&pngClsid,NULL);
where GetEncoderLCLsid looks like this:
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
don't forget to initialize GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
if don't have access to either you may use libjpeg but you need to put all the dependencies packages from the GnuWin32 site. Much faster the code in this page should work, just forget about the boost
libjpeg dying without message
Another option is to use the WIC API (Windows Imaging Component), which gives direct access to image encoders and decoders. I believe GDI+ may use this under the covers.

PNG/JPG file to HBITMAP with GDI+

I need to load an image as a HBITMAP in C++ so I can send it to a printer.
I've tried using Gdiplus::Bitmap::FromFileand then Gdiplus::Bitmap->GetHBITMAP however the HBITMAP returned is still empty.
I can load .bmp files fine with LoadImage() but this doesn't work with PNG files.
Could anyone point me to the correct way of implementing this to work with .png (and more, if possible) file types?
EDIT: CODE
ZeroMemory(&hBitmap, sizeof(HBITMAP));
Gdiplus::Bitmap* gpBitmap = Gdiplus::Bitmap::FromFile(L"img.png");
gpBitmap->GetHBITMAP(0, &hBitmap);
EDIT 2: Solution found
HBITMAP GetHBITMAPFromImageFile(WCHAR* pFilePath)
{
GdiplusStartupInput gpStartupInput;
ULONG_PTR gpToken;
GdiplusStartup(&gpToken, &gpStartupInput, NULL);
HBITMAP result = NULL;
Gdiplus::Bitmap* bitmap = Gdiplus::Bitmap::FromFile(pFilePath, false);
if (bitmap)
{
bitmap->GetHBITMAP(Color(255, 255, 255), &result);
delete bitmap;
}
GdiplusShutdown(gpToken);
return result;
}

Rotating a CImage and preserving its alpha/transparency channel

I have some existing code that uses a CImage which has an alpha channel, and I need to rotate it.
I have found the following suggestion which converts the CImage to a GDI+ Bitmap and then rotates it, and the rotated result ends up back in the CImage.
Bitmap* gdiPlusBitmap=Bitmap::FromHBITMAP(atlBitmap.Detach());
gdiPlusBitmap->RotateFlip(Rotate90FlipNone);
HBITMAP hbmp;
gdiPlusBitmap->GetHBITMAP(Color::White, &hbmp);
atlBitmap.Attach(hbmp);
Apparently it works without actually copying the bitmap bytes, which is great, but the problem is that if you create a Bitmap object from a HBITMAP it throws away the alpha channel.
Apparently to preserve the alpha channel you must instead create the Bitmap using the constructor
Bitmap(
[in] INT width,
[in] INT height,
[in] INT stride,
[in] PixelFormat format,
[in] BYTE *scan0
);
So I'm trying to adapt the above to use this constructor, but the interaction between CImage and Bitmap is a bit confusing. I think I need to create the Bitmap like this
Bitmap* gdiPlusBitmap = new Bitmap(
pCImage->GetWidth(),
pCImage->GetHeight(),
pCImage->GetPitch(),
PixelFormat32bppARGB,
(BYTE *)pCImage->GetBits());
nGDIStatus = gdiPlusBitmap->RotateFlip(Rotate90FlipNone);
but I'm not sure how to make the CImage pick up the changes (so that I end up with the original CImage rotated), or where to delete the Bitmap object.
Does anyone know the correct way to do this, preserving the alpha channel ?
Ideally I'd like to avoid copying the bitmap data, but it's not mandatory.
You can use Gdiplus::Graphics to draw bitmap on CImage.
Note, hard coding PixelFormat32bppARGB can cause problems if image doesn't support alpha channel. I added some basic error check.
CImage image;
if (S_OK != image.Load(L"c:\\test\\test.png"))
{
AfxMessageBox(L"can't open");
return 0;
}
int bpp = image.GetBPP();
//get pixel format:
HBITMAP hbmp = image.Detach();
Gdiplus::Bitmap* bmpTemp = Gdiplus::Bitmap::FromHBITMAP(hbmp, 0);
Gdiplus::PixelFormat pixel_format = bmpTemp->GetPixelFormat();
if (bpp == 32)
pixel_format = PixelFormat32bppARGB;
image.Attach(hbmp);
//rotate:
Gdiplus::Bitmap bmp(image.GetWidth(), image.GetHeight(), image.GetPitch(), pixel_format, static_cast<BYTE*>(image.GetBits()));
bmp.RotateFlip(Gdiplus::Rotate90FlipNone);
//convert back to image:
image.Destroy();
if (image.Create(bmp.GetWidth(), bmp.GetHeight(), 32, CImage::createAlphaChannel))
{
Gdiplus::Bitmap dst(image.GetWidth(), image.GetHeight(), image.GetPitch(), PixelFormat32bppARGB, static_cast<BYTE*>(image.GetBits()));
Gdiplus::Graphics graphics(&dst);
graphics.DrawImage(&bmp, 0, 0);
}

HBITMAP to JPEG /PNG without CImage in C++

I've got a HBITMAP that I want to save into a JPEG/PNG stream or array of bytes. The problem is that I'm using mingw as my compiler so I can't use CImage.. which would have made my life easier.
I can get the pixels from the bitmap without any problems, but I have no idea how to get access to them in JPEG/PNG-format.
Where do I start?
If you have access DirectX library you may use IStream to convert your image to JPEG
http://msdn.microsoft.com/en-us/library/windows/desktop/aa380034(v=vs.85).aspx
or if you have GDI+ something like this might work
Gdiplus::Bitmap bmp(hbmpImage,(HPALETTE)0);
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
bmp.Save(L"D:\image.png",&pngClsid,NULL);
where GetEncoderLCLsid looks like this:
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
don't forget to initialize GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
if don't have access to either you may use libjpeg but you need to put all the dependencies packages from the GnuWin32 site. Much faster the code in this page should work, just forget about the boost
libjpeg dying without message
Another option is to use the WIC API (Windows Imaging Component), which gives direct access to image encoders and decoders. I believe GDI+ may use this under the covers.