Recent

Author Topic: Trapping non-available resource  (Read 580 times)

J-G

  • Hero Member
  • *****
  • Posts: 884
Trapping non-available resource
« on: June 10, 2022, 04:25:18 pm »
I've successfully fathomed how to dynamically determine the name of a resouce and load the bitmap into a TImage, including re-dimentioning that. However, I now need to trap the fact that the image wanted doesn't exist  ---  ie. I have images of Gears, but not every tooth-count, so if the tooth count is calculated at 66 then the file name will be 66T.png but that image doesn't exist as an RCDATA in the [Resourse] list.

I could of course manually create a list of available images in an array and check that before calling 'LoadFromResource' but I suspect that there should be a means by which I can [Read] the resource list, or at least check the existance in a similar way that I can check whether a file exists using {$I-} {$I+} and IOResult   -- which I have tried :
Code: Pascal  [Select][+][-]
  1. {$I-} tmp := tmp.LoadFromResource(Name); {$I+}
  2.   if IOResult <> 0 then
  3.     begin
  4.       Form1.MissingImage.Caption := Copy(Name,1,Length(name)-4)+' Image not available';
  5.     end
  6.   else
... but does not trap it.

I've also tried at the point of creation ...
Code: Pascal  [Select][+][-]
  1.   {$I-} tmp := TBGRAbitmap.create(Name); {$I+}  
... though I didn't think this would be successful even though it does use the 'Name'.

Am I simply barking up the wrong tree?  or is there a method I'm missing?

FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

Handoko

  • Hero Member
  • *****
  • Posts: 4712
  • My goal: build my own game engine using Lazarus
Re: Trapping non-available resource
« Reply #1 on: June 10, 2022, 05:30:15 pm »
You can use exception.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. const
  3.   ImageName = 'IMG1';
  4. var
  5.   Image: TImage;
  6. begin
  7.   Image := TImage.Create(nil);
  8.   try
  9.     Image.Picture.LoadFromResourceName(HInstance, ImageName);
  10.   except
  11.     ShowMessage('Cannot find image named ' + ImageName + '.');
  12.   end;
  13.   Image.Free;
  14. end;

Read more:
https://www.freepascal.org/docs-html/ref/refch17.html
https://wiki.freepascal.org/Exceptions

J-G

  • Hero Member
  • *****
  • Posts: 884
