RAD Studio Render animated SVG to GIF, AVI or APNG

ADSoft

Местный
Регистрация
1 Окт 2007
Сообщения
223
Реакции
86
Credits
1,095
Render animated SVG to GIF, AVI or APNG
RenderAnimatedSVG.png

This example Delphi application demonstrates how to render an animated SVG to a GIF, AVI or animated PNG.​

The application makes use of the following libraries:
For the SVG it uses the SVG control package or Для просмотра ссылки Войди или Зарегистрируйся.
For the animated PNG, theДля просмотра ссылки Войди или Зарегистрируйся
For the AVI, Для просмотра ссылки Войди или Зарегистрируйся
For GIF, no external library is needed because it is part of the Delphi VCL.
The source code of the application can be found on Для просмотра ссылки Войди или Зарегистрируйся.
The SVG used in this example is “Для просмотра ссылки Войди или Зарегистрируйся“.
Application layout
On the top left of the application form there is a TValueListEditor control where you can set the SVG input file, width en height and some other settings.
On the bottom left there is a TTabControl with another TValueListEditor where you can select the output format and some settings for the output.
Then on the top you can specify the “duration” for the animation in milliseconds and the “frames per second”.
The “duration” is something you have to find out yourself by inspecting the SVG file. SVG animations can be very complex, because parts of the animation can be triggered by events. In simple cases you can figure out the duration by looking at the “Для просмотра ссылки Войди или Зарегистрируйся” attributes in the SVG.
For CSS animations, you look for the “Для просмотра ссылки Войди или Зарегистрируйся” property or the short hand version “Для просмотра ссылки Войди или Зарегистрируйся“, which has several animation properties rolled into one property.
Of course, increasing the “frames per second” will result in a smoother animation but a larger output file.
The center pane of the application will display a preview of each frame while it is recorded. You start the recording with the button “Record”.
At the end of the recording, the application will save the output file to the file specified in the output settings.
Stepping through the SVG animation
For stepping through the SVG animation I have created a class TSVGFrameRenderer. Because we do not have to display the SVG animation in real time, the animation timer is not needed.
The total number of frames is calculated by:
FrameCount := Duration * FPS div 1000;
Where FPS is the “Frames per second”.
And the time in milliseconds between each frame is:
DeltaTime := 1000 div FPS;
The input for the frame renderer is a “SVG root object”, specified by the interface ISVGRoot. The root object contains the parsed SVG file.
The root object also implements interface ISVGAnimationTimerTarget. This interface can be used to step through the SVG animation, for example:
Код:
var
Target: ISVGAnimationTimerTarget;
...
// Get the animation target interface
if not Supports(SVGRoot, ISVGAnimationTimerTarget, Target) then
Exit;
...
// Start the animation
Target.DoAnimationTimerStart;
...
// Advance the animation "Delay" milliseconds
Target.DoAnimationAdvanceFrame(Delay);
The frame render will render the SVG to a bitmap on each step.
The full code of the frame renderer can be found Для просмотра ссылки Войди или Зарегистрируйся.
Rendering to an animated PNG
For rendering the animated PNG we use theДля просмотра ссылки Войди или Зарегистрируйся. This is a library for reading, writing and converting many different image formats.
At the moment we use only one output parameter for the PNG animation: “AnimationLoops”. This specifies how many times the animation will repeat itself. A value of 0 means endless repetition.
procedure TSVGConvTargetApng.Convert(aFrameRenderer: TSVGFrameRenderer);
Код:
var
DataArray: TDynImageDataArray;
Meta: TMetadata;
Format: TPNGFileFormat;
Index: Integer;
begin
// https://github.com/galfar/imaginglib
SetLength(DataArray, 0);
try
if aFrameRenderer.RenderFirst then
begin
// Create an array to store the frame images
SetLength(DataArray, aFrameRenderer.StepCount);
try
repeat
// Convert the bitmap from the frame rendere to an image
// and store it in the array
ConvertBitmapToData(aFrameRenderer.Bitmap, DataArray[aFrameRenderer.Step]);
until not aFrameRenderer.RenderNext;
finally
aFrameRenderer.RenderClose;
end;
end;
// Create a meta object for PNG settings
Meta := TMetadata.Create;
try
// Set "AnimationLoops" in the meta object
Meta.SetMetaItemForSaving(SMetaAnimationLoops, AnimatedLoops);
// Set the frame delay on each image
for Index := 0 to Length(DataArray) - 1 do
Meta.SetMetaItemForSaving(SMetaFrameDelay, 1000 / aFrameRenderer.FPS, Index);
// Save the array to file in animated PNG format
Format := TPNGFileFormat.Create(Meta);
try
Format.SaveToFile(FileName, DataArray);
finally
Format.Free;
end;
finally
Meta.Free;
end;
finally
FreeImagesInArray(DataArray);
end;
end;
The resulting animated PNG looks like this (will only animate if your browser supports animated PNG).
Для просмотра ссылки Войди или Зарегистрируйся
The imaginglib doesn’t have components for displaying an animated PNG in Delphi (or Freepascal). Below is a very simplistic way to display the animated PNG based on a timer:
Код:
uses
...
Imaging,
ImagingComponents,
ImagingNetworkGraphics
...
TForm1 = class(TForm)
...
private
FFrame: Integer;
FDynImageDataArray: TDynImageDataArray;
public
procedure LoadPng(const aFilename: string);
...
procedure TForm1.FormCreate(Sender: TObject);
begin
SetLength(FDynImageDataArray, 0);
FFrame := 0;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Finalize(FDynImageDataArray);
end;
procedure TForm1.LoadPng(const aFilename: string);
var
PngFormat: TPNGFileFormat;
DelayVar: Variant;
begin
PngFormat := TPNGFileFormat.Create(nil);
try
// Load PNG in image array
PngFormat.LoadFromFile(OpenDialog1.Filename, FDynImageDataArray);
// Get delay from global meta
DelayVar := GlobalMetadata.MetaItems[SMetaFrameDelay];
if not VarIsNull(DelayVar) then
Timer1.Interval := DelayVar;
finally
PngFormat.Free;
end;
// Start timer
FFrame := 0;
Timer1.Enabled := True;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
Bitmap: TBitmap;
begin
if FFrame < Length(FDynImageDataArray) then
begin
Bitmap := TBitmap.Create;
try
ConvertDataToBitmap(FDynImageDataArray[FFrame], Bitmap);
PaintBox1.Canvas.Draw(0, 0, Bitmap);
finally
Bitmap.Free;
end;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
PaintBox1.Repaint;
if FFrame < Length(FDynImageDataArray) - 1 then
Inc(FFrame)
else
FFrame := 0;
end;
Rendering to GIF
In this example we use the GIF functionality that is part of the Delphi VCL, but you could also use the Для просмотра ссылки Войди или Зарегистрируйся for this. I based the example below on the GIF animation demo on Для просмотра ссылки Войди или Зарегистрируйся. The Gif also takes a parameter “AnimationLoops” for a finite or endless repetition. Also some other parameters which are explained in the Delphi help or on Andreas website.
procedure TSVGConvTargetGif.Convert(aFrameRenderer: TSVGFrameRenderer);
Код:
var
GIFImage: TGifImage;
GifFrame: TGIFFrame;
GCE: TGIFGraphicControlExtension;
LoopExt: TGIFAppExtNSLoop;
begin
GIFImage := TGifImage.Create;
try
if aFrameRenderer.RenderFirst then
begin
try
repeat
// Add the bitmap to the gif image list
GifFrame := GifImage.Add(aFrameRenderer.Bitmap);
// Loop extension must be the first extension in the first frame
if GifImage.Images.Count = 1 then
begin
LoopExt := TGIFAppExtNSLoop.Create(GifFrame);
// Number of loops (0 = forever)
LoopExt.Loops := AnimatedLoops;
end;
// Add Graphic Control Extension
GCE := TGIFGraphicControlExtension.Create(GifFrame);
// Delay is in hundreds of a second
GCE.Delay := aFrameRenderer.Delay div 10;
if Transparent then
begin
GCE.Transparent := True;
GCE.TransparentColorIndex := GifFrame.Pixels[0, aFrameRenderer.Height - 1];
GCE.Disposal := dmBackground;
end;
until not aFrameRenderer.RenderNext;
finally
aFrameRenderer.RenderClose;
end;
end;
// Optimize Color map...
if OptimizeColorMap then
GifImage.OptimizeColorMap;
// Optimize aGifImage frames...
if OptimizeOptions <> [] then
GifImage.Optimize(OptimizeOptions, rmNone, dmNearest, 0);
if not GifImage.Empty then
GifImage.SaveToFile(Filename);
finally
GifImage.Free;
end;
end;
The GIF format is older technology. It doesn’t support transparency by alpha channel as the PNG format does. On the other hand, it is widely supported. You can sort of simulate transparency by setting the background colour of the SVG. Choose a colour that is not present in the SVG itself. Then set the output parameter “Transparent” to True. The resulting GIF looks like this:
Для просмотра ссылки Войди или Зарегистрируйся
Rendering to AVI
For rendering to AVI, we use the library Для просмотра ссылки Войди или Зарегистрируйся. The only output setting for the AVI is a compression setting: “None” or “XVID”. “XVID” will only work if you have installed the necessary codec, as explained by Francois on his website.
The code for rendering to AVI is very simple:
Код:
procedure TSVGConvTargetAvi.Convert(aFrameRenderer: TSVGFrameRenderer);
var
Avi: TAviFromBitmaps;
CompressionTag: FOURCC;
begin
// http://francois-piette.blogspot.com/2013/07/creating-avi-file-from-bitmaps-using.html
// Compression will only work if you have installed the necessary codecs
// XVID compressor, download the setup from http://www.xvid.org
CompressionTag := MKFOURCC('D', 'I', 'B', ' ');
case Compression of
acXVID: CompressionTag := MKFOURCC('x', 'v', 'i', 'd');
end;
Avi := TAviFromBitmaps.CreateAviFile(
nil,
Filename,
CompressionTag,
Cardinal(aFrameRenderer.FPS),
1);
try
if aFrameRenderer.RenderFirst then
begin
try
repeat
// Add the bitmap to the avi
Avi.AppendNewFrame(aFrameRenderer.Bitmap.Handle);
until not aFrameRenderer.RenderNext;
finally
aFrameRenderer.RenderClose;
end;
end;
finally
Avi.Free;
end;
end;
Rendering an animated SVG to PNG, GIF or AVI and then using these in your application has the advantage that these later formats do not use as much computing power as an animated might SVG might need.
Simple SVG animations are usually not a problem, but In SVG you can animate almost every attribute, also for example attributes on filters. Rendering SVG filters can be very expensive in terms of computing power, so it might not be possible to render frames fast enough for a smooth animation.
Also you might be less depended on specific fonts or other resources that are needed on the host computer for rendering the SVG.
On the other hand, the SVG file is much smaller. In this example the PNG file is 602KB, the AVI (uncompressed) is 17583KB and the GIF is 492KB. The SVG file is only 13KB.