Переглянути джерело

Merge pull request #34 from bgrabitmap/dev-lazpaint

Dev lazpaint 7.0.2
circular17 6 роки тому
батько
коміт
3256a03c40
56 змінених файлів з 3081 додано та 1507 видалено
  1. BIN
      lazpaint/buttons/rasterize.lzp
  2. BIN
      lazpaint/buttons/rasterize.png
  3. BIN
      lazpaint/buttons/rasterize48.lzp
  4. BIN
      lazpaint/buttons/rasterize48.png
  5. 1 1
      lazpaint/lazpaint.lpi
  6. 1 1
      lazpaint/lazpaintembeddedpack.lpk
  7. 43 36
      lazpaint/lazpaintinstance.pas
  8. 342 64
      lazpaint/lazpaintmainform.lfm
  9. 33 11
      lazpaint/lazpaintmainform.pas
  10. 1 1
      lazpaint/lazpainttype.pas
  11. 50 19
      lazpaint/maintoolbar.inc
  12. 28 4
      lazpaint/release/i18n/lazpaint.ar.po
  13. 28 4
      lazpaint/release/i18n/lazpaint.cs.po
  14. 28 4
      lazpaint/release/i18n/lazpaint.de.po
  15. 28 4
      lazpaint/release/i18n/lazpaint.es.po
  16. 28 4
      lazpaint/release/i18n/lazpaint.fi.po
  17. 29 5
      lazpaint/release/i18n/lazpaint.fr.po
  18. 28 4
      lazpaint/release/i18n/lazpaint.ja.po
  19. 28 4
      lazpaint/release/i18n/lazpaint.lv.po
  20. 28 4
      lazpaint/release/i18n/lazpaint.nl.po
  21. 28 4
      lazpaint/release/i18n/lazpaint.po
  22. 28 4
      lazpaint/release/i18n/lazpaint.pt_BR.po
  23. 28 4
      lazpaint/release/i18n/lazpaint.ru.po
  24. 28 4
      lazpaint/release/i18n/lazpaint.sv.po
  25. 8 5
      lazpaint/ucanvassize.pas
  26. 5 5
      lazpaint/uchoosecolor.pas
  27. 18 17
      lazpaint/uconfig.pas
  28. 46 5
      lazpaint/ufilesystem.pas
  29. 5 16
      lazpaint/ufilterconnector.pas
  30. 9 29
      lazpaint/ugraph.pas
  31. 1 1
      lazpaint/ugridbitmap.pas
  32. 86 17
      lazpaint/uimage.pas
  33. 36 31
      lazpaint/uimageaction.pas
  34. 452 164
      lazpaint/uimagediff.pas
  35. 21 37
      lazpaint/uimagestate.pas
  36. 5 4
      lazpaint/uimageview.pas
  37. 53 114
      lazpaint/ulayeraction.pas
  38. 10 3
      lazpaint/ulayerstack.lfm
  39. 132 76
      lazpaint/ulayerstack.pas
  40. 14 3
      lazpaint/umenu.pas
  41. 6 6
      lazpaint/uphongfilter.pas
  42. 6 0
      lazpaint/uresourcestrings.pas
  43. 55 10
      lazpaint/ustatetype.pas
  44. 54 59
      lazpaint/utool.pas
  45. 196 58
      lazpaint/utoolbasic.pas
  46. 24 4
      lazpaint/utoolfloodfill.pas
  47. 367 455
      lazpaint/utoollayer.pas
  48. 18 7
      lazpaint/utoolphong.pas
  49. 146 8
      lazpaint/utoolpolygon.pas
  50. 60 103
      lazpaint/utoolselect.pas
  51. 30 6
      lazpaint/utooltext.pas
  52. 52 17
      lazpaintcontrols/lcvectororiginal.pas
  53. 174 10
      lazpaintcontrols/lcvectorpolyshapes.pas
  54. 61 35
      lazpaintcontrols/lcvectorrectshapes.pas
  55. 87 11
      lazpaintcontrols/lcvectortextshapes.pas
  56. 8 5
      vectoredit/umain.pas

BIN
lazpaint/buttons/rasterize.lzp


BIN
lazpaint/buttons/rasterize.png


BIN
lazpaint/buttons/rasterize48.lzp


BIN
lazpaint/buttons/rasterize48.png


+ 1 - 1
lazpaint/lazpaint.lpi

@@ -21,7 +21,7 @@
     <VersionInfo>
       <UseVersionInfo Value="True"/>
       <MajorVersionNr Value="7"/>
-      <RevisionNr Value="1"/>
+      <RevisionNr Value="2"/>
       <CharSet Value="04B0"/>
       <StringTable CompanyName="http://sourceforge.net/projects/lazpaint/" ProductName="LazPaint" InternalName="lazpaint" OriginalFilename="lazpaint.exe"/>
     </VersionInfo>

+ 1 - 1
lazpaint/lazpaintembeddedpack.lpk

@@ -25,7 +25,7 @@
         </Debugging>
       </Linking>
     </CompilerOptions>
-    <Version Major="7" Release="1"/>
+    <Version Major="7" Release="2"/>
     <Files Count="78">
       <Item1>
         <Filename Value="lazpaintinstance.pas"/>

+ 43 - 36
lazpaint/lazpaintinstance.pas

@@ -6,7 +6,7 @@ interface
 
 uses
   Classes, SysUtils, LazPaintType, BGRABitmap, BGRABitmapTypes, BGRALayers,
-  Menus, Controls,
+  Menus, Controls, fgl,
 
   LazPaintMainForm, UMainFormLayout,
 
@@ -20,6 +20,7 @@ const
   MaxToolPopupShowCount = 2;
 
 type
+  TImageListList = specialize TFPGObjectList<TImageList>;
 
   { TLazPaintInstance }
 
@@ -72,6 +73,7 @@ type
     FChooseColorPositionDefined,
     FLayerStackPositionDefined,
     FImageListPositionDefined : boolean;
+    FCustomImageList: TImageListList;
 
     function GetIcons(ASize: integer): TImageList; override;
     function GetToolBoxWindowPopup: TPopupMenu; override;
@@ -280,6 +282,7 @@ end;
 procedure TLazPaintInstance.Init(AEmbedded: boolean);
 begin
   Title := 'LazPaint ' + LazPaintCurrentVersion;
+  FCustomImageList := TImageListList.Create;
   FTopMostInfo.choosecolorHidden := 0;
   FTopMostInfo.layerstackHidden := 0;
   FTopMostInfo.toolboxHidden := 0;
@@ -319,7 +322,7 @@ begin
   Application.CreateForm(TFImageList, FImageList);
   FImageList.LazPaintInstance := self;
 
-  TFChooseColor_CustomDPI := round(Power(Config.DefaultIconSize(18)/16,0.7)*96);
+  TFChooseColor_CustomDPI := (Config.DefaultIconSize(DoScaleX(16,OriginalDPI))*96+8) div 16;
   Application.CreateForm(TFChooseColor, FChooseColor);
   FChooseColor.LazPaintInstance := self;
 
@@ -386,6 +389,7 @@ end;
 procedure TLazPaintInstance.CreateLayerStack;
 begin
   if Assigned(FLayerStack) then exit;
+  TFLayerStack_CustomDPI := (Config.DefaultIconSize(DoScaleX(16,OriginalDPI))*96+8) div 16;
   Application.CreateForm(TFLayerStack,FLayerStack);
   FLayerStack.LazPaintInstance := self;
 
@@ -393,6 +397,7 @@ begin
   FLayerStack.AddButton(FMain.LayerFromFile);
   FLayerStack.AddButton(FMain.LayerDuplicate);
   FLayerStack.AddButton(FMain.LayerMergeOver);
+  FLayerStack.AddButton(FMain.LayerRasterize);
   FLayerStack.AddSeparator;
   FLayerStack.AddButton(FMain.LayerMove);
   FLayerStack.AddButton(FMain.LayerRotate);
@@ -414,30 +419,30 @@ begin
   FToolbox.AddButton(FToolbox.Toolbar1, FMain.ToolBrush);
   FToolbox.AddButton(FToolbox.Toolbar1, FMain.ToolEraser);
   FToolbox.AddButton(FToolbox.Toolbar1, FMain.ToolFloodfill);
-  FToolbox.AddButton(FToolbox.Toolbar1, FMain.ToolGradient);
+  FToolbox.AddButton(FToolbox.Toolbar1, FMain.ToolClone);
 
   FToolbox.AddButton(FToolbox.Toolbar2, FMain.ToolRect);
   FToolbox.AddButton(FToolbox.Toolbar2, FMain.ToolEllipse);
   FToolbox.AddButton(FToolbox.Toolbar2, FMain.ToolPolygon);
   FToolbox.AddButton(FToolbox.Toolbar2, FMain.ToolSpline);
-  FToolbox.AddButton(FToolbox.Toolbar2, FMain.ToolText);
+  FToolbox.AddButton(FToolbox.Toolbar2, FMain.ToolGradient);
   FToolbox.AddButton(FToolbox.Toolbar2, FMain.ToolPhong);
 
   FToolbox.AddButton(FToolbox.Toolbar3, FMain.ToolSelectRect);
   FToolbox.AddButton(FToolbox.Toolbar3, FMain.ToolSelectEllipse);
   FToolbox.AddButton(FToolbox.Toolbar3, FMain.ToolSelectPoly);
   FToolbox.AddButton(FToolbox.Toolbar3, FMain.ToolSelectSpline);
-  FToolbox.AddButton(FToolbox.Toolbar3, FMain.ToolDeformation);
-  FToolbox.AddButton(FToolbox.Toolbar3, FMain.ToolTextureMapping);
+  FToolbox.AddButton(FToolbox.Toolbar3, FMain.ToolSelectPen);
+  FToolbox.AddButton(FToolbox.Toolbar3, FMain.ToolText);
 
   FToolbox.AddButton(FToolbox.Toolbar4, FMain.ToolColorPicker);
-  FToolbox.AddButton(FToolbox.Toolbar4, FMain.ToolClone);
-  FToolbox.AddButton(FToolbox.Toolbar4, FMain.ToolSelectPen);
+  FToolbox.AddButton(FToolbox.Toolbar4, FMain.ToolMagicWand);
   FToolbox.AddButton(FToolbox.Toolbar4, FMain.ToolMoveSelection);
   FToolbox.AddButton(FToolbox.Toolbar4, FMain.ToolRotateSelection);
-  FToolbox.AddButton(FToolbox.Toolbar4, FMain.ToolMagicWand);
+  FToolbox.AddButton(FToolbox.Toolbar4, FMain.ToolDeformation);
+  FToolbox.AddButton(FToolbox.Toolbar4, FMain.ToolTextureMapping);
 
-  FToolBox.SetImages(Icons[Config.DefaultIconSize(DoScaleX(24,OriginalDPI))]);
+  FToolBox.SetImages(Icons[Config.DefaultIconSize(DoScaleX(20,OriginalDPI))]);
 
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolChangeDocking);
 
@@ -446,22 +451,20 @@ begin
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolPen);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolBrush);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolEraser);
+  FMain.Layout.DockedToolBoxAddButton(FMain.ToolFloodfill);
+  FMain.Layout.DockedToolBoxAddButton(FMain.ToolClone);
 
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolRect);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolEllipse);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolPolygon);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolSpline);
-
-  FMain.Layout.DockedToolBoxAddButton(FMain.ToolFloodfill);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolGradient);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolPhong);
-
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolText);
+
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolDeformation);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolTextureMapping);
 
-  FMain.Layout.DockedToolBoxAddButton(FMain.ToolClone);
-
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolSelectRect);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolSelectEllipse);
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolSelectPoly);
@@ -474,7 +477,7 @@ begin
   FMain.Layout.DockedToolBoxAddButton(FMain.ToolRotateSelection);
   FMain.Layout.DockedToolBoxAddButton(FMain.EditDeselect);
 
-  FMain.Layout.DockedToolBoxSetImages(Icons[Config.DefaultIconSize(DoScaleX(24,OriginalDPI))]);
+  FMain.Layout.DockedToolBoxSetImages(Icons[Config.DefaultIconSize(DoScaleX(20,OriginalDPI))]);
 end;
 
 procedure TLazPaintInstance.SetBlackAndWhite(AValue: boolean);
@@ -843,33 +846,36 @@ begin
 end;
 
 function TLazPaintInstance.GetIcons(ASize: integer): TImageList;
+var
+  i: Integer;
 begin
   if Assigned(FMain) then
   begin
+    for i := 0 to FCustomImageList.Count-1 do
+      if FCustomImageList[i].Height = ASize then
+        exit(FCustomImageList[i]);
+
     if ASize < 24 then
-      result := FMain.ImageList16
-    else
-    if ASize < 32 then
-    begin
-      result := FMain.ImageList24;
-      if result.Count = 0 then
-        ScaleImageList(FMain.ImageList48, 24,24, result);
+    begin;
+      if ASize = 16 then
+        result := FMain.ImageList16
+      else
+      begin
+        result := TImageList.Create(nil);
+        ScaleImageList(FMain.ImageList16, ASize,ASize, result);
+        FCustomImageList.Add(result);
+      end;
     end
     else
-    if ASize < 48 then
-    begin
-      result := FMain.ImageList32;
-      if result.Count = 0 then
-        ScaleImageList(FMain.ImageList48, 32,32, result);
-    end
-    else
-    if ASize < 64 then
-      result := FMain.ImageList48
-    else
     begin
-      result := FMain.ImageList64;
-      if result.Count = 0 then
-        ScaleImageList(FMain.ImageList48, 64,64, result);
+      if ASize = 48 then
+        result := FMain.ImageList48
+      else
+      begin
+        result := TImageList.Create(nil);
+        ScaleImageList(FMain.ImageList48, ASize,ASize, result);
+        FCustomImageList.Add(result);
+      end;
     end;
   end else
     result := nil;
@@ -1098,6 +1104,7 @@ begin
   //MessageDlg(FScriptContext.RecordedScript,mtInformation,[mbOk],0);
   FreeAndNil(FScriptContext);
   FreeAndNil(FImageList);
+  FreeAndNil(FCustomImageList);
   inherited Destroy;
 end;
 

+ 342 - 64
lazpaint/lazpaintmainform.lfm

@@ -688,20 +688,6 @@ object FMain: TFMain
       Width = 45
       DropDownCount = 12
       ItemHeight = 17
-      Items.Strings = (
-        'None'
-        'Tail'
-        'Tip'
-        'Normal'
-        'Cut'
-        'Flipped'
-        'FlippedCut'
-        'Triangle'
-        'TriangleBack1'
-        'TriangleBack2'
-        'HollowTriangle'
-        'HollowTriangleBack1'
-      )
       Style = csOwnerDrawFixed
       TabOrder = 1
     end
@@ -712,20 +698,6 @@ object FMain: TFMain
       Width = 45
       DropDownCount = 12
       ItemHeight = 17
-      Items.Strings = (
-        'None'
-        'Tail'
-        'Tip'
-        'Normal'
-        'Cut'
-        'Flipped'
-        'FlippedCut'
-        'Triangle'
-        'TriangleBack1'
-        'TriangleBack2'
-        'HollowTriangle'
-        'HollowTriangleBack1'
-      )
       Style = csOwnerDrawFixed
       TabOrder = 2
     end
@@ -1617,10 +1589,10 @@ object FMain: TFMain
     end
   end
   object ImageList16: TBGRAImageList
-    left = 120
-    top = 432
+    left = 150
+    top = 540
     Bitmap = {
-      4C697C0000001000000010000000FFFFFF00B6B6B6FFB6B6B6FFB6B6B6FFB6B6
+      4C697D0000001000000010000000FFFFFF00B6B6B6FFB6B6B6FFB6B6B6FFB6B6
       B6FFB6B6B6FFB6B6B6FFB6B6B6FFB6B6B6FFB6B6B6FFB6B6B6EFB6B6B6300000
       000000000000FFFFFF00FFFFFF00FFFFFF00B6B6B6FFFFFFFFFFFFFFFFFFFFFF
       FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6B6B6FFDDDDDDFFB6B6B6EFB6B6
@@ -5588,13 +5560,45 @@ object FMain: TFMain
       000000000000000000000000000000000000000000000000000000000000C098
       3DF9CBA349FFCAA248F8C9A147EF000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000
+      000000000000000000000000000093375CFF8E3559FF8E3559FF8E3559FF0F0E
+      84FF0F0E84FF0F0E84FF0F0E84FF062753FF062753FF062753FF062753FF0000
+      00FF000000FF000000FF000000FF8E3559FFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF8E3559FFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF8E3559FFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF9F7290FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FF743319FF9F7290FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FF743319FF9F7290FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FF743319FF9F7290FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FF743319FF859E9FFFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FF996E2CFF859E9FFFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FF996E2CFF859E9FFFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FF996E2CFF859E9FFFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FF996E2CFF8585A0FFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFA2A373FF8585A0FFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFA2A373FF8585A0FFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFA2A373FF8A8AA6008585A0FF8585A0FF8585A0FF2E98
+      A2FF2E98A2FF2E98A2FF2E98A2FF607745FF607745FF607745FF607745FFA2A3
+      73FFA2A373FFA2A373FFA8A977FF
     }
   end
   object ActionList1: TActionList
     Images = ImageList16
-    left = 200
-    top = 432
+    left = 250
+    top = 540
     object FileNew: TAction
       Category = 'File'
       Caption = 'New...'
@@ -6532,6 +6536,7 @@ object FMain: TFMain
       Hint = 'Zoom layer'
       ImageIndex = 123
       OnExecute = LayerZoomExecute
+      OnUpdate = LayerZoomUpdate
     end
     object SelectionHorizontalFlip: TAction
       Category = 'SelectTool'
@@ -6552,6 +6557,13 @@ object FMain: TFMain
       Hint = 'Remember save format'
       OnExecute = FileRememberSaveFormatExecute
     end
+    object LayerRasterize: TAction
+      Category = 'Layer'
+      Hint = 'Rasterize layer'
+      ImageIndex = 124
+      OnExecute = ScriptExecute
+      OnUpdate = LayerRasterizeUpdate
+    end
   end
   object ColorDialog1: TColorDialog
     Title = 'Choose color'
@@ -6583,8 +6595,8 @@ object FMain: TFMain
   end
   object MainMenu1: TMainMenu
     Images = ImageList16
-    left = 48
-    top = 432
+    left = 56
+    top = 464
     object MenuFile: TMenuItem
       Caption = 'File'
       OnClick = MenuFileClick
@@ -6618,10 +6630,6 @@ object FMain: TFMain
           Caption = '48px'
           OnClick = ItemIconSize48Click
         end
-        object ItemIconSize64: TMenuItem
-          Caption = '64px'
-          OnClick = ItemIconSize64Click
-        end
         object ItemIconSizeAuto: TMenuItem
           Caption = 'auto'
           OnClick = ItemIconSizeAutoClick
@@ -6834,8 +6842,8 @@ object FMain: TFMain
   end
   object PopupToolbox: TPopupMenu
     OnPopup = PopupToolboxPopup
-    left = 336
-    top = 354
+    left = 152
+    top = 464
     object MenuDockToolboxLeft: TMenuItem
       Caption = 'Dock left'
       OnClick = MenuDockToolboxLeftClick
@@ -6852,10 +6860,10 @@ object FMain: TFMain
   object ImageList48: TBGRAImageList
     Height = 48
     Width = 48
-    left = 24
-    top = 354
+    left = 56
+    top = 540
     Bitmap = {
-      4C697C0000003000000030000000000000000000000000000000000000000000
+      4C697D0000003000000030000000000000000000000000000000000000000000
       0000000000000000000F000000720000007F0000007F0000007F0000007F0000
       007F0000007F0000007F0000007F0000007F0000007F0000007F0000007F0000
       007F0000007F0000007F0000007F0000007F0000007F0000007F000000850000
@@ -42567,25 +42575,295 @@ object FMain: TFMain
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000
+      000000000000000000000000000096385E9090365BFF8E3559FF8E3559FF8E35
+      59FF8E3559FF8E3559FF8E3559FF8E3559FF8E3559FF8E3559FF8E3559FF0F0E
+      84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E
+      84FF0F0E84FF0F0E84FF0F0E84FF062753FF062753FF062753FF062753FF0627
+      53FF062753FF062753FF062753FF062753FF062753FF062753FF062753FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF0000009090365BFF8E3559FF8E3559FF8E3559FF8E35
+      59FF8E3559FF8E3559FF8E3559FF8E3559FF8E3559FF8E3559FF8E3559FF0F0E
+      84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E
+      84FF0F0E84FF0F0E84FF0F0E84FF062753FF062753FF062753FF062753FF0627
+      53FF062753FF062753FF062753FF062753FF062753FF062753FF062753FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FF8E3559FF8E35
+      59FF8E3559FF8E3559FF8E3559FF8E3559FF8E3559FF8E3559FF8E3559FF0F0E
+      84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E84FF0F0E
+      84FF0F0E84FF0F0E84FF0F0E84FF062753FF062753FF062753FF062753FF0627
+      53FF062753FF062753FF062753FF062753FF062753FF062753FF062753FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FFDE538CFFDE53
+      8CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D
+      83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FFDE538CFFDE53
+      8CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D
+      83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FFDE538CFFDE53
+      8CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D
+      83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FFDE538CFFDE53
+      8CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D
+      83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FFDE538CFFDE53
+      8CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D
+      83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FFDE538CFFDE53
+      8CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D
+      83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FFDE538CFFDE53
+      8CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D
+      83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FFDE538CFFDE53
+      8CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D
+      83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF8E3559FF8E3559FF8E3559FFDE538CFFDE53
+      8CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFFDE538CFF1817
+      CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817CFFF1817
+      CFFF1817CFFF1817CFFF1817CFFF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D
+      83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0A3D83FF0000
+      00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+      00FF000000FF000000FF000000FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF9F7290FF9F7290FF9F7290FFF9B3E2FFF9B3
+      E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FFF9B3E2FF566E
+      FCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566EFCFF566E
+      FCFF566EFCFF566EFCFF566EFCFF287765FF287765FF287765FF287765FF2877
+      65FF287765FF287765FF287765FF287765FF287765FF287765FF287765FFB650
+      28FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB65028FFB650
+      28FF743319FF743319FF743319FF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF859E9FFF859E9FFF859E9FFFD1F7F9FFD1F7
+      F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FFD1F7F9FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6F1FF6FD6
+      F1FF6FD6F1FF6FD6F1FF6FD6F1FF39B23DFF39B23DFF39B23DFF39B23DFF39B2
+      3DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFF39B23DFFF0AD
+      45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD45FFF0AD
+      45FF996E2CFF996E2CFF996E2CFF8585A0FF8585A0FF8585A0FFD0D0FAFFD0D0
+      FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA
+      6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFF
+      B4FFA2A373FFA2A373FFA2A373FF8585A0FF8585A0FF8585A0FFD0D0FAFFD0D0
+      FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA
+      6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFF
+      B4FFA2A373FFA2A373FFA2A373FF8585A0FF8585A0FF8585A0FFD0D0FAFFD0D0
+      FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA
+      6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFF
+      B4FFA2A373FFA2A373FFA2A373FF8585A0FF8585A0FF8585A0FFD0D0FAFFD0D0
+      FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA
+      6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFF
+      B4FFA2A373FFA2A373FFA2A373FF8585A0FF8585A0FF8585A0FFD0D0FAFFD0D0
+      FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA
+      6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFF
+      B4FFA2A373FFA2A373FFA2A373FF8585A0FF8585A0FF8585A0FFD0D0FAFFD0D0
+      FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA
+      6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFF
+      B4FFA2A373FFA2A373FFA2A373FF8585A0FF8585A0FF8585A0FFD0D0FAFFD0D0
+      FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA
+      6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFF
+      B4FFA2A373FFA2A373FFA2A373FF8585A0FF8585A0FF8585A0FFD0D0FAFFD0D0
+      FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA
+      6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFF
+      B4FFA2A373FFA2A373FFA2A373FF8585A0FF8585A0FF8585A0FFD0D0FAFFD0D0
+      FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFFD0D0FAFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EEFEFF48EE
+      FEFF48EEFEFF48EEFEFF48EEFEFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA
+      6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFF96BA6CFFFEFF
+      B4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFFB4FFFEFF
+      B4FFA2A373FFA2A373FFA2A373FF8585A0FF8585A0FF8585A0FF8585A0FF8585
+      A0FF8585A0FF8585A0FF8585A0FF8585A0FF8585A0FF8585A0FF8585A0FF2E98
+      A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98
+      A2FF2E98A2FF2E98A2FF2E98A2FF607745FF607745FF607745FF607745FF6077
+      45FF607745FF607745FF607745FF607745FF607745FF607745FF607745FFA2A3
+      73FFA2A373FFA2A373FFA2A373FFA2A373FFA2A373FFA2A373FFA2A373FFA2A3
+      73FFA2A373FFA2A373FFA2A373FF8787A3FF8585A0FF8585A0FF8585A0FF8585
+      A0FF8585A0FF8585A0FF8585A0FF8585A0FF8585A0FF8585A0FF8585A0FF2E98
+      A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98
+      A2FF2E98A2FF2E98A2FF2E98A2FF607745FF607745FF607745FF607745FF6077
+      45FF607745FF607745FF607745FF607745FF607745FF607745FF607745FFA2A3
+      73FFA2A373FFA2A373FFA2A373FFA2A373FFA2A373FFA2A373FFA2A373FFA2A3
+      73FFA2A373FFA2A373FFA5A675FF8C8CA8908787A3FF8585A0FF8585A0FF8585
+      A0FF8585A0FF8585A0FF8585A0FF8585A0FF8585A0FF8585A0FF8585A0FF2E98
+      A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98A2FF2E98
+      A2FF2E98A2FF2E98A2FF2E98A2FF607745FF607745FF607745FF607745FF6077
+      45FF607745FF607745FF607745FF607745FF607745FF607745FF607745FFA2A3
+      73FFA2A373FFA2A373FFA2A373FFA2A373FFA2A373FFA2A373FFA2A373FFA2A3
+      73FFA2A373FFA5A675FFABAC7990
     }
   end
-  object ImageList32: TBGRAImageList
-    Height = 32
-    Width = 32
-    left = 184
-    top = 354
-  end
-  object ImageList64: TBGRAImageList
-    Height = 64
-    Width = 64
-    left = 256
-    top = 354
-  end
-  object ImageList24: TBGRAImageList
-    Height = 24
-    Width = 24
-    left = 104
-    top = 354
-  end
 end

+ 33 - 11
lazpaint/lazpaintmainform.pas

@@ -28,6 +28,7 @@ type
   { TFMain }
 
   TFMain = class(TForm)
+    LayerRasterize: TAction;
     FileRememberSaveFormat: TAction;
     SelectionVerticalFlip: TAction;
     SelectionHorizontalFlip: TAction;
@@ -50,9 +51,6 @@ type
     Tool_Aliasing: TToolButton;
     ViewPalette: TAction;
     ViewStatusBar: TAction;
-    ImageList24: TBGRAImageList;
-    ImageList64: TBGRAImageList;
-    ImageList32: TBGRAImageList;
     ImageList48: TBGRAImageList;
     ItemViewPalette: TMenuItem;
     MenuIconSize: TMenuItem;
@@ -60,7 +58,6 @@ type
     ItemIconSize32: TMenuItem;
     ItemIconSize48: TMenuItem;
     ItemIconSizeAuto: TMenuItem;
-    ItemIconSize64: TMenuItem;
     ItemIconSize24: TMenuItem;
     ItemViewStatusBar: TMenuItem;
     MenuShowPalette: TMenuItem;
@@ -456,7 +453,9 @@ type
     procedure ItemFullscreenClick(Sender: TObject);
     procedure ItemIconSize24Click(Sender: TObject);
     procedure ItemViewDockToolboxClick(Sender: TObject);
+    procedure LayerRasterizeUpdate(Sender: TObject);
     procedure LayerZoomExecute(Sender: TObject);
+    procedure LayerZoomUpdate(Sender: TObject);
     procedure MenuCoordinatesToolbarClick(Sender: TObject);
     procedure MenuCopyPasteToolbarClick(Sender: TObject);
     procedure MenuDockToolboxLeftClick(Sender: TObject);
@@ -820,7 +819,8 @@ implementation
 
 uses LCLIntf, BGRAUTF8, ugraph, math, umac, uclipboard, ucursors,
    ufilters, ULoadImage, ULoading, UFileExtensions, UBrushType,
-   ugeometricbrush, UPreviewDialog, UQuestion, BGRALayerOriginal;
+   ugeometricbrush, UPreviewDialog, UQuestion, BGRALayerOriginal,
+   BGRATransform, LCVectorPolyShapes;
 
 const PenWidthFactor = 10;
 
@@ -876,7 +876,6 @@ begin
   InFormMouseMove:= false;
   InFormPaint := false;
 
-  CreateMenuAndToolbar;
   {$IFDEF LINUX}
   ComboBox_BrushSelect.Top := ComboBox_BrushSelect.Top - 2;
   ComboBox_BrushSelect.Font.Height := -10;
@@ -906,6 +905,7 @@ begin
     Panel_Tool,Panel_Color,Panel_Texture,Panel_Grid,Panel_PenWidth,Panel_Aliasing,Panel_ShapeOption,Panel_LineCap,Panel_JoinStyle,
     Panel_PenStyle,Panel_SplineStyle,Panel_Eraser,Panel_Tolerance,Panel_GradientType,Panel_Text,Panel_TextOutline,
     Panel_PhongShape,Panel_Altitude,Panel_PerspectiveOption,Panel_Brush,Panel_Ratio],Panel_ToolbarBackground);
+  m.ImageList := LazPaintInstance.Icons[ScaleY(16, 96)];
   m.Apply;
   FLayout.Menu := m;
 end;
@@ -965,6 +965,7 @@ end;
 procedure TFMain.Init;
 begin
   initialized := false;
+  CreateMenuAndToolbar;
   Config := LazPaintInstance.Config;
   if Config.Default3dObjectDirectory = '' then
     Config.SetDefault3dObjectDirectory(StartDirectory);
@@ -1132,8 +1133,7 @@ begin
   updateForVSCursor:= false;
   if ToolManager.ToolMove(BmpPos,CurrentPressure) then
   begin
-    FImageView.UpdatePicture(PictureCanvasOfs, FLayout.WorkArea,
-                             {$IFDEF USEPAINTBOXPICTURE}PaintBox_Picture{$ELSE}self{$ENDIF});
+    FImageView.UpdatePicture(PictureCanvasOfs, FLayout.WorkArea, self);
     ToolManager.ToolMoveAfter(FImageView.FormToBitmap(FormMouseMovePos)); //new BmpPos after repaint
   end else
     updateForVSCursor := true;
@@ -2711,9 +2711,22 @@ begin
           end;
           ptMoveLayer, ptRotateLayer, ptZoomLayer:
           begin
-            if image.CurrentLayerEquals(BGRAPixelTransparent) then
+            if image.LayerOriginalDefined[image.CurrentLayerIndex] and
+               image.LayerOriginalKnown[image.CurrentLayerIndex] and
+               (image.LayerOriginal[image.CurrentLayerIndex]=nil) then
+            begin
+              Tool := ptHand;
+              result := srException;
+            end;
+
+            if image.CurrentLayerEquals(BGRAPixelTransparent) and not
+              (image.LayerOriginalDefined[image.CurrentLayerIndex] and
+               image.LayerOriginalKnown[image.CurrentLayerIndex] and
+               not image.LayerOriginal[image.CurrentLayerIndex].GetRenderBounds(
+                 rect(-maxLongInt div 2,-maxLongInt div 2,maxLongInt div 2,maxLongInt div 2),
+                 AffineMatrixIdentity).IsEmpty) then
             begin
-              MessagePopup(rsLazPaint, 4000);
+              MessagePopup(rsEmptyLayer, 4000);
               Tool := ptHand;
               result := srException;
             end;
@@ -2943,11 +2956,21 @@ begin
     Layout.ToolBoxDocking := twWindow;
 end;
 
+procedure TFMain.LayerRasterizeUpdate(Sender: TObject);
+begin
+  LayerRasterize.Enabled := Image.LayerOriginalDefined[Image.CurrentLayerIndex];
+end;
+
 procedure TFMain.LayerZoomExecute(Sender: TObject);
 begin
   ChooseTool(ptZoomLayer);
 end;
 
+procedure TFMain.LayerZoomUpdate(Sender: TObject);
+begin
+  LayerZoom.Enabled := Image.CurrentLayerVisible and Image.SelectionMaskEmpty;
+end;
+
 procedure TFMain.MenuCoordinatesToolbarClick(Sender: TObject);
 begin
   Panel_Coordinates.Visible := not Panel_Coordinates.Visible;
@@ -3010,7 +3033,6 @@ begin
   ItemIconSize24.Checked := iconSize=24;
   ItemIconSize32.Checked := iconSize=32;
   ItemIconSize48.Checked := iconSize=48;
-  ItemIconSize64.Checked := iconSize=64;
   ItemIconSizeAuto.Checked := iconSize=0;
 end;
 

+ 1 - 1
lazpaint/lazpainttype.pas

@@ -10,7 +10,7 @@ uses
   {$IFDEF LINUX}, InterfaceBase{$ENDIF};
 
 const
-  LazPaintVersion = 7000100;
+  LazPaintVersion = 7000200;
 
   function LazPaintVersionStr: string;
 

+ 50 - 19
lazpaint/maintoolbar.inc

@@ -282,6 +282,7 @@ end;
 procedure TFMain.InitToolbarElements;
 var
   i: Integer;
+  ak: TArrowKind;
 begin
   Panel_Embedded.Visible := LazPaintInstance.Embedded;
   Panel_File.Visible := not LazPaintInstance.Embedded;
@@ -292,6 +293,13 @@ begin
   Shape_BackColor.Brush.Color := BGRAToColor(ToolManager.ToolBackColor);
   SpinEdit_BackOpacity.Value := ToolManager.ToolBackColor.alpha;
   SpinEdit_PenWidth.Value := Round(ToolManager.ToolPenWidth*PenWidthFactor);
+  for ak := low(TArrowKind) to high(TArrowKind) do
+  begin
+    ComboBox_ArrowStart.Items.Add(ArrowKindToStr[ak]);
+    ComboBox_ArrowEnd.Items.Add(ArrowKindToStr[ak]);
+  end;
+  ComboBox_ArrowStart.ItemIndex := 0;
+  ComboBox_ArrowEnd.ItemIndex := 0;
   SpinEdit_ArrowSizeX.Value := round(ToolManager.ToolArrowSize.x*PenWidthFactor);
   SpinEdit_ArrowSizeY.Value := round(ToolManager.ToolArrowSize.y*PenWidthFactor);
   Tool_DrawShapeBorder.Down := ToolManager.ToolOptionDrawShape;
@@ -305,10 +313,7 @@ begin
   Tool_GridMoveWithoutDeformation.Down := ToolManager.ToolDeformationGridMoveWithoutDeformation;
   SpinEdit_GridNbX.Value := ToolManager.ToolDeformationGridNbX-1;
   SpinEdit_GridNbY.Value := ToolManager.ToolDeformationGridNbY-1;
-  if ToolManager.ToolSplineEasyBezier then
-    Combo_SplineStyle.ItemIndex := Combo_SplineStyle.Items.IndexOf('Easy Bézier')
-  else
-    Combo_SplineStyle.ItemIndex:= ord(ToolManager.ToolSplineStyle);
+  Combo_SplineStyle.ItemIndex:= ord(ToolManager.ToolSplineStyle);
   UpdateCurveMode;
   Tool_TextOutline.Down := ToolManager.ToolTextOutline;
   SpinEdit_TextOutlineWidth.Value := round(ToolManager.ToolTextOutlineWidth*PenWidthFactor);
@@ -755,8 +760,9 @@ begin
   if initialized then
   begin
      if not Tool_DrawShapeBorder.Down and not Tool_FillShape.Down then
-        Tool_DrawShapeBorder.Down := true;
+       Tool_FillShape.Down := true;
      ToolManager.ToolOptionDrawShape:= Tool_DrawShapeBorder.Down;
+     ToolManager.ToolOptionFillShape:= Tool_FillShape.Down;
      UpdateEditPicture;
   end;
 end;
@@ -766,7 +772,8 @@ begin
   if initialized then
   begin
      if not Tool_DrawShapeBorder.Down and not Tool_FillShape.Down then
-        Tool_FillShape.Down := true;
+        Tool_DrawShapeBorder.Down := true;
+     ToolManager.ToolOptionDrawShape:= Tool_DrawShapeBorder.Down;
      ToolManager.ToolOptionFillShape:= Tool_FillShape.Down;
      UpdateEditPicture;
   end;
@@ -821,20 +828,30 @@ end;
 
 procedure TFMain.ComboBox_ArrowStartChange(Sender: TObject);
 begin
-  if initialized then UpdateEditPicture;
+  if initialized then
+  begin
+    ToolManager.ToolArrowStart:= ComboBox_ArrowStart.Text;
+    UpdateEditPicture;
+  end;
 end;
 
 procedure TFMain.ComboBox_ArrowStartDrawItem(Control: TWinControl;
   Index: Integer; ARect: TRect; State: TOwnerDrawState);
+var
+  kind: String;
 begin
   if Index = -1 then exit;
-  ToolManager.ToolArrowStart := ComboBox_ArrowStart.Items[Index];
-  DrawArrow(ComboBox_ArrowStart.Canvas,ARect,True,ToolManager.ToolArrowStart,ToolManager.ToolLineCap,State);
+  kind := ComboBox_ArrowStart.Items[Index];
+  DrawArrow(ComboBox_ArrowStart.Canvas,ARect,True,kind,ToolManager.ToolLineCap,State);
 end;
 
 procedure TFMain.ComboBox_ArrowEndChange(Sender: TObject);
 begin
-  if initialized then UpdateEditPicture;
+  if initialized then
+  begin
+    ToolManager.ToolArrowEnd:= ComboBox_ArrowEnd.Text;
+    UpdateEditPicture;
+  end;
 end;
 
 procedure TFMain.BrushLoadFromFileExecute(Sender: TObject);
@@ -860,10 +877,12 @@ end;
 
 procedure TFMain.ComboBox_ArrowEndDrawItem(Control: TWinControl;
   Index: Integer; ARect: TRect; State: TOwnerDrawState);
+var
+  kind: String;
 begin
   if Index = -1 then exit;
-  ToolManager.ToolArrowEnd := ComboBox_ArrowEnd.Items[Index];
-  DrawArrow(ComboBox_ArrowEnd.Canvas,ARect,False,ToolManager.ToolArrowEnd,ToolManager.ToolLineCap,State);
+  kind := ComboBox_ArrowEnd.Items[Index];
+  DrawArrow(ComboBox_ArrowEnd.Canvas,ARect,False,kind,ToolManager.ToolLineCap,State);
 end;
 
 procedure TFMain.SpinEdit_ArrowSizeChange(Sender: TObject);
@@ -974,9 +993,9 @@ end;
 procedure TFMain.SetCurveMode(AMode: TToolSplineMode);
 begin
   if (ToolManager.CurrentTool <> nil) and
-     (ToolManager.CurrentTool is TToolGenericSpline) then
+     (ToolManager.CurrentTool is TToolSpline) then
   begin
-    (ToolManager.CurrentTool as TToolGenericSpline).CurrentMode := AMode;
+    (ToolManager.CurrentTool as TToolSpline).CurrentMode := AMode;
     UpdateCurveMode;
   end;
 end;
@@ -984,11 +1003,11 @@ end;
 procedure TFMain.UpdateCurveMode;
 var
   cm: TToolSplineMode;
-  splineTool: TToolGenericSpline;
+  splineTool: TToolSpline;
 begin
-  if (ToolManager.CurrentTool <> nil) and (ToolManager.CurrentTool is TToolGenericSpline) then
+  if (ToolManager.CurrentTool <> nil) and (ToolManager.CurrentTool is TToolSpline) then
   begin
-    splineTool := ToolManager.CurrentTool as TToolGenericSpline;
+    splineTool := ToolManager.CurrentTool as TToolSpline;
     Tool_CurveMovePoint.Enabled := not splineTool.IsHandDrawing and not splineTool.IsIdle;
     cm := splineTool.CurrentMode;
     if Tool_CurveMovePoint.Down <> (cm = tsmMovePoint) then
@@ -1008,7 +1027,6 @@ begin
   if initialized then
   begin
     v := Combo_SplineStyle.Text;
-    ToolManager.ToolSplineEasyBezier := false;
     if v = 'Inside' then ToolManager.ToolSplineStyle := ssInside else
     if v = 'Inside + ends' then ToolManager.ToolSplineStyle := ssInsideWithEnds else
     if v = 'Crossing' then ToolManager.ToolSplineStyle := ssCrossing else
@@ -1016,7 +1034,20 @@ begin
     if v = 'Outside' then ToolManager.ToolSplineStyle := ssOutside else
     if v = 'Round outside' then ToolManager.ToolSplineStyle:= ssRoundOutside else
     if v = 'Vertex to side' then ToolManager.ToolSplineStyle:= ssVertexToSide else
-    if v = 'Easy Bézier' then ToolManager.ToolSplineEasyBezier := true;
+    if v = 'Easy Bézier' then ToolManager.ToolSplineStyle := ssEasyBezier;
+    if ToolManager.ToolSplineStyle<>ssEasyBezier then
+    begin
+      Tool_CurveMovePoint.Down := true;
+      SetCurveMode(tsmMovePoint);
+      Tool_CurveModeAuto.Enabled := false;
+      Tool_CurveModeAngle.Enabled := false;
+      Tool_CurveModeCurve.Enabled := false;
+    end else
+    begin
+      Tool_CurveModeAuto.Enabled := true;
+      Tool_CurveModeAngle.Enabled := true;
+      Tool_CurveModeCurve.Enabled := true;
+    end;
     UpdateEditPicture(True);
     UpdateCurveMode;
   end;

+ 28 - 4
lazpaint/release/i18n/lazpaint.ar.po

@@ -1101,10 +1101,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1259,6 +1255,10 @@ msgctxt "tfmain.layermove.hint"
 msgid "Move layer"
 msgstr "تحريك الطبقة"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "tfmain.layerremovecurrent.hint"
 msgid "Remove layer"
@@ -2919,6 +2919,10 @@ msgstr ""
 msgid "Error"
 msgstr ""
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr ""
@@ -3311,6 +3315,10 @@ msgstr "px"
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr "المجلدات الأخيرة:"
@@ -3428,6 +3436,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""
@@ -3436,6 +3448,10 @@ msgstr ""
 msgid "Transfer selection to other layer?"
 msgstr "نقل التحديد إلى طبقة أخرى؟"
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3456,6 +3472,14 @@ msgstr "لايمكن حفظ الملف :"
 msgid "Unknown command : "
 msgstr "الأمر غير معروف :"
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr "نعم"

+ 28 - 4
lazpaint/release/i18n/lazpaint.cs.po

@@ -1089,10 +1089,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1247,6 +1243,10 @@ msgctxt "tfmain.layermove.hint"
 msgid "Move layer"
 msgstr "Přesunout vrstvu"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "tfmain.layerremovecurrent.hint"
 msgid "Remove layer"
@@ -2906,6 +2906,10 @@ msgstr ""
 msgid "Error"
 msgstr "Chyba"
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr ""
@@ -3298,6 +3302,10 @@ msgstr "px"
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr "Nedávné adresáře:"
@@ -3415,6 +3423,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""
@@ -3423,6 +3435,10 @@ msgstr ""
 msgid "Transfer selection to other layer?"
 msgstr "Přenést výběr do jiné vrstvy?"
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3443,6 +3459,14 @@ msgstr "Nelze uložit soubor :"
 msgid "Unknown command : "
 msgstr "Neznámý příkaz :"
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr "Ano"

+ 28 - 4
lazpaint/release/i18n/lazpaint.de.po

@@ -1106,10 +1106,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1264,6 +1260,10 @@ msgctxt "tfmain.layermove.hint"
 msgid "Move layer"
 msgstr "Ebene verschieben"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "tfmain.layerremovecurrent.hint"
 msgid "Remove layer"
@@ -2924,6 +2924,10 @@ msgstr ""
 msgid "Error"
 msgstr "Fehler"
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr "Fehler beim Öffnen der Datei \"%1\""
@@ -3316,6 +3320,10 @@ msgstr "px"
 msgid "RAM disk"
 msgstr "RAM-Disk"
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr "Letzte Verzeichnisse"
@@ -3433,6 +3441,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr "Zu viel Ebene"
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Bilder Gesamt: %1"
@@ -3441,6 +3453,10 @@ msgstr "Bilder Gesamt: %1"
 msgid "Transfer selection to other layer?"
 msgstr "Auswahl auf andere Ebene verschieben?"
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3461,6 +3477,14 @@ msgstr "Kann Datei nicht sichern: "
 msgid "Unknown command : "
 msgstr "Unbekannter Befehl: "
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr "Ja"

+ 28 - 4
lazpaint/release/i18n/lazpaint.es.po

@@ -1085,10 +1085,6 @@ msgstr "32px"
 msgid "48px"
 msgstr "48px"
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr "64px"
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr "automático"
@@ -1243,6 +1239,10 @@ msgctxt "tfmain.layermove.hint"
 msgid "Move layer"
 msgstr "Mover capa"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr "Pixelar capa"
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "tfmain.layerremovecurrent.hint"
 msgid "Remove layer"
@@ -2904,6 +2904,10 @@ msgstr "Entradas"
 msgid "Error"
 msgstr "Error"
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr "Error cargando el original pero se puedo pixelar la capa."
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr "Error abriendo archivo \"%1\""
@@ -3308,6 +3312,10 @@ msgstr "px"
 msgid "RAM disk"
 msgstr "Disco RAM"
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr "Capa de trama"
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr "Carpetas recientes:"
@@ -3427,6 +3435,10 @@ msgstr "La herramienta no puede ser usada en una capa invisible"
 msgid "Too many layers"
 msgstr "Demasiado capas"
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr "Demasiado figuras en la capa"
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Total: %1"
@@ -3435,6 +3447,10 @@ msgstr "Total: %1"
 msgid "Transfer selection to other layer?"
 msgstr "¿Transferir selección a otra capa?"
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr "Capa de trama transformada"
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr "¿Quieres transformar el contenido de la selección?"
@@ -3455,6 +3471,14 @@ msgstr "Imposible guardar archivo: "
 msgid "Unknown command : "
 msgstr "Comando desconocido: "
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr "Original desconocido"
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr "Capa vectorial"
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr "Sí"

+ 28 - 4
lazpaint/release/i18n/lazpaint.fi.po

@@ -1075,10 +1075,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1233,6 +1229,10 @@ msgctxt "TFMAIN.LAYERMOVE.HINT"
 msgid "Move layer"
 msgstr "Siirrä kerrosta"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "TFMAIN.LAYERREMOVECURRENT.HINT"
 msgid "Remove layer"
@@ -2889,6 +2889,10 @@ msgstr ""
 msgid "Error"
 msgstr "Virhe"
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr "Virhe avattaessa tiedostoa \"%1\""
@@ -3281,6 +3285,10 @@ msgstr ""
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr "Viimeisimmät kansiot:"
@@ -3398,6 +3406,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Kuvia kaikkiaan: %1"
@@ -3406,6 +3418,10 @@ msgstr "Kuvia kaikkiaan: %1"
 msgid "Transfer selection to other layer?"
 msgstr ""
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3426,6 +3442,14 @@ msgstr ""
 msgid "Unknown command : "
 msgstr ""
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr "Kyllä"

+ 29 - 5
lazpaint/release/i18n/lazpaint.fr.po

@@ -1091,10 +1091,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1249,6 +1245,10 @@ msgctxt "tfmain.layermove.hint"
 msgid "Move layer"
 msgstr "Déplacer le calque"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr "Pixéliser le calque"
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "tfmain.layerremovecurrent.hint"
 msgid "Remove layer"
@@ -2364,7 +2364,7 @@ msgstr "OK"
 #: tfpixelate.caption
 msgctxt "tfpixelate.caption"
 msgid "Pixelate"
-msgstr "Pixeliser"
+msgstr "Pixéliser"
 
 #: tfpixelate.label_pixelsize.caption
 msgctxt "tfpixelate.label_pixelsize.caption"
@@ -2911,6 +2911,10 @@ msgstr "Entrées"
 msgid "Error"
 msgstr "Erreur"
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr "Erreur au chargement de l'original alors le calque peut être pixelisé."
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr "Erreur lors de l'ouverture de \"%1\""
@@ -3315,6 +3319,10 @@ msgstr "px"
 msgid "RAM disk"
 msgstr "RAM disque"
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr "Calque en pixels"
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr "Répertoires récents :"
@@ -3434,6 +3442,10 @@ msgstr "L'outil ne peut pas être utilisé sur un calque invisible"
 msgid "Too many layers"
 msgstr "Trop de calques"
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr "Trop de formes dans le calque"
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Nb. total d'images : %1"
@@ -3442,6 +3454,10 @@ msgstr "Nb. total d'images : %1"
 msgid "Transfer selection to other layer?"
 msgstr "Voulez-vous transférer la sélection vers un autre calque ?"
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr "Calque en pixels transformé"
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr "Voulez-vous transformer le contenu de la sélection ?"
@@ -3462,6 +3478,14 @@ msgstr "Impossible d'enregistrer le fichier : "
 msgid "Unknown command : "
 msgstr "Commande inconnue : "
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr "Original inconnu"
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr "Calque vectoriel"
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr "Oui"

+ 28 - 4
lazpaint/release/i18n/lazpaint.ja.po

@@ -1096,10 +1096,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1254,6 +1250,10 @@ msgctxt "tfmain.layermove.hint"
 msgid "Move layer"
 msgstr ""
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "tfmain.layerremovecurrent.hint"
 msgid "Remove layer"
@@ -2912,6 +2912,10 @@ msgstr ""
 msgid "Error"
 msgstr ""
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr ""
@@ -3303,6 +3307,10 @@ msgstr ""
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr ""
@@ -3420,6 +3428,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""
@@ -3428,6 +3440,10 @@ msgstr ""
 msgid "Transfer selection to other layer?"
 msgstr ""
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3448,6 +3464,14 @@ msgstr "ファイルの保存に失敗しました:"
 msgid "Unknown command : "
 msgstr "Unknown command:"
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr ""

+ 28 - 4
lazpaint/release/i18n/lazpaint.lv.po

@@ -1092,10 +1092,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1250,6 +1246,10 @@ msgctxt "TFMAIN.LAYERMOVE.HINT"
 msgid "Move layer"
 msgstr "Pārvietot slāni"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "TFMAIN.LAYERREMOVECURRENT.HINT"
 msgid "Remove layer"
@@ -2907,6 +2907,10 @@ msgstr ""
 msgid "Error"
 msgstr "Kļūda"
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr "Nevar atvērt failu \"%1\""
@@ -3299,6 +3303,10 @@ msgstr "pikseļi"
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr "Nesen lietotās mapes:"
@@ -3416,6 +3424,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Attēlu kopskaits: %1"
@@ -3424,6 +3436,10 @@ msgstr "Attēlu kopskaits: %1"
 msgid "Transfer selection to other layer?"
 msgstr "Pārvietot iezīmējumu uz citu slāni?"
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3444,6 +3460,14 @@ msgstr "Nevar saglabāt failu:"
 msgid "Unknown command : "
 msgstr "Nepazīstama komanda:"
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr "Jā"

+ 28 - 4
lazpaint/release/i18n/lazpaint.nl.po

@@ -1113,10 +1113,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1272,6 +1268,10 @@ msgctxt "tfmain.layermove.hint"
 msgid "Move layer"
 msgstr "Laag verplaatsen"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "tfmain.layerremovecurrent.hint"
 msgid "Remove layer"
@@ -2933,6 +2933,10 @@ msgstr ""
 msgid "Error"
 msgstr "Fout"
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr ""
@@ -3325,6 +3329,10 @@ msgstr "px"
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr "Recente directories:"
@@ -3442,6 +3450,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""
@@ -3450,6 +3462,10 @@ msgstr ""
 msgid "Transfer selection to other layer?"
 msgstr "Verplaats selectie naar andere laag?"
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3470,6 +3486,14 @@ msgstr "Kan bestand niet opslaan : "
 msgid "Unknown command : "
 msgstr "Onbekend commando : "
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr "Ja"

+ 28 - 4
lazpaint/release/i18n/lazpaint.po

@@ -1075,10 +1075,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1233,6 +1229,10 @@ msgctxt "TFMAIN.LAYERMOVE.HINT"
 msgid "Move layer"
 msgstr ""
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "TFMAIN.LAYERREMOVECURRENT.HINT"
 msgid "Remove layer"
@@ -2889,6 +2889,10 @@ msgstr ""
 msgid "Error"
 msgstr ""
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr ""
@@ -3280,6 +3284,10 @@ msgstr ""
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr ""
@@ -3397,6 +3405,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""
@@ -3405,6 +3417,10 @@ msgstr ""
 msgid "Transfer selection to other layer?"
 msgstr ""
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3425,6 +3441,14 @@ msgstr ""
 msgid "Unknown command : "
 msgstr ""
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr ""

+ 28 - 4
lazpaint/release/i18n/lazpaint.pt_BR.po

@@ -1099,10 +1099,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1257,6 +1253,10 @@ msgctxt "TFMAIN.LAYERMOVE.HINT"
 msgid "Move layer"
 msgstr "Mover camada"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "TFMAIN.LAYERREMOVECURRENT.HINT"
 msgid "Remove layer"
@@ -2915,6 +2915,10 @@ msgstr ""
 msgid "Error"
 msgstr ""
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr ""
@@ -3307,6 +3311,10 @@ msgstr ""
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr "Diretórios recentes:"
@@ -3424,6 +3432,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""
@@ -3432,6 +3444,10 @@ msgstr ""
 msgid "Transfer selection to other layer?"
 msgstr "Transferir seleção para outra camada?"
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3452,6 +3468,14 @@ msgstr "Não foi possível salvar o arquivo : "
 msgid "Unknown command : "
 msgstr "Comando desconhecido : "
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr ""

+ 28 - 4
lazpaint/release/i18n/lazpaint.ru.po

@@ -1093,10 +1093,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1251,6 +1247,10 @@ msgctxt "tfmain.layermove.hint"
 msgid "Move layer"
 msgstr "Переместить слой"
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "tfmain.layerremovecurrent.hint"
 msgid "Remove layer"
@@ -2909,6 +2909,10 @@ msgstr ""
 msgid "Error"
 msgstr ""
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr ""
@@ -3301,6 +3305,10 @@ msgstr ""
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr ""
@@ -3418,6 +3426,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""
@@ -3426,6 +3438,10 @@ msgstr ""
 msgid "Transfer selection to other layer?"
 msgstr "Перенести выделение на другой слой?"
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3446,6 +3462,14 @@ msgstr "Не удается сохранить файл:"
 msgid "Unknown command : "
 msgstr "Неизвестная команда:"
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr ""

+ 28 - 4
lazpaint/release/i18n/lazpaint.sv.po

@@ -1081,10 +1081,6 @@ msgstr ""
 msgid "48px"
 msgstr ""
 
-#: tfmain.itemiconsize64.caption
-msgid "64px"
-msgstr ""
-
 #: tfmain.itemiconsizeauto.caption
 msgid "auto"
 msgstr ""
@@ -1239,6 +1235,10 @@ msgctxt "tfmain.layermove.hint"
 msgid "Move layer"
 msgstr ""
 
+#: tfmain.layerrasterize.hint
+msgid "Rasterize layer"
+msgstr ""
+
 #: tfmain.layerremovecurrent.hint
 msgctxt "tfmain.layerremovecurrent.hint"
 msgid "Remove layer"
@@ -2897,6 +2897,10 @@ msgstr ""
 msgid "Error"
 msgstr ""
 
+#: uresourcestrings.rserrorloadingoriginal
+msgid "Error while loading original however layer can be rasterized."
+msgstr ""
+
 #: uresourcestrings.rserroronopeningfile
 msgid "Error on opening file \"%1\""
 msgstr ""
@@ -3288,6 +3292,10 @@ msgstr ""
 msgid "RAM disk"
 msgstr ""
 
+#: uresourcestrings.rsrasterlayer
+msgid "Raster layer"
+msgstr ""
+
 #: uresourcestrings.rsrecentdirectories
 msgid "Recent directories:"
 msgstr ""
@@ -3405,6 +3413,10 @@ msgstr ""
 msgid "Too many layers"
 msgstr ""
 
+#: uresourcestrings.rstoomanyshapesinlayer
+msgid "Too many shapes in layer"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""
@@ -3413,6 +3425,10 @@ msgstr ""
 msgid "Transfer selection to other layer?"
 msgstr ""
 
+#: uresourcestrings.rstransformedrasterlayer
+msgid "Transformed raster layer"
+msgstr ""
+
 #: uresourcestrings.rstransformselectioncontent
 msgid "Do you want to transform content of the selection?"
 msgstr ""
@@ -3433,6 +3449,14 @@ msgstr "Kunde inte spara fil :"
 msgid "Unknown command : "
 msgstr "Okänt kommando :"
 
+#: uresourcestrings.rsunknownoriginal
+msgid "Unknown original"
+msgstr ""
+
+#: uresourcestrings.rsvectoriallayer
+msgid "Vectorial layer"
+msgstr ""
+
 #: uresourcestrings.rsyes
 msgid "Yes"
 msgstr ""

+ 8 - 5
lazpaint/ucanvassize.pas

@@ -68,11 +68,14 @@ begin
     if not repeatImage and layeredBmp.LayerOriginalDefined[i] and layeredBmp.LayerOriginalKnown[i] then
     begin
       orig := layeredBmp.LayerOriginal[i];
-      if result.IndexOfOriginal(orig)=-1 then result.AddOriginal(orig,false);
-      result.LayerOriginalGuid[idx] := orig.Guid;
-      newOrigin := ChangeCanvasSizeOrigin(layeredBmp.Width,layeredBmp.Height,newwidth,newHeight,anchor);
-      result.LayerOriginalMatrix[idx] := AffineMatrixTranslation(newOrigin.X,newOrigin.Y)*layeredBmp.LayerOriginalMatrix[i];
-      result.RenderLayerFromOriginal(idx);
+      if Assigned(orig) then
+      begin
+        if result.IndexOfOriginal(orig)=-1 then result.AddOriginal(orig,false);
+        result.LayerOriginalGuid[idx] := orig.Guid;
+        newOrigin := ChangeCanvasSizeOrigin(layeredBmp.Width,layeredBmp.Height,newwidth,newHeight,anchor);
+        result.LayerOriginalMatrix[idx] := AffineMatrixTranslation(newOrigin.X,newOrigin.Y)*layeredBmp.LayerOriginalMatrix[i];
+        result.RenderLayerFromOriginal(idx);
+      end;
     end;
   end;
 end;

+ 5 - 5
lazpaint/uchoosecolor.pas

@@ -85,7 +85,7 @@ type
     property AvailableBmpWidth: integer read GetAvailableBmpWidth;
   end;
 
-var TFChooseColor_CustomDPI: integer = 0;
+var TFChooseColor_CustomDPI: integer = 96;
 
 implementation
 
@@ -98,10 +98,10 @@ begin
    {$IFDEF LINUX}
    BorderStyle:= bsDialog;
    {$ENDIF}
-   ClientHeight := DoScaleY(160,OriginalDPI,TFChooseColor_CustomDPI);
+   ClientHeight := DoScaleY(150,OriginalDPI,TFChooseColor_CustomDPI);
    ScaleControl(Self,OriginalDPI,TFChooseColor_CustomDPI);
-   EColor.Font.Height := -FontEmHeightSign*DoScaleY(12,OriginalDPI,TFChooseColor_CustomDPI);
-   LColor.Font.Height := -FontEmHeightSign*DoScaleY(12,OriginalDPI,TFChooseColor_CustomDPI);
+   EColor.Font.Height := -FontEmHeightSign*DoScaleY(14,OriginalDPI,TFChooseColor_CustomDPI);
+   LColor.Font.Height := -FontEmHeightSign*DoScaleY(14,OriginalDPI,TFChooseColor_CustomDPI);
    EColor.AdjustSize;
    EColor.Text:= '';
    EColor.Top := ClientHeight;
@@ -118,7 +118,7 @@ begin
    EColor.Visible := false;
 
    topmargin := DoScaleY(27,OriginalDPI,TFChooseColor_CustomDPI);
-   barwidth := DoScaleX(20,OriginalDPI,TFChooseColor_CustomDPI);
+   barwidth := DoScaleX(18,OriginalDPI,TFChooseColor_CustomDPI);
    cursorplace := DoScaleX(10,OriginalDPI,TFChooseColor_CustomDPI);
    margin := DoScaleY(8,OriginalDPI,TFChooseColor_CustomDPI);
    cursormargin := DoScaleX(2,OriginalDPI,TFChooseColor_CustomDPI);

+ 18 - 17
lazpaint/uconfig.pas

@@ -209,12 +209,12 @@ type
     function ToolPopupMessageShownCount(index: integer): integer;
     procedure SetToolPopupMessageShownCount(index: integer; AValue: integer);
 
-    function DefaultToolLightPositionX: integer;
-    procedure SetDefaultToolLightPositionX(value: integer);
-    function DefaultToolLightPositionY: integer;
-    procedure SetDefaultToolLightPositionY(value: integer);
-    procedure SetDefaultToolLightPosition(value: TPoint);
-    function DefaultToolLightPosition: TPoint;
+    function DefaultToolLightPositionX: single;
+    procedure SetDefaultToolLightPositionX(value: single);
+    function DefaultToolLightPositionY: single;
+    procedure SetDefaultToolLightPositionY(value: single);
+    procedure SetDefaultToolLightPosition(const value: TPointF);
+    function DefaultToolLightPosition: TPointF;
     function DefaultToolLightAltitude: integer;
     procedure SetDefaultToolLightAltitude(value: integer);
     function DefaultToolShapeAltitude: integer;
@@ -891,35 +891,35 @@ begin
   iniOptions.WriteInteger('Popup','ToolPopupMessage' + inttostr(index), avalue);
 end;
 
-function TLazPaintConfig.DefaultToolLightPositionX: integer;
+function TLazPaintConfig.DefaultToolLightPositionX: single;
 begin
-  result := iniOptions.ReadInteger('Tool','LightPositionX',0);
+  result := iniOptions.ReadFloat('Tool','LightPositionX',0);
 end;
 
-procedure TLazPaintConfig.SetDefaultToolLightPositionX(value: integer);
+procedure TLazPaintConfig.SetDefaultToolLightPositionX(value: single);
 begin
-  iniOptions.WriteInteger('Tool','LightPositionX',value);
+  iniOptions.WriteFloat('Tool','LightPositionX',value);
 end;
 
-function TLazPaintConfig.DefaultToolLightPositionY: integer;
+function TLazPaintConfig.DefaultToolLightPositionY: single;
 begin
-  result := iniOptions.ReadInteger('Tool','LightPositionY',0);
+  result := iniOptions.ReadFloat('Tool','LightPositionY',0);
 end;
 
-procedure TLazPaintConfig.SetDefaultToolLightPositionY(value: integer);
+procedure TLazPaintConfig.SetDefaultToolLightPositionY(value: single);
 begin
-  iniOptions.WriteInteger('Tool','LightPositionY',value);
+  iniOptions.WriteFloat('Tool','LightPositionY',value);
 end;
 
-procedure TLazPaintConfig.SetDefaultToolLightPosition(value: TPoint);
+procedure TLazPaintConfig.SetDefaultToolLightPosition(const value: TPointF);
 begin
   SetDefaultToolLightPositionX(value.X);
   SetDefaultToolLightPositionY(value.Y);
 end;
 
-function TLazPaintConfig.DefaultToolLightPosition: TPoint;
+function TLazPaintConfig.DefaultToolLightPosition: TPointF;
 begin
-  result := Point(DefaultToolLightPositionX,DefaultToolLightPositionY);
+  result := PointF(DefaultToolLightPositionX,DefaultToolLightPositionY);
 end;
 
 function TLazPaintConfig.DefaultToolLightAltitude: integer;
@@ -1467,6 +1467,7 @@ end;
 function TLazPaintConfig.DefaultIconSize(defaultValue: integer): integer;
 begin
   result := iniOptions.ReadInteger('General','DefaultIconSize',0);
+  if result > 48 then result := 48;
   if result = 0 then result := defaultValue;
 end;
 

+ 46 - 5
lazpaint/ufilesystem.pas

@@ -113,13 +113,41 @@ const LinuxFileSystems: array[0..20] of string =
   {HPFS} 'hpfs', {NWFS} 'ncp',
   'nfs', 'smb', 'ncpfs');
 
+function ReadBooleanFromFile(AFilename: string): boolean;
+var t: textfile;
+  s: string;
+begin
+  assignfile(t, AFilename);
+  reset(t);
+  readln(t,s);
+  closefile(t);
+  result := trim(s)='1';
+end;
+
+function UnespacePath(APath: string): string;
+var
+  i, charCode: Integer;
+begin
+  result := APath;
+  for i := length(result)-3 downto 1 do
+    if (result[i]='\') and (result[i+1] in['0','1']) and
+      (result[i+2] in ['0'..'9']) and (result[i+3] in ['0'..'9']) then
+    begin
+      charCode := (ord(result[i+3])-ord('0'))+
+                  (ord(result[i+2])-ord('0'))*8+
+                  (ord(result[i+1])-ord('0'))*64;
+      delete(result,i+1,3);
+      result[i] := chr(charCode);
+    end;
+end;
+
 function GetLinuxFileSystems(AMountsFile: string): TFileSystemArray;
 var mtab: TextFile;
   desc: string;
   parsedDesc: TStringList;
-  lFileSystem: string;
+  lFileSystem, removableInfo, lPath: string;
   i: integer;
-  found: boolean;
+  found, isRemovable: boolean;
 begin
   result := nil;
   parsedDesc := TStringList.Create;
@@ -135,6 +163,7 @@ begin
         if parsedDesc.Count >= 4 then
         begin
           lFileSystem:= parsedDesc[2];
+          lPath := parsedDesc[1];
           found := false;
           for i := low(LinuxFileSystems) to high(LinuxFileSystems) do
             if LinuxFileSystems[i] = lFileSystem then
@@ -142,13 +171,13 @@ begin
               found := true;
               break;
             end;
-          if found then
+          if found and not lPath.StartsWith('/boot/') then
           begin
             setlength(result, length(result)+1);
             with result[high(result)] do
             begin
               fileSystem := lFileSystem;
-              path := parsedDesc[1];
+              path := UnespacePath(parsedDesc[1]);
               device := parsedDesc[0];
               longFilenames := (fileSystem <> 'minix') and
                 (fileSystem <> 'msdos');
@@ -167,8 +196,20 @@ begin
                   device := rsFixedDrive;
               end
               else
-              if (copy(device,1,2) = 'fd') or (copy(device,1,2) = 'sd') then
+              if copy(device,1,2) = 'fd' then
                 device := rsRemovableDrive
+              else if copy(device,1,2) = 'sd' then
+              begin
+                removableInfo := '/sys/block/'+copy(device,1,3)+'/removable';
+                if FileExists(removableInfo) then
+                  isRemovable := ReadBooleanFromFile(removableInfo)
+                else
+                  isRemovable := false;
+                if isRemovable then
+                  device := rsRemovableDrive
+                else
+                  device := rsFixedDrive;
+              end
               else if copy(device,1,3) = 'scd' then
                 device := rsCdRom;
               if (fileSystem = 'nfs') or (fileSystem = 'smb') or (fileSystem = 'ncpfs') then device := rsNetworkDrive;

+ 5 - 16
lazpaint/ufilterconnector.pas

@@ -23,7 +23,6 @@ type
     FWorkArea: TRect;
     FWorkAreaFullySelected: boolean;
     FParameters: TVariableSet;
-    FLayerBackupImage: TBGRABitmap;
     function GetActionDone: boolean;
     function GetActiveLayeOffset: TPoint;
     function GetActiveLayer: TBGRABitmap;
@@ -60,7 +59,7 @@ type
 
 implementation
 
-uses Types, BGRABitmapTypes, BGRATransform;
+uses Types, BGRABitmapTypes;
 
 { TFilterConnector }
 
@@ -90,9 +89,7 @@ end;
 
 function TFilterConnector.GetBackupLayer: TBGRABitmap;
 begin
-  if Assigned(FLayerBackupImage) then
-    result := FLayerBackupImage
-  else if ApplyOnSelectionLayer then
+  if ApplyOnSelectionLayer then
     result := FAction.BackupSelectionLayer
   else
     result := FAction.BackupSelectedLayer;
@@ -122,16 +119,9 @@ begin
   ApplyOnSelectionLayer:= not FLazPaintInstance.Image.SelectionLayerIsEmpty;
 
   if AAction = nil then
-    AAction := ALazPaintInstance.Image.CreateAction(AApplyOfsBefore and not ApplyOnSelectionLayer);
-
-  if ApplyOnSelectionLayer and not IsAffineMatrixIdentity(AAction.SelectionTransform) then
-  begin
-    AAction.ApplySelectionTransform;
-    FLayerBackupImage := AAction.GetOrCreateSelectionLayer.Duplicate as TBGRABitmap;
-  end else
-  begin
-    FLayerBackupImage := nil;
-  end;
+    AAction := ALazPaintInstance.Image.CreateAction(
+                  AApplyOfsBefore and not ApplyOnSelectionLayer,
+                  ApplyOnSelectionLayer);
 
   FAction := AAction;
   FActionOwned:= AOwned;
@@ -191,7 +181,6 @@ end;
 destructor TFilterConnector.Destroy;
 begin
   DiscardAction;
-  FLayerBackupImage.Free;
   inherited Destroy;
 end;
 

+ 9 - 29
lazpaint/ugraph.pas

@@ -51,15 +51,14 @@ function ClearTypeFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
 function ClearTypeInverseFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
 
 function DoResample(source :TBGRABitmap; newWidth, newHeight: integer; StretchMode: TResampleMode): TBGRABitmap;
-procedure DrawArrow(ACanvas: TCanvas; ARect: TRect; AStart: boolean; AKind: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
-procedure ApplyArrowStyle(AStart: boolean; AKind: string; ABmp: TBGRABitmap; ASize: TPointF);
+procedure DrawArrow(ACanvas: TCanvas; ARect: TRect; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
 procedure BCAssignSystemStyle(AButton: TBCButton);
 
 implementation
 
 uses GraphType, math, Types, BGRAUTF8, FileUtil, dialogs, BGRAAnimatedGif,
   BGRAGradients, BGRATextFX, uresourcestrings, LCScaleDPI, BCTypes,
-  BGRAThumbnail;
+  BGRAThumbnail, LCVectorPolyShapes;
 
 procedure BCAssignSystemState(AState: TBCButtonState; AFontColor, ATopColor, AMiddleTopColor, AMiddleBottomColor, ABottomColor, ABorderColor: TColor);
 begin
@@ -733,11 +732,13 @@ begin
   result := source.Resample(newWidth,newHeight,StretchMode) as TBGRABitmap;
 end;
 
-procedure DrawArrow(ACanvas: TCanvas; ARect: TRect; AStart: boolean; AKind: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
+procedure DrawArrow(ACanvas: TCanvas; ARect: TRect; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
 var bmp : TBGRABitmap;
   c,c2: TBGRAPixel;
   x1,x2,xm1,xm2,y,w,temp: single;
+  kind: TArrowKind;
 begin
+  kind := StrToArrowKind(AKindStr);
   if odSelected in State then
   begin
     c2 := ColorToBGRA(ColorToRGB(clHighlight));
@@ -748,7 +749,7 @@ begin
     c := ColorToBGRA(ColorToRGB(clWindowText));
   end;
   with Size(ARect) do bmp:= TBGRABitmap.Create(cx,cy,c2);
-  ApplyArrowStyle(AStart,AKind,bmp,PointF(1.5,1.5));
+  ApplyArrowStyle(bmp.Arrow,AStart,kind,PointF(1.5,1.5));
   bmp.LineCap := ALineCap;
   w := bmp.Height/5;
   if w > 0 then
@@ -757,8 +758,8 @@ begin
     x2 := 0;
     xm1 := 0;
     xm2 := w*2.5;
-    if (AKind = 'Normal') or (AKind = 'Cut') then x1 -= w*0.7 else
-    if (AKind = 'Flipped') or (AKind = 'FlippedCut') then x1 += w*0.7;
+    if kind in[akNone,akCut] then x1 -= w*0.7 else
+    if kind in[akFlipped,akFlippedCut] then x1 += w*0.7;
     if not AStart then
     begin
       temp := x1;
@@ -772,7 +773,7 @@ begin
     x1 -= 0.5;
     x2 += bmp.Width-0.5;
     y := (bmp.Height-1)/2;
-    if (AKind='Tail') or (AKind='None') or (AKind = 'Tip') then w *= 2;
+    if kind in[akTail,akNone,akTip] then w *= 2;
     bmp.DrawLineAntialias(x1,y,x2,y,c,w);
     if bmp.Width > bmp.Height*2 then
       bmp.GradientFill(0,0,bmp.width,bmp.height,c2,BGRAPixelTransparent,gtLinear,PointF(xm1,0),PointF(xm2,0),dmDrawWithTransparency);
@@ -781,27 +782,6 @@ begin
   bmp.Free;
 end;
 
-procedure ApplyArrowStyle(AStart: boolean; AKind: string; ABmp: TBGRABitmap; ASize: TPointF);
-var backOfs: single;
-begin
-  backOfs := 0;
-  if (ASize.x = 0) or (ASize.y = 0) then AKind := 'None';
-  if (length(AKind)>0) and (AKind[length(AKind)] in['1'..'9']) then backOfs := (ord(AKind[length(AKind)])-ord('0'))*0.25;
-  case AKind of
-  'Tail': if AStart then ABmp.ArrowStartAsTail else ABmp.ArrowEndAsTail;
-  'Tip': if AStart then ABmp.ArrowStartAsTriangle else ABmp.ArrowEndAsTriangle;
-  'Normal','Cut','Flipped','FlippedCut': if AStart then ABmp.ArrowStartAsClassic((AKind='Flipped') or (AKind='FlippedCut'),(AKind='Cut') or (AKind='FlippedCut'))
-    else ABmp.ArrowEndAsClassic((AKind='Flipped') or (AKind='FlippedCut'),(AKind='Cut') or (AKind='FlippedCut'));
-  'Triangle','TriangleBack1','TriangleBack2': if AStart then ABmp.ArrowStartAsTriangle(backOfs) else ABmp.ArrowEndAsTriangle(backOfs);
-  'HollowTriangle','HollowTriangleBack1','HollowTriangleBack2': if AStart then ABmp.ArrowStartAsTriangle(backOfs,False,True) else ABmp.ArrowEndAsTriangle(backOfs,False,True);
-  else if AStart then ABmp.ArrowStartAsNone else ABmp.ArrowEndAsNone;
-  end;
-  if (AKind = 'Tip') and not ((ASize.x = 0) or (ASize.y = 0)) then
-    ASize := ASize*(0.5/ASize.y);
-  if AStart then ABmp.ArrowStartSize := ASize
-  else ABmp.ArrowEndSize := ASize;
-end;
-
 function CreateMarbleTexture(tx,ty: integer): TBGRABitmap;
 var
   colorOscillation: integer;

+ 1 - 1
lazpaint/ugridbitmap.pas

@@ -133,7 +133,7 @@ begin
     m[1,3] := 0;
     m[2,3] := 0;
     ofs := m*PointF(ACellRect.Left,ACellRect.Top);
-    Destination.PutImageAffine(AffineMatrixTranslation(ofs.x,ofs.y)*Matrix,ACellBitmap,Filter,Opacity);
+    Destination.PutImageAffine(AffineMatrixTranslation(ofs.x,ofs.y)*Matrix,ACellBitmap,Filter,Mode,Opacity);
   end;
 end;
 

+ 86 - 17
lazpaint/uimage.pas

@@ -138,7 +138,7 @@ type
     procedure CompressUndo;
     function UsedMemory: int64;
 
-    function CreateAction(AApplyOfsBefore: boolean=false): TLayerAction;
+    function CreateAction(AApplyOfsBefore: boolean=false; AApplySelTransformBefore: boolean=false): TLayerAction;
 
     // invalidating
     procedure ImageMayChange(ARect: TRect; ADiscardSelectionLayerAfterMask: boolean = true);
@@ -172,6 +172,7 @@ type
     procedure AddNewLayer(AOriginal: TBGRALayerCustomOriginal; AName: string; ABlendOp: TBlendOperation; AMatrix: TAffineMatrix);
     procedure AddNewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation);
     procedure DuplicateLayer;
+    procedure RasterizeLayer;
     procedure MergeLayerOver;
     procedure MoveLayer(AFromIndex,AToIndex: integer);
     procedure RemoveLayer;
@@ -1016,15 +1017,18 @@ begin
       result += (TObject(FUndoList[i]) as TStateDifference).UsedMemory;
 end;
 
-function TLazPaintImage.CreateAction(AApplyOfsBefore: boolean=false): TLayerAction;
+function TLazPaintImage.CreateAction(AApplyOfsBefore: boolean;
+                                     AApplySelTransformBefore: boolean): TLayerAction;
 begin
   if not CheckNoAction(True) then
     raise exception.Create(rsConflictingActions);
-  result := TLayerAction.Create(FCurrentState, AApplyOfsBefore);
+  result := TLayerAction.Create(FCurrentState, AApplyOfsBefore, AApplySelTransformBefore);
   result.OnNotifyChange:= @LayerActionNotifyChange;
   result.OnDestroy:=@LayerActionDestroy;
   result.OnNotifyUndo:=@LayerActionNotifyUndo;
   FActionInProgress := result;
+  if Assigned(result.Prediff) then
+    InvalidateImageDifference(result.Prediff);
 end;
 
 procedure TLazPaintImage.ImageMayChange(ARect: TRect;
@@ -1097,7 +1101,14 @@ end;
 
 procedure TLazPaintImage.SelectionMaskMayChangeCompletely;
 begin
-  SelectionMaskMayChange(rect(0,0,Width,Height));
+  DiscardSelectionLayerAfterMask;
+  FRenderUpdateRectInPicCoord := rect(0,0,Width,Height);
+  FCurrentState.DiscardSelectionMaskBounds;
+  if Assigned(FOnSelectionMaskChanged) then FOnSelectionMaskChanged(self, rect(0,0,Width,Height));
+  if FCurrentState.SelectionLayer <> nil then
+    LayerMayChange(FCurrentState.SelectionLayer, rect(0,0,Width,Height))
+  else
+    OnImageChanged.NotifyObservers;
 end;
 
 procedure TLazPaintImage.RenderMayChange(ARect: TRect; APicCoords: boolean = false);
@@ -1207,7 +1218,15 @@ end;
 
 function TLazPaintImage.GetLayerOriginal(AIndex: integer): TBGRALayerCustomOriginal;
 begin
-  result := FCurrentState.LayerOriginal[AIndex];
+  try
+    result := FCurrentState.LayerOriginal[AIndex];
+  except
+    on ex:exception do
+    begin
+      MessagePopup(rsErrorLoadingOriginal, 4000);
+      result := nil;
+    end;
+  end;
 end;
 
 function TLazPaintImage.GetLayerOriginalClass(AIndex: integer): TBGRALayerOriginalAny;
@@ -1445,10 +1464,15 @@ end;
 
 function TLazPaintImage.GetRenderedImage: TBGRABitmap;
 var
-  backupCurrentLayer : TBGRABitmap;
-  backupTopLeft, ofs: TPoint;
+  backupFullLayer: TBGRABitmap;
+  backupFullLayerTopLeft: TPoint;
+  backupFullLayerGuid: TGuid;
+  backupFullLayerRenderStatus: TOriginalRenderStatus;
+  backupFullLayerMatrix: TAffineMatrix;
+  backupLayerPart : TBGRABitmap;
+  backupLayerPartTopLeft, ofs: TPoint;
   shownSelectionLayer , temp: TBGRABitmap;
-  rectOutput, invalidatedRect: TRect;
+  rectOutput, invalidatedRect, layerRect: TRect;
   actualTransformation: TAffineMatrix;
 begin
   if (FRenderedImage = nil) or ((FRenderedImageInvalidated.Right > FRenderedImageInvalidated.Left) and
@@ -1462,14 +1486,35 @@ begin
     end;
     PrepareForRendering;
 
-    backupCurrentLayer := nil;
-    backupTopLeft := Point(0,0);
+    backupLayerPart := nil;
+    backupLayerPartTopLeft := Point(0,0);
+    backupFullLayer := nil;
+    backupFullLayerTopLeft := Point(0,0);
     //if there is an overlapping selection, then we must draw it on current layer
     if (SelectionMask <> nil) and (GetSelectedImageLayer <> nil) then
     begin
       shownSelectionLayer := FCurrentState.SelectionLayer;
       if shownSelectionLayer <> nil then
       begin
+         if LayerOriginalDefined[CurrentLayerIndex] and LayerOriginalKnown[CurrentLayerIndex] then
+         begin
+           layerRect := RectWithSize(LayerOffset[CurrentLayerIndex].X,LayerOffset[CurrentLayerIndex].Y,
+                                     LayerBitmap[CurrentLayerIndex].Width,LayerBitmap[CurrentLayerIndex].Height);
+           layerRect.Intersect(rect(0,0,Width,Height));
+           if (layerRect.Width <> Width) or (layerRect.Height <> Height) then
+           begin
+             backupFullLayer := CurrentState.LayeredBitmap.TakeLayerBitmap(CurrentLayerIndex);
+             backupFullLayerTopLeft := CurrentState.LayeredBitmap.LayerOffset[CurrentLayerIndex];
+             backupFullLayerGuid := CurrentState.LayeredBitmap.LayerOriginalGuid[CurrentLayerIndex];
+             backupFullLayerRenderStatus := CurrentState.LayeredBitmap.LayerOriginalRenderStatus[CurrentLayerIndex];
+             backupFullLayerMatrix := CurrentState.LayeredBitmap.LayerOriginalMatrix[CurrentLayerIndex];
+             temp := TBGRABitmap.Create(Width,Height);
+             temp.PutImage(backupFullLayerTopLeft.X,backupFullLayerTopLeft.Y, backupFullLayer, dmSet);
+             CurrentState.LayeredBitmap.SetLayerBitmap(CurrentLayerIndex, temp,true);
+             CurrentState.LayeredBitmap.LayerOffset[CurrentLayerIndex] := Point(0,0);
+             temp := nil;
+           end;
+         end;
          if not IsAffineMatrixIdentity(SelectionTransform) then
          begin
            NeedSelectionLayerAfterMask;
@@ -1487,8 +1532,11 @@ begin
              IntersectRect(rectOutput,rectOutput,invalidatedRect);
              if not IsRectEmpty(rectOutput) then
              begin
-               backupCurrentLayer := GetSelectedImageLayer.GetPart(rectOutput) as TBGRABitmap;
-               backupTopLeft := rectoutput.TopLeft;
+               if backupFullLayer=nil then
+               begin
+                 backupLayerPart := GetSelectedImageLayer.GetPart(rectOutput) as TBGRABitmap;
+                 backupLayerPartTopLeft := rectoutput.TopLeft;
+               end;
                GetSelectedImageLayer.PutImageAffine(
                  actualTransformation, shownSelectionLayer,
                  rectOutput,255,True);
@@ -1504,8 +1552,11 @@ begin
            OffsetRect(rectoutput, -ofs.X,-ofs.Y);
            if not IsRectEmpty(rectoutput) then
            begin
-             backupTopLeft := rectOutput.TopLeft;
-             backupCurrentLayer := GetSelectedImageLayer.GetPart(rectoutput) as TBGRABitmap;
+             if backupFullLayer=nil then
+             begin
+               backupLayerPartTopLeft := rectOutput.TopLeft;
+               backupLayerPart := GetSelectedImageLayer.GetPart(rectoutput) as TBGRABitmap;
+             end;
              shownSelectionLayer.ScanOffset := Point(ofs.x,ofs.y);
              GetSelectedImageLayer.ClipRect := rectOutput;
              GetSelectedImageLayer.FillMask(-ofs.X,-ofs.Y,SelectionMask, shownSelectionLayer, dmDrawWithTransparency);
@@ -1542,10 +1593,18 @@ begin
     end;
 
     //restore
-    if backupCurrentLayer <> nil then
+    if backupLayerPart <> nil then
+    begin
+      GetSelectedImageLayer.PutImage(backupLayerPartTopLeft.X,backupLayerPartTopLeft.Y,backupLayerPart,dmSet);
+      backupLayerPart.Free;
+    end;
+    if backupFullLayer <> nil then
     begin
-      GetSelectedImageLayer.PutImage(backupTopLeft.X,backupTopLeft.Y,backupCurrentLayer,dmSet);
-      backupCurrentLayer.Free;
+      CurrentState.LayeredBitmap.SetLayerBitmap(CurrentLayerIndex, backupFullLayer, true);
+      CurrentState.LayeredBitmap.LayerOffset[CurrentLayerIndex] := backupFullLayerTopLeft;
+      CurrentState.LayeredBitmap.LayerOriginalGuid[CurrentLayerIndex] := backupFullLayerGuid;
+      CurrentState.LayeredBitmap.LayerOriginalMatrix[CurrentLayerIndex] := backupFullLayerMatrix;
+      CurrentState.LayeredBitmap.LayerOriginalRenderStatus[CurrentLayerIndex] := backupFullLayerRenderStatus;
     end;
     FRenderedImageInvalidated := EmptyRect; //up to date
   end;
@@ -1744,6 +1803,16 @@ begin
   end;
 end;
 
+procedure TLazPaintImage.RasterizeLayer;
+begin
+  if LayerOriginalDefined[CurrentLayerIndex] then
+  try
+    AddUndo(FCurrentState.DiscardOriginal(True));
+    OnImageChanged.NotifyObservers;
+  except on ex: exception do NotifyException('RasterizeLayer',ex);
+  end;
+end;
+
 procedure TLazPaintImage.MergeLayerOver;
 begin
   if not CheckNoAction then exit;

+ 36 - 31
lazpaint/uimageaction.pas

@@ -55,6 +55,7 @@ type
     function NewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation): boolean; overload;
     function NewLayer(ALayer: TBGRALayerCustomOriginal; AName: string; ABlendOp: TBlendOperation; AMatrix: TAffineMatrix): boolean; overload;
     procedure DuplicateLayer;
+    procedure RasterizeLayer;
     procedure MergeLayerOver;
     procedure RemoveLayer;
     procedure EditSelection(ACallback: TModifyImageCallback);
@@ -133,6 +134,7 @@ begin
   Scripting.RegisterScriptFunction('LayerVerticalFlip',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('LayerAddNew',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('LayerDuplicate',@GenericScriptFunction,ARegister);
+  Scripting.RegisterScriptFunction('LayerRasterize',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('LayerMergeOver',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('LayerRemoveCurrent',@GenericScriptFunction,ARegister);
 end;
@@ -186,6 +188,7 @@ begin
   if f = 'LayerVerticalFlip' then VerticalFlip(foCurrentLayer) else
   if f = 'LayerAddNew' then NewLayer else
   if f = 'LayerDuplicate' then DuplicateLayer else
+  if f = 'LayerRasterize' then RasterizeLayer else
   if f = 'LayerMergeOver' then MergeLayerOver else
   if f = 'LayerRemoveCurrent' then RemoveLayer else
     result := srFunctionNotDefined;
@@ -650,10 +653,8 @@ var LayerAction: TLayerAction;
 begin
   LayerAction := nil;
   try
-    if not (CurrentTool in[ptSelectRect,ptSelectEllipse]) then ChooseTool(ptSelectRect);
-    LayerAction := Image.CreateAction(false);
+    LayerAction := Image.CreateAction(false,true);
     LayerAction.QuerySelection;
-    LayerAction.ApplySelectionTransform;
     p := LayerAction.CurrentSelection.Data;
     for n := LayerAction.CurrentSelection.NbPixels-1 downto 0 do
     begin
@@ -669,22 +670,21 @@ begin
       FInstance.ShowError('InvertSelection',ex.Message);
   end;
   LayerAction.Free;
+  if Image.SelectionMaskEmpty then ChooseTool(ptHand) else
+  if not (CurrentTool in[ptSelectRect,ptSelectEllipse]) then ChooseTool(ptSelectRect);
 end;
 
 procedure TImageActions.Deselect;
-var LayerAction: TLayerAction;
 begin
   if (CurrentTool in[ptRotateSelection,ptMoveSelection]) then
     ChooseTool(ptHand);
   if not Image.CheckNoAction then exit;
-  LayerAction := nil;
   try
     if not image.SelectionMaskEmpty then ReleaseSelection;
   except
     on ex:Exception do
       FInstance.ShowError('Deselect',ex.Message);
   end;
-  LayerAction.Free;
 end;
 
 procedure TImageActions.CopySelection;
@@ -697,10 +697,9 @@ begin
     if not image.CheckNoAction then exit;
     bounds := Image.SelectionMaskBounds;
     if IsRectEmpty(bounds) then exit;
-    LayerAction := Image.CreateAction;
+    LayerAction := Image.CreateAction(true,true);
     LayerAction.ApplySelectionMask;
-    if Image.SelectionLayerIsEmpty then
-      LayerAction.RetrieveSelection;
+    if Image.SelectionLayerIsEmpty then LayerAction.RetrieveSelection;
     layer := LayerAction.GetOrCreateSelectionLayer;
     r := layer.GetImageBounds; //bounds may have been changed
     if (r.right > r.left) and (r.bottom > r.top) then
@@ -727,20 +726,19 @@ begin
   LayerAction := nil;
   try
     CopySelection;
-    LayerAction := Image.CreateAction;
-    LayerAction.ApplySelectionTransform;
+    LayerAction := Image.CreateAction(false,true);
     if (LayerAction.GetSelectionLayerIfExists = nil) or (LayerAction.GetSelectionLayerIfExists.Empty) then
       LayerAction.EraseSelectionInBitmap;
     LayerAction.RemoveSelection;
     LayerAction.Validate;
-    if (CurrentTool = ptRotateSelection) or
-       (CurrentTool = ptMoveSelection) then
-      ChooseTool(ptHand);
   except
     on ex:Exception do
       FInstance.ShowError('CutSelection',ex.Message);
   end;
   LayerAction.Free;
+  if (CurrentTool = ptRotateSelection) or
+     (CurrentTool = ptMoveSelection) then
+    ChooseTool(ptHand);
 end;
 
 procedure TImageActions.RetrieveSelection;
@@ -751,7 +749,7 @@ begin
   if not image.CheckNoAction then exit;
   LayerAction := nil;
   try
-    LayerAction := Image.CreateAction(false);
+    LayerAction := Image.CreateAction(false, true);
     if LayerAction.RetrieveSelectionIfLayerEmpty(True) then
     begin
       r := Image.SelectionMaskBounds;
@@ -768,27 +766,25 @@ end;
 
 procedure TImageActions.DeleteSelection;
 var LayerAction: TLayerAction;
+  doErase: Boolean;
 begin
   if image.SelectionMaskEmpty then exit;
   if not image.CheckNoAction then exit;
   LayerAction := nil;
   try
-    LayerAction := Image.CreateAction;
-    if Image.SelectionLayerIsEmpty then
-    begin
-      LayerAction.ApplySelectionTransform;
-      LayerAction.EraseSelectionInBitmap;
-    end;
+    doErase := Image.SelectionLayerIsEmpty;
+    LayerAction := Image.CreateAction(false, doErase);
+    if doErase then LayerAction.EraseSelectionInBitmap;
     LayerAction.RemoveSelection;
     LayerAction.Validate;
-    if (CurrentTool = ptRotateSelection) or
-       (CurrentTool = ptMoveSelection) then
-      ChooseTool(ptHand);
   except
     on ex:Exception do
       FInstance.ShowError('DeleteSelection',ex.Message);
   end;
   LayerAction.Free;
+  if (CurrentTool = ptRotateSelection) or
+     (CurrentTool = ptMoveSelection) then
+    ChooseTool(ptHand);
 end;
 
 procedure TImageActions.RemoveSelection;
@@ -801,19 +797,20 @@ begin
     LayerAction := Image.CreateAction;
     LayerAction.RemoveSelection;
     LayerAction.Validate;
-    if (CurrentTool = ptRotateSelection) or
-       (CurrentTool = ptMoveSelection) then
-      ChooseTool(ptHand);
   except on ex:exception do FInstance.ShowError('RemoveSelection',ex.Message);
   end;
   LayerAction.Free;
+  if (CurrentTool = ptRotateSelection) or
+     (CurrentTool = ptMoveSelection) then
+    ChooseTool(ptHand);
 end;
 
 procedure TImageActions.ReleaseSelection;
 var
   layeraction: TLayerAction;
 begin
-  layeraction := image.CreateAction;
+  if image.SelectionMaskEmpty then exit;
+  layeraction := image.CreateAction(true, true);
   layeraction.ReleaseSelection;
   layeraction.Validate;
   layeraction.Free;
@@ -831,7 +828,7 @@ begin
       if partial.NbPixels <> 0 then
       begin
         ToolManager.ToolCloseDontReopen;
-        layeraction := Image.CreateAction(true);
+        layeraction := Image.CreateAction(true, true);
         layeraction.ReleaseSelection;
         layeraction.QuerySelection;
         pastePos := Point((image.Width - partial.Width) div 2 - image.ImageOffset.X,
@@ -881,13 +878,13 @@ procedure TImageActions.SelectAll;
 var LayerAction : TLayerAction;
 begin
   try
-    if not ToolManager.IsSelectingTool then ChooseTool(ptSelectRect);
     LayerAction := Image.CreateAction;
     LayerAction.QuerySelection;
     LayerAction.currentSelection.Fill(BGRAWhite);
     Image.SelectionMaskMayChangeCompletely;
     LayerAction.Validate;
     LayerAction.Free;
+    if not ToolManager.IsSelectingTool then ChooseTool(ptSelectRect);
   except
     on ex:Exception do
       FInstance.ShowError('SelectAll',ex.Message);
@@ -900,7 +897,7 @@ var LayerAction: TLayerAction;
 begin
   if not image.CheckNoAction then exit;
   try
-    LayerAction := Image.CreateAction;
+    LayerAction := Image.CreateAction(false,true);
     LayerAction.ChangeBoundsNotified := true;
 
     if image.SelectionMaskEmpty then
@@ -1002,6 +999,14 @@ begin
   end;
 end;
 
+procedure TImageActions.RasterizeLayer;
+begin
+  if CurrentTool in[ptMoveLayer,ptRotateLayer,ptZoomLayer,ptLayerMapping] then
+    ChooseTool(ptHand);
+  Image.RasterizeLayer;
+  FInstance.ScrollLayerStackOnItem(Image.CurrentLayerIndex);
+end;
+
 procedure TImageActions.MergeLayerOver;
 begin
   if (Image.CurrentLayerIndex <> -1) and (image.NbLayers > 1) then

+ 452 - 164
lazpaint/uimagediff.pas

@@ -6,7 +6,7 @@ interface
 
 uses
   Classes, SysUtils, UStateType, BGRABitmap, BGRABitmapTypes, BGRALayers,
-  BGRALayerOriginal;
+  BGRALayerOriginal, LCVectorOriginal;
 
 function IsInverseImageDiff(ADiff1, ADiff2: TCustomImageDifference): boolean;
 function TryCombineImageDiff(ANewDiff, APrevDiff: TCustomImageDifference): boolean;
@@ -57,18 +57,12 @@ type
     function GetChangingBounds: TRect; override;
     procedure Init(AToState: TState; APreviousImage: TBGRABitmap; APreviousImageChangeRect: TRect;
         APreviousSelection: TBGRABitmap; APreviousSelectionChangeRect: TRect;
-        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect;
-        APreviousSelectionTransform: TAffineMatrix; APreviousLayerOriginalData: TStream;
-        APreviousLayerOriginalMatrix: TAffineMatrix);
+        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect);
   public
     layerId: integer;
     imageOfs: TPoint;
     imageDiff, selectionLayerDiff: TImageDiff;
     selectionMaskDiff: TGrayscaleImageDiff;
-    prevSelectionTransform, nextSelectionTransform: TAffineMatrix;
-    layerOriginalChange: boolean;
-    prevLayerOriginalData, nextLayerOriginalData: TStream;
-    prevLayerOriginalMatrix, nextLayerOriginalMatrix: TAffineMatrix;
     function TryCompress: boolean; override;
     procedure ApplyTo(AState: TState); override;
     procedure UnapplyTo(AState: TState); override;
@@ -76,16 +70,10 @@ type
     constructor Create(AFromState, AToState: TState);
     constructor Create(AToState: TState; APreviousImage: TBGRABitmap; APreviousImageDefined: boolean;
         APreviousSelection: TBGRABitmap; APreviousSelectionDefined: boolean;
-        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerDefined: boolean;
-        APreviousSelectionTransform: TAffineMatrix;
-        APreviousLayerOriginalData: TStream;
-        APreviousLayerOriginalMatrix: TAffineMatrix); overload;
+        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerDefined: boolean); overload;
     constructor Create(AToState: TState; APreviousImage: TBGRABitmap; APreviousImageChangeRect: TRect;
         APreviousSelection: TBGRABitmap; APreviousSelectionChangeRect: TRect;
-        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect;
-        APreviousSelectionTransform: TAffineMatrix;
-        APreviousLayerOriginalData: TStream;
-        APreviousLayerOriginalMatrix: TAffineMatrix); overload;
+        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect); overload;
     function ToString: ansistring; override;
     destructor Destroy; override;
     property ChangeImageLayer: boolean read GetChangeImageLayer;
@@ -191,6 +179,22 @@ type
     procedure UnapplyTo(AState: TState); override;
   end;
 
+  { TSelectionTransformDifference }
+
+  TSelectionTransformDifference = class(TCustomImageDifference)
+    FPrevTransform: TAffineMatrix;
+    FPrevSelectionMask, FPrevSelectionLayer: TStoredImage;
+  protected
+    function GetImageDifferenceKind: TImageDifferenceKind; override;
+    function GetIsIdentity: boolean; override;
+  public
+    constructor Create(ADestination: TState; AApplyNow: boolean);
+    function TryCompress: boolean; override;
+    destructor Destroy; override;
+    procedure ApplyTo(AState: TState); override;
+    procedure UnapplyTo(AState: TState); override;
+  end;
+
   { TSetLayerVisibleStateDifference }
 
   TSetLayerVisibleStateDifference = class(TCustomImageDifference)
@@ -279,9 +283,9 @@ type
     destructor Destroy; override;
   end;
 
-  { TReplaceLayerByImageOriginalDifference }
+  { TReplaceLayerByOriginalDifference }
 
-  TReplaceLayerByImageOriginalDifference = class(TCustomImageDifference)
+  TReplaceLayerByOriginalDifference = class(TCustomImageDifference)
   private
     function GetLayerId: integer;
   protected
@@ -289,8 +293,9 @@ type
     FPrevMatrix,FNextMatrix: TAffineMatrix;
     FSourceBounds: TRect;
     function GetImageDifferenceKind: TImageDifferenceKind; override;
+    function CreateOriginal(AState: TState; ALayerIndex: integer): TBGRALayerCustomOriginal; virtual; abstract;
   public
-    constructor Create(AFromState: TState; AIndex: integer);
+    constructor Create(AFromState: TState; AIndex: integer; AAlwaysStoreBitmap: boolean);
     function UsedMemory: int64; override;
     function TryCompress: boolean; override;
     procedure ApplyTo(AState: TState); override;
@@ -301,6 +306,62 @@ type
     destructor Destroy; override;
   end;
 
+  { TDiscardOriginalDifference }
+
+  TDiscardOriginalDifference = class(TCustomImageDifference)
+  private
+    function GetLayerId: integer;
+  protected
+    FPreviousOriginalData: TStream;
+    FPreviousOriginalGuid: TGuid;
+    FOriginalUsedInOtherLayer: boolean;
+    FPreviousOriginalMatrix: TAffineMatrix;
+    FPreviousOriginalRenderStatus: TOriginalRenderStatus;
+    FLayerId: integer;
+    function GetImageDifferenceKind: TImageDifferenceKind; override;
+  public
+    constructor Create(AFromState: TState; AIndex: integer; AApplyNow: boolean);
+    function UsedMemory: int64; override;
+    function TryCompress: boolean; override;
+    procedure ApplyTo(AState: TState); override;
+    procedure UnapplyTo(AState: TState); override;
+    property LayerId: integer read GetLayerId;
+    destructor Destroy; override;
+  end;
+
+  { TReplaceLayerByImageOriginalDifference }
+
+  TReplaceLayerByImageOriginalDifference = class(TReplaceLayerByOriginalDifference)
+  protected
+    function CreateOriginal(AState: TState; ALayerIndex: integer): TBGRALayerCustomOriginal; override;
+  end;
+
+  { TReplaceLayerByVectorOriginalDifference }
+
+  TReplaceLayerByVectorOriginalDifference = class(TReplaceLayerByOriginalDifference)
+  protected
+    function CreateOriginal(AState: TState; ALayerIndex: integer): TBGRALayerCustomOriginal; override;
+  end;
+
+  { TAddShapeToVectorOriginalDifference }
+
+  TAddShapeToVectorOriginalDifference = class(TCustomImageDifference)
+  private
+    FShapeIndex, FShapeId: integer;
+    FLayerId: integer;
+    FShapeData: TBGRAMemOriginalStorage;
+    FShapeBounds: TRect;
+  protected
+    function GetImageDifferenceKind: TImageDifferenceKind; override;
+    function GetChangingBounds: TRect; override;
+    function GetChangingBoundsDefined: boolean; override;
+  public
+    constructor Create(ADestination: TState; ALayerId: integer; AShape: TVectorShape; AShapeIndex: integer = -1);
+    destructor Destroy; override;
+    procedure ApplyTo(AState: TState); override;
+    procedure UnapplyTo(AState: TState); override;
+  end;
+
   { TDiscardOriginalStateDifference }
 
   TDiscardOriginalStateDifference = class(TCustomImageDifference)
@@ -391,7 +452,7 @@ type
 implementation
 
 uses BGRAWriteLzp, BGRAReadLzp, UImageState, BGRAStreamLayers, BGRALzpCommon, ugraph, Types,
-  BGRATransform, zstream;
+  BGRATransform, zstream, LCVectorRectShapes, BGRAPen, math;
 
 function IsInverseImageDiff(ADiff1, ADiff2: TCustomImageDifference): boolean;
 begin
@@ -481,11 +542,11 @@ begin
     else result := false;
   end
   else
-  if (APrevDiff is TReplaceLayerByImageOriginalDifference) and (ANewDiff is TSetLayerMatrixDifference) then
+  if (APrevDiff is TReplaceLayerByOriginalDifference) and (ANewDiff is TSetLayerMatrixDifference) then
   begin
-    if (APrevDiff as TReplaceLayerByImageOriginalDifference).nextMatrix = (ANewDiff as TSetLayerMatrixDifference).previousMatrix then
+    if (APrevDiff as TReplaceLayerByOriginalDifference).nextMatrix = (ANewDiff as TSetLayerMatrixDifference).previousMatrix then
     begin
-      (APrevDiff as TReplaceLayerByImageOriginalDifference).nextMatrix := (ANewDiff as TSetLayerMatrixDifference).nextMatrix;
+      (APrevDiff as TReplaceLayerByOriginalDifference).nextMatrix := (ANewDiff as TSetLayerMatrixDifference).nextMatrix;
       result := true;
     end
     else result := false;
@@ -514,61 +575,388 @@ begin
     result := false;
 end;
 
+{ TDiscardOriginalDifference }
+
+function TDiscardOriginalDifference.GetLayerId: integer;
+begin
+  result := FLayerId;
+end;
+
+function TDiscardOriginalDifference.GetImageDifferenceKind: TImageDifferenceKind;
+begin
+  Result:= idkChangeStack;
+end;
+
+constructor TDiscardOriginalDifference.Create(AFromState: TState;
+  AIndex: integer; AApplyNow: boolean);
+var
+  imgState: TImageState;
+  i: Integer;
+begin
+  imgState := AFromState as TImageState;
+  FLayerId := imgState.LayerId[AIndex];
+  if not imgState.LayerOriginalDefined[AIndex] then
+    raise exception.Create('Layer original is not defined');
+  FPreviousOriginalGuid := imgState.LayeredBitmap.LayerOriginalGuid[AIndex];
+  FOriginalUsedInOtherLayer := false;
+  for i := 0 to imgState.NbLayers-1 do
+    if (i <> AIndex) and (imgState.LayeredBitmap.LayerOriginalGuid[i] = FPreviousOriginalGuid) then
+    begin
+      FOriginalUsedInOtherLayer:= true;
+      break;
+    end;
+  if not FOriginalUsedInOtherLayer then
+  begin
+    FPreviousOriginalData := TMemoryStream.Create;
+    imgState.LayeredBitmap.SaveOriginalToStream(
+      imgState.LayeredBitmap.LayerOriginalGuid[AIndex],
+      FPreviousOriginalData);
+  end;
+  FPreviousOriginalMatrix := imgState.LayerOriginalMatrix[AIndex];
+  FPreviousOriginalRenderStatus:= imgState.layeredBitmap.LayerOriginalRenderStatus[AIndex];
+  if AApplyNow then ApplyTo(AFromState)
+end;
+
+function TDiscardOriginalDifference.UsedMemory: int64;
+begin
+  if Assigned(FPreviousOriginalData) then
+    Result:= FPreviousOriginalData.Size
+  else
+    result:= 0;
+end;
+
+function TDiscardOriginalDifference.TryCompress: boolean;
+begin
+  Result:= false;
+end;
+
+procedure TDiscardOriginalDifference.ApplyTo(AState: TState);
+var
+  imgState: TImageState;
+  layerIdx: Integer;
+begin
+  imgState := AState as TImageState;
+  layerIdx := imgState.LayeredBitmap.GetLayerIndexFromId(FLayerId);
+  imgState.LayeredBitmap.LayerOriginalGuid[layerIdx] := GUID_NULL;
+  imgState.LayeredBitmap.LayerOriginalMatrix[layerIdx] := AffineMatrixIdentity;
+  if not FOriginalUsedInOtherLayer then
+    imgState.LayeredBitmap.RemoveUnusedOriginals;
+  inherited ApplyTo(AState);
+end;
+
+procedure TDiscardOriginalDifference.UnapplyTo(AState: TState);
+var
+  imgState: TImageState;
+  layerIdx, origIdx: Integer;
+begin
+  imgState := AState as TImageState;
+  layerIdx := imgState.LayeredBitmap.GetLayerIndexFromId(FLayerId);
+  if FOriginalUsedInOtherLayer then
+  begin
+    imgState.LayeredBitmap.LayerOriginalGuid[layerIdx] := FPreviousOriginalGuid;
+  end else
+  begin
+    FPreviousOriginalData.Position := 0;
+    origIdx := imgState.LayeredBitmap.AddOriginalFromStream(FPreviousOriginalData, FPreviousOriginalGuid, true);
+    imgState.LayeredBitmap.LayerOriginalGuid[layerIdx] := imgState.LayeredBitmap.OriginalGuid[origIdx];
+  end;
+  imgState.LayeredBitmap.LayerOriginalMatrix[layerIdx] := FPreviousOriginalMatrix;
+  imgState.LayeredBitmap.LayerOriginalRenderStatus[layerIdx] := FPreviousOriginalRenderStatus;
+  inherited UnapplyTo(AState);
+end;
+
+destructor TDiscardOriginalDifference.Destroy;
+begin
+  FPreviousOriginalData.Free;
+  inherited Destroy;
+end;
+
+{ TAddShapeToVectorOriginalDifference }
+
+function TAddShapeToVectorOriginalDifference.GetImageDifferenceKind: TImageDifferenceKind;
+begin
+  Result:= idkChangeImage;
+end;
+
+function TAddShapeToVectorOriginalDifference.GetChangingBounds: TRect;
+begin
+  Result:= FShapeBounds;
+end;
+
+function TAddShapeToVectorOriginalDifference.GetChangingBoundsDefined: boolean;
+begin
+  Result:= true;
+end;
+
+constructor TAddShapeToVectorOriginalDifference.Create(ADestination: TState;
+  ALayerId: integer; AShape: TVectorShape; AShapeIndex: integer);
+var
+  imgState: TImageState;
+  layerIdx, idxShapeAdd: Integer;
+  orig: TBGRALayerCustomOriginal;
+begin
+  FLayerId := ALayerId;
+  imgState := ADestination as TImageState;
+  layerIdx := imgState.LayeredBitmap.GetLayerIndexFromId(FLayerId);
+  orig := imgState.LayerOriginal[layerIdx];
+  if not (orig is TVectorOriginal) then
+  begin
+    AShape.Free;
+    raise exception.Create('Vector original expected');
+  end;
+  if AShapeIndex = -1 then AShapeIndex := TVectorOriginal(orig).ShapeCount;
+  FShapeIndex:= AShapeIndex;
+  FShapeData := TBGRAMemOriginalStorage.Create;
+  AShape.SaveToStorage(FShapeData);
+  FShapeData.RawString['class'] := AShape.StorageClassName;
+
+  inherited ApplyTo(ADestination);
+  idxShapeAdd := TVectorOriginal(orig).AddShape(AShape);
+  FShapeId := TVectorOriginal(orig).Shape[idxShapeAdd].Id;
+  TVectorOriginal(orig).MoveShapeToIndex(idxShapeAdd, FShapeIndex);
+  FShapeBounds := imgState.LayeredBitmap.RenderOriginalIfNecessary(orig.Guid);
+end;
+
+destructor TAddShapeToVectorOriginalDifference.Destroy;
+begin
+  FShapeData.Free;
+  inherited Destroy;
+end;
+
+procedure TAddShapeToVectorOriginalDifference.ApplyTo(AState: TState);
+var
+  imgState: TImageState;
+  layerIdx, idxShapeAdd: Integer;
+  orig: TBGRALayerCustomOriginal;
+  shape: TVectorShape;
+begin
+  inherited ApplyTo(AState);
+  imgState := AState as TImageState;
+  layerIdx := imgState.LayeredBitmap.GetLayerIndexFromId(FLayerId);
+  orig := imgState.LayerOriginal[layerIdx];
+  if not (orig is TVectorOriginal) then
+    raise exception.Create('Vector original expected');
+
+  shape := TVectorShape.CreateFromStorage(FShapeData,nil);
+  idxShapeAdd := TVectorOriginal(orig).AddShape(shape);
+  TVectorOriginal(orig).Shape[idxShapeAdd].Id := FShapeId;
+  TVectorOriginal(orig).MoveShapeToIndex(idxShapeAdd, FShapeIndex);
+  imgState.LayeredBitmap.RenderLayerFromOriginal(layerIdx);
+end;
+
+procedure TAddShapeToVectorOriginalDifference.UnapplyTo(AState: TState);
+var
+  imgState: TImageState;
+  layerIdx: Integer;
+  orig: TBGRALayerCustomOriginal;
+begin
+  inherited UnapplyTo(AState);
+  imgState := AState as TImageState;
+  layerIdx := imgState.LayeredBitmap.GetLayerIndexFromId(FLayerId);
+  orig := imgState.LayerOriginal[layerIdx];
+  if not (orig is TVectorOriginal) then
+    raise exception.Create('Vector original expected');
+  TVectorOriginal(orig).RemoveShape(TVectorOriginal(orig).Shape[FShapeIndex]);
+end;
+
+{ TReplaceLayerByVectorOriginalDifference }
+
+function TReplaceLayerByVectorOriginalDifference.CreateOriginal(AState: TState;
+  ALayerIndex: integer): TBGRALayerCustomOriginal;
+var
+  source: TBGRABitmap;
+  temp: TBGRABitmap;
+  imgState: TImageState;
+  orig: TVectorOriginal;
+  shape: TRectShape;
+begin
+  imgState := TImageState(AState);
+  orig := TVectorOriginal.Create;
+  source := imgState.LayeredBitmap.LayerBitmap[ALayerIndex];
+  if not source.Empty then
+  begin
+    shape := TRectShape.Create(orig);
+    shape.QuickDefine(PointF(0,0),PointF(FSourceBounds.Width,FSourceBounds.Height));
+    shape.PenStyle := ClearPenStyle;
+    temp := source.GetPart(FSourceBounds) as TBGRABitmap;
+    shape.BackFill.SetTexture(temp,AffineMatrixIdentity);
+    temp.FreeReference;
+    orig.AddShape(shape);
+  end;
+  result := orig;
+end;
+
 { TReplaceLayerByImageOriginalDifference }
 
-function TReplaceLayerByImageOriginalDifference.GetLayerId: integer;
+function TReplaceLayerByImageOriginalDifference.CreateOriginal(AState: TState; ALayerIndex: integer): TBGRALayerCustomOriginal;
+var
+  source: TBGRABitmap;
+  temp: TBGRACustomBitmap;
+  imgState: TImageState;
+  orig: TBGRALayerImageOriginal;
+begin
+  imgState := TImageState(AState);
+  orig := TBGRALayerImageOriginal.Create;
+  source := imgState.LayeredBitmap.LayerBitmap[ALayerIndex];
+  if (FSourceBounds.Width <> source.Width) or (FSourceBounds.Height <> source.Height) then
+  begin
+    temp := source.GetPart(FSourceBounds);
+    orig.AssignImage(temp);
+    temp.Free;
+  end else
+    orig.AssignImage(source);
+  result := orig;
+end;
+
+{ TSelectionTransformDifference }
+
+function TSelectionTransformDifference.GetImageDifferenceKind: TImageDifferenceKind;
+begin
+  if Assigned(FPrevSelectionMask) then
+  begin
+    if Assigned(FPrevSelectionLayer) then
+      Result:= idkChangeImageAndSelection
+    else
+      Result:= idkChangeSelection;
+  end else
+    if Assigned(FPrevSelectionLayer) then
+      Result:= idkChangeImage;
+end;
+
+function TSelectionTransformDifference.GetIsIdentity: boolean;
+begin
+  Result:= IsAffineMatrixIdentity(FPrevTransform);
+end;
+
+function TSelectionTransformDifference.TryCompress: boolean;
+begin
+  Result:= (Assigned(FPrevSelectionMask) and FPrevSelectionMask.Compress) or
+           (Assigned(FPrevSelectionLayer) and FPrevSelectionLayer.Compress);
+end;
+
+constructor TSelectionTransformDifference.Create(ADestination: TState;
+  AApplyNow: boolean);
+var
+  ImgState: TImageState;
+begin
+  inherited Create(ADestination);
+  ImgState := ADestination as TImageState;
+  FPrevTransform := ImgState.SelectionTransform;
+  if not ImgState.SelectionMaskEmpty then
+    FPrevSelectionMask := TStoredImage.Create(ImgState.SelectionMask)
+  else
+    FPrevSelectionMask := nil;
+  if not ImgState.SelectionLayerEmpty then
+    FPrevSelectionLayer := TStoredImage.Create(ImgState.SelectionLayer)
+  else
+    FPrevSelectionLayer := nil;
+  if AApplyNow then ApplyTo(ADestination);
+end;
+
+destructor TSelectionTransformDifference.Destroy;
+begin
+  FPrevSelectionMask.Free;
+  FPrevSelectionLayer.Free;
+  inherited Destroy;
+end;
+
+procedure TSelectionTransformDifference.ApplyTo(AState: TState);
+var
+  ImgState: TImageState;
+  newBmp: TBGRABitmap;
+  newLeft, newTop: integer;
+  r: TRect;
+begin
+  inherited ApplyTo(AState);
+  if not IsIdentity then
+  begin
+    ImgState := AState as TImageState;
+    if not ImgState.SelectionMaskEmpty then
+    begin
+      ImgState.ComputeTransformedSelectionMask(newBmp,newLeft,newTop);
+      r := ImgState.GetSelectionMaskBounds;
+      ImgState.SelectionMask.FillRect(r, BGRABlack, dmSet);
+      ImgState.SelectionMask.PutImage(newLeft,newTop,newBmp,dmSet);
+      newBmp.Free;
+    end;
+    if not ImgState.SelectionLayerEmpty then
+    begin
+      ImgState.ComputeTransformedSelectionLayer(newBmp,newLeft,newTop);
+      r := ImgState.GetSelectionLayerBounds;
+      ImgState.SelectionLayer.FillRect(r, BGRAPixelTransparent, dmSet);
+      ImgState.SelectionLayer.PutImage(newLeft,newTop,newBmp,dmSet);
+      newBmp.Free;
+    end;
+    ImgState.SelectionTransform := AffineMatrixIdentity;
+    ImgState.DiscardSelectionMaskBounds;
+    ImgState.DiscardSelectionLayerBounds;
+  end;
+end;
+
+procedure TSelectionTransformDifference.UnapplyTo(AState: TState);
+var
+  ImgState: TImageState;
+  prevMask, prevSelectionLayer: TBGRABitmap;
+begin
+  if not IsIdentity then
+  begin
+    ImgState := AState as TImageState;
+    if Assigned(FPrevSelectionMask) then prevMask := FPrevSelectionMask.GetBitmap else prevMask := nil;
+    if Assigned(FPrevSelectionLayer) then prevSelectionLayer := FPrevSelectionLayer.GetBitmap else prevSelectionLayer := nil;
+    ImgState.ReplaceSelection(prevMask, prevSelectionLayer);
+    ImgState.DiscardSelectionMaskBounds;
+    ImgState.DiscardSelectionLayerBounds;
+    ImgState.SelectionTransform := FPrevTransform;
+  end;
+  inherited UnapplyTo(AState);
+end;
+
+{ TReplaceLayerByOriginalDifference }
+
+function TReplaceLayerByOriginalDifference.GetLayerId: integer;
 begin
   result := FPreviousLayerContent.LayerId;
 end;
 
-function TReplaceLayerByImageOriginalDifference.GetImageDifferenceKind: TImageDifferenceKind;
+function TReplaceLayerByOriginalDifference.GetImageDifferenceKind: TImageDifferenceKind;
 begin
   Result:= idkChangeImage;
 end;
 
-constructor TReplaceLayerByImageOriginalDifference.Create(
-  AFromState: TState; AIndex: integer);
+constructor TReplaceLayerByOriginalDifference.Create(
+  AFromState: TState; AIndex: integer; AAlwaysStoreBitmap: boolean);
 var
   imgState: TImageState;
 begin
   inherited Create(AFromState);
   imgState := AFromState as TImageState;
-  FPreviousLayerContent := TStoredLayer.Create(imgState.LayeredBitmap, AIndex);
+  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);
   FNextMatrix := FPrevMatrix;
   ApplyTo(imgState);
 end;
 
-function TReplaceLayerByImageOriginalDifference.UsedMemory: int64;
+function TReplaceLayerByOriginalDifference.UsedMemory: int64;
 begin
   Result:= FPreviousLayerContent.UsedMemory;
 end;
 
-function TReplaceLayerByImageOriginalDifference.TryCompress: boolean;
+function TReplaceLayerByOriginalDifference.TryCompress: boolean;
 begin
   Result:= FPreviousLayerContent.Compress;
 end;
 
-procedure TReplaceLayerByImageOriginalDifference.ApplyTo(AState: TState);
+procedure TReplaceLayerByOriginalDifference.ApplyTo(AState: TState);
 var
   imgState: TImageState;
-  orig: TBGRALayerImageOriginal;
+  orig: TBGRALayerCustomOriginal;
   origIndex,layerIdx: Integer;
-  source: TBGRABitmap;
-  temp: TBGRACustomBitmap;
 begin
+  inherited ApplyTo(AState);
   imgState := AState as TImageState;
   layerIdx := imgState.LayeredBitmap.GetLayerIndexFromId(FPreviousLayerContent.LayerId);
-  orig := TBGRALayerImageOriginal.Create;
-  source := imgState.LayeredBitmap.LayerBitmap[layerIdx];
-  if (FSourceBounds.Width <> source.Width) or (FSourceBounds.Height <> source.Height) then
-  begin
-    temp := source.GetPart(FSourceBounds);
-    orig.AssignImage(temp);
-    temp.Free;
-  end else
-    orig.AssignImage(source);
+  orig := CreateOriginal(imgState, layerIdx);
   origIndex := imgState.LayeredBitmap.AddOriginal(orig, true);
   imgState.LayeredBitmap.LayerOriginalGuid[layerIdx] := imgState.LayeredBitmap.OriginalGuid[origIndex];
   imgState.LayeredBitmap.LayerOriginalMatrix[layerIdx] := FNextMatrix;
@@ -581,15 +969,16 @@ begin
   end;
 end;
 
-procedure TReplaceLayerByImageOriginalDifference.UnapplyTo(AState: TState);
+procedure TReplaceLayerByOriginalDifference.UnapplyTo(AState: TState);
 var
   imgState: TImageState;
 begin
+  inherited UnapplyTo(AState);
   imgState := AState as TImageState;
   FPreviousLayerContent.Replace(imgState.LayeredBitmap);
 end;
 
-destructor TReplaceLayerByImageOriginalDifference.Destroy;
+destructor TReplaceLayerByOriginalDifference.Destroy;
 begin
   FPreviousLayerContent.Free;
   inherited Destroy;
@@ -1541,8 +1930,8 @@ begin
   with imgDest.LayeredBitmap do
   begin
     previousActiveLayerId:= LayerUniqueId[imgDest.SelectedImageLayerIndex];
-    layerOverCompressedBackup := TStoredLayer.Create(imgDest.LayeredBitmap, ALayerOverIndex);
-    layerUnderCompressedBackup := TStoredLayer.Create(imgDest.LayeredBitmap, ALayerOverIndex-1);
+    layerOverCompressedBackup := TStoredLayer.Create(imgDest.LayeredBitmap, ALayerOverIndex, true);
+    layerUnderCompressedBackup := TStoredLayer.Create(imgDest.LayeredBitmap, ALayerOverIndex-1, true);
   end;
 
   //select layer under and merge
@@ -1809,7 +2198,7 @@ end;
 
 function TImageLayerStateDifference.GetImageDifferenceKind: TImageDifferenceKind;
 begin
-  if ChangeImageLayer or ChangeSelectionLayer or (prevSelectionTransform <> nextSelectionTransform) then
+  if ChangeImageLayer or ChangeSelectionLayer then
   begin
     if ChangeSelectionMask then
       result := idkChangeImageAndSelection
@@ -1828,13 +2217,12 @@ function TImageLayerStateDifference.GetIsIdentity: boolean;
 begin
   Result:= not ChangeImageLayer and
           not ChangeSelectionMask and
-          not ChangeSelectionLayer and
-          (prevSelectionTransform = nextSelectionTransform);
+          not ChangeSelectionLayer;
 end;
 
 function TImageLayerStateDifference.GetChangingBoundsDefined: boolean;
 begin
-  Result:= (prevSelectionTransform = nextSelectionTransform);
+  Result:= true;
 end;
 
 function TImageLayerStateDifference.GetChangingBounds: TRect;
@@ -1854,13 +2242,10 @@ end;
 
 procedure TImageLayerStateDifference.Init(AToState: TState; APreviousImage: TBGRABitmap; APreviousImageChangeRect: TRect;
         APreviousSelection: TBGRABitmap; APreviousSelectionChangeRect: TRect;
-        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect;
-        APreviousSelectionTransform: TAffineMatrix; APreviousLayerOriginalData: TStream;
-        APreviousLayerOriginalMatrix: TAffineMatrix);
+        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect);
 var
   next: TImageState;
   curIdx: integer;
-  nextOrig: TBGRALayerCustomOriginal;
 begin
   inherited Create((AToState as TImageState).saved,false);
   layerId := -1;
@@ -1868,22 +2253,12 @@ begin
   imageOfs := Point(0,0);
   selectionMaskDiff := nil;
   selectionLayerDiff := nil;
-  prevSelectionTransform := APreviousSelectionTransform;
-  nextSelectionTransform := (AToState as TImageState).SelectionTransform;
-  prevLayerOriginalData := nil;
-  nextLayerOriginalData := nil;
-  layerOriginalChange:= false;
 
   next := AToState as TImageState;
   layerId := next.selectedLayerId;
   curIdx := next.LayeredBitmap.GetLayerIndexFromId(LayerId);
   if curIdx = -1 then
-  begin
-    layerId:= -1;
-    prevLayerOriginalMatrix := AffineMatrixIdentity;
-    nextLayerOriginalMatrix := AffineMatrixIdentity;
-    APreviousLayerOriginalData.Free;
-  end
+    raise exception.Create('Layer not found')
   else
   begin
     if not IsRectEmpty(APreviousImageChangeRect) then
@@ -1895,20 +2270,6 @@ begin
       selectionMaskDiff := TGrayscaleImageDiff.Create(APreviousSelection,next.SelectionMask,APreviousSelectionChangeRect);
     if not IsRectEmpty(APreviousSelectionLayerChangeRect) then
       selectionLayerDiff := TImageDiff.Create(APreviousSelectionLayer,next.SelectionLayer,APreviousSelectionLayerChangeRect);
-    prevLayerOriginalMatrix := APreviousLayerOriginalMatrix;
-    nextLayerOriginalMatrix := next.LayerOriginalMatrix[curIdx];
-    nextOrig := next.LayerOriginal[curIdx];
-    //only case handled here: when going from original to raster bitmap
-    if APreviousLayerOriginalData <> nil then
-    begin
-      layerOriginalChange:= true;
-      prevLayerOriginalData := APreviousLayerOriginalData;
-      if Assigned(nextOrig) then
-      begin
-        nextLayerOriginalData := TMemoryStream.Create;
-        nextOrig.SaveToStream(nextLayerOriginalData);
-      end;
-    end;
   end;
 end;
 
@@ -1922,7 +2283,7 @@ end;
 
 procedure TImageLayerStateDifference.ApplyTo(AState: TState);
 var
-  idx, origIdx: integer;
+  idx: integer;
   lState: TImageState;
   newSelectionMask, newSelectionLayer: TBGRABitmap;
 begin
@@ -1932,35 +2293,17 @@ begin
   begin
     idx := lState.LayeredBitmap.GetLayerIndexFromId(layerId);
     if idx = -1 then raise exception.Create('Layer not found');
-    if ChangeImageLayer then
-    begin
-      lState.LayeredBitmap.SetLayerBitmap(idx, imageDiff.ApplyCanCreateNew(lState.LayerBitmap[idx],False), True);
-      lState.LayeredBitmap.LayerOriginalGuid[idx] := GUID_NULL;
-      lState.LayeredBitmap.RemoveUnusedOriginals;
-    end;
-    if nextLayerOriginalData <> nil then
-    begin
-      nextLayerOriginalData.Position := 0;
-      origIdx := lState.LayeredBitmap.AddOriginalFromStream(nextLayerOriginalData, true);
-      lState.LayeredBitmap.LayerOriginalGuid[idx] := lState.LayeredBitmap.OriginalGuid[origIdx];
-      lState.LayeredBitmap.LayerOriginalMatrix[idx] := nextLayerOriginalMatrix;
-      lState.LayeredBitmap.LayerOriginalRenderStatus[idx] := orsProof;
-    end else
-    if lState.LayeredBitmap.LayerOriginal[idx] <> nil then
-    begin
-      lState.LayeredBitmap.LayerOriginalMatrix[idx] := nextLayerOriginalMatrix;
-      lState.LayeredBitmap.LayerOriginalRenderStatus[idx] := orsProof;
-    end;
+    if ChangeImageLayer and (lState.LayeredBitmap.LayerOriginalGuid[idx] <> GUID_NULL) then raise exception.Create('Does not apply to originals');
+    if ChangeImageLayer then lState.LayeredBitmap.SetLayerBitmap(idx, imageDiff.ApplyCanCreateNew(lState.LayerBitmap[idx],False), True);
     if ChangeSelectionMask then newSelectionMask := selectionMaskDiff.ApplyCanCreateNew(lState.SelectionMask,False) else newSelectionMask := lState.SelectionMask;
     if ChangeSelectionLayer then newSelectionLayer := selectionLayerDiff.ApplyCanCreateNew(lState.SelectionLayer,False) else newSelectionLayer := lState.SelectionLayer;
     lState.ReplaceSelection(newSelectionMask, newSelectionLayer);
-    lState.SelectionTransform := nextSelectionTransform;
   end;
 end;
 
 procedure TImageLayerStateDifference.UnapplyTo(AState: TState);
 var
-  idx, origIdx: integer;
+  idx: integer;
   lState: TImageState;
   newSelectionMask, newSelectionLayer: TBGRABitmap;
 begin
@@ -1970,29 +2313,11 @@ begin
   begin
     idx := lState.LayeredBitmap.GetLayerIndexFromId(layerId);
     if idx = -1 then raise exception.Create('Layer not found');
-    if ChangeImageLayer then
-    begin
-      lState.LayeredBitmap.SetLayerBitmap(idx, imageDiff.ApplyCanCreateNew(lState.LayerBitmap[idx],True), True);
-      lState.LayeredBitmap.LayerOriginalGuid[idx] := GUID_NULL;
-      lState.LayeredBitmap.RemoveUnusedOriginals;
-    end;
-    if prevLayerOriginalData <> nil then
-    begin
-      prevLayerOriginalData.Position:= 0;
-      origIdx := lState.LayeredBitmap.AddOriginalFromStream(prevLayerOriginalData, true);
-      lState.LayeredBitmap.LayerOriginalGuid[idx] := lState.LayeredBitmap.OriginalGuid[origIdx];
-      lState.LayeredBitmap.LayerOriginalMatrix[idx] := prevLayerOriginalMatrix;
-      lState.LayeredBitmap.LayerOriginalRenderStatus[idx] := orsProof;
-    end else
-    if lState.LayeredBitmap.LayerOriginal[idx] <> nil then
-    begin
-      lState.LayeredBitmap.LayerOriginalMatrix[idx] := prevLayerOriginalMatrix;
-      lState.LayeredBitmap.LayerOriginalRenderStatus[idx] := orsProof;
-    end;
+    if ChangeImageLayer and (lState.LayeredBitmap.LayerOriginalGuid[idx] <> GUID_NULL) then raise exception.Create('Does not apply to originals');
+    if ChangeImageLayer then lState.LayeredBitmap.SetLayerBitmap(idx, imageDiff.ApplyCanCreateNew(lState.LayerBitmap[idx],True), True);
     if ChangeSelectionMask then newSelectionMask := selectionMaskDiff.ApplyCanCreateNew(lState.SelectionMask,True) else newSelectionMask := lState.SelectionMask;
     if ChangeSelectionLayer then newSelectionLayer := selectionLayerDiff.ApplyCanCreateNew(lState.SelectionLayer,True) else newSelectionLayer := lState.SelectionLayer;
     lState.ReplaceSelection(newSelectionMask, newSelectionLayer);
-    lState.SelectionTransform := prevSelectionTransform;
   end;
 end;
 
@@ -2008,7 +2333,6 @@ constructor TImageLayerStateDifference.Create(AFromState, AToState: TState);
 var
   prev,next: TImageState;
   prevIdx,curIdx: integer;
-  prevOrig,nextOrig: TBGRALayerCustomOriginal;
 begin
   inherited Create(AFromState,AToState);
   layerId := -1;
@@ -2016,9 +2340,6 @@ begin
   imageOfs := Point(0,0);
   selectionMaskDiff := nil;
   selectionLayerDiff := nil;
-  prevLayerOriginalData := nil;
-  nextLayerOriginalData := nil;
-  layerOriginalChange := false;
 
   prev := AFromState as TImageState;
   next := AToState as TImageState;
@@ -2028,45 +2349,20 @@ begin
   prevIdx := prev.LayeredBitmap.GetLayerIndexFromId(LayerId);
   curIdx := next.LayeredBitmap.GetLayerIndexFromId(LayerId);
   if (curIdx = -1) or (prevIdx = -1) then
-  begin
-    layerId:= -1;
-    prevLayerOriginalMatrix := AffineMatrixIdentity;
-    nextLayerOriginalMatrix := AffineMatrixIdentity;
-  end
+    raise exception.Create('Layer not found')
   else
   begin
     imageDiff := TImageDiff.Create(prev.LayerBitmap[prevIdx],next.LayerBitmap[curIdx]);
     imageOfs := next.LayerOffset[curIdx];
     selectionMaskDiff := TGrayscaleImageDiff.Create(prev.SelectionMask,next.SelectionMask);
     selectionLayerDiff := TImageDiff.Create(prev.SelectionLayer,next.SelectionLayer);
-    prevOrig := prev.LayerOriginal[prevIdx];
-    prevLayerOriginalMatrix := prev.LayerOriginalMatrix[prevIdx];
-    nextOrig := next.LayerOriginal[curIdx];
-    nextLayerOriginalMatrix := next.LayerOriginalMatrix[curIdx];
-    if prevOrig <> nextOrig then
-    begin
-      layerOriginalChange := true;
-      if Assigned(prevOrig) then
-      begin
-        prevLayerOriginalData := TMemoryStream.Create;
-        prevOrig.SaveToStream(prevLayerOriginalData);
-      end;
-      if Assigned(nextOrig) then
-      begin
-        nextLayerOriginalData := TMemoryStream.Create;
-        nextOrig.SaveToStream(nextLayerOriginalData);
-      end;
-    end;
   end;
 end;
 
 constructor TImageLayerStateDifference.Create(AToState: TState;
   APreviousImage: TBGRABitmap; APreviousImageDefined: boolean;
   APreviousSelection: TBGRABitmap; APreviousSelectionDefined: boolean;
-  APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerDefined: boolean;
-  APreviousSelectionTransform: TAffineMatrix;
-  APreviousLayerOriginalData: TStream;
-  APreviousLayerOriginalMatrix: TAffineMatrix);
+  APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerDefined: boolean);
 var
   r1,r2,r3: TRect;
   w,h: integer;
@@ -2076,21 +2372,16 @@ begin
   if APreviousImageDefined then r1 := rect(0,0,w,h) else r1 := EmptyRect;
   if APreviousSelectionDefined then r2 := rect(0,0,w,h) else r2 := EmptyRect;
   if APreviousSelectionLayerDefined then r3 := rect(0,0,w,h) else r3 := EmptyRect;
-  Init(AToState,APreviousImage,r1,APreviousSelection,r2,APreviousSelectionLayer,r3,
-       APreviousSelectionTransform, APreviousLayerOriginalData, APreviousLayerOriginalMatrix);
+  Init(AToState,APreviousImage,r1,APreviousSelection,r2,APreviousSelectionLayer,r3);
 end;
 
 constructor TImageLayerStateDifference.Create(AToState: TState;
   APreviousImage: TBGRABitmap; APreviousImageChangeRect: TRect;
   APreviousSelection: TBGRABitmap; APreviousSelectionChangeRect: TRect;
-  APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect;
-  APreviousSelectionTransform: TAffineMatrix;
-  APreviousLayerOriginalData: TStream;
-  APreviousLayerOriginalMatrix: TAffineMatrix);
+  APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect);
 begin
   Init(AToState, APreviousImage, APreviousImageChangeRect, APreviousSelection,
-    APreviousSelectionChangeRect, APreviousSelectionLayer, APreviousSelectionLayerChangeRect,
-    APreviousSelectionTransform, APreviousLayerOriginalData, APreviousLayerOriginalMatrix);
+    APreviousSelectionChangeRect, APreviousSelectionLayer, APreviousSelectionLayerChangeRect);
 end;
 
 function TImageLayerStateDifference.ToString: ansistring;
@@ -2132,7 +2423,6 @@ begin
 
     result += 'SelectionLayer ';
   end;
-  if nextSelectionTransform<>prevSelectionTransform then result += 'SelectionTransform ';
   result := trim(Result)+')';
 end;
 
@@ -2141,8 +2431,6 @@ begin
   imageDiff.Free;
   selectionMaskDiff.Free;
   selectionLayerDiff.Free;
-  prevLayerOriginalData.Free;
-  nextLayerOriginalData.Free;
   inherited Destroy;
 end;
 

+ 21 - 37
lazpaint/uimagestate.pas

@@ -118,19 +118,14 @@ type
     function LinearNegative: TCustomImageDifference;
     function Negative: TCustomImageDifference;
     function ComputeLayerOffsetDifference(AOffsetX, AOffsetY: integer): TCustomImageDifference;
+    function ComputeSelectionTransformDifference: TCustomImageDifference;
     function ComputeLayerMatrixDifference(AIndex: integer; APrevMatrix, ANewMatrix: TAffineMatrix): TCustomImageDifference;
     function ComputeLayerDifference(APreviousImage: TBGRABitmap; APreviousImageDefined: boolean;
         APreviousSelection: TBGRABitmap; APreviousSelectionDefined: boolean;
-        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerDefined: boolean;
-        APreviousSelectionTransform: TAffineMatrix;
-        APreviousLayerOriginalData: TStream;
-        APreviousLayerOriginalMatrix: TAffineMatrix): TCustomImageDifference; overload;
+        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerDefined: boolean): TCustomImageDifference; overload;
     function ComputeLayerDifference(APreviousImage: TBGRABitmap; APreviousImageChangeRect: TRect;
         APreviousSelection: TBGRABitmap; APreviousSelectionChangeRect: TRect;
-        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect;
-        APreviousSelectionTransform: TAffineMatrix;
-        APreviousLayerOriginalData: TStream;
-        APreviousLayerOriginalMatrix: TAffineMatrix): TCustomImageDifference; overload;
+        APreviousSelectionLayer: TBGRABitmap; APreviousSelectionLayerChangeRect: TRect): TCustomImageDifference; overload;
     function GetLayeredBitmapCopy: TBGRALayeredBitmap;
     function ComputeFlatImage(AFromLayer, AToLayer: integer; ASeparateXorMask: boolean): TBGRABitmap;
     function ComputeFlatImage(ARect: TRect; AFromLayer, AToLayer: integer; ASeparateXorMask: boolean): TBGRABitmap;
@@ -524,7 +519,7 @@ begin
   begin
     r := SelectionLayer.GetImageAffineBounds(FSelectionTransform, GetSelectionLayerBounds);
     ANewLayer := TBGRABitmap.Create(r.Width,r.Height);
-    ANewLayer.PutImageAffine(AffineMatrixTranslation(-r.Left,-r.Top)*FSelectionTransform, SelectionLayer);
+    ANewLayer.PutImageAffine(AffineMatrixTranslation(-r.Left,-r.Top)*FSelectionTransform, SelectionLayer, rfHalfCosine);
     ALeft := r.Left;
     ATop := r.Top;
   end;
@@ -808,27 +803,15 @@ begin
 end;
 
 function TImageState.DiscardOriginal(ACreateUndo: boolean): TCustomImageDifference;
-var
-  prevOriginal: TBGRALayerCustomOriginal;
-  prevOriginalMatrix: TAffineMatrix;
-  prevOriginalData: TStream;
-  prevOriginalGuid: TGuid;
-begin
-  prevOriginalGuid := LayeredBitmap.LayerOriginalGuid[SelectedImageLayerIndex];
-  if prevOriginalGuid=GUID_NULL then exit;
-  prevOriginalMatrix:= LayeredBitmap.LayerOriginalMatrix[SelectedImageLayerIndex];
-  LayeredBitmap.LayerOriginalGuid[SelectedImageLayerIndex] := GUID_NULL;
-  LayeredBitmap.LayerOriginalMatrix[SelectedImageLayerIndex] := AffineMatrixIdentity;
+begin
   if ACreateUndo then
-  begin
-    prevOriginalData:= TMemoryStream.Create;
-    prevOriginal := LayeredBitmap.Original[LayeredBitmap.IndexOfOriginal(prevOriginalGuid)];
-    prevOriginal.SaveToStream(prevOriginalData);
-    result := TImageLayerStateDifference.Create(self, nil,false,nil,false,nil,false,SelectionTransform,prevOriginalData,prevOriginalMatrix);
-  end
+    result := TDiscardOriginalDifference.Create(self, SelectedImageLayerIndex, true)
   else
-    result := nil;
-  LayeredBitmap.RemoveUnusedOriginals;
+  begin
+    LayeredBitmap.LayerOriginalGuid[SelectedImageLayerIndex] := GUID_NULL;
+    LayeredBitmap.LayerOriginalMatrix[SelectedImageLayerIndex] := AffineMatrixIdentity;
+    LayeredBitmap.RemoveUnusedOriginals;
+  end;
 end;
 
 function TImageState.HorizontalFlip: TCustomImageDifference;
@@ -929,7 +912,7 @@ begin
   begin
     r := SelectionMask.GetImageAffineBounds(FSelectionTransform, GetSelectionMaskBounds);
     ANewMask := TBGRABitmap.Create(r.Width,r.Height,BGRABlack);
-    ANewMask.PutImageAffine(AffineMatrixTranslation(-r.Left,-r.Top)*FSelectionTransform, SelectionMask);
+    ANewMask.PutImageAffine(AffineMatrixTranslation(-r.Left,-r.Top)*FSelectionTransform, SelectionMask, rfHalfCosine, dmLinearBlend);
     ALeft := r.Left;
     ATop := r.Top;
   end;
@@ -1028,6 +1011,11 @@ begin
   result := TApplyLayerOffsetStateDifference.Create(self, LayeredBitmap.LayerUniqueId[SelectedImageLayerIndex], AOffsetX,AOffsetY, false);
 end;
 
+function TImageState.ComputeSelectionTransformDifference: TCustomImageDifference;
+begin
+  result := TSelectionTransformDifference.Create(self, false);
+end;
+
 function TImageState.ComputeLayerMatrixDifference(AIndex: integer; APrevMatrix,
   ANewMatrix: TAffineMatrix): TCustomImageDifference;
 begin
@@ -1037,29 +1025,25 @@ end;
 function TImageState.ComputeLayerDifference(APreviousImage: TBGRABitmap;
   APreviousImageDefined: boolean; APreviousSelection: TBGRABitmap;
   APreviousSelectionDefined: boolean; APreviousSelectionLayer: TBGRABitmap;
-  APreviousSelectionLayerDefined: boolean; APreviousSelectionTransform: TAffineMatrix;
-  APreviousLayerOriginalData: TStream; APreviousLayerOriginalMatrix: TAffineMatrix): TCustomImageDifference;
+  APreviousSelectionLayerDefined: boolean): TCustomImageDifference;
 begin
   if LayeredBitmap = nil then
     result := nil
   else
     result := TImageLayerStateDifference.Create(self, APreviousImage, APreviousImageDefined,
-      APreviousSelection, APreviousSelectionDefined, APreviousSelectionLayer, APreviousSelectionLayerDefined,
-      APreviousSelectionTransform, APreviousLayerOriginalData, APreviousLayerOriginalMatrix);
+      APreviousSelection, APreviousSelectionDefined, APreviousSelectionLayer, APreviousSelectionLayerDefined);
 end;
 
 function TImageState.ComputeLayerDifference(APreviousImage: TBGRABitmap;
   APreviousImageChangeRect: TRect; APreviousSelection: TBGRABitmap;
   APreviousSelectionChangeRect: TRect; APreviousSelectionLayer: TBGRABitmap;
-  APreviousSelectionLayerChangeRect: TRect; APreviousSelectionTransform: TAffineMatrix;
-  APreviousLayerOriginalData: TStream; APreviousLayerOriginalMatrix: TAffineMatrix): TCustomImageDifference;
+  APreviousSelectionLayerChangeRect: TRect): TCustomImageDifference;
 begin
   if LayeredBitmap = nil then
     result := nil
   else
     result := TImageLayerStateDifference.Create(self, APreviousImage, APreviousImageChangeRect,
-      APreviousSelection, APreviousSelectionChangeRect, APreviousSelectionLayer, APreviousSelectionLayerChangeRect,
-      APreviousSelectionTransform, APreviousLayerOriginalData, APreviousLayerOriginalMatrix);
+      APreviousSelection, APreviousSelectionChangeRect, APreviousSelectionLayer, APreviousSelectionLayerChangeRect);
 end;
 
 function TImageState.GetLayeredBitmapCopy: TBGRALayeredBitmap;

+ 5 - 4
lazpaint/uimageview.pas

@@ -55,7 +55,7 @@ type
     procedure OnZoomChanged({%H-}sender: TZoom; {%H-}ANewZoom: single; AWorkArea: TRect);
     procedure UpdateCursor(X,Y: integer; ACanvasOfs: TPoint; AWorkArea: TRect; AControl: TControl;
                           AWinControlOfs: TPoint; AWinControl: TWinControl);
-    procedure UpdatePicture(ACanvasOfs: TPoint; AWorkArea: TRect; {%H-}AControl: TControl);
+    procedure UpdatePicture(ACanvasOfs: TPoint; AWorkArea: TRect; {%H-}AWinControl: TWinControl);
     function BitmapToForm(pt: TPointF): TPointF;
     function BitmapToForm(X, Y: Single): TPointF;
     function BitmapToVirtualScreen(ptF: TPointF): TPointF;
@@ -530,7 +530,7 @@ begin
     PaintVirtualScreenCursor(ACanvasOfs, AWorkArea, AWinControlOfs, AWinControl);
 end;
 
-procedure TImageView.UpdatePicture(ACanvasOfs: TPoint; AWorkArea: TRect; AControl: TControl);
+procedure TImageView.UpdatePicture(ACanvasOfs: TPoint; AWorkArea: TRect; AWinControl: TWinControl);
 var
   updateArea: TRect;
   {$IFDEF IMAGEVIEW_DIRECTUPDATE}prevVSArea: TRect;{$ENDIF}
@@ -542,13 +542,14 @@ begin
   FPenCursorPos := GetPenCursorPosition;
   updateArea := GetRectToInvalidate(false, AWorkArea);
   FPenCursorPosBefore.bounds := EmptyRect;
-  OffsetRect(updateArea, -FLastPictureParameters.virtualScreenArea.Left,-FLastPictureParameters.virtualScreenArea.Top);
   {$IFDEF IMAGEVIEW_DIRECTUPDATE}
+  OffsetRect(updateArea, -FLastPictureParameters.virtualScreenArea.Left,-FLastPictureParameters.virtualScreenArea.Top);
   PaintPictureImplementation(ACanvasOfs, AWorkArea, updateArea);
   if prevVSArea <> FLastPictureParameters.virtualScreenArea then
     PaintBlueAreaImplementation(ACanvasOfs, AWorkArea);
   {$ELSE}
-  AControl.Update;
+  InvalidateRect(AWinControl.Handle, @updateArea, false);
+  AWinControl.Update;
   {$ENDIF}
 end;
 

+ 53 - 114
lazpaint/ulayeraction.pas

@@ -24,7 +24,6 @@ type
     FPrediff: TComposedImageDifference;
     FBackupSelectedLayer, FBackupSelectionLayer, FBackupSelection: TBGRABitmap;
     FBackupSelectedLayerDefined, FBackupSelectionLayerDefined, FBackupSelectionMaskDefined: boolean;
-    FBackupSelectionTransform: TAffineMatrix;
     FSelectedImageLayerChangedArea, FSelectionLayerChangedArea, FSelectionMaskChangedArea: TRect;
     FDone: boolean;
     FOnTryStop: TOnTryStopEventHandler;
@@ -38,7 +37,6 @@ type
     function GetDrawingLayer: TBGRABitmap;
     function GetSelectedImageLayerOffset: TPoint;
     function GetSelectionLayerBounds: TRect;
-    function GetSelectionTransform: TAffineMatrix;
     procedure SetOnDestroy(AValue: TNotifyEvent);
     procedure SetOnNotifyChange(AValue: TNotifyChangeEvent);
     procedure SetOnNotifyUndo(AValue: TNotifyUndoEvent);
@@ -49,7 +47,7 @@ type
     procedure NeedSelectionLayerBackup;
     property CurrentState: TImageState read GetCurrentState;
   public
-    constructor Create(AState: TImageState; AApplyOfsBefore: boolean = false);
+    constructor Create(AState: TImageState; AApplyOfsBefore: boolean = false; AApplySelectionTransformBefore: boolean = false);
     procedure Validate;
     procedure PartialValidate(ADiscardBackup: boolean = false);
     procedure PartialCancel;
@@ -62,7 +60,6 @@ type
     procedure ReleaseSelection;
     procedure RetrieveSelection;
     function RetrieveSelectionIfLayerEmpty(removeFromBitmap: boolean = false): boolean;
-    procedure ApplySelectionTransform(ApplyToMask: boolean= true);
     procedure ApplySelectionMask;
 
     procedure ReplaceSelectionLayer(bmp: TBGRABitmap; AOwned: boolean);
@@ -88,11 +85,11 @@ type
     property OnTryStop: TOnTryStopEventHandler read FOnTryStop write FOnTryStop;
     property Done: boolean read FDone;
     property ChangeBoundsNotified: boolean read FChangeBoundsNotified write FChangeBoundsNotified;
-    property SelectionTransform: TAffineMatrix read GetSelectionTransform;
     property SelectionLayerBounds: TRect read GetSelectionLayerBounds;
     property OnNotifyChange: TNotifyChangeEvent read FOnNotifyChange write SetOnNotifyChange;
     property OnNotifyUndo: TNotifyUndoEvent read FOnNotifyUndo write SetOnNotifyUndo;
     property OnDestroy: TNotifyEvent read FOnDestroy write SetOnDestroy;
+    property Prediff: TComposedImageDifference read FPrediff;
   end;
 
 implementation
@@ -160,11 +157,6 @@ begin
   result := CurrentState.GetSelectionLayerBounds;
 end;
 
-function TLayerAction.GetSelectionTransform: TAffineMatrix;
-begin
-  result:= CurrentState.SelectionTransform;
-end;
-
 procedure TLayerAction.SetOnDestroy(AValue: TNotifyEvent);
 begin
   if FOnDestroy=AValue then Exit;
@@ -189,7 +181,6 @@ begin
   RestoreSelectedLayer;
   RestoreSelectionLayer;
   RestoreSelectionMask;
-  CurrentState.SelectionTransform := FBackupSelectionTransform;
   if Assigned(FPrediff) then
   begin
     FPrediff.UnapplyTo(CurrentState);
@@ -225,9 +216,10 @@ begin
   end;
 end;
 
-constructor TLayerAction.Create(AState: TImageState; AApplyOfsBefore: boolean);
+constructor TLayerAction.Create(AState: TImageState; AApplyOfsBefore: boolean;
+  AApplySelectionTransformBefore: boolean);
 var
-  layerOfsDiff: TCustomImageDifference;
+  layerOfsDiff,selTransfDiff: TCustomImageDifference;
 begin
   FImageState := AState;
   FBackupSelectedLayer := nil;
@@ -236,7 +228,6 @@ begin
   FBackupSelectedLayerDefined := false;
   FBackupSelectionMaskDefined := false;
   FBackupSelectionLayerDefined := false;
-  FBackupSelectionTransform := CurrentState.SelectionTransform;
   FSelectedImageLayerChangedArea := EmptyRect;
   FSelectionLayerChangedArea := EmptyRect;
   FSelectionMaskChangedArea := EmptyRect;
@@ -253,12 +244,23 @@ begin
       FPrediff.Add(layerOfsDiff);
     end;
   end;
+  if AApplySelectionTransformBefore then
+  begin
+    selTransfDiff := CurrentState.ComputeSelectionTransformDifference;
+    if selTransfDiff.IsIdentity then FreeAndNil(selTransfDiff)
+    else
+    begin
+      selTransfDiff.ApplyTo(CurrentState);
+      FPrediff.Add(selTransfDiff);
+    end;
+  end;
   if FPrediff.Count = 0 then FreeAndNil(FPrediff);
 end;
 
 destructor TLayerAction.Destroy;
 begin
   if not FDone then Cancel;
+  FPrediff.Free;
   FBackupSelectedLayer.Free;
   FBackupSelection.Free;
   FBackupSelectionLayer.Free;
@@ -409,6 +411,7 @@ procedure TLayerAction.MergeWithSelection(AApplyMask: boolean);
 var offs: TPoint;
   sourceRect,destRect: TRect;
 begin
+  if not IsAffineMatrixIdentity(CurrentState.SelectionTransform) then raise exception.Create('Unexpected selection transform');
   if not CurrentState.SelectionLayerEmpty and not (AApplyMask and CurrentState.SelectionMaskEmpty) then
   begin
     sourceRect := CurrentState.GetSelectionLayerBounds;
@@ -430,6 +433,7 @@ end;
 procedure TLayerAction.ReleaseSelection;
 var bounds: TRect;
 begin
+  if not IsAffineMatrixIdentity(CurrentState.SelectionTransform) then raise exception.Create('Unexpected selection transform');
   if not CurrentState.SelectionMaskEmpty then
   begin
     bounds := CurrentState.GetSelectionMaskBounds;
@@ -446,7 +450,6 @@ begin
     ApplySelectionMask;
     CurrentState.SelectionMask.Free;
     CurrentState.SelectionMask := nil;
-    ApplySelectionTransform(False);
     MergeWithSelection(False);
   end;
 end;
@@ -456,11 +459,11 @@ var temp : TBGRABitmap;
   offs: TPoint;
   r, maskBounds: TRect;
 begin
+  if not IsAffineMatrixIdentity(CurrentState.SelectionTransform) then raise exception.Create('Unexpected selection transform');
   if not CurrentState.SelectionMaskEmpty then
   begin
     NeedSelectedLayerBackup;
     NeedSelectionLayerBackup;
-    ApplySelectionTransform;
     MergeWithSelection;
     offs := CurrentState.LayerOffset[CurrentState.SelectedImageLayerIndex];
     maskBounds := CurrentState.GetSelectionMaskBounds;
@@ -477,6 +480,7 @@ end;
 
 function TLayerAction.RetrieveSelectionIfLayerEmpty(removeFromBitmap: boolean): boolean;
 begin
+  if not IsAffineMatrixIdentity(CurrentState.SelectionTransform) then raise exception.Create('Unexpected selection transform');
   NeedSelectedLayerBackup;
   NeedSelectionLayerBackup;
   if CurrentState.SelectionLayerEmpty then
@@ -516,42 +520,6 @@ begin
   CurrentState.ReplaceSelectionLayer(bmp,AOwned);
 end;
 
-procedure TLayerAction.ApplySelectionTransform(ApplyToMask: boolean);
-var
-  newBmp: TBGRABitmap;
-  newLeft, newTop: integer;
-  r: TRect;
-begin
-  if not IsAffineMatrixIdentity(CurrentState.SelectionTransform) then
-  begin
-    if ApplyToMask and not CurrentState.SelectionMaskEmpty then
-    begin
-      NeedSelectionMaskBackup;
-      CurrentState.ComputeTransformedSelectionMask(newBmp,newLeft,newTop);
-      r := CurrentState.GetSelectionMaskBounds;
-      CurrentState.SelectionMask.FillRect(r, BGRABlack, dmSet);
-      NotifyChange(CurrentState.SelectionMask, r);
-      CurrentState.SelectionMask.PutImage(newLeft,newTop,newBmp,dmSet);
-      newBmp.Free;
-      CurrentState.DiscardSelectionMaskBounds;
-    end;
-    if not CurrentState.SelectionLayerEmpty then
-    begin
-      NeedSelectionLayerBackup;
-      CurrentState.ComputeTransformedSelectionLayer(newBmp,newLeft,newTop);
-      r := CurrentState.GetSelectionLayerBounds;
-      CurrentState.SelectionLayer.FillRect(r, BGRAPixelTransparent, dmSet);
-      NotifyChange(CurrentState.SelectionLayer, r);
-      CurrentState.SelectionLayer.PutImage(newLeft,newTop,newBmp,dmSet);
-      newBmp.Free;
-      CurrentState.DiscardSelectionLayerBounds;
-    end;
-    CurrentState.SelectionTransform := AffineMatrixIdentity;
-    NotifyChange(CurrentState.SelectionMask, CurrentState.GetSelectionMaskBounds);
-    NotifyChange(CurrentState.SelectionLayer, CurrentState.GetSelectionLayerBounds);
-  end;
-end;
-
 procedure TLayerAction.ApplySelectionMask;
 var r: TRect;
 begin
@@ -583,19 +551,30 @@ end;
 
 procedure TLayerAction.PartialValidate(ADiscardBackup: boolean = false);
 var
-  prevLayerOriginalMatrix: TAffineMatrix;
-  prevLayerOriginaData: TStream;
   imgDiff: TImageLayerStateDifference;
   composedDiff: TComposedImageDifference;
-  ofs: TPoint;
-  applyOfs: TCustomImageDifference;
-  appendOfs, owned: boolean;
+  owned, rasterizeOriginal: boolean;
+
+  procedure NotifyPrediff;
+  begin
+    if Assigned(FPrediff) then
+    begin
+      if Assigned(FOnNotifyUndo) then
+      begin
+        owned := false;
+        FOnNotifyUndo(self, FPrediff, owned);
+        if not owned then FPrediff.Free;
+      end else
+        FPrediff.Free;
+      FPrediff := nil;
+    end;
+  end;
+
 begin
-  if FBackupSelectedLayerDefined or FBackupSelectionMaskDefined or FBackupSelectionLayerDefined then
+  if (FBackupSelectedLayerDefined or FBackupSelectionMaskDefined or FBackupSelectionLayerDefined) and
+     not (ChangeBoundsNotified and IsRectEmpty(FSelectedImageLayerChangedArea) and IsRectEmpty(FSelectionMaskChangedArea) and
+         IsRectEmpty(FSelectionLayerChangedArea)) then
   begin
-    if ChangeBoundsNotified then
-      if IsRectEmpty(FSelectedImageLayerChangedArea) and IsRectEmpty(FSelectionMaskChangedArea) and
-         IsRectEmpty(FSelectionLayerChangedArea) then exit;
     if FBackupSelectionLayerDefined then
     begin
       CurrentState.DiscardSelectionLayerBounds;
@@ -608,32 +587,15 @@ begin
       if CurrentState.SelectionMaskEmpty then
         CurrentState.RemoveSelection;
     end;
-    //original will be backed up if there are changes in the raster image of the selected layer
-    if CurrentState.LayerOriginalDefined[CurrentState.SelectedImageLayerIndex] and
-       (FBackupSelectedLayerDefined or not IsRectEmpty(FSelectedImageLayerChangedArea)) then
-    begin
-      prevLayerOriginaData:= TMemoryStream.Create;
-      CurrentState.SaveOriginalToStream(prevLayerOriginaData);
-      prevLayerOriginalMatrix:= CurrentState.LayerOriginalMatrix[CurrentState.SelectedImageLayerIndex];
-      CurrentState.DiscardOriginal(false);
-    end else
-    begin
-      prevLayerOriginaData := nil;
-      prevLayerOriginalMatrix:= AffineMatrixIdentity;
-    end;
 
     if ChangeBoundsNotified then
       imgDiff := CurrentState.ComputeLayerDifference(FBackupSelectedLayer, FSelectedImageLayerChangedArea,
         FBackupSelection, FSelectionMaskChangedArea,
-        FBackupSelectionLayer, FSelectionLayerChangedArea,
-        FBackupSelectionTransform,
-        prevLayerOriginaData, prevLayerOriginalMatrix) as TImageLayerStateDifference
+        FBackupSelectionLayer, FSelectionLayerChangedArea) as TImageLayerStateDifference
     else
       imgDiff := CurrentState.ComputeLayerDifference(FBackupSelectedLayer, FBackupSelectedLayerDefined,
         FBackupSelection, FBackupSelectionMaskDefined,
-        FBackupSelectionLayer, FBackupSelectionLayerDefined,
-        FBackupSelectionTransform,
-        prevLayerOriginaData, prevLayerOriginalMatrix) as TImageLayerStateDifference;
+        FBackupSelectionLayer, FBackupSelectionLayerDefined) as TImageLayerStateDifference;
     if imgDiff.IsIdentity then FreeAndNil(imgDiff);
 
     if ADiscardBackup then
@@ -644,8 +606,6 @@ begin
       FBackupSelectedLayerDefined := false;
       FBackupSelectedLayerDefined := false;
       FBackupSelectionMaskDefined := false;
-
-      appendOfs:= Assigned(imgDiff) and imgDiff.ChangeImageLayer;
     end else
     begin
       if FBackupSelectionLayerDefined then
@@ -694,13 +654,12 @@ begin
         end;
         FSelectionMaskChangedArea := EmptyRect;
       end;
-
-      appendOfs := false;
     end;
 
     if assigned(imgDiff) then
     begin
-      if appendOfs or Assigned(FPrediff) then
+      rasterizeOriginal := CurrentState.LayerOriginalDefined[CurrentState.SelectedImageLayerIndex] and imgDiff.ChangeImageLayer;
+      if Assigned(FPrediff) or rasterizeOriginal then
       begin
         composedDiff := TComposedImageDifference.Create;
         if Assigned(FPrediff) then
@@ -708,24 +667,17 @@ begin
           composedDiff.AddRange(FPrediff);
           FPrediff := nil;
         end;
+        if rasterizeOriginal then
+          composedDiff.Add(TDiscardOriginalDifference.Create(CurrentState,
+            CurrentState.SelectedImageLayerIndex, true));
         composedDiff.Add(imgDiff);
-        if appendOfs then
-        begin
-          ofs := CurrentState.LayerOffset[CurrentState.SelectedImageLayerIndex];
-          applyOfs:= CurrentState.ComputeLayerOffsetDifference(ofs.x, ofs.y);
-          if not applyOfs.IsIdentity then
-          begin
-            composedDiff.Add(applyOfs);
-            applyOfs.ApplyTo(CurrentState);
-          end else
-            applyOfs.Free;
-        end;
         if Assigned(FOnNotifyUndo) then
         begin
           owned := false;
           FOnNotifyUndo(self, composedDiff, owned);
           if not owned then composedDiff.Free;
-        end;
+        end else
+          composedDiff.Free;
       end else
       begin
         if Assigned(FOnNotifyUndo) then
@@ -733,24 +685,11 @@ begin
           owned := false;
           FOnNotifyUndo(self, imgDiff, owned);
           if not owned then imgDiff.Free;
-        end;
-      end;
-    end;
-
-    FBackupSelectionTransform := CurrentState.SelectionTransform;
-  end else
-  begin
-    if Assigned(FPrediff) then
-    begin
-      if Assigned(FOnNotifyUndo) then
-      begin
-        owned := false;
-        FOnNotifyUndo(self, FPrediff, owned);
-        if not owned then FPrediff.Free;
+        end else
+          imgDiff.Free;
       end;
-      FPrediff := nil;
-    end;
-  end;
+    end else NotifyPrediff;
+  end else NotifyPrediff;
 end;
 
 

+ 10 - 3
lazpaint/ulayerstack.lfm

@@ -18,7 +18,7 @@ object FLayerStack: TFLayerStack
   OnHide = FormHide
   OnShow = FormShow
   ShowInTaskBar = stNever
-  LCLVersion = '2.0.0.4'
+  LCLVersion = '2.0.2.0'
   object BGRALayerStack: TBGRAVirtualScreen
     Left = 0
     Height = 37
@@ -33,6 +33,7 @@ object FLayerStack: TFLayerStack
     OnMouseDown = BGRALayerStackMouseDown
     OnMouseMove = BGRALayerStackMouseMove
     OnMouseUp = BGRALayerStackMouseUp
+    OnMouseWheel = BGRALayerStackMouseWheel
     OnResize = BGRALayerStackResize
   end
   object Panel1: TPanel
@@ -51,6 +52,8 @@ object FLayerStack: TFLayerStack
       Width = 71
       Align = alNone
       Anchors = [akTop, akRight]
+      ButtonHeight = 20
+      ButtonWidth = 20
       EdgeBorders = []
       EdgeInner = esNone
       EdgeOuter = esNone
@@ -59,7 +62,7 @@ object FLayerStack: TFLayerStack
       ShowHint = True
       TabOrder = 0
       object ToolZoomLayerStackIn: TToolButton
-        Left = 24
+        Left = 21
         Hint = 'Zoom layer stack in'
         Top = 0
         ImageIndex = 6
@@ -73,7 +76,7 @@ object FLayerStack: TFLayerStack
         OnClick = ToolZoomLayerStackOutClick
       end
       object ToolButton1: TToolButton
-        Left = 47
+        Left = 41
         Top = 0
         Action = FMain.LayerRemoveCurrent
       end
@@ -98,6 +101,8 @@ object FLayerStack: TFLayerStack
       Width = 243
       Align = alNone
       Anchors = [akTop, akLeft, akRight]
+      ButtonHeight = 20
+      ButtonWidth = 20
       EdgeBorders = []
       EdgeInner = esNone
       EdgeOuter = esNone
@@ -112,6 +117,8 @@ object FLayerStack: TFLayerStack
       Top = 2
       Width = 24
       Align = alNone
+      ButtonHeight = 20
+      ButtonWidth = 20
       EdgeBorders = []
       EdgeInner = esNone
       EdgeOuter = esNone

+ 132 - 76
lazpaint/ulayerstack.pas

@@ -14,6 +14,12 @@ type
     PreviewPts: array of TPointF;
     NameRect,OpacityBar: TRect;
   end;
+  TLayerItemInfo = record
+    RightPart: TDrawLayerItemResult;
+    VisibleCheckbox: TRect;
+    KindIcon: TRect;
+    KindIconHint: string;
+  end;
 
   { TFLayerStack }
 
@@ -36,6 +42,8 @@ type
       Y: Integer);
     procedure BGRALayerStackMouseUp(Sender: TObject; Button: TMouseButton;
       {%H-}Shift: TShiftState; X, Y: Integer);
+    procedure BGRALayerStackMouseWheel(Sender: TObject; Shift: TShiftState;
+      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
     procedure BGRALayerStackRedraw(Sender: TObject; Bitmap: TBGRABitmap);
     procedure BGRALayerStackResize(Sender: TObject);
     procedure ComboBox_BlendOpChange(Sender: TObject);
@@ -67,8 +75,7 @@ type
     VolatileHorzScrollBar, VolatileVertScrollBar: TVolatileScrollBar;
     ScrollButtonRect: TRect;
     InterruptorWidth,InterruptorHeight: integer;
-    interruptors: array of TRect;
-    LayerInfo: array of TDrawLayerItemResult;
+    LayerInfo: array of TLayerItemInfo;
     movingItemStart: boolean;
     movingItem: TBGRABitmap;
     movingItemSourceIndex: integer;
@@ -85,6 +92,7 @@ type
     procedure UpdateImage;
     procedure OnImageChangedHandler(AEvent: TLazPaintImageObservationEvent);
     procedure SelectBlendOp;
+    procedure DoScrollVertically(AAmount: integer);
   public
     { public declarations }
     LazPaintInstance: TLazPaintCustomInstance;
@@ -95,6 +103,8 @@ type
     property CompletelyResizeable: boolean read FCompletelyResizeable write SetCompletelyResizeable;
   end;
 
+var TFLayerStack_CustomDPI: integer = 96;
+
 implementation
 
 uses BGRAFillInfo,LCScaleDPI,uresourcestrings,ublendop, uimage, utool, BGRAText, BGRAThumbnail,
@@ -179,10 +189,10 @@ end;
 
 procedure TFLayerStack.FormCreate(Sender: TObject);
 begin
-  ScaleControl(Self,OriginalDPI);
+  ScaleControl(Self,OriginalDPI,TFLayerStack_CustomDPI,TFLayerStack_CustomDPI);
   FCompletelyResizeable:= true;
   Position := poDesigned;
-  ZoomFactor := 1;
+  ZoomFactor := TFLayerStack_CustomDPI/96;
   ScrollPos := point(0,0);
   VolatileHorzScrollBar := nil;
   VolatileVertScrollBar := nil;
@@ -226,37 +236,36 @@ var iconSize: integer;
     images: TImageList;
 begin
   LazPaintInstance.Image.OnImageChanged.AddObserver(@OnImageChangedHandler);
-  iconSize := LazPaintInstance.Config.DefaultIconSize(16);
-  if iconSize <> 16 then
-  begin
-    images := LazPaintInstance.Icons[iconSize];
-    ToolBar1.Images := images;
-    ToolBar1.ButtonWidth := images.Width+1;
-    ToolBar1.ButtonHeight := images.Height+1;
-    ToolBar1.Height := ToolBar1.ButtonHeight+1;
-    ToolBar2.Images := images;
-    ToolBar2.ButtonWidth := images.Width+1;
-    ToolBar2.ButtonHeight := images.Height+1;
-    ToolBar2.Height := ToolBar1.ButtonHeight+1;
-    ToolBar3.Images := images;
-    ToolBar3.ButtonWidth := images.Width+1;
-    ToolBar3.ButtonHeight := images.Height+1;
-    ToolBar3.Height := ToolBar1.ButtonHeight+1;
-
-    ClientWidth := ToolBar2.ButtonCount * (ToolBar2.ButtonWidth+1) + 5 + Toolbar2.Left;
-    Constraints.MinWidth := Width;
-
-    ToolBar1.Width := ToolBar1.ButtonCount * (ToolBar1.ButtonWidth+1) + 8;
-    ToolBar3.Width := ToolBar3.ButtonCount * (ToolBar3.ButtonWidth+1) + 5;
-
-    ComboBox_BlendOp.Left := Toolbar3.Left+Toolbar3.Width;
-    Toolbar1.Left := ClientWidth-Toolbar1.Width-6;
-    ComboBox_BlendOp.Width := Toolbar1.Left - ComboBox_BlendOp.Left;
-    Toolbar2.Top := Toolbar1.Top + Toolbar1.Height;
-    Panel1.Height := Toolbar2.Top+Toolbar2.Height+2;
+  iconSize := DoScaleX(16, 96, TFLayerStack_CustomDPI);
+
+  images := LazPaintInstance.Icons[iconSize];
+  ToolBar1.Images := images;
+  ToolBar1.ButtonWidth := images.Width+DoScaleX(4,96,TFLayerStack_CustomDPI);
+  ToolBar1.ButtonHeight := images.Height+DoScaleY(4,96,TFLayerStack_CustomDPI);
+  ToolBar1.Height := ToolBar1.ButtonHeight+1;
+  ToolBar2.Images := images;
+  ToolBar2.ButtonWidth := images.Width+DoScaleX(4,96,TFLayerStack_CustomDPI);
+  ToolBar2.ButtonHeight := images.Height+DoScaleY(4,96,TFLayerStack_CustomDPI);
+  ToolBar2.Height := ToolBar1.ButtonHeight+1;
+  ToolBar3.Images := images;
+  ToolBar3.ButtonWidth := images.Width+DoScaleX(4,96,TFLayerStack_CustomDPI);
+  ToolBar3.ButtonHeight := images.Height+DoScaleY(4,96,TFLayerStack_CustomDPI);
+  ToolBar3.Height := ToolBar1.ButtonHeight+1;
+
+  ClientWidth := ToolBar2.ButtonCount * (ToolBar2.ButtonWidth+1) + 5 + Toolbar2.Left;
+  Constraints.MinWidth := Width;
+
+  ToolBar1.Width := ToolBar1.ButtonCount * (ToolBar1.ButtonWidth+1) + 8;
+  ToolBar3.Width := ToolBar3.ButtonCount * (ToolBar3.ButtonWidth+1) + 5;
+
+  ComboBox_BlendOp.Left := Toolbar3.Left+Toolbar3.Width;
+  Toolbar1.Left := ClientWidth-Toolbar1.Width-6;
+  ComboBox_BlendOp.Width := Toolbar1.Left - ComboBox_BlendOp.Left;
+  Toolbar2.Top := Toolbar1.Top + Toolbar1.Height;
+  Panel1.Height := Toolbar2.Top+Toolbar2.Height+2;
+
+  ComboBox_BlendOp.Font.Height := -FontEmHeightSign * ((images.Height-2) * 6 div 10 + 2);
 
-    ComboBox_BlendOp.Font.Height := -FontEmHeightSign * ((images.Height-2) * 7 div 10);
-  end;
   if Toolbar2.Top < ComboBox_BlendOp.Top + ComboBox_BlendOp.Height then
     Toolbar2.Top := ComboBox_BlendOp.Top + ComboBox_BlendOp.Height;
   if Toolbar2.Top+Toolbar2.Height+2 > Panel1.Height then
@@ -264,15 +273,9 @@ begin
 end;
 
 procedure TFLayerStack.TimerScrollTimer(Sender: TObject);
-var prevY: integer;
 begin
-  prevY := scrollPos.Y;
-  ScrollPos.Y += TimerScrollDeltaY;
-  if ScrollPos.Y < 0 then ScrollPos.Y := 0;
-  if ScrollPos.Y > MaxScrollPos.Y then ScrollPos.Y := MaxScrollPos.Y;
-  movingItemMouseOrigin.Y -= ScrollPos.Y-prevY;
   TimerScroll.Enabled := False;
-  BGRALayerStack.RedrawBitmap;
+  DoScrollVertically(TimerScrollDeltaY);
 end;
 
 procedure TFLayerStack.ToolBlendOpClick(Sender: TObject);
@@ -304,7 +307,7 @@ procedure TFLayerStack.HandleChangeLayerOpacity(X, Y: integer);
 var newOpacity: integer;
 begin
   if (changingLayerOpacity <> -1) and (changingLayerOpacity <= high(LayerInfo)) then
-  with LayerInfo[changingLayerOpacity] do
+  with LayerInfo[changingLayerOpacity].RightPart do
   begin
     if changingLayerOpacity >= LazPaintInstance.Image.NbLayers then exit;
     newOpacity := round((X-(OpacityBar.left+1))/(OpacityBar.right-OpacityBar.left-2)*255);
@@ -398,7 +401,6 @@ end;
 procedure TFLayerStack.ComputeLayout(ABitmap: TBGRABitmap);
 var i,temp,h: integer;
 begin
-  interruptors:= nil;
   LayerInfo := nil;
   LayerRectWidth := round(100*zoomFactor);
   LayerRectHeight := round(50*zoomFactor);
@@ -547,21 +549,56 @@ var i: integer;
   layerPos: TPoint;
   lSelected: boolean;
   y: integer;
-  clipping, rKind: TRect;
+  clipping: TRect;
   lColor, lColorTrans: TBGRAPixel;
 
-  procedure DrawKind(AClass: TBGRALayerOriginalAny);
+  procedure DrawKindUnknown(rKind: TRect; out HintText: string);
   var
     eb: TEasyBezierCurve;
     w: single;
     i: integer;
     m: TAffineMatrix;
   begin
-    if AClass = TBGRALayerImageOriginal then
+    eb := EasyBezierCurve([PointF(0.25,0.25),PointF(0.32,0.07),PointF(0.5,0),PointF(0.68,0.07),PointF(0.75,0.20),
+                           PointF(0.75,0.30),PointF(0.70,0.40),PointF(0.5,0.5),PointF(0.5,0.70)],False,cmCurve);
+    m := AffineMatrixTranslation(rKind.Left,rKind.Top)*AffineMatrixScale(rKind.Width,rKind.Height);
+    for i := 0 to eb.PointCount-1 do eb.Point[i] := m*eb.Point[i];
+    w := max(1,rKind.Height/10);
+    Bitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w, true);
+    Bitmap.FillEllipseAntialias((rKind.Left+rKind.Right)/2, rKind.Bottom - 1 - (w-1)/2, w*0.6,w*0.6, lColor);
+    HintText := rsUnknownOriginal;
+  end;
+
+  procedure DrawKind(AClass: TBGRALayerOriginalAny; rKind: TRect; out HintText: string);
+  var
+    eb: TEasyBezierCurve;
+    w: single;
+    i: integer;
+    m: TAffineMatrix;
+    r: TRect;
+  begin
+    if AClass = nil then
     begin
       Bitmap.Rectangle(rKind, lColor,lColorTrans, dmDrawWithTransparency);
       Bitmap.HorizLine(rKind.Left+1,rKind.Top+(rKind.Height-1) div 2,rKind.Right-2, lColor, dmDrawWithTransparency);
       Bitmap.VertLine(rKind.Left+(rKind.Width-1) div 2,rKind.Top+1,rKind.Bottom-2, lColor, dmDrawWithTransparency);
+      HintText := rsRasterLayer;
+    end else
+    if AClass = TBGRALayerImageOriginal then
+    begin
+      r := rect(rKind.Left,(rKind.Top+rKind.Bottom) div 2, (rKind.Left+rKind.Right) div 2, rKind.Bottom);
+      w := max(1,rKind.Height/10);
+      Bitmap.Rectangle(r, lColor,lColorTrans, dmDrawWithTransparency);
+      eb := EasyBezierCurve([PointF(rKind.Left,rKind.Top+rKind.Height/4),
+                             PointF(rKind.Left+rKind.Width/2,rKind.Top+rKind.Height/4),
+                             PointF(rKind.Left+rKind.Width*3/4,rKind.Top+rKind.Height/2),
+                             PointF(rKind.Left+rKind.Width*3/4,rKind.Bottom)],False,cmCurve);
+      Bitmap.Arrow.StartAsClassic;
+      Bitmap.Arrow.EndAsClassic;
+      Bitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w);
+      Bitmap.Arrow.StartAsNone;
+      Bitmap.Arrow.EndAsNone;
+      HintText := rsTransformedRasterLayer;
     end else
     if AClass = TBGRALayerSVGOriginal then
     begin
@@ -577,16 +614,7 @@ var i: integer;
       eb.CurveMode[eb.PointCount-2] := cmAngle;
       for i := 0 to eb.PointCount-1 do eb.Point[i] := m*eb.Point[i];
       Bitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w, true);
-    end else
-    if AClass = nil then
-    begin
-      eb := EasyBezierCurve([PointF(0.25,0.25),PointF(0.32,0.07),PointF(0.5,0),PointF(0.68,0.07),PointF(0.75,0.20),
-                             PointF(0.75,0.30),PointF(0.70,0.40),PointF(0.5,0.5),PointF(0.5,0.70)],False,cmCurve);
-      m := AffineMatrixTranslation(rKind.Left,rKind.Top)*AffineMatrixScale(rKind.Width,rKind.Height);
-      for i := 0 to eb.PointCount-1 do eb.Point[i] := m*eb.Point[i];
-      w := max(1,rKind.Height/10);
-      Bitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w, true);
-      Bitmap.FillEllipseAntialias((rKind.Left+rKind.Right)/2, rKind.Bottom - 1 - (w-1)/2, w*0.6,w*0.6, lColor);
+      HintText := rsVectorialLayer;
     end else
     begin
       Bitmap.EllipseAntialias(rKind.Left+rKind.Width / 3, rKind.Top+rKind.Height / 3,rKind.Width / 3,rKind.Height / 3,
@@ -594,6 +622,7 @@ var i: integer;
       Bitmap.DrawPolygonAntialias([PointF(rKind.Left+rKind.Width/4,rKind.Bottom),
                                    PointF(rKind.Left+rKind.Width/2,rKind.Top+rKind.Height/4),
                                    PointF(rKind.Right,rKind.Bottom)],lColor,1, lColorTrans);
+      HintText := rsVectorialLayer;
     end;
   end;
 
@@ -605,7 +634,6 @@ begin
   end;
   layerPos.x := -Offset.X;
   layerPos.y := -Offset.Y;
-  SetLength(interruptors,LazPaintInstance.Image.NbLayers);
   SetLength(LayerInfo,LazPaintInstance.Image.NbLayers);
   clipping := EmptyRect;
   for i := LazPaintInstance.Image.NbLayers-1 downto 0 do
@@ -626,7 +654,7 @@ begin
         end;
         if UpdateItem <> -1 then clipping := rect(layerPos.X,layerPos.Y,layerPos.X+StackWidth,layerPos.Y+LayerRectHeight);
 
-        interruptors[i] := RectWithSize(layerPos.X+InterruptorWidth div 5,layerpos.Y+(LayerRectHeight-5*InterruptorHeight div 2) div 2,
+        LayerInfo[i].VisibleCheckbox := RectWithSize(layerPos.X+InterruptorWidth div 5,layerpos.Y+(LayerRectHeight-5*InterruptorHeight div 2) div 2,
                                         InterruptorWidth, InterruptorHeight);
 
         if (layerpos.Y+LayerRectHeight > 0) and (layerpos.Y < Bitmap.Height) then
@@ -639,9 +667,9 @@ begin
           lColorTrans := lColor;
           lColorTrans.alpha := lColorTrans.alpha div 3;
 
-          Bitmap.Rectangle(interruptors[i],lColor,dmDrawWithTransparency);
+          Bitmap.Rectangle(LayerInfo[i].VisibleCheckbox,lColor,dmDrawWithTransparency);
           if LayerVisible[i] then
-          with interruptors[i] do
+          with LayerInfo[i].VisibleCheckbox do
           begin
             Bitmap.DrawPolyLineAntialias(Bitmap.ComputeBezierSpline([
 
@@ -652,17 +680,17 @@ begin
                   PointF(right-2,top-2))]),lColor,1.5);
           end;
 
-          rKind := interruptors[i];
-          rKind.Offset(0, InterruptorHeight*3 div 2);
+          LayerInfo[i].KindIcon := LayerInfo[i].VisibleCheckbox;
+          LayerInfo[i].KindIcon.Offset(0, InterruptorHeight*3 div 2);
           if LayerOriginalDefined[i] then
           begin
             if LayerOriginalKnown[i] then
-              DrawKind(LayerOriginalClass[i])
+              DrawKind(LayerOriginalClass[i], LayerInfo[i].KindIcon, LayerInfo[i].KindIconHint)
             else
-              DrawKind(nil);
+              DrawKindUnknown(LayerInfo[i].KindIcon, LayerInfo[i].KindIconHint);
           end
           else
-            DrawKind(TBGRALayerImageOriginal);
+            DrawKind(nil, LayerInfo[i].KindIcon, LayerInfo[i].KindIconHint);
 
           inc(layerPos.X,InterruptorWidth);
           if movingItemStart and (i= movingItemSourceIndex) then
@@ -677,7 +705,7 @@ begin
             movingItemStart:= false;
           end;
 
-          LayerInfo[i] := DrawLayerItem(Bitmap,layerPos,i,lSelected);
+          LayerInfo[i].RightPart := DrawLayerItem(Bitmap,layerPos,i,lSelected);
           dec(layerPos.X,InterruptorWidth);
         end;
       end;
@@ -699,12 +727,12 @@ begin
       y := movingItemOrigin.Y + movingItemMousePos.Y - movingItemMouseOrigin.Y - Offset.Y;
       if y < 0 then
       begin
-        timerScrollDeltaY := -movingItem.Height div 10;
+        timerScrollDeltaY := -movingItem.Height div 3;
         TimerScroll.Enabled := true;
       end else
       if y + movingItem.Height > Bitmap.Height then
       begin
-        timerScrollDeltaY := +movingItem.Height div 10;
+        timerScrollDeltaY := +movingItem.Height div 3;
         TimerScroll.Enabled := true;
       end;
       Bitmap.PutImage(movingItemOrigin.X + movingItemMousePos.X - movingItemMouseOrigin.X - Offset.X,
@@ -777,6 +805,17 @@ begin
     LazPaintInstance.ToolManager.ToolPopup(tpmBlendOpBackground);
 end;
 
+procedure TFLayerStack.DoScrollVertically(AAmount: integer);
+var prevY: integer;
+begin
+  prevY := scrollPos.Y;
+  ScrollPos.Y += AAmount;
+  if ScrollPos.Y < 0 then ScrollPos.Y := 0;
+  if ScrollPos.Y > MaxScrollPos.Y then ScrollPos.Y := MaxScrollPos.Y;
+  movingItemMouseOrigin.Y -= ScrollPos.Y-prevY;
+  BGRALayerStack.DiscardBitmap;
+end;
+
 procedure TFLayerStack.ComboBox_BlendOpChange(Sender: TObject);
 var blendOp: TBlendOperation;
   itemStr: string;
@@ -825,8 +864,8 @@ begin
       BGRALayerStack.RedrawBitmap;
       exit;
     end;
-    for i := 0 to high(interruptors) do
-      if PtInRect(Point(x,Y),interruptors[i]) then
+    for i := 0 to high(LayerInfo) do
+      if PtInRect(Point(x,Y),LayerInfo[i].VisibleCheckbox) then
       begin
         if i < LazPaintInstance.Image.NbLayers then
         begin
@@ -838,13 +877,13 @@ begin
         exit;
       end;
     for i := 0 to high(LayerInfo) do
-      if IsPointInPolygon(LayerInfo[i].PreviewPts,pointF(x,y),true) then
+      if IsPointInPolygon(LayerInfo[i].RightPart.PreviewPts,pointF(x,y),true) then
       begin
         HandleSelectLayer(i,x,y);
         exit;
       end;
     for i := 0 to high(LayerInfo) do
-      if PtInRect(Point(x,Y),LayerInfo[i].NameRect) then
+      if PtInRect(Point(x,Y),LayerInfo[i].RightPart.NameRect) then
       begin
         if i < LazPaintInstance.Image.NbLayers then
         begin
@@ -865,8 +904,8 @@ begin
         exit;
       end;
     for i := 0 to high(LayerInfo) do
-      if PtInRect(Point(x,Y),LayerInfo[i].OpacityBar) or PtInRect(Point(x+4,Y),LayerInfo[i].OpacityBar) or
-        PtInRect(Point(x-4,Y),LayerInfo[i].OpacityBar) then
+      if PtInRect(Point(x,Y),LayerInfo[i].RightPart.OpacityBar) or PtInRect(Point(x+4,Y),LayerInfo[i].RightPart.OpacityBar) or
+        PtInRect(Point(x-4,Y),LayerInfo[i].RightPart.OpacityBar) then
       begin
         if i < LazPaintInstance.Image.NbLayers then
         begin
@@ -880,11 +919,13 @@ end;
 
 procedure TFLayerStack.BGRALayerStackMouseMove(Sender: TObject;
   Shift: TShiftState; X, Y: Integer);
+var
+  i: Integer;
 begin
   if movingItem <> nil then
   begin
     movingItemMousePos := point(X,Y);
-    BGRALayerStack.RedrawBitmap;
+    BGRALayerStack.DiscardBitmap;
     exit;
   end;
   if ((VolatileVertScrollBar <> nil) and VolatileVertScrollBar.MouseMove(X,Y)) or
@@ -892,7 +933,7 @@ begin
   begin
     if VolatileHorzScrollBar <> nil then ScrollPos.X := VolatileHorzScrollBar.Position;
     if VolatileVertScrollBar <> nil then ScrollPos.Y := VolatileVertScrollBar.Position;
-    BGRALayerStack.RedrawBitmap;
+    BGRALayerStack.DiscardBitmap;
     exit;
   end;
   if changingLayerOpacity <> -1 then
@@ -900,6 +941,14 @@ begin
     HandleChangeLayerOpacity(X,Y);
     exit;
   end;
+  for i := 0 to high(LayerInfo) do
+    if LayerInfo[i].KindIcon.Contains(Point(X,Y)) then
+    begin
+      BGRALayerStack.Hint := LayerInfo[i].KindIconHint;
+      BGRALayerStack.ShowHint:= true;
+      exit;
+    end;
+  BGRALayerStack.ShowHint:= false;
 end;
 
 procedure TFLayerStack.BGRALayerStackMouseUp(Sender: TObject;
@@ -938,6 +987,13 @@ begin
   end;
 end;
 
+procedure TFLayerStack.BGRALayerStackMouseWheel(Sender: TObject;
+  Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
+  var Handled: Boolean);
+begin
+  DoScrollVertically(round(-WheelDelta*ZoomFactor*50/120));
+end;
+
 {$R *.lfm}
 
 end.

+ 14 - 3
lazpaint/umenu.pas

@@ -5,7 +5,8 @@ unit UMenu;
 interface
 
 uses
-  Classes, SysUtils, ActnList, Forms, Menus, UTool, LCLType, ExtCtrls, UConfig;
+  Classes, SysUtils, ActnList, Forms, Menus, UTool, LCLType, ExtCtrls, UConfig,
+  Controls;
 
 type
 
@@ -19,6 +20,7 @@ type
     FToolbars: array of TPanel;
     FToolbarsHeight : integer;
     FToolbarBackground: TPanel;
+    FImageList: TImageList;
   protected
     procedure AddMenus(AMenu: TMenuItem; AActionList: TActionList; AActionsCommaText: string; AIndex: integer = -1); overload;
     procedure AddMenus(AMenuName: string; AActionsCommaText: string); overload;
@@ -33,12 +35,13 @@ type
     procedure ArrangeToolbars(ClientWidth: integer);
     procedure RepaintToolbar;
     property ToolbarsHeight: integer read FToolbarsHeight;
+    property ImageList: TImageList read FImageList write FImageList;
   end;
 
 implementation
 
 uses UResourceStrings, BGRAUTF8, LazPaintType, LCScaleDPI, ComCtrls, Graphics,
-  Spin, StdCtrls, BGRAText, Controls;
+  Spin, StdCtrls, BGRAText;
 
 { TMainFormMenu }
 
@@ -216,7 +219,7 @@ begin
   AddMenus('MenuFilter', 'MenuRadialBlur,FilterBlurMotion,FilterBlurCustom,FilterPixelate,-,FilterSharpen,FilterSmooth,FilterNoise,FilterMedian,FilterClearType,FilterClearTypeInverse,FilterFunction,-,FilterContour,FilterEmboss,FilterPhong,-,FilterSphere,FilterTwirl,FilterCylinder');
   AddMenus('MenuRadialBlur',  'FilterBlurBox,FilterBlurFast,FilterBlurRadial,FilterBlurCorona,FilterBlurDisk');
   AddMenus('MenuColors', 'ColorCurves,ColorPosterize,ColorColorize,ColorShiftColors,FilterComplementaryColor,ColorIntensity,-,ColorLightness,FilterNegative,FilterLinearNegative,FilterNormalize,FilterGrayscale');
-  AddMenus('MenuTool',   'ToolHand,ToolHotSpot,ToolColorPicker,-,ToolPen,ToolBrush,ToolEraser,-,ToolRect,ToolEllipse,ToolPolygon,ToolSpline,-,ToolFloodFill,ToolGradient,ToolPhong,-,ToolText,ToolDeformation,ToolTextureMapping,ToolClone');
+  AddMenus('MenuTool',   'ToolHand,ToolHotSpot,ToolColorPicker,-,ToolPen,ToolBrush,ToolEraser,ToolFloodFill,ToolClone,-,ToolRect,ToolEllipse,ToolPolygon,ToolSpline,ToolGradient,ToolPhong,ToolText,-,ToolDeformation,ToolTextureMapping');
   AddMenus('MenuRender', 'RenderPerlinNoise,RenderCyclicPerlinNoise,-,RenderWater,RenderCustomWater,RenderSnowPrint,RenderWood,RenderWoodVertical,RenderMetalFloor,RenderPlastik,RenderStone,RenderRoundStone,RenderMarble,RenderCamouflage,-,RenderClouds,FilterRain');
   AddMenus('MenuHelp',   'HelpIndex,-,HelpAbout');
   for i := 0 to high(FMainMenus) do
@@ -224,6 +227,9 @@ begin
 
   ApplyShortcuts;
 
+  if Assigned(FImageList) then
+    FActionList.Images := FImageList;
+
   tbHeightOrig := DoScaleY(26,OriginalDPI);
   tbHeight := tbHeightOrig;
   for i := 0 to high(FToolbars) do
@@ -235,7 +241,12 @@ begin
     for j := 0 to ControlCount-1 do
     begin
       if Controls[j] is TToolBar then
+      begin
         Controls[j].Color := clBtnFace;
+        if assigned(FImageList) then TToolbar(Controls[j]).Images := FImageList;
+        TToolbar(Controls[j]).ButtonWidth := TToolbar(Controls[j]).Images.Width+ScaleX(6, 96);
+        TToolbar(Controls[j]).ButtonHeight := TToolbar(Controls[j]).Images.Height+ScaleY(6, 96);
+      end;
       if Controls[j] is TSpinEdit then
       begin
         if Controls[j].Top + Controls[j].Height+4 > tbHeight then

+ 6 - 6
lazpaint/uphongfilter.pas

@@ -51,12 +51,12 @@ type
     FInitializing: boolean;
     FCenter: TPointF;
     FHeightMap: TBGRABitmap;
-    function GetCurrentLightPos: TPoint;
+    function GetCurrentLightPos: TPointF;
     procedure PreviewNeeded;
     function ComputeFilteredLayer: TBGRABitmap;
   public
     FilterConnector: TFilterConnector;
-    property CurrentLightPos: TPoint read GetCurrentLightPos;
+    property CurrentLightPos: TPointF read GetCurrentLightPos;
   end;
 
 function ShowPhongFilterDlg(AFilterConnector: TObject):boolean;
@@ -196,10 +196,10 @@ begin
   Button_OK.Enabled := false;
 end;
 
-function TFPhongFilter.GetCurrentLightPos: TPoint;
+function TFPhongFilter.GetCurrentLightPos: TPointF;
 begin
-  result := Point(round(FCenter.X*FilterConnector.ActiveLayer.Width),
-    round(FCenter.Y*FilterConnector.ActiveLayer.Height));
+  result := PointF(FCenter.X*FilterConnector.ActiveLayer.Width,
+    FCenter.Y*FilterConnector.ActiveLayer.Height);
 end;
 
 procedure ScanLineMapLightness(psrc,pdest: PBGRAPixel; count: integer);
@@ -293,7 +293,7 @@ begin
   shader := TPhongShading.Create;
   shader.AmbientFactor := 0.5;
   shader.NegativeDiffusionFactor := 0.15;
-  shader.LightPosition := CurrentLightPos;
+  shader.LightPositionF := CurrentLightPos;
   shader.LightPositionZ := FilterConnector.LazPaintInstance.ToolManager.ToolLightAltitude;
   if FHeightMap = nil then
   begin

+ 6 - 0
lazpaint/uresourcestrings.pas

@@ -30,6 +30,11 @@ resourcestring
   rsEnterLayerName='Enter layer name:';
   rsFileExtensionNotSupported='This file extension is not supported.';
   rsFileFormatNotRecognized='The file format has not been recognized.';
+  rsErrorLoadingOriginal='Error while loading original however layer can be rasterized.';
+  rsRasterLayer = 'Raster layer';
+  rsTransformedRasterLayer = 'Transformed raster layer';
+  rsVectorialLayer = 'Vectorial layer';
+  rsUnknownOriginal = 'Unknown original';
   rsFileName = 'Filename';
   rsFileSize = 'Size';
   rsFileType = 'Type';
@@ -167,6 +172,7 @@ resourcestring
   rsMoreThanOneFile='You are trying to open more than one file. How would you like these files to be opened?';
   rsOpenFilesAsLayers='Open files as layers in a single image';
   rsTooManyLayers='Too many layers';
+  rsTooManyShapesInLayer='Too many shapes in layer';
   rsAddToImageList='Add files to the image processing list';
   rsOpenFirstFileOnly='Open the first file only';
   rsLayeredImage = 'Layered image';

+ 55 - 10
lazpaint/ustatetype.pas

@@ -5,7 +5,8 @@ unit UStateType;
 interface
 
 uses
-  Types, Classes, SysUtils, BGRABitmap, BGRABitmapTypes, BGRALayers, fgl;
+  Types, Classes, SysUtils, BGRABitmap, BGRABitmapTypes, BGRALayers,
+  BGRALayerOriginal, fgl;
 
 const MinSizeToCompress = 512; //set to 1 if you want always compression
 const MinSerializedSize = 16384;
@@ -120,6 +121,8 @@ type
     procedure Init(Image1,Image2: TBGRABitmap; {%H-}AChangeRect: TRect); virtual;
     function SerializeCompressedData: boolean;
     procedure UnserializeCompressedData;
+  protected
+    function CreateNew(AWidth,AHeight: integer): TBGRABitmap; virtual; abstract;
   public
     SizeBefore, SizeAfter: TSize;
     constructor Create(Image1,Image2: TBGRABitmap; AChangeRect: TRect); overload;
@@ -145,6 +148,8 @@ type
     function GetIsIdentity: boolean; override;
     procedure Decompress;
     procedure Init(Image1,Image2: TBGRABitmap; AChangeRect: TRect); override;
+  protected
+    function CreateNew(AWidth, AHeight: integer): TBGRABitmap; override;
   public
     procedure ApplyInPlace(ADest: TBGRABitmap; {%H-}AReverse: boolean); override;
     function Compress: boolean; override;
@@ -166,6 +171,8 @@ type
     function GetIsIdentity: boolean; override;
     procedure Decompress;
     procedure Init(Image1,Image2: TBGRABitmap; AChangeRect: TRect); override;
+  protected
+    function CreateNew(AWidth, AHeight: integer): TBGRABitmap; override;
   public
     procedure ApplyInPlace(ADest: TBGRABitmap; {%H-}AReverse: boolean); override;
     function Compress: boolean; override;
@@ -193,12 +200,14 @@ type
     FInfo: TLayerInfo;
     FIndex: integer;
     FOriginalData: TMemoryStream;
-    FOriginalKnown: boolean;
+    FOriginalBitmapStored: boolean;
     FOriginalRenderStatus: TOriginalRenderStatus;
     FOriginalMatrix: TAffineMatrix;
     FOriginalDraft: boolean;
   public
     constructor Create(ALayeredImage: TBGRALayeredBitmap; AIndex: integer);
+    constructor Create(ALayeredImage: TBGRALayeredBitmap; AIndex: integer;
+                       AAlwaysStoreBitmap: boolean);
     procedure Restore(ALayeredImage: TBGRALayeredBitmap);
     procedure Replace(ALayeredImage: TBGRALayeredBitmap);
     property LayerIndex: integer read FIndex;
@@ -322,7 +331,12 @@ var
   DestSize: TSize;
 begin
   if (self = nil) or IsIdentity then
-    result := ASource.Duplicate as TBGRABitmap
+  begin
+    if ASource = nil then
+      result := nil
+    else
+      result := ASource.Duplicate as TBGRABitmap
+  end
   else
   begin
     if AReverse then DestSize := SizeBefore else
@@ -332,7 +346,7 @@ begin
       result := nil
     else
     begin
-      result := TBGRABitmap.Create(Destsize.cx,Destsize.cy);
+      result := CreateNew(Destsize.cx,Destsize.cy);
       if ASource <> nil then
         result.PutImage(0,0,ASource,dmSet);
       ApplyInPlace(result, AReverse);
@@ -345,8 +359,9 @@ function TCustomImageDiff.ApplyCanCreateNew(ASource: TBGRABitmap;
 begin
   if (self = nil) or IsIdentity then exit(ASource); //keep
 
-  if (SizeAfter.cx <> SizeBefore.cx) or
-     (SizeAfter.cy <> SizeBefore.cy) then
+  if (ASource = nil) or
+     ((SizeAfter.cx <> SizeBefore.cx) or
+     (SizeAfter.cy <> SizeBefore.cy)) then
      exit(ApplyInNew(ASource, AReverse))
   else
   begin
@@ -581,6 +596,11 @@ begin
   end;
 end;
 
+function TGrayscaleImageDiff.CreateNew(AWidth, AHeight: integer): TBGRABitmap;
+begin
+  result := TBGRABitmap.Create(AWidth,AHeight, BGRABlack);
+end;
+
 procedure TGrayscaleImageDiff.ApplyInPlace(ADest: TBGRABitmap; AReverse: boolean);
 var
   pdest: PBGRAPixel;
@@ -884,6 +904,11 @@ begin
   end;
 end;
 
+function TImageDiff.CreateNew(AWidth, AHeight: integer): TBGRABitmap;
+begin
+  result := TBGRABitmap.Create(AWidth,AHeight);
+end;
+
 procedure TImageDiff.ApplyInPlace(ADest: TBGRABitmap; AReverse: boolean);
 var
   pdest: PDWord;
@@ -1001,15 +1026,35 @@ end;
 
 constructor TStoredLayer.Create(ALayeredImage: TBGRALayeredBitmap;
   AIndex: integer);
+var
+  {%H-}orig: TBGRALayerCustomOriginal;
+  alwaysStoreBitmap: Boolean;
+begin
+  alwaysStoreBitmap := false;
+  if (ALayeredImage.LayerOriginalGuid[AIndex]<>GUID_NULL) and
+    ALayeredImage.LayerOriginalKnown[AIndex] then
+  begin
+    try
+      orig := ALayeredImage.LayerOriginal[AIndex];
+    except
+      on ex:exception do
+        alwaysStoreBitmap:= true;
+    end;
+  end;
+  Create(ALayeredImage, AIndex, alwaysStoreBitmap);
+end;
+
+constructor TStoredLayer.Create(ALayeredImage: TBGRALayeredBitmap;
+  AIndex: integer; AAlwaysStoreBitmap: boolean);
 begin
   FIndex := AIndex;
   FInfo := GetLayerInfo(ALayeredImage, AIndex);
   if ALayeredImage.LayerOriginalGuid[AIndex]<>GUID_NULL then
   begin
-    FOriginalKnown := ALayeredImage.LayerOriginalKnown[AIndex];
+    FOriginalBitmapStored := AAlwaysStoreBitmap or not ALayeredImage.LayerOriginalKnown[AIndex];
     FOriginalRenderStatus:= ALayeredImage.LayerOriginalRenderStatus[AIndex];
 
-    if FOriginalKnown then
+    if not FOriginalBitmapStored then
       inherited Create(nil)
     else
       inherited Create(ALayeredImage.LayerBitmap[AIndex]);
@@ -1034,7 +1079,7 @@ begin
     FOriginalData.Position:= 0;
     idxOrig := ALayeredImage.AddOriginalFromStream(FOriginalData, true);
 
-    if FOriginalKnown then
+    if not FOriginalBitmapStored then
     begin
       tempIdx := ALayeredImage.AddLayerFromOriginal(ALayeredImage.Original[idxOrig].Guid, FOriginalMatrix);
       ALayeredImage.RenderLayerFromOriginal(tempIdx, FOriginalDraft);
@@ -1060,7 +1105,7 @@ begin
   begin
     FOriginalData.Position:= 0;
     idxOrig := ALayeredImage.AddOriginalFromStream(FOriginalData, true);
-    if FOriginalKnown then
+    if not FOriginalBitmapStored then
     begin
       ALayeredImage.LayerOriginalGuid[FIndex] := ALayeredImage.OriginalGuid[idxOrig];
       ALayeredImage.LayerOriginalMatrix[FIndex] := FOriginalMatrix;

+ 54 - 59
lazpaint/utool.pas

@@ -46,12 +46,12 @@ type
   protected
     FManager: TToolManager;
     FLastToolDrawingLayer: TBGRABitmap;
-    FBackupDrawingLayerBounds: TRect;
-    FBackupDrawingLayer: TBGRABitmap;
+    FValidating, FCanceling: boolean;
     function GetAction: TLayerAction; virtual;
     function GetIdleAction: TLayerAction; virtual;
     function GetIsSelectingTool: boolean; virtual; abstract;
     function FixSelectionTransform: boolean; virtual;
+    function FixLayerOffset: boolean; virtual;
     function DoToolDown(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF; rightBtn: boolean): TRect; virtual;
     function DoToolMove(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF): TRect; virtual;
     procedure DoToolMoveAfter(pt: TPoint; ptF: TPointF); virtual;
@@ -95,6 +95,8 @@ type
     property LayerOffset : TPoint read GetLayerOffset;
     property LastToolDrawingLayer: TBGRABitmap read FLastToolDrawingLayer;
     property StatusText: string read GetStatusText;
+    property Validating: boolean read FValidating;
+    property Canceling: boolean read FCanceling;
   end;
 
   { TReadonlyTool }
@@ -185,7 +187,6 @@ type
     ToolArrowSize: TPointF;
     ToolJoinStyle: TPenJoinStyle;
     ToolSplineStyle: TSplineStyle;
-    ToolSplineEasyBezier: boolean;
     ToolPenStyle: TPenStyle;
     ToolPerspectiveRepeat,ToolPerspectiveTwoPlanes: boolean;
     ToolDeformationGridMoveWithoutDeformation: boolean;
@@ -193,7 +194,7 @@ type
     ToolTextFont: TFont;
     ToolTextBlur: single;
     ToolTextShadowOffset: TPoint;
-    ToolLightPosition: TPoint;
+    ToolLightPosition: TPointF;
     ToolLightAltitude: integer;
     ToolShapeAltitude: integer;
     ToolShapeBorderSize: integer;
@@ -411,27 +412,13 @@ begin
 end;
 
 function TGenericTool.GetAction: TLayerAction;
-var
-  layer: TBGRABitmap;
 begin
   if not Assigned(FAction) then
   begin
-    FAction := Manager.Image.CreateAction(not IsSelectingTool And Manager.Image.SelectionMaskEmpty);
+    FAction := Manager.Image.CreateAction(not IsSelectingTool And Manager.Image.SelectionMaskEmpty,
+                                          IsSelectingTool or not Manager.Image.SelectionMaskEmpty);
     FAction.OnTryStop := @OnTryStop;
     FAction.ChangeBoundsNotified:= true;
-    if IsSelectingTool or not Manager.Image.SelectionMaskEmpty then
-    begin
-      FAction.ApplySelectionTransform;
-      layer := GetToolDrawingLayer;
-      if Assigned(layer) then
-      begin
-        if layer = Manager.Image.SelectionMaskReadonly then
-          FBackupDrawingLayerBounds:= layer.GetImageBounds(cGreen)
-        else
-          FBackupDrawingLayerBounds:= layer.GetImageBounds;
-        FBackupDrawingLayer := layer.GetPart(FBackupDrawingLayerBounds) as TBGRABitmap;
-      end;
-    end;
   end;
   result := FAction;
 end;
@@ -452,6 +439,11 @@ begin
   result:= true;
 end;
 
+function TGenericTool.FixLayerOffset: boolean;
+begin
+  result:= true;
+end;
+
 function TGenericTool.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF; rightBtn: boolean): TRect;
 begin
@@ -485,7 +477,6 @@ end;
 destructor TGenericTool.Destroy;
 begin
   FAction.Free;
-  FBackupDrawingLayer.Free;
   inherited Destroy;
 end;
 
@@ -493,26 +484,41 @@ procedure TGenericTool.ValidateAction;
 begin
   if Assigned(FAction) then
   begin
+    FValidating := true;
     FAction.Validate;
+    FValidating := false;
     FreeAndNil(FAction);
-    FreeAndNil(FBackupDrawingLayer);
   end;
 end;
 
 procedure TGenericTool.ValidateActionPartially;
 begin
-  if Assigned(FAction) then FAction.PartialValidate;
+  if Assigned(FAction) then
+  begin
+    FValidating := true;
+    FAction.PartialValidate;
+    FValidating := false;
+  end;
 end;
 
 procedure TGenericTool.CancelAction;
 begin
   if FAction <> nil then
+  begin
+    FCanceling := true;
     FreeAndNil(FAction);
+    FCanceling := false;
+  end;
 end;
 
 procedure TGenericTool.CancelActionPartially;
 begin
-  if Assigned(FAction) then FAction.PartialCancel;
+  if Assigned(FAction) then
+  begin
+    FCanceling := true;
+    FAction.PartialCancel;
+    FCanceling := false;
+  end;
 end;
 
 procedure TGenericTool.BeforeGridSizeChange;
@@ -574,8 +580,11 @@ begin
   ptF := PointF(x,y);
   if toolDest = Manager.Image.CurrentLayerReadOnly then
   begin
-    ptF.x -= LayerOffset.x;
-    ptF.y -= LayerOffset.y;
+    if FixLayerOffset then
+    begin
+      ptF.x -= LayerOffset.x;
+      ptF.y -= LayerOffset.y;
+    end;
   end else if FixSelectionTransform and ((toolDest = Manager.Image.SelectionMaskReadonly)
     or (toolDest = Manager.Image.SelectionLayerReadonly)) and
       IsAffineMatrixInversible(Manager.Image.SelectionTransform) then
@@ -599,8 +608,11 @@ begin
   toolDest.PenStyle := Manager.ToolPenStyle;
   if toolDest = Manager.Image.CurrentLayerReadOnly then
   begin
-    ptF.x -= LayerOffset.x;
-    ptF.y -= LayerOffset.y;
+    if FixLayerOffset then
+    begin
+      ptF.x -= LayerOffset.x;
+      ptF.y -= LayerOffset.y;
+    end;
   end else if FixSelectionTransform and ((toolDest = Manager.Image.SelectionMaskReadonly)
     or (toolDest = Manager.Image.SelectionLayerReadonly)) and
       IsAffineMatrixInversible(Manager.Image.SelectionTransform) then
@@ -614,8 +626,11 @@ var
   pt: TPoint;
   ptF: TPointF;
 begin
-  x -= LayerOffset.x;
-  y -= LayerOffset.y;
+  if FixLayerOffset then
+  begin
+    x -= LayerOffset.x;
+    y -= LayerOffset.y;
+  end;
   pt := Point(round(x),round(y));
   ptF := PointF(x,y);
   DoToolMoveAfter(pt,ptF);
@@ -685,30 +700,13 @@ begin
 end;
 
 procedure TGenericTool.RestoreBackupDrawingLayer;
-var
-  layer: TBGRABitmap;
 begin
   if Assigned(FAction) then
   begin
-    if Assigned(FBackupDrawingLayer) then
-    begin
-      layer:= GetToolDrawingLayer;
-      if Assigned(layer) then
-      begin
-        if layer = Manager.Image.SelectionMaskReadonly then
-          layer.Fill(BGRABlack)
-        else
-          layer.FillTransparent;
-        layer.PutImage(FBackupDrawingLayerBounds.Left,FBackupDrawingLayerBounds.Top, FBackupDrawingLayer, dmSet);
-        Action.NotifyChange(layer, rect(0,0,layer.Width,layer.Height));
-      end;
-    end else
-    begin
-      if IsSelectingTool then
-        Action.RestoreSelectionMask
-      else
-        Action.RestoreDrawingLayer;
-    end;
+    if IsSelectingTool then
+      Action.RestoreSelectionMask
+    else
+      Action.RestoreDrawingLayer;
   end;
 end;
 
@@ -918,13 +916,12 @@ begin
   ToolFloodFillOptionProgressive := true;
   ToolLineCap := pecRound;
   ToolJoinStyle := pjsRound;
-  ToolArrowStart := 'None';
-  ToolArrowEnd := 'None';
+  ToolArrowStart := 'none';
+  ToolArrowEnd := 'none';
   ToolArrowSize := PointF(2,2);
   ToolPenStyle := psSolid;
   ToolEraserAlpha := 255;
-  ToolSplineStyle := ssRoundOutside;
-  ToolSplineEasyBezier := true;
+  ToolSplineStyle := ssEasyBezier;
   ToolTextOutline := False;
   ToolTextShadow := false;
   ToolTextFont := TFont.Create;
@@ -935,7 +932,7 @@ begin
   ToolTextBlur := 4;
   ToolTextShadowOffset := Point(5,5);
   ToolTextOutlineWidth := 2;
-  ToolLightPosition := Point(0,0);
+  ToolLightPosition := PointF(0,0);
   ToolLightAltitude := 100;
   ToolShapeAltitude := 50;
   ToolShapeBorderSize := 20;
@@ -1299,7 +1296,6 @@ function TToolManager.ToolDown(X, Y: single; ARightBtn: boolean;
 var changed: TRect;
 begin
   SetPressure(APressure);
-  Image.DraftOriginal := true;
   if ToolCanBeUsed then
     changed := currentTool.ToolDown(X,Y,ARightBtn)
   else
@@ -1425,7 +1421,6 @@ end;
 function TToolManager.ToolUp: boolean;
 var changed: TRect;
 begin
-  Image.DraftOriginal := false;
   if ToolCanBeUsed then
     changed := currentTool.ToolUp
   else
@@ -1514,7 +1509,7 @@ end;
 
 function TToolManager.GetRenderBounds(VirtualScreenWidth, VirtualScreenHeight: integer): TRect;
 begin
-  if ToolCanBeUsed then
+  if ToolCanBeUsed and not currentTool.Validating and not currentTool.Canceling then
     result := currentTool.Render(nil,VirtualScreenWidth,VirtualScreenHeight, @InternalBitmapToVirtualScreen)
   else
     result := EmptyRect;

+ 196 - 58
lazpaint/utoolbasic.pas

@@ -73,26 +73,35 @@ type
   { TVectorialTool }
 
   TVectorialTool = class(TGenericTool)
+  private
+    function GetIsHandDrawing: boolean;
+    function GetIsIdle: boolean;
   protected
     FShape: TVectorShape;
     FSwapColor: boolean;
     FQuickDefine: Boolean;
-    FQuickDefineStartPoint: TPointF;
+    FQuickDefineStartPoint, FQuickDefineEndPoint: TPointF;
     FQuickSquare: boolean;
     FPreviousUpdateBounds, FPreviousEditorBounds: TRect;
     FEditor: TBGRAOriginalEditor;
     FShiftState: TShiftState;
     FRightDown, FLeftDown: boolean;
     FLastPos: TPointF;
+    FLastShapeTransform: TAffineMatrix;
+    FUseOriginal: boolean;
+    function AlwaysRasterizeShape: boolean; virtual;
     function CreateShape: TVectorShape; virtual; abstract;
+    function UseOriginal: boolean; virtual;
     function GetCustomShapeBounds(ADestBounds: TRect; AMatrix: TAffineMatrix; {%H-}ADraft: boolean): TRect; virtual;
     procedure DrawCustomShape(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); virtual;
-    procedure AssignShapeStyle; virtual;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); virtual;
     procedure QuickDefineShape(AStart,AEnd: TPointF); virtual;
     function RoundCoordinate(ptF: TPointF): TPointF; virtual;
     function GetIsSelectingTool: boolean; override;
     function UpdateShape(toolDest: TBGRABitmap): TRect; virtual;
+    function VectorTransform: TAffineMatrix;
     procedure UpdateCursor(ACursor: TOriginalEditorCursor);
+    function FixLayerOffset: boolean; override;
     function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; {%H-}ptF: TPointF; rightBtn: boolean): TRect; override;
     function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; {%H-}ptF: TPointF): TRect; override;
     function DoToolUpdate({%H-}toolDest: TBGRABitmap): TRect; override;
@@ -102,6 +111,7 @@ type
     function SlowShape: boolean; virtual;
     procedure QuickDefineEnd; virtual;
     procedure OnTryStop({%H-}sender: TCustomLayerAction); override;
+    procedure UpdateUseOriginal;
   public
     function ValidateShape: TRect;
     function CancelShape: TRect;
@@ -111,6 +121,8 @@ type
     function ToolKeyPress(var key: TUTF8Char): TRect; override;
     function ToolKeyUp(var key: Word): TRect; override;
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
+    property IsIdle: boolean read GetIsIdle;
+    property IsHandDrawing: boolean read GetIsHandDrawing;
     destructor Destroy; override;
   end;
 
@@ -131,7 +143,8 @@ type
 implementation
 
 uses Types, Graphics, ugraph, Controls, LazPaintType,
-  UResourceStrings, BGRATransform, Math, BGRAPen, LCVectorRectShapes;
+  UResourceStrings, BGRATransform, Math, BGRAPen, LCVectorRectShapes,
+  uimagediff, UStateType, ULoading;
 
 { TVectorialTool }
 
@@ -139,13 +152,14 @@ procedure TVectorialTool.ShapeChange(ASender: TObject; ABounds: TRectF);
 var
   toolDest: TBGRABitmap;
   r: TRect;
+  matrix: TAffineMatrix;
 begin
   toolDest := GetToolDrawingLayer;
-  with ABounds do r := rect(floor(Left),floor(Top),ceil(Right),ceil(Bottom));
+  matrix := VectorTransform;
+  r := (matrix*TAffineBox.AffineBox(ABounds)).RectBounds;
   UpdateShape(toolDest);
   Action.NotifyChange(toolDest, r);
-  with FShape.GetRenderBounds(rect(0,0,toolDest.Width,toolDest.Height),
-       AffineMatrixIdentity,[]) do
+  with FShape.GetRenderBounds(rect(0,0,toolDest.Width,toolDest.Height),matrix,[]) do
     FPreviousUpdateBounds := rect(floor(Left),floor(Top),ceil(Right),ceil(Bottom));
   if IsRectEmpty(r) then ShapeEditingChange(ASender);
 end;
@@ -206,22 +220,103 @@ begin
   ValidateShape;
 end;
 
+procedure TVectorialTool.UpdateUseOriginal;
+begin
+  if not IsSelectingTool and Manager.Image.SelectionMaskEmpty and
+     Manager.Image.LayerOriginalDefined[Manager.Image.CurrentLayerIndex] and
+     Manager.Image.LayerOriginalKnown[Manager.Image.CurrentLayerIndex] and
+    (Manager.Image.LayerOriginalClass[Manager.Image.CurrentLayerIndex] = TVectorOriginal) then
+    FUseOriginal:= Assigned(Manager.Image.LayerOriginal[Manager.Image.CurrentLayerIndex])
+  else
+    FUseOriginal:= false;
+end;
+
 function TVectorialTool.ValidateShape: TRect;
+var
+  diff: TComposedImageDifference;
+  layerId: LongInt;
+  replaceDiff: TReplaceLayerByVectorOriginalDifference;
+  transf: TAffineMatrix;
+  addDiff: TAddShapeToVectorOriginalDifference;
+  rF: TRectF;
 begin
-  ValidateActionPartially;
-  FreeAndNil(FShape);
-  Cursor := crDefault;
-  result := OnlyRenderChange;
+  if Assigned(FShape) then
+  begin
+    FShape.OnChange:= nil;
+    FShape.OnEditingChange:= nil;
+    if not AlwaysRasterizeShape and Manager.Image.SelectionMaskEmpty then
+    begin
+      CancelAction;
+      if FShape.Usermode = vsuCreate then FShape.Usermode:= vsuEdit;
+      rF := FShape.GetRenderBounds(rect(0,0,Manager.Image.Width,Manager.Image.Height), VectorTransform);
+      if rF.IntersectsWith(rectF(0,0,Manager.Image.Width,Manager.Image.Height)) then
+      begin
+        layerId := Manager.Image.LayerId[Manager.Image.CurrentLayerIndex];
+        if UseOriginal then
+        begin
+          addDiff := TAddShapeToVectorOriginalDifference.Create(Manager.Image.CurrentState,layerId,FShape);
+          Manager.Image.AddUndo(addDiff);
+        end
+        else
+        begin
+          transf := VectorTransform;
+          diff := TComposedImageDifference.Create;
+          replaceDiff := TReplaceLayerByVectorOriginalDifference.Create(Manager.Image.CurrentState,Manager.Image.CurrentLayerIndex,
+                           Manager.Image.LayerOriginalClass[Manager.Image.CurrentLayerIndex]=TVectorOriginal);
+          diff.Add(replaceDiff);
+          transf := AffineMatrixInverse(Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex])*transf;
+          FShape.Transform(transf);
+          addDiff := TAddShapeToVectorOriginalDifference.Create(Manager.Image.CurrentState,layerId,FShape);
+          diff.Add(addDiff);
+          Manager.Image.AddUndo(diff);
+        end;
+        Manager.Image.ImageMayChange(addDiff.ChangingBounds);
+      end else
+        FShape.Free;
+      FShape := nil;
+      FEditor.Clear;
+    end else
+    begin
+      ValidateActionPartially;
+      FreeAndNil(FShape);
+      FEditor.Clear;
+    end;
+    Cursor := crDefault;
+    result := OnlyRenderChange;
+    UpdateUseOriginal;
+  end else
+    result := EmptyRect;
 end;
 
 function TVectorialTool.CancelShape: TRect;
 begin
   CancelActionPartially;
   FreeAndNil(FShape);
+  FEditor.Clear;
   Cursor := crDefault;
   result := OnlyRenderChange;
 end;
 
+function TVectorialTool.GetIsHandDrawing: boolean;
+begin
+  result := Assigned(FShape) and (FQuickDefine or (FShape.Usermode = vsuCreate));
+end;
+
+function TVectorialTool.GetIsIdle: boolean;
+begin
+  result := FShape = nil;
+end;
+
+function TVectorialTool.AlwaysRasterizeShape: boolean;
+begin
+  result := false;
+end;
+
+function TVectorialTool.UseOriginal: boolean;
+begin
+  result := FUseOriginal;
+end;
+
 function TVectorialTool.GetCustomShapeBounds(ADestBounds: TRect; AMatrix: TAffineMatrix; ADraft: boolean): TRect;
 begin
   with FShape.GetRenderBounds(ADestBounds,AMatrix,[]) do
@@ -233,17 +328,19 @@ begin
   FShape.Render(ADest,AMatrix,ADraft);
 end;
 
-procedure TVectorialTool.AssignShapeStyle;
+procedure TVectorialTool.AssignShapeStyle(AMatrix: TAffineMatrix);
 var
   f: TVectorShapeFields;
+  zoom: Single;
 begin
+  zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
   f:= FShape.Fields;
   if vsfPenFill in f then
   begin
     if Manager.ToolOptionDrawShape then
     begin
       if (not (vsfBackFill in f) or not Manager.ToolOptionFillShape) and (Manager.GetToolTexture <> nil) then
-        FShape.PenFill.SetTexture(Manager.GetToolTexture,AffineMatrixIdentity,Manager.ToolTextureOpacity)
+        FShape.PenFill.SetTexture(Manager.GetToolTexture,AMatrix,Manager.ToolTextureOpacity)
       else
       begin
         if FSwapColor then
@@ -254,7 +351,7 @@ begin
     end else
       FShape.PenFill.Clear;
   end;
-  if vsfPenWidth in f then FShape.PenWidth := Manager.ToolPenWidth;
+  if vsfPenWidth in f then FShape.PenWidth := zoom*Manager.ToolPenWidth;
   if vsfPenStyle in f Then FShape.PenStyle := PenStyleToBGRA(Manager.ToolPenStyle);
   if vsfJoinStyle in f then FShape.JoinStyle:= Manager.ToolJoinStyle;
   if vsfBackFill in f then
@@ -270,7 +367,8 @@ begin
         else
           FShape.BackFill.SetSolid(Manager.ToolBackColor);
       end;
-    end;
+    end else
+      FShape.BackFill.Clear;
   end;
 end;
 
@@ -302,7 +400,7 @@ var
 begin
   result := FPreviousUpdateBounds;
   RestoreBackupDrawingLayer;
-  matrix := AffineMatrixIdentity;
+  matrix := VectorTransform;
   draft := (FRightDown or FLeftDown) and SlowShape;
   newBounds := GetCustomShapeBounds(toolDest.ClipRect,matrix,draft);
   result := RectUnion(result, newBounds);
@@ -312,6 +410,14 @@ begin
   FPreviousUpdateBounds := newBounds;
 end;
 
+function TVectorialTool.VectorTransform: TAffineMatrix;
+begin
+  if not UseOriginal then
+    result := AffineMatrixIdentity
+  else
+    result := Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex];
+end;
+
 procedure TVectorialTool.UpdateCursor(ACursor: TOriginalEditorCursor);
 begin
   case ACursor of
@@ -330,6 +436,11 @@ begin
   end;
 end;
 
+function TVectorialTool.FixLayerOffset: boolean;
+begin
+  Result:= false;
+end;
+
 function TVectorialTool.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF; rightBtn: boolean): TRect;
 var
@@ -337,43 +448,54 @@ var
   cur: TOriginalEditorCursor;
   handled: boolean;
 begin
+  result := EmptyRect;
   FRightDown := rightBtn;
   FLeftDown := not rightBtn;
   FLastPos := ptF;
   if Assigned(FShape) then
   begin
-    viewPt := FEditor.Matrix*ptF;
+    with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
+      viewPt := FEditor.Matrix*AffineMatrixInverse(VectorTransform)*AffineMatrixTranslation(X,Y)*ptF;
     FEditor.MouseDown(rightBtn, FShiftState, viewPt.X,viewPt.Y, cur, handled);
     if not handled and Assigned(FShape) then
       FShape.MouseDown(rightBtn, FShiftState, ptF.X,ptF.Y, cur, handled);
     UpdateCursor(cur);
-    result := EmptyRect;
     if handled then exit
-    else
-    begin
-      ValidateAction;
-      FreeAndNil(FShape);
-    end;
+    else result := RectUnion(result, ValidateShape);
   end;
 
   if FShape=nil then
   begin
-    FSwapColor:= rightBtn;
-    FShape := CreateShape;
-    FQuickDefine := true;
-    FQuickDefineStartPoint := RoundCoordinate(ptF);
-    AssignShapeStyle;
-    QuickDefineShape(FQuickDefineStartPoint,FQuickDefineStartPoint);
-    FShape.OnChange:= @ShapeChange;
-    FShape.OnEditingChange:=@ShapeEditingChange;
-    result := UpdateShape(toolDest);
+    if UseOriginal and
+      ((Manager.Image.LayerOriginal[Manager.Image.CurrentLayerIndex] as TVectorOriginal).ShapeCount >= 10) then
+    begin
+      MessagePopup(rsTooManyShapesInLayer, 3000);
+    end
+    else
+    begin
+      toolDest := GetToolDrawingLayer;
+      FSwapColor:= rightBtn;
+      FShape := CreateShape;
+      FQuickDefine := true;
+      FQuickDefineStartPoint := RoundCoordinate(ptF);
+      FQuickDefineEndPoint := FQuickDefineStartPoint;
+      FShape.BeginUpdate;
+        QuickDefineShape(FQuickDefineStartPoint,FQuickDefineEndPoint);
+        FLastShapeTransform := AffineMatrixInverse(VectorTransform);
+        FShape.Transform(FLastShapeTransform);
+        AssignShapeStyle(FLastShapeTransform);
+      FShape.EndUpdate;
+      FShape.OnChange:= @ShapeChange;
+      FShape.OnEditingChange:=@ShapeEditingChange;
+      result := RectUnion(result, UpdateShape(toolDest));
+    end;
   end;
 end;
 
 function TVectorialTool.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF): TRect;
 var
-  secondCoord, s: TPointF;
+  s: TPointF;
   avg: single;
   viewPt: TPointF;
   handled: boolean;
@@ -382,42 +504,55 @@ begin
   FLastPos := ptF;
   if FQuickDefine then
   begin
-    secondCoord := RoundCoordinate(ptF);
+    FQuickDefineEndPoint := RoundCoordinate(ptF);
     if FQuickSquare then
     begin
-      s := secondCoord-FQuickDefineStartPoint;
+      s := FQuickDefineEndPoint-FQuickDefineStartPoint;
       avg := sqrt(abs(s.x*s.y));
-      if s.x > 0 then secondCoord.x := FQuickDefineStartPoint.x + avg else secondCoord.x := FQuickDefineStartPoint.x - avg;
-      if s.y > 0 then secondCoord.y := FQuickDefineStartPoint.y + avg else secondCoord.y := FQuickDefineStartPoint.y - avg;
+      if s.x > 0 then FQuickDefineEndPoint.x := FQuickDefineStartPoint.x + avg else FQuickDefineEndPoint.x := FQuickDefineStartPoint.x - avg;
+      if s.y > 0 then FQuickDefineEndPoint.y := FQuickDefineStartPoint.y + avg else FQuickDefineEndPoint.y := FQuickDefineStartPoint.y - avg;
     end;
-    QuickDefineShape(FQuickDefineStartPoint, secondCoord);
+    FShape.BeginUpdate;
+      QuickDefineShape(FQuickDefineStartPoint, FQuickDefineEndPoint);
+      FLastShapeTransform := AffineMatrixInverse(VectorTransform);
+      FShape.Transform(FLastShapeTransform);
+      AssignShapeStyle(FLastShapeTransform);
+    FShape.EndUpdate;
     result := OnlyRenderChange;
   end else
   begin
-    viewPt := FEditor.Matrix*ptF;
+    with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
+      viewPt := FEditor.Matrix*AffineMatrixInverse(VectorTransform)*AffineMatrixTranslation(X,Y)*ptF;
     FEditor.MouseMove(FShiftState, viewPt.X,viewPt.Y, cur, handled);
     if not handled and Assigned(FShape) then
       FShape.MouseMove(FShiftState, ptF.X,ptF.Y, cur, handled);
     UpdateCursor(cur);
-    result := EmptyRect;
+    if handled then result := OnlyRenderChange
+    else result := EmptyRect;
   end;
 end;
 
 function TVectorialTool.DoToolUpdate(toolDest: TBGRABitmap): TRect;
 begin
-  if Assigned(FShape) then AssignShapeStyle;
+  if Assigned(FShape) then
+  begin
+    FShape.BeginUpdate;
+    AssignShapeStyle(FLastShapeTransform);
+    FShape.EndUpdate;
+  end;
   result := EmptyRect;
 end;
 
 constructor TVectorialTool.Create(AManager: TToolManager);
 begin
   inherited Create(AManager);
-  Action.ChangeBoundsNotified:= true;
+  UpdateUseOriginal;
   FPreviousUpdateBounds := EmptyRect;
   FEditor := TVectorOriginalEditor.Create(nil);
   FEditor.GridMatrix := AffineMatrixScale(0.5,0.5);
   FEditor.Focused := true;
   FPreviousEditorBounds := EmptyRect;
+  FLastShapeTransform := AffineMatrixIdentity;
 end;
 
 function TVectorialTool.ToolUp: TRect;
@@ -443,7 +578,7 @@ begin
     UpdateCursor(cur);
     result := EmptyRect;
   end;
-  if SlowShape then
+  if SlowShape and Assigned(FShape) then
     result := UpdateShape(GetToolDrawingLayer);
 end;
 
@@ -545,26 +680,29 @@ function TVectorialTool.Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth,
 var
   orig, xAxis, yAxis: TPointF;
 begin
-  orig := BitmapToVirtualScreen(PointF(0,0));
-  xAxis := BitmapToVirtualScreen(PointF(1,0));
-  yAxis := BitmapToVirtualScreen(PointF(0,1));
-  FEditor.Matrix := AffineMatrix(xAxis-orig,yAxis-orig,orig);
-  FEditor.Clear;
-  if Assigned(FShape) then FShape.ConfigureEditor(FEditor);
-  if Assigned(VirtualScreen) then
-    Result:= FEditor.Render(VirtualScreen, rect(0,0,VirtualScreen.Width,VirtualScreen.Height))
-  else
-    Result:= FEditor.GetRenderBounds(rect(0,0,VirtualScreenWidth,VirtualScreenHeight));
-  FPreviousEditorBounds := result
+  if Assigned(FShape) then
+  begin
+    orig := BitmapToVirtualScreen(PointF(0,0));
+    xAxis := BitmapToVirtualScreen(PointF(1,0));
+    yAxis := BitmapToVirtualScreen(PointF(0,1));
+    FEditor.Matrix := AffineMatrix(xAxis-orig,yAxis-orig,orig)*VectorTransform;
+    FEditor.Clear;
+    if Assigned(FShape) then FShape.ConfigureEditor(FEditor);
+    if Assigned(VirtualScreen) then
+      Result:= FEditor.Render(VirtualScreen, rect(0,0,VirtualScreen.Width,VirtualScreen.Height))
+    else
+      Result:= FEditor.GetRenderBounds(rect(0,0,VirtualScreenWidth,VirtualScreenHeight));
+  end else
+  begin
+    result := EmptyRect;
+    FEditor.Clear;
+  end;
+  FPreviousEditorBounds := result;
 end;
 
 destructor TVectorialTool.Destroy;
 begin
-  if Assigned(FShape) then
-  begin
-    ValidateAction;
-    FShape.Free;
-  end;
+  if Assigned(FShape) then ValidateShape;
   FEditor.Free;
   inherited Destroy;
 end;

+ 24 - 4
lazpaint/utoolfloodfill.pas

@@ -26,7 +26,8 @@ type
   TToolGradient = class(TVectorialTool)
   protected
     function CreateShape: TVectorShape; override;
-    procedure AssignShapeStyle; override;
+    procedure DrawCustomShape(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
     procedure QuickDefineShape(AStart,AEnd: TPointF); override;
     function SlowShape: boolean; override;
     function GetStatusText: string; override;
@@ -34,20 +35,38 @@ type
 
 implementation
 
-uses ugraph, LazPaintType, BGRAGradientScanner, LCVectorRectShapes;
+uses ugraph, LazPaintType, BGRAGradientScanner, LCVectorRectShapes,
+  math, BGRATransform;
 
 { TToolGradient }
 
 function TToolGradient.CreateShape: TVectorShape;
 begin
   result := TRectShape.Create(nil);
-  result.QuickDefine(PointF(-0.5,-0.5),PointF(Manager.Image.Width-0.5,Manager.Image.Height-0.5));
   result.PenFill.Clear;
   result.BackFill.SetGradient(TBGRALayerGradientOriginal.Create,true);
   result.Usermode := vsuEditBackFill;
 end;
 
-procedure TToolGradient.AssignShapeStyle;
+procedure TToolGradient.DrawCustomShape(ADest: TBGRABitmap;
+  AMatrix: TAffineMatrix; ADraft: boolean);
+var
+  temp: TBGRABitmap;
+begin
+  if ADraft and (ADest.NbPixels > 384*384) then
+  begin
+    temp := TBGRABitmap.Create(0,0);
+    temp.SetSize(min(384,ADest.Width),min(384,ADest.Height));
+    FShape.BackFill.Gradient.Render(temp,
+      AffineMatrixScale(temp.Width/ADest.Width,
+                        temp.Height/ADest.Height)*AMatrix, ADraft);
+    ADest.StretchPutImage(rect(0,0,ADest.Width,Adest.Height),temp,dmSet);
+    temp.Free;
+  end else
+    FShape.BackFill.Gradient.Render(ADest,AMatrix,ADraft);
+end;
+
+procedure TToolGradient.AssignShapeStyle(AMatrix: TAffineMatrix);
 begin
   with FShape.BackFill.Gradient do
   begin
@@ -72,6 +91,7 @@ end;
 
 procedure TToolGradient.QuickDefineShape(AStart, AEnd: TPointF);
 begin
+  FShape.QuickDefine(PointF(-0.5,-0.5),PointF(Manager.Image.Width-0.5,Manager.Image.Height-0.5));
   FShape.BackFill.Gradient.Origin := AStart;
   FShape.BackFill.Gradient.XAxis := AEnd;
 end;

+ 367 - 455
lazpaint/utoollayer.pas

@@ -37,12 +37,11 @@ type
       BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; override;
   end;
 
-  { TToolRotateLayer }
+  { TToolTransformLayer }
 
-  TToolRotateLayer = class(TGenericTool)
+  TToolTransformLayer = class(TGenericTool)
   private
-    function GetActualAngle: single;
-    function GetOriginalLayerBounds: TRect;
+    function GetInitialLayerBounds: TRect;
     function GetTransformCenter: TPointF;
     procedure SetTransformCenter(AValue: TPointF);
     procedure NeedOriginal;
@@ -56,21 +55,26 @@ type
     FTransformCenter: TPointF;
     FTransformCenterDefined: boolean;
     FPreviousTransformCenter: TPointF;
-    FAngle,FActualAngle,FPreviousActualAngle: single;
     FPreviousFilter: TResampleFilter;
     FTransforming: boolean;
     FPreviousMousePos: TPointF;
     FCtrlDown: boolean;
     FLastUpdateRect: TRect;
     FLastUpdateRectDefined: boolean;
+    FOriginalBounds: TRect;
+    FOriginalBoundsDefined: boolean;
     function GetIsSelectingTool: boolean; override;
     function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
       rightBtn: boolean): TRect; override;
     function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect;
       override;
-    function UpdateTransform: TRect;
     procedure CancelTransform;
     procedure ValidateTransform;
+    function TransformOk: boolean; virtual; abstract;
+    function UpdateTransform: TRect; virtual; abstract;
+    procedure TransformCenterChanged; virtual; abstract;
+    function MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean; virtual; abstract;
+    function CtrlChangesTransform: boolean; virtual; abstract;
     property TransformCenter: TPointF read GetTransformCenter write SetTransformCenter;
     function GetAction: TLayerAction; override;
     function DoGetToolDrawingLayer: TBGRABitmap; override;
@@ -87,102 +91,248 @@ type
 
   { TToolZoomLayer }
 
-  TToolZoomLayer = class(TGenericTool)
+  TToolZoomLayer = class(TToolTransformLayer)
   private
+    FZoom,FActualZoom,FPreviousActualZoom: single;
     function GetActualZoom: single;
-    function GetOriginalLayerBounds: TRect;
-    function GetTransformCenter: TPointF;
-    procedure SetTransformCenter(AValue: TPointF);
-    procedure NeedOriginal;
   protected
-    FOriginalInit: boolean;
-    FBackupLayer: TReplaceLayerByImageOriginalDifference;
-    FInitialOriginalMatrix: TAffineMatrix;
-    FInitialLayerBounds: TRect;
-    FInitialLayerBoundsDefined: boolean;
+    function TransformOk: boolean; override;
+    function UpdateTransform: TRect; override;
+    procedure TransformCenterChanged; override;
+    function MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean; override;
+    function CtrlChangesTransform: boolean; override;
+  public
+    constructor Create(AManager: TToolManager); override;
+  end;
 
-    FTransformCenter: TPointF;
-    FTransformCenterDefined: boolean;
-    FPreviousTransformCenter: TPointF;
-    FZoom,FActualZoom,FPreviousActualZoom: single;
-    FPreviousFilter: TResampleFilter;
-    FTransforming: boolean;
-    FPreviousMousePos: TPointF;
-    FCtrlDown: boolean;
-    FLastUpdateRect: TRect;
-    FLastUpdateRectDefined: boolean;
-    function GetIsSelectingTool: boolean; override;
-    function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
-      rightBtn: boolean): TRect; override;
-    function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect;
-      override;
-    function UpdateTransform: TRect;
-    procedure CancelTransform;
-    procedure ValidateTransform;
-    property TransformCenter: TPointF read GetTransformCenter write SetTransformCenter;
-    function GetAction: TLayerAction; override;
-    function DoGetToolDrawingLayer: TBGRABitmap; override;
+  { TToolRotateLayer }
+
+  TToolRotateLayer = class(TToolTransformLayer)
+  private
+    FAngle,FActualAngle,FPreviousActualAngle: single;
+    function GetActualAngle: single;
+  protected
+    function TransformOk: boolean; override;
+    function UpdateTransform: TRect; override;
+    procedure TransformCenterChanged; override;
+    function MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean; override;
+    function CtrlChangesTransform: boolean; override;
   public
     constructor Create(AManager: TToolManager); override;
-    destructor Destroy; override;
-    function ToolKeyDown(var key: Word): TRect; override;
-    function ToolKeyUp(var key: Word): TRect; override;
-    function ToolUp: TRect; override;
-    function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth,
-      {%H-}VirtualScreenHeight: integer;
-      BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; override;
   end;
 
 implementation
 
 uses LazPaintType, ugraph, LCLType, Types, BGRALayerOriginal;
 
-{ TToolRotateLayer }
+const
+  VeryBigValue = maxLongInt div 2;
 
-function TToolRotateLayer.GetActualAngle: single;
+{ TToolMoveLayer }
+
+function TToolMoveLayer.GetIsSelectingTool: boolean;
 begin
-  if FCtrlDown then
-    result := round(FAngle/15)*15
-  else
-    result := FAngle;
+  result := false;
+end;
+
+function TToolMoveLayer.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
+  ptF: TPointF; rightBtn: boolean): TRect;
+var idx: integer;
+begin
+  result := EmptyRect;
+  if not handMoving then
+  begin
+    handMoving := true;
+    handOrigin := pt;
+    if not FStartLayerOffsetDefined then
+    begin
+      FStartLayerOffsetDefined := true;
+      idx := Manager.Image.CurrentLayerIndex;
+      NeedLayerBounds;
+      FStartLayerOffset := Manager.Image.LayerOffset[idx];
+      FStartLayerMatrix := Manager.Image.LayerOriginalMatrix[idx];
+    end;
+    if UseOriginal then Manager.Image.DraftOriginal := true;
+  end;
+end;
+
+function TToolMoveLayer.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
+  ptF: TPointF): TRect;
+var idx: integer;
+  prev: TPoint;
+begin
+  if handMoving and ((handOrigin.X <> pt.X) or (handOrigin.Y <> pt.Y)) then
+  begin
+    idx := Manager.Image.CurrentLayerIndex;
+    if UseOriginal then
+    begin
+      Manager.Image.LayerOriginalMatrix[idx] :=
+          AffineMatrixTranslation(pt.X-HandOrigin.X,pt.Y-HandOrigin.Y)*Manager.Image.LayerOriginalMatrix[idx];
+      result := OnlyRenderChange;
+    end else
+    begin
+      prev := Manager.Image.LayerOffset[idx];
+      Manager.Image.SetLayerOffset(idx, Point(prev.X+pt.X-HandOrigin.X,
+                                         prev.Y+pt.Y-HandOrigin.Y), FLayerBounds);
+      result := OnlyRenderChange;
+    end;
+  end else
+    result := EmptyRect;
+end;
+
+procedure TToolMoveLayer.DoToolMoveAfter(pt: TPoint; ptF: TPointF);
+begin
+  if handMoving then handOrigin := pt;
+end;
+
+function TToolMoveLayer.UseOriginal: boolean;
+begin
+  with Manager.Image do
+    result := LayerOriginalDefined[CurrentLayerIndex] and
+              LayerOriginalKnown[CurrentLayerIndex];
+end;
+
+procedure TToolMoveLayer.NeedLayerBounds;
+var
+  idx: Integer;
+begin
+  GetAction;
+  idx := Manager.Image.CurrentLayerIndex;
+  if not FLayerBoundsDefined then
+  begin
+    if UseOriginal then
+    begin
+      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;
+      if FLayerBounds.Bottom = VeryBigValue then FLayerBounds.Bottom := Manager.Image.Height;
+    end
+    else
+      FLayerBounds := Manager.Image.LayerBitmap[idx].GetImageBounds;
+    FLayerBoundsDefined := true;
+  end;
+end;
+
+function TToolMoveLayer.GetAction: TLayerAction;
+begin
+  result := GetIdleAction;
+end;
+
+function TToolMoveLayer.DoGetToolDrawingLayer: TBGRABitmap;
+begin
+  Result:= Manager.Image.CurrentLayerReadOnly;   //do not modify layer data directly and ignore selection
+end;
+
+function TToolMoveLayer.ToolUp: TRect;
+begin
+  handMoving := false;
+  result := EmptyRect;
+  if UseOriginal then Manager.Image.DraftOriginal := false;
+end;
+
+function TToolMoveLayer.ToolKeyDown(var key: Word): TRect;
+var idx: integer;
+begin
+  if key = VK_RETURN then
+  begin
+    Manager.QueryExitTool;
+    result := EmptyRect;
+    Key := 0;
+  end
+  else if key = VK_ESCAPE then
+  begin
+    if FStartLayerOffsetDefined then
+    begin
+      idx := Manager.Image.CurrentLayerIndex;
+      if UseOriginal then
+        Manager.Image.LayerOriginalMatrix[idx] := FStartLayerMatrix
+      else
+        Manager.Image.SetLayerOffset(idx, FStartLayerOffset, FLayerBounds);
+      result := OnlyRenderChange;
+    end else
+      result := EmptyRect;
+    Manager.QueryExitTool;
+    Key := 0;
+  end else
+    Result:=inherited ToolKeyDown(key);
+end;
+
+function TToolMoveLayer.Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth,
+  VirtualScreenHeight: integer;
+  BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
+var
+  idx, i: integer;
+  m: TAffineMatrix;
+  ab: TAffineBox;
+  ptsF: ArrayOfTPointF;
+  pts: array of TPoint;
+begin
+  NeedLayerBounds;
+
+  if UseOriginal then
+  begin
+    idx := Manager.Image.CurrentLayerIndex;
+    m := Manager.Image.LayerOriginalMatrix[idx];
+    with Manager.Image.LayerOffset[idx] do
+      m := AffineMatrixTranslation(-x,-y)*m;
+  end else m := AffineMatrixIdentity;
+
+  ab := TAffineBox.AffineBox(BitmapToVirtualScreen(m*PointF(FLayerBounds.Left-0.499,FLayerBounds.Top-0.499)),
+            BitmapToVirtualScreen(m*PointF(FLayerBounds.Right-0.501,FLayerBounds.Top-0.499)),
+            BitmapToVirtualScreen(m*PointF(FLayerBounds.Left-0.499,FLayerBounds.Bottom-0.501)));
+  ptsF := ab.AsPolygon;
+  setlength(pts, length(ptsF));
+  for i := 0 to high(pts) do
+    pts[i] := ptsF[i].Round;
+
+  result := TRect.Union(pts);
+  result.Inflate(1,1);
+
+  if Assigned(VirtualScreen) then
+    virtualScreen.DrawpolygonAntialias(pts,BGRA(230,255,230,255),BGRA(0,0,0,255),FrameDashLength);
 end;
 
-function TToolRotateLayer.GetOriginalLayerBounds: TRect;
+{ TToolTransformLayer }
+
+function TToolTransformLayer.GetInitialLayerBounds: TRect;
 begin
   if not FInitialLayerBoundsDefined then
   begin
     FInitialLayerBounds := GetToolDrawingLayer.GetImageBounds;
+    with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
+      FInitialLayerBounds.Offset(X,Y);
     FInitialLayerBoundsDefined := true;
   end;
   result := FInitialLayerBounds;
 end;
 
-function TToolRotateLayer.GetTransformCenter: TPointF;
+function TToolTransformLayer.GetTransformCenter: TPointF;
 var bounds: TRect;
 begin
   if not FTransformCenterDefined then
   begin
-    bounds := GetOriginalLayerBounds;
+    bounds := GetInitialLayerBounds;
     if IsRectEmpty(bounds) then
       FTransformCenter := PointF(Manager.Image.Width/2 - 0.5,Manager.Image.Height/2 - 0.5)
     else
     begin
       with bounds do
         FTransformCenter := PointF((Left+Right)/2 - 0.5, (Top+Bottom)/2 - 0.5);
-      with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
-        FTransformCenter += PointF(X,Y);
     end;
     FTransformCenterDefined := true;
   end;
   result := FTransformCenter;
 end;
 
-procedure TToolRotateLayer.SetTransformCenter(AValue: TPointF);
+procedure TToolTransformLayer.SetTransformCenter(AValue: TPointF);
 begin
   FTransformCenter := AValue;
 end;
 
-procedure TToolRotateLayer.NeedOriginal;
+procedure TToolTransformLayer.NeedOriginal;
 var
   layered: TBGRALayeredBitmap;
   layerIdx: Integer;
@@ -195,13 +345,13 @@ begin
      Manager.Image.LayerOriginalKnown[layerIdx]) then
   begin
     if Assigned(FBackupLayer) then raise exception.Create('Backup layer already assigned');
-    FBackupLayer:= TReplaceLayerByImageOriginalDifference.Create(Manager.Image.CurrentState, layerIdx);
+    FBackupLayer:= TReplaceLayerByImageOriginalDifference.Create(Manager.Image.CurrentState, layerIdx, true);
   end;
   FInitialOriginalMatrix := layered.LayerOriginalMatrix[layerIdx];
   FOriginalInit := true;
 end;
 
-function TToolRotateLayer.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
+function TToolTransformLayer.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF; rightBtn: boolean): TRect;
 begin
   with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
@@ -211,57 +361,42 @@ begin
   begin
     FTransforming := true;
     FPreviousMousePos := ptF;
-    if FCtrlDown then result := UpdateTransform
-     else result := EmptyRect;
+    if FCtrlDown then
+    begin
+      result := UpdateTransform;
+      if IsRectEmpty(result) then result := OnlyRenderChange;
+    end else result := EmptyRect;
+    Manager.Image.DraftOriginal := true;
   end else
   if rightBtn then
   begin
     FTransformCenter := ptF;
-    FAngle := 0;
-    FActualAngle:= GetActualAngle;
+    TransformCenterChanged;
     result := UpdateTransform;
     if IsRectEmpty(result) then result := OnlyRenderChange;
   end else
     result := EmptyRect;
 end;
 
-function TToolRotateLayer.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
+function TToolTransformLayer.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF): TRect;
-var angleDiff: single;
 begin
   with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
     ptF += PointF(X,Y);
   if FTransforming then
   begin
-    angleDiff := ComputeAngle(ptF.X-TransformCenter.X,ptF.Y-TransformCenter.Y)-
-               ComputeAngle(FPreviousMousePos.X-TransformCenter.X,FPreviousMousePos.Y-TransformCenter.Y);
-    FAngle += angleDiff;
-    FActualAngle:= GetActualAngle;
-    result := UpdateTransform;
+    If MouseChangesTransform(FPreviousMousePos, ptF) then
+    begin
+      result := UpdateTransform;
+      if result.IsEmpty then result := OnlyRenderChange;
+    end
+    else result := EmptyRect;
     FPreviousMousePos := ptF;
   end else
     result := EmptyRect;
 end;
 
-function TToolRotateLayer.UpdateTransform: TRect;
-begin
-  if (FActualAngle = FPreviousActualAngle) and ((FActualAngle = 0) or (TransformCenter = FPreviousTransformCenter)) then
-  begin
-    result := EmptyRect;
-    exit;
-  end;
-  FPreviousActualAngle := FActualAngle;
-  FPreviousTransformCenter := TransformCenter;
-  result := EmptyRect;
-  NeedOriginal;
-  Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] :=
-    AffineMatrixTranslation(TransformCenter.X,TransformCenter.Y)*
-    AffineMatrixRotationDeg(FActualAngle)*
-    AffineMatrixTranslation(-TransformCenter.X,-TransformCenter.Y)*
-    FInitialOriginalMatrix;
-end;
-
-procedure TToolRotateLayer.CancelTransform;
+procedure TToolTransformLayer.CancelTransform;
 begin
   if FOriginalInit then
   begin
@@ -276,7 +411,7 @@ begin
   Manager.QueryExitTool;
 end;
 
-procedure TToolRotateLayer.ValidateTransform;
+procedure TToolTransformLayer.ValidateTransform;
 var
   transform: TAffineMatrix;
 begin
@@ -297,50 +432,48 @@ begin
   Manager.QueryExitTool;
 end;
 
-function TToolRotateLayer.GetAction: TLayerAction;
+function TToolTransformLayer.GetAction: TLayerAction;
 begin
   result := GetIdleAction;
 end;
 
-function TToolRotateLayer.DoGetToolDrawingLayer: TBGRABitmap;
+function TToolTransformLayer.DoGetToolDrawingLayer: TBGRABitmap;
 begin
   Result:= Manager.Image.CurrentLayerReadOnly   //do not modify layer data directly and ignore selection
 end;
 
-constructor TToolRotateLayer.Create(AManager: TToolManager);
+constructor TToolTransformLayer.Create(AManager: TToolManager);
 begin
   inherited Create(AManager);
   FCtrlDown:= false;
   FTransformCenterDefined := false;
   FLastUpdateRectDefined:= false;
-
-  FAngle:= 0;
-  FPreviousActualAngle := 0;
 end;
 
-destructor TToolRotateLayer.Destroy;
+destructor TToolTransformLayer.Destroy;
 begin
-  ValidateTransform;
+  if TransformOk then ValidateTransform
+  else CancelTransform;
   inherited Destroy;
 end;
 
-function TToolRotateLayer.ToolKeyDown(var key: Word): TRect;
+function TToolTransformLayer.ToolKeyDown(var key: Word): TRect;
 begin
   if key = VK_CONTROL then
   begin
     FCtrlDown:= true;
-    if FTransforming then
+    if FTransforming and CtrlChangesTransform then
     begin
-      FActualAngle := GetActualAngle;
       result := UpdateTransform;
+      if result.IsEmpty then result := OnlyRenderChange;
     end
-     else result := EmptyRect;
+      else result := EmptyRect;
     Key := 0;
   end else
   if Key = VK_RETURN then
   begin
-    if FActualAngle = 0 then CancelTransform
-    else ValidateTransform;
+    if TransformOk then ValidateTransform
+    else CancelTransform;
     result := OnlyRenderChange;
     key := 0;
   end else
@@ -353,35 +486,85 @@ begin
     result := EmptyRect;
 end;
 
-function TToolRotateLayer.ToolKeyUp(var key: Word): TRect;
+function TToolTransformLayer.ToolKeyUp(var key: Word): TRect;
 begin
   if key = VK_CONTROL then
   begin
     FCtrlDown := false;
+    if FTransforming and CtrlChangesTransform then
+    begin
+      result := UpdateTransform;
+      if result.IsEmpty then result := OnlyRenderChange;
+    end
+      else result := EmptyRect;
     Key := 0;
-  end;
-  result := EmptyRect;
+  end else
+    result := EmptyRect;
 end;
 
-function TToolRotateLayer.ToolUp: TRect;
+function TToolTransformLayer.ToolUp: TRect;
 begin
   if FTransforming then
   begin
     FTransforming := false;
     result := UpdateTransform;
+    if result.IsEmpty then result := OnlyRenderChange;
+    Manager.Image.DraftOriginal := false;
   end else
     Result:=EmptyRect;
 end;
 
-function TToolRotateLayer.Render(VirtualScreen: TBGRABitmap;
+function TToolTransformLayer.Render(VirtualScreen: TBGRABitmap;
   VirtualScreenWidth, VirtualScreenHeight: integer;
   BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
-begin
-  with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
+var
+  idx, i: integer;
+  m: TAffineMatrix;
+  ab: TAffineBox;
+  ptsF: ArrayOfTPointF;
+  pts: array of TPoint;
+  ptsRect: TRect;
+begin
+  idx := Manager.Image.CurrentLayerIndex;
+  with Manager.Image.LayerOffset[idx] do
     Result:= NicePoint(VirtualScreen,BitmapToVirtualScreen(TransformCenter-PointF(X,Y)));
+
+  if not FOriginalBoundsDefined then
+  begin
+    if Manager.Image.LayerOriginalDefined[idx] then
+    begin
+      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;
+      if FOriginalBounds.Bottom = VeryBigValue then FOriginalBounds.Bottom := Manager.Image.Height;
+    end
+    else
+      FOriginalBounds := GetInitialLayerBounds;
+  end;
+  m := Manager.Image.LayerOriginalMatrix[idx];
+  with Manager.Image.LayerOffset[idx] do
+    m := AffineMatrixTranslation(-x,-y)*m;
+
+  ab := TAffineBox.AffineBox(BitmapToVirtualScreen(m*PointF(FOriginalBounds.Left-0.499,FOriginalBounds.Top-0.499)),
+            BitmapToVirtualScreen(m*PointF(FOriginalBounds.Right-0.501,FOriginalBounds.Top-0.499)),
+            BitmapToVirtualScreen(m*PointF(FOriginalBounds.Left-0.499,FOriginalBounds.Bottom-0.501)));
+  ptsF := ab.AsPolygon;
+  setlength(pts, length(ptsF));
+  for i := 0 to high(pts) do
+    pts[i] := ptsF[i].Round;
+
+  ptsRect := TRect.Union(pts);
+  ptsRect.Inflate(1,1);
+  Result.Union(ptsRect);
+
+  if Assigned(VirtualScreen) then
+    virtualScreen.DrawpolygonAntialias(pts,BGRA(230,255,230,255),BGRA(0,0,0,255),FrameDashLength);
 end;
 
-function TToolRotateLayer.GetIsSelectingTool: boolean;
+function TToolTransformLayer.GetIsSelectingTool: boolean;
 begin
   result := false;
 end;
@@ -418,104 +601,9 @@ begin
     result := FZoom;
 end;
 
-function TToolZoomLayer.GetOriginalLayerBounds: TRect;
-begin
-  if not FInitialLayerBoundsDefined then
-  begin
-    FInitialLayerBounds := GetToolDrawingLayer.GetImageBounds;
-    FInitialLayerBoundsDefined := true;
-  end;
-  result := FInitialLayerBounds;
-end;
-
-function TToolZoomLayer.GetTransformCenter: TPointF;
-var bounds: TRect;
-begin
-  if not FTransformCenterDefined then
-  begin
-    bounds := GetOriginalLayerBounds;
-    if IsRectEmpty(bounds) then
-      FTransformCenter := PointF(Manager.Image.Width/2 - 0.5,Manager.Image.Height/2 - 0.5)
-    else
-    begin
-      with bounds do
-        FTransformCenter := PointF((Left+Right)/2 - 0.5, (Top+Bottom)/2 - 0.5);
-      with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
-        FTransformCenter += PointF(X,Y);
-    end;
-    FTransformCenterDefined := true;
-  end;
-  result := FTransformCenter;
-end;
-
-procedure TToolZoomLayer.SetTransformCenter(AValue: TPointF);
-begin
-  FTransformCenter := AValue;
-end;
-
-procedure TToolZoomLayer.NeedOriginal;
-var
-  layered: TBGRALayeredBitmap;
-  layerIdx: Integer;
-begin
-  if FOriginalInit then exit;
-  GetAction;
-  layerIdx := Manager.Image.CurrentLayerIndex;
-  layered := Manager.Image.CurrentState.LayeredBitmap;
-  if not (Manager.Image.LayerOriginalDefined[layerIdx] and
-     Manager.Image.LayerOriginalKnown[layerIdx]) then
-  begin
-    if Assigned(FBackupLayer) then raise exception.Create('Backup layer already assigned');
-    FBackupLayer:= TReplaceLayerByImageOriginalDifference.Create(Manager.Image.CurrentState, layerIdx);
-  end;
-  FInitialOriginalMatrix := layered.LayerOriginalMatrix[layerIdx];
-  FOriginalInit := true;
-end;
-
-function TToolZoomLayer.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
-  ptF: TPointF; rightBtn: boolean): TRect;
-begin
-  with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
-    ptF += PointF(X,Y);
-
-  if not FTransforming and not rightBtn then
-  begin
-    FTransforming := true;
-    FPreviousMousePos := ptF;
-    if FCtrlDown then result := UpdateTransform
-     else result := EmptyRect;
-  end else
-  if rightBtn then
-  begin
-    FTransformCenter := ptF;
-    FZoom := 1;
-    FActualZoom:= GetActualZoom;
-    result := UpdateTransform;
-    if IsRectEmpty(result) then result := OnlyRenderChange;
-  end else
-    result := EmptyRect;
-end;
-
-function TToolZoomLayer.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
-  ptF: TPointF): TRect;
-var dist, prevDist: single;
+function TToolZoomLayer.TransformOk: boolean;
 begin
-  with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
-    ptF += PointF(X,Y);
-  if FTransforming then
-  begin
-    dist := VectLen(ptF-TransformCenter);
-    prevDist := VectLen(FPreviousMousePos-TransformCenter);
-    if (prevDist <> 0) and (dist <> 0) then
-    begin
-      FZoom *= dist/prevDist;
-      FActualZoom:= GetActualZoom;
-      result := UpdateTransform;
-    end
-    else result := EmptyRect;
-    FPreviousMousePos := ptF;
-  end else
-    result := EmptyRect;
+  result := FActualZoom <> 0;
 end;
 
 function TToolZoomLayer.UpdateTransform: TRect;
@@ -536,296 +624,120 @@ begin
     FInitialOriginalMatrix;
 end;
 
-procedure TToolZoomLayer.CancelTransform;
+procedure TToolZoomLayer.TransformCenterChanged;
 begin
-  if FOriginalInit then
-  begin
-    Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] := FInitialOriginalMatrix;
-    if Assigned(FBackupLayer) then
-    begin
-      FBackupLayer.UnapplyTo(Manager.Image.CurrentState);
-      FreeAndNil(FBackupLayer);
-    end;
-    FOriginalInit := false;
-  end;
-  Manager.QueryExitTool;
+  FZoom := 1;
+  FActualZoom:= GetActualZoom;
 end;
 
-procedure TToolZoomLayer.ValidateTransform;
+function TToolZoomLayer.MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean;
 var
-  transform: TAffineMatrix;
+  dist, prevDist: Single;
 begin
-  if FOriginalInit then
+  dist := VectLen(ANewPos-TransformCenter);
+  prevDist := VectLen(APrevPos-TransformCenter);
+  if (prevDist <> 0) and (dist <> 0) 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);
-      FBackupLayer.nextMatrix := transform;
-      Manager.Image.AddUndo(FBackupLayer);
-      FBackupLayer := nil;
-    end;
-    FOriginalInit := false;
-  end;
-  Manager.QueryExitTool;
-end;
-
-function TToolZoomLayer.GetAction: TLayerAction;
-begin
-  result := GetIdleAction;
+    FZoom *= dist/prevDist;
+    FActualZoom:= GetActualZoom;
+    result := true;
+  end
+  else result := false;
 end;
 
-function TToolZoomLayer.DoGetToolDrawingLayer: TBGRABitmap;
+function TToolZoomLayer.CtrlChangesTransform: boolean;
+var
+  newActualZoom: Single;
 begin
-  Result:= Manager.Image.CurrentLayerReadOnly   //do not modify layer data directly and ignore selection
+  newActualZoom := GetActualZoom;
+  if FActualZoom<>newActualZoom then
+  begin
+    FActualZoom := newActualZoom;
+    result := true;
+  end else
+    result := false;
 end;
 
 constructor TToolZoomLayer.Create(AManager: TToolManager);
 begin
   inherited Create(AManager);
-  FCtrlDown:= false;
-  FTransformCenterDefined := false;
-  FLastUpdateRectDefined:= false;
-
   FZoom:= 1;
   FPreviousActualZoom := 1;
 end;
 
-destructor TToolZoomLayer.Destroy;
-begin
-  ValidateTransform;
-  inherited Destroy;
-end;
-
-function TToolZoomLayer.ToolKeyDown(var key: Word): TRect;
-begin
-  if key = VK_CONTROL then
-  begin
-    FCtrlDown:= true;
-    if FTransforming then
-    begin
-      FActualZoom := GetActualZoom;
-      result := UpdateTransform;
-    end
-     else result := EmptyRect;
-    Key := 0;
-  end else
-  if Key = VK_RETURN then
-  begin
-    if FActualZoom = 0 then CancelTransform
-    else ValidateTransform;
-    result := OnlyRenderChange;
-    key := 0;
-  end else
-  if Key = VK_ESCAPE then
-  begin
-    CancelTransform;
-    result := OnlyRenderChange;
-    key := 0;
-  end else
-    result := EmptyRect;
-end;
-
-function TToolZoomLayer.ToolKeyUp(var key: Word): TRect;
-begin
-  if key = VK_CONTROL then
-  begin
-    FCtrlDown := false;
-    Key := 0;
-  end;
-  result := EmptyRect;
-end;
-
-function TToolZoomLayer.ToolUp: TRect;
-begin
-  if FTransforming then
-  begin
-    FTransforming := false;
-    result := UpdateTransform;
-  end else
-    Result:=EmptyRect;
-end;
+{ TToolRotateLayer }
 
-function TToolZoomLayer.Render(VirtualScreen: TBGRABitmap;
-  VirtualScreenWidth, VirtualScreenHeight: integer;
-  BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
+function TToolRotateLayer.GetActualAngle: single;
 begin
-  with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
-    Result:= NicePoint(VirtualScreen,BitmapToVirtualScreen(TransformCenter-PointF(X,Y)));
+  if FCtrlDown then
+    result := round(FAngle/15)*15
+  else
+    result := FAngle;
 end;
 
-function TToolZoomLayer.GetIsSelectingTool: boolean;
+function TToolRotateLayer.TransformOk: boolean;
 begin
-  result := false;
+  result := true;
 end;
 
-{ TToolMoveLayer }
-
-function TToolMoveLayer.GetIsSelectingTool: boolean;
+procedure TToolRotateLayer.TransformCenterChanged;
 begin
-  result := false;
+  FAngle := 0;
+  FActualAngle:= GetActualAngle;
 end;
 
-function TToolMoveLayer.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
-  ptF: TPointF; rightBtn: boolean): TRect;
-var idx: integer;
+function TToolRotateLayer.MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean;
+var
+  angleDiff, newActualAngle: Single;
 begin
-  result := EmptyRect;
-  if not handMoving then
+  angleDiff := ComputeAngle(ANewPos.X-TransformCenter.X,ANewPos.Y-TransformCenter.Y)-
+             ComputeAngle(APrevPos.X-TransformCenter.X,APrevPos.Y-TransformCenter.Y);
+  FAngle += angleDiff;
+  newActualAngle := GetActualAngle;
+  if newActualAngle <> FActualAngle then
   begin
-    handMoving := true;
-    handOrigin := pt;
-    if not FStartLayerOffsetDefined then
-    begin
-      FStartLayerOffsetDefined := true;
-      idx := Manager.Image.CurrentLayerIndex;
-      NeedLayerBounds;
-      FStartLayerOffset := Manager.Image.LayerOffset[idx];
-      FStartLayerMatrix := Manager.Image.LayerOriginalMatrix[idx];
-    end;
-  end;
+    FActualAngle:= newActualAngle;
+    result := true;
+  end
+  else result := false;
 end;
 
-function TToolMoveLayer.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
-  ptF: TPointF): TRect;
-var idx: integer;
-  prev: TPoint;
+function TToolRotateLayer.CtrlChangesTransform: boolean;
+var
+  newActualAngle: Single;
 begin
-  if handMoving and ((handOrigin.X <> pt.X) or (handOrigin.Y <> pt.Y)) then
+  newActualAngle := GetActualAngle;
+  if newActualAngle<>FActualAngle then
   begin
-    idx := Manager.Image.CurrentLayerIndex;
-    if UseOriginal then
-    begin
-      Manager.Image.LayerOriginalMatrix[idx] :=
-          AffineMatrixTranslation(pt.X-HandOrigin.X,pt.Y-HandOrigin.Y)*Manager.Image.LayerOriginalMatrix[idx];
-      result := OnlyRenderChange;
-    end else
-    begin
-      prev := Manager.Image.LayerOffset[idx];
-      Manager.Image.SetLayerOffset(idx, Point(prev.X+pt.X-HandOrigin.X,
-                                         prev.Y+pt.Y-HandOrigin.Y), FLayerBounds);
-      result := OnlyRenderChange;
-    end;
+    FActualAngle := newActualAngle;
+    result := true;
   end else
-    result := EmptyRect;
-end;
-
-procedure TToolMoveLayer.DoToolMoveAfter(pt: TPoint; ptF: TPointF);
-begin
-  if handMoving then handOrigin := pt;
+    result := false;
 end;
 
-function TToolMoveLayer.UseOriginal: boolean;
-begin
-  with Manager.Image do
-    result := LayerOriginalDefined[CurrentLayerIndex] and
-              LayerOriginalKnown[CurrentLayerIndex];
-end;
-
-procedure TToolMoveLayer.NeedLayerBounds;
-const
-  VeryBigValue = maxLongInt div 2;
-var
-  idx: Integer;
+function TToolRotateLayer.UpdateTransform: TRect;
 begin
-  GetAction;
-  idx := Manager.Image.CurrentLayerIndex;
-  if not FLayerBoundsDefined then
+  if (FActualAngle = FPreviousActualAngle) and ((FActualAngle = 0) or (TransformCenter = FPreviousTransformCenter)) then
   begin
-    if UseOriginal then
-    begin
-      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;
-      if FLayerBounds.Bottom = VeryBigValue then FLayerBounds.Bottom := Manager.Image.Height;
-    end
-    else
-      FLayerBounds := Manager.Image.LayerBitmap[idx].GetImageBounds;
-    FLayerBoundsDefined := true;
+    result := EmptyRect;
+    exit;
   end;
-end;
-
-function TToolMoveLayer.GetAction: TLayerAction;
-begin
-  result := GetIdleAction;
-end;
-
-function TToolMoveLayer.DoGetToolDrawingLayer: TBGRABitmap;
-begin
-  Result:= Manager.Image.CurrentLayerReadOnly;   //do not modify layer data directly and ignore selection
-end;
-
-function TToolMoveLayer.ToolUp: TRect;
-begin
-  handMoving := false;
+  FPreviousActualAngle := FActualAngle;
+  FPreviousTransformCenter := TransformCenter;
   result := EmptyRect;
+  NeedOriginal;
+  Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] :=
+    AffineMatrixTranslation(TransformCenter.X,TransformCenter.Y)*
+    AffineMatrixRotationDeg(FActualAngle)*
+    AffineMatrixTranslation(-TransformCenter.X,-TransformCenter.Y)*
+    FInitialOriginalMatrix;
 end;
 
-function TToolMoveLayer.ToolKeyDown(var key: Word): TRect;
-var idx: integer;
-begin
-  if key = VK_RETURN then
-  begin
-    Manager.QueryExitTool;
-    result := EmptyRect;
-    Key := 0;
-  end
-  else if key = VK_ESCAPE then
-  begin
-    if FStartLayerOffsetDefined then
-    begin
-      idx := Manager.Image.CurrentLayerIndex;
-      if UseOriginal then
-        Manager.Image.LayerOriginalMatrix[idx] := FStartLayerMatrix
-      else
-        Manager.Image.SetLayerOffset(idx, FStartLayerOffset, FLayerBounds);
-      result := OnlyRenderChange;
-    end else
-      result := EmptyRect;
-    Manager.QueryExitTool;
-    Key := 0;
-  end else
-    Result:=inherited ToolKeyDown(key);
-end;
-
-function TToolMoveLayer.Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth,
-  VirtualScreenHeight: integer;
-  BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
-var
-  idx, i: integer;
-  m: TAffineMatrix;
-  ab: TAffineBox;
-  ptsF: ArrayOfTPointF;
-  pts: array of TPoint;
+constructor TToolRotateLayer.Create(AManager: TToolManager);
 begin
-  NeedLayerBounds;
-
-  if UseOriginal then
-  begin
-    idx := Manager.Image.CurrentLayerIndex;
-    m := Manager.Image.LayerOriginalMatrix[idx];
-    with Manager.Image.LayerOffset[idx] do
-      m := AffineMatrixTranslation(-x,-y)*m;
-  end else m := AffineMatrixIdentity;
-
-  ab := TAffineBox.AffineBox(BitmapToVirtualScreen(m*PointF(FLayerBounds.Left-0.499,FLayerBounds.Top-0.499)),
-            BitmapToVirtualScreen(m*PointF(FLayerBounds.Right-0.501,FLayerBounds.Top-0.499)),
-            BitmapToVirtualScreen(m*PointF(FLayerBounds.Left-0.499,FLayerBounds.Bottom-0.501)));
-  ptsF := ab.AsPolygon;
-  setlength(pts, length(ptsF));
-  for i := 0 to high(pts) do
-    pts[i] := ptsF[i].Round;
-
-  result := TRect.Union(pts);
-  result.Inflate(1,1);
-
-  if Assigned(VirtualScreen) then
-    virtualScreen.DrawpolygonAntialias(pts,BGRA(230,255,230,255),BGRA(0,0,0,255),FrameDashLength);
+  inherited Create(AManager);
+  FAngle:= 0;
+  FPreviousActualAngle := 0;
 end;
 
 initialization

+ 18 - 7
lazpaint/utoolphong.pas

@@ -13,28 +13,39 @@ type
 
   TToolPhong = class(TVectorialTool)
   protected
+    FMatrix: TAffineMatrix;
+    constructor Create(AManager: TToolManager); override;
     procedure ShapeChange({%H-}ASender: TObject; ABounds: TRectF); override;
-    procedure AssignShapeStyle; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
     function CreateShape: TVectorShape; override;
     function SlowShape: boolean; override;
   end;
 
 implementation
 
-uses ugraph, Graphics, LazPaintType, LCVectorRectShapes;
+uses ugraph, Graphics, LazPaintType, LCVectorRectShapes, BGRATransform;
 
 { TToolPhong }
 
+constructor TToolPhong.Create(AManager: TToolManager);
+begin
+  inherited Create(AManager);
+  FMatrix := AffineMatrixIdentity;
+end;
+
 procedure TToolPhong.ShapeChange(ASender: TObject; ABounds: TRectF);
+var
+  posF: TPointF;
 begin
-  with (FShape as TPhongShape) do
-    Manager.ToolLightPosition := Point(round(LightPosition.X),round(LightPosition.Y));
+  posF := AffineMatrixInverse(FMatrix)*(FShape as TPhongShape).LightPosition;
+  Manager.ToolLightPosition := posF;
   inherited ShapeChange(ASender, ABounds);
 end;
 
-procedure TToolPhong.AssignShapeStyle;
+procedure TToolPhong.AssignShapeStyle(AMatrix: TAffineMatrix);
 begin
-  inherited AssignShapeStyle;
+  inherited AssignShapeStyle(AMatrix);
+  FMatrix := AMatrix;
   with (FShape as TPhongShape) do
   begin
     if Manager.ToolShapeType = 'RoundRectangle' then ShapeKind:= pskRoundRectangle else
@@ -44,7 +55,7 @@ begin
     if Manager.ToolShapeType = 'VerticalCylinder' then ShapeKind := pskVertCylinder else
     if Manager.ToolShapeType = 'HorizontalCylinder' then ShapeKind := pskHorizCylinder
     else ShapeKind := pskRectangle;
-    LightPosition := PointF(Manager.ToolLightPosition.X,Manager.ToolLightPosition.Y);
+    LightPosition := AMatrix*Manager.ToolLightPosition;
     ShapeAltitudePercent := Manager.ToolShapeAltitude;
     BorderSizePercent:= Manager.ToolShapeBorderSize;
   end;

+ 146 - 8
lazpaint/utoolpolygon.pas

@@ -5,13 +5,44 @@ unit UToolPolygon;
 interface
 
 uses
-  Classes, SysUtils, utool, BGRABitmap, BGRABitmapTypes, ULayerAction;
+  Classes, SysUtils, utool, utoolbasic, BGRABitmap, BGRABitmapTypes,
+  LCVectorOriginal, LCLType;
 
 const
   EasyBezierMinimumDotProduct = 0.5;
 
 type
+  TToolSplineMode = (tsmMovePoint, tsmCurveModeAuto, tsmCurveModeAngle, tsmCurveModeSpline);
+
+  { TToolPolygon }
+
+  TToolPolygon = class(TVectorialTool)
+  protected
+    function CreateShape: TVectorShape; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
+    procedure UpdateUserMode; virtual;
+  end;
+
+  { TToolSpline }
+
+  TToolSpline = class(TToolPolygon)
+  private
+    FCurrentMode: TToolSplineMode;
+    FNextCurveMode: TEasyBezierCurveMode;
+    FCurveModeHintShown: Boolean;
+    function GetCurrentMode: TToolSplineMode;
+    procedure SetCurrentMode(AValue: TToolSplineMode);
+    procedure UpdateUserMode; override;
+  protected
+    function CreateShape: TVectorShape; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
+  public
+    constructor Create(AManager: TToolManager); override;
+    function ToolKeyPress(var key: TUTF8Char): TRect; override;
+    property CurrentMode: TToolSplineMode read GetCurrentMode write SetCurrentMode;
+  end;
 
+{
   { TToolGenericPolygon }
 
   TToolGenericPolygon = class(TGenericTool)
@@ -80,8 +111,6 @@ type
     function FinalPolygonView(toolDest: TBGRABitmap): TRect; override;
   end;
 
-  TToolSplineMode = (tsmMovePoint, tsmCurveModeAuto, tsmCurveModeAngle, tsmCurveModeSpline);
-
   { TToolGenericSpline }
 
   TToolGenericSpline = class(TToolGenericPolygon)
@@ -125,14 +154,123 @@ type
     function GetIsSelectingTool: boolean; override;
     function HandDrawingPolygonView(toolDest: TBGRABitmap): TRect; override;
     function FinalPolygonView({%H-}toolDest: TBGRABitmap):TRect; override;
-  end;
+  end;}
 
 implementation
 
-uses Types, Graphics, LCLType, ugraph, Dialogs, Controls, LazPaintType,
-  BGRAFillInfo, BGRAPath;
+uses LazPaintType, LCVectorPolyShapes;
+
+{ TToolSpline }
+
+function TToolSpline.GetCurrentMode: TToolSplineMode;
+var
+  c: TCurveShape;
+begin
+  if Assigned(FShape) then
+  begin
+    c := TCurveShape(FShape);
+    case c.Usermode of
+    vsuEdit: FCurrentMode := tsmMovePoint;
+    vsuCreate: if c.PointCount > 1 then
+               begin
+                 case c.CurveMode[c.PointCount-2] of
+                   cmAuto: FCurrentMode := tsmCurveModeAuto;
+                   cmAngle: FCurrentMode := tsmCurveModeAngle;
+                   cmCurve: FCurrentMode := tsmCurveModeSpline;
+                 end;
+               end else
+                 result := tsmCurveModeAuto;
+    vsuCurveSetAuto: FCurrentMode := tsmCurveModeAuto;
+    vsuCurveSetAngle: FCurrentMode := tsmCurveModeAngle;
+    vsuCurveSetCurve: FCurrentMode := tsmCurveModeSpline;
+    end;
+  end;
+  result := FCurrentMode;
+end;
+
+procedure TToolSpline.SetCurrentMode(AValue: TToolSplineMode);
+begin
+  if FCurrentMode = AValue then exit;
+  FCurrentMode := AValue;
+  UpdateUserMode;
+end;
+
+procedure TToolSpline.UpdateUserMode;
+var
+  c: TCurveShape;
+begin
+  if FShape = nil then exit;
+  if FQuickDefine then
+  begin
+    FShape.Usermode := vsuCreate;
+    exit;
+  end;
+  c := TCurveShape(FShape);
+  case FCurrentMode of
+  tsmMovePoint: if not (c.Usermode in [vsuEdit,vsuCreate]) then c.Usermode := vsuEdit;
+  tsmCurveModeAuto: if c.Usermode <> vsuCreate then c.Usermode := vsuCurveSetAuto else
+                    if c.PointCount > 1 then c.CurveMode[c.PointCount-2] := cmAuto;
+  tsmCurveModeAngle: if c.Usermode <> vsuCreate then c.Usermode := vsuCurveSetAngle else
+                     if c.PointCount > 1 then c.CurveMode[c.PointCount-2] := cmAngle;
+  tsmCurveModeSpline: if c.Usermode <> vsuCreate then c.Usermode := vsuCurveSetCurve else
+                      if c.PointCount > 1 then c.CurveMode[c.PointCount-2] := cmCurve;
+  end;
+end;
+
+function TToolSpline.CreateShape: TVectorShape;
+begin
+  result := TCurveShape.Create(nil);
+  result.Usermode := vsuCreate;
+  TCurveShape(result).CosineAngle:= EasyBezierMinimumDotProduct;
+  if not FCurveModeHintShown then
+  begin
+    Manager.ToolPopup(tpmCurveModeHint);
+    FCurveModeHintShown := true;
+  end;
+end;
+
+procedure TToolSpline.AssignShapeStyle(AMatrix: TAffineMatrix);
+begin
+  inherited AssignShapeStyle(AMatrix);
+  TCurveShape(FShape).SplineStyle:= Manager.ToolSplineStyle;
+end;
+
+constructor TToolSpline.Create(AManager: TToolManager);
+begin
+  inherited Create(AManager);
+  FNextCurveMode := cmAuto;
+end;
+
+function TToolSpline.ToolKeyPress(var key: TUTF8Char): TRect;
+begin
+  Result:=inherited ToolKeyPress(key);
+  if Key='x' then Key := #0;
+end;
 
-{ TToolGenericSpline }
+{ TToolPolygon }
+
+function TToolPolygon.CreateShape: TVectorShape;
+begin
+  result := TPolylineShape.Create(nil);
+end;
+
+procedure TToolPolygon.AssignShapeStyle(AMatrix: TAffineMatrix);
+begin
+  inherited AssignShapeStyle(AMatrix);
+  TCustomPolypointShape(FShape).Closed := Manager.ToolOptionCloseShape;
+  TCustomPolypointShape(FShape).ArrowStartKind := StrToArrowKind(Manager.ToolArrowStart);
+  TCustomPolypointShape(FShape).ArrowEndKind := StrToArrowKind(Manager.ToolArrowEnd);
+  TCustomPolypointShape(FShape).ArrowSize := Manager.ToolArrowSize;
+  UpdateUserMode;
+end;
+
+procedure TToolPolygon.UpdateUserMode;
+begin
+  if FShape = nil then exit;
+  if FQuickDefine then FShape.Usermode := vsuCreate;
+end;
+
+{{ TToolGenericSpline }
 
 function TToolGenericSpline.GetCurrentMode: TToolSplineMode;
 begin
@@ -962,7 +1100,7 @@ begin
   if length(polygonPoints)<>0 then
     ValidatePolygon(GetToolDrawingLayer);
   inherited Destroy;
-end;
+end;}
 
 initialization
 

+ 60 - 103
lazpaint/utoolselect.pas

@@ -14,7 +14,7 @@ type
   TVectorialSelectTool = class(TVectorialTool)
   protected
     function GetIsSelectingTool: boolean; override;
-    procedure AssignShapeStyle; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
     function RoundCoordinate(ptF: TPointF): TPointF; override;
     function UpdateShape(toolDest: TBGRABitmap): TRect; override;
     procedure QuickDefineEnd; override;
@@ -41,22 +41,18 @@ type
 
   { TToolSelectPoly }
 
-  TToolSelectPoly = class(TToolGenericPolygon)
+  TToolSelectPoly = class(TToolPolygon)
   protected
-    function HandDrawingPolygonView({%H-}toolDest: TBGRABitmap): TRect; override;
-    function FinalPolygonView(toolDest: TBGRABitmap): TRect; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
     function GetIsSelectingTool: boolean; override;
-    function GetFillColor: TBGRAPixel; override;
   end;
 
   { TToolSelectSpline }
 
-  TToolSelectSpline = class(TToolGenericSpline)
+  TToolSelectSpline = class(TToolSpline)
   protected
-    function HandDrawingPolygonView(toolDest: TBGRABitmap): TRect; override;
-    function FinalPolygonView({%H-}toolDest: TBGRABitmap): TRect; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
     function GetIsSelectingTool: boolean; override;
-    function GetFillColor: TBGRAPixel; override;
   end;
 
   { TToolMagicWand }
@@ -133,6 +129,52 @@ implementation
 uses types, ugraph, LCLType, LazPaintType, Math, BGRATransform, BGRAPath,
   BGRAPen, LCVectorRectShapes;
 
+procedure AssignSelectShapeStyle(AShape: TVectorShape; ASwapColor: boolean);
+var
+  f: TVectorShapeFields;
+begin
+  f:= AShape.Fields;
+  if vsfPenFill in f then AShape.PenFill.Clear;
+  if vsfPenStyle in f Then AShape.PenStyle := ClearPenStyle;
+  if vsfBackFill in f then
+  begin
+    if ASwapColor then
+      AShape.BackFill.SetSolid(BGRABlack)
+    else
+      AShape.BackFill.SetSolid(BGRAWhite);
+  end;
+end;
+
+{ TToolSelectSpline }
+
+procedure TToolSelectSpline.AssignShapeStyle(AMatrix: TAffineMatrix);
+begin
+  FShape.BeginUpdate;
+  inherited AssignShapeStyle(AMatrix);
+  AssignSelectShapeStyle(FShape, FSwapColor);
+  FShape.EndUpdate;
+end;
+
+function TToolSelectSpline.GetIsSelectingTool: boolean;
+begin
+  Result:= true;
+end;
+
+{ TToolSelectPoly }
+
+procedure TToolSelectPoly.AssignShapeStyle(AMatrix: TAffineMatrix);
+begin
+  FShape.BeginUpdate;
+  inherited AssignShapeStyle(AMatrix);
+  AssignSelectShapeStyle(FShape, FSwapColor);
+  FShape.EndUpdate;
+end;
+
+function TToolSelectPoly.GetIsSelectingTool: boolean;
+begin
+  Result:= true;
+end;
+
 { TVectorialSelectTool }
 
 function TVectorialSelectTool.GetIsSelectingTool: boolean;
@@ -140,20 +182,9 @@ begin
   Result:= true;
 end;
 
-procedure TVectorialSelectTool.AssignShapeStyle;
-var
-  f: TVectorShapeFields;
+procedure TVectorialSelectTool.AssignShapeStyle(AMatrix: TAffineMatrix);
 begin
-  f:= FShape.Fields;
-  if vsfPenFill in f then FShape.PenFill.Clear;
-  if vsfPenStyle in f Then FShape.PenStyle := ClearPenStyle;
-  if vsfBackFill in f then
-  begin
-    if FSwapColor then
-      FShape.BackFill.SetSolid(BGRABlack)
-    else
-      FShape.BackFill.SetSolid(BGRAWhite);
-  end;
+  AssignSelectShapeStyle(FShape, FSwapColor);
   if FShape is TCustomRectShape then
   begin
     if Manager.ToolRatio = 0 then
@@ -177,13 +208,18 @@ begin
 end;
 
 procedure TVectorialSelectTool.QuickDefineEnd;
+var
+  toolDest: TBGRABitmap;
+  r: TRect;
 begin
-  UpdateShape(GetToolDrawingLayer);
+  toolDest := GetToolDrawingLayer;
+  r := UpdateShape(toolDest);
+  Action.NotifyChange(toolDest, r);
 end;
 
 function TVectorialSelectTool.BigImage: boolean;
 begin
-  result := GetToolDrawingLayer.NbPixels > 480000;
+  result := Manager.Image.Width*Manager.Image.Height > 480000;
 end;
 
 { TToolSelectRect }
@@ -465,49 +501,6 @@ begin
   inherited Destroy;
 end;
 
-{ TToolSelectSpline }
-
-function TToolSelectSpline.HandDrawingPolygonView(toolDest: TBGRABitmap): TRect;
-var
-  splinePoints: ArrayOfTPointF;
-begin
-  if Manager.ToolSplineEasyBezier then
-  begin
-    NeedCurveMode;
-    splinePoints := EasyBezierCurve(polygonPoints,True,FCurveMode,EasyBezierMinimumDotProduct).ToPoints;
-  end else
-    splinePoints := toolDest.ComputeClosedSpline(polygonPoints,Manager.ToolSplineStyle);
-  FRenderedPolygonPoints := splinePoints;
-
-  if length(splinePoints) > 2 then
-  begin
-    toolDest.FillPolyAntialias(splinePoints, fillColor);
-    result := GetShapeBounds(splinePoints,1);
-  end else
-    result := EmptyRect;
-end;
-
-function TToolSelectSpline.FinalPolygonView(toolDest: TBGRABitmap): TRect;
-begin
-  if FAfterHandDrawing then
-    result := HandDrawingPolygonView(toolDest)
-  else
-    result := EmptyRect;
-end;
-
-function TToolSelectSpline.GetIsSelectingTool: boolean;
-begin
-  Result:= true;
-end;
-
-function TToolSelectSpline.GetFillColor: TBGRAPixel;
-begin
-  if swapedColor then
-    result := BGRABlack
-  else
-    result := BGRAWhite;
-end;
-
 { TToolSelectionPen }
 
 function TToolSelectionPen.GetIsSelectingTool: boolean;
@@ -553,42 +546,6 @@ begin
   ValidateAction;
 end;
 
-{ TToolSelectPoly }
-
-function TToolSelectPoly.HandDrawingPolygonView(toolDest: TBGRABitmap): TRect;
-begin
-  result := EmptyRect;
-  //nothing
-end;
-
-function TToolSelectPoly.FinalPolygonView(toolDest: TBGRABitmap): TRect;
-var
-  i: Integer;
-begin
-  if length(polygonPoints) > 2 then
-  begin
-    toolDest.FillPolyAntialias(polygonPoints, fillColor);
-    result := GetShapeBounds(polygonPoints,1);
-  end else
-    result := EmptyRect;
-  setlength(FRenderedPolygonPoints, length(polygonPoints));
-  for i := 0 to high(polygonPoints) do
-    FRenderedPolygonPoints[i] := polygonPoints[i];
-end;
-
-function TToolSelectPoly.GetIsSelectingTool: boolean;
-begin
-  result := true;
-end;
-
-function TToolSelectPoly.GetFillColor: TBGRAPixel;
-begin
-  if swapedColor then
-    result := BGRABlack
-  else
-    result := BGRAWhite;
-end;
-
 initialization
 
   RegisterTool(ptMagicWand,TToolMagicWand);

+ 30 - 6
lazpaint/utooltext.pas

@@ -14,19 +14,23 @@ type
 
   TToolText = class(TVectorialTool)
   protected
+    FMatrix: TAffineMatrix;
     FPrevShadow: boolean;
     FPrevShadowOffset: TPoint;
     FPrevShadowRadius: single;
     function CreateShape: TVectorShape; override;
+    function AlwaysRasterizeShape: boolean; override;
     procedure IncludeShadowBounds(var ARect: TRect);
     function GetCustomShapeBounds(ADestBounds: TRect; AMatrix: TAffineMatrix; ADraft: boolean): TRect; override;
     procedure DrawCustomShape(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     procedure ShapeChange(ASender: TObject; ABounds: TRectF); override;
     procedure ShapeEditingChange(ASender: TObject); override;
-    procedure AssignShapeStyle; override;
+    procedure AssignShapeStyle(AMatrix: TAffineMatrix); override;
     function SlowShape: boolean; override;
     procedure QuickDefineEnd; override;
+    function RoundCoordinate(ptF: TPointF): TPointF; override;
   public
+    constructor Create(AManager: TToolManager); override;
     function ToolKeyDown(var key: Word): TRect; override;
     function ToolCopy: boolean; override;
     function ToolCut: boolean; override;
@@ -48,6 +52,11 @@ begin
   result := TTextShape.Create(nil);
 end;
 
+function TToolText.AlwaysRasterizeShape: boolean;
+begin
+  Result:= Manager.ToolTextShadow;
+end;
+
 procedure TToolText.IncludeShadowBounds(var ARect: TRect);
 var
   shadowRect: TRect;
@@ -120,9 +129,10 @@ end;
 procedure TToolText.ShapeChange(ASender: TObject; ABounds: TRectF);
 var
   r: TRect;
+  posF: TPointF;
 begin
-  with (FShape as TTextShape) do
-    Manager.ToolLightPosition := Point(round(LightPosition.X),round(LightPosition.Y));
+  posF := AffineMatrixInverse(FMatrix)*(FShape as TTextShape).LightPosition;
+  Manager.ToolLightPosition := posF;
   with ABounds do r := rect(floor(Left),floor(Top),ceil(Right),ceil(Bottom));
   IncludeShadowBounds(r);
   inherited ShapeChange(ASender, RectF(r.Left,r.Top,r.Right,r.Bottom));
@@ -135,14 +145,17 @@ begin
   inherited ShapeEditingChange(ASender);
 end;
 
-procedure TToolText.AssignShapeStyle;
+procedure TToolText.AssignShapeStyle(AMatrix: TAffineMatrix);
 var
   r: TRect;
   toolDest: TBGRABitmap;
+  zoom: Single;
 begin
+  FMatrix := AMatrix;
   with TTextShape(FShape) do
   begin
-    FontEmHeight:= Manager.ToolTextFont.Size*ScreenInfo.PixelsPerInchY/72;
+    zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
+    FontEmHeight:= zoom*Manager.ToolTextFont.Size*ScreenInfo.PixelsPerInchY/72;
     FontName:= Manager.ToolTextFont.Name;
     FontStyle:= Manager.ToolTextFont.Style;
 
@@ -168,7 +181,7 @@ begin
     else
       OutlineFill.Clear;
 
-    LightPosition := PointF(Manager.ToolLightPosition.X,Manager.ToolLightPosition.Y);
+    LightPosition := AMatrix*Manager.ToolLightPosition;
     AltitudePercent:= Manager.ToolShapeAltitude;
     ParagraphAlignment:= Manager.ToolTextAlign;
     PenPhong := Manager.ToolTextPhong;
@@ -193,6 +206,17 @@ begin
   FShape.Usermode := vsuEditText;
 end;
 
+function TToolText.RoundCoordinate(ptF: TPointF): TPointF;
+begin
+  result := PointF(floor(ptF.x)+0.5,floor(ptF.y)+0.5);
+end;
+
+constructor TToolText.Create(AManager: TToolManager);
+begin
+  inherited Create(AManager);
+  FMatrix := AffineMatrixIdentity;
+end;
+
 function TToolText.ToolKeyDown(var key: Word): TRect;
 var
   keyUtf8: TUTF8Char;

+ 52 - 17
lazpaintcontrols/lcvectororiginal.pas

@@ -57,8 +57,6 @@ type
     procedure SetId(AValue: integer);
     procedure SetOutlineWidth(AValue: single);
   protected
-    procedure BeginUpdate;
-    procedure EndUpdate;
     procedure BeginEditingUpdate;
     procedure EndEditingUpdate;
     procedure DoOnChange(ABoundsBefore: TRectF); virtual;
@@ -79,7 +77,7 @@ type
     procedure SetUsermode(AValue: TVectorShapeUsermode); virtual;
     procedure LoadFill(AStorage: TBGRACustomOriginalStorage; AObjectName: string; var AValue: TVectorialFill);
     procedure SaveFill(AStorage: TBGRACustomOriginalStorage; AObjectName: string; AValue: TVectorialFill);
-    function ComputeStroke(APoints: ArrayOfTPointF; AClosed: boolean; AStrokeMatrix: TAffineMatrix): ArrayOfTPointF;
+    function ComputeStroke(APoints: ArrayOfTPointF; AClosed: boolean; AStrokeMatrix: TAffineMatrix): ArrayOfTPointF; virtual;
     function GetStroker: TBGRAPenStroker;
     property Stroker: TBGRAPenStroker read GetStroker;
     procedure FillChange({%H-}ASender: TObject); virtual;
@@ -89,7 +87,10 @@ type
     function CanHaveRenderStorage: boolean;
   public
     constructor Create(AContainer: TVectorOriginal); virtual;
+    class function CreateFromStorage(AStorage: TBGRACustomOriginalStorage; AContainer: TVectorOriginal): TVectorShape;
     destructor Destroy; override;
+    procedure BeginUpdate;
+    procedure EndUpdate;
     procedure QuickDefine(const APoint1,APoint2: TPointF); virtual; abstract;
     //one of the two Render functions must be overriden
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); virtual;
@@ -116,6 +117,7 @@ type
     class function StorageClassName: RawByteString; virtual; abstract;
     function GetIsSlow({%H-}AMatrix: TAffineMatrix): boolean; virtual;
     function GetUsedTextures: ArrayOfBGRABitmap;
+    procedure Transform(AMatrix: TAffineMatrix); virtual;
     class function Fields: TVectorShapeFields; virtual;
     class function Usermodes: TVectorShapeUsermodes; virtual;
     class function PreferPixelCentered: boolean; virtual;
@@ -531,6 +533,20 @@ begin
   setlength(result, nb);
 end;
 
+procedure TVectorShape.Transform(AMatrix: TAffineMatrix);
+var
+  zoom: Single;
+begin
+  if IsAffineMatrixIdentity(AMatrix) then exit;
+  BeginUpdate;
+  if vsfBackFill in Fields then BackFill.Transform(AMatrix);
+  if vsfPenFill in Fields then PenFill.Transform(AMatrix);
+  zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
+  if vsfPenWidth in Fields then PenWidth := zoom*PenWidth;
+  if vsfOutlineFill in Fields then OutlineWidth := zoom*OutlineWidth;
+  EndUpdate;
+end;
+
 class function TVectorShape.Fields: TVectorShapeFields;
 begin
   result := [];
@@ -855,7 +871,7 @@ begin
   if FStroker = nil then
   begin
     FStroker := TBGRAPenStroker.Create;
-    FStroker.MiterLimit:= sqrt(2);
+    FStroker.MiterLimit:= 2;
   end;
   result := FStroker;
 end;
@@ -983,6 +999,20 @@ begin
   FRenderIteration:= 0;
 end;
 
+class function TVectorShape.CreateFromStorage(
+  AStorage: TBGRACustomOriginalStorage; AContainer: TVectorOriginal): TVectorShape;
+var
+  objClassName: RawByteString;
+  shapeClass: TVectorShapeAny;
+begin
+  objClassName := AStorage.RawString['class'];
+  if objClassName = '' then raise exception.Create('Shape class not defined');
+  shapeClass:= GetVectorShapeByStorageClassName(objClassName);
+  if shapeClass = nil then raise exception.Create('Unknown shape class "'+objClassName+'"');
+  result := shapeClass.Create(AContainer);
+  result.LoadFromStorage(AStorage);
+end;
+
 destructor TVectorShape.Destroy;
 begin
   FreeAndNil(FStroker);
@@ -1536,6 +1566,8 @@ procedure TVectorOriginal.Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMat
 var
   i: Integer;
   idxSelected: LongInt;
+  clipRectF,allRectF: TRectF;
+  mOfs: TAffineMatrix;
 begin
   if AMatrix <> FFrozenShapeMatrix then DiscardFrozenShapes;
   idxSelected := FShapes.IndexOf(FSelectedShape);
@@ -1544,12 +1576,16 @@ begin
     FSelectedShape := nil;
     DiscardFrozenShapes;
   end;
+  with ADest.ClipRect do
+    clipRectF := RectF(Left,Top,Right,Bottom);
+  mOfs := AffineMatrixTranslation(ARenderOffset.X,ARenderOffset.Y)*AMatrix;
   if FFrozenShapesComputed then
   begin
     ADest.PutImage(ARenderOffset.X-FFrozenShapesRenderOffset.X,
                    ARenderOffset.Y-FFrozenShapesRenderOffset.Y,
                    FFrozenShapesUnderSelection, dmSet);
-    FSelectedShape.Render(ADest, ARenderOffset, AMatrix, ADraft);
+    if FSelectedShape.GetRenderBounds(ADest.ClipRect, mOfs, []).IntersectsWith(clipRectF) then
+      FSelectedShape.Render(ADest, ARenderOffset, AMatrix, ADraft);
     ADest.PutImage(ARenderOffset.X-FFrozenShapesRenderOffset.X,
                    ARenderOffset.Y-FFrozenShapesRenderOffset.Y,
                    FFrozenShapesOverSelection, dmDrawWithTransparency);
@@ -1557,21 +1593,25 @@ begin
   begin
     if idxSelected <> -1 then
     begin
+      allRectF := rectF(0,0,ADest.Width,ADest.Height);
       if idxSelected > 0 then
       begin
         FreeAndNil(FFrozenShapesUnderSelection);
         FFrozenShapesUnderSelection := TBGRABitmap.Create(ADest.Width,ADest.Height);
         for i:= 0 to idxSelected-1 do
-          FShapes[i].Render(FFrozenShapesUnderSelection, ARenderOffset, AMatrix, false);
+          if FShapes[i].GetRenderBounds(rect(0,0,ADest.Width,ADest.Height), mOfs, []).IntersectsWith(allRectF) then
+            FShapes[i].Render(FFrozenShapesUnderSelection, ARenderOffset, AMatrix, false);
         ADest.PutImage(0,0,FFrozenShapesUnderSelection, dmSet);
       end;
-      FSelectedShape.Render(ADest, ARenderOffset, AMatrix, ADraft);
+      if FSelectedShape.GetRenderBounds(ADest.ClipRect, mOfs, []).IntersectsWith(clipRectF) then
+        FSelectedShape.Render(ADest, ARenderOffset, AMatrix, ADraft);
       if idxSelected < FShapes.Count-1 then
       begin
         FreeAndNil(FFrozenShapesOverSelection);
         FFrozenShapesOverSelection := TBGRABitmap.Create(ADest.Width,ADest.Height);
         for i:= idxSelected+1 to FShapes.Count-1 do
-          FShapes[i].Render(FFrozenShapesOverSelection, ARenderOffset, AMatrix, false);
+          if FShapes[i].GetRenderBounds(rect(0,0,ADest.Width,ADest.Height), mOfs, []).IntersectsWith(allRectF) then
+            FShapes[i].Render(FFrozenShapesOverSelection, ARenderOffset, AMatrix, false);
         ADest.PutImage(0,0,FFrozenShapesOverSelection, dmDrawWithTransparency);
       end;
       FFrozenShapesRenderOffset := ARenderOffset;
@@ -1580,7 +1620,8 @@ begin
     end else
     begin
       for i:= 0 to FShapes.Count-1 do
-        FShapes[i].Render(ADest, ARenderOffset, AMatrix, ADraft);
+        if FShapes[i].GetRenderBounds(ADest.ClipRect, mOfs, []).IntersectsWith(clipRectF) then
+          FShapes[i].Render(ADest, ARenderOffset, AMatrix, ADraft);
     end;
   end;
   DiscardUnusedRenderStorage;
@@ -1651,8 +1692,7 @@ var
   nb: LongInt;
   i: Integer;
   shapeObj, texObj: TBGRACustomOriginalStorage;
-  objClassName, texName: String;
-  shapeClass: TVectorShapeAny;
+  texName: String;
   loadedShape: TVectorShape;
   idList: array of single;
   mem: TMemoryStream;
@@ -1694,12 +1734,7 @@ begin
     shapeObj := AStorage.OpenObject('shape'+inttostr(i+1));
     if shapeObj <> nil then
     try
-      objClassName := shapeObj.RawString['class'];
-      if objClassName = '' then raise exception.Create('Shape class not defined');
-      shapeClass:= GetVectorShapeByStorageClassName(objClassName);
-      if shapeClass = nil then raise exception.Create('Unknown shape class "'+objClassName+'"');
-      loadedShape := shapeClass.Create(self);
-      loadedShape.LoadFromStorage(shapeObj);
+      loadedShape := TVectorShape.CreateFromStorage(shapeObj, self);
       loadedShape.OnChange := @OnShapeChange;
       loadedShape.OnEditingChange := @OnShapeEditingChange;
       if loadedShape.Id > FLastShapeId then FLastShapeId := loadedShape.Id;

+ 174 - 10
lazpaintcontrols/lcvectorpolyshapes.pas

@@ -8,6 +8,19 @@ uses
   Classes, SysUtils, Types, LCVectorOriginal, BGRABitmapTypes, BGRALayerOriginal,
   BGRABitmap, BGRATransform, BGRAGradients;
 
+type
+  TArrowKind = (akNone, akTail, akTip, akNormal, akCut, akFlipped, akFlippedCut,
+                akTriangle, akTriangleBack1, akTriangleBack2,
+                akHollowTriangle, akHollowTriangleBack1, akHollowTriangleBack2);
+
+const
+  ArrowKindToStr: array[TArrowKind] of string =
+    ('none', 'tail', 'tip', 'normal', 'cut', 'flipped', 'flipped-cut',
+     'triangle', 'triangle-back1', 'triangle-back2',
+     'hollow-triangle', 'hollow-triangle-back1', 'hollow-triangle-back2');
+
+function StrToArrowKind(AStr: string): TArrowKind;
+
 type
   { TCustomPolypointShape }
 
@@ -16,6 +29,9 @@ type
     FClosed: boolean;
     function GetPoint(AIndex: integer): TPointF;
     function GetPointCount: integer;
+    procedure SetArrowEndKind(AValue: TArrowKind);
+    procedure SetArrowSize(AValue: TPointF);
+    procedure SetArrowStartKind(AValue: TArrowKind);
     procedure SetPoint(AIndex: integer; AValue: TPointF);
   protected
     FPoints: array of record
@@ -29,6 +45,8 @@ type
     FAddingPoint: boolean;
     FMousePos: TPointF;
     FHoverPoint: integer;
+    FArrowStartKind,FArrowEndKind: TArrowKind;
+    FArrowSize: TPointF;
     procedure OnMovePoint({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; {%H-}AShift: TShiftState);
     procedure OnMoveCenterPoint({%H-}ASender: TObject; {%H-}APrevCoord, ANewCoord: TPointF; {%H-}AShift: TShiftState);
     procedure OnStartMove({%H-}ASender: TObject; APointIndex: integer; {%H-}AShift: TShiftState);
@@ -42,11 +60,13 @@ type
     procedure DoClickPoint({%H-}APointIndex: integer; {%H-}AShift: TShiftState); virtual;
     function CanMovePoints: boolean; virtual;
     procedure InsertPointAuto;
+    function ComputeStroke(APoints: ArrayOfTPointF; AClosed: boolean;
+      AStrokeMatrix: TAffineMatrix): ArrayOfTPointF; override;
   public
     constructor Create(AContainer: TVectorOriginal); override;
     procedure Clear;
     destructor Destroy; override;
-    function AddPoint(const APoint: TPointF): integer;
+    function AddPoint(const APoint: TPointF): integer; virtual;
     function RemovePoint(AIndex: integer): boolean;
     procedure RemovePointRange(AFromIndex, AToIndexPlus1: integer);
     procedure InsertPoint(AIndex: integer; APoint: TPointF);
@@ -57,11 +77,16 @@ type
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
+    procedure Transform(AMatrix: TAffineMatrix); override;
     class function Usermodes: TVectorShapeUsermodes; override;
+    class function DefaultArrowSize: TPointF;
     property Points[AIndex:integer]: TPointF read GetPoint write SetPoint;
     property PointCount: integer read GetPointCount;
     property Closed: boolean read GetClosed write SetClosed;
     property HoverPoint: integer read FHoverPoint;
+    property ArrowStartKind: TArrowKind read FArrowStartKind write SetArrowStartKind;
+    property ArrowEndKind: TArrowKind read FArrowEndKind write SetArrowEndKind;
+    property ArrowSize: TPointF read FArrowSize write SetArrowSize;
   end;
 
   { TPolylineShape }
@@ -83,8 +108,10 @@ type
 
   TCurveShape = class(TPolylineShape)
   private
+    FCosineAngle: single;
     FSplineStyle: TSplineStyle;
     function GetCurveMode(AIndex: integer): TEasyBezierCurveMode;
+    procedure SetCosineAngle(AValue: single);
     procedure SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
     procedure SetSplineStyle(AValue: TSplineStyle);
   protected
@@ -94,17 +121,55 @@ type
   public
     class function Usermodes: TVectorShapeUsermodes; override;
     constructor Create(AContainer: TVectorOriginal); override;
+    function AddPoint(const APoint: TPointF): integer; override;
     procedure KeyPress(UTF8Key: string; var AHandled: boolean); override;
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
     procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
     class function StorageClassName: RawByteString; override;
     property SplineStyle: TSplineStyle read FSplineStyle write SetSplineStyle;
     property CurveMode[AIndex: integer]: TEasyBezierCurveMode read GetCurveMode write SetCurveMode;
+    property CosineAngle: single read FCosineAngle write SetCosineAngle;
   end;
 
+procedure ApplyArrowStyle(AArrow: TBGRACustomArrow; AStart: boolean; AKind: TArrowKind; ASize: TPointF);
+
 implementation
 
-uses BGRAPen, BGRAGraphics, BGRAFillInfo, BGRAPath, math, LCVectorialFill;
+uses BGRAPen, BGRAGraphics, BGRAFillInfo, BGRAPath, math, LCVectorialFill,
+  BGRAArrow;
+
+function StrToArrowKind(AStr: string): TArrowKind;
+var
+  ak: TArrowKind;
+begin
+  for ak := low(TArrowKind) to high(TArrowKind) do
+    if CompareText(AStr, ArrowKindToStr[ak])=0 then exit(ak);
+  result := akNone;
+end;
+
+procedure ApplyArrowStyle(AArrow: TBGRACustomArrow; AStart: boolean; AKind: TArrowKind; ASize: TPointF);
+var backOfs: single;
+begin
+  backOfs := 0;
+  if (ASize.x = 0) or (ASize.y = 0) then AKind := akNone;
+  if AKind in[akTriangleBack1,akHollowTriangleBack1] then backOfs := 0.25;
+  if AKind in[akTriangleBack2,akHollowTriangleBack2] then backOfs := 0.50;
+  case AKind of
+  akTail: if AStart then AArrow.StartAsTail else AArrow.EndAsTail;
+  akTip: if AStart then AArrow.StartAsTriangle else AArrow.EndAsTriangle;
+  akNormal,akCut,akFlipped,akFlippedCut:
+    if AStart then AArrow.StartAsClassic(AKind in[akFlipped,akFlippedCut], AKind in[akCut,akFlippedCut])
+    else AArrow.EndAsClassic(AKind in[akFlipped,akFlippedCut], AKind in[akCut,akFlippedCut]);
+  akTriangle,akTriangleBack1,akTriangleBack2:
+    if AStart then AArrow.StartAsTriangle(backOfs) else AArrow.EndAsTriangle(backOfs);
+  akHollowTriangle,akHollowTriangleBack1,akHollowTriangleBack2:
+    if AStart then AArrow.StartAsTriangle(backOfs,False,True) else AArrow.EndAsTriangle(backOfs,False,True);
+  else if AStart then AArrow.StartAsNone else AArrow.EndAsNone;
+  end;
+  if (AKind = akTip) and not ((ASize.x = 0) or (ASize.y = 0)) then
+    ASize := ASize*(0.5/ASize.y);
+  if AStart then AArrow.StartSize := ASize else AArrow.EndSize := ASize;
+end;
 
 procedure IncludePointF(var ARectF: TRectF; APointF: TPointF);
 begin
@@ -153,6 +218,30 @@ begin
   result:= length(FPoints);
 end;
 
+procedure TCustomPolypointShape.SetArrowEndKind(AValue: TArrowKind);
+begin
+  if FArrowEndKind=AValue then Exit;
+  BeginUpdate;
+  FArrowEndKind:=AValue;
+  EndUpdate;
+end;
+
+procedure TCustomPolypointShape.SetArrowSize(AValue: TPointF);
+begin
+  if FArrowSize=AValue then Exit;
+  BeginUpdate;
+  FArrowSize:=AValue;
+  EndUpdate;
+end;
+
+procedure TCustomPolypointShape.SetArrowStartKind(AValue: TArrowKind);
+begin
+  if FArrowStartKind=AValue then Exit;
+  BeginUpdate;
+  FArrowStartKind:=AValue;
+  EndUpdate;
+end;
+
 procedure TCustomPolypointShape.SetClosed(AValue: boolean);
 begin
   if AValue = FClosed then exit;
@@ -234,6 +323,11 @@ begin
   Result:= inherited Usermodes + [vsuCreate];
 end;
 
+class function TCustomPolypointShape.DefaultArrowSize: TPointF;
+begin
+  result := PointF(2,2);
+end;
+
 procedure TCustomPolypointShape.SetUsermode(AValue: TVectorShapeUsermode);
 var
   add: Boolean;
@@ -355,6 +449,21 @@ begin
   end;
 end;
 
+function TCustomPolypointShape.ComputeStroke(APoints: ArrayOfTPointF;
+  AClosed: boolean; AStrokeMatrix: TAffineMatrix): ArrayOfTPointF;
+begin
+  if Stroker.Arrow = nil then
+  begin
+    Stroker.Arrow := TBGRAArrow.Create;
+    Stroker.ArrowOwned:= true;
+  end;
+  ApplyArrowStyle(Stroker.Arrow, true, ArrowStartKind, ArrowSize);
+  ApplyArrowStyle(Stroker.Arrow, false, ArrowEndKind, ArrowSize);
+  Result:=inherited ComputeStroke(APoints, AClosed, AStrokeMatrix);
+  Stroker.Arrow.StartAsNone;
+  Stroker.Arrow.EndAsNone;
+end;
+
 constructor TCustomPolypointShape.Create(AContainer: TVectorOriginal);
 begin
   inherited Create(AContainer);
@@ -434,7 +543,10 @@ begin
   FMousePos := PointF(X,Y);
   if FAddingPoint then
   begin
-    Points[PointCount-1] := FMousePos;
+    if (PointCount = 1) and (FMousePos <> Points[PointCount-1]) then
+      Points[PointCount] := FMousePos
+    else
+      Points[PointCount-1] := FMousePos;
     AHandled:= true;
   end;
 end;
@@ -513,6 +625,11 @@ begin
     FPoints[i].data := nil;
   end;
   FClosed:= AStorage.Bool['closed'];
+  if AStorage.HasAttribute('arrow-size') then
+    FArrowSize := AStorage.PointF['arrow-size']
+  else FArrowSize := DefaultArrowSize;
+  FArrowStartKind:= StrToArrowKind(AStorage.RawString['arrow-start-kind']);
+  FArrowEndKind:= StrToArrowKind(AStorage.RawString['arrow-end-kind']);
   EndUpdate;
 end;
 
@@ -532,6 +649,12 @@ begin
   AStorage.FloatArray['x'] := x;
   AStorage.FloatArray['y'] := y;
   AStorage.Bool['closed'] := Closed;
+  if ArrowStartKind=akNone then AStorage.RemoveAttribute('arrow-start-kind')
+  else AStorage.RawString['arrow-start-kind'] := ArrowKindToStr[ArrowStartKind];
+  if ArrowEndKind=akNone then AStorage.RemoveAttribute('arrow-end-kind')
+  else AStorage.RawString['arrow-end-kind'] := ArrowKindToStr[ArrowEndKind];
+  if (ArrowStartKind=akNone) and (ArrowEndKind=akNone) then AStorage.RemoveAttribute('arrow-size')
+  else AStorage.PointF['arrow-size'] := FArrowSize;
 end;
 
 procedure TCustomPolypointShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
@@ -546,7 +669,7 @@ begin
   for i:= 0 to PointCount-1 do
     if isEmptyPointF(Points[i]) then
       FPoints[i].editorIndex := -1
-    else if (FAddingPoint and ((i = 0) or (i = PointCount-1))) then
+    else if (FAddingPoint and (i = PointCount-1)) then
     begin
       FPoints[i].editorIndex := -1;
       FCenterPoint += Points[i];
@@ -569,6 +692,17 @@ begin
   end;
 end;
 
+procedure TCustomPolypointShape.Transform(AMatrix: TAffineMatrix);
+var
+  i: Integer;
+begin
+  BeginUpdate;
+  inherited Transform(AMatrix);
+  for i := 0 to PointCount-1 do
+    FPoints[i].coord := AMatrix*FPoints[i].coord;
+  EndUpdate;
+end;
+
 { TPolylineShape }
 
 function TPolylineShape.PenVisible(AAssumePenFill: boolean): boolean;
@@ -648,7 +782,7 @@ begin
     pts := GetCurve(AMatrix);
     if PenVisible(rboAssumePenFill in AOptions) then
     begin
-      if JoinStyle = pjsRound then
+      if (JoinStyle = pjsRound) and (ArrowStartKind = akNone) and (ArrowEndKind = akNone) 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;
@@ -716,6 +850,14 @@ begin
     result := cmAuto;
 end;
 
+procedure TCurveShape.SetCosineAngle(AValue: single);
+begin
+  if FCosineAngle=AValue then Exit;
+  BeginUpdate;
+  FCosineAngle:=AValue;
+  EndUpdate;
+end;
+
 procedure TCurveShape.SetCurveMode(AIndex: integer; AValue: TEasyBezierCurveMode);
 begin
   if (AIndex < 0) or (AIndex >= PointCount) then exit;
@@ -739,7 +881,7 @@ begin
     setlength(cm, PointCount);
     for i := 0 to PointCount-1 do
       cm[i] := CurveMode[i];
-    eb := EasyBezierCurve(pts, Closed, cm);
+    eb := EasyBezierCurve(pts, Closed, cm, CosineAngle);
     result := eb.ToPoints;
   end else
   begin
@@ -775,23 +917,43 @@ begin
   FSplineStyle:= ssEasyBezier;
 end;
 
+function TCurveShape.AddPoint(const APoint: TPointF): integer;
+begin
+  if (PointCount > 1) and (APoint = Points[PointCount-1]) then
+  begin
+    BeginUpdate;
+    CurveMode[PointCount-1] := CurveMode[PointCount-2];
+    Result:=inherited AddPoint(APoint);
+    EndUpdate;
+  end
+  else Result:=inherited AddPoint(APoint);
+end;
+
 procedure TCurveShape.KeyPress(UTF8Key: string; var AHandled: boolean);
+var
+  targetPoint: Integer;
 begin
-  if (FHoverPoint >= 0) and (FHoverPoint < PointCount) then
+  if FHoverPoint<>-1 then
+    targetPoint := FHoverPoint
+  else if FAddingPoint and (PointCount > 1) then
+    targetPoint := PointCount-2
+  else
+    targetPoint := -1;
+  if (targetPoint >= 0) and (targetPoint < PointCount) then
   begin
     if (UTF8Key = 'A') or (UTF8Key = 'a') then
     begin
-      CurveMode[FHoverPoint] := cmAuto;
+      CurveMode[targetPoint] := cmAuto;
       AHandled := true;
     end else
     if (UTF8Key = 'S') or (UTF8Key = 's') then
     begin
-      CurveMode[FHoverPoint] := cmCurve;
+      CurveMode[targetPoint] := cmCurve;
       AHandled:= true;
     end else
     if (UTF8Key = 'X') or (UTF8Key = 'x') then
     begin
-      CurveMode[FHoverPoint] := cmAngle;
+      CurveMode[targetPoint] := cmAngle;
       AHandled:= true;
     end;
   end;
@@ -829,6 +991,7 @@ begin
       for i:= length(cm) to PointCount-1 do
         CurveMode[i] := cmCurve;
   end;
+  CosineAngle := AStorage.FloatDef['cosine-angle', EasyBezierDefaultMinimumDotProduct];
   EndUpdate;
 end;
 
@@ -857,6 +1020,7 @@ begin
       cm[i] := ord(CurveMode[i]);
     AStorage.FloatArray['curve-mode'] := cm;
   end;
+  AStorage.Float['cosine-angle'] := CosineAngle;
 end;
 
 class function TCurveShape.StorageClassName: RawByteString;

+ 61 - 35
lazpaintcontrols/lcvectorrectshapes.pas

@@ -50,6 +50,7 @@ type
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; {%H-}AOptions: TRenderBoundsOptions = []): TRectF; override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     function GetAffineBox(AMatrix: TAffineMatrix; APixelCentered: boolean): TAffineBox;
+    procedure Transform(AMatrix: TAffineMatrix); override;
     property Origin: TPointF read FOrigin write SetOrigin;
     property XAxis: TPointF read FXAxis;
     property YAxis: TPointF read FYAxis;
@@ -130,6 +131,7 @@ type
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; override;
     function GetIsSlow(AMatrix: TAffineMatrix): boolean; override;
+    procedure Transform(AMatrix: TAffineMatrix); override;
     class function StorageClassName: RawByteString; override;
     property ShapeKind: TPhongShapeKind read FShapeKind write SetShapeKind;
     property LightPosition: TPointF read FLightPosition write SetLightPosition;
@@ -146,17 +148,17 @@ uses BGRAPen, BGRAGraphics, BGRAFillInfo, BGRAPath, math, LCVectorialFill;
 procedure TCustomRectShape.SetOrigin(AValue: TPointF);
 var
   delta: TPointF;
+  t: TAffineMatrix;
 begin
   if FOrigin=AValue then Exit;
   BeginUpdate;
   delta := AValue - FOrigin;
+  t := AffineMatrixTranslation(delta.x, delta.y);
   FOrigin := AValue;
-  FXAxis += delta;
-  FYAxis += delta;
-  if vsfBackFill in Fields then
-    BackFill.Transform(AffineMatrixTranslation(delta.x, delta.y));
-  if vsfPenFill in Fields then
-    PenFill.Transform(AffineMatrixTranslation(delta.x, delta.y));
+  FXAxis := t*FXAxis;
+  FYAxis := t*FYAxis;
+  if vsfBackFill in Fields then BackFill.Transform(t);
+  if vsfPenFill in Fields then PenFill.Transform(t);
   EndUpdate;
 end;
 
@@ -346,18 +348,22 @@ begin
   end else
   begin
     d := GetCornerPositition;
-    m := AffineMatrixInverse(AffineMatrix(AFactorX*FXUnitBackup*d,AFactorY*FYUnitBackup*d,FOriginBackup));
-    newSize := m*ANewCoord;
-    if (ssShift in AShift) and (FXSizeBackup <> 0) and (FYSizeBackup <> 0) then
+    m := AffineMatrix(AFactorX*FXUnitBackup*d,AFactorY*FYUnitBackup*d,FOriginBackup);
+    if IsAffineMatrixInversible(m) then
     begin
-      ratio := (newSize.X/FXSizeBackup + newSize.Y/FYSizeBackup)/2;
-      newSize.X := ratio*FXSizeBackup;
-      newSize.Y := ratio*FYSizeBackup;
+      m := AffineMatrixInverse(m);
+      newSize := m*ANewCoord;
+      if (ssShift in AShift) and (FXSizeBackup <> 0) and (FYSizeBackup <> 0) then
+      begin
+        ratio := (newSize.X/FXSizeBackup + newSize.Y/FYSizeBackup)/2;
+        newSize.X := ratio*FXSizeBackup;
+        newSize.Y := ratio*FYSizeBackup;
+      end;
+      FXAxis := FXAxisBackup + (AFactorX+1)*0.5*sqrt(d)*(newSize.X-FXSizeBackup)*FXUnitBackup + AFactorY*(newSize.Y-FYSizeBackup)*0.5*sqrt(d)*FYUnitBackup;
+      FYAxis := FYAxisBackup + (AFactorY+1)*0.5*sqrt(d)*(newSize.Y-FYSizeBackup)*FYUnitBackup + AFactorX*(newSize.X-FXSizeBackup)*0.5*sqrt(d)*FXUnitBackup;
+      FOrigin := FOriginBackup + AFactorX*(newSize.X-FXSizeBackup)*0.5*sqrt(d)*FXUnitBackup
+                               + AFactorY*(newSize.Y-FYSizeBackup)*0.5*sqrt(d)*FYUnitBackup;
     end;
-    FXAxis := FXAxisBackup + (AFactorX+1)*0.5*sqrt(d)*(newSize.X-FXSizeBackup)*FXUnitBackup + AFactorY*(newSize.Y-FYSizeBackup)*0.5*sqrt(d)*FYUnitBackup;
-    FYAxis := FYAxisBackup + (AFactorY+1)*0.5*sqrt(d)*(newSize.Y-FYSizeBackup)*FYUnitBackup + AFactorX*(newSize.X-FXSizeBackup)*0.5*sqrt(d)*FXUnitBackup;
-    FOrigin := FOriginBackup + AFactorX*(newSize.X-FXSizeBackup)*0.5*sqrt(d)*FXUnitBackup
-                             + AFactorY*(newSize.Y-FYSizeBackup)*0.5*sqrt(d)*FYUnitBackup;
   end;
   EnsureRatio(-AFactorX,-AFactorY);
   EndUpdate;
@@ -443,6 +449,16 @@ begin
       FXAxis - (FYAxis - FOrigin), FYAxis - (FXAxis - FOrigin));
 end;
 
+procedure TCustomRectShape.Transform(AMatrix: TAffineMatrix);
+begin
+  BeginUpdate;
+  FOrigin := AMatrix*FOrigin;
+  FXAxis := AMatrix*FXAxis;
+  FYAxis := AMatrix*FYAxis;
+  inherited Transform(AMatrix);
+  EndUpdate;
+end;
+
 function TCustomRectShape.GetOrthoRect(AMatrix: TAffineMatrix; out ARect: TRectF): boolean;
 var
   sx,sy: single;
@@ -525,6 +541,14 @@ begin
   u := FXAxis - FOrigin;
   v := FYAxis - FOrigin;
   AEditor.AddStartMoveHandler(@OnStartMove);
+  d := GetCornerPositition;
+  if d <> 0 then
+  begin
+    AEditor.AddPoint(FOrigin + (u+v)*d, @OnMoveXYCorner, false);
+    AEditor.AddPoint(FOrigin + (-u+v)*d, @OnMoveXNegYCorner, false);
+    AEditor.AddPoint(FOrigin + (u-v)*d, @OnMoveXYNegCorner, false);
+    AEditor.AddPoint(FOrigin + (-u-v)*d, @OnMoveXNegYNegCorner, false);
+  end;
   if ShowArrows then
   begin
     idxX := AEditor.AddArrow(FOrigin, FXAxis, @OnMoveXAxis);
@@ -538,14 +562,6 @@ begin
     idxXNeg := AEditor.AddPoint(FOrigin - u, @OnMoveXAxisNeg);
     idxYNeg := AEditor.AddPoint(FOrigin - v, @OnMoveYAxisNeg);
   end;
-  d := GetCornerPositition;
-  if d <> 0 then
-  begin
-    AEditor.AddPoint(FOrigin + (u+v)*d, @OnMoveXYCorner, false);
-    AEditor.AddPoint(FOrigin + (-u+v)*d, @OnMoveXNegYCorner, false);
-    AEditor.AddPoint(FOrigin + (u-v)*d, @OnMoveXYNegCorner, false);
-    AEditor.AddPoint(FOrigin + (-u-v)*d, @OnMoveXNegYNegCorner, false);
-  end;
   idxOrig := AEditor.AddPoint(FOrigin, @OnMoveOrigin, true);
   if ShowArrows then
   begin
@@ -1144,7 +1160,7 @@ var
   shader: TPhongShading;
   approxFactor,borderSize: single;
   m: TAffineMatrix;
-  h: single;
+  h, lightPosZ: single;
   map,raster: TBGRABitmap;
   u,v,lightPosF: TPointF;
   scan: TBGRACustomScanner;
@@ -1176,8 +1192,12 @@ begin
   approxFactor := 1;
   if ADraft then
   begin
-    if mapWidth > 300 then approxFactor:= min(approxFactor, 300/mapWidth);
-    if mapHeight > 300 then approxFactor:= min(approxFactor, 300/mapHeight);
+    if mapWidth > 100 then approxFactor:= min(approxFactor, 100/mapWidth);
+    if mapHeight > 100 then approxFactor:= min(approxFactor, 100/mapHeight);
+  end else
+  begin
+    if mapWidth > 800 then approxFactor:= min(approxFactor, 800/mapWidth);
+    if mapHeight > 800 then approxFactor:= min(approxFactor, 800/mapHeight);
   end;
   mapWidth:= ceil(mapWidth*approxFactor);
   mapHeight:= ceil(mapHeight*approxFactor);
@@ -1240,20 +1260,18 @@ begin
       shader.AmbientFactor := 0.5;
       shader.NegativeDiffusionFactor := 0.15;
       lightPosF := AffineMatrixTranslation(-rectRaster.Left,-rectRaster.Top)
-                    *AffineMatrixInverse(m)*AMatrix
-                    *PointF(FLightPosition.x,FLightPosition.y);
-      shader.LightPosition := Point(round(lightPosF.x),round(lightPosF.y));
-      shader.LightPositionZ := round(100*power(approxFactor,1.18));
-      if h*3/2 > shader.LightPositionZ then
-       shader.LightPositionZ := round(h*3/2);
+                    *AffineMatrixInverse(m)*AMatrix*FLightPosition;
+      lightPosZ := 100*Power(approxFactor,1.1);
+      if h*3/2 > lightPosZ then lightposZ := h*3/2;
+      shader.LightPosition3D := Point3D(lightPosF.x,lightPosF.y,lightPosZ);
 
       raster := TBGRABitmap.Create(rectRaster.Width,rectRaster.Height);
       if BackFill.FillType = vftSolid then
-        shader.Draw(raster,map,round(h),-rectRaster.Left,-rectRaster.Top,BackFill.SolidColor)
+        shader.Draw(raster,map,h,-rectRaster.Left,-rectRaster.Top,BackFill.SolidColor)
       else
       begin
         scan := BackFill.CreateScanner(AffineMatrixTranslation(-rectRaster.left,-rectRaster.top)*AffineMatrixInverse(m)*AMatrix,ADraft);
-        shader.DrawScan(raster,map,round(h),-rectRaster.Left,-rectRaster.Top,scan);
+        shader.DrawScan(raster,map,h,-rectRaster.Left,-rectRaster.Top,scan);
         scan.Free;
       end;
 
@@ -1314,6 +1332,14 @@ begin
   result := ab.Surface > 320*240;
 end;
 
+procedure TPhongShape.Transform(AMatrix: TAffineMatrix);
+begin
+  BeginUpdate;
+  inherited Transform(AMatrix);
+  LightPosition := AMatrix*LightPosition;
+  EndUpdate;
+end;
+
 class function TPhongShape.StorageClassName: RawByteString;
 begin
   result := 'phong';

+ 87 - 11
lazpaintcontrols/lcvectortextshapes.pas

@@ -8,6 +8,9 @@ uses
   Classes, SysUtils, LCVectorRectShapes, BGRATextBidi, BGRABitmapTypes, LCVectorOriginal, BGRAGraphics,
   BGRABitmap, BGRALayerOriginal, BGRACanvas2D;
 
+const
+  AlwaysVectorialText = true;
+
 type
 
   { TTextShape }
@@ -23,6 +26,9 @@ type
     FLightPosition: TPointF;
     FText: string;
     FSelStart,FSelEnd: integer;
+    FEnteringUnicode: boolean;
+    FUnicodeValue: cardinal;
+    FUnicodeDigitCount: integer;
     FMouseSelecting: boolean;
     FVertAlign: TTextLayout;
     function GetBidiParagraphAlignment: TBidiTextAlignment;
@@ -53,6 +59,7 @@ type
     function ShowArrows: boolean; override;
     function GetTextLayout: TBidiTextLayout;
     function GetFontRenderer: TBGRACustomFontRenderer;
+    function UseVectorialTextRenderer: boolean;
     function UpdateFontRenderer: boolean;
     function GetTextRenderZoom: single;
     function GetUntransformedMatrix: TAffineMatrix; //matrix before render transform
@@ -65,6 +72,7 @@ type
     procedure InsertText(ATextUTF8: string);
     procedure SelectWithMouse(X,Y: single; AExtend: boolean);
     function HasOutline: boolean;
+    procedure InsertUnicodeValue;
   public
     constructor Create(AContainer: TVectorOriginal); override;
     procedure QuickDefine(const APoint1,APoint2: TPointF); override;
@@ -89,10 +97,12 @@ type
     procedure MouseUp({%H-}RightButton: boolean; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
     procedure KeyDown({%H-}Shift: TShiftState; {%H-}Key: TSpecialKey; var {%H-}AHandled: boolean); override;
     procedure KeyPress({%H-}UTF8Key: string; var {%H-}AHandled: boolean); override;
+    procedure KeyUp({%H-}Shift: TShiftState; {%H-}Key: TSpecialKey; var {%H-}AHandled: boolean); override;
     procedure SetFontNameAndStyle(AFontName: string; AFontStyle: TFontStyles);
     function CopySelection: boolean;
     function CutSelection: boolean;
     function PasteSelection: boolean;
+    procedure Transform(AMatrix: TAffineMatrix); override;
     property HasSelection: boolean read GetHasSelection;
     property CanPasteSelection: boolean read GetCanPasteSelection;
     property Text: string read FText write SetText;
@@ -364,7 +374,7 @@ begin
   if Assigned(FFontRenderer) then
   begin
     freeRenderer := false;
-    if OutlineFill.FillType <> vftNone then
+    if UseVectorialTextRenderer then
     begin
       if not (FFontRenderer is TBGRAVectorizedFontRenderer) then
         freeRenderer:= true;
@@ -428,27 +438,37 @@ begin
   result := FFontRenderer;
 end;
 
+function TTextShape.UseVectorialTextRenderer: boolean;
+begin
+  result := AlwaysVectorialText or HasOutline;
+end;
+
 function TTextShape.UpdateFontRenderer: boolean;
 var
-  newEmHeight: integer;
+  newEmHeight: single;
 begin
   if FFontRenderer = nil then
   begin
-    if OutlineFill.FillType <> vftNone then
-      FFontRenderer := TBGRAVectorizedFontRenderer.Create
+    if UseVectorialTextRenderer then
+    begin
+      FFontRenderer := TBGRAVectorizedFontRenderer.Create;
+      TBGRAVectorizedFontRenderer(FFontRenderer).QuadraticCurves := true;
+      TBGRAVectorizedFontRenderer(FFontRenderer).MinFontResolution := 300;
+      TBGRAVectorizedFontRenderer(FFontRenderer).MaxFontResolution := 300;
+    end
     else
     begin
       FFontRenderer := TLCLFontRenderer.Create;
       TLCLFontRenderer(FFontRenderer).OverrideUnderlineDecoration:= true;
     end;
   end;
-  newEmHeight := Round(FontEmHeight*GetTextRenderZoom);
+  newEmHeight := FontEmHeight*GetTextRenderZoom;
   if (newEmHeight <> FFontRenderer.FontEmHeight) or
      (FFontRenderer.FontName <> FontName) or
      (FFontRenderer.FontStyle <> FontStyle) or
      (FFontRenderer.FontQuality <> fqFineAntialiasing) then
   begin
-    FFontRenderer.FontEmHeight := newEmHeight;
+    FFontRenderer.FontEmHeightF := newEmHeight;
     FFontRenderer.FontName:= FontName;
     FFontRenderer.FontStyle:= FontStyle;
     FFontRenderer.FontQuality:= fqFineAntialiasing;
@@ -597,6 +617,15 @@ begin
   result := not OutlineFill.IsFullyTransparent and (OutlineWidth > 0);
 end;
 
+procedure TTextShape.InsertUnicodeValue;
+begin
+  if FEnteringUnicode then
+  begin
+    InsertText(UnicodeCharToUTF8(FUnicodeValue));
+    FEnteringUnicode:= false;
+  end;
+end;
+
 constructor TTextShape.Create(AContainer: TVectorOriginal);
 begin
   inherited Create(AContainer);
@@ -835,13 +864,16 @@ procedure TTextShape.Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix:
   end;
 
   function CreateShader(AOfsX,AOfsY: integer): TPhongShading;
+  var
+    lightPosF: TPointF;
+    lightPosZ: Single;
   begin
     result := TPhongShading.Create;
     result.AmbientFactor := 0.6;
     result.NegativeDiffusionFactor := 0.15;
-    result.LightPosition := Point(round(LightPosition.x)+AOfsX+ARenderOffset.X,
-                                  round(LightPosition.y)+AOfsY+ARenderOffset.Y);
-    result.LightPositionZ := round(max(AltitudePercent, 1.2*GetTextPhongHeight));
+    lightPosF := FGlobalMatrix*LightPosition+PointF(AOfsX,AOfsY);
+    lightPosZ := max(AltitudePercent, 1.2*GetTextPhongHeight);
+    result.LightPosition3D := Point3D(lightPosF.x,lightPosF.y,lightPosZ);
   end;
 
 var
@@ -923,7 +955,7 @@ begin
   with transfRectF do
     transfRect := Rect(floor(Left),floor(Top),ceil(Right),ceil(Bottom));
 
-  if HasOutline then
+  if UseVectorialTextRenderer then
   begin
     tmpTransf := TBGRABitmap.Create(transfRect.Width,transfRect.Height);
     ctx := tmpTransf.Canvas2D;
@@ -952,7 +984,7 @@ begin
     end else
       textFx := nil;
 
-    if OutLineFill.FillType <> vftNone then
+    if HasOutline then
     begin
       ctx := tmpTransf.Canvas2D;
       ctx.lineWidth := OutlineWidth;
@@ -1243,6 +1275,11 @@ begin
     EndEditingUpdate;
     AHandled := true;
   end else
+  if (Key = skReturn) and ([ssCtrl,ssShift] <= Shift) and FEnteringUnicode then
+  begin
+    InsertUnicodeValue;
+    AHandled:= true;
+  end else
   if Key = skReturn then
   begin
     if ssShift in Shift then
@@ -1256,6 +1293,26 @@ begin
     InsertText(#9);
     AHandled := true;
   end else
+  if (Key = skU) and ([ssCtrl,ssShift] <= Shift) then
+  begin
+    if FEnteringUnicode then InsertUnicodeValue;
+    FEnteringUnicode:= true;
+    FUnicodeValue:= 0;
+    FUnicodeDigitCount:= 0;
+    AHandled := true;
+  end else
+  if (Key in[sk0..sk9,skNum0..skNum9,skA..skF]) and ([ssCtrl,ssShift] <= Shift) and FEnteringUnicode then
+  begin
+    if FUnicodeDigitCount >= 8 then FEnteringUnicode:= false else
+    begin
+      FUnicodeValue := (FUnicodeValue shl 4);
+      case Key of
+      sk0..sk9: inc(FUnicodeValue, ord(Key)-ord(sk0));
+      skNum0..skNum9: inc(FUnicodeValue, ord(Key)-ord(sk0));
+      skA..skF: inc(FUnicodeValue, ord(Key)-ord(skA)+10);
+      end;
+    end;
+  end else
   if (Key = skC) and (ssCtrl in Shift) then
   begin
     if CopySelection then AHandled:= true;
@@ -1274,6 +1331,7 @@ begin
     FSelStart:= 0;
     FSelEnd:= GetTextLayout.CharCount;
     EndEditingUpdate;
+    AHandled := true;
   end;
 end;
 
@@ -1300,6 +1358,12 @@ begin
   end;
 end;
 
+procedure TTextShape.KeyUp(Shift: TShiftState; Key: TSpecialKey;
+  var AHandled: boolean);
+begin
+  if (Key in[skCtrl,skShift]) and FEnteringUnicode then InsertUnicodeValue;
+end;
+
 procedure TTextShape.SetFontNameAndStyle(AFontName: string;
   AFontStyle: TFontStyles);
 begin
@@ -1355,6 +1419,18 @@ begin
     result := false;
 end;
 
+procedure TTextShape.Transform(AMatrix: TAffineMatrix);
+var
+  zoom: Single;
+begin
+  BeginUpdate;
+  inherited Transform(AMatrix);
+  zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
+  FontEmHeight:= zoom*FontEmHeight;
+  LightPosition := AMatrix*LightPosition;
+  EndUpdate;
+end;
+
 class function TTextShape.StorageClassName: RawByteString;
 begin
   result := 'text';

+ 8 - 5
vectoredit/umain.pas

@@ -900,12 +900,15 @@ begin
   if Assigned(img) and img.EditorFocused then
   begin
     sk := LCLKeyToSpecialKey(Key, Shift);
-    FSpecialKeyPressed[sk] := true;
-    img.KeyDown(Shift, sk, AHandled);
-    if AHandled then
+    if sk<>skUnknown then
     begin
-      Key := 0;
-      UpdateTextAlignment;
+      FSpecialKeyPressed[sk] := true;
+      img.KeyDown(Shift, sk, AHandled);
+      if AHandled then
+      begin
+        Key := 0;
+        UpdateTextAlignment;
+      end;
     end;
   end;