|
@@ -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;
|