Recent

Author Topic: Trying to save an Animated GIF [SOLVED]  (Read 1279 times)

zxandris

  • Full Member
  • ***
  • Posts: 141
Trying to save an Animated GIF [SOLVED]
« on: January 25, 2025, 04:54:51 am »
Hey there, I've been trying to save an ani gif using BGRA and a snipet of the code I'm using below.

Code: Pascal  [Select][+][-]
  1.  
  2.                     for a := 0 to frm.lvFiles.items.count - 1 do
  3.                     begin
  4.                          pbar.value := a;
  5.                          application.ProcessMessages;
  6.                          Bgra := TBgraBitmap.create;
  7.                          try
  8.                             LoadToBgra(frm.lvFiles.items.item[a].caption, bgra);
  9.                             application.ProcessMessages;
  10.                             Bgra.ResampleFilter := rfBestQuality;
  11.                             bgraSized := TbgraBitmap.create(size.x, size.y);
  12.                             try
  13.                                BgraSized.ResampleFilter := rfBestQuality;
  14.                                BgraSized := Bgra.Resample(size.x, size.y, rmFineResample);
  15.                                GIF.AddFullFrame(BgraSized, 50);
  16.                             finally
  17.                                    BgraSized.free;
  18.                             end;
  19.                          finally
  20.                                 bgra.free;
  21.                          end;
  22.                     end;
  23.                     if fileEXists(strOutput) then RecycleItem(strOutput, true, false, true);
  24.                     strm := TFileStream.create(strOutput, fmCreate);
  25.                     try
  26.                        Gif.SaveToStream(strm, BGRAColorQuantizerFactory, daFloydSteinberg);
  27.                     finally
  28.                            strm.free;
  29.                     end;
  30.  

Crashes when trying to save, either with stream or just save to File.  I really don't know why.  I'm sizing all the frames to the first frame, so I'm a little confused.

CJ
« Last Edit: February 06, 2025, 06:58:20 am by zxandris »

zxandris

  • Full Member
  • ***
  • Posts: 141
Re: Trying to save an Animated GIF
« Reply #1 on: January 25, 2025, 04:58:36 am »
Just in case I'm not adding them correctly or something here is the entire creation action

