Browse Source

load SVG as vector, refactoring, nice render, message too many layers

Unknown 6 năm trước cách đây
mục cha
commit
0622ba8393

+ 42 - 6
lazpaint/lazpaintmainform.pas

@@ -13,7 +13,7 @@ uses
   Controls, Graphics, Dialogs, Menus, ExtDlgs, ComCtrls, ActnList, StdCtrls,
   ExtCtrls, Buttons, types, LCLType, BGRAImageList, BGRAVirtualScreen,
 
-  BGRABitmap, BGRABitmapTypes, BGRALayers,
+  BGRABitmap, BGRABitmapTypes, BGRALayers, BGRASVGOriginal,
 
   LazPaintType, UMainFormLayout, UTool, UImage, UImageAction, ULayerAction, UZoom, UImageView,
   UImageObservation, UConfig, UScaleDPI, UResourceStrings,
@@ -823,7 +823,7 @@ implementation
 
 uses LCLIntf, BGRAUTF8, ugraph, math, umac, uclipboard, ucursors,
    ufilters, ULoadImage, ULoading, UFileExtensions, UBrushType,
-   ugeometricbrush, UPreviewDialog, UQuestion;
+   ugeometricbrush, UPreviewDialog, UQuestion, BGRALayerOriginal;
 
 const PenWidthFactor = 10;
 
@@ -1816,10 +1816,12 @@ var
   Errors: String='';
   loadedLayers: array of record
      bmp: TBGRABitmap;
+     orig: TBGRALayerCustomOriginal;
      filename: string;
   end;
   topmost: TTopMostInfo;
   choice: TModalResult;
+  svgOrig: TBGRALayerSVGOriginal;
 begin
   if Length(FileNames)<1 then exit;
   if Length(FileNames)= 1
@@ -1850,9 +1852,21 @@ begin
                   MessagePopupForever(rsLoading + ' ' + inttostr(i+1) + '/' + inttostr(length(FileNames)));
                   LazPaintInstance.UpdateWindows;
                   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;
                 except on ex:exception do
                   //begin
@@ -1869,9 +1883,14 @@ begin
                   Image.Assign(TBGRABitmap.Create(tx,ty),true,false);
                   ZoomFitIfTooBig;
                   for i := 0 to high(loadedLayers) do
+                  if Assigned(loadedLayers[i].bmp) then
                   begin
                     FImageActions.AddLayerFromBitmap(loadedLayers[i].bmp,ExtractFileName(loadedLayers[i].filename));
                     loadedLayers[i].bmp := nil;
+                  end else
+                  begin
+                    FImageActions.AddLayerFromOriginal(loadedLayers[i].orig,ExtractFileName(loadedLayers[i].filename));
+                    loadedLayers[i].orig := nil;
                   end;
                 end;
               except on ex:exception do
@@ -1890,7 +1909,10 @@ begin
                 LazPaintInstance.ShowTopmost(topmost);
               end;
               for i := 0 to high(loadedLayers) do
+              begin
                 FreeAndNil(loadedLayers[i].bmp);
+                FreeAndNil(loadedLayers[i].orig);
+              end;
           end;  //OpenFilesAsLayers
        mrLast+2: begin
              if not LazPaintInstance.ImageListWindowVisible then
@@ -3321,12 +3343,22 @@ var
       with ComputeAcceptableImageSize(newPicture.bmp.Width,newPicture.bmp.Height) do
         if (cx < newPicture.bmp.Width) or (cy < newPicture.bmp.Height) then
           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;
       EndImport(newPicture.bpp, newPicture.frameIndex);
     end else FreeAndNil(newPicture.bmp);
   end;
 
+  procedure ImportSvg;
+  var
+    layered: TBGRALayeredBitmap;
+  begin
+    StartImport;
+    layered := LoadSVGImageUTF8(filenameUTF8);
+    Image.Assign(layered,true, false);
+    EndImport;
+  end;
+
 begin
   result := false;
   if filenameUTF8 = '' then exit;
@@ -3339,6 +3371,10 @@ begin
   newPicture := TImageEntry.Empty;
   try
     format := Image.DetectImageFormat(filenameUTF8);
+    if format = ifSvg then
+    begin
+      ImportSvg;
+    end else
     if Assigned(ALoadedImage) and Assigned(ALoadedImage^.bmp) then
     begin
       newPicture := ALoadedImage^;

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

@@ -3455,6 +3455,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""

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

@@ -3442,6 +3442,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""

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

@@ -3460,6 +3460,10 @@ msgstr "Bilder verbleibend: %1"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr "Zu viel Ebene"
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Bilder Gesamt: %1"

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

