Ver Fonte

add wave displacement filter

Johann há 6 anos atrás
pai
commit
e903f86275

+ 58 - 39
lazpaint/dialog/filter/utwirl.lfm

@@ -1,8 +1,8 @@
 object FTwirl: TFTwirl
   Left = 631
-  Height = 217
+  Height = 288
   Top = 173
-  Width = 148
+  Width = 173
   AutoSize = True
   BorderIcons = [biSystemMenu]
   BorderStyle = bsDialog
@@ -13,113 +13,132 @@ object FTwirl: TFTwirl
   ChildSizing.VerticalSpacing = 8
   ChildSizing.Layout = cclLeftToRightThenTopToBottom
   ChildSizing.ControlsPerLine = 1
-  ClientHeight = 217
-  ClientWidth = 148
-  Font.Height = -12
+  ClientHeight = 288
+  ClientWidth = 173
+  DesignTimePPI = 120
+  Font.Height = -15
   OnCreate = FormCreate
   OnDestroy = FormDestroy
   OnShow = FormShow
   Position = poScreenCenter
-  LCLVersion = '1.6.0.4'
+  LCLVersion = '2.0.2.0'
   object Panel1: TPanel
     Left = 8
-    Height = 54
+    Height = 76
     Top = 8
-    Width = 114
+    Width = 143
     BevelOuter = bvNone
     ChildSizing.HorizontalSpacing = 8
     ChildSizing.VerticalSpacing = 8
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.ControlsPerLine = 2
-    ClientHeight = 54
-    ClientWidth = 114
+    ClientHeight = 76
+    ClientWidth = 143
+    Font.Height = -15
+    ParentFont = False
     TabOrder = 0
     object Label_Radius: TLabel
       Left = 0
-      Height = 23
+      Height = 34
       Top = 0
-      Width = 41
+      Width = 54
       Caption = 'Radius :'
+      Font.Height = -15
       Layout = tlCenter
       ParentColor = False
+      ParentFont = False
     end
     object SpinEdit_Radius: TSpinEdit
-      Left = 49
-      Height = 23
+      Left = 62
+      Height = 34
       Top = 0
-      Width = 65
-      Constraints.MinWidth = 65
+      Width = 81
+      Constraints.MinWidth = 81
+      Font.Height = -15
       Increment = 10
       MaxValue = 10000
       MinValue = 1
       OnChange = SpinEdit_RadiusChange
+      ParentFont = False
       TabOrder = 0
       Value = 100
     end
     object Label_Angle: TLabel
       Left = 0
-      Height = 23
-      Top = 31
-      Width = 41
+      Height = 34
+      Top = 42
+      Width = 54
       Caption = 'Angle :'
+      Font.Height = -15
       Layout = tlCenter
       ParentColor = False
+      ParentFont = False
     end
     object SpinEdit_Angle: TSpinEdit
-      Left = 49
-      Height = 23
-      Top = 31
-      Width = 65
-      Constraints.MinWidth = 65
+      Left = 62
+      Height = 34
+      Top = 42
+      Width = 81
+      Constraints.MinWidth = 81
+      Font.Height = -15
       Increment = 30
       MaxValue = 10000
       MinValue = 1
       OnChange = SpinEdit_AngleChange
+      ParentFont = False
       TabOrder = 1
       Value = 360
     end
   end
   object PaintBox1: TPaintBox
     Left = 8
-    Height = 105
-    Top = 70
-    Width = 114
+    Height = 131
+    Top = 92
+    Width = 143
+    Font.Height = -15
+    ParentFont = False
     OnMouseDown = PaintBox1MouseDown
     OnMouseMove = PaintBox1MouseMove
     OnPaint = PaintBox1Paint
   end
   object Panel2: TPanel
     Left = 8
-    Height = 25
-    Top = 183
-    Width = 114
+    Height = 36
+    Top = 231
+    Width = 143
     BevelOuter = bvNone
     ChildSizing.HorizontalSpacing = 8
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.ControlsPerLine = 2
-    ClientHeight = 25
-    ClientWidth = 114
+    ClientHeight = 36
+    ClientWidth = 143
+    Font.Height = -15
+    ParentFont = False
     TabOrder = 1
     object Button_OK: TButton
       Left = 0
-      Height = 25
+      Height = 36
       Top = 0
-      Width = 42
+      Width = 35
       AutoSize = True
       Caption = 'OK'
       Default = True
+      Font.Height = -15
       OnClick = Button_OKClick
+      ParentFont = False
       TabOrder = 0
     end
     object Button_Cancel: TButton
-      Left = 50
-      Height = 25
+      Left = 43
+      Height = 36
       Top = 0
-      Width = 62
+      Width = 59
       AutoSize = True
       Cancel = True
       Caption = 'Cancel'
+      Font.Height = -15
       ModalResult = 2
+      ParentFont = False
       TabOrder = 1
     end
   end
@@ -127,7 +146,7 @@ object FTwirl: TFTwirl
     Enabled = False
     Interval = 200
     OnTimer = Timer1Timer
-    left = 40
-    top = 8
+    left = 64
+    top = 120
   end
 end

+ 1 - 1
lazpaint/dialog/filter/utwirl.pas

@@ -92,7 +92,7 @@ begin
   SpinEdit_Angle.Value := round(FilterConnector.LazPaintInstance.Config.DefaultTwirlTurn*360);
   FInitializing := false;
   PreviewNeeded;
