Explorar el Código

lcl: RoundRect and Arc

mattias hace 1 año
padre
commit
3bd1d335ee

+ 110 - 6
src/base/fresnel.classes.pas

@@ -45,6 +45,32 @@ type
 const
   MaxFresnelLength = TFresnelLength(high(longint));
 
+const
+  FresnelCSSFormatSettings: TFormatSettings = (
+    CurrencyFormat: 1;
+    NegCurrFormat: 5;
+    ThousandSeparator: ',';
+    DecimalSeparator: '.';
+    CurrencyDecimals: 2;
+    DateSeparator: '-';
+    TimeSeparator: ':';
+    ListSeparator: ',';
+    CurrencyString: '$';
+    ShortDateFormat: 'd/m/y';
+    LongDateFormat: 'dd" "mmmm" "yyyy';
+    TimeAMString: 'AM';
+    TimePMString: 'PM';
+    ShortTimeFormat: 'hh:nn';
+    LongTimeFormat: 'hh:nn:ss';
+    ShortMonthNames: ('Jan','Feb','Mar','Apr','May','Jun',
+                      'Jul','Aug','Sep','Oct','Nov','Dec');
+    LongMonthNames: ('January','February','March','April','May','June',
+                     'July','August','September','October','November','December');
+    ShortDayNames: ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+    LongDayNames:  ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
+    TwoDigitYearCenturyWindow: 50;
+  );
+
 type
   TFresnelCaption = type string;
 
@@ -75,7 +101,7 @@ type
     function ToString : String;
   end;
   TFLPoint = TFresnelPoint;
-  TFresnelPointDynArray = array of TFresnelPoint;
+  TFresnelPointArray = array of TFresnelPoint;
 
   { TFresnelRect }
 
@@ -106,7 +132,7 @@ type
     procedure Intersect(const R: TFresnelRect);
     class function Union(const R1, R2: TFresnelRect): TFresnelRect; static;
     procedure Union(const R: TFresnelRect);
-    class function Union(const Points: TFresnelPointDynArray): TFresnelRect; static;
+    class function Union(const Points: TFresnelPointArray): TFresnelRect; static;
     procedure Offset(const DX, DY: TFresnelLength);
     procedure Offset(const DP: TFresnelPoint);
     procedure SetLocation(const X, Y: TFresnelLength);
@@ -147,9 +173,18 @@ type
   end;
   TFLComponent = TFresnelComponent;
 
-Procedure FLLog(aType: TEventType; Const Msg : string);
-Procedure FLLog(aType: TEventType; Const Fmt : string; Const Args : Array of const);
-Procedure FLLog(aType: TEventType; const args : Array of string);
+
+function FloatToCSSStr(const f: TFresnelLength): string;
+function CSSStrToFloat(const s: string; out l: TFresnelLength): boolean;
+function CompareFresnelPoint(const A, B: TFresnelPoint): integer;
+function CompareFresnelRect(const A, B: TFresnelRect): integer;
+
+function PointFre(const X, Y: TFresnelLength): TFresnelPoint; overload;
+function RectFre(const Left, Top, Right, Bottom: TFresnelLength): TFresnelRect; overload;
+
+Procedure FLLog(aType: TEventType; Const Msg : string); overload;
+Procedure FLLog(aType: TEventType; Const Fmt : string; Const Args : Array of const); overload;
+Procedure FLLog(aType: TEventType; const args : Array of string); overload;
 function DbgSName(const p: TObject): string; overload;
 function DbgSName(const p: TClass): string; overload;
 
@@ -164,6 +199,75 @@ function  NormalizeStroke(s: TFresnelLength; NoNegative: boolean) : TFresnelLeng
 
 implementation
 
