Creating an HICON from a byte array in C++? - c++

I have a PNG-encoded icon as a byte array in memory. What is the recommended way of creating an HICON object from this byte array?
Imaginary bonus points if you know a solution without ATL or GDI+... :)

HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, dataSize);
LPVOID pImage = GlobalLock(hMem);
memcpy(pImage, pngData, dataSize);
GlobalUnlock(hMem);
ATL::CComPtr<IStream> pStream;
CreateStreamOnHGlobal(hMem, TRUE, &pStream);
Gdiplus::Bitmap *pBitmap = new Gdiplus::Bitmap(pStream);
HICON YOUR_HICON = pBitmap->GetHICON();

It looks like you could do this with CreateBitmap and CreateIconIndirect, or maybe even just CreateIcon. Don't ask me for code because I'm not really familiar with this low-level graphics stuff.

Related

Win32 Unable to add custom toolbar icon having transparency

I am using this code to add a toolbar to the window with one button having a custom image:
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME, NULL,WS_CHILD |
TBSTYLE_FLAT|TBSTYLE_AUTOSIZE |TBSTYLE_LIST|CCS_BOTTOM, 0, 0, 0, 0,
hwnd, NULL, ghInstance, NULL); //create the toolbar
SendMessage(hToolbar, WM_SETFONT, (WPARAM)hFontBold,
static_cast<LPARAM>(MAKELONG(TRUE, 0))); //set the font. there cannot be the problem
//↓↓↓↓↓**ILC_COLOR32 is specificied here but 32bit bmp is still not showing**//
auto hImagelist = ImageList_Create(32, 32,ILC_COLOR32|ILC_MASK,1, 0);
//↑↑↑↑↑**ILC_COLOR32 is specificied here but 32bit bmp is still not showing**//
HBITMAP bitmap = static_cast<HBITMAP>(ghInstance,
MAKEINTRESOURCE(ID_IMG_SPAWN),// <- ID_IMG_SPAWN is my custom resource
IMAGE_BITMAP, 32, 32, NULL));
ImageList_Add(hImagelist, bitmap, nullptr);
SendMessage(hToolbar, TB_SETIMAGELIST,static_cast<WPARAM>(0),(LPARAM)hImagelist);
In the code above, ID_IMG_SPAWN is the resource bmp image I imported to Visual Studio. However, Visual Studio threw an error saying it didn't recognize my bmp, and the bmp showed up blank when running the application.
Visual Studio told me my bmp wasn't recognized, see the below error:
When the app runs it looks as below:
I learned that Visual Studio hereby recognizes ONLY 24bit bmp.
So, I converted the bmp to 24bit and imported it again, changed ILC_COLOR32 to ILC_COLOR24, that really worked. No error and my image were shown on the button.
However, I lost my alpha channel. Because 24bit bitmap does not support an alpha channel, my image ended up having an ugly square background.
Try something like this (Note I used a "BINARY" resource.. maybe it will work with others too but not sure.. This code works with 32-bit bitmaps, alpha-transparent PNGs, and JPEG):
#include <windows.h>
#include <gdiplus.h>
HBITMAP LoadBitmapFromResource(DWORD ResourceID, bool transparent = true)
{
HANDLE hGlobal = NULL;
ULONG_PTR GDIToken = 0;
Gdiplus::Image* Img = NULL;
Gdiplus::GdiplusStartupInput GDIStartInput = NULL;
Gdiplus::GdiplusStartup(&GDIToken, &GDIStartInput, NULL);
HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(ResourceID), "BINARY");
if (!hResource) {return NULL;}
HGLOBAL hFileResource = LoadResource(NULL, hResource);
if (!hFileResource) {return NULL;}
LPVOID lpFile = LockResource(hFileResource);
if (!lpFile) {return NULL;}
DWORD dwSize = SizeofResource(NULL, hResource);
if (!dwSize) {return NULL;}
void* data = LockResource(hFileResource);
if (!data) {return NULL;}
IStream* pStream = NULL;
hGlobal = GlobalAlloc(GMEM_FIXED, dwSize);
memcpy(hGlobal, data, dwSize);
UnlockResource(hFileResource);
FreeResource(hFileResource);
if (CreateStreamOnHGlobal(hGlobal, true, &pStream) == S_OK)
{
Img = new Gdiplus::Image(pStream, false);
pStream->Release();
GlobalFree(hGlobal);
hGlobal = NULL;
HBITMAP hBitmap = NULL;
static_cast<Gdiplus::Bitmap*>(Img)->GetHBITMAP(transparent ? Gdiplus::Color::Transparent : Gdiplus::Color(0, 0, 0), &hBitmap);
delete Img;
Gdiplus::GdiplusShutdown(GdiImage::GDIToken);
GDIStartInput = NULL;
GDIToken = 0;
return hBitmap;
}
GlobalFree(hGlobal);
hGlobal = NULL;
Gdiplus::GdiplusShutdown(GdiImage::GDIToken);
GDIStartInput = NULL;
GDIToken = 0;
return NULL;
}
You will have to link against GDIPlus. It's not very nice to constantly startup and shutdown GDIPlus for every image you load.. so it's best to move that somewhere.. but other than that, everything should work just fine.
There are other ways without GDIPlus but it makes for a long post..
I learned that Visual Studio hereby recognizes ONLY 24bit bmp.
No, that's wrong. Visual Studio supports alpha-premultiplied 32-bit bitmaps.
32-bit bitmap has 8 bits for ALPHA, it's capable to make a smooth effect.
To show a 32-bit bitmap properly, you have to:
specify ILC_COLOR32
change the resource of ID_IMG_SPAWN to a 32-bit bitmap with premultiplied alpha.
create a DIB section to your bitmap when loading
(Win32's format requirement is very strict)
Q: How to create a DIB section to the bitmap?
A: Specify LR_CREATEDIBSECTION in the last parameter of LoadImage.
Explanation:
LoadImage((HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,32, 32,NULL)
This is your code of the LoadImage function. See the MSDN document of LoadImage, to create the DIB section, all you need is to specify LR_CREATEDIBSECTION in the last parameter of LoadImage.
Q: How to get a BMP with premultiplied alpha?
A: Pixelformer can help you convert your alpha-channeled file to a premultiplied-alpha BMP.
The steps are
Open your image (any format) in Pixelformer and choose Export from the menu
Select A8:R8:G8: B8 (32bpp) and Premultiplied Alpha, then click Ok.
Then you can save your BMP file! Import this BMP file to Visual Studio resources, replacing your previous 24-bit BMP.
Then, you don't need to use the ImageList_AddMasked (which makes the image sharp) anymore, because you already have a recognizable ALPHA in your 32-bit BMP. So, straight use ImageList_Add.
Okay, after the manipulations explained above your code should be this:
// Create the toolbar
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME,NULL,
WS_CHILD | TBSTYLE_FLAT | TBSTYLE_AUTOSIZE | TBSTYLE_LIST | CCS_BOTTOM,
0, 0, 0, 0, hwnd, NULL, ghInstance, NULL);
// Set the font (this cannot be the problem)
SendMessage(hToolbar, WM_SETFONT, (WPARAM)hFontBold,
static_cast<LPARAM>(MAKELONG(TRUE, 0)));
auto hImagelist =
ImageList_Create(32, 32,ILC_COLOR32 /*DON'T NEED THE MASK. CHANGED TO ILC_COLOR32.*/, 1, 0);
HBITMAP bitmap = static_cast<HBITMAP>(LoadImage((HINSTANCE)GetWindowLong(hwnd,
GWL_HINSTANCE), MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,
32, 32, LR_CREATEDIBSECTION /*THIS IS IMPORTANT*/ ));
ImageList_Add(hImagelist, bitmap, NULL);
SendMessage(hToolbar, TB_SETIMAGELIST, static_cast<WPARAM>(0), (LPARAM)hImagelist);
This worked fine as below.
These I answered above is well enough to solve this problem. You may wonder "What means DIB bitmaps?" "What is Premultiplied Alpha?". Those are the deep topics.
To learn DIB bitmaps and Premultiplied Alpha, see the links.
If you are using Visual Studio 2010 or higher (i guess), you can use PNG files. As stated here.

Windows 10 CredentialProvider Tile Image transparency

A while ago, I have created a CredentialProvider for Windows 7 that showed a custom tile image for the user. When I tested my CredentialProvider on Windows 10, I noticed that the tile image is not round as the usual Windows user image is, so I assumed, I have to provide my own round image with transparency. I tried to load a transparent PNG with the round shape and pass the corresponding HBITMAP in the ICredentialProviderCredentiall::GetBitmapValue() function. Unfortunately, instead of being transparent, the background of the PNG is white. Is there any way to show the actual transparency? Since the HBITMAP is drawn by LogonUI itself, I cannot modify the drawing behavior.
To create the HBITMAP out of my PNG resource, I use the following function:
HBITMAP LoadPNG(HINSTANCE hInst, int resourceId)
{
HGLOBAL hGlobal;
LPSTREAM pStream;
HBITMAP tBmp = NULL;
ULONG_PTR token = 0;
Gdiplus::GdiplusStartupInput input = NULL;
Gdiplus::GdiplusStartup(&token, &input, NULL);
if (token != 0)
{
HRSRC hRsrc = FindResource(hInst, MAKEINTRESOURCE(resourceId), TEXT("PNG"));
HGLOBAL hGlob1 = LoadResource(hInst, hRsrc);
int size = SizeofResource(hInst, hRsrc);
hGlobal = GlobalAlloc(GMEM_FIXED, size);
LPVOID resPtr = LockResource(hGlob1);
memcpy(hGlobal, resPtr, size);
FreeResource(hGlob1);
CreateStreamOnHGlobal(hGlobal, true, &pStream);
Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(pStream, false);
bmp->GetHBITMAP(Gdiplus::Color::Transparent, &tBmp);
Gdiplus::GdiplusShutdown(token);
}
return tBmp;
}
I tried also other values for the GetHBITMAP() background color parameter such as AlphaMask and AlphaShift, but these did not work either (instead, the white background turned black)
Is there any way to accomplish my goal?
The newest Credential Provider Technical Reference (which is not that easy to find on MSDN) actually tells what the issue with the tile image is:
In Windows 10, the selected User/V1/PLAP credential provider has an
image size of 192x192. The ones on the bottom left list is of 48x48.
Note LogonUI uses circular image for user and square image for V1/PLAP
according to the new design direction. The image size of V2 Credential
Provider tile under a selected user is 48x48.
If I read the documentation correctly then the challenge must be somehow to convince the LogonUI that its a User and not a V1/PLAP, I think this could be archieved returning a dummy user in the GetSerialization call.

c++ Load bitmap from memory - memory leak

I have function, which creates Gdiplus::Bitmap.
Bitmap *LoadBitmapT(const unsigned char* fileBuffer, size_t length) {
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, length);
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, fileBuffer, length);
IStream* pstm;
CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
Gdiplus::Bitmap *bitmap = Gdiplus::Bitmap::FromStream(pstm, FALSE);
GlobalUnlock(m_hMem);
pstm->Release();
return bitmap;
}
As you can see, memory leak appears thanks to GlobalAlloc().
When I try to use GlobalFree(m_hmem) it solves my problem and memory leak disappear. But I am drawing obtained bitmap in another function with this code:
Graphics graphics(hdc);
graphics.DrawImage(bitmap, ....);
and when I don't use GlobalFree(), painted image is correct. But when I use mentioned function, than I lose correct image and it is image like blue screen.
Than I try to save pointer of m_hMem and call GlobalFree() after drawing bitmap. So, this is OK. But I need to use rotation on obtained bitmap, so when I call bitMap->RotateFlip(RotateNoneFlipX); than memory leaks appears again. Changing some color of pixel manually in bitmap makes same behaviour.
So, how can I release memory for this image and draw correctly this image. I need it, because I am drawing periodically a lot of images and so this allocates a lot of memory and then my program crash.
Edit
I tried this code:
Bitmap *LoadBitmapT(const unsigned char* fileBuffer, size_t length) {
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, length);
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, fileBuffer, length);
IStream* pstm;
CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
Gdiplus::Bitmap *bitmap = Gdiplus::Bitmap::FromStream(pstm, FALSE);
GlobalUnlock(m_hMem);
pstm->Release();
GlobalFree(m_hMem);
return NULL;
}
After this when I am looking on task manager, than I see that memory don't increase.
When I give bitmap->RotateFlip(RotateNoneFlipX);after Gdiplus::Bitmap *bitmap = Gdiplus::Bitmap::FromStream(pstm, FALSE); and code is same but only this one line is added, than memory is increasing.
I found answer.
This code deallocates everything correct.
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, length);
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, fileBuffer, length);
IStream* pstm;
CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
Gdiplus::Bitmap *bitmap = Gdiplus::Bitmap::FromStream(pstm, FALSE);
pstm->Release();
GlobalUnlock(m_hMem);
bitmap->RotateFlip(RotateNoneFlipX);
delete bitmap;
GlobalFree(m_hMem);
Need to call in correct order. So first delete bitmap and then GlobalFree

