Why is Graphics::DrawString drawing misc characters? - c++

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.

Related

bmp.Save returns FileNotFound

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.

How to create a HICON from base64?

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 '&' ...

C++ Memory management failing when deleting Bitmap and CLSID objects using GDI+

I am unable to manage memory for Bitmap and CLSID objects I have created in a screenshot object class. Both of these are from the GDI+ library. The header lists the following private variables in Screenshot.h
#include <gdiplus.h>
#include <iostream>
#include <fstream>
#include <string>
#include "windows.h"
#pragma once
#pragma comment(lib, "gdiplus.lib")
using namespace std;
using namespace Gdiplus;
class Screenshot
{
private:
HDC dc, memdc, fontdc;
HBITMAP membit;
Bitmap* bmpPtr;
CLSID clsid;
ULONG_PTR gdiplusToken;
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);
public:
Screenshot();
~Screenshot();
void TakeScreenshot(string userAction, string winName, long xMousePos, long yMousePos, long long tStamp);
void SaveScreenshot(string filename);
void memoryManagement();
};
Then when my main program takes a screenshot, the values are filled in with TakeScreenshot(), but not yet saved to disk
void Screenshot::TakeScreenshot(//redacted for readibility) {
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HWND hwnd = GetDesktopWindow();
dc = ::GetDC(0);
int scaleHeight, scaleWidth = 0;
int Height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int Width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
scaleHeight = Height + (0.1 * Height);
memdc = CreateCompatibleDC(dc);
membit = CreateCompatibleBitmap(dc, Width, scaleHeight);
HBITMAP bmpContainer = (HBITMAP)SelectObject(memdc, membit);
BitBlt(memdc, 0, 0, Width, Height, dc, 0, 0, SRCCOPY);
//Other code that adds fonts, etc. Does not invoke bmpPtr
bmpPtr = new Bitmap(membit, NULL);
GetEncoderClsid(L"image/jpeg", &clsid);
If the screenshot is saved, another function SaveScreenshot() uses bmpPtr->Save() and Gdiplus shutdown is called inside of it. However, some of the screenshots get popped off from a queue (STL queue) and out of memory instead of saved, as follows:
void ManageQueue(Screenshot& ssObj)
{
//If queue contains 30 screenshots, pop off first element and push new object
//Else just push new object
if (screenshotQueue.size() == MAX_SCREENSHOTS)
{
screenshotQueue.front().memoryManagement();
screenshotQueue.pop();
screenshotQueue.push(ssObj);
}
else
{
screenshotQueue.push(ssObj);
}
}
I wrote a MemoryManagement() function to perform the necessary releases and deletes before the Screenshot is popped off. This function is not called if the screenshot has been saved:
void Screenshot::memoryManagement()
{
delete bmpPtr;
delete &clsid;
ReleaseDC(NULL, memdc);
DeleteObject(fontdc);
DeleteObject(memdc);
DeleteObject(membit);
}
When either the delete on bmpPtr or clsid is called, whether it is from this function or in the deconstructor, the program is crashing. I am experiencing significant memory leaks with the program now and without running a windows equivalent of Valgrind I'm assuming it's coming from here. How can I successfully delete these objects? I will credit any answer in my source code as a contributing programmer. Please leave any suggestions for improving my question if needed.
scaleHeight = Height + (0.1 * Height);
This seems to be an attempt to fix the problem with DPI scaling. It will work if DPI settings is at 10%, but that's usually not the case. You have to make your program DPI aware through the manifest file. Use SetProcessDPIAware for a quick fix.
Don't declare dc, memdc, etc. as class members. These are GDI handles (not GDI+) which you can hold for a short time, usually during the duration of the function. You have to release them as soon as possible.
Also other variables like clsid don't need to be declared as class members. You can declare them as class member if you wish, but there is nothing to gain.
If you have a multi-monitor setup you also need SM_XVIRTUALSCREEN/Y to get the top-left corner of the monitor setup.
//call this once on start up
SetProcessDPIAware();
HDC dc = ::GetDC(0);
int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
int Height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int Width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
HDC memdc = CreateCompatibleDC(dc);
HBITMAP membit = CreateCompatibleBitmap(dc, Width, Height);
HBITMAP bmpContainer = (HBITMAP)SelectObject(memdc, membit);
BitBlt(memdc, 0, 0, Width, Height, dc, x, y, SRCCOPY);
Bitmap* bmpPtr = new Bitmap(membit, NULL);
// or just Bitmap bmp(membit, NULL);
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
bmpPtr->Save(L"output.jpg", &clsid);
//cleanup:
delete bmpPtr;
SelectObject(memdc, bmpContainer);
DeleteObject(membit);
DeleteDC(memdc);
ReleaseDC(0, dc);
The solution for this problem was to use the namespace delete instead of regular delete. Switching to this prevented the break point trigger during debug and has sealed the memory leak.
void Screenshot::memoryManagement()
{
::delete bmpPtr;
ReleaseDC(NULL, memdc);
DeleteObject(fontdc);
DeleteObject(memdc);
DeleteObject(membit);
GdiplusShutdown(gdiplusToken);
}

ColorBlend not found

#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
then draw some text:
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HDC hdc;
Font *fn = new Font(TEXT("Arial"),50);
hdc=GetDC(hWnd);
ColorBlend cb = new ColorBlend();
LinearGradientBrush *linGrBrush=new LinearGradientBrush(
Point(0, 10),
Point(200, 10),
Color(255, 255, 0, 0),
Color(255, 0, 0, 255));
Graphics *graphics=new Graphics(hdc);
PointF drawPoint = PointF(150.0F,150.0F);
SolidBrush* myBrush = new SolidBrush(Color::Black);
graphics->DrawString(L"Test text",strlen("Test text"),fn,drawPoint,linGrBrush);
GdiplusShutdown(gdiplusToken);
And had error that ColorBlend not found identifier,but seem all right. How I can fix it?
The ColorBlend class is part of the .Net Framework, as far as I can tell there is nothing by that name in GDI+ for C++.
I think the corresponding function in GDI+ is LinearGradientBrush::SetInterpolationColors
As far as I understand the .NET documentation the InterpolationColors member in GDI+ is used here with this function.

Obtaining kerning information

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.