2
0
Эх сурвалжийг харах

Merge pull request #29 from bgrabitmap/dev-vectorial

Dev vectorial
circular17 6 жил өмнө
parent
commit
87223048bf
57 өөрчлөгдсөн 3883 нэмэгдсэн , 1910 устгасан
  1. 15 7
      lazpaint/lazpaint.lpi
  2. 1 1
      lazpaint/lazpaintinstance.pas
  3. 22 3
      lazpaint/lazpaintmainform.lfm
  4. 104 66
      lazpaint/lazpaintmainform.pas
  5. 2 2
      lazpaint/lazpainttype.pas
  6. 1 0
      lazpaint/maintoolbar.inc
  7. 29 0
      lazpaint/release/i18n/lazpaint.ar.po
  8. 29 0
      lazpaint/release/i18n/lazpaint.cs.po
  9. 29 0
      lazpaint/release/i18n/lazpaint.de.po
  10. 29 0
      lazpaint/release/i18n/lazpaint.es.po
  11. 29 0
      lazpaint/release/i18n/lazpaint.fi.po
  12. 29 0
      lazpaint/release/i18n/lazpaint.fr.po
  13. 29 0
      lazpaint/release/i18n/lazpaint.ja.po
  14. 29 0
      lazpaint/release/i18n/lazpaint.lv.po
  15. 29 0
      lazpaint/release/i18n/lazpaint.nl.po
  16. 29 0
      lazpaint/release/i18n/lazpaint.po
  17. 29 0
      lazpaint/release/i18n/lazpaint.pt_BR.po
  18. 29 0
      lazpaint/release/i18n/lazpaint.ru.po
  19. 29 0
      lazpaint/release/i18n/lazpaint.sv.po
  20. 1 1
      lazpaint/uadjustcurves.pas
  21. 25 11
      lazpaint/ucanvassize.pas
  22. 1 1
      lazpaint/ucolorintensity.pas
  23. 1 1
      lazpaint/ucolorize.pas
  24. 2 2
      lazpaint/ucommandline.pas
  25. 3 3
      lazpaint/uconfig.pas
  26. 3 1
      lazpaint/ufileextensions.pas
  27. 33 13
      lazpaint/ufilterconnector.pas
  28. 10 2
      lazpaint/ufilters.pas
  29. 26 14
      lazpaint/ugraph.pas
  30. 294 323
      lazpaint/uimage.pas
  31. 235 136
      lazpaint/uimageaction.pas
  32. 584 93
      lazpaint/uimagediff.pas
  33. 148 133
      lazpaint/uimagepreview.pas
  34. 565 142
      lazpaint/uimagestate.pas
  35. 5 3
      lazpaint/uimageview.pas
  36. 399 183
      lazpaint/ulayeraction.pas
  37. 106 29
      lazpaint/ulayerstack.pas
  38. 32 2
      lazpaint/uloadimage.pas
  39. 1 1
      lazpaint/umenu.pas
  40. 2 2
      lazpaint/upalettetoolbar.pas
  41. 1 1
      lazpaint/uposterize.pas
  42. 1 0
      lazpaint/uresourcestrings.pas
  43. 35 35
      lazpaint/uscripting.pas
  44. 2 2
      lazpaint/uselectionhighlight.pas
  45. 1 1
      lazpaint/ushiftcolors.pas
  46. 474 300
      lazpaint/ustatetype.pas
  47. 109 82
      lazpaint/utool.pas
  48. 39 67
      lazpaint/utoolbasic.pas
  49. 1 2
      lazpaint/utoolbrush.pas
  50. 30 31
      lazpaint/utooldeformationgrid.pas
  51. 1 1
      lazpaint/utoolfloodfill.pas
  52. 151 78
      lazpaint/utoollayer.pas
  53. 1 4
      lazpaint/utoolphong.pas
  54. 4 20
      lazpaint/utoolpolygon.pas
  55. 24 75
      lazpaint/utoolselect.pas
  56. 2 11
      lazpaint/utooltext.pas
  57. 9 25
      lazpaintcontrols/lctoolbars.pas

+ 15 - 7
lazpaint/lazpaint.lpi

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
 <CONFIG>
   <ProjectOptions>
   <ProjectOptions>
-    <Version Value="10"/>
+    <Version Value="9"/>
     <PathDelim Value="\"/>
     <PathDelim Value="\"/>
     <General>
     <General>
       <SessionStorage Value="InProjectDir"/>
       <SessionStorage Value="InProjectDir"/>
@@ -23,7 +23,7 @@
       <MinorVersionNr Value="4"/>
       <MinorVersionNr Value="4"/>
       <RevisionNr Value="1"/>
       <RevisionNr Value="1"/>
       <CharSet Value="04B0"/>
       <CharSet Value="04B0"/>
-      <StringTable CompanyName="http://sourceforge.net/projects/lazpaint/" ProductName="LazPaint" InternalName="lazpaint" OriginalFilename="lazpaint.exe"/>
+      <StringTable CompanyName="http://sourceforge.net/projects/lazpaint/" ProductName="LazPaint" InternalName="lazpaint" OriginalFilename="lazpaint.exe" ProductVersion=""/>
     </VersionInfo>
     </VersionInfo>
     <BuildModes Count="4">
     <BuildModes Count="4">
       <Item1 Name="Debug" Default="True"/>
       <Item1 Name="Debug" Default="True"/>
@@ -157,19 +157,22 @@
         <LaunchingApplication PathPlusParams="\usr\X11R6\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
         <LaunchingApplication PathPlusParams="\usr\X11R6\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
       </local>
       </local>
     </RunParams>
     </RunParams>
-    <RequiredPackages Count="4">
+    <RequiredPackages Count="5">
       <Item1>
       <Item1>
-        <PackageName Value="BGRABitmapPack"/>
+        <PackageName Value="lazpaintcontrols"/>
       </Item1>
       </Item1>
       <Item2>
       <Item2>
-        <PackageName Value="Printer4Lazarus"/>
+        <PackageName Value="BGRABitmapPack"/>
       </Item2>
       </Item2>
       <Item3>
       <Item3>
-        <PackageName Value="bgracontrols"/>
+        <PackageName Value="Printer4Lazarus"/>
       </Item3>
       </Item3>
       <Item4>
       <Item4>
-        <PackageName Value="LCL"/>
+        <PackageName Value="bgracontrols"/>
       </Item4>
       </Item4>
+      <Item5>
+        <PackageName Value="LCL"/>
+      </Item5>
     </RequiredPackages>
     </RequiredPackages>
     <Units Count="97">
     <Units Count="97">
       <Unit0>
       <Unit0>
@@ -404,6 +407,7 @@
       <Unit36>
       <Unit36>
         <Filename Value="utoolphong.pas"/>
         <Filename Value="utoolphong.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
+        <UnitName Value="UToolPhong"/>
       </Unit36>
       </Unit36>
       <Unit37>
       <Unit37>
         <Filename Value="utooltext.pas"/>
         <Filename Value="utooltext.pas"/>
@@ -460,10 +464,12 @@
         <ComponentName Value="FLoading"/>
         <ComponentName Value="FLoading"/>
         <HasResources Value="True"/>
         <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
         <ResourceBaseClass Value="Form"/>
+        <UnitName Value="ULoading"/>
       </Unit45>
       </Unit45>
       <Unit46>
       <Unit46>
         <Filename Value="ufilterconnector.pas"/>
         <Filename Value="ufilterconnector.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
+        <UnitName Value="UFilterConnector"/>
       </Unit46>
       </Unit46>
       <Unit47>
       <Unit47>
         <Filename Value="uzoom.pas"/>
         <Filename Value="uzoom.pas"/>
@@ -567,6 +573,7 @@
       <Unit65>
       <Unit65>
         <Filename Value="uimagetype.pas"/>
         <Filename Value="uimagetype.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
+        <UnitName Value="UImageType"/>
       </Unit65>
       </Unit65>
       <Unit66>
       <Unit66>
         <Filename Value="uposterize.pas"/>
         <Filename Value="uposterize.pas"/>
@@ -725,6 +732,7 @@
         <Filename Value="uquestion.pas"/>
         <Filename Value="uquestion.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
         <ComponentName Value="FQuestion"/>
         <ComponentName Value="FQuestion"/>
+        <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
         <ResourceBaseClass Value="Form"/>
         <UnitName Value="UQuestion"/>
         <UnitName Value="UQuestion"/>
       </Unit94>
       </Unit94>

+ 1 - 1
lazpaint/lazpaintinstance.pas

@@ -1299,7 +1299,7 @@ begin
     FLayerStack.SetLayerStackScrollPosOnItem(AIndex);
     FLayerStack.SetLayerStackScrollPosOnItem(AIndex);
     if FMain <> nil then
     if FMain <> nil then
     begin
     begin
-      FMain.StackNeedUpdate := true;
+      FMain.UpdateStackOnTimer := true;
     end else
     end else
       NotifyStackChange;
       NotifyStackChange;
   end;
   end;

+ 22 - 3
lazpaint/lazpaintmainform.lfm

@@ -1,7 +1,7 @@
 object FMain: TFMain
 object FMain: TFMain
-  Left = 467
+  Left = 462
   Height = 578
   Height = 578
-  Top = 93
+  Top = 16
   Width = 739
   Width = 739
   AllowDropFiles = True
   AllowDropFiles = True
   Caption = 'LazPaint'
   Caption = 'LazPaint'
@@ -31,7 +31,7 @@ object FMain: TFMain
   OnUTF8KeyPress = FormUTF8KeyPress
   OnUTF8KeyPress = FormUTF8KeyPress
   Position = poDefault
   Position = poDefault
   ShowHint = True
   ShowHint = True
-  LCLVersion = '1.8.0.6'
+  LCLVersion = '1.6.0.4'
   object Panel_ToolbarBackground: TPanel
   object Panel_ToolbarBackground: TPanel
     Left = 8
     Left = 8
     Height = 36
     Height = 36
@@ -5504,6 +5504,7 @@ object FMain: TFMain
     }
     }
   end
   end
   object ActionList1: TActionList
   object ActionList1: TActionList
+    Images = ImageList16
     left = 200
     left = 200
     top = 432
     top = 432
     object FileNew: TAction
     object FileNew: TAction
@@ -6092,6 +6093,7 @@ object FMain: TFMain
     object ImageFlatten: TAction
     object ImageFlatten: TAction
       Category = 'Image'
       Category = 'Image'
       Caption = 'Flatten image'
       Caption = 'Flatten image'
+      ImageIndex = 83
       OnExecute = ImageFlattenExecute
       OnExecute = ImageFlattenExecute
       OnUpdate = ImageFlattenUpdate
       OnUpdate = ImageFlattenUpdate
       ShortCut = 24646
       ShortCut = 24646
@@ -6420,6 +6422,23 @@ object FMain: TFMain
       Hint = 'Forget answers given to dialog boxes'
       Hint = 'Forget answers given to dialog boxes'
       OnExecute = ForgetDialogAnswersExecute
       OnExecute = ForgetDialogAnswersExecute
     end
     end
+    object ImageNegative: TAction
+      Category = 'Image'
+      Caption = 'Negative'
+      ImageIndex = 66
+      OnExecute = ScriptExecute
+    end
+    object ImageLinearNegative: TAction
+      Category = 'Image'
+      Caption = 'Linear negative'
+      ImageIndex = 66
+      OnExecute = ScriptExecute
+    end
+    object ImageSwapRedBlue: TAction
+      Category = 'Image'
+      Caption = 'Swap red and blue channels'
+      OnExecute = ScriptExecute
+    end
   end
   end
   object ColorDialog1: TColorDialog
   object ColorDialog1: TColorDialog
     Title = 'Choose color'
     Title = 'Choose color'

+ 104 - 66
lazpaint/lazpaintmainform.pas

@@ -13,7 +13,7 @@ uses
   Controls, Graphics, Dialogs, Menus, ExtDlgs, ComCtrls, ActnList, StdCtrls,
   Controls, Graphics, Dialogs, Menus, ExtDlgs, ComCtrls, ActnList, StdCtrls,
   ExtCtrls, Buttons, types, LCLType, BGRAImageList, BGRAVirtualScreen,
   ExtCtrls, Buttons, types, LCLType, BGRAImageList, BGRAVirtualScreen,
 
 
-  BGRABitmap, BGRABitmapTypes, BGRALayers,
+  BGRABitmap, BGRABitmapTypes, BGRALayers, BGRASVGOriginal,
 
 
   LazPaintType, UMainFormLayout, UTool, UImage, UImageAction, ULayerAction, UZoom, UImageView,
   LazPaintType, UMainFormLayout, UTool, UImage, UImageAction, ULayerAction, UZoom, UImageView,
   UImageObservation, UConfig, UScaleDPI, UResourceStrings,
   UImageObservation, UConfig, UScaleDPI, UResourceStrings,
@@ -28,6 +28,9 @@ type
   { TFMain }
   { TFMain }
 
 
   TFMain = class(TForm)
   TFMain = class(TForm)
+    ImageSwapRedBlue: TAction;
+    ImageLinearNegative: TAction;
+    ImageNegative: TAction;
     ForgetDialogAnswers: TAction;
     ForgetDialogAnswers: TAction;
     FileChooseEntry: TAction;
     FileChooseEntry: TAction;
     ToolButton8: TToolButton;
     ToolButton8: TToolButton;
@@ -711,6 +714,7 @@ type
     FCoordinatesCaption: string;
     FCoordinatesCaption: string;
     FCoordinatesCaptionCount: NativeInt;
     FCoordinatesCaptionCount: NativeInt;
     FImageView: TImageView;
     FImageView: TImageView;
+    FUpdateStackWhenIdle: boolean;
 
 
     function GetCurrentPressure: single;
     function GetCurrentPressure: single;
     function GetUseImageBrowser: boolean;
     function GetUseImageBrowser: boolean;
@@ -792,7 +796,7 @@ type
   public
   public
     { public declarations }
     { public declarations }
     FormBackgroundColor: TColor;
     FormBackgroundColor: TColor;
-    StackNeedUpdate: boolean;
+    UpdateStackOnTimer: boolean;
     Zoom: TZoom;
     Zoom: TZoom;
 
 
     procedure PaintPictureNow;
     procedure PaintPictureNow;
@@ -803,6 +807,7 @@ type
     procedure UpdateToolbar;
     procedure UpdateToolbar;
     function ChooseTool(Tool : TPaintToolType): boolean;
     function ChooseTool(Tool : TPaintToolType): boolean;
     procedure PictureSelectedLayerIndexChanged({%H-}sender: TLazPaintImage);
     procedure PictureSelectedLayerIndexChanged({%H-}sender: TLazPaintImage);
+    procedure PictureSelectedLayerIndexChanging({%H-}sender: TLazPaintImage);
     property LazPaintInstance: TLazPaintCustomInstance read FLazPaintInstance write SetLazPaintInstance;
     property LazPaintInstance: TLazPaintCustomInstance read FLazPaintInstance write SetLazPaintInstance;
     procedure UpdateEditPicture(ADelayed: boolean = false);
     procedure UpdateEditPicture(ADelayed: boolean = false);
     property CurrentTool: TPaintToolType read GetCurrentTool;
     property CurrentTool: TPaintToolType read GetCurrentTool;
@@ -818,7 +823,7 @@ implementation
 
 
 uses LCLIntf, BGRAUTF8, ugraph, math, umac, uclipboard, ucursors,
 uses LCLIntf, BGRAUTF8, ugraph, math, umac, uclipboard, ucursors,
    ufilters, ULoadImage, ULoading, UFileExtensions, UBrushType,
    ufilters, ULoadImage, ULoading, UFileExtensions, UBrushType,
-   ugeometricbrush, UPreviewDialog, UQuestion;
+   ugeometricbrush, UPreviewDialog, UQuestion, BGRALayerOriginal;
 
 
 const PenWidthFactor = 10;
 const PenWidthFactor = 10;
 
 
@@ -922,6 +927,7 @@ begin
   begin
   begin
     Image.OnSelectionChanged := nil;
     Image.OnSelectionChanged := nil;
     Image.OnSelectedLayerIndexChanged:= nil;
     Image.OnSelectedLayerIndexChanged:= nil;
+    Image.OnSelectedLayerIndexChanging:= nil;
   end;
   end;
   FLayout.ToolBoxPopup := nil;
   FLayout.ToolBoxPopup := nil;
   RegisterScripts(False);
   RegisterScripts(False);
@@ -977,6 +983,7 @@ begin
   LazPaintInstance.EmbeddedResult := mrNone;
   LazPaintInstance.EmbeddedResult := mrNone;
 
 
   Image.OnSelectedLayerIndexChanged:= @PictureSelectedLayerIndexChanged;
   Image.OnSelectedLayerIndexChanged:= @PictureSelectedLayerIndexChanged;
+  Image.OnSelectedLayerIndexChanging:= @PictureSelectedLayerIndexChanging;
   Image.CurrentFilenameUTF8 := '';
   Image.CurrentFilenameUTF8 := '';
 
 
   RegisterToolbarElements;
   RegisterToolbarElements;
@@ -1160,6 +1167,11 @@ begin
     btnMiddleDown:= false;
     btnMiddleDown:= false;
   end;
   end;
   if redraw then PaintPictureNow;
   if redraw then PaintPictureNow;
+  if FUpdateStackWhenIdle then
+  begin
+    UpdateStackOnTimer:= true;
+    FUpdateStackWhenIdle:= false;
+  end;
   UpdateToolbar;
   UpdateToolbar;
   ReleaseMouseButtons(Shift);
   ReleaseMouseButtons(Shift);
 
 
@@ -1253,7 +1265,7 @@ begin
         begin
         begin
           if length(chosenFiles) = 1 then
           if length(chosenFiles) = 1 then
           begin
           begin
-            if TryOpenFileUTF8(chosenFiles[0],true,@loadedImage) then
+            if TryOpenFileUTF8(chosenFiles[0],true,@loadedImage,true) then
             begin
             begin
               result := srOk;
               result := srOk;
               if Assigned(Scripting.RecordingFunctionParameters) then
               if Assigned(Scripting.RecordingFunctionParameters) then
@@ -1578,8 +1590,8 @@ begin
   if Sender is TAction then
   if Sender is TAction then
   begin
   begin
     actionName:= (Sender as TAction).Name;
     actionName:= (Sender as TAction).Name;
-    if (actionName = 'ImageHorizontalFlip') and not image.SelectionEmpty then actionName := 'SelectionHorizontalFlip' else
-    if (actionName = 'ImageVerticalFlip') and not image.SelectionEmpty  then actionName := 'SelectionVerticalFlip';
+    if (actionName = 'ImageHorizontalFlip') and not image.SelectionMaskEmpty then actionName := 'SelectionHorizontalFlip' else
+    if (actionName = 'ImageVerticalFlip') and not image.SelectionMaskEmpty  then actionName := 'SelectionVerticalFlip';
     CallScriptFunction(actionName);
     CallScriptFunction(actionName);
   end;
   end;
 end;
 end;
@@ -1713,7 +1725,7 @@ function TFMain.ScriptFileSaveSelectionAs(AVars: TVariableSet): TScriptResult;
 var filename: string;
 var filename: string;
     vFileName: TScriptVariableReference;
     vFileName: TScriptVariableReference;
 begin
 begin
-  if Image.SelectionEmpty then
+  if Image.SelectionMaskEmpty then
   begin
   begin
     result := srOk;
     result := srOk;
     exit;
     exit;
@@ -1762,7 +1774,7 @@ begin
     end else
     end else
     begin
     begin
       try
       try
-        Image.SaveSelectionToFileUTF8(filename);
+        Image.SaveSelectionMaskToFileUTF8(filename);
         result := srOk;
         result := srOk;
         if Assigned(Scripting.RecordingFunctionParameters) then
         if Assigned(Scripting.RecordingFunctionParameters) then
            Scripting.RecordingFunctionParameters.AddString('FileName',filename);
            Scripting.RecordingFunctionParameters.AddString('FileName',filename);
@@ -1795,7 +1807,7 @@ end;
 
 
 procedure TFMain.FileSaveSelectionAsUpdate(Sender: TObject);
 procedure TFMain.FileSaveSelectionAsUpdate(Sender: TObject);
 begin
 begin
-  FileSaveSelectionAs.Enabled := not Image.SelectionEmpty;
+  FileSaveSelectionAs.Enabled := not Image.SelectionMaskEmpty;
 end;
 end;
 
 
 procedure TFMain.FormDropFiles(Sender: TObject; const FileNames: array of String);
 procedure TFMain.FormDropFiles(Sender: TObject; const FileNames: array of String);
@@ -1804,10 +1816,12 @@ var
   Errors: String='';
   Errors: String='';
   loadedLayers: array of record
   loadedLayers: array of record
      bmp: TBGRABitmap;
      bmp: TBGRABitmap;
+     orig: TBGRALayerCustomOriginal;
      filename: string;
      filename: string;
   end;
   end;
   topmost: TTopMostInfo;
   topmost: TTopMostInfo;
   choice: TModalResult;
   choice: TModalResult;
+  svgOrig: TBGRALayerSVGOriginal;
 begin
 begin
   if Length(FileNames)<1 then exit;
   if Length(FileNames)<1 then exit;
   if Length(FileNames)= 1
   if Length(FileNames)= 1
@@ -1838,9 +1852,21 @@ begin
                   MessagePopupForever(rsLoading + ' ' + inttostr(i+1) + '/' + inttostr(length(FileNames)));
                   MessagePopupForever(rsLoading + ' ' + inttostr(i+1) + '/' + inttostr(length(FileNames)));
                   LazPaintInstance.UpdateWindows;
                   LazPaintInstance.UpdateWindows;
                   loadedLayers[i].filename := Filenames[i];
                   loadedLayers[i].filename := Filenames[i];
-                  loadedLayers[i].bmp := LoadFlatImageUTF8(Filenames[i]).bmp;
-                  if loadedLayers[i].bmp.Width > tx then tx := loadedLayers[i].bmp.Width;
-                  if loadedLayers[i].bmp.Height > ty then ty := loadedLayers[i].bmp.Height;
+                  case DetectFileFormat(Filenames[i]) of
+                   ifSvg:
+                     begin
+                       svgOrig := LoadSVGOriginalUTF8(Filenames[i]);
+                       loadedLayers[i].orig := svgOrig;
+                       if ceil(svgOrig.Width) > tx then tx := ceil(svgOrig.Width);
+                       if ceil(svgOrig.Height) > ty then ty := ceil(svgOrig.Height);
+                     end
+                   else
+                     begin
+                       loadedLayers[i].bmp := LoadFlatImageUTF8(Filenames[i]).bmp;
+                       if loadedLayers[i].bmp.Width > tx then tx := loadedLayers[i].bmp.Width;
+                       if loadedLayers[i].bmp.Height > ty then ty := loadedLayers[i].bmp.Height;
+                     end;
+                  end;
                   MessagePopupHide;
                   MessagePopupHide;
                 except on ex:exception do
                 except on ex:exception do
                   //begin
                   //begin
@@ -1857,9 +1883,14 @@ begin
                   Image.Assign(TBGRABitmap.Create(tx,ty),true,false);
                   Image.Assign(TBGRABitmap.Create(tx,ty),true,false);
                   ZoomFitIfTooBig;
                   ZoomFitIfTooBig;
                   for i := 0 to high(loadedLayers) do
                   for i := 0 to high(loadedLayers) do
+                  if Assigned(loadedLayers[i].bmp) then
                   begin
                   begin
                     FImageActions.AddLayerFromBitmap(loadedLayers[i].bmp,ExtractFileName(loadedLayers[i].filename));
                     FImageActions.AddLayerFromBitmap(loadedLayers[i].bmp,ExtractFileName(loadedLayers[i].filename));
                     loadedLayers[i].bmp := nil;
                     loadedLayers[i].bmp := nil;
+                  end else
+                  begin
+                    FImageActions.AddLayerFromOriginal(loadedLayers[i].orig,ExtractFileName(loadedLayers[i].filename));
+                    loadedLayers[i].orig := nil;
                   end;
                   end;
                 end;
                 end;
               except on ex:exception do
               except on ex:exception do
@@ -1878,7 +1909,10 @@ begin
                 LazPaintInstance.ShowTopmost(topmost);
                 LazPaintInstance.ShowTopmost(topmost);
               end;
               end;
               for i := 0 to high(loadedLayers) do
               for i := 0 to high(loadedLayers) do
+              begin
                 FreeAndNil(loadedLayers[i].bmp);
                 FreeAndNil(loadedLayers[i].bmp);
+                FreeAndNil(loadedLayers[i].orig);
+              end;
           end;  //OpenFilesAsLayers
           end;  //OpenFilesAsLayers
        mrLast+2: begin
        mrLast+2: begin
              if not LazPaintInstance.ImageListWindowVisible then
              if not LazPaintInstance.ImageListWindowVisible then
@@ -1986,7 +2020,7 @@ end;
 
 
 procedure TFMain.ImageCropLayerUpdate(Sender: TObject);
 procedure TFMain.ImageCropLayerUpdate(Sender: TObject);
 begin
 begin
-  ImageCropLayer.Enabled := not image.SelectionEmpty;
+  ImageCropLayer.Enabled := not image.SelectionMaskEmpty;
   ImageCropLayer.Visible := (image.NbLayers > 1);
   ImageCropLayer.Visible := (image.NbLayers > 1);
 end;
 end;
 
 
@@ -2086,7 +2120,7 @@ end;
 
 
 procedure TFMain.LayerMergeOverUpdate(Sender: TObject);
 procedure TFMain.LayerMergeOverUpdate(Sender: TObject);
 begin
 begin
-  LayerMergeOver.Enabled := (image.currentImageLayerIndex > 0) and Image.CurrentLayerVisible;
+  LayerMergeOver.Enabled := (image.CurrentLayerIndex > 0) and Image.CurrentLayerVisible;
 end;
 end;
 
 
 procedure TFMain.LayerMoveExecute(Sender: TObject);
 procedure TFMain.LayerMoveExecute(Sender: TObject);
@@ -2096,7 +2130,7 @@ end;
 
 
 procedure TFMain.LayerMoveUpdate(Sender: TObject);
 procedure TFMain.LayerMoveUpdate(Sender: TObject);
 begin
 begin
-  LayerMove.Enabled := Image.CurrentLayerVisible and Image.SelectionEmpty;
+  LayerMove.Enabled := Image.CurrentLayerVisible and Image.SelectionMaskEmpty;
 end;
 end;
 
 
 procedure TFMain.LayerRemoveCurrentUpdate(Sender: TObject);
 procedure TFMain.LayerRemoveCurrentUpdate(Sender: TObject);
@@ -2111,7 +2145,7 @@ end;
 
 
 procedure TFMain.LayerRotateUpdate(Sender: TObject);
 procedure TFMain.LayerRotateUpdate(Sender: TObject);
 begin
 begin
-  LayerRotate.Enabled := Image.CurrentLayerVisible and Image.SelectionEmpty;
+  LayerRotate.Enabled := Image.CurrentLayerVisible and Image.SelectionMaskEmpty;
 end;
 end;
 
 
 procedure TFMain.ItemDonateClick(Sender: TObject);
 procedure TFMain.ItemDonateClick(Sender: TObject);
@@ -2137,11 +2171,11 @@ end;
 procedure TFMain.MenuImageClick(Sender: TObject);
 procedure TFMain.MenuImageClick(Sender: TObject);
 begin
 begin
   ItemHorizFlipLayer.Visible := (image.NbLayers > 1) and not LazPaintInstance.LayerWindowVisible;
   ItemHorizFlipLayer.Visible := (image.NbLayers > 1) and not LazPaintInstance.LayerWindowVisible;
-  ItemHorizFlipSelection.Visible := not image.SelectionEmpty;
+  ItemHorizFlipSelection.Visible := not image.SelectionMaskEmpty;
   ImageHorizontalFlip.Visible := not ItemHorizFlipLayer.Visible and not ItemHorizFlipSelection.Visible;
   ImageHorizontalFlip.Visible := not ItemHorizFlipLayer.Visible and not ItemHorizFlipSelection.Visible;
   MenuHorizFlipSub.Visible := not ImageHorizontalFlip.Visible;
   MenuHorizFlipSub.Visible := not ImageHorizontalFlip.Visible;
   ItemVertFlipLayer.Visible := (image.NbLayers > 1) and not LazPaintInstance.LayerWindowVisible;
   ItemVertFlipLayer.Visible := (image.NbLayers > 1) and not LazPaintInstance.LayerWindowVisible;
-  ItemVertFlipSelection.Visible := not image.SelectionEmpty;
+  ItemVertFlipSelection.Visible := not image.SelectionMaskEmpty;
   ImageVerticalFlip.Visible := not ItemVertFlipLayer.Visible and not ItemVertFlipSelection.Visible;
   ImageVerticalFlip.Visible := not ItemVertFlipLayer.Visible and not ItemVertFlipSelection.Visible;
   MenuVertFlipSub.Visible := not ImageVerticalFlip.Visible;
   MenuVertFlipSub.Visible := not ImageVerticalFlip.Visible;
 end;
 end;
@@ -2279,7 +2313,7 @@ end;
 
 
 procedure TFMain.ImageCropUpdate(Sender: TObject);
 procedure TFMain.ImageCropUpdate(Sender: TObject);
 begin
 begin
-  ImageCrop.Enabled := not image.SelectionEmpty;
+  ImageCrop.Enabled := not image.SelectionMaskEmpty;
 end;
 end;
 
 
 procedure TFMain.ImageRepeatExecute(Sender: TObject);
 procedure TFMain.ImageRepeatExecute(Sender: TObject);
@@ -2367,10 +2401,10 @@ begin
     Label_Coordinates.Update;
     Label_Coordinates.Update;
     FCoordinatesCaptionCount := 0;
     FCoordinatesCaptionCount := 0;
   end;
   end;
-  if CanCompressOrUpdateStack and StackNeedUpdate then
+  if CanCompressOrUpdateStack and UpdateStackOnTimer then
   begin
   begin
     LazPaintInstance.NotifyStackChange;
     LazPaintInstance.NotifyStackChange;
-    StackNeedUpdate := false;
+    UpdateStackOnTimer := false;
   end else
   end else
   begin
   begin
     if CanCompressOrUpdateStack then image.CompressUndo;
     if CanCompressOrUpdateStack then image.CompressUndo;
@@ -2385,12 +2419,12 @@ end;
 
 
 procedure TFMain.ToolRotateSelectionUpdate(Sender: TObject);
 procedure TFMain.ToolRotateSelectionUpdate(Sender: TObject);
 begin
 begin
-  ToolRotateSelection.Enabled := not image.SelectionEmpty;
+  ToolRotateSelection.Enabled := not image.SelectionMaskEmpty;
 end;
 end;
 
 
 procedure TFMain.ToolLayerMappingUpdate(Sender: TObject);
 procedure TFMain.ToolLayerMappingUpdate(Sender: TObject);
 begin
 begin
-  ToolLayerMapping.Enabled := Image.CurrentLayerVisible and Image.SelectionEmpty;
+  ToolLayerMapping.Enabled := Image.CurrentLayerVisible and Image.SelectionMaskEmpty;
 end;
 end;
 
 
 procedure TFMain.ToolLoadTextureExecute(Sender: TObject);
 procedure TFMain.ToolLoadTextureExecute(Sender: TObject);
@@ -2622,7 +2656,7 @@ begin
           begin
           begin
             useSelection:= false;
             useSelection:= false;
             newTexture := nil;
             newTexture := nil;
-            if not image.SelectionEmpty and not image.SelectionLayerIsEmpty then
+            if not image.SelectionMaskEmpty and not image.SelectionLayerIsEmpty then
             begin
             begin
               topmostInfo := LazPaintInstance.HideTopmost;
               topmostInfo := LazPaintInstance.HideTopmost;
               if Config.DefaultTransformSelectionAnswer <> mrNone then
               if Config.DefaultTransformSelectionAnswer <> mrNone then
@@ -2640,19 +2674,15 @@ begin
                   if image.SelectionLayerReadonly <> nil then
                   if image.SelectionLayerReadonly <> nil then
                   begin
                   begin
                     newTexture := image.SelectionLayerReadonly.Duplicate as TBGRABitmap;
                     newTexture := image.SelectionLayerReadonly.Duplicate as TBGRABitmap;
-                    newTexture.ApplyMask(image.SelectionReadonly, image.SelectionLayerBounds);
+                    newTexture.ApplyMask(image.SelectionMaskReadonly, image.SelectionLayerBounds);
                     if newTexture.Empty then
                     if newTexture.Empty then
-                      MessagePopup(rsNothingToBeRetrieved,2000)
+                    begin
+                      newTexture.Free;
+                      MessagePopup(rsNothingToBeRetrieved,2000);
+                    end
                     else
                     else
                     begin
                     begin
-                      LayerAction := nil;
-                      try
-                        LayerAction := TLayerAction.Create(Image);
-                        LayerAction.RemoveSelection;
-                        LayerAction.Validate;
-                      except on ex:exception do LazPaintInstance.ShowError(rsTextureMapping,ex.Message);
-                      end;
-                      LayerAction.Free;
+                      FImageActions.RemoveSelection;
                       BGRAReplace(newTexture, newTexture.GetPart(newTexture.GetImageBounds));
                       BGRAReplace(newTexture, newTexture.GetPart(newTexture.GetImageBounds));
                       ToolManager.SetToolTexture(newTexture);
                       ToolManager.SetToolTexture(newTexture);
                       UpdateTextureIcon;
                       UpdateTextureIcon;
@@ -2684,7 +2714,7 @@ begin
           ptLayerMapping:
           ptLayerMapping:
           begin
           begin
             EditDeselect.Execute;
             EditDeselect.Execute;
-            if image.SelectedLayerEmpty then
+            if image.CurrentLayerEmpty then
             begin
             begin
               MessagePopup(rsEmptyLayer,2000);
               MessagePopup(rsEmptyLayer,2000);
               Tool := ptHand;
               Tool := ptHand;
@@ -2693,7 +2723,7 @@ begin
           end;
           end;
           ptMoveLayer:
           ptMoveLayer:
           begin
           begin
-            if image.SelectedLayerEquals(image.SelectedLayerPixel[0,0]) then
+            if image.CurrentLayerEquals(image.CurrentLayerPixel[0,0]) then
             begin
             begin
               LazPaintInstance.ShowMessage(rsLazPaint, rsEmptyLayer);
               LazPaintInstance.ShowMessage(rsLazPaint, rsEmptyLayer);
               Tool := ptHand;
               Tool := ptHand;
@@ -2702,8 +2732,8 @@ begin
           end;
           end;
           ptDeformation:
           ptDeformation:
           begin
           begin
-            if (image.SelectionEmpty and image.SelectedLayerEquals(image.SelectedLayerPixel[0,0])) or
-               (not image.SelectionEmpty and image.SelectionLayerIsEmpty) then
+            if (image.SelectionMaskEmpty and image.CurrentLayerEquals(image.CurrentLayerPixel[0,0])) or
+               (not image.SelectionMaskEmpty and image.SelectionLayerIsEmpty) then
             begin
             begin
               LazPaintInstance.ShowMessage(rsLazPaint, rsNothingToBeDeformed);
               LazPaintInstance.ShowMessage(rsLazPaint, rsNothingToBeDeformed);
               Tool := ptHand;
               Tool := ptHand;
@@ -2717,7 +2747,7 @@ begin
               result := srException;
               result := srException;
               exit;
               exit;
             end;
             end;
-            if image.CurrentLayerVisible and not image.SelectionEmpty and image.SelectionLayerIsEmpty and not image.SelectedLayerEmpty then
+            if image.CurrentLayerVisible and not image.SelectionMaskEmpty and image.SelectionLayerIsEmpty and not image.CurrentLayerEmpty then
             begin
             begin
               topmostInfo := LazPaintInstance.HideTopmost;
               topmostInfo := LazPaintInstance.HideTopmost;
               if Config.DefaultRetrieveSelectionAnswer <> mrNone then
               if Config.DefaultRetrieveSelectionAnswer <> mrNone then
@@ -2730,21 +2760,7 @@ begin
               end;
               end;
               LazPaintInstance.ShowTopmost(topmostInfo);
               LazPaintInstance.ShowTopmost(topmostInfo);
               case res.ButtonResult of
               case res.ButtonResult of
-                mrYes: begin
-                  LayerAction := nil;
-                  try
-                    LayerAction := TLayerAction.Create(Image);
-                    if LayerAction.RetrieveSelectionIfLayerEmpty(True) then
-                    begin
-                      ComputeSelectionMask(LayerAction.GetOrCreateSelectionLayer,LayerAction.CurrentSelection,Image.SelectionBounds);
-                      Image.SelectionMayChange(Image.SelectionBounds);
-                      LayerAction.Validate;
-                    end;
-                    if image.SelectionLayerIsEmpty then MessagePopup(rsNothingToBeRetrieved,2000);
-                  except on ex:exception do LazPaintInstance.ShowError(rsMovingOrRotatingSelection,ex.Message);
-                  end;
-                  LayerAction.Free;
-                end;
+                mrYes: FImageActions.RetrieveSelection;
               end;
               end;
             end;
             end;
           end;
           end;
@@ -2843,7 +2859,7 @@ end;
 
 
 procedure TFMain.EditCopyUpdate(Sender: TObject);
 procedure TFMain.EditCopyUpdate(Sender: TObject);
 begin
 begin
-  EditCopy.Enabled := ToolManager.ToolProvideCopy or not image.SelectionEmpty;
+  EditCopy.Enabled := ToolManager.ToolProvideCopy or not image.SelectionMaskEmpty;
 end;
 end;
 
 
 procedure TFMain.EditCutExecute(Sender: TObject);
 procedure TFMain.EditCutExecute(Sender: TObject);
@@ -2854,12 +2870,12 @@ end;
 
 
 procedure TFMain.EditCutUpdate(Sender: TObject);
 procedure TFMain.EditCutUpdate(Sender: TObject);
 begin
 begin
-  EditCut.Enabled := ToolManager.ToolProvideCut or not image.SelectionEmpty;
+  EditCut.Enabled := ToolManager.ToolProvideCut or not image.SelectionMaskEmpty;
 end;
 end;
 
 
 procedure TFMain.EditDeleteSelectionUpdate(Sender: TObject);
 procedure TFMain.EditDeleteSelectionUpdate(Sender: TObject);
 begin
 begin
-  EditDeleteSelection.Enabled := not image.SelectionEmpty;
+  EditDeleteSelection.Enabled := not image.SelectionMaskEmpty;
 end;
 end;
 
 
 procedure TFMain.EditPasteExecute(Sender: TObject);
 procedure TFMain.EditPasteExecute(Sender: TObject);
@@ -3040,8 +3056,8 @@ end;
 
 
 procedure TFMain.EditDeselectUpdate(Sender: TObject);
 procedure TFMain.EditDeselectUpdate(Sender: TObject);
 begin
 begin
-  EditDeselect.Enabled := not image.SelectionEmpty;
-  if image.SelectionEmpty then FSaveSelectionInitialFilename := '';
+  EditDeselect.Enabled := not image.SelectionMaskEmpty;
+  if image.SelectionMaskEmpty then FSaveSelectionInitialFilename := '';
 end;
 end;
 
 
 procedure TFMain.EditRedoUpdate(Sender: TObject);
 procedure TFMain.EditRedoUpdate(Sender: TObject);
@@ -3060,8 +3076,8 @@ begin
   if Sender is TAction then
   if Sender is TAction then
   begin
   begin
     actionName := (Sender as TAction).Name;
     actionName := (Sender as TAction).Name;
-    if (actionName = 'ImageHorizontalFlip') and not image.SelectionEmpty then actionName := 'SelectionHorizontalFlip' else
-    if (actionName = 'ImageVerticalFlip') and not image.SelectionEmpty  then actionName := 'SelectionVerticalFlip';
+    if (actionName = 'ImageHorizontalFlip') and not image.SelectionMaskEmpty then actionName := 'SelectionHorizontalFlip' else
+    if (actionName = 'ImageVerticalFlip') and not image.SelectionMaskEmpty  then actionName := 'SelectionVerticalFlip';
     CallScriptFunction(actionName);
     CallScriptFunction(actionName);
   end;
   end;
 end;
 end;
@@ -3069,7 +3085,7 @@ end;
 procedure TFMain.AskMergeSelection(ACaption: string);
 procedure TFMain.AskMergeSelection(ACaption: string);
 var topmostInfo: TTopMostInfo; res: integer;
 var topmostInfo: TTopMostInfo; res: integer;
 begin
 begin
-  if not image.SelectionEmpty and not image.SelectionLayerIsEmpty then
+  if not image.SelectionMaskEmpty and not image.SelectionLayerIsEmpty then
   begin
   begin
     topmostInfo:= LazPaintInstance.HideTopmost;
     topmostInfo:= LazPaintInstance.HideTopmost;
     res := MessageDlg(ACaption,rsMergeSelection,mtConfirmation,[mbYes,mbNo],0);
     res := MessageDlg(ACaption,rsMergeSelection,mtConfirmation,[mbYes,mbNo],0);
@@ -3327,12 +3343,22 @@ var
       with ComputeAcceptableImageSize(newPicture.bmp.Width,newPicture.bmp.Height) do
       with ComputeAcceptableImageSize(newPicture.bmp.Width,newPicture.bmp.Height) do
         if (cx < newPicture.bmp.Width) or (cy < newPicture.bmp.Height) then
         if (cx < newPicture.bmp.Width) or (cy < newPicture.bmp.Height) then
           BGRAReplace(newPicture.bmp, newPicture.bmp.Resample(cx,cy,rmFineResample));
           BGRAReplace(newPicture.bmp, newPicture.bmp.Resample(cx,cy,rmFineResample));
-      FImageActions.SetCurrentBitmap(newPicture.bmp, False); //image owned
+      image.Assign(newPicture.bmp,True, false);
       newPicture.bmp := nil;
       newPicture.bmp := nil;
       EndImport(newPicture.bpp, newPicture.frameIndex);
       EndImport(newPicture.bpp, newPicture.frameIndex);
     end else FreeAndNil(newPicture.bmp);
     end else FreeAndNil(newPicture.bmp);
   end;
   end;
 
 
+  procedure ImportSvg;
+  var
+    layered: TBGRALayeredBitmap;
+  begin
+    StartImport;
+    layered := LoadSVGImageUTF8(filenameUTF8);
+    Image.Assign(layered,true, false);
+    EndImport;
+  end;
+
 begin
 begin
   result := false;
   result := false;
   if filenameUTF8 = '' then exit;
   if filenameUTF8 = '' then exit;
@@ -3345,6 +3371,10 @@ begin
   newPicture := TImageEntry.Empty;
   newPicture := TImageEntry.Empty;
   try
   try
     format := Image.DetectImageFormat(filenameUTF8);
     format := Image.DetectImageFormat(filenameUTF8);
+    if format = ifSvg then
+    begin
+      ImportSvg;
+    end else
     if Assigned(ALoadedImage) and Assigned(ALoadedImage^.bmp) then
     if Assigned(ALoadedImage) and Assigned(ALoadedImage^.bmp) then
     begin
     begin
       newPicture := ALoadedImage^;
       newPicture := ALoadedImage^;
@@ -3388,7 +3418,7 @@ end;
 
 
 procedure TFMain.ToolMoveSelectionUpdate(Sender: TObject);
 procedure TFMain.ToolMoveSelectionUpdate(Sender: TObject);
 begin
 begin
-  ToolMoveSelection.Enabled := not image.SelectionEmpty;
+  ToolMoveSelection.Enabled := not image.SelectionMaskEmpty;
 end;
 end;
 
 
 {****************************** Picture ************************}
 {****************************** Picture ************************}
@@ -3433,6 +3463,7 @@ begin
     ChooseTool(ptHand);
     ChooseTool(ptHand);
     MessagePopup(rsToolOnInvisibleLayer,5000);
     MessagePopup(rsToolOnInvisibleLayer,5000);
   end;
   end;
+  if AEvent.DelayedStackUpdate then FUpdateStackWhenIdle := true;
 end;
 end;
 
 
 procedure TFMain.UpdateEditPicture(ADelayed: boolean = false);
 procedure TFMain.UpdateEditPicture(ADelayed: boolean = false);
@@ -3455,7 +3486,7 @@ end;
 procedure TFMain.PaintPictureNow;
 procedure TFMain.PaintPictureNow;
 begin
 begin
   if not visible then exit;
   if not visible then exit;
-  StackNeedUpdate := true;
+  UpdateStackOnTimer := true;
   Image.OnImageChanged.NotifyObservers;
   Image.OnImageChanged.NotifyObservers;
   {$IFDEF USEPAINTBOXPICTURE}PaintBox_Picture{$ELSE}self{$ENDIF}.Update;
   {$IFDEF USEPAINTBOXPICTURE}PaintBox_Picture{$ELSE}self{$ENDIF}.Update;
 end;
 end;
@@ -3470,7 +3501,14 @@ end;
 procedure TFMain.PictureSelectedLayerIndexChanged(sender: TLazPaintImage);
 procedure TFMain.PictureSelectedLayerIndexChanged(sender: TLazPaintImage);
 begin
 begin
   if not image.CurrentLayerVisible and not ToolManager.ToolCanBeUsed then
   if not image.CurrentLayerVisible and not ToolManager.ToolCanBeUsed then
-    ChooseTool(ptHand);
+    ChooseTool(ptHand)
+  else
+    ToolManager.ToolOpen;
+end;
+
+procedure TFMain.PictureSelectedLayerIndexChanging(sender: TLazPaintImage);
+begin
+  ToolManager.ToolCloseDontReopen;
 end;
 end;
 
 
 procedure TFMain.SetShowSelectionNormal(const AValue: boolean);
 procedure TFMain.SetShowSelectionNormal(const AValue: boolean);

+ 2 - 2
lazpaint/lazpainttype.pas

@@ -132,8 +132,8 @@ type
       bmp: TBGRABitmap;
       bmp: TBGRABitmap;
       bpp: integer;
       bpp: integer;
       frameIndex: integer;
       frameIndex: integer;
-      class function Empty: TImageEntry;
-      class function NewFrameIndex: integer;
+      class function Empty: TImageEntry; static;
+      class function NewFrameIndex: integer; static;
       procedure FreeAndNil;
       procedure FreeAndNil;
     end;
     end;
     ArrayOfImageEntry = array of TImageEntry;
     ArrayOfImageEntry = array of TImageEntry;

+ 1 - 0
lazpaint/maintoolbar.inc

@@ -194,6 +194,7 @@ function TFMain.GetCurrentToolAction: TAction;
 begin
 begin
   Case CurrentTool of
   Case CurrentTool of
   ptMoveLayer: result := LayerMove;
   ptMoveLayer: result := LayerMove;
+  ptRotateLayer: result := LayerRotate;
   else result := ActionList1.ActionByName('Tool'+PaintToolTypeStr[CurrentTool]) as TAction;
   else result := ActionList1.ActionByName('Tool'+PaintToolTypeStr[CurrentTool]) as TAction;
   end;
   end;
 end;
 end;

+ 29 - 0
lazpaint/release/i18n/lazpaint.ar.po

@@ -903,6 +903,7 @@ msgid "Median"
 msgstr "متوسط"
 msgstr "متوسط"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "سلبي"
 msgstr "سلبي"
 
 
@@ -1000,6 +1001,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "قلب الصورة افقيا"
 msgstr "قلب الصورة افقيا"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "خط السلبية"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "سلبي"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "تكرار..."
 msgstr "تكرار..."
@@ -1024,6 +1035,10 @@ msgstr "تدوير 90° CW"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "التكبير الذكي x3"
 msgstr "التكبير الذكي x3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1101,6 +1116,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1344,6 +1364,11 @@ msgctxt "tfmain.menuimage.caption"
 msgid "Image"
 msgid "Image"
 msgstr "صورة"
 msgstr "صورة"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3430,6 +3455,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr ""
 msgstr ""

+ 29 - 0
lazpaint/release/i18n/lazpaint.cs.po

@@ -894,6 +894,7 @@ msgid "Median"
 msgstr "Medián"
 msgstr "Medián"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Negativ"
 msgstr "Negativ"
 
 
@@ -989,6 +990,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "Převrátit obrázek vodorovně"
 msgstr "Převrátit obrázek vodorovně"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Lineární negativ"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Negativ"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Opakovat..."
 msgstr "Opakovat..."
@@ -1013,6 +1024,10 @@ msgstr "Otočit 90° CW"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "Chytré zvětšení x3"
 msgstr "Chytré zvětšení x3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1089,6 +1104,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1332,6 +1352,11 @@ msgctxt "tfmain.menuimage.caption"
 msgid "Image"
 msgid "Image"
 msgstr "Obrázek"
 msgstr "Obrázek"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3417,6 +3442,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr ""
 msgstr ""

+ 29 - 0
lazpaint/release/i18n/lazpaint.de.po

@@ -911,6 +911,7 @@ msgid "Median"
 msgstr "Median"
 msgstr "Median"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Negativ"
 msgstr "Negativ"
 
 
@@ -1006,6 +1007,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "Spiegle das Bild horizontal"
 msgstr "Spiegle das Bild horizontal"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Lineares Negativ"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Negativ"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Wiederholen..."
 msgstr "Wiederholen..."
@@ -1030,6 +1041,10 @@ msgstr "Drehung 90° Uhrzeigersinn"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "Smart Zoom x3"
 msgstr "Smart Zoom x3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr "Rote und blaue Kanäle tauschen"
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1106,6 +1121,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1349,6 +1369,11 @@ msgctxt "tfmain.menuimage.caption"
 msgid "Image"
 msgid "Image"
 msgstr "Bild"
 msgstr "Bild"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3435,6 +3460,10 @@ msgstr "Bilder verbleibend: %1"
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr "Zu viel Ebene"
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr "Bilder Gesamt: %1"
 msgstr "Bilder Gesamt: %1"

+ 29 - 0
lazpaint/release/i18n/lazpaint.es.po

@@ -891,6 +891,7 @@ msgid "Median"
 msgstr "Mediana"
 msgstr "Mediana"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Negativo"
 msgstr "Negativo"
 
 
@@ -986,6 +987,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "Voltear imágen horizontalmente"
 msgstr "Voltear imágen horizontalmente"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Negativo linear"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Negativo"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Repetir..."
 msgstr "Repetir..."
@@ -1010,6 +1021,10 @@ msgstr "Rotar 90º CW"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "Zoom inteligente x3"
 msgstr "Zoom inteligente x3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr "Intercambiar canales rojos y azules"
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1086,6 +1101,11 @@ msgstr "64px"
 msgid "auto"
 msgid "auto"
 msgstr "automático"
 msgstr "automático"
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1329,6 +1349,11 @@ msgctxt "tfmain.menuimage.caption"
 msgid "Image"
 msgid "Image"
 msgstr "Imágen"
 msgstr "Imágen"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3433,6 +3458,10 @@ msgstr "Quedan: %1"
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr "La herramienta no puede ser usada en una capa invisible"
 msgstr "La herramienta no puede ser usada en una capa invisible"
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr "Demasiado capas"
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr "Total: %1"
 msgstr "Total: %1"

+ 29 - 0
lazpaint/release/i18n/lazpaint.fi.po

@@ -880,6 +880,7 @@ msgid "Median"
 msgstr "Mediaani"
 msgstr "Mediaani"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Negatiivi"
 msgstr "Negatiivi"
 
 
@@ -975,6 +976,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Lineaarinen negatiivi"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Negatiivi"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr ""
 msgstr ""
@@ -999,6 +1010,10 @@ msgstr "Kierrä 90° myötäpäivään"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1075,6 +1090,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1318,6 +1338,11 @@ msgctxt "TFMAIN.MENUIMAGE.CAPTION"
 msgid "Image"
 msgid "Image"
 msgstr "Kuva"
 msgstr "Kuva"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "TFMAIN.MENULANGUAGE.CAPTION"
 msgctxt "TFMAIN.MENULANGUAGE.CAPTION"
 msgid "Language"
 msgid "Language"
@@ -3400,6 +3425,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr "Kuvia kaikkiaan: %1"
 msgstr "Kuvia kaikkiaan: %1"

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

@@ -896,6 +896,7 @@ msgid "Median"
 msgstr "Médiane"
 msgstr "Médiane"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Négatif"
 msgstr "Négatif"
 
 
@@ -991,6 +992,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "Retournement vertical"
 msgstr "Retournement vertical"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Négatif linéaire"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Négatif"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Répéter..."
 msgstr "Répéter..."
@@ -1015,6 +1026,10 @@ msgstr "Rotation 90° (horloge)"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "Zoom intelligent x3"
 msgstr "Zoom intelligent x3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr "Echanger les canaux rouge et bleu"
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1091,6 +1106,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1334,6 +1354,11 @@ msgctxt "tfmain.menuimage.caption"
 msgid "Image"
 msgid "Image"
 msgstr "Image"
 msgstr "Image"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3436,6 +3461,10 @@ msgstr "Images restantes : %1"
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr "L'outil ne peut pas être utilisé sur un calque invisible"
 msgstr "L'outil ne peut pas être utilisé sur un calque invisible"
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr "Trop de calques"
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr "Nb. total d'images : %1"
 msgstr "Nb. total d'images : %1"

+ 29 - 0
lazpaint/release/i18n/lazpaint.ja.po

@@ -900,6 +900,7 @@ msgid "Median"
 msgstr "Median"
 msgstr "Median"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "色調反転"
 msgstr "色調反転"
 
 
@@ -995,6 +996,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "水平方向にイメージをひっくり返す"
 msgstr "水平方向にイメージをひっくり返す"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Linear negative"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "色調反転"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Repeat..."
 msgstr "Repeat..."
@@ -1019,6 +1030,10 @@ msgstr "Rotate 90° CW"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "スマートズームx3"
 msgstr "スマートズームx3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1096,6 +1111,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1339,6 +1359,11 @@ msgctxt "tfmain.menuimage.caption"
 msgid "Image"
 msgid "Image"
 msgstr "イメージ"
 msgstr "イメージ"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3422,6 +3447,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr ""
 msgstr ""

+ 29 - 0
lazpaint/release/i18n/lazpaint.lv.po

@@ -897,6 +897,7 @@ msgid "Median"
 msgstr "Izlīdzināt"
 msgstr "Izlīdzināt"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Negatīvs"
 msgstr "Negatīvs"
 
 
@@ -992,6 +993,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "Apmet attēlu caur sānu malu (spoguļattēls)"
 msgstr "Apmet attēlu caur sānu malu (spoguļattēls)"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Tiešs negatīvs"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Negatīvs"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Flīzēt ..."
 msgstr "Flīzēt ..."
@@ -1016,6 +1027,10 @@ msgstr "Pagriezt pa 90°"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "Palielināt attēlu trīskārtīgi"
 msgstr "Palielināt attēlu trīskārtīgi"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1092,6 +1107,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1335,6 +1355,11 @@ msgctxt "TFMAIN.MENUIMAGE.CAPTION"
 msgid "Image"
 msgid "Image"
 msgstr "Attēls"
 msgstr "Attēls"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "TFMAIN.MENULANGUAGE.CAPTION"
 msgctxt "TFMAIN.MENULANGUAGE.CAPTION"
 msgid "Language"
 msgid "Language"
@@ -3418,6 +3443,10 @@ msgstr "Palicis: %1"
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr "Attēlu kopskaits: %1"
 msgstr "Attēlu kopskaits: %1"

+ 29 - 0
lazpaint/release/i18n/lazpaint.nl.po

@@ -918,6 +918,7 @@ msgid "Median"
 msgstr "Mediaan"
 msgstr "Mediaan"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Negatief"
 msgstr "Negatief"
 
 
@@ -1013,6 +1014,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "Afbeelding horizontaal spiegelen"
 msgstr "Afbeelding horizontaal spiegelen"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Lineair negatief"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Negatief"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Herhalen..."
 msgstr "Herhalen..."
@@ -1037,6 +1048,10 @@ msgstr "90° naar rechts draaien"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "Slimme zoom x3"
 msgstr "Slimme zoom x3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1113,6 +1128,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1357,6 +1377,11 @@ msgctxt "tfmain.menuimage.caption"
 msgid "Image"
 msgid "Image"
 msgstr "Afbeelding"
 msgstr "Afbeelding"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr "-"
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3444,6 +3469,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr ""
 msgstr ""

+ 29 - 0
lazpaint/release/i18n/lazpaint.po

@@ -880,6 +880,7 @@ msgid "Median"
 msgstr ""
 msgstr ""
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr ""
 msgstr ""
 
 
@@ -975,6 +976,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "TFMAIN.IMAGELINEARNEGATIVE.CAPTION"
+msgid "Linear negative"
+msgstr ""
+
+#: tfmain.imagenegative.caption
+msgctxt "TFMAIN.IMAGENEGATIVE.CAPTION"
+msgid "Negative"
+msgstr ""
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr ""
 msgstr ""
@@ -999,6 +1010,10 @@ msgstr ""
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1075,6 +1090,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "TFMAIN.ITEMQUITSEPARATOR.CAPTION"
+msgid "-"
+msgstr ""
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "TFMAIN.ITEMUSEIMAGEBROWSER.CAPTION"
 msgctxt "TFMAIN.ITEMUSEIMAGEBROWSER.CAPTION"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1318,6 +1338,11 @@ msgctxt "TFMAIN.MENUIMAGE.CAPTION"
 msgid "Image"
 msgid "Image"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.menuitem1.caption
+msgctxt "TFMAIN.MENUITEM1.CAPTION"
+msgid "-"
+msgstr ""
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "TFMAIN.MENULANGUAGE.CAPTION"
 msgctxt "TFMAIN.MENULANGUAGE.CAPTION"
 msgid "Language"
 msgid "Language"
@@ -3399,6 +3424,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr ""
 msgstr ""

+ 29 - 0
lazpaint/release/i18n/lazpaint.pt_BR.po

@@ -904,6 +904,7 @@ msgid "Median"
 msgstr "Mediano"
 msgstr "Mediano"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Negativo"
 msgstr "Negativo"
 
 
@@ -999,6 +1000,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "Inverter imagem horizontalmente"
 msgstr "Inverter imagem horizontalmente"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Negativo linear"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Negativo"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Repetir..."
 msgstr "Repetir..."
@@ -1023,6 +1034,10 @@ msgstr "Girar 90° CW"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "Zoom inteligente x3"
 msgstr "Zoom inteligente x3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1099,6 +1114,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1342,6 +1362,11 @@ msgctxt "TFMAIN.MENUIMAGE.CAPTION"
 msgid "Image"
 msgid "Image"
 msgstr "Imagem"
 msgstr "Imagem"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3426,6 +3451,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr ""
 msgstr ""

+ 29 - 0
lazpaint/release/i18n/lazpaint.ru.po

@@ -898,6 +898,7 @@ msgid "Median"
 msgstr "Медиана"
 msgstr "Медиана"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Негатив"
 msgstr "Негатив"
 
 
@@ -993,6 +994,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "Отразить изображение по горизонтали"
 msgstr "Отразить изображение по горизонтали"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Линейный негатив"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Негатив"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Повторить ..."
 msgstr "Повторить ..."
@@ -1017,6 +1028,10 @@ msgstr "Поворот на 90° по часовой стрелке"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "Интеллектуальное увеличение x3"
 msgstr "Интеллектуальное увеличение x3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1093,6 +1108,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1336,6 +1356,11 @@ msgctxt "tfmain.menuimage.caption"
 msgid "Image"
 msgid "Image"
 msgstr "Изображение"
 msgstr "Изображение"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3420,6 +3445,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr ""
 msgstr ""

+ 29 - 0
lazpaint/release/i18n/lazpaint.sv.po

@@ -886,6 +886,7 @@ msgid "Median"
 msgstr "Median"
 msgstr "Median"
 
 
 #: tfmain.filternegative.caption
 #: tfmain.filternegative.caption
+msgctxt "tfmain.filternegative.caption"
 msgid "Negative"
 msgid "Negative"
 msgstr "Negativ"
 msgstr "Negativ"
 
 
@@ -981,6 +982,16 @@ msgctxt "tfmain.imagehorizontalflip.hint"
 msgid "Flip image horizontally"
 msgid "Flip image horizontally"
 msgstr "Vänd bild horisontalt"
 msgstr "Vänd bild horisontalt"
 
 
+#: tfmain.imagelinearnegative.caption
+msgctxt "tfmain.imagelinearnegative.caption"
+msgid "Linear negative"
+msgstr "Linjär negativ"
+
+#: tfmain.imagenegative.caption
+msgctxt "tfmain.imagenegative.caption"
+msgid "Negative"
+msgstr "Negativ"
+
 #: tfmain.imagerepeat.caption
 #: tfmain.imagerepeat.caption
 msgid "Repeat..."
 msgid "Repeat..."
 msgstr "Upprepa..."
 msgstr "Upprepa..."
@@ -1005,6 +1016,10 @@ msgstr "Rotera 90° medurs"
 msgid "Smart zoom x3"
 msgid "Smart zoom x3"
 msgstr "Smart zoom x3"
 msgstr "Smart zoom x3"
 
 
+#: tfmain.imageswapredblue.caption
+msgid "Swap red and blue channels"
+msgstr ""
+
 #: tfmain.imageverticalflip.caption
 #: tfmain.imageverticalflip.caption
 msgctxt "tfmain.imageverticalflip.caption"
 msgctxt "tfmain.imageverticalflip.caption"
 msgid "Vertical flip"
 msgid "Vertical flip"
@@ -1081,6 +1096,11 @@ msgstr ""
 msgid "auto"
 msgid "auto"
 msgstr ""
 msgstr ""
 
 
+#: tfmain.itemquitseparator.caption
+msgctxt "tfmain.itemquitseparator.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.itemuseimagebrowser.caption
 #: tfmain.itemuseimagebrowser.caption
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgctxt "tfmain.itemuseimagebrowser.caption"
 msgid "Use image browser"
 msgid "Use image browser"
@@ -1324,6 +1344,11 @@ msgctxt "tfmain.menuimage.caption"
 msgid "Image"
 msgid "Image"
 msgstr "Bild"
 msgstr "Bild"
 
 
+#: tfmain.menuitem1.caption
+msgctxt "tfmain.menuitem1.caption"
+msgid "-"
+msgstr ""
+
 #: tfmain.menulanguage.caption
 #: tfmain.menulanguage.caption
 msgctxt "tfmain.menulanguage.caption"
 msgctxt "tfmain.menulanguage.caption"
 msgid "Language"
 msgid "Language"
@@ -3407,6 +3432,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 msgstr ""
 
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgid "Total images: %1"
 msgstr ""
 msgstr ""

+ 1 - 1
lazpaint/uadjustcurves.pas

@@ -818,7 +818,7 @@ var
 begin
 begin
   tempParameters := AParameters.Duplicate;
   tempParameters := AParameters.Duplicate;
   try
   try
-    FFilterConnector := TFilterConnector.Create(AInstance, tempParameters);
+    FFilterConnector := TFilterConnector.Create(AInstance, tempParameters, false);
     FFilterConnector.OnTryStopAction := @OnTryStopAction;
     FFilterConnector.OnTryStopAction := @OnTryStopAction;
   except
   except
     on ex: Exception do
     on ex: Exception do

+ 25 - 11
lazpaint/ucanvassize.pas

@@ -5,9 +5,9 @@ unit UCanvassize;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
-  StdCtrls, Spin, BGRAVirtualScreen, LazPaintType, uscaledpi, uresourcestrings,
-  BGRABitmap, BGRALayers, uimage;
+  Classes, SysUtils, Types, FileUtil, LResources, Forms, Controls, Graphics,
+  Dialogs, StdCtrls, Spin, BGRAVirtualScreen, LazPaintType, uscaledpi,
+  uresourcestrings, BGRABitmap, BGRALayers, BGRALayerOriginal, uimage;
 
 
 type
 type
   { TFCanvasSize }
   { TFCanvasSize }
@@ -48,20 +48,32 @@ type
 
 
 implementation
 implementation
 
 
-uses ugraph, bgrabitmaptypes, umac;
+uses ugraph, bgrabitmaptypes, umac, BGRATransform;
 
 
 function ChangeLayeredImageCanvasSize(layeredBmp: TLazPaintImage; newWidth,
 function ChangeLayeredImageCanvasSize(layeredBmp: TLazPaintImage; newWidth,
   newHeight: integer; anchor: string; background: TBGRAPixel;
   newHeight: integer; anchor: string; background: TBGRAPixel;
   repeatImage: boolean; flipMode: boolean): TBGRALayeredBitmap;
   repeatImage: boolean; flipMode: boolean): TBGRALayeredBitmap;
 var i,idx: integer;
 var i,idx: integer;
+  orig: TBGRALayerCustomOriginal;
+  newOrigin: TPoint;
+  newBmp: TBGRABitmap;
 begin
 begin
   result := TBGRALayeredBitmap.Create;
   result := TBGRALayeredBitmap.Create;
   for i := 0 to layeredbmp.NbLayers-1 do
   for i := 0 to layeredbmp.NbLayers-1 do
   begin
   begin
-    idx := result.AddOwnedLayer(ChangeCanvasSize(layeredbmp.LayerBitmap[i],newwidth,newHeight,anchor,background,repeatImage,flipMode),
-      layeredBmp.BlendOperation[i],layeredbmp.LayerOpacity[i]);
+    newBmp := ChangeCanvasSize(layeredbmp.LayerBitmap[i],layeredbmp.LayerOffset[i],layeredBmp.Width,layeredBmp.Height, newwidth,newHeight,anchor,background,repeatImage,flipMode);
+    idx := result.AddOwnedLayer(newBmp,layeredBmp.BlendOperation[i],layeredbmp.LayerOpacity[i]);
     result.LayerName[idx] := layeredbmp.LayerName[i];
     result.LayerName[idx] := layeredbmp.LayerName[i];
     result.LayerVisible[idx] := layeredbmp.LayerVisible[i];
     result.LayerVisible[idx] := layeredbmp.LayerVisible[i];
+    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);
+    end;
   end;
   end;
 end;
 end;
 
 
@@ -110,13 +122,15 @@ begin
     ModalResult := mrCancel else
     ModalResult := mrCancel else
     begin
     begin
 
 
-      canvasSizeResult.layeredBitmap := ChangeLayeredImageCanvasSize(LazPaintInstance.Image,tx,
-         ty,SelectedAnchor,BGRAPixelTransparent, repeatImage, CheckBox_FlipMode.Checked);
-      if LazPaintInstance.Image.SelectionReadonly <> nil then
-        canvasSizeResult.selection := ChangeCanvasSize(LazPaintInstance.Image.SelectionReadonly,tx,
-          ty,SelectedAnchor,BGRABlack, repeatImage, CheckBox_FlipMode.Checked);
+      canvasSizeResult.layeredBitmap := ChangeLayeredImageCanvasSize(LazPaintInstance.Image,
+         tx,ty,SelectedAnchor,BGRAPixelTransparent, repeatImage, CheckBox_FlipMode.Checked);
+      if LazPaintInstance.Image.SelectionMaskReadonly <> nil then
+        canvasSizeResult.selection := ChangeCanvasSize(LazPaintInstance.Image.SelectionMaskReadonly,
+          Point(0,0),LazPaintInstance.Image.Width,LazPaintInstance.Image.Height,
+          tx,ty,SelectedAnchor,BGRABlack, repeatImage, CheckBox_FlipMode.Checked);
       if LazPaintInstance.Image.SelectionLayerReadonly <> nil then
       if LazPaintInstance.Image.SelectionLayerReadonly <> nil then
         canvasSizeResult.selectionLayer := ChangeCanvasSize(LazPaintInstance.Image.SelectionLayerReadonly,
         canvasSizeResult.selectionLayer := ChangeCanvasSize(LazPaintInstance.Image.SelectionLayerReadonly,
+           Point(0,0),LazPaintInstance.Image.Width,LazPaintInstance.Image.Height,
            tx,ty,SelectedAnchor,BGRAPixelTransparent, repeatImage, CheckBox_FlipMode.Checked);
            tx,ty,SelectedAnchor,BGRAPixelTransparent, repeatImage, CheckBox_FlipMode.Checked);
 
 
       ModalResult := mrOK;
       ModalResult := mrOK;

+ 1 - 1
lazpaint/ucolorintensity.pas

@@ -220,7 +220,7 @@ var topmostInfo: TTopMostInfo;
 begin
 begin
   FMode := AMode;
   FMode := AMode;
   try
   try
-    FFilterConnector := TFilterConnector.Create(AInstance,AParameters);
+    FFilterConnector := TFilterConnector.Create(AInstance,AParameters,false);
     FFilterConnector.OnTryStopAction := @OnTryStopAction;
     FFilterConnector.OnTryStopAction := @OnTryStopAction;
   except
   except
     on ex: exception do
     on ex: exception do

+ 1 - 1
lazpaint/ucolorize.pas

@@ -298,7 +298,7 @@ var gsbaOptionFromConfig: boolean;
     topmostInfo: TTopMostInfo;
     topmostInfo: TTopMostInfo;
 begin
 begin
   try
   try
-    FFilterConnector := TFilterConnector.Create(AInstance,AParameters);
+    FFilterConnector := TFilterConnector.Create(AInstance,AParameters,false);
     FFilterConnector.OnTryStopAction := @OnTryStopAction;
     FFilterConnector.OnTryStopAction := @OnTryStopAction;
   except
   except
     on ex: exception do
     on ex: exception do

+ 2 - 2
lazpaint/ucommandline.pas

@@ -119,7 +119,7 @@ begin
           val(funcParams[10],o1.y,errPos);
           val(funcParams[10],o1.y,errPos);
           val(funcParams[11],o2.x,errPos);
           val(funcParams[11],o2.x,errPos);
           val(funcParams[12],o2.y,errPos);
           val(funcParams[12],o2.y,errPos);
-          layerAction := TLayerAction.Create(instance.Image);
+          layerAction := instance.Image.CreateAction(true);
           layerAction.DrawingLayer.GradientFill(0,0,
           layerAction.DrawingLayer.GradientFill(0,0,
             instance.Image.Width,instance.Image.Height,
             instance.Image.Width,instance.Image.Height,
             c1,c2,gt,o1,o2,dmDrawWithTransparency,True,False);
             c1,c2,gt,o1,o2,dmDrawWithTransparency,True,False);
@@ -142,7 +142,7 @@ begin
             errorEncountered := true;
             errorEncountered := true;
             exit;
             exit;
           end;
           end;
-          layerAction := TLayerAction.Create(instance.Image);
+          layerAction := instance.Image.CreateAction(true);
           layerAction.DrawingLayer.ApplyGlobalOpacity(opacity);
           layerAction.DrawingLayer.ApplyGlobalOpacity(opacity);
           layerAction.Validate;
           layerAction.Validate;
           FreeAndNil(layerAction);
           FreeAndNil(layerAction);

+ 3 - 3
lazpaint/uconfig.pas

@@ -56,7 +56,7 @@ type
     procedure FinalizeColorizePresets;
     procedure FinalizeColorizePresets;
 
 
     function DefaultLangage: string;
     function DefaultLangage: string;
-    class function ClassGetDefaultLangage(AIni: TIniFile): string;
+    class function ClassGetDefaultLangage(AIni: TIniFile): string; static;
     procedure SetDefaultLangage(value: string);
     procedure SetDefaultLangage(value: string);
 
 
     function GetLastUpdateCheck: TDateTime;
     function GetLastUpdateCheck: TDateTime;
@@ -66,8 +66,8 @@ type
     procedure SetLatestVersion(value: string);
     procedure SetLatestVersion(value: string);
     procedure GetUpdatedLanguages(AList: TStringList);
     procedure GetUpdatedLanguages(AList: TStringList);
     procedure SetUpdatedLanguages(AList: TStringList);
     procedure SetUpdatedLanguages(AList: TStringList);
-    class procedure ClassGetUpdatedLanguages(AList: TStringList; AIni: TIniFile; AVersion: string);
-    class procedure ClassSetUpdatedLanguages(AList: TStringList; AIni: TIniFile; AVersion: string);
+    class procedure ClassGetUpdatedLanguages(AList: TStringList; AIni: TIniFile; AVersion: string); static;
+    class procedure ClassSetUpdatedLanguages(AList: TStringList; AIni: TIniFile; AVersion: string); static;
     procedure AddUpdatedLanguage(ALang: string);
     procedure AddUpdatedLanguage(ALang: string);
 
 
     function Default3dObjectDirectory: string;
     function Default3dObjectDirectory: string;

+ 3 - 1
lazpaint/ufileextensions.pas

@@ -39,7 +39,9 @@ function GetImageFormatName(AFormat: TBGRAImageFormat): string;
 
 
 implementation
 implementation
 
 
-uses Masks, LazUTF8, UResourceStrings, BGRASVG;
+uses Masks, LazUTF8, UResourceStrings, BGRASVG,
+     BGRALayerOriginal, BGRASVGOriginal, BGRAGradientOriginal,
+     LCVectorOriginal, LCVectorShapes;
 
 
 function GetSelectedFilterExtensions(const Filter: string;
 function GetSelectedFilterExtensions(const Filter: string;
   FilterIndex: integer; ARemoveLeadingDot: boolean): TStringList;
   FilterIndex: integer; ARemoveLeadingDot: boolean): TStringList;

+ 33 - 13
lazpaint/ufilterconnector.pas

@@ -23,12 +23,14 @@ type
     FWorkArea: TRect;
     FWorkArea: TRect;
     FWorkAreaFullySelected: boolean;
     FWorkAreaFullySelected: boolean;
     FParameters: TVariableSet;
     FParameters: TVariableSet;
+    FLayerBackupImage: TBGRABitmap;
     function GetActionDone: boolean;
     function GetActionDone: boolean;
     function GetActiveLayeOffset: TPoint;
     function GetActiveLayeOffset: TPoint;
     function GetActiveLayer: TBGRABitmap;
     function GetActiveLayer: TBGRABitmap;
     function GetBackupLayer: TBGRABitmap;
     function GetBackupLayer: TBGRABitmap;
     function GetCurrentSelection: TBGRABitmap;
     function GetCurrentSelection: TBGRABitmap;
-    procedure Init(ALazPaintInstance: TLazPaintCustomInstance; AAction: TLayerAction; AOwned: boolean; AParameters: TVariableSet);
+    procedure Init(ALazPaintInstance: TLazPaintCustomInstance; AAction: TLayerAction; AOwned: boolean;
+                   AParameters: TVariableSet; AApplyOfsBefore: boolean);
     procedure OnTryStop({%H-}sender: TCustomLayerAction);
     procedure OnTryStop({%H-}sender: TCustomLayerAction);
     procedure DiscardAction;
     procedure DiscardAction;
     procedure ApplySelectionMaskOn(AFilteredLayer: TBGRABitmap);
     procedure ApplySelectionMaskOn(AFilteredLayer: TBGRABitmap);
@@ -36,7 +38,7 @@ type
   public
   public
     ApplyOnSelectionLayer: boolean;
     ApplyOnSelectionLayer: boolean;
     Form: TForm;
     Form: TForm;
-    constructor Create(ALazPaintInstance : TLazPaintCustomInstance; AParameters: TVariableSet);
+    constructor Create(ALazPaintInstance : TLazPaintCustomInstance; AParameters: TVariableSet; AApplyOfsBefore: boolean);
     constructor Create(ALazPaintInstance : TLazPaintCustomInstance; AParameters: TVariableSet; AAction: TLayerAction; AOwned: boolean);
     constructor Create(ALazPaintInstance : TLazPaintCustomInstance; AParameters: TVariableSet; AAction: TLayerAction; AOwned: boolean);
     destructor Destroy; override;
     destructor Destroy; override;
     procedure ValidateAction;
     procedure ValidateAction;
@@ -58,7 +60,7 @@ type
 
 
 implementation
 implementation
 
 
-uses Types, BGRABitmapTypes;
+uses Types, BGRABitmapTypes, BGRATransform;
 
 
 { TFilterConnector }
 { TFilterConnector }
 
 
@@ -88,7 +90,9 @@ end;
 
 
 function TFilterConnector.GetBackupLayer: TBGRABitmap;
 function TFilterConnector.GetBackupLayer: TBGRABitmap;
 begin
 begin
-  if ApplyOnSelectionLayer then
+  if Assigned(FLayerBackupImage) then
+    result := FLayerBackupImage
+  else if ApplyOnSelectionLayer then
     result := FAction.BackupSelectionLayer
     result := FAction.BackupSelectionLayer
   else
   else
     result := FAction.BackupSelectedLayer;
     result := FAction.BackupSelectedLayer;
@@ -96,17 +100,18 @@ end;
 
 
 function TFilterConnector.GetCurrentSelection: TBGRABitmap;
 function TFilterConnector.GetCurrentSelection: TBGRABitmap;
 begin
 begin
-  if ApplyOnSelectionLayer or FLazPaintInstance.Image.SelectionEmpty then
+  if ApplyOnSelectionLayer or FLazPaintInstance.Image.SelectionMaskEmpty then
     result := nil
     result := nil
   else
   else
   begin
   begin
-    result := FLazPaintInstance.Image.SelectionReadonly;
+    result := FLazPaintInstance.Image.SelectionMaskReadonly;
     if (result.Width <> ActiveLayer.Width) or (result.Height <> ActiveLayer.Height) then
     if (result.Width <> ActiveLayer.Width) or (result.Height <> ActiveLayer.Height) then
       result := nil;
       result := nil;
   end;
   end;
 end;
 end;
 
 
-procedure TFilterConnector.Init(ALazPaintInstance: TLazPaintCustomInstance; AAction: TLayerAction; AOwned: boolean; AParameters: TVariableSet);
+procedure TFilterConnector.Init(ALazPaintInstance: TLazPaintCustomInstance; AAction: TLayerAction; AOwned: boolean;
+                                AParameters: TVariableSet; AApplyOfsBefore: boolean);
 var sel: TBGRABitmap;
 var sel: TBGRABitmap;
   y,x: integer;
   y,x: integer;
   p : PBGRAPixel;
   p : PBGRAPixel;
@@ -114,13 +119,27 @@ var sel: TBGRABitmap;
 begin
 begin
   FLazPaintInstance := ALazPaintInstance;
   FLazPaintInstance := ALazPaintInstance;
   FParameters := AParameters;
   FParameters := AParameters;
+  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;
+
   FAction := AAction;
   FAction := AAction;
   FActionOwned:= AOwned;
   FActionOwned:= AOwned;
   FAction.OnTryStop := @OnTryStop;
   FAction.OnTryStop := @OnTryStop;
-  ApplyOnSelectionLayer:= not FLazPaintInstance.Image.SelectionLayerIsEmpty;
+
   sel := CurrentSelection;
   sel := CurrentSelection;
   if sel <> nil then
   if sel <> nil then
-    FWorkArea := FLazPaintInstance.Image.SelectionBounds
+    FWorkArea := FLazPaintInstance.Image.SelectionMaskBounds
   else
   else
     FWorkArea := rect(0,0,ActiveLayer.Width,ActiveLayer.Height);
     FWorkArea := rect(0,0,ActiveLayer.Width,ActiveLayer.Height);
   FWorkAreaFullySelected := true;
   FWorkAreaFullySelected := true;
@@ -159,19 +178,20 @@ begin
   end;
   end;
 end;
 end;
 
 
-constructor TFilterConnector.Create(ALazPaintInstance : TLazPaintCustomInstance; AParameters: TVariableSet);
+constructor TFilterConnector.Create(ALazPaintInstance : TLazPaintCustomInstance; AParameters: TVariableSet; AApplyOfsBefore: boolean);
 begin
 begin
-  Init(ALazPaintInstance,TLayerAction.Create(ALazPaintInstance.Image),True,AParameters);
+  Init(ALazPaintInstance,nil,True,AParameters,AApplyOfsBefore);
 end;
 end;
 
 
 constructor TFilterConnector.Create(ALazPaintInstance : TLazPaintCustomInstance; AParameters: TVariableSet; AAction: TLayerAction; AOwned: boolean);
 constructor TFilterConnector.Create(ALazPaintInstance : TLazPaintCustomInstance; AParameters: TVariableSet; AAction: TLayerAction; AOwned: boolean);
 begin
 begin
-  Init(ALazPaintInstance,AAction,AOwned,AParameters);
+  Init(ALazPaintInstance,AAction,AOwned,AParameters,false);
 end;
 end;
 
 
 destructor TFilterConnector.Destroy;
 destructor TFilterConnector.Destroy;
 begin
 begin
   DiscardAction;
   DiscardAction;
+  FLayerBackupImage.Free;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
@@ -189,7 +209,7 @@ procedure TFilterConnector.InvalidateActiveLayer(ARect: TRect);
 begin
 begin
   if IntersectRect(ARect, ARect, FWorkArea) then
   if IntersectRect(ARect, ARect, FWorkArea) then
   begin
   begin
-    with FLazPaintInstance.Image.LayerOffset[FLazPaintInstance.Image.currentImageLayerIndex] do
+    with FLazPaintInstance.Image.LayerOffset[FLazPaintInstance.Image.CurrentLayerIndex] do
       OffsetRect(ARect, X,Y);
       OffsetRect(ARect, X,Y);
     FLazPaintInstance.NotifyImageChange(True, ARect);
     FLazPaintInstance.NotifyImageChange(True, ARect);
   end;
   end;

+ 10 - 2
lazpaint/ufilters.pas

@@ -87,20 +87,28 @@ var
 
 
 var
 var
   layer: TBGRABitmap;
   layer: TBGRABitmap;
+  applyOfsBefore: Boolean;
 
 
 begin
 begin
   result := false;
   result := false;
   if filter = pfNone then exit;
   if filter = pfNone then exit;
   if not AInstance.Image.CheckNoAction then exit;
   if not AInstance.Image.CheckNoAction then exit;
   if not AInstance.image.CheckCurrentLayerVisible then exit;
   if not AInstance.image.CheckCurrentLayerVisible then exit;
-  if (filter = pfLinearNegative) and AInstance.Image.SelectionEmpty and (AInstance.Image.NbLayers = 1) then
+  if (filter = pfLinearNegative) and AInstance.Image.SelectionMaskEmpty and (AInstance.Image.NbLayers = 1) then
   begin
   begin
       AInstance.Image.LinearNegativeAll;
       AInstance.Image.LinearNegativeAll;
       result := true;
       result := true;
       exit;
       exit;
   end;
   end;
+
+  applyOfsBefore:= false;
+  if not (filter in[pfSharpen, pfSmooth, pfClearType, pfClearTypeInverse, pfNormalize, pfMedian,
+            pfNegative, pfLinearNegative, pfComplementaryColor, pfGrayscale]) then
+    if AInstance.Image.SelectionLayerIsEmpty then
+      applyOfsBefore := true;
+
   try
   try
-    FilterConnector := TFilterConnector.Create(AInstance, AParameters);
+    FilterConnector := TFilterConnector.Create(AInstance, AParameters, applyOfsBefore);
     layer := FilterConnector.ActiveLayer;
     layer := FilterConnector.ActiveLayer;
 
 
     filteredLayer := nil;
     filteredLayer := nil;

+ 26 - 14
lazpaint/ugraph.pas

@@ -30,7 +30,8 @@ function NicePoint(bmp: TBGRABitmap; ptF: TPointF; alpha: byte = 192):TRect; ove
 procedure NiceLine(bmp: TBGRABitmap; x1,y1,x2,y2: single; alpha: byte = 192);
 procedure NiceLine(bmp: TBGRABitmap; x1,y1,x2,y2: single; alpha: byte = 192);
 function NiceText(bmp: TBGRABitmap; x,y,bmpWidth,bmpHeight: integer; s: string; align: TAlignment = taLeftJustify; valign: TTextLayout = tlTop): TRect;
 function NiceText(bmp: TBGRABitmap; x,y,bmpWidth,bmpHeight: integer; s: string; align: TAlignment = taLeftJustify; valign: TTextLayout = tlTop): TRect;
 function ComputeColorCircle(tx,ty: integer; light: word; hueCorrection: boolean = true): TBGRABitmap;
 function ComputeColorCircle(tx,ty: integer; light: word; hueCorrection: boolean = true): TBGRABitmap;
-function ChangeCanvasSize(bmp: TBGRABitmap; newWidth,newHeight: integer; anchor: string; background: TBGRAPixel; repeatImage: boolean; flipMode: boolean = false): TBGRABitmap; overload;
+function ChangeCanvasSizeOrigin(oldWidth,oldHeight,newWidth, newHeight: integer; anchor: string): TPoint;
+function ChangeCanvasSize(bmp: TBGRABitmap; ofs: TPoint; oldWidth,oldHeight,newWidth,newHeight: integer; anchor: string; background: TBGRAPixel; repeatImage: boolean; flipMode: boolean = false): TBGRABitmap; overload;
 
 
 procedure RenderCloudsOn(bmp: TBGRABitmap; color: TBGRAPixel);
 procedure RenderCloudsOn(bmp: TBGRABitmap; color: TBGRAPixel);
 procedure RenderWaterOn(bmp: TBGRABitmap; waterColor, skyColor: TBGRAPixel);
 procedure RenderWaterOn(bmp: TBGRABitmap; waterColor, skyColor: TBGRAPixel);
@@ -1251,7 +1252,20 @@ begin
   end;
   end;
 end;
 end;
 
 
-function ChangeCanvasSize(bmp: TBGRABitmap; newWidth, newHeight: integer;
+function ChangeCanvasSizeOrigin(oldWidth,oldHeight,newWidth, newHeight: integer; anchor: string): TPoint;
+var
+  origin: TPoint;
+begin
+  origin := Point((newWidth div 2)-(oldWidth div 2),(newHeight div 2)-(oldHeight div 2));
+  anchor := UTF8LowerCase(anchor);
+  if (anchor='topleft') or (anchor='top') or (anchor='topright') then origin.Y := 0;
+  if (anchor='bottomleft') or (anchor='bottom') or (anchor='bottomright') then origin.Y := newHeight-oldHeight;
+  if (anchor='topleft') or (anchor='left') or (anchor='bottomleft') then origin.X := 0;
+  if (anchor='topright') or (anchor='right') or (anchor='bottomright') then origin.X := newWidth-oldWidth;
+  result := origin;
+end;
+
+function ChangeCanvasSize(bmp: TBGRABitmap; ofs: TPoint; oldWidth,oldHeight,newWidth, newHeight: integer;
   anchor: string; background: TBGRAPixel; repeatImage: boolean; flipMode: boolean = false): TBGRABitmap;
   anchor: string; background: TBGRAPixel; repeatImage: boolean; flipMode: boolean = false): TBGRABitmap;
 var origin: TPoint;
 var origin: TPoint;
     xb,yb: integer;
     xb,yb: integer;
@@ -1261,21 +1275,19 @@ var origin: TPoint;
 begin
 begin
    if (newWidth < 1) or (newHeight < 1) then
    if (newWidth < 1) or (newHeight < 1) then
      raise exception.Create('Invalid canvas size');
      raise exception.Create('Invalid canvas size');
-   origin := Point((newWidth-bmp.Width) div 2,(newHeight-bmp.Height) div 2);
-   anchor := UTF8LowerCase(anchor);
-   if (anchor='topleft') or (anchor='top') or (anchor='topright') then origin.Y := 0;
-   if (anchor='bottomleft') or (anchor='bottom') or (anchor='bottomright') then origin.Y := newHeight-bmp.Height;
-   if (anchor='topleft') or (anchor='left') or (anchor='bottomleft') then origin.X := 0;
-   if (anchor='topright') or (anchor='right') or (anchor='bottomright') then origin.X := newWidth-bmp.Width;
+   origin := ChangeCanvasSizeOrigin(oldWidth, oldHeight, newWidth, newHeight, anchor);
+   inc(origin.x, ofs.x);
+   inc(origin.y, ofs.y);
+
    result := TBGRABitmap.Create(newWidth,newHeight, background);
    result := TBGRABitmap.Create(newWidth,newHeight, background);
-   dx := bmp.Width;
-   dy := bmp.Height;
+   dx := oldWidth;
+   dy := oldHeight;
    if repeatImage then
    if repeatImage then
    begin
    begin
-     minx := (0-origin.X-bmp.Width+1) div bmp.Width;
-     miny := (0-origin.Y-bmp.Height+1) div bmp.Height;
-     maxx := (newWidth-origin.X+bmp.Width-1) div bmp.Width;
-     maxy := (newHeight-origin.Y+bmp.Height-1) div bmp.Height;
+     minx := (0-origin.X-oldWidth+1) div oldWidth;
+     miny := (0-origin.Y-oldHeight+1) div oldHeight;
+     maxx := (newWidth-origin.X+oldWidth-1) div oldWidth;
+     maxy := (newHeight-origin.Y+oldHeight-1) div oldHeight;
    end else
    end else
    begin
    begin
      minx := 0;
      minx := 0;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 294 - 323
lazpaint/uimage.pas


+ 235 - 136
lazpaint/uimageaction.pas

@@ -6,7 +6,7 @@ interface
 
 
 uses
 uses
   Classes, SysUtils, LazPaintType, BGRABitmap, UImage, UTool, UScripting,
   Classes, SysUtils, LazPaintType, BGRABitmap, UImage, UTool, UScripting,
-  ULayerAction, UImageType, BGRABitmapTypes;
+  ULayerAction, UImageType, BGRABitmapTypes, BGRALayerOriginal, BGRASVGOriginal;
 
 
 type
 type
 
 
@@ -21,6 +21,7 @@ type
     procedure ChooseTool(ATool: TPaintToolType);
     procedure ChooseTool(ATool: TPaintToolType);
     procedure RegisterScripts(ARegister: Boolean);
     procedure RegisterScripts(ARegister: Boolean);
     function GenericScriptFunction(AVars: TVariableSet): TScriptResult;
     function GenericScriptFunction(AVars: TVariableSet): TScriptResult;
+    procedure ReleaseSelection;
   public
   public
     constructor Create(AInstance: TLazPaintCustomInstance);
     constructor Create(AInstance: TLazPaintCustomInstance);
     destructor Destroy; override;
     destructor Destroy; override;
@@ -37,17 +38,22 @@ type
     procedure RotateCW;
     procedure RotateCW;
     procedure RotateCCW;
     procedure RotateCCW;
     procedure LinearNegativeAll;
     procedure LinearNegativeAll;
+    procedure NegativeAll;
+    procedure SwapRedBlueAll;
     procedure InvertSelection;
     procedure InvertSelection;
     procedure Deselect;
     procedure Deselect;
     procedure CopySelection;
     procedure CopySelection;
     procedure CutSelection;
     procedure CutSelection;
+    procedure RetrieveSelection;
     procedure DeleteSelection;
     procedure DeleteSelection;
+    procedure RemoveSelection;
     procedure Paste;
     procedure Paste;
     procedure PasteAsNewLayer;
     procedure PasteAsNewLayer;
     procedure SelectAll;
     procedure SelectAll;
     procedure SelectionFit;
     procedure SelectionFit;
-    procedure NewLayer;
-    procedure NewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation);
+    procedure NewLayer; overload;
+    function NewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation): boolean; overload;
+    function NewLayer(ALayer: TBGRALayerCustomOriginal; AName: string; ABlendOp: TBlendOperation): boolean; overload;
     procedure DuplicateLayer;
     procedure DuplicateLayer;
     procedure MergeLayerOver;
     procedure MergeLayerOver;
     procedure RemoveLayer;
     procedure RemoveLayer;
@@ -55,6 +61,7 @@ type
     procedure Import3DObject(AFilenameUTF8: string);
     procedure Import3DObject(AFilenameUTF8: string);
     function TryAddLayerFromFile(AFilenameUTF8: string; ALoadedImage: TBGRABitmap = nil): boolean;
     function TryAddLayerFromFile(AFilenameUTF8: string; ALoadedImage: TBGRABitmap = nil): boolean;
     function AddLayerFromBitmap(ABitmap: TBGRABitmap; AName: string): boolean;
     function AddLayerFromBitmap(ABitmap: TBGRABitmap; AName: string): boolean;
+    function AddLayerFromOriginal(AOriginal: TBGRALayerCustomOriginal; AName: string): boolean;
     function LoadSelection(AFilenameUTF8: string; ALoadedImage: PImageEntry = nil): boolean;
     function LoadSelection(AFilenameUTF8: string; ALoadedImage: PImageEntry = nil): boolean;
     property Image: TLazPaintImage read GetImage;
     property Image: TLazPaintImage read GetImage;
     property ToolManager: TToolManager read GetToolManager;
     property ToolManager: TToolManager read GetToolManager;
@@ -64,7 +71,8 @@ type
 implementation
 implementation
 
 
 uses Controls, Dialogs, UResourceStrings, UObject3D,
 uses Controls, Dialogs, UResourceStrings, UObject3D,
-     ULoadImage, UGraph, UClipboard, Types;
+     ULoadImage, UGraph, UClipboard, Types, BGRAGradientOriginal,
+     BGRATransform, ULoading;
 
 
 { TImageActions }
 { TImageActions }
 
 
@@ -75,7 +83,10 @@ end;
 
 
 function TImageActions.GetCurrentTool: TPaintToolType;
 function TImageActions.GetCurrentTool: TPaintToolType;
 begin
 begin
-  result := FInstance.ToolManager.GetCurrentToolType;
+  if FInstance.ToolManager.CurrentTool = nil then
+    result := ptHand
+  else
+    result := FInstance.ToolManager.GetCurrentToolType;
 end;
 end;
 
 
 function TImageActions.GetToolManager: TToolManager;
 function TImageActions.GetToolManager: TToolManager;
@@ -103,6 +114,9 @@ begin
   Scripting.RegisterScriptFunction('ImageFillBackground',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('ImageFillBackground',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('ImageRotateCW',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('ImageRotateCW',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('ImageRotateCCW',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('ImageRotateCCW',@GenericScriptFunction,ARegister);
+  Scripting.RegisterScriptFunction('ImageLinearNegative',@GenericScriptFunction,ARegister);
+  Scripting.RegisterScriptFunction('ImageNegative',@GenericScriptFunction,ARegister);
+  Scripting.RegisterScriptFunction('ImageSwapRedBlue',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('EditUndo',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('EditUndo',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('EditRedo',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('EditRedo',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('EditInvertSelection',@GenericScriptFunction,ARegister);
   Scripting.RegisterScriptFunction('EditInvertSelection',@GenericScriptFunction,ARegister);
@@ -153,6 +167,9 @@ begin
   if f = 'ImageFillBackground' then FillBackground else
   if f = 'ImageFillBackground' then FillBackground else
   if f = 'ImageRotateCW' then RotateCW else
   if f = 'ImageRotateCW' then RotateCW else
   if f = 'ImageRotateCCW' then RotateCCW else
   if f = 'ImageRotateCCW' then RotateCCW else
+  if f = 'ImageLinearNegative' then LinearNegativeAll else
+  if f = 'ImageNegative' then NegativeAll else
+  if f = 'ImageSwapRedBlue' then SwapRedBlueAll else
   if f = 'EditUndo' then Undo else
   if f = 'EditUndo' then Undo else
   if f = 'EditRedo' then Redo else
   if f = 'EditRedo' then Redo else
   if f = 'EditInvertSelection' then InvertSelection else
   if f = 'EditInvertSelection' then InvertSelection else
@@ -184,7 +201,7 @@ begin
   try
   try
     c := ToolManager.ToolBackColor;
     c := ToolManager.ToolBackColor;
     c.alpha := 255;
     c.alpha := 255;
-    LayerAction := TLayerAction.Create(Image);
+    LayerAction := Image.CreateAction(true);
     LayerAction.SelectedImageLayer.ReplaceColor(BGRAPixelTransparent,c);
     LayerAction.SelectedImageLayer.ReplaceColor(BGRAPixelTransparent,c);
     p := LayerAction.SelectedImageLayer.Data;
     p := LayerAction.SelectedImageLayer.Data;
     for n := LayerAction.SelectedImageLayer.NbPixels-1 downto 0 do
     for n := LayerAction.SelectedImageLayer.NbPixels-1 downto 0 do
@@ -206,16 +223,22 @@ procedure TImageActions.FillBackground;
 var tempBmp: TBGRABitmap;
 var tempBmp: TBGRABitmap;
     c: TBGRAPixel;
     c: TBGRAPixel;
     LayerAction: TLayerAction;
     LayerAction: TLayerAction;
+    y: Integer;
 begin
 begin
   if not Image.CheckNoAction then exit;
   if not Image.CheckNoAction then exit;
   LayerAction := nil;
   LayerAction := nil;
   try
   try
     c := ToolManager.ToolBackColor;
     c := ToolManager.ToolBackColor;
     c.alpha := 255;
     c.alpha := 255;
-    LayerAction := TLayerAction.Create(Image);
-    tempBmp := TBGRABitmap.Create(image.Width,image.Height,c);
-    tempBmp.PutImage(0,0,LayerAction.SelectedImageLayer,dmDrawWithTransparency);
-    LayerAction.ReplaceSelectedLayer(tempBmp, True);
+    LayerAction := Image.CreateAction(True);
+    tempBmp := TBGRABitmap.Create(LayerAction.SelectedImageLayer.Width,1);
+    for y := 0 to LayerAction.SelectedImageLayer.Height-1 do
+    begin
+       tempBmp.Fill(c);
+       tempBmp.PutImage(0,-y,LayerAction.SelectedImageLayer,dmDrawWithTransparency);
+       LayerAction.SelectedImageLayer.PutImage(0,y,tempBmp,dmSet);
+    end;
+    tempBmp.Free;
     image.LayerMayChangeCompletely(LayerAction.SelectedImageLayer);
     image.LayerMayChangeCompletely(LayerAction.SelectedImageLayer);
     LayerAction.Validate;
     LayerAction.Validate;
   except
   except
@@ -317,11 +340,11 @@ begin
 
 
     if Image.CheckNoAction then
     if Image.CheckNoAction then
     begin
     begin
-      LayerAction := TLayerAction.Create(Image);
+      LayerAction := Image.CreateAction;
+      LayerAction.RemoveSelection;
       LayerAction.QuerySelection;
       LayerAction.QuerySelection;
-      LayerAction.CurrentSelection.Fill(BGRABlack);
       LayerAction.CurrentSelection.PutImage(0,0,newSelection,dmSet);
       LayerAction.CurrentSelection.PutImage(0,0,newSelection,dmSet);
-      Image.SelectionMayChangeCompletely;
+      LayerAction.NotifyChange(Image.SelectionMask,Image.SelectionMaskBounds);
       LayerAction.Validate;
       LayerAction.Validate;
       result := true;
       result := true;
     end;
     end;
@@ -339,7 +362,7 @@ begin
   if not image.CheckNoAction then exit;
   if not image.CheckNoAction then exit;
   if not image.CheckCurrentLayerVisible then exit;
   if not image.CheckCurrentLayerVisible then exit;
   try
   try
-    if image.SelectionEmpty then
+    if image.SelectionMaskEmpty then
     begin
     begin
       FInstance.ShowMessage(rsCrop, rsEmptySelection);
       FInstance.ShowMessage(rsCrop, rsEmptySelection);
       exit;
       exit;
@@ -384,17 +407,17 @@ begin
     exit;
     exit;
   end;
   end;
   try
   try
-    if image.SelectionEmpty then
+    if image.SelectionMaskEmpty then
     begin
     begin
       FInstance.ShowMessage(rsCrop,rsEmptySelection);
       FInstance.ShowMessage(rsCrop,rsEmptySelection);
       exit;
       exit;
     end;
     end;
-    if not image.SelectionEmpty then
+    if not image.SelectionMaskEmpty then
     begin
     begin
-      r := image.SelectionBounds;
+      r := image.SelectionMaskBounds;
       if (r.left = 0) and (r.Top = 0) and (r.right = image.width) and (r.Bottom =image.height) then exit;
       if (r.left = 0) and (r.Top = 0) and (r.right = image.width) and (r.Bottom =image.height) then exit;
       cropped := image.MakeLayeredBitmapAndSelectionCopy;
       cropped := image.MakeLayeredBitmapAndSelectionCopy;
-      selectedLayer := image.currentImageLayerIndex;
+      selectedLayer := image.CurrentLayerIndex;
       for i := 0 to cropped.layeredBitmap.NbLayers-1 do
       for i := 0 to cropped.layeredBitmap.NbLayers-1 do
       begin
       begin
         cropped.layeredBitmap.LayerBitmap[i].ApplyMask(cropped.selection);
         cropped.layeredBitmap.LayerBitmap[i].ApplyMask(cropped.selection);
@@ -404,7 +427,7 @@ begin
       BGRAReplace(cropped.selection,cropped.selection.GetPart(r));
       BGRAReplace(cropped.selection,cropped.selection.GetPart(r));
       if cropped.selectionLayer <> nil then BGRAReplace(cropped.selectionLayer,cropped.selectionLayer.GetPart(r));
       if cropped.selectionLayer <> nil then BGRAReplace(cropped.selectionLayer,cropped.selectionLayer.GetPart(r));
       image.Assign(cropped,true,true);
       image.Assign(cropped,true,true);
-      image.SetCurrentImageLayerIndex(selectedLayer);
+      image.SetCurrentLayerByIndex(selectedLayer);
     end;
     end;
   except
   except
     on ex:Exception do
     on ex:Exception do
@@ -425,6 +448,7 @@ end;
 function TImageActions.TryAddLayerFromFile(AFilenameUTF8: string; ALoadedImage: TBGRABitmap = nil): boolean;
 function TImageActions.TryAddLayerFromFile(AFilenameUTF8: string; ALoadedImage: TBGRABitmap = nil): boolean;
 var
 var
   newPicture: TBGRABitmap;
   newPicture: TBGRABitmap;
+  svgOrig: TBGRALayerSVGOriginal;
 begin
 begin
   result := false;
   result := false;
   if not AbleToLoadUTF8(AFilenameUTF8) then
   if not AbleToLoadUTF8(AFilenameUTF8) then
@@ -434,37 +458,44 @@ begin
     exit;
     exit;
   end;
   end;
   try
   try
-    if Assigned(ALoadedImage) then
-      newPicture := ALoadedImage
-    else
-      newPicture := LoadFlatImageUTF8(AFilenameUTF8).bmp;
-    AddLayerFromBitmap(newPicture, ExtractFileName(AFilenameUTF8));
+    if Image.DetectImageFormat(AFilenameUTF8) = ifSvg then
+    begin
+      svgOrig := LoadSVGOriginalUTF8(AFilenameUTF8);
+      AddLayerFromOriginal(svgOrig, ExtractFileName(AFilenameUTF8));
+      FreeAndNil(ALoadedImage);
+    end else
+    begin
+      if Assigned(ALoadedImage) then
+      begin
+        newPicture := ALoadedImage;
+        ALoadedImage := nil;
+      end
+      else
+        newPicture := LoadFlatImageUTF8(AFilenameUTF8).bmp;
+      AddLayerFromBitmap(newPicture, ExtractFileName(AFilenameUTF8));
+    end;
+
   except
   except
     on ex: Exception do
     on ex: Exception do
+    begin
+      ALoadedImage.Free;
       FInstance.ShowError('TryAddLayerFromFile',ex.Message);
       FInstance.ShowError('TryAddLayerFromFile',ex.Message);
+    end;
   end;
   end;
 end;
 end;
 
 
-function TImageActions.AddLayerFromBitmap(ABitmap: TBGRABitmap; AName: string
-  ): boolean;
+function TImageActions.AddLayerFromBitmap(ABitmap: TBGRABitmap; AName: string): boolean;
 var
 var
-  layeraction: TLayerAction;
   ratio: single;
   ratio: single;
   xorMask: TBGRABitmap;
   xorMask: TBGRABitmap;
 begin
 begin
   if (ABitmap <> nil) and (ABitmap.Width > 0) and (ABitmap.Height > 0) then
   if (ABitmap <> nil) and (ABitmap.Width > 0) and (ABitmap.Height > 0) then
   begin
   begin
-    if ToolManager.GetCurrentToolType in [ptDeformation,ptRotateSelection,ptMoveSelection,ptTextureMapping,ptLayerMapping] then
+    if CurrentTool in [ptDeformation,ptRotateSelection,ptMoveSelection,ptTextureMapping,ptLayerMapping] then
       ChooseTool(ptHand);
       ChooseTool(ptHand);
     if image.CheckNoAction then
     if image.CheckNoAction then
     begin
     begin
-      if not Image.SelectionEmpty then
-      begin
-        layeraction := TLayerAction.Create(image);
-        layeraction.ReleaseSelection;
-        layeraction.Validate;
-        layeraction.Free;
-      end;
+      if not Image.SelectionMaskEmpty then ReleaseSelection;
       if (ABitmap.Width > Image.Width) or (ABitmap.Height > Image.Height) then
       if (ABitmap.Width > Image.Width) or (ABitmap.Height > Image.Height) then
       begin
       begin
         ratio := 1;
         ratio := 1;
@@ -482,10 +513,17 @@ begin
       end
       end
       else
       else
         xorMask := nil;
         xorMask := nil;
-      NewLayer(ABitmap, AName, boTransparent);
-      if Assigned(xorMask) then
-        NewLayer(xorMask, AName + ' (xor)', boXor);
-      result := true;
+      if NewLayer(ABitmap, AName, boTransparent) then
+      begin
+        if Assigned(xorMask) then
+          result := NewLayer(xorMask, AName + ' (xor)', boXor)
+        else
+          result := true;
+      end else
+      begin
+        xorMask.Free;
+        result := false;
+      end;
     end else
     end else
     begin
     begin
       ABitmap.Free;
       ABitmap.Free;
@@ -498,44 +536,45 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TImageActions.AddLayerFromOriginal(AOriginal: TBGRALayerCustomOriginal;
+  AName: string): boolean;
+begin
+  if AOriginal <> nil then
+  begin
+    if CurrentTool in [ptDeformation,ptRotateSelection,ptMoveSelection,ptTextureMapping,ptLayerMapping] then
+      ChooseTool(ptHand);
+    if image.CheckNoAction then
+    begin
+      if not Image.SelectionMaskEmpty then ReleaseSelection;
+      result := NewLayer(AOriginal, AName, boTransparent);
+    end else
+    begin
+      AOriginal.Free;
+      result := false;
+    end;
+  end else
+  begin
+    AOriginal.Free;
+    result := false;
+  end;
+end;
+
 procedure TImageActions.HorizontalFlip(AOption: TFlipOption);
 procedure TImageActions.HorizontalFlip(AOption: TFlipOption);
-var bounds: TRect;
-    LayerAction: TLayerAction;
 begin
 begin
   try
   try
     if (AOption = foCurrentLayer) then
     if (AOption = foCurrentLayer) then
+      image.HorizontalFlip(Image.CurrentLayerIndex) else
+    if ((AOption = foAuto) and not image.SelectionMaskEmpty) or (AOption = foSelection) then
     begin
     begin
-      if not Image.CheckNoAction then exit;
-      LayerAction := TLayerAction.Create(Image);
-      LayerAction.SelectedImageLayer.HorizontalFlip;
-      Image.LayerMayChangeCompletely(LayerAction.SelectedImageLayer);
-      LayerAction.Validate;
-      LayerAction.Free;
-    end else
-    if ((AOption = foAuto) and not image.SelectionEmpty) or (AOption = foSelection) then
-    begin
-      if not image.SelectionEmpty then
+      if not image.SelectionMaskEmpty then
       begin
       begin
         ChooseTool(ptMoveSelection);
         ChooseTool(ptMoveSelection);
         if not Image.CheckNoAction then exit;
         if not Image.CheckNoAction then exit;
-        LayerAction := TLayerAction.Create(Image);
-        LayerAction.AllChangesNotified := true;
-        bounds := Image.SelectionBounds;
-        LayerAction.currentSelection.HorizontalFlip(bounds);
-        LayerAction.NotifyChange(LayerAction.currentSelection,bounds);
-        Image.SelectionMayChange(bounds);
-        if LayerAction.DrawingLayer <> nil then
-        begin
-          LayerAction.DrawingLayer.HorizontalFlip(bounds);
-          LayerAction.NotifyChange(LayerAction.DrawingLayer,bounds);
-          Image.LayerMayChange(LayerAction.DrawingLayer,bounds);
-        end;
-        LayerAction.Validate;
-        LayerAction.Free;
+        Image.SelectionTransform := AffineMatrixTranslation(+Image.Width/2,0)*AffineMatrixScale(-1,1)*AffineMatrixTranslation(-Image.Width/2,0)*Image.SelectionTransform;
       end else
       end else
         exit;
         exit;
     end else
     end else
-    if ((AOption = foAuto) and image.SelectionEmpty) or (AOption = foWholePicture) then
+    if ((AOption = foAuto) and image.SelectionMaskEmpty) or (AOption = foWholePicture) then
       image.HorizontalFlip;
       image.HorizontalFlip;
   except
   except
     on ex:Exception do
     on ex:Exception do
@@ -544,42 +583,21 @@ begin
 end;
 end;
 
 
 procedure TImageActions.VerticalFlip(AOption: TFlipOption);
 procedure TImageActions.VerticalFlip(AOption: TFlipOption);
-var bounds: TRect;
-    LayerAction: TLayerAction;
 begin
 begin
   try
   try
     if (AOption = foCurrentLayer) then
     if (AOption = foCurrentLayer) then
+      image.VerticalFlip(Image.CurrentLayerIndex) else
+    if ((AOption = foAuto) and not image.SelectionMaskEmpty) or (AOption = foSelection) then
     begin
     begin
-      if not Image.CheckNoAction then exit;
-      LayerAction := TLayerAction.Create(Image);
-      LayerAction.SelectedImageLayer.VerticalFlip;
-      Image.LayerMayChangeCompletely(LayerAction.SelectedImageLayer);
-      LayerAction.Validate;
-      LayerAction.Free;
-    end else
-    if ((AOption = foAuto) and not image.SelectionEmpty) or (AOption = foSelection) then
-    begin
-      if not image.SelectionEmpty then
+      if not image.SelectionMaskEmpty then
       begin
       begin
         ChooseTool(ptMoveSelection);
         ChooseTool(ptMoveSelection);
         if not Image.CheckNoAction then exit;
         if not Image.CheckNoAction then exit;
-        LayerAction := TLayerAction.Create(Image);
-        bounds := Image.SelectionBounds;
-        LayerAction.currentSelection.VerticalFlip(bounds);
-        LayerAction.NotifyChange(LayerAction.currentSelection,bounds);
-        Image.SelectionMayChange(bounds);
-        if LayerAction.DrawingLayer <> nil then
-        begin
-          LayerAction.DrawingLayer.VerticalFlip(bounds);
-          LayerAction.NotifyChange(LayerAction.DrawingLayer,bounds);
-          Image.LayerMayChange(LayerAction.DrawingLayer,bounds);
-        end;
-        LayerAction.Validate;
-        LayerAction.Free;
+        Image.SelectionTransform := AffineMatrixTranslation(0,+Image.Height/2)*AffineMatrixScale(1,-1)*AffineMatrixTranslation(0,-Image.Height/2)*Image.SelectionTransform;
       end else
       end else
         exit;
         exit;
     end else
     end else
-    if ((AOption = foAuto) and image.SelectionEmpty) or (AOption = foWholePicture) then
+    if ((AOption = foAuto) and image.SelectionMaskEmpty) or (AOption = foWholePicture) then
       image.VerticalFlip;
       image.VerticalFlip;
   except
   except
     on ex:Exception do
     on ex:Exception do
@@ -602,6 +620,16 @@ begin
   Image.LinearNegativeAll;
   Image.LinearNegativeAll;
 end;
 end;
 
 
+procedure TImageActions.NegativeAll;
+begin
+  Image.NegativeAll;
+end;
+
+procedure TImageActions.SwapRedBlueAll;
+begin
+  Image.SwapRedBlue;
+end;
+
 procedure TImageActions.InvertSelection;
 procedure TImageActions.InvertSelection;
 var LayerAction: TLayerAction;
 var LayerAction: TLayerAction;
     p : PBGRAPixel;
     p : PBGRAPixel;
@@ -609,9 +637,10 @@ var LayerAction: TLayerAction;
 begin
 begin
   LayerAction := nil;
   LayerAction := nil;
   try
   try
-    if not ToolManager.IsSelectingTool then ChooseTool(ptSelectRect);
-    LayerAction := TLayerAction.Create(Image);
+    if not (CurrentTool in[ptSelectRect,ptSelectEllipse]) then ChooseTool(ptSelectRect);
+    LayerAction := Image.CreateAction(false);
     LayerAction.QuerySelection;
     LayerAction.QuerySelection;
+    LayerAction.ApplySelectionTransform;
     p := LayerAction.CurrentSelection.Data;
     p := LayerAction.CurrentSelection.Data;
     for n := LayerAction.CurrentSelection.NbPixels-1 downto 0 do
     for n := LayerAction.CurrentSelection.NbPixels-1 downto 0 do
     begin
     begin
@@ -621,7 +650,7 @@ begin
     LayerAction.CurrentSelection.InvalidateBitmap;
     LayerAction.CurrentSelection.InvalidateBitmap;
     LayerAction.CurrentSelection.LinearNegative;
     LayerAction.CurrentSelection.LinearNegative;
     LayerAction.Validate;
     LayerAction.Validate;
-    Image.SelectionMayChangeCompletely;
+    Image.SelectionMaskMayChangeCompletely;
   except
   except
     on ex:Exception do
     on ex:Exception do
       FInstance.ShowError('InvertSelection',ex.Message);
       FInstance.ShowError('InvertSelection',ex.Message);
@@ -637,13 +666,7 @@ begin
   if not Image.CheckNoAction then exit;
   if not Image.CheckNoAction then exit;
   LayerAction := nil;
   LayerAction := nil;
   try
   try
-    if not image.SelectionEmpty then
-    begin
-      LayerAction := TLayerAction.Create(Image);
-      LayerAction.AllChangesNotified:= true;
-      LayerAction.ReleaseSelection;
-      LayerAction.Validate;
-    end;
+    if not image.SelectionMaskEmpty then ReleaseSelection;
   except
   except
     on ex:Exception do
     on ex:Exception do
       FInstance.ShowError('Deselect',ex.Message);
       FInstance.ShowError('Deselect',ex.Message);
@@ -659,9 +682,9 @@ begin
   LayerAction := nil;
   LayerAction := nil;
   try
   try
     if not image.CheckNoAction then exit;
     if not image.CheckNoAction then exit;
-    bounds := Image.SelectionBounds;
+    bounds := Image.SelectionMaskBounds;
     if IsRectEmpty(bounds) then exit;
     if IsRectEmpty(bounds) then exit;
-    LayerAction := TLayerAction.Create(Image);
+    LayerAction := Image.CreateAction;
     LayerAction.ApplySelectionMask;
     LayerAction.ApplySelectionMask;
     if Image.SelectionLayerIsEmpty then
     if Image.SelectionLayerIsEmpty then
       LayerAction.RetrieveSelection;
       LayerAction.RetrieveSelection;
@@ -686,12 +709,13 @@ end;
 procedure TImageActions.CutSelection;
 procedure TImageActions.CutSelection;
 var LayerAction: TLayerAction;
 var LayerAction: TLayerAction;
 begin
 begin
+  if image.SelectionMaskEmpty then exit;
   if not image.CheckNoAction then exit;
   if not image.CheckNoAction then exit;
   LayerAction := nil;
   LayerAction := nil;
   try
   try
-    if image.SelectionEmpty then exit;
     CopySelection;
     CopySelection;
-    LayerAction := TLayerAction.Create(Image);
+    LayerAction := Image.CreateAction;
+    LayerAction.ApplySelectionTransform;
     if (LayerAction.GetSelectionLayerIfExists = nil) or (LayerAction.GetSelectionLayerIfExists.Empty) then
     if (LayerAction.GetSelectionLayerIfExists = nil) or (LayerAction.GetSelectionLayerIfExists.Empty) then
       LayerAction.EraseSelectionInBitmap;
       LayerAction.EraseSelectionInBitmap;
     LayerAction.RemoveSelection;
     LayerAction.RemoveSelection;
@@ -706,17 +730,41 @@ begin
   LayerAction.Free;
   LayerAction.Free;
 end;
 end;
 
 
-procedure TImageActions.DeleteSelection;
+procedure TImageActions.RetrieveSelection;
 var LayerAction: TLayerAction;
 var LayerAction: TLayerAction;
+  r: TRect;
 begin
 begin
+  if image.SelectionMaskEmpty then exit;
   if not image.CheckNoAction then exit;
   if not image.CheckNoAction then exit;
   LayerAction := nil;
   LayerAction := nil;
   try
   try
-    if image.SelectionEmpty then exit;
+    LayerAction := Image.CreateAction(false);
+    if LayerAction.RetrieveSelectionIfLayerEmpty(True) then
+    begin
+      r := Image.SelectionMaskBounds;
+      ComputeSelectionMask(LayerAction.GetOrCreateSelectionLayer,LayerAction.CurrentSelection,r);
+      LayerAction.NotifyChange(LayerAction.GetOrCreateSelectionLayer, r);
+      LayerAction.Validate;
+    end;
+    if image.SelectionLayerIsEmpty then MessagePopup(rsNothingToBeRetrieved,2000);
+  except on ex:exception do FInstance.ShowError('RetrieveSelection',ex.Message);
+  end;
+  LayerAction.Free;
+end;
 
 
-    LayerAction := TLayerAction.Create(Image);
+procedure TImageActions.DeleteSelection;
+var LayerAction: TLayerAction;
+begin
+  if image.SelectionMaskEmpty then exit;
+  if not image.CheckNoAction then exit;
+  LayerAction := nil;
+  try
+    LayerAction := Image.CreateAction;
     if Image.SelectionLayerIsEmpty then
     if Image.SelectionLayerIsEmpty then
+    begin
+      LayerAction.ApplySelectionTransform;
       LayerAction.EraseSelectionInBitmap;
       LayerAction.EraseSelectionInBitmap;
+    end;
     LayerAction.RemoveSelection;
     LayerAction.RemoveSelection;
     LayerAction.Validate;
     LayerAction.Validate;
     if (CurrentTool = ptRotateSelection) or
     if (CurrentTool = ptRotateSelection) or
@@ -729,6 +777,34 @@ begin
   LayerAction.Free;
   LayerAction.Free;
 end;
 end;
 
 
+procedure TImageActions.RemoveSelection;
+var LayerAction: TLayerAction;
+begin
+  if image.SelectionMaskEmpty then exit;
+  if not image.CheckNoAction then exit;
+  LayerAction := nil;
+  try
+    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;
+end;
+
+procedure TImageActions.ReleaseSelection;
+var
+  layeraction: TLayerAction;
+begin
+  layeraction := image.CreateAction;
+  layeraction.ReleaseSelection;
+  layeraction.Validate;
+  layeraction.Free;
+end;
+
 procedure TImageActions.Paste;
 procedure TImageActions.Paste;
 var partial: TBGRABitmap;
 var partial: TBGRABitmap;
     layeraction: TLayerAction;
     layeraction: TLayerAction;
@@ -741,7 +817,7 @@ begin
       if partial.NbPixels <> 0 then
       if partial.NbPixels <> 0 then
       begin
       begin
         ToolManager.ToolCloseDontReopen;
         ToolManager.ToolCloseDontReopen;
-        layeraction := TLayerAction.Create(Image);
+        layeraction := Image.CreateAction(true);
         layeraction.ReleaseSelection;
         layeraction.ReleaseSelection;
         layeraction.QuerySelection;
         layeraction.QuerySelection;
         pastePos := Point((image.Width - partial.Width) div 2 - image.ImageOffset.X,
         pastePos := Point((image.Width - partial.Width) div 2 - image.ImageOffset.X,
@@ -753,7 +829,7 @@ begin
         layeraction.GetOrCreateSelectionLayer.PutImage(pastePos.x,pastePos.y,partial,dmFastBlend);
         layeraction.GetOrCreateSelectionLayer.PutImage(pastePos.x,pastePos.y,partial,dmFastBlend);
         ComputeSelectionMask(layeraction.GetOrCreateSelectionLayer,layeraction.currentSelection,
         ComputeSelectionMask(layeraction.GetOrCreateSelectionLayer,layeraction.currentSelection,
           rect(pastePos.x,pastePos.y,pastePos.x+partial.Width,pastePos.y+partial.Height));
           rect(pastePos.x,pastePos.y,pastePos.x+partial.Width,pastePos.y+partial.Height));
-        Image.SelectionMayChange(rect(pastePos.x,pastePos.y,pastePos.x+partial.Width,pastePos.y+partial.Height));
+        Image.SelectionMaskMayChange(rect(pastePos.x,pastePos.y,pastePos.x+partial.Width,pastePos.y+partial.Height));
         layeraction.Validate;
         layeraction.Validate;
         layeraction.Free;
         layeraction.Free;
         ChooseTool(ptMoveSelection);
         ChooseTool(ptMoveSelection);
@@ -792,10 +868,10 @@ var LayerAction : TLayerAction;
 begin
 begin
   try
   try
     if not ToolManager.IsSelectingTool then ChooseTool(ptSelectRect);
     if not ToolManager.IsSelectingTool then ChooseTool(ptSelectRect);
-    LayerAction := TLayerAction.Create(Image);
+    LayerAction := Image.CreateAction;
     LayerAction.QuerySelection;
     LayerAction.QuerySelection;
     LayerAction.currentSelection.Fill(BGRAWhite);
     LayerAction.currentSelection.Fill(BGRAWhite);
-    Image.SelectionMayChangeCompletely;
+    Image.SelectionMaskMayChangeCompletely;
     LayerAction.Validate;
     LayerAction.Validate;
     LayerAction.Free;
     LayerAction.Free;
   except
   except
@@ -810,24 +886,24 @@ var LayerAction: TLayerAction;
 begin
 begin
   if not image.CheckNoAction then exit;
   if not image.CheckNoAction then exit;
   try
   try
-    LayerAction := TLayerAction.Create(Image);
-    LayerAction.AllChangesNotified := true;
+    LayerAction := Image.CreateAction;
+    LayerAction.ChangeBoundsNotified := true;
 
 
-    if image.SelectionEmpty then
+    if image.SelectionMaskEmpty then
     begin
     begin
       bounds := rect(0,0,Image.width,image.height);
       bounds := rect(0,0,Image.width,image.height);
       LayerAction.QuerySelection;
       LayerAction.QuerySelection;
       LayerAction.currentSelection.Fill(BGRAWhite);
       LayerAction.currentSelection.Fill(BGRAWhite);
       LayerAction.NotifyChange(LayerAction.currentSelection, bounds);
       LayerAction.NotifyChange(LayerAction.currentSelection, bounds);
-      Image.SelectionMayChange(bounds);
+      Image.SelectionMaskMayChange(bounds);
     end else
     end else
     begin
     begin
       bounds := image.SelectionLayerBounds;
       bounds := image.SelectionLayerBounds;
-      Image.SelectionMayChange(bounds);
+      Image.SelectionMaskMayChange(bounds);
       LayerAction.ApplySelectionMask;
       LayerAction.ApplySelectionMask;
       LayerAction.NotifyChange(LayerAction.GetSelectionLayerIfExists, bounds);
       LayerAction.NotifyChange(LayerAction.GetSelectionLayerIfExists, bounds);
-      bounds := image.SelectionBounds;
-      Image.SelectionMayChange(bounds);
+      bounds := image.SelectionMaskBounds;
+      Image.SelectionMaskMayChange(bounds);
     end;
     end;
 
 
     if LayerAction.RetrieveSelectionIfLayerEmpty(True) then
     if LayerAction.RetrieveSelectionIfLayerEmpty(True) then
@@ -840,10 +916,10 @@ begin
     LayerAction.NotifyChange(LayerAction.GetOrCreateSelectionLayer, bounds);
     LayerAction.NotifyChange(LayerAction.GetOrCreateSelectionLayer, bounds);
     LayerAction.Validate;
     LayerAction.Validate;
     LayerAction.Free;
     LayerAction.Free;
-    if image.SelectionEmpty then
+    if image.SelectionMaskEmpty then
     begin
     begin
       if (CurrentTool = ptRotateSelection) or
       if (CurrentTool = ptRotateSelection) or
-         (CurrentTool  = ptMoveSelection) then
+         (CurrentTool = ptMoveSelection) then
         ChooseTool(ptHand);
         ChooseTool(ptHand);
     end else
     end else
       if not ToolManager.IsSelectingTool then ChooseTool(ptMoveSelection);
       if not ToolManager.IsSelectingTool then ChooseTool(ptMoveSelection);
@@ -854,29 +930,52 @@ begin
 end;
 end;
 
 
 procedure TImageActions.NewLayer;
 procedure TImageActions.NewLayer;
-var top: TTopMostInfo;
-    res: integer;
+{var top: TTopMostInfo;
+    res: integer;}
 begin
 begin
-  if not image.SelectionLayerIsEmpty then
+  {if not image.SelectionLayerIsEmpty then
   begin
   begin
     top := FInstance.HideTopmost;
     top := FInstance.HideTopmost;
     res := MessageDlg(rsTransferSelectionToOtherLayer,mtConfirmation,[mbOk,mbCancel],0);
     res := MessageDlg(rsTransferSelectionToOtherLayer,mtConfirmation,[mbOk,mbCancel],0);
     FInstance.ShowTopmost(top);
     FInstance.ShowTopmost(top);
     if res <> mrOk then exit;
     if res <> mrOk then exit;
-  end;
+  end;}
   if image.NbLayers < MaxLayersToAdd then
   if image.NbLayers < MaxLayersToAdd then
   begin
   begin
     Image.AddNewLayer;
     Image.AddNewLayer;
-    FInstance.ScrollLayerStackOnItem(Image.currentImageLayerIndex);
+    FInstance.ScrollLayerStackOnItem(Image.CurrentLayerIndex);
+  end;
+end;
+
+function TImageActions.NewLayer(ALayer: TBGRABitmap; AName: string;
+  ABlendOp: TBlendOperation): boolean;
+begin
+  if image.NbLayers < MaxLayersToAdd then
+  begin
+    Image.AddNewLayer(ALayer, AName, ABlendOp);
+    FInstance.ScrollLayerStackOnItem(Image.CurrentLayerIndex);
+    result := true;
+  end else
+  begin
+    FInstance.ShowMessage(rsLayers, rsTooManyLayers);
+    ALayer.Free;
+    result := false;
   end;
   end;
 end;
 end;
 
 
-procedure TImageActions.NewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation);
+function TImageActions.NewLayer(ALayer: TBGRALayerCustomOriginal;
+  AName: string; ABlendOp: TBlendOperation): boolean;
 begin
 begin
   if image.NbLayers < MaxLayersToAdd then
   if image.NbLayers < MaxLayersToAdd then
   begin
   begin
     Image.AddNewLayer(ALayer, AName, ABlendOp);
     Image.AddNewLayer(ALayer, AName, ABlendOp);
-    FInstance.ScrollLayerStackOnItem(Image.currentImageLayerIndex);
+    FInstance.ScrollLayerStackOnItem(Image.CurrentLayerIndex);
+    result := true;
+  end else
+  begin
+    FInstance.ShowMessage(rsLayers, rsTooManyLayers);
+    ALayer.Free;
+    result := false;
   end;
   end;
 end;
 end;
 
 
@@ -885,25 +984,25 @@ begin
   if image.NbLayers < MaxLayersToAdd then
   if image.NbLayers < MaxLayersToAdd then
   begin
   begin
     Image.DuplicateLayer;
     Image.DuplicateLayer;
-    FInstance.ScrollLayerStackOnItem(Image.currentImageLayerIndex);
+    FInstance.ScrollLayerStackOnItem(Image.CurrentLayerIndex);
   end;
   end;
 end;
 end;
 
 
 procedure TImageActions.MergeLayerOver;
 procedure TImageActions.MergeLayerOver;
 begin
 begin
-  if (Image.currentImageLayerIndex <> -1) and (image.NbLayers > 1) then
+  if (Image.CurrentLayerIndex <> -1) and (image.NbLayers > 1) then
   begin
   begin
     Image.MergeLayerOver;
     Image.MergeLayerOver;
-    FInstance.ScrollLayerStackOnItem(Image.currentImageLayerIndex);
+    FInstance.ScrollLayerStackOnItem(Image.CurrentLayerIndex);
   end;
   end;
 end;
 end;
 
 
 procedure TImageActions.RemoveLayer;
 procedure TImageActions.RemoveLayer;
 var idx: integer;
 var idx: integer;
 begin
 begin
-  if (Image.currentImageLayerIndex <> -1) and (Image.NbLayers > 1) then
+  if (Image.CurrentLayerIndex <> -1) and (Image.NbLayers > 1) then
   begin
   begin
-    idx := Image.currentImageLayerIndex;
+    idx := Image.CurrentLayerIndex;
     Image.RemoveLayer;
     Image.RemoveLayer;
     FInstance.ScrollLayerStackOnItem(idx);
     FInstance.ScrollLayerStackOnItem(idx);
   end;
   end;
@@ -915,7 +1014,7 @@ var lSelection,lTemp: TBGRABitmap;
 begin
 begin
   if not image.CheckNoAction then exit;
   if not image.CheckNoAction then exit;
   try
   try
-    LayerAction := TLayerAction.Create(Image);
+    LayerAction := Image.CreateAction;
     try
     try
       LayerAction.QuerySelection;
       LayerAction.QuerySelection;
       lSelection:= LayerAction.currentSelection.Duplicate as TBGRABitmap;
       lSelection:= LayerAction.currentSelection.Duplicate as TBGRABitmap;
@@ -937,7 +1036,7 @@ begin
       LayerAction.Validate;
       LayerAction.Validate;
     finally
     finally
       LayerAction.Free;
       LayerAction.Free;
-      Image.SelectionMayChangeCompletely;
+      Image.SelectionMaskMayChangeCompletely;
     end;
     end;
   except on ex:Exception do FInstance.ShowError('EditSelection',ex.Message);
   except on ex:Exception do FInstance.ShowError('EditSelection',ex.Message);
   end;
   end;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 584 - 93
lazpaint/uimagediff.pas


+ 148 - 133
lazpaint/uimagepreview.pas

@@ -24,6 +24,7 @@ type
 
 
     FFilename: string;
     FFilename: string;
     FLoadError: string;
     FLoadError: string;
+    FInUpdatePreview: boolean;
 
 
     FImageFormat: TBGRAImageFormat;
     FImageFormat: TBGRAImageFormat;
     FImageNbLayers: integer;
     FImageNbLayers: integer;
@@ -77,6 +78,7 @@ type
     procedure ClearThumbnails;
     procedure ClearThumbnails;
     procedure DoValidate;
     procedure DoValidate;
     procedure SetLoopCount;
     procedure SetLoopCount;
+    procedure FinishUpdatePreview;
   public
   public
     LazPaintInstance: TLazPaintCustomInstance;
     LazPaintInstance: TLazPaintCustomInstance;
     constructor Create(ASurface: TBGRAVirtualScreen; AStatus: TLabel; AAnimate: boolean);
     constructor Create(ASurface: TBGRAVirtualScreen; AStatus: TLabel; AAnimate: boolean);
@@ -100,6 +102,7 @@ uses FPimage, BGRAReadJpeg, BGRAOpenRaster, BGRAPaintNet, BGRAReadLzp, Dialogs,
 
 
 function TImagePreview.GetPreviewDataLoss: boolean;
 function TImagePreview.GetPreviewDataLoss: boolean;
 begin
 begin
+  FinishUpdatePreview;
   result := (FImageFormat in[ifJpeg,     {compression loss}
   result := (FImageFormat in[ifJpeg,     {compression loss}
                              ifLazPaint, {layer loss}
                              ifLazPaint, {layer loss}
                              ifOpenRaster,
                              ifOpenRaster,
@@ -741,6 +744,148 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TImagePreview.FinishUpdatePreview;
+var reader: TFPCustomImageReader;
+  jpegReader: TBGRAReaderJpeg;
+  source: TStream;
+begin
+  if FInUpdatePreview then
+  begin
+    source := nil;
+    try
+      source := FileManager.CreateFileStream(FFilename, fmOpenRead or fmShareDenyWrite);
+      FImageFormat := DetectFileFormat(source,ExtractFileExt(FFilename));
+      case FImageFormat of
+      ifGif:
+        begin
+          try
+            FAnimatedGif := TBGRAAnimatedGif.Create;
+            FAnimatedGif.LoadFromStream(source);
+            FImageNbLayers := 1;
+          except
+            on ex: Exception do
+            begin
+              FLoadError := ex.Message;
+              FreeAndNil(FAnimatedGif);
+            end;
+          end;
+        end;
+      ifTiff:
+        begin
+          try
+            FTiff := TTiff.Create;
+            if FTiff.LoadFromStream(source) <> teNone then
+              raise exception.Create(rsCannotOpenFile);
+
+            FImageNbLayers := 1;
+            if FTiff.Count = 0 then
+            begin
+              FreeAndNil(FTiff);
+              FLoadError := rsFileCannotBeEmpty;
+            end;
+          except
+            on ex: Exception do
+            begin
+              FLoadError := ex.Message;
+              FreeAndNil(FTiff);
+            end;
+          end;
+        end;
+      ifIco,ifCur:
+        begin
+          FIconCursor := TBGRAIconCursor.Create;
+          try
+            FIconCursor.LoadFromStream(source);
+            FImageNbLayers := 1;
+          except
+            on ex: Exception do
+            begin
+              FLoadError:= ex.Message;
+              FreeAndNil(FIconCursor);
+            end;
+          end;
+        end;
+      ifJpeg:
+        begin
+          jpegReader := TBGRAReaderJpeg.Create;
+          jpegReader.Performance := jpBestSpeed;
+          jpegReader.MinWidth := Screen.Width;
+          jpegReader.MinHeight := Screen.Height;
+          try
+            FSingleImage := TBGRABitmap.Create;
+            FSingleImage.LoadFromStream(source,jpegReader);
+            FImageNbLayers := 1;
+          except
+            on ex: Exception do
+            begin
+              FLoadError:= ex.Message;
+              FreeAndNil(FSingleImage);
+            end;
+          end;
+          jpegReader.Free;
+        end;
+      else
+        begin
+          reader := CreateBGRAImageReader(FImageFormat);
+          try
+            FSingleImage := TBGRABitmap.Create;
+            FSingleImage.LoadFromStream(source,reader);
+            if reader is TFPReaderOpenRaster then FImageNbLayers := TFPReaderOpenRaster(reader).NbLayers else
+            if reader is TFPReaderPaintDotNet then FImageNbLayers := TFPReaderPaintDotNet(reader).NbLayers else
+            if reader is TBGRAReaderLazPaint then FImageNbLayers := TBGRAReaderLazPaint(reader).NbLayers else
+            if reader is TBGRAReaderOXO then FImageNbLayers := TBGRAReaderOXO(reader).NbLayers else
+              FImageNbLayers := 1;
+          except
+            on ex: Exception do
+            begin
+              FLoadError:= ex.Message;
+              FreeAndNil(FSingleImage);
+            end;
+          end;
+          reader.Free;
+        end;
+      end;
+    except
+      on ex: Exception do
+        FLoadError:= ex.Message;
+    end;
+    source.Free;
+
+    if Assigned(FIconCursor) then
+    begin
+      if FIconCursor.Count > 0 then
+        FStatus.Caption := rsCanvasSize + ': ' + IntToStr(FIconCursor.Width[0])+'x'+IntToStr(FIconCursor.Height[0])+ ', ' +
+                           rsEntries + ': ' + IntToStr(FIconCursor.Count)
+      else
+        FStatus.Caption := rsEntries + ': ' + IntToStr(FIconCursor.Count);
+    end else
+    if Assigned(FAnimatedGif) then
+    begin
+      FStatus.Caption := rsCanvasSize + ': ' + IntToStr(FAnimatedGif.Width)+'x'+IntToStr(FAnimatedGif.Height)+', '+
+                         rsFrames+': '+IntToStr(FAnimatedGif.Count);
+    end else
+    if Assigned(FTiff) then
+    begin
+      with FTiff.GetBiggestImage do
+        FStatus.Caption := rsCanvasSize + ': ' + IntToStr(Width)+'x'+IntToStr(Height)+', '+
+                           rsEntries+': '+IntToStr(FTiff.Count);
+    end else
+    if Assigned(FSingleImage) then
+    begin
+      FStatus.Caption := rsCanvasSize + ': ' + IntToStr(FSingleImage.Width)+'x'+IntToStr(FSingleImage.Height)+', '+
+                         rsLayers+': '+IntToStr(FImageNbLayers);
+    end else
+    if FLoadError <> '' then
+    begin
+      FStatus.Caption := FLoadError;
+    end else
+      FStatus.Caption := '';
+
+    FInUpdatePreview := false;
+    FSurface.RedrawBitmap;
+  end;
+end;
+
 procedure TImagePreview.DeleteEntry(i: integer);
 procedure TImagePreview.DeleteEntry(i: integer);
 var outputStream: TStream;
 var outputStream: TStream;
 begin
 begin
@@ -865,9 +1010,6 @@ begin
 end;
 end;
 
 
 procedure TImagePreview.UpdatePreview;
 procedure TImagePreview.UpdatePreview;
-var reader: TFPCustomImageReader;
-  jpegReader: TBGRAReaderJpeg;
-  source: TStream;
 begin
 begin
   ClearThumbnails;
   ClearThumbnails;
   FreeAndNil(FSingleImage);
   FreeAndNil(FSingleImage);
@@ -883,139 +1025,11 @@ begin
   FSurface.RedrawBitmap;
   FSurface.RedrawBitmap;
   FStatus.Caption := rsLoading+'...';
   FStatus.Caption := rsLoading+'...';
   FStatus.Update;
   FStatus.Update;
+  FInUpdatePreview := true;
   {$IFDEF LINUX}
   {$IFDEF LINUX}
   Application.ProcessMessages;
   Application.ProcessMessages;
   {$ENDIF}
   {$ENDIF}
-  source := nil;
-  try
-    source := FileManager.CreateFileStream(FFilename, fmOpenRead or fmShareDenyWrite);
-    FImageFormat := DetectFileFormat(source,ExtractFileExt(FFilename));
-    case FImageFormat of
-    ifGif:
-      begin
-        try
-          FAnimatedGif := TBGRAAnimatedGif.Create;
-          FAnimatedGif.LoadFromStream(source);
-          FImageNbLayers := 1;
-        except
-          on ex: Exception do
-          begin
-            FLoadError := ex.Message;
-            FreeAndNil(FAnimatedGif);
-          end;
-        end;
-      end;
-    ifTiff:
-      begin
-        try
-          FTiff := TTiff.Create;
-          if FTiff.LoadFromStream(source) <> teNone then
-            raise exception.Create(rsCannotOpenFile);
-
-          FImageNbLayers := 1;
-          if FTiff.Count = 0 then
-          begin
-            FreeAndNil(FTiff);
-            FLoadError := rsFileCannotBeEmpty;
-          end;
-        except
-          on ex: Exception do
-          begin
-            FLoadError := ex.Message;
-            FreeAndNil(FTiff);
-          end;
-        end;
-      end;
-    ifIco,ifCur:
-      begin
-        FIconCursor := TBGRAIconCursor.Create;
-        try
-          FIconCursor.LoadFromStream(source);
-          FImageNbLayers := 1;
-        except
-          on ex: Exception do
-          begin
-            FLoadError:= ex.Message;
-            FreeAndNil(FIconCursor);
-          end;
-        end;
-      end;
-    ifJpeg:
-      begin
-        jpegReader := TBGRAReaderJpeg.Create;
-        jpegReader.Performance := jpBestSpeed;
-        jpegReader.MinWidth := Screen.Width;
-        jpegReader.MinHeight := Screen.Height;
-        try
-          FSingleImage := TBGRABitmap.Create;
-          FSingleImage.LoadFromStream(source,jpegReader);
-          FImageNbLayers := 1;
-        except
-          on ex: Exception do
-          begin
-            FLoadError:= ex.Message;
-            FreeAndNil(FSingleImage);
-          end;
-        end;
-        jpegReader.Free;
-      end;
-    else
-      begin
-        reader := CreateBGRAImageReader(FImageFormat);
-        try
-          FSingleImage := TBGRABitmap.Create;
-          FSingleImage.LoadFromStream(source,reader);
-          if reader is TFPReaderOpenRaster then FImageNbLayers := TFPReaderOpenRaster(reader).NbLayers else
-          if reader is TFPReaderPaintDotNet then FImageNbLayers := TFPReaderPaintDotNet(reader).NbLayers else
-          if reader is TBGRAReaderLazPaint then FImageNbLayers := TBGRAReaderLazPaint(reader).NbLayers else
-          if reader is TBGRAReaderOXO then FImageNbLayers := TBGRAReaderOXO(reader).NbLayers else
-            FImageNbLayers := 1;
-        except
-          on ex: Exception do
-          begin
-            FLoadError:= ex.Message;
-            FreeAndNil(FSingleImage);
-          end;
-        end;
-        reader.Free;
-      end;
-    end;
-  except
-    on ex: Exception do
-      FLoadError:= ex.Message;
-  end;
-  source.Free;
-
-  if Assigned(FIconCursor) then
-  begin
-    if FIconCursor.Count > 0 then
-      FStatus.Caption := rsCanvasSize + ': ' + IntToStr(FIconCursor.Width[0])+'x'+IntToStr(FIconCursor.Height[0])+ ', ' +
-                         rsEntries + ': ' + IntToStr(FIconCursor.Count)
-    else
-      FStatus.Caption := rsEntries + ': ' + IntToStr(FIconCursor.Count);
-  end else
-  if Assigned(FAnimatedGif) then
-  begin
-    FStatus.Caption := rsCanvasSize + ': ' + IntToStr(FAnimatedGif.Width)+'x'+IntToStr(FAnimatedGif.Height)+', '+
-                       rsFrames+': '+IntToStr(FAnimatedGif.Count);
-  end else
-  if Assigned(FTiff) then
-  begin
-    with FTiff.GetBiggestImage do
-      FStatus.Caption := rsCanvasSize + ': ' + IntToStr(Width)+'x'+IntToStr(Height)+', '+
-                         rsEntries+': '+IntToStr(FTiff.Count);
-  end else
-  if Assigned(FSingleImage) then
-  begin
-    FStatus.Caption := rsCanvasSize + ': ' + IntToStr(FSingleImage.Width)+'x'+IntToStr(FSingleImage.Height)+', '+
-                       rsLayers+': '+IntToStr(FImageNbLayers);
-  end else
-  if FLoadError <> '' then
-  begin
-    FStatus.Caption := FLoadError;
-  end else
-    FStatus.Caption := '';
-  FSurface.RedrawBitmap;
+  FinishUpdatePreview;
 end;
 end;
 
 
 procedure TImagePreview.HandleTimer;
 procedure TImagePreview.HandleTimer;
@@ -1027,6 +1041,7 @@ end;
 function TImagePreview.GetPreviewBitmap: TImageEntry;
 function TImagePreview.GetPreviewBitmap: TImageEntry;
 var tx,ty,bpp: integer; back: TBGRAPixel;
 var tx,ty,bpp: integer; back: TBGRAPixel;
 begin
 begin
+  FinishUpdatePreview;
   result := TImageEntry.Empty;
   result := TImageEntry.Empty;
 
 
   if Assigned(FIconCursor) then
   if Assigned(FIconCursor) then

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 565 - 142
lazpaint/uimagestate.pas


+ 5 - 3
lazpaint/uimageview.pas

@@ -55,7 +55,7 @@ type
     procedure OnZoomChanged({%H-}sender: TZoom; {%H-}ANewZoom: single; AWorkArea: TRect);
     procedure OnZoomChanged({%H-}sender: TZoom; {%H-}ANewZoom: single; AWorkArea: TRect);
     procedure UpdateCursor(X,Y: integer; ACanvasOfs: TPoint; AWorkArea: TRect; AControl: TControl;
     procedure UpdateCursor(X,Y: integer; ACanvasOfs: TPoint; AWorkArea: TRect; AControl: TControl;
                           AWinControlOfs: TPoint; AWinControl: TWinControl);
                           AWinControlOfs: TPoint; AWinControl: TWinControl);
-    procedure UpdatePicture(ACanvasOfs: TPoint; AWorkArea: TRect; AControl: TControl);
+    procedure UpdatePicture(ACanvasOfs: TPoint; AWorkArea: TRect; {%H-}AControl: TControl);
     function BitmapToForm(pt: TPointF): TPointF;
     function BitmapToForm(pt: TPointF): TPointF;
     function BitmapToForm(X, Y: Single): TPointF;
     function BitmapToForm(X, Y: Single): TPointF;
     function BitmapToVirtualScreen(ptF: TPointF): TPointF;
     function BitmapToVirtualScreen(ptF: TPointF): TPointF;
@@ -71,7 +71,7 @@ type
 
 
 implementation
 implementation
 
 
-uses BGRATransform, LCLIntf, Types, ugraph, math, UTool;
+uses BGRATransform, LCLIntf, Types, ugraph, math, UTool, BGRAThumbnail;
 
 
 function TImageView.GetFillSelectionHighlight: boolean;
 function TImageView.GetFillSelectionHighlight: boolean;
 begin
 begin
@@ -80,7 +80,9 @@ end;
 
 
 procedure TImageView.SetFillSelectionHighlight(AValue: boolean);
 procedure TImageView.SetFillSelectionHighlight(AValue: boolean);
 begin
 begin
+  if AValue = FSelectionHighlight.FillSelection then exit;
   FSelectionHighlight.FillSelection := AValue;
   FSelectionHighlight.FillSelection := AValue;
+  Image.ImageMayChangeCompletely;
 end;
 end;
 
 
 function TImageView.GetImage: TLazPaintImage;
 function TImageView.GetImage: TLazPaintImage;
@@ -151,7 +153,7 @@ begin
   OffsetRect(renderRect, -FLastPictureParameters.virtualScreenArea.Left,
   OffsetRect(renderRect, -FLastPictureParameters.virtualScreenArea.Left,
                          -FLastPictureParameters.virtualScreenArea.Top);
                          -FLastPictureParameters.virtualScreenArea.Top);
 
 
-  Image.DrawBackground(FVirtualScreen, renderRect);
+  DrawThumbnailCheckers(FVirtualScreen,renderRect,Image.IsIconCursor);
 
 
   //draw image (with merged selection)
   //draw image (with merged selection)
   FVirtualScreen.StretchPutImage(renderRect,Image.RenderedImage,dmDrawWithTransparency);
   FVirtualScreen.StretchPutImage(renderRect,Image.RenderedImage,dmDrawWithTransparency);

+ 399 - 183
lazpaint/ulayeraction.pas

@@ -5,18 +5,27 @@ unit ULayerAction;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, UImage, BGRABitmap, BGRABitmapTypes, UImageType;
+  Classes, SysUtils, BGRABitmap, BGRABitmapTypes, UImageType,
+  UStateType, UImageState;
 
 
 type
 type
+  TNotifyChangeEvent = procedure(ASender: TObject; ALayer: TBGRABitmap; ARect: TRect) of object;
+  TNotifyUndoEvent = procedure(ASender: TObject; AUndo: TCustomImageDifference; var Owned: boolean) of object;
+
   { TLayerAction }
   { TLayerAction }
 
 
   TLayerAction = class(TCustomLayerAction)
   TLayerAction = class(TCustomLayerAction)
   private
   private
-    FAllChangesNotified: boolean;
-    FImage: TLazPaintImage;
+    FChangeBoundsNotified: boolean;
+    FImageState: TImageState;
+    FOnDestroy: TNotifyEvent;
+    FOnNotifyChange: TNotifyChangeEvent;
+    FOnNotifyUndo: TNotifyUndoEvent;
+    FPrediff: TComposedImageDifference;
     FBackupSelectedLayer, FBackupSelectionLayer, FBackupSelection: TBGRABitmap;
     FBackupSelectedLayer, FBackupSelectionLayer, FBackupSelection: TBGRABitmap;
-    FBackupSelectedLayerDefined, FBackupSelectionLayerDefined, FBackupSelectionDefined: boolean;
-    FSelectedLayerChangedArea, FSelectionLayerChangedArea, FSelectionChangedArea: TRect;
+    FBackupSelectedLayerDefined, FBackupSelectionLayerDefined, FBackupSelectionMaskDefined: boolean;
+    FBackupSelectionTransform: TAffineMatrix;
+    FSelectedImageLayerChangedArea, FSelectionLayerChangedArea, FSelectionMaskChangedArea: TRect;
     FDone: boolean;
     FDone: boolean;
     FOnTryStop: TOnTryStopEventHandler;
     FOnTryStop: TOnTryStopEventHandler;
     function GetBackupDrawingLayer: TBGRABitmap;
     function GetBackupDrawingLayer: TBGRABitmap;
@@ -24,18 +33,23 @@ type
     function GetBackupSelection: TBGRABitmap;
     function GetBackupSelection: TBGRABitmap;
     function GetBackupSelectionLayer: TBGRABitmap;
     function GetBackupSelectionLayer: TBGRABitmap;
     function GetCurrentSelection: TBGRABitmap;
     function GetCurrentSelection: TBGRABitmap;
+    function GetCurrentState: TImageState;
     function GetSelectedImageLayer: TBGRABitmap;
     function GetSelectedImageLayer: TBGRABitmap;
     function GetDrawingLayer: TBGRABitmap;
     function GetDrawingLayer: TBGRABitmap;
     function GetSelectedImageLayerOffset: TPoint;
     function GetSelectedImageLayerOffset: TPoint;
+    function GetSelectionLayerBounds: TRect;
+    function GetSelectionTransform: TAffineMatrix;
+    procedure SetOnDestroy(AValue: TNotifyEvent);
+    procedure SetOnNotifyChange(AValue: TNotifyChangeEvent);
+    procedure SetOnNotifyUndo(AValue: TNotifyUndoEvent);
   protected
   protected
     procedure Cancel;
     procedure Cancel;
-    procedure NeedSelectionBackup;
+    procedure NeedSelectionMaskBackup;
     procedure NeedSelectedLayerBackup;
     procedure NeedSelectedLayerBackup;
     procedure NeedSelectionLayerBackup;
     procedure NeedSelectionLayerBackup;
-    function GetSelectionTransform: TAffineMatrix;
-    procedure SetSelectionTransform(AValue: TAffineMatrix);
+    property CurrentState: TImageState read GetCurrentState;
   public
   public
-    constructor Create(AImage: TLazPaintImage);
+    constructor Create(AState: TImageState; AApplyOfsBefore: boolean = false);
     procedure Validate;
     procedure Validate;
     procedure PartialValidate(ADiscardBackup: boolean = false);
     procedure PartialValidate(ADiscardBackup: boolean = false);
     procedure PartialCancel;
     procedure PartialCancel;
@@ -44,18 +58,17 @@ type
     procedure QuerySelection;
     procedure QuerySelection;
     procedure RemoveSelection;
     procedure RemoveSelection;
     procedure EraseSelectionInBitmap;
     procedure EraseSelectionInBitmap;
+    procedure MergeWithSelection(AApplyMask: boolean = true);
     procedure ReleaseSelection;
     procedure ReleaseSelection;
     procedure RetrieveSelection;
     procedure RetrieveSelection;
     function RetrieveSelectionIfLayerEmpty(removeFromBitmap: boolean = false): boolean;
     function RetrieveSelectionIfLayerEmpty(removeFromBitmap: boolean = false): boolean;
     procedure ApplySelectionTransform(ApplyToMask: boolean= true);
     procedure ApplySelectionTransform(ApplyToMask: boolean= true);
     procedure ApplySelectionMask;
     procedure ApplySelectionMask;
 
 
-    procedure ReplaceSelectedLayer(AValue: TBGRABitmap; AOwned: boolean);
     procedure ReplaceSelectionLayer(bmp: TBGRABitmap; AOwned: boolean);
     procedure ReplaceSelectionLayer(bmp: TBGRABitmap; AOwned: boolean);
-    procedure ReplaceDrawingLayer(bmp: TBGRABitmap; AOwned: boolean);
     procedure ReplaceCurrentSelection(AValue: TBGRABitmap);
     procedure ReplaceCurrentSelection(AValue: TBGRABitmap);
     procedure NotifyChange(ADest: TBGRABitmap; ARect: TRect);
     procedure NotifyChange(ADest: TBGRABitmap; ARect: TRect);
-    procedure RestoreSelection;
+    procedure RestoreSelectionMask;
     procedure RestoreDrawingLayer;
     procedure RestoreDrawingLayer;
     procedure RestoreSelectedLayer;
     procedure RestoreSelectedLayer;
     procedure RestoreSelectionLayer;
     procedure RestoreSelectionLayer;
@@ -73,27 +86,38 @@ type
     property BackupSelectedLayer: TBGRABitmap read GetBackupSelectedLayer;
     property BackupSelectedLayer: TBGRABitmap read GetBackupSelectedLayer;
     property BackupDrawingLayer: TBGRABitmap read GetBackupDrawingLayer;
     property BackupDrawingLayer: TBGRABitmap read GetBackupDrawingLayer;
     property OnTryStop: TOnTryStopEventHandler read FOnTryStop write FOnTryStop;
     property OnTryStop: TOnTryStopEventHandler read FOnTryStop write FOnTryStop;
-    property SelectionTransform: TAffineMatrix read GetSelectionTransform write SetSelectionTransform;
     property Done: boolean read FDone;
     property Done: boolean read FDone;
-    property AllChangesNotified: boolean read FAllChangesNotified write FAllChangesNotified;
+    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;
   end;
   end;
 
 
 implementation
 implementation
 
 
-uses UResourceStrings, UGraph, Types, Dialogs;
+uses UResourceStrings, UGraph, Types, Dialogs, BGRATransform, BGRALayerOriginal, UImageDiff;
 
 
 { TLayerAction }
 { TLayerAction }
 
 
 function TLayerAction.GetSelectedImageLayer: TBGRABitmap;
 function TLayerAction.GetSelectedImageLayer: TBGRABitmap;
 begin
 begin
+  result := CurrentState.SelectedImageLayer;
+  if not Assigned(result) then
+    raise exception.Create('No image layer selected');
   NeedSelectedLayerBackup;
   NeedSelectedLayerBackup;
-  result := FImage.GetSelectedImageLayer;
 end;
 end;
 
 
 function TLayerAction.GetCurrentSelection: TBGRABitmap;
 function TLayerAction.GetCurrentSelection: TBGRABitmap;
 begin
 begin
-  NeedSelectionBackup;
-  result := fImage.currentSelection;
+  NeedSelectionMaskBackup;
+  result := CurrentState.SelectionMask;
+end;
+
+function TLayerAction.GetCurrentState: TImageState;
+begin
+  result := FImageState;
 end;
 end;
 
 
 function TLayerAction.GetBackupSelectedLayer: TBGRABitmap;
 function TLayerAction.GetBackupSelectedLayer: TBGRABitmap;
@@ -104,13 +128,13 @@ end;
 
 
 function TLayerAction.GetBackupDrawingLayer: TBGRABitmap;
 function TLayerAction.GetBackupDrawingLayer: TBGRABitmap;
 begin
 begin
-  if FImage.SelectionEmpty then result := BackupSelectedLayer else
+  if CurrentState.SelectionMaskEmpty then result := BackupSelectedLayer else
     result := BackupSelectionLayer;
     result := BackupSelectionLayer;
 end;
 end;
 
 
 function TLayerAction.GetBackupSelection: TBGRABitmap;
 function TLayerAction.GetBackupSelection: TBGRABitmap;
 begin
 begin
-  NeedSelectionBackup;
+  NeedSelectionMaskBackup;
   result := FBackupSelection;
   result := FBackupSelection;
 end;
 end;
 
 
@@ -122,23 +146,41 @@ end;
 
 
 function TLayerAction.GetDrawingLayer: TBGRABitmap;
 function TLayerAction.GetDrawingLayer: TBGRABitmap;
 begin
 begin
-  if FImage.SelectionEmpty then result := GetSelectedImageLayer else
+  if CurrentState.SelectionMaskEmpty then result := GetSelectedImageLayer else
     result := GetOrCreateSelectionLayer;
     result := GetOrCreateSelectionLayer;
 end;
 end;
 
 
 function TLayerAction.GetSelectedImageLayerOffset: TPoint;
 function TLayerAction.GetSelectedImageLayerOffset: TPoint;
 begin
 begin
-  result := FImage.LayerOffset[FImage.currentImageLayerIndex];
+  result := CurrentState.LayerOffset[CurrentState.SelectedImageLayerIndex];
+end;
+
+function TLayerAction.GetSelectionLayerBounds: TRect;
+begin
+  result := CurrentState.GetSelectionLayerBounds;
 end;
 end;
 
 
 function TLayerAction.GetSelectionTransform: TAffineMatrix;
 function TLayerAction.GetSelectionTransform: TAffineMatrix;
 begin
 begin
-  result := FImage.SelectionTransform;
+  result:= CurrentState.SelectionTransform;
 end;
 end;
 
 
-procedure TLayerAction.SetSelectionTransform(AValue: TAffineMatrix);
+procedure TLayerAction.SetOnDestroy(AValue: TNotifyEvent);
 begin
 begin
-  FImage.SelectionTransform := AValue;
+  if FOnDestroy=AValue then Exit;
+  FOnDestroy:=AValue;
+end;
+
+procedure TLayerAction.SetOnNotifyChange(AValue: TNotifyChangeEvent);
+begin
+  if FOnNotifyChange=AValue then Exit;
+  FOnNotifyChange:=AValue;
+end;
+
+procedure TLayerAction.SetOnNotifyUndo(AValue: TNotifyUndoEvent);
+begin
+  if FOnNotifyUndo=AValue then Exit;
+  FOnNotifyUndo:=AValue;
 end;
 end;
 
 
 procedure TLayerAction.Cancel;
 procedure TLayerAction.Cancel;
@@ -146,16 +188,22 @@ begin
   if FDone then raise Exception.Create('Already done');
   if FDone then raise Exception.Create('Already done');
   RestoreSelectedLayer;
   RestoreSelectedLayer;
   RestoreSelectionLayer;
   RestoreSelectionLayer;
-  RestoreSelection;
+  RestoreSelectionMask;
+  CurrentState.SelectionTransform := FBackupSelectionTransform;
+  if Assigned(FPrediff) then
+  begin
+    FPrediff.UnapplyTo(CurrentState);
+    FreeAndNil(FPrediff);
+  end;
   FDone := true;
   FDone := true;
 end;
 end;
 
 
-procedure TLayerAction.NeedSelectionBackup;
+procedure TLayerAction.NeedSelectionMaskBackup;
 begin
 begin
-  if not FBackupSelectionDefined then
+  if not FBackupSelectionMaskDefined then
   begin
   begin
-    FBackupSelection := DuplicateBitmap(FImage.currentSelection);
-    FBackupSelectionDefined := true;
+    FBackupSelection := DuplicateBitmap(CurrentState.SelectionMask);
+    FBackupSelectionMaskDefined := true;
   end;
   end;
 end;
 end;
 
 
@@ -163,7 +211,7 @@ procedure TLayerAction.NeedSelectedLayerBackup;
 begin
 begin
   if not FBackupSelectedLayerDefined then
   if not FBackupSelectedLayerDefined then
   begin
   begin
-    FBackupSelectedLayer := DuplicateBitmap(FImage.GetSelectedImageLayer);
+    FBackupSelectedLayer := DuplicateBitmap(CurrentState.SelectedImageLayer);
     FBackupSelectedLayerDefined := true;
     FBackupSelectedLayerDefined := true;
   end;
   end;
 end;
 end;
@@ -172,30 +220,40 @@ procedure TLayerAction.NeedSelectionLayerBackup;
 begin
 begin
   if not FBackupSelectionLayerDefined then
   if not FBackupSelectionLayerDefined then
   begin
   begin
-    FBackupSelectionLayer := DuplicateBitmap(FImage.GetSelectionLayerIfExists);
+    FBackupSelectionLayer := DuplicateBitmap(CurrentState.SelectionLayer);
     FBackupSelectionLayerDefined := true;
     FBackupSelectionLayerDefined := true;
   end;
   end;
 end;
 end;
 
 
-constructor TLayerAction.Create(AImage: TLazPaintImage);
+constructor TLayerAction.Create(AState: TImageState; AApplyOfsBefore: boolean);
+var
+  layerOfsDiff: TCustomImageDifference;
 begin
 begin
-  if AImage <> nil then
-  begin
-    if not AImage.CheckNoAction(True) then
-      raise exception.Create(rsConflictingActions);
-  end;
-  FImage := AImage;
-  FImage.ActionInProgress := self;
+  FImageState := AState;
   FBackupSelectedLayer := nil;
   FBackupSelectedLayer := nil;
   FBackupSelection := nil;
   FBackupSelection := nil;
   FBackupSelectionLayer := nil;
   FBackupSelectionLayer := nil;
   FBackupSelectedLayerDefined := false;
   FBackupSelectedLayerDefined := false;
-  FBackupSelectionDefined := false;
+  FBackupSelectionMaskDefined := false;
   FBackupSelectionLayerDefined := false;
   FBackupSelectionLayerDefined := false;
-  FSelectedLayerChangedArea := EmptyRect;
+  FBackupSelectionTransform := CurrentState.SelectionTransform;
+  FSelectedImageLayerChangedArea := EmptyRect;
   FSelectionLayerChangedArea := EmptyRect;
   FSelectionLayerChangedArea := EmptyRect;
-  FSelectionChangedArea := EmptyRect;
+  FSelectionMaskChangedArea := EmptyRect;
   FDone := false;
   FDone := false;
+  FPrediff := TComposedImageDifference.Create;
+  if AApplyOfsBefore then
+  begin
+    with CurrentState.LayerOffset[CurrentState.SelectedImageLayerIndex] do
+      layerOfsDiff := CurrentState.ComputeLayerOffsetDifference(X,Y);
+    if layerOfsDiff.IsIdentity then FreeAndNil(layerOfsDiff)
+    else
+    begin
+      layerOfsDiff.ApplyTo(CurrentState);
+      FPrediff.Add(layerOfsDiff);
+    end;
+  end;
+  if FPrediff.Count = 0 then FreeAndNil(FPrediff);
 end;
 end;
 
 
 destructor TLayerAction.Destroy;
 destructor TLayerAction.Destroy;
@@ -204,67 +262,66 @@ begin
   FBackupSelectedLayer.Free;
   FBackupSelectedLayer.Free;
   FBackupSelection.Free;
   FBackupSelection.Free;
   FBackupSelectionLayer.Free;
   FBackupSelectionLayer.Free;
-  if FImage <> nil then
-  begin
-    if FImage.ActionInProgress = self then
-      FImage.ActionInProgress := nil;
-  end;
+  if Assigned(FOnDestroy) then FOnDestroy(self);
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
-procedure TLayerAction.ReplaceDrawingLayer(bmp: TBGRABitmap; AOwned: boolean);
-begin
-  FImage.ReplaceDrawingLayer(bmp,AOwned);
-end;
-
 procedure TLayerAction.ReplaceCurrentSelection(AValue: TBGRABitmap);
 procedure TLayerAction.ReplaceCurrentSelection(AValue: TBGRABitmap);
 begin
 begin
-  NeedSelectionBackup;
-  if (AValue.Width = FImage.CurrentSelection.Width) and
-    (AValue.Height = FImage.CurrentSelection.Height) then
-    NotifyChange(FImage.CurrentSelection, AValue.GetDifferenceBounds(FImage.CurrentSelection))
+  if AValue = CurrentState.SelectionMask then exit;
+  NeedSelectionMaskBackup;
+  if Assigned(AValue) and Assigned(CurrentState.SelectionMask) and
+    (AValue.Width = CurrentState.SelectionMask.Width) and
+    (AValue.Height = CurrentState.SelectionMask.Height) then
+    NotifyChange(CurrentState.SelectionMask, AValue.GetDifferenceBounds(CurrentState.SelectionMask))
   else
   else
   begin
   begin
-    NotifyChange(FImage.CurrentSelection, rect(0,0,FImage.CurrentSelection.Width,FImage.CurrentSelection.Height));
-    NotifyChange(AValue, rect(0,0,AValue.Width,AValue.Height));
+    if Assigned(CurrentState.SelectionMask) then
+      NotifyChange(CurrentState.SelectionMask, rect(0,0,CurrentState.SelectionMask.Width,CurrentState.SelectionMask.Height));
+    if Assigned(AValue) then
+      NotifyChange(AValue, rect(0,0,AValue.Width,AValue.Height));
   end;
   end;
-  FImage.ReplaceCurrentSelection(AValue);
+  CurrentState.SelectionMask.Free;
+  CurrentState.SelectionMask := AValue
 end;
 end;
 
 
 procedure TLayerAction.NotifyChange(ADest: TBGRABitmap; ARect: TRect);
 procedure TLayerAction.NotifyChange(ADest: TBGRABitmap; ARect: TRect);
 begin
 begin
   if ADest = nil then exit;
   if ADest = nil then exit;
-  if not IntersectRect(ARect, ARect, rect(0,0,FImage.Width,FImage.Height)) then exit;
-  if ADest = FImage.CurrentSelection then
-    FSelectionChangedArea := RectUnion(FSelectionChangedArea, ARect)
-  else if ADest = FImage.GetSelectedImageLayer then
-    FSelectedLayerChangedArea := RectUnion(FSelectedLayerChangedArea, ARect)
-  else if ADest = FImage.GetSelectionLayerIfExists then
+  if not IntersectRect(ARect, ARect, rect(0,0,CurrentState.Width,CurrentState.Height)) then exit;
+  if ADest = CurrentState.SelectionMask then
+    FSelectionMaskChangedArea := RectUnion(FSelectionMaskChangedArea, ARect)
+  else if ADest = CurrentState.SelectedImageLayer then
+    FSelectedImageLayerChangedArea := RectUnion(FSelectedImageLayerChangedArea, ARect)
+  else if ADest = CurrentState.SelectionLayer then
     FSelectionLayerChangedArea := RectUnion(FSelectionLayerChangedArea, ARect);
     FSelectionLayerChangedArea := RectUnion(FSelectionLayerChangedArea, ARect);
+  if Assigned(FOnNotifyChange) then
+    FOnNotifyChange(self, ADest, ARect);
 end;
 end;
 
 
-procedure TLayerAction.RestoreSelection;
+procedure TLayerAction.RestoreSelectionMask;
 var prevClip: TRect;
 var prevClip: TRect;
 begin
 begin
-  if FBackupSelectionDefined then
+  if FBackupSelectionMaskDefined then
   begin
   begin
-    if not AllChangesNotified then FSelectionChangedArea := rect(0,0,FImage.Width,FImage.Height);
-    if IsRectEmpty(FSelectionChangedArea) then exit;
-    prevClip := FImage.CurrentSelection.ClipRect;
-    FImage.CurrentSelection.ClipRect := FSelectionChangedArea;
+    if not ChangeBoundsNotified then FSelectionMaskChangedArea := rect(0,0,CurrentState.Width,CurrentState.Height);
+    if IsRectEmpty(FSelectionMaskChangedArea) then exit;
+    prevClip := CurrentState.SelectionMask.ClipRect;
+    CurrentState.SelectionMask.ClipRect := FSelectionMaskChangedArea;
     if Assigned(FBackupSelection) then
     if Assigned(FBackupSelection) then
-      FImage.CurrentSelection.PutImage(0,0,FBackupSelection,dmSet)
+      CurrentState.SelectionMask.PutImage(0,0,FBackupSelection,dmSet)
     else
     else
-      FImage.CurrentSelection.FillRect(0,0,FImage.Width,FImage.Height,BGRABlack,dmSet);
-    FImage.CurrentSelection.ClipRect := prevClip;
-    FImage.SelectionMayChange(FSelectionChangedArea);
-    FSelectionChangedArea := EmptyRect;
+      CurrentState.SelectionMask.FillRect(0,0,CurrentState.Width,CurrentState.Height,BGRABlack,dmSet);
+    CurrentState.SelectionMask.ClipRect := prevClip;
+    If Assigned(FOnNotifyChange) then
+      FOnNotifyChange(self, CurrentState.SelectionMask, FSelectionMaskChangedArea);
+    FSelectionMaskChangedArea := EmptyRect;
   end;
   end;
 end;
 end;
 
 
 procedure TLayerAction.RestoreDrawingLayer;
 procedure TLayerAction.RestoreDrawingLayer;
 begin
 begin
-  if FImage.SelectionEmpty then RestoreSelectedLayer
+  if CurrentState.SelectionMaskEmpty then RestoreSelectedLayer
     else RestoreSelectionLayer;
     else RestoreSelectionLayer;
 end;
 end;
 
 
@@ -273,35 +330,37 @@ var prevClip: TRect;
 begin
 begin
   if FBackupSelectedLayerDefined then
   if FBackupSelectedLayerDefined then
   begin
   begin
-    if not AllChangesNotified then FSelectedLayerChangedArea := rect(0,0,FImage.Width,FImage.Height);
-    if IsRectEmpty(FSelectedLayerChangedArea) then exit;
-    prevClip := FImage.GetSelectedImageLayer.ClipRect;
-    FImage.GetSelectedImageLayer.ClipRect := FSelectedLayerChangedArea;
+    if not ChangeBoundsNotified then FSelectedImageLayerChangedArea := rect(0,0,CurrentState.Width,CurrentState.Height);
+    if IsRectEmpty(FSelectedImageLayerChangedArea) then exit;
+    prevClip := CurrentState.SelectedImageLayer.ClipRect;
+    CurrentState.SelectedImageLayer.ClipRect := FSelectedImageLayerChangedArea;
     if Assigned(FBackupSelectedLayer) then
     if Assigned(FBackupSelectedLayer) then
-      FImage.GetSelectedImageLayer.PutImage(0,0,FBackupSelectedLayer,dmSet)
+      CurrentState.SelectedImageLayer.PutImage(0,0,FBackupSelectedLayer,dmSet)
     else
     else
-      FImage.GetSelectedImageLayer.FillRect(0,0,FImage.Width,FImage.Height,BGRAPixelTransparent,dmSet);
-    FImage.GetSelectedImageLayer.ClipRect := prevClip;
-    FImage.LayerMayChange(FImage.GetSelectedImageLayer,FSelectedLayerChangedArea);
-    FSelectedLayerChangedArea := EmptyRect;
+      CurrentState.SelectedImageLayer.FillRect(0,0,CurrentState.Width,CurrentState.Height,BGRAPixelTransparent,dmSet);
+    CurrentState.SelectedImageLayer.ClipRect := prevClip;
+    If Assigned(FOnNotifyChange) then
+      FOnNotifyChange(self, CurrentState.SelectedImageLayer, FSelectedImageLayerChangedArea);
+    FSelectedImageLayerChangedArea := EmptyRect;
   end;
   end;
 end;
 end;
 
 
 procedure TLayerAction.RestoreSelectionLayer;
 procedure TLayerAction.RestoreSelectionLayer;
 var prevClip: TRect;
 var prevClip: TRect;
 begin
 begin
-  if FBackupSelectionLayerDefined and (GetSelectionLayerIfExists <> nil) then
+  if FBackupSelectionLayerDefined and (CurrentState.SelectionLayer <> nil) then
   begin
   begin
-    if not AllChangesNotified then FSelectionLayerChangedArea := rect(0,0,FImage.Width,FImage.Height);
+    if not ChangeBoundsNotified then FSelectionLayerChangedArea := rect(0,0,CurrentState.Width,CurrentState.Height);
     if IsRectEmpty(FSelectionLayerChangedArea) then exit;
     if IsRectEmpty(FSelectionLayerChangedArea) then exit;
-    prevClip := FImage.GetSelectionLayerIfExists.ClipRect;
-    FImage.GetSelectionLayerIfExists.ClipRect := FSelectionLayerChangedArea;
+    prevClip := CurrentState.SelectionLayer.ClipRect;
+    CurrentState.SelectionLayer.ClipRect := FSelectionLayerChangedArea;
     if Assigned(FBackupSelectionLayer) then
     if Assigned(FBackupSelectionLayer) then
-      FImage.GetSelectionLayerIfExists.PutImage(0,0,FBackupSelectionLayer,dmSet)
+      CurrentState.SelectionLayer.PutImage(0,0,FBackupSelectionLayer,dmSet)
     else
     else
-      FImage.GetSelectionLayerIfExists.FillRect(0,0,FImage.Width,FImage.Height,BGRAPixelTransparent,dmSet);
-    FImage.GetSelectionLayerIfExists.ClipRect := prevClip;
-    FImage.LayerMayChange(FImage.GetSelectionLayerIfExists,FSelectionLayerChangedArea);
+      CurrentState.SelectionLayer.FillRect(0,0,CurrentState.Width,CurrentState.Height,BGRAPixelTransparent,dmSet);
+    CurrentState.SelectionLayer.ClipRect := prevClip;
+    If Assigned(FOnNotifyChange) then
+      FOnNotifyChange(self, CurrentState.SelectionLayer, FSelectionLayerChangedArea);
     FSelectionLayerChangedArea := EmptyRect;
     FSelectionLayerChangedArea := EmptyRect;
   end;
   end;
 end;
 end;
@@ -314,76 +373,131 @@ end;
 
 
 procedure TLayerAction.QuerySelection;
 procedure TLayerAction.QuerySelection;
 begin
 begin
-  NeedSelectionBackup;
-  FImage.QuerySelection;
+  NeedSelectionMaskBackup;
+  CurrentState.QuerySelectionMask;
 end;
 end;
 
 
 procedure TLayerAction.RemoveSelection;
 procedure TLayerAction.RemoveSelection;
+var bounds: TRect;
 begin
 begin
-  if not FImage.SelectionEmpty or (FImage.GetSelectionLayerIfExists <> nil) then
+  if not CurrentState.SelectionMaskEmpty or (CurrentState.SelectionLayer <> nil) then
   begin
   begin
-    NeedSelectionBackup;
+    NeedSelectionMaskBackup;
     NeedSelectionLayerBackup;
     NeedSelectionLayerBackup;
-    FImage.RemoveSelection;
+    bounds := CurrentState.GetTransformedSelectionMaskBounds;
+    NotifyChange(CurrentState.SelectionLayer, bounds);
+    CurrentState.RemoveSelection;
   end;
   end;
 end;
 end;
 
 
 procedure TLayerAction.EraseSelectionInBitmap;
 procedure TLayerAction.EraseSelectionInBitmap;
+var offs: TPoint;
+  r: TRect;
 begin
 begin
-  if not FImage.SelectionEmpty then
+  if not CurrentState.SelectionMaskEmpty then
   begin
   begin
     NeedSelectedLayerBackup;
     NeedSelectedLayerBackup;
-    FImage.EraseSelectionInBitmap;
+    offs := CurrentState.LayerOffset[CurrentState.SelectedImageLayerIndex];
+    r := CurrentState.GetSelectionMaskBounds;
+    SubstractMask(GetSelectedImageLayer,-offs.X+r.left,-offs.Y+r.top,CurrentState.SelectionMask,r);
+    OffsetRect(r,-offs.x,-offs.y);
+    NotifyChange(GetSelectedImageLayer,r);
+  end;
+end;
+
+procedure TLayerAction.MergeWithSelection(AApplyMask: boolean);
+var offs: TPoint;
+  sourceRect,destRect: TRect;
+begin
+  if not CurrentState.SelectionLayerEmpty and not (AApplyMask and CurrentState.SelectionMaskEmpty) then
+  begin
+    sourceRect := CurrentState.GetSelectionLayerBounds;
+    if AApplyMask then
+    begin
+      CurrentState.SelectionLayer.ApplyMask(CurrentState.SelectionMask,CurrentState.GetSelectionLayerBounds);
+      IntersectRect(sourceRect,sourceRect,CurrentState.GetSelectionMaskBounds);
+      NotifyChange(CurrentState.SelectionLayer,CurrentState.GetSelectionLayerBounds);
+    end;
+    offs := CurrentState.LayerOffset[CurrentState.SelectedImageLayerIndex];
+    destRect := sourceRect;
+    OffsetRect(destRect, -offs.x,-offs.y);
+    GetSelectedImageLayer.PutImagePart(destRect.left,destRect.top,CurrentState.SelectionLayer,sourceRect,dmDrawWithTransparency);
+    NotifyChange(GetSelectedImageLayer,destRect);
+    CurrentState.ReplaceSelectionLayer(nil,true);
   end;
   end;
 end;
 end;
 
 
 procedure TLayerAction.ReleaseSelection;
 procedure TLayerAction.ReleaseSelection;
 var bounds: TRect;
 var bounds: TRect;
 begin
 begin
-  if not FImage.SelectionEmpty then
+  if not CurrentState.SelectionMaskEmpty then
   begin
   begin
-    bounds := FImage.SelectionBounds;
-    NeedSelectionBackup;
-    NotifyChange(FImage.CurrentSelection, bounds);
-    if FImage.GetSelectionLayerIfExists <> nil then
+    bounds := CurrentState.GetSelectionMaskBounds;
+    NeedSelectionMaskBackup;
+    NotifyChange(CurrentState.SelectionMask, bounds);
+    if CurrentState.SelectionLayer <> nil then
     begin
     begin
       NeedSelectedLayerBackup;
       NeedSelectedLayerBackup;
-      NotifyChange(FImage.GetSelectedImageLayer, bounds);
+      NotifyChange(CurrentState.SelectedImageLayer, bounds);
       NeedSelectionLayerBackup;
       NeedSelectionLayerBackup;
-      NotifyChange(FImage.GetSelectionLayerIfExists, bounds);
+      NotifyChange(CurrentState.SelectionLayer, bounds);
     end;
     end;
-    FImage.ReleaseSelection;
+
+    ApplySelectionMask;
+    CurrentState.SelectionMask.Free;
+    CurrentState.SelectionMask := nil;
+    ApplySelectionTransform(False);
+    MergeWithSelection(False);
   end;
   end;
 end;
 end;
 
 
 procedure TLayerAction.RetrieveSelection;
 procedure TLayerAction.RetrieveSelection;
+var temp : TBGRABitmap;
+  offs: TPoint;
+  r, maskBounds: TRect;
 begin
 begin
-  if not FImage.SelectionEmpty then
+  if not CurrentState.SelectionMaskEmpty then
   begin
   begin
     NeedSelectedLayerBackup;
     NeedSelectedLayerBackup;
     NeedSelectionLayerBackup;
     NeedSelectionLayerBackup;
-    FImage.RetrieveSelection;
+    ApplySelectionTransform;
+    MergeWithSelection;
+    offs := CurrentState.LayerOffset[CurrentState.SelectedImageLayerIndex];
+    maskBounds := CurrentState.GetSelectionMaskBounds;
+    r := maskBounds;
+    OffsetRect(r, -offs.x, -offs.y);
+    IntersectRect(r, r, rect(0,0,GetSelectedImageLayer.Width,GetSelectedImageLayer.Height));
+    temp := TBGRABitmap.Create(CurrentState.Width,CurrentState.Height);
+    temp.PutImagePart(r.left+offs.x,r.top+offs.y,GetSelectedImageLayer,r,dmSet);
+    temp.ApplyMask(CurrentState.SelectionMask,maskBounds);
+    BGRAReplace(CurrentState.SelectionLayer,temp);
+    NotifyChange(CurrentState.SelectionLayer,maskBounds);
   end;
   end;
 end;
 end;
 
 
-function TLayerAction.RetrieveSelectionIfLayerEmpty(removeFromBitmap: boolean
-  ): boolean;
+function TLayerAction.RetrieveSelectionIfLayerEmpty(removeFromBitmap: boolean): boolean;
 begin
 begin
   NeedSelectedLayerBackup;
   NeedSelectedLayerBackup;
   NeedSelectionLayerBackup;
   NeedSelectionLayerBackup;
-  result := FImage.RetrieveSelectionIfLayerEmpty(removeFromBitmap);
+  if CurrentState.SelectionLayerEmpty then
+  begin
+    RetrieveSelection;
+    if removeFromBitmap then EraseSelectionInBitmap;
+    result := true;
+  end
+  else result := false;
 end;
 end;
 
 
 function TLayerAction.GetOrCreateSelectionLayer: TBGRABitmap;
 function TLayerAction.GetOrCreateSelectionLayer: TBGRABitmap;
 begin
 begin
   NeedSelectionLayerBackup;
   NeedSelectionLayerBackup;
-  result := FImage.GetOrCreateSelectionLayer;
+  result := CurrentState.GetOrCreateSelectionLayer;
 end;
 end;
 
 
 function TLayerAction.GetSelectionLayerIfExists: TBGRABitmap;
 function TLayerAction.GetSelectionLayerIfExists: TBGRABitmap;
 begin
 begin
   NeedSelectionLayerBackup;
   NeedSelectionLayerBackup;
-  result := FImage.GetSelectionLayerIfExists;
+  result := CurrentState.SelectionLayer;
 end;
 end;
 
 
 procedure TLayerAction.ReplaceSelectionLayer(bmp: TBGRABitmap; AOwned: boolean);
 procedure TLayerAction.ReplaceSelectionLayer(bmp: TBGRABitmap; AOwned: boolean);
@@ -396,38 +510,61 @@ begin
     NotifyChange(dest, bmp.GetDifferenceBounds(dest))
     NotifyChange(dest, bmp.GetDifferenceBounds(dest))
   else
   else
   begin
   begin
-    if dest <> nil then NotifyChange(dest, rect(0,0,dest.Width,dest.Height));
-    if bmp <> nil then NotifyChange(bmp, rect(0,0,bmp.Width,bmp.Height));
+    if dest <> nil then NotifyChange(dest, dest.GetImageBounds);
+    if bmp <> nil then NotifyChange(bmp, bmp.GetImageBounds);
   end;
   end;
-  FImage.ReplaceSelectionLayer(bmp,AOwned);
+  CurrentState.ReplaceSelectionLayer(bmp,AOwned);
 end;
 end;
 
 
 procedure TLayerAction.ApplySelectionTransform(ApplyToMask: boolean);
 procedure TLayerAction.ApplySelectionTransform(ApplyToMask: boolean);
+var
+  newBmp: TBGRABitmap;
+  newLeft, newTop: integer;
+  r: TRect;
 begin
 begin
-  NeedSelectionLayerBackup;
-  FImage.ApplySelectionTransform(ApplyToMask);
+  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;
 end;
 
 
 procedure TLayerAction.ApplySelectionMask;
 procedure TLayerAction.ApplySelectionMask;
+var r: TRect;
 begin
 begin
   NeedSelectionLayerBackup;
   NeedSelectionLayerBackup;
-  FImage.ApplySelectionMask;
-end;
-
-procedure TLayerAction.ReplaceSelectedLayer(AValue: TBGRABitmap; AOwned: boolean);
-var dest: TBGRABitmap;
-begin
-  NeedSelectedLayerBackup;
-  dest := GetSelectedImageLayer;
-  if (dest <> nil) and (AValue.Width = dest.Width) and
-    (AValue.Height = dest.Height) then
-    NotifyChange(dest, AValue.GetDifferenceBounds(dest))
-  else
+  if (CurrentState.SelectionMask <> nil) and (CurrentState.SelectionLayer <> nil) then
   begin
   begin
-    if dest <> nil then NotifyChange(dest, rect(0,0,dest.Width,dest.Height));
-    NotifyChange(AValue, rect(0,0,AValue.Width,AValue.Height));
+    r := GetSelectionLayerBounds;
+    if not IsRectEmpty(r) then
+    begin
+      GetOrCreateSelectionLayer.ApplyMask(CurrentState.SelectionMask,r);
+      NotifyChange(CurrentState.GetOrCreateSelectionLayer,r);
+    end;
   end;
   end;
-  FImage.ReplaceSelectedLayer(AValue, AOwned);
 end;
 end;
 
 
 procedure TLayerAction.Validate;
 procedure TLayerAction.Validate;
@@ -441,41 +578,63 @@ procedure TLayerAction.PartialCancel;
 begin
 begin
   RestoreSelectedLayer;
   RestoreSelectedLayer;
   RestoreSelectionLayer;
   RestoreSelectionLayer;
-  RestoreSelection;
+  RestoreSelectionMask;
 end;
 end;
 
 
 procedure TLayerAction.PartialValidate(ADiscardBackup: boolean = false);
 procedure TLayerAction.PartialValidate(ADiscardBackup: boolean = false);
-begin
-  if FBackupSelectedLayerDefined or FBackupSelectionDefined or FBackupSelectionLayerDefined then
+var
+  prevLayerOriginalMatrix: TAffineMatrix;
+  prevLayerOriginaData: TStream;
+  imgDiff: TImageLayerStateDifference;
+  composedDiff: TComposedImageDifference;
+  ofs: TPoint;
+  applyOfs: TCustomImageDifference;
+  appendOfs, owned: boolean;
+begin
+  if FBackupSelectedLayerDefined or FBackupSelectionMaskDefined or FBackupSelectionLayerDefined then
   begin
   begin
-    if AllChangesNotified then
-      if IsRectEmpty(FSelectedLayerChangedArea) and IsRectEmpty(FSelectionChangedArea) and
+    if ChangeBoundsNotified then
+      if IsRectEmpty(FSelectedImageLayerChangedArea) and IsRectEmpty(FSelectionMaskChangedArea) and
          IsRectEmpty(FSelectionLayerChangedArea) then exit;
          IsRectEmpty(FSelectionLayerChangedArea) then exit;
     if FBackupSelectionLayerDefined then
     if FBackupSelectionLayerDefined then
     begin
     begin
-      FImage.DiscardSelectionLayerBounds;
-      if FImage.SelectionLayerIsEmpty then
-        FImage.ReplaceSelectionLayer(nil,True);
+      CurrentState.DiscardSelectionLayerBounds;
+      if CurrentState.SelectionLayerEmpty then
+        CurrentState.ReplaceSelectionLayer(nil,True);
     end;
     end;
-    if FBackupSelectionDefined then
+    if FBackupSelectionMaskDefined then
     begin
     begin
-      FImage.DiscardSelectionBounds;
-      if FImage.SelectionEmpty then
-      begin
-        FImage.ReplaceSelectionLayer(nil,true);
-        FImage.ReplaceCurrentSelection(nil);
-      end;
+      CurrentState.DiscardSelectionMaskBounds;
+      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;
     end;
-    if AllChangesNotified then
-      FImage.AddLayerUndo(FBackupSelectedLayer, FSelectedLayerChangedArea,
-        FBackupSelection, FSelectionChangedArea,
-        FBackupSelectionLayer, FSelectionLayerChangedArea)
-      else
-      FImage.AddLayerUndo(FBackupSelectedLayer, FBackupSelectedLayerDefined,
-        FBackupSelection, FBackupSelectionDefined,
-        FBackupSelectionLayer, FBackupSelectionLayerDefined);
 
 
-    FImage.OnImageChanged.NotifyObservers;
+    if ChangeBoundsNotified then
+      imgDiff := CurrentState.ComputeLayerDifference(FBackupSelectedLayer, FSelectedImageLayerChangedArea,
+        FBackupSelection, FSelectionMaskChangedArea,
+        FBackupSelectionLayer, FSelectionLayerChangedArea,
+        FBackupSelectionTransform,
+        prevLayerOriginaData, prevLayerOriginalMatrix) as TImageLayerStateDifference
+    else
+      imgDiff := CurrentState.ComputeLayerDifference(FBackupSelectedLayer, FBackupSelectedLayerDefined,
+        FBackupSelection, FBackupSelectionMaskDefined,
+        FBackupSelectionLayer, FBackupSelectionLayerDefined,
+        FBackupSelectionTransform,
+        prevLayerOriginaData, prevLayerOriginalMatrix) as TImageLayerStateDifference;
+    if imgDiff.IsIdentity then FreeAndNil(imgDiff);
 
 
     if ADiscardBackup then
     if ADiscardBackup then
     begin
     begin
@@ -484,55 +643,112 @@ begin
       FreeAndNil(FBackupSelection);
       FreeAndNil(FBackupSelection);
       FBackupSelectedLayerDefined := false;
       FBackupSelectedLayerDefined := false;
       FBackupSelectedLayerDefined := false;
       FBackupSelectedLayerDefined := false;
-      FBackupSelectionDefined := false;
+      FBackupSelectionMaskDefined := false;
+
+      appendOfs:= Assigned(imgDiff) and imgDiff.ChangeImageLayer;
     end else
     end else
     begin
     begin
       if FBackupSelectionLayerDefined then
       if FBackupSelectionLayerDefined then
       begin
       begin
-        if (FBackupSelectionLayer = nil) and (FImage.GetSelectionLayerIfExists <> nil) then
+        if (FBackupSelectionLayer = nil) and (CurrentState.SelectionLayer <> nil) then
         begin
         begin
-          if not FImage.SelectionLayerIsEmpty then
-            FBackupSelectionLayer := Fimage.GetSelectionLayerIfExists.Duplicate as TBGRABitmap;
+          if not CurrentState.SelectionLayerEmpty then
+            FBackupSelectionLayer := CurrentState.SelectionLayer.Duplicate as TBGRABitmap;
         end else
         end else
         if Assigned(FBackupSelectionLayer) then
         if Assigned(FBackupSelectionLayer) then
         begin
         begin
-          if AllChangesNotified then FBackupSelectionLayer.ClipRect := FSelectionLayerChangedArea;
-          if Assigned(FImage.GetSelectionLayerIfExists) then
-            FBackupSelectionLayer.PutImage(0,0,FImage.GetSelectionLayerIfExists,dmSet)
+          if ChangeBoundsNotified then FBackupSelectionLayer.ClipRect := FSelectionLayerChangedArea;
+          if Assigned(CurrentState.SelectionLayer) then
+            FBackupSelectionLayer.PutImage(0,0,CurrentState.SelectionLayer,dmSet)
           else
           else
-            FBackupSelectionLayer.FillRect(0,0,FImage.Width,FImage.Height,BGRAPixelTransparent,dmSet);
+            FBackupSelectionLayer.FillRect(0,0,CurrentState.Width,CurrentState.Height,BGRAPixelTransparent,dmSet);
           FBackupSelectionLayer.NoClip;
           FBackupSelectionLayer.NoClip;
         end;
         end;
         FSelectionLayerChangedArea := EmptyRect;
         FSelectionLayerChangedArea := EmptyRect;
       end;
       end;
       if FBackupSelectedLayerDefined then
       if FBackupSelectedLayerDefined then
       begin
       begin
-        if AllChangesNotified then FBackupSelectedLayer.ClipRect := FSelectedLayerChangedArea;
-        if Assigned(FImage.GetSelectedImageLayer) then
-          FBackupSelectedLayer.PutImage(0,0,FImage.GetSelectedImageLayer,dmSet)
+        if ChangeBoundsNotified then FBackupSelectedLayer.ClipRect := FSelectedImageLayerChangedArea;
+        if Assigned(CurrentState.SelectedImageLayer) then
+          FBackupSelectedLayer.PutImage(0,0,CurrentState.SelectedImageLayer,dmSet)
         else
         else
-          FBackupSelectedLayer.FillRect(0,0,FImage.Width,FImage.Height,BGRAPixelTransparent,dmSet);
+          FBackupSelectedLayer.FillRect(0,0,CurrentState.Width,CurrentState.Height,BGRAPixelTransparent,dmSet);
         FBackupSelectedLayer.NoClip;
         FBackupSelectedLayer.NoClip;
-        FSelectedLayerChangedArea := EmptyRect;
+        FSelectedImageLayerChangedArea := EmptyRect;
       end;
       end;
-      if FBackupSelectionDefined then
+      if FBackupSelectionMaskDefined then
       begin
       begin
-        if (FBackupSelection = nil) and (FImage.CurrentSelection <> nil) then
+        if (FBackupSelection = nil) and (CurrentState.SelectionMask <> nil) then
         begin
         begin
-          if not FImage.SelectionEmpty then
-            FBackupSelection := Fimage.CurrentSelection.Duplicate as TBGRABitmap;
+          if not CurrentState.SelectionMaskEmpty then
+            FBackupSelection := CurrentState.SelectionMask.Duplicate as TBGRABitmap;
         end else
         end else
         if (FBackupSelection <> nil) then
         if (FBackupSelection <> nil) then
         begin
         begin
-          if AllChangesNotified then FBackupSelection.ClipRect := FSelectionChangedArea;
-          if Assigned(FImage.CurrentSelection) then
-            FBackupSelection.PutImage(0,0,FImage.CurrentSelection,dmSet)
+          if ChangeBoundsNotified then FBackupSelection.ClipRect := FSelectionMaskChangedArea;
+          if Assigned(CurrentState.SelectionMask) then
+            FBackupSelection.PutImage(0,0,CurrentState.SelectionMask,dmSet)
           else
           else
-            FBackupSelection.FillRect(0,0,FImage.Width,FImage.Height,BGRABlack,dmSet);
+            FBackupSelection.FillRect(0,0,CurrentState.Width,CurrentState.Height,BGRABlack,dmSet);
           FBackupSelection.NoClip;
           FBackupSelection.NoClip;
         end;
         end;
-        FSelectionChangedArea := EmptyRect;
+        FSelectionMaskChangedArea := EmptyRect;
+      end;
+
+      appendOfs := false;
+    end;
+
+    if assigned(imgDiff) then
+    begin
+      if appendOfs or Assigned(FPrediff) then
+      begin
+        composedDiff := TComposedImageDifference.Create;
+        if Assigned(FPrediff) then
+        begin
+          composedDiff.AddRange(FPrediff);
+          FPrediff := nil;
+        end;
+        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
+      begin
+        if Assigned(FOnNotifyUndo) then
+        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;
       end;
+      FPrediff := nil;
     end;
     end;
   end;
   end;
 end;
 end;

+ 106 - 29
lazpaint/ulayerstack.pas

@@ -96,13 +96,16 @@ type
 
 
 implementation
 implementation
 
 
-uses BGRAFillInfo,uscaledpi,uresourcestrings,ublendop, uimage, utool, BGRAText, BGRAThumbnail;
+uses BGRAFillInfo,uscaledpi,uresourcestrings,ublendop, uimage, utool, BGRAText, BGRAThumbnail,
+   BGRALayerOriginal, math, BGRATransform, BGRASVGOriginal;
 
 
 function TFLayerStack.DrawLayerItem(ABitmap: TBGRABitmap; layerPos: TPoint; layerIndex: integer; ASelected: boolean): TDrawLayerItemResult;
 function TFLayerStack.DrawLayerItem(ABitmap: TBGRABitmap; layerPos: TPoint; layerIndex: integer; ASelected: boolean): TDrawLayerItemResult;
-var LayerBmp: TBGRABitmap;
-    lColor,lColorTransp: TBGRAPixel;
-    barwidth: integer;
-    sourceCoords: Array Of TPointF;
+var
+  lColor,lColorTransp: TBGRAPixel;
+  barwidth: integer;
+  sourceCoords: Array Of TPointF;
+  reduced: TBGRABitmap;
+  reducedBounds: TRect;
 begin
 begin
   if ASelected then
   if ASelected then
     lColor := ColorToBGRA(ColorToRGB(clHighlightText))
     lColor := ColorToBGRA(ColorToRGB(clHighlightText))
@@ -115,6 +118,16 @@ begin
      pointf(layerPos.X+0.9*LayerRectWidth,layerPos.Y+round(LayerRectHeight*0.1)),
      pointf(layerPos.X+0.9*LayerRectWidth,layerPos.Y+round(LayerRectHeight*0.1)),
     pointf(layerPos.X+0.7*LayerRectWidth,layerPos.Y+round(LayerRectHeight*0.9)),
     pointf(layerPos.X+0.7*LayerRectWidth,layerPos.Y+round(LayerRectHeight*0.9)),
     pointf(layerPos.X+0.05*LayerRectWidth,layerPos.Y+round(LayerRectHeight*0.9))]);
     pointf(layerPos.X+0.05*LayerRectWidth,layerPos.Y+round(LayerRectHeight*0.9))]);
+  reduced := TBGRABitmap.Create(round(LayerRectWidth*0.65), round(LayerRectHeight*0.8));
+  reducedBounds := RectWithSize(LazPaintInstance.Image.LayerOffset[layerIndex].X,
+                        LazPaintInstance.Image.LayerOffset[layerIndex].Y,
+                        LazPaintInstance.Image.LayerBitmap[layerIndex].Width,
+                        LazPaintInstance.Image.LayerBitmap[layerIndex].Height);
+  reducedBounds.Left := round(reducedBounds.Left*reduced.Width/LazPaintInstance.Image.Width);
+  reducedBounds.Top := round(reducedBounds.Top*reduced.Height/LazPaintInstance.Image.Height);
+  reducedBounds.Right := round(reducedBounds.Right*reduced.Width/LazPaintInstance.Image.Width);
+  reducedBounds.Bottom := round(reducedBounds.Bottom*reduced.Height/LazPaintInstance.Image.Height);
+  reduced.StretchPutImage(reducedBounds, LazPaintInstance.Image.LayerBitmap[layerIndex], dmDrawWithTransparency);
 
 
   result.PreviewPts[0].y += 0.5;
   result.PreviewPts[0].y += 0.5;
   result.PreviewPts[1].y += 0.5;
   result.PreviewPts[1].y += 0.5;
@@ -125,10 +138,11 @@ begin
   else
   else
     ABitmap.FillPolyAntialias(result.PreviewPts,background);
     ABitmap.FillPolyAntialias(result.PreviewPts,background);
 
 
-  layerBmp := LazPaintInstance.Image.LayerBitmap[layerIndex];
-  sourceCoords := PointsF([pointf(-0.49,-0.49),pointf(layerBmp.Width-0.51,-0.49),
-            pointf(layerBmp.Width-0.51,layerBmp.Height-0.51),pointf(-0.49,layerBmp.Height-0.51)]);
-  ABitmap.FillPolyLinearMapping( result.PreviewPts, layerBmp, sourceCoords, False);
+  sourceCoords := PointsF([pointf(-0.49,-0.49),pointf(reduced.Width-0.51,-0.49),
+            pointf(reduced.Width-0.51,reduced.Height-0.51),pointf(-0.49,reduced.Height-0.51)]);
+  ABitmap.FillPolyLinearMapping(result.PreviewPts, reduced, sourceCoords, False);
+  reduced.Free;
+
   result.PreviewPts[0].y -= 0.5;
   result.PreviewPts[0].y -= 0.5;
   result.PreviewPts[1].y -= 0.5;
   result.PreviewPts[1].y -= 0.5;
   result.PreviewPts[2].y += 0.5;
   result.PreviewPts[2].y += 0.5;
@@ -309,14 +323,14 @@ begin
   if i < LazPaintInstance.Image.NbLayers then
   if i < LazPaintInstance.Image.NbLayers then
   begin
   begin
     if not LazPaintInstance.Image.SelectionLayerIsEmpty and
     if not LazPaintInstance.Image.SelectionLayerIsEmpty and
-        (i <> LazPaintInstance.Image.currentImageLayerIndex) then
+        (i <> LazPaintInstance.Image.CurrentLayerIndex) then
     begin
     begin
       topmostInfo := LazPaintInstance.HideTopmost;
       topmostInfo := LazPaintInstance.HideTopmost;
       res := MessageDlg(rsTransferSelectionToOtherLayer,mtConfirmation,[mbOk,mbCancel],0);
       res := MessageDlg(rsTransferSelectionToOtherLayer,mtConfirmation,[mbOk,mbCancel],0);
       LazPaintInstance.ShowTopmost(topmostInfo);
       LazPaintInstance.ShowTopmost(topmostInfo);
       if res = mrOk then
       if res = mrOk then
       begin
       begin
-        if LazPaintInstance.Image.SetCurrentImageLayerIndex(i) then
+        if LazPaintInstance.Image.SetCurrentLayerByIndex(i) then
         begin
         begin
           renaming := false;
           renaming := false;
           BGRALayerStack.RedrawBitmap;
           BGRALayerStack.RedrawBitmap;
@@ -324,7 +338,7 @@ begin
       end;
       end;
       exit;
       exit;
     end;
     end;
-    if LazPaintInstance.Image.SetCurrentImageLayerIndex(i) then
+    if LazPaintInstance.Image.SetCurrentLayerByIndex(i) then
     begin
     begin
       renaming := false;
       renaming := false;
       movingItemStart := true;
       movingItemStart := true;
@@ -356,7 +370,7 @@ begin
       str := BlendOperationStr[BlendOperation[i]];
       str := BlendOperationStr[BlendOperation[i]];
       if blendOps.IndexOf(str) = -1 then
       if blendOps.IndexOf(str) = -1 then
         blendOps.Add(str);
         blendOps.Add(str);
-      if i = LazPaintInstance.Image.currentImageLayerIndex then
+      if i = LazPaintInstance.Image.CurrentLayerIndex then
         selectedStr := str;
         selectedStr := str;
     end;
     end;
   if selectedStr = BlendOperationStr[boTransparent] then
   if selectedStr = BlendOperationStr[boTransparent] then
@@ -399,10 +413,10 @@ begin
 
 
   InterruptorWidth := LayerRectHeight div 4;
   InterruptorWidth := LayerRectHeight div 4;
   InterruptorHeight := LayerRectHeight div 4;
   InterruptorHeight := LayerRectHeight div 4;
-  temp := ScaleY(20,OriginalDPI);
+  temp := ScaleY(28,OriginalDPI);
   if InterruptorWidth > temp then InterruptorWidth := temp;
   if InterruptorWidth > temp then InterruptorWidth := temp;
   if InterruptorHeight > temp then InterruptorHeight := temp;
   if InterruptorHeight > temp then InterruptorHeight := temp;
-  temp := ScaleY(10,OriginalDPI);
+  temp := ScaleY(7,OriginalDPI);
   if InterruptorHeight < temp then InterruptorHeight := temp;
   if InterruptorHeight < temp then InterruptorHeight := temp;
   if InterruptorWidth < temp then InterruptorWidth := temp;
   if InterruptorWidth < temp then InterruptorWidth := temp;
   StackWidth := InterruptorWidth+LayerRectWidth+ABitmap.TextSize('Some layer name').cx;
   StackWidth := InterruptorWidth+LayerRectWidth+ABitmap.TextSize('Some layer name').cx;
@@ -469,7 +483,7 @@ begin
   if ScrollStackItemIntoView then
   if ScrollStackItemIntoView then
   begin
   begin
     ScrollPos.X := 0;
     ScrollPos.X := 0;
-    ScrollPos.Y := (LazPaintInstance.Image.NbLayers-1-LazPaintInstance.Image.currentImageLayerIndex)*LayerRectHeight;
+    ScrollPos.Y := (LazPaintInstance.Image.NbLayers-1-LazPaintInstance.Image.CurrentLayerIndex)*LayerRectHeight;
     ScrollStackItemIntoView := false;
     ScrollStackItemIntoView := false;
   end;
   end;
 
 
@@ -532,8 +546,56 @@ var i: integer;
   layerPos: TPoint;
   layerPos: TPoint;
   lSelected: boolean;
   lSelected: boolean;
   y: integer;
   y: integer;
-  clipping: TRect;
-  lColor: TBGRAPixel;
+  clipping, rKind: TRect;
+  lColor, lColorTrans: TBGRAPixel;
+
+  procedure DrawKind(AClass: TBGRALayerOriginalAny);
+  var
+    eb: TEasyBezierCurve;
+    w: single;
+    i: integer;
+    m: TAffineMatrix;
+  begin
+    if AClass = TBGRALayerImageOriginal 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);
+    end else
+    if AClass = TBGRALayerSVGOriginal then
+    begin
+      m := AffineMatrixTranslation(rKind.Left,rKind.Top+rKind.Height*0.1)*AffineMatrixScale(rKind.Width,rKind.Height*0.8);
+      w := max(1,rKind.Height/10);
+      eb := EasyBezierCurve([PointF(0.28,0),PointF(0,0),PointF(0,0.5),PointF(0.28,0.5),PointF(1/3,1),PointF(0,1)],False,cmCurve);
+      for i := 0 to eb.PointCount-1 do eb.Point[i] := m*eb.Point[i];
+      Bitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w, true);
+      eb := EasyBezierCurve([PointF(0.33,0),PointF(0.47,1),PointF(0.6,0)],False,cmAngle);
+      for i := 0 to eb.PointCount-1 do eb.Point[i] := m*eb.Point[i];
+      Bitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w, true);
+      eb := EasyBezierCurve([PointF(1,0),PointF(0.7,0),PointF(2/3,1),PointF(1,1),PointF(1,0.5),PointF(5/6,0.5)],False,cmCurve);
+      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);
+    end else
+    begin
+      Bitmap.EllipseAntialias(rKind.Left+rKind.Width / 3, rKind.Top+rKind.Height / 3,rKind.Width / 3,rKind.Height / 3,
+                              lColor, 1, lColorTrans);
+      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);
+    end;
+  end;
+
 begin
 begin
   if Layout then
   if Layout then
   begin
   begin
@@ -551,7 +613,7 @@ begin
     begin
     begin
       with LazPaintInstance.Image do
       with LazPaintInstance.Image do
       begin
       begin
-        if i = currentImageLayerIndex then
+        if i = CurrentLayerIndex then
         begin
         begin
           Bitmap.FillRect(layerPos.X,layerPos.Y,layerPos.X+StackWidth,layerPos.Y+LayerRectHeight,ColorToBGRA(ColorToRGB(clHighlight)),dmSet);
           Bitmap.FillRect(layerPos.X,layerPos.Y,layerPos.X+StackWidth,layerPos.Y+LayerRectHeight,ColorToBGRA(ColorToRGB(clHighlight)),dmSet);
           lSelected:= true;
           lSelected:= true;
@@ -563,8 +625,8 @@ begin
         end;
         end;
         if UpdateItem <> -1 then clipping := rect(layerPos.X,layerPos.Y,layerPos.X+StackWidth,layerPos.Y+LayerRectHeight);
         if UpdateItem <> -1 then clipping := rect(layerPos.X,layerPos.Y,layerPos.X+StackWidth,layerPos.Y+LayerRectHeight);
 
 
-        interruptors[i] := rect(layerPos.X+InterruptorWidth div 5,layerpos.Y+(LayerRectHeight-InterruptorHeight) div 2,layerPos.X+InterruptorWidth,
-           layerpos.Y+(LayerRectHeight-InterruptorHeight) div 2+InterruptorHeight);
+        interruptors[i] := 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
         if (layerpos.Y+LayerRectHeight > 0) and (layerpos.Y < Bitmap.Height) then
         begin
         begin
@@ -573,6 +635,9 @@ begin
           else
           else
             lColor := ColorToBGRA(ColorToRGB(clWindowText));
             lColor := ColorToBGRA(ColorToRGB(clWindowText));
 
 
+          lColorTrans := lColor;
+          lColorTrans.alpha := lColorTrans.alpha div 3;
+
           Bitmap.Rectangle(interruptors[i],lColor,dmDrawWithTransparency);
           Bitmap.Rectangle(interruptors[i],lColor,dmDrawWithTransparency);
           if LayerVisible[i] then
           if LayerVisible[i] then
           with interruptors[i] do
           with interruptors[i] do
@@ -586,6 +651,18 @@ begin
                   PointF(right-2,top-2))]),lColor,1.5);
                   PointF(right-2,top-2))]),lColor,1.5);
           end;
           end;
 
 
+          rKind := interruptors[i];
+          rKind.Offset(0, InterruptorHeight*3 div 2);
+          if LayerOriginalDefined[i] then
+          begin
+            if LayerOriginalKnown[i] then
+              DrawKind(LayerOriginalClass[i])
+            else
+              DrawKind(nil);
+          end
+          else
+            DrawKind(TBGRALayerImageOriginal);
+
           inc(layerPos.X,InterruptorWidth);
           inc(layerPos.X,InterruptorWidth);
           if movingItemStart and (i= movingItemSourceIndex) then
           if movingItemStart and (i= movingItemSourceIndex) then
           begin
           begin
@@ -682,20 +759,20 @@ var blendOp: TBlendOperation;
 begin
 begin
   blendOp := boTransparent;
   blendOp := boTransparent;
   topmostInfo := LazPaintInstance.HideTopmost;
   topmostInfo := LazPaintInstance.HideTopmost;
-  if LazPaintInstance.Image.currentImageLayerIndex > 0 then
-    tempUnder := LazPaintInstance.Image.ComputeFlatImage(0,LazPaintInstance.Image.currentImageLayerIndex-1,False)
+  if LazPaintInstance.Image.CurrentLayerIndex > 0 then
+    tempUnder := LazPaintInstance.Image.ComputeFlatImage(0,LazPaintInstance.Image.CurrentLayerIndex-1,False)
   else
   else
     tempUnder := TBGRABitmap.Create(1,1);
     tempUnder := TBGRABitmap.Create(1,1);
-  if ublendop.ShowBlendOpDialog(LazPaintInstance, blendOp, tempUnder,LazPaintInstance.Image.SelectedImageLayerReadOnly) then
+  if ublendop.ShowBlendOpDialog(LazPaintInstance, blendOp, tempUnder,LazPaintInstance.Image.CurrentLayerReadOnly) then
   begin
   begin
     updatingImageOnly := true;
     updatingImageOnly := true;
-    LazPaintInstance.Image.BlendOperation[LazPaintInstance.Image.currentImageLayerIndex] := blendOp;
+    LazPaintInstance.Image.BlendOperation[LazPaintInstance.Image.CurrentLayerIndex] := blendOp;
     updatingImageOnly := false;
     updatingImageOnly := false;
     UpdateComboBlendOp;
     UpdateComboBlendOp;
   end;
   end;
   tempUnder.Free;
   tempUnder.Free;
   LazPaintInstance.ShowTopmost(topmostInfo);
   LazPaintInstance.ShowTopmost(topmostInfo);
-  if LazPaintInstance.Image.currentImageLayerIndex = 0 then
+  if LazPaintInstance.Image.CurrentLayerIndex = 0 then
     LazPaintInstance.ToolManager.ToolPopup(tpmBlendOpBackground);
     LazPaintInstance.ToolManager.ToolPopup(tpmBlendOpBackground);
 end;
 end;
 
 
@@ -715,8 +792,8 @@ begin
         else
         else
           blendOp := StrToBlendOperation(itemStr);
           blendOp := StrToBlendOperation(itemStr);
         updatingImageOnly := true;
         updatingImageOnly := true;
-        LazPaintInstance.Image.BlendOperation[LazPaintInstance.Image.currentImageLayerIndex] := blendOp;
-        if LazPaintInstance.Image.currentImageLayerIndex = 0 then
+        LazPaintInstance.Image.BlendOperation[LazPaintInstance.Image.CurrentLayerIndex] := blendOp;
+        if LazPaintInstance.Image.CurrentLayerIndex = 0 then
           LazPaintInstance.ToolManager.ToolPopup(tpmBlendOpBackground);
           LazPaintInstance.ToolManager.ToolPopup(tpmBlendOpBackground);
         updatingImageOnly := false;
         updatingImageOnly := false;
       end else
       end else
@@ -770,7 +847,7 @@ begin
       begin
       begin
         if i < LazPaintInstance.Image.NbLayers then
         if i < LazPaintInstance.Image.NbLayers then
         begin
         begin
-          if (i <> LazPaintInstance.image.currentImageLayerIndex) and not renaming then
+          if (i <> LazPaintInstance.image.CurrentLayerIndex) and not renaming then
             HandleSelectLayer(i,x,y)
             HandleSelectLayer(i,x,y)
           else
           else
           begin
           begin

+ 32 - 2
lazpaint/uloadimage.pas

@@ -5,18 +5,20 @@ unit ULoadImage;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, LazPaintType, BGRABitmap;
+  Classes, SysUtils, LazPaintType, BGRABitmap, BGRALayers, BGRASVGOriginal;
 
 
 function LoadFlatImageUTF8(AFilename: string; ASkipDialog: boolean = false): TImageEntry;
 function LoadFlatImageUTF8(AFilename: string; ASkipDialog: boolean = false): TImageEntry;
 procedure FreeMultiImage(var images: ArrayOfImageEntry);
 procedure FreeMultiImage(var images: ArrayOfImageEntry);
 function AbleToLoadUTF8(AFilename: string): boolean;
 function AbleToLoadUTF8(AFilename: string): boolean;
+function LoadSVGImageUTF8(AFilename: string): TBGRALayeredBitmap;
+function LoadSVGOriginalUTF8(AFilename: string): TBGRALayerSVGOriginal;
 
 
 implementation
 implementation
 
 
 uses FileUtil, BGRAAnimatedGif, Graphics, UMultiImage,
 uses FileUtil, BGRAAnimatedGif, Graphics, UMultiImage,
   BGRAReadLzp, LCLProc, BGRABitmapTypes, BGRAReadPng,
   BGRAReadLzp, LCLProc, BGRABitmapTypes, BGRAReadPng,
   UFileSystem, BGRAIconCursor, BGRAReadTiff,
   UFileSystem, BGRAIconCursor, BGRAReadTiff,
-  Dialogs;
+  Dialogs, math;
 
 
 function LoadIcoMultiImageFromStream(AStream: TStream): ArrayOfImageEntry;
 function LoadIcoMultiImageFromStream(AStream: TStream): ArrayOfImageEntry;
 var ico: TBGRAIconCursor; i: integer;
 var ico: TBGRAIconCursor; i: integer;
@@ -126,6 +128,34 @@ begin
   end;
   end;
 end;
 end;
 
 
+function LoadSVGImageUTF8(AFilename: string): TBGRALayeredBitmap;
+var
+  svg: TBGRALayerSVGOriginal;
+begin
+  svg := LoadSVGOriginalUTF8(AFilename);
+  result := TBGRALayeredBitmap.Create(ceil(svg.Width),ceil(svg.Height));
+  result.AddLayerFromOwnedOriginal(svg);
+  result.RenderLayerFromOriginal(0);
+end;
+
+function LoadSVGOriginalUTF8(AFilename: string): TBGRALayerSVGOriginal;
+var
+  svg: TBGRALayerSVGOriginal;
+  s: TStream;
+begin
+  s := FileManager.CreateFileStream(AFilename, fmOpenRead or fmShareDenyWrite);
+  result := nil;
+  try
+    svg := TBGRALayerSVGOriginal.Create;
+    svg.LoadFromStream(s);
+    result:= svg;
+    svg:= nil;
+  finally
+    s.Free;
+    svg.Free;
+  end;
+end;
+
 function LoadFlatImageUTF8(AFilename: string; ASkipDialog: boolean): TImageEntry;
 function LoadFlatImageUTF8(AFilename: string; ASkipDialog: boolean): TImageEntry;
 var
 var
   formMultiImage: TFMultiImage;
   formMultiImage: TFMultiImage;

+ 1 - 1
lazpaint/umenu.pas

@@ -211,7 +211,7 @@ begin
   AddMenus('MenuEdit',   'EditUndo,EditRedo,-,EditCut,EditCopy,EditPaste,EditPasteAsNew,EditPasteAsNewLayer,EditDeleteSelection,-,EditSelectAll,EditInvertSelection,EditSelectionFit,EditDeselect');
   AddMenus('MenuEdit',   'EditUndo,EditRedo,-,EditCut,EditCopy,EditPaste,EditPasteAsNew,EditPasteAsNewLayer,EditDeleteSelection,-,EditSelectAll,EditInvertSelection,EditSelectionFit,EditDeselect');
   AddMenus('MenuSelect', 'EditSelection,FileLoadSelection,FileSaveSelectionAs,-,EditSelectAll,EditInvertSelection,EditSelectionFit,EditDeselect,-,ToolSelectRect,ToolSelectEllipse,ToolSelectPoly,ToolSelectSpline,-,ToolMoveSelection,ToolRotateSelection,-,ToolSelectPen,ToolMagicWand');
   AddMenus('MenuSelect', 'EditSelection,FileLoadSelection,FileSaveSelectionAs,-,EditSelectAll,EditInvertSelection,EditSelectionFit,EditDeselect,-,ToolSelectRect,ToolSelectEllipse,ToolSelectPoly,ToolSelectSpline,-,ToolMoveSelection,ToolRotateSelection,-,ToolSelectPen,ToolMagicWand');
   AddMenus('MenuView',   'ViewZoomOriginal,ViewZoomIn,ViewZoomOut,ViewZoomFit,-,*');
   AddMenus('MenuView',   'ViewZoomOriginal,ViewZoomIn,ViewZoomOut,ViewZoomFit,-,*');
-  AddMenus('MenuImage',  'ImageCrop,ImageCropLayer,ImageFlatten,-,MenuRemoveTransparency,-,ImageChangeCanvasSize,ImageRepeat,-,ImageResample,ImageSmartZoom3,-,ImageRotateCW,ImageRotateCCW,ImageHorizontalFlip,MenuHorizFlipSub,ImageVerticalFlip,MenuVertFlipSub');
+  AddMenus('MenuImage',  'ImageCrop,ImageCropLayer,ImageFlatten,MenuRemoveTransparency,-,ImageNegative,ImageLinearNegative,ImageSwapRedBlue,-,ImageChangeCanvasSize,ImageRepeat,-,ImageResample,ImageSmartZoom3,-,ImageRotateCW,ImageRotateCCW,ImageHorizontalFlip,MenuHorizFlipSub,ImageVerticalFlip,MenuVertFlipSub');
   AddMenus('MenuRemoveTransparency', 'ImageClearAlpha,ImageFillBackground');
   AddMenus('MenuRemoveTransparency', 'ImageClearAlpha,ImageFillBackground');
   AddMenus('MenuFilter', 'MenuRadialBlur,FilterBlurMotion,FilterBlurCustom,FilterPixelate,-,FilterSharpen,FilterSmooth,FilterNoise,FilterMedian,FilterClearType,FilterClearTypeInverse,FilterFunction,-,FilterContour,FilterEmboss,FilterPhong,-,FilterSphere,FilterTwirl,FilterCylinder');
   AddMenus('MenuFilter', 'MenuRadialBlur,FilterBlurMotion,FilterBlurCustom,FilterPixelate,-,FilterSharpen,FilterSmooth,FilterNoise,FilterMedian,FilterClearType,FilterClearTypeInverse,FilterFunction,-,FilterContour,FilterEmboss,FilterPhong,-,FilterSphere,FilterTwirl,FilterCylinder');
   AddMenus('MenuRadialBlur',  'FilterBlurBox,FilterBlurFast,FilterBlurRadial,FilterBlurCorona,FilterBlurDisk');
   AddMenus('MenuRadialBlur',  'FilterBlurBox,FilterBlurFast,FilterBlurRadial,FilterBlurCorona,FilterBlurDisk');

+ 2 - 2
lazpaint/upalettetoolbar.pas

@@ -491,8 +491,8 @@ begin
   quant := TBGRAColorQuantizer.Create(FColors, not FTransparentPalette);
   quant := TBGRAColorQuantizer.Create(FColors, not FTransparentPalette);
   LayerAction := nil;
   LayerAction := nil;
   try
   try
-    LayerAction := TLayerAction.Create(LazPaintInstance.Image);
-    LayerAction.ReplaceSelectedLayer(quant.GetDitheredBitmap(ADither,LayerAction.SelectedImageLayer) as TBGRABitmap, True);
+    LayerAction := LazPaintInstance.Image.CreateAction;
+    quant.ApplyDitheringInplace(ADither,LayerAction.SelectedImageLayer);
     LazPaintInstance.image.LayerMayChangeCompletely(LayerAction.SelectedImageLayer);
     LazPaintInstance.image.LayerMayChangeCompletely(LayerAction.SelectedImageLayer);
     LayerAction.Validate;
     LayerAction.Validate;
   except
   except

+ 1 - 1
lazpaint/uposterize.pas

@@ -47,7 +47,7 @@ var FPosterize: TFPosterize;
 begin
 begin
   FPosterize := TFPosterize.Create(nil);
   FPosterize := TFPosterize.Create(nil);
   try
   try
-    FPosterize.FFilterConnector := TFilterConnector.Create(AInstance, AParameters);
+    FPosterize.FFilterConnector := TFilterConnector.Create(AInstance, AParameters, false);
     FPosterize.FFilterConnector.OnTryStopAction := @FPosterize.OnTryStopAction;
     FPosterize.FFilterConnector.OnTryStopAction := @FPosterize.OnTryStopAction;
   except
   except
     on ex: exception do
     on ex: exception do

+ 1 - 0
lazpaint/uresourcestrings.pas

@@ -166,6 +166,7 @@ resourcestring
   rsOpenMultipleImageFiles='Open multiple image files';
   rsOpenMultipleImageFiles='Open multiple image files';
   rsMoreThanOneFile='You are trying to open more than one file. How would you like these files to be opened?';
   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';
   rsOpenFilesAsLayers='Open files as layers in a single image';
+  rsTooManyLayers='Too many layers';
   rsAddToImageList='Add files to the image processing list';
   rsAddToImageList='Add files to the image processing list';
   rsOpenFirstFileOnly='Open the first file only';
   rsOpenFirstFileOnly='Open the first file only';
   rsLayeredImage = 'Layered image';
   rsLayeredImage = 'Layered image';

+ 35 - 35
lazpaint/uscripting.pas

@@ -87,41 +87,41 @@ type
     function AddStringList(const AName: string): TScriptVariableReference;
     function AddStringList(const AName: string): TScriptVariableReference;
     function GetVariable(const AName: string): TScriptVariableReference;
     function GetVariable(const AName: string): TScriptVariableReference;
     function IsDefined(const AName: string): boolean;
     function IsDefined(const AName: string): boolean;
-    class procedure ClearList(const ADest: TScriptVariableReference);
-    class function AppendFloat(const ADest: TScriptVariableReference; AValue: double): boolean;
-    class function AssignFloat(const ADest: TScriptVariableReference; AValue: double): boolean;
-    class function AssignFloatAt(const ADest: TScriptVariableReference; AIndex: NativeInt; AValue: double): boolean;
-    class function AppendInteger(const ADest: TScriptVariableReference; AValue: TScriptInteger): boolean;
-    class function AssignInteger(const ADest: TScriptVariableReference; AValue: TScriptInteger): boolean;
-    class function AssignIntegerAt(const ADest: TScriptVariableReference; AIndex: NativeInt; AValue: TScriptInteger): boolean;
-    class function AppendObject(const ADest: TScriptVariableReference; AValue: TScriptInteger): boolean;
-    class function AssignObject(const ADest: TScriptVariableReference; AValue: TScriptInteger): boolean;
-    class function AssignObjectAt(const ADest: TScriptVariableReference; AIndex: NativeInt; AValue: TScriptInteger): boolean;
-    class function AppendBoolean(const ADest: TScriptVariableReference; AValue: boolean): boolean;
-    class function AssignBoolean(const ADest: TScriptVariableReference; AValue: boolean): boolean;
-    class function AppendString(const ADest: TScriptVariableReference; AValue: string): boolean;
-    class function AssignString(const ADest: TScriptVariableReference; AValue: string): boolean;
-    class function AppendPixel(const ADest: TScriptVariableReference; const AValue: TBGRAPixel): boolean;
-    class function AssignPixel(const ADest: TScriptVariableReference; const AValue: TBGRAPixel): boolean;
-    class function AssignList(const ADest: TScriptVariableReference; AListExpr: string): TInterpretationErrors;
-    class function AssignVariable(const ADest, ASource: TScriptVariableReference): boolean;
-    class function IsReferenceDefined(const AReference: TScriptVariableReference): boolean;
-    class function GetFloat(const ASource: TScriptVariableReference) : double;
-    class function GetInteger(const ASource: TScriptVariableReference) : TScriptInteger;
-    class function GetObject(const ASource: TScriptVariableReference) : TScriptInteger;
-    class function GetBoolean(const ASource: TScriptVariableReference) : boolean;
-    class function GetString(const ASource: TScriptVariableReference) : string;
-    class function GetPixel(const ASource: TScriptVariableReference) : TBGRAPixel;
-    class function GetSubset(const ASource: TScriptVariableReference) : TVariableSet;
-    class function GetList(const ASource: TScriptVariableReference) : string;
-    class function GetListCount(const ASource: TScriptVariableReference) : NativeInt;
-    class function GetFloatAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : double;
-    class function GetIntegerAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : TScriptInteger;
-    class function GetObjectAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : TScriptInteger;
-    class function GetBooleanAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : boolean;
-    class function GetStringAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : string;
-    class function GetPixelAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : TBGRAPixel;
-    class function RemoveAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : boolean;
+    class procedure ClearList(const ADest: TScriptVariableReference); static;
+    class function AppendFloat(const ADest: TScriptVariableReference; AValue: double): boolean; overload; static;
+    class function AssignFloat(const ADest: TScriptVariableReference; AValue: double): boolean; overload; static;
+    class function AssignFloatAt(const ADest: TScriptVariableReference; AIndex: NativeInt; AValue: double): boolean; static;
+    class function AppendInteger(const ADest: TScriptVariableReference; AValue: TScriptInteger): boolean; overload; static;
+    class function AssignInteger(const ADest: TScriptVariableReference; AValue: TScriptInteger): boolean; overload; static;
+    class function AssignIntegerAt(const ADest: TScriptVariableReference; AIndex: NativeInt; AValue: TScriptInteger): boolean; static;
+    class function AppendObject(const ADest: TScriptVariableReference; AValue: TScriptInteger): boolean; overload; static;
+    class function AssignObject(const ADest: TScriptVariableReference; AValue: TScriptInteger): boolean; overload; static;
+    class function AssignObjectAt(const ADest: TScriptVariableReference; AIndex: NativeInt; AValue: TScriptInteger): boolean; static;
+    class function AppendBoolean(const ADest: TScriptVariableReference; AValue: boolean): boolean; overload; static;
+    class function AssignBoolean(const ADest: TScriptVariableReference; AValue: boolean): boolean; overload; static;
+    class function AppendString(const ADest: TScriptVariableReference; AValue: string): boolean; overload; static;
+    class function AssignString(const ADest: TScriptVariableReference; AValue: string): boolean; overload; static;
+    class function AppendPixel(const ADest: TScriptVariableReference; const AValue: TBGRAPixel): boolean; overload; static;
+    class function AssignPixel(const ADest: TScriptVariableReference; const AValue: TBGRAPixel): boolean; overload; static;
+    class function AssignList(const ADest: TScriptVariableReference; AListExpr: string): TInterpretationErrors; static;
+    class function AssignVariable(const ADest, ASource: TScriptVariableReference): boolean; static;
+    class function IsReferenceDefined(const AReference: TScriptVariableReference): boolean; static;
+    class function GetFloat(const ASource: TScriptVariableReference) : double; static;
+    class function GetInteger(const ASource: TScriptVariableReference) : TScriptInteger; static;
+    class function GetObject(const ASource: TScriptVariableReference) : TScriptInteger; static;
+    class function GetBoolean(const ASource: TScriptVariableReference) : boolean; static;
+    class function GetString(const ASource: TScriptVariableReference) : string; static;
+    class function GetPixel(const ASource: TScriptVariableReference) : TBGRAPixel; static;
+    class function GetSubset(const ASource: TScriptVariableReference) : TVariableSet; static;
+    class function GetList(const ASource: TScriptVariableReference) : string; static;
+    class function GetListCount(const ASource: TScriptVariableReference) : NativeInt; static;
+    class function GetFloatAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : double; static;
+    class function GetIntegerAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : TScriptInteger; static;
+    class function GetObjectAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : TScriptInteger; static;
+    class function GetBooleanAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : boolean; static;
+    class function GetStringAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : string; static;
+    class function GetPixelAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : TBGRAPixel; static;
+    class function RemoveAt(const ASource: TScriptVariableReference; AIndex: NativeInt) : boolean; static;
     function Duplicate: TVariableSet;
     function Duplicate: TVariableSet;
     function CopyValuesTo(ASet: TVariableSet): boolean;
     function CopyValuesTo(ASet: TVariableSet): boolean;
     property FunctionName: string read FFunctionName;
     property FunctionName: string read FFunctionName;

+ 2 - 2
lazpaint/uselectionhighlight.pas

@@ -68,8 +68,8 @@ var filter: TBGRAEmbossHightlightScanner;
   selectionBounds: TRect;
   selectionBounds: TRect;
   gridCoverage, extendedGridCoverage: TRect;
   gridCoverage, extendedGridCoverage: TRect;
 begin
 begin
-  selection := FImage.SelectionReadonly;
-  selectionBounds := FImage.SelectionBounds;
+  selection := FImage.SelectionMaskReadonly;
+  selectionBounds := FImage.SelectionMaskBounds;
   if (selection = nil) or (selection.Width = 0) or (selection.Width = 0) or
   if (selection = nil) or (selection.Width = 0) or (selection.Width = 0) or
      IsRectEmpty(selectionBounds) then
      IsRectEmpty(selectionBounds) then
   begin
   begin

+ 1 - 1
lazpaint/ushiftcolors.pas

@@ -196,7 +196,7 @@ var gsbaOptionFromConfig: boolean;
     topmostInfo: TTopMostInfo;
     topmostInfo: TTopMostInfo;
 begin
 begin
   try
   try
-    FFilterConnector := TFilterConnector.Create(AInstance,AParameters);
+    FFilterConnector := TFilterConnector.Create(AInstance,AParameters,false);
     FFilterConnector.OnTryStopAction := @OnTryStopAction;
     FFilterConnector.OnTryStopAction := @OnTryStopAction;
   except
   except
     on ex: exception do
     on ex: exception do

+ 474 - 300
lazpaint/ustatetype.pas

@@ -5,7 +5,7 @@ unit UStateType;
 interface
 interface
 
 
 uses
 uses
-  Types, Classes, SysUtils, BGRABitmap, BGRABitmapTypes, BGRALayers;
+  Types, Classes, SysUtils, BGRABitmap, BGRABitmapTypes, BGRALayers, fgl;
 
 
 const MinSizeToCompress = 512; //set to 1 if you want always compression
 const MinSizeToCompress = 512; //set to 1 if you want always compression
 const MinSerializedSize = 16384;
 const MinSerializedSize = 16384;
@@ -20,6 +20,7 @@ type
     procedure ApplyTo(AState: TState); virtual; abstract;
     procedure ApplyTo(AState: TState); virtual; abstract;
     procedure UnapplyTo(AState: TState); virtual; abstract;
     procedure UnapplyTo(AState: TState); virtual; abstract;
     function UsedMemory: int64; virtual;
     function UsedMemory: int64; virtual;
+    function ToString: ansistring; override;
   end;
   end;
 
 
   TState = class
   TState = class
@@ -55,6 +56,31 @@ type
     property IsIdentity: boolean read GetIsIdentity;
     property IsIdentity: boolean read GetIsIdentity;
   end;
   end;
 
 
+  TImageDifferenceList = specialize TFPGObjectList<TCustomImageDifference>;
+
+  { TComposedImageDifference }
+
+  TComposedImageDifference = class(TCustomImageDifference)
+  private
+    function GetCount: integer;
+  protected
+    FDiffs: TImageDifferenceList;
+    function GetIsIdentity: boolean; override;
+    function GetImageDifferenceKind: TImageDifferenceKind; override;
+    function GetChangingBounds: TRect; override;
+    function GetChangingBoundsDefined: boolean; override;
+  public
+    constructor Create;
+    function TryCompress: boolean; override;
+    function UsedMemory: int64; override;
+    procedure Add(ADiff: TCustomImageDifference);
+    procedure AddRange(AComposed: TComposedImageDifference);
+    procedure ApplyTo(AState: TState); override;
+    procedure UnapplyTo(AState: TState); override;
+    function ToString: ansistring; override;
+    property Count: integer read GetCount;
+  end;
+
 {*********** Layer info *************}
 {*********** Layer info *************}
 
 
 type
 type
@@ -64,83 +90,89 @@ type
     BlendOp: TBlendOperation;
     BlendOp: TBlendOperation;
     Name: string;
     Name: string;
     Opactiy: byte;
     Opactiy: byte;
+    Offset: TPoint;
   end;
   end;
 
 
 procedure ApplyLayerInfo(AInfo: TLayerInfo; ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer);
 procedure ApplyLayerInfo(AInfo: TLayerInfo; ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer);
-procedure ApplyLayerInfo(AInfo: TLayerInfo; ALayeredBitmap: TBGRALayeredBitmap);
 function GetLayerInfo(ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer): TLayerInfo;
 function GetLayerInfo(ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer): TLayerInfo;
 
 
 {*********** Inversible **************}
 {*********** Inversible **************}
 
 
 type
 type
-  TInversibleAction = (iaHorizontalFlip, iaVerticalFlip, iaRotateCW, iaRotateCCW, iaRotate180, iaSwapRedBlue, iaLinearNegative);
+  TInversibleAction = (iaHorizontalFlip, iaHorizontalFlipLayer, iaVerticalFlip, iaVerticalFlipLayer, iaRotateCW, iaRotateCCW, iaRotate180, iaSwapRedBlue, iaLinearNegative);
+
+const
+  InversibleActionStr : array[TInversibleAction] of string =
+    ('HorizontalFlip', 'HorizontalFlipLayer', 'VerticalFlip', 'VerticalFlipLayer', 'RotateCW', 'RotateCCW', 'Rotate180', 'SwapRedBlue', 'LinearNegative');
 
 
 function GetInverseAction(AAction: TInversibleAction): TInversibleAction;
 function GetInverseAction(AAction: TInversibleAction): TInversibleAction;
 function CanCombineInversibleAction(AAction1, AAction2: TInversibleAction; out
 function CanCombineInversibleAction(AAction1, AAction2: TInversibleAction; out
   ACombined: TInversibleAction): boolean;
   ACombined: TInversibleAction): boolean;
 
 
 type
 type
+  TCustomImageDiff = class
+  private
+    FSavedFilename: string;
+    FCompressedData: TMemoryStream;
+    procedure DiscardFile;
+    function GetIsIdentity: boolean; virtual;
+    procedure Init(Image1,Image2: TBGRABitmap; {%H-}AChangeRect: TRect); virtual;
+    function SerializeCompressedData: boolean;
+    procedure UnserializeCompressedData;
+  public
+    SizeBefore, SizeAfter: TSize;
+    constructor Create(Image1,Image2: TBGRABitmap; AChangeRect: TRect); overload;
+    constructor Create(Image1,Image2: TBGRABitmap); overload;
+    procedure ApplyInPlace(ADest: TBGRABitmap; {%H-}AReverse: boolean); virtual; abstract;
+    function ApplyInNew(ASource: TBGRABitmap; AReverse: boolean): TBGRABitmap;
+    function ApplyCanCreateNew(ASource: TBGRABitmap; AReverse: boolean): TBGRABitmap;
+    function Compress: boolean; virtual;
+    destructor Destroy; override;
+    function UsedMemory: int64;
+    property IsIdentity: boolean read GetIsIdentity;
+  end;
+
   { TImageDiff }
   { TImageDiff }
 
 
-  TImageDiff = class
+  TImageDiff = class(TCustomImageDiff)
   private
   private
     FChangeRect: TRect;
     FChangeRect: TRect;
     FUncompressedData: record
     FUncompressedData: record
       data0,data1,data2,data3: PByte;
       data0,data1,data2,data3: PByte;
       dataLen: PtrUInt;
       dataLen: PtrUInt;
     end;
     end;
-    FSavedFilename: string;
-    FCompressedData: TMemoryStream;
-    procedure DiscardFile;
-    function GetIsIdentity: boolean;
+    function GetIsIdentity: boolean; override;
     procedure Decompress;
     procedure Decompress;
-    procedure Init(Image1,Image2: TBGRABitmap; AChangeRect: TRect);
+    procedure Init(Image1,Image2: TBGRABitmap; AChangeRect: TRect); override;
   public
   public
-    SizeBefore, SizeAfter: TSize;
-    constructor Create(Image1,Image2: TBGRABitmap; AChangeRect: TRect); overload;
-    constructor Create(Image1,Image2: TBGRABitmap); overload;
-    procedure Apply(ADest: TBGRABitmap; {%H-}AReverse: boolean);
-    function Compress: boolean;
+    procedure ApplyInPlace(ADest: TBGRABitmap; {%H-}AReverse: boolean); override;
+    function Compress: boolean; override;
     destructor Destroy; override;
     destructor Destroy; override;
     function UsedMemory: int64;
     function UsedMemory: int64;
-    property IsIdentity: boolean read GetIsIdentity;
     property ChangeRect: TRect read FChangeRect;
     property ChangeRect: TRect read FChangeRect;
   end;
   end;
 
 
-function ComputeFromImageDiff(FromImage: TBGRABitmap; ADiff: TImageDiff; AReverse: boolean): TBGRABitmap;
-procedure ApplyImageDiffAndReplace(var AImage: TBGRABitmap; ADiff: TImageDiff; AReverse: boolean);
-
 type
 type
   { TGrayscaleImageDiff }
   { TGrayscaleImageDiff }
 
 
-  TGrayscaleImageDiff = class
+  TGrayscaleImageDiff = class(TCustomImageDiff)
   private
   private
     FChangeRect: TRect;
     FChangeRect: TRect;
     FUncompressedData: record
     FUncompressedData: record
       data0: PByte;
       data0: PByte;
       dataLen: PtrUInt;
       dataLen: PtrUInt;
     end;
     end;
-    FSavedFilename: string;
-    FCompressedData: TMemoryStream;
-    procedure DiscardFile;
-    function GetIsIdentity: boolean;
+    function GetIsIdentity: boolean; override;
     procedure Decompress;
     procedure Decompress;
-    procedure Init(Image1,Image2: TBGRABitmap; AChangeRect: TRect);
+    procedure Init(Image1,Image2: TBGRABitmap; AChangeRect: TRect); override;
   public
   public
-    SizeBefore, SizeAfter: TSize;
-    constructor Create(Image1,Image2: TBGRABitmap; AChangeRect: TRect); overload;
-    constructor Create(Image1,Image2: TBGRABitmap); overload;
-    procedure Apply(ADest: TBGRABitmap; {%H-}AReverse: boolean);
-    function Compress: boolean;
+    procedure ApplyInPlace(ADest: TBGRABitmap; {%H-}AReverse: boolean); override;
+    function Compress: boolean; override;
     destructor Destroy; override;
     destructor Destroy; override;
     function UsedMemory: int64;
     function UsedMemory: int64;
-    property IsIdentity: boolean read GetIsIdentity;
     property ChangeRect: TRect read FChangeRect;
     property ChangeRect: TRect read FChangeRect;
   end;
   end;
 
 
-function ComputeFromGrayscaleImageDiff(FromImage: TBGRABitmap; ADiff: TGrayscaleImageDiff; AReverse: boolean): TBGRABitmap;
-procedure ApplyGrayscaleImageDiffAndReplace(var AImage: TBGRABitmap; ADiff: TGrayscaleImageDiff; AReverse: boolean);
-
 type
 type
   { TStoredImage }
   { TStoredImage }
 
 
@@ -150,13 +182,34 @@ type
     function GetBitmap: TBGRABitmap;
     function GetBitmap: TBGRABitmap;
   end;
   end;
 
 
+  { TStoredLayer }
+
+  TStoredLayer = class(TStoredImage)
+  private
+    function GetId: integer;
+  protected
+    FInfo: TLayerInfo;
+    FIndex: integer;
+    FOriginalData: TMemoryStream;
+    FOriginalKnown: boolean;
+    FOriginalRenderStatus: TOriginalRenderStatus;
+    FOriginalMatrix: TAffineMatrix;
+    FOriginalDraft: boolean;
+  public
+    constructor Create(ALayeredImage: TBGRALayeredBitmap; AIndex: integer);
+    procedure Restore(ALayeredImage: TBGRALayeredBitmap);
+    procedure Replace(ALayeredImage: TBGRALayeredBitmap);
+    property LayerIndex: integer read FIndex;
+    property LayerId: integer read GetId;
+  end;
+
 implementation
 implementation
 
 
 uses Math, BGRALzpCommon, UFileSystem;
 uses Math, BGRALzpCommon, UFileSystem;
 
 
-{ TGrayscaleImageDiff }
+{ TCustomImageDiff }
 
 
-procedure TGrayscaleImageDiff.DiscardFile;
+procedure TCustomImageDiff.DiscardFile;
 begin
 begin
   if FSavedFilename <> '' then
   if FSavedFilename <> '' then
   begin
   begin
@@ -169,12 +222,59 @@ begin
   end;
   end;
 end;
 end;
 
 
-function TGrayscaleImageDiff.GetIsIdentity: boolean;
+function TCustomImageDiff.GetIsIdentity: boolean;
 begin
 begin
-  result := (SizeBefore.cx = SizeAfter.cx) and (SizeBefore.cy = SizeAfter.cy) and (FUncompressedData.dataLen=0);
+  result := (SizeBefore.cx = SizeAfter.cx) and (SizeBefore.cy = SizeAfter.cy);
 end;
 end;
 
 
-procedure TGrayscaleImageDiff.Decompress;
+procedure TCustomImageDiff.Init(Image1, Image2: TBGRABitmap; AChangeRect: TRect);
+begin
+  if Image1 = nil then
+  begin
+    SizeBefore.cx := 0;
+    SizeBefore.cy := 0;
+  end else
+  begin
+    SizeBefore.cx := Image1.Width;
+    SizeBefore.cy := Image1.Height;
+  end;
+  if Image2 = nil then
+  begin
+    SizeAfter.cx := 0;
+    SizeAfter.cy := 0;
+  end else
+  begin
+    SizeAfter.cx := Image2.Width;
+    SizeAfter.cy := Image2.Height;
+  end;
+end;
+
+function TCustomImageDiff.SerializeCompressedData: boolean;
+var
+  savedFile: TStream;
+begin
+  FSavedFilename := GetTempFileName;
+  try
+    savedFile := FileManager.CreateFileStream(FSavedFilename,fmCreate);
+    try
+      FCompressedData.Position := 0;
+      savedFile.CopyFrom(FCompressedData, FCompressedData.Size);
+      FreeAndNil(FCompressedData);
+      result := true;
+    finally
+      savedFile.Free;
+    end;
+  except
+    on ex: exception do
+    begin
+      if FileManager.FileExists(FSavedFilename) then FileManager.DeleteFile(FSavedFilename);
+      FSavedFilename := '';
+      result := false;
+    end;
+  end;
+end;
+
+procedure TCustomImageDiff.UnserializeCompressedData;
 var stream: TStream;
 var stream: TStream;
 begin
 begin
   if (FCompressedData = nil) and (FSavedFilename <> '') then
   if (FCompressedData = nil) and (FSavedFilename <> '') then
@@ -188,6 +288,228 @@ begin
     end;
     end;
     stream.free;
     stream.free;
   end;
   end;
+end;
+
+constructor TCustomImageDiff.Create(Image1, Image2: TBGRABitmap;
+  AChangeRect: TRect);
+begin
+  Init(Image1,Image2,AChangeRect);
+end;
+
+constructor TCustomImageDiff.Create(Image1, Image2: TBGRABitmap);
+var
+  r: TRect;
+begin
+  r := rect(0,0,0,0);
+  if Image1 <> nil then
+  begin
+    if image1.Width > r.Right then r.Right:= Image1.Width;
+    if image1.Height > r.Bottom then r.Bottom:= Image1.Height;
+  end;
+  if Image2 <> nil then
+  begin
+    if image2.Width > r.Right then r.Right:= Image2.Width;
+    if image2.Height > r.Bottom then r.Bottom:= Image2.Height;
+  end;
+  Init(Image1,Image2,r);
+end;
+
+function TCustomImageDiff.ApplyInNew(ASource: TBGRABitmap; AReverse: boolean): TBGRABitmap;
+var
+  DestSize: TSize;
+begin
+  if (self = nil) or IsIdentity then
+    result := ASource.Duplicate as TBGRABitmap
+  else
+  begin
+    if AReverse then DestSize := SizeBefore else
+      DestSize := SizeAfter;
+
+    if (DestSize.cx = 0) or (DestSize.cy = 0) then
+      result := nil
+    else
+    begin
+      result := TBGRABitmap.Create(Destsize.cx,Destsize.cy);
+      if ASource <> nil then
+        result.PutImage(0,0,ASource,dmSet);
+      ApplyInPlace(result, AReverse);
+    end;
+  end;
+end;
+
+function TCustomImageDiff.ApplyCanCreateNew(ASource: TBGRABitmap;
+  AReverse: boolean): TBGRABitmap;
+begin
+  if (self = nil) or IsIdentity then exit(ASource); //keep
+
+  if (SizeAfter.cx <> SizeBefore.cx) or
+     (SizeAfter.cy <> SizeBefore.cy) then
+     exit(ApplyInNew(ASource, AReverse))
+  else
+  begin
+    ApplyInPlace(ASource, AReverse);
+    exit(ASource);
+  end;
+end;
+
+function TCustomImageDiff.Compress: boolean;
+begin
+  result := false;
+end;
+
+destructor TCustomImageDiff.Destroy;
+begin
+  FreeAndnil(FCompressedData);
+  DiscardFile;
+  inherited Destroy;
+end;
+
+function TCustomImageDiff.UsedMemory: int64;
+begin
+  if Assigned(FCompressedData) then
+    result := FCompressedData.Size
+  else
+    result := 0;
+end;
+
+{ TComposedImageDifference }
+
+function TComposedImageDifference.GetCount: integer;
+begin
+  result := FDiffs.Count;
+end;
+
+function TComposedImageDifference.GetIsIdentity: boolean;
+var
+  i: Integer;
+begin
+  for i := 0 to FDiffs.Count-1 do
+    if not FDiffs[i].GetIsIdentity then exit(false);
+  exit(true);
+end;
+
+function TComposedImageDifference.GetImageDifferenceKind: TImageDifferenceKind;
+var
+  i: Integer;
+begin
+  result := idkChangeStack;
+  for i := 0 to FDiffs.Count-1 do
+    case FDiffs[i].GetImageDifferenceKind of
+      idkChangeImageAndSelection: result := idkChangeImageAndSelection;
+      idkChangeSelection: if result in[idkChangeImage,idkChangeLayer,idkChangeImageAndSelection] then
+                            result := idkChangeImageAndSelection
+                          else result := idkChangeSelection;
+      idkChangeImage: if result in[idkChangeImageAndSelection,idkChangeSelection] then
+                            result := idkChangeImageAndSelection
+                          else result := idkChangeImage;
+      idkChangeLayer: if result in[idkChangeImageAndSelection,idkChangeSelection] then
+                            result := idkChangeImageAndSelection
+                      else if result = idkChangeStack then
+                        result := idkChangeLayer;
+    end;
+end;
+
+function TComposedImageDifference.GetChangingBounds: TRect;
+var
+  i: Integer;
+  r: TRect;
+begin
+  result:= EmptyRect;
+  for i := 0 to FDiffs.Count-1 do
+  begin
+    r := FDiffs[i].GetChangingBounds;
+    if not IsRectEmpty(r) then
+    begin
+      if IsRectEmpty(result) then result:= r
+      else UnionRect(result, result,r);
+    end;
+  end;
+end;
+
+function TComposedImageDifference.GetChangingBoundsDefined: boolean;
+var
+  i: Integer;
+begin
+  for i := 0 to FDiffs.Count-1 do
+    if not FDiffs[i].GetChangingBoundsDefined then exit(false);
+  exit(true);
+end;
+
+constructor TComposedImageDifference.Create;
+begin
+  FDiffs := TImageDifferenceList.Create;
+end;
+
+function TComposedImageDifference.TryCompress: boolean;
+var
+  i: Integer;
+begin
+  for i := 0 to FDiffs.Count-1 do
+    if FDiffs[i].TryCompress then exit(true);
+  exit(false);
+end;
+
+function TComposedImageDifference.UsedMemory: int64;
+var
+  i: Integer;
+begin
+  result := 0;
+  for i := 0 to FDiffs.Count-1 do
+    inc(result, FDiffs[i].UsedMemory);
+end;
+
+procedure TComposedImageDifference.Add(ADiff: TCustomImageDifference);
+begin
+  FDiffs.Add(ADiff);
+end;
+
+procedure TComposedImageDifference.AddRange(AComposed: TComposedImageDifference);
+var
+  i: Integer;
+begin
+  for i:= 0 to AComposed.Count-1 do
+    Add(AComposed.FDiffs[i]);
+end;
+
+procedure TComposedImageDifference.ApplyTo(AState: TState);
+var
+  i: Integer;
+begin
+  for i := 0 to FDiffs.Count-1 do
+    FDiffs[i].ApplyTo(AState);
+end;
+
+procedure TComposedImageDifference.UnapplyTo(AState: TState);
+var
+  i: Integer;
+begin
+  for i := FDiffs.Count-1 downto 0 do
+    FDiffs[i].UnapplyTo(AState);
+end;
+
+function TComposedImageDifference.ToString: ansistring;
+var
+  i: Integer;
+begin
+  Result:= '[';
+  for i := 0 to Count-1 do
+  begin
+    if i <> 0 then result += ', ';
+    result += FDiffs[i].ToString;
+  end;
+  result += ']';
+end;
+
+{ TGrayscaleImageDiff }
+
+function TGrayscaleImageDiff.GetIsIdentity: boolean;
+begin
+  result := inherited GetIsIdentity and (FUncompressedData.dataLen=0);
+end;
+
+procedure TGrayscaleImageDiff.Decompress;
+begin
+  UnserializeCompressedData;
   if FCompressedData <> nil then
   if FCompressedData <> nil then
   begin
   begin
     FCompressedData.Position := 0;
     FCompressedData.Position := 0;
@@ -207,28 +529,9 @@ var tx,ty: integer;
   p: PBGRAPixel;
   p: PBGRAPixel;
   uncompressedChangeRect: TRect;
   uncompressedChangeRect: TRect;
 begin
 begin
-  FUncompressedData.dataLen := 0;
+  inherited Init(Image1,Image2,AChangeRect);
   FChangeRect := EmptyRect;
   FChangeRect := EmptyRect;
-  if Image1 = nil then
-  begin
-    SizeBefore.cx := 0;
-    SizeBefore.cy := 0;
-  end else
-  begin
-    SizeBefore.cx := Image1.Width;
-    SizeBefore.cy := Image1.Height;
-  end;
-
-  if Image2 = nil then
-  begin
-    SizeAfter.cx := 0;
-    SizeAfter.cy := 0;
-  end else
-  begin
-    SizeAfter.cx := Image2.Width;
-    SizeAfter.cy := Image2.Height;
-  end;
-
+  FUncompressedData.dataLen := 0;
   tx := max(SizeBefore.cx,SizeAfter.cx);
   tx := max(SizeBefore.cx,SizeAfter.cx);
   ty := max(SizeBefore.cy,SizeAfter.cy);
   ty := max(SizeBefore.cy,SizeAfter.cy);
   if IntersectRect(AChangeRect, AChangeRect, rect(0,0,tx,ty)) then
   if IntersectRect(AChangeRect, AChangeRect, rect(0,0,tx,ty)) then
@@ -269,31 +572,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-constructor TGrayscaleImageDiff.Create(Image1, Image2: TBGRABitmap;
-  AChangeRect: TRect);
-begin
-  Init(Image1,Image2,AChangeRect);
-end;
-
-constructor TGrayscaleImageDiff.Create(Image1, Image2: TBGRABitmap);
-var
-  r: TRect;
-begin
-  r := rect(0,0,0,0);
-  if Image1 <> nil then
-  begin
-    if image1.Width > r.Right then r.Right:= Image1.Width;
-    if image1.Height > r.Bottom then r.Bottom:= Image1.Height;
-  end;
-  if Image2 <> nil then
-  begin
-    if image2.Width > r.Right then r.Right:= Image2.Width;
-    if image2.Height > r.Bottom then r.Bottom:= Image2.Height;
-  end;
-  Init(Image1,Image2,r);
-end;
-
-procedure TGrayscaleImageDiff.Apply(ADest: TBGRABitmap; AReverse: boolean);
+procedure TGrayscaleImageDiff.ApplyInPlace(ADest: TBGRABitmap; AReverse: boolean);
 var
 var
   pdest: PBGRAPixel;
   pdest: PBGRAPixel;
   data0: PByte;
   data0: PByte;
@@ -325,8 +604,6 @@ begin
 end;
 end;
 
 
 function TGrayscaleImageDiff.Compress: boolean;
 function TGrayscaleImageDiff.Compress: boolean;
-var
-  FSavedFile: TStream;
 begin
 begin
   if (FUncompressedData.data0 <> nil) and
   if (FUncompressedData.data0 <> nil) and
     ((FCompressedData <> nil) or (FSavedFilename <> '')) then
     ((FCompressedData <> nil) or (FSavedFilename <> '')) then
@@ -350,43 +627,19 @@ begin
     result := true;
     result := true;
 
 
     if FCompressedData.Size >= MinSerializedSize then
     if FCompressedData.Size >= MinSerializedSize then
-    begin
-      FSavedFilename := GetTempFileName;
-      try
-        FSavedFile := FileManager.CreateFileStream(FSavedFilename,fmCreate);
-        try
-          FCompressedData.Position := 0;
-          FSavedFile.CopyFrom(FCompressedData, FCompressedData.Size);
-          FreeAndNil(FCompressedData);
-        finally
-          FSavedFile.Free;
-        end;
-      except
-        on ex: exception do
-        begin
-          if FileManager.FileExists(FSavedFilename) then FileManager.DeleteFile(FSavedFilename);
-          FSavedFilename := '';
-          result := false;
-        end;
-      end;
-    end;
+      SerializeCompressedData;
   end;
   end;
 end;
 end;
 
 
 destructor TGrayscaleImageDiff.Destroy;
 destructor TGrayscaleImageDiff.Destroy;
 begin
 begin
-  FreeAndnil(FCompressedData);
   ReAllocMem(FUncompressedData.data0,0);
   ReAllocMem(FUncompressedData.data0,0);
-  DiscardFile;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
 function TGrayscaleImageDiff.UsedMemory: int64;
 function TGrayscaleImageDiff.UsedMemory: int64;
 begin
 begin
-  if Assigned(FCompressedData) then
-    result := FCompressedData.Size
-  else
-    result := 0;
+  result := inherited UsedMemory;
   if Assigned(FUncompressedData.data0) then inc(result,FUncompressedData.dataLen);
   if Assigned(FUncompressedData.data0) then inc(result,FUncompressedData.dataLen);
 end;
 end;
 
 
@@ -402,6 +655,11 @@ begin
   result := 0;
   result := 0;
 end;
 end;
 
 
+function TStateDifference.ToString: ansistring;
+begin
+  Result:= ClassName;
+end;
+
 { TCustomImageDifference }
 { TCustomImageDifference }
 
 
 function TCustomImageDifference.GetIsIdentity: boolean;
 function TCustomImageDifference.GetIsIdentity: boolean;
@@ -453,60 +711,6 @@ begin
   AState.saved:= FSavedBefore;
   AState.saved:= FSavedBefore;
 end;
 end;
 
 
-{***********************************}
-
-procedure ApplyImageDiffAndReplace(var AImage: TBGRABitmap; ADiff: TImageDiff; AReverse: boolean);
-var tempBmp: TBGRABitmap;
-begin
-  if (ADiff = nil) or ADiff.IsIdentity then exit;
-  if (ADiff.SizeAfter.cx <> ADiff.SizeBefore.cx) or
-     (ADiff.SizeAfter.cy <> ADiff.SizeBefore.cy) then
-  begin
-    tempBmp := ComputeFromImageDiff(AImage, ADiff, AReverse);
-    FreeAndNil(AImage);
-    AImage := tempBmp;
-  end else
-    ADiff.Apply(AImage, AReverse);
-end;
-
-function ComputeFromGrayscaleImageDiff(FromImage: TBGRABitmap;
-  ADiff: TGrayscaleImageDiff; AReverse: boolean): TBGRABitmap;
-var
-  DestSize: TSize;
-begin
-  if (ADiff = nil) or ADiff.IsIdentity then
-  begin
-    result := FromImage.Duplicate as TBGRABitmap;
-    exit;
-  end;
-  if AReverse then DestSize := ADiff.SizeBefore else
-    DestSize := ADiff.SizeAfter;
-  if (DestSize.cx = 0) or (DestSize.cy = 0) then
-    result := nil
-  else
-  begin
-    result := TBGRABitmap.Create(Destsize.cx,Destsize.cy,BGRABlack);
-    if FromImage <> nil then
-      result.PutImage(0,0,FromImage,dmSet);
-    ADiff.Apply(result, AReverse);
-  end;
-end;
-
-procedure ApplyGrayscaleImageDiffAndReplace(var AImage: TBGRABitmap;
-  ADiff: TGrayscaleImageDiff; AReverse: boolean);
-var tempBmp: TBGRABitmap;
-begin
-  if (ADiff = nil) or ADiff.IsIdentity then exit;
-  if (ADiff.SizeAfter.cx <> ADiff.SizeBefore.cx) or
-     (ADiff.SizeAfter.cy <> ADiff.SizeBefore.cy) then
-  begin
-    tempBmp := ComputeFromGrayscaleImageDiff(AImage, ADiff, AReverse);
-    FreeAndNil(AImage);
-    AImage := tempBmp;
-  end else
-    ADiff.Apply(AImage, AReverse);
-end;
-
 {*********** Layer info *************}
 {*********** Layer info *************}
 
 
 procedure ApplyLayerInfo(AInfo: TLayerInfo; ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer);
 procedure ApplyLayerInfo(AInfo: TLayerInfo; ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer);
@@ -518,15 +722,8 @@ begin
   ALayeredBitmap.BlendOperation[AIndex] := AInfo.BlendOp;
   ALayeredBitmap.BlendOperation[AIndex] := AInfo.BlendOp;
   ALayeredBitmap.LayerName[AIndex] := AInfo.Name;
   ALayeredBitmap.LayerName[AIndex] := AInfo.Name;
   ALayeredBitmap.LayerOpacity[AIndex] := AInfo.Opactiy;
   ALayeredBitmap.LayerOpacity[AIndex] := AInfo.Opactiy;
-end;
-
-procedure ApplyLayerInfo(AInfo: TLayerInfo; ALayeredBitmap: TBGRALayeredBitmap);
-var idx: integer;
-begin
-  idx := ALayeredBitmap.GetLayerIndexFromId(AInfo.Id);
-  if idx = -1 then
-    raise exception.Create('Layer not found');
-  ApplyLayerInfo(AInfo, ALayeredBitmap, idx);
+  if ALayeredBitmap.LayerOriginalGuid[AIndex] = GUID_NULL then
+    ALayeredBitmap.LayerOffset[AIndex] := AInfo.Offset;
 end;
 end;
 
 
 function GetLayerInfo(ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer): TLayerInfo;
 function GetLayerInfo(ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer): TLayerInfo;
@@ -538,6 +735,7 @@ begin
   result.BlendOp := ALayeredBitmap.BlendOperation[AIndex];
   result.BlendOp := ALayeredBitmap.BlendOperation[AIndex];
   result.Name := ALayeredBitmap.LayerName[AIndex];
   result.Name := ALayeredBitmap.LayerName[AIndex];
   result.Opactiy := ALayeredBitmap.LayerOpacity[AIndex];
   result.Opactiy := ALayeredBitmap.LayerOpacity[AIndex];
+  result.Offset := ALayeredBitmap.LayerOffset[AIndex];
 end;
 end;
 
 
 {*********** Inversible **************}
 {*********** Inversible **************}
@@ -589,64 +787,16 @@ begin
   end;
   end;
 end;
 end;
 
 
-{**************** Image diff ****************}
-
-function ComputeFromImageDiff(FromImage: TBGRABitmap; ADiff: TImageDiff; AReverse: boolean): TBGRABitmap;
-var
-  DestSize: TSize;
-begin
-  if (ADiff = nil) or ADiff.IsIdentity then
-  begin
-    result := FromImage.Duplicate as TBGRABitmap;
-    exit;
-  end;
-  if AReverse then DestSize := ADiff.SizeBefore else
-    DestSize := ADiff.SizeAfter;
-  if (DestSize.cx = 0) or (DestSize.cy = 0) then
-    result := nil
-  else
-  begin
-    result := TBGRABitmap.Create(Destsize.cx,Destsize.cy);
-    if FromImage <> nil then
-      result.PutImage(0,0,FromImage,dmSet);
-    ADiff.Apply(result, AReverse);
-  end;
-end;
-
 { TImageDiff }
 { TImageDiff }
 
 
-procedure TImageDiff.DiscardFile;
-begin
-  if FSavedFilename <> '' then
-  begin
-    try
-      if FileManager.FileExists(FSavedFilename) then
-        FileManager.DeleteFile(FSavedFilename);
-    except on ex:exception do begin end;
-    end;
-    FSavedFilename:= '';
-  end;
-end;
-
 function TImageDiff.GetIsIdentity: boolean;
 function TImageDiff.GetIsIdentity: boolean;
 begin
 begin
-  result := (SizeBefore.cx = SizeAfter.cx) and (SizeBefore.cy = SizeAfter.cy) and (FUncompressedData.dataLen=0);
+  result := inherited GetIsIdentity and (FUncompressedData.dataLen=0);
 end;
 end;
 
 
 procedure TImageDiff.Decompress;
 procedure TImageDiff.Decompress;
-var stream: TStream;
 begin
 begin
-  if (FCompressedData = nil) and (FSavedFilename <> '') then
-  begin
-    FCompressedData := TMemoryStream.Create;
-    stream := nil;
-    try
-      stream := FileManager.CreateFileStream(FSavedFilename,fmOpenRead or fmShareDenyWrite);
-      FCompressedData.CopyFrom(stream, stream.Size);
-    except
-    end;
-    stream.free;
-  end;
+  UnserializeCompressedData;
   if FCompressedData <> nil then
   if FCompressedData <> nil then
   begin
   begin
     FCompressedData.Position := 0;
     FCompressedData.Position := 0;
@@ -672,28 +822,9 @@ var tx,ty: integer;
   v: DWord;
   v: DWord;
   uncompressedChangeRect: TRect;
   uncompressedChangeRect: TRect;
 begin
 begin
-  FUncompressedData.dataLen := 0;
+  inherited Init(Image1, Image2, ChangeRect);
   FChangeRect := EmptyRect;
   FChangeRect := EmptyRect;
-  if Image1 = nil then
-  begin
-    SizeBefore.cx := 0;
-    SizeBefore.cy := 0;
-  end else
-  begin
-    SizeBefore.cx := Image1.Width;
-    SizeBefore.cy := Image1.Height;
-  end;
-
-  if Image2 = nil then
-  begin
-    SizeAfter.cx := 0;
-    SizeAfter.cy := 0;
-  end else
-  begin
-    SizeAfter.cx := Image2.Width;
-    SizeAfter.cy := Image2.Height;
-  end;
-
+  FUncompressedData.dataLen := 0;
   tx := max(SizeBefore.cx,SizeAfter.cx);
   tx := max(SizeBefore.cx,SizeAfter.cx);
   ty := max(SizeBefore.cy,SizeAfter.cy);
   ty := max(SizeBefore.cy,SizeAfter.cy);
   if IntersectRect(AChangeRect, AChangeRect, rect(0,0,tx,ty)) then
   if IntersectRect(AChangeRect, AChangeRect, rect(0,0,tx,ty)) then
@@ -744,36 +875,14 @@ begin
   end;
   end;
 end;
 end;
 
 
-constructor TImageDiff.Create(Image1, Image2: TBGRABitmap; AChangeRect: TRect);
-begin
-  Init(Image1,Image2,AChangeRect);
-end;
-
-constructor TImageDiff.Create(Image1, Image2: TBGRABitmap);
-var
-  r: TRect;
-begin
-  r := rect(0,0,0,0);
-  if Image1 <> nil then
-  begin
-    if image1.Width > r.Right then r.Right:= Image1.Width;
-    if image1.Height > r.Bottom then r.Bottom:= Image1.Height;
-  end;
-  if Image2 <> nil then
-  begin
-    if image2.Width > r.Right then r.Right:= Image2.Width;
-    if image2.Height > r.Bottom then r.Bottom:= Image2.Height;
-  end;
-  Init(Image1,Image2,r);
-end;
-
-procedure TImageDiff.Apply(ADest: TBGRABitmap; AReverse: boolean);
+procedure TImageDiff.ApplyInPlace(ADest: TBGRABitmap; AReverse: boolean);
 var
 var
   pdest: PDWord;
   pdest: PDWord;
   data0,data1,data2,data3: PByte;
   data0,data1,data2,data3: PByte;
   r: TRect;
   r: TRect;
   xb,yb,w,offset: PtrUInt;
   xb,yb,w,offset: PtrUInt;
 begin
 begin
+  if ADest = nil then raise exception.Create('Unexpected nil reference');
   r := FChangeRect;
   r := FChangeRect;
   w := FChangeRect.Right-FChangeRect.Left;
   w := FChangeRect.Right-FChangeRect.Left;
   if not IntersectRect(r, r,rect(0,0,ADest.Width,ADest.Height)) then exit;
   if not IntersectRect(r, r,rect(0,0,ADest.Width,ADest.Height)) then exit;
@@ -798,8 +907,6 @@ begin
 end;
 end;
 
 
 function TImageDiff.Compress: boolean;
 function TImageDiff.Compress: boolean;
-var
-  FSavedFile: TStream;
 begin
 begin
   if ((FUncompressedData.data0 <> nil) or (FUncompressedData.data1 <> nil) or
   if ((FUncompressedData.data0 <> nil) or (FUncompressedData.data1 <> nil) or
     (FUncompressedData.data2 <> nil) or (FUncompressedData.data3 <> nil)) and
     (FUncompressedData.data2 <> nil) or (FUncompressedData.data3 <> nil)) and
@@ -833,46 +940,22 @@ begin
     result := true;
     result := true;
 
 
     if FCompressedData.Size >= MinSerializedSize then
     if FCompressedData.Size >= MinSerializedSize then
-    begin
-      FSavedFilename := GetTempFileName;
-      try
-        FSavedFile := FileManager.CreateFileStream(FSavedFilename,fmCreate);
-        try
-          FCompressedData.Position := 0;
-          FSavedFile.CopyFrom(FCompressedData, FCompressedData.Size);
-          FreeAndNil(FCompressedData);
-        finally
-          FSavedFile.Free;
-        end;
-      except
-        on ex: exception do
-        begin
-          if FileManager.FileExists(FSavedFilename) then FileManager.DeleteFile(FSavedFilename);
-          FSavedFilename := '';
-          result := false;
-        end;
-      end;
-    end;
+      SerializeCompressedData;
   end;
   end;
 end;
 end;
 
 
 destructor TImageDiff.Destroy;
 destructor TImageDiff.Destroy;
 begin
 begin
-  FreeAndnil(FCompressedData);
   ReAllocMem(FUncompressedData.data0,0);
   ReAllocMem(FUncompressedData.data0,0);
   ReAllocMem(FUncompressedData.data1,0);
   ReAllocMem(FUncompressedData.data1,0);
   ReAllocMem(FUncompressedData.data2,0);
   ReAllocMem(FUncompressedData.data2,0);
   ReAllocMem(FUncompressedData.data3,0);
   ReAllocMem(FUncompressedData.data3,0);
-  DiscardFile;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
 function TImageDiff.UsedMemory: int64;
 function TImageDiff.UsedMemory: int64;
 begin
 begin
-  if Assigned(FCompressedData) then
-    result := FCompressedData.Size
-  else
-    result := 0;
+  result := inherited UsedMemory;
   if Assigned(FUncompressedData.data0) then inc(result,FUncompressedData.dataLen);
   if Assigned(FUncompressedData.data0) then inc(result,FUncompressedData.dataLen);
   if Assigned(FUncompressedData.data1) then inc(result,FUncompressedData.dataLen);
   if Assigned(FUncompressedData.data1) then inc(result,FUncompressedData.dataLen);
   if Assigned(FUncompressedData.data2) then inc(result,FUncompressedData.dataLen);
   if Assigned(FUncompressedData.data2) then inc(result,FUncompressedData.dataLen);
@@ -883,13 +966,104 @@ end;
 
 
 constructor TStoredImage.Create(ABitmap: TBGRABitmap);
 constructor TStoredImage.Create(ABitmap: TBGRABitmap);
 begin
 begin
-  inherited Create(nil,ABitmap,rect(0,0,ABitmap.Width,ABitmap.Height));
+  if Assigned(ABitmap) then
+    inherited Create(nil,ABitmap,rect(0,0,ABitmap.Width,ABitmap.Height))
+  else
+    inherited Create(nil,ABitmap,EmptyRect)
 end;
 end;
 
 
 function TStoredImage.GetBitmap: TBGRABitmap;
 function TStoredImage.GetBitmap: TBGRABitmap;
 begin
 begin
   result := TBGRABitmap.Create(SizeAfter.cx, SizeAfter.cy);
   result := TBGRABitmap.Create(SizeAfter.cx, SizeAfter.cy);
-  Apply(result,false);
+  ApplyInPlace(result,false);
+end;
+
+{ TStoredLayer }
+
+function TStoredLayer.GetId: integer;
+begin
+  result := FInfo.Id;
+end;
+
+constructor TStoredLayer.Create(ALayeredImage: TBGRALayeredBitmap;
+  AIndex: integer);
+begin
+  FIndex := AIndex;
+  FInfo := GetLayerInfo(ALayeredImage, AIndex);
+  if ALayeredImage.LayerOriginalGuid[AIndex]<>GUID_NULL then
+  begin
+    FOriginalKnown := ALayeredImage.LayerOriginalKnown[AIndex];
+    FOriginalRenderStatus:= ALayeredImage.LayerOriginalRenderStatus[AIndex];
+
+    if FOriginalKnown then
+      inherited Create(nil)
+    else
+      inherited Create(ALayeredImage.LayerBitmap[AIndex]);
+
+    FOriginalData := TMemoryStream.Create;
+    ALayeredImage.SaveOriginalToStream(ALayeredImage.LayerOriginalGuid[AIndex], FOriginalData);
+    FOriginalMatrix := ALayeredImage.LayerOriginalMatrix[AIndex];
+    FOriginalDraft := ALayeredImage.LayerOriginalRenderStatus[AIndex] in[orsDraft,orsPartialDraft];
+  end else
+  begin
+    inherited Create(ALayeredImage.LayerBitmap[AIndex]);
+    FOriginalData := nil;
+  end;
+end;
+
+procedure TStoredLayer.Restore(ALayeredImage: TBGRALayeredBitmap);
+var
+  tempIdx, idxOrig: Integer;
+begin
+  if Assigned(FOriginalData) then
+  begin
+    FOriginalData.Position:= 0;
+    idxOrig := ALayeredImage.AddOriginalFromStream(FOriginalData, true);
+
+    if FOriginalKnown then
+    begin
+      tempIdx := ALayeredImage.AddLayerFromOriginal(ALayeredImage.Original[idxOrig].Guid, FOriginalMatrix);
+      ALayeredImage.RenderLayerFromOriginal(tempIdx, FOriginalDraft);
+    end else
+    begin
+      tempIdx := ALayeredImage.AddOwnedLayer(GetBitmap);
+      ALayeredImage.LayerOriginalGuid[tempIdx] := ALayeredImage.OriginalGuid[idxOrig];
+      ALayeredImage.LayerOriginalMatrix[tempIdx] := FOriginalMatrix;
+      ALayeredImage.LayerOriginalRenderStatus[tempIdx] := FOriginalRenderStatus;
+    end;
+  end else
+    tempIdx := ALayeredImage.AddOwnedLayer(GetBitmap);
+
+  ApplyLayerInfo(FInfo,ALayeredImage,tempIdx);
+  ALayeredImage.InsertLayer(FIndex,tempIdx);
+end;
+
+procedure TStoredLayer.Replace(ALayeredImage: TBGRALayeredBitmap);
+var
+  idxOrig: Integer;
+begin
+  if Assigned(FOriginalData) then
+  begin
+    FOriginalData.Position:= 0;
+    idxOrig := ALayeredImage.AddOriginalFromStream(FOriginalData, true);
+    if FOriginalKnown then
+    begin
+      ALayeredImage.LayerOriginalGuid[FIndex] := ALayeredImage.OriginalGuid[idxOrig];
+      ALayeredImage.LayerOriginalMatrix[FIndex] := FOriginalMatrix;
+      ALayeredImage.RenderLayerFromOriginal(FIndex, FOriginalDraft);
+    end else
+    begin
+      ALayeredImage.SetLayerBitmap(FIndex,GetBitmap,True);
+      ALayeredImage.LayerOffset[FIndex] := FInfo.Offset;
+      ALayeredImage.LayerOriginalGuid[FIndex] := ALayeredImage.OriginalGuid[idxOrig];
+      ALayeredImage.LayerOriginalMatrix[FIndex] := FOriginalMatrix;
+      ALayeredImage.LayerOriginalRenderStatus[FIndex] := FOriginalRenderStatus;
+    end;
+  end else
+    ALayeredImage.SetLayerBitmap(FIndex,GetBitmap,True);
+  ALayeredImage.RemoveUnusedOriginals;
+
+  ApplyLayerInfo(FInfo,ALayeredImage,FIndex);
 end;
 end;
 
 
 end.
 end.

+ 109 - 82
lazpaint/utool.pas

@@ -45,8 +45,11 @@ type
   protected
   protected
     FManager: TToolManager;
     FManager: TToolManager;
     FLastToolDrawingLayer: TBGRABitmap;
     FLastToolDrawingLayer: TBGRABitmap;
+    FBackupDrawingLayerBounds: TRect;
+    FBackupDrawingLayer: TBGRABitmap;
     function GetAction: TLayerAction; virtual;
     function GetAction: TLayerAction; virtual;
     function GetIsSelectingTool: boolean; virtual; abstract;
     function GetIsSelectingTool: boolean; virtual; abstract;
+    function FixSelectionTransform: boolean; virtual;
     function DoToolDown(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF; rightBtn: boolean): TRect; virtual;
     function DoToolDown(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF; rightBtn: boolean): TRect; virtual;
     function DoToolMove(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF): TRect; virtual;
     function DoToolMove(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF): TRect; virtual;
     procedure DoToolMoveAfter(pt: TPoint; ptF: TPointF); virtual;
     procedure DoToolMoveAfter(pt: TPoint; ptF: TPointF); virtual;
@@ -54,6 +57,7 @@ type
     procedure OnTryStop(sender: TCustomLayerAction); virtual;
     procedure OnTryStop(sender: TCustomLayerAction); virtual;
     function SelectionMaxPointDistance: single;
     function SelectionMaxPointDistance: single;
     function GetStatusText: string; virtual;
     function GetStatusText: string; virtual;
+    function DoGetToolDrawingLayer: TBGRABitmap; virtual;
   public
   public
     ToolUpdateNeeded: boolean;
     ToolUpdateNeeded: boolean;
     Cursor: TCursor;
     Cursor: TCursor;
@@ -79,7 +83,7 @@ type
     function ToolProvideCopy: boolean; virtual;
     function ToolProvideCopy: boolean; virtual;
     function ToolProvideCut: boolean; virtual;
     function ToolProvideCut: boolean; virtual;
     function ToolProvidePaste: boolean; virtual;
     function ToolProvidePaste: boolean; virtual;
-    function GetToolDrawingLayer: TBGRABitmap; virtual;
+    function GetToolDrawingLayer: TBGRABitmap;
     procedure RestoreBackupDrawingLayer;
     procedure RestoreBackupDrawingLayer;
     function GetBackupLayerIfExists: TBGRABitmap;
     function GetBackupLayerIfExists: TBGRABitmap;
     function Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth, VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; virtual;
     function Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth, VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; virtual;
@@ -91,24 +95,13 @@ type
     property StatusText: string read GetStatusText;
     property StatusText: string read GetStatusText;
   end;
   end;
 
 
-  { TGenericTransformTool }
-
-  TGenericTransformTool = class(TGenericTool)
-  protected
-    function GetKeepTransformOnDestroy: boolean; virtual; abstract;
-    procedure SetKeepTransformOnDestroy(AValue: boolean); virtual; abstract;
-  public
-    property KeepTransformOnDestroy: boolean read GetKeepTransformOnDestroy write SetKeepTransformOnDestroy;
-  end;
-
   { TReadonlyTool }
   { TReadonlyTool }
 
 
   TReadonlyTool = class(TGenericTool)
   TReadonlyTool = class(TGenericTool)
   protected
   protected
     function GetAction: TLayerAction; override;
     function GetAction: TLayerAction; override;
     function GetIsSelectingTool: boolean; override;
     function GetIsSelectingTool: boolean; override;
-  public
-    function GetToolDrawingLayer: TBGRABitmap; override;
+    function DoGetToolDrawingLayer: TBGRABitmap; override;
   end;
   end;
 
 
   TToolClass = class of TGenericTool;
   TToolClass = class of TGenericTool;
@@ -284,7 +277,8 @@ function ToolPopupMessageToStr(AMessage :TToolPopupMessage): string;
 
 
 implementation
 implementation
 
 
-uses Types, ugraph, uscaledpi, LazPaintType, UCursors, BGRATextFX, ULoading, uresourcestrings;
+uses Types, ugraph, uscaledpi, LazPaintType, UCursors, BGRATextFX, ULoading, uresourcestrings,
+  BGRATransform;
 
 
 function StrToPaintToolType(const s: ansistring): TPaintToolType;
 function StrToPaintToolType(const s: ansistring): TPaintToolType;
 var pt: TPaintToolType;
 var pt: TPaintToolType;
@@ -361,10 +355,10 @@ begin
   result := false;
   result := false;
 end;
 end;
 
 
-function TReadonlyTool.GetToolDrawingLayer: TBGRABitmap;
+function TReadonlyTool.DoGetToolDrawingLayer: TBGRABitmap;
 begin
 begin
-  if Manager.Image.SelectionEmpty or not assigned(Manager.Image.SelectionLayerReadonly) then
-    Result:= Manager.Image.SelectedImageLayerReadOnly
+  if Manager.Image.SelectionMaskEmpty or not assigned(Manager.Image.SelectionLayerReadonly) then
+    Result:= Manager.Image.CurrentLayerReadOnly
   else
   else
     Result:= Manager.Image.SelectionLayerReadonly;
     Result:= Manager.Image.SelectionLayerReadonly;
 end;
 end;
@@ -387,8 +381,8 @@ begin
   if IsSelectingTool or not Assigned(Manager.Image) then
   if IsSelectingTool or not Assigned(Manager.Image) then
     result := Point(0,0)
     result := Point(0,0)
   else
   else
-    if GetToolDrawingLayer = Manager.Image.SelectedImageLayerReadOnly then
-      result := Manager.Image.LayerOffset[Manager.Image.currentImageLayerIndex]
+    if GetToolDrawingLayer = Manager.Image.CurrentLayerReadOnly then
+      result := Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex]
     else
     else
       result := Point(0,0);
       result := Point(0,0);
 end;
 end;
@@ -398,16 +392,52 @@ begin
   result := '';
   result := '';
 end;
 end;
 
 
+function TGenericTool.DoGetToolDrawingLayer: TBGRABitmap;
+begin
+  if Action = nil then
+    result := nil
+  else if IsSelectingTool then
+  begin
+    Action.QuerySelection;
+    result := Action.CurrentSelection;
+    if result = nil then
+      raise exception.Create('Selection not created');
+  end
+  else
+    result := Action.DrawingLayer;
+end;
+
 function TGenericTool.GetAction: TLayerAction;
 function TGenericTool.GetAction: TLayerAction;
+var
+  layer: TBGRABitmap;
 begin
 begin
   if not Assigned(FAction) then
   if not Assigned(FAction) then
   begin
   begin
-    FAction := TLayerAction.Create(Manager.Image);
+    FAction := Manager.Image.CreateAction(not IsSelectingTool And Manager.Image.SelectionMaskEmpty);
     FAction.OnTryStop := @OnTryStop;
     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;
   end;
   result := FAction;
   result := FAction;
 end;
 end;
 
 
+function TGenericTool.FixSelectionTransform: boolean;
+begin
+  result:= true;
+end;
+
 function TGenericTool.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
 function TGenericTool.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF; rightBtn: boolean): TRect;
   ptF: TPointF; rightBtn: boolean): TRect;
 begin
 begin
@@ -517,7 +547,6 @@ end;
 function TGenericTool.ToolDown(X, Y: single; rightBtn: boolean): TRect;
 function TGenericTool.ToolDown(X, Y: single; rightBtn: boolean): TRect;
 var
 var
   toolDest: TBGRABitmap;
   toolDest: TBGRABitmap;
-  pt: TPoint;
   ptF: TPointF;
   ptF: TPointF;
 begin
 begin
   result := EmptyRect;
   result := EmptyRect;
@@ -526,23 +555,24 @@ begin
   toolDest.JoinStyle := Manager.ToolJoinStyle;
   toolDest.JoinStyle := Manager.ToolJoinStyle;
   toolDest.LineCap := Manager.ToolLineCap;
   toolDest.LineCap := Manager.ToolLineCap;
   toolDest.PenStyle := Manager.ToolPenStyle;
   toolDest.PenStyle := Manager.ToolPenStyle;
-  if toolDest = Manager.Image.SelectedImageLayerReadOnly then
-  begin
-    x -= LayerOffset.x;
-    y -= LayerOffset.y;
-  end;
-  pt := Point(round(x),round(y));
   ptF := PointF(x,y);
   ptF := PointF(x,y);
-  result := DoToolDown(toolDest,pt,ptF,rightBtn);
+  if toolDest = Manager.Image.CurrentLayerReadOnly then
+  begin
+    ptF.x -= LayerOffset.x;
+    ptF.y -= LayerOffset.y;
+  end else if FixSelectionTransform and ((toolDest = Manager.Image.SelectionMaskReadonly)
+    or (toolDest = Manager.Image.SelectionLayerReadonly)) and
+      IsAffineMatrixInversible(Manager.Image.SelectionTransform) then
+    ptF := AffineMatrixInverse(Manager.Image.SelectionTransform)*ptF;
+
+  result := DoToolDown(toolDest,ptF.Round,ptF,rightBtn);
 end;
 end;
 
 
 function TGenericTool.ToolMove(X, Y: single): TRect;
 function TGenericTool.ToolMove(X, Y: single): TRect;
 var
 var
   toolDest: TBGRABitmap;
   toolDest: TBGRABitmap;
-  pt: TPoint;
   ptF: TPointF;
   ptF: TPointF;
 begin
 begin
-  pt := Point(round(x),round(y));
   ptF := PointF(x,y);
   ptF := PointF(x,y);
   Manager.ToolCurrentCursorPos := ptF;
   Manager.ToolCurrentCursorPos := ptF;
   result := EmptyRect;
   result := EmptyRect;
@@ -551,14 +581,16 @@ begin
   toolDest.JoinStyle := Manager.ToolJoinStyle;
   toolDest.JoinStyle := Manager.ToolJoinStyle;
   toolDest.LineCap := Manager.ToolLineCap;
   toolDest.LineCap := Manager.ToolLineCap;
   toolDest.PenStyle := Manager.ToolPenStyle;
   toolDest.PenStyle := Manager.ToolPenStyle;
-  if toolDest = Manager.Image.SelectedImageLayerReadOnly then
+  if toolDest = Manager.Image.CurrentLayerReadOnly then
   begin
   begin
-    x -= LayerOffset.x;
-    y -= LayerOffset.y;
-    pt := Point(round(x),round(y));
-    ptF := PointF(x,y);
-  end;
-  result := DoToolMove(toolDest,pt,ptF);
+    ptF.x -= LayerOffset.x;
+    ptF.y -= LayerOffset.y;
+  end else if FixSelectionTransform and ((toolDest = Manager.Image.SelectionMaskReadonly)
+    or (toolDest = Manager.Image.SelectionLayerReadonly)) and
+      IsAffineMatrixInversible(Manager.Image.SelectionTransform) then
+    ptF := AffineMatrixInverse(Manager.Image.SelectionTransform)*ptF;
+
+  result := DoToolMove(toolDest,ptF.Round,ptF);
 end;
 end;
 
 
 procedure TGenericTool.ToolMoveAfter(X, Y: single);
 procedure TGenericTool.ToolMoveAfter(X, Y: single);
@@ -632,32 +664,35 @@ end;
 
 
 function TGenericTool.GetToolDrawingLayer: TBGRABitmap;
 function TGenericTool.GetToolDrawingLayer: TBGRABitmap;
 begin
 begin
-  if Action = nil then
-  begin
-    result := nil;
-  end else
-  if IsSelectingTool then
-  begin
-    Action.QuerySelection;
-    result := Action.CurrentSelection;
-    if result = nil then
-      raise exception.Create('Selection not created');
-  end
-  else
-  begin
-    result := Action.DrawingLayer;
-  end;
+  result := DoGetToolDrawingLayer;
   FLastToolDrawingLayer := result;
   FLastToolDrawingLayer := result;
 end;
 end;
 
 
 procedure TGenericTool.RestoreBackupDrawingLayer;
 procedure TGenericTool.RestoreBackupDrawingLayer;
+var
+  layer: TBGRABitmap;
 begin
 begin
   if Assigned(FAction) then
   if Assigned(FAction) then
   begin
   begin
-    if IsSelectingTool then
-      Action.RestoreSelection
-    else
-      Action.RestoreDrawingLayer;
+    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;
   end;
   end;
 end;
 end;
 
 
@@ -741,12 +776,17 @@ procedure TToolManager.NotifyImageOrSelectionChanged(ALayer: TBGRABitmap; ARect:
 begin
 begin
   if (CurrentTool <> nil) and not IsRectEmpty(ARect) then
   if (CurrentTool <> nil) and not IsRectEmpty(ARect) then
   begin
   begin
-    if CurrentTool.IsSelectingTool then
-      Image.SelectionMayChange(ARect)
-    else if ALayer <> nil then
-      Image.LayerMayChange(ALayer, ARect)
-    else
-      Image.ImageMayChange(AddLayerOffset(ARect))
+    if Assigned(CurrentTool.FAction) then
+      if not IsOnlyRenderChange(ARect) then
+        CurrentTool.FAction.NotifyChange(ALayer, ARect);
+
+    if ALayer = nil then
+    begin
+      if ALayer = Image.CurrentLayerReadOnly then
+        Image.ImageMayChange(AddLayerOffset(ARect))
+      else
+        Image.LayerMayChange(ALayer, ARect);
+    end
   end;
   end;
 end;
 end;
 
 
@@ -1031,30 +1071,15 @@ end;
 procedure TToolManager.InternalSetCurrentToolType(tool: TPaintToolType);
 procedure TToolManager.InternalSetCurrentToolType(tool: TPaintToolType);
 var showPenwidth, showShape, showLineCap, showJoinStyle, showSplineStyle, showEraserOption, showTolerance, showGradient, showDeformation,
 var showPenwidth, showShape, showLineCap, showJoinStyle, showSplineStyle, showEraserOption, showTolerance, showGradient, showDeformation,
     showText, showPhong, showAltitude, showPerspective, showColor, showTexture, showBrush: boolean;
     showText, showPhong, showAltitude, showPerspective, showColor, showTexture, showBrush: boolean;
-    newTool: TGenericTool;
 begin
 begin
   if (tool <> FCurrentToolType) or (FCurrentTool=nil) then
   if (tool <> FCurrentToolType) or (FCurrentTool=nil) then
   begin
   begin
-    if Assigned(FCurrentTool) and (FCurrentTool is TGenericTransformTool) then
-    begin
-      if PaintTools[tool] <> nil then
-        newTool := PaintTools[tool].Create(self)
-      else
-        newTool := nil;
-
-      if Assigned(newTool) and (newTool is TGenericTransformTool) then
-        TGenericTransformTool(FCurrentTool).KeepTransformOnDestroy := true;
-      FreeAndNil(FCurrentTool);
+    FreeAndNil(FCurrentTool);
+    if PaintTools[tool] <> nil then
+      FCurrentTool := PaintTools[tool].Create(self)
+    else
+      FCurrentTool := nil;
 
 
-      FCurrentTool := newTool;
-    end else
-    begin
-      FreeAndNil(FCurrentTool);
-      if PaintTools[tool] <> nil then
-        FCurrentTool := PaintTools[tool].Create(self)
-      else
-        FCurrentTool := nil;
-    end;
     FCurrentToolType:= tool;
     FCurrentToolType:= tool;
   end;
   end;
 
 
@@ -1252,6 +1277,7 @@ function TToolManager.ToolDown(X, Y: single; ARightBtn: boolean;
 var changed: TRect;
 var changed: TRect;
 begin
 begin
   SetPressure(APressure);
   SetPressure(APressure);
+  Image.DraftOriginal := true;
   if ToolCanBeUsed then
   if ToolCanBeUsed then
     changed := currentTool.ToolDown(X,Y,ARightBtn)
     changed := currentTool.ToolDown(X,Y,ARightBtn)
   else
   else
@@ -1377,6 +1403,7 @@ end;
 function TToolManager.ToolUp: boolean;
 function TToolManager.ToolUp: boolean;
 var changed: TRect;
 var changed: TRect;
 begin
 begin
+  Image.DraftOriginal := false;
   if ToolCanBeUsed then
   if ToolCanBeUsed then
     changed := currentTool.ToolUp
     changed := currentTool.ToolUp
   else
   else

+ 39 - 67
lazpaint/utoolbasic.pas

@@ -16,6 +16,7 @@ type
   protected
   protected
     handMoving: boolean;
     handMoving: boolean;
     handOrigin: TPoint;
     handOrigin: TPoint;
+    function FixSelectionTransform: boolean; override;
     function DoToolDown({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF;
     function DoToolDown({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF;
       {%H-}rightBtn: boolean): TRect; override;
       {%H-}rightBtn: boolean): TRect; override;
     function DoToolMove({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF): TRect; override;
     function DoToolMove({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF): TRect; override;
@@ -46,7 +47,6 @@ type
     penOrigin: TPointF;
     penOrigin: TPointF;
     penColor: TBGRAPixel;
     penColor: TBGRAPixel;
     snapToPixel: boolean;
     snapToPixel: boolean;
-    function GetAction: TLayerAction; override;
     function GetIsSelectingTool: boolean; override;
     function GetIsSelectingTool: boolean; override;
     function StartDrawing(toolDest: TBGRABitmap; ptF: TPointF; rightBtn: boolean): TRect; virtual;
     function StartDrawing(toolDest: TBGRABitmap; ptF: TPointF; rightBtn: boolean): TRect; virtual;
     function ContinueDrawing(toolDest: TBGRABitmap; originF, destF: TPointF): TRect; virtual;
     function ContinueDrawing(toolDest: TBGRABitmap; originF, destF: TPointF): TRect; virtual;
@@ -80,7 +80,6 @@ type
     swapedColor: boolean;
     swapedColor: boolean;
     rectDrawing,afterRectDrawing: boolean;
     rectDrawing,afterRectDrawing: boolean;
     rectOrigin, rectDest: TPointF;
     rectOrigin, rectDest: TPointF;
-    previousRect: TRect;
     rectMovingPoint,rectMovingCenterPoint: boolean;
     rectMovingPoint,rectMovingCenterPoint: boolean;
     rectMovingPointValueDiff: TPointF;
     rectMovingPointValueDiff: TPointF;
     rectMovingPointClick: TPointF;
     rectMovingPointClick: TPointF;
@@ -88,7 +87,6 @@ type
     rectMovingX,rectMovingY: PSingle;
     rectMovingX,rectMovingY: PSingle;
     lastMousePos: TPointF;
     lastMousePos: TPointF;
     squareConstraint: boolean;
     squareConstraint: boolean;
-    function GetAction: TLayerAction; override;
     function GetFillColor: TBGRAPixel; virtual;
     function GetFillColor: TBGRAPixel; virtual;
     function GetPenColor: TBGRAPixel; virtual;
     function GetPenColor: TBGRAPixel; virtual;
     function GetIsSelectingTool: boolean; override;
     function GetIsSelectingTool: boolean; override;
@@ -175,6 +173,7 @@ begin
    if Manager.ToolOptionDrawShape then
    if Manager.ToolOptionDrawShape then
    begin
    begin
      result := GetShapeBounds([PointF(rectOrigin.x-rx,rectOrigin.y-ry),PointF(rectOrigin.x+rx,rectOrigin.y+ry)],Manager.ToolPenWidth);
      result := GetShapeBounds([PointF(rectOrigin.x-rx,rectOrigin.y-ry),PointF(rectOrigin.x+rx,rectOrigin.y+ry)],Manager.ToolPenWidth);
+     toolDest.ClipRect := result;
      if Manager.ToolPenStyle = psSolid then
      if Manager.ToolPenStyle = psSolid then
      begin
      begin
        if not Manager.ToolOptionFillShape and (Manager.GetToolTextureAfterAlpha <> nil) then
        if not Manager.ToolOptionFillShape and (Manager.GetToolTextureAfterAlpha <> nil) then
@@ -197,6 +196,7 @@ begin
    end else
    end else
    begin
    begin
      result := GetShapeBounds([PointF(rectOrigin.x-rx,rectOrigin.y-ry),PointF(rectOrigin.x+rx,rectOrigin.y+ry)],1);
      result := GetShapeBounds([PointF(rectOrigin.x-rx,rectOrigin.y-ry),PointF(rectOrigin.x+rx,rectOrigin.y+ry)],1);
+     toolDest.ClipRect := result;
    end;
    end;
    if Manager.ToolOptionFillShape then
    if Manager.ToolOptionFillShape then
      if (rx>0) and (ry>0) then
      if (rx>0) and (ry>0) then
@@ -208,6 +208,7 @@ begin
      end;
      end;
    multi.Draw(toolDest);
    multi.Draw(toolDest);
    multi.Free;
    multi.Free;
+   toolDest.NoClip;
 end;
 end;
 
 
 function TToolEllipse.RoundCoordinate(ptF: TPointF): TPointF;
 function TToolEllipse.RoundCoordinate(ptF: TPointF): TPointF;
@@ -257,21 +258,27 @@ begin
     if rectDest.X > rectOrigin.X then sx := 1 else sx := -1;
     if rectDest.X > rectOrigin.X then sx := 1 else sx := -1;
     if rectDest.Y > rectOrigin.Y then sy := 1 else sy := -1;
     if rectDest.Y > rectOrigin.Y then sy := 1 else sy := -1;
     result := GetShapeBounds([PointF(rectOrigin.x,rectOrigin.y),PointF(rectDest.x,rectDest.y)],1);
     result := GetShapeBounds([PointF(rectOrigin.x,rectOrigin.y),PointF(rectDest.x,rectDest.y)],1);
+    toolDest.ClipRect := result;
     if Manager.GetToolTextureAfterAlpha <> nil then
     if Manager.GetToolTextureAfterAlpha <> nil then
       toolDest.FillRectAntialias(rectOrigin.X-0.5*sx,rectOrigin.Y-0.5*sy,rectDest.X+0.5*sx,rectDest.Y+0.5*sy,Manager.GetToolTextureAfterAlpha) else
       toolDest.FillRectAntialias(rectOrigin.X-0.5*sx,rectOrigin.Y-0.5*sy,rectDest.X+0.5*sx,rectDest.Y+0.5*sy,Manager.GetToolTextureAfterAlpha) else
       toolDest.FillRectAntialias(rectOrigin.X-0.5*sx,rectOrigin.Y-0.5*sy,rectDest.X+0.5*sx,rectDest.Y+0.5*sy,fillColor);
       toolDest.FillRectAntialias(rectOrigin.X-0.5*sx,rectOrigin.Y-0.5*sy,rectDest.X+0.5*sx,rectDest.Y+0.5*sy,fillColor);
+    toolDest.NoClip;
   end else
   end else
   if Manager.ToolOptionDrawShape and not Manager.ToolOptionFillShape then
   if Manager.ToolOptionDrawShape and not Manager.ToolOptionFillShape then
   begin
   begin
     result := GetShapeBounds([PointF(rectOrigin.x,rectOrigin.y),PointF(rectDest.x,rectDest.y)],Manager.ToolPenWidth);
     result := GetShapeBounds([PointF(rectOrigin.x,rectOrigin.y),PointF(rectDest.x,rectDest.y)],Manager.ToolPenWidth);
+    toolDest.ClipRect := result;
     if Manager.GetToolTextureAfterAlpha <> nil then
     if Manager.GetToolTextureAfterAlpha <> nil then
       toolDest.RectangleAntialias(rectOrigin.X,rectOrigin.Y,rectDest.X,rectDest.Y,Manager.GetToolTextureAfterAlpha,Manager.ToolPenWidth) else
       toolDest.RectangleAntialias(rectOrigin.X,rectOrigin.Y,rectDest.X,rectDest.Y,Manager.GetToolTextureAfterAlpha,Manager.ToolPenWidth) else
-      toolDest.RectangleAntialias(rectOrigin.X,rectOrigin.Y,rectDest.X,rectDest.Y,penColor,Manager.ToolPenWidth)
+      toolDest.RectangleAntialias(rectOrigin.X,rectOrigin.Y,rectDest.X,rectDest.Y,penColor,Manager.ToolPenWidth);
+    toolDest.NoClip;
   end else
   end else
   if Manager.ToolOptionDrawShape and Manager.ToolOptionFillShape then
   if Manager.ToolOptionDrawShape and Manager.ToolOptionFillShape then
   begin
   begin
     result := GetShapeBounds([PointF(rectOrigin.x,rectOrigin.y),PointF(rectDest.x,rectDest.y)],Manager.ToolPenWidth);
     result := GetShapeBounds([PointF(rectOrigin.x,rectOrigin.y),PointF(rectDest.x,rectDest.y)],Manager.ToolPenWidth);
+    toolDest.ClipRect := result;
     toolDest.RectangleAntialias(rectOrigin.X,rectOrigin.Y,rectDest.X,rectDest.Y,penColor,Manager.ToolPenWidth,fillColor);
     toolDest.RectangleAntialias(rectOrigin.X,rectOrigin.Y,rectDest.X,rectDest.Y,penColor,Manager.ToolPenWidth,fillColor);
+    toolDest.NoClip;
   end else
   end else
     result := EmptyRect;
     result := EmptyRect;
 end;
 end;
@@ -293,12 +300,6 @@ end;
 
 
 { TToolRectangular }
 { TToolRectangular }
 
 
-function TToolRectangular.GetAction: TLayerAction;
-begin
-  Result:=inherited GetAction;
-  Result.AllChangesNotified := true;
-end;
-
 function TToolRectangular.GetFillColor: TBGRAPixel;
 function TToolRectangular.GetFillColor: TBGRAPixel;
 begin
 begin
   if swapedColor then
   if swapedColor then
@@ -429,13 +430,12 @@ begin
     rectOrigin := RoundCoordinate(ptF);
     rectOrigin := RoundCoordinate(ptF);
     rectDest := RoundCoordinate(ptF);
     rectDest := RoundCoordinate(ptF);
     PrepareDrawing(rightBtn);
     PrepareDrawing(rightBtn);
-    previousRect := EmptyRect;
   end;
   end;
 end;
 end;
 
 
 function TToolRectangular.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
 function TToolRectangular.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF): TRect;
   ptF: TPointF): TRect;
-var currentRect: TRect;
+var
   delta: TPointF;
   delta: TPointF;
 begin
 begin
   result := EmptyRect;
   result := EmptyRect;
@@ -457,19 +457,13 @@ begin
       rectDest += delta;
       rectDest += delta;
     end;
     end;
     ApplyConstraint(rectMovingX,rectMovingY);
     ApplyConstraint(rectMovingX,rectMovingY);
-    currentRect := FinishShape(toolDest);
-    Action.NotifyChange(toolDest, currentRect);
-    result := RectUnion(previousRect,currentRect);
-    previousRect := currentRect;
+    result := FinishShape(toolDest);
   end else
   end else
   if rectDrawing and (rectDest <> RoundCoordinate(ptF)) then
   if rectDrawing and (rectDest <> RoundCoordinate(ptF)) then
   begin
   begin
     rectDest := RoundCoordinate(ptF);
     rectDest := RoundCoordinate(ptF);
     ApplyConstraint(@rectDest.X,@rectDest.Y);
     ApplyConstraint(@rectDest.X,@rectDest.Y);
-    currentRect := UpdateShape(toolDest);
-    Action.NotifyChange(toolDest, currentRect);
-    result := RectUnion(previousRect,currentRect);
-    previousRect := currentRect;
+    result := UpdateShape(toolDest);
   end;
   end;
   UpdateCursor(ptF);
   UpdateCursor(ptF);
 end;
 end;
@@ -480,7 +474,6 @@ begin
 end;
 end;
 
 
 function TToolRectangular.ToolUp: TRect;
 function TToolRectangular.ToolUp: TRect;
-var currentRect: TRect;
 begin
 begin
   if rectMovingPoint then
   if rectMovingPoint then
   begin
   begin
@@ -492,17 +485,9 @@ begin
   end else
   end else
   if rectDrawing then
   if rectDrawing then
   begin
   begin
-    currentRect := ValidateDrawing;
-    if IsOnlyRenderChange(previousRect) then
-    begin
-      if IsRectEmpty(currentRect) then
-        result := OnlyRenderChange
-      else
-        result := currentRect;
-    end
-    else
-      result := RectUnion(previousRect,currentRect);
-    previousRect := currentRect;
+    result := ValidateDrawing;
+    if IsRectEmpty(result) then
+      result := OnlyRenderChange;
     afterRectDrawing := true;
     afterRectDrawing := true;
     UpdateCursor(lastMousePos);
     UpdateCursor(lastMousePos);
   end
   end
@@ -575,7 +560,6 @@ begin
   begin
   begin
     rectDrawing := false;
     rectDrawing := false;
     result := FinishShape(GetToolDrawingLayer);
     result := FinishShape(GetToolDrawingLayer);
-    Action.NotifyChange(GetToolDrawingLayer, result);
     afterRectDrawing:= true;
     afterRectDrawing:= true;
   end else
   end else
     result := EmptyRect;
     result := EmptyRect;
@@ -621,22 +605,12 @@ begin
 end;
 end;
 
 
 function TToolRectangular.DoToolUpdate(toolDest: TBGRABitmap): TRect;
 function TToolRectangular.DoToolUpdate(toolDest: TBGRABitmap): TRect;
-var currentRect: TRect;
 begin
 begin
   if rectDrawing then
   if rectDrawing then
-  begin
-    currentRect := UpdateShape(toolDest);
-    Action.NotifyChange(toolDest, currentRect);
-    result := RectUnion(previousRect,currentRect);
-    previousRect := currentRect;
-  end else
+    result := UpdateShape(toolDest)
+  else
   if afterRectDrawing then
   if afterRectDrawing then
-  begin
-    currentRect := FinishShape(toolDest);
-    Action.NotifyChange(toolDest, currentRect);
-    result := RectUnion(previousRect,currentRect);
-    previousRect := currentRect;
-  end
+    result := FinishShape(toolDest)
   else
   else
     result := EmptyRect;
     result := EmptyRect;
 end;
 end;
@@ -717,10 +691,9 @@ end;
 constructor TToolRectangular.Create(AManager: TToolManager);
 constructor TToolRectangular.Create(AManager: TToolManager);
 begin
 begin
   inherited Create(AManager);
   inherited Create(AManager);
-  Action.AllChangesNotified:= true;
+  Action.ChangeBoundsNotified:= true;
   rectMovingPoint := false;
   rectMovingPoint := false;
   afterRectDrawing:= false;
   afterRectDrawing:= false;
-  previousRect := EmptyRect;
   squareConstraint := false;
   squareConstraint := false;
 end;
 end;
 
 
@@ -766,7 +739,6 @@ begin
         Manager.ToolPenWidth,True);
         Manager.ToolPenWidth,True);
       mask.ScanOffset := Point(-result.left,-result.top);
       mask.ScanOffset := Point(-result.left,-result.top);
       areaCopy.ScanOffset := Point(-result.left,-result.top);
       areaCopy.ScanOffset := Point(-result.left,-result.top);
-      toolDest.ScanOffset := Point(0,0);
       toolDest.CrossFade(result,toolDest,areaCopy,mask,dmSet);
       toolDest.CrossFade(result,toolDest,areaCopy,mask,dmSet);
       mask.Free;
       mask.Free;
       areaCopy.Free;
       areaCopy.Free;
@@ -782,11 +754,12 @@ begin
     end
     end
     else
     else
     begin
     begin
-      toolDest.EraseLineAntialias(ptF.X,ptF.Y,ptF.X,ptF.Y,round(Manager.ToolEraserAlpha*Manager.ToolPressure),Manager.ToolPenWidth,True);
       result := GetShapeBounds([ptF],Manager.ToolPenWidth);
       result := GetShapeBounds([ptF],Manager.ToolPenWidth);
+      toolDest.ClipRect := result;
+      toolDest.EraseLineAntialias(ptF.X,ptF.Y,ptF.X,ptF.Y,round(Manager.ToolEraserAlpha*Manager.ToolPressure),Manager.ToolPenWidth,True);
+      toolDest.NoClip;
     end;
     end;
   end;
   end;
-  Action.NotifyChange(toolDest, result);
 end;
 end;
 
 
 function TToolErase.ContinueDrawing(toolDest: TBGRABitmap; originF,
 function TToolErase.ContinueDrawing(toolDest: TBGRABitmap; originF,
@@ -807,7 +780,6 @@ begin
         Manager.ToolPenWidth,false);
         Manager.ToolPenWidth,false);
       mask.ScanOffset := Point(-result.left,-result.top);
       mask.ScanOffset := Point(-result.left,-result.top);
       areaCopy.ScanOffset := Point(-result.left,-result.top);
       areaCopy.ScanOffset := Point(-result.left,-result.top);
-      toolDest.ScanOffset := Point(0,0);
       toolDest.CrossFade(result,toolDest,areaCopy,mask,dmSet);
       toolDest.CrossFade(result,toolDest,areaCopy,mask,dmSet);
       mask.Free;
       mask.Free;
       areaCopy.Free;
       areaCopy.Free;
@@ -822,17 +794,10 @@ begin
     toolDest.EraseLineAntialias(destF.X,destF.Y,originF.X,originF.Y,round(Manager.ToolEraserAlpha*Manager.ToolPressure),Manager.ToolPenWidth,False);
     toolDest.EraseLineAntialias(destF.X,destF.Y,originF.X,originF.Y,round(Manager.ToolEraserAlpha*Manager.ToolPressure),Manager.ToolPenWidth,False);
     result := GetShapeBounds([destF,originF],Manager.ToolPenWidth);
     result := GetShapeBounds([destF,originF],Manager.ToolPenWidth);
   end;
   end;
-  Action.NotifyChange(toolDest, result);
 end;
 end;
 
 
 { TToolPen }
 { TToolPen }
 
 
-function TToolPen.GetAction: TLayerAction;
-begin
-  Result:=inherited GetAction;
-  result.AllChangesNotified:= true;
-end;
-
 function TToolPen.GetIsSelectingTool: boolean;
 function TToolPen.GetIsSelectingTool: boolean;
 begin
 begin
   Result:= false;
   Result:= false;
@@ -851,13 +816,14 @@ begin
     result := rect(ix,iy,ix+1,iy+1);
     result := rect(ix,iy,ix+1,iy+1);
   end else
   end else
   begin
   begin
+    result := GetShapeBounds([ptF],Manager.ToolPenWidth);
+    toolDest.ClipRect := result;
      if Manager.GetToolTextureAfterAlpha <> nil then
      if Manager.GetToolTextureAfterAlpha <> nil then
        toolDest.FillEllipseAntialias(ptF.X,ptF.Y,Manager.ToolPenWidth/2,Manager.ToolPenWidth/2,Manager.GetToolTextureAfterAlpha)
        toolDest.FillEllipseAntialias(ptF.X,ptF.Y,Manager.ToolPenWidth/2,Manager.ToolPenWidth/2,Manager.GetToolTextureAfterAlpha)
      else
      else
        toolDest.FillEllipseAntialias(ptF.X,ptF.Y,Manager.ToolPenWidth/2,Manager.ToolPenWidth/2,Manager.ApplyPressure(penColor));
        toolDest.FillEllipseAntialias(ptF.X,ptF.Y,Manager.ToolPenWidth/2,Manager.ToolPenWidth/2,Manager.ApplyPressure(penColor));
-     result := GetShapeBounds([ptF],Manager.ToolPenWidth);
+     toolDest.NoClip;
   end;
   end;
-  Action.NotifyChange(toolDest, result);
 end;
 end;
 
 
 function TToolPen.ContinueDrawing(toolDest: TBGRABitmap; originF, destF: TPointF): TRect;
 function TToolPen.ContinueDrawing(toolDest: TBGRABitmap; originF, destF: TPointF): TRect;
@@ -868,13 +834,14 @@ begin
     result := GetShapeBounds([destF,originF],1);
     result := GetShapeBounds([destF,originF],1);
   end else
   end else
   begin
   begin
-     if Manager.GetToolTextureAfterAlpha <> nil then
-       toolDest.DrawLineAntialias(destF.X,destF.Y,originF.X,originF.Y,Manager.GetToolTextureAfterAlpha,Manager.ToolPenWidth,False)
-     else
-       toolDest.DrawLineAntialias(destF.X,destF.Y,originF.X,originF.Y,Manager.ApplyPressure(penColor),Manager.ToolPenWidth,False);
-     result := GetShapeBounds([destF,originF],Manager.ToolPenWidth+1);
+    result := GetShapeBounds([destF,originF],Manager.ToolPenWidth+1);
+    toolDest.ClipRect := result;
+    if Manager.GetToolTextureAfterAlpha <> nil then
+      toolDest.DrawLineAntialias(destF.X,destF.Y,originF.X,originF.Y,Manager.GetToolTextureAfterAlpha,Manager.ToolPenWidth,False)
+    else
+      toolDest.DrawLineAntialias(destF.X,destF.Y,originF.X,originF.Y,Manager.ApplyPressure(penColor),Manager.ToolPenWidth,False);
+    toolDest.NoClip;
   end;
   end;
-  Action.NotifyChange(toolDest, result);
 end;
 end;
 
 
 function TToolPen.DoToolDown(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF;
 function TToolPen.DoToolDown(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF;
@@ -986,6 +953,11 @@ end;
 
 
 { TToolHand }
 { TToolHand }
 
 
+function TToolHand.FixSelectionTransform: boolean;
+begin
+  Result:= false;
+end;
+
 function TToolHand.DoToolDown(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF;
 function TToolHand.DoToolDown(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF;
   rightBtn: boolean): TRect;
   rightBtn: boolean): TRect;
 begin
 begin

+ 1 - 2
lazpaint/utoolbrush.pas

@@ -77,7 +77,7 @@ begin
   if definingSource then
   if definingSource then
   begin
   begin
     sourcePosition := Point(round(x),round(y));
     sourcePosition := Point(round(x),round(y));
-    sourceLayerId := Manager.Image.LayerId[Manager.Image.currentImageLayerIndex];
+    sourceLayerId := Manager.Image.LayerId[Manager.Image.CurrentLayerIndex];
     sourcePositionRelative:= false;
     sourcePositionRelative:= false;
     result := OnlyRenderChange;
     result := OnlyRenderChange;
   end else
   end else
@@ -270,7 +270,6 @@ begin
       end;
       end;
     end;
     end;
   end;
   end;
-  Action.NotifyChange(toolDest,result);
 end;
 end;
 
 
 function TToolGenericBrush.GetBrushAlpha(AAlpha: byte): byte;
 function TToolGenericBrush.GetBrushAlpha(AAlpha: byte): byte;

+ 30 - 31
lazpaint/utooldeformationgrid.pas

@@ -44,7 +44,7 @@ type
   TToolTextureMapping = class(TGenericTool)
   TToolTextureMapping = class(TGenericTool)
   private
   private
     class var FHintShowed: boolean;
     class var FHintShowed: boolean;
-    FCurrentBounds,FMergedBounds: TRect;
+    FCurrentBounds: TRect;
     FAdaptedTexture: TBGRABitmap;
     FAdaptedTexture: TBGRABitmap;
     FCanReadaptTexture: boolean;
     FCanReadaptTexture: boolean;
     FHighQuality: boolean;
     FHighQuality: boolean;
@@ -75,7 +75,6 @@ type
     procedure PrepareBackground({%H-}toolDest: TBGRABitmap; AFirstTime: boolean); virtual;
     procedure PrepareBackground({%H-}toolDest: TBGRABitmap; AFirstTime: boolean); virtual;
     function DefaultTextureCenter: TPointF; virtual;
     function DefaultTextureCenter: TPointF; virtual;
     function DoToolUpdate({%H-}toolDest: TBGRABitmap): TRect; override;
     function DoToolUpdate({%H-}toolDest: TBGRABitmap): TRect; override;
-    function GetAction: TLayerAction; override;
     function GetStatusText: string; override;
     function GetStatusText: string; override;
   public
   public
     constructor Create(AManager: TToolManager); override;
     constructor Create(AManager: TToolManager); override;
@@ -124,13 +123,18 @@ end;
 
 
 procedure TToolLayerMapping.PrepareBackground(toolDest: TBGRABitmap;
 procedure TToolLayerMapping.PrepareBackground(toolDest: TBGRABitmap;
   AFirstTime: boolean);
   AFirstTime: boolean);
+var
+  r: TRect;
 begin
 begin
-  toolDest.FillTransparent;
   if not FAlreadyDrawnOnce then
   if not FAlreadyDrawnOnce then
   begin
   begin
     FAlreadyDrawnOnce := true;
     FAlreadyDrawnOnce := true;
-    Manager.Image.LayerMayChangeCompletely(toolDest);
-  end;
+    r := toolDest.GetImageBounds;
+  end else
+    r := FCurrentBounds;
+
+  toolDest.FillRect(r, BGRAPixelTransparent, dmSet);
+  Action.NotifyChange(toolDest, r);
 end;
 end;
 
 
 function TToolLayerMapping.GetTexture: TBGRABitmap;
 function TToolLayerMapping.GetTexture: TBGRABitmap;
@@ -182,6 +186,7 @@ begin
       quadDefined:= true;
       quadDefined:= true;
       PrepareBackground(GetToolDrawingLayer, True);
       PrepareBackground(GetToolDrawingLayer, True);
       DrawQuad;
       DrawQuad;
+      Action.NotifyChange(GetToolDrawingLayer, FCurrentBounds);
     end;
     end;
   end;
   end;
 end;
 end;
@@ -198,10 +203,8 @@ begin
       DrawQuad;
       DrawQuad;
       FCanReadaptTexture:= false;
       FCanReadaptTexture:= false;
       FHighQuality := false;
       FHighQuality := false;
-      Manager.Image.LayerMayChange(GetToolDrawingLayer,FMergedBounds);
+      Action.NotifyChange(GetToolDrawingLayer, FCurrentBounds);
     end;
     end;
-    Action.AllChangesNotified := true;
-    Action.NotifyChange(GetToolDrawingLayer, FMergedBounds);
     ValidateAction;
     ValidateAction;
     quadDefined := false;
     quadDefined := false;
     quad := nil;
     quad := nil;
@@ -210,7 +213,7 @@ end;
 
 
 procedure TToolTextureMapping.DrawQuad;
 procedure TToolTextureMapping.DrawQuad;
 const OversampleQuality = 2;
 const OversampleQuality = 2;
-var previousBounds: TRect;
+var
   tex: TBGRABitmap;
   tex: TBGRABitmap;
   persp: TBGRAPerspectiveScannerTransform;
   persp: TBGRAPerspectiveScannerTransform;
   dest: TBGRABitmap;
   dest: TBGRABitmap;
@@ -233,23 +236,27 @@ begin
     tex := GetAdaptedTexture;
     tex := GetAdaptedTexture;
     if tex <> nil then
     if tex <> nil then
     begin
     begin
-      previousBounds := FCurrentBounds;
+
+      if Manager.ToolPerspectiveRepeat then
+        FCurrentBounds := rect(0,0,Manager.Image.Width,Manager.Image.Height)
+      else
+        FCurrentBounds := GetShapeBounds([quad[0],quad[1],quad[2],quad[3]],1);
 
 
       if FHighQuality then
       if FHighQuality then
       begin
       begin
-        dest := TBGRABitmap.Create(Manager.Image.Width*OversampleQuality,Manager.Image.Height*OversampleQuality);
+        dest := TBGRABitmap.Create(FCurrentBounds.Width*OversampleQuality,FCurrentBounds.Height*OversampleQuality);
         setlength(quadHQ, length(quad));
         setlength(quadHQ, length(quad));
-        for i := 0 to high(quad) do quadHQ[i] := (quad[i]+PointF(0.5,0.5))*OversampleQuality - PointF(0.5,0.5);
+        for i := 0 to high(quad) do quadHQ[i] := (quad[i]+PointF(0.5,0.5))*OversampleQuality - PointF(0.5,0.5) - PointF(FCurrentBounds.TopLeft)*OversampleQuality;
       end
       end
       else
       else
       begin
       begin
         dest := GetToolDrawingLayer;
         dest := GetToolDrawingLayer;
         quadHQ := quad;
         quadHQ := quad;
+        dest.ClipRect := FCurrentBounds;
       end;
       end;
 
 
       if Manager.ToolPerspectiveRepeat then
       if Manager.ToolPerspectiveRepeat then
       begin
       begin
-        FCurrentBounds := rect(0,0,Manager.Image.Width,Manager.Image.Height);
         persp := TBGRAPerspectiveScannerTransform.Create(tex,[PointF(-0.5,-0.5),PointF(tex.Width-0.5,-0.5),
         persp := TBGRAPerspectiveScannerTransform.Create(tex,[PointF(-0.5,-0.5),PointF(tex.Width-0.5,-0.5),
           PointF(tex.Width-0.5,tex.Height-0.5),PointF(-0.5,tex.Height-0.5)],quadHQ);
           PointF(tex.Width-0.5,tex.Height-0.5),PointF(-0.5,tex.Height-0.5)],quadHQ);
         persp.IncludeOppositePlane := Manager.ToolPerspectiveTwoPlanes;
         persp.IncludeOppositePlane := Manager.ToolPerspectiveTwoPlanes;
@@ -257,7 +264,6 @@ begin
         persp.Free;
         persp.Free;
       end else
       end else
       begin
       begin
-        FCurrentBounds := GetShapeBounds([quad[0],quad[1],quad[2],quad[3]],1);
         dest.FillQuadPerspectiveMappingAntialias(quadHQ[0],quadHQ[1],quadHQ[2],quadHQ[3],tex,PointF(-0.5,-0.5),PointF(tex.Width-0.5,-0.5),
         dest.FillQuadPerspectiveMappingAntialias(quadHQ[0],quadHQ[1],quadHQ[2],quadHQ[3],tex,PointF(-0.5,-0.5),PointF(tex.Width-0.5,-0.5),
           PointF(tex.Width-0.5,tex.Height-0.5),PointF(-0.5,tex.Height-0.5), rect(0,0,tex.Width,tex.Height));
           PointF(tex.Width-0.5,tex.Height-0.5),PointF(-0.5,tex.Height-0.5), rect(0,0,tex.Width,tex.Height));
       end;
       end;
@@ -266,14 +272,14 @@ begin
       begin
       begin
         BGRAReplace(dest, dest.Resample(dest.Width div OversampleQuality, dest.Height div OversampleQuality,rmSimpleStretch));
         BGRAReplace(dest, dest.Resample(dest.Width div OversampleQuality, dest.Height div OversampleQuality,rmSimpleStretch));
         BGRAReplace(dest, dest.FilterSharpen(96/256));
         BGRAReplace(dest, dest.FilterSharpen(96/256));
-        GetToolDrawingLayer.PutImage(0,0,dest,dmDrawWithTransparency);
+        GetToolDrawingLayer.PutImage(FCurrentBounds.Left,FCurrentBounds.Top,dest,dmDrawWithTransparency);
         FreeAndNil(dest);
         FreeAndNil(dest);
-      end;
-      Action.NotifyChange(dest, FCurrentBounds);
-      FMergedBounds := RectUnion(previousBounds,FCurrentBounds);
-      Manager.Image.LayerMayChange(GetToolDrawingLayer,FMergedBounds);
+      end else
+        dest.NoClip;
     end;
     end;
-  end;
+  end
+  else
+    FCurrentBounds := EmptyRect;
 end;
 end;
 
 
 function TToolTextureMapping.GetAdaptedTexture: TBGRABitmap;
 function TToolTextureMapping.GetAdaptedTexture: TBGRABitmap;
@@ -463,7 +469,7 @@ begin
       quad[quadMovingIndex] := SnapIfNecessary(quadMovingDelta + ptF);
       quad[quadMovingIndex] := SnapIfNecessary(quadMovingDelta + ptF);
     PrepareBackground(toolDest,False);
     PrepareBackground(toolDest,False);
     DrawQuad;
     DrawQuad;
-    result := FMergedBounds;
+    result := FCurrentBounds;
   end;
   end;
 end;
 end;
 
 
@@ -520,18 +526,12 @@ begin
   begin
   begin
     PrepareBackground(GetToolDrawingLayer,False);
     PrepareBackground(GetToolDrawingLayer,False);
     DrawQuad;
     DrawQuad;
-    result := FMergedBounds;
+    result := FCurrentBounds;
   end
   end
     else
     else
       result := EmptyRect;
       result := EmptyRect;
 end;
 end;
 
 
-function TToolTextureMapping.GetAction: TLayerAction;
-begin
-  Result:=inherited GetAction;
-  result.AllChangesNotified:= true;
-end;
-
 function TToolTextureMapping.GetStatusText: string;
 function TToolTextureMapping.GetStatusText: string;
 var
 var
   i: Integer;
   i: Integer;
@@ -665,7 +665,7 @@ begin
       FCanReadaptTexture:= true;
       FCanReadaptTexture:= true;
       DrawQuad;
       DrawQuad;
       FCanReadaptTexture:= false;
       FCanReadaptTexture:= false;
-      result := FMergedBounds;
+      result := FCurrentBounds;
     end else
     end else
       result := EmptyRect;
       result := EmptyRect;
     quadMoving := false;
     quadMoving := false;
@@ -706,6 +706,7 @@ end;
 
 
 destructor TToolTextureMapping.Destroy;
 destructor TToolTextureMapping.Destroy;
 begin
 begin
+  ValidateAction;
   FreeAndNil(FAdaptedTexture);
   FreeAndNil(FAdaptedTexture);
   inherited Destroy;
   inherited Destroy;
 end;
 end;
@@ -777,7 +778,6 @@ begin
   begin
   begin
     DeformationGrid := nil;
     DeformationGrid := nil;
     DeformationGridTexCoord := nil;
     DeformationGridTexCoord := nil;
-    Manager.Image.LayerMayChange(GetToolDrawingLayer,FMergedBounds);
     ValidateAction;
     ValidateAction;
     DoingDeformation := false;
     DoingDeformation := false;
   end;
   end;
@@ -937,7 +937,6 @@ begin
     result := FMergedBounds;
     result := FMergedBounds;
   end;
   end;
   deformationOrigin := ptF;
   deformationOrigin := ptF;
-
 end;
 end;
 
 
 function TToolDeformationGrid.GetIsSelectingTool: boolean;
 function TToolDeformationGrid.GetIsSelectingTool: boolean;

+ 1 - 1
lazpaint/utoolfloodfill.pas

@@ -156,7 +156,7 @@ begin
     if Manager.ToolFloodFillOptionProgressive then
     if Manager.ToolFloodFillOptionProgressive then
       toolDest.FloodFill(pt.X,pt.Y,penColor,fmProgressive,Manager.ToolTolerance) else
       toolDest.FloodFill(pt.X,pt.Y,penColor,fmProgressive,Manager.ToolTolerance) else
         toolDest.FloodFill(pt.X,pt.Y,penColor,fmDrawWithTransparency,Manager.ToolTolerance);
         toolDest.FloodFill(pt.X,pt.Y,penColor,fmDrawWithTransparency,Manager.ToolTolerance);
-  Manager.Image.LayerMayChangeCompletely(toolDest);
+  Action.NotifyChange(toolDest, rect(0,0,toolDest.Width,toolDest.Height));
   ValidateAction;
   ValidateAction;
   result := OnlyRenderChange;
   result := OnlyRenderChange;
 end;
 end;

+ 151 - 78
lazpaint/utoollayer.pas

@@ -5,7 +5,7 @@ unit UToolLayer;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, UTool, BGRABitmap, BGRABitmapTypes, UImageType;
+  Classes, SysUtils, UTool, BGRABitmap, BGRABitmapTypes, UImageType, BGRATransform;
 
 
 type
 type
   { TToolMoveLayer }
   { TToolMoveLayer }
@@ -14,8 +14,9 @@ type
   protected
   protected
     handMoving: boolean;
     handMoving: boolean;
     handOrigin: TPoint;
     handOrigin: TPoint;
-    FOriginalLayerOffset: TPoint;
-    FOriginalLayerOffsetDefined: boolean;
+    FStartLayerOffset: TPoint;
+    FStartLayerMatrix: TAffineMatrix;
+    FStartLayerOffsetDefined: boolean;
     FLayerBounds: TRect;
     FLayerBounds: TRect;
     FLayerBoundsDefined: boolean;
     FLayerBoundsDefined: boolean;
     function GetIsSelectingTool: boolean; override;
     function GetIsSelectingTool: boolean; override;
@@ -24,10 +25,10 @@ type
     function DoToolMove({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF): TRect; override;
     function DoToolMove({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF): TRect; override;
     procedure DoToolMoveAfter(pt: TPoint; {%H-}ptF: TPointF); override;
     procedure DoToolMoveAfter(pt: TPoint; {%H-}ptF: TPointF); override;
     procedure OnTryStop({%H-}sender: TCustomLayerAction); override;
     procedure OnTryStop({%H-}sender: TCustomLayerAction); override;
-    procedure ApplyMoveLayer;
+    function UseOriginal: boolean;
+    procedure NeedLayerBounds;
+    function DoGetToolDrawingLayer: TBGRABitmap; override;
   public
   public
-    destructor Destroy; override;
-    function GetToolDrawingLayer: TBGRABitmap; override;
     function ToolUp: TRect; override;
     function ToolUp: TRect; override;
     function ToolKeyDown(var key: Word): TRect; override;
     function ToolKeyDown(var key: Word): TRect; override;
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth,
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth,
@@ -43,9 +44,11 @@ type
     function GetOriginalLayerBounds: TRect;
     function GetOriginalLayerBounds: TRect;
     function GetRotationCenter: TPointF;
     function GetRotationCenter: TPointF;
     procedure SetRotationCenter(AValue: TPointF);
     procedure SetRotationCenter(AValue: TPointF);
+    function UseOriginal: boolean;
   protected
   protected
-    FOriginalLayerBounds: TRect;
-    FOriginalLayerBoundsDefined: boolean;
+    FInitialOriginalMatrix: TAffineMatrix;
+    FInitialLayerBounds: TRect;
+    FInitialLayerBoundsDefined: boolean;
     FRotationCenter: TPointF;
     FRotationCenter: TPointF;
     FRotationCenterDefined: boolean;
     FRotationCenterDefined: boolean;
     FFilter: TResampleFilter;
     FFilter: TResampleFilter;
@@ -63,9 +66,12 @@ type
     function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect;
     function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect;
       override;
       override;
     function UpdateRotation: TRect;
     function UpdateRotation: TRect;
+    procedure CancelRotation;
+    procedure ValidateRotation;
     property RotationCenter: TPointF read GetRotationCenter write SetRotationCenter;
     property RotationCenter: TPointF read GetRotationCenter write SetRotationCenter;
     property OriginalLayerBounds: TRect read GetOriginalLayerBounds;
     property OriginalLayerBounds: TRect read GetOriginalLayerBounds;
     procedure OnTryStop({%H-}sender: TCustomLayerAction); override;
     procedure OnTryStop({%H-}sender: TCustomLayerAction); override;
+    function DoGetToolDrawingLayer: TBGRABitmap; override;
   public
   public
     constructor Create(AManager: TToolManager); override;
     constructor Create(AManager: TToolManager); override;
     destructor Destroy; override;
     destructor Destroy; override;
@@ -93,12 +99,12 @@ end;
 
 
 function TToolRotateLayer.GetOriginalLayerBounds: TRect;
 function TToolRotateLayer.GetOriginalLayerBounds: TRect;
 begin
 begin
-  if not FOriginalLayerBoundsDefined then
+  if not FInitialLayerBoundsDefined then
   begin
   begin
-    FOriginalLayerBounds := GetToolDrawingLayer.GetImageBounds;
-    FOriginalLayerBoundsDefined := true;
+    FInitialLayerBounds := GetToolDrawingLayer.GetImageBounds;
+    FInitialLayerBoundsDefined := true;
   end;
   end;
-  result := FOriginalLayerBounds;
+  result := FInitialLayerBounds;
 end;
 end;
 
 
 function TToolRotateLayer.GetRotationCenter: TPointF;
 function TToolRotateLayer.GetRotationCenter: TPointF;
@@ -108,8 +114,12 @@ begin
     if IsRectEmpty(OriginalLayerBounds) then
     if IsRectEmpty(OriginalLayerBounds) then
       FRotationCenter := PointF(Manager.Image.Width/2 - 0.5,Manager.Image.Height/2 - 0.5)
       FRotationCenter := PointF(Manager.Image.Width/2 - 0.5,Manager.Image.Height/2 - 0.5)
     else
     else
-    with OriginalLayerBounds do
-      FRotationCenter := PointF((Left+Right)/2 - 0.5, (Top+Bottom)/2 - 0.5);
+    begin
+      with OriginalLayerBounds do
+        FRotationCenter := PointF((Left+Right)/2 - 0.5, (Top+Bottom)/2 - 0.5);
+      with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
+        FRotationCenter += PointF(X,Y);
+    end;
     FRotationCenterDefined := true;
     FRotationCenterDefined := true;
   end;
   end;
   result := FRotationCenter;
   result := FRotationCenter;
@@ -120,9 +130,19 @@ begin
   FRotationCenter := AValue;
   FRotationCenter := AValue;
 end;
 end;
 
 
+function TToolRotateLayer.UseOriginal: boolean;
+begin
+  with Manager.Image do
+    result := LayerOriginalDefined[CurrentLayerIndex] and
+              LayerOriginalKnown[CurrentLayerIndex];
+end;
+
 function TToolRotateLayer.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
 function TToolRotateLayer.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF; rightBtn: boolean): TRect;
   ptF: TPointF; rightBtn: boolean): TRect;
 begin
 begin
+  with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
+    ptF += PointF(X,Y);
+
   if not FRotating and not rightBtn then
   if not FRotating and not rightBtn then
   begin
   begin
     FRotating := true;
     FRotating := true;
@@ -148,7 +168,10 @@ end;
 function TToolRotateLayer.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
 function TToolRotateLayer.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF): TRect;
   ptF: TPointF): TRect;
 var angleDiff: single;
 var angleDiff: single;
+  ofs: TPoint;
 begin
 begin
+  with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
+    ptF += PointF(X,Y);
   if FRotating then
   if FRotating then
   begin
   begin
     angleDiff := ComputeAngle(ptF.X-RotationCenter.X,ptF.Y-RotationCenter.Y)-
     angleDiff := ComputeAngle(ptF.X-RotationCenter.X,ptF.Y-RotationCenter.Y)-
@@ -174,22 +197,47 @@ begin
   FPreviousRotationCenter := RotationCenter;
   FPreviousRotationCenter := RotationCenter;
   FPreviousFilter := FFilter;
   FPreviousFilter := FFilter;
   result := EmptyRect;
   result := EmptyRect;
-  if not FLastUpdateRectDefined then
+
+  if UseOriginal then
   begin
   begin
-    GetToolDrawingLayer.FillTransparent;
-    result := rect(0,0,GetToolDrawingLayer.Width,GetToolDrawingLayer.Height);
+    Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] :=
+      AffineMatrixTranslation(RotationCenter.X,RotationCenter.Y)*
+      AffineMatrixRotationDeg(FActualAngle)*
+      AffineMatrixTranslation(-RotationCenter.X,-RotationCenter.Y)*
+      FInitialOriginalMatrix;
   end else
   end else
-  if not IsRectEmpty(FLastUpdateRect) then
   begin
   begin
-    GetToolDrawingLayer.FillRect(FLastUpdateRect,BGRAPixelTransparent,dmSet);
-    result := FLastUpdateRect;
+    if not FLastUpdateRectDefined then
+    begin
+      GetToolDrawingLayer.FillTransparent;
+      result := rect(0,0,GetToolDrawingLayer.Width,GetToolDrawingLayer.Height);
+    end else
+    if not IsRectEmpty(FLastUpdateRect) then
+    begin
+      GetToolDrawingLayer.FillRect(FLastUpdateRect,BGRAPixelTransparent,dmSet);
+      result := FLastUpdateRect;
+    end;
+    FLastUpdateRect := GetToolDrawingLayer.GetImageAngleBounds(0,0,Action.BackupDrawingLayer,FActualAngle,RotationCenter.X,RotationCenter.Y,True);
+    FLastUpdateRectDefined:= true;
+    GetToolDrawingLayer.ComputeImageAngleAxes(0,0,Action.BackupDrawingLayer.Width,Action.BackupDrawingLayer.Height,FActualAngle,RotationCenter.X,RotationCenter.Y,True,
+    origin,haxis,vaxis);
+    GetToolDrawingLayer.PutImageAffine(origin,haxis,vaxis,Action.BackupDrawingLayer,FLastUpdateRect,FFilter,dmSet,255);
+    result := RectUnion(result,FLastUpdateRect);
   end;
   end;
-  FLastUpdateRect := GetToolDrawingLayer.GetImageAngleBounds(0,0,Action.BackupDrawingLayer,FActualAngle,RotationCenter.X,RotationCenter.Y,True);
-  FLastUpdateRectDefined:= true;
-  GetToolDrawingLayer.ComputeImageAngleAxes(0,0,Action.BackupDrawingLayer.Width,Action.BackupDrawingLayer.Height,FActualAngle,RotationCenter.X,RotationCenter.Y,True,
-  origin,haxis,vaxis);
-  GetToolDrawingLayer.PutImageAffine(origin,haxis,vaxis,Action.BackupDrawingLayer,FLastUpdateRect,FFilter,dmSet,255);
-  result := RectUnion(result,FLastUpdateRect);
+end;
+
+procedure TToolRotateLayer.CancelRotation;
+begin
+  if UseOriginal then
+    Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] := FInitialOriginalMatrix
+  else
+    CancelActionPartially;
+  Manager.QueryExitTool;
+end;
+
+procedure TToolRotateLayer.ValidateRotation;
+begin
+  Manager.QueryExitTool;
 end;
 end;
 
 
 procedure TToolRotateLayer.OnTryStop(sender: TCustomLayerAction);
 procedure TToolRotateLayer.OnTryStop(sender: TCustomLayerAction);
@@ -197,22 +245,32 @@ begin
   //nothing
   //nothing
 end;
 end;
 
 
+function TToolRotateLayer.DoGetToolDrawingLayer: TBGRABitmap;
+begin
+  if UseOriginal then
+    Result:= Manager.Image.CurrentLayerReadOnly   //do not modify layer data directly and ignore selection
+  else
+    Result:= Action.SelectedImageLayer;
+end;
+
 constructor TToolRotateLayer.Create(AManager: TToolManager);
 constructor TToolRotateLayer.Create(AManager: TToolManager);
 begin
 begin
   inherited Create(AManager);
   inherited Create(AManager);
   FAngle:= 0;
   FAngle:= 0;
   FPreviousActualAngle := 0;
   FPreviousActualAngle := 0;
   FCtrlDown:= false;
   FCtrlDown:= false;
-  FOriginalLayerBoundsDefined:= false;
+  FInitialLayerBoundsDefined:= false;
   FRotationCenterDefined := false;
   FRotationCenterDefined := false;
   FLastUpdateRectDefined:= false;
   FLastUpdateRectDefined:= false;
   FFilter := rfCosine;
   FFilter := rfCosine;
   FPreviousFilter := FFilter;
   FPreviousFilter := FFilter;
+  FInitialOriginalMatrix := Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex];
 end;
 end;
 
 
 destructor TToolRotateLayer.Destroy;
 destructor TToolRotateLayer.Destroy;
 begin
 begin
-  ValidateActionPartially;
+  if not UseOriginal then
+    ValidateAction;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
@@ -231,17 +289,15 @@ begin
   end else
   end else
   if Key = VK_RETURN then
   if Key = VK_RETURN then
   begin
   begin
-    if FActualAngle = 0 then CancelActionPartially
-     else ValidateActionPartially;
+    if FActualAngle = 0 then CancelRotation
+    else ValidateRotation;
     result := OnlyRenderChange;
     result := OnlyRenderChange;
-    manager.QueryExitTool;
     key := 0;
     key := 0;
   end else
   end else
   if Key = VK_ESCAPE then
   if Key = VK_ESCAPE then
   begin
   begin
-    CancelActionPartially;
+    CancelRotation;
     result := OnlyRenderChange;
     result := OnlyRenderChange;
-    manager.QueryExitTool;
     key := 0;
     key := 0;
   end else
   end else
     result := EmptyRect;
     result := EmptyRect;
@@ -272,7 +328,8 @@ function TToolRotateLayer.Render(VirtualScreen: TBGRABitmap;
   VirtualScreenWidth, VirtualScreenHeight: integer;
   VirtualScreenWidth, VirtualScreenHeight: integer;
   BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
   BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
 begin
 begin
-  Result:= NicePoint(VirtualScreen,BitmapToVirtualScreen(RotationCenter));
+  with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
+    Result:= NicePoint(VirtualScreen,BitmapToVirtualScreen(RotationCenter-PointF(X,Y)));
 end;
 end;
 
 
 function TToolRotateLayer.GetIsSelectingTool: boolean;
 function TToolRotateLayer.GetIsSelectingTool: boolean;
@@ -296,17 +353,13 @@ begin
   begin
   begin
     handMoving := true;
     handMoving := true;
     handOrigin := pt;
     handOrigin := pt;
-    if not FOriginalLayerOffsetDefined then
+    if not FStartLayerOffsetDefined then
     begin
     begin
-      FOriginalLayerOffsetDefined := true;
-      idx := Manager.Image.currentImageLayerIndex;
-      if not FLayerBoundsDefined then
-      begin
-        FLayerBounds := Manager.Image.LayerBitmap[idx].GetImageBounds;
-        FLayerBoundsDefined := true;
-      end;
-      FOriginalLayerOffset := Manager.Image.LayerOffset[idx];
-      GetAction;
+      FStartLayerOffsetDefined := true;
+      idx := Manager.Image.CurrentLayerIndex;
+      NeedLayerBounds;
+      FStartLayerOffset := Manager.Image.LayerOffset[idx];
+      FStartLayerMatrix := Manager.Image.LayerOriginalMatrix[idx];
     end;
     end;
   end;
   end;
 end;
 end;
@@ -318,11 +371,19 @@ var idx: integer;
 begin
 begin
   if handMoving and ((handOrigin.X <> pt.X) or (handOrigin.Y <> pt.Y)) then
   if handMoving and ((handOrigin.X <> pt.X) or (handOrigin.Y <> pt.Y)) then
   begin
   begin
-    idx := Manager.Image.currentImageLayerIndex;
-    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;
+    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
   end else
     result := EmptyRect;
     result := EmptyRect;
 end;
 end;
@@ -337,29 +398,33 @@ begin
   //nothing
   //nothing
 end;
 end;
 
 
-procedure TToolMoveLayer.ApplyMoveLayer;
-var finalOffset: TPoint;
-  idx: integer;
+function TToolMoveLayer.UseOriginal: boolean;
 begin
 begin
-  if FOriginalLayerOffsetDefined then
-  begin
-    idx := Manager.Image.currentImageLayerIndex;
-    finalOffset := Manager.Image.LayerOffset[idx];
-    Manager.Image.SetLayerOffset(idx, FOriginalLayerOffset, FLayerBounds);
-    Manager.Image.ApplyLayerOffset(finalOffset.x,finalOffset.y);
-    FOriginalLayerOffsetDefined := false;
-  end;
+  with Manager.Image do
+    result := LayerOriginalDefined[CurrentLayerIndex] and
+              LayerOriginalKnown[CurrentLayerIndex];
 end;
 end;
 
 
-destructor TToolMoveLayer.Destroy;
+procedure TToolMoveLayer.NeedLayerBounds;
+var
+  idx: Integer;
 begin
 begin
-  ApplyMoveLayer;
-  inherited Destroy;
+  idx := Manager.Image.CurrentLayerIndex;
+  if not FLayerBoundsDefined then
+  begin
+    if UseOriginal then
+      FLayerBounds := Manager.Image.LayerOriginal[idx].GetRenderBounds(
+                        Rect(-maxLongint div 2,-maxLongint div 2,maxLongint div 2,maxLongint div 2),
+                        AffineMatrixLinear(Manager.Image.LayerOriginalMatrix[idx]))
+    else
+      FLayerBounds := Manager.Image.LayerBitmap[idx].GetImageBounds;
+    FLayerBoundsDefined := true;
+  end;
 end;
 end;
 
 
-function TToolMoveLayer.GetToolDrawingLayer: TBGRABitmap;
+function TToolMoveLayer.DoGetToolDrawingLayer: TBGRABitmap;
 begin
 begin
-  Result:= Manager.Image.SelectedImageLayerReadOnly;   //do not create a selection layer
+  Result:= Manager.Image.CurrentLayerReadOnly;   //do not modify layer data directly and ignore selection
 end;
 end;
 
 
 function TToolMoveLayer.ToolUp: TRect;
 function TToolMoveLayer.ToolUp: TRect;
@@ -379,10 +444,13 @@ begin
   end
   end
   else if key = VK_ESCAPE then
   else if key = VK_ESCAPE then
   begin
   begin
-    if FOriginalLayerOffsetDefined then
+    if FStartLayerOffsetDefined then
     begin
     begin
-      idx := Manager.Image.currentImageLayerIndex;
-      Manager.Image.LayerOffset[idx] := FOriginalLayerOffset;
+      idx := Manager.Image.CurrentLayerIndex;
+      if UseOriginal then
+        Manager.Image.LayerOriginalMatrix[idx] := FStartLayerMatrix
+      else
+        Manager.Image.SetLayerOffset(idx, FStartLayerOffset, FLayerBounds);
       result := OnlyRenderChange;
       result := OnlyRenderChange;
     end else
     end else
       result := EmptyRect;
       result := EmptyRect;
@@ -396,19 +464,24 @@ function TToolMoveLayer.Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth,
   VirtualScreenHeight: integer;
   VirtualScreenHeight: integer;
   BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
   BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
 var pt1,pt2:TPoint;
 var pt1,pt2:TPoint;
-  ptF1,ptF2: TPointF;
+  ptF1,ptF2, ofs: TPointF;
   penWidth,i,idx: integer;
   penWidth,i,idx: integer;
+  m: BGRABitmapTypes.TAffineMatrix;
 begin
 begin
-  idx := Manager.Image.currentImageLayerIndex;
-  if not FLayerBoundsDefined then
-  begin
-    Action;
-    FLayerBounds := Manager.Image.LayerBitmap[idx].GetImageBounds;
-    FLayerBoundsDefined := true;
-  end;
+  idx := Manager.Image.CurrentLayerIndex;
+  NeedLayerBounds;
 
 
-  ptF1 := BitmapToVirtualScreen(PointF(FLayerBounds.Left-0.5,FLayerBounds.Top-0.5));
-  ptF2 := BitmapToVirtualScreen(PointF(FLayerBounds.Right-0.5,FLayerBounds.Bottom-0.5));
+  if UseOriginal then
+  begin
+    m := Manager.Image.LayerOriginalMatrix[idx];
+    ofs := PointF(m[1,3],m[2,3]);
+    with Manager.Image.LayerOffset[idx] do
+      ofs := ofs-PointF(x,y);
+  end
+  else
+    ofs := PointF(0,0);
+  ptF1 := BitmapToVirtualScreen(PointF(FLayerBounds.Left-0.5,FLayerBounds.Top-0.5)+ofs);
+  ptF2 := BitmapToVirtualScreen(PointF(FLayerBounds.Right-0.5,FLayerBounds.Bottom-0.5)+ofs);
   pt1 := point(round(ptF1.x),round(ptF1.y));
   pt1 := point(round(ptF1.x),round(ptF1.y));
   pt2 := point(round(ptF2.X)-1,round(ptF2.Y)-1);
   pt2 := point(round(ptF2.X)-1,round(ptF2.Y)-1);
 
 

+ 1 - 4
lazpaint/utoolphong.pas

@@ -138,10 +138,7 @@ begin
     rightBtnDown := true;
     rightBtnDown := true;
     Manager.ToolLightPosition := pt;
     Manager.ToolLightPosition := pt;
     if afterRectDrawing then
     if afterRectDrawing then
-    begin
-      result := FinishShape(toolDest);
-      Action.NotifyChange(toolDest, result);
-    end
+      result := FinishShape(toolDest)
     else
     else
       result := OnlyRenderChange;
       result := OnlyRenderChange;
     exit;
     exit;

+ 4 - 20
lazpaint/utoolpolygon.pas

@@ -17,7 +17,6 @@ type
   TToolGenericPolygon = class(TGenericTool)
   TToolGenericPolygon = class(TGenericTool)
   strict private
   strict private
     swapColorKey: boolean;
     swapColorKey: boolean;
-    FCurrentBounds: TRect;
     lastMousePos: TPointF;
     lastMousePos: TPointF;
   protected
   protected
     class var HintShowed: boolean;
     class var HintShowed: boolean;
@@ -49,7 +48,6 @@ type
     function GetPenColor: TBGRAPixel; virtual;
     function GetPenColor: TBGRAPixel; virtual;
     function FinishHandDrawing: TRect;
     function FinishHandDrawing: TRect;
     function AddLastClickedPoint: boolean; virtual;
     function AddLastClickedPoint: boolean; virtual;
-    function GetAction: TLayerAction; override;
     procedure OnFinishHandDrawing; virtual;
     procedure OnFinishHandDrawing; virtual;
     procedure OnAddPoint({%H-}AIndex: integer); virtual;
     procedure OnAddPoint({%H-}AIndex: integer); virtual;
     procedure OnDeletePoint({%H-}AIndex: integer); virtual;
     procedure OnDeletePoint({%H-}AIndex: integer); virtual;
@@ -535,12 +533,6 @@ begin
   result := false;
   result := false;
 end;
 end;
 
 
-function TToolGenericPolygon.GetAction: TLayerAction;
-begin
-  Result:=inherited GetAction;
-  result.AllChangesNotified := true;
-end;
-
 procedure TToolGenericPolygon.OnFinishHandDrawing;
 procedure TToolGenericPolygon.OnFinishHandDrawing;
 begin
 begin
 
 
@@ -609,10 +601,10 @@ var r: TRect;
 begin
 begin
   if not FAfterHandDrawing then
   if not FAfterHandDrawing then
   begin
   begin
+    RestoreBackupDrawingLayer;
     r := FinalPolygonView(toolDest);
     r := FinalPolygonView(toolDest);
     Action.NotifyChange(toolDest,r);
     Action.NotifyChange(toolDest,r);
     setlength(polygonPoints,0);
     setlength(polygonPoints,0);
-    Manager.Image.LayerMayChange(toolDest,r);
     FAfterHandDrawing:= True;
     FAfterHandDrawing:= True;
   end else
   end else
   begin
   begin
@@ -624,23 +616,17 @@ begin
 end;
 end;
 
 
 function TToolGenericPolygon.UpdatePolygonView(toolDest: TBGRABitmap): TRect;
 function TToolGenericPolygon.UpdatePolygonView(toolDest: TBGRABitmap): TRect;
-var
-   previousBounds : TRect;
 begin
 begin
-  previousBounds := FCurrentBounds;
   RestoreBackupDrawingLayer;
   RestoreBackupDrawingLayer;
   if FAfterHandDrawing then
   if FAfterHandDrawing then
-    FCurrentBounds := FinalPolygonView(toolDest)
+    result := FinalPolygonView(toolDest)
   else
   else
-    FCurrentBounds := HandDrawingPolygonView(toolDest);
-  Action.NotifyChange(toolDest, FCurrentBounds);
-  result := RectUnion(previousBounds,FCurrentBounds);
+    result := HandDrawingPolygonView(toolDest);
 end;
 end;
 
 
 procedure TToolGenericPolygon.StartPolygon(rightBtn: boolean);
 procedure TToolGenericPolygon.StartPolygon(rightBtn: boolean);
 begin
 begin
   swapedColor := rightBtn;
   swapedColor := rightBtn;
-  FCurrentBounds := EmptyRect;
 end;
 end;
 
 
 function TToolGenericPolygon.SnapToPixelEdge: boolean;
 function TToolGenericPolygon.SnapToPixelEdge: boolean;
@@ -887,9 +873,7 @@ begin
         begin
         begin
           RestoreBackupDrawingLayer;
           RestoreBackupDrawingLayer;
           polygonPoints := nil;
           polygonPoints := nil;
-          result := FCurrentBounds;
-          if IsRectEmpty(result) then result := OnlyRenderChange;
-          FCurrentBounds := EmptyRect;
+          result := OnlyRenderChange;
         end else
         end else
           result := FinishHandDrawing
           result := FinishHandDrawing
       end
       end

+ 24 - 75
lazpaint/utoolselect.pas

@@ -5,7 +5,8 @@ unit UToolSelect;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, Graphics, utool, utoolpolygon, utoolbasic, BGRABitmapTypes, BGRABitmap;
+  Classes, SysUtils, Graphics, utool, utoolpolygon, utoolbasic, BGRABitmapTypes, BGRABitmap,
+  ULayerAction;
 
 
 type
 type
 
 
@@ -78,26 +79,22 @@ type
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; override;
     function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; override;
   end;
   end;
 
 
-  { TToolTransformSelection }
+  { TTransformSelectionTool }
 
 
-  TToolTransformSelection = class(TGenericTransformTool)
+  TTransformSelectionTool = class(TGenericTool)
   protected
   protected
-    FKeepTransformOnDestroy: boolean;
-    function GetKeepTransformOnDestroy: boolean; override;
-    procedure SetKeepTransformOnDestroy(AValue: boolean); override;
-  public
-    procedure SelectionTransformChange;
-    constructor Create(AManager: TToolManager); override;
-    destructor Destroy; override;
+    function GetIsSelectingTool: boolean; override;
+    function GetAction: TLayerAction; override;
+    function FixSelectionTransform: boolean; override;
+    function DoGetToolDrawingLayer: TBGRABitmap; override;
   end;
   end;
 
 
   { TToolMoveSelection }
   { TToolMoveSelection }
 
 
-  TToolMoveSelection = class(TToolTransformSelection)
+  TToolMoveSelection = class(TTransformSelectionTool)
   protected
   protected
     handMoving: boolean;
     handMoving: boolean;
     handOrigin: TPoint;
     handOrigin: TPoint;
-    function GetIsSelectingTool: boolean; override;
     function DoToolDown({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF;
     function DoToolDown({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF;
       {%H-}rightBtn: boolean): TRect; override;
       {%H-}rightBtn: boolean): TRect; override;
     function DoToolMove({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF): TRect; override;
     function DoToolMove({%H-}toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF): TRect; override;
@@ -109,7 +106,7 @@ type
 
 
   { TToolRotateSelection }
   { TToolRotateSelection }
 
 
-  TToolRotateSelection = class(TToolTransformSelection)
+  TToolRotateSelection = class(TTransformSelectionTool)
   protected
   protected
     class var HintShowed: boolean;
     class var HintShowed: boolean;
     handMoving: boolean;
     handMoving: boolean;
@@ -119,7 +116,6 @@ type
     FOriginalTransform: TAffineMatrix;
     FOriginalTransform: TAffineMatrix;
     FCurrentAngle: single;
     FCurrentAngle: single;
     FCurrentCenter: TPointF;
     FCurrentCenter: TPointF;
-    function GetIsSelectingTool: boolean; override;
     function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
     function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
       rightBtn: boolean): TRect; override;
       rightBtn: boolean): TRect; override;
     function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect; override;
     function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect; override;
@@ -138,72 +134,45 @@ implementation
 
 
 uses types, ugraph, LCLType, LazPaintType, Math, BGRATransform, BGRAPath;
 uses types, ugraph, LCLType, LazPaintType, Math, BGRATransform, BGRAPath;
 
 
-{ TToolTransformSelection }
-
-function TToolTransformSelection.GetKeepTransformOnDestroy: boolean;
-begin
-  result := FKeepTransformOnDestroy;
-end;
+{ TTransformSelectionTool }
 
 
-procedure TToolTransformSelection.SetKeepTransformOnDestroy(AValue: boolean);
+function TTransformSelectionTool.GetIsSelectingTool: boolean;
 begin
 begin
-  FKeepTransformOnDestroy:= AValue;
+  result := true;
 end;
 end;
 
 
-procedure TToolTransformSelection.SelectionTransformChange;
-var selectionChangeRect: TRect;
+function TTransformSelectionTool.GetAction: TLayerAction;
 begin
 begin
-  selectionChangeRect := Manager.Image.TransformedSelectionBounds;
-  if not Manager.Image.SelectionLayerIsEmpty then
-    Manager.Image.ImageMayChange(selectionChangeRect,False);
-  if not IsRectEmpty(selectionChangeRect) then
-  begin
-    InflateRect(selectionChangeRect,1,1);
-    Manager.Image.RenderMayChange(selectionChangeRect,true);
-  end;
+  Result:= nil;
 end;
 end;
 
 
-constructor TToolTransformSelection.Create(AManager: TToolManager);
+function TTransformSelectionTool.FixSelectionTransform: boolean;
 begin
 begin
-  inherited Create(AManager);
-  FKeepTransformOnDestroy:= False;
+  Result:= false;
 end;
 end;
 
 
-destructor TToolTransformSelection.Destroy;
+function TTransformSelectionTool.DoGetToolDrawingLayer: TBGRABitmap;
 begin
 begin
-  if not FKeepTransformOnDestroy and not IsAffineMatrixIdentity(Manager.Image.SelectionTransform) then
-  begin
-    Action.ApplySelectionTransform;
-    ValidateAction;
-  end;
-  inherited Destroy;
+  result := Manager.Image.SelectionMaskReadonly;
 end;
 end;
 
 
 { TToolRotateSelection }
 { TToolRotateSelection }
 
 
-function TToolRotateSelection.GetIsSelectingTool: boolean;
-begin
-  Result:= true;
-end;
-
 function TToolRotateSelection.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
 function TToolRotateSelection.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF; rightBtn: boolean): TRect;
   ptF: TPointF; rightBtn: boolean): TRect;
 begin
 begin
   result := EmptyRect;
   result := EmptyRect;
-  if not handMoving and not Manager.Image.SelectionEmpty then
+  if not handMoving and not Manager.Image.SelectionMaskEmpty then
   begin
   begin
     if rightBtn then
     if rightBtn then
     begin
     begin
       if FCurrentAngle <> 0 then
       if FCurrentAngle <> 0 then
       begin
       begin
-        SelectionTransformChange;
         FCurrentAngle := 0;
         FCurrentAngle := 0;
         FCurrentCenter := ptF;
         FCurrentCenter := ptF;
         UpdateTransform;
         UpdateTransform;
-        SelectionTransformChange;
       end else
       end else
       begin
       begin
-        FCurrentAngle := 0;
         FCurrentCenter := ptF;
         FCurrentCenter := ptF;
         UpdateTransform;
         UpdateTransform;
       end;
       end;
@@ -227,7 +196,6 @@ begin
   end;
   end;
   if handMoving and ((handOrigin.X <> ptF.X) or (handOrigin.Y <> ptF.Y)) then
   if handMoving and ((handOrigin.X <> ptF.X) or (handOrigin.Y <> ptF.Y)) then
   begin
   begin
-    SelectionTransformChange;
     angleDiff := ComputeAngle(ptF.X-FCurrentCenter.X,ptF.Y-FCurrentCenter.Y)-
     angleDiff := ComputeAngle(ptF.X-FCurrentCenter.X,ptF.Y-FCurrentCenter.Y)-
                  ComputeAngle(handOrigin.X-FCurrentCenter.X,handOrigin.Y-FCurrentCenter.Y);
                  ComputeAngle(handOrigin.X-FCurrentCenter.X,handOrigin.Y-FCurrentCenter.Y);
     if snapRotate then
     if snapRotate then
@@ -238,7 +206,6 @@ begin
      else
      else
        FCurrentAngle := FCurrentAngle + angleDiff;
        FCurrentAngle := FCurrentAngle + angleDiff;
     UpdateTransform;
     UpdateTransform;
-    SelectionTransformChange;
     handOrigin := ptF;
     handOrigin := ptF;
     result := OnlyRenderChange;
     result := OnlyRenderChange;
   end else
   end else
@@ -260,7 +227,7 @@ end;
 constructor TToolRotateSelection.Create(AManager: TToolManager);
 constructor TToolRotateSelection.Create(AManager: TToolManager);
 begin
 begin
   inherited Create(AManager);
   inherited Create(AManager);
-  FCurrentCenter := Manager.Image.SelectionTransform * Manager.Image.GetSelectionCenter;
+  FCurrentCenter := Manager.Image.SelectionTransform * Manager.Image.GetSelectionMaskCenter;
   FOriginalTransform := Manager.Image.SelectionTransform;
   FOriginalTransform := Manager.Image.SelectionTransform;
   FCurrentAngle := 0;
   FCurrentAngle := 0;
 end;
 end;
@@ -277,10 +244,8 @@ begin
 
 
       if handMoving then
       if handMoving then
       begin
       begin
-        SelectionTransformChange;
         FCurrentAngle := round(snapAngle/15)*15;
         FCurrentAngle := round(snapAngle/15)*15;
         UpdateTransform;
         UpdateTransform;
-        SelectionTransformChange;
         result := OnlyRenderChange;
         result := OnlyRenderChange;
       end;
       end;
     end;
     end;
@@ -289,13 +254,6 @@ begin
   if key = VK_ESCAPE then
   if key = VK_ESCAPE then
   begin
   begin
     if FCurrentAngle <> 0 then
     if FCurrentAngle <> 0 then
-    begin
-      SelectionTransformChange;
-      FCurrentAngle := 0;
-      UpdateTransform;
-      SelectionTransformChange;
-      result := OnlyRenderChange;
-    end else
     begin
     begin
       FCurrentAngle := 0;
       FCurrentAngle := 0;
       UpdateTransform;
       UpdateTransform;
@@ -337,11 +295,6 @@ end;
 
 
 { TToolMoveSelection }
 { TToolMoveSelection }
 
 
-function TToolMoveSelection.GetIsSelectingTool: boolean;
-begin
-  result := true;
-end;
-
 function TToolMoveSelection.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
 function TToolMoveSelection.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
   ptF: TPointF; rightBtn: boolean): TRect;
   ptF: TPointF; rightBtn: boolean): TRect;
 begin
 begin
@@ -360,11 +313,9 @@ begin
   result := EmptyRect;
   result := EmptyRect;
   if handMoving and ((handOrigin.X <> pt.X) or (handOrigin.Y <> pt.Y)) then
   if handMoving and ((handOrigin.X <> pt.X) or (handOrigin.Y <> pt.Y)) then
   begin
   begin
-    SelectionTransformChange;
     dx := pt.X-HandOrigin.X;
     dx := pt.X-HandOrigin.X;
     dy := pt.Y-HandOrigin.Y;
     dy := pt.Y-HandOrigin.Y;
     Manager.Image.SelectionTransform := AffineMatrixTranslation(dx,dy) * Manager.Image.SelectionTransform;
     Manager.Image.SelectionTransform := AffineMatrixTranslation(dx,dy) * Manager.Image.SelectionTransform;
-    SelectionTransformChange;
     result := OnlyRenderChange;
     result := OnlyRenderChange;
   end;
   end;
 end;
 end;
@@ -653,7 +604,6 @@ begin
   if rightBtn then penColor := BGRABlack else penColor := BGRAWhite;
   if rightBtn then penColor := BGRABlack else penColor := BGRAWhite;
   toolDest.DrawLineAntialias(ptF.X,ptF.Y,ptF.X,ptF.Y,penColor,Manager.ToolPenWidth,True);
   toolDest.DrawLineAntialias(ptF.X,ptF.Y,ptF.X,ptF.Y,penColor,Manager.ToolPenWidth,True);
   result := GetShapeBounds([ptF],Manager.ToolPenWidth+1);
   result := GetShapeBounds([ptF],Manager.ToolPenWidth+1);
-  Action.NotifyChange(toolDest, result);
 end;
 end;
 
 
 function TToolSelectionPen.ContinueDrawing(toolDest: TBGRABitmap; originF,
 function TToolSelectionPen.ContinueDrawing(toolDest: TBGRABitmap; originF,
@@ -661,7 +611,6 @@ function TToolSelectionPen.ContinueDrawing(toolDest: TBGRABitmap; originF,
 begin
 begin
   toolDest.DrawLineAntialias(destF.X,destF.Y,originF.X,originF.Y,penColor,Manager.ToolPenWidth,False);
   toolDest.DrawLineAntialias(destF.X,destF.Y,originF.X,originF.Y,penColor,Manager.ToolPenWidth,False);
   result := GetShapeBounds([destF,originF],Manager.ToolPenWidth+1);
   result := GetShapeBounds([destF,originF],Manager.ToolPenWidth+1);
-  Action.NotifyChange(toolDest, result);
 end;
 end;
 
 
 { TToolMagicWand }
 { TToolMagicWand }
@@ -681,10 +630,10 @@ begin
     exit;
     exit;
   end;
   end;
   if rightBtn then penColor := BGRABlack else penColor := BGRAWhite;
   if rightBtn then penColor := BGRABlack else penColor := BGRAWhite;
-  Manager.Image.SelectedImageLayerReadOnly.ParallelFloodFill(pt.X,pt.Y,toolDest,penColor,fmDrawWithTransparency,Manager.ToolTolerance);
-  Manager.Image.SelectionMayChangeCompletely;
+  Manager.Image.CurrentLayerReadOnly.ParallelFloodFill(pt.X,pt.Y,toolDest,penColor,fmDrawWithTransparency,Manager.ToolTolerance);
+  result := rect(0,0,toolDest.Width,toolDest.Height);
+  Action.NotifyChange(toolDest, result);
   ValidateAction;
   ValidateAction;
-  result := rect(0,0,Manager.Image.Width,Manager.Image.Height);
 end;
 end;
 
 
 { TToolSelectPoly }
 { TToolSelectPoly }

+ 2 - 11
lazpaint/utooltext.pas

@@ -6,7 +6,7 @@ interface
 
 
 uses
 uses
   Types, Classes, SysUtils, utool, utoolbasic, LCLType, Graphics, BGRABitmap, BGRABitmapTypes, BGRATextFX,
   Types, Classes, SysUtils, utool, utoolbasic, LCLType, Graphics, BGRABitmap, BGRABitmapTypes, BGRATextFX,
-  BGRAGradients, ULayerAction;
+  BGRAGradients;
 
 
 type
 type
 
 
@@ -39,7 +39,6 @@ type
     function TextSize: TSize;
     function TextSize: TSize;
     function TextSizeOf(s: string): TSize;
     function TextSizeOf(s: string): TSize;
     procedure UpdateTextFX;
     procedure UpdateTextFX;
-    function GetAction: TLayerAction; override;
     function DeleteSelection: boolean;
     function DeleteSelection: boolean;
     procedure SelectionFollows;
     procedure SelectionFollows;
     procedure GetCaretPos(AIndex:integer; out caretTop,caretBottom:TPointF);
     procedure GetCaretPos(AIndex:integer; out caretTop,caretBottom:TPointF);
@@ -186,9 +185,7 @@ begin
   end else
   end else
     currentRect := EmptyRect;
     currentRect := EmptyRect;
 
 
-  Action.NotifyChange(toolDest,currentRect);
-  result := RectUnion(currentRect,previousRect);
-  previousRect := currentRect;
+  result := currentRect;
   if IsRectEmpty(result) then result := OnlyRenderChange; //no text but update caret
   if IsRectEmpty(result) then result := OnlyRenderChange; //no text but update caret
 end;
 end;
 
 
@@ -271,12 +268,6 @@ begin
   shader.LightPositionZ := Manager.ToolLightAltitude;
   shader.LightPositionZ := Manager.ToolLightAltitude;
 end;
 end;
 
 
-function TToolText.GetAction: TLayerAction;
-begin
-  Result:=inherited GetAction;
-  result.AllChangesNotified:= true;
-end;
-
 function TToolText.DeleteSelection: boolean;
 function TToolText.DeleteSelection: boolean;
 begin
 begin
   if FTextCharPos <> FTextSelStart then
   if FTextCharPos <> FTextSelStart then

+ 9 - 25
lazpaintcontrols/lctoolbars.pas

@@ -20,7 +20,6 @@ function AddToolbarButton(AToolbar: TToolbar; ACaption: string; AImageIndex: int
           AOnClick: TNotifyEvent; ATag: PtrInt = 0): TToolButton;
           AOnClick: TNotifyEvent; ATag: PtrInt = 0): TToolButton;
 function AddToolbarUpDown(AToolbar: TToolbar; ACaption: string; AMin,AMax,AValue: Integer; AOnChange: TTrackBarUpDownChangeEvent): TBCTrackbarUpdown;
 function AddToolbarUpDown(AToolbar: TToolbar; ACaption: string; AMin,AMax,AValue: Integer; AOnChange: TTrackBarUpDownChangeEvent): TBCTrackbarUpdown;
 procedure AddToolbarControl(AToolbar: TToolbar; AControl: TControl);
 procedure AddToolbarControl(AToolbar: TToolbar; AControl: TControl);
-function GetResourceStream(AFilename: string): TLazarusResourceStream;
 function GetResourceString(AFilename: string): string;
 function GetResourceString(AFilename: string): string;
 procedure LoadToolbarImage(AImages: TImageList; AIndex: integer; AFilename: string);
 procedure LoadToolbarImage(AImages: TImageList; AIndex: integer; AFilename: string);
 
 
@@ -98,41 +97,26 @@ begin
   AToolbar.ButtonHeight:= AImages.Height+4;
   AToolbar.ButtonHeight:= AImages.Height+4;
 end;
 end;
 
 
-function GetResourceStream(AFilename: string): TLazarusResourceStream;
-var
-  ext: RawByteString;
-begin
-  ext := UpperCase(ExtractFileExt(AFilename));
-  if (ext<>'') and (ext[1]='.') then Delete(ext,1,1);
-  result := TLazarusResourceStream.Create(ChangeFileExt(AFilename,''), pchar(ext));
-end;
-
 function GetResourceString(AFilename: string): string;
 function GetResourceString(AFilename: string): string;
 var
 var
-  res: TLResource;
-  ext: RawByteString;
+  strStream: TStringStream;
+  resStream: TStream;
 begin
 begin
-  ext := UpperCase(ExtractFileExt(AFilename));
-  if (ext<>'') and (ext[1]='.') then Delete(ext,1,1);
-  res := LazarusResources.Find(ChangeFileExt(AFilename,''), ext);
-  if assigned(res) then result:= res.Value else result:= '';
+  resStream := BGRAResource.GetResourceStream(AFilename);
+  strStream := TStringStream.Create('');
+  strStream.CopyFrom(resStream, resStream.Size);
+  resStream.Free;
+  result:= strStream.DataString;
+  strStream.Free;
 end;
 end;
 
 
 procedure LoadToolbarImage(AImages: TImageList; AIndex: integer; AFilename: string);
 procedure LoadToolbarImage(AImages: TImageList; AIndex: integer; AFilename: string);
 var
 var
   iconImg: TBGRALazPaintImage;
   iconImg: TBGRALazPaintImage;
   iconFlat: TBGRABitmap;
   iconFlat: TBGRABitmap;
-  res: TLazarusResourceStream;
-  mem: TMemoryStream;
 begin
 begin
   iconImg := TBGRALazPaintImage.Create;
   iconImg := TBGRALazPaintImage.Create;
-  res := GetResourceStream(AFilename);
-  mem:= TMemoryStream.Create;
-  res.SaveToStream(mem);
-  res.Free;
-  mem.Position:= 0;
-  iconImg.LoadFromStream(mem);
-  mem.Free;
+  iconImg.LoadFromResource(AFilename);
   iconImg.Resample(AImages.Width,AImages.Height,rmFineResample,rfBestQuality);
   iconImg.Resample(AImages.Width,AImages.Height,rmFineResample,rfBestQuality);
   iconFlat := TBGRABitmap.Create(iconImg.Width,iconImg.Height);
   iconFlat := TBGRABitmap.Create(iconImg.Width,iconImg.Height);
   iconImg.Draw(iconFlat,0,0);
   iconImg.Draw(iconFlat,0,0);

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно