Browse Source

* Rework background and textshadow API

Michaël Van Canneyt 1 year ago
parent
commit
aff9506910

+ 7 - 5
src/base/fresnel.controls.pas

@@ -572,6 +572,7 @@ var
   aColor,aCaption : string;
   aColorFP , ShadowColor: TFPColor;
   aOffsetX, aOffsetY, aRadius: TFresnelLength;
+  HaveShadow : Boolean;
 begin
   aCaption:=RenderedCaption;
   if aCaption='' then
@@ -582,13 +583,14 @@ begin
   if aColorFP.Alpha=alphaTransparent then
     exit;
 
-  if GetRenderedCSSTextShadow(aOffsetX, aOffsetY, aRadius, ShadowColor) then
-  begin
-    aRenderer.TextShadow(RenderedContentBox.Left+aOffsetX,RenderedContentBox.Top+aOffsetY,
-                         Font,ShadowColor,aRadius,aCaption);
-  end;
+  // Change to loop, later
+  HaveShadow:=GetRenderedCSSTextShadow(aOffsetX, aOffsetY, aRadius, ShadowColor);
+  if HaveShadow then
+    aRenderer.AddTextShadow(aOffsetX,aOffsetY,ShadowColor,aRadius);
 
   aRenderer.TextOut(RenderedContentBox.Left,RenderedContentBox.Top,Font,aColorFP,aCaption);
+  if HaveShadow then
+    aRenderer.ClearTextShadows;
 end;
 
 function TCustomLabel.GetMinWidthIntrinsicContentBox: TFresnelLength;

+ 48 - 9
src/base/fresnel.dom.pas

@@ -274,6 +274,25 @@ const
     ':hover'
     );
 
+Type
+  { TFresnelTextShadow }
+  TFresnelTextShadow = record
+    Offset : TFresnelPoint;
+    Radius : TFresnelLength;
+    Color : TFPColor;
+  end;
+  PFresnelTextShadow = ^TFresnelTextShadow;
+  TFresnelTextShadowArray = Array of TFresnelTextShadow;
+
+  { TFresnelRoundRect }
+
+  TFresnelRoundRect = record
+    Box : TFresnelRect;
+    Radius: array[TFresnelCSSCorner] of TFresnelPoint;
+    function ToString : String;
+  end;
+
+
 type
   TFresnelViewport = class;
   TFresnelElement = class;
@@ -339,11 +358,18 @@ type
     procedure FillRect(const aColor: TFPColor; const aRect: TFresnelRect);
     procedure Line(const aColor: TFPColor; const x1, y1, x2, y2: TFresnelLength);
     procedure TextOut(const aLeft, aTop: TFresnelLength; const aFont: IFresnelFont; const aColor: TFPColor; const aText: string);
-    procedure TextShadow(const aLeft, aTop: TFresnelLength; const aFont: IFresnelFont; const aColor: TFPColor; const aRadius: TFresnelLength; const aText: string);
+    procedure AddTextShadow(const aOffsetX, aOffsetY: TFresnelLength; const aColor: TFPColor; const aRadius: TFresnelLength);
+    procedure ClearTextShadows;
+    // Get reference to TFresnelTextShadow. Index between 0 and GetTextShadowCount-1
+    function GetTextShadow(aIndex : Integer): PFresnelTextShadow;
+    // Number of TextShadows that will be applied
+    function GetTextShadowCount: Integer;
     procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength; const aImage: TFPCustomImage);
     function GetOrigin : TFresnelPoint;
     procedure SetOrigin (const aValue : TFresnelPoint);
     property Origin : TFresnelPoint Read GetOrigin Write SetOrigin;
+    property TextShadowCount : Integer Read GetTextShadowCount;
+    property TextShadow[aIndex : Integer] : PFresnelTextShadow read GetTextShadow ;
   end;
 
   IFresnelRenderable = Interface ['{1364DA87-CA22-48A4-B1B2-A8A2C3047FD8}']
@@ -374,10 +400,10 @@ type
     );
   TFresnelLengthChecks = set of TFresnelLengthCheck;
 
