Просмотр исходного кода

fix crop layer with offseted layers

keep vector original, layer name and opacity, ask to remove transparent pixel around with layers
circular17 6 лет назад
Родитель
Сommit
f17c5d1ff0
2 измененных файлов с 111 добавлено и 49 удалено
  1. 43 35
      lazpaint/image/uimage.pas
  2. 68 14
      lazpaint/image/uimageaction.pas

+ 43 - 35
lazpaint/image/uimage.pas

@@ -187,7 +187,8 @@ type
     procedure VerticalFlip(ALayerIndex: integer); overload;
 
     // whole image
-    procedure Assign(const AValue: TBGRABitmap; AOwned: boolean; AUndoable: boolean); overload;
+    procedure Assign(const AValue: TBGRABitmap; AOwned: boolean; AUndoable: boolean;
+                     ACaption: string = ''; AOpacity: byte = 255); overload;
     procedure Assign(const AValue: TBGRALayeredBitmap; AOwned: boolean; AUndoable: boolean); overload;
     procedure Assign(const AValue: TLayeredBitmapAndSelection; AOwned: boolean; AUndoable: boolean); overload;
 
@@ -315,26 +316,41 @@ end;
 
 function TLazPaintImage.MakeCroppedLayer: TBGRABitmap;
 var r: TRect;
+  cropped: TBGRABitmap;
+  ofs: TPoint;
 begin
+  ofs := Point(0,0);
   result := DuplicateBitmap(FCurrentState.SelectionLayer);
   if (result <> nil) and (SelectionMask <> nil) then result.ApplyMask(SelectionMask);
   if (result <> nil) and result.Empty then FreeAndNil(result);
   if result = nil then
   begin
+    ofs := LayerOffset[CurrentLayerIndex];
     result := DuplicateBitmap(GetSelectedImageLayer);
-    if (result <> nil) and (SelectionMask <> nil) then result.ApplyMask(SelectionMask);
+    if (result <> nil) and (SelectionMask <> nil) then
+      result.ApplyMask(SelectionMask, rect(0,0,result.Width,result.Height),
+                       Point(ofs.X,ofs.Y));
   end;
   if result <> nil then
   begin
     if SelectionMask = nil then
       r := result.GetImageBounds
     else
+    begin
       r := SelectionMaskBounds;
+      OffsetRect(r, -ofs.x, -ofs.y);
+    end;
     if IsRectEmpty(r) then
       FreeAndNil(result)
     else
+    begin
       if (r.left <> 0) or (r.top <> 0) or (r.right <> result.Width) or (r.bottom <> result.Height) then
-        BGRAReplace(result, result.GetPart(r));
+      begin
+        cropped := TBGRABitmap.Create(r.Width,r.Height);
+        cropped.PutImage(-r.Left, -r.Top, result, dmSet);
+        BGRAReplace(result, cropped);
+      end;
+    end;
   end;
 end;
 
@@ -1669,48 +1685,40 @@ begin
   if Assigned(Zoom) then result := Zoom.Factor else result := 1;
 end;
 
-procedure TLazPaintImage.Assign(const AValue: TBGRABitmap; AOwned: boolean; AUndoable: boolean);
+procedure TLazPaintImage.Assign(const AValue: TBGRABitmap; AOwned: boolean; AUndoable: boolean;
+  ACaption: string; AOpacity: byte);
 var layeredBmp: TBGRALayeredBitmap;
   mask: TBGRABitmap;
 begin
   if not CheckNoAction then exit;
   CursorHotSpot := AValue.HotSpot;
-  if not AUndoable then
+  layeredBmp := TBGRALayeredBitmap.Create(AValue.Width,AValue.Height);
+  if AOwned then
   begin
-    FCurrentState.Assign(AValue, AOwned);
-    FCurrentState.RemoveSelection;
-    LayeredBitmapReplaced;
-    ImageMayChangeCompletely;
-    SelectionMaskMayChangeCompletely;
-    ClearUndo;
-  end else
-  begin
-    layeredBmp := TBGRALayeredBitmap.Create(AValue.Width,AValue.Height);
-    if AOwned then
+    layeredBmp.AddOwnedLayer(AValue);
+    if Assigned(AValue.XorMask) then
     begin