-  Left := FilterConnector.LazPaintInstance.MainFormBounds.Left
+  Left := FilterConnector.LazPaintInstance.MainFormBounds.Left;
 end;
 
 procedure TFTwirl.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;

+ 174 - 0
lazpaint/dialog/filter/uwavedisplacement.lfm

@@ -0,0 +1,174 @@
+object FWaveDisplacement: TFWaveDisplacement
+  Left = 306
+  Height = 356
+  Top = 172
+  Width = 222
+  AutoSize = True
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsDialog
+  Caption = 'Wave displacement'
+  ChildSizing.LeftRightSpacing = 8
+  ChildSizing.TopBottomSpacing = 8
+  ChildSizing.HorizontalSpacing = 8
+  ChildSizing.VerticalSpacing = 8
+  ChildSizing.Layout = cclLeftToRightThenTopToBottom
+  ChildSizing.ControlsPerLine = 1
+  ClientHeight = 356
+  ClientWidth = 222
+  DesignTimePPI = 120
+  OnCreate = FormCreate
+  OnShow = FormShow
+  Position = poScreenCenter
+  LCLVersion = '2.0.2.0'
+  object Panel1: TPanel
+    Left = 8
+    Height = 118
+    Top = 8
+    Width = 191
+    AutoSize = True
+    BevelOuter = bvNone
+    ChildSizing.HorizontalSpacing = 8
+    ChildSizing.VerticalSpacing = 8
+    ChildSizing.Layout = cclLeftToRightThenTopToBottom
+    ChildSizing.ControlsPerLine = 2
+    ClientHeight = 118
+    ClientWidth = 191
+    Font.Height = -15
+    ParentFont = False
+    TabOrder = 0
+    object Label_Wavelength: TLabel
+      Left = 0
+      Height = 34
+      Top = 0
+      Width = 102
+      Caption = 'Wavelength :'
+      Font.Height = -15
+      Layout = tlCenter
+      ParentColor = False
+      ParentFont = False
+    end
+    object SpinEdit_Wavelength: TSpinEdit
+      Left = 110
+      Height = 34
+      Top = 0
+      Width = 81
+      Constraints.MinWidth = 81
+      Font.Height = -15
+      Increment = 10
+      MaxValue = 10000
+      MinValue = 1
+      OnChange = SpinEdit_WavelengthChange
+      ParentFont = False
+      TabOrder = 0
+      Value = 100
+    end
+    object Label_Displacement: TLabel
+      Left = 0
+      Height = 34
+      Top = 42
+      Width = 102
+      Caption = 'Displacement :'
+      Font.Height = -15
+      Layout = tlCenter
+      ParentColor = False
+      ParentFont = False
+    end
+    object SpinEdit_Displacement: TSpinEdit
+      Left = 110
+      Height = 34
+      Top = 42
+      Width = 81
+      Constraints.MinWidth = 81
+      Font.Height = -15
+      Increment = 5
+      MaxValue = 1000
+      OnChange = SpinEdit_DisplacementChange
+      ParentFont = False
+      TabOrder = 1
+      Value = 50
+    end
+    object Label_Phase: TLabel
+      Left = 0
+      Height = 34
+      Top = 84
+      Width = 102
+      Caption = 'Phase :'
+      Font.Height = -15
+      Layout = tlCenter
+      ParentColor = False
+      ParentFont = False
+    end
+    object SpinEdit_Phase: TSpinEdit
+      Left = 110
+      Height = 34
+      Top = 84
+      Width = 81
+      Constraints.MinWidth = 81
+      Font.Height = -15
+      Increment = 30
+      MaxValue = 360
+      OnChange = SpinEdit_PhaseChange
+      ParentFont = False
+      TabOrder = 2
+    end
+  end
+  object PaintBox1: TPaintBox
+    Left = 9
+    Height = 160
+    Top = 136
+    Width = 189
+    Font.Height = -15
+    ParentFont = False
+    OnMouseDown = PaintBox1MouseDown
+    OnMouseMove = PaintBox1MouseMove
+    OnPaint = PaintBox1Paint
+  end
+  object Panel2: TPanel
+    Left = 9
+    Height = 36
+    Top = 304
+    Width = 188
+    BevelOuter = bvNone
+    ChildSizing.HorizontalSpacing = 8
+    ChildSizing.Layout = cclLeftToRightThenTopToBottom
+    ChildSizing.ControlsPerLine = 2
+    ClientHeight = 36
+    ClientWidth = 188
+    Font.Height = -15
+    ParentFont = False
+    TabOrder = 1
+    object Button_OK: TButton
+      Left = 0
+      Height = 36
+      Top = 0
+      Width = 35
+      AutoSize = True
+      Caption = 'OK'
+      Default = True
+      Font.Height = -15
+      OnClick = Button_OKClick
+      ParentFont = False
+      TabOrder = 0
+    end
+    object Button_Cancel: TButton
+      Left = 43
+      Height = 36
+      Top = 0
+      Width = 59
+      AutoSize = True
+      Cancel = True
+      Caption = 'Cancel'
+      Font.Height = -15
+      ModalResult = 2
+      ParentFont = False
+      TabOrder = 1
+    end
+  end
+  object Timer1: TTimer
+    Enabled = False
+    Interval = 200
+    OnTimer = Timer1Timer
+    left = 48
+    top = 184
+  end
+end