@@ -3458,6 +3458,10 @@ msgstr "Quedan: %1"
 msgid "Tool cannot be used on an invisible layer"
 msgstr "La herramienta no puede ser usada en una capa invisible"
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr "Demasiado capas"
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Total: %1"

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

@@ -3425,6 +3425,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Kuvia kaikkiaan: %1"

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

@@ -3461,6 +3461,10 @@ msgstr "Images restantes : %1"
 msgid "Tool cannot be used on an invisible layer"
 msgstr "L'outil ne peut pas être utilisé sur un calque invisible"
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr "Trop de calques"
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Nb. total d'images : %1"

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

@@ -3447,6 +3447,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""

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

@@ -3443,6 +3443,10 @@ msgstr "Palicis: %1"
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr "Attēlu kopskaits: %1"

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

@@ -3469,6 +3469,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""

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

@@ -3424,6 +3424,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""

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

@@ -3451,6 +3451,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""

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

@@ -3445,6 +3445,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""

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

@@ -3432,6 +3432,10 @@ msgstr ""
 msgid "Tool cannot be used on an invisible layer"
 msgstr ""
 
+#: uresourcestrings.rstoomanylayers
+msgid "Too many layers"
+msgstr ""
+
 #: uresourcestrings.rstotalimages
 msgid "Total images: %1"
 msgstr ""

+ 98 - 30
lazpaint/uimageaction.pas

@@ -6,7 +6,7 @@ interface
 
 uses
   Classes, SysUtils, LazPaintType, BGRABitmap, UImage, UTool, UScripting,
-  ULayerAction, UImageType, BGRABitmapTypes;
+  ULayerAction, UImageType, BGRABitmapTypes, BGRALayerOriginal, BGRASVGOriginal;
 
 type
 
@@ -21,6 +21,7 @@ type
     procedure ChooseTool(ATool: TPaintToolType);
     procedure RegisterScripts(ARegister: Boolean);
     function GenericScriptFunction(AVars: TVariableSet): TScriptResult;
+    procedure ReleaseSelection;
   public
     constructor Create(AInstance: TLazPaintCustomInstance);
     destructor Destroy; override;
@@ -50,8 +51,9 @@ type
     procedure PasteAsNewLayer;
     procedure SelectAll;
     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 MergeLayerOver;
     procedure RemoveLayer;
@@ -59,6 +61,7 @@ type
     procedure Import3DObject(AFilenameUTF8: string);
     function TryAddLayerFromFile(AFilenameUTF8: string; ALoadedImage: TBGRABitmap = nil): boolean;
     function AddLayerFromBitmap(ABitmap: TBGRABitmap; AName: string): boolean;
+    function AddLayerFromOriginal(AOriginal: TBGRALayerCustomOriginal; AName: string): boolean;
     function LoadSelection(AFilenameUTF8: string; ALoadedImage: PImageEntry = nil): boolean;
     property Image: TLazPaintImage read GetImage;
     property ToolManager: TToolManager read GetToolManager;
@@ -445,6 +448,7 @@ end;
 function TImageActions.TryAddLayerFromFile(AFilenameUTF8: string; ALoadedImage: TBGRABitmap = nil): boolean;
 var
   newPicture: TBGRABitmap;
+  svgOrig: TBGRALayerSVGOriginal;
 begin
   result := false;
   if not AbleToLoadUTF8(AFilenameUTF8) then
@@ -454,21 +458,34 @@ begin
     exit;
   end;
   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
     on ex: Exception do
+    begin
+      ALoadedImage.Free;
       FInstance.ShowError('TryAddLayerFromFile',ex.Message);
+    end;
   end;
 end;
 
-function TImageActions.AddLayerFromBitmap(ABitmap: TBGRABitmap; AName: string
-  ): boolean;
+function TImageActions.AddLayerFromBitmap(ABitmap: TBGRABitmap; AName: string): boolean;
 var
-  layeraction: TLayerAction;
   ratio: single;
   xorMask: TBGRABitmap;
 begin
@@ -478,13 +495,7 @@ begin
       ChooseTool(ptHand);
     if image.CheckNoAction then
     begin
-      if not Image.SelectionMaskEmpty then
-      begin
-        layeraction := image.CreateAction;
-        layeraction.ReleaseSelection;
-        layeraction.Validate;
-        layeraction.Free;
-      end;
+      if not Image.SelectionMaskEmpty then ReleaseSelection;
       if (ABitmap.Width > Image.Width) or (ABitmap.Height > Image.Height) then
       begin
         ratio := 1;
@@ -502,10 +513,17 @@ begin
       end
       else
         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
     begin
       ABitmap.Free;