-      layeredBmp.AddOwnedLayer(AValue);
-      if Assigned(AValue.XorMask) then
-      begin
-        mask := AValue.XorMask.Duplicate as TBGRABitmap;
-        mask.AlphaFill(255);
-        mask.ReplaceColor(BGRABlack,BGRAPixelTransparent);
-        layeredBmp.LayerName[layeredBmp.AddOwnedLayer(mask,boXor)] := 'Xor';
-        AValue.DiscardXorMask;
-      end;
-    end
-    else
+      mask := AValue.XorMask.Duplicate as TBGRABitmap;
+      mask.AlphaFill(255);
+      mask.ReplaceColor(BGRABlack,BGRAPixelTransparent);
+      layeredBmp.LayerName[layeredBmp.AddOwnedLayer(mask,boXor)] := 'Xor';
+      AValue.DiscardXorMask;
+    end;
+  end
+  else
+  begin
+    layeredBmp.AddLayer(AValue);
+    if Assigned(AValue.XorMask) then
     begin
-      layeredBmp.AddLayer(AValue);
-      if Assigned(AValue.XorMask) then
-      begin
-        mask := AValue.XorMask.Duplicate as TBGRABitmap;
-        mask.AlphaFill(255);
-        mask.ReplaceColor(BGRABlack,BGRAPixelTransparent);
-        layeredBmp.LayerName[layeredBmp.AddOwnedLayer(mask,boXor)] := 'Xor';
-      end;
+      mask := AValue.XorMask.Duplicate as TBGRABitmap;
+      mask.AlphaFill(255);
+      mask.ReplaceColor(BGRABlack,BGRAPixelTransparent);
+      layeredBmp.LayerName[layeredBmp.AddOwnedLayer(mask,boXor)] := 'Xor';
     end;
-    Assign(layeredBmp,True,AUndoable);
   end;