-  TFresnelCSSImage = class
+  TFresnelCSSBackgroundInfo = class
   end;
 
-  TFresnelCSSImageLinearGradient = class(TFresnelCSSImage)
+  TFresnelCSSLinearGradient = class(TFresnelCSSBackgroundInfo)
   public type
     TColorPercentage = record
       Color: TFPColor;
@@ -385,7 +411,8 @@ type
     end;
     TColorPercentageArray = array of TColorPercentage;
   public
-    // angle
+    StartPoint : TFresnelPoint;
+    EndPoint : TFresnelPoint;
     // side corner
     Colors: TColorPercentageArray;
   end;
@@ -620,8 +647,8 @@ type
     function GetRenderedCSSBorderWidth(Attr: TFresnelCSSAttribute): TFresnelLength; virtual;
     function GetRenderedCSSBorderRadius(Corner: TFresnelCSSCorner): TFresnelPoint; virtual; // on fail returns 0
     function GetRenderedCSSTextShadow(out aOffsetX, aOffsetY, aRadius: TFresnelLength; out aColor: TFPColor): boolean; virtual; // on fail returns 0
-    function GetRenderedCSSImage(Attr: TFresnelCSSAttribute): TFresnelCSSImage; virtual; // on fail returns nil
-    function GetRenderedCSSLinearGradient(const LGParams: string): TFresnelCSSImageLinearGradient; virtual; // on fail returns nil
+    function GetRenderedCSSImage(Attr: TFresnelCSSAttribute): TFresnelCSSBackgroundInfo; virtual; // on fail returns nil
+    function GetRenderedCSSLinearGradient(const LGParams: string): TFresnelCSSLinearGradient; virtual; // on fail returns nil
     property Rendered: boolean read FRendered write FRendered;
     property RenderedBorderBox: TFresnelRect read FRenderedBorderBox write FRenderedBorderBox; // relative to layout parent
     property RenderedContentBox: TFresnelRect read FRenderedContentBox write FRenderedContentBox; // relative to layout parent
@@ -3853,7 +3880,7 @@ begin
 end;
 
 function TFresnelElement.GetRenderedCSSImage(Attr: TFresnelCSSAttribute
-  ): TFresnelCSSImage;
+  ): TFresnelCSSBackgroundInfo;
 var
   p: Integer;
   s, aValue: String;
@@ -3870,7 +3897,7 @@ begin
 end;
 
 function TFresnelElement.GetRenderedCSSLinearGradient(const LGParams: string
-  ): TFresnelCSSImageLinearGradient;
+  ): TFresnelCSSLinearGradient;
 // For example:
 // linear-gradient(red, orange, yellow, green, blue);
 // linear-gradient(red 0%, orange 25%, yellow 50%, green 75%, blue 100%);
@@ -3885,7 +3912,7 @@ var
   s: String;
   aColor: TFPColor;
 begin
-  Result:=TFresnelCSSImageLinearGradient.Create;
+  Result:=TFresnelCSSLinearGradient.Create;
   p:=1;
   s:=CSSReadNextToken(LGParams,p);
   if s<>'(' then exit;
@@ -4810,5 +4837,17 @@ begin
   ConvertCSSTextShadowToPix(Layouter);
 end;
 
+{ TFresnelRoundRect }
+
+function TFresnelRoundRect.ToString: String;
+begin
+  Result:='['+Box.TopLeft.ToString+','+Box.BottomRight.ToString+'] - [';
+  Result:=Result+Radius[fcsTopLeft].ToString+',';
+  Result:=Result+Radius[fcsTopRight].ToString+',';
+  Result:=Result+Radius[fcsBottomLeft].ToString+',';
+  Result:=Result+Radius[fcsBottomRight].ToString+']';
+end;
+
+
 end.
 

+ 1 - 1
src/base/fresnel.forms.pas

@@ -672,7 +672,7 @@ begin
     WSData.ControlPos:=WSData.PagePos;
   end;
 