+ 183 - 0
lazpaint/dialog/filter/uwavedisplacement.pas

@@ -0,0 +1,183 @@
+unit UWaveDisplacement;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
+  Spin, UFilterConnector, BGRABitmap, BGRABitmapTypes, LazPaintType;
+
+type
+
+  { TFWaveDisplacement }
+
+  TFWaveDisplacement = class(TForm)
+    Button_Cancel: TButton;
+    Button_OK: TButton;
+    Label_Displacement: TLabel;
+    Label_Phase: TLabel;
+    Label_Wavelength: TLabel;
+    PaintBox1: TPaintBox;
+    Panel1: TPanel;
+    Panel2: TPanel;
+    SpinEdit_Displacement: TSpinEdit;
+    SpinEdit_Phase: TSpinEdit;
+    SpinEdit_Wavelength: TSpinEdit;
+    Timer1: TTimer;
+    procedure Button_OKClick(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormShow(Sender: TObject);
+    procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
+      Shift: TShiftState; X, Y: Integer);
+    procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
+      Y: Integer);
+    procedure PaintBox1Paint(Sender: TObject);
+    procedure SpinEdit_DisplacementChange(Sender: TObject);
+    procedure SpinEdit_PhaseChange(Sender: TObject);
+    procedure SpinEdit_WavelengthChange(Sender: TObject);
+    procedure Timer1Timer(Sender: TObject);
+  private
+    { private declarations }
+    FInitializing: boolean;
+    FCenter: TPointF;
+    procedure PreviewNeeded;
+    function ComputeFilteredLayer: TBGRABitmap;
+  public
+    FilterConnector: TFilterConnector;
+  end;
+
+var
+  FWaveDisplacement: TFWaveDisplacement;
+
+function ShowWaveDisplacementDlg(AFilterConnector: TObject):boolean;
+
+implementation
+
+uses umac, ugraph, LCScaleDPI;
+
+function ShowWaveDisplacementDlg(AFilterConnector: TObject):boolean;
+var
+  FWaveDisplacement: TFWaveDisplacement;
+begin
+  result := false;
+  FWaveDisplacement:= TFWaveDisplacement.create(nil);
+  FWaveDisplacement.FilterConnector := AFilterConnector as TFilterConnector;
+  try
+    if FWaveDisplacement.FilterConnector.ActiveLayer <> nil then
+      result:= (FWaveDisplacement.showModal = mrOk)
+    else
+      result := false;
+  finally
+    FWaveDisplacement.free;
+  end;
+end;
+
+{ TFWaveDisplacement }
+
+procedure TFWaveDisplacement.FormCreate(Sender: TObject);
+begin
+  ScaleControl(Self,OriginalDPI);
+
+  CheckSpinEdit(SpinEdit_Wavelength);
+  CheckSpinEdit(SpinEdit_Displacement);
+  CheckSpinEdit(SpinEdit_Phase);
+  CheckOKCancelBtns(Button_OK{,Button_Cancel});
+
+  FCenter := PointF(0.5,0.5);
+end;
+
+procedure TFWaveDisplacement.FormShow(Sender: TObject);
+begin
+  FInitializing:= true;
+  SpinEdit_Wavelength.Value := round(FilterConnector.LazPaintInstance.Config.DefaultWaveDisplacementWavelength);
+  SpinEdit_Displacement.Value := round(FilterConnector.LazPaintInstance.Config.DefaultWaveDisplacementAmount);
+  SpinEdit_Phase.Value := round(FilterConnector.LazPaintInstance.Config.DefaultWaveDisplacementPhase);
+  FInitializing := false;
+  PreviewNeeded;
+  Left := FilterConnector.LazPaintInstance.MainFormBounds.Left;
+end;
+
+procedure TFWaveDisplacement.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
+  Shift: TShiftState; X, Y: Integer);
+begin
+  FCenter := PointF(X/PaintBox1.Width,Y/PaintBox1.Height);
+  PaintBox1.Invalidate;
+  PreviewNeeded;
+end;
+
+procedure TFWaveDisplacement.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
+  Y: Integer);
+begin
+  if ssLeft in Shift then
+  begin
+    FCenter := PointF(X/PaintBox1.Width,Y/PaintBox1.Height);
+    PaintBox1.Invalidate;
+    PreviewNeeded;
+  end;
+end;
+
+procedure TFWaveDisplacement.PaintBox1Paint(Sender: TObject);
+var x,y: integer;
+begin
+  x := round(FCenter.X*PaintBox1.Width);
+  y := round(FCenter.Y*PaintBox1.Height);
+  PaintBox1.Canvas.Brush.Style := bsClear;
+  PaintBox1.Canvas.Pen.Style := psSolid;
+  PaintBox1.Canvas.Pen.Color := clWindowText;
+  PaintBox1.Canvas.Rectangle(0,0,PaintBox1.Width,PaintBox1.Height);
+  PaintBox1.Canvas.Pen.Color := clBlack;
+  PaintBox1.Canvas.Brush.Style := bsSolid;
+  PaintBox1.Canvas.Brush.Color := clWhite;
+  PaintBox1.Canvas.Ellipse(x-3,y-3,x+4,y+4);
+end;
+
+procedure TFWaveDisplacement.SpinEdit_WavelengthChange(Sender: TObject);
+begin
+  if not FInitializing then PreviewNeeded;
+end;
+
+procedure TFWaveDisplacement.SpinEdit_DisplacementChange(Sender: TObject);
+begin
+  if not FInitializing then PreviewNeeded;
+end;
+
+procedure TFWaveDisplacement.SpinEdit_PhaseChange(Sender: TObject);
+begin
+  if not FInitializing then PreviewNeeded;
+end;
+
+procedure TFWaveDisplacement.Timer1Timer(Sender: TObject);
+begin
+  Timer1.Enabled := false;
+  FilterConnector.PutImage(ComputeFilteredLayer,False,true);
+  Button_OK.Enabled := true;
+end;
+
+procedure TFWaveDisplacement.PreviewNeeded;
+begin
+  Timer1.Enabled := false;
+  Timer1.Enabled := True;
+  Button_OK.Enabled := false;
+end;
+
+function TFWaveDisplacement.ComputeFilteredLayer: TBGRABitmap;
+begin
+  result := ugraph.WaveDisplacementFilter(FilterConnector.BackupLayer,FilterConnector.WorkArea,
+      PointF(FCenter.X*FilterConnector.ActiveLayer.Width,FCenter.Y*FilterConnector.ActiveLayer.Height),
+      SpinEdit_Wavelength.Value,SpinEdit_Displacement.Value,SpinEdit_Phase.Value) as TBGRABitmap;
+end;
+
+procedure TFWaveDisplacement.Button_OKClick(Sender: TObject);
+begin
+  FilterConnector.ValidateAction;
+  FilterConnector.LazPaintInstance.Config.SetDefaultWaveDisplacementWavelength(SpinEdit_Wavelength.Value);
+  FilterConnector.LazPaintInstance.Config.SetDefaultWaveDisplacementAmount(SpinEdit_Displacement.Value);
+  FilterConnector.LazPaintInstance.Config.SetDefaultWaveDisplacementPhase(SpinEdit_Phase.Value);
+  ModalResult := mrOK;
+end;
+
+{$R *.lfm}
+
+end.
+

