0
  Formulae: array [1..6] of TJPEGImage;

I have an array in which I want to assign images into so that I can display them onto a form. I've used similar code from the JPEG data-stream to TImage question but I get an access violation error message at the if statement

procedure Tfrm_calc2.ChangeDisplay(ImgNo: Integer; NewImage: Boolean);
var
  TempImg: TJPEGImage;
begin
  TempImg:= TJPEGImage.Create;
  TempImg.LoadFromFile('C2F'+inttostr(ImgNo)+'.jpg');
  img_Formulae.Picture.Assign(TempImg);

 // assigning each picture to an element in array if it is the first time. This will be used to save the pictures later on
  If NewImage = True then Formulae[ImgNo].Assign(TempImg);


  TempImg.Free;
  ImgDisplayed:= ImgNo;

  lbl_FormulaDisplay.Caption:= 'Formula ' + inttostr(ImgNo); //user can see which formula can be seen
end;

Thanks.

Community
  • 1
  • 1
astudent
  • 3
  • 1
  • Does `Formulae` array contain initialized `TJpegImage` objects? – Sergej Christoforov Apr 07 '17 at 15:10
  • The Assign method transfers information from one instance to another. It does not create a new instance. The elements of the Formulae array need to be instantiated somewhere. – Dave Olson Apr 07 '17 at 15:12
  • no it doesn't and how I would I do that? – astudent Apr 07 '17 at 15:26
  • Apart from the solutions proposed, I would suggest you look into the use of generics... TObjectList is probably a better solution than the array in most situations. That does not avoid that you initialize objects before calling their Assign method though ;) – Frazz Apr 08 '17 at 06:25

3 Answers3

2

Did you populate the array with allocated objects before calling Assign on them? Probably not. Try something more like this instead:

procedure Tfrm_calc2.ChangeDisplay(ImgNo: Integer);
var
  TempImg: TJPEGImage;
begin
  TempImg := TJPEGImage.Create;
  try
    TempImg.LoadFromFile('C2F'+IntToStr(ImgNo)+'.jpg');
    img_Formulae.Picture.Assign(TempImg);

    if Formulae[ImgNo] = nil then
    begin
      Formulae[ImgNo] := TempImg;
      TempImg := nil;
    end else
      Formulae[ImgNo].Assign(TempImg);
  finally
    TempImg.Free;
  end;
  ImgDisplayed := ImgNo;
  lbl_FormulaDisplay.Caption := 'Formula ' + IntToStr(ImgNo);
end;

Alternatively:

procedure Tfrm_calc2.ChangeDisplay(ImgNo: Integer);
var
  TempImg: TJPEGImage;
begin
  TempImg := TJPEGImage.Create;
  try
    TempImg.LoadFromFile('C2F'+IntToStr(ImgNo)+'.jpg');
    img_Formulae.Picture.Assign(TempImg);

    FreeAndNil(Formulae[ImgNo]);
    Formulae[ImgNo] := TempImg;
  except
    TempImg.Free;
    raise;
  end;
  ImgDisplayed := ImgNo;
  lbl_FormulaDisplay.Caption := 'Formula ' + IntToStr(ImgNo);
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

Formulae[ImgNo].Assign(TempImg);

This is attempting to assign the picture to an already created object. However, your object, most likely, is not already created, as I see no means of initializing them in your code above. Assign requires that the object is already created.

In your case, the ideal thing to do is on startup, be sure to create all of these objects in the array in advance. Then, also be sure to destroy them on closing.

Your entire code could be as simple as...

procedure Tfrm_calc2.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  for I := Low(Formulae) to High(Formulae) do begin
    Formulae[I] := TJpegImage.Create;
  end;
end;

procedure Tfrm_calc2.FormDestroy(Sender: TObject);
var
  I: Integer;
begin
  for I := Low(Formulae) to High(Formulae) do begin
    FreeAndNil(Formulae[I]);
  end;
end;

procedure Tfrm_calc2.ChangeDisplay(ImgNo: Integer);
begin
  Formulae[ImgNo].LoadFromFile('C2F'+IntToStr(ImgNo)+'.jpg');
  ImgDisplayed:= ImgNo;
  lbl_FormulaDisplay.Caption:= 'Formula ' + IntToStr(ImgNo);
end;

There's no need to load another instance and assign it. Just load the file directly.

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
  • I see. Thank you how do I initialise it is? Do I just do Formulae[i].Create? – astudent Apr 07 '17 at 15:28
  • @astudent No, that is incorrect. `Formulae[i] := TJPEGImage.Create;` is what you'll need to do, within a loop, for example in the constructor of your form. The exact reverse in the destructor to free them. – Jerry Dodge Apr 07 '17 at 16:01
  • @astudent no, use `Formulae[i] := TJpegImage.Create;` instead. – Remy Lebeau Apr 07 '17 at 16:02
0

From Embarcadero VCL Reference

Call Assign to copy the properties or other attributes of one object from another.

So the Destination Class must be created before calling Assign

Another suggestion is fist create an instance of TJpegImage class and then Assign it to your temp TJpegImage, something like this :

var
 JPegArray : array[0..10] of TJPEGImage;

...

var
 JPeg : TJPEGImage;
begin
 JPeg := TJPEGImage.Create;
 JPeg.LoadFromFile('C:/M.jpg');

 JPegArray[1] := TJPEGImage.Create;
 JPegArray[1].Assign(JPeg);

 JPeg.Free;

 Image1.Picture.Assign(JPegArray[1]);

 ...

end;
Mahmoud_Mehri
  • 1,643
  • 2
  • 20
  • 38
  • That is a terrible solution, as it leaks memory if there was already an object instantiated in the array element. Especially for something as memory-intensive as an image, memory leaks are a critical issue. – Jerry Dodge Apr 07 '17 at 16:02
  • This is an example to know that the object you want to assign to, must be created first. The problem you have mentioned will be solved with an IF statement before creating TJPEGImage for the Array Item – Mahmoud_Mehri Apr 07 '17 at 16:30
  • If they just get created on startup, then there would be no need to worry about any of this. It would be as simple as `JPegArray[i].LoadFromFile('C:\M.jpg');` Also, your code puts the `JPegArray` directly in the scope of the local procedure. – Jerry Dodge Apr 07 '17 at 18:18