+  layeredBmp.LayerName[0] := ACaption;
+  layeredBmp.LayerOpacity[0] := AOpacity;
+  Assign(layeredBmp,True,AUndoable);
 end;
 
 procedure TLazPaintImage.Assign(const AValue: TBGRALayeredBitmap;

+ 68 - 14
lazpaint/image/uimageaction.pas

@@ -30,7 +30,8 @@ type
     function SmartZoom3: boolean;
     procedure Undo;
     procedure Redo;
-    procedure SetCurrentBitmap(bmp: TBGRABitmap; AUndoable: boolean);
+    procedure SetCurrentBitmap(bmp: TBGRABitmap; AUndoable: boolean;
+      ACaption: string = ''; AOpacity: byte = 255);
     procedure CropToSelectionAndLayer;
     procedure CropToSelection;
     procedure HorizontalFlip(AOption: TFlipOption);
@@ -388,7 +389,7 @@ begin
           end;
           FInstance.ShowTopmost(top);
         end;
-        SetCurrentBitmap(partial,true);
+        SetCurrentBitmap(partial,true,image.LayerName[image.CurrentLayerIndex],image.LayerOpacity[image.CurrentLayerIndex]);
       end
       else
         partial.Free;
@@ -401,15 +402,14 @@ end;
 
 procedure TImageActions.CropToSelection;
 var cropped: TLayeredBitmapAndSelection;
-    r: TRect;
+    r, subBounds: TRect;
     i,selectedLayer: integer;
+    ofs: TPoint;
+    tempLayer, flattened: TBGRABitmap;
+    selectionIsRect: Boolean;
+    top: TTopMostInfo;
 begin
   if not image.CheckNoAction then exit;
-  if image.NbLayers = 1 then
-  begin
-    CropToSelectionAndLayer;
-    exit;
-  end;
   try
     if image.SelectionMaskEmpty then
     begin
@@ -421,15 +421,68 @@ begin
       r := image.SelectionMaskBounds;
       if (r.left = 0) and (r.Top = 0) and (r.right = image.width) and (r.Bottom =image.height) then exit;
       cropped := image.MakeLayeredBitmapAndSelectionCopy;
+      BGRAReplace(cropped.selection,cropped.selection.GetPart(r));
+      selectionIsRect := cropped.selection.Equals(BGRAWhite);
+      if cropped.selectionLayer <> nil then BGRAReplace(cropped.selectionLayer,cropped.selectionLayer.GetPart(r));
       selectedLayer := image.CurrentLayerIndex;
       for i := 0 to cropped.layeredBitmap.NbLayers-1 do
       begin
-        cropped.layeredBitmap.LayerBitmap[i].ApplyMask(cropped.selection);
-        cropped.layeredBitmap.SetLayerBitmap(i, cropped.layeredBitmap.LayerBitmap[i].GetPart(r) as TBGRABitmap, true);
+        tempLayer := TBGRABitmap.Create(r.Width,r.Height);
+        if selectionIsRect and (cropped.layeredBitmap.LayerOriginalGuid[i]<>GUID_NULL) and
+          cropped.layeredBitmap.LayerOriginalKnown[i] then
+        begin
+          ofs := cropped.layeredBitmap.LayerOffset[i];
+          cropped.layeredBitmap.LayerOriginalMatrix[i] :=
+             AffineMatrixTranslation(-r.Left, -r.Top)*
+             cropped.layeredBitmap.LayerOriginalMatrix[i];
+          cropped.layeredBitmap.RenderLayerFromOriginal(i);
+        end else
+        begin
+          ofs := cropped.layeredBitmap.LayerOffset[i];
+          tempLayer.PutImage(ofs.x-r.Left,ofs.y-r.Top, cropped.layeredBitmap.LayerBitmap[i], dmSet);
+          tempLayer.ApplyMask(cropped.selection);
+          cropped.layeredBitmap.SetLayerBitmap(i, tempLayer, true);
+          cropped.layeredBitmap.LayerOffset[i] := Point(0,0);
+        end;
+      end;
+      if cropped.selectionLayer = nil then
+      begin
+        FreeAndNil(cropped.selection);
+        if (CurrentTool in [ptMoveSelection,ptRotateSelection]) then
+          ChooseTool(ptHand);
       end;
       cropped.layeredBitmap.SetSize(r.right-r.left,r.Bottom-r.top);
-      BGRAReplace(cropped.selection,cropped.selection.GetPart(r));
-      if cropped.selectionLayer <> nil then BGRAReplace(cropped.selectionLayer,cropped.selectionLayer.GetPart(r));
+      cropped.layeredBitmap.RemoveUnusedOriginals;
+      flattened := cropped.layeredBitmap.ComputeFlatImage;
+      subBounds := flattened.GetImageBounds;
+      flattened.Free;
+      if cropped.selectionLayer<>nil then
+        subBounds := RectUnion(subBounds, cropped.selectionLayer.GetImageBounds);
+      if (subBounds.Left > 0) or (subBounds.Top > 0) or
+        (subBounds.Right < cropped.layeredBitmap.Width) or (subBounds.Bottom < cropped.layeredBitmap.Height) then
+      begin
+        top := FInstance.HideTopmost;
+        case MessageDlg(rsCrop,rsKeepEmptySpace,mtConfirmation,mbYesNo,0) of
+        mrNo: begin
+            for i := 0 to cropped.layeredBitmap.NbLayers-1 do
+            begin
+              if cropped.layeredBitmap.LayerOriginalGuid[i]=GUID_NULL then
+              begin
+                ofs := cropped.layeredBitmap.LayerOffset[i];
+                cropped.layeredBitmap.LayerOffset[i] := Point(ofs.x-subBounds.Left,ofs.y-subBounds.Top);
+              end else
+              begin
+                cropped.layeredBitmap.LayerOriginalMatrix[i] :=
+                  AffineMatrixTranslation(-subBounds.Left,-subBounds.Top)*
+                  cropped.layeredBitmap.LayerOriginalMatrix[i];
+                cropped.layeredBitmap.RenderLayerFromOriginal(i);
+              end;
+            end;
+            cropped.layeredBitmap.SetSize(subBounds.Width, subBounds.Height);
+          end;
+        end;
+        FInstance.ShowTopmost(top);
+      end;
       image.Assign(cropped,true,true);
       image.SetCurrentLayerByIndex(selectedLayer);
     end;
@@ -439,11 +492,12 @@ begin
   end;
 end;
 
-procedure TImageActions.SetCurrentBitmap(bmp: TBGRABitmap; AUndoable : boolean);
+procedure TImageActions.SetCurrentBitmap(bmp: TBGRABitmap; AUndoable : boolean;
+  ACaption: string; AOpacity: byte);
 begin
   ToolManager.ToolCloseDontReopen;
   try
-    image.Assign(bmp,True,AUndoable);
+    image.Assign(bmp,True,AUndoable, ACaption,AOpacity);
   finally
     ToolManager.ToolOpen;
   end;