How to load GDI::Image (GIF) from Resource in c++(winapi)? - c++

I'm making a window c++ with winapi(non-MFC) and showing gif. For this Im using GDI++. I'm loading a gif to GDI::Image from the path, but I want to load it from resources. How can I do it?
hMWDC = GetDC(hWnd);
pGphcs = new Graphics(hMWDC);
WCHAR path[MAX_PATH];
GetModuleFileNameW(NULL, path, MAX_PATH);
PathRemoveFileSpecW(path);
PathAppendW(path, L"gifs\\test.gif");
pImg = new Image(path);
if (pImg) {
nFrmCnt = pImg->GetFrameCount(&FrameDimensionTime);
SetTimer(hWnd, DRAW_ANIM, 100, NULL);
}
case WM_TIMER:
if (wParam == DRAW_ANIM)
{
pImg->SelectActiveFrame(&FrameDimensionTime, nFrm);
Rect DRC(0, 0, pImg->GetWidth(), pImg->GetHeight());
pGphcs->Clear(Color(128, 128, 128));
pGphcs->DrawImage(pImg, DRC);
if (nFrm < (nFrmCnt - 1)) nFrm++; else nFrm = 0;
}
break;

There is an Image constructor that accepts an IStream*.
You can create a stream by calling SHCreateMemStream on the raw buffer of a resource, which can be obtained by calling FindResource/LoadResource/LockResource and SizeOfResource.

Add the GIF file to your app's resources at compile time. For instance, by compiling an .rc file similar to below into a .res file that you can then link into your executable (some compilers/IDEs have tools to automate this step):
Resources.rh
#define MY_GIF_ID 100
Resources.rc
#include "Resources.rh"
MY_GIF_ID RCDATA "gifs\\test.gif"
Then, you can obtain a pointer to the raw bytes of the resource at runtime.
#include "Resources.rh"
HMODULE hMod = GetModuleHandle(NULL);
HRSRC hRes = FindResource(hMod, MAKEINTRESOURCE(MY_GIF_ID), RT_RCDATA);
if (!hRes) { ... error handling ... }
HGLOBAL hGlobal = LoadResource(hMod, hRes);
if (!hGlobal) { ... error handling ... }
void *pResData = LockResource(hGlobal);
if (!pResData) { ... error handling ... }
DWORD dwResData = SizeofResource(hMod, hRes);
See MSDN for more details:
Introduction to Resources
Finding and Loading Resources
And then finally, pass the resource bytes to the Image constructor that takes an IStream* as input:
#include <shlwapi.h>
IStream *pStream = SHCreateMemStream((BYTE*)pResData, dwResData);
if (!pStream) { ... error handling ... }
pImg = new Image(pStream);
pStream->Release();

Related

Missing resource name in Code::Blocks and therefore FindResource doesn't work