Save Excel Spreadsheet As Jpeg In C++?

I have an Excel spreadsheet which is one sheet with a table and chart in it. What I need to do is add this to a PDF. The PDF API I am using does not support importing Excel documents so I wondered if there is any way from C++ to save/convert an Excel document to a Jpeg (or BMP)?
Just to add, obviously this has to be done in C++ as part of an existing application which creates PDFs every time it is run. I wouldn't be asking the question if it was a one off that could just be done manually.
Well I have, after much effort and hair pulling, managed to answer my own question. Contrary to what was said in other answers, it is possible to save an Excel spreadsheet as Jpeg (although I have stuck to bitmap format as that's good enough for me. It would be easy enough to convert to jpeg though). What I did via the Excel API was select the used range. From there, I copy this to the clipboard as a picture, then get the clipboard data and convert it to a picture object with an IPicture interface. From there, create a stream and copy this to the stream before saving this stream to file. The result is a bmp file with exactly the image I was after. Code sample below.
CRange range = sheet.get_UsedRange();
range.CopyPicture(Excel::xlScreen, Excel::xlBitmap);
if (IsClipboardFormatAvailable(CF_BITMAP)) // just to be sure it copied
{
if (OpenClipboard(NULL) > 0)
{
// save clipboard as bmp
HBITMAP hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
SaveBitmap(ps_filename, hBitmap, NULL);
CloseClipboard();
}
}
}
bool CExcelDocument::SaveBitmap(LPCSTR filename, HBITMAP bmp, HPALETTE pal)
{
bool bReturn = false;
PICTDESC pictdesc;
LPPICTURE pPicture;
::ZeroMemory( &pictdesc, sizeof(pictdesc) );
pictdesc.picType = PICTYPE_BITMAP;
pictdesc.bmp.hbitmap = bmp;
pictdesc.bmp.hpal = pal;
pictdesc.cbSizeofstruct = sizeof(PICTDESC);
HRESULT hr = OleCreatePictureIndirect(&pictdesc, IID_IPicture, false, reinterpret_cast<void**>(&pPicture));
if (!SUCCEEDED(hr))
return false;
LPSTREAM pStream;
hr = CreateStreamOnHGlobal(0, true, &pStream);
if (!SUCCEEDED(hr))
{
pPicture->Release();
return false;
}
LONG lBytesRead = 0;
DWORD lBytesWritten = 0;
hr = pPicture->SaveAsFile(pStream, true, &lBytesRead);
if (!SUCCEEDED(hr))
{
pStream->Release();
pPicture->Release();
return false;
}
HANDLE hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (!hFile)
{
return false;
}
HGLOBAL hMemory = 0;
GetHGlobalFromStream(pStream, &hMemory);
LPVOID pData = GlobalLock(hMemory);
bReturn = !!WriteFile(hFile, pData, lBytesRead, &lBytesWritten, 0);
bReturn &= (lBytesWritten == static_cast<DWORD>(lBytesRead));
GlobalUnlock(hMemory);
CloseHandle(hFile);
pStream->Release();
pPicture->Release();
return bReturn;
}
What you can do is, load the spreadsheet into C++ using the Execl API (Office Interop). Now you have access to the values within the rows and columns. You should also be able to extract how large they are (width and height). This is your starting point.
Now the simplest approach is to use GDI (or whatever) to create a new picture and walk through every cell within your excel sheet and draw the content of it into your picture. After this you can draw lines resembling the cells itself.
Anyway this results in a lot of work and won't be easy, especially if you have to consider things like text alignment within the cell and border sizes and so on.

