Reputation: 21
I don't use c++ or MFC/ATL etc so assume I know nothing.
The whole picture is that I need to take accept a PNG with a transparency layer and write it out as a JPEG with specified compression to pass to another system.
What I would like to know here, is how can I initialize a target CImage structure with solid white?
I've done this so far (yes I know it has other stylistic issues)
void ClipBoardFuncs::savePNGASJPEG(char filePath[256], char errorBuff[256]) {
int sizeOfErrorBuff = 256;
CImage imagePNG;
CImage imageJPG;
int xPNG, yPNG = 0;
imagePNG.Load(filePath);
xPNG = imagePNG.GetWidth();
yPNG = imagePNG.GetHeight();
//Create my target JPG same size and bit depth specifiying that there is no alpha channel (dwflag last param)
imageJPG.Create(xPNG, yPNG, imagePNG.GetBPP(), 0);
//Let there be white....
for (int x = 0; x <= xPNG; x++)
{
for (int y = 0; y <= yPNG; y++)
{
imageJPG.SetPixelRGB(x, y, 255, 255, 255);
}
}
//Copy the image over onto on the white
//BitBlt copies everything....
//imagePNG.BitBlt(imageJPG.GetDC(), 0, 0);
//Draw is more like the C# draw examples I've seen
imagePNG.Draw(imageJPG.GetDC(), 0, 0);
imageJPG.ReleaseDC();
HRESULT hr = NULL;
hr = imageJPG.Save(filePath, Gdiplus::ImageFormatJPEG);
imagePNG.Destroy();
imagePNG.ReleaseGDIPlus();
imageJPG.Destroy();
imageJPG.ReleaseGDIPlus();
LPCTSTR error = _com_error(hr).ErrorMessage();
strncpy_s(errorBuff, sizeOfErrorBuff, _com_error(hr).ErrorMessage(), _TRUNCATE);
}
The lovely C# people have this answer:
Convert Transparent PNG to JPG with Non-Black Background Color
But I need the c++ MFC solution to use as an exported function in a DLL.
By exported Function I mean the same architecture as you would find in kernel32.dll - sorry I do not know the terminology to differentiate that kind of DLL from one stuffed with COM objects.
Can anyone suggest a faster way to initialize the imageJPEG structure to solid white than the nested x/y for loops I have here?
Cheers
4GLGuy
Upvotes: 2
Views: 1453
Reputation: 490318
The initialization can be done with Rectangle
or FillRect
(for this, FillRect
is probably preferred--Rectangle
draws an outline of the rectangle in the current pen color, which we probably don't want).
So, the sequence looks something like this:
CImage png;
png.Load(pDoc->filename);
CRect rect{ 0, 0, png.GetWidth(), png.GetHeight() };
CImage jpeg;
jpeg.Create(rect.Width(), rect.Height(), png.GetBPP());
auto dc = jpeg.GetDC();
HBRUSH white = CreateSolidBrush(RGB(255, 255, 255));
FillRect(dc, &rect, white);
png.Draw(dc, 0, 0);
jpeg.ReleaseDC();
jpeg.Save(L"Insert File name here", Gdiplus::ImageFormatJPEG);
jpeg.Destroy();
jpeg.ReleaseGDIPlus();
png.ReleaseGDIPlus();
GDI+ is "smart" enough that when you do a .Draw
with an image that has an alpha channel, it takes that channel into account, without your having to use TransparentBlt
(or anything similar).
SetTransparentColor
will not work for what you're trying to do here. SetTransparentColor
is for an image that does not have an alpha channel ("transparency layer"). You then use it to choose a color that will be treated as if it were transparent--which can certainly be useful, but isn't what you want here.
You can use memset
instead, but only for colors where the red, green, and blue channels all have the same values (i.e., black, white, or some shade of grey). Otherwise, you can do the fill on your own with a nested loop, but in most cases you probably want to use FillRect
instead (it may be able to use graphics hardware for acceleration, where the loop will pretty dependably just run on the CPU--worst case, they're both about the same speed, but in some cases FillRect
will be faster).
Upvotes: 1
Reputation: 31609
imagePNG.Draw(imageJPG.GetDC(), 0, 0);
Each call to GetDC
must have a subsequent call to ReleaseDC
. See also CImage::GetDC
documentation.
CImage::GetDC
provides a handle to memory device context. This handle can be used to draw using standard GDI functions. The handle should later be cleaned up with CImage::ReleaseDC
.
CImage::Draw
may not know what the transparent color is. You have to use TransparentBlt
to tell it what the transparent color is. For example, to replace red color with white color:
HDC hdc = imageJPG.GetDC();
CDC dc;
dc.Attach(hdc);
CRect rc(0, 0, xPNG, yPNG);
dc.FillSolidRect(&rc, RGB(255, 255, 255));
dc.Detach();
imagePNG.TransparentBlt(hdc, rc, RGB(255, 0, 0));//replace red with white
imageJPG.ReleaseDC();
...
imageJPG.Save(...);
Or just use CImage::SetTransparentColor
:
imagePNG.SetTransparentColor(RGB(255, 0, 0));
imagePNG.Draw(hdc, rc);
for (int x = 0; x <= xPNG; x++){...}
To do this using a loop, change the condition in the loop to x < xPNG
and x < yPNG
.
Upvotes: 0