浏览代码

floodfill with solid color preserve vectors

johann 5 年之前
父节点
当前提交
ab9b412d90

+ 1 - 0
lazpaint/tools/utool.pas

@@ -484,6 +484,7 @@ type
     procedure SetDeformationGridSize(ASize: TSize);
 
     property Image: TLazPaintImage read FImage;
+    property Scripting: TScriptContext read FScriptContext;
     property BlackAndWhite: boolean read FBlackAndWhite write FBlackAndWhite;
     property CurrentTool: TGenericTool read FCurrentTool;
     property ToolCurrentCursorPos: TPointF read FToolCurrentCursorPos;

+ 94 - 34
lazpaint/tools/utoolfloodfill.pas

@@ -40,7 +40,7 @@ type
 implementation
 
 uses ugraph, LazPaintType, BGRAGradientScanner, LCVectorRectShapes,
-  BGRATransform, UImageDiff, BGRAPen;
+  BGRATransform, UImageDiff, BGRAPen, UScripting, BGRABlend;
 
 { TToolGradient }
 
@@ -124,47 +124,107 @@ var
   diff: TCustomImageDifference;
   rectShape: TRectShape;
   homogeneous: Boolean;
+  params: TVariableSet;
+  vectOrig: TVectorOriginal;
+  i: Integer;
+  ptOrig: TPointF;
+  newColor: TBGRAPixel;
 begin
+  result := OnlyRenderChange;
   homogeneous := toolDest.Equals(toolDest.GetPixel(0,0));
-  if Manager.Image.SelectionMaskEmpty and homogeneous then
+  if Manager.Image.SelectionMaskEmpty then
   begin
-    CancelAction;
-    if rightBtn then f := Manager.BackFill.Duplicate
-    else f := Manager.ForeFill.Duplicate;
-    try
-      f.ApplyOpacity(Manager.GetPressureB);
-      f.FitGeometry(SuggestGradientBox);
-      case f.FillType of
-      vftGradient: orig := f.Gradient.Duplicate;
-      else
+    if homogeneous then
+    begin
+      CancelAction;
+      if rightBtn then f := Manager.BackFill.Duplicate
+      else f := Manager.ForeFill.Duplicate;
+      try
+        f.ApplyOpacity(Manager.GetPressureB);
+        f.FitGeometry(SuggestGradientBox);
+        case f.FillType of
+        vftGradient: orig := f.Gradient.Duplicate;
+        else
+          begin
+            orig := TVectorOriginal.Create;
+            rectShape := TRectShape.Create(nil);
+            rectShape.QuickDefine(PointF(-0.5,-0.5), PointF(Manager.Image.Width-0.5,Manager.Image.Height-0.5));
+            rectShape.PenStyle := ClearPenStyle;
+            rectShape.BackFill.Assign(f);
+            TVectorOriginal(orig).AddShape(rectShape);
+          end;
+        end;
+        diff := TReplaceLayerByCustomOriginalDifference.Create(Manager.Image.CurrentState,
+                      Manager.Image.CurrentLayerIndex, false, orig);
+        Manager.Image.AddUndo(diff);
+        Manager.Image.ImageMayChangeCompletely;
+      finally
+        f.Free;
+      end;
+      exit;
+    end else
+    if GetCurrentLayerKind = lkVectorial then
+    begin
+      if rightBtn then f := Manager.BackFill else f := Manager.ForeFill;
+      if f.FillType = vftSolid then
+      begin
+        vectOrig := TVectorOriginal(Manager.Image.LayerOriginal[Manager.Image.CurrentLayerIndex]);
+        ptOrig := AffineMatrixInverse(Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex]) * ptF;
+        for i := vectOrig.ShapeCount-1 downto 0 do
+        begin
+          if (vsfPenFill in vectOrig.Shape[i].Fields) and
+            vectOrig.Shape[i].PointInPen(ptOrig) then
+          begin
+            if not (vectOrig.Shape[i].PenFill.FillType = vftSolid) then break;
+            CancelAction;
+            Manager.Image.CurrentState.DiscardOriginalDiff:= false;
+            try
+              newColor := vectOrig.Shape[i].PenFill.SolidColor;
+              DrawPixelsInline(@newColor, f.SolidColor, 1);
+              vectOrig.Shape[i].PenFill.SetSolid(newColor);
+            finally
+              Manager.Image.CurrentState.DiscardOriginalDiff:= true;
+            end;
+            exit;
+          end;
+          if (vsfBackFill in vectOrig.Shape[i].Fields) and
+            vectOrig.Shape[i].PointInBack(ptOrig) then
+          begin
+            if not (vectOrig.Shape[i].BackFill.FillType = vftSolid) then break;
+            CancelAction;
+            Manager.Image.CurrentState.DiscardOriginalDiff:= false;
+            try
+              newColor := vectOrig.Shape[i].BackFill.SolidColor;
+              DrawPixelsInline(@newColor, f.SolidColor, 1);
+              vectOrig.Shape[i].BackFill.SetSolid(newColor);
+            finally
+              Manager.Image.CurrentState.DiscardOriginalDiff:= true;
+            end;
+            exit;
+          end;
+        end;
+        if (toolDest.GetPixel(pt.X,pt.Y).alpha = 0)
+          and Assigned(Manager.Scripting) then
         begin
