فهرست منبع

export pen and back gradient to svg

Johann ELSASS 4 سال پیش
والد
کامیت
0c5e65ca2c

+ 49 - 10
lazpaintcontrols/lcvectororiginal.pas

@@ -253,14 +253,16 @@ type
     function GetPenVisibleNow: boolean;
     function GetBackVisible: boolean; virtual;
     function GetOutlineVisible: boolean; virtual;
-    procedure ApplyStrokeStyleToSVG(AElement: TSVGElement);
-    procedure ApplyFillStyleToSVG(AElement: TSVGElement);
+    function AppendVectorialFillToSVGDefs(AFill: TVectorialFill; const AMatrix: TAffineMatrix;
+      ADefs: TSVGDefine; ANamePrefix: string): string;
+    procedure ApplyStrokeStyleToSVG(AElement: TSVGElement; ADefs: TSVGDefine);
+    procedure ApplyFillStyleToSVG(AElement: TSVGElement; ADefs: TSVGDefine);
     property Stroker: TBGRAPenStroker read GetStroker;
   public
     constructor Create(AContainer: TVectorOriginal); virtual;
     class function CreateFromStorage(AStorage: TBGRACustomOriginalStorage; AContainer: TVectorOriginal): TVectorShape;
     destructor Destroy; override;
-    function AppendToSVG(AContent: TSVGContent): TSVGElement; virtual; abstract;
+    function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; virtual; abstract;
     procedure BeginUpdate(ADiffHandler: TVectorShapeDiffAny=nil); virtual;
     procedure EndUpdate; virtual;
     procedure FillFit;
@@ -1876,13 +1878,36 @@ begin
             (not (vsfOutlineWidth in Fields) or (OutlineWidth > 0));
 end;
 
-procedure TVectorShape.ApplyStrokeStyleToSVG(AElement: TSVGElement);
+function TVectorShape.AppendVectorialFillToSVGDefs(AFill: TVectorialFill; const AMatrix: TAffineMatrix;
+  ADefs: TSVGDefine; ANamePrefix: string): string;
+var
+  grad: TSVGGradient;
+begin
+  if AFill.FillType = vftGradient then
+  begin
+    grad := AFill.Gradient.AddToSVGDefs(AMatrix, ADefs) as TSVGGradient;
+    if grad = nil then exit('');
+    grad.ID := ANamePrefix + 'grad' + inttostr(Id);
+    result := grad.ID;
+  end else
+    result := '';
+end;
+
+procedure TVectorShape.ApplyStrokeStyleToSVG(AElement: TSVGElement; ADefs: TSVGDefine);
 var ps: array of single;
   i: Integer;
+  fillId: String;
 begin
   if PenVisible then
   begin
-    AElement.strokeColor := PenColor;
+    if IsAffineMatrixInversible(AElement.matrix[cuPixel]) then
+      fillId := AppendVectorialFillToSVGDefs(PenFill,
+        AffineMatrixInverse(AElement.matrix[cuPixel]), ADefs, 'stroke')
+      else fillId := '';
+    if fillId <> '' then
+      AElement.stroke:= 'url(#'+fillId+')'
+      else AElement.strokeColor := PenColor;
+
     if IsSolidPenStyle(PenStyle) then
       AElement.strokeDashArrayNone else
       begin
@@ -1897,11 +1922,21 @@ begin
     AElement.strokeNone;
 end;
 
-procedure TVectorShape.ApplyFillStyleToSVG(AElement: TSVGElement);
+procedure TVectorShape.ApplyFillStyleToSVG(AElement: TSVGElement; ADefs: TSVGDefine);
+var
+  fillId: String;
 begin
   if BackVisible then
-    AElement.fillColor := BackFill.AverageColor
-    else AElement.fillNone;
+  begin
+    if IsAffineMatrixInversible(AElement.matrix[cuPixel]) then
+      fillId := AppendVectorialFillToSVGDefs(BackFill,
+        AffineMatrixInverse(AElement.matrix[cuPixel]), ADefs, 'fill')
+      else fillId := '';
+    if fillId <> '' then
+      AElement.fill:= 'url(#'+fillId+')'
+      else AElement.fillColor := BackFill.AverageColor;
+  end
+  else AElement.fillNone;
 end;
 
 procedure TVectorShape.TransformFill(const AMatrix: TAffineMatrix; ABackOnly: boolean);