-  //FLLog(etDebug,'TFresnelCustomForm.WSMouseXY El=%s PagePos=%s ControlPos=%s',[El.ToString, WSData.PagePos.ToString, WSData.ControlPos.ToString]);
+  FLLog(etDebug,'TFresnelCustomForm.WSMouseXY(%s) El=%s PagePos=%s ControlPos=%s',[EventDispatcher.Registry.GetEventName(MouseEventID),El.ToString, WSData.PagePos.ToString, WSData.ControlPos.ToString]);
   case MouseEventId of
   evtMouseDown:
     fMouseDownElement:=El;

+ 131 - 42
src/base/fresnel.renderer.pas

@@ -1,6 +1,7 @@
 unit Fresnel.Renderer;
 
 {$mode objfpc}{$H+}
+{$modeswitch advancedrecords}
 
 interface
 
@@ -9,14 +10,16 @@ uses
 //  LazLoggerBase,
   Fresnel.Classes, Fresnel.DOM, Fresnel.Controls, Fresnel.Layouter;
 
-type
+const
+  DoublePi = 2*Pi;
 
-  { TFresnelRenderer }
+type
 
   TFresnelRenderer = class(TComponent,IFresnelRenderer)
   private
     FSubPixel: boolean;
     FOrigin: TFresnelPoint;
+    FTextShadows : TFresnelTextShadowArray;
   protected
     type
       { TBorderAndBackground }
@@ -28,12 +31,11 @@ type
         FSameBorderWidth : TCalcBoolean;
         FRenderer : TFresnelRenderer;
       public
-        Box: TFresnelRect;
+        BoundingBox: TFresnelRoundRect;
         Width: array[TFresnelCSSSide] of TFresnelLength;
         Color: array[TFresnelCSSSide] of TFPColor;
         BackgroundColorFP: TFPColor;
-        BackgroundImage: TFresnelCSSImage;
-        Radius: array[TFresnelCSSCorner] of TFresnelPoint;
+        BackgroundImage: TFresnelCSSBackgroundInfo;
         Constructor Create(aRenderer : TFresnelRenderer);
         destructor Destroy; override;
         procedure NormStroke(var aNorm: TFresnelLength; NoNegative: Boolean); inline;
@@ -44,34 +46,75 @@ type
         property Renderer : TFresnelRenderer read FRenderer;
       end;
   protected
+    // Not in IFresnelRenderer
     // Create backend-specific TBorderAndBackground if needed
     function CreateBorderAndBackground : TBorderAndBackground; virtual;
-    // Prepare background and border drawing. Return false if no background/border needed.
+    // Prepare background and border drawing. Return false if no background/border drawing needed.
     function PrepareBackgroundBorder(El: TFresnelElement; Params: TBorderAndBackground) : Boolean; virtual;
+    // Draw the background of the element. This is called before drawing the border. Not called if PrepareBackgroundBorder returned False.
     procedure DrawElBackground(El: TFresnelElement; Params: TBorderAndBackground); virtual;
-    procedure FillRect(const aColor: TFPColor; const aRect: TFresnelRect); virtual; abstract;
-    procedure Line(const aColor: TFPColor; const x1, y1, x2, y2: TFresnelLength); virtual; abstract;
-    procedure TextOut(const aLeft, aTop: TFresnelLength; const aFont: IFresnelFont; const aColor: TFPColor; const aText: string); virtual; abstract;
-    procedure TextShadow(const aLeft, aTop: TFresnelLength; const aFont: IFresnelFont; const aColor: TFPColor; const aRadius: TFresnelLength; const aText: string); virtual;
-    procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength; const aImage: TFPCustomImage); virtual; abstract;
-    procedure MathRoundRect(var r: TFresnelRect);
+    // Draw the border of the element. This is called after drawing the background. Not called if PrepareBackgroundBorder returned False.
     procedure DrawElBorder(El: TFresnelElement; Params: TBorderAndBackground); virtual;
+    // Draw an element
     procedure DrawElement(El: TFresnelElement); virtual;
+    // Draw the children of the element
     procedure DrawChildren(El: TFresnelElement); virtual;
+    // Call UpdateRenderedAttributes on an element and all its children.
     procedure UpdateRenderedAttributes(El: TFresnelElement); virtual;
+    // Set the origin of the currently drawn element.
     procedure SetOrigin(const AValue: TFresnelPoint); virtual;