+function FloatToCSSStr(const f: TFresnelLength): string;
+begin
+  Result:=FloatToStr(f,FresnelCSSFormatSettings);
+end;
+
+function CSSStrToFloat(const s: string; out l: TFresnelLength): boolean;
+var
+  Code: Integer;
+begin
+  Code:=0;
+  l:=0;
+  val(s,l,Code);
+  if Code<>0 then exit(false);
+  if IsNan(l) or (l>MaxFresnelLength) or (l<-MaxFresnelLength) then
+    Result:=false
+  else
+    Result:=true;
+end;
+
+function CompareFresnelPoint(const A, B: TFresnelPoint): integer;
+begin
+  if A.X>B.X then
+    Result:=1
+  else if A.X<B.X then
+    Result:=-1
+  else if A.Y>B.Y then
+    Result:=1
+  else if A.Y<B.Y then
+    Result:=-1
+  else
+    Result:=0;
+end;
+
+function CompareFresnelRect(const A, B: TFresnelRect): integer;
+begin
+  if A.Left>B.Left then
+    Result:=1
+  else if A.Left<B.Left then
+    Result:=-1
+  else if A.Top>B.Top then
+    Result:=1
+  else if A.Top<B.Top then
+    Result:=-1
+  else if A.Right>B.Right then
+    Result:=1
+  else if A.Right<B.Right then
+    Result:=-1
+  else if A.Bottom>B.Bottom then
+    Result:=1
+  else if A.Bottom<B.Bottom then
+    Result:=-1
+  else
+    Result:=0;
+end;
+
+function PointFre(const X, Y: TFresnelLength): TFresnelPoint;
+begin
+  Result.X:=X;
+  Result.Y:=Y;
+end;
+
+function RectFre(const Left, Top, Right, Bottom: TFresnelLength): TFresnelRect;
+begin
+  Result.Left:=Left;
+  Result.Top:=Top;
+  Result.Right:=Right;
+  Result.Bottom:=Bottom;
+end;
+
 procedure FLLog(aType: TEventType; const Msg: string);
 begin
   TFresnelComponent.DoLog(aType,Msg);
@@ -487,7 +591,7 @@ begin
     FillByte(Self,SizeOf(TFresnelRect),0);
 end;
 