@@ -2895,9 +2930,11 @@ var
   i: Integer;
   sCopy: TVectorShape;
   m: TAffineMatrix;
+  defs: TSVGDefine;
 begin
   m := AffineMatrixTranslation(0.5, 0.5) * AMatrix;
   svg := TBGRASVG.Create;
+  defs := svg.Content.AppendDefine;
   result := svg;
   rb := GetRenderBounds(InfiniteRect, AffineMatrixIdentity);
   svg.WidthAsPixel:= rb.Width;
@@ -2913,13 +2950,15 @@ begin
       sCopy := Shape[i].Duplicate;
       try
         sCopy.Transform(m);
-        sCopy.AppendToSVG(svg.Content);
+        sCopy.AppendToSVG(svg.Content, defs);
       finally
         sCopy.Free;
       end;
     end else
-      Shape[i].AppendToSVG(svg.Content);
+      Shape[i].AppendToSVG(svg.Content, defs);
   end;
+  if defs.Content.ElementCount = 0 then
+    svg.Content.RemoveElement(defs);
 end;
 
 function TVectorOriginal.AddTexture(ATexture: TBGRABitmap): integer;

+ 4 - 4
lazpaintcontrols/lcvectorpolyshapes.pas

@@ -142,7 +142,7 @@ type
   public
     class function Fields: TVectorShapeFields; override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
-    function AppendToSVG(AContent: TSVGContent): TSVGElement; override;
+    function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
     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;
@@ -1224,17 +1224,17 @@ begin
   end;
 end;
 
-function TPolylineShape.AppendToSVG(AContent: TSVGContent): TSVGElement;
+function TPolylineShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
 var
   p: TBGRAPath;
 begin
   p := GetPath(AffineMatrixIdentity);
   result := AContent.AppendPath(p.SvgString);
   p.Free;
-  ApplyStrokeStyleToSVG(result);
+  ApplyStrokeStyleToSVG(result, ADefs);
   if PenVisible then
     result.strokeLineCapLCL := LineCap;
-  ApplyFillStyleToSVG(result);
+  ApplyFillStyleToSVG(result, ADefs);
 end;
 
 function TPolylineShape.GetRenderBounds(ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions): TRectF;

+ 11 - 11
lazpaintcontrols/lcvectorrectshapes.pas

@@ -100,7 +100,7 @@ type
     function GetCornerPositition: single; override;
   public
     class function Fields: TVectorShapeFields; override;
-    function AppendToSVG(AContent: TSVGContent): TSVGElement; override;
+    function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; overload; override;
@@ -119,7 +119,7 @@ type
   public
     constructor Create(AContainer: TVectorOriginal); override;
     class function Fields: TVectorShapeFields; override;