Win32 C/C++ Load Image from memory buffer

I want to load an image (.bmp) file on a Win32 application, but I do not want to use the standard LoadBitmap/LoadImage from Windows API: I want it to load from a buffer that is already in memory. I can easily load a bitmap directly from a file and print it on the screen, but this issue is making me stuck.
What I'm looking for is a function that works like this:
HBITMAP LoadBitmapFromBuffer(char* buffer, int width, int height);
Try CreateBitmap():
HBITMAP LoadBitmapFromBuffer(char *buffer, int width, int height)
{
return CreateBitmap(width, height, 1, 24, buffer);
}
Nevermind, I found my solution! Here's the initializing code:
std::ifstream is;
is.open("Image.bmp", std::ios::binary);
is.seekg (0, std::ios::end);
length = is.tellg();
is.seekg (0, std::ios::beg);
pBuffer = new char [length];
is.read (pBuffer,length);
is.close();
tagBITMAPFILEHEADER bfh = *(tagBITMAPFILEHEADER*)pBuffer;
tagBITMAPINFOHEADER bih = *(tagBITMAPINFOHEADER*)(pBuffer+sizeof(tagBITMAPFILEHEADER));
RGBQUAD rgb = *(RGBQUAD*)(pBuffer+sizeof(tagBITMAPFILEHEADER)+sizeof(tagBITMAPINFOHEADER));
BITMAPINFO bi;
bi.bmiColors[0] = rgb;
bi.bmiHeader = bih;
char* pPixels = (pBuffer+bfh.bfOffBits);
char* ppvBits;
hBitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (void**) &ppvBits, NULL, 0);
SetDIBits(NULL, hBitmap, 0, bih.biHeight, pPixels, &bi, DIB_RGB_COLORS);
GetObject(hBitmap, sizeof(BITMAP), &cBitmap);
CreateDIBSection can be a little complicated to use, but one of the things it can do is create a device-independent bitmap and give you a pointer to the buffer for the bitmap bits. Granted, you already have a buffer full of bitmap bits, but at least you could copy the data.
Speculating a bit: CreateDIBSection can also create bitmaps from file objects, and there's probably a way to get Windows to give you a file object representing a chunk of memory, which might trick CreateDIBSection into giving you a bitmap built directly from your buffer.
No, but you can create a new bitmap the size of the current one in memory, and write your memory structure onto it.
You're looking for the CreateBitmap function. Set lpvBits to your data.