bmp.Save returns FileNotFound - c++

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.

Related

Saving GDI+ Metafile as BMP cropped when screen scaling not 100%

When saving a bitmap file from a GDI+ metafile object, the output is affected by the current display scale of the monitor.
For example, the following code sample generates these images:
with screen at 100% scaling (81x81 pixel bitmap generated).
with screen at 125% scaling (52x52 pixel bitmap generated).
#include <Windows.h>
// GDI+ libraries
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
void main()
{
// Startup GDI+
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
{
// Create a metafile
const auto dc = GetDC(nullptr);
const auto cdc = CreateCompatibleDC(dc);
Gdiplus::Metafile metafile{ L"test.emf", cdc };
auto graphics = Gdiplus::Graphics::FromImage(&metafile);
// Draw some things
Gdiplus::SolidBrush yellowBrush{ Gdiplus::Color::Yellow };
graphics->FillRectangle(&yellowBrush, 0, 0, 70, 70);
Gdiplus::SolidBrush redBrush(Gdiplus::Color::Red);
graphics->FillRectangle(&redBrush, 10, 10, 50, 50);
delete graphics; // Must be deleted before saving (is this the correct way?)
// Save as BMP (MIME type: image/bmp)
CLSID bmpEncoderId{1434252288, 6660, 4563, {154, 115, 0, 0, 248, 30, 243, 46}};
auto status = metafile.Save(L"test.bmp", &bmpEncoderId);
// Cleanup
DeleteDC(cdc);
ReleaseDC(nullptr, dc);
}
// Shutdown GDI+
Gdiplus::GdiplusShutdown(gdiplusToken);
}
I've tried adding the following in several places (as suggested in other SO answers) but this doesn't seem to change the behviour.
// Set scaling
Gdiplus::MetafileHeader mfh{};
metafile.GetMetafileHeader(&mfh);
graphics->ScaleTransform(mfh.GetDpiX() / graphics->GetDpiX(),
mfh.GetDpiY() / graphics->GetDpiY());
Is it possible to export the full bitmap image independent of what scaling is set on the monitor?
SOLUTION: This was caused by not compiling as DPI-aware.

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);
}

What am I doing wrong? Program crashes. [GDI+, WINAPI, C++]

I wrote a program in C++ which has to print an image on the screen and exit after 2 seconds. Everything was ok until the program has to exit. When the program is on return 0; instruction, it crashes. I think it's because of wrong dealocation, but code seems to be correct.
#include <iostream>
#include <windows.h>
#include <gdiplus.h>
using namespace std;
using namespace Gdiplus;
HDC hdc = GetDC(NULL);
int main() {
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Graphics graphics(hdc);
Rect dst(500, 500, 17, 17);
Image* image = Image::FromFile(L"image.png", false);
cout<<"Image size: "<<image->GetWidth()<<", "<<image->GetHeight()<<endl;
graphics.DrawImage(image, dst);
delete image;
GdiplusShutdown(gdiplusToken);
Sleep(2000);
return 0;
}
I've got the newest compilator and I compile by:
g++ -c main.cpp -std=g++11
g++ -o main main.o -lgdiplus
graphics destructor is executed after GdiplusShutdown. you need enclose block with
Graphics graphics(hoc);
in { }

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.

Why is Graphics::DrawString drawing misc characters?

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.