Cannot Convert GDI Plus Bitmap to Base 64

I cannot convert a GDI+ bitmap to base 64.

I have so far:
1. Downloaded and included this library: https://github.com/ReneNyffenegger/cpp-base64

2. Written the function that you can see below.

3. Called it, passing in a bitmap that I know has data. I know that the bitmap has data because I am using in another function called directly after this one.

The problem is that the charPixels array is always full of zeros.

Secondly can you please explain to me what the safe way of unlocking the bits is. I fear that if I just do it in the finally, if the bits aren't actually locked at that point I will get an exception.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
std::string GdiBitmapToBase64(Gdiplus::Bitmap* gdiBitmap, int width, int height)
{
	unsigned char* charPixels = nullptr;

	try
	{
		Gdiplus::Rect rect = Gdiplus::Rect(0, 0, width, height);

		Gdiplus::BitmapData gdiBitmapData;
		gdiBitmap->LockBits(&rect, Gdiplus::ImageLockMode::ImageLockModeRead, PixelFormat32bppARGB, &gdiBitmapData);
		charPixels = new unsigned char[height * gdiBitmapData.Stride];
		
		memset(charPixels, 0, height * gdiBitmapData.Stride);

		memcpy(charPixels, gdiBitmapData.Scan0, gdiBitmapData.Stride * height);

		std::string ret = base64_encode(charPixels,  gdiBitmapData.Stride * height);
		gdiBitmap->UnlockBits(&gdiBitmapData);
		return ret;
	}
	finally
	{
		if(charPixels != nullptr)
		{
			delete[] charPixels;
		}
	}
}
Last edited on
¿there is `finally' in c++?

> what the safe way of unlocking the bits is
¿what does that mean? ¿what is locking bits?

> the charPixels array is always full of zeros.
check out gdiBitmapData values in a debugger
its Stride and Scan0 members.
-there is `finally' in c++?
Yes


> ¿what does that mean? ¿what is locking bits?
See https://docs.microsoft.com/en-us/windows/win32/api/gdiplusheaders/nf-gdiplusheaders-bitmap-unlockbits


,>what the safe way of unlocking the bits is
Ensuring if the bits are locked they will be unlocked even if there is an error and that they are not unlocked if the program never is able to lock in the first place


> check out gdiBitmapData values in a debugger
its Stride and Scan0 members

I tried to inspect Scan0, but it caused an 'internal compiler error' (not in front of the computer so those words are not exact). I will tell you the stride when I get home from work.
Last edited on
-there is `finally' in c++?
Yes

No.
you can create something like finally, but there isn't anything that is immediately like it. You don't need it, though: creating it is probably a design fubar, indicating a copy from another language?
I believe that the Stride can be negative. Maybe try something like:

1
2
3
auto stride = gdiBitmapData.Stride;
if (stride < 0) stride = -stride;
//... 

No stride is a positive number it is: 1280.

Here is the code which calls this method. This may help:
1
2
3
4
5
6
7
8
9
10
11
12
13
void CLIScumm::Wrapper::ScreenUpdated(const void* buf, int pitch, int x, int y, int w, int h, PalletteColor* color)
{
	const unsigned char* bufCounter = static_cast<const unsigned char*>(buf);
	for (int hightCounter = 0; hightCounter < h; hightCounter++, bufCounter = bufCounter + pitch)
	{
		for (int widthCounter = 0; widthCounter < w; widthCounter++)
		{
			PalletteColor currentColor = *(color + *(bufCounter + widthCounter));
			gdiBitmap->SetPixel(x + widthCounter, y + hightCounter, Gdiplus::Color(currentColor.r, currentColor.g, currentColor.b));
		}
	}
	_screenUpdated->Invoke(gcnew System::String(GdiBitmapToBase64(gdiBitmap, DISPLAY_DEFAULT_WIDTH, DISPLAY_DEFAULT_HEIGHT).c_str()));
}


And the declarations:
1
2
3
4
5
6
7
8
9
10
namespace CLIScumm {
	public ref class Wrapper {
	...
	private:
         ...
		Gdiplus::Graphics* gdiGraphics;
		Gdiplus::Bitmap* gdiBitmap;
         ...
	};


And the initialization:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void CLIScumm::Wrapper::init()
{
	if (!hasStarted)
	{
		try
		{
			if (!hasStarted)
			{
				...

				Gdiplus::GdiplusStartupInput gdiplusStartupInput;
				Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);

(malloc(sizeof(100000) * DISPLAY_DEFAULT_HEIGHT * DISPLAY_DEFAULT_WIDTH));
				gdiBitmap = new Gdiplus::Bitmap(DISPLAY_DEFAULT_WIDTH, DISPLAY_DEFAULT_HEIGHT, PixelFormat32bppARGB);
				gdiGraphics = new Gdiplus::Graphics(gdiBitmap);
				InitImage();
                            ...
			}
		}
		...
	}
}
No stride is a positive number it is: 1280.

How do you know? It's common for it to be negative.

What is this strange line of code supposed to do? It's allocating memory and immediately leaking it.

 
(malloc(sizeof(100000) * DISPLAY_DEFAULT_HEIGHT * DISPLAY_DEFAULT_WIDTH));

sizeof(100000) is just the size of an int, probably 4.
Ignore the strange line of code; that is commented out on my PC.

I am confused clearly 1280 is a positive number.
Are you saying that stride might not always be the same number even for images of identical format and size?
I tried putting in your line, because it should probably in there anyway.

No luck. Putting in that line did not fix my issue.
clearly 1280 is a positive number

Clearly the number you've typed in your post and said was the Stride is positive, but that doesn't mean you're right. You never said where you got it from. Did you actually print out Stride in the code? Or are you just "calculating" it from a width of 426?

If I create and save a default 24-bit bitmap, the "height" member is stored as positive, which means that the pixels are store in a bottom-up manner, which means that the Stride is negative. So you are saying that your height member is stored as negative, indicating a positive stride?

If you could post a link to a complete (but minimal) program that demonstrates the problem, I'm sure someone can help you. If an input bmp is needed, or any other input, provide that too.
Last edited on
Ok. I am on it. I will soon provide the example you asked for.
Thank you all for you help, but I solved my own problem.

I was confusing bitmap pixel data with the actual bytes of a bitmap, and Scan0 is the former. The reason I was getting only zero's is because the first few frames were black.

I followed the example from https://stackoverflow.com/questions/51383896/c-gdibitmap-to-png-image-in-memory to get the actual bitmap bytes.

I modified the example function to:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
bool SavePngMemory(Gdiplus::Bitmap* gdiBitmap, std::vector<BYTE>& data)
{
	//write to IStream
	IStream* istream = nullptr;
	CreateStreamOnHGlobal(NULL, TRUE, &istream);

	CLSID clsid_bmp;
	CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}", &clsid_bmp);
	Gdiplus::Status status = gdiBitmap->Save(istream, &clsid_bmp);
	if (status != Gdiplus::Status::Ok)
		return false;

	//get memory handle associated with istream
	HGLOBAL hg = NULL;
	GetHGlobalFromStream(istream, &hg);

	//copy IStream to buffer
	int bufsize = GlobalSize(hg);
	data.resize(bufsize);

	//lock & unlock memory
	LPVOID pimage = GlobalLock(hg);
	memcpy(&data[0], pimage, bufsize);
	GlobalUnlock(hg);

	istream->Release();
	return true;
}


I was then able to convert data to base64 by calling:

 
base64_encode(&data[0], data.size());


I can't vouch for the quality of the code, because I don't understand how everything works.
Topic archived. No new replies allowed.