-    function AppendToSVG(AContent: TSVGContent): TSVGElement; override;
+    function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
     function GetAlignBounds(const {%H-}ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
@@ -181,7 +181,7 @@ type
     function GetCornerPositition: single; override;
     class function Fields: TVectorShapeFields; override;
     class function PreferPixelCentered: boolean; override;
-    function AppendToSVG(AContent: TSVGContent): TSVGElement; override;
+    function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
     function GetAlignBounds(const ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     procedure MouseDown(RightButton: boolean; Shift: TShiftState; X, Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean); override;
@@ -930,7 +930,7 @@ begin
   Result:= [vsfPenFill, vsfPenWidth, vsfPenStyle, vsfJoinStyle, vsfBackFill];
 end;
 
-function TRectShape.AppendToSVG(AContent: TSVGContent): TSVGElement;
+function TRectShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
 var
   topLeft, u, v: TPointF;
   w, h: Single;
@@ -948,8 +948,8 @@ begin
                               AffineMatrix(u, v, PointF(0, 0)) *
                               AffineMatrixTranslation(-topLeft.X, -topLeft.Y);
   end;
-  ApplyStrokeStyleToSVG(result);
-  ApplyFillStyleToSVG(result);
+  ApplyStrokeStyleToSVG(result, ADefs);
+  ApplyFillStyleToSVG(result, ADefs);
 end;
 
 procedure TRectShape.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
@@ -1173,7 +1173,7 @@ begin
   Result:= [vsfPenFill, vsfPenWidth, vsfPenStyle, vsfBackFill];
 end;
 
-function TEllipseShape.AppendToSVG(AContent: TSVGContent): TSVGElement;
+function TEllipseShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
 var
   u, v: TPointF;
   rx, ry: Single;
@@ -1192,8 +1192,8 @@ begin
                               AffineMatrix(u, v, PointF(0, 0)) *
                               AffineMatrixTranslation(-Origin.X, -Origin.Y);
   end;
-  ApplyStrokeStyleToSVG(result);
-  ApplyFillStyleToSVG(result);
+  ApplyStrokeStyleToSVG(result, ADefs);
+  ApplyFillStyleToSVG(result, ADefs);
 end;
 
 function TEllipseShape.GetAlignBounds(const ALayoutRect: TRect;
@@ -1563,7 +1563,7 @@ begin
   Result:= false;
 end;
 
-function TPhongShape.AppendToSVG(AContent: TSVGContent): TSVGElement;
+function TPhongShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
 var
   u, v: TPointF;
   rx, ry: Single;
@@ -1598,7 +1598,7 @@ begin
                               AffineMatrixTranslation(-Origin.X, -Origin.Y);
   end;
   result.strokeNone;
-  ApplyFillStyleToSVG(result);
+  ApplyFillStyleToSVG(result, ADefs);
 end;
 
 function TPhongShape.GetAlignBounds(const ALayoutRect: TRect;

+ 19 - 4
lazpaintcontrols/lcvectortextshapes.pas

@@ -166,7 +166,7 @@ type
     class function CreateEmpty: boolean; override;
     class function StorageClassName: RawByteString; override;
     class function Usermodes: TVectorShapeUsermodes; override;
-    function AppendToSVG(AContent: TSVGContent): TSVGElement; override;
+    function AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement; override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     procedure Render(ADest: TBGRABitmap; ARenderOffset: TPoint; AMatrix: TAffineMatrix; ADraft: boolean); overload; override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
@@ -2377,7 +2377,7 @@ begin
   Result:=inherited Usermodes + [vsuEditText];
 end;
 
-function TTextShape.AppendToSVG(AContent: TSVGContent): TSVGElement;
+function TTextShape.AppendToSVG(AContent: TSVGContent; ADefs: TSVGDefine): TSVGElement;
 var
   topLeft, u, v: TPointF;
   w, h, zoom: Single;
@@ -2387,6 +2387,7 @@ var
   span: TSVGTSpan;
   fm: TFontPixelMetric;
   rF: TRectF;
+  penFillId, outlineFillId: String;
 begin
   topLeft := Origin - (XAxis - Origin) - (YAxis - Origin);
   w := Width*2; h := Height*2;
@@ -2402,11 +2403,25 @@ begin
                               AffineMatrixTranslation(-topLeft.X, -topLeft.Y);
   end;
   if PenVisible then
-    result.fillColor := PenColor
+  begin
+    if IsAffineMatrixInversible(result.Matrix[cuPixel]) then
+      penFillId := AppendVectorialFillToSVGDefs(PenFill,
+        AffineMatrixInverse(result.Matrix[cuPixel]), ADefs, 'fill')
+      else penFillId := '';
+    if penFillId <> '' then
+      result.fill := 'url(#' + penFillId + ')'
+      else result.fillColor := PenColor;
+  end
     else result.fillNone;
   if OutlineVisible then
   begin
-    result.strokeColor := OutlineFill.AverageColor;
+    if IsAffineMatrixInversible(result.Matrix[cuPixel]) then
+      outlineFillId := AppendVectorialFillToSVGDefs(OutlineFill,
+        AffineMatrixInverse(result.Matrix[cuPixel]), ADefs, 'stroke')
+      else outlineFillId:= '';
+    if outlineFillId <> '' then
+      result.stroke := 'url(#' + outlineFillId + ')'
+      else result.strokeColor := OutlineFill.AverageColor;
     result.strokeWidth := FloatWithCSSUnit(OutlineWidth, cuCustom);
     result.strokeLineJoinLCL:= pjsRound;
   end else