+    // Get the origin of the currently drawn element.
     function GetOrigin : TFresnelPoint; virtual;
+    // Minimum stroke width. If a length is less than this, it will be considered zero.
     class function GetMinStrokeWidth : TFresnelLength;
+    // Normalize a length. If it is less than minimum stroke width, 0 is returned.
+    // if NoNegative is True, then negative values will be changed to 0.
     class function NormalizeLength(s: TFresnelLength; NoNegative: boolean) : TFresnelLength;
   public
+    //
+    // Put methods not part of IFresnelRenderer here
+    //
+
+    // draw the given viewport.
     procedure Draw(Viewport: TFresnelViewport); virtual;
+    // Round coordinates of given rectangle.
+    procedure MathRoundRect(var r: TFresnelRect);
+    // Is the rendered capable of SubPixel rendering ?
     property SubPixel: boolean read FSubPixel write FSubPixel;
+  Public
+    {  IFresnelRenderer }
+    // Add 1 Shadow text to
+    procedure AddTextShadow(const aX, aY: TFresnelLength; const aColor: TFPColor; const aRadius: TFresnelLength);
+    // Clear all text shadows
+    procedure ClearTextShadows;
+    // Get reference to TFresnelTextShadow. Index between 0 and GetTextShadowCount-1
+    function GetTextShadow(aIndex : Integer): PFresnelTextShadow;
+    // Number of TextShadows that will be applied
+    function GetTextShadowCount: Integer;
+    // Draw and fill a rectangle with given boundaries and color.
+    procedure FillRect(const aColor: TFPColor; const aRect: TFresnelRect); virtual; abstract;
+    // Draw an elliptic arc with with given center and radii, from start to stop angle, using specified color.
+    procedure Arc(const aColor : TFPColor; aCenter,aRadii : TFresnelPoint; aStartAngle : Double = 0; aStopAngle : Double = DoublePi); virtual; abstract;
+    // Draw (and optionally fill) a rounded rectangle with given boundaries and color.
+    procedure RoundRect(const aColor: TFPColor; const aRect: TFresnelRoundRect; Fill : Boolean); virtual; abstract;
+    // Draw a line from point A (x1,y1) to B (x2,y2) using given color.
+    procedure Line(const aColor: TFPColor; const x1, y1, x2, y2: TFresnelLength); virtual; abstract;
+    // Draw a text (aText) at aTop,aLeft using given color and font.
+    procedure TextOut(const aLeft, aTop: TFresnelLength; const aFont: IFresnelFont; const aColor: TFPColor; const aText: string); virtual; abstract;
+    // Draw an image
+    procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength; const aImage: TFPCustomImage); virtual; abstract;
+    // Origin of the currently drawn
     property Origin: TFresnelPoint read GetOrigin write SetOrigin;
+    // Number of TextShadows that will be applied
+    property TextShadowCount : Integer Read GetTextShadowCount;
+    // Indexed access to TFresnelTextShadow references . Index between 0 and TextShadowCount-1
+    property TextShadow[aIndex : Integer] : PFresnelTextShadow read GetTextShadow ;
   end;
   TFresnelRendererClass = class of TFresnelRenderer;
 
 implementation
 
+
 { TFresnelRenderer }
 
 procedure TFresnelRenderer.SetOrigin(const AValue: TFresnelPoint);
@@ -99,34 +142,72 @@ begin
   Result:=S;
 end;
 
+function TFresnelRenderer.GetTextShadow(aIndex : Integer): PFresnelTextShadow;
+begin
+  Result:=Nil;
+  If (aIndex>=0) and (aIndex<Length(FTextShadows)) then
+    Result:=@FTextShadows[aIndex];
+end;
+
+function TFresnelRenderer.GetTextShadowCount: Integer;
+begin
+  Result:=Length(FTextShadows);
+end;
+
+
+procedure TFresnelRenderer.AddTextShadow(const aX, aY: TFresnelLength; const aColor: TFPColor; const aRadius: TFresnelLength);
+var
+  Len : Integer;
+  Shadow : TFresnelTextShadow;
+
+begin
+  Len:=Length(FTextShadows);
+  SetLength(FTextShadows,Len+1);
+  FTextShadows[Len].Color:=aColor;
+  FTextShadows[Len].Radius:=aRadius;
+  FTextShadows[Len].Offset.X:=aX;
+  FTextShadows[Len].Offset.Y:=aY;
+end;
+
 function TFresnelRenderer.CreateBorderAndBackground: TBorderAndBackground;
 begin
   Result:=TBorderAndBackground.Create(Self);
 end;
 
 function TFresnelRenderer.PrepareBackgroundBorder(El: TFresnelElement; Params: TBorderAndBackground): Boolean;
