I am converting a picture from a base64 string into an HICON that could work when registering a new class:
WNDCLASSEX wc{};
wc.hIcon = < here >;
I got the base64_decode() function here: base64.cpp
#include <windows.h> // GDI includes.
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
std::string base64 = "......";
std::string decodedImage = base64_decode(base64);
DWORD imageSize = decodedImage.length();
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);
LPVOID pImage = ::GlobalLock(hMem);
memcpy(pImage, decodedImage.c_str(), imageSize);
IStream* pStream = NULL;
::CreateStreamOnHGlobal(hMem, FALSE, &pStream);
Gdiplus::Image image(pStream);
int wd = image.GetWidth();
int hgt = image.GetHeight();
auto format = image.GetPixelFormat();
Bitmap* bmp = new Bitmap(wd, hgt, format);
auto gg = std::unique_ptr<Graphics>(Graphics::FromImage(bmp));
gg->Clear(Color::Transparent);
gg->DrawImage(image, 0, 0, wd, hgt);
...
I couldn't find a way to get the HICON from the Image, so I was trying to convert the Image to Bitmap.
I'm getting these two errors on the line gg->DrawImage(image, 0, 0, wd, hgt);
C2664 'Gdiplus::Status Gdiplus::Graphics::DrawImage(Gdiplus::Image *,Gdiplus::REAL,Gdiplus::REAL,Gdiplus::REAL,Gdiplus::REAL)': cannot convert argument 1 from 'Gdiplus::Image' to 'Gdiplus::Image *'
E0304 no instance of overloaded function "Gdiplus::Graphics::DrawImage" matches the argument list
cannot convert argument 1 from 'Gdiplus::Image' to 'Gdiplus::Image *'
DrawImage() expects a pointer to an Image object, but you are passing it the actual object instead.
Change this statement:
gg->DrawImage(image, 0, 0, wd, hgt);
To this instead:
gg->DrawImage(&image, 0, 0, wd, hgt); // <-- note the added '&' ...
Related
I wrote a program to save a BMP of a Greek word.
Here it is:
#include <Windows.h>
#include <gdiplus.h>
#include <iostream>
#pragma comment(lib,"gdiplus.lib")
using namespace Gdiplus;
using namespace std;
int main()
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
{
// Create a bitmap.
Bitmap bmp(500, 500, PixelFormat24bppRGB);
Graphics graphics(&bmp);
// Set the font.
FontFamily fontFamily(L"Arial");
Font font(&fontFamily, 36, FontStyleRegular, UnitPixel);
graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
graphics.SetSmoothingMode(SmoothingModeHighQuality);
// Draw the text.
SolidBrush brush(Color(255, 0, 0, 0));
WCHAR text[] = L"ΔΙΔΑΣΚΑΛΙΑ";
PointF origin(100.0f, 100.0f);
graphics.DrawString(text, -1, &font, origin, &brush);
// Save the bitmap.
Status test = bmp.Save(L"greek_word.bmp", &ImageFormatBMP);
printf("%d", test);
}
// Clean up GDI+.
GdiplusShutdown(gdiplusToken);
return 0;
}
For some reason, it creates the BMP, but it has a file size of 0.
And, for some reason, bmp.Save returns a FileNotFound error.
Anyone know why I am getting this strange behavior?
Thanks.
#include "screenshot.h"
#include "changewallpaper.h"
using namespace std;
int main()
{
screenshot();
changewallpaper();
}
My screenshot();
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
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
}
void screenshot()
{
// get the device context of the screen
HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
int width = GetDeviceCaps(hScreenDC, HORZRES);
int height = GetDeviceCaps(hScreenDC, VERTRES);
POINT a,b;
a.x=0;
a.y=0;
b.x=width;
b.y=height;
// copy screen to bitmap
HDC hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, abs(b.x-a.x), abs(b.y-a.y));
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, abs(b.x-a.x), abs(b.y-a.y), hScreen, a.x, a.y, SRCCOPY);
//Initialize GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Gdiplus::Bitmap bitmap(hBitmap, NULL);
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
bitmap.Save(L"D:\\Pictures\\screen.png", &pngClsid, NULL);
// clean up
SelectObject(hDC, old_obj);
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);
//delete image;
GdiplusShutdown(gdiplusToken);
}
My problem is that changewallpaper(); never runs. If I place changewallpaper(); before screenshot(); in my main everything works but not if I have it like above. I need my program to take the screenshot before it changes the wallpaper so I can't just switch them. Does anyone know what might be the problem? I am clueless.
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.
A couple changes. The scoping is necessary to get the bitmap out of scope and clean up after itself. The second is releasing the hScreenDC although I'll leave that as an exercise for you to see if it is necessary. You were hanging in the image::~image function. Let me know if it fixes it for you.
{
Gdiplus::Bitmap bitmap(hBitmap, NULL);
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
bitmap.Save(L"c:\\temp\\screen.png", &pngClsid, NULL);
}
// clean up
SelectObject(hDC, old_obj);
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);
DeleteDC(hScreenDC);
I have an dev-c++ Application which makes an Screenshot and writes it into an File. Now I want to write the image into an Variable/Stream. Originally I used three Writefile Functions, which write the header, the info and the hbitmap into an file. Now I want to save the data not to the file, but to an Stream, so I can use it for further processing. The Code I use is this:
/* <Include> */
#include <windows.h>
#include <iostream>
#include <sstream>
/* </Include> */
/* <Const> */
const char *AppName="Yeah";
using namespace std;
/* </Const> */
/* <Function> */
void SaveScreen(HWND pScreen, stringstream Path)
{
int Width = GetSystemMetrics(SM_CXSCREEN);//1280;
int Height = GetSystemMetrics(SM_CYSCREEN);//1024;
HDC hdcScreen;
HBITMAP hbmScreen;
//---------------Bitmap Informationen
BITMAPINFO infobmp;
infobmp.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
infobmp.bmiHeader.biWidth = Width;
infobmp.bmiHeader.biHeight = Height;
infobmp.bmiHeader.biPlanes = 1;
infobmp.bmiHeader.biBitCount = 24;
infobmp.bmiHeader.biCompression = 0;
infobmp.bmiHeader.biSizeImage = 0;
infobmp.bmiHeader.biXPelsPerMeter = 0;
infobmp.bmiHeader.biYPelsPerMeter = 0;
infobmp.bmiHeader.biClrUsed = 0;
infobmp.bmiHeader.biClrImportant = 0;
int* bitmap = new int[Width*Height*3];
BITMAPFILEHEADER bfheader;
bfheader.bfType = 19778;
bfheader.bfSize = sizeof(BITMAPFILEHEADER) + Width*Height*3 + sizeof(BITMAPINFOHEADER);
bfheader.bfReserved1 = 0;
bfheader.bfReserved2 = 0;
bfheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Bitmap ----------------------- Informationen
hdcScreen = GetWindowDC(pScreen);
hbmScreen = CreateCompatibleBitmap(hdcScreen, Width, Height);
// tempor?rer DC
HDC hdcTemp = CreateCompatibleDC(hdcScreen);
// Bitmap reinselektieren
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcTemp, hbmScreen);
// Inhalt von Desktop ?bertragen
BitBlt(hdcTemp, 0, 0, Width, Height, hdcScreen, 0, 0, SRCCOPY);
int iResult = GetDIBits(hdcTemp, hbmScreen, 0, Height, bitmap, &infobmp, DIB_RGB_COLORS);
// aufr?umen
SelectObject(hdcTemp, hbmOld);
DeleteObject(hbmScreen);
DeleteDC(hdcTemp);
// HANDLE hfile = CreateFile(Path, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0);
//Datei Schreiben
DWORD word;
WriteFile(Path, &bfheader, 14, &word, NULL);
WriteFile(Path, &infobmp, 40,& word, NULL);
WriteFile(Path, bitmap, Width*Height*3, &word,NULL);
// Path = &bfheader & &infobmp & bitmap;
ReleaseDC(pScreen, hdcScreen);
// CloseHandle(hfile);
delete[] bitmap;
}
/* </Function> */
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HWND hWnd = FindWindow(NULL, AppName);
stringstream ms;
SaveScreen(hWnd, ms);
return 0;
}
Can someone please show me what I am doing wrong?
You can create a structure like this:
struct ScreenShotBuffer
{
BITMAPFILEHEADER bfheader;
BITMAPINFO infobmp;
int* bitmap;
};
Then create a global variable using that structure.
struct ScreenShotBuffer myScreenShot;
Anyway you are allocating the "memory" to hold the data using malloc.
So you can just use these three lines instead of WriteBuffer() :
myScreenShot.bfheader=bfheader;
myScreenShot.infobmp=infobmp;
myScreenShot.bitmap=bitmap;
How can I obtain kerning information for GDI to then use in GetKerningPairs? The documentation states that
The number of pairs in the lpkrnpair array. If the font has more than
nNumPairs kerning pairs, the function returns an error.
However, I do not know how many pairs to pass in, and I don't see a way to query for it.
EDIT #2
Here is my fill application that I have also tried, this is always producing 0 for any font for the number of pairs. GetLastError will always return 0 also.
#include <windows.h>
#include <Gdiplus.h>
#include <iostream>
using namespace std;
using namespace Gdiplus;
int main(void)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Font* myFont = new Font(L"Times New Roman", 12);
Bitmap* bitmap = new Bitmap(256, 256, PixelFormat32bppARGB);
Graphics* g = new Graphics(bitmap);
//HDC hdc = g->GetHDC();
HDC hdc = GetDC(NULL);
SelectObject(hdc, myFont->Clone());
DWORD numberOfKerningPairs = GetKerningPairs(hdc, INT_MAX, NULL );
cout << GetLastError() << endl;
cout << numberOfKerningPairs << endl;
GdiplusShutdown(gdiplusToken);
return 0;
}
EDIT
I tried to do the following, however, it still gave me 0.
Font* myFont = new Font(L"Times New Roman", 10);
Bitmap* bitmap = new Bitmap(256, 256, PixelFormat32bppARGB);
Graphics* g = new Graphics(bitmap);
SelectObject(g->GetHDC(), myFont);
//DWORD numberOfKerningPairs = GetKerningPairs( g->GetHDC(), -1, NULL );
DWORD numberOfKerningPairs = GetKerningPairs( g->GetHDC(), INT_MAX, NULL );
The problem lies in the fact that you are passing in a Gdiplus::Font and not a HFONT for SelectObject. You need to convert Font* myFont into a HFONT, then pass that HFONT into SelectObject.
First, to convert a Gdiplus::Font into a HFONT, you need to get the LOGFONT from the Gdiplus::Font. Once you do this, the rest is simple. The working solution to get number of kerning pairs is
Font* gdiFont = new Font(L"Times New Roman", 12);
Bitmap* bitmap = new Bitmap(256, 256, PixelFormat32bppARGB);
Graphics* g = new Graphics(bitmap);
LOGFONT logFont;
gdiFont->GetLogFontA(g, &logFont);
HFONT hfont = CreateFontIndirect(&logFont);
HDC hdc = GetDC(NULL);
SelectObject(hdc, hfont);
DWORD numberOfKerningPairs = GetKerningPairs(hdc, INT_MAX, NULL );
As you can tell, the only functional change I gave was to creating a FONT.
You first call it with the third parameter set to NULL, in which case it returns the number of kerning pairs for the font. You then allocate memory, and call it again passing that buffer:
int num_pairs = GetKerningPairs(your_dc, -1, NULL);
KERNINGPAIR *pairs = malloc(sizeof(*pairs) * num_pairs);
GetKernningPairs(your_dc, num_pairs, pairs);
Edit: I did a quick test (using MFC by not GDI+) and got what seemed like reasonable results. The code I used was:
CFont font;
font.CreatePointFont(120, "Times New Roman", pDC);
pDC->SelectObject(&font);
int pairs = pDC->GetKerningPairs(1000, NULL);
CString result;
result.Format("%d", pairs);
pDC->TextOut(10, 10, result);
This printed out 116 as the result.
I am trying to use GDI+ to draw text onto an image, however, I notice that with DrawString(...) there is my text, followed by several misc characters (it looks like maybe Japanese). These characters only show up when using DrawString, and I notice by saving the Bitmap to a file. Does anyone know what may be causing this? My GDI Code is
#include <windows.h>
#include <Gdiplus.h>
using namespace Gdiplus;
int main(void)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Font* myFont = new Font(L"Times New Roman", 10);
Bitmap* characterBitmap = new Bitmap(256, 256, PixelFormat32bppARGB);
Graphics* g = new Graphics(characterBitmap);
g->Clear(Color::Transparent);
SolidBrush* myBrush = new SolidBrush(Color::Black);
g->DrawString(L"TEST", 48, myFont, PointF(0, 0), myBrush);
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
characterBitmap->Save(L"test.png", &pngClsid, NULL);
GdiplusShutdown(gdiplusToken);
return 0;
}
You should read the documentation of the Graphics::DrawString function.
The second parameter should be:
Integer that specifies the number of characters in the string array. The length parameter can be set to –1 if the string is null terminated.