+ 10 - 1
lazpaint/lazpaint.lpi

@@ -336,7 +336,7 @@
         <PackageName Value="LCL"/>
       </Item5>
     </RequiredPackages>
-    <Units Count="97">
+    <Units Count="98">
       <Unit0>
         <Filename Value="lazpaint.lpr"/>
         <IsPartOfProject Value="True"/>
@@ -907,7 +907,16 @@
       <Unit96>
         <Filename Value="udarktheme.pas"/>
         <IsPartOfProject Value="True"/>
+        <UnitName Value="UDarkTheme"/>
       </Unit96>
+      <Unit97>
+        <Filename Value="dialog\filter\uwavedisplacement.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FWaveDisplacement"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+        <UnitName Value="UWaveDisplacement"/>
+      </Unit97>
     </Units>
   </ProjectOptions>
   <CompilerOptions>

+ 2 - 2
lazpaint/lazpaint.lpr

@@ -24,7 +24,7 @@ uses
 
   //forms
   UNewimage, UMultiImage, UBrowseImages, UBlendOp, UCanvassize, UResample, UObject3D,
-  URadialBlur, UMotionBlur, UCustomblur, UEmboss, UTwirl, UPixelate,
+  URadialBlur, UMotionBlur, UCustomblur, UEmboss, UTwirl, UWaveDisplacement, UPixelate,
   UColorintensity, UShiftColors, UColorize, USharpen,
   UPhongFilter, UFilterFunction,
   UAbout, ULoading,
@@ -37,7 +37,7 @@ uses
   UToolBrush, UMainFormLayout, USaveOption, UBrushType, ugeometricbrush,
   URainType, UFormRain, UPaletteToolbar, uselectionhighlight, UGridBitmap,
   UToolIcon, UImagePreview, UPreviewDialog, UQuestion, UTiff, UImageView,
-  udarktheme;
+  UDarkTheme;
 
 //sometimes LResources disappear in the uses clause
 

+ 12 - 0
lazpaint/lazpaintdialogs.inc

@@ -396,6 +396,18 @@ begin
   self.ShowTopmost(top);
 end;
 
+function TLazPaintInstance.ShowwaveDisplacementDlg(AFilterConnector: TObject): boolean;
+var oldSelectionNormal: boolean;
+    top: TTopMostInfo;
+begin
+  top := self.HideTopmost;
+  oldSelectionNormal := ShowSelectionNormal;
+  ShowSelectionNormal := true;
+  result := UWaveDisplacement.ShowWaveDisplacementDlg(AFilterConnector);
+  ShowSelectionNormal := oldSelectionNormal;
+  self.ShowTopmost(top);
+end;
+
 function TLazPaintInstance.ShowPhongFilterDlg(AFilterConnector: TObject): boolean;
 var oldSelectionNormal: boolean;
     top: TTopMostInfo;

+ 2 - 1
lazpaint/lazpaintinstance.pas

@@ -170,6 +170,7 @@ type
     function ShowPixelateDlg(AFilterConnector: TObject):boolean; override;
     function ShowNoiseFilterDlg(AFilterConnector: TObject):boolean; override;
     function ShowTwirlDlg(AFilterConnector: TObject):boolean; override;
+    function ShowWaveDisplacementDlg(AFilterConnector: TObject):boolean; override;
     function ShowPhongFilterDlg(AFilterConnector: TObject): boolean; override;
     function ShowFunctionFilterDlg(AFilterConnector: TObject): boolean; override;
     function ShowSharpenDlg(AFilterConnector: TObject):boolean; override;