Code: Pascal  [Select][+][-]
  1.  
  2. procedure TfrmMain.actGeneratorPagedImageExecute(Sender: TObject);
  3. var
  4.    frm : TfrmDLGPagedImageGenerator;
  5.    lst : TStringList;
  6.    a : Integer;
  7.    TIF : TBGRAWriterTiff;
  8.    img : TFPMemoryImage;
  9.    Bgra, bgraSized : TBgraBitmap;
  10.    strm : TFileStream;
  11.    strOutput : string;
  12.    Gif : TBGRAAnimatedGif;
  13.    Size : TPoint;
  14. //   opt : TBGRAColorQuantizerAny;
  15. //   ADitheringAlgorithm : TDitheringAlgorithm
  16. begin
  17.      frm := TfrmDLGPagedImageGenerator.create(self);
  18.      try
  19.         lst := TStringList.create;
  20.         try
  21.            GetCurrentList(lst);
  22.            for A := 0 to lst.count - 1 do
  23.            begin
  24.                 frm.AddToFiles(lst[a]);
  25.            end;
  26.         finally
  27.                lst.free;
  28.         end;
  29.         if frm.ShowModal = mrOK then
  30.         begin
  31.              strOutput := frm.feOutput.filename;
  32.              if frm.cboType.ItemIndex = 0 then ChangeFileExt(strOutput, '.tif')
  33.              else if frm.cboType.ItemIndex = 1 then changeFileExt(strOutput, '.gif');
  34.              if frm.cboType.ItemIndex = 0 then
  35.              begin
  36.                  // TIFF Image
  37.                  TIF := TBgraWriterTiff.Create;
  38.                  try
  39.                      tif.PremultiplyRGB := true;
  40.                      tif.SaveCMYKAsRGB := true;
  41.                      pbar.MaxValue := frm.lvFiles.items.count - 1;
  42.                      for A := 0 to frm.lvFiles.items.count - 1 do
  43.                      begin
  44.                           pbar.value := a;
  45.                           application.ProcessMessages;
  46.                           Bgra := TBgraBitmap.create;
  47.                           try
  48.                                LoadToBgra(frm.lvFiles.items.item[a].Caption, bgra);
  49.                                Bgra.ClearTransparentPixels;
  50.                                img := TFPMemoryImage.Create(bgra.width, bgra.height);
  51.                                try
  52.                                   img.assign(bgra);
  53.                                   Tif.AddImage(img);
  54.                                finally
  55.                                       img.free;
  56.                                end;
  57.                           finally
  58.                                  bgra.free;
  59.                           end;
  60.                      end;
  61.                      if fileExists(strOutput) then RecycleItem(strOutput, true, false, true);
  62.                      strm := TFilestream.create(strOutput, fmCreate);
  63.                      try
  64.                         tif.SaveToStream(strm);
  65.                      finally
  66.                             strm.free;
  67.                      end;
  68.                      application.ProcessMessages;
  69.                      if fileExists(strOutput) then
  70.                      begin
  71.                           DoMessage('Paged Image Generator', ExtractFileName(strOutput) + ' - Generated!');
  72.                           if ask('Would you like to open the file?') = mrYes then LoadAnyFile(strOutput);
  73.                      end else DoWarning('Paged Image Generator', ExtractFileName(strOutput) + ' - Not Generated!');
  74.                  finally
  75.                         TIF.free;
  76.                  end;
  77.              end else if frm.cboType.ItemIndex = 1 then
  78.              begin
  79.                  // Write to GIF
  80.                  Gif := TBGRAAnimatedGif.Create;
  81.                  try
  82.                     if frm.lvFiles.items.count-1=-1 then
  83.                     begin
  84.                         DoWarning('Paged Image Generator', 'Cannot add zero images!');
  85.                         exit;
  86.                     end;
  87.                     Bgra := TBgraBitmap.create;
  88.                     try
  89.                        LoadToBgra(frm.lvFiles.items.item[0].caption, bgra);
  90.                        size.x := Bgra.Width;
  91.                        size.y := bgra.height;
  92.                     finally
  93.                            bgra.free;
  94.                     end;
  95.                     gif.SetSize(size.x, size.y);
  96.                     gif.LoopCount := frm.lvFiles.items.count-1;
  97.                     pbar.MaxValue := frm.lvFiles.items.count - 1;
  98.                     for a := 0 to frm.lvFiles.items.count - 1 do
  99.                     begin
  100.                          pbar.value := a;
  101.                          application.ProcessMessages;
  102.                          Bgra := TBgraBitmap.create;
  103.                          try
  104.                             LoadToBgra(frm.lvFiles.items.item[a].caption, bgra);
  105.                             application.ProcessMessages;
  106.                             Bgra.ResampleFilter := rfBestQuality;
  107.                             bgraSized := TbgraBitmap.create(size.x, size.y);
  108.                             try
  109.                                BgraSized.ResampleFilter := rfBestQuality;
  110.                                BgraSized := Bgra.Resample(size.x, size.y, rmFineResample);
  111.                                GIF.AddFullFrame(BgraSized, 50);
  112.                             finally
  113.                                    BgraSized.free;
  114.                             end;
  115.                          finally
  116.                                 bgra.free;
  117.                          end;
  118.                     end;
  119.                     ShowMessage(strOutput);
  120.                     if fileExists(strOutput) then RecycleItem(strOutput, true, false, true);
  121.                     strm := TFileStream.create(strOutput, fmCreate);
  122.                     try
  123.                        try
  124.                           Gif.SaveToStream(strm, BGRAColorQuantizerFactory, daFloydSteinberg);
  125.                        except
  126.                              //
  127.                        end;
  128.                     finally
  129.                            strm.free;
  130.                     end;
  131.                     application.ProcessMessages;
  132.                     if fileExists(strOutput) then
  133.                     begin
  134.                          DoMessage('Paged Image Generator', ExtractFileName(strOutput) + ' - Generated!');
  135.                          if ask('Would you like to open the file?') = mrYes then LoadAnyFile(strOutput);
  136.                     end else DoWarning('Paged Image Generator', ExtractFileName(strOutput) + ' - Not Generated!');
  137.                  finally
  138.                         gif.free;
  139.                  end;
  140.              end;
  141.         end;
  142.      finally
  143.             frm.free;
  144.      end;
  145. end;
  146.  

circular

  • Hero Member
  • *****
  • Posts: 4383
    • Personal webpage
Re: Trying to save an Animated GIF
« Reply #2 on: January 26, 2025, 09:13:10 am »
Hi zxandris,

Maybe the color quantizer is missing. It is provided by BGRAColorQuantization unit. You need to:
  • Either pass TBGRAColorQuantizer class as a parameter of SaveToStream,
  • Or assign TBGRAColorQuantizer class to BGRAPalette.BGRAColorQuantizerFactory

Code snippet to do the latter:
Code: Pascal  [Select][+][-]
  1. uses BGRAPalette, BGRAColorQuantization;
  2. begin
  3.   BGRAColorQuantizerFactory := TBGRAColorQuantizer;

