229 lines
6.2 KiB
ObjectPascal
229 lines
6.2 KiB
ObjectPascal
|
|
{******************************************}
|
|
{ }
|
|
{ FastReport VCL }
|
|
{ SVG paint servers }
|
|
{ }
|
|
{ Copyright (c) 1998-2021 }
|
|
{ by Fast Reports Inc. }
|
|
{ }
|
|
{******************************************}
|
|
|
|
unit frxSVGPaint;
|
|
|
|
interface
|
|
|
|
{$I frx.inc}
|
|
|
|
uses
|
|
Classes, Graphics,
|
|
frxSVGCanvas, frxSVGHelpers, frxSVGBase, frxSVGColor,
|
|
frxSVGElement, frxSVGComponents;
|
|
|
|
type
|
|
Tel_stop = class(TSVGElementObj)
|
|
protected
|
|
function New(Parent: TSVGElementObj): TSVGElementObj; override;
|
|
public
|
|
constructor Create; override;
|
|
function GetStopData: TSVGGradientStopData;
|
|
end;
|
|
|
|
TSVGPaintServer = class(TSVGElementObj)
|
|
protected
|
|
FUser: TSVGElementObj;
|
|
public
|
|
property User: TSVGElementObj read FUser;
|
|
end;
|
|
|
|
TSVGGradient = class(TSVGPaintServer)
|
|
private
|
|
FHrefObj: TSVGGradient;
|
|
protected
|
|
function GetGradientArray(Opacity: Single): TSVGGradientArray; virtual;
|
|
procedure SurePercent(AA: array of TSVGAttribute);
|
|
public
|
|
procedure GetGradientData(Opacity: Single; const User: TSVGElementObj; out GData: TSVGGradientData); virtual;
|
|
end;
|
|
|
|
Tel_linearGradient = class(TSVGGradient)
|
|
protected
|
|
function New(Parent: TSVGElementObj): TSVGElementObj; override;
|
|
public
|
|
constructor Create; override;
|
|
|
|
procedure GetGradientData(Opacity: Single; const User: TSVGElementObj; out GData: TSVGGradientData); override;
|
|
end;
|
|
|
|
Tel_radialGradient = class(TSVGGradient)
|
|
protected
|
|
function New(Parent: TSVGElementObj): TSVGElementObj; override;
|
|
public
|
|
constructor Create; override;
|
|
|
|
procedure GetGradientData(Opacity: Single; const User: TSVGElementObj; out GData: TSVGGradientData); override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Math,
|
|
frxSVGParse, frxUtils;
|
|
|
|
{ TSVGStop }
|
|
|
|
constructor Tel_stop.Create;
|
|
begin
|
|
inherited Create;
|
|
ConstructAttributes(el_stop);
|
|
end;
|
|
|
|
function Tel_stop.GetStopData: TSVGGradientStopData;
|
|
begin
|
|
Result.SVGColor := atColorCurrent(at_stop_color);
|
|
Result.SVGColor.Alpha := Result.SVGColor.Alpha * atAlpha(at_stop_opacity);
|
|
Result.Offset := atAlpha(at_offset);
|
|
end;
|
|
|
|
function Tel_stop.New(Parent: TSVGElementObj): TSVGElementObj;
|
|
begin
|
|
Result := Tel_stop.Create(Parent);
|
|
end;
|
|
|
|
{ TSVGGradient }
|
|
|
|
function TSVGGradient.GetGradientArray(Opacity: Single): TSVGGradientArray;
|
|
|
|
function IsFind(const Href: string): Boolean;
|
|
begin
|
|
if Href = '' then
|
|
FHrefObj := Self
|
|
else if Href[1] = '#' then
|
|
FHrefObj := TSVGGradient(FRoot.FindByID(Copy(Href, 2, MaxInt)));
|
|
|
|
Result := Assigned(FHrefObj) and (FHrefObj is TSVGGradient) and (FHrefObj.Count > 0);
|
|
end;
|
|
|
|
function Stop(Index: Integer): Tel_stop;
|
|
begin
|
|
Result := Tel_stop(FHrefObj.Items[Index]);
|
|
end;
|
|
var
|
|
i, AddStart, AddEnd, Len: Integer;
|
|
begin
|
|
SetLength(Result, 0);
|
|
|
|
if (FHrefObj = nil) and not IsFind(atString(at_href)) then
|
|
Exit;
|
|
|
|
AddStart := IfInt(Stop(0).GetStopData.Offset > 0, 1);
|
|
AddEnd := IfInt(Stop(FHrefObj.Count - 1).GetStopData.Offset < 1, 1);
|
|
|
|
Len := FHrefObj.Count + AddStart + AddEnd;
|
|
|
|
SetLength(Result, Len);
|
|
|
|
for i := 0 to FHrefObj.Count - 1 do
|
|
Result[i + AddStart] := Stop(i).GetStopData;
|
|
|
|
Result[0] := ToSVGGradientStopData(Result[0 + AddStart].SVGColor, 0.0);
|
|
Result[Len - 1] := ToSVGGradientStopData(Result[Len - 1 - AddEnd].SVGColor, 1.0);
|
|
|
|
for i := 0 to FHrefObj.Count - 1 do
|
|
Result[i].SVGColor.Alpha := Result[i + AddStart].SVGColor.Alpha * Opacity;
|
|
end;
|
|
|
|
procedure TSVGGradient.GetGradientData(Opacity: Single; const User: TSVGElementObj; out GData: TSVGGradientData);
|
|
begin
|
|
FUser := User;
|
|
|
|
GData.Bounds := User.GetBounds;
|
|
GData.csu := atSpecificWord(at_gradientUnits);
|
|
GData.spreadMethod := atSpecificWord(at_spreadMethod);
|
|
|
|
if AttrObj[at_Transform].IsDefault then
|
|
GData.Matrix := atMatrix(at_gradientTransform)
|
|
else
|
|
GData.Matrix := atMatrix(at_Transform);
|
|
|
|
GData.GradientArray := GetGradientArray(Opacity);
|
|
end;
|
|
|
|
procedure TSVGGradient.SurePercent(AA: array of TSVGAttribute);
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := Low(AA) to High(AA) do
|
|
AttrObj[AA[i]].PartToPercent;
|
|
end;
|
|
|
|
{ TSVGLinearGradient }
|
|
|
|
constructor Tel_linearGradient.Create;
|
|
begin
|
|
inherited Create;
|
|
ConstructAttributes(el_linearGradient);
|
|
end;
|
|
|
|
procedure Tel_linearGradient.GetGradientData(Opacity: Single; const User: TSVGElementObj; out GData: TSVGGradientData);
|
|
begin
|
|
inherited GetGradientData(Opacity, User, GData);
|
|
|
|
GData.FillerType := ftLinearGradient;
|
|
|
|
if GData.csu = svg_objectBoundingBox then
|
|
SurePercent([at_x1, at_y1, at_x2, at_y2]);
|
|
|
|
GData.x1 := atLength(at_x1) + IfReal(atIsPercent(at_x1), GData.Bounds.X);
|
|
GData.y1 := atLength(at_y1) + IfReal(atIsPercent(at_y1), GData.Bounds.Y);
|
|
GData.x2 := atLength(at_x2) + IfReal(atIsPercent(at_x2), GData.Bounds.X);
|
|
GData.y2 := atLength(at_y2) + IfReal(atIsPercent(at_y2), GData.Bounds.Y);
|
|
end;
|
|
|
|
function Tel_linearGradient.New(Parent: TSVGElementObj): TSVGElementObj;
|
|
begin
|
|
Result := Tel_linearGradient.Create(Parent);
|
|
end;
|
|
|
|
{ TSVGRadialGradient }
|
|
|
|
constructor Tel_radialGradient.Create;
|
|
begin
|
|
inherited Create;
|
|
ConstructAttributes(el_radialGradient);
|
|
end;
|
|
|
|
procedure Tel_radialGradient.GetGradientData(Opacity: Single; const User: TSVGElementObj; out GData: TSVGGradientData);
|
|
begin
|
|
inherited GetGradientData(Opacity, User, GData);
|
|
|
|
GData.FillerType := ftRadialGradient;
|
|
|
|
if GData.csu = svg_objectBoundingBox then
|
|
SurePercent([at_cx, at_cy, at_fr, at_fx, at_fy, at_r]);
|
|
|
|
GData.cx := atLength(at_cx) + IfReal(atIsPercent(at_cx), GData.Bounds.X);
|
|
GData.cy := atLength(at_cy) + IfReal(atIsPercent(at_cy), GData.Bounds.Y);
|
|
|
|
// fx / fy Default: Coincides with the presentational value of cx for the element whether the value for cx was inherited or not.
|
|
if atIsDefault(at_fx) then
|
|
GData.fx := GData.cx
|
|
else
|
|
GData.fx := atLength(at_fx) + IfReal(atIsPercent(at_fx), GData.Bounds.X);
|
|
|
|
if atIsDefault(at_fy) then
|
|
GData.fy := GData.cy
|
|
else
|
|
GData.fy := atLength(at_fy) + IfReal(atIsPercent(at_fx), GData.Bounds.Y);
|
|
|
|
GData.fr := atLength(at_fr);
|
|
GData.r := atLength(at_r);
|
|
end;
|
|
|
|
function Tel_radialGradient.New(Parent: TSVGElementObj): TSVGElementObj;
|
|
begin
|
|
Result := Tel_radialGradient.Create(Parent);
|
|
end;
|
|
|
|
end.
|