@@ -206,7 +207,7 @@ implementation
 
 uses LCLType, Types, Forms, Dialogs, FileUtil, LCLIntf, Math,
 
-     uradialblur, umotionblur, uemboss, utwirl,
+     uradialblur, umotionblur, uemboss, UTwirl, UWaveDisplacement,
      unewimage, uresample, upixelate, unoisefilter, ufilters,
      UImageAction, USharpen, uposterize, UPhongFilter, UFilterFunction,
      uprint, USaveOption, UFormRain,

+ 5 - 0
lazpaint/lazpaintmainform.lfm

@@ -6805,6 +6805,11 @@ object FMain: TFMain
       OnExecute = ViewDarkThemeExecute
       OnUpdate = ViewDarkThemeUpdate
     end
+    object FilterWaveDisplacement: TAction
+      Category = 'Filter'
+      Caption = 'Wave diplacement...'
+      OnExecute = FilterAnyExecute
+    end
   end
   object ColorDialog1: TColorDialog
     Title = 'Choose color'

+ 1 - 0
lazpaint/lazpaintmainform.pas

@@ -28,6 +28,7 @@ type
   { TFMain }
 
   TFMain = class(TForm)
+    FilterWaveDisplacement: TAction;
     ViewDarkTheme: TAction;
     MenuFileToolbar: TMenuItem;
     ViewWorkspaceColor: TAction;

+ 4 - 3
lazpaint/lazpainttype.pas

@@ -76,7 +76,7 @@ type
                     pfBlurPrecise, pfBlurRadial, pfBlurFast, pfBlurBox, pfBlurCorona, pfBlurDisk, pfBlurMotion, pfBlurCustom,
                     pfSharpen, pfSmooth, pfMedian, pfNoise, pfPixelate, pfClearType, pfClearTypeInverse, pfFunction,
                     pfEmboss, pfPhong, pfContour, pfGrayscale, pfNegative, pfLinearNegative, pfComplementaryColor, pfNormalize,
-                    pfSphere, pfTwirl, pfCylinder, pfPlane,
+                    pfSphere, pfTwirl, pfWaveDisplacement, pfCylinder, pfPlane,
                     pfPerlinNoise,pfCyclicPerlinNoise,pfClouds,pfCustomWater,pfWater,pfRain,pfWood,pfWoodVertical,pfPlastik,pfMetalFloor,pfCamouflage,
                     pfSnowPrint,pfStone,pfRoundStone,pfMarble);
 
@@ -86,7 +86,7 @@ const
                     'BlurPrecise', 'BlurRadial', 'BlurFast', 'BlurBox', 'BlurCorona', 'BlurDisk', 'BlurMotion', 'BlurCustom',
                     'Sharpen', 'Smooth', 'Median', 'Noise', 'Pixelate', 'ClearType', 'ClearTypeInverse', 'Function',
                     'Emboss', 'Phong', 'Contour', 'Grayscale', 'Negative', 'LinearNegative', 'ComplementaryColor', 'Normalize',
-                    'Sphere', 'Twirl', 'Cylinder', 'Plane',
+                    'Sphere', 'Twirl', 'WaveDisplacement', 'Cylinder', 'Plane',
                     'PerlinNoise','CyclicPerlinNoise','Clouds','CustomWater','Water','Rain','Wood','WoodVertical','Plastik','MetalFloor','Camouflage',
                     'SnowPrint','Stone','RoundStone','Marble');
 
@@ -95,7 +95,7 @@ const
                     false, false, false, false, false, false, false, false,
                     false, false, false, false, false, true, true, true,
                     false, true, false, false, false, false, false, false,
-                    false, false, false, false,
+                    false, false, false, false, false,
                     false,false,true,true,true,true,true,true,true,true,true,
                     true,true,true,true);
 
@@ -255,6 +255,7 @@ type
     function ShowPixelateDlg(AFilterConnector: TObject):boolean; virtual; abstract;
     function ShowNoiseFilterDlg(AFilterConnector: TObject):boolean; virtual; abstract;
     function ShowTwirlDlg(AFilterConnector: TObject):boolean; virtual; abstract;
+    function ShowWaveDisplacementDlg(AFilterConnector: TObject):boolean; virtual; abstract;
     function ShowPhongFilterDlg(AFilterConnector: TObject): boolean; virtual; abstract;
     function ShowFunctionFilterDlg(AFilterConnector: TObject): boolean; virtual; abstract;
     function ShowSharpenDlg(AFilterConnector: TObject):boolean; virtual; abstract;

+ 32 - 0
lazpaint/release/bin/i18n/lazpaint.ar.po

@@ -950,6 +950,10 @@ msgstr "كرة"
 msgid "Twirl..."
 msgstr "برم"
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2663,6 +2667,34 @@ msgctxt "tftwirl.label_radius.caption"
 msgid "Radius :"
 msgstr "نصف القطر :"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "إلغاء"
+
+#: tfwavedisplacement.button_ok.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr "موافق"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr "العمل في تقدم"

+ 32 - 0
lazpaint/release/bin/i18n/lazpaint.cs.po

@@ -947,6 +947,10 @@ msgstr "Koule"
 msgid "Twirl..."
 msgstr "Vířit..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2661,6 +2665,34 @@ msgctxt "tftwirl.label_radius.caption"
 msgid "Radius :"
 msgstr "Poloměr :"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "Zrušit"