Note that when it crashes, it gives you a message or a type of error. This can give you a clue on what is going on.

I noticed one small mistake when resampling. The following code will lead to a small memory leak:
Code: Pascal  [Select][+][-]
  1.   bgraSized := TbgraBitmap.create(size.x, size.y); // one instance
  2.   try
  3.     BgraSized.ResampleFilter := rfBestQuality;
  4.     BgraSized := Bgra.Resample(size.x, size.y, rmFineResample); // another instance
  5.  
The reason is that Bgra.Resample returns a new instance of TBGRABitmap, so here you get 2 instances. Regarding the ResampleFilter property, it needs to be set in the source bitmap.

Regards
Conscience is the debugger of the mind

zxandris

  • Full Member
  • ***
  • Posts: 141
Re: Trying to save an Animated GIF
« Reply #3 on: January 27, 2025, 05:05:40 pm »

I'm probably being dense here, but I did create the class as per snippet to pass but I'm unsure what to actually do with the class, like what settings need to be applied to make that work.  Would you mind helping me there.  Say I pass that to the save stream, what should I set on that class to make that save and work.  It did actually save I found out, but the gif is certainly not animated so this really has me at a loss.

CJ

Hi zxandris,

Maybe the color quantizer is missing. It is provided by BGRAColorQuantization unit. You need to:
  • Either pass TBGRAColorQuantizer class as a parameter of SaveToStream,
  • Or assign TBGRAColorQuantizer class to BGRAPalette.BGRAColorQuantizerFactory

Code snippet to do the latter:
Code: Pascal  [Select][+][-]
  1. uses BGRAPalette, BGRAColorQuantization;
  2. begin
  3.   BGRAColorQuantizerFactory := TBGRAColorQuantizer;

Note that when it crashes, it gives you a message or a type of error. This can give you a clue on what is going on.

I noticed one small mistake when resampling. The following code will lead to a small memory leak:
Code: Pascal  [Select][+][-]
  1.   bgraSized := TbgraBitmap.create(size.x, size.y); // one instance
  2.   try
  3.     BgraSized.ResampleFilter := rfBestQuality;
  4.     BgraSized := Bgra.Resample(size.x, size.y, rmFineResample); // another instance
  5.  
The reason is that Bgra.Resample returns a new instance of TBGRABitmap, so here you get 2 instances. Regarding the ResampleFilter property, it needs to be set in the source bitmap.

Regards

circular

  • Hero Member
  • *****
  • Posts: 4383
    • Personal webpage
Re: Trying to save an Animated GIF
« Reply #4 on: January 29, 2025, 07:34:25 pm »
It's great that you ask, so that we can clear out any confusion.

You don't need to create the class. TBGRAColorQuantizer is the class type, it doesn't have any instance. It is created internally by TBGRAAnimatedGif when writing.

I am not sure what is the reason you don't get an animation. You saved it with TBGRAAnimatedGif class? Maybe try with a simpler example:

Code: Pascal  [Select][+][-]
  1. var
  2.   img: TBGRABitmap;
  3.   gif: TBGRAAnimatedGif;
  4.   i, delayms: integer;
  5. begin
  6.   delayms := 100;
  7.   gif := TBGRAAnimatedGif.Create;
  8.   gif.SetSize(355, 355);
  9.   gif.LoopCount := 0;  //endless loop
  10.   for i := 0 to 2 do
  11.   begin
  12.     img := TBGRABitmap.Create(Format('frame%d.png', [i]));
  13.     //add frame
  14.     gif.AddFullFrame(img, delayms);
  15.     img.Free;
  16.   end;
  17.   gif.OptimizeFrames;
  18.   gif.SaveToFile('sample-ani-2.gif'); //save to file
  19.   gif.Free; //clean
  20. end;
Copied from: https://forum.lazarus.freepascal.org/index.php/topic,61283.msg468570.html#msg468570

Hope it helps
Conscience is the debugger of the mind

zxandris

  • Full Member
  • ***
  • Posts: 141
Re: Trying to save an Animated GIF
« Reply #5 on: February 01, 2025, 05:21:48 pm »

I reallyl don't know what I'm doing wrong, I used the example to change my code (excuse the comments) to this :-