I have a project in Code::Blocks.
In my resource.rc file I have
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif
#define TWEETY 102
In my resource.h file I have
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"
TWEETY IMAGE "Tweety.png"
I build the project and afterwards open the exe file in ResEdit.
This is what I see there.
ResEdit
There are no resource names, only resource ids.
This has a consequence (at least it's my conclusion) that I cannot find the image resource using FindResource function.
// Loads the PNG containing the splash image into a HBITMAP.
HBITMAP LoadSplashImage()
{
HBITMAP hbmpSplash = NULL;
// load the PNG image data into a stream
IStream * ipImageStream = CreateStreamOnResource(MAKEINTRESOURCE(TWEETY), _T("PNG"));
if (ipImageStream == NULL)
goto Return;
// load the bitmap with WIC
IWICBitmapSource * ipBitmap = LoadBitmapFromStream(ipImageStream);
if (ipBitmap == NULL)
goto ReleaseStream;
// create a HBITMAP containing the image
hbmpSplash = CreateHBITMAP(ipBitmap);
ipBitmap->Release();
ReleaseStream:
ipImageStream->Release();
Return:
return hbmpSplash;
}
// Creates a stream object initialized with the data from an executable resource.
IStream * CreateStreamOnResource(LPCTSTR lpName, LPCTSTR lpType)
{
// initialize return value
IStream * ipStream = NULL;
// find the resource
HRSRC hrsrc = FindResource(NULL, lpName, lpType);
if (hrsrc == NULL)
goto Return;
// load the resource
DWORD dwResourceSize = SizeofResource(NULL, hrsrc);
HGLOBAL hglbImage = LoadResource(NULL, hrsrc);
if (hglbImage == NULL)
goto Return;
// lock the resource, getting a pointer to its data
LPVOID pvSourceResourceData = LockResource(hglbImage);
if (pvSourceResourceData == NULL)
goto Return;
// allocate memory to hold the resource data
HGLOBAL hgblResourceData = GlobalAlloc(GMEM_MOVEABLE, dwResourceSize);
if (hgblResourceData == NULL)
goto Return;
// get a pointer to the allocated memory
LPVOID pvResourceData = GlobalLock(hgblResourceData);
if (pvResourceData == NULL)
goto FreeData;
// copy the data from the resource to the new memory block
CopyMemory(pvResourceData, pvSourceResourceData, dwResourceSize);
GlobalUnlock(hgblResourceData);
// create a stream on the HGLOBAL containing the data
if (SUCCEEDED(CreateStreamOnHGlobal(hgblResourceData, TRUE, &ipStream)))
goto Return;
FreeData:
// couldn't create stream; free the memory
GlobalFree(hgblResourceData);
Return:
return ipStream;
}
Please advise what I'm doing wrong.
Update - it is working now.
TWEETY IMAGE "Tweety.png"
IStream * ipImageStream = CreateStreamOnResource(MAKEINTRESOURCE(TWEETY), _T("IMAGE"));
HRSRC hrsrc = FindResource(NULL, lpName, lpType);
However, in ResEdit I still don't see the resource name, only resource id.
And this:
MAKEINTRESOURCE(TWEETY);
returns "error - cannot access memory at address 0x66".
Resource types are identified by ID or name. Your resource script defines a resource type with name IMAGE (that's why you see "IMAGE" in ResEdit; note the quotation marks).
You are passing a resource type with name PNG to the call to FindResource. The module doesn't have a resource type named PNG. It contains a resource type named IMAGE. When you pass _T("IMAGE") your code starts to work.

Convert LPVOID bitmap pointer to QPixmap

I am trying to load in a BMP image from a resources folder into a QPixmap object. However, I can't read the bytes even though rewriting those bytes to a new file makes a correct copy of the original. Here is my loading method:
QPixmap* GUIMain::loadImage(int name) {
// Resource loading, works fine
HRSRC rc = FindResource(NULL, MAKEINTRESOURCE(name), RT_BITMAP);
if (rc == NULL) {
printf("INVALID RESOURCE ADDRESS (%i)\n", name);
return new QPixmap();
}
HGLOBAL rcData = LoadResource(NULL, rc);
LPVOID data = LockResource(rcData);
DWORD data_size = SizeofResource(NULL, rc);
// Rewrite file to new file, works fine
ofstream output("E:\\" + to_string(name) + ".bmp", std::ios::binary);
BITMAPFILEHEADER bfh = { 'MB', 54 + data_size, 0, 0, 54 };
output.write((char*)&bfh, sizeof(bfh));
output.write((char*)data, data_size);
output.close();
// Need to return, can't get bytes working
return new QPixmap(/*?*/);
}
This method is called with the definitions from the resource.h file.
I have tried to use a stringstream with the same calls as the ofstream, followed by using that stream as a source for the QPixmap, but the stream didn't produce the same output.
Here are the relevant parts of my resource.h file:
#define IDB_BITMAP1 101
#define IDB_BITMAP2 102
Here is my Resource.rc file:
IDB_BITMAP1 BITMAP "E:\\Downloads\\onIcon.bmp"
IDB_BITMAP2 BITMAP "E:\\Downloads\\offIcon.bmp"
I know I should be using the Qt tools for resource management, but I don't have the capabilities to do so.
You can use QPixmap::loadFromData(...) to create a QPixmap from bytes in bmp format, but you also need to not declare the resource as "BITMAP" in the .rc file.
Bitmap resources are intended to be used with LoadBitmap(...) or LoadImage(...) and are stored in the .exe with the bitmap header stripped off. (Raymond Chen discusses this here) Since you are not using LoadBitmap set the type of the resources to be arbitrary binary data, e.g.
IDB_BITMAP1 RCDATA "E:\\Downloads\\onIcon.bmp"
and then implement your image loading routine as below:
QPixmap* GUIMain::loadImage(int name) {
// ...
HGLOBAL rcData = LoadResource(NULL, rc);
LPVOID data = LockResource(rcData);
DWORD data_size = SizeofResource(NULL, rc);
QPixmap* pm = new QPixmap();
pm->loadFromData( static_cast<uchar*>(data), data_size, "bmp");
return pm;
}

How to extract file icon and save as .ico while preserving color depth in C++?

I am able to extract a file's icon and save it using the script below, but the script saves the icons in grey (seems to be 4 bit color depth).
How can I save icons while preserving their original color depth?
using namespace std;
#include <iostream>
#include <string>
#include <fstream>
#include <windows.h>
#include "commctrl.h"
#pragma comment(lib, "comctl32.lib")
#include <olectl.h>
#pragma comment(lib, "oleaut32.lib")
HRESULT SaveIcon(HICON hIcon, PCTSTR path) {
// Create the IPicture intrface
PICTDESC desc = { sizeof(PICTDESC) };
desc.picType = PICTYPE_ICON;
desc.icon.hicon = hIcon;
IPicture* pPicture = 0;
HRESULT hr = OleCreatePictureIndirect(&desc, IID_IPicture, FALSE, (void**)& pPicture);
if (FAILED(hr)) return hr;
// Create a stream and save the image
IStream* pStream = 0;
CreateStreamOnHGlobal(0, TRUE, &pStream);
LONG cbSize = 0;
hr = pPicture->SaveAsFile(pStream, TRUE, &cbSize);
// Write the stream content to the file
if (!FAILED(hr)) {
HGLOBAL hBuf = 0;
GetHGlobalFromStream(pStream, &hBuf);
void* buffer = GlobalLock(hBuf);
HANDLE hFile = CreateFile(path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
if (!hFile) hr = HRESULT_FROM_WIN32(GetLastError());
else {
DWORD written = 0;
WriteFile(hFile, buffer, cbSize, &written, 0);
CloseHandle(hFile);
}
GlobalUnlock(buffer);
}
// Cleanup
pStream->Release();
pPicture->Release();
return hr;
}
HICON GetIcon(PCTSTR pszFile)
{
SHFILEINFO sfi;
HIMAGELIST himl = reinterpret_cast<HIMAGELIST>(
SHGetFileInfo(pszFile, 0, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX));
if (himl) {
return ImageList_GetIcon(himl, sfi.iIcon, ILD_NORMAL);
}
else {
return NULL;
}
}
int main()
{
string fileBaseName = "appName";
wstring fileBaseNameWSTRING(fileBaseName.begin(), fileBaseName.end());
HICON hIcon = GetIcon((fileBaseNameWSTRING + L".lnk").c_str());
if (hIcon == NULL) {
cout << "GetIcon failed" << endl;
return 1;
}
else {
HRESULT hr = SaveIcon(hIcon, (L"temp\\" + fileBaseNameWSTRING + L".ico").c_str());
return hr;
}
}
The SaveIcon function was taken from this page:
How can I save HICON to an .ico file?
Generally, icons contain multiple original bit depths, also multiple resolutions. APIs which return HICON won’t do, they pick a single image from the icon.
If you want the complete one, change SHGFI_SYSICONINDEX to SHGFI_ICONLOCATION, you’ll get path to a DLL with icon + index of the icon.
Load the DLL, ideally with LoadLibraryEx API and LOAD_LIBRARY_AS_DATAFILE option.
Then call resource APIs, FindResource / LoadResource / SizeofResource / LockResource, to get the source data for the icon.

Get bytes/char* from hIcon/hBitmap

I'm working on a C/S application, Server in C++ and Client in C#, I need to send some information about current running processes and related icon.
I get icon file thanks to EnumWindows with this code inside the callback...
// Get the window icon
HICON hIcon = (HICON)(::SendMessageW(hWnd, WM_GETICON, ICON_SMALL, 0));
if (hIcon == 0) {
// Alternative method. Get from the window class
hIcon = reinterpret_cast<HICON>(::GetClassLongPtrW(hWnd, GCLP_HICONSM));
}
// Alternative: get the first icon from the main module
if (hIcon == 0) {
hIcon = ::LoadIcon(GetModuleHandleW(0), MAKEINTRESOURCE(0));
}
// Alternative method. Use OS default icon
if (hIcon == 0) {
hIcon = ::LoadIcon(0, IDI_APPLICATION);
}
OK, now I have the Icon and I can "print" it (simply for check) with DrawIcon().
My question is: How to get bytes starting from this?
I need a buffer to send this data to my Client and CONVERT the same data to display icon in a list (icon + process_name). So, I need to get bytes of this icon/hIcon (or bitmap/hBitmap, etc.)
(Of course I need help for server side.)
I think is good to copy the icon in a temp buffer to get bytes but nothing works.
Any help would be appreciated.
EDIT:
#DavidHeffernan thank you for reply. I found this: Converting-DDB-to-DIB through StackOverflow's past questions (sorry if is bad to post external links).
Now, with GetDIBits() I have in the fifth param the LPVOID lpvBits , that is "A pointer to a buffer to receive the bitmap data msdn - GetDIBits()"
Now, How Should I send from lpvBits? What's about Bitmap Size?
I've found something on MSDN and StackOverflow and it helped me: link;
#include "stdafx.h"
#include <windows.h>
#include <olectl.h>
#pragma comment(lib, "oleaut32.lib")
HRESULT SaveIcon(HICON hIcon, const wchar_t* path) {
// Create the IPicture intrface
PICTDESC desc = { sizeof(PICTDESC) };
desc.picType = PICTYPE_ICON;
desc.icon.hicon = hIcon;
IPicture* pPicture = 0;
HRESULT hr = OleCreatePictureIndirect(&desc, IID_IPicture, FALSE, (void**)&pPicture);
if (FAILED(hr)) return hr;
// Create a stream and save the image
IStream* pStream = 0;
CreateStreamOnHGlobal(0, TRUE, &pStream);
LONG cbSize = 0;
hr = pPicture->SaveAsFile(pStream, TRUE, &cbSize);
// Write the stream content to the file
if (!FAILED(hr)) {
HGLOBAL hBuf = 0;
GetHGlobalFromStream(pStream, &hBuf);
void* buffer = GlobalLock(hBuf);
HANDLE hFile = CreateFile(path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
if (!hFile) hr = HRESULT_FROM_WIN32(GetLastError());
else {
DWORD written = 0;
WriteFile(hFile, buffer, cbSize, &written, 0);
CloseHandle(hFile);
}
GlobalUnlock(buffer);
}
// Cleanup
pStream->Release();
pPicture->Release();
return hr;
}
int _tmain(int argc, _TCHAR* argv[])
{
HICON hIcon = (HICON)LoadImage(0, L"c:\\windows\\system32\\perfcentercpl.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
if (!hIcon) return GetLastError();
HRESULT hr = SaveIcon(hIcon, L"c:\\temp\\test.ico");
return hr;
}
Thanks to SaveIcon() I can save it and after, when needed, I can open it as binary file and send it via socket.

Using CImage.Save (InMemory)

I'm working with some pictures in a MFC application, and I realized that the CImage class (from MFC) has 2 types of save, to a file and to a IStream interface. I'm trying to use that IStream interface to save to memory, using the convention features of the CImage class without another library to do that (for example bmp to jpg convertion). I don't want to save a file, just work with the memory buffers. The CImage object is already loaded with a picture file.
But I'm not able to use this IStream interface because I cannot create a object from that class and not realized how to create the buffer to use with that Save(IStream * pStream...) feature.
Any example will be very helpful.
Thanks,
Vitor
First, you should add your picture to a resource id, and to do like this:
BOOL ImageFromIDResource(UINT nID, LPCTSTR sTR, CImage * & pImg)
{
HINSTANCE hInst = AfxGetResourceHandle();
HRSRC hRsrc = ::FindResource(hInst, MAKEINTRESOURCE(nID), sTR); // type
if (!hRsrc)
return FALSE;
// load resource into memory
DWORD len = SizeofResource(hInst, hRsrc);
BYTE* lpRsrc = (BYTE*)LoadResource(hInst, hRsrc);
if (!lpRsrc)
return FALSE;
// Allocate global memory on which to create stream
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len);
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, lpRsrc, len);
IStream* pstm = NULL;
CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
// load from stream
//pImg = Gdiplus::Image::FromStream(pstm);
pImg = new CImage();
pImg->Load(pstm);
DWORD err = GetLastError();
// free/release stuff
GlobalUnlock(m_hMem);
pstm->Release();
FreeResource(lpRsrc);
return TRUE;
}