@@ -518,6 +536,29 @@ begin
   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);
 begin
   try
@@ -625,13 +666,7 @@ begin
   if not Image.CheckNoAction then exit;
   LayerAction := nil;
   try
-    if not image.SelectionMaskEmpty then
-    begin
-      LayerAction := Image.CreateAction;
-      LayerAction.ChangeBoundsNotified:= true;
-      LayerAction.ReleaseSelection;
-      LayerAction.Validate;
-    end;
+    if not image.SelectionMaskEmpty then ReleaseSelection;
   except
     on ex:Exception do
       FInstance.ShowError('Deselect',ex.Message);
@@ -760,6 +795,16 @@ begin
   LayerAction.Free;
 end;
 
+procedure TImageActions.ReleaseSelection;
+var
+  layeraction: TLayerAction;
+begin
+  layeraction := image.CreateAction;
+  layeraction.ReleaseSelection;
+  layeraction.Validate;
+  layeraction.Free;
+end;
+
 procedure TImageActions.Paste;
 var partial: TBGRABitmap;
     layeraction: TLayerAction;
@@ -902,12 +947,35 @@ begin
   end;
 end;
 
-procedure TImageActions.NewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation);
+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;
+
+function TImageActions.NewLayer(ALayer: TBGRALayerCustomOriginal;
+  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;
 

+ 2 - 0
lazpaint/uimagediff.pas

@@ -741,6 +741,7 @@ begin
     idx := LayeredBitmap.AddLayerFromOriginal(LayeredBitmap.Original[origIdx].Guid, self.blendOp);
     LayeredBitmap.LayerUniqueId[idx] := self.layerId;
     LayeredBitmap.LayerName[idx] := name;
+    LayeredBitmap.RenderLayerFromOriginal(idx);
     SelectedImageLayerIndex := idx;
   end;
 end;
@@ -776,6 +777,7 @@ begin
   idx := imgDest.LayeredBitmap.AddLayerFromOwnedOriginal(AOriginal, ABlendOp);
   imgDest.LayeredBitmap.LayerName[idx] := name;
   self.layerId := imgDest.LayeredBitmap.LayerUniqueId[idx];
+  imgDest.LayeredBitmap.RenderLayerFromOriginal(idx);
   imgDest.SelectedImageLayerIndex := idx;
 end;
 

+ 4 - 1
lazpaint/uimagestate.pas

@@ -747,12 +747,15 @@ end;
 
 function TImageState.AddNewLayer(AOriginal: TBGRALayerCustomOriginal;
   AName: string; ABlendOp: TBlendOperation): TCustomImageDifference;
+var
+  idx: Integer;
 begin
   //no undo if no previous image
   if LayeredBitmap = nil then
   begin
     SetLayeredBitmap(TBGRALayeredBitmap.Create);
-    LayeredBitmap.AddLayerFromOwnedOriginal(AOriginal, ABlendOp);
+    idx := LayeredBitmap.AddLayerFromOwnedOriginal(AOriginal, ABlendOp);
+    LayeredBitmap.RenderLayerFromOriginal(idx);
     result := nil;
   end else
     result := TAddLayerFromOwnedOriginalStateDifference.Create(self, AOriginal, AName, ABlendOp);

+ 32 - 2
lazpaint/uloadimage.pas

@@ -5,18 +5,20 @@ unit ULoadImage;
 interface
 
 uses
-  Classes, SysUtils, LazPaintType, BGRABitmap;
+  Classes, SysUtils, LazPaintType, BGRABitmap, BGRALayers, BGRASVGOriginal;
 
 function LoadFlatImageUTF8(AFilename: string; ASkipDialog: boolean = false): TImageEntry;
 procedure FreeMultiImage(var images: ArrayOfImageEntry);
 function AbleToLoadUTF8(AFilename: string): boolean;
+function LoadSVGImageUTF8(AFilename: string): TBGRALayeredBitmap;
+function LoadSVGOriginalUTF8(AFilename: string): TBGRALayerSVGOriginal;
 
 implementation
 
 uses FileUtil, BGRAAnimatedGif, Graphics, UMultiImage,
   BGRAReadLzp, LCLProc, BGRABitmapTypes, BGRAReadPng,
   UFileSystem, BGRAIconCursor, BGRAReadTiff,
-  Dialogs;
+  Dialogs, math;
 
 function LoadIcoMultiImageFromStream(AStream: TStream): ArrayOfImageEntry;
 var ico: TBGRAIconCursor; i: integer;
@@ -126,6 +128,34 @@ begin
   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;
 var
   formMultiImage: TFMultiImage;

+ 1 - 0
lazpaint/uresourcestrings.pas

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