Code: Pascal  [Select][+][-]
  1.                  // Write to GIF
  2.                  Gif := TBGRAAnimatedGif.Create;
  3.                  try
  4.                     if frm.lvFiles.items.count-1=-1 then
  5.                     begin
  6.                         DoWarning('Paged Image Generator', 'Cannot add zero images!');
  7.                         exit;
  8.                     end;
  9.                     Bgra := TBgraBitmap.create;
  10.                     try
  11.                        LoadToBgra(frm.lvFiles.items.item[0].caption, bgra);
  12.                        size.x := Bgra.Width;
  13.                        size.y := bgra.height;
  14.                     finally
  15.                            bgra.free;
  16.                     end;
  17.                     gif.SetSize(size.x, size.y);
  18.                     gif.LoopCount := 0; // Endless Loop
  19.                     intDelayMS := 100;
  20.                     pbar.MaxValue := frm.lvFiles.items.count - 1;
  21.                     for a := 0 to frm.lvFiles.items.count - 1 do
  22.                     begin
  23.                          pbar.value := a;
  24.                          application.ProcessMessages;
  25.                          Bgra := TBgraBitmap.create;
  26.                          try
  27.                             LoadToBgra(frm.lvFiles.items.item[a].caption, bgra);
  28.                             application.ProcessMessages;
  29.                             Bgra.ResampleFilter := rfBestQuality;
  30.                             bgraSized := TbgraBitmap.create(size.x, size.y);
  31.                             try
  32.                                BgraSized := Bgra.Resample(size.x, size.y, rmFineResample);
  33. //                               img := TFPMemoryImage.Create(size.x, size.y);
  34.                                try
  35. //                                  img.assign(bgraSized);
  36.                                   GIF.AddFullFrame(bgraSized, intDelayMS);
  37. //                               finally
  38.                                except
  39. //                                      img.free;
  40.                                      // Ignore for now
  41.                                end;
  42.                             finally
  43.                                    BgraSized.free;
  44.                             end;
  45.                          finally
  46.                                 bgra.free;
  47.                          end;
  48.                     end;
  49. //                    ShowMessage(CommaThou(gif.Count-1));
  50.                     if fileExists(strOutput) then RecycleItem(strOutput, true, false, true);
  51.                     gif.OptimizeFrames;
  52.                     try
  53.                        gif.SaveToFile(strOutput);
  54.                     except
  55.                           //
  56.                     end;
  57.  
  58. {                    strm := TFileStream.create(strOutput, fmCreate);
  59.                     try
  60.                        try
  61.                           Gif.SaveToStream(strm, BGRAColorQuantizerFactory, daFloydSteinberg);
  62.                        except
  63.                              //
  64.                        end;
  65.                     finally
  66.                            strm.free;
  67.                     end;
  68. }
  69.                     application.ProcessMessages;
  70.                     if fileExists(strOutput) then
  71.                     begin
  72.                          DoMessage('Paged Image Generator', ExtractFileName(strOutput) + ' - Generated!');
  73.                          if ask('Would you like to open the file?') = mrYes then LoadAnyFile(strOutput);
  74.                     end else DoWarning('Paged Image Generator', ExtractFileName(strOutput) + ' - Not Generated!');
  75.                  finally
  76.                         gif.free;
  77.                  end;
  78.  
  79.  

And the error I get is non-sensical, just an access error nothing meaningful to garner anything from, just an address, even with debug mode on.  I must admit I am totally lost right now.  Perhaps a more experienced eye would be able to see the error in my gif making code there.  I'm resizing the image to the first image loaded on purpose becuase it could be any size of image in that list.  I realised that it's possible to just assign a Bgra bitmap from the example you gave as you can tell from my comments, but it only seems to save the first frame then bails with that very confusing error. I attached a screenie of the error box.
It's great that you ask, so that we can clear out any confusion.

You don't need to create the class. TBGRAColorQuantizer is the class type, it doesn't have any instance. It is created internally by TBGRAAnimatedGif when writing.

I am not sure what is the reason you don't get an animation. You saved it with TBGRAAnimatedGif class? Maybe try with a simpler example:

Code: Pascal  [Select][+][-]
  1. var
  2.   img: TBGRABitmap;
  3.   gif: TBGRAAnimatedGif;
  4.   i, delayms: integer;
  5. begin
  6.   delayms := 100;
  7.   gif := TBGRAAnimatedGif.Create;
  8.   gif.SetSize(355, 355);
  9.   gif.LoopCount := 0;  //endless loop
  10.   for i := 0 to 2 do
  11.   begin
  12.     img := TBGRABitmap.Create(Format('frame%d.png', [i]));
  13.     //add frame
  14.     gif.AddFullFrame(img, delayms);
  15.     img.Free;
  16.   end;
  17.   gif.OptimizeFrames;
  18.   gif.SaveToFile('sample-ani-2.gif'); //save to file
  19.   gif.Free; //clean
  20. end;
Copied from: https://forum.lazarus.freepascal.org/index.php/topic,61283.msg468570.html#msg468570

Hope it helps

zxandris

  • Full Member
  • ***
  • Posts: 141
Re: Trying to save an Animated GIF
« Reply #6 on: February 01, 2025, 05:32:27 pm »
It's odd, just ran a debug through and it seems to fail on the second image it tries to write??

circular

  • Hero Member
  • *****
  • Posts: 4383
    • Personal webpage
Re: Trying to save an Animated GIF
« Reply #7 on: February 02, 2025, 03:48:10 pm »
I am puzzled as well. An access violation is often related to memory allocation. I don't know if you already have heaptrc unit (either checked in compiler options or explicitly in the uses clause). It can detect memory allocation errors.

It can also be that BGRABitmap needs to be recompiled clean, this can be done from the package window of this package.

Hmm let's see. In the code, I only see a memory leak, which shouldn't crash the program. It is about initializing bgraSized instead of calling TBGRABitmap.Create:
Code: Pascal  [Select][+][-]
  1. bgraSized := nil;

If that still doesn't work, can you send me a sample project that I can run on my computer. You can send as PM if you prefer to keep it private.
Conscience is the debugger of the mind

qk

  • New Member
  • *
  • Posts: 35
Re: Trying to save an Animated GIF
« Reply #8 on: February 02, 2025, 04:45:56 pm »
If I try to run the example, an exception is raised because BGRAColorQuantizerFactory is not assigned.

Adding the line

Code: Pascal  [Select][+][-]
  1. BGRAColorQuantizerFactory := TBGRAColorQuantizer;

on top it works.

Code: Pascal  [Select][+][-]
  1. uses
  2.   ...
  3.   BGRABitmap,
  4.   BGRAAnimatedGif,
  5.   BGRAPalette,
  6.   BGRAColorQuantization;
  7.  
  8. ...  
  9.  
  10. var
  11.   img: TBGRABitmap;
  12.   gif: TBGRAAnimatedGif;
  13.   i, delayms: integer;
  14. begin
  15.   delayms := 100;
  16.   BGRAColorQuantizerFactory := TBGRAColorQuantizer;
  17.   gif := TBGRAAnimatedGif.Create;
  18.   gif.SetSize(355, 355);
  19.   gif.LoopCount := 0;  //endless loop
  20.   for i := 0 to 2 do
  21.   begin
  22.     img := TBGRABitmap.Create(Format('frame%d.png', [i]));
  23.     //add frame
  24.     gif.AddFullFrame(img, delayms);
  25.     img.Free;
  26.   end;
  27.   gif.OptimizeFrames;
  28.   gif.SaveToFile('sample-ani-2.gif'); //save to file
  29.   gif.Free; //clean
  30. end;  
« Last Edit: February 02, 2025, 05:47:57 pm by qk »

zxandris

  • Full Member
  • ***
  • Posts: 141
Re: Trying to save an Animated GIF
« Reply #9 on: February 06, 2025, 06:58:04 am »
I think this is what did it, thank you!  But I also did Circular's suggestion so I'm not entirely sure which worked, but it is working now!.

Thanks Guys, this was a puzzler huh? :)

CJ

If I try to run the example, an exception is raised because BGRAColorQuantizerFactory is not assigned.

Adding the line

Code: Pascal  [Select][+][-]
  1. BGRAColorQuantizerFactory := TBGRAColorQuantizer;

on top it works.

Code: Pascal  [Select][+][-]
  1. uses
  2.   ...
  3.   BGRABitmap,
  4.   BGRAAnimatedGif,
  5.   BGRAPalette,
  6.   BGRAColorQuantization;
  7.  
  8. ...  
  9.  
  10. var
  11.   img: TBGRABitmap;
  12.   gif: TBGRAAnimatedGif;
  13.   i, delayms: integer;
  14. begin
  15.   delayms := 100;
  16.   BGRAColorQuantizerFactory := TBGRAColorQuantizer;
  17.   gif := TBGRAAnimatedGif.Create;
  18.   gif.SetSize(355, 355);
  19.   gif.LoopCount := 0;  //endless loop
  20.   for i := 0 to 2 do
  21.   begin
  22.     img := TBGRABitmap.Create(Format('frame%d.png', [i]));
  23.     //add frame
  24.     gif.AddFullFrame(img, delayms);
  25.     img.Free;
  26.   end;
  27.   gif.OptimizeFrames;
  28.   gif.SaveToFile('sample-ani-2.gif'); //save to file
  29.   gif.Free; //clean
  30. end;  

 

TinyPortal © 2005-2018