+
+var
+  Grad : TFresnelCSSLinearGradient;
+
 begin
+  if el=nil then;
   Params.Normalize;
+  if (Params.BackgroundImage is TFresnelCSSLinearGradient) then
+    begin
+    Grad:=Params.BackgroundImage as TFresnelCSSLinearGradient;
+    // If none specified, use vertical gradient
+    if Grad.StartPoint=Grad.EndPoint then
+      begin
+      // Vertical gradient
+      Grad.StartPoint:=Params.BoundingBox.Box.TopLeft;
+      Grad.StartPoint.Offset(Origin);
+      Grad.EndPoint:=Grad.StartPoint;
+      Grad.EndPoint.OffSet(0,Params.BoundingBox.Box.Height);
+      end;
+    end;
   Result:=True;
 end;
 
 procedure TFresnelRenderer.DrawElBackground(El: TFresnelElement; Params: TBorderAndBackground);
 begin
+  if el=nil then;
   if Params.BackgroundColorFP.Alpha>alphaTransparent then
   begin
     //FLLog(etDebug,'TFresnelRenderer.DrawElBorder drawing background %s',[El.GetPath]);
-    FillRect(Params.BackgroundColorFP,Params.Box);
+    FillRect(Params.BackgroundColorFP,Params.BoundingBox.Box);
   end;
 end;
 
-procedure TFresnelRenderer.TextShadow(const aLeft, aTop: TFresnelLength;
-  const aFont: IFresnelFont; const aColor: TFPColor;
-  const aRadius: TFresnelLength; const aText: string);
-begin
-  if aRadius=0 then ;
-  TextOut(aLeft,aTop,aFont,aColor,aText);
-end;
-
 procedure TFresnelRenderer.MathRoundRect(var r: TFresnelRect);
 begin
   r.Left:=round(r.Left);
@@ -149,12 +230,13 @@ begin
     c:=Params.Color[s];
     if c.Alpha=alphaTransparent then continue;
     for i:=0 to ceil(Params.Width[s])-1 do
-      case s of
-      ffsLeft: Line(c,Params.Box.Left+i,Params.Box.Top,Params.Box.Left+i,Params.Box.Bottom);
-      ffsTop: Line(c,Params.Box.Left,Params.Box.Top+i,Params.Box.Right,Params.Box.Top+i);
-      ffsRight: Line(c,Params.Box.Right-i,Params.Box.Top,Params.Box.Right-i,Params.Box.Bottom);
-      ffsBottom: Line(c,Params.Box.Left,Params.Box.Bottom-i,Params.Box.Right,Params.Box.Bottom-i);
-      end;
+      With Params.BoundingBox.Box do
+        case s of
+        ffsLeft: Line(c,Left+i,Top,Left+i,Bottom);
+        ffsTop: Line(c,Left,Top+i,Right,Top+i);
+        ffsRight: Line(c,Right-i,Top,Right-i,Bottom);
+        ffsBottom: Line(c,Left,Bottom-i,Right,Bottom-i);
+        end;
   end;
 end;
 
@@ -216,9 +298,9 @@ begin
 
   BorderParams:=CreateBorderAndBackground;
   try
-    BorderParams.Box:=El.RenderedBorderBox;
+    BorderParams.BoundingBox.Box:=El.RenderedBorderBox;
     if not SubPixel then
-      MathRoundRect(BorderParams.Box);
+      MathRoundRect(BorderParams.BoundingBox.Box);
 
     // border-width
     BorderParams.Width[ffsLeft]:=aBorderLeft;
