I have an issue with FMX on Android not showing images correctly.
My app, which is my first ever for Android, has a background and a splash image that are shown upon app start. The splash shows on top of everything, and then a timer makes it go away, and show the background.
Screenshot from Memu emulator:
Screenshot from my phone:
Source from replicated issue:
http://anbech.me/bgtest/bg_test.zip
I've been coding Pascal since Delphi 6, and I never asked about any coding related issues before this, but this is very new to me.
So far, I've been trying for 3 days to get it working, but without luck.
Currently I'm using RCDATA
resources and loading images from there. I tried to even have the images on a different form, as other TImage
components, and load from there. I also notice that if the images are set to Align=Client
, and WrapMode=Center
, then they all get misplaced on the MainForm
, but work great on other forms. So now, I tried cropping the images, which are squares, so they'll have the same aspect ratio as the device it runs on.
I could go on, because what I've tried so far is a long talk.
procedure TForm1.FormShow(Sender: TObject);
var
Bmp, BmpSplash: TBitmap;
iRect: TRect;
begin
Load_image_from_resource(Form1.Image_bg, '0_bg');
Load_image_from_resource(Form1.Image_splash, '0_splash');
Bmp := TBitmap.Create;
try
Bmp.Width := round(Form1.Image_bg.Bitmap.Width * (Form1.ClientWidth / Form1.ClientHeight));
Bmp.Height := round(Form1.Image_bg.Bitmap.Height);
iRect.Width := round(Form1.Image_bg.Bitmap.Width * (Form1.ClientWidth / Form1.ClientHeight));
iRect.Height := Bmp.Height;
iRect.Left := round((iRect.Height - iRect.Width) / 2);
iRect.Top := 0;
Bmp.CopyFromBitmap(Form1.Image_bg.Bitmap, iRect, 0, 0);
//Form1.Image_bg.Bitmap := nil;
Form1.Image_bg.Bitmap.Assign(Bmp);
Form1.Image_bg.Align := TAlignLayout.Client;
Form1.Image_bg.WrapMode := TImageWrapMode.Stretch;
finally
Bmp.DisposeOf;
Bmp := nil;
end;
BmpSplash := TBitmap.Create;
try
BmpSplash.Width := round(Form1.Image_splash.Bitmap.Width * (Form1.ClientWidth / Form1.ClientHeight));
BmpSplash.Height := round(Form1.Image_splash.Bitmap.Height);
iRect.Width := round(Form1.Image_splash.Bitmap.Width * (Form1.ClientWidth / Form1.ClientHeight));
iRect.Height := Bmp.Height;
iRect.Left := round((iRect.Height - iRect.Width) / 2);
iRect.Top := 0;
BmpSplash.CopyFromBitmap(Form1.Image_splash.Bitmap, iRect, 0, 0);
//Form1.Image_splash.Bitmap := nil;
Form1.Image_splash.Bitmap.Assign(BmpSplash);
Form1.Image_splash.Align := TAlignLayout.Client;
Form1.Image_splash.WrapMode := TImageWrapMode.Stretch;
finally
BmpSplash.DisposeOf;
BmpSplash := nil;
end;
Form1.Image_bg.SendToBack;
Form1.Image_bg.Visible := False;
Form1.Image_splash.BringToFront;
Form1.Image_splash.Visible := True;
Form1.Timer1.Enabled := True;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Form1.Timer1.Enabled := False;
Form1.Image_bg.Visible := True;
Form1.Image_splash.Visible := False;
end;
You were quite close, I think the following peculiarity in the Delphi code probably gave you most headache:
In your code you have something like this:
iRect.Width := ...
iRect.Height := ...
iRect.Left := ...
iRect.Top := ...
A TRect
(or TRectF
) is determined by its four properties: Left, Top, Right, Bottom
.
When you change the dimensions using width
(and height
), you call this setter procedure:
// This is the code in XE7, I do not know if it has been changed later
procedure TRect.SetWidth(const Value: Integer);
begin
Self.Right := Self.Left + Value;
end;
SetHeight
is very similar.
When you change Left
or Top
the corresponding fields are simply changed, but not Right
nor Bottom
. The result is that the width
or height
also changes.
Imagine a local uninitialized variable iRect
, that might have whatever values for Left
and Right
. Your code above may end up with very strange TRect
rectangles. The fix is to set Left
and Top
properties first, then either Right
and Top
or Width
and Height
.
To show the image, you required it to follow aspect ratio, which means that slices need to be cut away from the sides of the image when showing it vertically (and from top and bottom when showing it horizontally). I made this with iRect
so that it is initialized to the size of the screen, and then offset with half the difference between screen size and image size.
Here is code to show the image according to screen size. Here is only vertical display, but horizontal would follow same scheme. For brevity I also did not include the splash image, use the same code and your own change logic. The comments in the code should explain my toughts. As you see it is largely very similar as yours, your calculations of iRect.Width
and iRect.Left
were not right, probably because of the TRect
peculiarity.
// I feel more comfortable with a bmp as temporary storage than the TImage, therefore this minor mod
procedure Load_Bitmap_From_Resource(var bmp: TBitmap; res_name: String);
var InStream: TResourceStream;
begin
InStream := TResourceStream.Create(HInstance, res_name, RT_RCDATA);
try
Bmp.LoadFromStream(InStream);
finally
InStream.Free;
end;
end;
procedure TForm1.FormShow(Sender: TObject);
var
ScrHeight, ScrWidth: integer;
bmp, bmp2, BmpSplash: TBitmap;
iRect: TRect;
begin
// Form1 acted for me also as a simulation form during dev, therefore
// these settings are not meant to be included in actual application
// ***********
self.Left := 0; self.ClientWidth := 270;
self.Top := 0; self.ClientHeight := 540;
// ***********
// Use FireMonkey Platform Services, IFMXScreeService to get real size of screen
ScrHeight := 2160; // just arbitrary examples, ..
ScrWidth := 1080; // ..for testing use size of your own phone
// I feel more comfortable with a bmp as temporary storage than the TImage
bmp := TBitmap.Create;
try
Load_Bitmap_From_Resource(bmp, '0_bg');
iRect := Rect(Point(0, 0), Point(ScrWidth, ScrHeight));
iRect.Offset((bmp.Width-ScrWidth) div 2, (bmp.Height-ScrHeight) div 2);
bmp2 := TBitmap.Create;
try
bmp2.SetSize(iRect.Width, iRect.Height);
bmp2.CopyFromBitmap(bmp, iRect, 0, 0);
Form1.Image_bg.Bitmap.Assign(bmp2);
Form1.Image_bg.Align := TAlignLayout.Client;
finally
bmp2.DisposeOf;
end;
finally
bmp.DisposeOf;
end;
Form1.Image_bg.Visible := True;
end;