소스 검색

Merge pull request #134 from bgrabitmap/dev-lazpaint

Dev lazpaint 7.0.9
circular17 5 년 전
부모
커밋
8892e25752
88개의 변경된 파일5227개의 추가작업 그리고 2108개의 파일을 삭제
  1. 1 0
      lazpaint/dialog/filter/umotionblur.pas
  2. 23 24
      lazpaint/dialog/filter/uradialblur.lfm
  3. 1 0
      lazpaint/dialog/filter/uradialblur.pas
  4. 29 10
      lazpaint/dialog/ubrowseimages.pas
  5. 10 6
      lazpaint/dialog/uchoosecolorinterface.pas
  6. 9 4
      lazpaint/image/uimage.pas
  7. 32 10
      lazpaint/image/uimageaction.pas
  8. 71 5
      lazpaint/image/uimagediff.pas
  9. 2 8
      lazpaint/lazpaint.lpi
  10. 11 0
      lazpaint/lazpaintdialogs.inc
  11. 1 1
      lazpaint/lazpaintembeddedpack.lpk
  12. 68 26
      lazpaint/lazpaintinstance.pas
  13. 64 9
      lazpaint/lazpaintmainform.lfm
  14. 76 35
      lazpaint/lazpaintmainform.pas
  15. 48 17
      lazpaint/lazpainttype.pas
  16. 120 155
      lazpaint/maintoolbar.inc
  17. 5 1
      lazpaint/release/bin/i18n/lazpaint.ar.po
  18. 126 168
      lazpaint/release/bin/i18n/lazpaint.bg.po
  19. 5 1
      lazpaint/release/bin/i18n/lazpaint.cs.po
  20. 5 1
      lazpaint/release/bin/i18n/lazpaint.de.po
  21. 5 1
      lazpaint/release/bin/i18n/lazpaint.es.po
  22. 5 1
      lazpaint/release/bin/i18n/lazpaint.fi.po
  23. 5 1
      lazpaint/release/bin/i18n/lazpaint.fr.po
  24. 5 1
      lazpaint/release/bin/i18n/lazpaint.ja.po
  25. 133 134
      lazpaint/release/bin/i18n/lazpaint.kab.po
  26. 171 166
      lazpaint/release/bin/i18n/lazpaint.lv.po
  27. 6 2
      lazpaint/release/bin/i18n/lazpaint.nl.po
  28. 5 1
      lazpaint/release/bin/i18n/lazpaint.po
  29. 6 2
      lazpaint/release/bin/i18n/lazpaint.pt_BR.po
  30. 5 1
      lazpaint/release/bin/i18n/lazpaint.ru.po
  31. 5 1
      lazpaint/release/bin/i18n/lazpaint.sv.po
  32. 6 2
      lazpaint/release/bin/i18n/lazpaint.zh_CN.po
  33. 174 173
      lazpaint/release/bin/i18n/lclstrconsts.kab.po
  34. 180 171
      lazpaint/release/bin/i18n/lcresourcestring.bg.po
  35. 180 171
      lazpaint/release/bin/i18n/lcresourcestring.kab.po
  36. 35 26
      lazpaint/release/bin/i18n/lcresourcestring.lv.po
  37. 28 0
      lazpaint/release/changelog
  38. 28 0
      lazpaint/release/debian/linux32/DEBIAN/changelog
  39. 1 1
      lazpaint/release/debian/linux32/DEBIAN/control
  40. 28 0
      lazpaint/release/debian/linux64/DEBIAN/changelog
  41. 1 1
      lazpaint/release/debian/linux64/DEBIAN/control
  42. 2 2
      lazpaint/release/macOS/LazPaint.app/Contents/Info.plist
  43. 1 1
      lazpaint/release/macOS/makedmg.sh
  44. 1 1
      lazpaint/release/windows/lazpaint.iss
  45. 18 2
      lazpaint/release/windows/stage.bat
  46. 242 72
      lazpaint/tools/utool.pas
  47. 11 1
      lazpaint/tools/utooldeformationgrid.pas
  48. 103 36
      lazpaint/tools/utoolfloodfill.pas
  49. 26 11
      lazpaint/tools/utoollayer.pas
  50. 3 3
      lazpaint/tools/utoolphong.pas
  51. 19 34
      lazpaint/tools/utoolpolygon.pas
  52. 7 7
      lazpaint/tools/utoolselect.pas
  53. 41 77
      lazpaint/tools/utooltext.pas
  54. 258 156
      lazpaint/tools/utoolvectorial.pas
  55. 1 4
      lazpaint/ucommandline.pas
  56. 25 1
      lazpaint/uconfig.pas
  57. 31 0
      lazpaint/ufileextensions.pas
  58. 39 21
      lazpaint/uimageview.pas
  59. 72 49
      lazpaint/umainformlayout.pas
  60. 45 9
      lazpaint/umenu.pas
  61. 1 0
      lazpaint/upalettetoolbar.pas
  62. 1 1
      lazpaint/upython.pas
  63. 2 1
      lazpaint/uresourcestrings.pas
  64. 8 7
      lazpaintcontrols/lazpaintcontrols.lpk
  65. 1 1
      lazpaintcontrols/lazpaintcontrols.pas
  66. 24 7
      lazpaintcontrols/lcvectorclipboard.pas
  67. 35 5
      lazpaintcontrols/lcvectorialfill.pas
  68. 3 3
      lazpaintcontrols/lcvectorialfillinterface.pas
  69. 1018 0
      lazpaintcontrols/lcvectormultishape.pas
  70. 423 112
      lazpaintcontrols/lcvectororiginal.pas
  71. 51 31
      lazpaintcontrols/lcvectorpolyshapes.pas
  72. 126 86
      lazpaintcontrols/lcvectorrectshapes.pas
  73. 50 16
      lazpaintcontrols/lcvectortextshapes.pas
  74. 1 1
      scripts/channels_merge.py
  75. 1 1
      scripts/channels_split_hsl.py
  76. 1 1
      scripts/channels_split_rgb.py
  77. 10 0
      scripts/display_version.py
  78. 1 1
      scripts/layerfx_color_overlay.py
  79. 261 0
      scripts/layerfx_innerlight.py
  80. 261 0
      scripts/layerfx_innershadow.py
  81. 7 7
      scripts/layerfx_shadow.py
  82. 215 0
      scripts/layerfx_stroke.py
  83. 2 0
      scripts/lazpaint/command.py
  84. 45 0
      scripts/lazpaint/tools.py
  85. 12 0
      scripts/mask_from_alpha.py
  86. 1 1
      scripts/mask_new.py
  87. 1 1
      scripts/render_fractal_tree.py
  88. 1 1
      scripts/render_lava.py

+ 1 - 0
lazpaint/dialog/filter/umotionblur.pas

@@ -218,6 +218,7 @@ begin
   InPaintBoxMouseMove := false;
   CheckOKCancelBtns(Button_OK{,Button_Cancel});
   CheckFloatSpinEdit(SpinEdit_Distance);
+  SpinEdit_Distance.Constraints.MinWidth := DoScaleX(70, OriginalDPI);
 end;
 
 procedure TFMotionBlur.FormShow(Sender: TObject);

+ 23 - 24
lazpaint/dialog/filter/uradialblur.lfm

@@ -1,6 +1,6 @@
 object FRadialBlur: TFRadialBlur
   Left = 403
-  Height = 70
+  Height = 82
   Top = 328
   Width = 175
   AutoSize = True
@@ -13,43 +13,42 @@ object FRadialBlur: TFRadialBlur
   ChildSizing.VerticalSpacing = 8
   ChildSizing.Layout = cclLeftToRightThenTopToBottom
   ChildSizing.ControlsPerLine = 1
-  ClientHeight = 70
+  ClientHeight = 82
   ClientWidth = 175
   Font.Height = -12
   OnCloseQuery = FormCloseQuery
   OnCreate = FormCreate
   OnShow = FormShow
   Position = poOwnerFormCenter
-  LCLVersion = '1.6.0.4'
+  LCLVersion = '2.0.2.0'
   object Panel1: TPanel
     Left = 8
-    Height = 23
+    Height = 27
     Top = 8
-    Width = 114
+    Width = 126
     BevelOuter = bvNone
     ChildSizing.HorizontalSpacing = 8
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.ControlsPerLine = 2
-    ClientHeight = 23
-    ClientWidth = 114
+    ClientHeight = 27
+    ClientWidth = 126
     TabOrder = 0
     object Label_Radius: TLabel
       Left = 0
-      Height = 23
+      Height = 27
       Top = 0
-      Width = 41
+      Width = 48
       Caption = 'Radius :'
       Layout = tlCenter
       ParentColor = False
     end
     object SpinEdit_Radius: TFloatSpinEdit
-      Left = 49
-      Height = 23
+      Left = 56
+      Height = 27
       Top = 0
-      Width = 65
-      Constraints.MinWidth = 65
+      Width = 70
+      Constraints.MinWidth = 70
       DecimalPlaces = 1
-      Increment = 1
       MaxValue = 1000
       MinValue = 0.1
       OnChange = SpinEdit_RadiusChange
@@ -59,21 +58,21 @@ object FRadialBlur: TFRadialBlur
   end
   object Panel2: TPanel
     Left = 8
-    Height = 25
-    Top = 39
-    Width = 114
+    Height = 29
+    Top = 43
+    Width = 126
     BevelOuter = bvNone
     ChildSizing.HorizontalSpacing = 8
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.ControlsPerLine = 2
-    ClientHeight = 25
-    ClientWidth = 114
+    ClientHeight = 29
+    ClientWidth = 126
     TabOrder = 1
     object Button_OK: TButton
       Left = 0
-      Height = 25
+      Height = 29
       Top = 0
-      Width = 42
+      Width = 31
       AutoSize = True
       Caption = 'OK'
       Default = True
@@ -81,10 +80,10 @@ object FRadialBlur: TFRadialBlur
       TabOrder = 0
     end
     object Button_Cancel: TButton
-      Left = 50
-      Height = 25
+      Left = 39
+      Height = 29
       Top = 0
-      Width = 62
+      Width = 54
       AutoSize = True
       Cancel = True
       Caption = 'Cancel'

+ 1 - 0
lazpaint/dialog/filter/uradialblur.pas

@@ -95,6 +95,7 @@ begin
   blurType := rbNormal;
   CheckOKCancelBtns(Button_OK{,Button_Cancel});
   CheckFloatSpinEdit(SpinEdit_Radius);
+  SpinEdit_Radius.Constraints.MinWidth := DoScaleX(70, OriginalDPI);
 end;
 
 procedure TFRadialBlur.FormShow(Sender: TObject);

+ 29 - 10
lazpaint/dialog/ubrowseimages.pas

@@ -105,6 +105,7 @@ type
     function GetInitialFilename: string;
     function GetOpenLayerIcon: boolean;
     function GetRememberStartDirectory: boolean;
+    procedure SetDefaultExtensions(AValue: string);
     procedure SetFileExtensionFilter(AValue: string);
     procedure SetFilterIndex(AValue: integer);
     procedure SetInitialFilename(AValue: string);
@@ -134,6 +135,7 @@ type
     procedure SetShellMask;
     procedure DeleteSelectedFiles;
     procedure SelectFile(AName: string);
+    procedure SelectDefaultExtensions;
     procedure PreviewValidate({%H-}ASender: TObject);
     property CurrentFullname: string read GetCurrentFullname;
     property CurrentDirectory: string read GetCurrentDirectory write SetCurrentDirectory;
@@ -151,7 +153,7 @@ type
     property IsSaveDialog: boolean read FIsSaveDialog write SetIsSaveDialog;
     property OverwritePrompt: boolean read FOverwritePrompt write FOverwritePrompt;
     property DefaultExtension: string read FDefaultExtension write FDefaultExtension;
-    property DefaultExtensions: string read FDefaultExtensions write FDefaultExtensions;
+    property DefaultExtensions: string read FDefaultExtensions write SetDefaultExtensions;
     property InitialFilename: string read GetInitialFilename write SetInitialFilename;
     property CurrentExtensionFilter: string read GetCurrentExtensionFilter;
     property Filter: string read FFileExtensionFilter write SetFileExtensionFilter;
@@ -427,15 +429,6 @@ begin
   FreeAndNil(FChosenImage.bmp);
   UpdatePreview;
   UpdateToolButtonOpen;
-  if (FDefaultExtensions<>'') and (ComboBox_FileExtension.ItemIndex = -1) then
-  begin
-    for i := 0 to high(FFileExtensions) do
-      if FFileExtensions[i] = FDefaultExtensions then
-      begin
-        ComboBox_FileExtension.ItemIndex := i;
-        break;
-      end;
-  end;
   if IsSaveDialog then
   begin
     If not AdaptExtension then
@@ -761,6 +754,13 @@ begin
   result := CheckBox_UseDirectoryOnStartup.Checked;
 end;
 
+procedure TFBrowseImages.SetDefaultExtensions(AValue: string);
+begin
+  if FDefaultExtensions=AValue then Exit;
+  FDefaultExtensions:=AValue;
+  SelectDefaultExtensions;
+end;
+
 procedure TFBrowseImages.SetFileExtensionFilter(AValue: string);
 begin
   if FFileExtensionFilter=AValue then Exit;
@@ -1129,8 +1129,12 @@ begin
     ComboBox_FileExtension.Items.Add(parsedExt[i*2]);
   end;
   parsedExt.Free;
+
   if ComboBox_FileExtension.Items.Count > 0 then
+  begin
     ComboBox_FileExtension.ItemIndex := 0;
+    SelectDefaultExtensions;
+  end;
 end;
 
 procedure TFBrowseImages.SetShellMask;
@@ -1212,6 +1216,21 @@ begin
   end;
 end;
 
+procedure TFBrowseImages.SelectDefaultExtensions;
+var
+  i: Integer;
+begin
+  if FDefaultExtensions <> '' then
+  begin
+    for i := 0 to high(FFileExtensions) do
+      if FFileExtensions[i] = FDefaultExtensions then
+      begin
+        ComboBox_FileExtension.ItemIndex := i;
+        break;
+      end;
+  end;
+end;
+
 procedure TFBrowseImages.PreviewValidate(ASender: TObject);
 begin
   ValidateFileOrDir;

+ 10 - 6
lazpaint/dialog/uchoosecolorinterface.pas

@@ -501,7 +501,7 @@ begin
 end;
 
 procedure TChooseColorInterface.DoSelect(X, Y: integer);
-var pix: TBGRAPixel;
+var pix, newColor: TBGRAPixel;
   newLight: Word;
 begin
   case FSelectZone of
@@ -518,10 +518,14 @@ begin
       pix := FColorCircle.bmpMaxlight.GetPixel(x-FColorCircle.Bounds.Left,y-FColorCircle.Bounds.top);
       if pix.alpha <> 0 then
       begin
-        FCurrentColor := BGRA(pix.Red,pix.Green,pix.Blue,FCurrentColor.Alpha);
-        ColorX := x-FColorCircle.Bounds.Left;
-        ColorY := y-FColorCircle.Bounds.top;
-        UpdateColorview(False, True, True);
+        newColor := BGRA(pix.Red,pix.Green,pix.Blue,FCurrentColor.Alpha);
+        if not FCurrentColor.EqualsExactly(newColor) then
+        begin
+          FCurrentColor := newColor;
+          ColorX := x-FColorCircle.Bounds.Left;
+          ColorY := y-FColorCircle.Bounds.top;
+          UpdateColorview(False, True, True);
+        end;
       end;
     end;
   szLightScale:
@@ -980,7 +984,7 @@ var newcolorlight: word;
 begin
    newcolorlight := ColorLightOf(value);
    newcurrentColor := ColorWithLight(value,$FFFF);
-   if (newcolorlight<>FColorLight) or (newcurrentcolor <> FCurrentColor) then
+   if (newcolorlight<>FColorLight) or not newcurrentcolor.EqualsExactly(FCurrentColor) then
    begin
      FColorLight := newcolorlight;
      FCurrentColor := newcurrentcolor;

+ 9 - 4
lazpaint/image/uimage.pas

@@ -48,6 +48,7 @@ type
     FRenderedImage: TBGRABitmap;
     FRenderedImageInvalidated: TRect;
     FOnImageChanged, FOnImageSaving, FOnImageExport: TLazPaintImageObservable;
+    FOnImageRenderChanged: TNotifyEvent;
     FUndoList: TComposedImageDifference;
     FUndoPos: integer;
     FRenderUpdateRectInPicCoord, FRenderUpdateRectInVSCoord: TRect;
@@ -160,7 +161,7 @@ type
     procedure LayerMayChangeCompletely(ALayer: TBGRABitmap);
     procedure SelectionMaskMayChange(ARect: TRect);
     procedure SelectionMaskMayChangeCompletely;
-    procedure RenderMayChange(ARect: TRect; APicCoords: boolean = false);
+    procedure RenderMayChange(ARect: TRect; APicCoords: boolean = false; ANotify: boolean = true);
     procedure ResetRenderUpdateRect;
 
     // selection mask
@@ -257,6 +258,7 @@ type
     property OnSelectedLayerIndexChanged: TOnCurrentLayerIndexChanged read FOnSelectedLayerIndexChanged write FOnSelectedLayerIndexChanged;
     property OnStackChanged: TOnStackChanged read FOnStackChanged write FOnStackChanged;
     property OnImageChanged: TLazPaintImageObservable read FOnImageChanged;
+    property OnImageRenderChanged: TNotifyEvent read FOnImageRenderChanged write FOnImageRenderChanged;
     property OnImageSaving: TLazPaintImageObservable read FOnImageSaving;
     property OnImageExport: TLazPaintImageObservable read FOnImageExport;
     property OnActionProgress: TLayeredActionProgressEvent read FOnActionProgress write SetOnActionProgress;
@@ -1076,7 +1078,8 @@ var
   r: TRect;
 begin
   r := FCurrentState.LayeredBitmap.RenderOriginalIfNecessary(AOriginal.Guid, FDraftOriginal);
-  ImageMayChange(r, false);
+  if r.IsEmpty then OnImageChanged.NotifyObservers
+  else ImageMayChange(r, false);
   if Assigned(ADiff) then
   begin
     AddUndo(TVectorOriginalEmbeddedDifference.Create(CurrentState,AOriginal.Guid,ADiff,r));
@@ -1280,12 +1283,14 @@ begin
     OnImageChanged.NotifyObservers;
 end;
 
-procedure TLazPaintImage.RenderMayChange(ARect: TRect; APicCoords: boolean = false);
+procedure TLazPaintImage.RenderMayChange(ARect: TRect; APicCoords: boolean; ANotify: boolean);
 begin
   if APicCoords then
      FRenderUpdateRectInPicCoord := RectUnion(FRenderUpdateRectInPicCoord,ARect)
   else
-      FRenderUpdateRectInVSCoord := RectUnion(FRenderUpdateRectInVSCoord,ARect);
+     FRenderUpdateRectInVSCoord := RectUnion(FRenderUpdateRectInVSCoord,ARect);
+  if ANotify and Assigned(OnImageRenderChanged) then
+    OnImageRenderChanged(self);
 end;
 
 procedure TLazPaintImage.LayerBlendMayChange(AIndex: integer);

+ 32 - 10
lazpaint/image/uimageaction.pas

@@ -106,7 +106,7 @@ implementation
 
 uses Controls, Dialogs, UResourceStrings, UObject3D,
      ULoadImage, UGraph, UClipboard, Types, BGRAGradientOriginal,
-     BGRATransform, ULoading, math, LCVectorClipboard, LCVectorOriginal,
+     BGRATransform, ULoading, math, LCVectorClipboard, LCVectorOriginal, LCVectorRectShapes,
      BGRALayers, BGRAUTF8, UFileSystem;
 
 { TImageActions }
@@ -702,21 +702,43 @@ procedure TImageActions.FillBackground(AColor: TBGRAPixel);
 var tempBmp: TBGRABitmap;
     LayerAction: TLayerAction;
     y: Integer;
+    orig: TVectorOriginal;
+    ab: TAffineBox;
+    backRect: TRectShape;
 begin
   if not Image.CheckNoAction then exit;
   LayerAction := nil;
   try
-    LayerAction := Image.CreateAction(True);
-    tempBmp := TBGRABitmap.Create(LayerAction.SelectedImageLayer.Width,1);
-    for y := 0 to LayerAction.SelectedImageLayer.Height-1 do
+    if Image.LayerOriginalClass[Image.CurrentLayerIndex] = TVectorOriginal then
     begin
-       tempBmp.Fill(AColor);
-       tempBmp.PutImage(0,-y,LayerAction.SelectedImageLayer,dmDrawWithTransparency);
-       LayerAction.SelectedImageLayer.PutImage(0,y,tempBmp,dmSet);
+      Image.CurrentState.DiscardOriginalDiff := false;
+      try
+        orig := Image.LayerOriginal[Image.CurrentLayerIndex] as TVectorOriginal;
+        backRect := TRectShape.Create(nil);
+        ab := AffineMatrixInverse(Image.LayerOriginalMatrix[Image.CurrentLayerIndex]) *
+              TAffineBox.AffineBox(rectF(-0.5, -0.5, Image.Width-0.5, Image.Height-0.5));
+        backRect.Origin := ab.Center;
+        backRect.XAxis := backRect.Origin + (ab.TopRight - ab.TopLeft)*0.5;
+        backRect.YAxis := backRect.Origin + (ab.BottomLeft - ab.TopLeft)*0.5;
+        backRect.BackFill.SolidColor := AColor;
+        orig.InsertShape(backRect, 0);
+      finally
+        Image.CurrentState.DiscardOriginalDiff := true;
+      end;
+    end else
+    begin
+      LayerAction := Image.CreateAction(True);
+      tempBmp := TBGRABitmap.Create(LayerAction.SelectedImageLayer.Width,1);
+      for y := 0 to LayerAction.SelectedImageLayer.Height-1 do
+      begin
+         tempBmp.Fill(AColor);
+         tempBmp.PutImage(0,-y,LayerAction.SelectedImageLayer,dmDrawWithTransparency);
+         LayerAction.SelectedImageLayer.PutImage(0,y,tempBmp,dmSet);
+      end;
+      tempBmp.Free;
+      image.LayerMayChangeCompletely(LayerAction.SelectedImageLayer);
+      LayerAction.Validate;
     end;
-    tempBmp.Free;
-    image.LayerMayChangeCompletely(LayerAction.SelectedImageLayer);
-    LayerAction.Validate;
   except
     on ex:Exception do
       FInstance.ShowError('FillBackground',ex.Message);

+ 71 - 5
lazpaint/image/uimagediff.pas

@@ -340,10 +340,14 @@ type
     FPreviousLayerContent: TStoredLayer;
     FPrevMatrix,FNextMatrix: TAffineMatrix;
     FSourceBounds: TRect;
+    FSourceLayerId: integer;
     FOriginalGuid: TGUID;
     function GetImageDifferenceKind: TImageDifferenceKind; override;
     function CreateOriginal(AState: TState; ALayerIndex: integer): TBGRALayerCustomOriginal; virtual; abstract;
     function ShouldRenderOriginal: boolean; virtual;
+    procedure StorePreviousLayer(AImgState: TImageState; ALayerIndex: integer;
+      AAlwaysStoreBitmap: boolean); virtual;
+    procedure CustomUnapplyto(AState: TState);
   public
     constructor Create(AFromState: TState; AIndex: integer; AAlwaysStoreBitmap: boolean);
     function UsedMemory: int64; override;
@@ -383,7 +387,14 @@ type
 
   TReplaceLayerByImageOriginalDifference = class(TReplaceLayerByOriginalDifference)
   protected
+    FSourceStoredInOriginal: boolean;
+    procedure StorePreviousLayer(AImgState: TImageState; ALayerIndex: integer;
+      AAlwaysStoreBitmap: boolean); override;
     function CreateOriginal(AState: TState; ALayerIndex: integer): TBGRALayerCustomOriginal; override;
+  public
+    function UsedMemory: int64; override;
+    function TryCompress: boolean; override;
+    procedure UnapplyTo(AState: TState); override;
   end;
 
   { TReplaceLayerByVectorOriginalDifference }
@@ -1130,6 +1141,15 @@ end;
 
 { TReplaceLayerByImageOriginalDifference }
 
+procedure TReplaceLayerByImageOriginalDifference.StorePreviousLayer(
+  AImgState: TImageState; ALayerIndex: integer; AAlwaysStoreBitmap: boolean);
+begin
+  if not AImgState.LayerOriginalDefined[ALayerIndex] then
+    FSourceStoredInOriginal:= true
+  else
+    inherited StorePreviousLayer(AImgState, ALayerIndex, AAlwaysStoreBitmap);
+end;
+
 function TReplaceLayerByImageOriginalDifference.CreateOriginal(AState: TState; ALayerIndex: integer): TBGRALayerCustomOriginal;
 var
   source: TBGRABitmap;
@@ -1150,6 +1170,40 @@ begin
   result := orig;
 end;
 
+function TReplaceLayerByImageOriginalDifference.UsedMemory: int64;
+begin
+  if FSourceStoredInOriginal then
+    result := 0
+  else
+    Result:=inherited UsedMemory;
+end;
+
+function TReplaceLayerByImageOriginalDifference.TryCompress: boolean;
+begin
+  if FSourceStoredInOriginal then
+    result := false
+  else Result:=inherited TryCompress;
+end;
+
+procedure TReplaceLayerByImageOriginalDifference.UnapplyTo(AState: TState);
+var
+  imgState: TImageState;
+  layerIdx: Integer;
+  bmp: TBGRABitmap;
+begin
+  if FSourceStoredInOriginal then
+  begin
+    CustomUnapplyto(AState);
+    imgState := AState as TImageState;
+    layerIdx := imgState.LayeredBitmap.GetLayerIndexFromId(LayerId);
+    bmp := (imgState.LayeredBitmap.LayerOriginal[layerIdx] as TBGRALayerImageOriginal).GetImageCopy;
+    imgState.LayeredBitmap.SetLayerBitmap(layerIdx, bmp, True);
+    imgState.LayeredBitmap.LayerOffset[layerIdx] := Point(round(FPrevMatrix[1,3]), round(FPrevMatrix[2,3]));
+    imgState.LayeredBitmap.RemoveUnusedOriginals;
+  end else
+    inherited UnapplyTo(AState);
+end;
+
 { TSelectionTransformDifference }
 
 function TSelectionTransformDifference.GetImageDifferenceKind: TImageDifferenceKind;
@@ -1255,7 +1309,7 @@ end;
 
 function TReplaceLayerByOriginalDifference.GetLayerId: integer;
 begin
-  result := FPreviousLayerContent.LayerId;
+  result := FSourceLayerId;
 end;
 
 function TReplaceLayerByOriginalDifference.GetImageDifferenceKind: TImageDifferenceKind;
@@ -1268,6 +1322,17 @@ begin
   result := false;
 end;
 
+procedure TReplaceLayerByOriginalDifference.StorePreviousLayer(
+  AImgState: TImageState; ALayerIndex: integer; AAlwaysStoreBitmap: boolean);
+begin
+  FPreviousLayerContent := TStoredLayer.Create(AImgState.LayeredBitmap, ALayerIndex, AAlwaysStoreBitmap);
+end;
+
+procedure TReplaceLayerByOriginalDifference.CustomUnapplyto(AState: TState);
+begin
+  inherited UnapplyTo(AState);
+end;
+
 constructor TReplaceLayerByOriginalDifference.Create(
   AFromState: TState; AIndex: integer; AAlwaysStoreBitmap: boolean);
 var
@@ -1275,9 +1340,10 @@ var
 begin
   inherited Create(AFromState);
   imgState := AFromState as TImageState;
-  FPreviousLayerContent := TStoredLayer.Create(imgState.LayeredBitmap, AIndex, AAlwaysStoreBitmap);
   FSourceBounds := imgState.LayeredBitmap.LayerBitmap[AIndex].GetImageBounds;
-  with FPreviousLayerContent.Offset do FPrevMatrix := AffineMatrixTranslation(x+FSourceBounds.Left,y+FSourceBounds.Top);
+  FSourceLayerId := imgState.LayeredBitmap.LayerUniqueId[AIndex];
+  with imgState.LayeredBitmap.LayerOffset[AIndex] do FPrevMatrix := AffineMatrixTranslation(x+FSourceBounds.Left,y+FSourceBounds.Top);
+  StorePreviousLayer(imgState, AIndex, AAlwaysStoreBitmap);
   FNextMatrix := FPrevMatrix;
   ApplyTo(imgState);
 end;
@@ -1300,7 +1366,7 @@ var
 begin
   inherited ApplyTo(AState);
   imgState := AState as TImageState;
-  layerIdx := imgState.LayeredBitmap.GetLayerIndexFromId(FPreviousLayerContent.LayerId);
+  layerIdx := imgState.LayeredBitmap.GetLayerIndexFromId(FSourceLayerId);
   orig := CreateOriginal(imgState, layerIdx);
   if FOriginalGuid <> GUID_NULL then orig.Guid := FOriginalGuid;
   origIndex := imgState.LayeredBitmap.AddOriginal(orig, true);
@@ -1321,7 +1387,7 @@ procedure TReplaceLayerByOriginalDifference.UnapplyTo(AState: TState);
 var
   imgState: TImageState;
 begin
-  inherited UnapplyTo(AState);
+  CustomUnapplyto(AState);
   imgState := AState as TImageState;
   FPreviousLayerContent.Replace(imgState.LayeredBitmap);
 end;

+ 2 - 8
lazpaint/lazpaint.lpi

@@ -23,7 +23,7 @@
     <VersionInfo>
       <UseVersionInfo Value="True"/>
       <MajorVersionNr Value="7"/>
-      <RevisionNr Value="8"/>
+      <RevisionNr Value="9"/>
       <CharSet Value="04B0"/>
       <StringTable CompanyName="http://sourceforge.net/projects/lazpaint/" ProductName="LazPaint" InternalName="lazpaint" OriginalFilename="lazpaint.exe"/>
     </VersionInfo>
@@ -333,7 +333,7 @@
     <RequiredPackages Count="5">
       <Item1>
         <PackageName Value="BGRABitmapPack"/>
-        <MinVersion Major="10" Minor="6" Release="5" Valid="True"/>
+        <MinVersion Major="10" Minor="8" Valid="True"/>
       </Item1>
       <Item2>
         <PackageName Value="bgracontrols"/>
@@ -978,12 +978,6 @@
       <UnitOutputDirectory Value="debug\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <CodeGeneration>
-      <Checks>
-        <RangeChecks Value="True"/>
-        <OverflowChecks Value="True"/>
-        <StackChecks Value="True"/>
-      </Checks>
-      <VerifyObjMethodCallValidity Value="True"/>
       <Optimizations>
         <OptimizationLevel Value="0"/>
       </Optimizations>

+ 11 - 0
lazpaint/lazpaintdialogs.inc

@@ -206,6 +206,17 @@ begin
       result := srCancelledByUser;
 end;
 
+function TLazPaintInstance.ScriptLazPaintGetVersion(AVars: TVariableSet): TScriptResult;
+var
+  resList: TScriptVariableReference;
+begin
+  resList := AVars.AddIntegerList('Result');
+  AVars.AppendInteger(resList, LazPaintVersion div 1000000);
+  AVars.AppendInteger(resList, (LazPaintVersion div 10000) mod 100);
+  AVars.AppendInteger(resList, (LazPaintVersion div 100) mod 100);
+  result := srOk;
+end;
+
 function TLazPaintInstance.ScriptShowDirectoryDialog(AVars: TVariableSet): TScriptResult;
 var
   chosenDir: string;

+ 1 - 1
lazpaint/lazpaintembeddedpack.lpk

@@ -26,7 +26,7 @@
         </Debugging>
       </Linking>
     </CompilerOptions>
-    <Version Major="7" Release="8"/>
+    <Version Major="7" Release="9"/>
     <Files Count="94">
       <Item1>
         <Filename Value="lazpaintinstance.pas"/>

+ 68 - 26
lazpaint/lazpaintinstance.pas

@@ -14,7 +14,7 @@ uses
   UColorintensity, UShiftColors, UColorize, uadjustcurves,
   UCustomblur, uimagelist,
 
-  ULoading, UImage, UTool, uconfig, IniFiles, UResourceStrings, UScripting,
+  ULoading, UImage, UImageAction, UTool, uconfig, IniFiles, UResourceStrings, UScripting,
   UScriptType;
 
 const
@@ -56,6 +56,7 @@ type
     function ScriptImageCanvasSize(AVars: TVariableSet): TScriptResult;
     function ScriptImageRepeat(AVars: TVariableSet): TScriptResult;
     function ScriptImageResample(AParams: TVariableSet): TScriptResult;
+    function ScriptLazPaintGetVersion(AVars: TVariableSet): TScriptResult;
     function ScriptShowDirectoryDialog(AVars: TVariableSet): TScriptResult;
     procedure SelectionInstanceOnRun(AInstance: TLazPaintCustomInstance);
     procedure ToolFillChanged(Sender: TObject);
@@ -85,6 +86,7 @@ type
     FGridVisible: boolean;
     FConfig: TLazPaintConfig;
     FImage: TLazPaintImage;
+    FImageAction: TImageActions;
     FToolManager : TToolManager;
     FEmbedded: boolean;
     FDestroying: boolean;
@@ -138,6 +140,7 @@ type
     procedure SetChooseColorTarget(const AValue: TColorTarget); override;
     function GetConfig: TLazPaintConfig; override;
     function GetImage: TLazPaintImage; override;
+    function GetImageAction: TImageActions; override;
     function GetToolManager: TToolManager; override;
     procedure CreateLayerStack;
     procedure CreateToolBox;
@@ -259,7 +262,7 @@ uses LCLType, Types, Forms, Dialogs, FileUtil, StdCtrls, LCLIntf, BGRAUTF8,
 
      URadialBlur, UMotionBlur, UEmboss, UTwirl, UWaveDisplacement,
      unewimage, uresample, UPixelate, unoisefilter, ufilters,
-     UImageAction, USharpen, uposterize, UPhongFilter, UFilterFunction,
+     USharpen, uposterize, UPhongFilter, UFilterFunction,
      uprint, USaveOption, UFormRain,
 
      ugraph, LCScaleDPI, ucommandline, uabout, UPython, UVolatileScrollBar;
@@ -367,6 +370,7 @@ begin
   ScriptContext.RegisterScriptFunction('ShowMessage',@ScriptShowMessage,ARegister);
   ScriptContext.RegisterScriptFunction('ShowDirectoryDialog',@ScriptShowDirectoryDialog,ARegister);
   ScriptContext.RegisterScriptFunction('InputBox',@ScriptInputBox,ARegister);
+  ScriptContext.RegisterScriptFunction('LazPaintGetVersion',@ScriptLazPaintGetVersion,ARegister);
 end;
 
 function TLazPaintInstance.ScriptFileGetTemporaryName(AVars: TVariableSet): TScriptResult;
@@ -424,6 +428,7 @@ begin
   FToolManager.OnFillChanged:= @ToolFillChanged;
   FSelectionEditConfig := nil;
   FTextureEditConfig := nil;
+  FImageAction := TImageActions.Create(self);
 end;
 
 procedure TLazPaintInstance.FormsNeeded;
@@ -466,6 +471,11 @@ begin
   Result:= FImage;
 end;
 
+function TLazPaintInstance.GetImageAction: TImageActions;
+begin
+  result := FImageAction;
+end;
+
 function TLazPaintInstance.GetToolManager: TToolManager;
 begin
   Result:= FToolManager;
@@ -657,8 +667,13 @@ var
   delay: Integer;
 begin
   if AProgressPercent < 100 then delay := 10000 else delay := 1000;
-  MessagePopup(rsActionInProgress+'... '+inttostr(AProgressPercent)+'%', delay);
-  UpdateWindows;
+  if Assigned(FMain) then FMain.UpdatingPopup:= true;
+  try
+    MessagePopup(rsActionInProgress+'... '+inttostr(AProgressPercent)+'%', delay);
+    UpdateWindows;
+  finally
+    if Assigned(FMain) then FMain.UpdatingPopup:= false;
+  end;
 end;
 
 function TLazPaintInstance.GetInitialized: boolean;
@@ -796,8 +811,13 @@ procedure TLazPaintInstance.OnLayeredBitmapLoadStartHandler(AFilenameUTF8: strin
 begin
   if FLoadingLayers = nil then FLoadingLayers := TFLoading.Create(nil);
   if (AFilenameUTF8 = '<Stream>') and (FLoadingFilename <> '') then AFilenameUTF8 := FLoadingFilename;
-  FLoadingLayers.ShowMessage(rsOpening+' ' +AFilenameUTF8+'...');
-  UpdateWindows;
+  if Assigned(FMain) then FMain.UpdatingPopup:= true;
+  try
+    FLoadingLayers.ShowMessage(rsOpening+' ' +AFilenameUTF8+'...');
+    UpdateWindows;
+  finally
+    if Assigned(FMain) then FMain.UpdatingPopup:= false;
+  end;
 end;
 
 procedure TLazPaintInstance.OnLayeredBitmapLoadProgressHandler(
@@ -805,8 +825,13 @@ procedure TLazPaintInstance.OnLayeredBitmapLoadProgressHandler(
 begin
   if FLoadingLayers <> nil then
   begin
-    FLoadingLayers.ShowMessage(rsLoading+' (' +inttostr(APercentage)+'%)');
-    UpdateWindows;
+    if Assigned(FMain) then FMain.UpdatingPopup:= true;
+    try
+      FLoadingLayers.ShowMessage(rsLoading+' (' +inttostr(APercentage)+'%)');
+      UpdateWindows;
+    finally
+      if Assigned(FMain) then FMain.UpdatingPopup:= false;
+    end;
   end;
 end;
 
@@ -814,8 +839,13 @@ procedure TLazPaintInstance.OnLayeredBitmapLoadedHandler;
 begin
   if FLoadingLayers <> nil then
   begin
-    FreeAndNil(FLoadingLayers);
-    UpdateWindows;
+    if Assigned(FMain) then FMain.UpdatingPopup:= true;
+    try
+      FreeAndNil(FLoadingLayers);
+      UpdateWindows;
+    finally
+      if Assigned(FMain) then FMain.UpdatingPopup:= false;
+    end;
   end;
 end;
 
@@ -823,8 +853,13 @@ procedure TLazPaintInstance.OnLayeredBitmapSavedHandler();
 begin
   if FLoadingLayers <> nil then
   begin
-    FreeAndNil(FLoadingLayers);
-    UpdateWindows;
+    if Assigned(FMain) then FMain.UpdatingPopup:= true;
+    try
+      FreeAndNil(FLoadingLayers);
+      UpdateWindows;
+    finally
+      if Assigned(FMain) then FMain.UpdatingPopup:= false;
+    end;
   end;
 end;
 
@@ -833,8 +868,13 @@ procedure TLazPaintInstance.OnLayeredBitmapSaveProgressHandler(
 begin
   if FLoadingLayers <> nil then
   begin
-    FLoadingLayers.ShowMessage(rsSave+' (' +inttostr(APercentage)+'%)');
-    UpdateWindows;
+    if Assigned(FMain) then FMain.UpdatingPopup:= true;
+    try
+      FLoadingLayers.ShowMessage(rsSave+' (' +inttostr(APercentage)+'%)');
+      UpdateWindows;
+    finally
+      if Assigned(FMain) then FMain.UpdatingPopup:= false;
+    end;
   end;
 end;
 
@@ -843,8 +883,13 @@ procedure TLazPaintInstance.OnLayeredBitmapSaveStartHandler(
 begin
   if FLoadingLayers = nil then FLoadingLayers := TFLoading.Create(nil);
   if (AFilenameUTF8 = '<Stream>') and (FSavingFilename <> '') then AFilenameUTF8 := FSavingFilename;
-  FLoadingLayers.ShowMessage(rsSave+' ' +AFilenameUTF8+'...');
-  UpdateWindows;
+  if Assigned(FMain) then FMain.UpdatingPopup:= true;
+  try
+    FLoadingLayers.ShowMessage(rsSave+' ' +AFilenameUTF8+'...');
+    UpdateWindows;
+  finally
+    if Assigned(FMain) then FMain.UpdatingPopup:= false;
+  end;
 end;
 
 procedure TLazPaintInstance.PythonBusy(Sender: TObject);
@@ -993,7 +1038,6 @@ procedure TLazPaintInstance.SetGridVisible(const AValue: boolean);
 begin
   FGridVisible := AValue;
   Image.RenderMayChange(rect(0,0,Image.Width,Image.Height),True);
-  NotifyImageChange(False,EmptyRect);
 end;
 
 function TLazPaintInstance.GetChooseColorVisible: boolean;
@@ -1074,11 +1118,9 @@ begin
 end;
 
 procedure TLazPaintInstance.AssignBitmap(bmp: TBGRABitmap);
-var imageActions: TimageActions;
 begin
-  imageActions := TImageActions.Create(self);
-  imageActions.SetCurrentBitmap(bmp.Duplicate as TBGRABitmap, False);
-  imageActions.Free;
+  if Assigned(FImageAction) then
+    FImageAction.SetCurrentBitmap(bmp.Duplicate as TBGRABitmap, False);
 end;
 
 procedure TLazPaintInstance.EditBitmap(var bmp: TBGRABitmap; ConfigStream: TStream; ATitle: String; AOnRun: TLazPaintInstanceEvent; AOnExit: TLazPaintInstanceEvent; ABlackAndWhite: boolean);
@@ -1138,16 +1180,13 @@ begin
 end;
 
 procedure TLazPaintInstance.EditSelection;
-var imageActions: TimageActions;
 begin
-  imageActions := TImageActions.Create(self);
   try
-    imageActions.EditSelection(@EditSelectionHandler);
+    TImageActions(ImageAction).EditSelection(@EditSelectionHandler);
   except
     on ex: Exception do
       ShowError('EditSelection',ex.Message);
   end;
-  imageActions.Free;
 end;
 
 function TLazPaintInstance.EditTexture(ASource: TBGRABitmap): TBGRABitmap;
@@ -1241,7 +1280,6 @@ end;
 procedure TLazPaintInstance.SetFullscreen(AValue: boolean);
 begin
   if (AValue = FFullscreen) or not MainFormVisible or (FMain.WindowState = wsMinimized) then exit;
-  if AValue then DockLayersAndColors:= false;
   FFullscreen := AValue;
   if AValue then
   begin
@@ -1359,6 +1397,7 @@ end;
 
 destructor TLazPaintInstance.Destroy;
 begin
+  FreeAndNil(FImageAction);
   RegisterScripts(False);
 
   FDestroying := true;
@@ -1462,17 +1501,20 @@ begin
   if Assigned(FLayerStack) and (AInfo.layerstackHidden > 0) then
   begin
     FLayerStack.Show;
+    FLayerStack.BringToFront;
     FLayerStack.InvalidateStack(False);
     dec(FTopMostInfo.layerstackHidden);
   end;
   if Assigned(FChooseColor) and (AInfo.choosecolorHidden > 0) then
   begin
     FChooseColor.Show;
+    FChooseColor.BringToFront;
     dec(FTopMostInfo.choosecolorHidden);
   end;
   if Assigned(FFormToolbox) and (AInfo.toolboxHidden > 0) then
   begin
     FFormToolbox.Show;
+    FFormToolbox.BringToFront;
     dec(FTopMostInfo.toolboxHidden);
   end;
 end;

+ 64 - 9
lazpaint/lazpaintmainform.lfm

@@ -214,7 +214,7 @@ object FMain: TFMain
       Height = 20
       Hint = 'Color difference'
       Top = 1
-      Width = 34
+      Width = 36
       Alignment = taCenter
       AutoSize = False
       Caption = '100%'
@@ -228,9 +228,9 @@ object FMain: TFMain
     Left = 608
     Height = 36
     Top = 144
-    Width = 98
+    Width = 105
     ClientHeight = 36
-    ClientWidth = 98
+    ClientWidth = 105
     Font.Height = -12
     ParentColor = False
     ParentFont = False
@@ -252,7 +252,7 @@ object FMain: TFMain
       Height = 20
       Hint = 'Pen width'
       Top = 2
-      Width = 46
+      Width = 53
       AllowNegativeValues = False
       BarExponent = 1
       Increment = 1
@@ -718,9 +718,9 @@ object FMain: TFMain
     Left = 608
     Height = 36
     Top = 8
-    Width = 72
+    Width = 75
     ClientHeight = 36
-    ClientWidth = 72
+    ClientWidth = 75
     Font.Height = -12
     ParentColor = False
     ParentFont = False
@@ -731,7 +731,7 @@ object FMain: TFMain
       Height = 20
       Hint = 'Coordinates'
       Top = 2
-      Width = 62
+      Width = 66
       Alignment = taCenter
       AutoSize = False
       Caption = '9999x9999'
@@ -2304,7 +2304,7 @@ object FMain: TFMain
     end
   end
   object Panel_PerspectiveOption: TPanel
-    Left = 479
+    Left = 432
     Height = 36
     Top = 152
     Width = 65
@@ -2603,7 +2603,7 @@ object FMain: TFMain
     end
   end
   object Panel_Ratio: TPanel
-    Left = 288
+    Left = 272
     Height = 36
     Top = 152
     Width = 147
@@ -3118,6 +3118,61 @@ object FMain: TFMain
       AllowedFillTypes = [vftSolid, vftGradient, vftTexture]
     end
   end
+  object Panel_Donate: TPanel
+    Left = 512
+    Height = 36
+    Top = 152
+    Width = 44
+    ClientHeight = 36
+    ClientWidth = 44
+    Font.Height = -12
+    ParentColor = False
+    ParentFont = False
+    TabOrder = 32
+    object ToolBar25: TToolBar
+      Left = 1
+      Height = 22
+      Top = 1
+      Width = 24
+      Align = alNone
+      AutoSize = True
+      EdgeBorders = []
+      Font.Height = -12
+      Images = ImageList16
+      ParentFont = False
+      TabOrder = 0
+      object ToolButton_Donate: TToolButton
+        Left = 1
+        Hint = 'Donate...'
+        Top = 0
+        ImageIndex = 110
+        OnClick = ToolButton_DonateClick
+      end
+    end
+  end
+  object Panel_OutlineFill: TPanel
+    Left = 480
+    Height = 36
+    Top = 240
+    Width = 88
+    ClientHeight = 36
+    ClientWidth = 88
+    Font.Height = -12
+    ParentColor = False
+    ParentFont = False
+    PopupMenu = PopupToolbar
+    TabOrder = 33
+    object VectorialFill_Outline: TLCVectorialFillControl
+      Left = 2
+      Height = 40
+      Top = 1
+      Width = 80
+      AutoSize = True
+      ToolIconSize = 16
+      VerticalPadding = 4
+      AllowedFillTypes = [vftSolid, vftGradient, vftTexture]
+    end
+  end
   object ImageList16: TBGRAImageList
     left = 40
     top = 496

+ 76 - 35
lazpaint/lazpaintmainform.pas

@@ -13,7 +13,7 @@ uses
   Graphics, Dialogs, Menus, ExtDlgs, ComCtrls, ActnList, StdCtrls, ExtCtrls,
   Buttons, types, LCLType, BGRAImageList, BCTrackbarUpdown, BCComboBox, BCButton,
 
-  BGRABitmap, BGRABitmapTypes, BGRALayers, BGRASVGOriginal, BGRAGradientScanner,
+  BGRABitmap, BGRABitmapTypes, BGRALayers, BGRASVGOriginal, BGRAGradientScanner, BGRAGradientOriginal,
 
   LazPaintType, UMainFormLayout, UTool, UImage, UImageAction, UZoom, UImageView,
   UImageObservation, UConfig, LCScaleDPI, UResourceStrings, UMenu, uscripting,
@@ -29,6 +29,10 @@ type
     FileExport: TAction;
     ExportPictureDialog: TSaveDialog;
     MenuScript: TMenuItem;
+    Panel_OutlineFill: TPanel;
+    Panel_Donate: TPanel;
+    ToolButton_Donate: TToolButton;
+    ToolBar25: TToolBar;
     ToolOpenedCurve: TAction;
     ToolPolyline: TAction;
     FileRunScript: TAction;
@@ -55,6 +59,7 @@ type
     Tool_EraseSharpen: TToolButton;
     Tool_EraseLighten: TToolButton;
     Tool_EraseDarken: TToolButton;
+    VectorialFill_Outline: TLCVectorialFillControl;
     VectorialFill_Pen: TLCVectorialFillControl;
     VectorialFill_Back: TLCVectorialFillControl;
     Panel_BackFill: TPanel;
@@ -536,6 +541,7 @@ type
     procedure GridNb_SpinEditChange(Sender: TObject; AByUser: boolean);
     procedure TimerArrangeTimer(Sender: TObject);
     procedure TimerHideFillTimer(Sender: TObject);
+    procedure ToolButton_DonateClick(Sender: TObject);
     procedure VectorialFill_TextureClick(Sender: TObject);
     procedure PaintBox_PenPreviewPaint(Sender: TObject);
     procedure PaintBox_PictureMouseDown(Sender: TObject; Button: TMouseButton;
@@ -637,9 +643,7 @@ type
       {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
     procedure SpinEdit_PenWidthChange(Sender: TObject; AByUser: boolean);
     procedure Tool_CloseShapeClick(Sender: TObject);
-    procedure VectorialFill_BackChooseColor({%H-}ASender: TObject; AButton: TMouseButton;
-              AColorIndex: integer; var {%H-}AColorValue: TBGRAPixel; out AHandled: boolean);
-    procedure VectorialFill_PenChooseColor({%H-}ASender: TObject; AButton: TMouseButton;
+    procedure VectorialFill_ChooseColor({%H-}ASender: TObject; AButton: TMouseButton;
               AColorIndex: integer; var {%H-}AColorValue: TBGRAPixel; out AHandled: boolean);
     procedure SpinEdit_ArrowSizeChange(Sender: TObject; AByUser: boolean);
     procedure SpinEdit_ToleranceChange(Sender: TObject; AByUser: boolean);
@@ -684,20 +688,14 @@ type
     procedure ManagerToolbarChanged(Sender: TObject);
     procedure Perspective_RepeatClick(Sender: TObject);
     function ScriptShowColorDialog(AVars: TVariableSet): TScriptResult;
-    procedure VectorialFill_BackAdjustToShape(Sender: TObject);
-    procedure VectorialFill_BackChange(Sender: TObject);
-    procedure VectorialFill_BackEditGradTexPoints(Sender: TObject);
-    procedure VectorialFill_BackResize(Sender: TObject);
-    procedure VectorialFill_BackTypeChange(Sender: TObject);
-    procedure VectorialFill_PenAdjustToShape(Sender: TObject);
-    procedure VectorialFill_PenChange(Sender: TObject);
-    procedure VectorialFill_PenEditGradTexPoints(Sender: TObject);
-    procedure VectorialFill_PenResize(Sender: TObject);
-    procedure VectorialFill_PenTypeChange(Sender: TObject);
-    procedure VectorialFill_ShowBackFill(Sender: TObject; {%H-}Shift: TShiftState;
-      {%H-}X, {%H-}Y: Integer);
-    procedure VectorialFill_ShowPenFill(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X,
-      {%H-}Y: Integer);
+    procedure VectorialFill_Change(Sender: TObject);
+    procedure VectorialFill_TypeChange(Sender: TObject);
+    procedure VectorialFill_Resize(Sender: TObject);
+    procedure VectorialFill_EditGradTexPoints(Sender: TObject);
+    procedure VectorialFill_AdjustToShape(Sender: TObject);
+    procedure VectorialFill_ShowBackFill(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
+    procedure VectorialFill_ShowPenFill(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
+    procedure VectorialFill_ShowOutlineFill(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
   private
     { private declarations }
     FLayout: TMainFormLayout;
@@ -734,7 +732,6 @@ type
     FShowSelectionNormal: boolean;
     FLazPaintInstance: TLazPaintCustomInstance;
     Config: TLazPaintConfig;
-    FImageActions: TImageActions;
     StartDirectory: string;
     previousToolImg: integer;
     currentToolLabel: string;
@@ -751,8 +748,11 @@ type
 
     function GetCurrentPressure: single;
     function GetDarkTheme: boolean;
+    function GetImageAction: TImageActions;
+    function GetUpdatingPopup: boolean;
     function GetUseImageBrowser: boolean;
     procedure SetDarkTheme(AValue: boolean);
+    procedure SetUpdatingPopup(AValue: boolean);
     procedure UpdateStatusText;
     procedure CreateToolbarElements;
     function GetCurrentToolAction: TAction;
@@ -807,11 +807,11 @@ type
     function ShowColorDialogFor(ATarget: TColorTarget): boolean;
     procedure ShowPenPreview(ShouldRepaint: boolean= False);
     procedure HidePenPreview(ATimeMs: Integer = 300; AClearTime: boolean = false);
-    procedure ShowPenFill;
-    procedure ShowBackFill;
+    procedure ShowFill(AFillControl: TLCVectorialFillControl; APanel: TPanel);
     procedure HideFill(ATimeMs: Integer = 300; AClearTime: boolean = false);
     procedure OnPaintHandler;
     procedure OnImageChangedHandler({%H-}AEvent: TLazPaintImageObservationEvent);
+    procedure OnImageRenderChanged(sender: TObject);
     procedure LabelAutosize(ALabel: TLabel);
     procedure AskMergeSelection(ACaption: string);
     procedure ReleaseMouseButtons(Shift: TShiftState);
@@ -889,6 +889,8 @@ type
     property CurrentPressure: single read GetCurrentPressure;
     property DarkTheme: boolean read GetDarkTheme write SetDarkTheme;
     property Initialized: boolean read FInitialized;
+    property UpdatingPopup: boolean read GetUpdatingPopup write SetUpdatingPopup;
+    property ImageAction: TImageActions read GetImageAction;
   end;
 
 implementation
@@ -993,7 +995,6 @@ begin
   end;
   FLayout.ToolBoxPopup := nil;
   RegisterScripts(False);
-  FreeAndNil(FImageActions);
 
   If Assigned(ToolManager) then
   begin
@@ -1029,8 +1030,13 @@ begin
   FreeAndNil(FBrowseImages);
   FreeAndNil(FBrowseTextures);
   FreeAndNil(FBrowseBrushes);
-  if Assigned(FSaveImage) and Config.DefaultRememberSaveFormat then
-    Config.SetSaveExtensions(FSaveImage.DefaultExtensions);
+  if Config.DefaultRememberSaveFormat then
+  begin
+    if Assigned(FSaveImage) and Config.DefaultRememberSaveFormat and Config.DefaultUseImageBrowser then
+      Config.SetSaveExtensions(FSaveImage.DefaultExtensions)
+    else
+      Config.SetSaveExtensions(GetExtensionFilterByIndex([eoWritable], SavePictureDialog1.FilterIndex));
+  end;
   FreeAndNil(FSaveImage);
   FreeAndNil(FSaveSelection);
 
@@ -1066,10 +1072,19 @@ begin
     FLoadInitialDir := Config.DefaultStartupSourceDirectory;
   FileRememberSaveFormat.Checked:= Config.DefaultRememberSaveFormat;
 
+  if Config.DefaultRememberSaveFormat then
+  begin
+    SavePictureDialog1.FilterIndex := GetExtensionFilterIndex([eoWritable], Config.DefaultSaveExtensions);
+    ExportPictureDialog.FilterIndex:= SavePictureDialog1.FilterIndex;
+  end else
+  begin
+    SavePictureDialog1.FilterIndex := 1;
+    ExportPictureDialog.FilterIndex:= 1;
+  end;
+
   FImageView := TImageView.Create(LazPaintInstance, Zoom,
                 {$IFDEF USEPAINTBOXPICTURE}PaintBox_Picture.Canvas{$ELSE}self.Canvas{$ENDIF});
 
-  FImageActions := TImageActions.Create(LazPaintInstance);
   LazPaintInstance.EmbeddedResult := mrNone;
 
   Image.OnSelectedLayerIndexChanged:= @PictureSelectedLayerIndexChanged;
@@ -1104,7 +1119,7 @@ begin
   InitToolbarElements;
 
   Image.CurrentFilenameUTF8 := '';
-  FImageActions.SetCurrentBitmap(TBGRABitmap.Create(Config.DefaultImageWidth,Config.DefaultImageHeight,Config.DefaultImageBackgroundColor), false);
+  ImageAction.SetCurrentBitmap(TBGRABitmap.Create(Config.DefaultImageWidth,Config.DefaultImageHeight,Config.DefaultImageBackgroundColor), false);
   image.ClearUndo;
   image.SetSavedFlag(0, -1, 0);
 
@@ -1138,6 +1153,7 @@ begin
   RegisterScripts(True);
 
   Image.OnImageChanged.AddObserver(@OnImageChangedHandler);
+  Image.OnImageRenderChanged := @OnImageRenderChanged;
   Image.OnQueryExitToolHandler := @OnQueryExitToolHandler;
   Image.Zoom := Zoom;
   UpdateWindowCaption;
@@ -1160,7 +1176,7 @@ begin
       Panel_ShapeOption,Panel_PenWidth,Panel_PenStyle,Panel_JoinStyle,
       Panel_CloseShape,Panel_LineCap,Panel_Aliasing,
       Panel_SplineStyle,Panel_Eraser,Panel_Tolerance,Panel_Text,Panel_Altitude,Panel_TextShadow,Panel_TextOutline,
-      Panel_PhongShape,Panel_PerspectiveOption,Panel_Brush,Panel_Ratio],Panel_ToolbarBackground);
+      Panel_OutlineFill,Panel_PhongShape,Panel_PerspectiveOption,Panel_Brush,Panel_Ratio,Panel_Donate],Panel_ToolbarBackground);
     m.ImageList := LazPaintInstance.Icons[ScaleY(16, 96)];
     m.Apply;
     FLayout.Menu := m;
@@ -2016,7 +2032,7 @@ begin
   end;
   if Open3DObjectDialog.Execute then
   begin
-    FImageActions.Import3DObject(Open3DObjectDialog.FileName);
+    ImageAction.Import3DObject(Open3DObjectDialog.FileName);
     Config.SetDefault3dObjectDirectory(ExtractFilePath(Open3DObjectDialog.FileName));
   end;
   LazPaintInstance.ShowTopmost(topmostInfo);
@@ -2079,7 +2095,7 @@ begin
       end;
     end;
   end;
-  if FImageActions.LoadSelection(selectionFileName, @loadedImage) then
+  if ImageAction.LoadSelection(selectionFileName, @loadedImage) then
   begin
     FSaveSelectionInitialFilename := selectionFileName;
     if Assigned(Scripting.RecordingFunctionParameters) then
@@ -2314,11 +2330,11 @@ begin
                   for i := 0 to high(loadedLayers) do
                   if Assigned(loadedLayers[i].bmp) then
                   begin
-                    FImageActions.AddLayerFromBitmap(loadedLayers[i].bmp,ExtractFileName(loadedLayers[i].filename));
+                    ImageAction.AddLayerFromBitmap(loadedLayers[i].bmp,ExtractFileName(loadedLayers[i].filename));
                     loadedLayers[i].bmp := nil;
                   end else
                   begin
-                    FImageActions.AddLayerFromOriginal(loadedLayers[i].orig,ExtractFileName(loadedLayers[i].filename));
+                    ImageAction.AddLayerFromOriginal(loadedLayers[i].orig,ExtractFileName(loadedLayers[i].filename));
                     loadedLayers[i].orig := nil;
                   end;
                 end;
@@ -2547,13 +2563,13 @@ begin
   begin
     if Assigned(loadedImage) and (length(chosenFiles)=1) then
     begin
-      layerLoaded := length(FImageActions.TryAddLayerFromFile(chosenFiles[0], loadedImage)) > 0;
+      layerLoaded := length(ImageAction.TryAddLayerFromFile(chosenFiles[0], loadedImage)) > 0;
     end else
     begin
       FreeAndNil(loadedImage);
       for i := 0 to high(chosenFiles) do
         begin
-          if length(FImageActions.TryAddLayerFromFile(chosenFiles[i])) > 0 then
+          if length(ImageAction.TryAddLayerFromFile(chosenFiles[i])) > 0 then
             layerLoaded := true;
         end;
     end;
@@ -3091,7 +3107,7 @@ begin
                     end
                     else
                     begin
-                      FImageActions.RemoveSelection;
+                      ImageAction.RemoveSelection;
                       texMapBounds := newTexture.GetImageBounds;
                       BGRAReplace(newTexture, newTexture.GetPart(texMapBounds));
                       ToolManager.BackFill.SetTexture(newTexture, AffineMatrixIdentity,
@@ -3192,7 +3208,7 @@ begin
                   mrYes:
                     begin
                       ToolManager.ToolCloseDontReopen;
-                      FImageActions.RetrieveSelection;
+                      ImageAction.RetrieveSelection;
                       ToolManager.ToolOpen;
                     end;
                 end;
@@ -3927,6 +3943,11 @@ begin
     btnRightDown := false;
     if ToolManager.ToolUp then PaintPictureNow;
   end;
+  if not (ssMiddle in Shift) and btnMiddleDown then
+  begin
+    btnMiddleDown := false;
+    if ToolManager.ToolUp then PaintPictureNow;
+  end;
   if not btnLeftDown and not btnRightDown then
   begin
     CanCompressOrUpdateStack := true;
@@ -4379,6 +4400,11 @@ begin
   if AEvent.DelayedStackUpdate then FUpdateStackWhenIdle := true;
 end;
 
+procedure TFMain.OnImageRenderChanged(sender: TObject);
+begin
+  InvalidatePicture;
+end;
+
 procedure TFMain.UpdateEditPicture(ADelayed: boolean = false);
 begin
   if ToolManager.ToolUpdate then
@@ -4590,6 +4616,11 @@ begin
   end;
 end;
 
+procedure TFMain.SetUpdatingPopup(AValue: boolean);
+begin
+  FImageView.UpdatingPopup := AValue;
+end;
+
 function TFMain.GetCurrentPressure: single;
 begin
   if Assigned(FTablet) and FTablet.Present and FTablet.Entering and (FTablet.Max > 0) then
@@ -4604,6 +4635,16 @@ begin
   else result := false;
 end;
 
+function TFMain.GetImageAction: TImageActions;
+begin
+  result := TImageActions(LazPaintInstance.ImageAction);
+end;
+
+function TFMain.GetUpdatingPopup: boolean;
+begin
+  result := FImageView.UpdatingPopup;
+end;
+
 function TFMain.GetScriptContext: TScriptContext;
 begin
   result := LazPaintInstance.ScriptContext;

+ 48 - 17
lazpaint/lazpainttype.pas

@@ -10,7 +10,7 @@ uses
   {$IFDEF LINUX}, InterfaceBase{$ENDIF};
 
 const
-  LazPaintVersion = 7000800;
+  LazPaintVersion = 7000900;
 
   function LazPaintVersionStr: string;
 
@@ -109,7 +109,8 @@ function IsOnlyRenderChange(const ARect:TRect): boolean;
 type
     ArrayOfBGRABitmap = array of TBGRABitmap;
     TColorTarget = (ctForeColorSolid, ctForeColorStartGrad, ctForeColorEndGrad,
-                    ctBackColorSolid, ctBackColorStartGrad, ctBackColorEndGrad);
+                    ctBackColorSolid, ctBackColorStartGrad, ctBackColorEndGrad,
+                    ctOutlineColorSolid, ctOutlineColorStartGrad, ctOutlineColorEndGrad);
     TFlipOption = (foAuto, foWholePicture, foSelection, foCurrentLayer);
 
     PImageEntry = ^TImageEntry;
@@ -174,6 +175,7 @@ type
 
     function GetConfig: TLazPaintConfig; virtual; abstract;
     function GetImage: TLazPaintImage; virtual; abstract;
+    function GetImageAction: TObject; virtual; abstract;
     function GetToolManager: TToolManager; virtual; abstract;
     procedure SetBlackAndWhite(AValue: boolean); virtual;
     function GetZoomFactor: single; virtual;
@@ -321,6 +323,7 @@ type
     property ChooseColorTarget: TColorTarget read GetChooseColorTarget write setChooseColorTarget;
     property Config: TLazPaintConfig read GetConfig;
     property Image: TLazPaintImage read GetImage;
+    property ImageAction: TObject read GetImageAction;
     property ZoomFactor: single read GetZoomFactor;
     property ToolManager: TToolManager read GetToolManager;
     property Embedded: boolean read GetEmbedded;
@@ -645,30 +648,40 @@ begin
 end;
 
 function TLazPaintCustomInstance.GetColor(ATarget: TColorTarget): TBGRAPixel;
+  function GetStartColor(AFill: TVectorialFill): TBGRAPixel;
+  begin
+    if AFill.FillType = vftGradient then
+      result := AFill.Gradient.StartColor
+      else result := AFill.AverageColor;
+  end;
+  function GetEndColor(AFill: TVectorialFill): TBGRAPixel;
+  begin
+    if AFill.FillType = vftGradient then
+      result := AFill.Gradient.EndColor
+      else result := AFill.AverageColor;
+  end;
+
 begin
   case ATarget of
-    ctForeColorSolid: result := ToolManager.ForeColor;
-    ctForeColorStartGrad: if ToolManager.ForeFill.FillType = vftGradient then
-                            result := ToolManager.ForeFill.Gradient.StartColor
-                          else result := ToolManager.ForeColor;
-    ctForeColorEndGrad: if ToolManager.ForeFill.FillType = vftGradient then
-                          result := ToolManager.ForeFill.Gradient.EndColor
-                        else result := ToolManager.ForeColor;
-    ctBackColorSolid: result := ToolManager.BackColor;
-    ctBackColorStartGrad: if ToolManager.BackFill.FillType = vftGradient then
-                            result := ToolManager.BackFill.Gradient.StartColor
-                          else result := ToolManager.BackColor;
-    ctBackColorEndGrad: if ToolManager.BackFill.FillType = vftGradient then
-                          result := ToolManager.BackFill.Gradient.EndColor
-                        else result := ToolManager.BackColor;
+    ctForeColorSolid: result := ToolManager.ForeFill.AverageColor;
+    ctForeColorStartGrad: result := GetStartColor(ToolManager.ForeFill);
+    ctForeColorEndGrad: result := GetEndColor(ToolManager.ForeFill);
+    ctBackColorSolid: result := ToolManager.BackFill.AverageColor;
+    ctBackColorStartGrad: result := GetStartColor(ToolManager.BackFill);
+    ctBackColorEndGrad: result := GetEndColor(ToolManager.BackFill);
+    ctOutlineColorSolid: result := ToolManager.OutlineFill.AverageColor;
+    ctOutlineColorStartGrad: result := GetStartColor(ToolManager.OutlineFill);
+    ctOutlineColorEndGrad: result := GetEndColor(ToolManager.OutlineFill);
   else
     result := BGRAPixelTransparent;
   end;
+  if BlackAndWhite then result := BGRAToGrayscale(result);
 end;
 
 procedure TLazPaintCustomInstance.SetColor(ATarget: TColorTarget;
   AColor: TBGRAPixel);
 begin
+  if BlackAndWhite then AColor := BGRAToGrayscale(AColor);
   case ATarget of
     ctForeColorSolid: if ToolManager.ForeFill.FillType = vftSolid then
                         ToolManager.ForeColor := AColor;
@@ -682,6 +695,12 @@ begin
                             ToolManager.BackFill.Gradient.StartColor := AColor;
     ctBackColorEndGrad: if ToolManager.BackFill.FillType = vftGradient then
                           ToolManager.BackFill.Gradient.EndColor := AColor;
+    ctOutlineColorSolid: if ToolManager.OutlineFill.FillType = vftSolid then
+                        ToolManager.OutlineColor := AColor;
+    ctOutlineColorStartGrad: if ToolManager.OutlineFill.FillType = vftGradient then
+                            ToolManager.OutlineFill.Gradient.StartColor := AColor;
+    ctOutlineColorEndGrad: if ToolManager.OutlineFill.FillType = vftGradient then
+                          ToolManager.OutlineFill.Gradient.EndColor := AColor;
   end;
 end;
 
@@ -693,9 +712,21 @@ end;
 
 procedure TLazPaintCustomInstance.ShowMessage(ACaption: string; AMessage: string; ADlgType: TMsgDlgType = mtInformation);
 var top: TTopMostInfo;
+  elems: TStringList;
+  res: TModalResult;
 begin
   top := HideTopmost;
-  QuestionDlg(ACaption,AMessage,ADlgType,[mrOk,rsOkay],'');
+  elems := TStringList.Create;
+  elems.Delimiter:= #9;
+  elems.StrictDelimiter:= true;
+  elems.DelimitedText:= AMessage;
+  if (elems.Count = 3) and (elems[1] = rsDownload) then
+  begin
+    res := QuestionDlg(ACaption,elems[0],ADlgType,[mrOk,rsDownload,mrCancel,rsCancel],'');
+    if res = mrOk then OpenURL(elems[2]);
+  end else
+    QuestionDlg(ACaption,AMessage,ADlgType,[mrOk,rsOkay],'');
+  elems.Free;
   ShowTopmost(top);
 end;
 

+ 120 - 155
lazpaint/maintoolbar.inc

@@ -1,4 +1,31 @@
 procedure TFMain.CreateToolbarElements;
+
+  procedure InitVectorialFill(vf: TLCVectorialFillControl; grad: TBGRALayerGradientOriginal;
+      lbl: TLabel; pnl: TPanel; mouseMove: TMouseMoveEvent);
+  begin
+    vf.PopupMenu := nil;
+    vf.VerticalPadding:= DoScaleY(6, OriginalDPI);
+    vf.OnChooseColor :=  @VectorialFill_ChooseColor;
+    vf.OnTextureClick := @VectorialFill_TextureClick;
+    vf.OnFillChange:=    @VectorialFill_Change;
+    vf.AutoSize := False;
+    vf.OnFillTypeChange:=    @VectorialFill_TypeChange;
+    vf.OnAdjustToShape:=     @VectorialFill_AdjustToShape;
+    vf.OnEditGradTexPoints:= @VectorialFill_EditGradTexPoints;
+    vf.OnResize:= @VectorialFill_Resize;
+    with grad do
+    begin
+      vf.GradStartColor:= StartColor;
+      vf.GradEndColor:= EndColor;
+      vf.GradientType:= GradientType;
+      vf.GradRepetition:= Repetition;
+      vf.GradInterpolation:= ColorInterpolation;
+    end;
+    if Assigned(lbl) then lbl.OnMouseMove:= mouseMove;
+    pnl.OnMouseMove:= mouseMove;
+    vf.OnMouseMove:= mouseMove;
+  end;
+
 var
   ps: TPenStyle;
 begin
@@ -8,48 +35,12 @@ begin
   Panel_ToolbarBackground.PopupMenu := PopupToolbar;
   Perspective_Repeat.OnClick:=@Perspective_RepeatClick;
   Perspective_TwoPlanes.OnClick := @Perspective_TwoPlanesClick;
-  Panel_PenFill.PopupMenu := nil;
-  Panel_BackFill.PopupMenu := nil;
-  VectorialFill_Pen.VerticalPadding:= DoScaleY(6, OriginalDPI);
-  VectorialFill_Back.VerticalPadding:= DoScaleY(6, OriginalDPI);
-  VectorialFill_Pen.OnChooseColor := @VectorialFill_PenChooseColor;
-  VectorialFill_Back.OnChooseColor := @VectorialFill_BackChooseColor;
-  VectorialFill_Pen.OnTextureClick := @VectorialFill_TextureClick;
-  VectorialFill_Back.OnTextureClick := @VectorialFill_TextureClick;
-  VectorialFill_Pen.OnFillChange:=@VectorialFill_PenChange;
-  VectorialFill_Back.OnFillChange:=@VectorialFill_BackChange;
-  VectorialFill_Pen.AutoSize := False;
-  VectorialFill_Back.AutoSize := False;
-  VectorialFill_Pen.OnFillTypeChange:=@VectorialFill_PenTypeChange;
-  VectorialFill_Back.OnFillTypeChange:=@VectorialFill_BackTypeChange;
-  VectorialFill_Pen.OnAdjustToShape:=@VectorialFill_PenAdjustToShape;
-  VectorialFill_Back.OnAdjustToShape:=@VectorialFill_BackAdjustToShape;
-  VectorialFill_Pen.OnEditGradTexPoints:=@VectorialFill_PenEditGradTexPoints;
-  VectorialFill_Back.OnEditGradTexPoints:=@VectorialFill_BackEditGradTexPoints;
-  VectorialFill_Pen.OnResize:=@VectorialFill_PenResize;
-  VectorialFill_Back.OnResize:=@VectorialFill_BackResize;
-  with ToolManager.ForeLastGradient do
-  begin
-    VectorialFill_Pen.GradStartColor:= StartColor;
-    VectorialFill_Pen.GradEndColor:= EndColor;
-    VectorialFill_Pen.GradientType:= GradientType;
-    VectorialFill_Pen.GradRepetition:= Repetition;
-    VectorialFill_Pen.GradInterpolation:= ColorInterpolation;
-  end;
-  with ToolManager.BackLastGradient do
-  begin
-    VectorialFill_Back.GradStartColor:= StartColor;
-    VectorialFill_Back.GradEndColor:= EndColor;
-    VectorialFill_Back.GradientType:= GradientType;
-    VectorialFill_Back.GradRepetition:= Repetition;
-    VectorialFill_Back.GradInterpolation:= ColorInterpolation;
-  end;
-  Label_Pen.OnMouseMove:=@VectorialFill_ShowPenFill;
-  Panel_PenFill.OnMouseMove:=@VectorialFill_ShowPenFill;
-  VectorialFill_Pen.OnMouseMove:=@VectorialFill_ShowPenFill;
-  Label_Back.OnMouseMove:=@VectorialFill_ShowBackFill;
-  Panel_BackFill.OnMouseMove:=@VectorialFill_ShowBackFill;
-  VectorialFill_Back.OnMouseMove:=@VectorialFill_ShowBackFill;
+  InitVectorialFill(VectorialFill_Pen, ToolManager.ForeLastGradient, Label_Pen, Panel_PenFill, @VectorialFill_ShowPenFill);
+  VectorialFill_Pen.SolidColor := ToolManager.ForeColor;
+  InitVectorialFill(VectorialFill_Back, ToolManager.BackLastGradient, Label_Back, Panel_BackFill, @VectorialFill_ShowBackFill);
+  VectorialFill_Back.SolidColor := ToolManager.BackColor;
+  InitVectorialFill(VectorialFill_Outline, ToolManager.OutlineLastGradient, nil, Panel_OutlineFill, @VectorialFill_ShowOutlineFill);
+  VectorialFill_Outline.SolidColor := ToolManager.OutlineColor;
   Image_SwapColors.OnMouseDown := @Image_SwapColorsMouseDown;
   Tool_DrawShapeBorder.OnClick := @Tool_DrawShapeBorderClick;
   Tool_Aliasing.OnClick := @Tool_AliasingClick;
@@ -319,8 +310,10 @@ begin
   ToolManager.FillControls.Add(Panel_SwapColor);
   ToolManager.FillControls.Add(Panel_BackFill);
   ToolManager.FillControls.Add(Panel_ColorDiff);
+  ToolManager.OutlineFillControls.Add(Panel_OutlineFill);
   ToolManager.BrushControls.Add(Panel_Brush);
   ToolManager.RatioControls.Add(Panel_Ratio);
+  ToolManager.DonateControls.Add(Panel_Donate);
 end;
 
 procedure TFMain.UpdateToolOptions;
@@ -561,10 +554,13 @@ begin
   UpdateAllowedFillTypes;
   VectorialFill_Pen.CanAdjustToShape := ToolManager.ToolProvideCommand(tcForeAdjustToShape);
   VectorialFill_Back.CanAdjustToShape := ToolManager.ToolProvideCommand(tcBackAdjustToShape);
+  VectorialFill_Outline.CanAdjustToShape := ToolManager.ToolProvideCommand(tcOutlineAdjustToShape);
   VectorialFill_Pen.CanEditGradTexPoints := ToolManager.ToolProvideCommand(tcForeEditGradTexPoints);
   VectorialFill_Back.CanEditGradTexPoints := ToolManager.ToolProvideCommand(tcBackEditGradTexPoints);
+  VectorialFill_Outline.CanEditGradTexPoints := ToolManager.ToolProvideCommand(tcOutlineEditGradTexPoints);
   VectorialFill_Pen.EditingGradTexPoints := ToolManager.IsForeEditGradTexPoints;
   VectorialFill_Back.EditingGradTexPoints := ToolManager.IsBackEditGradTexPoints;
+  VectorialFill_Outline.EditingGradTexPoints := ToolManager.IsOutlineEditGradTexPoints;
 end;
 
 procedure TFMain.QueryArrange;
@@ -733,8 +729,15 @@ begin
   TimerHideFill.Enabled := false;
   VectorialFill_Pen.Height := VectorialFill_Pen.ToolIconSize + VectorialFill_Pen.VerticalPadding;
   VectorialFill_Back.Height := VectorialFill_Back.ToolIconSize + VectorialFill_Back.VerticalPadding;
+  VectorialFill_Outline.Height := VectorialFill_Outline.ToolIconSize + VectorialFill_Outline.VerticalPadding;
   Panel_PenFill.Height := Panel_SwapColor.Height;
   Panel_BackFill.Height := Panel_SwapColor.Height;
+  Panel_OutlineFill.Height := Panel_SwapColor.Height;
+end;
+
+procedure TFMain.ToolButton_DonateClick(Sender: TObject);
+begin
+  LazPaintInstance.Donate;
 end;
 
 procedure TFMain.SpinEdit_GridNbExit(Sender: TObject);
@@ -812,20 +815,20 @@ begin
 end;
 
 procedure TFMain.UpdateChooseColors;
+  procedure UpdateFor(AFillControl: TLCVectorialFillControl; ATargetSolid, ATargetStart, ATargetEnd: TColorTarget);
+  begin
+    if (AFillControl.FillType = vftSolid) and
+       (LazPaintInstance.ChooseColorTarget in [ATargetStart, ATargetEnd]) then
+      LazPaintInstance.ChooseColorTarget := ATargetSolid else
+    if (AFillControl.FillType = vftGradient) and
+       (LazPaintInstance.ChooseColorTarget = ATargetSolid) then
+      LazPaintInstance.ChooseColorTarget := ATargetStart;
+  end;
+
 begin
-  if (VectorialFill_Back.FillType = vftSolid) and
-     (LazPaintInstance.ChooseColorTarget in [ctBackColorStartGrad,ctBackColorEndGrad]) then
-    LazPaintInstance.ChooseColorTarget := ctBackColorSolid else
-  if (VectorialFill_Back.FillType = vftGradient) and
-     (LazPaintInstance.ChooseColorTarget = ctBackColorSolid) then
-    LazPaintInstance.ChooseColorTarget := ctBackColorStartGrad else
-  if (VectorialFill_Pen.FillType = vftSolid) and
-     (LazPaintInstance.ChooseColorTarget in [ctForeColorStartGrad,ctForeColorEndGrad]) then
-    LazPaintInstance.ChooseColorTarget := ctForeColorSolid else
-  if (VectorialFill_Pen.FillType = vftGradient) and
-     (LazPaintInstance.ChooseColorTarget = ctForeColorSolid) then
-    LazPaintInstance.ChooseColorTarget := ctForeColorStartGrad else
-      LazPaintInstance.ColorToFChooseColor;
+  UpdateFor(VectorialFill_Back, ctBackColorSolid, ctBackColorStartGrad, ctBackColorEndGrad);
+  UpdateFor(VectorialFill_Pen, ctForeColorSolid, ctForeColorStartGrad, ctForeColorEndGrad);
+  UpdateFor(VectorialFill_Outline, ctOutlineColorSolid, ctOutlineColorStartGrad, ctOutlineColorEndGrad);
 end;
 
 procedure TFMain.UpdateAllowedFillTypes;
@@ -896,137 +899,107 @@ begin
   result := Assigned(ActiveControl) and (ActiveControl.Name = 'EColor');
 end;
 
-procedure TFMain.VectorialFill_BackChange(Sender: TObject);
-var
-  tempFill: TVectorialFill;
-begin
-  if FInFillChange then exit;
-  FInFillChange:= true;
-
-  if ToolManager.BackFill.FillType <> VectorialFill_Back.FillType then
-  begin
-    tempFill := TVectorialFill.Create;
-    VectorialFill_Back.UpdateFillExceptGeometry(tempFill);
-    tempFill.FitGeometry(ToolManager.SuggestGradientBox);
-    ToolManager.BackFill.Assign(tempFill);
-    tempFill.Free;
-  end else
-    VectorialFill_Back.UpdateFillExceptGeometry(ToolManager.BackFill);
-
-  UpdateChooseColors;
-  UpdateEditPicture;
-  FInFillChange:= false;
-end;
-
-procedure TFMain.VectorialFill_BackEditGradTexPoints(Sender: TObject);
-begin
-  ToolManager.ToolCommand(tcBackEditGradTexPoints);
-end;
-
-procedure TFMain.VectorialFill_BackResize(Sender: TObject);
-begin
-  QueryArrange;
-end;
-
-procedure TFMain.VectorialFill_BackTypeChange(Sender: TObject);
-begin
-  DarkThemeInstance.Apply(VectorialFill_Back, DarkTheme);
-  VectorialFill_Back.Width := VectorialFill_Back.PreferredSize.cx;
-end;
-
-procedure TFMain.VectorialFill_BackAdjustToShape(Sender: TObject);
+procedure TFMain.VectorialFill_EditGradTexPoints(Sender: TObject);
 begin
-  ToolManager.ToolCommand(tcBackAdjustToShape);
+  if Sender = VectorialFill_Pen then ToolManager.ToolCommand(tcForeEditGradTexPoints)
+  else if Sender = VectorialFill_Back then ToolManager.ToolCommand(tcBackEditGradTexPoints)
+  else if Sender = VectorialFill_Outline then ToolManager.ToolCommand(tcOutlineEditGradTexPoints);
 end;
 
-procedure TFMain.VectorialFill_PenAdjustToShape(Sender: TObject);
+procedure TFMain.VectorialFill_AdjustToShape(Sender: TObject);
 begin
-  ToolManager.ToolCommand(tcForeAdjustToShape);
+  if Sender = VectorialFill_Pen then ToolManager.ToolCommand(tcForeAdjustToShape)
+  else if Sender = VectorialFill_Back then ToolManager.ToolCommand(tcBackAdjustToShape)
+  else if Sender = VectorialFill_Outline then ToolManager.ToolCommand(tcOutlineAdjustToShape);
 end;
 
-procedure TFMain.VectorialFill_PenChange(Sender: TObject);
+procedure TFMain.VectorialFill_Change(Sender: TObject);
 var
-  tempFill: TVectorialFill;
+  tempFill, targetFill: TVectorialFill;
+  vf: TLCVectorialFillControl;
 begin
   if FInFillChange then exit;
   FInFillChange:= true;
+  vf := Sender as TLCVectorialFillControl;
+  if vf = VectorialFill_Pen then targetFill := ToolManager.ForeFill
+  else if vf = VectorialFill_Back then targetFill := ToolManager.BackFill
+  else if vf = VectorialFill_Outline then targetFill := ToolManager.OutlineFill
+  else exit;
 
-  if ToolManager.ForeFill.FillType <> VectorialFill_Pen.FillType then
+  if targetFill.FillType <> vf.FillType then
   begin
     tempFill := TVectorialFill.Create;
-    VectorialFill_Pen.UpdateFillExceptGeometry(tempFill);
+    vf.UpdateFillExceptGeometry(tempFill);
     tempFill.FitGeometry(ToolManager.SuggestGradientBox);
-    ToolManager.ForeFill.Assign(tempFill);
+    targetFill.Assign(tempFill);
     tempFill.Free;
   end else
-    VectorialFill_Pen.UpdateFillExceptGeometry(ToolManager.ForeFill);
+    vf.UpdateFillExceptGeometry(targetFill);
 
   UpdateChooseColors;
   UpdateEditPicture;
   FInFillChange:= false;
 end;
 
-procedure TFMain.VectorialFill_PenEditGradTexPoints(Sender: TObject);
-begin
-  ToolManager.ToolCommand(tcForeEditGradTexPoints);
-end;
-
-procedure TFMain.VectorialFill_PenResize(Sender: TObject);
+procedure TFMain.VectorialFill_Resize(Sender: TObject);
 begin
   QueryArrange;
 end;
 
-procedure TFMain.VectorialFill_PenTypeChange(Sender: TObject);
+procedure TFMain.VectorialFill_TypeChange(Sender: TObject);
+var
+  vf: TLCVectorialFillControl;
 begin
-  DarkThemeInstance.Apply(VectorialFill_Pen, DarkTheme);
-  VectorialFill_Pen.Width := VectorialFill_Pen.PreferredSize.cx;
+  vf := Sender as TLCVectorialFillControl;
+  DarkThemeInstance.Apply(vf, DarkTheme);
+  vf.Width := vf.PreferredSize.cx;
 end;
 
-procedure TFMain.VectorialFill_ShowBackFill(Sender: TObject;
-  Shift: TShiftState; X, Y: Integer);
+procedure TFMain.VectorialFill_ShowBackFill(Sender: TObject; Shift: TShiftState; X, Y: Integer);
 begin
-  ShowBackFill;
+  ShowFill(VectorialFill_Back, Panel_BackFill);
 end;
 
-procedure TFMain.VectorialFill_ShowPenFill(Sender: TObject; Shift: TShiftState;
-  X, Y: Integer);
+procedure TFMain.VectorialFill_ShowPenFill(Sender: TObject; Shift: TShiftState; X, Y: Integer);
 begin
-  ShowPenFill;
+  ShowFill(VectorialFill_Pen, Panel_PenFill);
 end;
 
-procedure TFMain.VectorialFill_PenChooseColor(ASender: TObject; AButton: TMouseButton;
-  AColorIndex: integer; var AColorValue: TBGRAPixel; out AHandled: boolean);
-var
-  target: TColorTarget;
+procedure TFMain.VectorialFill_ShowOutlineFill(Sender: TObject; Shift: TShiftState; X, Y: Integer);
 begin
-  AHandled := true;
-  case AColorIndex of
-    -1: target := ctForeColorSolid;
-    0: target := ctForeColorStartGrad;
-    1: target := ctForeColorEndGrad;
-    else exit;
-  end;
-  if LazPaintInstance.ChooseColorVisible and (AButton = mbLeft) then
-    LazPaintInstance.ChooseColorTarget := target
-  else
-  begin
-    if ShowColorDialogFor(target) then
-      AColorValue := LazPaintInstance.GetColor(target);
-  end;
+  ShowFill(VectorialFill_Outline, Panel_OutlineFill);
 end;
 
-procedure TFMain.VectorialFill_BackChooseColor(ASender: TObject; AButton: TMouseButton;
+procedure TFMain.VectorialFill_ChooseColor(ASender: TObject; AButton: TMouseButton;
   AColorIndex: integer; var AColorValue: TBGRAPixel; out AHandled: boolean);
 var
   target: TColorTarget;
 begin
+  AHandled := false;
+  if ASender = VectorialFill_Pen then
+    case AColorIndex of
+      -1: target := ctForeColorSolid;
+      0: target := ctForeColorStartGrad;
+      1: target := ctForeColorEndGrad;
+      else exit;
+    end
+  else if ASender = VectorialFill_Back then
+    case AColorIndex of
+      -1: target := ctBackColorSolid;
+      0: target := ctBackColorStartGrad;
+      1: target := ctBackColorEndGrad;
+      else exit;
+    end
+  else if ASender = VectorialFill_Outline then
+    case AColorIndex of
+      -1: target := ctOutlineColorSolid;
+      0: target := ctOutlineColorStartGrad;
+      1: target := ctOutlineColorEndGrad;
+      else exit;
+    end
+  else exit;
+
   AHandled := true;
-  case AColorIndex of
-    -1: target := ctBackColorSolid;
-    0: target := ctBackColorStartGrad;
-    1: target := ctBackColorEndGrad;
-    else exit;
-  end;
   if LazPaintInstance.ChooseColorVisible and (AButton = mbLeft) then
     LazPaintInstance.ChooseColorTarget := target
   else
@@ -1489,19 +1462,11 @@ begin
   end;
 end;
 
-procedure TFMain.ShowPenFill;
-begin
-  VectorialFill_Pen.BringToFront;
-  VectorialFill_Pen.Height := VectorialFill_Pen.PreferredSize.cy;
-  Panel_PenFill.Height := VectorialFill_Pen.Top+VectorialFill_Pen.Height+DoScaleY(3, OriginalDPI);
-  HideFill(3000, true);
-end;
-
-procedure TFMain.ShowBackFill;
+procedure TFMain.ShowFill(AFillControl: TLCVectorialFillControl; APanel: TPanel);
 begin
-  VectorialFill_Back.BringToFront;
-  VectorialFill_Back.Height := VectorialFill_Back.PreferredSize.cy;
-  Panel_BackFill.Height := VectorialFill_Back.Top+VectorialFill_Back.height+DoScaleY(3, OriginalDPI);
+  AFillControl.BringToFront;
+  AFillControl.Height := AFillControl.PreferredSize.cy;
+  APanel.Height := AFillControl.Top + AFillControl.Height + DoScaleY(3, OriginalDPI);
   HideFill(3000, true);
 end;
 

+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.ar.po

@@ -3112,6 +3112,10 @@ msgstr ""
 msgid "Information"
 msgstr "معلومات"
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "تحميل"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3359,7 +3363,7 @@ msgid "px"
 msgstr "px"
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr ""
 
 #: uresourcestrings.rsramdisk

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 126 - 168
lazpaint/release/bin/i18n/lazpaint.bg.po


+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.cs.po

@@ -3110,6 +3110,10 @@ msgstr ""
 msgid "Information"
 msgstr "Informace"
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "Stažení"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3357,7 +3361,7 @@ msgid "px"
 msgstr "px"
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr ""
 
 #: uresourcestrings.rsramdisk

+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.de.po

@@ -3126,6 +3126,10 @@ msgstr "Unendlichkeit"
 msgid "Information"
 msgstr "Information"
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "Herunterladen"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3373,7 +3377,7 @@ msgid "px"
 msgstr "px"
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr "Erwartete Python-Version %1, aber %2 gefunden."
 
 #: uresourcestrings.rsramdisk

+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.es.po

@@ -3112,6 +3112,10 @@ msgstr "Infinito"
 msgid "Information"
 msgstr "Información"
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "Descargar"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3368,7 +3372,7 @@ msgid "px"
 msgstr "px"
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr "Se esperaba la versión de Python %1 pero se encontró %2."
 
 #: uresourcestrings.rsramdisk

+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.fi.po

@@ -3101,6 +3101,10 @@ msgstr ""
 msgid "Information"
 msgstr ""
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "Ladata"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3348,7 +3352,7 @@ msgid "px"
 msgstr ""
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr ""
 
 #: uresourcestrings.rsramdisk

+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.fr.po

@@ -3122,6 +3122,10 @@ msgstr "Infinité"
 msgid "Information"
 msgstr "Informations"
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "Télécharger"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3378,7 +3382,7 @@ msgid "px"
 msgstr "px"
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr "Version %1 de Python attendue mais %2 trouvée."
 
 #: uresourcestrings.rsramdisk

+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.ja.po

@@ -3111,6 +3111,10 @@ msgstr ""
 msgid "Information"
 msgstr ""
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "ダウンロード"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3358,7 +3362,7 @@ msgid "px"
 msgstr ""
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr ""
 
 #: uresourcestrings.rsramdisk

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 133 - 134
lazpaint/release/bin/i18n/lazpaint.kab.po


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 171 - 166
lazpaint/release/bin/i18n/lazpaint.lv.po


+ 6 - 2
lazpaint/release/bin/i18n/lazpaint.nl.po

@@ -3141,6 +3141,10 @@ msgstr "Oneindigheid"
 msgid "Information"
 msgstr "Informatie"
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "Downloaden"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3388,8 +3392,8 @@ msgid "px"
 msgstr "px"
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
-msgstr "Verwachte python-versie %1 maar %2 gevonden."
+msgid "Expected Python version %1 but %2 found."
+msgstr "Verwachte Python-versie %1 maar %2 gevonden."
 
 #: uresourcestrings.rsramdisk
 msgid "RAM disk"

+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.po

@@ -3101,6 +3101,10 @@ msgstr ""
 msgid "Information"
 msgstr ""
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr ""
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3357,7 +3361,7 @@ msgid "px"
 msgstr ""
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr ""
 
 #: uresourcestrings.rsramdisk

+ 6 - 2
lazpaint/release/bin/i18n/lazpaint.pt_BR.po

@@ -3127,6 +3127,10 @@ msgstr "Infinidade"
 msgid "Information"
 msgstr "informações"
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "Baixar"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3374,8 +3378,8 @@ msgid "px"
 msgstr ""
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
-msgstr "Versão python esperada %1, mas %2 encontrada."
+msgid "Expected Python version %1 but %2 found."
+msgstr "Versão Python esperada %1, mas %2 encontrada."
 
 #: uresourcestrings.rsramdisk
 msgid "RAM disk"

+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.ru.po

@@ -3106,6 +3106,10 @@ msgstr ""
 msgid "Information"
 msgstr ""
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "скачать"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3353,7 +3357,7 @@ msgid "px"
 msgstr ""
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr ""
 
 #: uresourcestrings.rsramdisk

+ 5 - 1
lazpaint/release/bin/i18n/lazpaint.sv.po

@@ -3095,6 +3095,10 @@ msgstr "Oändlig"
 msgid "Information"
 msgstr "Information"
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "Ladda ner"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3342,7 +3346,7 @@ msgid "px"
 msgstr "px"
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
+msgid "Expected Python version %1 but %2 found."
 msgstr "Python version %1 var förväntad men hittade %2."
 
 #: uresourcestrings.rsramdisk

+ 6 - 2
lazpaint/release/bin/i18n/lazpaint.zh_CN.po

@@ -3111,6 +3111,10 @@ msgstr "无限"
 msgid "Information"
 msgstr "信息"
 
+#: uresourcestrings.rsdownload
+msgid "Download"
+msgstr "下载"
+
 #: uresourcestrings.rsintensity
 msgctxt "uresourcestrings.rsintensity"
 msgid "Intensity"
@@ -3367,8 +3371,8 @@ msgid "px"
 msgstr "px"
 
 #: uresourcestrings.rspythonunexpectedversion
-msgid "Expected python version %1 but %2 found."
-msgstr "需要 python 版本 %1 但找到 %2。"
+msgid "Expected Python version %1 but %2 found."
+msgstr "需要 Python 版本 %1 但找到 %2。"
 
 #: uresourcestrings.rsramdisk
 msgid "RAM disk"

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 174 - 173
lazpaint/release/bin/i18n/lclstrconsts.kab.po


+ 180 - 171
lazpaint/release/bin/i18n/lcresourcestring.bg.po

@@ -1,171 +1,180 @@
-msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
-
-#: lcresourcestring.rsadjusttoshape
-msgid "Adjust to shape"
-msgstr ""
-
-#: lcresourcestring.rscigsbnegative
-msgid "Corr. HSL CCW"
-msgstr ""
-
-#: lcresourcestring.rscigsbpositive
-msgid "Corr. HSL CW"
-msgstr ""
-
-#: lcresourcestring.rscilinearhslnegative
-msgid "HSL CCW"
-msgstr ""
-
-#: lcresourcestring.rscilinearhslpositive
-msgid "HSL CW"
-msgstr ""
-
-#: lcresourcestring.rscilinearrgb
-msgid "RGB"
-msgstr ""
-
-#: lcresourcestring.rscistdrgb
-msgid "sRGB"
-msgstr ""
-
-#: lcresourcestring.rscolor
-msgid "Color"
-msgstr "Цвят"
-
-#: lcresourcestring.rscolorinterpolation
-msgid "Color interpolation"
-msgstr ""
-
-#: lcresourcestring.rseditgradtexpoints
-msgid "Edit gradient/texture points"
-msgstr ""
-
-#: lcresourcestring.rsendopacity
-msgid "End opacity"
-msgstr ""
-
-#: lcresourcestring.rsgradientangular
-msgid "Angular"
-msgstr ""
-
-#: lcresourcestring.rsgradientdiamond
-msgid "Diamond"
-msgstr ""
-
-#: lcresourcestring.rsgradientfill
-msgid "Gradient fill"
-msgstr "Преливане"
-
-#: lcresourcestring.rsgradientlinear
-msgid "Linear"
-msgstr "Линейно"
-
-#: lcresourcestring.rsgradientradial
-msgid "Radial"
-msgstr "Лъчево"
-
-#: lcresourcestring.rsgradientreflected
-msgid "Reflected"
-msgstr "Отражателно"
-
-#: lcresourcestring.rsgradientrepetition
-msgid "Gradient repetition"
-msgstr ""
-
-#: lcresourcestring.rsgrpad
-msgid "Pad"
-msgstr ""
-
-#: lcresourcestring.rsgrreflect
-msgid "Reflect"
-msgstr ""
-
-#: lcresourcestring.rsgrrepeat
-msgid "Repeat"
-msgstr ""
-
-#: lcresourcestring.rsgrsine
-msgid "Sine"
-msgstr "Синусоидално"
-
-#: lcresourcestring.rsincompatibletype
-msgid "Incompatible type"
-msgstr ""
-
-#: lcresourcestring.rsindexoutofbounds
-msgid "Index out of bounds"
-msgstr ""
-
-#: lcresourcestring.rslightposition
-msgid "Light position"
-msgstr "Разположение на светлината"
-
-#: lcresourcestring.rsloadtexture
-msgid "Load texture"
-msgstr ""
-
-#: lcresourcestring.rsnofill
-msgid "No fill"
-msgstr ""
-
-#: lcresourcestring.rsnottexturefill
-msgid "It is not a texture fill"
-msgstr ""
-
-#: lcresourcestring.rsopacity
-msgid "Opacity"
-msgstr "Прозрачност"
-
-#: lcresourcestring.rspreview
-msgid "Preview"
-msgstr ""
-
-#: lcresourcestring.rsshapeclassnotspecified
-msgid "Shape class not specified"
-msgstr ""
-
-#: lcresourcestring.rsshapenotfound
-msgid "Shape not found"
-msgstr ""
-
-#: lcresourcestring.rssolidcolor
-msgid "Solid color"
-msgstr ""
-
-#: lcresourcestring.rsstartopacity
-msgid "Start opacity"
-msgstr ""
-
-#: lcresourcestring.rsswapcolors
-msgid "Swap colors"
-msgstr "Превключване на цвета"
-
-#: lcresourcestring.rstexturefill
-msgid "Texture fill"
-msgstr ""
-
-#: lcresourcestring.rstexturerepetition
-msgid "Texture repetition"
-msgstr ""
-
-#: lcresourcestring.rstrnone
-msgid "No repetition"
-msgstr ""
-
-#: lcresourcestring.rstrrepeatboth
-msgid "Repeat both"
-msgstr ""
-
-#: lcresourcestring.rstrrepeatx
-msgid "Repeat X"
-msgstr ""
-
-#: lcresourcestring.rstrrepeaty
-msgid "Repeat Y"
-msgstr ""
-
-#: lcresourcestring.rsunknownshapeclass
-msgid "Unknown shape class \"%1\""
-msgstr ""
-
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: bg\n"
+"X-Generator: Poedit 2.2.3\n"
+
+#: lcresourcestring.rsadjusttoshape
+msgid "Adjust to shape"
+msgstr "Нагласяне според формата"
+
+#: lcresourcestring.rscigsbnegative
+msgid "Corr. HSL CCW"
+msgstr "Попр. НОО ОЧС"
+
+#: lcresourcestring.rscigsbpositive
+msgid "Corr. HSL CW"
+msgstr "Попр. НОО ЧС"
+
+#: lcresourcestring.rscilinearhslnegative
+msgid "HSL CCW"
+msgstr "НОО ОЧС"
+
+#: lcresourcestring.rscilinearhslpositive
+msgid "HSL CW"
+msgstr "НОО ЧС"
+
+#: lcresourcestring.rscilinearrgb
+msgid "RGB"
+msgstr "ЧЗС"
+
+#: lcresourcestring.rscistdrgb
+msgid "sRGB"
+msgstr ""
+
+#: lcresourcestring.rscolor
+msgid "Color"
+msgstr "Цвят"
+
+#: lcresourcestring.rscolorinterpolation
+msgid "Color interpolation"
+msgstr ""
+
+#: lcresourcestring.rseditgradtexpoints
+msgid "Edit gradient/texture points"
+msgstr ""
+
+#: lcresourcestring.rsendopacity
+msgid "End opacity"
+msgstr "Крайна прозрачност"
+
+#: lcresourcestring.rsgradientangular
+msgid "Angular"
+msgstr "Ъглово"
+
+#: lcresourcestring.rsgradientdiamond
+msgid "Diamond"
+msgstr "Ромбовидно"
+
+#: lcresourcestring.rsgradientfill
+msgid "Gradient fill"
+msgstr "Преливане"
+
+#: lcresourcestring.rsgradientlinear
+msgid "Linear"
+msgstr "Линейно"
+
+#: lcresourcestring.rsgradientradial
+msgid "Radial"
+msgstr "Лъчево"
+
+#: lcresourcestring.rsgradientreflected
+msgid "Reflected"
+msgstr "Отражателно"
+
+#: lcresourcestring.rsgradientrepetition
+msgid "Gradient repetition"
+msgstr "Повтаряне на преливането"
+
+#: lcresourcestring.rsgrpad
+msgid "Pad"
+msgstr ""
+
+#: lcresourcestring.rsgrreflect
+msgid "Reflect"
+msgstr "Отразяване"
+
+#: lcresourcestring.rsgrrepeat
+msgid "Repeat"
+msgstr "Повтаряне"
+
+#: lcresourcestring.rsgrsine
+msgid "Sine"
+msgstr "Синусоидално"
+
+#: lcresourcestring.rsincompatibletype
+msgid "Incompatible type"
+msgstr "Несъвместим вид"
+
+#: lcresourcestring.rsindexoutofbounds
+msgid "Index out of bounds"
+msgstr "Указателят е извън границите"
+
+#: lcresourcestring.rslightposition
+msgid "Light position"
+msgstr "Разположение на светлината"
+
+#: lcresourcestring.rsloadtexture
+msgid "Load texture"
+msgstr ""
+
+#: lcresourcestring.rsnofill
+msgid "No fill"
+msgstr "Без запълване"
+
+#: lcresourcestring.rsnottexturefill
+msgid "It is not a texture fill"
+msgstr ""
+
+#: lcresourcestring.rsopacity
+msgid "Opacity"
+msgstr "Прозрачност"
+
+#: lcresourcestring.rspreview
+msgid "Preview"
+msgstr "Преглед"
+
+#: lcresourcestring.rsshapeclassnotspecified
+msgid "Shape class not specified"
+msgstr ""
+
+#: lcresourcestring.rsshapenotfound
+msgid "Shape not found"
+msgstr "Формата не е намерена"
+
+#: lcresourcestring.rssolidcolor
+msgid "Solid color"
+msgstr "Плътен цвят"
+
+#: lcresourcestring.rsstartopacity
+msgid "Start opacity"
+msgstr "Начална прозрачност"
+
+#: lcresourcestring.rsswapcolors
+msgid "Swap colors"
+msgstr "Превключване на цвета"
+
+#: lcresourcestring.rstexturefill
+msgid "Texture fill"
+msgstr ""
+
+#: lcresourcestring.rstexturerepetition
+msgid "Texture repetition"
+msgstr ""
+
+#: lcresourcestring.rstrnone
+msgid "No repetition"
+msgstr "Без повтаряне"
+
+#: lcresourcestring.rstrrepeatboth
+msgid "Repeat both"
+msgstr "Повтаряне и по двете"
+
+#: lcresourcestring.rstrrepeatx
+msgid "Repeat X"
+msgstr "Повтаряне по X"
+
+#: lcresourcestring.rstrrepeaty
+msgid "Repeat Y"
+msgstr "Повтаряне по Y"
+
+#: lcresourcestring.rsunknownshapeclass
+msgid "Unknown shape class \"%1\""
+msgstr ""

+ 180 - 171
lazpaint/release/bin/i18n/lcresourcestring.kab.po

@@ -1,171 +1,180 @@
-msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
-
-#: lcresourcestring.rsadjusttoshape
-msgid "Adjust to shape"
-msgstr ""
-
-#: lcresourcestring.rscigsbnegative
-msgid "Corr. HSL CCW"
-msgstr ""
-
-#: lcresourcestring.rscigsbpositive
-msgid "Corr. HSL CW"
-msgstr ""
-
-#: lcresourcestring.rscilinearhslnegative
-msgid "HSL CCW"
-msgstr ""
-
-#: lcresourcestring.rscilinearhslpositive
-msgid "HSL CW"
-msgstr ""
-
-#: lcresourcestring.rscilinearrgb
-msgid "RGB"
-msgstr ""
-
-#: lcresourcestring.rscistdrgb
-msgid "sRGB"
-msgstr ""
-
-#: lcresourcestring.rscolor
-msgid "Color"
-msgstr "Ini"
-
-#: lcresourcestring.rscolorinterpolation
-msgid "Color interpolation"
-msgstr ""
-
-#: lcresourcestring.rseditgradtexpoints
-msgid "Edit gradient/texture points"
-msgstr ""
-
-#: lcresourcestring.rsendopacity
-msgid "End opacity"
-msgstr ""
-
-#: lcresourcestring.rsgradientangular
-msgid "Angular"
-msgstr ""
-
-#: lcresourcestring.rsgradientdiamond
-msgid "Diamond"
-msgstr "Am dyaman"
-
-#: lcresourcestring.rsgradientfill
-msgid "Gradient fill"
-msgstr "Tafesna n yini"
-
-#: lcresourcestring.rsgradientlinear
-msgid "Linear"
-msgstr "Tamziregt"
-
-#: lcresourcestring.rsgradientradial
-msgid "Radial"
-msgstr "Tazenẓarant"
-
-#: lcresourcestring.rsgradientreflected
-msgid "Reflected"
-msgstr "Ittusenddeden"
-
-#: lcresourcestring.rsgradientrepetition
-msgid "Gradient repetition"
-msgstr ""
-
-#: lcresourcestring.rsgrpad
-msgid "Pad"
-msgstr ""
-
-#: lcresourcestring.rsgrreflect
-msgid "Reflect"
-msgstr ""
-
-#: lcresourcestring.rsgrrepeat
-msgid "Repeat"
-msgstr ""
-
-#: lcresourcestring.rsgrsine
-msgid "Sine"
-msgstr ""
-
-#: lcresourcestring.rsincompatibletype
-msgid "Incompatible type"
-msgstr ""
-
-#: lcresourcestring.rsindexoutofbounds
-msgid "Index out of bounds"
-msgstr ""
-
-#: lcresourcestring.rslightposition
-msgid "Light position"
-msgstr "Ideg n tafat"
-
-#: lcresourcestring.rsloadtexture
-msgid "Load texture"
-msgstr "Sali-d tizḍi"
-
-#: lcresourcestring.rsnofill
-msgid "No fill"
-msgstr ""
-
-#: lcresourcestring.rsnottexturefill
-msgid "It is not a texture fill"
-msgstr ""
-
-#: lcresourcestring.rsopacity
-msgid "Opacity"
-msgstr "Tiḍullest"
-
-#: lcresourcestring.rspreview
-msgid "Preview"
-msgstr "Tamuɣli"
-
-#: lcresourcestring.rsshapeclassnotspecified
-msgid "Shape class not specified"
-msgstr ""
-
-#: lcresourcestring.rsshapenotfound
-msgid "Shape not found"
-msgstr ""
-
-#: lcresourcestring.rssolidcolor
-msgid "Solid color"
-msgstr ""
-
-#: lcresourcestring.rsstartopacity
-msgid "Start opacity"
-msgstr ""
-
-#: lcresourcestring.rsswapcolors
-msgid "Swap colors"
-msgstr "Senfel initen"
-
-#: lcresourcestring.rstexturefill
-msgid "Texture fill"
-msgstr ""
-
-#: lcresourcestring.rstexturerepetition
-msgid "Texture repetition"
-msgstr ""
-
-#: lcresourcestring.rstrnone
-msgid "No repetition"
-msgstr ""
-
-#: lcresourcestring.rstrrepeatboth
-msgid "Repeat both"
-msgstr ""
-
-#: lcresourcestring.rstrrepeatx
-msgid "Repeat X"
-msgstr ""
-
-#: lcresourcestring.rstrrepeaty
-msgid "Repeat Y"
-msgstr ""
-
-#: lcresourcestring.rsunknownshapeclass
-msgid "Unknown shape class \"%1\""
-msgstr ""
-
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: Yacine Bouklif <[email protected]>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: kab\n"
+"X-Generator: Poedit 2.3\n"
+
+#: lcresourcestring.rsadjusttoshape
+msgid "Adjust to shape"
+msgstr "Sezg ɣer talɣa"
+
+#: lcresourcestring.rscigsbnegative
+msgid "Corr. HSL CCW"
+msgstr "HSL yettuseɣtin di tnila mgal tamrilt"
+
+#: lcresourcestring.rscigsbpositive
+msgid "Corr. HSL CW"
+msgstr "HSL yettuseɣtin di tnila n temrilt"
+
+#: lcresourcestring.rscilinearhslnegative
+msgid "HSL CCW"
+msgstr "HSL di tnila mgal tamrilt"
+
+#: lcresourcestring.rscilinearhslpositive
+msgid "HSL CW"
+msgstr "HSL di tnila n temrilt"
+
+#: lcresourcestring.rscilinearrgb
+msgid "RGB"
+msgstr "RGB"
+
+#: lcresourcestring.rscistdrgb
+msgid "sRGB"
+msgstr "sRGB"
+
+#: lcresourcestring.rscolor
+msgid "Color"
+msgstr "Ini"
+
+#: lcresourcestring.rscolorinterpolation
+msgid "Color interpolation"
+msgstr "Asenṭeḍ n yini"
+
+#: lcresourcestring.rseditgradtexpoints
+msgid "Edit gradient/texture points"
+msgstr "Ẓreg tinqiḍin n ufesniw/tizḍi"
+
+#: lcresourcestring.rsendopacity
+msgid "End opacity"
+msgstr "Tiḍullest n taggara"
+
+#: lcresourcestring.rsgradientangular
+msgid "Angular"
+msgstr "Uɣmir"
+
+#: lcresourcestring.rsgradientdiamond
+msgid "Diamond"
+msgstr "Am dyaman"
+
+#: lcresourcestring.rsgradientfill
+msgid "Gradient fill"
+msgstr "Taččart s ufesniw"
+
+#: lcresourcestring.rsgradientlinear
+msgid "Linear"
+msgstr "Imzireg"
+
+#: lcresourcestring.rsgradientradial
+msgid "Radial"
+msgstr "Azenẓaran"
+
+#: lcresourcestring.rsgradientreflected
+msgid "Reflected"
+msgstr "Ittusendded"
+
+#: lcresourcestring.rsgradientrepetition
+msgid "Gradient repetition"
+msgstr "Allus n ufesniw"
+
+#: lcresourcestring.rsgrpad
+msgid "Pad"
+msgstr "Aččar"
+
+#: lcresourcestring.rsgrreflect
+msgid "Reflect"
+msgstr "Sended"
+
+#: lcresourcestring.rsgrrepeat
+msgid "Repeat"
+msgstr "Ales"
+
+#: lcresourcestring.rsgrsine
+msgid "Sine"
+msgstr "Asinus"
+
+#: lcresourcestring.rsincompatibletype
+msgid "Incompatible type"
+msgstr "Anaw amyagan"
+
+#: lcresourcestring.rsindexoutofbounds
+msgid "Index out of bounds"
+msgstr "Amatar iɛedda tilisa"
+
+#: lcresourcestring.rslightposition
+msgid "Light position"
+msgstr "Ideg n tafat"
+
+#: lcresourcestring.rsloadtexture
+msgid "Load texture"
+msgstr "Sali-d tizḍi"
+
+#: lcresourcestring.rsnofill
+msgid "No fill"
+msgstr "Ulac taččart"
+
+#: lcresourcestring.rsnottexturefill
+msgid "It is not a texture fill"
+msgstr "Mačči d taččart s tizḍi"
+
+#: lcresourcestring.rsopacity
+msgid "Opacity"
+msgstr "Tiḍullest"
+
+#: lcresourcestring.rspreview
+msgid "Preview"
+msgstr "Tadlayt"
+
+#: lcresourcestring.rsshapeclassnotspecified
+msgid "Shape class not specified"
+msgstr "Taserkemt n talɣa ur d-ttunefk ara"
+
+#: lcresourcestring.rsshapenotfound
+msgid "Shape not found"
+msgstr "Talɣa ur tettwaf ara"
+
+#: lcresourcestring.rssolidcolor
+msgid "Solid color"
+msgstr "Ini amsari"
+
+#: lcresourcestring.rsstartopacity
+msgid "Start opacity"
+msgstr "Tiḍullest n tazwara"
+
+#: lcresourcestring.rsswapcolors
+msgid "Swap colors"
+msgstr "Semmeskel initen"
+
+#: lcresourcestring.rstexturefill
+msgid "Texture fill"
+msgstr "Taččart s tizḍi"
+
+#: lcresourcestring.rstexturerepetition
+msgid "Texture repetition"
+msgstr "Allus n tizḍi"
+
+#: lcresourcestring.rstrnone
+msgid "No repetition"
+msgstr "Ulac allus"
+
+#: lcresourcestring.rstrrepeatboth
+msgid "Repeat both"
+msgstr "Ales di sin"
+
+#: lcresourcestring.rstrrepeatx
+msgid "Repeat X"
+msgstr "Ales X"
+
+#: lcresourcestring.rstrrepeaty
+msgid "Repeat Y"
+msgstr "Ales Y"
+
+#: lcresourcestring.rsunknownshapeclass
+msgid "Unknown shape class \"%1\""
+msgstr "Taserkemt n talɣa tarussint \"%1\""

+ 35 - 26
lazpaint/release/bin/i18n/lcresourcestring.lv.po

@@ -1,9 +1,19 @@
 msgid ""
-msgstr "Content-Type: text/plain; charset=UTF-8"
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: Kārlis Kalviškis <[email protected]>\n"
+"Language-Team: \n"
+"Language: lv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.1\n"
 
 #: lcresourcestring.rsadjusttoshape
 msgid "Adjust to shape"
-msgstr ""
+msgstr "Pielāgot objektam"
 
 #: lcresourcestring.rscigsbnegative
 msgid "Corr. HSL CCW"
@@ -35,19 +45,19 @@ msgstr "Krāsa"
 
 #: lcresourcestring.rscolorinterpolation
 msgid "Color interpolation"
-msgstr ""
+msgstr "Krāsu pārejas veids"
 
 #: lcresourcestring.rseditgradtexpoints
 msgid "Edit gradient/texture points"
-msgstr ""
+msgstr "Labot izvietojuma punktus"
 
 #: lcresourcestring.rsendopacity
 msgid "End opacity"
-msgstr ""
+msgstr "Beigu redzamība"
 
 #: lcresourcestring.rsgradientangular
 msgid "Angular"
-msgstr ""
+msgstr "Leņķiska"
 
 #: lcresourcestring.rsgradientdiamond
 msgid "Diamond"
@@ -71,19 +81,19 @@ msgstr "Spoguļveida"
 
 #: lcresourcestring.rsgradientrepetition
 msgid "Gradient repetition"
-msgstr ""
+msgstr "Toņu pārejas atkārtošanās"
 
 #: lcresourcestring.rsgrpad
 msgid "Pad"
-msgstr ""
+msgstr "Aizpilda"
 
 #: lcresourcestring.rsgrreflect
 msgid "Reflect"
-msgstr ""
+msgstr "Spoguļskats"
 
 #: lcresourcestring.rsgrrepeat
 msgid "Repeat"
-msgstr ""
+msgstr "Atkārtojas"
 
 #: lcresourcestring.rsgrsine
 msgid "Sine"
@@ -91,11 +101,11 @@ msgstr "Viļnveida"
 
 #: lcresourcestring.rsincompatibletype
 msgid "Incompatible type"
-msgstr ""
+msgstr "Nesaderīgs veids"
 
 #: lcresourcestring.rsindexoutofbounds
 msgid "Index out of bounds"
-msgstr ""
+msgstr "Vērtība ārpus atļautā"
 
 #: lcresourcestring.rslightposition
 msgid "Light position"
@@ -107,11 +117,11 @@ msgstr "Ielasīt virsmas rakstu"
 
 #: lcresourcestring.rsnofill
 msgid "No fill"
-msgstr ""
+msgstr "Bez pildījuma"
 
 #: lcresourcestring.rsnottexturefill
 msgid "It is not a texture fill"
-msgstr ""
+msgstr "Nav aizpildīts ar rakstu"
 
 #: lcresourcestring.rsopacity
 msgid "Opacity"
@@ -123,19 +133,19 @@ msgstr "Priekšskatīt"
 
 #: lcresourcestring.rsshapeclassnotspecified
 msgid "Shape class not specified"
-msgstr ""
+msgstr "Nav norādīta objekta klase"
 
 #: lcresourcestring.rsshapenotfound
 msgid "Shape not found"
-msgstr ""
+msgstr "Objekts netika atrasts"
 
 #: lcresourcestring.rssolidcolor
 msgid "Solid color"
-msgstr ""
+msgstr "Viendabīga krāsa"
 
 #: lcresourcestring.rsstartopacity
 msgid "Start opacity"
-msgstr ""
+msgstr "Sākuma redzamība"
 
 #: lcresourcestring.rsswapcolors
 msgid "Swap colors"
@@ -143,29 +153,28 @@ msgstr "Apmainīt krāsas vietām"
 
 #: lcresourcestring.rstexturefill
 msgid "Texture fill"
-msgstr ""
+msgstr "Aizpildīt ar rakstu"
 
 #: lcresourcestring.rstexturerepetition
 msgid "Texture repetition"
-msgstr ""
+msgstr "Raksta atkārtošana"
 
 #: lcresourcestring.rstrnone
 msgid "No repetition"
-msgstr ""
+msgstr "Neatkārtot"
 
 #: lcresourcestring.rstrrepeatboth
 msgid "Repeat both"
-msgstr ""
+msgstr "Atkārtot abējādi"
 
 #: lcresourcestring.rstrrepeatx
 msgid "Repeat X"
-msgstr ""
+msgstr "Atkārtot uz sāniem"
 
 #: lcresourcestring.rstrrepeaty
 msgid "Repeat Y"
-msgstr ""
+msgstr "Atkārtot uz augšu/leju"
 
 #: lcresourcestring.rsunknownshapeclass
 msgid "Unknown shape class \"%1\""
-msgstr ""
-
+msgstr "Nepazīstama objekta klase \"%1\""

+ 28 - 0
lazpaint/release/changelog

@@ -145,3 +145,31 @@ lazpaint (7.0.8) unstable; urgency=low
 
 -- circular <[email protected]>  Fri, 6 Mar 2020 18:32:00 +0100
 
+lazpaint (7.0.9) unstable; urgency=low
+
+  * interface: less flickering during action progress
+  * interface: minor scaling improvements
+  * interface: ensure toolwindows in front when restoring app
+  * interface: dark theme for status bar
+  * interface: add Donate tool button
+  * interface: add Python download button if missing
+  * interface: separate color for background and outline
+  * interface: don't undock windows when going fullscreen
+  * interface: fix remember save file extension
+  * translation: added Latvian
+  * translation: completed Kabyle and Bulgarian
+  * tool: fix updating transparent colors
+  * tool: fix release of middle mouse button
+  * tool: optimize layer transform
+  * tool: added font aliasing option
+  * tool: replace layer by gradient if it is opaque
+  * tool: floodfill using vector if possible
+  * tool: fix gradient undo/redo
+  * tool: multiselection of shapes
+  * tool: fix text editor handling of keys
+  * script: sort in menus
+  * script: added inner shadow/light
+  * script: added version function
+
+-- circular <[email protected]>  Fri, 3 Apr 2020 12:40:00 +0100
+

+ 28 - 0
lazpaint/release/debian/linux32/DEBIAN/changelog

@@ -145,3 +145,31 @@ lazpaint (7.0.8) unstable; urgency=low
 
 -- circular <[email protected]>  Fri, 6 Mar 2020 18:32:00 +0100
 
+lazpaint (7.0.9) unstable; urgency=low
+
+  * interface: less flickering during action progress
+  * interface: minor scaling improvements
+  * interface: ensure toolwindows in front when restoring app
+  * interface: dark theme for status bar
+  * interface: add Donate tool button
+  * interface: add Python download button if missing
+  * interface: separate color for background and outline
+  * interface: don't undock windows when going fullscreen
+  * interface: fix remember save file extension
+  * translation: added Latvian
+  * translation: completed Kabyle and Bulgarian
+  * tool: fix updating transparent colors
+  * tool: fix release of middle mouse button
+  * tool: optimize layer transform
+  * tool: added font aliasing option
+  * tool: replace layer by gradient if it is opaque
+  * tool: floodfill using vector if possible
+  * tool: fix gradient undo/redo
+  * tool: multiselection of shapes
+  * tool: fix text editor handling of keys
+  * script: sort in menus
+  * script: added inner shadow/light
+  * script: added version function
+
+-- circular <[email protected]>  Fri, 3 Apr 2020 12:40:00 +0100
+

+ 1 - 1
lazpaint/release/debian/linux32/DEBIAN/control

@@ -1,5 +1,5 @@
 Package: lazpaint
-Version: 7.0.8
+Version: 7.0.9
 Section: base
 Priority: optional
 Architecture: i386

+ 28 - 0
lazpaint/release/debian/linux64/DEBIAN/changelog

@@ -145,3 +145,31 @@ lazpaint (7.0.8) unstable; urgency=low
 
 -- circular <[email protected]>  Fri, 6 Mar 2020 18:32:00 +0100
 
+lazpaint (7.0.9) unstable; urgency=low
+
+  * interface: less flickering during action progress
+  * interface: minor scaling improvements
+  * interface: ensure toolwindows in front when restoring app
+  * interface: dark theme for status bar
+  * interface: add Donate tool button
+  * interface: add Python download button if missing
+  * interface: separate color for background and outline
+  * interface: don't undock windows when going fullscreen
+  * interface: fix remember save file extension
+  * translation: added Latvian
+  * translation: completed Kabyle and Bulgarian
+  * tool: fix updating transparent colors
+  * tool: fix release of middle mouse button
+  * tool: optimize layer transform
+  * tool: added font aliasing option
+  * tool: replace layer by gradient if it is opaque
+  * tool: floodfill using vector if possible
+  * tool: fix gradient undo/redo
+  * tool: multiselection of shapes
+  * tool: fix text editor handling of keys
+  * script: sort in menus
+  * script: added inner shadow/light
+  * script: added version function
+
+-- circular <[email protected]>  Fri, 3 Apr 2020 12:40:00 +0100
+

+ 1 - 1
lazpaint/release/debian/linux64/DEBIAN/control

@@ -1,5 +1,5 @@
 Package: lazpaint
-Version: 7.0.8
+Version: 7.0.9
 Section: base
 Priority: optional
 Architecture: amd64

+ 2 - 2
lazpaint/release/macOS/LazPaint.app/Contents/Info.plist

@@ -19,9 +19,9 @@
     <key>CFBundleSignature</key>
     <string>lazp</string>
     <key>CFBundleShortVersionString</key>
-    <string>7.0.8</string>
+    <string>7.0.9</string>
     <key>CFBundleVersion</key>
-    <string>7.0.8</string>
+    <string>7.0.9</string>
     <key>CSResourcesFileMapped</key>
     <true/>
     <key>CFBundleDocumentTypes</key>

+ 1 - 1
lazpaint/release/macOS/makedmg.sh

@@ -12,7 +12,7 @@ fi
 
 
 appname=LazPaint
-appversion=7.0.8
+appversion=7.0.9
 pkgversion=0
 appnamenospaces=lazpaint
 appbundle="$appname.app"

+ 1 - 1
lazpaint/release/windows/lazpaint.iss

@@ -1,7 +1,7 @@
 #define MyAppName "LazPaint"
 #define MyAppOutputName "lazpaint"
 #define MyInstallerSuffix "_setup_win32_win64"
-#define MyAppVersion "7.0.8"
+#define MyAppVersion "7.0.9"
 #define MyAppPublisher "Circular, Fabien Wang, Lainz and others"
 #define MyAppURL "http://sourceforge.net/projects/lazpaint/"
 #define MyAppExeName "lazpaint.exe"

+ 18 - 2
lazpaint/release/windows/stage.bat

@@ -5,10 +5,14 @@ echo Cleaning previous staging files...
 if exist lazpaint32 del /s /q lazpaint32 >nul
 if exist lazpaint32\i18n rmdir lazpaint32\i18n
 if exist lazpaint32\models rmdir lazpaint32\models
+if exist lazpaint32\scripts\lazpaint rmdir lazpaint32\scripts\lazpaint
+if exist lazpaint32\scripts rmdir lazpaint32\scripts
 if exist lazpaint32 rmdir lazpaint32
 if exist lazpaint64 del /s /q lazpaint64 >nul
 if exist lazpaint64\i18n rmdir lazpaint64\i18n
 if exist lazpaint64\models rmdir lazpaint64\models
+if exist lazpaint64\scripts\lazpaint rmdir lazpaint64\scripts\lazpaint
+if exist lazpaint64\scripts rmdir lazpaint64\scripts
 if exist lazpaint64 rmdir lazpaint64
 
 echo Binary found:
@@ -19,13 +23,19 @@ echo Staging 32-bit version...
 if not exist lazpaint32 mkdir lazpaint32
 copy ..\bin\lazpaint32.exe lazpaint32\lazpaint.exe >nul
 copy dcraw\dcraw32.exe lazpaint32\dcraw.exe >nul
+copy libwebp\libwebp32.dll lazpaint32 >nul
 copy ..\bin\readme.txt lazpaint32 >nul
 copy ..\bin\*.ini lazpaint32 >nul
 if not exist lazpaint32\i18n mkdir lazpaint32\i18n
 copy ..\bin\i18n\lazpaint.* lazpaint32\i18n >nul
+copy ..\bin\i18n\lcresourcestring.* lazpaint32\i18n >nul
 copy ..\bin\i18n\lclstrconsts.* lazpaint32\i18n >nul
 if not exist lazpaint32\models mkdir lazpaint32\models
 copy ..\bin\models lazpaint32\models >nul
+if not exist lazpaint32\scripts mkdir lazpaint32\scripts
+copy ..\..\..\scripts lazpaint32\scripts >nul
+if not exist lazpaint32\scripts\lazpaint mkdir lazpaint32\scripts\lazpaint
+copy ..\..\..\scripts\lazpaint lazpaint32\scripts\lazpaint >nul
 goto donebin32
 :missingbin32
 echo Error: 32-bit binary not found
@@ -36,13 +46,19 @@ echo Staging 64-bit version...
 if not exist lazpaint64 mkdir lazpaint64
 copy ..\bin\lazpaint_x64.exe lazpaint64\lazpaint.exe >nul
 copy dcraw\dcraw_x64.exe lazpaint64\dcraw.exe >nul
-copy ..\bin\readme.txt lazpaint32 >nul
-copy ..\bin\*.ini lazpaint32 >nul
+copy libwebp\libwebp64.dll lazpaint64 >nul
+copy ..\bin\readme.txt lazpaint64 >nul
+copy ..\bin\*.ini lazpaint64 >nul
 if not exist lazpaint64\i18n mkdir lazpaint64\i18n
 copy ..\bin\i18n\lazpaint.* lazpaint64\i18n >nul
+copy ..\bin\i18n\lcresourcestring.* lazpaint64\i18n >nul
 copy ..\bin\i18n\lclstrconsts.* lazpaint64\i18n >nul
 if not exist lazpaint64\models mkdir lazpaint64\models
 copy ..\bin\models lazpaint64\models >nul
+if not exist lazpaint64\scripts mkdir lazpaint64\scripts
+copy ..\..\..\scripts lazpaint64\scripts >nul
+if not exist lazpaint64\scripts\lazpaint mkdir lazpaint64\scripts\lazpaint
+copy ..\..\..\scripts\lazpaint lazpaint64\scripts\lazpaint >nul
 goto donebin64
 :missingbin64
 echo Error: 64-bit binary not found

+ 242 - 72
lazpaint/tools/utool.pas

@@ -35,9 +35,10 @@ const
 function StrToPaintToolType(const s: ansistring): TPaintToolType;
 
 type
-  TContextualToolbar = (ctPenFill, ctBackFill, ctPenWidth, ctPenStyle, ctAliasing, ctShape, ctEraserOption, ctTolerance,
-    ctDeformation, ctCloseShape, ctLineCap, ctJoinStyle, ctSplineStyle, ctText, ctTextShadow,
-    ctPhong, ctAltitude, ctPerspective, ctBrush, ctRatio);
+  TContextualToolbar = (ctPenFill, ctPenWidth, ctPenStyle, ctJoinStyle, ctLineCap,
+    ctCloseShape, ctSplineStyle, ctShape, ctRatio, ctBackFill,
+    ctBrush, ctEraserOption, ctAliasing, ctTolerance, ctDeformation, ctPerspective,
+    ctText, ctOutlineWidth, ctOutlineFill, ctTextShadow, ctPhong, ctAltitude);
   TContextualToolbars = set of TContextualToolbar;
 
 type
@@ -47,7 +48,8 @@ type
   TEraserMode = (emEraseAlpha, emSharpen, emSoften, emLighten, emDarken);
   TToolCommand = (tcCut, tcCopy, tcPaste, tcDelete, tcFinish, tcMoveUp, tcMoveDown, tcMoveToFront, tcMoveToBack,
     tcAlignLeft, tcCenterHorizontally, tcAlignRight, tcAlignTop, tcCenterVertically, tcAlignBottom,
-    tcShapeToSpline, tcForeAdjustToShape, tcBackAdjustToShape, tcForeEditGradTexPoints, tcBackEditGradTexPoints);
+    tcShapeToSpline, tcForeAdjustToShape, tcBackAdjustToShape, tcOutlineAdjustToShape,
+    tcForeEditGradTexPoints, tcBackEditGradTexPoints, tcOutlineEditGradTexPoints);
 
   TDeformationGridMode = (gmDeform, gmMovePointWithoutDeformation);
 
@@ -100,8 +102,10 @@ type
     function GetCurrentLayerKind: TLayerKind;
     function GetIsForeEditGradTexPoints: boolean; virtual;
     function GetIsBackEditGradTexPoints: boolean; virtual;
+    function GetIsOutlineEditGradTexPoints: boolean; virtual;
     function GetAllowedBackFillTypes: TVectorialFillTypes; virtual;
     function GetAllowedForeFillTypes: TVectorialFillTypes; virtual;
+    function GetAllowedOutlineFillTypes: TVectorialFillTypes; virtual;
     property ShiftState: TShiftState read FShiftState;
   public
     ToolUpdateNeeded: boolean;
@@ -142,8 +146,10 @@ type
     property ForeUniversalBrush: TUniversalBrush read GetForeUniversalBrush;
     property IsForeEditGradTexPoints: boolean read GetIsForeEditGradTexPoints;
     property IsBackEditGradTexPoints: boolean read GetIsBackEditGradTexPoints;
+    property IsOutlineEditGradTexPoints: boolean read GetIsOutlineEditGradTexPoints;
     property AllowedForeFillTypes: TVectorialFillTypes read GetAllowedForeFillTypes;
     property AllowedBackFillTypes: TVectorialFillTypes read GetAllowedBackFillTypes;
+    property AllowedOutlineFillTypes: TVectorialFillTypes read GetAllowedOutlineFillTypes;
   end;
 
   { TReadonlyTool }
@@ -192,11 +198,12 @@ type
     FSleepingToolType: TPaintToolType;
     FReturnValidatesHintShown: boolean;
     FOnToolChangedHandler: TOnToolChangedHandler;
+    FOnToolRenderChanged: TNotifyEvent;
     FOnToolbarChanged: TNotifyEvent;
     FOnPopupToolHandler: TOnPopupToolHandler;
 
-    FForeFill, FBackFill: TVectorialFill;
-    FForeLastGradient, FBackLastGradient: TBGRALayerGradientOriginal;
+    FForeFill, FBackFill, FOutlineFill: TVectorialFill;
+    FForeLastGradient, FBackLastGradient, FOutlineLastGradient: TBGRALayerGradientOriginal;
     FEraserMode: TEraserMode;
     FEraserAlpha: byte;
     FBrushInfoList: TList;
@@ -252,10 +259,11 @@ type
     FOnFloodFillOptionChanged: TNotifyEvent;
     FOnPerspectiveOptionChanged: TNotifyEvent;
 
-    procedure BackFillChange({%H-}ASender: TObject;
+    procedure FillChange(ASender: TObject;
       var {%H-}ADiff: TCustomVectorialFillDiff);
     function GetAllowedBackFillTypes: TVectorialFillTypes;
     function GetAllowedForeFillTypes: TVectorialFillTypes;
+    function GetAllowedOutlineFillTypes: TVectorialFillTypes;
     function GetCursor: TCursor;
     function GetBackColor: TBGRAPixel;
     function GetBrushAt(AIndex: integer): TLazPaintBrush;
@@ -263,19 +271,19 @@ type
     function GetBrushInfo: TLazPaintBrush;
     function GetForeColor: TBGRAPixel;
     function GetMaxDeformationGridSize: TSize;
+    function GetOutlineColor: TBGRAPixel;
     function GetShapeOptionAliasing: boolean;
     function GetPenWidth: single;
     function GetToolSleeping: boolean;
     function GetTextFontName: string;
     function GetTextFontSize: single;
     function GetTextFontStyle: TFontStyles;
-    procedure ForeFillChange({%H-}ASender: TObject;
-      var {%H-}ADiff: TCustomVectorialFillDiff);
     function ScriptGetAliasing(AVars: TVariableSet): TScriptResult;
     function ScriptGetArrowEnd(AVars: TVariableSet): TScriptResult;
     function ScriptGetArrowSize(AVars: TVariableSet): TScriptResult;
     function ScriptGetArrowStart(AVars: TVariableSet): TScriptResult;
     function ScriptGetBackColor(AVars: TVariableSet): TScriptResult;
+    function ScriptGetOutlineColor(AVars: TVariableSet): TScriptResult;
     function ScriptGetBrushCount(AVars: TVariableSet): TScriptResult;
     function ScriptGetBrushIndex(AVars: TVariableSet): TScriptResult;
     function ScriptGetBrushSpacing(AVars: TVariableSet): TScriptResult;
@@ -299,12 +307,18 @@ type
     function ScriptGetForeGradientRepetition(AVars: TVariableSet): TScriptResult;
     function ScriptGetForeGradientType(AVars: TVariableSet): TScriptResult;
     function ScriptGetForeGradientColors(AVars: TVariableSet): TScriptResult;
+    function ScriptGetOutlineGradientInterpolation(AVars: TVariableSet): TScriptResult;
+    function ScriptGetOutlineGradientRepetition(AVars: TVariableSet): TScriptResult;
+    function ScriptGetOutlineGradientType(AVars: TVariableSet): TScriptResult;
+    function ScriptGetOutlineGradientColors(AVars: TVariableSet): TScriptResult;
     function ScriptGetTextureRepetition(AVars: TVariableSet; AFill: TVectorialFill): TScriptResult;
     function ScriptGetTextureOpacity(AVars: TVariableSet; AFill: TVectorialFill): TScriptResult;
     function ScriptGetBackTextureRepetition(AVars: TVariableSet): TScriptResult;
     function ScriptGetBackTextureOpacity(AVars: TVariableSet): TScriptResult;
     function ScriptGetForeTextureRepetition(AVars: TVariableSet): TScriptResult;
     function ScriptGetForeTextureOpacity(AVars: TVariableSet): TScriptResult;
+    function ScriptGetOutlineTextureRepetition(AVars: TVariableSet): TScriptResult;
+    function ScriptGetOutlineTextureOpacity(AVars: TVariableSet): TScriptResult;
     function ScriptGetJoinStyle(AVars: TVariableSet): TScriptResult;
     function ScriptGetLightPosition(AVars: TVariableSet): TScriptResult;
     function ScriptGetLineCap(AVars: TVariableSet): TScriptResult;
@@ -327,6 +341,7 @@ type
     function ScriptSetArrowSize(AVars: TVariableSet): TScriptResult;
     function ScriptSetArrowStart(AVars: TVariableSet): TScriptResult;
     function ScriptSetBackColor(AVars: TVariableSet): TScriptResult;
+    function ScriptSetOutlineColor(AVars: TVariableSet): TScriptResult;
     function ScriptSetBrushIndex(AVars: TVariableSet): TScriptResult;
     function ScriptSetBrushSpacing(AVars: TVariableSet): TScriptResult;
     function ScriptSetDeformationGridMode(AVars: TVariableSet): TScriptResult;
@@ -345,6 +360,10 @@ type
     function ScriptSetBackGradientRepetition(AVars: TVariableSet): TScriptResult;
     function ScriptSetBackGradientType(AVars: TVariableSet): TScriptResult;
     function ScriptSetBackGradientColors(AVars: TVariableSet): TScriptResult;
+    function ScriptSetOutlineGradientInterpolation(AVars: TVariableSet): TScriptResult;
+    function ScriptSetOutlineGradientRepetition(AVars: TVariableSet): TScriptResult;
+    function ScriptSetOutlineGradientType(AVars: TVariableSet): TScriptResult;
+    function ScriptSetOutlineGradientColors(AVars: TVariableSet): TScriptResult;
     function ScriptSetForeGradientInterpolation(AVars: TVariableSet): TScriptResult;
     function ScriptSetForeGradientRepetition(AVars: TVariableSet): TScriptResult;
     function ScriptSetForeGradientType(AVars: TVariableSet): TScriptResult;
@@ -358,6 +377,9 @@ type
     function ScriptSetForeTexture(AVars: TVariableSet): TScriptResult;
     function ScriptSetForeTextureRepetition(AVars: TVariableSet): TScriptResult;
     function ScriptSetForeTextureOpacity(AVars: TVariableSet): TScriptResult;
+    function ScriptSetOutlineTexture(AVars: TVariableSet): TScriptResult;
+    function ScriptSetOutlineTextureRepetition(AVars: TVariableSet): TScriptResult;
+    function ScriptSetOutlineTextureOpacity(AVars: TVariableSet): TScriptResult;
     function ScriptSetJoinStyle(AVars: TVariableSet): TScriptResult;
     function ScriptSetLightPosition(AVars: TVariableSet): TScriptResult;
     function ScriptSetLineCap(AVars: TVariableSet): TScriptResult;
@@ -391,6 +413,7 @@ type
     procedure SetLightAltitude(AValue: integer);
     procedure SetLightPosition(AValue: TPointF);
     procedure SetLineCap(AValue: TPenEndCap);
+    procedure SetOutlineColor(AValue: TBGRAPixel);
     procedure SetPerspectiveOptions(AValue: TPerspectiveOptions);
     procedure SetPhongShapeAltitude(AValue: integer);
     procedure SetPhongShapeBorderSize(AValue: integer);
@@ -420,8 +443,8 @@ type
     ShapeControls, PenStyleControls, JoinStyleControls, SplineStyleControls,
     CloseShapeControls, LineCapControls, DeformationControls,
     TextControls, TextShadowControls, PhongControls, AltitudeControls,
-    PerspectiveControls,FillControls,
-    BrushControls, RatioControls: TList;
+    PerspectiveControls,FillControls,OutlineFillControls,
+    BrushControls, RatioControls, DonateControls: TList;
 
     constructor Create(AImage: TLazPaintImage; AConfigProvider: IConfigProvider;
       ABitmapToVirtualScreen: TBitmapToVirtualScreenFunction = nil;
@@ -466,9 +489,10 @@ type
     function DisplayFilledSelection: boolean;
     function IsForeEditGradTexPoints: boolean;
     function IsBackEditGradTexPoints: boolean;
+    function IsOutlineEditGradTexPoints: boolean;
     procedure QueryExitTool;
 
-    procedure RenderTool(formBitmap: TBGRABitmap);
+    function RenderTool(formBitmap: TBGRABitmap): TRect;
     function GetRenderBounds(VirtualScreenWidth, VirtualScreenHeight: integer): TRect;
     function SuggestGradientBox: TAffineBox;
 
@@ -484,6 +508,7 @@ type
     procedure SetDeformationGridSize(ASize: TSize);
 
     property Image: TLazPaintImage read FImage;
+    property Scripting: TScriptContext read FScriptContext;
     property BlackAndWhite: boolean read FBlackAndWhite write FBlackAndWhite;
     property CurrentTool: TGenericTool read FCurrentTool;
     property ToolCurrentCursorPos: TPointF read FToolCurrentCursorPos;
@@ -494,10 +519,14 @@ type
     property AllowedForeFillTypes: TVectorialFillTypes read GetAllowedForeFillTypes;
     property BackFill: TVectorialFill read FBackFill;
     property AllowedBackFillTypes: TVectorialFillTypes read GetAllowedBackFillTypes;
+    property OutlineFill: TVectorialFill read FOutlineFill;
+    property AllowedOutlineFillTypes: TVectorialFillTypes read GetAllowedOutlineFillTypes;
     property ForeColor: TBGRAPixel read GetForeColor write SetForeColor;
     property BackColor: TBGRAPixel read GetBackColor write SetBackColor;
+    property OutlineColor: TBGRAPixel read GetOutlineColor write SetOutlineColor;
     property ForeLastGradient: TBGRALayerGradientOriginal read FForeLastGradient;
     property BackLastGradient: TBGRALayerGradientOriginal read FBackLastGradient;
+    property OutlineLastGradient: TBGRALayerGradientOriginal read FOutlineLastGradient;
     property EraserMode: TEraserMode read FEraserMode write SetEraserMode;
     property EraserAlpha: byte read FEraserAlpha write SetEraserAlpha;
     property PenWidth: single read GetPenWidth write SetPenWidth;
@@ -541,6 +570,7 @@ type
     property PerspectiveOptions: TPerspectiveOptions read FPerspectiveOptions write SetPerspectiveOptions;
 
     property OnToolChanged: TOnToolChangedHandler read FOnToolChangedHandler write FOnToolChangedHandler;
+    property OnToolRenderChanged: TNotifyEvent read FOnToolRenderChanged write FOnToolRenderChanged;
     property OnToolbarChanged: TNotifyEvent read FOnToolbarChanged write FOnToolbarChanged;
     property OnPopup: TOnPopupToolHandler read FOnPopupToolHandler write FOnPopupToolHandler;
     property OnEraserChanged: TNotifyEvent read FOnEraserChanged write FOnEraserChanged;
@@ -573,7 +603,7 @@ function ToolPopupMessageToStr(AMessage :TToolPopupMessage; AKey: Word = 0): str
 implementation
 
 uses UGraph, LCScaleDPI, LazPaintType, UCursors, BGRATextFX, ULoading, UResourceStrings,
-  BGRATransform, LCVectorOriginal, BGRASVGOriginal, math, ULoadImage;
+  BGRATransform, LCVectorOriginal, BGRASVGOriginal, math, ULoadImage, LCVectorTextShapes;
 
 function StrToPaintToolType(const s: ansistring): TPaintToolType;
 var pt: TPaintToolType;
@@ -792,11 +822,21 @@ begin
   result := [vftSolid,vftGradient,vftTexture];
 end;
 
+function TGenericTool.GetAllowedOutlineFillTypes: TVectorialFillTypes;
+begin
+  result := [vftSolid,vftGradient,vftTexture];
+end;
+
 function TGenericTool.GetIsBackEditGradTexPoints: boolean;
 begin
   result := false;
 end;
 
+function TGenericTool.GetIsOutlineEditGradTexPoints: boolean;
+begin
+  result := false;
+end;
+
 function TGenericTool.GetIsForeEditGradTexPoints: boolean;
 begin
   result := false;
@@ -1075,63 +1115,45 @@ function TGenericTool.ToolKeyDown(var key: Word): TRect;
 var
   key2: Word;
 begin
-  if (Key = VK_SNAP) or (Key = VK_SNAP2) then
-  begin
-    key2 := VK_CONTROL;
-    result := DoToolKeyDown(key2);
-    if key2 = 0 then key := 0;
-  end else
-    result := DoToolKeyDown(key);
-
   if key = VK_SHIFT then
   begin
     Include(FShiftState, ssShift);
-    key := 0;
+    //do not reset Key to preserve typing ^o or "o
   end else
+  if (key = VK_MENU) then
+    Include(FShiftState, ssAlt);
+
   if (Key = VK_SNAP) or (Key = VK_SNAP2) then
   begin
+    key2 := VK_CONTROL;
     Include(FShiftState, ssSnap);
-    key := 0;
+    result := DoToolKeyDown(key2);
+    if key2 = 0 then key := 0;
   end else
-  if (key = VK_MENU) then
-  begin
-    Include(FShiftState, ssAlt);
-    key := 0;
-  end;
+    result := DoToolKeyDown(key);
 end;
 
 function TGenericTool.ToolKeyUp(var key: Word): TRect;
 var
-  handled: Boolean;
   key2: word;
 begin
   if (key = VK_SHIFT) and (ssShift in FShiftState) then
   begin
     Exclude(FShiftState, ssShift);
-    handled := true;
-  end else
-  if ((key = VK_SNAP) or (key = VK_SNAP2)) and (ssSnap in FShiftState) then
-  begin
-    Exclude(FShiftState, ssSnap);
-    handled := true;
+    //do not reset key to preserve typing ^o or "o
   end else
   if (key = VK_MENU) and (ssAlt in FShiftState) then
-  begin
     Exclude(FShiftState, ssAlt);
-    handled := true;
-  end else
-    handled := false;
 
   //propagate in all cases to know when keys are released for unicode input
   if (Key = VK_SNAP) or (Key = VK_SNAP2) then
   begin
     key2 := VK_CONTROL;
+    Exclude(FShiftState, ssSnap);
     result := DoToolKeyUp(key2);
     if key2 = 0 then key := 0;
   end else
     result := DoToolKeyUp(key);
-
-  if handled then key := 0;
 end;
 
 function TGenericTool.ToolKeyPress(var key: TUTF8Char): TRect;
@@ -1365,6 +1387,11 @@ begin
   if Assigned(FOnLineCapChanged) then FOnLineCapChanged(self);
 end;
 
+procedure TToolManager.SetOutlineColor(AValue: TBGRAPixel);
+begin
+  FOutlineFill.SolidColor := AValue;
+end;
+
 procedure TToolManager.SetPerspectiveOptions(AValue: TPerspectiveOptions);
 begin
   if FPerspectiveOptions=AValue then Exit;
@@ -1611,16 +1638,26 @@ begin
   if toolCursor <> crDefault then result := toolCursor;
 end;
 
-procedure TToolManager.BackFillChange(ASender: TObject;
+procedure TToolManager.FillChange(ASender: TObject;
   var ADiff: TCustomVectorialFillDiff);
 begin
   if FInToolUpdate or FInSwapFill then exit;
   ToolUpdate;
   if Assigned(FOnFillChanged) then FOnFillChanged(self);
-  if FBackFill.FillType = vftGradient then
+  if (ASender = FBackFill) and (FBackFill.FillType = vftGradient) then
   begin
     FBackLastGradient.Free;
     FBackLastGradient := FBackFill.Gradient.Duplicate as TBGRALayerGradientOriginal;
+  end else
+  if (ASender = FForeFill) and (FForeFill.FillType = vftGradient) then
+  begin
+    FForeLastGradient.Free;
+    FForeLastGradient := FForeFill.Gradient.Duplicate as TBGRALayerGradientOriginal;
+  end else
+  if (ASender = FOutlineFill) and (FOutlineFill.FillType = vftGradient) then
+  begin
+    FOutlineLastGradient.Free;
+    FOutlineLastGradient := FOutlineFill.Gradient.Duplicate as TBGRALayerGradientOriginal;
   end;
 end;
 
@@ -1636,6 +1673,12 @@ begin
   else result := [vftSolid,vftGradient,vftTexture];
 end;
 
+function TToolManager.GetAllowedOutlineFillTypes: TVectorialFillTypes;
+begin
+  if Assigned(CurrentTool) then result := CurrentTool.AllowedOutlineFillTypes
+  else result := [vftSolid,vftGradient,vftTexture];
+end;
+
 function TToolManager.GetForeColor: TBGRAPixel;
 begin
   if BlackAndWhite then
@@ -1650,6 +1693,14 @@ begin
   result.cy := Max(MinDeformationGridSize,Min(image.Height div 2,50)+1);
 end;
 
+function TToolManager.GetOutlineColor: TBGRAPixel;
+begin
+  if BlackAndWhite then
+    result := BGRAToGrayscale(FOutlineFill.AverageColor)
+  else
+    result := FOutlineFill.AverageColor;
+end;
+
 function TToolManager.GetShapeOptionAliasing: boolean;
 begin
   result := toAliasing in FShapeOptions;
@@ -1681,19 +1732,6 @@ begin
   result := FTextFontStyle;
 end;
 
-procedure TToolManager.ForeFillChange(ASender: TObject;
-  var ADiff: TCustomVectorialFillDiff);
-begin
-  if FInToolUpdate or FInSwapFill then exit;
-  ToolUpdate;
-  if Assigned(FOnFillChanged) then FOnFillChanged(self);
-  if FForeFill.FillType = vftGradient then
-  begin
-    FForeLastGradient.Free;
-    FForeLastGradient := FForeFill.Gradient.Duplicate as TBGRALayerGradientOriginal;
-  end;
-end;
-
 function TToolManager.ScriptGetAliasing(AVars: TVariableSet): TScriptResult;
 begin
   AVars.Booleans['Result'] := toAliasing in ShapeOptions;
@@ -1724,6 +1762,12 @@ begin
   result := srOk;
 end;
 
+function TToolManager.ScriptGetOutlineColor(AVars: TVariableSet): TScriptResult;
+begin
+  AVars.Pixels['Result'] := OutlineColor;
+  result := srOk;
+end;
+
 function TToolManager.ScriptGetBrushCount(AVars: TVariableSet): TScriptResult;
 begin
   AVars.Integers['Result'] := BrushCount;
@@ -1897,6 +1941,26 @@ begin
   result := ScriptGetGradientColors(AVars, FForeFill);
 end;
 
+function TToolManager.ScriptGetOutlineGradientInterpolation(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptGetGradientInterpolation(AVars, FOutlineFill);
+end;
+
+function TToolManager.ScriptGetOutlineGradientRepetition(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptGetGradientRepetition(AVars, FOutlineFill);
+end;
+
+function TToolManager.ScriptGetOutlineGradientType(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptGetGradientType(AVars, FOutlineFill);
+end;
+
+function TToolManager.ScriptGetOutlineGradientColors(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptGetGradientColors(AVars, FOutlineFill);
+end;
+
 function TToolManager.ScriptGetTextureRepetition(AVars: TVariableSet;
   AFill: TVectorialFill): TScriptResult;
 begin
@@ -1940,6 +2004,16 @@ begin
   result := ScriptGetTextureOpacity(AVars, ForeFill);
 end;
 
+function TToolManager.ScriptGetOutlineTextureRepetition(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptGetTextureRepetition(AVars, OutlineFill);
+end;
+
+function TToolManager.ScriptGetOutlineTextureOpacity(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptGetTextureOpacity(AVars, OutlineFill);
+end;
+
 function TToolManager.ScriptGetJoinStyle(AVars: TVariableSet): TScriptResult;
 begin
   result := srOk;
@@ -2158,6 +2232,12 @@ begin
   result := srOk;
 end;
 
+function TToolManager.ScriptSetOutlineColor(AVars: TVariableSet): TScriptResult;
+begin
+  OutlineColor := AVars.Pixels['Color'];
+  result := srOk;
+end;
+
 function TToolManager.ScriptSetBrushIndex(AVars: TVariableSet): TScriptResult;
 var
   index: Int64;
@@ -2310,8 +2390,8 @@ begin
     AFill.Gradient.GradientType:= gt
   else
   begin
-    if AFill = BackFill then
-      lastGrad := FBackLastGradient
+    if AFill = BackFill then lastGrad := FBackLastGradient
+    else if AFill = OutlineFill then lastGrad := FOutlineLastGradient
     else lastGrad := FForeLastGradient;
 
     lastGrad.GradientType:= gt;
@@ -2343,8 +2423,8 @@ begin
     AFill.Gradient.SetColors(TVariableSet.GetPixelAt(colors, 0), TVariableSet.GetPixelAt(colors, 1))
   else
   begin
-    if AFill = BackFill then
-      lastGrad := FBackLastGradient
+    if AFill = BackFill then lastGrad := FBackLastGradient
+    else if AFill = OutlineFill then lastGrad := FOutlineLastGradient
     else lastGrad := FForeLastGradient;
 
     b := SuggestGradientBox;
@@ -2380,6 +2460,26 @@ begin
   result := ScriptSetGradientColors(AVars, FBackFill);
 end;
 
+function TToolManager.ScriptSetOutlineGradientInterpolation(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptSetGradientInterpolation(AVars, FOutlineFill);
+end;
+
+function TToolManager.ScriptSetOutlineGradientRepetition(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptSetGradientRepetition(AVars, FOutlineFill);
+end;
+
+function TToolManager.ScriptSetOutlineGradientType(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptSetGradientType(AVars, FOutlineFill);
+end;
+
+function TToolManager.ScriptSetOutlineGradientColors(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptSetGradientColors(AVars, FOutlineFill);
+end;
+
 function TToolManager.ScriptSetForeGradientInterpolation(AVars: TVariableSet): TScriptResult;
 begin
   result := ScriptSetGradientInterpolation(AVars, FForeFill);
@@ -2473,6 +2573,21 @@ begin
   result := ScriptSetTextureOpacity(AVars, ForeFill);
 end;
 
+function TToolManager.ScriptSetOutlineTexture(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptSetTexture(AVars, OutlineFill);
+end;
+
+function TToolManager.ScriptSetOutlineTextureRepetition(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptSetTextureRepetition(AVars, OutlineFill);
+end;
+
+function TToolManager.ScriptSetOutlineTextureOpacity(AVars: TVariableSet): TScriptResult;
+begin
+  result := ScriptSetTextureOpacity(AVars, OutlineFill);
+end;
+
 function TToolManager.ScriptSetJoinStyle(AVars: TVariableSet): TScriptResult;
 begin
   result := srOk;
@@ -2711,15 +2826,26 @@ begin
   RegisterScriptFunctions(True);
 
   FForeFill := TVectorialFill.Create;
+  FForeFill.TransparentMode := tmAlphaZeroOnly;
   FForeFill.SolidColor := BGRABlack;
-  FForeFill.OnChange:=@ForeFillChange;
-  FBackFill := TVectorialFill.Create;
-  FBackFill.SolidColor := CSSSkyBlue;
-  FBackFill.OnChange:=@BackFillChange;
+  FForeFill.OnChange:= @FillChange;
   FForeLastGradient:= TBGRALayerGradientOriginal.Create;
   FForeLastGradient.ColorInterpolation:= ciLinearRGB;
+
+  FBackFill := TVectorialFill.Create;
+  FBackFill.TransparentMode := tmAlphaZeroOnly;
+  FBackFill.SolidColor := CSSSkyBlue;
+  FBackFill.OnChange:= @FillChange;
   FBackLastGradient:= TBGRALayerGradientOriginal.Create;
   FBackLastGradient.ColorInterpolation:= ciLinearRGB;
+
+  FOutlineFill := TVectorialFill.Create;
+  FOutlineFill.TransparentMode := tmAlphaZeroOnly;
+  FOutlineFill.SolidColor := CSSRed;
+  FOutlineFill.OnChange:= @FillChange;
+  FOutlineLastGradient:= TBGRALayerGradientOriginal.Create;
+  FOutlineLastGradient.ColorInterpolation:= ciLinearRGB;
+
   FNormalPenWidth := 5;
   FEraserWidth := 10;
   FEraserAlpha := 255;
@@ -2740,7 +2866,7 @@ begin
   FTextOutlineWidth := 2;
   FTextShadow := false;
   FTextFontSize := 10;
-  FTextFontName := 'Arial';
+  FTextFontName := TTextShape.DefaultFontName;
   FTextFontStyle:= [];
   FTextAlign := taLeftJustify;
   FTextPhong := False;
@@ -2773,8 +2899,10 @@ begin
   AltitudeControls := TList.Create;
   PerspectiveControls := TList.Create;
   FillControls := TList.Create;
+  OutlineFillControls := TList.Create;
   BrushControls := TList.Create;
   RatioControls := TList.Create;
+  DonateControls := TList.Create;
 
   FCurrentToolType := ptHand;
   FCurrentTool := PaintTools[ptHand].Create(Self);
@@ -2804,8 +2932,10 @@ begin
   AltitudeControls.Free;
   PerspectiveControls.Free;
   FillControls.Free;
+  OutlineFillControls.Free;
   BrushControls.Free;
   RatioControls.Free;
+  DonateControls.Free;
 
   for i := 0 to BrushCount do
     BrushAt[i].Free;
@@ -2813,8 +2943,10 @@ begin
 
   FForeFill.Free;
   FBackFill.Free;
+  FOutlineFill.Free;
   FForeLastGradient.Free;
   FBackLastGradient.Free;
+  FOutlineLastGradient.Free;
 
   RegisterScriptFunctions(False);
   inherited Destroy;
@@ -2831,8 +2963,10 @@ begin
     exit;
   ForeColor := Config.DefaultToolForeColor;
   BackColor := Config.DefaultToolBackColor;
+  OutlineColor := Config.DefaultToolOutlineColor;
   AssignGradientFromConfigStr(FForeLastGradient, Config.DefaultToolForeGradient);
   AssignGradientFromConfigStr(FBackLastGradient, Config.DefaultToolBackGradient);
+  AssignGradientFromConfigStr(FOutlineLastGradient, Config.DefaultToolOutlineGradient);
   FNormalPenWidth := Config.DefaultToolPenWidth;
   FEraserWidth := Config.DefaultToolEraserWidth;
   if Assigned(FOnPenWidthChanged) then FOnPenWidthChanged(self);
@@ -2869,8 +3003,10 @@ begin
     exit;
   if ForeFill.FillType = vftSolid then Config.SetDefaultToolForeColor(ForeColor);
   if BackFill.FillType = vftSolid then Config.SetDefaultToolBackColor(BackColor);
+  if OutlineFill.FillType = vftSolid then Config.SetDefaultToolOutlineColor(OutlineColor);
   Config.SetDefaultToolForeGradient(GradientToConfigStr(FForeLastGradient));
   Config.SetDefaultToolBackGradient(GradientToConfigStr(FBackLastGradient));
+  Config.SetDefaultToolOutlineGradient(GradientToConfigStr(FOutlineLastGradient));
   Config.SetDefaultToolPenWidth(FNormalPenWidth);
   Config.SetDefaultToolEraserWidth(FEraserWidth);
   Config.SetDefaultToolOptionDrawShape(toDrawShape in ShapeOptions);
@@ -2995,11 +3131,11 @@ begin
     if not IsSelectingTool then
       Image.ReleaseEmptySelection;
 
-    Image.RenderMayChange(rect(0,0,Image.Width,Image.Height),True);
-
     UpdateContextualToolbars;
     If Assigned(FOnToolChangedHandler) then
       FOnToolChangedHandler(self, FCurrentToolType);
+    If Assigned(FOnToolRenderChanged) then
+      FOnToolRenderChanged(self);
   end;
   FShouldExitTool:= false;
 end;
@@ -3046,12 +3182,19 @@ begin
   OrResult(SetControlsVisible(EraserControls, ctEraserOption in contextualToolbars));
   OrResult(SetControlsVisible(ToleranceControls, ctTolerance in contextualToolbars));
   OrResult(SetControlsVisible(DeformationControls, ctDeformation in contextualToolbars));
-  OrResult(SetControlsVisible(TextControls, ctText in contextualToolbars));
+  if (ctText in contextualToolbars) and not (ctOutlineWidth in contextualToolbars) then
+    OrResult(SetControlsVisible(TextControls, True, 'Panel_Text')) else
+  if (ctOutlineWidth in contextualToolbars) and not (ctText in contextualToolbars) then
+    OrResult(SetControlsVisible(TextControls, True, 'Panel_TextOutline'))
+  else
+    OrResult(SetControlsVisible(TextControls, (ctText in contextualToolbars) and (ctOutlineWidth in contextualToolbars)));
+  OrResult(SetControlsVisible(OutlineFillControls, ctOutlineFill in contextualToolbars));
   OrResult(SetControlsVisible(TextShadowControls, ctTextShadow in contextualToolbars));
   OrResult(SetControlsVisible(PhongControls, ctPhong in contextualToolbars));
   OrResult(SetControlsVisible(AltitudeControls, ctAltitude in contextualToolbars));
   OrResult(SetControlsVisible(PerspectiveControls, ctPerspective in contextualToolbars));
   OrResult(SetControlsVisible(RatioControls, ctRatio in contextualToolbars));
+  OrResult(SetControlsVisible(DonateControls, FCurrentToolType = ptHand));
 
   if result and Assigned(FOnToolbarChanged) then FOnToolbarChanged(self);
 end;
@@ -3084,6 +3227,8 @@ begin
   FScriptContext.RegisterScriptFunction('ToolGetForeColor', @ScriptGetForeColor, ARegister);
   FScriptContext.RegisterScriptFunction('ToolSetBackColor', @ScriptSetBackColor, ARegister);
   FScriptContext.RegisterScriptFunction('ToolGetBackColor', @ScriptGetBackColor, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolSetOutlineColor', @ScriptSetOutlineColor, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolGetOutlineColor', @ScriptGetOutlineColor, ARegister);
   FScriptContext.RegisterScriptFunction('ToolSetEraserMode', @ScriptSetEraserMode, ARegister);
   FScriptContext.RegisterScriptFunction('ToolGetEraserMode', @ScriptGetEraserMode, ARegister);
   FScriptContext.RegisterScriptFunction('ToolSetEraserAlpha', @ScriptSetEraserAlpha, ARegister);
@@ -3147,6 +3292,14 @@ begin
   FScriptContext.RegisterScriptFunction('ToolGetBackGradientInterpolation', @ScriptGetBackGradientInterpolation, ARegister);
   FScriptContext.RegisterScriptFunction('ToolSetBackGradientColors', @ScriptSetBackGradientColors, ARegister);
   FScriptContext.RegisterScriptFunction('ToolGetBackGradientColors', @ScriptGetBackGradientColors, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolSetOutlineGradientType', @ScriptSetOutlineGradientType, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolGetOutlineGradientType', @ScriptGetOutlineGradientType, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolSetOutlineGradientRepetition', @ScriptSetOutlineGradientRepetition, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolGetOutlineGradientRepetition', @ScriptGetOutlineGradientRepetition, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolSetOutlineGradientInterpolation', @ScriptSetOutlineGradientInterpolation, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolGetOutlineGradientInterpolation', @ScriptGetOutlineGradientInterpolation, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolSetOutlineGradientColors', @ScriptSetOutlineGradientColors, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolGetOutlineGradientColors', @ScriptGetOutlineGradientColors, ARegister);
   FScriptContext.RegisterScriptFunction('ToolSetForeTexture', @ScriptSetForeTexture, ARegister);
   FScriptContext.RegisterScriptFunction('ToolSetForeTextureRepetition', @ScriptSetForeTextureRepetition, ARegister);
   FScriptContext.RegisterScriptFunction('ToolGetForeTextureRepetition', @ScriptGetForeTextureRepetition, ARegister);
@@ -3157,6 +3310,11 @@ begin
   FScriptContext.RegisterScriptFunction('ToolGetBackTextureRepetition', @ScriptGetBackTextureRepetition, ARegister);
   FScriptContext.RegisterScriptFunction('ToolSetBackTextureOpacity', @ScriptSetBackTextureOpacity, ARegister);
   FScriptContext.RegisterScriptFunction('ToolGetBackTextureOpacity', @ScriptGetBackTextureOpacity, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolSetOutlineTexture', @ScriptSetOutlineTexture, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolSetOutlineTextureRepetition', @ScriptSetOutlineTextureRepetition, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolGetOutlineTextureRepetition', @ScriptGetOutlineTextureRepetition, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolSetOutlineTextureOpacity', @ScriptSetOutlineTextureOpacity, ARegister);
+  FScriptContext.RegisterScriptFunction('ToolGetOutlineTextureOpacity', @ScriptGetOutlineTextureOpacity, ARegister);
   FScriptContext.RegisterScriptFunction('ToolSetPhongShapeAltitude', @ScriptSetPhongShapeAltitude, ARegister);
   FScriptContext.RegisterScriptFunction('ToolGetPhongShapeAltitude', @ScriptGetPhongShapeAltitude, ARegister);
   FScriptContext.RegisterScriptFunction('ToolSetPhongShapeBorderSize', @ScriptSetPhongShapeBorderSize, ARegister);
@@ -3183,10 +3341,11 @@ begin
     FCurrentTool := FSleepingTool;
     FSleepingTool := nil;
     FCurrentToolType := FSleepingToolType;
-    Image.RenderMayChange(rect(0,0,Image.Width,Image.Height),True);
     UpdateContextualToolbars;
     If Assigned(FOnToolChangedHandler) then
       FOnToolChangedHandler(self, FCurrentToolType);
+    If Assigned(FOnToolRenderChanged) then
+      FOnToolRenderChanged(self);
   end;
 end;
 
@@ -3301,6 +3460,7 @@ end;
 procedure TToolManager.SetTextFont(AName: string; ASize: single;
   AStyle: TFontStyles);
 begin
+  if AName = '' then AName := FTextFontName;
   if (FTextFontName <> AName) or
     (FTextFontSize <> ASize) or
     (FTextFontStyle <> AStyle) then
@@ -3510,6 +3670,9 @@ begin
     FInTool := true;
     try
       FCurrentTool := PaintTools[FCurrentToolType].Create(self);
+      UpdateContextualToolbars;
+      If Assigned(FOnToolRenderChanged) then
+        FOnToolRenderChanged(self);
     finally
       FInTool := false;
     end;
@@ -3579,22 +3742,29 @@ begin
   else result := false;
 end;
 
+function TToolManager.IsOutlineEditGradTexPoints: boolean;
+begin
+  if Assigned(CurrentTool) then result := CurrentTool.IsOutlineEditGradTexPoints
+  else result := false;
+end;
+
 procedure TToolManager.QueryExitTool;
 begin
   FShouldExitTool:= true;
 end;
 
-procedure TToolManager.RenderTool(formBitmap: TBGRABitmap);
+function TToolManager.RenderTool(formBitmap: TBGRABitmap): TRect;
 begin
   if ToolCanBeUsed and Assigned(CurrentTool) and not FInTool then
   begin
     FInTool := true;
     try
-      Image.RenderMayChange(CurrentTool.Render(formBitmap,formBitmap.Width,formBitmap.Height, @InternalBitmapToVirtualScreen));
+      result := CurrentTool.Render(formBitmap,formBitmap.Width,formBitmap.Height, @InternalBitmapToVirtualScreen);
     finally
       FInTool := false;
     end;
-  end;
+  end else
+    result := EmptyRect;
 end;
 
 function TToolManager.GetRenderBounds(VirtualScreenWidth, VirtualScreenHeight: integer): TRect;

+ 11 - 1
lazpaint/tools/utooldeformationgrid.pas

@@ -74,6 +74,7 @@ type
     function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect;
       override;
     function DoToolKeyDown(var key: Word): TRect; override;
+    function DoToolKeyUp(var key: Word): TRect; override;
     function GetIsSelectingTool: boolean; override;
     function GetTexture: TBGRABitmap; virtual;
     function GetTextureRepetition: TTextureRepetition; virtual;
@@ -727,7 +728,16 @@ begin
       manager.QueryExitTool;
       key := 0;
     end;
-  end;
+  end else
+  if (Key = VK_SHIFT) or (Key = VK_MENU) then
+    UpdateBoundsMode(result);
+end;
+
+function TToolTextureMapping.DoToolKeyUp(var key: Word): TRect;
+begin
+  Result:= EmptyRect;
+  if (Key = VK_SHIFT) or (Key = VK_MENU) then
+    UpdateBoundsMode(result);
 end;
 
 function TToolTextureMapping.ToolUp: TRect;

+ 103 - 36
lazpaint/tools/utoolfloodfill.pas

@@ -25,6 +25,7 @@ type
 
   TToolGradient = class(TVectorialTool)
   protected
+    function ShapeClass: TVectorShapeAny; override;
     function CreateShape: TVectorShape; override;
     procedure DrawCustomShape(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     procedure AssignShapeStyle({%H-}AMatrix: TAffineMatrix; {%H-}AAlwaysFit: boolean); override;
@@ -40,13 +41,18 @@ type
 implementation
 
 uses ugraph, LazPaintType, BGRAGradientScanner, LCVectorRectShapes,
-  BGRATransform, UImageDiff, BGRAPen;
+  BGRATransform, UImageDiff, BGRAPen, UScripting, BGRABlend;
 
 { TToolGradient }
 
+function TToolGradient.ShapeClass: TVectorShapeAny;
+begin
+  result := TRectShape;
+end;
+
 function TToolGradient.CreateShape: TVectorShape;
 begin
-  result := TRectShape.Create(nil);
+  result := inherited CreateShape;
   result.PenFill.Clear;
   result.BackFill.SetGradient(TBGRALayerGradientOriginal.Create,true);
   result.Usermode := vsuEditBackFill;
@@ -82,7 +88,8 @@ function TToolGradient.ReplaceLayerAndAddShape(out ARect: TRect): TCustomImageDi
 var
   gradientOrig: TBGRALayerCustomOriginal;
 begin
-  if Manager.Image.CurrentLayerEmpty then
+  if (FShape.BackFill.FillType = vftGradient) and
+    (Manager.Image.CurrentLayerEmpty or FShape.BackFill.Gradient.IsOpaque) then
   begin
     gradientOrig := FShape.BackFill.Gradient.Duplicate;
     result := TReplaceLayerByCustomOriginalDifference.Create(Manager.Image.CurrentState,
@@ -123,47 +130,107 @@ var
   diff: TCustomImageDifference;
   rectShape: TRectShape;
   homogeneous: Boolean;
+  params: TVariableSet;
+  vectOrig: TVectorOriginal;
+  i: Integer;
+  ptOrig: TPointF;
+  newColor: TBGRAPixel;
 begin
+  result := OnlyRenderChange;
   homogeneous := toolDest.Equals(toolDest.GetPixel(0,0));
-  if Manager.Image.SelectionMaskEmpty and homogeneous then
+  if Manager.Image.SelectionMaskEmpty then
   begin
-    CancelAction;
-    if rightBtn then f := Manager.BackFill.Duplicate
-    else f := Manager.ForeFill.Duplicate;
-    try
-      f.ApplyOpacity(Manager.GetPressureB);
-      f.FitGeometry(SuggestGradientBox);
-      case f.FillType of
-      vftGradient: orig := f.Gradient.Duplicate;
-      else
+    if homogeneous then
+    begin
+      CancelAction;
+      if rightBtn then f := Manager.BackFill.Duplicate
+      else f := Manager.ForeFill.Duplicate;
+      try
+        f.ApplyOpacity(Manager.GetPressureB);
+        f.FitGeometry(SuggestGradientBox);
+        case f.FillType of
+        vftGradient: orig := f.Gradient.Duplicate;
+        else
+          begin
+            orig := TVectorOriginal.Create;
+            rectShape := TRectShape.Create(nil);
+            rectShape.QuickDefine(PointF(-0.5,-0.5), PointF(Manager.Image.Width-0.5,Manager.Image.Height-0.5));
+            rectShape.PenStyle := ClearPenStyle;
+            rectShape.BackFill.Assign(f);
+            TVectorOriginal(orig).AddShape(rectShape);
+          end;
+        end;
+        diff := TReplaceLayerByCustomOriginalDifference.Create(Manager.Image.CurrentState,
+                      Manager.Image.CurrentLayerIndex, false, orig);
+        Manager.Image.AddUndo(diff);
+        Manager.Image.ImageMayChangeCompletely;
+      finally
+        f.Free;
+      end;
+      exit;
+    end else
+    if GetCurrentLayerKind = lkVectorial then
+    begin
+      if rightBtn then f := Manager.BackFill else f := Manager.ForeFill;
+      if f.FillType = vftSolid then
+      begin
+        vectOrig := TVectorOriginal(Manager.Image.LayerOriginal[Manager.Image.CurrentLayerIndex]);
+        ptOrig := AffineMatrixInverse(Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex]) * ptF;
+        for i := vectOrig.ShapeCount-1 downto 0 do
         begin
-          orig := TVectorOriginal.Create;
-          rectShape := TRectShape.Create(nil);
-          rectShape.QuickDefine(PointF(-0.5,-0.5), PointF(Manager.Image.Width-0.5,Manager.Image.Height-0.5));
-          rectShape.PenStyle := ClearPenStyle;
-          rectShape.BackFill.Assign(f);
-          TVectorOriginal(orig).AddShape(rectShape);
+          if (vsfPenFill in vectOrig.Shape[i].MultiFields) and
+            vectOrig.Shape[i].PointInPen(ptOrig) then
+          begin
+            if not (vectOrig.Shape[i].PenFill.FillType = vftSolid) then break;
+            CancelAction;
+            Manager.Image.CurrentState.DiscardOriginalDiff:= false;
+            try
+              newColor := vectOrig.Shape[i].PenFill.SolidColor;
+              DrawPixelsInline(@newColor, f.SolidColor, 1);
+              vectOrig.Shape[i].PenFill.SetSolid(newColor);
+            finally
+              Manager.Image.CurrentState.DiscardOriginalDiff:= true;
+            end;
+            exit;
+          end;
+          if (vsfBackFill in vectOrig.Shape[i].MultiFields) and
+            vectOrig.Shape[i].PointInBack(ptOrig) then
+          begin
+            if not (vectOrig.Shape[i].BackFill.FillType = vftSolid) then break;
+            CancelAction;
+            Manager.Image.CurrentState.DiscardOriginalDiff:= false;
+            try
+              newColor := vectOrig.Shape[i].BackFill.SolidColor;
+              DrawPixelsInline(@newColor, f.SolidColor, 1);
+              vectOrig.Shape[i].BackFill.SetSolid(newColor);
+            finally
+              Manager.Image.CurrentState.DiscardOriginalDiff:= true;
+            end;
+            exit;
+          end;
+        end;
+        if (toolDest.GetPixel(pt.X,pt.Y).alpha = 0)
+          and Assigned(Manager.Scripting) then
+        begin
+          CancelAction;
+          params := TVariableSet.Create('ImageFillBackground');
+          params.Pixels['BackColor'] := f.SolidColor;
+          Manager.Scripting.CallScriptFunction(params);
+          params.Free;
+          exit;
         end;
       end;
-      diff := TReplaceLayerByCustomOriginalDifference.Create(Manager.Image.CurrentState,
-                    Manager.Image.CurrentLayerIndex, false, orig);
-      Manager.Image.AddUndo(diff);
-      Manager.Image.ImageMayChangeCompletely;
-    finally
-      f.Free;
     end;
-  end else
-  begin
-    if rightBtn then b := GetBackUniversalBrush
-    else b := GetForeUniversalBrush;
-    if homogeneous then toolDest.Fill(b)
-      else toolDest.FloodFill(pt.X, pt.Y, b,
-                    ffProgressive in Manager.FloodFillOptions, Manager.Tolerance*$101);
-    ReleaseUniversalBrushes;
-    Action.NotifyChange(toolDest, rect(0,0,toolDest.Width,toolDest.Height));
-    ValidateAction;
   end;
-  result := OnlyRenderChange;
+
+  if rightBtn then b := GetBackUniversalBrush
+  else b := GetForeUniversalBrush;
+  if homogeneous then toolDest.Fill(b)
+    else toolDest.FloodFill(pt.X, pt.Y, b,
+                  ffProgressive in Manager.FloodFillOptions, Manager.Tolerance*$101);
+  ReleaseUniversalBrushes;
+  Action.NotifyChange(toolDest, rect(0,0,toolDest.Width,toolDest.Height));
+  ValidateAction;
 end;
 
 function TToolFloodFill.GetContextualToolbars: TContextualToolbars;

+ 26 - 11
lazpaint/tools/utoollayer.pas

@@ -7,7 +7,7 @@ interface
 uses
   Classes, SysUtils, UTool, BGRABitmap, BGRABitmapTypes,
   BGRATransform, BGRALayers, ULayerAction, UImageDiff,
-  UImageType;
+  UImageType, UStateType;
 
 type
   { TToolMoveLayer }
@@ -197,9 +197,14 @@ begin
   begin
     if UseOriginal then
     begin
-      FLayerBounds := Manager.Image.LayerOriginal[idx].GetRenderBounds(
-                        Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
-                        AffineMatrixIdentity);
+      if Manager.Image.LayerOriginal[idx] is TVectorOriginal then
+        FLayerBounds := TVectorOriginal(Manager.Image.LayerOriginal[idx]).GetAlignBounds(
+                          Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
+                          AffineMatrixIdentity)
+      else
+        FLayerBounds := Manager.Image.LayerOriginal[idx].GetRenderBounds(
+                          Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
+                          AffineMatrixIdentity);
       if FLayerBounds.Left = -VeryBigValue then FLayerBounds.Left := 0;
       if FLayerBounds.Top = -VeryBigValue then FLayerBounds.Top := 0;
       if FLayerBounds.Right = VeryBigValue then FLayerBounds.Right := Manager.Image.Width;
@@ -570,17 +575,22 @@ end;
 procedure TToolTransformLayer.ValidateTransform;
 var
   transform: TAffineMatrix;
+  layerIdx: Integer;
+  invTransformDiff: TCustomImageDifference;
+  r: TRect;
 begin
   if FOriginalInit then
   begin
     if Assigned(FBackupLayer) then
     begin
-      transform := Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex];
-      Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] := FInitialOriginalMatrix;
-      Manager.Image.CurrentState.LayeredBitmap.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] := transform;
-      Manager.Image.CurrentState.LayeredBitmap.RenderLayerFromOriginal(Manager.Image.CurrentLayerIndex);
+      layerIdx := Manager.Image.CurrentLayerIndex;
+      transform := Manager.Image.LayerOriginalMatrix[layerIdx];
+      invTransformDiff := Manager.Image.CurrentState.ComputeLayerMatrixDifference(layerIdx,
+                          transform, FInitialOriginalMatrix);
       FBackupLayer.nextMatrix := transform;
+      Manager.Image.AddUndo(invTransformDiff);
       Manager.Image.AddUndo(FBackupLayer);
+      Manager.Image.CurrentState.LayeredBitmap.RenderLayerFromOriginalIfNecessary(layerIdx, false, r);
       FBackupLayer := nil;
     end;
     FOriginalInit := false;
@@ -717,9 +727,14 @@ begin
   begin
     if Manager.Image.LayerOriginalDefined[idx] then
     begin
-      FOriginalBounds := Manager.Image.LayerOriginal[idx].GetRenderBounds(
-                        Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
-                        AffineMatrixIdentity);
+      if Manager.Image.LayerOriginal[idx] is TVectorOriginal then
+        FOriginalBounds := TVectorOriginal(Manager.Image.LayerOriginal[idx]).GetAlignBounds(
+                          Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
+                          AffineMatrixIdentity)
+      else
+        FOriginalBounds := Manager.Image.LayerOriginal[idx].GetRenderBounds(
+                          Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
+                          AffineMatrixIdentity);
       if FOriginalBounds.Left = -VeryBigValue then FOriginalBounds.Left := 0;
       if FOriginalBounds.Top = -VeryBigValue then FOriginalBounds.Top := 0;
       if FOriginalBounds.Right = VeryBigValue then FOriginalBounds.Right := Manager.Image.Width;

+ 3 - 3
lazpaint/tools/utoolphong.pas

@@ -16,7 +16,7 @@ type
     FMatrix: TAffineMatrix;
     procedure ShapeChange({%H-}ASender: TObject; ABounds: TRectF; ADiff: TVectorShapeDiff); override;
     procedure AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean); override;
-    function CreateShape: TVectorShape; override;
+    function ShapeClass: TVectorShapeAny; override;
   public
     constructor Create(AManager: TToolManager); override;
     function GetContextualToolbars: TContextualToolbars; override;
@@ -61,9 +61,9 @@ begin
   end;
 end;
 
-function TToolPhong.CreateShape: TVectorShape;
+function TToolPhong.ShapeClass: TVectorShapeAny;
 begin
-  result := TPhongShape.Create(nil);
+  result := TPhongShape;
 end;
 
 initialization

+ 19 - 34
lazpaint/tools/utoolpolygon.pas

@@ -16,19 +16,15 @@ type
 
   TToolRectangle = class(TVectorialTool)
   protected
-    function CreateShape: TVectorShape; override;
-  public
-    function GetContextualToolbars: TContextualToolbars; override;
+    function ShapeClass: TVectorShapeAny; override;
   end;
 
   { TToolEllipse }
 
   TToolEllipse = class(TVectorialTool)
   protected
-    function CreateShape: TVectorShape; override;
+    function ShapeClass: TVectorShapeAny; override;
     function GetGridMatrix: TAffineMatrix; override;
-  public
-    function GetContextualToolbars: TContextualToolbars; override;
   end;
 
   { TToolPolygon }
@@ -36,6 +32,7 @@ type
   TToolPolygon = class(TVectorialTool)
   protected
     initiallyClosed : boolean;
+    function ShapeClass: TVectorShapeAny; override;
     function CreateShape: TVectorShape; override;
     function ShouldCloseShape: boolean; virtual;
     procedure UpdateManagerCloseShape({%H-}AClose: boolean); virtual;
@@ -47,7 +44,6 @@ type
   public
     function ToolUp: TRect; override;
     function ToolKeyPress(var key: TUTF8Char): TRect; override;
-    function GetContextualToolbars: TContextualToolbars; override;
   end;
 
   { TToolPolyline }
@@ -73,13 +69,13 @@ type
     function GetCurrentMode: TToolSplineMode;
     procedure SetCurrentMode(AValue: TToolSplineMode);
   protected
+    function ShapeClass: TVectorShapeAny; override;
     function CreateShape: TVectorShape; override;
     procedure AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean); override;
     procedure UpdateUserMode; override;
   public
     constructor Create(AManager: TToolManager); override;
     function ToolKeyPress(var key: TUTF8Char): TRect; override;
-    function GetContextualToolbars: TContextualToolbars; override;
     property CurrentMode: TToolSplineMode read GetCurrentMode write SetCurrentMode;
   end;
 
@@ -166,9 +162,9 @@ end;
 
 { TToolEllipse }
 
-function TToolEllipse.CreateShape: TVectorShape;
+function TToolEllipse.ShapeClass: TVectorShapeAny;
 begin
-  result := TEllipseShape.Create(nil);
+  result := TEllipseShape;
 end;
 
 function TToolEllipse.GetGridMatrix: TAffineMatrix;
@@ -176,21 +172,11 @@ begin
   Result:= AffineMatrixScale(0.5, 0.5);
 end;
 
-function TToolEllipse.GetContextualToolbars: TContextualToolbars;
-begin
-  Result:= [ctPenFill,ctBackFill,ctShape,ctPenWidth,ctPenStyle];
-end;
-
 { TToolRectangle }
 
-function TToolRectangle.CreateShape: TVectorShape;
-begin
-  result := TRectShape.Create(nil);
-end;
-
-function TToolRectangle.GetContextualToolbars: TContextualToolbars;
+function TToolRectangle.ShapeClass: TVectorShapeAny;
 begin
-  Result:= [ctPenFill,ctBackFill,ctShape,ctPenWidth,ctPenStyle,ctJoinStyle];
+  result := TRectShape;
 end;
 
 { TToolSpline }
@@ -209,6 +195,11 @@ begin
   UpdateUserMode;
 end;
 
+function TToolSpline.ShapeClass: TVectorShapeAny;
+begin
+  result := TCurveShape;
+end;
+
 procedure TToolSpline.UpdateUserMode;
 var
   c: TCurveShape;
@@ -233,8 +224,7 @@ end;
 
 function TToolSpline.CreateShape: TVectorShape;
 begin
-  result := TCurveShape.Create(nil);
-  result.Usermode := vsuCreate;
+  result := inherited CreateShape;
   TCurveShape(result).CosineAngle:= EasyBezierMinimumDotProduct;
   if not FCurveModeHintShown then
   begin
@@ -269,16 +259,16 @@ begin
   end;
 end;
 
-function TToolSpline.GetContextualToolbars: TContextualToolbars;
+{ TToolPolygon }
+
+function TToolPolygon.ShapeClass: TVectorShapeAny;
 begin
-  Result:= [ctPenFill,ctBackFill,ctShape,ctCloseShape,ctPenWidth,ctPenStyle,ctLineCap,ctSplineStyle];
+  result := TPolylineShape;
 end;
 
-{ TToolPolygon }
-
 function TToolPolygon.CreateShape: TVectorShape;
 begin
-  result := TPolylineShape.Create(nil);
+  result := inherited CreateShape;
   initiallyClosed := ShouldCloseShape;
 end;
 
@@ -366,11 +356,6 @@ begin
     result := ptF;
 end;
 
-function TToolPolygon.GetContextualToolbars: TContextualToolbars;
-begin
-  Result:= [ctPenFill,ctBackFill,ctShape,ctCloseShape,ctPenWidth,ctPenStyle,ctJoinStyle,ctLineCap];
-end;
-
 initialization
 
   RegisterTool(ptRect,TToolRectangle);

+ 7 - 7
lazpaint/tools/utoolselect.pas

@@ -29,7 +29,7 @@ type
 
   TToolSelectRect = class(TVectorialSelectTool)
   protected
-    function CreateShape: TVectorShape; override;
+    function ShapeClass: TVectorShapeAny; override;
   public
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
     function GetContextualToolbars: TContextualToolbars; override;
@@ -39,7 +39,7 @@ type
 
   TToolSelectEllipse = class(TVectorialSelectTool)
   protected
-    function CreateShape: TVectorShape; override;
+    function ShapeClass: TVectorShapeAny; override;
     function GetGridMatrix: TAffineMatrix; override;
   public
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
@@ -149,7 +149,7 @@ procedure AssignSelectShapeStyle(AShape: TVectorShape; ASwapColor: boolean);
 var
   f: TVectorShapeFields;
 begin
-  f:= AShape.Fields;
+  f:= AShape.MultiFields;
   if vsfPenFill in f then AShape.PenFill.Clear;
   if vsfPenStyle in f Then AShape.PenStyle := ClearPenStyle;
   if vsfBackFill in f then
@@ -266,9 +266,9 @@ end;
 
 { TToolSelectRect }
 
-function TToolSelectRect.CreateShape: TVectorShape;
+function TToolSelectRect.ShapeClass: TVectorShapeAny;
 begin
-  result := TRectShape.Create(nil);
+  result := TRectShape;
 end;
 
 function TToolSelectRect.Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth,
@@ -309,9 +309,9 @@ end;
 
 { TToolSelectEllipse }
 
-function TToolSelectEllipse.CreateShape: TVectorShape;
+function TToolSelectEllipse.ShapeClass: TVectorShapeAny;
 begin
-  result := TEllipseShape.Create(nil);
+  result := TEllipseShape;
 end;
 
 function TToolSelectEllipse.GetGridMatrix: TAffineMatrix;

+ 41 - 77
lazpaint/tools/utooltext.pas

@@ -18,7 +18,7 @@ type
     FPrevShadow: boolean;
     FPrevShadowOffset: TPoint;
     FPrevShadowRadius: single;
-    function CreateShape: TVectorShape; override;
+    function ShapeClass: TVectorShapeAny; override;
     function AlwaysRasterizeShape: boolean; override;
     procedure IncludeShadowBounds(var ARect: TRect);
     function GetCustomShapeBounds(ADestBounds: TRect; AMatrix: TAffineMatrix; ADraft: boolean): TRect; override;
@@ -28,10 +28,6 @@ type
     procedure AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean); override;
     procedure QuickDefineEnd; override;
     function RoundCoordinate(constref ptF: TPointF): TPointF; override;
-    function ForeGradTexMode: TVectorShapeUsermode; override;
-    function BackGradTexMode: TVectorShapeUsermode; override;
-    function ShapeForeFill: TVectorialFill; override;
-    function ShapeBackFill: TVectorialFill; override;
     function DoToolKeyDown(var key: Word): TRect; override;
   public
     constructor Create(AManager: TToolManager); override;
@@ -47,9 +43,9 @@ uses LCVectorTextShapes, BGRALayerOriginal, BGRATransform, BGRAGrayscaleMask,
 
 { TToolText }
 
-function TToolText.CreateShape: TVectorShape;
+function TToolText.ShapeClass: TVectorShapeAny;
 begin
-  result := TTextShape.Create(nil);
+  result := TTextShape;
 end;
 
 function TToolText.AlwaysRasterizeShape: boolean;
@@ -150,9 +146,8 @@ var
   r: TRect;
   toolDest: TBGRABitmap;
   zoom: Single;
-  gradBox: TAffineBox;
-  fitMode: TFitMode;
 begin
+  inherited AssignShapeStyle(AMatrix, AAlwaysFit);
   FMatrix := AMatrix;
   with TTextShape(FShape) do
   begin
@@ -160,27 +155,7 @@ begin
     FontEmHeight:= zoom*Manager.TextFontSize*Manager.Image.DPI/72;
     FontName:= Manager.TextFontName;
     FontStyle:= Manager.TextFontStyle;
-    gradBox := self.SuggestGradientBox;
-
-    if AAlwaysFit then fitMode := fmAlways else fitMode := ForeFitMode;
-    if FSwapColor then
-      AssignFill(FShape.PenFill, Manager.BackFill, gradBox, fitMode)
-    else
-      AssignFill(FShape.PenFill, Manager.ForeFill, gradBox, fitMode);
-
-    if Manager.TextOutline and (Manager.TextOutlineWidth>0) and
-       (Manager.BackColor.alpha > 0) then
-    begin
-      if AAlwaysFit then fitMode := fmAlways else fitMode := BackFitMode;
-      if FSwapColor then
-        AssignFill(FShape.OutlineFill, Manager.ForeFill, gradBox, fitMode)
-      else
-        AssignFill(FShape.OutlineFill, Manager.BackFill, gradBox, fitMode);
-      OutlineWidth := Manager.TextOutlineWidth;
-    end
-    else
-      OutlineFill.Clear;
-
+    Aliased := Manager.ShapeOptionAliasing;
     LightPosition := AMatrix*Manager.LightPosition;
     AltitudePercent:= Manager.PhongShapeAltitude;
     ParagraphAlignment:= Manager.TextAlign;
@@ -206,38 +181,6 @@ begin
   result := PointF(floor(ptF.x)+0.5,floor(ptF.y)+0.5);
 end;
 
-function TToolText.ForeGradTexMode: TVectorShapeUsermode;
-begin
-  if FSwapColor then result := vsuEditOutlineFill else
-    result := vsuEditPenFill;
-end;
-
-function TToolText.BackGradTexMode: TVectorShapeUsermode;
-begin
-  if FSwapColor then result := vsuEditPenFill else
-    result := vsuEditOutlineFill;
-end;
-
-function TToolText.ShapeForeFill: TVectorialFill;
-begin
-  if Assigned(FShape) then
-  begin
-    if FSwapColor then result := FShape.OutlineFill else
-      result := FShape.PenFill;
-  end else
-    result := nil;
-end;
-
-function TToolText.ShapeBackFill: TVectorialFill;
-begin
-  if Assigned(FShape) then
-  begin
-    if FSwapColor then result := FShape.PenFill else
-      result := FShape.OutlineFill;
-  end else
-    result := nil;
-end;
-
 constructor TToolText.Create(AManager: TToolManager);
 begin
   inherited Create(AManager);
@@ -246,7 +189,7 @@ end;
 
 function TToolText.GetContextualToolbars: TContextualToolbars;
 begin
-  Result:= [ctPenFill,ctBackFill,ctText];
+  Result:= [ctPenFill,ctText,ctOutlineFill,ctOutlineWidth,ctAliasing];
   if Manager.TextPhong then include(result, ctAltitude);
 end;
 
@@ -263,38 +206,59 @@ begin
   end else
   if (Key = VK_ESCAPE) and Assigned(FShape) then
   begin
-    result := ValidateShape;
+    if FShape.Usermode = vsuEditText then
+      FShape.Usermode := vsuEdit
+    else
+      result := ValidateShape;
     Key := 0;
   end else
   if (Key = VK_RETURN) and Assigned(FShape) then
   begin
     handled := false;
     FShape.KeyDown(ShiftState, skReturn, handled);
-    if handled then Key := 0;
+    if not handled then ValidateShape;
+    Key := 0;
   end else
     Result:=inherited DoToolKeyDown(key);
 end;
 
 function TToolText.ToolCommand(ACommand: TToolCommand): boolean;
 begin
-  case ACommand of
-  tcCopy: Result:= Assigned(FShape) and TTextShape(FShape).CopySelection;
-  tcCut: Result:= Assigned(FShape) and TTextShape(FShape).CutSelection;
-  tcPaste: Result:= Assigned(FShape) and TTextShape(FShape).PasteSelection;
-  tcDelete: Result:= Assigned(FShape) and TTextShape(FShape).DeleteSelection;
+  if Assigned(FShape) and (FShape.Usermode = vsuEditText) then
+    case ACommand of
+    tcCopy: Result:= TTextShape(FShape).CopySelection;
+    tcCut: Result:= TTextShape(FShape).CutSelection;
+    tcPaste: Result:= TTextShape(FShape).PasteSelection;
+    tcDelete: Result:= TTextShape(FShape).DeleteSelection;
+    else
+      result := inherited ToolCommand(ACommand);
+    end
   else
-    result := inherited ToolCommand(ACommand);
-  end;
+    case ACommand of
+    tcDelete:
+      if Assigned(FShape) then
+      begin
+        CancelShape;
+        result := true;
+      end else result := false;
+    else result := inherited ToolCommand(ACommand);
+    end;
 end;
 
 function TToolText.ToolProvideCommand(ACommand: TToolCommand): boolean;
 begin
-  case ACommand of
-  tcCopy,tcCut,tcDelete: result := Assigned(FShape) and TTextShape(FShape).HasSelection;
-  tcPaste: result := Assigned(FShape);
+  if Assigned(FShape) and (FShape.Usermode = vsuEditText) then
+    case ACommand of
+    tcCopy,tcCut,tcDelete: result := TTextShape(FShape).HasSelection;
+    tcPaste: result := true;
+    else
+      result := inherited ToolProvideCommand(ACommand);
+    end
   else
-    result := inherited ToolProvideCommand(ACommand);
-  end;
+    case ACommand of
+    tcDelete: result := Assigned(FShape);
+    else result := inherited ToolProvideCommand(ACommand);
+    end;
 end;
 
 initialization

+ 258 - 156
lazpaint/tools/utoolvectorial.pas

@@ -7,7 +7,7 @@ interface
 uses
   Classes, SysUtils, LCLType, BGRABitmap, BGRABitmapTypes,
   BGRALayerOriginal, BGRAGraphics, LCVectorOriginal, LCVectorialFill,
-  UTool, UImageType, ULayerAction, LCVectorRectShapes,
+  UTool, UImageType, ULayerAction, LCVectorRectShapes, LCVectorMultishape,
   BGRAGradientOriginal, UStateType;
 
 type
@@ -40,7 +40,8 @@ type
     FLastShapeTransform: TAffineMatrix;
     FUseOriginal: boolean;
     function AlwaysRasterizeShape: boolean; virtual;
-    function CreateShape: TVectorShape; virtual; abstract;
+    function CreateShape: TVectorShape; virtual;
+    function ShapeClass: TVectorShapeAny; virtual; abstract;
     function UseOriginal: boolean; virtual;
     function HasBrush: boolean; virtual;
     function GetCustomShapeBounds(ADestBounds: TRect; AMatrix: TAffineMatrix; {%H-}ADraft: boolean): TRect; virtual;
@@ -72,12 +73,16 @@ type
     procedure ShapeValidated; virtual;
     function ForeGradTexMode: TVectorShapeUsermode; virtual;
     function BackGradTexMode: TVectorShapeUsermode; virtual;
-    function ShapeForeFill: TVectorialFill; virtual;
-    function ShapeBackFill: TVectorialFill; virtual;
+    function OutlineGradTexMode: TVectorShapeUsermode; virtual;
     function ForeFitMode: TFitMode;
     function BackFitMode: TFitMode;
+    function OutlineFitMode: TFitMode;
+    function ManagerForeFill: TVectorialFill;
+    function ManagerBackFill: TVectorialFill;
+    function ManagerOutlineFill: TVectorialFill;
     function GetIsForeEditGradTexPoints: boolean; override;
     function GetIsBackEditGradTexPoints: boolean; override;
+    function GetIsOutlineEditGradTexPoints: boolean; override;
     function GetGridMatrix: TAffineMatrix; virtual;
     property Editor: TBGRAOriginalEditor read GetEditor;
   public
@@ -89,6 +94,7 @@ type
     function ToolCommand(ACommand: TToolCommand): boolean; override;
     function ToolProvideCommand(ACommand: TToolCommand): boolean; override;
     function SuggestGradientBox: TAffineBox; override;
+    function GetContextualToolbars: TContextualToolbars; override;
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
     property IsIdle: boolean read GetIsIdle;
     property IsHandDrawing: boolean read GetIsHandDrawing;
@@ -145,12 +151,13 @@ type
     function InvalidEditMode: boolean;
     function ForeGradTexMode: TVectorShapeUsermode; virtual;
     function BackGradTexMode: TVectorShapeUsermode; virtual;
-    function ShapeForeFill: TVectorialFill; virtual;
-    function ShapeBackFill: TVectorialFill; virtual;
+    function OutlineGradTexMode: TVectorShapeUsermode; virtual;
     function ForeFitMode: TFitMode;
     function BackFitMode: TFitMode;
+    function OutlineFitMode: TFitMode;
     function GetIsForeEditGradTexPoints: boolean; override;
     function GetIsBackEditGradTexPoints: boolean; override;
+    function GetIsOutlineEditGradTexPoints: boolean; override;
     function GetAllowedBackFillTypes: TVectorialFillTypes; override;
     function GetStatusText: string; override;
   public
@@ -220,6 +227,35 @@ begin
   end;
 end;
 
+function ContextualToolbarsFromShape(AShapeClass: TVectorShapeAny; AShape: TVectorShape): TContextualToolbars;
+var
+  f: TVectorShapeFields;
+begin
+  result:= [ctPenFill, ctBackFill];
+  if Assigned(AShape) then
+    f := AShape.MultiFields
+    else f := AShapeClass.Fields;
+  if vsfPenWidth in f then result += [ctPenWidth];
+  if vsfPenStyle in f then result += [ctPenStyle];
+  if vsfJoinStyle in f then result += [ctJoinStyle];
+  if [vsfPenStyle,vsfPenFill,vsfBackFill] <= f then result += [ctShape];
+  if vsfOutlineFill in f then
+  begin
+    result += [ctOutlineFill];
+    if not (vsfBackFill in f) then result -= [ctBackFill];
+  end;
+  if vsfOutlineWidth in f then result += [ctOutlineWidth];
+
+  if AShapeClass = TCurveShape then result := result + [ctShape,ctCloseShape,ctLineCap,ctSplineStyle]
+  else if AShapeClass = TPolylineShape then result := result + [ctShape,ctCloseShape,ctLineCap]
+  else if AShapeClass = TPhongShape then result := result + [ctPhong,ctAltitude]
+  else if AShapeClass = TTextShape then
+  begin
+    result := result + [ctText,ctAliasing];
+    if TTextShape(AShape).PenPhong then include(result, ctAltitude);
+  end;
+end;
+
 procedure AlignShape(AShape: TVectorShape; ACommand: TToolCommand; const AMatrix: TAffineMatrix; const ARect: TRect);
 begin
   case ACommand of
@@ -376,45 +412,35 @@ var
   zoom: single;
   m: TAffineMatrix;
   doFill, doDraw: Boolean;
+  f: TVectorShapeFields;
 begin
   m := Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex];
   zoom := (VectLen(m[1,1],m[2,1])+VectLen(m[1,2],m[2,2]))/2;
   if AShape.Usermode in [vsuEditBackFill, vsuEditPenFill] then
     AShape.Usermode := vsuEdit;
   opt := Manager.ShapeOptions;
-  if AShape.Fields*[vsfPenFill,vsfBackFill,vsfPenStyle] = [vsfPenFill,vsfBackFill,vsfPenStyle] then
-  begin
-    if AShape.BackFill.FillType = vftNone then
-    begin;
-      exclude(opt,toFillShape);
-      doFill := false;
-    end
-    else
-    begin
-      include(opt,toFillShape);
-      doFill := true;
-    end;
-    ps := BGRAToPenStyle(AShape.PenStyle);
-    if (ps = psClear) or (AShape.PenFill.FillType = vftNone) then
-    begin
-      exclude(opt,toDrawShape);
-      doDraw := false;
-    end
-    else
-    begin
-      include(opt,toDrawShape);
-      Manager.PenStyle := ps;
-      doDraw := true;
-    end;
-  end else
+  f := AShape.MultiFields;
+  doDraw := vsfPenFill in f;
+  doFill := vsfBackFill in f;
+  if vsfPenStyle in f then
   begin
-    doDraw := vsfPenFill in AShape.Fields;
-    doFill := vsfBackFill in AShape.Fields;
+    doDraw := AShape.PenVisible;
+    if doDraw then doFill := AShape.BackVisible;
+
+    if not doFill then
+      exclude(opt,toFillShape)
+      else include(opt,toFillShape);
+    if not doDraw then
+      exclude(opt,toDrawShape)
+      else
+      begin
+        include(opt,toDrawShape);
+        Manager.PenStyle := ps;
+      end;
   end;
-
   if doDraw then
   begin
-    if AShape.PenFill.FillType = vftNone then
+    if not AShape.PenVisible then
       Manager.ForeColor := BGRA(Manager.ForeColor.red,
         Manager.ForeColor.green,Manager.ForeColor.blue,0)
     else
@@ -422,17 +448,23 @@ begin
   end;
   if doFill then
   begin
-    if AShape.BackFill.FillType = vftNone then
+    if not AShape.BackVisible then
       Manager.BackColor := BGRA(Manager.BackColor.red,
         Manager.BackColor.green,Manager.BackColor.blue,0)
     else
       Manager.BackFill.Assign(AShape.BackFill);
   end;
+  if not AShape.OutlineVisible then
+    Manager.SetTextOutline(false, Manager.TextOutlineWidth) else
+  begin
+    Manager.SetTextOutline(true, AShape.OutlineWidth*zoom);
+    Manager.OutlineFill.Assign(AShape.OutlineFill);
+  end;
 
   if toDrawShape in opt then
   begin
-    if vsfPenWidth in AShape.Fields then Manager.PenWidth := AShape.PenWidth*zoom;
-    if vsfJoinStyle in AShape.Fields then Manager.JoinStyle:= AShape.JoinStyle;
+    if vsfPenWidth in f then Manager.PenWidth := AShape.PenWidth*zoom;
+    if vsfJoinStyle in f then Manager.JoinStyle:= AShape.JoinStyle;
     if AShape is TCustomPolypointShape then
     begin
       if TCustomPolypointShape(AShape).Closed then
@@ -447,7 +479,6 @@ begin
     if AShape is TCurveShape then
       Manager.SplineStyle := TCurveShape(AShape).SplineStyle;
   end;
-  Manager.ShapeOptions := opt;
 
   if AShape is TTextShape then
   with TTextShape(AShape) do
@@ -456,15 +487,13 @@ begin
     Manager.LightPosition := m*LightPosition;
     Manager.PhongShapeAltitude := round(AltitudePercent);
     Manager.TextAlign:= ParagraphAlignment;
-    Manager.SetTextFont(FontName,round(FontEmHeight*zoom*72/Manager.Image.DPI),FontStyle);
+    Manager.SetTextFont(FontName, FontEmHeight*zoom*72/Manager.Image.DPI, FontStyle);
     Manager.TextShadow:= false;
-    if OutlineFill.FillType = vftNone then
-      Manager.SetTextOutline(false, Manager.TextOutlineWidth) else
-    begin
-      Manager.SetTextOutline(true, OutlineWidth);
-      Manager.BackFill.Assign(OutlineFill);
-    end;
+    if Aliased then
+      include(opt,toAliasing)
+      else exclude(opt,toAliasing);
   end;
+  Manager.ShapeOptions := opt;
 
   if AShape is TPhongShape then
   with TPhongShape(AShape) do
@@ -603,52 +632,64 @@ var
   m: TAffineMatrix;
   zoom: Single;
   gradBox: TAffineBox;
+  f: TVectorShapeFields;
+  shape: TVectorShape;
 begin
+  shape := nil;
   case GetEditMode of
   esmShape:
-    with GetVectorOriginal do
     try
       BindOriginalEvent(true);
-      gradBox := SelectedShape.SuggestGradientBox(AffineMatrixIdentity);
+      shape := GetVectorOriginal.SelectedShape;
+      shape.BeginUpdate;
+      gradBox := shape.SuggestGradientBox(AffineMatrixIdentity);
       m := AffineMatrixInverse(Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex]);
       zoom := (VectLen(m[1,1],m[2,1])+VectLen(m[1,2],m[2,2]))/2;
-      if SelectedShape.Fields*[vsfPenFill,vsfBackFill,vsfPenStyle] = [vsfPenFill,vsfBackFill,vsfPenStyle] then
+      f := shape.MultiFields;
+      if f*[vsfPenFill,vsfBackFill,vsfPenStyle] = [vsfPenFill,vsfBackFill,vsfPenStyle] then
       begin
         doDraw := toDrawShape in Manager.ShapeOptions;
         doFill := toFillShape in Manager.ShapeOptions;
 
         if doDraw then
-          SelectedShape.PenStyle := PenStyleToBGRA(Manager.PenStyle)
+          shape.PenStyle := PenStyleToBGRA(Manager.PenStyle)
         else
-          SelectedShape.PenStyle := ClearPenStyle;
+          shape.PenStyle := ClearPenStyle;
 
-        if vsfPenWidth in SelectedShape.Fields then SelectedShape.PenWidth := Manager.PenWidth*zoom;
-        if vsfJoinStyle in SelectedShape.Fields then SelectedShape.JoinStyle := Manager.JoinStyle;
-        if SelectedShape is TCustomPolypointShape then
+        if doDraw and (vsfPenWidth in f) then shape.PenWidth := Manager.PenWidth*zoom;
+        if doDraw and (vsfJoinStyle in f) then shape.JoinStyle := Manager.JoinStyle;
+        if shape is TCustomPolypointShape then
         begin
-          TCustomPolypointShape(SelectedShape).Closed := toCloseShape in Manager.ShapeOptions;
-          if not TCustomPolypointShape(SelectedShape).Closed then
+          TCustomPolypointShape(shape).Closed := toCloseShape in Manager.ShapeOptions;
+          if not TCustomPolypointShape(shape).Closed then
           begin
-            TCustomPolypointShape(SelectedShape).LineCap:= Manager.LineCap;
-            TCustomPolypointShape(SelectedShape).ArrowSize:= Manager.ArrowSize;
-            TCustomPolypointShape(SelectedShape).ArrowStartKind:= Manager.ArrowStart;
-            TCustomPolypointShape(SelectedShape).ArrowEndKind:= Manager.ArrowEnd;
+            TCustomPolypointShape(shape).LineCap:= Manager.LineCap;
+            TCustomPolypointShape(shape).ArrowSize:= Manager.ArrowSize;
+            TCustomPolypointShape(shape).ArrowStartKind:= Manager.ArrowStart;
+            TCustomPolypointShape(shape).ArrowEndKind:= Manager.ArrowEnd;
           end;
         end;
-        if SelectedShape is TCurveShape then
-          TCurveShape(SelectedShape).SplineStyle:= Manager.SplineStyle;
+        if shape is TCurveShape then
+          TCurveShape(shape).SplineStyle:= Manager.SplineStyle;
       end else
       begin
-        doDraw := vsfPenFill in SelectedShape.Fields;
-        doFill := vsfBackFill in SelectedShape.Fields;
+        doDraw := vsfPenFill in f;
+        doFill := vsfBackFill in f;
+      end;
+      if doFill then AssignFill(shape.BackFill, Manager.BackFill, gradBox, BackFitMode)
+      else if vsfBackFill in f then
+          shape.BackFill.Clear;
+      if doDraw then AssignFill(shape.PenFill, Manager.ForeFill, gradBox, ForeFitMode);
+      if (vsfOutlineWidth in f) and Manager.TextOutline then shape.OutlineWidth := Manager.TextOutlineWidth*zoom;
+      if vsfOutlineFill in f then
+      begin
+        if Manager.TextOutline then
+          AssignFill(shape.OutLineFill, Manager.OutLineFill, gradBox, OutlineFitMode)
+          else shape.OutlineFill.Clear;
       end;
-      if doFill then AssignFill(SelectedShape.BackFill, Manager.BackFill, gradBox, BackFitMode)
-      else if vsfBackFill in SelectedShape.Fields then
-          SelectedShape.BackFill.Clear;
-      if doDraw then AssignFill(SelectedShape.PenFill, Manager.ForeFill, gradBox, ForeFitMode);
 
-      if SelectedShape is TTextShape then
-      with TTextShape(SelectedShape) do
+      if shape is TTextShape then
+      with TTextShape(shape) do
       begin
         PenPhong := Manager.TextPhong;
         LightPosition := m*Manager.LightPosition;
@@ -657,15 +698,10 @@ begin
         FontName:= Manager.TextFontName;
         FontEmHeight:= Manager.TextFontSize*zoom*Manager.Image.DPI/72;
         FontStyle := Manager.TextFontStyle;
-        if Manager.TextOutline then
-        begin
-          OutlineWidth := Manager.TextOutlineWidth;
-          AssignFill(OutLineFill, Manager.BackFill, gradBox, BackFitMode);
-        end else
-          OutlineFill.Clear;
+        Aliased := Manager.ShapeOptionAliasing;
       end;
-      if SelectedShape is TPhongShape then
-      with TPhongShape(SelectedShape) do
+      if shape is TPhongShape then
+      with TPhongShape(shape) do
       begin
         ShapeKind := Manager.PhongShapeKind;
         LightPosition := Manager.LightPosition;
@@ -673,6 +709,7 @@ begin
         BorderSizePercent := Manager.PhongShapeBorderSize;
       end;
     finally
+      if Assigned(shape) then shape.EndUpdate;
       BindOriginalEvent(false);
     end;
   esmGradient:
@@ -721,7 +758,7 @@ begin
     Manager.Image.LayerMayChange(GetToolDrawingLayer,r);
   end;
   case GetEditMode of
-  esmShape: GetVectorOriginal.DeselectShape;
+  esmShape: GetVectorOriginal.DeselectShapes;
   esmGradient: FIsEditingGradient:= false;
   esmOtherOriginal: FreeAndNil(FOriginalRect);
   esmSelection: FreeAndNil(FSelectionRect);
@@ -862,7 +899,10 @@ end;
 
 destructor TEditShapeTool.Destroy;
 begin
-  StopEdit(False, False);
+  FreeAndNil(FOriginalRect);
+  FreeAndNil(FSelectionRect);
+  Manager.Image.CurrentState.LayeredBitmap.ClearEditor;
+  FreeAndNil(FRectEditor);
   inherited Destroy;
 end;
 
@@ -870,23 +910,15 @@ function TEditShapeTool.GetContextualToolbars: TContextualToolbars;
 var
   shape: TVectorShape;
 begin
-  Result:= [ctPenFill, ctBackFill];
   case GetEditMode of
   esmShape:
     begin
       shape := GetVectorOriginal.SelectedShape;
-      if shape is TRectShape then result := result + [ctShape,ctPenWidth,ctPenStyle,ctJoinStyle]
-      else if shape is TEllipseShape then result := result + [ctShape,ctPenWidth,ctPenStyle]
-      else if shape is TCurveShape then result := result + [ctShape,ctCloseShape,ctPenWidth,ctPenStyle,ctLineCap,ctSplineStyle]
-      else if shape is TPolylineShape then result := result + [ctShape,ctCloseShape,ctPenWidth,ctPenStyle,ctJoinStyle,ctLineCap]
-      else if shape is TPhongShape then result := result + [ctPhong,ctAltitude]
-      else if shape is TTextShape then
-      begin
-        result := result + [ctText];
-        if TTextShape(shape).PenPhong then include(result, ctAltitude);
-      end;
+      result := ContextualToolbarsFromShape(TVectorShapeAny(shape.ClassType), shape);
     end;
   esmGradient: result := [ctBackFill];
+  else
+    Result:= [ctPenFill, ctBackFill];
   end;
 end;
 
@@ -1006,7 +1038,7 @@ begin
     shapeAfter := TCurveShape.CreateFrom(orig, shapeBefore);
     shapeAfter.JoinStyle := pjsRound;
     orig.ReplaceShape(orig.IndexOfShape(shapeBefore), shapeAfter);
-    orig.SelectShape(shapeAfter);
+    orig.SelectShape(shapeAfter, False);
     result := true;
   end else
     result := false;
@@ -1046,28 +1078,12 @@ end;
 
 function TEditShapeTool.BackGradTexMode: TVectorShapeUsermode;
 begin
-  if (GetEditMode = esmShape) and (GetVectorOriginal.SelectedShape is TTextShape) then
-    result := vsuEditOutlineFill
-  else
-    result := vsuEditBackFill;
+  result := vsuEditBackFill;
 end;
 
-function TEditShapeTool.ShapeForeFill: TVectorialFill;
+function TEditShapeTool.OutlineGradTexMode: TVectorShapeUsermode;
 begin
-  if GetEditMode = esmShape then result := GetVectorOriginal.SelectedShape.PenFill
-  else result := nil;
-end;
-
-function TEditShapeTool.ShapeBackFill: TVectorialFill;
-begin
-  if GetEditMode = esmShape then
-  begin
-    if GetVectorOriginal.SelectedShape is TTextShape then
-      result := GetVectorOriginal.SelectedShape.OutlineFill
-    else
-      result := GetVectorOriginal.SelectedShape.BackFill;
-  end
-  else result := nil;
+  result := vsuEditOutlineFill;
 end;
 
 function TEditShapeTool.ForeFitMode: TFitMode;
@@ -1082,6 +1098,12 @@ begin
   else result := fmIfChange;
 end;
 
+function TEditShapeTool.OutlineFitMode: TFitMode;
+begin
+  if IsOutlineEditGradTexPoints then result := fmNever
+  else result := fmIfChange;
+end;
+
 function TEditShapeTool.GetIsForeEditGradTexPoints: boolean;
 begin
   result := (GetEditMode = esmShape) and (GetVectorOriginal.SelectedShape.Usermode = ForeGradTexMode);
@@ -1092,6 +1114,11 @@ begin
   result := (GetEditMode = esmShape) and (GetVectorOriginal.SelectedShape.Usermode = BackGradTexMode);
 end;
 
+function TEditShapeTool.GetIsOutlineEditGradTexPoints: boolean;
+begin
+  result := (GetEditMode = esmShape) and (GetVectorOriginal.SelectedShape.Usermode = OutlineGradTexMode);
+end;
+
 function TEditShapeTool.GetAllowedBackFillTypes: TVectorialFillTypes;
 begin
   if GetEditMode = esmGradient then
@@ -1130,7 +1157,7 @@ begin
   if not Manager.Image.SelectionMaskEmpty then
   begin
     if (GetCurrentLayerKind = lkVectorial) and Assigned(GetVectorOriginal.SelectedShape) then
-      GetVectorOriginal.DeselectShape;
+      GetVectorOriginal.DeselectShapes;
     DoEditSelection;
   end else
   if (GetCurrentLayerKind = lkVectorial) and Assigned(GetVectorOriginal.SelectedShape) then
@@ -1161,6 +1188,14 @@ begin
           begin
             GetVectorOriginal.RemoveShape(GetVectorOriginal.SelectedShape);
             key := 0;
+          end else
+          if (key = VK_ESCAPE) and Assigned(GetVectorOriginal.SelectedShape) then
+          begin
+            if GetVectorOriginal.SelectedShape.Usermode = vsuEditText then
+              GetVectorOriginal.SelectedShape.Usermode := vsuEdit
+            else
+              GetVectorOriginal.DeselectShapes;
+            key := 0;
           end;
         end;
       finally
@@ -1305,7 +1340,7 @@ begin
           zoom := (VectLen(m[1,1],m[2,1])+VectLen(m[1,2],m[2,2]))/2/Manager.Image.ZoomFactor;
           BindOriginalEvent(true);
           try
-            if GetVectorOriginal.MouseClick(m*FLastPos, DoScaleX(PointSize, OriginalDPI)*zoom) then
+            if GetVectorOriginal.MouseClick(m*FLastPos, DoScaleX(PointSize, OriginalDPI)*zoom, ssCtrl in ShiftState) then
             begin
               handled := true;
               result := OnlyRenderChange;
@@ -1419,8 +1454,12 @@ begin
           tcBackEditGradTexPoints: if GetVectorOriginal.SelectedShape.Usermode = BackGradTexMode then
                                     GetVectorOriginal.SelectedShape.Usermode := vsuEdit else
                                     GetVectorOriginal.SelectedShape.Usermode := BackGradTexMode;
-          tcForeAdjustToShape: ShapeForeFill.FitGeometry(SuggestGradientBox);
-          tcBackAdjustToShape: ShapeBackFill.FitGeometry(SuggestGradientBox);
+          tcOutlineEditGradTexPoints: if GetVectorOriginal.SelectedShape.Usermode = OutlineGradTexMode then
+                                    GetVectorOriginal.SelectedShape.Usermode := vsuEdit else
+                                    GetVectorOriginal.SelectedShape.Usermode := OutlineGradTexMode;
+          tcForeAdjustToShape: GetVectorOriginal.SelectedShape.PenFill.FitGeometry(SuggestGradientBox);
+          tcBackAdjustToShape: GetVectorOriginal.SelectedShape.BackFill.FitGeometry(SuggestGradientBox);
+          tcOutlineAdjustToShape: GetVectorOriginal.SelectedShape.OutlineFill.FitGeometry(SuggestGradientBox);
           tcShapeToSpline: result := ConvertToSpline;
           else result := false;
         end;
@@ -1508,12 +1547,14 @@ function TEditShapeTool.ToolProvideCommand(ACommand: TToolCommand): boolean;
 begin
   case ACommand of
   tcCut,tcCopy,tcDelete: result:= GetEditMode in [esmShape,esmOtherOriginal,esmGradient];
-  tcForeAdjustToShape: result := GetEditMode = esmShape;
+  tcForeAdjustToShape,tcOutlineAdjustToShape: result := GetEditMode = esmShape;
   tcBackAdjustToShape: result := GetEditMode in [esmShape,esmGradient];
   tcForeEditGradTexPoints: result := (GetEditMode = esmShape) and
-                     (ForeGradTexMode in GetVectorOriginal.SelectedShape.Usermodes);
+                     (ForeGradTexMode in GetVectorOriginal.SelectedShape.MultiUsermodes);
   tcBackEditGradTexPoints: result := (GetEditMode = esmShape) and
-                     (BackGradTexMode in GetVectorOriginal.SelectedShape.Usermodes);
+                     (BackGradTexMode in GetVectorOriginal.SelectedShape.MultiUsermodes);
+  tcOutlineEditGradTexPoints: result := (GetEditMode = esmShape) and
+                     (OutlineGradTexMode in GetVectorOriginal.SelectedShape.MultiUsermodes);
   tcShapeToSpline: result:= (GetEditMode = esmShape)
                             and TCurveShape.CanCreateFrom(GetVectorOriginal.SelectedShape);
   tcAlignLeft..tcAlignBottom: result:= GetEditMode in [esmShape, esmOtherOriginal, esmSelection];
@@ -1564,10 +1605,7 @@ begin
     newEditorBounds := Editor.GetRenderBounds(rect(0,0,ceil(x),ceil(y)));
   r := RectUnion(FPreviousEditorBounds,newEditorBounds);
   if not r.IsEmpty then
-  begin
     Manager.Image.RenderMayChange(r,false);
-    Manager.Image.OnImageChanged.NotifyObservers;
-  end;
   FPreviousEditorBounds := newEditorBounds;
 end;
 
@@ -1648,34 +1686,32 @@ end;
 
 function TVectorialTool.ForeGradTexMode: TVectorShapeUsermode;
 begin
-  if FSwapColor then result := vsuEditBackFill else
+  if Assigned(FShape) and FSwapColor then
+  begin
+    if vsfBackFill in FShape.Fields then
+      result := vsuEditBackFill
+    else if vsfOutlineFill in FShape.Fields then
+      result := vsuEditOutlineFill
+    else
+      result := vsuEditPenFill;
+  end else
     result := vsuEditPenFill;
 end;
 
 function TVectorialTool.BackGradTexMode: TVectorShapeUsermode;
 begin
-  if FSwapColor then result := vsuEditPenFill else
+  if Assigned(FShape) and FSwapColor and (vsfPenFill in FShape.Fields) then
+    result := vsuEditPenFill
+  else
     result := vsuEditBackFill;
 end;
 
-function TVectorialTool.ShapeForeFill: TVectorialFill;
-begin
-  if Assigned(FShape) then
-  begin
-    if FSwapColor then result := FShape.BackFill else
-      result := FShape.PenFill;
-  end else
-    result := nil;
-end;
-
-function TVectorialTool.ShapeBackFill: TVectorialFill;
+function TVectorialTool.OutlineGradTexMode: TVectorShapeUsermode;
 begin
-  if Assigned(FShape) then
-  begin
-    if FSwapColor then result := FShape.PenFill else
-      result := FShape.BackFill;
-  end else
-    result := nil;
+  if Assigned(FShape) and FSwapColor and ([vsfPenFill,vsfBackFill]*FShape.Fields = [vsfPenFill]) then
+    result := vsuEditPenFill
+  else
+    result := vsuEditOutlineFill;
 end;
 
 function TVectorialTool.ForeFitMode: TFitMode;
@@ -1689,6 +1725,42 @@ begin
   if IsBackEditGradTexPoints then result := fmNever
   else result := fmIfChange;
 end;
+
+function TVectorialTool.OutlineFitMode: TFitMode;
+begin
+  if IsOutlineEditGradTexPoints then result := fmNever
+  else result := fmIfChange;
+end;
+
+function TVectorialTool.ManagerForeFill: TVectorialFill;
+begin
+  if Assigned(FShape) and FSwapColor then
+  begin
+    if vsfBackFill in FShape.Fields then
+      result := Manager.BackFill
+    else if vsfOutlineFill in FShape.Fields then
+      result := Manager.OutlineFill
+    else
+      result := Manager.ForeFill;
+  end else
+    result := Manager.ForeFill;
+end;
+
+function TVectorialTool.ManagerBackFill: TVectorialFill;
+begin
+  if Assigned(FShape) and FSwapColor and (vsfPenFill in FShape.Fields) then
+    result := Manager.ForeFill
+  else
+    result := Manager.BackFill;
+end;
+
+function TVectorialTool.ManagerOutlineFill: TVectorialFill;
+begin
+  if Assigned(FShape) and FSwapColor and ([vsfPenFill,vsfBackFill]*FShape.Fields = [vsfPenFill]) then
+    result := Manager.ForeFill
+  else
+    result := Manager.OutlineFill;
+end;
    
 function TVectorialTool.GetIsForeEditGradTexPoints: boolean;
 begin
@@ -1700,6 +1772,11 @@ begin
   result := Assigned(FShape) and (FShape.Usermode = BackGradTexMode);
 end;
 
+function TVectorialTool.GetIsOutlineEditGradTexPoints: boolean;
+begin
+  result := Assigned(FShape) and (FShape.Usermode = OutlineGradTexMode);
+end;
+
 function TVectorialTool.GetGridMatrix: TAffineMatrix;
 begin
   if Manager.Image.ZoomFactor > DoScaleX(35, OriginalDPI)/10 then
@@ -1707,7 +1784,7 @@ begin
   else
   begin
     if Assigned(FShape) and
-         (not (vsfPenFill in FShape.Fields) or
+         (not (vsfPenFill in FShape.MultiFields) or
            (FShape.PenFill.IsFullyTransparent)) then
       result := AffineMatrixTranslation(0.5, 0.5)
     else
@@ -1792,6 +1869,11 @@ begin
   result := false;
 end;
 
+function TVectorialTool.CreateShape: TVectorShape;
+begin
+  result := ShapeClass.Create(nil);
+end;
+
 function TVectorialTool.UseOriginal: boolean;
 begin
   result := FUseOriginal;
@@ -1821,17 +1903,14 @@ var
   fitMode: TFitMode;
 begin
   zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
-  f:= FShape.Fields;
+  f := FShape.MultiFields;
   gradBox := FShape.SuggestGradientBox(AffineMatrixIdentity);
   if vsfPenFill in f then
   begin
     if HasPen then
     begin
       if AAlwaysFit then fitMode := fmAlways else fitMode := ForeFitMode;
-      if FSwapColor then
-        AssignFill(FShape.PenFill, Manager.BackFill, gradBox, fitMode)
-      else
-        AssignFill(FShape.PenFill, Manager.ForeFill, gradBox, fitMode);
+      AssignFill(FShape.PenFill, ManagerForeFill, gradBox, fitMode)
     end else
       FShape.PenFill.Clear;
   end;
@@ -1843,13 +1922,21 @@ begin
     if HasBrush then
     begin
       if AAlwaysFit then fitMode := fmAlways else fitMode := BackFitMode;
-      if FSwapColor then
-        AssignFill(FShape.BackFill, Manager.ForeFill, gradBox, fitMode)
-      else
-        AssignFill(FShape.BackFill, Manager.BackFill, gradBox, fitMode);
+      AssignFill(FShape.BackFill, ManagerBackFill, gradBox, fitMode)
     end else
       FShape.BackFill.Clear;
   end;
+  if vsfOutlineFill in f then
+  begin
+    if Manager.TextOutline then
+    begin
+      if AAlwaysFit then fitMode := fmAlways else fitMode := OutlineFitMode;
+      AssignFill(FShape.OutlineFill, ManagerOutlineFill, gradBox, fitMode);
+    end else
+      FShape.OutlineFill.Clear;
+  end;
+  if (vsfOutlineWidth in f) and Manager.TextOutline then
+    FShape.OutlineWidth := zoom*Manager.TextOutlineWidth;
 end;
 
 function TVectorialTool.GetManagerShapeOptions: TShapeOptions;
@@ -1865,7 +1952,7 @@ end;
 function TVectorialTool.RoundCoordinate(constref ptF: TPointF): TPointF;
 begin
   if not (toDrawShape in GetManagerShapeOptions) or
-    (Assigned(FShape) and not (vsfPenFill in FShape.Fields)) then
+    (Assigned(FShape) and not (vsfPenFill in FShape.MultiFields)) then
     result := PointF(floor(ptF.x)+0.5,floor(ptF.y)+0.5)
   else
     result := PointF(round(ptF.x),round(ptF.y));
@@ -2154,8 +2241,9 @@ begin
         Action.NotifyChange(toolDest, r);
         result := true;
       end;
-  tcForeAdjustToShape: if Assigned(FShape) then ShapeForeFill.FitGeometry(SuggestGradientBox);
-  tcBackAdjustToShape: if Assigned(FShape) then ShapeBackFill.FitGeometry(SuggestGradientBox);
+  tcForeAdjustToShape: if Assigned(FShape) then FShape.PenFill.FitGeometry(SuggestGradientBox);
+  tcBackAdjustToShape: if Assigned(FShape) then FShape.BackFill.FitGeometry(SuggestGradientBox);
+  tcOutlineAdjustToShape: if Assigned(FShape) then FShape.OutlineFill.FitGeometry(SuggestGradientBox);
   tcForeEditGradTexPoints: if Assigned(FShape) and not FQuickDefine then
                           begin
                             if FShape.Usermode = ForeGradTexMode then
@@ -2168,6 +2256,12 @@ begin
                               FShape.Usermode := vsuEdit else
                               FShape.Usermode := BackGradTexMode;
                           end;
+  tcOutlineEditGradTexPoints: if Assigned(FShape) and not FQuickDefine then
+                          begin
+                            if FShape.Usermode = OutlineGradTexMode then
+                              FShape.Usermode := vsuEdit else
+                              FShape.Usermode := OutlineGradTexMode;
+                          end;
   tcFinish: begin
               toolDest := GetToolDrawingLayer;
               r := ValidateShape;
@@ -2192,11 +2286,14 @@ begin
   case ACommand of
   tcCopy,tcCut: Result:= not IsSelectingTool and not FQuickDefine and Assigned(FShape);
   tcFinish: result := not IsIdle;
-  tcForeAdjustToShape, tcBackAdjustToShape: result := not IsSelectingTool and Assigned(FShape) and not FQuickDefine;
+  tcForeAdjustToShape, tcBackAdjustToShape, tcOutlineAdjustToShape:
+      result := not IsSelectingTool and Assigned(FShape) and not FQuickDefine;
   tcForeEditGradTexPoints: result := not IsSelectingTool and Assigned(FShape) and not FQuickDefine and
-                            (vsuEditPenFill in FShape.Usermodes) and not (FShape.Usermode = vsuCreate);
+                            (ForeGradTexMode in FShape.Usermodes) and not (FShape.Usermode = vsuCreate);
   tcBackEditGradTexPoints: result := not IsSelectingTool and Assigned(FShape) and not FQuickDefine and
-                            (vsuEditPenFill in FShape.Usermodes) and not (FShape.Usermode = vsuCreate);
+                            (BackGradTexMode in FShape.Usermodes) and not (FShape.Usermode = vsuCreate);
+  tcOutlineEditGradTexPoints: result := not IsSelectingTool and Assigned(FShape) and not FQuickDefine and
+                            (OutlineGradTexMode in FShape.Usermodes) and not (FShape.Usermode = vsuCreate);
   tcShapeToSpline: result:= not IsSelectingTool and not FQuickDefine and Assigned(FShape)
                             and TCurveShape.CanCreateFrom(FShape);
   tcAlignLeft..tcAlignBottom: Result:= not FQuickDefine and Assigned(FShape);
@@ -2214,6 +2311,11 @@ begin
     result:= inherited SuggestGradientBox;
 end;
 
+function TVectorialTool.GetContextualToolbars: TContextualToolbars;
+begin
+  result := ContextualToolbarsFromShape(ShapeClass, FShape);
+end;
+
 function TVectorialTool.Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth,
   VirtualScreenHeight: integer;
   BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;

+ 1 - 4
lazpaint/ucommandline.pas

@@ -278,11 +278,8 @@ end;
 
 procedure ProcessCommands(instance: TLazPaintCustomInstance; commandsUTF8: TStringList;
   out errorEncountered, fileSaved, quitQuery: boolean);
-var imageActions: TImageActions;
 begin
-  imageActions := TImageActions.Create(instance);
-  InternalProcessCommands(instance, commandsUTF8, errorEncountered, fileSaved, quitQuery, imageActions);
-  imageActions.Free;
+  InternalProcessCommands(instance, commandsUTF8, errorEncountered, fileSaved, quitQuery, TImageActions(instance.ImageAction));
 end;
 
 end.

+ 25 - 1
lazpaint/uconfig.pas

@@ -196,12 +196,16 @@ type
     //tools
     function DefaultToolForeColor: TBGRAPixel;
     function DefaultToolBackColor: TBGRAPixel;
+    function DefaultToolOutlineColor: TBGRAPixel;
     procedure SetDefaultToolForeColor(value: TBGRAPixel);
     procedure SetDefaultToolBackColor(value: TBGRAPixel);
+    procedure SetDefaultToolOutlineColor(value: TBGRAPixel);
     function DefaultToolForeGradient: string;
     function DefaultToolBackGradient: string;
+    function DefaultToolOutlineGradient: string;
     procedure SetDefaultToolForeGradient(value: string);
     procedure SetDefaultToolBackGradient(value: string);
+    procedure SetDefaultToolOutlineGradient(value: string);
     function DefaultToolPenWidth: single;
     procedure SetDefaultToolPenWidth(value: single);
     function DefaultToolEraserWidth: single;
@@ -799,6 +803,11 @@ begin
   result := StrToBGRA(iniOptions.ReadString('Tool','BackColor','0080FFC0'));
 end;
 
+function TLazPaintConfig.DefaultToolOutlineColor: TBGRAPixel;
+begin
+  result := StrToBGRA(iniOptions.ReadString('Tool','OutlineColor','FF0000C0'));
+end;
+
 procedure TLazPaintConfig.SetDefaultToolForeColor(value: TBGRAPixel);
 begin
   iniOptions.WriteString('Tool','ForeColor',BGRAToStr(value));
@@ -809,6 +818,11 @@ begin
   iniOptions.WriteString('Tool','BackColor',BGRAToStr(value));
 end;
 
+procedure TLazPaintConfig.SetDefaultToolOutlineColor(value: TBGRAPixel);
+begin
+  iniOptions.WriteString('Tool','OutlineColor',BGRAToStr(value));
+end;
+
 function TLazPaintConfig.DefaultToolForeGradient: string;
 begin
   result := iniOptions.ReadString('Tool','ForeGradient','');
@@ -819,6 +833,11 @@ begin
   result := iniOptions.ReadString('Tool','BackGradient','');
 end;
 
+function TLazPaintConfig.DefaultToolOutlineGradient: string;
+begin
+  result := iniOptions.ReadString('Tool','OutlineGradient','');
+end;
+
 procedure TLazPaintConfig.SetDefaultToolForeGradient(value: string);
 begin
   iniOptions.WriteString('Tool','ForeGradient',value);
@@ -829,6 +848,11 @@ begin
   iniOptions.WriteString('Tool','BackGradient',value);
 end;
 
+procedure TLazPaintConfig.SetDefaultToolOutlineGradient(value: string);
+begin
+  iniOptions.WriteString('Tool','OutlineGradient',value);
+end;
+
 function TLazPaintConfig.DefaultToolPenWidth: single;
 begin
   result := iniOptions.ReadFloat('Tool','PenWidth',5);
@@ -922,7 +946,7 @@ end;
 function TLazPaintConfig.DefaultToolTextFont: TFont;
 var fontStyle: TFontStyles;
 begin
-  tempFont.Name := iniOptions.ReadString('Tool','TextFontName','Arial');
+  tempFont.Name := iniOptions.ReadString('Tool','TextFontName','');
   tempFont.Size := iniOptions.ReadInteger('Tool','TextFontSize',10);
   fontStyle := [];
   if iniOptions.ReadBool('Tool','TextFontBold',False) then fontStyle += [fsBold];

+ 31 - 0
lazpaint/ufileextensions.pas

@@ -29,6 +29,8 @@ function GetSelectedFilterExtensions(const Filter: string; FilterIndex: integer;
 function ApplySelectedFilterExtension(const FileName: string; const Filter: string; FilterIndex: integer): string;
 
 function GetExtensionFilter(AOption: TExtensionOptions; ADisplayPrefix: string = '*.'): string;
+function GetExtensionFilterIndex(AOption: TExtensionOptions; AExtensions: string): integer;
+function GetExtensionFilterByIndex(AOption: TExtensionOptions; AIndex: integer): string;
 
 procedure RegisterPicExt(AName: string; AExtensionsWithoutDot: string; AOptions: TExtensionOptions);
 
@@ -139,6 +141,35 @@ begin
     result := rsAllSupportedFiletypes + ' (' + allExtWithoutDot + ')|' + allExtFilter + result;
 end;
 
+function GetExtensionFilterIndex(AOption: TExtensionOptions; AExtensions: string): integer;
+var
+  i: Integer;
+begin
+  result := 2;
+  for i := 0 to high(PictureFileExtensions) do
+    if (PictureFileExtensions[i].options * AOption = AOption) and
+     (PictureFileExtensions[i].filterForAllCases <> '') then
+    begin
+      if PictureFileExtensions[i].filterForAllCases = AExtensions then exit;
+      inc(result);
+    end;
+  result := 1;
+end;
+
+function GetExtensionFilterByIndex(AOption: TExtensionOptions; AIndex: integer): string;
+var curIndex, i: integer;
+begin
+  curIndex := 2;
+  for i := 0 to high(PictureFileExtensions) do
+    if (PictureFileExtensions[i].options * AOption = AOption) and
+     (PictureFileExtensions[i].filterForAllCases <> '') then
+    begin
+      if curIndex = AIndex then exit(PictureFileExtensions[i].filterForAllCases);
+      inc(curIndex);
+    end;
+  result := '*.*';
+end;
+
 function GetBit(Value: QWord; Index: Byte): Boolean;
 begin
   Result := ((Value shr Index) and 1) = 1;

+ 39 - 21
lazpaint/uimageview.pas

@@ -16,6 +16,7 @@ type
   TImageView = class
   protected
     FVirtualScreen : TBGRABitmap;
+    FUpdatingPopup: boolean;
     FPenCursorVisible: boolean;
     FPenCursorPos,FPenCursorPosBefore: TVSCursorPosition;
     FQueryPaintVirtualScreen: boolean;
@@ -35,7 +36,7 @@ type
     FZoom: TZoom;
     FPictureCanvas: TCanvas;
     function GetImage: TLazPaintImage;
-    function GetRenderUpdateRectVS(AIncludeLastToolState: boolean): TRect;
+    function GetRenderUpdateRectVS(AIncludeCurrentToolEditor: boolean): TRect;
     function GetFillSelectionHighlight: boolean;
     function GetPenCursorPosition: TVSCursorPosition;
     function GetWorkspaceColor: TColor;
@@ -47,6 +48,7 @@ type
     procedure SetFillSelectionHighlight(AValue: boolean);
     procedure SetShowSelection(AValue: boolean);
     procedure PictureSelectionChanged({%H-}sender: TLazPaintImage; const ARect: TRect);
+    procedure ToolManagerRenderChanged(Sender: TObject);
     procedure PaintVirtualScreenCursor({%H-}ACanvasOfs: TPoint; {%H-}AWorkArea: TRect; {%H-}AWinControlOfs: TPoint; {%H-}AWinControl: TWinControl);
     function GetRectToInvalidate(AInvalidateAll: boolean; AWorkArea: TRect): TRect;
     function GetPictureCoordsDefined: boolean;
@@ -73,6 +75,7 @@ type
     property ShowSelection: boolean read FShowSelection write SetShowSelection;
     property WorkspaceColor: TColor read GetWorkspaceColor;
     property PictureCoordsDefined: boolean read GetPictureCoordsDefined;
+    property UpdatingPopup: boolean read FUpdatingPopup write FUpdatingPopup;
   end;
 
 implementation
@@ -108,6 +111,13 @@ begin
   result := FLastPictureParameters.defined;
 end;
 
+procedure TImageView.ToolManagerRenderChanged(Sender: TObject);
+begin
+  if Assigned(FVirtualScreen) then
+    Image.RenderMayChange(LazPaintInstance.ToolManager.GetRenderBounds(
+                            FVirtualScreen.Width, FVirtualScreen.Height));
+end;
+
 function TImageView.GetImage: TLazPaintImage;
 begin
   result := FInstance.Image;
@@ -159,32 +169,39 @@ begin
     FreeAndNil(FVirtualScreen);
 
   if not Assigned(FVirtualScreen) then
+  begin
     FVirtualScreen := TBGRABitmap.Create(FLastPictureParameters.virtualScreenArea.Right-FLastPictureParameters.virtualScreenArea.Left,
                                         FLastPictureParameters.virtualScreenArea.Bottom-FLastPictureParameters.virtualScreenArea.Top, WorkspaceColor);
+  end else
+  begin
+    if picParamWereDefined then FVirtualScreen.ClipRect := GetRenderUpdateRectVS(False);
+  end;
 
-  if picParamWereDefined then FVirtualScreen.ClipRect := GetRenderUpdateRectVS(False);
-  Image.ResetRenderUpdateRect;
-
-  if not FVirtualScreen.ClipRect.IsEmpty then
+  if not FUpdatingPopup then
   begin
-    renderRect := FLastPictureParameters.scaledArea;
-    OffsetRect(renderRect, -FLastPictureParameters.virtualScreenArea.Left,
-                           -FLastPictureParameters.virtualScreenArea.Top);
+    Image.ResetRenderUpdateRect;
 
-    DrawThumbnailCheckers(FVirtualScreen,renderRect,Image.IsIconCursor);
+    if not FVirtualScreen.ClipRect.IsEmpty then
+    begin
+      renderRect := FLastPictureParameters.scaledArea;
+      OffsetRect(renderRect, -FLastPictureParameters.virtualScreenArea.Left,
+                             -FLastPictureParameters.virtualScreenArea.Top);
 
-    //draw image (with merged selection)
-    FVirtualScreen.StretchPutImage(renderRect,Image.RenderedImage,dmDrawWithTransparency);
-    if (Zoom.Factor > DoScaleX(MinZoomForGrid, OriginalDPI)) and LazPaintInstance.GridVisible then
-      DrawGrid(FVirtualScreen,FLastPictureParameters.zoomFactorX,FLastPictureParameters.zoomFactorY,
-         FLastPictureParameters.originInVS.X,FLastPictureParameters.originInVS.Y);
+      DrawThumbnailCheckers(FVirtualScreen,renderRect,Image.IsIconCursor);
 
-    DrawSelectionHighlight(renderRect);
-  end;
-  FVirtualScreen.NoClip;
+      //draw image (with merged selection)
+      FVirtualScreen.StretchPutImage(renderRect,Image.RenderedImage,dmDrawWithTransparency);
+      if (Zoom.Factor > DoScaleX(MinZoomForGrid, OriginalDPI)) and LazPaintInstance.GridVisible then
+        DrawGrid(FVirtualScreen,FLastPictureParameters.zoomFactorX,FLastPictureParameters.zoomFactorY,
+           FLastPictureParameters.originInVS.X,FLastPictureParameters.originInVS.Y);
 
-  //show tools info
-  LazPaintInstance.ToolManager.RenderTool(FVirtualScreen);
+      DrawSelectionHighlight(renderRect);
+    end;
+    FVirtualScreen.NoClip;
+
+    //show tools info
+    Image.RenderMayChange(LazPaintInstance.ToolManager.RenderTool(FVirtualScreen), false, false);
+  end;
 
   PaintVirtualScreenImplementation(ACanvasOfs, AWorkArea, AVSPart);
   Image.VisibleArea := TRectF.Intersect(rectF(FormToBitmap(AWorkArea.Left, AWorkArea.Top),
@@ -299,6 +316,7 @@ begin
   FSelectionHighlight := TSelectionHighlight.Create(Image);
   FShowSelection:= true;
   Image.OnSelectionChanged := @PictureSelectionChanged;
+  LazPaintInstance.ToolManager.OnToolRenderChanged:=@ToolManagerRenderChanged;
   LazPaintInstance.ToolManager.BitmapToVirtualScreen := @BitmapToVirtualScreen;
 end;
 
@@ -382,7 +400,7 @@ begin
   FLastPictureParameters.defined := false;
 end;
 
-function TImageView.GetRenderUpdateRectVS(AIncludeLastToolState: boolean): TRect;
+function TImageView.GetRenderUpdateRectVS(AIncludeCurrentToolEditor: boolean): TRect;
 const displayMargin = 1;
 begin
   result := Image.RenderUpdateRectInPicCoord;
@@ -400,7 +418,7 @@ begin
     end;
   end;
   result := RectUnion(result, Image.RenderUpdateRectInVSCoord);
-  if AIncludeLastToolState and Assigned(FVirtualScreen) then
+  if AIncludeCurrentToolEditor and Assigned(FVirtualScreen) then
     result := RectUnion(result, LazPaintInstance.ToolManager.GetRenderBounds(FVirtualScreen.Width,FVirtualScreen.Height));
 end;
 

+ 72 - 49
lazpaint/umainformlayout.pas

@@ -29,8 +29,9 @@ type
     FDockedToolBoxToolBar: TToolBar;
     FPaletteToolbar: TPaletteToolbar;
     FStatusBarVisible: boolean;
-    FStatusBar: TStatusBar;
+    FStatusBar: TPanel;
     FStatusText: string;
+    FStatusTextSplit: TStringList;
     FDarkTheme: boolean;
     FDockedControlsPanel: TPanel;
     FDockedChooseColorSplitter: TSplitter;
@@ -39,6 +40,7 @@ type
     function GetStatusBarVisible: boolean;
     function GetStatusText: string;
     function GetToolBoxVisible: boolean;
+    procedure StatusBar_Paint(Sender: TObject);
     procedure ToolboxGroupMainButton_MouseMove(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X,
       {%H-}Y: Integer);
     procedure SetDarkTheme(AValue: boolean);
@@ -165,12 +167,19 @@ begin
 
   FDockedControlsPanel := TPanel.Create(nil);
   FDockedControlsPanel.Visible := false;
+  FDockedControlsPanel.Anchors:= [akRight,akTop,akBottom];
   FForm.InsertControl(FDockedControlsPanel);
 
-  FStatusBar := TStatusBar.Create(nil);
-  FStatusBar.SizeGrip := false;
+  FStatusBar := TPanel.Create(nil);
   FStatusBar.Align := alNone;
   FStatusBar.Visible := false;
+  FStatusBar.Anchors := [akLeft,akRight,akBottom];
+  FStatusBar.BevelOuter:= bvNone;
+  FStatusBar.BevelInner:= bvNone;
+  FStatusBar.Height := DoScaleY(15, OriginalDPI);
+  FStatusBar.Font.Height := -DoScaleY(12, OriginalDPI);
+  FStatusBar.OnPaint:=@StatusBar_Paint;
+  FStatusTextSplit := TStringList.Create;
   FForm.InsertControl(FStatusBar);
 
   ApplyTheme;
@@ -180,6 +189,7 @@ destructor TMainFormLayout.Destroy;
 begin
   FreeAndNil(FDockedControlsPanel);
   FreeAndNil(FStatusBar);
+  FreeAndNil(FStatusTextSplit);
   FreeAndNil(FPaletteToolbar);
   FreeAndNil(FPanelToolBox);
   FreeAndNil(FMenu);
@@ -207,6 +217,49 @@ begin
   result := LazPaintInstance.ToolboxVisible;
 end;
 
+procedure TMainFormLayout.StatusBar_Paint(Sender: TObject);
+var
+  colWidth, spacing, i, x: Integer;
+begin
+  if FStatusTextSplit.Count > 0 then
+  begin
+    spacing := DoScaleX(6, OriginalDPI);
+    colWidth := (FStatusBar.ClientWidth - spacing*2) div FStatusTextSplit.Count;
+    if colWidth > spacing*2 then
+    begin
+      if DarkTheme then
+        FStatusBar.Canvas.Pen.Color := clDarkPanelHighlight
+        else FStatusBar.Canvas.Pen.Color := clBtnHighlight;
+      FStatusBar.Canvas.Line(0,0, FStatusBar.Width,0);
+      x := 0;
+      for i := 0 to FStatusTextSplit.Count-1 do
+      begin
+        FStatusBar.Canvas.Font := FStatusBar.Font;
+        if DarkTheme then
+          FStatusBar.Canvas.Font.Color := clLightText
+          else FStatusBar.Canvas.Font.Color := clBtnText;
+        FStatusBar.Canvas.TextOut(x + spacing,
+          (FStatusBar.ClientHeight - FStatusBar.Canvas.TextHeight('Hg')) div 2,
+          FStatusTextSplit[i]);
+
+        if i > 0 then
+        begin
+          if DarkTheme then
+            FStatusBar.Canvas.Pen.Color := clDarkPanelShadow
+            else FStatusBar.Canvas.Pen.Color := clBtnShadow;
+          FStatusBar.Canvas.Line(x-1,0, x-1,FStatusBar.Height);
+          if DarkTheme then
+            FStatusBar.Canvas.Pen.Color := clDarkPanelHighlight
+            else FStatusBar.Canvas.Pen.Color := clBtnHighlight;
+          FStatusBar.Canvas.Line(x,0, x,FStatusBar.Height);
+        end;
+
+        inc(x, max(colWidth, FStatusBar.Canvas.TextWidth(FStatusTextSplit[i]) + 2*spacing));
+      end;
+    end;
+  end;
+end;
+
 procedure TMainFormLayout.ToolboxGroupMainButton_MouseMove(Sender: TObject;
   Shift: TShiftState; X, Y: Integer);
 var
@@ -254,7 +307,7 @@ end;
 
 function TMainFormLayout.GetStatusText: string;
 begin
-  result := FStatusBar.SimpleText;
+  result := FStatusText;
 end;
 
 procedure TMainFormLayout.PaletteVisibilityChangedByUser(Sender: TObject);
@@ -282,46 +335,13 @@ begin
 end;
 
 procedure TMainFormLayout.SetStatusText(AValue: string);
-var elems: TStringList;
-  i,w: Integer;
-  idxDelim: integer;
 begin
   if AValue = FStatusText then exit;
   FStatusText := AValue;
-  if pos('|',AValue) = 0 then
-  begin
-    if FStatusBar.SimplePanel <> true then
-      FStatusBar.SimplePanel := true;
-    FStatusBar.SimpleText := AValue;
-  end else
-  begin
-    elems := TStringList.Create;
-    repeat
-      idxDelim := pos('|',AValue);
-      if idxDelim = 0 then
-      begin
-        elems.Add(AValue);
-        break;
-      end;
-      elems.Add(copy(AValue,1,idxDelim-1));
-      AValue := copy(AValue,idxDelim+1,length(AValue)-idxDelim);
-    until false;
-    if FStatusBar.SimplePanel <> false then
-      FStatusBar.SimplePanel := false;
-    while FStatusBar.Panels.Count > elems.Count do
-      FStatusBar.Panels.Delete(FStatusBar.Panels.Count-1);
-    while FStatusBar.Panels.Count < elems.Count do
-      with FStatusBar.Panels.Add do
-        Width := FStatusBar.Height*10;
-    w := FStatusBar.ClientWidth div elems.Count;
-    for i := 0 to elems.Count-1 do
-      with FStatusBar.Panels[i] do
-      begin
-        Text := elems[i];
-        Width := w;
-      end;
-    elems.Free;
-  end;
+  FStatusTextSplit.Delimiter:= '|';
+  FStatusTextSplit.StrictDelimiter:= true;
+  FStatusTextSplit.DelimitedText := FStatusText;
+  FStatusBar.Invalidate;
 end;
 
 procedure TMainFormLayout.SetToolBoxVisible(AValue: boolean);
@@ -446,8 +466,15 @@ begin
       w := FDockedToolBoxToolBar.ButtonWidth * nbX+2;
       FDockedToolBoxToolBar.Width := w;
       FPanelToolBox.Width := w;
-      if FToolBoxDocking = twLeft then FPanelToolBox.Left:= Left
-      else FPanelToolBox.Left:= Right-FPanelToolBox.Width;
+      if FToolBoxDocking = twLeft then
+      begin
+        FPanelToolBox.Left:= Left;
+        FPanelToolBox.Anchors:= [akLeft,akTop,akBottom];
+      end else
+      begin
+        FPanelToolBox.Left:= Right-FPanelToolBox.Width;
+        FPanelToolBox.Anchors:= [akRight,akTop,akBottom];
+      end;
       for i := 0 to FDockedToolBoxToolBar.ButtonCount-1 do
         FDockedToolBoxToolBar.Buttons[i].Top := i*FDockedToolBoxToolBar.ButtonHeight;
     end;
@@ -487,12 +514,6 @@ begin
   begin
     with GetWorkAreaAt(lsAfterDockedControlsPanel) do
       FStatusBar.SetBounds(Left,Bottom-FStatusBar.Height,Right-Left,FStatusBar.Height);
-    if not FStatusBar.SimplePanel then
-    begin
-      w := FStatusBar.ClientWidth div FStatusBar.Panels.Count;
-      for i := 0 to FStatusBar.Panels.Count-1 do
-        FStatusBar.Panels[i].Width := w;
-    end;
     if not FStatusBar.Visible then
     begin
       FStatusBar.Visible := true;
@@ -513,6 +534,7 @@ begin
     FDockedToolBoxToolBar.EdgeOuter := esNone;
     FDockedToolBoxToolBar.OnPaint := @DarkThemeInstance.ToolBarPaint;
     FDockedToolBoxToolBar.OnPaintButton:= @DarkThemeInstance.ToolBarPaintButton;
+    FStatusBar.Color:= clDarkBtnFace;
   end
   else
   begin
@@ -521,6 +543,7 @@ begin
     FDockedToolBoxToolBar.EdgeOuter := esNone;
     FDockedToolBoxToolBar.OnPaint := nil;
     FDockedToolBoxToolBar.OnPaintButton:= nil;
+    FStatusBar.Color:= clBtnFace;
   end;
   DarkThemeInstance.Apply(FDockedControlsPanel, DarkTheme, false);
   bevelOfs := integer(FDockedControlsPanel.BevelOuter <> bvNone)*FDockedControlsPanel.BevelWidth;

+ 45 - 9
lazpaint/umenu.pas

@@ -240,6 +240,47 @@ begin
 end;
 
 procedure TMainFormMenu.AddInstalledScripts(AMenu: TMenuItem; AIndex: integer);
+
+  procedure AddScriptRec(AMenu: TMenuItem; var AIndex: integer; AItem: TMenuItem);
+  var
+    posSub, j, subIndex: integer;
+    sectionName: String;
+    sectionItem: TMenuItem;
+  begin
+    posSub := pos('>', AItem.Caption);
+    if posSub > 0 then
+    begin
+      sectionName := copy(AItem.Caption, 1, posSub-1);
+      AItem.Caption := copy(AItem.Caption, posSub+1, length(AItem.Caption) - posSub);
+      subIndex := -1;
+      for j := 0 to AMenu.Count-1 do
+        if AMenu.Items[j].Caption = sectionName then
+        begin
+          AddScriptRec(AMenu.Items[j], subIndex, AItem);
+          exit;
+        end;
+      sectionItem := TMenuItem.Create(AMenu);
+      sectionItem.Caption := sectionName;
+      if AIndex = -1 then
+        AMenu.Add(sectionItem)
+      else
+      begin
+        AMenu.Insert(AIndex, sectionItem);
+        inc(AIndex);
+      end;
+      AddScriptRec(sectionItem, subIndex, AItem);
+      exit;
+    end;
+
+    if AIndex = -1 then
+      AMenu.Add(AItem)
+    else
+    begin
+      AMenu.Insert(AIndex, AItem);
+      inc(AIndex);
+    end;
+  end;
+
 var
   path, fullname, header, title: String;
   searchRec: TSearchRec;
@@ -266,6 +307,8 @@ begin
           if header.StartsWith('#') then
           begin
             title := header.Substring(1).Trim;
+            title := StringReplace(title, ' >', '>', [rfReplaceAll]);
+            title := StringReplace(title, '> ', '>', [rfReplaceAll]);
             item := TMenuItem.Create(AMenu);
             item.Caption := title;
             item.Tag := FInstalledScripts.Add(fullname);
@@ -277,15 +320,7 @@ begin
     finally
       FindCloseUTF8(searchRec);
       for i := 0 to items.Count-1 do
-      begin
-        if AIndex = -1 then
-          AMenu.Add(TMenuItem(items.Objects[i]))
-        else
-        begin
-          AMenu.Insert(AIndex, TMenuItem(items.Objects[i]));
-          inc(AIndex);
-        end;
-      end;
+        AddScriptRec(AMenu, AIndex, TMenuItem(items.Objects[i]));
       items.Free;
     end;
   end;
@@ -557,6 +592,7 @@ begin
      FToolbarBackground.Left := 0;
      FToolbarBackground.width := ClientWidth;
      FToolbarBackground.Height := FToolbarsHeight;
+     FToolbarBackground.Anchors:= [akLeft,akTop,akRight];
      FToolbarBackground.Visible := true;
    end;
 end;

+ 1 - 0
lazpaint/upalettetoolbar.pas

@@ -473,6 +473,7 @@ begin
     FPaletteAlphaWidth := FPaletteItemWidth div 3;
 
     FPanelPalette := TBGRAVirtualScreen.Create(nil);
+    FPanelPalette.Anchors:= [akTop,akRight,akBottom];
     FPanelPalette.Width := DoScaleX(FPaletteItemWidth, OriginalDPI)+VolatileScrollBarSize;
     FPanelPalette.Visible := false;
     FPanelPalette.OnRedraw := @RepaintPalette;

+ 1 - 1
lazpaint/upython.pas

@@ -204,7 +204,7 @@ begin
     raise exception.Create(
       StringReplace( StringReplace(rsPythonUnexpectedVersion,
         '%1',inttostr(APythonVersion),[]),
-        '%2',inttostr(PythonVersionMajor),[]) );
+        '%2',inttostr(PythonVersionMajor),[]) + #9 + rsDownload + #9 + 'https://www.python.org');
   FFirstOutput:= true;
   AutomationEnvironment.Values['PYTHONPATH'] := DefaultScriptDirectory;
   try

+ 2 - 1
lazpaint/uresourcestrings.pas

@@ -19,7 +19,7 @@ resourcestring
   rsLazPaint = 'LazPaint';
   rsScript = 'Script';
   rsFunctionNotDefined = 'The function %1 is not defined.';
-  rsPythonUnexpectedVersion = 'Expected python version %1 but %2 found.';
+  rsPythonUnexpectedVersion = 'Expected Python version %1 but %2 found.';
   rsOpening='Opening';
   rsLoading='Loading';
   rsRecentDirectories='Recent directories:';
@@ -180,6 +180,7 @@ resourcestring
   rsCancelledByUser='Cancelled by user';
   rsNoAndProceedToNext='Do not save and open another file';
   rsInformation='Information';
+  rsDownload='Download';
   rsError='Error';
   rsEndWithoutMatchingBegin = 'End without matching begin';
   rsThereAreNoCheckedItems='There are no checked items. Check some items or add some new ones.';

+ 8 - 7
lazpaintcontrols/lazpaintcontrols.lpk

@@ -21,7 +21,7 @@
       </Linking>
     </CompilerOptions>
     <Version Minor="1"/>
-    <Files Count="12">
+    <Files Count="13">
       <Item1>
         <Filename Value="lctoolbars.pas"/>
         <UnitName Value="LCToolbars"/>
@@ -71,24 +71,25 @@
         <Filename Value="lcresourcestring.pas"/>
         <UnitName Value="LCResourceString"/>
       </Item12>
+      <Item13>
+        <Filename Value="lcvectormultishape.pas"/>
+        <UnitName Value="LCVectorMultishape"/>
+      </Item13>
     </Files>
     <i18n>
       <EnableI18N Value="True"/>
       <OutDir Value="../lazpaint/release/bin/i18n"/>
     </i18n>
-    <RequiredPkgs Count="4">
+    <RequiredPkgs Count="3">
       <Item1>
         <PackageName Value="bgracontrols"/>
       </Item1>
       <Item2>
-        <PackageName Value="BGRABitmapPack"/>
+        <PackageName Value="LCL"/>
       </Item2>
       <Item3>
-        <PackageName Value="LCL"/>
-      </Item3>
-      <Item4>
         <PackageName Value="FCL"/>
-      </Item4>
+      </Item3>
     </RequiredPkgs>
     <UsageOptions>
       <UnitPath Value="$(PkgOutDir)"/>

+ 1 - 1
lazpaintcontrols/lazpaintcontrols.pas

@@ -11,7 +11,7 @@ uses
   LCToolbars, LCVectorialFill, LCVectorialFillInterface, LCVectorOriginal, 
   LCVectorPolyShapes, LCVectorRectShapes, LCVectorialFillControl, 
   LCVectorShapes, LCVectorTextShapes, LCScaleDPI, LCVectorClipboard, 
-  LCResourceString, LazarusPackageIntf;
+  LCResourceString, LCVectorMultishape, LazarusPackageIntf;
 
 implementation
 

+ 24 - 7
lazpaintcontrols/lcvectorclipboard.pas

@@ -22,8 +22,9 @@ function CopyShapesToClipboard(AShapes: array of TVectorShape; const AMatrix: TA
 var
   tempContainer: TVectorOriginal;
   mem: TMemoryStream;
-  i: Integer;
+  i, j: Integer;
   s: TVectorShape;
+  multiSel: IVectorMultishape;
 begin
   result:= false;
   if length(AShapes)=0 then exit;
@@ -32,9 +33,21 @@ begin
   try
     for i := 0 to high(AShapes) do
     begin
-      s := AShapes[i].Duplicate;
-      s.Transform(AMatrix);
-      tempContainer.AddShape(s);
+      if AShapes[i] is VectorMultiselectionFactory then
+      begin
+        multiSel := AShapes[i].GetAsMultishape;
+        for j := 0 to multiSel.ShapeCount-1 do
+        begin
+          s := multiSel.GetShape(j).Duplicate;
+          s.Transform(AMatrix);
+          tempContainer.AddShape(s);
+        end;
+      end else
+      begin
+        s := AShapes[i].Duplicate;
+        s.Transform(AMatrix);
+        tempContainer.AddShape(s);
+      end;
     end;
     tempContainer.SaveToStream(mem);
     Clipboard.Clear;
@@ -52,6 +65,7 @@ var
   mem: TMemoryStream;
   i: Integer;
   pastedShape: TVectorShape;
+  pastedShapes: TVectorShapes;
   invMatrix, m: TAffineMatrix;
   pastedBounds: TRectF;
   ofs: TPointF;
@@ -84,14 +98,17 @@ begin
             ofs.y := floor(ABounds.Bottom - pastedBounds.Bottom);
         end;
         m := invMatrix*AffineMatrixTranslation(ofs.x,ofs.y);
+        ATargetContainer.DeselectShapes;
+        pastedShapes := TVectorShapes.Create;
         for i := 0 to tempContainer.ShapeCount-1 do
         begin
           pastedShape := tempContainer.Shape[i].Duplicate;
           pastedShape.Transform(m);
-          ATargetContainer.AddShape(pastedShape);
-          if i = tempContainer.ShapeCount-1 then
-            ATargetContainer.SelectShape(pastedShape);
+          pastedShapes.Add(pastedShape);
         end;
+        ATargetContainer.AddShapes(pastedShapes);
+        ATargetContainer.SelectShapes(pastedShapes);
+        pastedShapes.Free;
       end;
     finally
       tempContainer.Free;

+ 35 - 5
lazpaintcontrols/lcvectorialfill.pas

@@ -10,6 +10,7 @@ uses
 
 type
   TTextureRepetition = (trNone, trRepeatX, trRepeatY, trRepeatBoth);
+  TTransparentMode = (tmEnforeAllChannelsZero, tmAlphaZeroOnly, tmNoFill);
   TVectorialFillType = (vftNone, vftSolid, vftGradient, vftTexture);
   TVectorialFillTypes = set of TVectorialFillType;
   TVectorialFill = class;
@@ -44,6 +45,7 @@ type
   TVectorialFillDiff = class(TCustomVectorialFillDiff)
   protected
     FStart,FEnd: TVectorialFill;
+    FTransparentMode: TTransparentMode;
   public
     constructor Create(AFrom: TVectorialFill);
     procedure ComputeDiff(ATo: TVectorialFill);
@@ -68,6 +70,7 @@ type
     FTextureRepetition: TTextureRepetition;
     FTextureAverageColor: TBGRAPixel;
     FTextureAverageColorComputed: boolean;
+    FTransparentMode: TTransparentMode;
     FGradient: TBGRALayerGradientOriginal;
     FOnChange: TVectorialFillChangeEvent;
     FOnBeforeChange: TNotifyEvent;
@@ -81,6 +84,7 @@ type
     procedure SetTextureMatrix(AValue: TAffineMatrix);
     procedure SetTextureOpacity(AValue: byte);
     procedure SetTextureRepetition(AValue: TTextureRepetition);
+    procedure SetTransparentMode(AValue: TTransparentMode);
     procedure InternalClear;
     procedure BeginUpdate;
     procedure EndUpdate;
@@ -129,6 +133,7 @@ type
     property TextureRepetition: TTextureRepetition read FTextureRepetition write SetTextureRepetition;
     property OnChange: TVectorialFillChangeEvent read FOnChange write SetOnChange;
     property OnBeforeChange: TNotifyEvent read FOnBeforeChange write FOnBeforeChange;
+    property TransparentMode: TTransparentMode read FTransparentMode write SetTransparentMode;
   end;
 
 implementation
@@ -140,12 +145,14 @@ uses BGRAGradientScanner, BGRABlend, LCResourceString;
 constructor TVectorialFillDiff.Create(AFrom: TVectorialFill);
 begin
   FStart := TVectorialFill.Create;
+  FStart.TransparentMode:= AFrom.TransparentMode;
   FStart.Assign(AFrom);
 end;
 
 procedure TVectorialFillDiff.ComputeDiff(ATo: TVectorialFill);
 begin
   FEnd := TVectorialFill.Create;
+  FEnd.TransparentMode := ATo.TransparentMode;
   FEnd.Assign(ATo);
 end;
 
@@ -401,6 +408,7 @@ begin
   FTextureAverageColorComputed:= false;
   FGradient := nil;
   FIsSolid := false;
+  FTransparentMode := tmEnforeAllChannelsZero;
 end;
 
 function TVectorialFill.GetIsEditable: boolean;
@@ -446,13 +454,31 @@ begin
   end;
 end;
 
+procedure TVectorialFill.SetTransparentMode(AValue: TTransparentMode);
+begin
+  if FTransparentMode=AValue then Exit;
+  if (FillType = vftSolid) and (SolidColor.alpha = 0) then
+  begin
+    case FTransparentMode of
+    tmNoFill: Clear;
+    tmEnforeAllChannelsZero: SolidColor := BGRAPixelTransparent;
+    end;
+  end;
+  FTransparentMode:=AValue;
+end;
+
 procedure TVectorialFill.GradientChange(ASender: TObject; ABounds: PRectF; var ADiff: TBGRAOriginalDiff);
 var
   fillDiff: TVectorialFillGradientDiff;
 begin
+  if Assigned(FDiff) then
+  begin
+    FreeAndNil(ADiff);
+    exit;
+  end;
   if Assigned(OnChange) then
   begin
-    if Assigned(FDiff) then
+    if Assigned(ADiff) then
     begin
       fillDiff := TVectorialFillGradientDiff.Create(ADiff as TBGRAGradientOriginalDiff);
       ADiff := nil;
@@ -501,10 +527,14 @@ end;
 
 procedure TVectorialFill.SetSolid(AColor: TBGRAPixel);
 begin
-  if (FillType = vftSolid) and (SolidColor = AColor) then exit;
+  if AColor.alpha = 0 then
+  case TransparentMode of
+  tmNoFill: begin Clear; exit; end;
+  tmEnforeAllChannelsZero: AColor := BGRAPixelTransparent;
+  end;
+  if (FillType = vftSolid) and SolidColor.EqualsExactly(AColor) then exit;
   BeginUpdate;
   InternalClear;
-  if AColor.alpha = 0 then AColor := BGRAPixelTransparent;
   FColor := AColor;
   FIsSolid:= true;
   EndUpdate;
@@ -635,7 +665,7 @@ begin
     else
     begin
       case other.FillType of
-      vftSolid: result := (FillType = vftSolid) and (other.SolidColor = SolidColor);
+      vftSolid: result := (FillType = vftSolid) and other.SolidColor.EqualsExactly(SolidColor);
       vftGradient: result := (FillType = vftGradient) and (other.Gradient.Equals(Gradient));
       vftTexture: result := (FillType = vftTexture) and (other.Texture = Texture) and
                        (other.TextureMatrix = TextureMatrix) and (other.TextureOpacity = TextureOpacity)
@@ -727,7 +757,7 @@ begin
   end;
 end;
 
-procedure TVectorialFill.ApplyOpacity(AOpacity: byte);
+procedure TVectorialFill.ApplyOpacity(AOpacity: Byte);
 var
   c: TBGRAPixel;
 begin

+ 3 - 3
lazpaintcontrols/lcvectorialfillinterface.pas

@@ -904,7 +904,7 @@ end;
 
 procedure TVectorialFillInterface.SetSolidColor(AValue: TBGRAPixel);
 begin
-  if FSolidColor=AValue then Exit;
+  if FSolidColor.EqualsExactly(AValue) then Exit;
   FSolidColor:=AValue;
   UpdateShapeSolidColor;
   If FillType = vftSolid then Changed;
@@ -957,7 +957,7 @@ end;
 
 procedure TVectorialFillInterface.SetGradEndColor(AValue: TBGRAPixel);
 begin
-  if CompareMem(@FGradEndColor,@AValue,sizeof(TBGRAPixel)) then Exit;
+  if FGradEndColor.EqualsExactly(AValue) then Exit;
   FGradEndColor:=AValue;
   UpdateGradientParams;
   if FillType = vftGradient then Changed;
@@ -965,7 +965,7 @@ end;
 
 procedure TVectorialFillInterface.SetGradStartColor(AValue: TBGRAPixel);
 begin
-  if CompareMem(@FGradStartColor,@AValue,sizeof(TBGRAPixel)) then Exit;
+  if FGradStartColor.EqualsExactly(AValue) then Exit;
   FGradStartColor:=AValue;
   UpdateGradientParams;
   if FillType = vftGradient then Changed;

+ 1018 - 0
lazpaintcontrols/lcvectormultishape.pas

@@ -0,0 +1,1018 @@
+unit LCVectorMultishape;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, LCVectorRectShapes, LCVectorOriginal, BGRALayerOriginal,
+  BGRATransform, BGRABitmap, BGRABitmapTypes, BGRAPen, fgl, LCVectorialFill;
+
+type
+  TShapeChangeHandlerMap = specialize TFPGMap<Pointer, TShapeChangeEvent>;
+  TShapeDiffMap = specialize TFPGMap<integer, TVectorShapeDiff>;
+  TIntegerList = specialize TFPGList<integer>;
+
+  { TMultiSelectionShapesDiff }
+
+  TMultiSelectionShapesDiff = class(TCustomMultiSelectionDiff)
+  protected
+    FDiffs: TShapeDiffMap;
+    FSelectedIds: TIntegerList;
+    function GetShapeById(AContainer: TVectorShape; AId: integer): TVectorShape;
+    function GetShapeCount: integer; override;
+    function GetShapeId(AIndex: integer): integer; override;
+  public
+    constructor Create(AStartShape: TVectorShape); override;
+    procedure ComputeDiff(AEndShape: TVectorShape); override;
+    procedure Apply(AStartShape: TVectorShape); override;
+    procedure Unapply(AEndShape: TVectorShape); override;
+    function CanAppend(ADiff: TVectorShapeDiff): boolean; override;
+    procedure Append(ADiff: TVectorShapeDiff); override; //does not preserve ADiff
+    procedure AppendForShape(AShape: TVectorShape; var ADiff: TVectorShapeDiff); //does not preserve ADiff
+    function IsIdentity: boolean; override;
+    destructor Destroy; override;
+    property ShapeCount: integer read GetShapeCount;
+    property ShapeId[AIndex: integer]: integer read GetShapeId;
+  end;
+
+  { TVectorMultiselection }
+
+  TVectorMultiselection = class(TCustomRectShape, IVectorMultishape)
+  protected
+    function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
+    function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
+    function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
+  protected
+    FShapes: TVectorShapes;
+    FOldChangeHandler: TShapeChangeHandlerMap;
+    FOnSelectionChange: TNotifyEvent;
+    FUpdatingFromShape, FInMultiTranformFill: Boolean;
+    function GetCornerPositition: single; override;
+    procedure RestoreChangeHandler(AShape: TVectorShape);
+    procedure AttachChangeHandler(AShape: TVectorShape);
+    procedure ContainedShape_Change(ASender: TObject; ABounds: TRectF; ADiff: TVectorShapeDiff);
+    procedure UpdateFromShapes;
+    procedure UpdateFrameFromShapes;
+    procedure FillChange({%H-}ASender: TObject; var ADiff: TCustomVectorialFillDiff); override;
+    procedure FillBeforeChange({%H-}ASender: TObject); override;
+    procedure SetPenStyle(AValue: TBGRAPenStyle); override;
+    procedure SetPenWidth(AValue: single); override;
+    procedure SetJoinStyle(AValue: TPenJoinStyle); override;
+    procedure SetOutlineWidth(AValue: single); override;
+    function GetIsFront: boolean; override;
+    function GetIsBack: boolean; override;
+    function GetPenVisible(AAssumePenFill: boolean = False): boolean; override;
+    function GetBackVisible: boolean; override;
+    function GetOutlineVisible: boolean; override;
+    procedure NotifySelectionChanged;
+    procedure InternalMoveToIndex(AFirst: integer);
+  public
+    constructor Create(AContainer: TVectorOriginal); override;
+    class function StorageClassName: RawByteString; override;
+    destructor Destroy; override;
+    procedure BeginUpdate(ADiffHandler: TVectorShapeDiffAny=nil); override;
+    procedure EndUpdate; override;
+    procedure BringToFront; override;
+    procedure SendToBack; override;
+    procedure MoveUp(APassNonIntersectingShapes: boolean); override;
+    procedure MoveDown(APassNonIntersectingShapes: boolean); override;
+    procedure ClearShapes;
+    procedure AddShape(AShape: TVectorShape);
+    procedure RemoveShape(AShape: TVectorShape);
+    function ContainsShape(AShape: TVectorShape): boolean;
+    function ShapeCount: integer;
+    function GetShape(AIndex: integer): TVectorShape;
+    function SetShapes(AShapes: TVectorShapes): boolean;
+    function FrontShape: TVectorShape;
+    function BackShape: TVectorShape;
+    function GetShapeById(AId: integer): TVectorShape;
+    function MultiFields: TVectorShapeFields; override;
+    procedure TransformFrame(const AMatrix: TAffineMatrix); override;
+    procedure TransformFill(const AMatrix: TAffineMatrix; ABackOnly: boolean); override;
+    function AllowShearTransform: boolean; override;
+    procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
+    procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
+    procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
+    procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
+    function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; {%H-}AOptions: TRenderBoundsOptions = []): TRectF; override;
+    function GetAlignBounds(const ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
+    function SuggestGradientBox(AMatrix: TAffineMatrix): TAffineBox; override;
+    procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
+    function PointInShape(APoint: TPointF): boolean; overload; override;
+    function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
+    function PointInBack(APoint: TPointF): boolean; overload; override;
+    function PointInPen(APoint: TPointF): boolean; overload; override;
+    function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
+    function GetAsMultishape: IVectorMultishape; override;
+    procedure SetOnSelectionChange(AHandler: TNotifyEvent);
+    function GetOnSelectionChange: TNotifyEvent;
+    property OnSelectionChange: TNotifyEvent read GetOnSelectionChange write SetOnSelectionChange;
+  end;
+
+implementation
+
+function Shapes_CompareDepth(const Item1, Item2: TVectorShape): Integer;
+var
+  idx1, idx2: Integer;
+begin
+  if Assigned(Item1.Container) and Assigned(Item2.Container) then
+  begin
+    idx1 := Item1.Container.IndexOfShape(Item1);
+    idx2 := Item2.Container.IndexOfShape(Item2);
+    result := idx1 - idx2;
+  end;
+end;
+
+{ TMultiSelectionShapesDiff }
+
+function TMultiSelectionShapesDiff.GetShapeCount: integer;
+begin
+  result := FSelectedIds.Count;
+end;
+
+function TMultiSelectionShapesDiff.GetShapeId(AIndex: integer): integer;
+begin
+  result := FSelectedIds.Items[AIndex];
+end;
+
+function TMultiSelectionShapesDiff.GetShapeById(AContainer: TVectorShape; AId: integer): TVectorShape;
+begin
+  result := AContainer.Container.FindShapeById(AId);
+end;
+
+constructor TMultiSelectionShapesDiff.Create(AStartShape: TVectorShape);
+var
+  i: Integer;
+begin
+  if not (AStartShape is TVectorMultiselection) then
+    raise exception.Create('Expecting TVectorMultishape');
+  FDiffs := TShapeDiffMap.Create;
+  FSelectedIds := TIntegerList.Create;
+  with AStartShape.GetAsMultishape do
+  begin
+    for i := 0 to ShapeCount-1 do
+      FSelectedIds.Add(GetShape(i).Id);
+  end;
+end;
+
+procedure TMultiSelectionShapesDiff.ComputeDiff(AEndShape: TVectorShape);
+begin
+  //nothing
+end;
+
+procedure TMultiSelectionShapesDiff.Apply(AStartShape: TVectorShape);
+var
+  s: TVectorShape;
+  i: Integer;
+begin
+  for i := 0 to FDiffs.Count-1 do
+  begin
+    s := GetShapeById(AStartShape, FDiffs.Keys[i]);
+    if Assigned(s) then
+      FDiffs.Data[i].Apply(s);
+  end;
+end;
+
+procedure TMultiSelectionShapesDiff.Unapply(AEndShape: TVectorShape);
+var
+  s: TVectorShape;
+  i: Integer;
+begin
+  for i := FDiffs.Count-1 downto 0 do
+  begin
+    s := GetShapeById(AEndShape, FDiffs.Keys[i]);
+    if Assigned(s) then
+      FDiffs.Data[i].Unapply(s);
+  end;
+end;
+
+function TMultiSelectionShapesDiff.CanAppend(ADiff: TVectorShapeDiff): boolean;
+var
+  other: TMultiSelectionShapesDiff;
+  i, j: Integer;
+begin
+  if not (ADiff is TMultiSelectionShapesDiff) then exit(false);
+  other := TMultiSelectionShapesDiff(ADiff);
+  for i := 0 to other.FDiffs.Count-1 do
+    for j := 0 to FDiffs.Count-1 do
+      if FDiffs.Keys[j] = other.FDiffs.Keys[i] then
+      begin
+        if not FDiffs.Data[j].CanAppend(other.FDiffs.Data[i]) then
+          exit(false);
+      end;
+  result := true;
+end;
+
+procedure TMultiSelectionShapesDiff.Append(ADiff: TVectorShapeDiff);
+var found: boolean;
+  other: TMultiSelectionShapesDiff;
+  otherKey, i, j: integer;
+  otherData: TVectorShapeDiff;
+  toCopy: TShapeDiffMap;
+begin
+  if not (ADiff is TMultiSelectionShapesDiff) then raise exception.Create('Unexpected diff type');
+  other := TMultiSelectionShapesDiff(ADiff);
+  toCopy := TShapeDiffMap.Create;
+  for i := 0 to other.FDiffs.Count-1 do
+  begin
+    found := false;
+    otherKey := other.FDiffs.Keys[i];
+    otherData := other.FDiffs.Data[i];
+    for j := 0 to FDiffs.Count-1 do
+      if FDiffs.Keys[j] = otherKey then
+      begin
+        FDiffs.Data[j].Append(otherData);
+        found := true;
+        break;
+      end;
+    if not found then toCopy.Add(otherKey, otherData);
+  end;
+  for i := 0 to toCopy.Count-1 do
+  begin
+    FDiffs.Add(toCopy.Keys[i], toCopy.Data[i]);
+    other.FDiffs.Remove(toCopy.Keys[i]);
+  end;
+  toCopy.Free;
+end;
+
+procedure TMultiSelectionShapesDiff.AppendForShape(AShape: TVectorShape;
+  var ADiff: TVectorShapeDiff);
+var
+  idx: Integer;
+begin
+  idx := FDiffs.IndexOf(AShape.Id);
+  if idx <> -1 then
+    FDiffs.Data[idx].Append(ADiff)
+  else
+  begin
+    FDiffs.Add(AShape.Id, ADiff);
+    ADiff := nil;
+  end;
+end;
+
+function TMultiSelectionShapesDiff.IsIdentity: boolean;
+var
+  i: Integer;
+begin
+  for i := 0 to FDiffs.Count-1 do
+    if not FDiffs.Data[i].IsIdentity then exit(false);
+  result := true;
+end;
+
+destructor TMultiSelectionShapesDiff.Destroy;
+var
+  i: Integer;
+begin
+  for i := 0 to FDiffs.Count-1 do
+    FDiffs.Data[i].Free;
+  FDiffs.Free;
+  FSelectedIds.Free;
+  inherited Destroy;
+end;
+
+{ TVectorMultiselection }
+
+function TVectorMultiselection.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
+begin
+  if GetInterface(iid, obj) then
+    Result := S_OK
+  else
+    Result := longint(E_NOINTERFACE);
+end;
+
+function TVectorMultiselection._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
+begin
+  result := 0;
+end;
+
+function TVectorMultiselection._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
+begin
+  result := 0;
+end;
+
+procedure TVectorMultiselection.TransformFill(const AMatrix: TAffineMatrix;
+  ABackOnly: boolean);
+var
+  i: Integer;
+begin
+  FInMultiTranformFill := true;
+  BeginUpdate(TMultiSelectionShapesDiff);
+  for i := 0 to FShapes.Count-1 do
+  begin
+    FShapes[i].BeginUpdate;
+    FShapes[i].TransformFrame(AMatrix);
+    FShapes[i].TransformFill(AMatrix, ABackOnly);
+    FShapes[i].EndUpdate;
+  end;
+  inherited TransformFill(AMatrix, ABackOnly);
+  EndUpdate;
+  FInMultiTranformFill := false;
+end;
+
+procedure TVectorMultiselection.ContainedShape_Change(ASender: TObject;
+  ABounds: TRectF; ADiff: TVectorShapeDiff);
+var
+  contained: TMultiSelectionShapesDiff;
+begin
+  if Assigned(ADiff) then
+  begin
+    contained := TMultiSelectionShapesDiff(AddDiffHandler(TMultiSelectionShapesDiff));
+    contained.AppendForShape(ASender as TVectorShape, ADiff);
+    ADiff.Free;
+  end else
+  begin
+    if not IsUpdating and Assigned(OnChange) then
+      OnChange(self, ABounds, nil);
+  end;
+end;
+
+procedure TVectorMultiselection.UpdateFromShapes;
+var
+  i: Integer;
+  found: boolean;
+begin
+  if FShapes.Count > 0 then
+  begin
+    FUpdatingFromShape := true;
+    BeginEditingUpdate;
+    UpdateFrameFromShapes;
+
+    found := false;
+    for i := 0 to FShapes.Count-1 do
+      if (vsfPenFill in FShapes[i].Fields) and
+         FShapes[i].PenVisible then
+      begin
+        PenFill.Assign(FShapes[i].PenFill);
+        found := true;
+        break;
+      end;
+    if not found then PenFill.Clear;
+
+    found := false;
+    for i := 0 to FShapes.Count-1 do
+      if (vsfPenStyle in FShapes[i].Fields) and
+         FShapes[i].PenVisible then
+      begin
+        Stroker.CustomPenStyle := FShapes[i].PenStyle;
+        found := true;
+        break;
+      end;
+    if not found then Stroker.CustomPenStyle := ClearPenStyle;
+
+    for i := 0 to FShapes.Count-1 do
+      if (vsfPenWidth in FShapes[i].Fields) and
+         FShapes[i].PenVisible then
+      begin
+        FPenWidth := FShapes[i].PenWidth;
+        break;
+      end;
+
+    for i := 0 to FShapes.Count-1 do
+      if vsfJoinStyle in FShapes[i].Fields then
+      begin
+        Stroker.JoinStyle := FShapes[i].JoinStyle;
+        break;
+      end;
+
+    found := false;
+    for i := 0 to FShapes.Count-1 do
+      if (vsfBackFill in FShapes[i].Fields) and
+         FShapes[i].BackVisible then
+      begin
+        BackFill.Assign(FShapes[i].BackFill);
+        found := true;
+        break;
+      end;
+    if not found then BackFill.Clear;
+
+    found := false;
+    for i := 0 to FShapes.Count-1 do
+      if (vsfOutlineFill in FShapes[i].Fields) and
+         FShapes[i].OutlineVisible then
+      begin
+        OutlineFill.Assign(FShapes[i].OutlineFill);
+        found := true;
+        break;
+      end;
+    if not found then OutlineFill.Clear;
+
+    for i := 0 to FShapes.Count-1 do
+      if (vsfOutlineWidth in FShapes[i].Fields) and
+         FShapes[i].OutlineVisible then
+      begin
+        FOutlineWidth:= FShapes[i].OutlineWidth;
+        break;
+      end;
+
+    EndEditingUpdate;
+    FUpdatingFromShape := false;
+  end;
+end;
+
+procedure TVectorMultiselection.UpdateFrameFromShapes;
+var
+  rF: TRectF;
+  i: Integer;
+begin
+  BeginEditingUpdate;
+  rF := EmptyRectF;
+  for i := 0 to FShapes.Count-1 do
+    rF := rF.Union(FShapes[i].GetAlignBounds(InfiniteRect, AffineMatrixIdentity), true);
+  FOrigin := (rF.TopLeft + rf.BottomRight)*0.5;
+  FXAxis := FOrigin + PointF(rF.Width/2, 0);
+  FYAxis := FOrigin + PointF(0, rF.Height/2);
+  EndEditingUpdate;
+end;
+
+procedure TVectorMultiselection.FillChange(ASender: TObject;
+  var ADiff: TCustomVectorialFillDiff);
+var
+  i: Integer;
+begin
+  if FUpdatingFromShape or FInMultiTranformFill then exit;
+  BeginUpdate;
+  AddFillDiffHandler(ASender as TVectorialFill, ADiff);
+  if ASender = PenFill then
+  begin
+    for i := 0 to FShapes.Count-1 do
+      if vsfPenFill in FShapes[i].Fields then
+      begin
+        if not PenFill.IsFullyTransparent or FShapes[i].BackVisible or FShapes[i].OutlineVisible then
+        FShapes[i].PenFill.Assign(PenFill);
+      end;
+  end else
+  if ASender = BackFill then
+  begin
+    for i := 0 to FShapes.Count-1 do
+      if vsfBackFill in FShapes[i].Fields then
+      begin
+        if not BackFill.IsFullyTransparent or FShapes[i].PenVisible or FShapes[i].OutlineVisible then
+          FShapes[i].BackFill.Assign(BackFill);
+      end;
+  end else
+  if ASender = OutlineFill then
+  begin
+    for i := 0 to FShapes.Count-1 do
+      if vsfOutlineFill in FShapes[i].Fields then
+      begin
+        if not OutlineFill.IsFullyTransparent or FShapes[i].PenVisible or FShapes[i].BackVisible then
+          FShapes[i].OutlineFill.Assign(OutlineFill);
+      end;
+  end;
+  EndUpdate;
+end;
+
+procedure TVectorMultiselection.FillBeforeChange(ASender: TObject);
+begin
+  //nothing
+end;
+
+procedure TVectorMultiselection.SetPenStyle(AValue: TBGRAPenStyle);
+var
+  i: Integer;
+begin
+  if PenStyleEqual(AValue, PenStyle) then exit;
+  BeginUpdate;
+  inherited SetPenStyle(AValue);
+  for i := 0 to FShapes.Count-1 do
+    if vsfPenStyle in FShapes[i].Fields then
+    begin
+      if not IsClearPenStyle(AValue) or FShapes[i].BackVisible then
+        FShapes[i].PenStyle := AValue;
+    end;
+  EndUpdate;
+end;
+
+procedure TVectorMultiselection.SetPenWidth(AValue: single);
+var
+  i: Integer;
+begin
+  if AValue < 0 then AValue := 0;
+  if AValue = PenWidth then exit;
+  BeginUpdate;
+  inherited SetPenWidth(AValue);
+  for i := 0 to FShapes.Count-1 do
+    if vsfPenWidth in FShapes[i].Fields then
+    begin
+      if (AValue > 0) or FShapes[i].BackVisible then
+        FShapes[i].PenWidth := AValue;
+    end;
+  EndUpdate;
+end;
+
+procedure TVectorMultiselection.SetJoinStyle(AValue: TPenJoinStyle);
+var
+  i: Integer;
+begin
+  if AValue = JoinStyle then exit;
+  BeginUpdate;
+  inherited SetJoinStyle(AValue);
+  for i := 0 to FShapes.Count-1 do
+    if vsfJoinStyle in FShapes[i].Fields then
+      FShapes[i].JoinStyle := AValue;
+  EndUpdate;
+end;
+
+procedure TVectorMultiselection.SetOutlineWidth(AValue: single);
+var
+  i: Integer;
+begin
+  if AValue < 0 then AValue := 0;
+  if AValue = OutlineWidth then exit;
+  BeginUpdate;
+  inherited SetOutlineWidth(AValue);
+  for i := 0 to FShapes.Count-1 do
+    if vsfOutlineWidth in FShapes[i].Fields then
+      FShapes[i].OutlineWidth := AValue;
+  EndUpdate;
+end;
+
+function TVectorMultiselection.GetIsFront: boolean;
+var
+  i, containerIdx: Integer;
+  s: TVectorShape;
+begin
+  s := FrontShape;
+  if not Assigned(s) or not s.IsFront then exit(false);
+  containerIdx := Container.IndexOfShape(s);
+  for i := FShapes.Count-2 downto 0 do
+  begin
+    dec(containerIdx);
+    if Container.IndexOfShape(FShapes[i]) <> containerIdx then
+      exit(false);
+  end;
+  result := true;
+end;
+
+function TVectorMultiselection.GetIsBack: boolean;
+var
+  i, containerIdx: Integer;
+  s: TVectorShape;
+begin
+  s := BackShape;
+  if not Assigned(s) or not s.IsBack then exit(false);
+  containerIdx := Container.IndexOfShape(s);
+  for i := 1 to FShapes.Count-1 do
+  begin
+    inc(containerIdx);
+    if Container.IndexOfShape(FShapes[i]) <> containerIdx then
+      exit(false);
+  end;
+  result := true;
+end;
+
+function TVectorMultiselection.GetPenVisible(AAssumePenFill: boolean): boolean;
+var
+  i: Integer;
+begin
+  for i := 0 to ShapeCount-1 do
+    if FShapes[i].PenVisible then exit(true);
+  result := false;
+end;
+
+function TVectorMultiselection.GetBackVisible: boolean;
+var
+  i: Integer;
+begin
+  for i := 0 to ShapeCount-1 do
+    if FShapes[i].BackVisible then exit(true);
+  result := false;
+end;
+
+function TVectorMultiselection.GetOutlineVisible: boolean;
+var
+  i: Integer;
+begin
+  for i := 0 to ShapeCount-1 do
+    if FShapes[i].OutlineVisible then exit(true);
+  Result:= false;
+end;
+
+procedure TVectorMultiselection.NotifySelectionChanged;
+begin
+  if OnSelectionChange <> nil then
+    OnSelectionChange(self);
+end;
+
+procedure TVectorMultiselection.InternalMoveToIndex(AFirst: integer);
+var fromIndex, toIndex: array of integer;
+  i: Integer;
+begin
+  if Container = nil then exit;
+  setlength(fromIndex, ShapeCount);
+  setlength(toIndex, ShapeCount);
+  for i := 0 to ShapeCount-1 do
+  begin
+    fromIndex[i] := Container.IndexOfShape(FShapes[i]);
+    toIndex[i] := AFirst + i;
+  end;
+  Container.MoveShapeToIndex(fromIndex, toIndex);
+end;
+
+function TVectorMultiselection.GetCornerPositition: single;
+begin
+  result := 1;
+end;
+
+procedure TVectorMultiselection.RestoreChangeHandler(AShape: TVectorShape);
+var
+  handlerIndex: Integer;
+begin
+  if AShape.OnChange <> @ContainedShape_Change then exit;
+  handlerIndex := FOldChangeHandler.IndexOf(AShape);
+  if handlerIndex <> -1 then
+  begin
+    AShape.OnChange:= FOldChangeHandler.Data[handlerIndex];
+    FOldChangeHandler.Delete(handlerIndex);
+  end
+  else
+    AShape.OnChange:= nil;
+end;
+
+procedure TVectorMultiselection.AttachChangeHandler(AShape: TVectorShape);
+begin
+  if AShape.OnChange <> @ContainedShape_Change then
+  begin
+    FOldChangeHandler.Add(AShape, AShape.OnChange);
+    AShape.OnChange:= @ContainedShape_Change;
+  end;
+end;
+
+function TVectorMultiselection.AllowShearTransform: boolean;
+var
+  i: Integer;
+begin
+  for i := 0 to FShapes.Count-1 do
+    if not FShapes[i].AllowShearTransform then exit(false);
+  result := true;
+end;
+
+procedure TVectorMultiselection.LoadFromStorage(AStorage: TBGRACustomOriginalStorage);
+begin
+  raise exception.Create('Cannot be deserialized');
+end;
+
+procedure TVectorMultiselection.SaveToStorage(AStorage: TBGRACustomOriginalStorage);
+begin
+  raise exception.Create('Cannot be serialized');
+end;
+
+procedure TVectorMultiselection.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
+  ADraft: boolean);
+var
+  i: Integer;
+begin
+  for i := 0 to FShapes.Count-1 do
+    FShapes[i].Render(ADest, AMatrix, ADraft);
+end;
+
+procedure TVectorMultiselection.Render(ADest: TBGRABitmap; ARenderOffset: TPoint;
+  AMatrix: TAffineMatrix; ADraft: boolean);
+var
+  i: Integer;
+begin
+  for i := 0 to FShapes.Count-1 do
+    FShapes[i].Render(ADest, ARenderOffset, AMatrix, ADraft);
+end;
+
+function TVectorMultiselection.GetRenderBounds(ADestRect: TRect;
+  AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions): TRectF;
+var
+  i: Integer;
+begin
+  result := EmptyRectF;
+  for i := 0 to FShapes.Count-1 do
+    result := result.Union(FShapes[i].GetRenderBounds(ADestRect, AMatrix, AOptions), true);
+end;
+
+function TVectorMultiselection.GetAlignBounds(const ALayoutRect: TRect;
+  const AMatrix: TAffineMatrix): TRectF;
+var
+  i: Integer;
+begin
+  result := EmptyRectF;
+  for i := 0 to FShapes.Count-1 do
+    result := result.Union(FShapes[i].GetAlignBounds(ALayoutRect, AMatrix), true);
+end;
+
+function TVectorMultiselection.SuggestGradientBox(AMatrix: TAffineMatrix): TAffineBox;
+var
+  i: Integer;
+  r: TRectF;
+begin
+  if FShapes.Count = 1 then
+    result := FShapes[i].SuggestGradientBox(AMatrix)
+  else
+  begin
+    r := EmptyRectF;
+    for i := 0 to FShapes.Count-1 do
+      r := r.Union(FShapes[i].SuggestGradientBox(AMatrix).RectBoundsF, true);
+    result := TAffineBox.AffineBox(r);
+  end;
+end;
+
+procedure TVectorMultiselection.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
+var
+  i: Integer;
+  ab: TAffineBox;
+begin
+  for i := 0 to FShapes.Count-1 do
+  begin
+    ab := FShapes[i].SuggestGradientBox(AffineMatrixIdentity);
+    AEditor.AddPolyline(ab.AsPolygon, true, opsDash);
+  end;
+  inherited ConfigureCustomEditor(AEditor);
+end;
+
+function TVectorMultiselection.PointInShape(APoint: TPointF): boolean;
+var
+  i: LongInt;
+begin
+  for i := FShapes.Count-1 downto 0 do
+    if FShapes[i].PointInShape(APoint) then exit(true);
+  result := false;
+end;
+
+function TVectorMultiselection.PointInShape(APoint: TPointF; ARadius: single): boolean;
+var
+  i: LongInt;
+begin
+  for i := FShapes.Count-1 downto 0 do
+    if FShapes[i].PointInShape(APoint, ARadius) then exit(true);
+  result := false;
+end;
+
+function TVectorMultiselection.PointInBack(APoint: TPointF): boolean;
+var
+  i: LongInt;
+begin
+  for i := FShapes.Count-1 downto 0 do
+    if FShapes[i].PointInBack(APoint) then exit(true);
+  result := false;
+end;
+
+function TVectorMultiselection.PointInPen(APoint: TPointF): boolean;
+var
+  i: LongInt;
+begin
+  for i := FShapes.Count-1 downto 0 do
+    if FShapes[i].PointInPen(APoint) then exit(true);
+  result := false;
+end;
+
+function TVectorMultiselection.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
+var
+  i: LongInt;
+begin
+  if FShapes.Count >= 5 then exit(true);
+  for i := 0 to FShapes.Count-1 do
+    if FShapes[i].GetIsSlow(AMatrix) then exit(true);
+  result := false;
+end;
+
+function TVectorMultiselection.GetAsMultishape: IVectorMultishape;
+begin
+  Result:= self;
+end;
+
+procedure TVectorMultiselection.SetOnSelectionChange(AHandler: TNotifyEvent);
+begin
+  FOnSelectionChange := AHandler;
+end;
+
+function TVectorMultiselection.GetOnSelectionChange: TNotifyEvent;
+begin
+  result := FOnSelectionChange;
+end;
+
+class function TVectorMultiselection.StorageClassName: RawByteString;
+begin
+  result := 'multishape';
+end;
+
+procedure TVectorMultiselection.ClearShapes;
+var
+  i: Integer;
+begin
+  if FShapes.Count = 0 then exit;
+  for i := 0 to FShapes.Count-1 do
+    RestoreChangeHandler(FShapes[i]);
+  FShapes.Clear;
+  UpdateFromShapes;
+  NotifySelectionChanged;
+end;
+
+procedure TVectorMultiselection.AddShape(AShape: TVectorShape);
+begin
+  if ContainsShape(AShape) then exit;
+  FShapes.Add(AShape);
+  FShapes.Sort(@Shapes_CompareDepth);
+  AttachChangeHandler(AShape);
+  UpdateFromShapes;
+  NotifySelectionChanged;
+end;
+
+procedure TVectorMultiselection.RemoveShape(AShape: TVectorShape);
+begin
+  RestoreChangeHandler(AShape);
+  FShapes.Remove(AShape);
+  UpdateFromShapes;
+  NotifySelectionChanged;
+end;
+
+function TVectorMultiselection.ContainsShape(AShape: TVectorShape): boolean;
+begin
+  result := FShapes.IndexOf(AShape) <> -1;
+end;
+
+function TVectorMultiselection.ShapeCount: integer;
+begin
+  result := FShapes.Count;
+end;
+
+function TVectorMultiselection.GetShape(AIndex: integer): TVectorShape;
+begin
+  if (AIndex < 0) or (AIndex >= FShapes.Count) then
+    raise exception.Create('Index out of bounds');
+  result := FShapes[AIndex];
+end;
+
+function TVectorMultiselection.SetShapes(AShapes: TVectorShapes): boolean;
+var
+  i: Integer;
+  different: Boolean;
+begin
+  different := false;
+  for i := 0 to FShapes.Count-1 do
+    if AShapes.IndexOf(FShapes[i]) = -1 then
+    begin
+      different := true;
+      break;
+    end;
+  for i := 0 to AShapes.Count-1 do
+    if FShapes.IndexOf(AShapes[i]) = -1 then
+    begin
+      different := true;
+      break;
+    end;
+  if not different then exit(false);
+
+  for i := 0 to FShapes.Count-1 do
+    RestoreChangeHandler(FShapes[i]);
+  FShapes.Clear;
+
+  for i := 0 to AShapes.Count-1 do
+  begin
+    FShapes.Add(AShapes[i]);
+    AttachChangeHandler(AShapes[i]);
+  end;
+  FShapes.Sort(@Shapes_CompareDepth);
+  UpdateFromShapes;
+  NotifySelectionChanged;
+  exit(true);
+end;
+
+function TVectorMultiselection.FrontShape: TVectorShape;
+begin
+  if FShapes.Count > 0 then
+    result := FShapes[FShapes.Count-1]
+    else result := nil;
+end;
+
+function TVectorMultiselection.BackShape: TVectorShape;
+begin
+  if FShapes.Count > 0 then
+    result := FShapes[0]
+    else result := nil;
+end;
+
+function TVectorMultiselection.GetShapeById(AId: integer): TVectorShape;
+var
+  i: Integer;
+begin
+  for i := 0 to FShapes.Count-1 do
+    if FShapes[i].Id = AId then exit(FShapes[i]);
+  result := nil;
+end;
+
+function TVectorMultiselection.MultiFields: TVectorShapeFields;
+var
+  i: Integer;
+begin
+  result := [];
+  for i := 0 to FShapes.Count-1 do
+    result += FShapes[i].Fields;
+end;
+
+constructor TVectorMultiselection.Create(AContainer: TVectorOriginal);
+begin
+  inherited Create(AContainer);
+  FShapes := TVectorShapes.Create;
+  FOldChangeHandler := TShapeChangeHandlerMap.Create;
+end;
+
+procedure TVectorMultiselection.TransformFrame(const AMatrix: TAffineMatrix);
+var
+  i: Integer;
+begin
+  BeginUpdate(TMultiSelectionShapesDiff);
+  for i := 0 to FShapes.Count-1 do
+    FShapes[i].TransformFrame(AMatrix);
+  inherited TransformFrame(AMatrix);
+  EndUpdate;
+end;
+
+destructor TVectorMultiselection.Destroy;
+begin
+  ClearShapes;
+  FShapes.Free;
+  FOldChangeHandler.Free;
+  inherited Destroy;
+end;
+
+procedure TVectorMultiselection.BeginUpdate(ADiffHandler: TVectorShapeDiffAny);
+var
+  i: Integer;
+begin
+  inherited BeginUpdate(ADiffHandler);
+  for i := 0 to FShapes.Count-1 do
+    FShapes[i].BeginUpdate;
+end;
+
+procedure TVectorMultiselection.EndUpdate;
+var
+  i: Integer;
+begin
+  for i := 0 to FShapes.Count-1 do
+    FShapes[i].EndUpdate;
+  inherited EndUpdate;
+end;
+
+procedure TVectorMultiselection.BringToFront;
+begin
+  if Assigned(Container) then
+    InternalMoveToIndex(Container.ShapeCount - ShapeCount);
+end;
+
+procedure TVectorMultiselection.SendToBack;
+begin
+  InternalMoveToIndex(0);
+end;
+
+procedure TVectorMultiselection.MoveUp(APassNonIntersectingShapes: boolean);
+var
+  topIndex, i: Integer;
+  curBounds: TRectF;
+  touch: Boolean;
+begin
+  if Container = nil then exit;
+  topIndex := Container.IndexOfShape(FrontShape);
+  while topIndex < Container.ShapeCount-1 do
+  begin
+    inc(topIndex);
+    curBounds := Container.Shape[topIndex].GetAlignBounds(InfiniteRect, AffineMatrixIdentity);
+    if not APassNonIntersectingShapes then break;
+    touch := false;
+    for i := 0 to ShapeCount-1 do
+      if FShapes[i].GetAlignBounds(InfiniteRect, AffineMatrixIdentity).IntersectsWith(curBounds) then
+      begin
+        touch := true;
+        break;
+      end;
+    if touch then break;
+  end;
+  InternalMoveToIndex(topIndex + 1 - ShapeCount);
+end;
+
+procedure TVectorMultiselection.MoveDown(APassNonIntersectingShapes: boolean);
+var
+  bottomIndex, i: Integer;
+  curBounds: TRectF;
+  touch: Boolean;
+begin
+  if Container = nil then exit;
+  bottomIndex := Container.IndexOfShape(FrontShape);
+  while bottomIndex > 0 do
+  begin
+    dec(bottomIndex);
+    curBounds := Container.Shape[bottomIndex].GetAlignBounds(InfiniteRect, AffineMatrixIdentity);
+    if not APassNonIntersectingShapes then break;
+    touch := false;
+    for i := 0 to ShapeCount-1 do
+      if FShapes[i].GetAlignBounds(InfiniteRect, AffineMatrixIdentity).IntersectsWith(curBounds) then
+      begin
+        touch := true;
+        break;
+      end;
+    if touch then break;
+  end;
+  InternalMoveToIndex(bottomIndex);
+end;
+
+initialization
+
+  VectorMultiselectionFactory := TVectorMultiselection;
+
+end.
+

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 423 - 112
lazpaintcontrols/lcvectororiginal.pas


+ 51 - 31
lazpaintcontrols/lcvectorpolyshapes.pas

@@ -118,7 +118,7 @@ type
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
-    procedure Transform(const AMatrix: TAffineMatrix); override;
+    procedure TransformFrame(const AMatrix: TAffineMatrix); override;
     class function Usermodes: TVectorShapeUsermodes; override;
     class function DefaultArrowSize: TPointF;
     property Points[AIndex:integer]: TPointF read GetPoint write SetPoint;
@@ -136,15 +136,14 @@ type
   { TPolylineShape }
 
   TPolylineShape = class(TCustomPolypointShape)
-  protected
-    function PenVisible(AAssumePenFill: boolean = false): boolean;
-    function BackVisible: boolean;
   public
     class function Fields: TVectorShapeFields; override;
-    procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
+    procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
+    function PointInBack(APoint: TPointF): boolean; overload; override;
+    function PointInPen(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
     class function StorageClassName: RawByteString; override;
   end;
@@ -1145,7 +1144,7 @@ begin
     FCenterPointEditorIndex := -1;
 end;
 
-procedure TCustomPolypointShape.Transform(const AMatrix: TAffineMatrix);
+procedure TCustomPolypointShape.TransformFrame(const AMatrix: TAffineMatrix);
 var
   i: Integer;
   m: TAffineMatrix;
@@ -1154,22 +1153,11 @@ begin
   m := MatrixForPixelCentered(AMatrix);
   for i := 0 to PointCount-1 do
     FPoints[i].coord := m*FPoints[i].coord;
-  inherited Transform(AMatrix);
   EndUpdate;
 end;
 
 { TPolylineShape }
 
-function TPolylineShape.PenVisible(AAssumePenFill: boolean): boolean;
-begin
-  result := (PenWidth>0) and not IsClearPenStyle(PenStyle) and (not PenFill.IsFullyTransparent or AAssumePenFill);
-end;
-
-function TPolylineShape.BackVisible: boolean;
-begin
-  result := not BackFill.IsFullyTransparent;
-end;
-
 class function TPolylineShape.Fields: TVectorShapeFields;
 begin
   Result:= [vsfPenFill, vsfPenWidth, vsfPenStyle, vsfJoinStyle, vsfBackFill];
@@ -1181,9 +1169,9 @@ var
   pts: array of TPointF;
   backScan, penScan: TBGRACustomScanner;
 begin
-  if not BackVisible and not PenVisible then exit;
+  if not GetBackVisible and not GetPenVisible then exit;
   pts := GetCurve(AMatrix);
-  if BackVisible then
+  if GetBackVisible then
   begin
     if BackFill.FillType = vftSolid then backScan := nil
     else backScan := BackFill.CreateScanner(AMatrix, ADraft);
@@ -1203,7 +1191,7 @@ begin
 
     backScan.Free;
   end;
-  if PenVisible then
+  if GetPenVisible then
   begin
     if PenFill.FillType = vftSolid then penScan := nil
     else penScan := PenFill.CreateScanner(AMatrix, ADraft);
@@ -1232,12 +1220,12 @@ var
   xMargin, yMargin: single;
   fillBounds, penBounds: TRectF;
 begin
-  if not (BackVisible or (rboAssumeBackFill in AOptions)) and not PenVisible(rboAssumePenFill in AOptions) then
+  if not (GetBackVisible or (rboAssumeBackFill in AOptions)) and not GetPenVisible(rboAssumePenFill in AOptions) then
     result:= EmptyRectF
   else
   begin
     pts := GetCurve(AMatrix);
-    if PenVisible(rboAssumePenFill in AOptions) then
+    if GetPenVisible(rboAssumePenFill in AOptions) then
     begin
       if (JoinStyle = pjsRound) and (ArrowStartKind = akNone) and (ArrowEndKind = akNone) then
       begin
@@ -1250,7 +1238,7 @@ begin
         result.Bottom += yMargin;
       end else
       begin
-        if BackVisible or (rboAssumeBackFill in AOptions) then fillBounds := GetPointsBoundsF(pts)
+        if GetBackVisible or (rboAssumeBackFill in AOptions) then fillBounds := GetPointsBoundsF(pts)
         else fillBounds := EmptyRectF;
         pts := ComputeStroke(pts, Closed, AMatrix);
         penBounds := GetPointsBoundsF(pts);
@@ -1267,10 +1255,10 @@ function TPolylineShape.PointInShape(APoint: TPointF): boolean;
 var
   pts: ArrayOfTPointF;
 begin
-  if not BackVisible and not PenVisible then exit(false);
+  if not GetBackVisible and not GetPenVisible then exit(false);
   pts := GetCurve(AffineMatrixIdentity);
-  if BackVisible and IsPointInPolygon(pts, APoint, true) then exit(true);
-  if PenVisible then
+  if GetBackVisible and IsPointInPolygon(pts, APoint, true) then exit(true);
+  if GetPenVisible then
   begin
     pts := ComputeStroke(pts, Closed, AffineMatrixIdentity);
     if IsPointInPolygon(pts, APoint, true) then exit(true);
@@ -1282,12 +1270,44 @@ function TPolylineShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
 var
   pts: ArrayOfTPointF;
 begin
-  if not BackVisible and not PenVisible then exit(false);
+  if not GetBackVisible and not GetPenVisible then exit(false);
   pts := GetCurve(AffineMatrixIdentity);
   pts := ComputeStrokeEnvelope(pts, Closed, ARadius*2);
   result := IsPointInPolygon(pts, APoint, true);
 end;
 
+function TPolylineShape.PointInBack(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
+  scan: TBGRACustomScanner;
+begin
+  if GetBackVisible then
+  begin
+    pts := GetCurve(AffineMatrixIdentity);
+    result := IsPointInPolygon(pts, APoint, true);
+    if result and (BackFill.FillType = vftTexture) then
+    begin
+      scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
+      if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
+      scan.Free;
+    end;
+  end else
+    result := false;
+end;
+
+function TPolylineShape.PointInPen(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
+begin
+  if GetBackVisible then
+  begin
+    pts := GetCurve(AffineMatrixIdentity);
+    pts := ComputeStroke(pts, Closed, AffineMatrixIdentity);
+    result := IsPointInPolygon(pts, APoint, true);
+  end else
+    result := false;
+end;
+
 function TPolylineShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 var pts: ArrayOfTPointF;
   i: Integer;
@@ -1295,13 +1315,13 @@ var pts: ArrayOfTPointF;
   backSurface: Single;
   penLength, zoomFactor, penSurface, totalSurface: single;
 begin
-  if not PenVisible and not BackVisible or (PointCount = 0) then exit(false);
+  if not GetPenVisible and not GetBackVisible or (PointCount = 0) then exit(false);
 
   setlength(pts, PointCount);
   for i := 0 to high(pts) do
     pts[i] := AMatrix * Points[i];
 
-  if PenVisible then
+  if GetPenVisible then
   begin
     penLength := 0;
     zoomFactor := max(VectLen(AMatrix[1,1],AMatrix[2,1]), VectLen(AMatrix[1,2],AMatrix[2,2]));
@@ -1321,14 +1341,14 @@ begin
     penSurface := penLength*PenWidth*zoomFactor;
   end else penSurface := 0;
 
-  if BackVisible then
+  if GetBackVisible then
   begin
     ptsBounds := GetPointsBoundsF(pts);
     backSurface := ptsBounds.Width*ptsBounds.Height;
   end else
     backSurface := 0;
 
-  if PenVisible and BackVisible then totalSurface := backSurface+penSurface/2
+  if GetPenVisible and GetBackVisible then totalSurface := backSurface+penSurface/2
   else totalSurface := backSurface+penSurface;
 
   Result:= (PointCount > 40) or

+ 126 - 86
lazpaintcontrols/lcvectorrectshapes.pas

@@ -62,10 +62,9 @@ type
     procedure OnMoveXYNegCornerAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
     procedure OnMoveXNegYNegCornerAlt({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; AShift: TShiftState);
     procedure OnStartMove({%H-}ASender: TObject; {%H-}APointIndex: integer; {%H-}AShift: TShiftState);
-    procedure UpdateFillMatrixFromRect;
+    procedure UpdateFillFromRectDiff;
     function GetCornerPositition: single; virtual; abstract;
     function GetOrthoRect(AMatrix: TAffineMatrix; out ARect: TRectF): boolean;
-    function AllowShearTransform: boolean; virtual;
     function ShowArrows: boolean; virtual;
     procedure SetOrigin(AValue: TPointF);
     function GetHeight: single;
@@ -82,7 +81,7 @@ type
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; {%H-}AOptions: TRenderBoundsOptions = []): TRectF; override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     function GetAffineBox(const AMatrix: TAffineMatrix; APixelCentered: boolean): TAffineBox;
-    procedure Transform(const AMatrix: TAffineMatrix); override;
+    procedure TransformFrame(const AMatrix: TAffineMatrix); override;
     procedure AlignTransform(const AMatrix: TAffineMatrix); override;
     property Origin: TPointF read FOrigin write SetOrigin;
     property XAxis: TPointF read FXAxis write SetXAxis;
@@ -96,15 +95,15 @@ type
 
   TRectShape = class(TCustomRectShape)
   protected
-    function PenVisible(AAssumePenFill: boolean = false): boolean;
-    function BackVisible: boolean;
     function GetCornerPositition: single; override;
   public
     class function Fields: TVectorShapeFields; override;
-    procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
+    procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
+    function PointInBack(APoint: TPointF): boolean; overload; override;
+    function PointInPen(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     class function StorageClassName: RawByteString; override;
   end;
@@ -113,17 +112,17 @@ type
 
   TEllipseShape = class(TCustomRectShape)
   protected
-    function PenVisible(AAssumePenFill: boolean = false): boolean;
-    function BackVisible: boolean;
     function GetCornerPositition: single; override;
   public
     constructor Create(AContainer: TVectorOriginal); override;
     class function Fields: TVectorShapeFields; override;
     function GetAlignBounds(const {%H-}ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
-    procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
+    procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
+    function PointInBack(APoint: TPointF): boolean; overload; override;
+    function PointInPen(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     class function StorageClassName: RawByteString; override;
   end;
@@ -171,10 +170,7 @@ type
     procedure SetLightPosition(AValue: TPointF);
     procedure SetShapeAltitudePercent(AValue: single);
     procedure SetShapeKind(AValue: TPhongShapeKind);
-    function BackVisible: boolean;
     function GetEnvelope: ArrayOfTPointF;
-  protected
-    function AllowShearTransform: boolean; override;
   public
     constructor Create(AContainer: TVectorOriginal); override;
     destructor Destroy; override;
@@ -186,13 +182,15 @@ type
     procedure MouseDown(RightButton: boolean; Shift: TShiftState; X, Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean); override;
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
-    procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
+    procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
+    function PointInBack(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     function GetGenericCost: integer; override;
     procedure Transform(const AMatrix: TAffineMatrix); override;
+    function AllowShearTransform: boolean; override;
     class function StorageClassName: RawByteString; override;
     property ShapeKind: TPhongShapeKind read FShapeKind write SetShapeKind;
     property LightPosition: TPointF read FLightPosition write SetLightPosition;
@@ -354,8 +352,7 @@ begin
   FOrigin := AValue;
   FXAxis := t*FXAxis;
   FYAxis := t*FYAxis;
-  if vsfBackFill in Fields then BackFill.Transform(t);
-  if vsfPenFill in Fields then PenFill.Transform(t);
+  TransformFill(t, False);
   EndUpdate;
 end;
 
@@ -499,7 +496,7 @@ begin
     end;
   end;
   EnsureRatio(-AFactor,0);
-  UpdateFillMatrixFromRect;
+  UpdateFillFromRectDiff;
   EndUpdate;
 end;
 
@@ -538,7 +535,7 @@ begin
     end;
   end;
   EnsureRatio(0,-AFactor);
-  UpdateFillMatrixFromRect;
+  UpdateFillFromRectDiff;
   EndUpdate;
 end;
 
@@ -598,7 +595,7 @@ begin
     end;
   end;
   EnsureRatio(-AFactorX,-AFactorY);
-  UpdateFillMatrixFromRect;
+  UpdateFillFromRectDiff;
   EndUpdate;
 end;
 
@@ -719,18 +716,15 @@ begin
   FMatrixBackup := AffineMatrix(FXAxis-FOrigin, FYAxis-FOrigin, FOrigin);
 end;
 
-procedure TCustomRectShape.UpdateFillMatrixFromRect;
+procedure TCustomRectShape.UpdateFillFromRectDiff;
 var
   newMatrix, matrixDiff: TAffineMatrix;
 begin
   newMatrix := AffineMatrix(FXAxis-FOrigin, FYAxis-FOrigin, FOrigin);
   if IsAffineMatrixInversible(newMatrix) and IsAffineMatrixInversible(FMatrixBackup) then
   begin
-    if vsfBackFill in Fields then
-    begin
-      matrixDiff := newMatrix*AffineMatrixInverse(FMatrixBackup);
-      BackFill.Transform(matrixDiff);
-    end;
+    matrixDiff := newMatrix*AffineMatrixInverse(FMatrixBackup);
+    TransformFill(matrixDiff, True);
     FMatrixBackup := newMatrix;
   end;
 end;
@@ -747,7 +741,7 @@ begin
       FXAxis - (FYAxis - FOrigin), FYAxis - (FXAxis - FOrigin));
 end;
 
-procedure TCustomRectShape.Transform(const AMatrix: TAffineMatrix);
+procedure TCustomRectShape.TransformFrame(const AMatrix: TAffineMatrix);
 var
   m: TAffineMatrix;
 begin
@@ -756,7 +750,6 @@ begin
   FOrigin := m*FOrigin;
   FXAxis := m*FXAxis;
   FYAxis := m*FYAxis;
-  inherited Transform(AMatrix);
   EndUpdate;
 end;
 
@@ -788,11 +781,6 @@ begin
   end;
 end;
 
-function TCustomRectShape.AllowShearTransform: boolean;
-begin
-  result := true;
-end;
-
 function TCustomRectShape.ShowArrows: boolean;
 begin
   result := true;
@@ -899,16 +887,6 @@ end;
 
 { TRectShape }
 
-function TRectShape.PenVisible(AAssumePenFill: boolean): boolean;
-begin
-  result := (PenWidth>0) and not IsClearPenStyle(PenStyle) and (not PenFill.IsFullyTransparent or AAssumePenFill);
-end;
-
-function TRectShape.BackVisible: boolean;
-begin
-  result := not BackFill.IsFullyTransparent;
-end;
-
 function TRectShape.GetCornerPositition: single;
 begin
   result := 1;
@@ -919,24 +897,24 @@ var
   ab: TAffineBox;
   backSurface, totalSurface, penSurface: Single;
 begin
-  if not PenVisible and not BackVisible then
+  if not GetPenVisible and not GetBackVisible then
     result := false
   else
   begin
     ab := GetAffineBox(AMatrix, true);
     backSurface := ab.Surface;
-    if PenVisible then
+    if GetPenVisible then
     begin
       penSurface := (ab.Width+ab.Height)*2*PenWidth;
-      if BackVisible then
+      if GetBackVisible then
         totalSurface:= backSurface+penSurface/2
       else
         totalSurface := penSurface;
     end else
       totalSurface := backSurface;
     result := (totalSurface > 800*600) or
-              ((backSurface > 320*240) and BackVisible and BackFill.IsSlow(AMatrix)) or
-              ((penSurface > 320*240) and PenVisible and PenFill.IsSlow(AMatrix));
+              ((backSurface > 320*240) and GetBackVisible and BackFill.IsSlow(AMatrix)) or
+              ((penSurface > 320*240) and GetPenVisible and PenFill.IsSlow(AMatrix));
   end;
 end;
 
@@ -957,7 +935,7 @@ var
   i: Integer;
 begin
   pts := GetAffineBox(AMatrix, true).AsPolygon;
-  If BackVisible then
+  If GetBackVisible then
   begin
     if (BackFill.FillType = vftSolid) then backScan := nil
     else backScan := BackFill.CreateScanner(AMatrix, ADraft);
@@ -1022,7 +1000,7 @@ begin
 
     backScan.Free;
   end;
-  if PenVisible then
+  if GetPenVisible then
   begin
     if (PenFill.FillType = vftSolid) then penScan := nil
     else penScan := PenFill.CreateScanner(AMatrix, ADraft);
@@ -1051,12 +1029,12 @@ var
   pts: ArrayOfTPointF;
   xMargin, yMargin: single;
 begin
-  if not (BackVisible or (rboAssumeBackFill in AOptions)) and not PenVisible(rboAssumePenFill in AOptions) then
+  if not (GetBackVisible or (rboAssumeBackFill in AOptions)) and not GetPenVisible(rboAssumePenFill in AOptions) then
     result:= EmptyRectF
   else
   begin
     result := inherited GetRenderBounds(ADestRect, AMatrix, AOptions);
-    if PenVisible(rboAssumePenFill in AOptions) then
+    if GetPenVisible(rboAssumePenFill in AOptions) then
     begin
       if (JoinStyle <> pjsMiter) or (Stroker.MiterLimit <= 1) then
       begin
@@ -1087,9 +1065,9 @@ var
   box: TAffineBox;
 begin
   box := GetAffineBox(AffineMatrixIdentity, true);
-  if BackVisible and box.Contains(APoint) then
+  if GetBackVisible and box.Contains(APoint) then
     result := true else
-  if PenVisible then
+  if GetPenVisible then
   begin
     pts := ComputeStroke(box.AsPolygon, true, AffineMatrixIdentity);
     result:= IsPointInPolygon(pts, APoint, true);
@@ -1102,7 +1080,7 @@ var
   pts: ArrayOfTPointF;
   box: TAffineBox;
 begin
-  if PenVisible or BackVisible then
+  if GetPenVisible or GetBackVisible then
   begin
     box := GetAffineBox(AffineMatrixIdentity, true);
     pts := ComputeStrokeEnvelope(box.AsPolygon, true, ARadius*2);
@@ -1111,23 +1089,45 @@ begin
   else result := false;
 end;
 
-class function TRectShape.StorageClassName: RawByteString;
+function TRectShape.PointInBack(APoint: TPointF): boolean;
+var
+  box: TAffineBox;
+  scan: TBGRACustomScanner;
 begin
-  result := 'rect';
+  if GetBackVisible then
+  begin
+    box := GetAffineBox(AffineMatrixIdentity, true);
+    result := box.Contains(APoint);
+    if result and (BackFill.FillType = vftTexture) then
+    begin
+      scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
+      if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
+      scan.Free;
+    end;
+  end else
+    result := false;
 end;
 
-{ TEllipseShape }
-
-function TEllipseShape.PenVisible(AAssumePenFill: boolean): boolean;
+function TRectShape.PointInPen(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
 begin
-  result := (PenWidth>0) and not IsClearPenStyle(PenStyle) and (not PenFill.IsFullyTransparent or AAssumePenFill);
+  if GetPenVisible then
+  begin
+    pts := GetAffineBox(AffineMatrixIdentity, true).AsPolygon;
+    pts := ComputeStroke(pts,true, AffineMatrixIdentity);
+    result:= IsPointInPolygon(pts, APoint, true);
+  end else
+    result := false;
 end;
 
-function TEllipseShape.BackVisible: boolean;
+class function TRectShape.StorageClassName: RawByteString;
 begin
-  result := not BackFill.IsFullyTransparent;
+  result := 'rect';
 end;
 
+{ TEllipseShape }
+
 function TEllipseShape.GetCornerPositition: single;
 begin
   result := sqrt(2)/2;
@@ -1171,7 +1171,7 @@ begin
   IncludePoint(m*YAxis);
   IncludePoint(m*(Origin-(XAxis-Origin)));
   IncludePoint(m*(Origin-(YAxis-Origin)));
-  if PenVisible then
+  if GetPenVisible then
   begin
     zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
     result.Left -= zoom*PenWidth/2;
@@ -1198,7 +1198,7 @@ begin
   begin
     center := (orthoRect.TopLeft+orthoRect.BottomRight)*0.5;
     radius := (orthoRect.BottomRight-orthoRect.TopLeft)*0.5;
-    If BackVisible then
+    If GetBackVisible then
     begin
       if BackFill.FillType = vftSolid then backScan := nil
       else backScan := BackFill.CreateScanner(AMatrix, ADraft);
@@ -1219,7 +1219,7 @@ begin
 
       backScan.Free;
     end;
-    if PenVisible then
+    if GetPenVisible then
     begin
       if PenFill.FillType = vftSolid then penScan := nil
       else penScan := PenFill.CreateScanner(AMatrix, ADraft);
@@ -1263,7 +1263,7 @@ begin
   begin
     m:= MatrixForPixelCentered(AMatrix);
     pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
-    If BackVisible then
+    If GetBackVisible then
     begin
       if BackFill.FillType = vftSolid then backScan := nil
       else backScan := BackFill.CreateScanner(AMatrix, ADraft);
@@ -1283,7 +1283,7 @@ begin
 
       backScan.Free;
     end;
-    if PenVisible then
+    if GetPenVisible then
     begin
       if PenFill.FillType = vftSolid then penScan := nil
       else penScan := PenFill.CreateScanner(AMatrix, ADraft);
@@ -1311,12 +1311,12 @@ function TEllipseShape.GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMa
 var
   xMargin, yMargin: single;
 begin
-  if not (BackVisible or (rboAssumeBackFill in AOptions)) and not PenVisible(rboAssumePenFill in AOptions) then
+  if not (GetBackVisible or (rboAssumeBackFill in AOptions)) and not GetPenVisible(rboAssumePenFill in AOptions) then
     result:= EmptyRectF
   else
   begin
     result := inherited GetRenderBounds(ADestRect, AMatrix, AOptions);
-    if PenVisible(rboAssumePenFill in AOptions) then
+    if GetPenVisible(rboAssumePenFill in AOptions) then
     begin
       xMargin := (abs(AMatrix[1,1])+abs(AMatrix[1,2]))*PenWidth*0.5;
       yMargin := (abs(AMatrix[2,1])+abs(AMatrix[2,2]))*PenWidth*0.5;
@@ -1333,9 +1333,9 @@ var
   pts: ArrayOfTPointF;
 begin
   pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
-  if BackVisible and IsPointInPolygon(pts, APoint, true) then
+  if GetBackVisible and IsPointInPolygon(pts, APoint, true) then
     result := true else
-  if PenVisible then
+  if GetPenVisible then
   begin
     pts := ComputeStroke(pts, true, AffineMatrixIdentity);
     result:= IsPointInPolygon(pts, APoint, true);
@@ -1347,7 +1347,7 @@ function TEllipseShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
 var
   pts: ArrayOfTPointF;
 begin
-  if PenVisible or BackVisible then
+  if GetPenVisible or GetBackVisible then
   begin
     pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
     pts := ComputeStrokeEnvelope(pts, true, ARadius*2);
@@ -1356,29 +1356,61 @@ begin
     result := false;
 end;
 
+function TEllipseShape.PointInBack(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
+  scan: TBGRACustomScanner;
+begin
+  if GetBackVisible then
+  begin
+    pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
+    result:= IsPointInPolygon(pts, APoint, true);
+    if result and (BackFill.FillType = vftTexture) then
+    begin
+      scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
+      if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
+      scan.Free;
+    end;
+  end else
+    result := false;
+end;
+
+function TEllipseShape.PointInPen(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
+begin
+  if GetPenVisible then
+  begin
+    pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
+    pts := ComputeStroke(pts,true, AffineMatrixIdentity);
+    result:= IsPointInPolygon(pts, APoint, true);
+  end else
+    result := false;
+end;
+
 function TEllipseShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 var
   ab: TAffineBox;
   backSurface, totalSurface, penSurface: Single;
 begin
-  if not PenVisible and not BackVisible then
+  if not GetPenVisible and not GetBackVisible then
     result := false
   else
   begin
     ab := GetAffineBox(AMatrix, true);
     backSurface := ab.Surface*Pi/4;
-    if PenVisible then
+    if GetPenVisible then
     begin
       penSurface := (ab.Width+ab.Height)*(Pi/2)*PenWidth;
-      if BackVisible then
+      if GetBackVisible then
         totalSurface:= backSurface+penSurface/2
       else
         totalSurface := penSurface;
     end else
       totalSurface := backSurface;
     result := (totalSurface > 640*480) or
-              ((backSurface > 320*240) and BackVisible and BackFill.IsSlow(AMatrix)) or
-              ((penSurface > 320*240) and PenVisible and PenFill.IsSlow(AMatrix));
+              ((backSurface > 320*240) and GetBackVisible and BackFill.IsSlow(AMatrix)) or
+              ((penSurface > 320*240) and GetPenVisible and PenFill.IsSlow(AMatrix));
   end;
 end;
 
@@ -1427,11 +1459,6 @@ begin
   EndUpdate;
 end;
 
-function TPhongShape.BackVisible: boolean;
-begin
-  result := not BackFill.IsFullyTransparent;
-end;
-
 function TPhongShape.GetEnvelope: ArrayOfTPointF;
 var
   box: TAffineBox;
@@ -1605,7 +1632,7 @@ var
   rectRenderF,rectRasterF: TRectF;
   rectRender,rectRaster, prevClip: TRect;
 begin
-  if not BackVisible then exit;
+  if not GetBackVisible then exit;
 
   //determine final render bounds
   rectRenderF := GetRenderBounds(InfiniteRect,AMatrix);
@@ -1733,7 +1760,7 @@ end;
 function TPhongShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix;
   AOptions: TRenderBoundsOptions): TRectF;
 begin
-  if not (BackVisible or (rboAssumeBackFill in AOptions)) then
+  if not (GetBackVisible or (rboAssumeBackFill in AOptions)) then
     result:= EmptyRectF
   else
     result := inherited GetRenderBounds(ADestRect, AMatrix, AOptions);
@@ -1743,7 +1770,7 @@ function TPhongShape.PointInShape(APoint: TPointF): boolean;
 var
   pts: ArrayOfTPointF;
 begin
-  if not BackVisible then exit(false);
+  if not GetBackVisible then exit(false);
   pts := GetEnvelope;
   result := IsPointInPolygon(pts, APoint, true);
 end;
@@ -1752,7 +1779,7 @@ function TPhongShape.PointInShape(APoint: TPointF; ARadius: single): boolean;
 var
   pts: ArrayOfTPointF;
 begin
-  if BackVisible then
+  if GetBackVisible then
   begin
     pts := ComputeStrokeEnvelope(GetEnvelope, true, ARadius*2);
     result:= IsPointInPolygon(pts, APoint, true);
@@ -1760,11 +1787,24 @@ begin
     else result := false;
 end;
 
+function TPhongShape.PointInBack(APoint: TPointF): boolean;
+var
+  scan: TBGRACustomScanner;
+begin
+  result := PointInShape(APoint);
+  if result and (BackFill.FillType = vftTexture) then
+  begin
+    scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
+    if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
+    scan.Free;
+  end;
+end;
+
 function TPhongShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 var
   ab: TAffineBox;
 begin
-  if not BackVisible then exit(false);
+  if not GetBackVisible then exit(false);
   ab := GetAffineBox(AMatrix, true);
   result := ab.Surface > 320*240;
 end;

+ 50 - 16
lazpaintcontrols/lcvectortextshapes.pas

@@ -22,10 +22,12 @@ type
     FFontEmHeightBefore: single;
     FFontNameBefore: string;
     FFontStyleBefore: TFontStyles;
+    FAliasedBefore: boolean;
     FFontBidiModeAfter: TFontBidiMode;
     FFontEmHeightAfter: single;
     FFontNameAfter: string;
     FFontStyleAfter: TFontStyles;
+    FAliasedAfter: boolean;
   public
     constructor Create(AStartShape: TVectorShape); override;
     procedure ComputeDiff(AEndShape: TVectorShape); override;
@@ -79,6 +81,7 @@ type
 
   TTextShape = class(TCustomRectShape)
   private
+    FAliased: boolean;
     FAltitudePercent: single;
     FPenPhong: boolean;
     FLightPosition: TPointF;
@@ -99,6 +102,7 @@ type
     function GetParagraphAlignment: TAlignment;
     procedure OnMoveLightPos({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF;
       {%H-}AShift: TShiftState);
+    procedure SetAliased(AValue: boolean);
     procedure SetAltitudePercent(AValue: single);
     procedure SetPenPhong(AValue: boolean);
     procedure SetFontBidiMode(AValue: TFontBidiMode);
@@ -116,8 +120,6 @@ type
     FGlobalMatrix: TAffineMatrix;
     procedure DoOnChange(ABoundsBefore: TRectF; ADiff: TVectorShapeDiff); override;
     procedure SetGlobalMatrix(AMatrix: TAffineMatrix);
-    function PenVisible(AAssumePenFill: boolean = false): boolean;
-    function AllowShearTransform: boolean; override;
     function ShowArrows: boolean; override;
     function GetTextLayout: TBidiTextLayout;
     function GetFontRenderer: TBGRACustomFontRenderer;
@@ -149,10 +151,11 @@ type
     class function StorageClassName: RawByteString; override;
     class function Usermodes: TVectorShapeUsermodes; override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
-    procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); override;
+    procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape({%H-}APoint: TPointF; {%H-}ARadius: single): boolean; overload; override;
+    function PointInPen(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
     function GetGenericCost: integer; override;
     procedure MouseMove({%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
@@ -168,6 +171,7 @@ type
     function DeleteSelection: boolean;
     function GetAlignBounds(const {%H-}ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
     procedure Transform(const AMatrix: TAffineMatrix); override;
+    function AllowShearTransform: boolean; override;
     property HasSelection: boolean read GetHasSelection;
     property CanPasteSelection: boolean read GetCanPasteSelection;
     property Text: string read FText write SetText;
@@ -181,6 +185,7 @@ type
     property PenPhong: boolean read FPenPhong write SetPenPhong;
     property LightPosition: TPointF read FLightPosition write SetLightPosition;
     property AltitudePercent: single read FAltitudePercent write SetAltitudePercent;
+    property Aliased: boolean read FAliased write SetAliased;
   end;
 
 function FontStyleToStr(AStyle: TFontStyles): string;
@@ -193,7 +198,7 @@ implementation
 
 uses BGRATransform, BGRAText, BGRAVectorize, LCVectorialFill, math,
   BGRAUTF8, BGRAUnicode, Graphics, Clipbrd, LCLType, LCLIntf,
-  BGRAGradients, BGRACustomTextFX, LCResourceString;
+  BGRAGradients, BGRACustomTextFX, LCResourceString, BGRAFillInfo;
 
 function FontStyleToStr(AStyle: TFontStyles): string;
 begin
@@ -420,6 +425,7 @@ begin
     FFontEmHeightBefore:= FFontEmHeight;
     FFontNameBefore:= FFontName;
     FFontStyleBefore:= FFontStyle;
+    FAliasedBefore := FAliased;
   end;
 end;
 
@@ -431,6 +437,7 @@ begin
     FFontEmHeightAfter:= FFontEmHeight;
     FFontNameAfter:= FFontName;
     FFontStyleAfter:= FFontStyle;
+    FAliasedAfter := FAliased;
   end;
 end;
 
@@ -443,6 +450,7 @@ begin
     FFontEmHeight := FFontEmHeightAfter;
     FFontName := FFontNameAfter;
     FFontStyle := FFontStyleAfter;
+    FAliased := FAliasedAfter;
     if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
     EndUpdate;
   end;
@@ -457,6 +465,7 @@ begin
     FFontEmHeight := FFontEmHeightBefore;
     FFontName := FFontNameBefore;
     FFontStyle := FFontStyleBefore;
+    FAliased := FAliasedBefore;
     if Assigned(FTextLayout) then FTextLayout.InvalidateLayout;
     EndUpdate;
   end;
@@ -471,6 +480,7 @@ begin
   FFontEmHeightAfter := next.FFontEmHeightAfter;
   FFontNameAfter := next.FFontNameAfter;
   FFontStyleAfter := next.FFontStyleAfter;
+  FAliasedAfter := next.FAliasedAfter;
 end;
 
 function TTextShapeFontDiff.IsIdentity: boolean;
@@ -478,7 +488,8 @@ begin
   result := (FFontBidiModeBefore = FFontBidiModeAfter) and
     (FFontEmHeightBefore = FFontEmHeightAfter) and
     (FFontNameBefore = FFontNameAfter) and
-    (FFontStyleBefore = FFontStyleAfter);
+    (FFontStyleBefore = FFontStyleAfter) and
+    (FAliasedBefore = FAliasedAfter);
 end;
 
 { TTextShape }
@@ -547,6 +558,14 @@ begin
   LightPosition := ANewCoord;
 end;
 
+procedure TTextShape.SetAliased(AValue: boolean);
+begin
+  if FAliased=AValue then Exit;
+  BeginUpdate(TTextShapeFontDiff);
+  FAliased:=AValue;
+  EndUpdate;
+end;
+
 procedure TTextShape.SetAltitudePercent(AValue: single);
 begin
   if AValue < 0 then AValue := 0;
@@ -711,11 +730,6 @@ begin
   FGlobalMatrix := AMatrix;
 end;
 
-function TTextShape.PenVisible(AAssumePenFill: boolean): boolean;
-begin
-  result := not PenFill.IsFullyTransparent or AAssumePenFill;
-end;
-
 function TTextShape.AllowShearTransform: boolean;
 begin
   Result:= true;
@@ -960,6 +974,7 @@ begin
   FPenPhong:= false;
   FAltitudePercent:= DefaultAltitudePercent;
   FLightPosition := PointF(0,0);
+  FAliased := false;
 end;
 
 procedure TTextShape.QuickDefine(constref APoint1, APoint2: TPointF);
@@ -1019,6 +1034,8 @@ begin
   end else
     SetDefaultFont;
 
+  Aliased := AStorage.Bool['aliased'];
+
   phongObj := AStorage.OpenObject('pen-phong');
   PenPhong := Assigned(phongObj);
   if PenPhong then
@@ -1064,6 +1081,7 @@ begin
   font.RawString['bidi'] := FontBidiModeToStr(FontBidiMode);
   font.RawString['style'] := FontStyleToStr(FontStyle);
   font.Free;
+  AStorage.Bool['aliased'] := Aliased;
 
   if PenPhong then
   begin
@@ -1099,7 +1117,7 @@ end;
 
 class function TTextShape.Fields: TVectorShapeFields;
 begin
-  Result:= [vsfPenFill,vsfOutlineFill];
+  Result:= [vsfPenFill,vsfOutlineFill,vsfOutlineWidth];
 end;
 
 class function TTextShape.PreferPixelCentered: boolean;
@@ -1109,7 +1127,7 @@ end;
 
 class function TTextShape.DefaultFontName: string;
 begin
-  result := {$IFDEF WINDOWS}'Arial'{$ELSE}{$IFDEF DARWIN}'Helvetica'{$ELSE}'FreeSans'{$ENDIF}{$ENDIF};
+  result := {$IFDEF WINDOWS}'Arial'{$ELSE}{$IFDEF DARWIN}'Helvetica'{$ELSE}'Liberation Sans'{$ENDIF}{$ENDIF};
 end;
 
 class function TTextShape.DefaultFontEmHeight: single;
@@ -1283,7 +1301,7 @@ begin
     ctx := tmpTransf.Canvas2D;
     ctx.transform(AffineMatrixTranslation(-transfRect.Left,-transfRect.Top)*m);
     ctx.fillMode := fmWinding;
-    ctx.antialiasing:= not ADraft;
+    ctx.antialiasing:= not ADraft and not Aliased;
     ctx.beginPath;
     tl.PathText(ctx);
     ctx.resetTransform;
@@ -1309,7 +1327,7 @@ begin
     if HasOutline then
     begin
       ctx := tmpTransf.Canvas2D;
-      ctx.lineWidth := OutlineWidth;
+      ctx.lineWidth := zoom*OutlineWidth;
       ctx.lineJoinLCL:= pjsRound;
       ctx.lineStyle(psSolid);
       if OutlineFill.FillType = vftSolid then
@@ -1366,7 +1384,7 @@ begin
     ADest.PutImage(transfRect.Left, transfRect.Top, tmpTransf, dmDrawWithTransparency);
   end else
   begin
-    if ADraft then rf := rfBox else rf := rfHalfCosine;
+    if ADraft or Aliased then rf := rfBox else rf := rfHalfCosine;
     if storeImage then
       tmpTransf := TBGRABitmap.Create(transfRect.Width,transfRect.Height)
     else
@@ -1445,7 +1463,7 @@ var
   u: TPointF;
   lenU, margin: Single;
 begin
-  if (PenVisible(rboAssumePenFill in AOptions) or HasOutline) and
+  if (GetPenVisible(rboAssumePenFill in AOptions) or HasOutline) and
     (Text <> '') then
   begin
     ab := GetAffineBox(AMatrix, false);
@@ -1474,6 +1492,22 @@ begin
   result := false;
 end;
 
+function TTextShape.PointInPen(APoint: TPointF): boolean;
+var
+  tl: TBidiTextLayout;
+  pt: TPointF;
+  i: Integer;
+begin
+  if not GetAffineBox(AffineMatrixIdentity,true).Contains(APoint) then
+    exit(false);
+  SetGlobalMatrix(AffineMatrixIdentity);
+  tl := GetTextLayout;
+  pt := AffineMatrixInverse(GetUntransformedMatrix)*APoint;
+  for i := 0 to tl.PartCount-1 do
+    if tl.PartAffineBox[i].Contains(pt) then exit(true);
+  result := false;
+end;
+
 function TTextShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 begin
   Result:= true;

+ 1 - 1
scripts/merge_channels.py → scripts/channels_merge.py

@@ -1,4 +1,4 @@
-# Merge channels
+# Channels > Merge
 from lazpaint import image, dialog, layer
 
 channels_id = None

+ 1 - 1
scripts/split_hsl_native.py → scripts/channels_split_hsl.py

@@ -1,4 +1,4 @@
-# Split HSL channels
+# Channels > Split HSL
 from lazpaint import image, dialog, layer, filters
 
 # check if it is a channel

+ 1 - 1
scripts/split_rgb_native.py → scripts/channels_split_rgb.py

@@ -1,4 +1,4 @@
-# Split RGB channels
+# Channels > Split RGB
 from lazpaint import image, dialog, layer, filters
 
 # check if it is a channel

+ 10 - 0
scripts/display_version.py

@@ -0,0 +1,10 @@
+# Version
+
+from lazpaint import command, dialog
+import sys
+
+lazpaint_version = command.get_version()
+python_version = sys.version_info
+
+dialog.show_message("Python version " + str(python_version[0]) + "." + str(python_version[1]) + "." + str(python_version[2]) + ", " + "LazPaint version " + str(lazpaint_version[0]) + "." + str(lazpaint_version[1]) + "." + str(lazpaint_version[2]) )
+

+ 1 - 1
scripts/color_overlay.py → scripts/layerfx_color_overlay.py

@@ -1,4 +1,4 @@
-# Color overlay
+# Layer effect > Color overlay
 from lazpaint import image, colors, layer, filters
 
 color = colors.show_dialog(layer.get_registry("overlay-color"))

+ 261 - 0
scripts/layerfx_innerlight.py

@@ -0,0 +1,261 @@
+# Layer effect > Inner light
+from lazpaint import dialog
+
+try:
+    from tkinter import *
+except ImportError:
+    dialog.show_message("Please install tkinter.")
+    exit()
+        
+from lazpaint import colors, image, layer, filters, tools, selection
+import math
+
+if layer.is_empty():
+    dialog.show_message("Layer is empty")
+    exit()
+
+############ image processing
+
+FRIENDLY_NAME = "Inner light"
+REGISTRY_NAME = "innerlight"
+OPPOSITE_REGISTRY_NAME = "innershadow"
+DEFAULT_ANGLE = 315
+DEFAULT_COLOR = colors.WHITE
+
+MAX_RADIUS = 200
+MAX_OPACITY = 255
+MAX_ANGLE = 360
+
+source_layer_id = layer.get_registry(REGISTRY_NAME+"-source-layer-id")
+if source_layer_id is not None:
+  layer.select_id(source_layer_id)
+else:
+  source_layer_id = layer.get_id()
+source_layer_name = layer.get_name()
+
+chosen_radius = layer.get_registry(REGISTRY_NAME+"-radius")
+if chosen_radius == None: 
+    chosen_radius = layer.get_registry(OPPOSITE_REGISTRY_NAME+"-radius")
+if chosen_radius == None: 
+    chosen_radius = image.get_registry(REGISTRY_NAME+"-radius")
+if chosen_radius == None: 
+    chosen_radius = image.get_registry(OPPOSITE_REGISTRY_NAME+"-radius")
+if chosen_radius == None:
+    chosen_radius = 20
+
+chosen_angle = layer.get_registry(REGISTRY_NAME+"-angle")
+if chosen_angle == None: 
+    chosen_angle = layer.get_registry(OPPOSITE_REGISTRY_NAME+"-angle")
+    if chosen_angle is not None:
+        chosen_angle = (chosen_angle+180) % 360
+if chosen_angle == None: 
+    chosen_angle = image.get_registry(REGISTRY_NAME+"-angle")
+if chosen_angle == None: 
+    chosen_angle = image.get_registry(OPPOSITE_REGISTRY_NAME+"-angle")
+    if chosen_angle is not None:
+        chosen_angle = (chosen_angle+180) % 360
+if chosen_angle == None:
+    chosen_angle = DEFAULT_ANGLE
+
+shadow_layer_id = layer.get_registry(REGISTRY_NAME+"-layer-id")
+if image.get_layer_index(shadow_layer_id) == None:
+    shadow_layer_id = None
+
+if shadow_layer_id is not None:
+    layer.select_id(shadow_layer_id)
+    chosen_opacity = layer.get_opacity()
+    overlay_color = colors.str_to_RGBA(layer.get_registry("overlay-color"))
+    layer.select_id(source_layer_id)
+else:
+    chosen_opacity = layer.get_opacity()*2/3 
+    overlay_color = None
+
+if overlay_color is None:
+  overlay_color = DEFAULT_COLOR
+
+def create_shadow_layer():
+    global shadow_layer_id
+    image.do_begin()
+    if shadow_layer_id != None:
+        layer.select_id(shadow_layer_id)
+        layer.remove()
+    layer.select_id(source_layer_id)
+    layer.duplicate()
+    layer.rasterize()
+    layer.set_name(FRIENDLY_NAME+" of "+source_layer_name)
+    layer.set_registry(REGISTRY_NAME+"-source-layer-id", source_layer_id)
+    shadow_layer_id = layer.get_id()
+    layer.set_registry("overlay-color", overlay_color)
+    layer.set_opacity(chosen_opacity)    
+    image.do_end()
+
+blur_done = False
+opacity_done = False
+
+def apply_blur():
+    global blur_done, opacity_done
+    if opacity_done:
+        image.undo()
+        opacity_done = False
+    if blur_done:
+        image.undo()
+        blur_done = False
+    image.do_begin()
+    if chosen_radius == 0:
+        filters.filter_function(alpha=0, gamma_correction=False)
+    else:
+        layer.duplicate()
+        layer.set_opacity(255)
+        filters.filter_function(red="alpha", green="alpha", blue="alpha", alpha=1, gamma_correction=False)
+        mask_layer_id = layer.get_id()    
+
+        layer.select_id(shadow_layer_id)
+        filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, gamma_correction=False)
+        tools.choose(tools.MOVE_LAYER)
+        offset = (math.sin(chosen_angle*math.pi/180)*chosen_radius, -math.cos(chosen_angle*math.pi/180)*chosen_radius)
+        tools.mouse([(0,0), (offset[0],offset[1])])
+
+        layer.select_id(mask_layer_id)
+        layer.duplicate()
+        mask_layer_id2 = layer.get_id()
+        layer.select_id(mask_layer_id)
+        tools.choose(tools.MOVE_LAYER)
+        tools.mouse([(offset[0]/2,offset[1]/2), (0,0)])
+        colors.linear_negative()
+        layer.set_blend_op(layer.BLEND_MASK)
+        layer.merge_over()
+        mask_layer_id = mask_layer_id2
+
+        filters.blur(radius=chosen_radius)
+
+        layer.select_id(mask_layer_id)
+        layer.set_blend_op(layer.BLEND_MASK)
+        layer.merge_over()  
+   
+    blur_done = image.do_end()
+    apply_opacity()
+
+def apply_opacity():
+    global opacity_done
+    if opacity_done:
+        image.undo()
+        opacity_done = False
+    image.do_begin()
+    layer.set_opacity(chosen_opacity)
+    opacity_done = image.do_end()
+
+######## interface
+
+def button_ok_click():
+    global source_layer_id, chosen_radius, chosen_angle
+    layer.select_id(source_layer_id)
+    layer.set_registry(REGISTRY_NAME+"-radius", chosen_radius)
+    layer.set_registry(REGISTRY_NAME+"-angle", chosen_angle)
+    layer.set_registry(REGISTRY_NAME+"-layer-id", shadow_layer_id)
+    image.set_registry(REGISTRY_NAME+"-radius", chosen_radius)
+    image.set_registry(REGISTRY_NAME+"-angle", chosen_angle)
+    image.do_end()
+    exit()
+
+def button_cancel_click():    
+    if image.do_end():
+        image.undo()
+    layer.select_id(source_layer_id)
+    exit()
+
+scale_radius_update_job = None
+
+def scale_radius_update_do():
+    global scale_radius_update_job, chosen_radius, scale_radius
+    new_radius = scale_radius.get() 
+    if new_radius != chosen_radius:
+        chosen_radius = new_radius
+        apply_blur()
+    scale_radius_update_job = None    
+
+def scale_radius_update(event):
+    global window, scale_radius_update_job
+    if scale_radius_update_job:
+        window.after_cancel(scale_radius_update_job)
+    scale_radius_update_job = window.after(800, scale_radius_update_do)
+
+scale_angle_update_job = None
+
+def scale_angle_update_do():
+    global scale_angle_update_job, chosen_angle, scale_angle
+    new_angle = scale_angle.get() 
+    if new_angle != chosen_angle:
+        chosen_angle = new_angle
+        apply_blur()
+    scale_angle_update_job = None    
+
+def scale_angle_update(event):
+    global window, scale_angle_update_job
+    if scale_angle_update_job:
+        window.after_cancel(scale_angle_update_job)
+    scale_angle_update_job = window.after(800, scale_angle_update_do)
+
+scale_opacity_update_job = None
+
+def scale_opacity_update_do():
+    global chosen_opacity 
+    new_opacity = scale_opacity.get()
+    if new_opacity != chosen_opacity:
+        chosen_opacity = new_opacity
+        apply_opacity()
+    scale_opacity_update_job = None
+
+def scale_opacity_update(event):   
+    global window, scale_opacity_update_job
+    if scale_opacity_update_job:
+        window.after_cancel(scale_opacity_update_job)
+    scale_opacity_update_job = window.after(100, scale_opacity_update_do)
+
+window = Tk()
+window.title(FRIENDLY_NAME)
+window.resizable(False, False)
+
+frame = Frame(window)
+frame.pack()
+
+label_radius = Label(frame, text="Radius:")
+label_radius.grid(column=0, row=0)
+scale_radius = Scale(frame, from_=0, to=MAX_RADIUS, orient=HORIZONTAL, command=scale_radius_update)
+scale_radius.grid(column=1, row=0, sticky=W+E, padx=10)
+scale_radius.set(chosen_radius)
+
+label_angle = Label(frame, text="Angle:")
+label_angle.grid(column=0, row=1)
+scale_angle = Scale(frame, from_=0, to=MAX_ANGLE, orient=HORIZONTAL, command=scale_angle_update)
+scale_angle.grid(column=1, row=1, sticky=W+E, padx=10)
+scale_angle.set(chosen_angle)
+
+label_opacity = Label(frame, text="Opacity:")
+label_opacity.grid(column=0, row=2)
+scale_opacity = Scale(frame, from_=0, to=MAX_OPACITY, orient=HORIZONTAL, command=scale_opacity_update)
+scale_opacity.grid(column=1, row=2, sticky=W+E, padx=10)
+scale_opacity.set(chosen_opacity)
+
+frame.columnconfigure(0, pad=20)
+frame.columnconfigure(1, minsize=250)
+frame.rowconfigure(0, pad=20)
+frame.rowconfigure(1, pad=20)
+frame.rowconfigure(2, pad=20)
+
+button_ok = Button(window, text="Ok", command=button_ok_click)
+button_ok.pack(side=RIGHT, padx=10, pady=10)
+button_cancel = Button(window, text="Cancel", command=button_cancel_click)
+button_cancel.pack(side=RIGHT, pady=10)
+
+image.do_begin()
+selection.deselect()
+create_shadow_layer()
+apply_blur()
+
+window.update()
+window_width = window.winfo_width()
+screen_width = window.winfo_screenwidth()
+window.geometry('+%d+0' % (int((screen_width - window_width) / 2)))
+
+window.mainloop()
+button_cancel_click()

+ 261 - 0
scripts/layerfx_innershadow.py

@@ -0,0 +1,261 @@
+# Layer effect > Inner shadow
+from lazpaint import dialog
+
+try:
+    from tkinter import *
+except ImportError:
+    dialog.show_message("Please install tkinter.")
+    exit()
+        
+from lazpaint import colors, image, layer, filters, tools, selection
+import math
+
+if layer.is_empty():
+    dialog.show_message("Layer is empty")
+    exit()
+
+############ image processing
+
+FRIENDLY_NAME = "Inner shadow"
+REGISTRY_NAME = "innershadow"
+OPPOSITE_REGISTRY_NAME = "innerlight"
+DEFAULT_ANGLE = 135
+DEFAULT_COLOR = colors.BLACK
+
+MAX_RADIUS = 200
+MAX_OPACITY = 255
+MAX_ANGLE = 360
+
+source_layer_id = layer.get_registry(REGISTRY_NAME+"-source-layer-id")
+if source_layer_id is not None:
+  layer.select_id(source_layer_id)
+else:
+  source_layer_id = layer.get_id()
+source_layer_name = layer.get_name()
+
+chosen_radius = layer.get_registry(REGISTRY_NAME+"-radius")
+if chosen_radius == None: 
+    chosen_radius = layer.get_registry(OPPOSITE_REGISTRY_NAME+"-radius")
+if chosen_radius == None: 
+    chosen_radius = image.get_registry(REGISTRY_NAME+"-radius")
+if chosen_radius == None: 
+    chosen_radius = image.get_registry(OPPOSITE_REGISTRY_NAME+"-radius")
+if chosen_radius == None:
+    chosen_radius = 20
+
+chosen_angle = layer.get_registry(REGISTRY_NAME+"-angle")
+if chosen_angle == None: 
+    chosen_angle = layer.get_registry(OPPOSITE_REGISTRY_NAME+"-angle")
+    if chosen_angle is not None:
+        chosen_angle = (chosen_angle+180) % 360
+if chosen_angle == None: 
+    chosen_angle = image.get_registry(REGISTRY_NAME+"-angle")
+if chosen_angle == None: 
+    chosen_angle = image.get_registry(OPPOSITE_REGISTRY_NAME+"-angle")
+    if chosen_angle is not None:
+        chosen_angle = (chosen_angle+180) % 360
+if chosen_angle == None:
+    chosen_angle = DEFAULT_ANGLE
+
+shadow_layer_id = layer.get_registry(REGISTRY_NAME+"-layer-id")
+if image.get_layer_index(shadow_layer_id) == None:
+    shadow_layer_id = None
+
+if shadow_layer_id is not None:
+    layer.select_id(shadow_layer_id)
+    chosen_opacity = layer.get_opacity()
+    overlay_color = colors.str_to_RGBA(layer.get_registry("overlay-color"))
+    layer.select_id(source_layer_id)
+else:
+    chosen_opacity = layer.get_opacity()*2/3 
+    overlay_color = None
+
+if overlay_color is None:
+  overlay_color = DEFAULT_COLOR
+
+def create_shadow_layer():
+    global shadow_layer_id
+    image.do_begin()
+    if shadow_layer_id != None:
+        layer.select_id(shadow_layer_id)
+        layer.remove()
+    layer.select_id(source_layer_id)
+    layer.duplicate()
+    layer.rasterize()
+    layer.set_name(FRIENDLY_NAME+" of "+source_layer_name)
+    layer.set_registry(REGISTRY_NAME+"-source-layer-id", source_layer_id)
+    shadow_layer_id = layer.get_id()
+    layer.set_registry("overlay-color", overlay_color)
+    layer.set_opacity(chosen_opacity)    
+    image.do_end()
+
+blur_done = False
+opacity_done = False
+
+def apply_blur():
+    global blur_done, opacity_done
+    if opacity_done:
+        image.undo()
+        opacity_done = False
+    if blur_done:
+        image.undo()
+        blur_done = False
+    image.do_begin()
+    if chosen_radius == 0:
+        filters.filter_function(alpha=0, gamma_correction=False)
+    else:
+        layer.duplicate()
+        layer.set_opacity(255)
+        filters.filter_function(red="alpha", green="alpha", blue="alpha", alpha=1, gamma_correction=False)
+        mask_layer_id = layer.get_id()    
+
+        layer.select_id(shadow_layer_id)
+        filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, gamma_correction=False)
+        tools.choose(tools.MOVE_LAYER)
+        offset = (math.sin(chosen_angle*math.pi/180)*chosen_radius, -math.cos(chosen_angle*math.pi/180)*chosen_radius)
+        tools.mouse([(0,0), (offset[0],offset[1])])
+
+        layer.select_id(mask_layer_id)
+        layer.duplicate()
+        mask_layer_id2 = layer.get_id()
+        layer.select_id(mask_layer_id)
+        tools.choose(tools.MOVE_LAYER)
+        tools.mouse([(offset[0]/2,offset[1]/2), (0,0)])
+        colors.linear_negative()
+        layer.set_blend_op(layer.BLEND_MASK)
+        layer.merge_over()
+        mask_layer_id = mask_layer_id2
+
+        filters.blur(radius=chosen_radius)
+
+        layer.select_id(mask_layer_id)
+        layer.set_blend_op(layer.BLEND_MASK)
+        layer.merge_over()  
+   
+    blur_done = image.do_end()
+    apply_opacity()
+
+def apply_opacity():
+    global opacity_done
+    if opacity_done:
+        image.undo()
+        opacity_done = False
+    image.do_begin()
+    layer.set_opacity(chosen_opacity)
+    opacity_done = image.do_end()
+
+######## interface
+
+def button_ok_click():
+    global source_layer_id, chosen_radius, chosen_angle
+    layer.select_id(source_layer_id)
+    layer.set_registry(REGISTRY_NAME+"-radius", chosen_radius)
+    layer.set_registry(REGISTRY_NAME+"-angle", chosen_angle)
+    layer.set_registry(REGISTRY_NAME+"-layer-id", shadow_layer_id)
+    image.set_registry(REGISTRY_NAME+"-radius", chosen_radius)
+    image.set_registry(REGISTRY_NAME+"-angle", chosen_angle)
+    image.do_end()
+    exit()
+
+def button_cancel_click():    
+    if image.do_end():
+        image.undo()
+    layer.select_id(source_layer_id)
+    exit()
+
+scale_radius_update_job = None
+
+def scale_radius_update_do():
+    global scale_radius_update_job, chosen_radius, scale_radius
+    new_radius = scale_radius.get() 
+    if new_radius != chosen_radius:
+        chosen_radius = new_radius
+        apply_blur()
+    scale_radius_update_job = None    
+
+def scale_radius_update(event):
+    global window, scale_radius_update_job
+    if scale_radius_update_job:
+        window.after_cancel(scale_radius_update_job)
+    scale_radius_update_job = window.after(800, scale_radius_update_do)
+
+scale_angle_update_job = None
+
+def scale_angle_update_do():
+    global scale_angle_update_job, chosen_angle, scale_angle
+    new_angle = scale_angle.get() 
+    if new_angle != chosen_angle:
+        chosen_angle = new_angle
+        apply_blur()
+    scale_angle_update_job = None    
+
+def scale_angle_update(event):
+    global window, scale_angle_update_job
+    if scale_angle_update_job:
+        window.after_cancel(scale_angle_update_job)
+    scale_angle_update_job = window.after(800, scale_angle_update_do)
+
+scale_opacity_update_job = None
+
+def scale_opacity_update_do():
+    global chosen_opacity 
+    new_opacity = scale_opacity.get()
+    if new_opacity != chosen_opacity:
+        chosen_opacity = new_opacity
+        apply_opacity()
+    scale_opacity_update_job = None
+
+def scale_opacity_update(event):   
+    global window, scale_opacity_update_job
+    if scale_opacity_update_job:
+        window.after_cancel(scale_opacity_update_job)
+    scale_opacity_update_job = window.after(100, scale_opacity_update_do)
+
+window = Tk()
+window.title(FRIENDLY_NAME)
+window.resizable(False, False)
+
+frame = Frame(window)
+frame.pack()
+
+label_radius = Label(frame, text="Radius:")
+label_radius.grid(column=0, row=0)
+scale_radius = Scale(frame, from_=0, to=MAX_RADIUS, orient=HORIZONTAL, command=scale_radius_update)
+scale_radius.grid(column=1, row=0, sticky=W+E, padx=10)
+scale_radius.set(chosen_radius)
+
+label_angle = Label(frame, text="Angle:")
+label_angle.grid(column=0, row=1)
+scale_angle = Scale(frame, from_=0, to=MAX_ANGLE, orient=HORIZONTAL, command=scale_angle_update)
+scale_angle.grid(column=1, row=1, sticky=W+E, padx=10)
+scale_angle.set(chosen_angle)
+
+label_opacity = Label(frame, text="Opacity:")
+label_opacity.grid(column=0, row=2)
+scale_opacity = Scale(frame, from_=0, to=MAX_OPACITY, orient=HORIZONTAL, command=scale_opacity_update)
+scale_opacity.grid(column=1, row=2, sticky=W+E, padx=10)
+scale_opacity.set(chosen_opacity)
+
+frame.columnconfigure(0, pad=20)
+frame.columnconfigure(1, minsize=250)
+frame.rowconfigure(0, pad=20)
+frame.rowconfigure(1, pad=20)
+frame.rowconfigure(2, pad=20)
+
+button_ok = Button(window, text="Ok", command=button_ok_click)
+button_ok.pack(side=RIGHT, padx=10, pady=10)
+button_cancel = Button(window, text="Cancel", command=button_cancel_click)
+button_cancel.pack(side=RIGHT, pady=10)
+
+image.do_begin()
+selection.deselect()
+create_shadow_layer()
+apply_blur()
+
+window.update()
+window_width = window.winfo_width()
+screen_width = window.winfo_screenwidth()
+window.geometry('+%d+0' % (int((screen_width - window_width) / 2)))
+
+window.mainloop()
+button_cancel_click()

+ 7 - 7
scripts/layer_shadow.py → scripts/layerfx_shadow.py

@@ -1,4 +1,4 @@
-# Layer shadow
+# Layer effect > Drop shadow
 from lazpaint import dialog
 
 try:
@@ -176,7 +176,7 @@ frame.pack()
 label_radius = Label(frame, text="Radius:")
 label_radius.grid(column=0, row=0)
 scale_radius = Scale(frame, from_=0, to=MAX_RADIUS, orient=HORIZONTAL, command=scale_radius_update)
-scale_radius.grid(column=1, row=0, columnspan=2, sticky=W+E, padx=10)
+scale_radius.grid(column=1, row=0, sticky=W+E, padx=10)
 scale_radius.set(chosen_radius)
 
 label_offset = Label(frame, text="Offset:")
@@ -185,21 +185,21 @@ scale_offset_x = Scale(frame, from_=-MAX_OFFSET, to=MAX_OFFSET, orient=HORIZONTA
 scale_offset_x.grid(column=1, row=1, sticky=W+E, padx=10)
 scale_offset_x.set(chosen_offset[0])
 scale_offset_y = Scale(frame, from_=-MAX_OFFSET, to=MAX_OFFSET, orient=HORIZONTAL, command=scale_offset_update)
-scale_offset_y.grid(column=2, row=1, sticky=W+E, padx=10)
+scale_offset_y.grid(column=1, row=2, sticky=W+E, padx=10)
 scale_offset_y.set(chosen_offset[1])
 
 label_opacity = Label(frame, text="Opacity:")
-label_opacity.grid(column=0, row=2)
+label_opacity.grid(column=0, row=3)
 scale_opacity = Scale(frame, from_=0, to=MAX_OPACITY, orient=HORIZONTAL, command=scale_opacity_update)
-scale_opacity.grid(column=1, row=2, columnspan=2, sticky=W+E, padx=10)
+scale_opacity.grid(column=1, row=3, sticky=W+E, padx=10)
 scale_opacity.set(chosen_opacity)
 
 frame.columnconfigure(0, pad=20)
-frame.columnconfigure(1, weight=1)
-frame.columnconfigure(2, weight=1)
+frame.columnconfigure(1, minsize=250)
 frame.rowconfigure(0, pad=20)
 frame.rowconfigure(1, pad=20)
 frame.rowconfigure(2, pad=20)
+frame.rowconfigure(3, pad=20)
 
 button_ok = Button(window, text="Ok", command=button_ok_click)
 button_ok.pack(side=RIGHT, padx=10, pady=10)

+ 215 - 0
scripts/layerfx_stroke.py

@@ -0,0 +1,215 @@
+# Layer effect > Stroke
+from lazpaint import dialog
+
+try:
+    from tkinter import *
+except ImportError:
+    dialog.show_message("Please install tkinter.")
+    exit()
+        
+from lazpaint import colors, image, layer, filters, tools, selection
+
+if layer.is_empty():
+    dialog.show_message("Layer is empty")
+    exit()
+
+############ image processing
+
+MAX_RADIUS = 100
+MAX_OPACITY = 255
+
+source_layer_id = layer.get_id()
+source_layer_name = layer.get_name()
+
+chosen_radius = layer.get_registry("stroke-radius")
+if chosen_radius == None: 
+    chosen_radius = image.get_registry("stroke-radius")
+if chosen_radius == None:
+    chosen_radius = 10
+
+stroke_layer_id = layer.get_registry("stroke-layer-id")
+if image.get_layer_index(stroke_layer_id) == None:
+    stroke_layer_id = None
+
+if stroke_layer_id is not None:
+    layer.select_id(stroke_layer_id)
+    chosen_opacity = layer.get_opacity()
+    overlay_color = colors.str_to_RGBA(layer.get_registry("overlay-color"))
+    layer.select_id(source_layer_id)
+else:
+    chosen_opacity = layer.get_opacity() 
+    overlay_color = None
+
+if overlay_color is None:
+  overlay_color = colors.BLACK
+
+def create_stroke_layer():
+    global stroke_layer_id
+    image.do_begin()
+    if stroke_layer_id != None:
+        layer.select_id(stroke_layer_id)
+        stroke_index = image.get_layer_index()
+        selection.select_all()
+        selection.delete()
+        layer.select_id(source_layer_id)
+        layer.duplicate()
+        image.move_layer_index(image.get_layer_index(), stroke_index+1)
+        layer.merge_over()        
+    else:
+        layer.select_id(source_layer_id)
+        layer.duplicate()
+        layer.set_name("Stroke of "+source_layer_name)
+        layer.set_registry("stroke-source-layer-id", source_layer_id)
+        stroke_layer_id = layer.get_id()
+        stroke_index = image.get_layer_index()
+        image.move_layer_index(stroke_index, stroke_index-1)
+    image.do_end()
+
+stroke_done = False
+opacity_done = False
+stroke_initial_color = None
+
+def apply_stroke():
+    global stroke_done, opacity_done, stroke_initial_color
+    if opacity_done:
+        image.undo()
+        opacity_done = False
+    if stroke_done:
+        image.undo()
+        stroke_done = False
+    image.do_begin() 
+    if chosen_radius > 0.5:
+        layer.duplicate()
+        disk_id = layer.get_id()
+        filters.blur(name=filters.BLUR_DISK, radius=chosen_radius-0.5)
+        filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, alpha="min(alpha*"+str(chosen_radius*5)+",(1-alpha)*"+str(chosen_radius*5)+")", gamma_correction=False)
+        layer.select_id(stroke_layer_id) 
+    else:
+        disk_id = None   
+    filters.blur(name=filters.BLUR_CORONA, radius=chosen_radius+0.5)
+    filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, alpha="min(alpha*"+str(chosen_radius)+",(1-alpha)*"+str(chosen_radius)+")", gamma_correction=False)
+    if disk_id is not None:
+        layer.select_id(disk_id)
+        layer.merge_over()
+    layer.set_registry("overlay-color", overlay_color)
+    stroke_initial_color = overlay_color
+    stroke_done = image.do_end()
+    apply_opacity()
+
+def apply_opacity():
+    global opacity_done, chosen_opacity, overlay_color
+    if opacity_done:
+        image.undo()
+        opacity_done = False
+    image.do_begin()
+    layer.set_opacity(chosen_opacity)
+    if overlay_color != stroke_initial_color:
+        filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, gamma_correction=False)
+        layer.set_registry("overlay-color", overlay_color)
+    opacity_done = image.do_end()
+
+######## interface
+
+def button_ok_click():
+    global source_layer_id, chosen_radius, chosen_offset
+    layer.select_id(source_layer_id)
+    layer.set_registry("stroke-radius", chosen_radius)
+    layer.set_registry("stroke-layer-id", stroke_layer_id)
+    image.set_registry("stroke-radius", chosen_radius)
+    image.do_end()
+    exit()
+
+def button_cancel_click():    
+    if image.do_end():
+        image.undo()
+    layer.select_id(source_layer_id)
+    exit()
+
+scale_radius_update_job = None
+
+def scale_radius_update_do():
+    global scale_radius_update_job, chosen_radius, scale_radius
+    new_radius = scale_radius.get() 
+    if new_radius != chosen_radius:
+        chosen_radius = new_radius
+        apply_stroke()
+    scale_radius_update_job = None    
+
+def scale_radius_update(event):
+    global window, scale_radius_update_job
+    if scale_radius_update_job:
+        window.after_cancel(scale_radius_update_job)
+    scale_radius_update_job = window.after(500, scale_radius_update_do)
+
+scale_opacity_update_job = None
+
+def scale_opacity_update_do():
+    global chosen_opacity 
+    new_opacity = scale_opacity.get()
+    if new_opacity != chosen_opacity:
+        chosen_opacity = new_opacity
+        apply_opacity()
+    scale_opacity_update_job = None
+
+def scale_opacity_update(event):   
+    global window, scale_opacity_update_job
+    if scale_opacity_update_job:
+        window.after_cancel(scale_opacity_update_job)
+    scale_opacity_update_job = window.after(100, scale_opacity_update_do)
+
+def button_color_click():
+    global overlay_color, window
+    new_color = colors.show_dialog(overlay_color)
+    window.attributes('-topmost', True)
+    window.attributes('-topmost', False)
+    if new_color is not None and new_color != overlay_color:
+        overlay_color = new_color
+        apply_opacity()
+
+window = Tk()
+window.title("Layer stroke")
+window.resizable(False, False)
+
+frame = Frame(window)
+frame.pack()
+
+label_radius = Label(frame, text="Radius:")
+label_radius.grid(column=0, row=0)
+scale_radius = Scale(frame, from_=0, to=MAX_RADIUS, orient=HORIZONTAL, command=scale_radius_update)
+scale_radius.grid(column=1, row=0, sticky=W+E, padx=10)
+scale_radius.set(chosen_radius)
+
+label_opacity = Label(frame, text="Opacity:")
+label_opacity.grid(column=0, row=1)
+scale_opacity = Scale(frame, from_=0, to=MAX_OPACITY, orient=HORIZONTAL, command=scale_opacity_update)
+scale_opacity.grid(column=1, row=1, sticky=W+E, padx=10)
+scale_opacity.set(chosen_opacity)
+
+label_color = Label(frame, text="Color:")
+label_color.grid(column=0, row=2)
+button_color = Button(frame, text="Color...", command=button_color_click)
+button_color.grid(column=1, row=2)
+
+frame.columnconfigure(0, pad=20)
+frame.columnconfigure(1, minsize=250)
+frame.rowconfigure(0, pad=20)
+frame.rowconfigure(1, pad=20)
+frame.rowconfigure(2, pad=20)
+
+button_ok = Button(window, text="Ok", command=button_ok_click)
+button_ok.pack(side=RIGHT, padx=10, pady=10)
+button_cancel = Button(window, text="Cancel", command=button_cancel_click)
+button_cancel.pack(side=RIGHT, pady=10)
+
+image.do_begin()
+selection.deselect()
+create_stroke_layer()
+apply_stroke()
+
+window.update()
+window_width = window.winfo_width()
+screen_width = window.winfo_screenwidth()
+window.geometry('+%d+0' % (int((screen_width - window_width) / 2)))
+
+window.mainloop()
+button_cancel_click()

+ 2 - 0
scripts/lazpaint/command.py

@@ -24,3 +24,5 @@ def send(command: str, **keywords):
   else:
     return
 
+def get_version(): # (major, minor, revision)
+  return send("LazPaintGetVersion?")

+ 45 - 0
scripts/lazpaint/tools.py

@@ -240,12 +240,18 @@ def set_fore_color(color):
 def set_back_color(color):
   command.send("ToolSetBackColor", Color=color)
 
+def set_outline_color(color):
+  command.send("ToolSetOutlineColor", Color=color)
+
 def get_fore_color():
   return colors.str_to_RGBA(command.send("ToolGetForeColor?"))
 
 def get_back_color():
   return colors.str_to_RGBA(command.send("ToolGetBackColor?"))
 
+def get_outline_color():
+  return colors.str_to_RGBA(command.send("ToolGetOutlineColor?"))
+
 def set_eraser_mode(mode):
   command.send('ToolSetEraserMode', Mode=mode)
 
@@ -429,6 +435,30 @@ def set_back_gradient_repetition(repetition):
 def get_back_gradient_repetition():
   return command.send('ToolGetBackGradientRepetition?')
 
+def set_outline_gradient_type(gradient_type):
+  command.send('ToolSetOutlineGradientType', GradientType=gradient_type)
+
+def get_outline_gradient_type():
+  return command.send('ToolGetOutlineGradientType?')
+
+def set_outline_gradient_colors(colors: list):
+  command.send('ToolSetOutlineGradientColors', Colors=colors)
+
+def get_outline_gradient_colors() -> list:
+  return colors.str_to_RGBA(command.send('ToolGetOutlineGradientColors?'))
+
+def set_outline_gradient_interpolation(interpolation):
+  command.send('ToolSetOutlineGradientInterpolation', Interpolation=interpolation)
+
+def get_outline_gradient_interpolation():
+  return command.send('ToolGetOutlineGradientInterpolation?')
+
+def set_outline_gradient_repetition(repetition):
+  command.send('ToolSetOutlineGradientRepetition', Repetition=repetition)
+
+def get_outline_gradient_repetition():
+  return command.send('ToolGetOutlineGradientRepetition?')
+
 def set_fore_texture(file_name):
   command.send('ToolSetForeTexture', FileName=file_name)
 
@@ -459,6 +489,21 @@ def set_back_texture_opacity(opacity: int):
 def get_back_texture_opacity(): #-> int 0..255
   return command.send('ToolGetBackTextureOpacity?')
 
+def set_outline_texture(file_name):
+  command.send('ToolSetOutlineTexture', FileName=file_name)
+
+def set_outline_texture_repetition(repetition):
+  command.send('ToolSetOutlineTextureRepetition', Repetition=repetition)
+
+def get_outline_texture_repetition():
+  return command.send('ToolGetOutlineTextureRepetition?')
+
+def set_outline_texture_opacity(opacity: int):
+  command.send('ToolSetOutlineTextureOpacity', Opacity=opacity)
+
+def get_outline_texture_opacity(): #-> int 0..255
+  return command.send('ToolGetOutlineTextureOpacity?')
+
 def set_phong_shape_kind(kind):
   command.send('ToolSetPhongShapeKind', Kind=kind)
 

+ 12 - 0
scripts/mask_from_alpha.py

@@ -0,0 +1,12 @@
+# Mask > Mask from alpha channel
+from lazpaint import image, layer, filters, selection
+
+image.do_begin()
+
+selection.deselect()
+layer.duplicate()
+layer.set_name("Mask")
+filters.filter_function(red="alpha", green="alpha", blue="alpha", alpha=255, gamma_correction=False)
+layer.set_blend_op(layer.BLEND_MASK)
+
+image.do_end()

+ 1 - 1
scripts/new_mask.py → scripts/mask_new.py

@@ -1,4 +1,4 @@
-# New mask
+# Mask > New mask
 from lazpaint import image, layer, tools, colors, selection
 
 image.do_begin()

+ 1 - 1
scripts/fractal_tree.py → scripts/render_fractal_tree.py

@@ -1,4 +1,4 @@
-# Render fractal tree
+# Render > Fractal tree
 from lazpaint import tools, image, layer, dialog
 import math, random
 

+ 1 - 1
scripts/render_lava.py

@@ -1,4 +1,4 @@
-# Render Lava
+# Render > Lava
 from lazpaint import image, layer, filters, colors
 
 image.do_begin()

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.