Fastest way to draw a wave distortion effect in Delphi FMX?

68 Views Asked by At

In Delphi 10.2. using Firemonkey for a Windows 10 desktop application I would like distort the image of a TImage according to a wave function (or a Perlin Noise function or a similar function).

On the creation of the application's main form I first calculate the wave (or noise) data and keep it in a 3 dimensional array var WaveData: array[0..MaxNumOfFunctionValues-1, 0..BitmapWidth-1, 0..BitmapHeight-1] of Integer.

On a TTimer.OnTimer event I initiate the drawing of the distortion by calling the following procedure:

    procedure DrawWaveDistortion;
    var
      SourceBitmap, TargetBitmap: TBitmap;
      SourceBitmapData, TargetBitmapData: TBitmapData;
      MapAccessOk: Boolean;
      BMWidth, BMHeight: Integer;
      ix, iy: Integer;
      Shift: Integer;
      Shiftedix, Shiftediy: Integer;
      Color: TAlphaColor;
    begin
      SourceBitmap:=Image1.Bitmap;
      BMWidth:=SourceBitmap.Width;
      BMHeight:=SourceBitmap.Height;
      TargetBitmap:=TBitmap.Create(BMWidth, BMHeight);
      try
        MapAccessOk:=SourceBitmap.Map(TMapAccess.Read, SourceBitmapData);
        try
          if MapAccessOk then
          begin
            MapAccessOk:=TargetBitmap.Map(TMapAccess.Write, TargetBitmapData);
            try
              if MapAccessOk then
              begin
                for ix := 0 to BMWidth-1 do
                begin
                  for iy := 0 to BMHeight-1 do
                  begin
                    Shift:=WaveData[StepCounter, ix, iy];
                    Shiftedix:=EnsureRange(ix+Shift, 0, BMWidth-1);
                    Shiftediy:=EnsureRange(iy+Shift, 0, BMHeight-1);
                    Color:=SourceBitmapData.GetPixel(Shiftedix, Shiftediy);
                    TargetBitmapData.SetPixel(ix, iy, Color);
                  end;
                end;
              end;
            finally
              TargetBitmap.Unmap(TargetBitmapData);
            end;
          end;
        finally
          SourceBitmap.Unmap(SourceBitmapData);
        end;
        Image1.Bitmap.Assign(TargetBitmap);
        Inc(StepCounter);
        if StepCounter>=MaxNumOfFunctionValues then
        begin
          StepCounter:=0;
        end;
      finally
        FTargetBitmap.Free;
      end;
    end;

I use 32 ms for the TTimer.Interval.

This works fine and animates the TImage with some sort of water wave effect. However it is to slow, the number of frames per second is low and so I do get a jerky animation (specially when I try to add some other effects).

What can I try to do to improve the performance and increase the number of frames per second?

0

There are 0 best solutions below