@@ -244,7 +326,7 @@ begin
 
     // border-radius
     for Corner in TFresnelCSSCorner do
-      BorderParams.Radius[Corner]:=El.GetRenderedCSSBorderRadius(Corner);
+      BorderParams.BoundingBox.Radius[Corner]:=El.GetRenderedCSSBorderRadius(Corner);
     // Normalize
     if PrepareBackgroundBorder(El,BorderParams) then
       begin
@@ -325,6 +407,11 @@ begin
   DrawChildren(Viewport);
 end;
 
+procedure TFresnelRenderer.ClearTextShadows;
+begin
+  SetLength(FTextShadows,0);
+end;
+
 { TFresnelRenderer.TBorderAndBackground }
 
 
@@ -353,11 +440,12 @@ var
 begin
   for Side in TFresnelCSSSide do
     NormStroke(Width[Side],True);
-  for Corner in TFresnelCSSCorner do
-    begin
-    NormStroke(Radius[Corner].X,True);
-    NormStroke(Radius[Corner].Y,True);
-    end;
+  With BoundingBox do
+    for Corner in TFresnelCSSCorner do
+      begin
+      NormStroke(Radius[Corner].X,True);
+      NormStroke(Radius[Corner].Y,True);
+      end;
 end;
 
 function TFresnelRenderer.TBorderAndBackground.HasBorder: Boolean;
@@ -398,16 +486,17 @@ begin
   if (FHasRadius=cbCalc) then
     begin
     FHasRadius:=cbFalse;
-    for Corner in TFresnelCSSCorner do
-    begin
-      if (NormalizeStroke(Radius[Corner].X,True)>0)
-          and (NormalizeStroke(Radius[Corner].Y,True)>0) then
+    With BoundingBox Do
+      for Corner in TFresnelCSSCorner do
       begin
-        FHasRadius:=cbtrue;
-        break;
+        if (NormalizeStroke(Radius[Corner].X,True)>0)
+            and (NormalizeStroke(Radius[Corner].Y,True)>0) then
+        begin
+          FHasRadius:=cbtrue;
+          break;
+        end;
       end;
     end;
-    end;
   Result:=FHasRadius;
 end;
 

+ 52 - 35
src/skia/fresnel.skiarenderer.pas

@@ -123,6 +123,9 @@ type
     function CreateBorderAndBackGround : TBorderAndBackground; override;
     procedure DrawElBackground(El: TFresnelElement; Params: TBorderAndBackground); override;
     procedure DrawElBorder(El: TFresnelElement; Params: TBorderAndBackground); override;
+    procedure DrawTextShadow(const aLeft, aTop: TFresnelLength; const FreSkiaFont: TFresnelSkiaFont;
+      const aColor: TFPColor; const aRadius: TFresnelLength; const aTextBlob: ISkTextBlob);
+ Public
     procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength;
       const aImage: TFPCustomImage); override;
     procedure FillRect(const aColor: TFPColor; const aRect: TFresnelRect); override;
@@ -130,8 +133,6 @@ type
     procedure TextOut(const aLeft, aTop: TFresnelLength;
       const aFont: IFresnelFont; const aColor: TFPColor;
       const aText: string); override;
-    procedure TextShadow(const aLeft, aTop: TFresnelLength; const aFont: IFresnelFont;
-      const aColor: TFPColor; const aRadius: TFresnelLength; const aText: string); override;
   public
     constructor Create(AOwner: TComponent); override;
     property Canvas: ISkCanvas read FCanvas write FCanvas;
@@ -571,7 +572,8 @@ begin
   if not Result then
     exit;
   MinWidth:=GetMinStrokeWidth;
-  Result:=(Params.Box.Width>MinWidth) and (Params.Box.Height>MinWidth);
+  With Params.BoundingBox.Box do
+    Result:=(Width>MinWidth) and (Height>MinWidth);
   if Result then
     if Params.HasRadius then
       (Params as TSkiaBorderAndBackground).CalcRadii;
@@ -586,7 +588,7 @@ end;
 procedure TFresnelSkiaRenderer.DrawElBackground(El: TFresnelElement; Params: TBorderAndBackground);
 var
   r: TRectF;