-          orig := TVectorOriginal.Create;
-          rectShape := TRectShape.Create(nil);
-          rectShape.QuickDefine(PointF(-0.5,-0.5), PointF(Manager.Image.Width-0.5,Manager.Image.Height-0.5));
-          rectShape.PenStyle := ClearPenStyle;
-          rectShape.BackFill.Assign(f);
-          TVectorOriginal(orig).AddShape(rectShape);
+          CancelAction;
+          params := TVariableSet.Create('ImageFillBackground');
+          params.Pixels['BackColor'] := f.SolidColor;
+          Manager.Scripting.CallScriptFunction(params);
+          params.Free;
+          exit;
         end;
       end;
-      diff := TReplaceLayerByCustomOriginalDifference.Create(Manager.Image.CurrentState,
-                    Manager.Image.CurrentLayerIndex, false, orig);
-      Manager.Image.AddUndo(diff);
-      Manager.Image.ImageMayChangeCompletely;
-    finally
-      f.Free;
     end;
-  end else
-  begin
-    if rightBtn then b := GetBackUniversalBrush
-    else b := GetForeUniversalBrush;
-    if homogeneous then toolDest.Fill(b)
-      else toolDest.FloodFill(pt.X, pt.Y, b,
-                    ffProgressive in Manager.FloodFillOptions, Manager.Tolerance*$101);
-    ReleaseUniversalBrushes;
-    Action.NotifyChange(toolDest, rect(0,0,toolDest.Width,toolDest.Height));
-    ValidateAction;
   end;
-  result := OnlyRenderChange;
+
+  if rightBtn then b := GetBackUniversalBrush
+  else b := GetForeUniversalBrush;
+  if homogeneous then toolDest.Fill(b)
+    else toolDest.FloodFill(pt.X, pt.Y, b,
+                  ffProgressive in Manager.FloodFillOptions, Manager.Tolerance*$101);
+  ReleaseUniversalBrushes;
+  Action.NotifyChange(toolDest, rect(0,0,toolDest.Width,toolDest.Height));
+  ValidateAction;
 end;
 
 function TToolFloodFill.GetContextualToolbars: TContextualToolbars;

+ 12 - 0
lazpaintcontrols/lcvectororiginal.pas

@@ -218,6 +218,8 @@ type
     function SuggestGradientBox(AMatrix: TAffineMatrix): TAffineBox; virtual;
     function PointInShape(APoint: TPointF): boolean; overload; virtual; abstract;
     function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; virtual; abstract;
+    function PointInBack(APoint: TPointF): boolean; overload; virtual;
+    function PointInPen(APoint: TPointF): boolean; overload; virtual;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); virtual; abstract;
     procedure ConfigureEditor(AEditor: TBGRAOriginalEditor); virtual;
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); virtual;
@@ -1960,6 +1962,16 @@ begin
   result := TAffineBox.AffineBox(rF);
 end;
 
+function TVectorShape.PointInBack(APoint: TPointF): boolean;
+begin
+  result := false;
+end;
+
+function TVectorShape.PointInPen(APoint: TPointF): boolean;
+begin
+  result := false;
+end;
+
 procedure TVectorShape.ConfigureEditor(AEditor: TBGRAOriginalEditor);
 begin
   if (Usermode = vsuEditBackFill) and BackFill.IsEditable then

+ 34 - 0
lazpaintcontrols/lcvectorpolyshapes.pas