-class function TFresnelRect.Union(const Points: TFresnelPointDynArray
+class function TFresnelRect.Union(const Points: TFresnelPointArray
   ): TFresnelRect;
 var
   i: Integer;

+ 5 - 91
src/base/fresnel.dom.pas

@@ -288,7 +288,7 @@ Type
 
   TFresnelRoundRect = record
     Box : TFresnelRect;
-    Radius: array[TFresnelCSSCorner] of TFresnelPoint;
+    Radii: array[TFresnelCSSCorner] of TFresnelPoint;
     function ToString : String;
   end;
 
@@ -776,37 +776,6 @@ type
     function GetViewport: TFresnelViewport;
   end;
 
-const
-  FresnelCSSFormatSettings: TFormatSettings = (
-    CurrencyFormat: 1;
-    NegCurrFormat: 5;
-    ThousandSeparator: ',';
-    DecimalSeparator: '.';
-    CurrencyDecimals: 2;
-    DateSeparator: '-';
-    TimeSeparator: ':';
-    ListSeparator: ',';
-    CurrencyString: '$';
-    ShortDateFormat: 'd/m/y';
-    LongDateFormat: 'dd" "mmmm" "yyyy';
-    TimeAMString: 'AM';
-    TimePMString: 'PM';
-    ShortTimeFormat: 'hh:nn';
-    LongTimeFormat: 'hh:nn:ss';
-    ShortMonthNames: ('Jan','Feb','Mar','Apr','May','Jun',
-                      'Jul','Aug','Sep','Oct','Nov','Dec');
-    LongMonthNames: ('January','February','March','April','May','June',
-                     'July','August','September','October','November','December');
-    ShortDayNames: ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
-    LongDayNames:  ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
-    TwoDigitYearCenturyWindow: 50;
-  );
-
-function FloatToCSSStr(const f: TFresnelLength): string;
-function CSSStrToFloat(const s: string; out l: TFresnelLength): boolean;
-function CompareFresnelPoint(const A, B: TFresnelPoint): integer;
-function CompareFresnelRect(const A, B: TFresnelRect): integer;
-
 function FPColorToCSS(const c: TFPColor): string;
 function CSSToFPColor(const s: string; out c: TFPColor): boolean;
 
@@ -816,61 +785,6 @@ function dbgs(const c: TFPColor): string; overload;
 
 implementation
 
-function FloatToCSSStr(const f: TFresnelLength): string;
-begin
-  Result:=FloatToStr(f,FresnelCSSFormatSettings);
-end;
-
-function CSSStrToFloat(const s: string; out l: TFresnelLength): boolean;
-var
-  Code: Integer;
-begin
-  Code:=0;
-  l:=0;
-  val(s,l,Code);
-  if Code<>0 then exit(false);
-  if IsNan(l) or (l>MaxFresnelLength) or (l<-MaxFresnelLength) then
-    Result:=false
-  else
-    Result:=true;
-end;
-
-function CompareFresnelPoint(const A, B: TFresnelPoint): integer;
-begin
-  if A.X>B.X then
-    Result:=1
-  else if A.X<B.X then
-    Result:=-1
-  else if A.Y>B.Y then
-    Result:=1
-  else if A.Y<B.Y then
-    Result:=-1
-  else
-    Result:=0;
-end;
-
-function CompareFresnelRect(const A, B: TFresnelRect): integer;
-begin
-  if A.Left>B.Left then
-    Result:=1
-  else if A.Left<B.Left then
-    Result:=-1
-  else if A.Top>B.Top then
-    Result:=1
-  else if A.Top<B.Top then
-    Result:=-1
-  else if A.Right>B.Right then
-    Result:=1
-  else if A.Right<B.Right then
-    Result:=-1
-  else if A.Bottom>B.Bottom then
-    Result:=1
-  else if A.Bottom<B.Bottom then
-    Result:=-1
-  else
-    Result:=0;
-end;
-
 function FPColorToCSS(const c: TFPColor): string;
 const
   hex: array[0..15] of char = '0123456789abcdef';
@@ -4842,10 +4756,10 @@ end;
 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+']';
+  Result:=Result+Radii[fcsTopLeft].ToString+',';
+  Result:=Result+Radii[fcsTopRight].ToString+',';
+  Result:=Result+Radii[fcsBottomLeft].ToString+',';
+  Result:=Result+Radii[fcsBottomRight].ToString+']';
 end;
 
 

+ 240 - 7
src/base/fresnel.renderer.pas

@@ -108,8 +108,8 @@ type
     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 an elliptic arc with with given center and radii, from start to stop angle in rad (0=right), using specified color.
+    procedure Arc(const aColor : TFPColor; const aCenter, aRadii : TFresnelPoint; aStartAngle : TFresnelLength = 0; aStopAngle : TFresnelLength = 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.
@@ -127,8 +127,241 @@ type
   end;
   TFresnelRendererClass = class of TFresnelRenderer;
 
+function DiameterToArc90DegCount(Diameter: single): integer; overload; // number of points to draw a 90deg arc
+function RoundCorner2Polygon(const aRect: TFresnelRect; Corner: TFresnelCSSCorner): TFresnelPointArray; overload;
+function RoundRect2Polygon(aRect: TFresnelRoundRect): TFresnelPointArray; overload;
+procedure NormalizeRoundRect(var aRect: TFresnelRoundRect); overload;
+procedure SwapFreLen(var a, b: TFresnelLength);
+
 implementation
 
+function DiameterToArc90DegCount(Diameter: single): integer;
+// returns the minimum number of sub arcs a 90 degree arc has to be divided
+// so that the points of the polygon has a deviation of less than 0.4
+const
+  NToDiameter: array[1..20] of integer = (
+   1, // diameter 1..2 need 1 arc per 90degree
+   3, // diameter 3..10 need 2 arcs per 90degree
+   11,
+   24,
+   42,
+   65,
+   94,
+   128,
+   167,
+   211,
+   260,
+   314,
+   374,
+   439,
+   509,
+   584,
+   665,
+   750,
+   841,
+   937
+    );
+
+// The above values were computed by choosing the minimum number of points,
+// so that the error - the distance between the edges and the mathematical circle
+// is less than the rounding error (0.5), for a smoother step 0.4.
+//n_points:=0;
+//repeat
+//  inc(n_points,4);
+//  t := single(1) / single(n_points) * 2 * Pi;
+//  SinCos(t,SinT,CosT);
+//  AX := MaxR * CosT;
+//  AY := MaxR * SinT;
+//  SinCos(t/2,SinT,CosT);
+//  BX := MaxR * CosT;
+//  BY := MaxR * SinT;
+//  CX := (AX + MaxR) /2;
+//  CY := (AY + 0) /2;
+//  Deviation := sqrt(sqr(BX-CX)+sqr(BY-CY));
+//until Deviation<0.4;
+begin
+  for Result:=0 to high(NToDiameter)-1 do
+    if Diameter<NToDiameter[Result+1] then
+      exit;
+
+  // use a heuristic
+  Result:=round(Sqrt(Diameter/2.5));
+end;
+
+function RoundCorner2Polygon(const aRect: TFresnelRect;
+  Corner: TFresnelCSSCorner): TFresnelPointArray;
+// returns the points of a 90degree corner arc, going clockwise.
+var
+  n_points, i: Integer;
+  Xc, Yc, a, b, MaxR, AddRad, Rad, CosT, SinT: TFresnelLength;
+begin
+  Xc:=(aRect.Left+aRect.Right)/2;
+  Yc:=(aRect.Top+aRect.Bottom)/2;
+  a:=(aRect.Width)/2;
+  b:=(aRect.Height)/2;
+  MaxR:=Max(abs(a),abs(b));
+
+  n_points:=DiameterToArc90DegCount(MaxR*2)+1;
+  SetLength(Result{%H-}, n_points);
+
+  case Corner of
+  fcsTopLeft:
+    begin
+      AddRad:=Pi;
+      Result[0].X:=aRect.Left;
+      Result[0].Y:=aRect.Top+b;
+      if n_points>0 then
+      begin
+        Result[n_points-1].X:=aRect.Left+a;
+        Result[n_points-1].Y:=aRect.Top;
+      end;
+    end;
+  fcsTopRight:
+    begin
+      Addrad:=Pi*3/2;
+      Result[0].X:=aRect.Right-a;
+      Result[0].Y:=aRect.Top;
+      if n_points>0 then
+      begin
+        Result[n_points-1].X:=aRect.Right;
+        Result[n_points-1].Y:=aRect.Top+b;
+      end;
+    end;
+  fcsBottomLeft:
+    begin
+      AddRad:=Pi/2;
+      Result[0].X:=aRect.Left+a;
+      Result[0].Y:=aRect.Bottom;
+      if n_points>0 then
+      begin
+        Result[n_points-1].X:=aRect.Left;
+        Result[n_points-1].Y:=aRect.Bottom-b;
+      end;
+    end;
+  fcsBottomRight:
+    begin
+      AddRad:=0;
+      Result[0].X:=aRect.Right;
+      Result[0].Y:=aRect.Bottom-b;
+      if n_points>0 then
+      begin
+        Result[n_points-1].X:=aRect.Right-a;
+        Result[n_points-1].Y:=aRect.Bottom;
+      end;
+    end;
+  end;
+
+  for i := 1 to n_points-2 do
+  begin
+    Rad := single(i) / single(n_points) / 4 * 2 * Pi + AddRad;
+    SinCos(Rad,SinT,CosT);
+    Result[i].X := Xc + a * CosT;
+    Result[i].Y := Yc + b * SinT;
+  end;
+end;
+
+function RoundRect2Polygon(aRect: TFresnelRoundRect): TFresnelPointArray;
+var
+  W, H: TFresnelLength;
+  BottomRight, BottomLeft, TopLeft, TopRight: TFresnelPointArray;
+  r: TFresnelRect;
+begin
+  Result:=[];
+  NormalizeRoundRect(aRect);
+  W:=aRect.Box.Width;
+  H:=aRect.Box.Height;
+  if (W<0) or (H<0) then exit;
+
+  // bottom-right
+  if aRect.Radii[fcsBottomRight].X>0 then
+  begin
+    r.Left:=aRect.Box.Right-2*aRect.Radii[fcsBottomRight].X;
+    r.Right:=aRect.Box.Right;
+    r.Top:=aRect.Box.Bottom-2*aRect.Radii[fcsBottomRight].Y;
+    r.Bottom:=aRect.Box.Bottom;
+    BottomRight:=RoundCorner2Polygon(r,fcsBottomRight);
+  end else begin
+    BottomRight:=[PointFre(aRect.Box.Right,aRect.Box.Bottom)];
+  end;
+  // bottom-left
+  if aRect.Radii[fcsBottomLeft].X>0 then
+  begin
+    r.Left:=aRect.Box.Left;
+    r.Right:=aRect.Box.Left+2*aRect.Radii[fcsBottomLeft].X;
+    r.Top:=aRect.Box.Bottom-2*aRect.Radii[fcsBottomLeft].Y;
+    r.Bottom:=aRect.Box.Bottom;
+    BottomLeft:=RoundCorner2Polygon(r,fcsBottomLeft);
+  end else begin
+    BottomLeft:=[PointFre(aRect.Box.Left,aRect.Box.Bottom)];
+  end;
+  // top-left
+  if aRect.Radii[fcsTopLeft].X>0 then
+  begin
+    r.Left:=aRect.Box.Left;
+    r.Right:=aRect.Box.Left+2*aRect.Radii[fcsTopLeft].X;
+    r.Top:=aRect.Box.Top;
+    r.Bottom:=aRect.Box.Top+2*aRect.Radii[fcsTopLeft].Y;
+    TopLeft:=RoundCorner2Polygon(r,fcsTopLeft);
+  end else begin
+    TopLeft:=[PointFre(aRect.Box.Left,aRect.Box.Top)];
+  end;
+  // top-right
+  if aRect.Radii[fcsTopRight].X>0 then
+  begin
+    r.Left:=aRect.Box.Right-2*aRect.Radii[fcsTopRight].X;
+    r.Right:=aRect.Box.Right;
+    r.Top:=aRect.Box.Top;
+    r.Bottom:=aRect.Box.Top+2*aRect.Radii[fcsTopRight].Y;
+    TopRight:=RoundCorner2Polygon(r,fcsTopRight);
+  end else begin
+    TopRight:=[PointFre(aRect.Box.Right,aRect.Box.Top)];
+  end;
+
+  Result:=Concat(BottomRight,BottomLeft,TopLeft,TopRight);
+end;
+
+procedure NormalizeRoundRect(var aRect: TFresnelRoundRect);
+var
+  W, H, MaxRadiusX, MaxRadiusY: TFresnelLength;
+  c: TFresnelCSSCorner;
+begin
+  W:=aRect.Box.Width;
+  H:=aRect.Box.Width;
+  if W<0 then
+  begin
+    SwapFreLen(aRect.Box.Left,aRect.Box.Right);
+    W:=-W;
+  end;
+  if H<0 then
+  begin
+    SwapFreLen(aRect.Box.Top,aRect.Box.Bottom);
+    H:=-H;
+  end;
+  if (W=0) or (H=0) then
+    exit;
+
+  MaxRadiusX:=W/2;
+  MaxRadiusY:=H/2;
+
+  for c in TFresnelCSSCorner do
+  begin
+    aRect.Radii[c].X:=Min(Max(0,aRect.Radii[c].X),MaxRadiusX);
+    aRect.Radii[c].Y:=Min(Max(0,aRect.Radii[c].Y),MaxRadiusY);
+    if aRect.Radii[c].X=0 then
+      aRect.Radii[c].Y:=0
+    else if aRect.Radii[c].Y=0 then
+      aRect.Radii[c].X:=0;
+  end;
+end;
+
+procedure SwapFreLen(var a, b: TFresnelLength);
+var
+  h: TFresnelLength;
+begin
+  h:=a;
+  a:=b;
+  b:=h;
+end;
 
 { TFresnelRenderer }
 
@@ -340,7 +573,7 @@ begin
 
     // border-radius
     for Corner in TFresnelCSSCorner do
-      BorderParams.BoundingBox.Radius[Corner]:=El.GetRenderedCSSBorderRadius(Corner);
+      BorderParams.BoundingBox.Radii[Corner]:=El.GetRenderedCSSBorderRadius(Corner);
     // Normalize
     if PrepareBackgroundBorder(El,BorderParams) then
       begin
@@ -457,8 +690,8 @@ begin
   With BoundingBox do
     for Corner in TFresnelCSSCorner do
       begin
-      NormStroke(Radius[Corner].X,True);
-      NormStroke(Radius[Corner].Y,True);
+      NormStroke(Radii[Corner].X,True);
+      NormStroke(Radii[Corner].Y,True);
       end;
 end;
 
@@ -503,8 +736,8 @@ begin
     With BoundingBox Do
       for Corner in TFresnelCSSCorner do
       begin
-        if (NormalizeStroke(Radius[Corner].X,True)>0)
-            and (NormalizeStroke(Radius[Corner].Y,True)>0) then
+        if (NormalizeStroke(Radii[Corner].X,True)>0)
+            and (NormalizeStroke(Radii[Corner].Y,True)>0) then
         begin
           FHasRadius:=cbtrue;
           break;

+ 105 - 1
src/lcl/fresnel.lcl.pas

@@ -80,12 +80,18 @@ type
   private
     FCanvas: TCanvas;
   public
+    function RadToLCLAngle16(Angle: TFresnelLength): integer;
+    procedure Arc(const aColor: TFPColor; const aCenter, aRadii: TFresnelPoint;
+      aStartAngle: TFresnelLength = 0; aStopAngle: TFresnelLength = DoublePi); override;
     procedure FillRect(const aColor: TFPColor; const aRect: TFresnelRect); override;
     procedure Line(const aColor: TFPColor; const x1, y1, x2, y2: TFresnelLength); override;
+    procedure RoundRect(const aColor: TFPColor; const aRect: TFresnelRoundRect;
+      Fill: Boolean); override;
     procedure TextOut(const aLeft, aTop: TFresnelLength;
       const aFont: IFresnelFont; const aColor: TFPColor;
       const aText: string); override;
     procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength; const aImage: TFPCustomImage); override;
+
     property Canvas: TCanvas read FCanvas write FCanvas;
   end;
 
@@ -162,6 +168,7 @@ var
 function CompareFresnelLCLFont(Item1, Item2: Pointer): integer;
 function CompareFresnelFontDescWithLCLFont(Key, Item: Pointer): integer;
 procedure FresnelRectToRect(const Src: TFresnelRect; out Dest: TRect);
+procedure FresnelPointsToPoints(const Src: TFresnelPointArray; out Dest: TPointArray);
 
 implementation
 
@@ -209,8 +216,46 @@ begin
   Dest.Bottom:=ceil(Src.Bottom);
 end;
 
+procedure FresnelPointsToPoints(const Src: TFresnelPointArray; out
+  Dest: TPointArray);
+var
+  l, i: Integer;
+begin
+  l:=length(Src);
+  SetLength(Dest{%H-},l);
+  for i:=0 to l-1 do
+    Dest[i]:=Src[i].GetPoint;
+end;
+
 { TFresnelLCLRenderer }
 
+function TFresnelLCLRenderer.RadToLCLAngle16(Angle: TFresnelLength): integer;
+// Fresnel is in Rad (0..2*pi) starting right going clockwise
+// LCL is in 16th degree (0..5760=16*360) starting right going counter-clockwise
+const
+  aFactor = TFresnelLength(-16*180)/TFresnelLength(pi);
+begin
+  Result:=round(Angle*aFactor);
+end;
+
+procedure TFresnelLCLRenderer.Arc(const aColor: TFPColor; const aCenter,
+  aRadii: TFresnelPoint; aStartAngle: TFresnelLength; aStopAngle: TFresnelLength
+  );
+var
+  aLeft, aTop, aRight, aBottom, AngleStart, AngleSweep: integer;
+begin
+  Canvas.Pen.FPColor:=aColor;
+  Canvas.Pen.Style:=psSolid;
+  aLeft:=round(aCenter.X-aRadii.X);
+  aTop:=round(aCenter.Y-aRadii.Y);
+  aRight:=round(aCenter.X+aRadii.X);
+  aBottom:=round(aCenter.Y+aRadii.Y);
+  AngleStart:=RadToLCLAngle16(aStartAngle);
+  AngleSweep:=RadToLCLAngle16(aStopAngle-aStartAngle);
+
+  Canvas.Arc(aLeft,aTop,aRight,aBottom,AngleStart,AngleSweep);
+end;
+
 procedure TFresnelLCLRenderer.FillRect(const aColor: TFPColor;
   const aRect: TFresnelRect);
 begin
@@ -228,6 +273,66 @@ begin
   Canvas.Line(round(Origin.X+x1),round(Origin.Y+y1),round(Origin.X+x2),round(Origin.Y+y2));
 end;
 
+procedure TFresnelLCLRenderer.RoundRect(const aColor: TFPColor;
+  const aRect: TFresnelRoundRect; Fill: Boolean);
+var
+  R: TRect;
+  c: TFresnelCSSCorner;
+  Radii: array[TFresnelCSSCorner] of TPoint;
+  SameRadii: Boolean;
+  W, H, MaxRadiusX, MaxRadiusY: Integer;
+  p: TFresnelPointArray;
+  Points: TPointArray;
+begin
+  FresnelRectToRect(aRect.Box,R);
+  W:=R.Width;
+  H:=R.Height;
+  if (W<=0) or (H<=0) then exit;
+
+  MaxRadiusX:=W div 2;
+  MaxRadiusY:=H div 2;
+
+  for c in TFresnelCSSCorner do
+  begin
+    Radii[c].X:=Min(Max(0,round(aRect.Radii[c].X)),MaxRadiusX);
+    Radii[c].Y:=Min(Max(0,round(aRect.Radii[c].Y)),MaxRadiusY);
+    if Radii[c].X=0 then
+      Radii[c].Y:=0
+    else if Radii[c].Y=0 then
+      Radii[c].X:=0;
+  end;
+
+  SameRadii:=true;
+  for c:=fcsTopRight to fcsBottomRight do
+    if (Radii[c].X<>Radii[fcsTopLeft].X)
+        or (Radii[c].Y<>Radii[fcsTopLeft].Y) then
+      SameRadii:=false;
+
+  if Fill then
+  begin
+    Canvas.Brush.FPColor:=aColor;
+    Canvas.Brush.Style:=bsSolid;
+    Canvas.Pen.Style:=psClear;
+  end else begin
+    Canvas.Pen.FPColor:=aColor;
+    Canvas.Pen.Style:=psSolid;
+    Canvas.Brush.Style:=bsClear;
+  end;
+
+  if SameRadii then
+    Canvas.RoundRect(R,Radii[fcsTopLeft].X,Radii[fcsTopLeft].Y)
+  else begin
+    p:=RoundRect2Polygon(aRect);
+    if length(p)=0 then exit;
+    FresnelPointsToPoints(p,Points);
+
+    if Fill then
+      Canvas.Polyline(Points)
+    else
+      Canvas.Polygon(Points);
+  end;
+end;
+
 procedure TFresnelLCLRenderer.TextOut(const aLeft, aTop: TFresnelLength;
   const aFont: IFresnelFont; const aColor: TFPColor; const aText: string
   );
@@ -260,7 +365,6 @@ begin
   Img:=TBitmap.Create;
   try
     Img.Assign(aImage);
-    // Canvas.Draw(FOrigin.X+aLeft,FOrigin.Top+aTop,G);
     Canvas.StretchDraw(R,Img);
   finally
     Img.Free;

+ 16 - 13
src/skia/fresnel.skiarenderer.pas

@@ -141,8 +141,8 @@ type
     procedure DrawTextShadow(const aLeft, aTop: TFresnelLength; const FreSkiaFont: TFresnelSkiaFont;
       const aColor: TFPColor; const aRadius: TFresnelLength; const aTextBlob: ISkTextBlob); virtual;
  Public
-   procedure Arc(const aColor: TFPColor; aCenter, aRadii: TFresnelPoint;
-     aStartAngle: Double=0; aStopAngle: Double=DoublePi); override;
+    procedure Arc(const aColor: TFPColor; const aCenter, aRadii: TFresnelPoint;
+      aStartAngle: TFresnelLength = 0; aStopAngle: TFresnelLength = DoublePi); override;
     procedure DrawImage(const aLeft, aTop, aWidth, aHeight: TFresnelLength;
       const aImage: TFPCustomImage); override;
     procedure FillRect(const aColor: TFPColor; const aRect: TFresnelRect); override;
@@ -771,7 +771,7 @@ begin
   SkPaint.setColor(FPColorToSkia(aColor));
 
   for c in TFresnelCSSCorner do
-    Radii[CSSToSkRoundRectCorner[c]]:=aRect.Radius[c].GetPointF;
+    Radii[CSSToSkRoundRectCorner[c]]:=aRect.Radii[c].GetPointF;
   RR:=aRect.Box.GetRectF;
 
   RoundR:=TSkRoundRect.Create;
@@ -826,16 +826,20 @@ begin
   Canvas.DrawTextBlob(aTextBlob, X, Y, SkPaint);
 end;
 
-procedure TFresnelSkiaRenderer.Arc(const aColor: TFPColor; aCenter,
-  aRadii: TFresnelPoint; aStartAngle: Double; aStopAngle: Double);
+procedure TFresnelSkiaRenderer.Arc(const aColor: TFPColor; const aCenter,
+  aRadii: TFresnelPoint; aStartAngle: TFresnelLength; aStopAngle: TFresnelLength
+  );
 var
   Oval: TRectF;
   SkPaint: ISkPaint;
+  SkStartAngle, SkSweepAngle: single;
 begin
   SkPaint:=TSkPaint.Create(TSkPaintStyle.Stroke);
   SkPaint.setColor(FPColorToSkia(aColor));
   Oval:=RectF(aCenter.X-aRadii.X,aCenter.Y-aRadii.Y,aCenter.X+aRadii.X,aCenter.Y+aRadii.Y);
-  Canvas.DrawArc(Oval,aStartAngle,aStopAngle-aStartAngle,false,SkPaint);
+  SkStartAngle:=RadToDeg(aStartAngle);
+  SkSweepAngle:=RadToDeg(aStopAngle-aStartAngle);
+  Canvas.DrawArc(Oval,SkStartAngle,SkSweepAngle,false,SkPaint);
 end;
 
 constructor TFresnelSkiaRenderer.Create(AOwner: TComponent);
@@ -855,15 +859,14 @@ var
 begin
   Radii:=Default(TSkRoundRectRadii);
   for Corner in TFresnelCSSCorner do
-    With BoundingBox do
+  begin
+    SkCorner:=CSSToSkRoundRectCorner[Corner];
+    if (BoundingBox.Radii[Corner].X>0) and (BoundingBox.Radii[Corner].Y>0) then
     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;
+      Radii[SkCorner].x:=BoundingBox.Radii[Corner].X;
+      Radii[SkCorner].y:=BoundingBox.Radii[Corner].Y;
     end;
+  end;
 end;
 
 end.