More hassles with writing to images on FMX

More problems with FMX, writing to images. Here is the programme in text form (needs 4 buttons and a memo), with comments as to which attempt does what.

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects, FMX.Memo.Types,
  FMX.ScrollBox, FMX.Memo;

type
  TForm1 = class(TForm)
    ButtonGo: TButton;
    ButtonExit: TButton;
    ButtonN1: TButton;
    Image1: TImage;
    ButtonN2: TButton;
    Memo1: TMemo;
    ButtonN3: TButton;
    procedure ButtonExitClick(Sender: TObject);
    procedure ButtonGoClick(Sender: TObject);

    procedure DoIt(Form : tForm; var Image1 : tImage);
    procedure ButtonN1Click(Sender: TObject);
    procedure ButtonN2Click(Sender: TObject);
    procedure ButtonN3Click(Sender: TObject);

   private
      { Private declarations }
      ImageTop : tImage;
   public
      { Public declarations }

  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.ButtonExitClick(Sender: TObject);
begin
   Application.Terminate;
end;

procedure TForm1.ButtonGoClick(Sender: TObject);
begin
//Briefly flashes the image.
   DoIt(Form1, ImageTop);
end;




procedure TForm1.ButtonN1Click(Sender: TObject);
var
   rect : tRectF;
   brush : tBrush;
begin
//Briefly flashes the image
   brush := tBrush.Create(tBrushKind.Solid, tAlphaColorRec.Springgreen);

   if Image1.Bitmap = nil then Image1.Bitmap := tBitmap.Create(200, 200);

   rect  := tRectF.Create(50, 50, 100, 100);
try
   Image1.Canvas.BeginScene;
   Image1.Canvas.FillRect(rect, 1, brush);
   Image1.Canvas.EndScene;
except
     Memo1.Lines.Add('Error on N1');
end;
   Memo1.GoToTextEnd;
   brush.Destroy;
end;




procedure TForm1.ButtonN2Click(Sender: TObject);
var
   rect : tRectF;
   brush : tBrush;
begin
//Comes up with Handle noit allocated, then access violation reading address 8
   brush := tBrush.Create(tBrushKind.Solid, tAlphaColorRec.Springgreen);

   if Image1.Bitmap = nil then Image1.Bitmap := tBitmap.Create(200, 200);

   rect  := tRectF.Create(50, 50, 100, 100);
try
   Image1.Bitmap.Canvas.BeginScene;
   Image1.Bitmap.Canvas.FillRect(rect, 1, brush);
   Image1.Bitmap.Canvas.EndScene;
except
   Memo1.Lines.Add('Error on N2');
end;
   Memo1.GoToTextEnd;
   brush.Destroy;
end;





procedure TForm1.ButtonN3Click(Sender: TObject);
var
   rect : tRectF;
   brush : tBrush;
begin
//Comes up with accessv violation reading address 20
   brush := tBrush.Create(tBrushKind.Solid, tAlphaColorRec.Springgreen);

   if Image1.Bitmap = nil then Image1.Bitmap := tBitmap.Create(200, 200);

   rect  := tRectF.Create(50, 50, 100, 100);
try
   Image1.Canvas.Bitmap.Canvas.BeginScene;
   Image1.Canvas.Bitmap.Canvas.FillRect(rect, 1, brush);
   Image1.Canvas.Bitmap.Canvas.EndScene;
except
   Memo1.Lines.Add('Error on N3');
end;
   Memo1.GoToTextEnd;
   brush.Destroy;
end;




procedure tForm1.DoIt(Form : tForm; var Image1 : tImage);
var
   rect : tRectF;
   brush : tBrush;
begin
// Trying to see if I can do all this via a subroutine.
   brush := tBrush.Create(tBrushKind.Solid, tAlphaColorRec.Springgreen);

   if Image1 = nil then
   begin
      Image1 := tImage.Create(Form);
      Image1.Bitmap := tBitmap.Create(200, 200);
      Image1.Parent := Form;
      Image1.BringToFront;
   end;
   rect  := tRectF.Create(50, 50, 100, 100);
try
   Image1.Canvas.BeginScene;
   Image1.Canvas.FillRect(rect, 1, brush);
   Image1.Canvas.EndScene;
except
   Memo1.Lines.Add('Error on DoIt');
end;
   Memo1.GoToTextEnd;
   brush.Destroy;
end;


end.

Think I’ve got it worked out. I’ll have anoither go at it in the morning. All day on it so far has been too much!

Essay on writing to Android Images:

I would LOVE some comments on this to see if I have (finally) got it right!

Sorry about the formatting, but this wretched system deletes all leading spaces in a line, or you have to put it all in a black box with all sorts of coloured bits interspersed throughout the text!

I started out to get around the latest android headache of giving us the whole
screen,status and navigation bars included, without it even being an option!
To do this requires putting dark-coloured bars in the areas of the system and
navigation bars and restricting the area for programme stuff so it is not
scribbled on by the bars, and your buttons work, instead of being damned to
inactivity by system buttons.

AsI have a couple of hundred of these, I wanted to make a subroutine capable of
knowing which bars needed to go where, the size of the now-restricted area with
various appropriate offsets. To do this I had to gain a greater understanding of
writing to images, and put it all down so that when I have to do it next I can
use it without all the experimentation all over again!

If you write to the form’s canvas, like