+
+#: tfwavedisplacement.button_ok.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr "OK"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr "Akce v běhu"

+ 32 - 0
lazpaint/release/bin/i18n/lazpaint.de.po

@@ -962,6 +962,10 @@ msgstr "Sphäre"
 msgid "Twirl..."
 msgstr "Wirbel..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2677,6 +2681,34 @@ msgctxt "tftwirl.label_radius.caption"
 msgid "Radius :"
 msgstr "Radius:"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "Abbruch"
+
+#: tfwavedisplacement.button_ok.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr "OK"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr "Aktion wird durchgeführt"

+ 30 - 0
lazpaint/release/bin/i18n/lazpaint.es.po

@@ -944,6 +944,10 @@ msgstr "Esfera"
 msgid "Twirl..."
 msgstr "Remolino..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr "Desplazamiento de onda..."
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr "Olvidar respuestas de cuadro de diálogo"
@@ -2657,6 +2661,32 @@ msgctxt "tftwirl.label_radius.caption"
 msgid "Radius :"
 msgstr "Rádio:"
 
+#: tfwavedisplacement.button_cancel.caption
+msgctxt "TFWAVEDISPLACEMENT.BUTTON_CANCEL.CAPTION"
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: tfwavedisplacement.button_ok.caption
+msgctxt "TFWAVEDISPLACEMENT.BUTTON_OK.CAPTION"
+msgid "OK"
+msgstr "Aceptar"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr "Desplazamiento de onda"
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr "Desplazamiento:"
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr "Fase:"
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr "Longitud de onda:"
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr "Acción en progreso"

+ 31 - 0
lazpaint/release/bin/i18n/lazpaint.fi.po

@@ -934,6 +934,10 @@ msgstr ""
 msgid "Twirl..."
 msgstr "Pyörre"
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2647,6 +2651,33 @@ msgctxt "TFTWIRL.LABEL_RADIUS.CAPTION"
 msgid "Radius :"
 msgstr "Säde :"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "Peru"
+
+#: tfwavedisplacement.button_ok.caption
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr ""
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr ""

+ 30 - 0
lazpaint/release/bin/i18n/lazpaint.fr.po

@@ -950,6 +950,10 @@ msgstr "Sphère"
 msgid "Twirl..."
 msgstr "Torsion..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr "Décalage d'onde..."
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr "Oublier les choix aux boites de dialogue"
@@ -2664,6 +2668,32 @@ msgctxt "tftwirl.label_radius.caption"
 msgid "Radius :"
 msgstr "Rayon :"
 
+#: tfwavedisplacement.button_cancel.caption
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "Annuler"
+
+#: tfwavedisplacement.button_ok.caption
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr "OK"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr "Décalage d'onde"
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr "Décalage :"
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr "Phase :"
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr "Longueur d'onde :"
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr "Action en cours"

+ 32 - 0
lazpaint/release/bin/i18n/lazpaint.ja.po

@@ -950,6 +950,10 @@ msgstr "Sphereフィルタ"
 msgid "Twirl..."
 msgstr "Twirlフィルタ..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2663,6 +2667,34 @@ msgctxt "tftwirl.label_radius.caption"
 msgid "Radius :"
 msgstr "半径:"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "キャンセル"
+
+#: tfwavedisplacement.button_ok.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr "OK"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr ""

+ 32 - 0
lazpaint/release/bin/i18n/lazpaint.lv.po

@@ -948,6 +948,10 @@ msgstr "Olveida"
 msgid "Twirl..."
 msgstr "Savirpināt ..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2660,6 +2664,34 @@ msgctxt "TFTWIRL.LABEL_RADIUS.CAPTION"
 msgid "Radius :"
 msgstr "Rādiuss:"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "Atsaukt"
+
+#: tfwavedisplacement.button_ok.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr "Labi"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr "Moris darbojas"

+ 32 - 0
lazpaint/release/bin/i18n/lazpaint.nl.po

@@ -971,6 +971,10 @@ msgstr "Bol"
 msgid "Twirl..."
 msgstr "Wervelen..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2686,6 +2690,34 @@ msgctxt "tftwirl.label_radius.caption"
 msgid "Radius :"
 msgstr "Radius :"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "Annuleren"
+
+#: tfwavedisplacement.button_ok.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr "OK"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr "Bezig met actie"

+ 30 - 0
lazpaint/release/bin/i18n/lazpaint.po

@@ -934,6 +934,10 @@ msgstr ""
 msgid "Twirl..."
 msgstr ""
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2647,6 +2651,32 @@ msgctxt "TFTWIRL.LABEL_RADIUS.CAPTION"
 msgid "Radius :"
 msgstr ""
 
+#: tfwavedisplacement.button_cancel.caption
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr ""
+
+#: tfwavedisplacement.button_ok.caption
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr ""
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr ""

+ 31 - 0
lazpaint/release/bin/i18n/lazpaint.pt_BR.po

@@ -954,6 +954,10 @@ msgstr "Esfera"
 msgid "Twirl..."
 msgstr "Rodopio..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2667,6 +2671,33 @@ msgctxt "TFTWIRL.LABEL_RADIUS.CAPTION"
 msgid "Radius :"
 msgstr "Raio :"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: tfwavedisplacement.button_ok.caption
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr ""
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr "Ação em progresso"

