I am trying to load and resize an image. I have an OpenPictureDialog
to select the file. The dialog filters are set to *.bmp|*.jpg|*.jpeg|*.png
. I run the app and get an error when opening a PNG.
Project HDLite.exe raised exception class EInvalidGraphic with message 'Bitmap image is not valid'.
My thinking is: I'm using the <Graphics::TBitmap>
, but I'm unable to figure out which to use. iNet research indicated that <Graphics::TBitmap>
was the correct class.
void __fastcall TMainForm::LogoImageClick(TObject* Sender)
{
int AIndex = ControlList1->ItemIndex;
if (ImageModule->OpenPictureDialog1->Execute()) {
std::unique_ptr<Graphics::TBitmap> srcBitmap(new Graphics::TBitmap());
std::unique_ptr<Graphics::TBitmap> dstBitmap(new Graphics::TBitmap());
String AFileName =
ExtractFileName(ImageModule->OpenPictureDialog1->FileName);
FSettings->LogoFileName[AIndex] =
TPath::Combine(ExtractFilePath(FSettings->FileName), AFileName);
try {
srcBitmap->LoadFromFile(ImageModule->OpenPictureDialog1->FileName); //<-- ERROR
dstBitmap->SetSize(64, 64);
srcBitmap->Canvas->StretchDraw(Rect(0, 0, 64, 64), dstBitmap.get());
dstBitmap->SaveToFile(FSettings->LogoFileName[AIndex]);
} catch (...) {
MessageBeep(0);
}
}
}
As a bonus: I would also like the SaveToFile()
to always save the resized image as a PNG
.
I run the app and get an error when opening a PNG.
That is because the VCL's TBitmap
class only supports BMP images.
In VCL, different image formats are handled by different TGraphic
-derived classes, such as:
Vcl::Graphics::TBitmap
Vcl::Imaging::Jpeg::TJpegImage
Vcl::Imaging::Gifimg::TGIFImage
Vcl::Imaging::Pngimage::TPngImage
Or, you can use Vcl::Graphics::TWICImage
, which can handle any image format that Microsoft's Windows Imaging Component supports.
You need to use the appropriate class for each file type you want to load. Or, you can use Vcl::Graphics::TPicture
instead and let it figure that out for you. Its LoadFromFile()
method will look at the file extension and create an instance of the appropriate TGraphic
class, which you can then access from the TPicture::Graphic
property.
Otherwise, switch to FireMonkey instead of VCL. The Fmx::Graphics::TBitmap
class behaves the way you want - it supports multiple input formats (which vary depending on platform), and can save to PNG.
As a bonus: I would also like the SaveToFile() to always save the resized image as a PNG.
To do that in VCL, you will have to first Assign()
the modified image to a separate TPngImage
object (if you are not already resizing a TPngImage
object) and then save that.
With all of that said, try something like this:
#include <Vcl.Graphics.hpp>
#include <Vcl.Imaging.PngImage.hpp>
#include <Vcl.Imaging.Jpeg.hpp>
#include <Vcl.Imaging.GIFImg.hpp>
void __fastcall TMainForm::LogoImageClick(TObject* Sender)
{
int AIndex = ControlList1->ItemIndex;
if (ImageModule->OpenPictureDialog1->Execute()) {
try {
auto srcPicture = std::make_unique<TPicture>();
srcPicture->LoadFromFile(ImageModule->OpenPictureDialog1->FileName);
auto srcBitmap = std::make_unique<TBitmap>();
srcBitmap->Assign(srcPicture->Graphic);
auto dstBitmap = std::make_unique<TBitmap>();
dstBitmap->SetSize(64, 64);
dstBitmap->Canvas->StretchDraw(Rect(0, 0, 64, 64), srcBitmap.get());
auto dstPng = std::make_unique<TPngImage>();
dstPng->Assign(dstBitmap.get());
String AFileName = TPath::Combine(
ExtractFilePath(FSettings->FileName),
ChangeFileExt(ExtractFileName(ImageModule->OpenPictureDialog1->FileName), _D(".png"))
);
dstPng->SaveToFile(AFileName);
FSettings->LogoFileName[AIndex] = AFileName;
} catch (...) {
MessageBeep(0);
}
}
}