@@ -145,6 +145,8 @@ type
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
+    function PointInBack(APoint: TPointF): boolean; overload; override;
+    function PointInPen(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
     class function StorageClassName: RawByteString; override;
   end;
@@ -1288,6 +1290,38 @@ begin
   result := IsPointInPolygon(pts, APoint, true);
 end;
 
+function TPolylineShape.PointInBack(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
+  scan: TBGRACustomScanner;
+begin
+  if BackVisible then
+  begin
+    pts := GetCurve(AffineMatrixIdentity);
+    result := IsPointInPolygon(pts, APoint, true);
+    if result and (BackFill.FillType = vftTexture) then
+    begin
+      scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
+      if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
+      scan.Free;
+    end;
+  end else
+    result := false;
+end;
+
+function TPolylineShape.PointInPen(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
+begin
+  if BackVisible then
+  begin
+    pts := GetCurve(AffineMatrixIdentity);
+    pts := ComputeStroke(pts, Closed, AffineMatrixIdentity);
+    result := IsPointInPolygon(pts, APoint, true);
+  end else
+    result := false;
+end;
+
 function TPolylineShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 var pts: ArrayOfTPointF;
   i: Integer;

+ 82 - 0
lazpaintcontrols/lcvectorrectshapes.pas

@@ -105,6 +105,8 @@ type
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
+    function PointInBack(APoint: TPointF): boolean; overload; override;
+    function PointInPen(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     class function StorageClassName: RawByteString; override;
   end;
@@ -124,6 +126,8 @@ type
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
+    function PointInBack(APoint: TPointF): boolean; overload; override;
+    function PointInPen(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     class function StorageClassName: RawByteString; override;
   end;
@@ -190,6 +194,7 @@ type
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape(APoint: TPointF; ARadius: single): boolean; overload; override;
+    function PointInBack(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const AMatrix: TAffineMatrix): boolean; override;
     function GetGenericCost: integer; override;
     procedure Transform(const AMatrix: TAffineMatrix); override;
@@ -1111,6 +1116,38 @@ begin
   else result := false;
 end;
 
+function TRectShape.PointInBack(APoint: TPointF): boolean;
+var
+  box: TAffineBox;
+  scan: TBGRACustomScanner;
+begin
+  if BackVisible then
+  begin
+    box := GetAffineBox(AffineMatrixIdentity, true);
+    result := box.Contains(APoint);
+    if result and (BackFill.FillType = vftTexture) then
+    begin
+      scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
+      if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
+      scan.Free;
+    end;
+  end else
+    result := false;
+end;
+
+function TRectShape.PointInPen(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
+begin
+  if PenVisible then
+  begin
+    pts := GetAffineBox(AffineMatrixIdentity, true).AsPolygon;
+    pts := ComputeStroke(pts,true, AffineMatrixIdentity);
+    result:= IsPointInPolygon(pts, APoint, true);
+  end else
+    result := false;
+end;
+
 class function TRectShape.StorageClassName: RawByteString;
 begin
   result := 'rect';
@@ -1356,6 +1393,38 @@ begin
     result := false;
 end;
 
+function TEllipseShape.PointInBack(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
+  scan: TBGRACustomScanner;
+begin
+  if BackVisible then
+  begin
+    pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
+    result:= IsPointInPolygon(pts, APoint, true);
+    if result and (BackFill.FillType = vftTexture) then
+    begin
+      scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
+      if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
+      scan.Free;
+    end;
+  end else
+    result := false;
+end;
+
+function TEllipseShape.PointInPen(APoint: TPointF): boolean;
+var
+  pts: ArrayOfTPointF;
+begin
+  if PenVisible then
+  begin
+    pts := ComputeEllipse(FOrigin, FXAxis, FYAxis);
+    pts := ComputeStroke(pts,true, AffineMatrixIdentity);
+    result:= IsPointInPolygon(pts, APoint, true);
+  end else
+    result := false;
+end;
+
 function TEllipseShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 var
   ab: TAffineBox;
@@ -1760,6 +1829,19 @@ begin
     else result := false;
 end;
 
+function TPhongShape.PointInBack(APoint: TPointF): boolean;
+var
+  scan: TBGRACustomScanner;
+begin
+  result := PointInShape(APoint);
+  if result and (BackFill.FillType = vftTexture) then
+  begin
+    scan := BackFill.CreateScanner(AffineMatrixIdentity, false);
+    if scan.ScanAt(APoint.X,APoint.Y).alpha = 0 then result := false;
+    scan.Free;
+  end;
+end;
+
 function TPhongShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 var
   ab: TAffineBox;

+ 18 - 1
lazpaintcontrols/lcvectortextshapes.pas

@@ -157,6 +157,7 @@ type
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
     function PointInShape({%H-}APoint: TPointF; {%H-}ARadius: single): boolean; overload; override;
+    function PointInPen(APoint: TPointF): boolean; overload; override;
     function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; override;
     function GetGenericCost: integer; override;
     procedure MouseMove({%H-}Shift: TShiftState; {%H-}X, {%H-}Y: single; var {%H-}ACursor: TOriginalEditorCursor; var {%H-}AHandled: boolean); override;
@@ -198,7 +199,7 @@ implementation
 
 uses BGRATransform, BGRAText, BGRAVectorize, LCVectorialFill, math,
   BGRAUTF8, BGRAUnicode, Graphics, Clipbrd, LCLType, LCLIntf,
-  BGRAGradients, BGRACustomTextFX, LCResourceString;
+  BGRAGradients, BGRACustomTextFX, LCResourceString, BGRAFillInfo;
 
 function FontStyleToStr(AStyle: TFontStyles): string;
 begin
@@ -1497,6 +1498,22 @@ begin
   result := false;
 end;
 
+function TTextShape.PointInPen(APoint: TPointF): boolean;
+var
+  tl: TBidiTextLayout;
+  pt: TPointF;
+  i: Integer;
+begin
+  if not GetAffineBox(AffineMatrixIdentity,true).Contains(APoint) then
+    exit(false);
+  SetGlobalMatrix(AffineMatrixIdentity);
+  tl := GetTextLayout;
+  pt := AffineMatrixInverse(GetUntransformedMatrix)*APoint;
+  for i := 0 to tl.PartCount-1 do
+    if tl.PartAffineBox[i].Contains(pt) then exit(true);
+  result := false;
+end;
+
 function TTextShape.GetIsSlow(const AMatrix: TAffineMatrix): boolean;
 begin
   Result:= true;