Image1.Canvas.BeginScene;
Image1.Canvas.FillRect(rect, 1, brush);
Image1.Canvas.Fill.Color := tAlphaColorRec.Black;
Image1.Canvas.FillText(rect, ‘Hello’, false, 1,
[TFillTextFlag.RightToLeft], tTextAlign.Leading, tTextAlign.Leading);
Image1.Canvas.EndScene;

you will wonder why it does not work, with no error message. If you step through
it with the debugger, you will see your rectangle, which, as soon as you finish,
disappears.

The Image1.Paint subroutine gets called at the end of everything before going
back to the user, and this cleans out anything like what we added above, so you
never see it.

Now consider

Image1.Canvas.BeginScene;
Image1.Canvas.FillRect(rect, 1, brush);
Image1.Canvas.Fill.Color := tAlphaColorRec.Black;
Image1.Canvas.FillText(rect, ‘Hello’, false, 1,
[TFillTextFlag.RightToLeft], tTextAlign.Leading, tTextAlign.Leading);
Image1.Canvas.EndScene;

This looks as though it should write to the image, doesn’t it? Not quite!
Somewhere in the dim dark past I read that the canvas of an image is actually
just a chunk of the canvas of the parent of that image, so if the image is on
the form, its canvas is Form.Canvas (or at lease a part thereof). The same
applies as for the last section - you can step through and see your efforts,
and watch them disappear before your eyes!

Let’s look at a slight variation:

Image1.Bitmap.Canvas.BeginScene;
Image1.Bitmap.Canvas.FillRect(rect, 1, brush);
Image1.Bitmap.Canvas.Fill.Color := tAlphaColorRec.Black;
Image1.Bitmap.Canvas.FillText(rect, ‘Hello’, false, 1,
[TFillTextFlag.RightToLeft], tTextAlign.Leading, tTextAlign.Leading);
Image1.Bitmap.Canvas.EndScene;

Here, the canvas is not part of the underlying form, and this bitmap is painted
happily on the form (probably in the Form.Paint subroutine). Yay!!
However, there are a few caveats!

Let’s look at the version where the images are passed to a subrouting (so there
is only one in the programme, rather than one in every form. (I have reduced
this to bedrock, without all the other stuff needed to get the dimensions of
the bars and the orientation.)

procedure tForm1.DoIt(Form : tForm; var Image1 : tImage);
var
rect : tRectF;
brush : tBrush;
begin
brush := tBrush.Create(tBrushKind.Solid, tAlphaColorRec.Springgreen);
//(No, this won’t be the final colour, but is great for testing!)

Make sure that the image exists:

if Image1 = nil then
begin
Image1 := tImage.Create(Form);
Image1.Parent := Form;
end;

//Now we know that we have an image, but this image has no bitmap.
// Trying to use it causes interesting errors which do not lead
// to knowing where the error is, but you eventually find it!

if Image1.Bitmap = nil then
Image1.Bitmap := tBitmap.Create;

//Now we have an image and an image.bitmap. This bitmap does NOT
// have a canvas, and trying to use it again leads to confusing
// error messages! To get a canvas for it, use

Image1.SetSize(ClientWidth, 50);

//NOW we are ready to use the image to write on. However, there
// is still some housekeeping to be done:

//Set up the size of the image

Image1.Height := 50;
Image1.Width := ClientWidth;

//Where we want the image to be

Image1.Position.X := 0;
Image1.Position.Y := ClientHeight - 50;

//And make sure it is in front so it will be seen - if it sneaks to
// the back it can disappear and cause a lot of head scratching!

Image1.BringToFront;

rect := tRectF.Create(0, 0, ClientWidth, 50);

Image1.BitmapCanvas.BeginScene;
Image1.Bitmap.Canvas.FillRect(rect, 1, brush);
Image1.Bitmap.Canvas.EndScene;

brush.Destroy;
end;

Can you explain what end result you’re trying to achieve?

Why do you need to calculate the status and navigation bars for multiple screens? Can’t you just place everything on a TLayout or similar and set the padding to keep the contents away from those areas?

Do you need to draw the text manually or can you use a TLabel as an overlay?

G’day, David.

I have, in one programme, 25 forms, all of which have multiple components whose positions change with screen orientation in any of 3 positions, and there may or may not be a keyboard showing.

I have never used a layout - just move and size the items where I want them in the Form.Resize. This was no problem until android 15 brought in using the status and navigation bar areas, which rather spoil the appearance of things at the edges of the screen now.

If I go to using a layout, I will still have to change where everything is in the layout depending upon orientation as above. The status bar is always at the top of the screen, but the navigation bar may be to the left, the right or the bottom of the screen. There are limitations of the navigation bar: it seems to run with an opacity, so you can’t change its colour very much, whereas you can make the status bar whatever colour you like (I use black).

Whichever way I go, I have a lot of work revamping all the Form.Resize routines in every accursed programme I have written, not just the main one on which I have been sweating for the last 9 months to make it use newer android funnies (it was started in 2008 on android 7).

Thank you for your ideas though.

David, I have had an interesting week trying to get around the android 15 business of giving you all the screen to play with, even if it makes your status bar unreadable and obliterates the buttons covering them with the navigation bar.

It has been sheer hell! I had no idea the convolutions of handling images across forms, in both Delphi and FMX.

Yes, it finally works, and will probably never be wanted by anyone else on the planet, but it is written in case anyone may like to be able to read his or her status bar!