Re: Trapping non-available resource
« Reply #2 on: June 10, 2022, 06:02:50 pm »
Thank Handoko, I've tried (and failed) to use [Try][Except] - probably because I don't (yet) fully understand the concept  :-[

This is my interpreation of your suggestion :
Code: Pascal  [Select][+][-]
  1. begin
  2.   Form1.MissingImage.Hide;
  3.   tmp := TBGRAbitmap.create(Name);
  4.   Try
  5.     tmp.LoadFromResource(Name);
  6.   except
  7.     begin
  8.       Form1.MissingImage.Caption := Copy(Name,1,Length(name)-4)+' Image not available';
  9.       Form1.MissingImage.Show;
  10.       Exit;
  11.     end
  12.   end;
  13.       dest := TBGRAbitmap.create;
  14.       BGRAReplace(dest, tmp.FilterRotate(PointF(X,Y),angle,true));
  15.      ...
  16.  

When the image IS available then it works perfectly, testing and then moving on to the 'dest:=' part but if the image doesn't exist it doesn't get into the [except] section.

This may well be because I'm not using the same "Image.Picture.LoadFromResourceName(HInstance, ImageName);"
method that you site.


FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

Handoko

  • Hero Member
  • *****
  • Posts: 4712
  • My goal: build my own game engine using Lazarus
Re: Trapping non-available resource
« Reply #3 on: June 10, 2022, 07:19:41 pm »
Don't give up easily, friend.

Here is a working demo:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, Forms, Controls, Graphics, StdCtrls, ExtCtrls, BGRABitmap, uniError;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     btn1: TButton;
  16.     btn2: TButton;
  17.     btn3: TButton;
  18.     Image1: TImage;
  19.     procedure btn1Click(Sender: TObject);
  20.     procedure btn2Click(Sender: TObject);
  21.     procedure btn3Click(Sender: TObject);
  22.   private
  23.     procedure LoadImage(const ResName: string);
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. {$R *.lfm}
  32.  
  33. { TForm1 }
  34.  
  35. procedure TForm1.btn1Click(Sender: TObject);
  36. begin
  37.   LoadImage('IMG1');
  38. end;
  39.  
  40. procedure TForm1.btn2Click(Sender: TObject);
  41. begin
  42.   LoadImage('IMG2');
  43. end;
  44.  
  45. procedure TForm1.btn3Click(Sender: TObject);
  46. begin
  47.   LoadImage('IMG3');
  48. end;
  49.  
  50. procedure TForm1.LoadImage(const ResName: string);
  51. var
  52.   tmpBitmap: TBGRABitmap;
  53. begin
  54.   tmpBitmap := TBGRABitmap.Create;
  55.   try
  56.     try
  57.       tmpBitmap.LoadFromResource(ResName);
  58.       Image1.Picture.Bitmap.Assign(tmpBitmap);
  59.     except
  60.       frmError.SetMessage(ResName + ' cannot be found.');
  61.       frmError.ShowModal;
  62.     end;
  63.   finally
  64.     tmpBitmap.Free;
  65.   end;
  66. end;
  67.  
  68. end.

I personally rarely use exception. But for your case, because you create something in memory, you can't simply exit when exception happens. You should put a try-finally block (see line #64).

Download the file and run it. To better understand what will really show on user screen, you should run it directly from the binary, don't run it from your Lazarus IDE.

This demo will cause an exception if you click the third button 'IMG3' because the image doesn't exist in the resources.
« Last Edit: June 10, 2022, 07:21:42 pm by Handoko »

J-G

  • Hero Member
  • *****
  • Posts: 884
Re: Trapping non-available resource
« Reply #4 on: June 10, 2022, 08:30:22 pm »
Don't give up easily, friend.
:D :D  That I very seldom do!  - I'm known for my stubborn determination to get my way  ::)
Quote from: Handoko
Here is a working demo:
Works perfectly (of course)
Quote from: Handoko
I personally rarely use exception. But for your case, because you create something in memory, you can't simply exit when exception happens. You should put a try-finally block (see line #64).
As you can tell, I've also avoided the [Try][Except][Finally]  -  I did consider that a [Finally] might be needed but didn't know not to use [Exit].

I have solved the problem by using a referenced list (array) as a 'look-up'  but it's not a perfect solution since that list can be modified by the user without necessarily adding to the resource.

My latest 'trial' code - based again upon your excellent suggestion - though naturally including the extra code needed to complete the task in hand - is:
Code: Pascal  [Select][+][-]
  1. procedure Rotate(Pic:TImage;Name:string;Angle:single;X,Y:word;T:byte);
  2. Var
  3.   tmp, dest : TBGRABitmap;
  4. begin
  5.   tmp := TBGRAbitmap.create(Name);
  6.   try
  7.     try
  8.       tmp.LoadFromResource(Name);
  9.     except
  10.       Form1.MissingImage.Caption := Copy(Name,1,Length(name)-4)+' Image not available';
  11.       Form1.MissingImage.Show;
  12.     end;
  13.   finally
  14.     dest := TBGRAbitmap.create;
  15.     BGRAReplace(dest, tmp.FilterRotate(PointF(X,Y),angle,true));
  16.  
  17.     dest.ReplaceTransparent(clRed);
  18.     Pic.Picture.Clear;
  19.     Pic.Picture.Bitmap.SetSize(tmp.Width,tmp.Height);
  20.     Pic.Picture.Bitmap.TransparentColor:=clRed;
  21.     Pic.Transparent:=true;
  22.  
  23.     dest.Draw(Pic.Picture.Bitmap.Canvas,0,0);
  24.  
  25.     application.ProcessMessages;
  26.     dest.free;
  27.     tmp.free;
  28.   end;
  29. end;
  30.  

It doesn't trap the missing image  - - -  well it throws an error when run under the IDE in Debug mode, so I assume that would mean the same directly from the .exe (your comment to run your program from the binary).

I'm obviously missing a 'key' to jumping out of the routine after line 11 : (Form1.MissingImage.Show;)   since even though the [Try] may have failed it will continue into the [Finally] section which I only want to happen if the [Try] is successful but I can't (yet) fathom how to accomplish that  :( :-[ :-[   

I can understand [If][Then][Else] but the [Try]..... is currently defeating my aging grey matter!

. . . . . . . .    Just 'thinking' . . . . . . .  If I put a boolean [True] in the [Finally] - or somewhere where [Try] has been successful - and use that to determine whether the rest of the code should be executed, would that work ??  . . . . . .   more testing needed :)


FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

Handoko

  • Hero Member
  • *****
  • Posts: 4712
  • My goal: build my own game engine using Lazarus
Re: Trapping non-available resource
« Reply #5 on: June 10, 2022, 08:57:13 pm »
Try-except and try-finally blocks can be confusing. I even saw experienced programmers use them not fully correct, at least that was what I think.

The simple rule is:
Try-finally should be used if you create object in memory, you put the .Free command in the finally section.

Alternatively, you don't need to use try-finally block if you're sure or already make sure nothing bad may happen (like: all data already validated before running a command).

Try-finally is to make sure the object will be free even exception happens. Without try-finally, the .Free command may be skipped because the lines below the the exception happens, will not be executed.

You can consider them as: try-except is a panic exit and try-finally will make sure it always executed even the panic happens.

Don't give up easily, friend.
:D :D  That I very seldom do!  - I'm known for my stubborn determination to get my way  ::)

That is a requirement for programmer.  :)
« Last Edit: June 10, 2022, 09:02:08 pm by Handoko »

Josh

  • Hero Member
  • *****
  • Posts: 993
Re: Trapping non-available resource
« Reply #6 on: June 10, 2022, 11:07:52 pm »
or this may suit

hopefully The name variable is the name of the resource, in the resource file; hense it does not have .extension, and should be ALL CAPS, curios as to why you taking off the last 4 chars when displaying message?

Code: Pascal  [Select][+][-]
  1. procedure Rotate(Pic:TImage;Name:string;Angle:single;X,Y:word;T:byte);
  2. Var
  3.   tmp, dest : TBGRABitmap;
  4. begin
  5.   // will need lcltype in the uses clause to get the RT_RCDATA, RT_BITMAP etc,
  6.   IF FindResource(hInstance, PChar(Name), RT_RCDATA)<>0 THEN  // resource exists
  7.   begin
  8.     tmp:=tbGRABitmap.Create;
  9.     tmp.LoadFromResource(Name);
  10.     dest := TBGRAbitmap.create;
  11.     BGRAReplace(dest, tmp.FilterRotate(PointF(X,Y),angle,true));
  12.     dest.ReplaceTransparent(clRed);
  13.     Pic.Picture.Clear;
  14.     Pic.Picture.Bitmap.SetSize(tmp.Width,tmp.Height);  
  15.     Pic.Picture.Bitmap.TransparentColor:=clRed;  
  16.     Pic.Transparent:=true;
  17.     dest.Draw(Pic.Picture.Bitmap.Canvas,0,0);
  18.     tmp.Free;
  19.     dest.free;
  20.   end
  21.   else
  22.   begin
  23.     Form1.MissingImage.Caption := Copy(Name,1,Length(name)-4)+' Image not available';  
  24.     Form1.MissingImage.Show;
  25.   end;
  26. end;
« Last Edit: June 10, 2022, 11:13:03 pm by Josh »
Development Installation Lazarus 1.3, FPC 2.7.1,Windows 7/8 32/64, OSX, *nix

Test Environment Lazarus & FPC Trunk on Windows and OSX (Cocoa Mainly on OSX). Testing also Crosscompile windows to OSX.. 
Any posts made from 2015 will be based on Lazarus Trunk.

J-G

  • Hero Member
  • *****
  • Posts: 884
Re: Trapping non-available resource
« Reply #7 on: June 11, 2022, 12:10:52 am »
or this may suit

hopefully The name variable is the name of the resource, in the resource file; hense it does not have .extension, and should be ALL CAPS, curios as to why you taking off the last 4 chars when displaying message?

  IF FindResource(hInstance, PChar(Name), RT_RCDATA)<>0 THEN  // resource exists

That suits fine Josh - thanks very much  -  the 'name' is the full filename and therefore does include the extension (.png) and of course that is the reason for trimming the last four characters - I only need to report the number of teeth on the gear, not the name of the file

Naturally my first attempt failed since I hadn't seen your comments (was that an [Edit]? ) about the extension but as soon as I created a 'ShortName' - using  ' ShortName := Copy(Name,1,Length(Name)-4); ' and used that in the 'FindResorce' call, all was hunky-dory.  The fact that the filename consists of a number and an uppercase 'T' means that I wasn't afflicted by the 'All Caps' issue.

So  -  that is the code I'll be using for now, though if I can solve the issues with @Winni's suggestion in the BGRA... forum that would be an improvement.

FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

 

TinyPortal © 2005-2018