-  LinGrad: TFresnelCSSImageLinearGradient;
+  LinGrad: TFresnelCSSLinearGradient;
   StartP, EndP: TPointF;
   Color1, Color2: TAlphaColor;
   Shader: ISkShader;
@@ -597,15 +599,18 @@ var
 begin
   if El=nil then ;
 
-  r:=RectF(0,0,Params.Box.Width,Params.Box.Height);
-  r.Offset(Params.Box.Left+Origin.X,Params.Box.Top+Origin.Y);
+  With Params.BoundingBox.Box do
+    begin
+    r:=RectF(0,0,Width,Height);
+    r.Offset(Left+Origin.X,Top+Origin.Y);
+    end;
 
-  if Params.BackgroundImage is TFresnelCSSImageLinearGradient then
+  if Params.BackgroundImage is TFresnelCSSLinearGradient then
   begin
     // draw linear gradient background
-    LinGrad:=TFresnelCSSImageLinearGradient(Params.BackgroundImage);
-    StartP:=Pointf(r.Left,r.Top);
-    EndP:=Pointf(r.Left,r.Bottom);
+    LinGrad:=TFresnelCSSLinearGradient(Params.BackgroundImage);
+    StartP:=PointF(LinGrad.StartPoint.X,LinGrad.StartPoint.Y);
+    EndP:=PointF(LinGrad.EndPoint.X,LinGrad.EndPoint.Y);
     Color1:=FPColorToSkia(LinGrad.Colors[0].Color);
     if length(LinGrad.Colors)=1 then
       Color2:=Color1
@@ -650,17 +655,20 @@ begin
   SameBorderWidth:=Params.SameBorderWidth;
   HasBorder:=Params.HasBorder;
   HasRadius:=Params.HasRadius;
-  if HasBorder then begin
-    // with border
-    r:=RectF(Params.Width[ffsLeft]/2,
-             Params.Width[ffsTop]/2,
-             Params.Box.Width-Params.Width[ffsRight]/2,
-             Params.Box.Height-Params.Width[ffsBottom]/2);
-  end else begin
-    // no border
-    r:=RectF(0,0,Params.Box.Width,Params.Box.Height);
-  end;
-  r.Offset(Params.Box.Left+Origin.X,Params.Box.Top+Origin.Y);
+  With Params do
+    begin
+    if HasBorder then begin
+      // with border
+        r:=RectF(Width[ffsLeft]/2,
+                 Width[ffsTop]/2,
+                 BoundingBox.Box.Width-Width[ffsRight]/2,
+                 BoundingBox.Box.Height-Width[ffsBottom]/2);
+    end else begin
+      // no border
+      r:=RectF(0,0,BoundingBox.Box.Width,BoundingBox.Box.Height);
+    end;
+    r.Offset(BoundingBox.Box.Left+Origin.X,BoundingBox.Box.Top+Origin.Y);
+    end;
   //writeln('TFresnelSkiaRenderer.DrawElBorder ',El.GetPath,' Box=',Params.Box.ToString,' Origin=',Origin.ToString,' r=',r.Left,',',r.Top,',',r.Right,',',r.Bottom);
 
   // draw border
@@ -735,31 +743,39 @@ var
   SkPaint: ISkPaint;
   aTextBlob: ISkTextBlob;
   X, Y: TFresnelLength;
+  P : PFresnelTextShadow;
+  I : Integer;
+
 begin
   if not TFresnelSkiaFontEngine.GetFont(aFont,FreSkiaFont) then exit;
   //writeln('TFresnelSkiaRenderer.TextOut Origin=',Origin.ToString,' Left=',FloatToStr(aLeft),' Top=',FloatToStr(aTop),' FontSize=',FloatToStr(FreSkiaFont.SKFont.Size),' Color=',dbgs(aColor),' "',aText,'" FontTop=',FloatToStr(FreSkiaFont.SKMetrics.Top),' Ascent=',FloatToStr(FreSkiaFont.SKMetrics.Ascent),' Descent=',FloatToStr(FreSkiaFont.SKMetrics.Descent),' FontBottom=',FloatToStr(FreSkiaFont.SKMetrics.Bottom),' CapHeight=',FloatToStr(FreSkiaFont.SKMetrics.CapHeight));
