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