+ 32 - 0
lazpaint/release/bin/i18n/lazpaint.ru.po

@@ -945,6 +945,10 @@ msgstr "Сфера"
 msgid "Twirl..."
 msgstr "Вращение ..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2656,6 +2660,34 @@ msgctxt "tftwirl.label_radius.caption"
 msgid "Radius :"
 msgstr "Радиус:"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "Отменить"
+
+#: tfwavedisplacement.button_ok.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr "ОК"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr "Прогресс действия"

+ 32 - 0
lazpaint/release/bin/i18n/lazpaint.sv.po

@@ -934,6 +934,10 @@ msgstr "Sfär"
 msgid "Twirl..."
 msgstr "Snurra..."
 
+#: tfmain.filterwavedisplacement.caption
+msgid "Wave diplacement..."
+msgstr ""
+
 #: tfmain.forgetdialoganswers.caption
 msgid "Forget dialog box answers"
 msgstr ""
@@ -2647,6 +2651,34 @@ msgctxt "tftwirl.label_radius.caption"
 msgid "Radius :"
 msgstr "Radie :"
 
+#: tfwavedisplacement.button_cancel.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_cancel.caption"
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: tfwavedisplacement.button_ok.caption
+#, fuzzy
+msgctxt "tfwavedisplacement.button_ok.caption"
+msgid "OK"
+msgstr "OK"
+
+#: tfwavedisplacement.caption
+msgid "Wave displacement"
+msgstr ""
+
+#: tfwavedisplacement.label_displacement.caption
+msgid "Displacement :"
+msgstr ""
+
+#: tfwavedisplacement.label_phase.caption
+msgid "Phase :"
+msgstr ""
+
+#: tfwavedisplacement.label_wavelength.caption
+msgid "Wavelength :"
+msgstr ""
+
 #: uresourcestrings.rsactioninprogress
 msgid "Action in progress"
 msgstr ""

+ 38 - 0
lazpaint/uconfig.pas

@@ -276,6 +276,14 @@ type
     function DefaultTwirlTurn: double;
     procedure SetDefaultTwirlTurn(value: double);
 
+    //wave displacement config
+    function DefaultWaveDisplacementWavelength: double;
+    procedure SetDefaultWaveDisplacementWavelength(value: double);
+    function DefaultWaveDisplacementAmount: double;
+    procedure SetDefaultWaveDisplacementAmount(value: double);
+    function DefaultWaveDisplacementPhase: double;
+    procedure SetDefaultWaveDisplacementPhase(value: double);
+
     //phong filter
     function DefaultPhongFilterAltitude: integer;
     procedure SetDefaultPhongFilterAltitude(value: integer);
@@ -1152,6 +1160,36 @@ begin
   iniOptions.WriteFloat('Filter','TwirlTurn',value);
 end;
 
+function TLazPaintConfig.DefaultWaveDisplacementWavelength: double;
+begin
+  result := iniOptions.ReadFloat('Filter','WaveDisplacementWavelength',100);
+end;
+
+procedure TLazPaintConfig.SetDefaultWaveDisplacementWavelength(value: double);
+begin
+  iniOptions.WriteFloat('Filter','WaveDisplacementWavelength',value);
+end;
+
+function TLazPaintConfig.DefaultWaveDisplacementAmount: double;
+begin
+  result := iniOptions.ReadFloat('Filter','WaveDisplacementAmount',50);
+end;
+
+procedure TLazPaintConfig.SetDefaultWaveDisplacementAmount(value: double);
+begin
+  iniOptions.WriteFloat('Filter','WaveDisplacementAmount',value);
+end;
+
+function TLazPaintConfig.DefaultWaveDisplacementPhase: double;
+begin
+  result := iniOptions.ReadFloat('Filter','WaveDisplacementPhase',0);
+end;
+
+procedure TLazPaintConfig.SetDefaultWaveDisplacementPhase(value: double);
+begin
+  iniOptions.WriteFloat('Filter','WaveDisplacementPhase',value);
+end;
+
 function TLazPaintConfig.DefaultPhongFilterAltitude: integer;
 begin
   result := iniOptions.ReadInteger('Filter','MapAltitude',10);

+ 5 - 0
lazpaint/ufilters.pas

@@ -163,6 +163,11 @@ begin
           filteredLayer := layer.FilterTwirl(FilterConnector.WorkArea, Point(layer.Width div 2,layer.Height div 2), AInstance.Config.DefaultTwirlRadius, AInstance.Config.DefaultTwirlTurn ) as TBGRABitmap
         else
           AInstance.ShowTwirlDlg(FilterConnector);
+    pfWaveDisplacement:
+        if skipDialog then
+          filteredLayer := ugraph.WaveDisplacementFilter(layer,FilterConnector.WorkArea, PointF(layer.Width/2,layer.Height/2), AInstance.Config.DefaultWaveDisplacementWavelength, AInstance.Config.DefaultWaveDisplacementAmount, AInstance.Config.DefaultWaveDisplacementPhase ) as TBGRABitmap
+        else
+          AInstance.ShowWaveDisplacementDlg(FilterConnector);
     pfContour: filteredLayer := layer.FilterContour as TBGRABitmap;
     pfGrayscale: filteredLayer := layer.FilterGrayscale(FilterConnector.WorkArea) as TBGRABitmap;
     pfPerlinNoise: filteredLayer := CreatePerlinNoiseMap(layer.Width,layer.Height,layer.Width/256,layer.Height/256,1,rfBestQuality);