+
+  aTextBlob:=TSkTextBlob.MakeFromText(UnicodeString(aText),FreSkiaFont.SKFont);
+
+  for I:=0 to TextShadowCount-1 do
+    begin
+    P:=TextShadow[i];
+    DrawTextShadow(aLeft+P^.Offset.X,aLeft+P^.Offset.Y,
+                   FreSkiaFont,P^.Color,P^.Radius,aTextBlob);
+    end;
+
   SkPaint:=TSkPaint.Create;
   SkPaint.setColor(FPColorToSkia(aColor));
-  aTextBlob:=TSkTextBlob.MakeFromText(UnicodeString(aText),FreSkiaFont.SKFont);
   X:=Origin.X+aLeft;
   Y:=Origin.Y+aTop - FreSkiaFont.SKMetrics.Ascent;
   Canvas.DrawTextBlob(aTextBlob, X, Y, SkPaint);
 end;
 
-procedure TFresnelSkiaRenderer.TextShadow(const aLeft, aTop: TFresnelLength;
-  const aFont: IFresnelFont; const aColor: TFPColor;
-  const aRadius: TFresnelLength; const aText: string);
+procedure TFresnelSkiaRenderer.DrawTextShadow(const aLeft, aTop: TFresnelLength;
+  const FreSkiaFont: TFresnelSkiaFont; const aColor: TFPColor;
+  const aRadius: TFresnelLength; const aTextBlob: ISkTextBlob);
 var
-  FreSkiaFont: TFresnelSkiaFont;
   SkPaint: ISkPaint;
-  aTextBlob: ISkTextBlob;
   X, Y: TFresnelLength;
 begin
-  if not TFresnelSkiaFontEngine.GetFont(aFont,FreSkiaFont) then exit;
   //writeln('TFresnelSkiaRenderer.TextShadow Origin=',Origin.ToString,' Left=',FloatToStr(aLeft),' Top=',FloatToStr(aTop),' FontSize=',FloatToStr(FreSkiaFont.SKFont.Size),' Color=',dbgs(aColor),' Radius=',FloatToStr(aRadius),' "',aText,'" FontTop=',FloatToStr(FreSkiaFont.SKMetrics.Top),' Ascent=',FloatToStr(FreSkiaFont.SKMetrics.Ascent),' Descent=',FloatToStr(FreSkiaFont.SKMetrics.Descent),' FontBottom=',FloatToStr(FreSkiaFont.SKMetrics.Bottom),' CapHeight=',FloatToStr(FreSkiaFont.SKMetrics.CapHeight));
   SkPaint:=TSkPaint.Create;
   SkPaint.setColor(FPColorToSkia(aColor));
-  aTextBlob:=TSkTextBlob.MakeFromText(UnicodeString(aText),FreSkiaFont.SKFont);
   //SkPaint.MaskFilter:=TSkMaskFilter.MakeBlur(TSkBlurStyle.Normal,);
   SkPaint.ImageFilter:=TSkImageFilter.MakeBlur(aRadius,aRadius);
   X:=Origin.X+aLeft;
@@ -784,13 +800,14 @@ var
 begin
   Radii:=Default(TSkRoundRectRadii);
   for Corner in TFresnelCSSCorner do
+    With BoundingBox do
     begin
-    SkCorner:=CSSToSkRoundRectCorner[Corner];
-    if (Radius[Corner].X>0) and (Radius[Corner].Y>0) then
-    begin
-      Radii[SkCorner].x:=Radius[Corner].X;
-      Radii[SkCorner].y:=Radius[Corner].Y;
-    end;
+      SkCorner:=CSSToSkRoundRectCorner[Corner];
+      if (Radius[Corner].X>0) and (Radius[Corner].Y>0) then
+      begin
+        Radii[SkCorner].x:=Radius[Corner].X;
+        Radii[SkCorner].y:=Radius[Corner].Y;
+      end;
     end;
 end;