+ 47 - 0
lazpaint/ugraph.pas

@@ -49,6 +49,9 @@ function CreateVerticalWoodTexture(tx,ty: integer): TBGRABitmap;
 
 function ClearTypeFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
 function ClearTypeInverseFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
+function WaveDisplacementFilter(source: TBGRACustomBitmap;
+  ARect: TRect; ACenter: TPointF;
+  AWaveLength, ADisplacement, APhase: single): TBGRACustomBitmap;
 
 function DoResample(source :TBGRABitmap; newWidth, newHeight: integer; StretchMode: TResampleMode): TBGRABitmap;
 procedure DrawArrow(ACanvas: TCanvas; ARect: TRect; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
@@ -738,6 +741,50 @@ begin
   result := temp;
 end;
 
+type
+  { TWaveDisplacementScanner }
+
+  TWaveDisplacementScanner = class(TBGRACustomScanner)
+    Source: TBGRACustomBitmap;
+    Center: TPointF;
+    Wavelength, Displacement, PhaseRad: single;
+    function ScanAt(X,Y: Single): TBGRAPixel; override;
+  end;
+
+{ TWaveDisplacementScanner }
+
+function TWaveDisplacementScanner.ScanAt(X, Y: Single): TBGRAPixel;
+var
+  u, disp: TPointF;
+  dist: Single;
+  alpha: ValReal;
+begin
+  u := PointF(X,Y)-Center;
+  dist := VectLen(u);
+  if dist = 0 then disp := PointF(0,0) else
+  begin
+    u := u*(1/dist);
+    alpha := PhaseRad+dist*2*Pi/Wavelength;
+    disp := u*sin(alpha)*Displacement;
+  end;
+  result := Source.GetPixel(x+disp.x,y+disp.y);
+end;
+
+function WaveDisplacementFilter(source: TBGRACustomBitmap; ARect: TRect;
+  ACenter: TPointF; AWaveLength, ADisplacement, APhase: single): TBGRACustomBitmap;
+var scan: TWaveDisplacementScanner;
+begin
+ scan := TWaveDisplacementScanner.Create;
+ scan.Center := ACenter;
+ scan.Source := source;
+ scan.Wavelength := AWaveLength;
+ scan.Displacement := ADisplacement;
+ scan.PhaseRad := APhase*Pi/180;
+ result := TBGRABitmap.Create(source.Width,source.Height);
+ result.FillRect(ARect, scan, dmSet);
+ scan.Free;
+end;
+
 function DoResample(source: TBGRABitmap; newWidth, newHeight: integer;
   StretchMode: TResampleMode): TBGRABitmap;
 begin

+ 1 - 1
lazpaint/umenu.pas

@@ -341,7 +341,7 @@ begin
   AddMenus('MenuView',   'ViewGrid,ViewZoomOriginal,ViewZoomIn,ViewZoomOut,ViewZoomFit,-,ViewToolBox,ViewColors,ViewPalette,ViewLayerStack,ViewImageList,ViewStatusBar,-,*,-,ViewDarkTheme,ViewWorkspaceColor,MenuIconSize');
   AddMenus('MenuImage',  'ImageCrop,ImageCropLayer,ImageFlatten,MenuRemoveTransparency,-,ImageNegative,ImageLinearNegative,ImageSwapRedBlue,-,ImageChangeCanvasSize,ImageRepeat,-,ImageResample,ImageSmartZoom3,-,ImageRotateCW,ImageRotateCCW,ImageHorizontalFlip,ImageVerticalFlip');
   AddMenus('MenuRemoveTransparency', 'ImageClearAlpha,ImageFillBackground');
-  AddMenus('MenuFilter', 'MenuRadialBlur,FilterBlurMotion,FilterBlurCustom,FilterPixelate,-,FilterSharpen,FilterSmooth,FilterNoise,FilterMedian,FilterClearType,FilterClearTypeInverse,FilterFunction,-,FilterContour,FilterEmboss,FilterPhong,-,FilterSphere,FilterTwirl,FilterCylinder');
+  AddMenus('MenuFilter', 'MenuRadialBlur,FilterBlurMotion,FilterBlurCustom,FilterPixelate,-,FilterSharpen,FilterSmooth,FilterNoise,FilterMedian,FilterClearType,FilterClearTypeInverse,FilterFunction,-,FilterContour,FilterEmboss,FilterPhong,-,FilterSphere,FilterTwirl,FilterWaveDisplacement,FilterCylinder');
   AddMenus('MenuRadialBlur',  'FilterBlurBox,FilterBlurFast,FilterBlurRadial,FilterBlurCorona,FilterBlurDisk');
   AddMenus('MenuColors', 'ColorCurves,ColorPosterize,ColorColorize,ColorShiftColors,FilterComplementaryColor,ColorIntensity,-,ColorLightness,FilterNegative,FilterLinearNegative,FilterNormalize,FilterGrayscale');
   AddMenus('MenuTool',   'ToolHand,ToolHotSpot,ToolColorPicker,-,ToolPen,ToolBrush,ToolEraser,ToolFloodFill,ToolClone,-,ToolRect,ToolEllipse,ToolPolygon,ToolSpline,ToolGradient,ToolPhong,ToolText,-,ToolDeformation,ToolTextureMapping');