mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-06-03 03:58:19 +02:00
fpvectorial: Supported rotated linear gradients. Support entity- and page-relative coordinates for gradient vectors.
git-svn-id: trunk@51232 -
This commit is contained in:
parent
811799c840
commit
d593e000f8
@ -113,6 +113,22 @@ type
|
||||
TvVectorialDocument = class;
|
||||
TvEmbeddedVectorialDoc = class;
|
||||
|
||||
{ Coordinates }
|
||||
|
||||
T2DPoint = record
|
||||
X, Y: Double;
|
||||
end;
|
||||
P2DPoint = ^T2DPoint;
|
||||
|
||||
T3DPoint = record
|
||||
X, Y, Z: Double;
|
||||
end;
|
||||
P3DPoint = ^T3DPoint;
|
||||
|
||||
T2DPointsArray = array of T2DPoint;
|
||||
T3DPointsArray = array of T3DPoint;
|
||||
TPointsArray = array of TPoint;
|
||||
|
||||
{ Pen, Brush and Font }
|
||||
|
||||
TvPen = record
|
||||
@ -123,12 +139,16 @@ type
|
||||
end;
|
||||
PvPen = ^TvPen;
|
||||
|
||||
TvBrushKind = (bkSimpleBrush, bkHorizontalGradient, bkVerticalGradient, bkOtherLinearGradient, bkRadialGradient);
|
||||
TvBrushKind = (bkSimpleBrush, bkHorizontalGradient, bkVerticalGradient,
|
||||
bkOtherLinearGradient, bkRadialGradient);
|
||||
TvCoordinateUnit = (vcuDocumentUnit, vcuPercentage);
|
||||
|
||||
TvGradientFlag = (gfRelStartX, gfRelStartY, gfRelEndX, gfRelEndY, gfRelToUserSpace);
|
||||
TvGradientFlags = set of TvGradientFlag;
|
||||
|
||||
TvGradientColor = record
|
||||
Color: TFPColor;
|
||||
Position: Double;
|
||||
Position: Double; // 0 ... 1
|
||||
end;
|
||||
|
||||
TvGradientColors = array of TvGradientColor;
|
||||
@ -138,6 +158,9 @@ type
|
||||
Style: TFPBrushStyle;
|
||||
Kind: TvBrushKind;
|
||||
// Gradient filling support
|
||||
Gradient_start: T2DPoint; // Start/end point of gradient, in pixels by default,
|
||||
Gradient_end: T2DPoint; // but if gfRel* in flags relative to entity boundary or user space
|
||||
Gradient_flags: TvGradientFlags;
|
||||
Gradient_cx, Gradient_cy, Gradient_r, Gradient_fx, Gradient_fy: Double;
|
||||
Gradient_cx_Unit, Gradient_cy_Unit, Gradient_r_Unit, Gradient_fx_Unit, Gradient_fy_Unit: TvCoordinateUnit;
|
||||
Gradient_colors: TvGradientColors;
|
||||
@ -273,16 +296,7 @@ type
|
||||
function GetListLevelStyle(AIndex: Integer): TvListLevelStyle;
|
||||
end;
|
||||
|
||||
{ Coordinates and polyline segments }
|
||||
|
||||
T3DPoint = record
|
||||
X, Y, Z: Double;
|
||||
end;
|
||||
|
||||
P3DPoint = ^T3DPoint;
|
||||
|
||||
T3DPointsArray = array of T3DPoint;
|
||||
TPointsArray = array of TPoint;
|
||||
{ Polyline segments }
|
||||
|
||||
TSegmentType = (
|
||||
st2DLine, st2DLineWithPen, st2DBezier,
|
||||
@ -426,7 +440,7 @@ type
|
||||
Selected: Boolean;
|
||||
ForceRenderBlock: Boolean; // Blocks are usually invisible, but when rendering an insert, their drawing can be forced
|
||||
// Fields which are output from the rendering process
|
||||
EntityCanvasMinXY, EntityCanvasMaxXY: TPoint; // The size utilized in the canvas to draw this entity
|
||||
EntityCanvasMinXY, EntityCanvasMaxXY: TPoint; // The size utilized in the canvas to draw this entity, in pixels
|
||||
end;
|
||||
|
||||
TvEntityFeatures = record
|
||||
@ -466,8 +480,8 @@ type
|
||||
{@@ ASubpart is only valid if this routine returns vfrSubpartFound }
|
||||
function GetLineIntersectionPoints(ACoord: Double;
|
||||
ACoordIsX: Boolean): TDoubleDynArray; virtual; // get all points where the entity inner area crosses a line
|
||||
function GetLinePolygonIntersectionPoints(ACoord: Integer;
|
||||
const APoints: TPointsArray; ACoordIsX: Boolean): TPointsArray; virtual;
|
||||
function GetLinePolygonIntersectionPoints(ACoord: Double;
|
||||
const APoints: T2DPointsArray; ACoordIsX: BOolean): T2DPointsArray; virtual;
|
||||
function TryToSelect(APos: TPoint; var ASubpart: Cardinal; ASnapFlexibility: Integer = 5): TvFindEntityResult; virtual;
|
||||
procedure Move(ADeltaX, ADeltaY: Double); virtual;
|
||||
procedure MoveSubpart(ADeltaX, ADeltaY: Double; ASubpart: Cardinal); virtual;
|
||||
@ -520,6 +534,9 @@ type
|
||||
TvClipMode = (vcmNonzeroWindingRule, vcmEvenOddRule);
|
||||
|
||||
TvEntityWithPenAndBrush = class(TvEntityWithPen)
|
||||
protected
|
||||
procedure DrawPolygonBrushGradient(ADest: TFPCustomCanvas; const APoints:
|
||||
TPointsArray; ARect: TRect; AGradientStart, AGradientEnd: T2DPoint);
|
||||
public
|
||||
{@@ The global Brush for the entire entity. In the case of paths, individual
|
||||
elements might be able to override this setting. }
|
||||
@ -532,8 +549,6 @@ type
|
||||
procedure DrawBrushGradient(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo;
|
||||
x1, y1, x2, y2: Integer;
|
||||
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); virtual;
|
||||
procedure DrawPolygonBrushGradient(ADest: TFPCustomCanvas;
|
||||
const APolyPoints: TPointsArray; x1, y1, x2, y2: Integer);
|
||||
procedure Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; ADestX: Integer = 0;
|
||||
ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0; ADoDraw: Boolean = True); override;
|
||||
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
||||
@ -599,8 +614,8 @@ type
|
||||
procedure AppendEllipticalArc(ARadX, ARadY, AXAxisRotation, ADestX, ADestY: Double; ALeftmostEllipse, AClockwiseArcFlag: Boolean); // See http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
|
||||
procedure AppendEllipticalArcWithCenter(ARadX, ARadY, AXAxisRotation, ADestX, ADestY, ACenterX, ACenterY: Double; AClockwiseArcFlag: Boolean); // See http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
|
||||
function GetLineIntersectionPoints(ACoord: Double; ACoordIsX: Boolean): TDoubleDynArray; override;
|
||||
function GetLinePolygonIntersectionPoints(ACoord: Integer;
|
||||
const APoints: TPointsArray; ACoordIsX: Boolean): TPointsArray; override;
|
||||
function GetLinePolygonIntersectionPoints(ACoord: Double;
|
||||
const APoints: T2DPointsArray; ACoordIsX: Boolean): T2DPointsArray; override;
|
||||
procedure Move(ADeltaX, ADeltaY: Double); override;
|
||||
procedure MoveSubpart(ADeltaX, ADeltaY: Double; ASubpart: Cardinal); override;
|
||||
function MoveToSubpart(ASubpart: Cardinal): TPathSegment;
|
||||
@ -1629,7 +1644,7 @@ procedure RegisterVectorialReader(
|
||||
procedure RegisterVectorialWriter(
|
||||
AWriterClass: TvVectorialWriterClass;
|
||||
AFormat: TvVectorialFormat);
|
||||
function Make2DPoint(AX, AY: Double): T3DPoint;
|
||||
//function Make2DPoint(AX, AY: Double): T3DPoint;
|
||||
function Dimension(AValue : Double; AUnits : TvUnits) : TvDimension;
|
||||
function ConvertDimensionToMM(ADimension: TvDimension; ATotalSize: Double): Double;
|
||||
|
||||
@ -1726,13 +1741,14 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{
|
||||
function Make2DPoint(AX, AY: Double): T3DPoint;
|
||||
begin
|
||||
Result.X := AX;
|
||||
Result.Y := AY;
|
||||
Result.Z := 0;
|
||||
end;
|
||||
|
||||
}
|
||||
function Dimension(AValue: Double; AUnits: TvUnits): TvDimension;
|
||||
begin
|
||||
Result.Value := AValue;
|
||||
@ -3087,6 +3103,7 @@ begin
|
||||
Result := True;
|
||||
APoint.X := T2DSegment(Previous).X;
|
||||
APoint.Y := T2DSegment(Previous).Y;
|
||||
APoint.Z := 0;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
@ -3194,8 +3211,8 @@ var
|
||||
begin
|
||||
Result := 0;
|
||||
if not GetStartPoint(lStartPoint) then Exit;
|
||||
Result := BezierEquation_GetLength(lStartPoint, Make2DPoint(X2, Y2),
|
||||
Make2DPoint(X3, Y3), Make2DPoint(X, Y));
|
||||
Result := BezierEquation_GetLength(lStartPoint, Make3DPoint(X2, Y2, 0),
|
||||
Make3DPoint(X3, Y3, 0), Make3DPoint(X, Y, 0), 0);
|
||||
end;
|
||||
|
||||
function T2DBezierSegment.GetPointAndTangentForDistance(ADistance: Double; out
|
||||
@ -3206,8 +3223,8 @@ begin
|
||||
Result:=inherited GetPointAndTangentForDistance(ADistance, AX, AY,
|
||||
ATangentAngle);
|
||||
if not GetStartPoint(lStartPoint) then Exit;
|
||||
Result := BezierEquation_GetPointAndTangentForLength(lStartPoint, Make2DPoint(X2, Y2),
|
||||
Make2DPoint(X3, Y3), Make2DPoint(X, Y), ADistance, AX, AY, ATangentAngle);
|
||||
Result := BezierEquation_GetPointAndTangentForLength(lStartPoint, Make3DPoint(X2, Y2, 0),
|
||||
Make3DPoint(X3, Y3, 0), Make3DPoint(X, Y, 0), ADistance, AX, AY, ATangentAngle);
|
||||
end;
|
||||
|
||||
procedure T2DBezierSegment.Move(ADeltaX, ADeltaY: Double);
|
||||
@ -3250,10 +3267,10 @@ begin
|
||||
|
||||
SetLength(pts, 0);
|
||||
AddBezierToPoints(
|
||||
Make2DPoint(coordX, coordY),
|
||||
Make2DPoint(coord2X, coord2Y),
|
||||
Make2DPoint(coord3X, coord3Y),
|
||||
Make2DPoint(coord4X, coord4Y),
|
||||
Make3DPoint(coordX, coordY, 0),
|
||||
Make3DPoint(coord2X, coord2Y, 0),
|
||||
Make3DPoint(coord3X, coord3Y, 0),
|
||||
Make3DPoint(coord4X, coord4Y, 0),
|
||||
pts);
|
||||
|
||||
if Length(pts) = 0 then
|
||||
@ -3436,8 +3453,8 @@ end;
|
||||
{ calculates the intersection points of a line at the specified coordinate with
|
||||
the entity's boundary. This overload uses the boundary in canvas units,
|
||||
specified by the points array APoints. }
|
||||
function TvEntity.GetLinePolygonIntersectionPoints(ACoord: Integer;
|
||||
const APoints: TPointsArray; ACoordIsX: Boolean): TPointsArray;
|
||||
function TvEntity.GetLinePolygonIntersectionPoints(ACoord: Double;
|
||||
const APoints: T2DPointsArray; ACoordIsX: Boolean): T2DPointsArray;
|
||||
begin
|
||||
SetLength(Result, 0);
|
||||
end;
|
||||
@ -3634,107 +3651,161 @@ end;
|
||||
|
||||
{ Fills the entity with a gradient.
|
||||
Assumes that the boundary is already in canvas units and is specified by
|
||||
polygon APolyPoints. }
|
||||
polygon APoints. }
|
||||
procedure TvEntityWithPenAndBrush.DrawPolygonBrushGradient(ADest: TFPCustomCanvas;
|
||||
const APolyPoints: TPointsArray; x1, y1, x2, y2: Integer);
|
||||
const APoints: TPointsArray; ARect: TRect; AGradientStart, AGradientEnd: T2dPoint);
|
||||
var
|
||||
lPoints: TPointsArray;
|
||||
lColor, lColor1, lColor2: TFPColor;
|
||||
i, j, c: Integer;
|
||||
i1, i2: Integer;
|
||||
p, p1, p2: Double;
|
||||
lPoints, pts: T2DPointsArray;
|
||||
i, j: Integer;
|
||||
pf: Double; // fraction of path travelled along gradient vector
|
||||
px, py: Double;
|
||||
phi, sinphi, cosphi: Double;
|
||||
coord, coord1, coord2, dcoord: Double;
|
||||
coordIsX: Boolean;
|
||||
p1, p2: T2dPoint;
|
||||
gv: T2dPoint; // gradient vector
|
||||
gvlen: Double; // length of gradient vector
|
||||
gstart: Double; // Gradient start point (1-dim)
|
||||
dir: Integer;
|
||||
begin
|
||||
if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient]) then
|
||||
if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient, bkOtherLinearGradient]) then
|
||||
Exit;
|
||||
|
||||
if Length(Brush.Gradient_colors) = 1 then
|
||||
begin
|
||||
lColor1 := Brush.Gradient_colors[0].Color;
|
||||
lColor2 := Brush.Gradient_colors[0].Color;
|
||||
c := 0;
|
||||
end else
|
||||
begin
|
||||
c := 0;
|
||||
lColor1 := Brush.Gradient_colors[0].Color;
|
||||
lColor2 := Brush.Gradient_colors[1].Color;
|
||||
p1 := Brush.Gradient_colors[0].Position;
|
||||
p2 := Brush.Gradient_colors[1].Position;
|
||||
end;
|
||||
// Direction of gradient vector. The gradient vector begins at color position 0%
|
||||
// and ends at 100%.
|
||||
gv := Make2dPoint(AGradientEnd.X-AGradientStart.X, AGradientEnd.Y-AGradientStart.Y);
|
||||
gvlen := sqrt(sqr(gv.x) + sqr(gv.y));
|
||||
if gvlen = 0 then
|
||||
exit;
|
||||
|
||||
// Find boundary points where the gradient starts and ends. The gradient is
|
||||
// always travered from 0% to 100% color fractions.
|
||||
p1 := Make2dPoint(
|
||||
IfThen(AGradientEnd.x > AGradientStart.x, ARect.Left, ARect.Right),
|
||||
IfThen(AGradientEnd.Y > AGradientStart.y, ARect.Top, ARect.Bottom)
|
||||
);
|
||||
p2 := Make2dPoint(
|
||||
IfThen(AGradientEnd.x > AGradientStart.x, ARect.Right, ARect.Left),
|
||||
IfThen(AGradientEnd.Y > AGradientStart.y, ARect.Bottom, ARect.Top)
|
||||
);
|
||||
|
||||
// Prepare parameters and polygon points
|
||||
ADest.Pen.Style := psSolid;
|
||||
ADest.Pen.Width := 1;
|
||||
|
||||
SetLength(pts, Length(APoints));
|
||||
case Brush.Kind of
|
||||
bkVerticalGradient:
|
||||
begin // horizontal (!) lines have same color
|
||||
i1 := y1;
|
||||
i2 := y2;
|
||||
begin // Run vertically, horizontal lines have same color
|
||||
coord1 := p1.y;
|
||||
coord2 := p2.y;
|
||||
dcoord := IfThen(AGradientEnd.Y > AGradientStart.Y, 1.0, -1.0);
|
||||
gstart := coord1;
|
||||
dir := round(dcoord);
|
||||
for i := 0 to High(APoints) do
|
||||
pts[i] := Make2DPoint(APoints[i].X, APoints[i].Y);
|
||||
coordIsX := false;
|
||||
gstart := coord1;
|
||||
end;
|
||||
bkHorizontalGradient:
|
||||
begin // vertical lines have same color
|
||||
i1 := x1;
|
||||
i2 := x2;
|
||||
begin // Run horizontally, vertical lines have same color
|
||||
coord1 := p1.x;
|
||||
coord2 := p2.x;
|
||||
dcoord := IfThen(AGradientEnd.X > AGradientStart.X, 1.0, -1.0);
|
||||
gstart := coord1;
|
||||
dir := round(dcoord);
|
||||
for i := 0 to High(APoints) do
|
||||
pts[i] := Make2DPoint(APoints[i].X, APoints[i].Y);
|
||||
coordIsX := true;
|
||||
end;
|
||||
bkOtherLinearGradient:
|
||||
begin // Run along gradient vector, lines perpendicular to gradient vector
|
||||
phi := arctan2(gv.y, gv.x);
|
||||
Sincos(phi, sinphi, cosphi);
|
||||
coordIsX := (abs(sinphi) <= sin(pi/4));
|
||||
if not coordIsX then begin
|
||||
phi := -(pi/2 - phi);
|
||||
Sincos(phi, sinphi, cosphi);
|
||||
end;
|
||||
// p1 is the boundary point around which the shape is rotated in order to
|
||||
// to get the gradient vector in horizontal or vertical direction for
|
||||
// easier finding of intersection points.
|
||||
// Projection of vector from GradientStart to p1 onto gradient vector
|
||||
coord1 := (((p1.x - AGradientStart.X)*gv.x) + (p1.y - AGradientStart.Y)*gv.y) / gvlen;
|
||||
// dto for p2.
|
||||
coord2 := (((p2.x - AGradientStart.X)*gv.x) + (p2.y - AGradientStart.Y)*gv.Y) / gvlen;
|
||||
// dcoord := 1.0 / abs(cosphi);
|
||||
// dcoord := abs(cosphi);
|
||||
dcoord := 1.0;
|
||||
gstart := -coord1;
|
||||
dir := +1;
|
||||
// Rotate polygon point such that gradient axis is parallel to x axis
|
||||
// (if angle < 45°) or y axis (if angle > 45°)
|
||||
// Rotation center is the projection of the corner of the bounding box
|
||||
// onto the gradient vector
|
||||
p1 := Make2dPoint(
|
||||
AGradientStart.X + coord1 * gv.x / gvlen,
|
||||
AGradientStart.Y + coord1 * gv.y / gvlen
|
||||
);
|
||||
for j := 0 to High(APoints) do
|
||||
begin
|
||||
px := APoints[j].X - p1.x;
|
||||
py := APoints[j].Y - p1.y;
|
||||
pts[j] := Make2DPoint(px*cosPhi + py*sinPhi, -px*sinPhi + py*cosPhi);
|
||||
end;
|
||||
// Begin painting at corner
|
||||
coord2 := coord2 - coord1;
|
||||
coord1 := 0;
|
||||
ADest.Pen.Width := 2; // make sure that there are no gaps due to rounding errors
|
||||
end;
|
||||
end;
|
||||
|
||||
ADest.Pen.Style := psSolid;
|
||||
for i := i1 to i2 do
|
||||
// Draw gradient
|
||||
coord := coord1;
|
||||
while ((dcoord > 0) and (coord <= coord2)) or (dcoord < 0) and (coord >= coord2) do
|
||||
begin
|
||||
lPoints := GetLinePolygonIntersectionPoints(i, APolyPoints,
|
||||
Brush.Kind = bkHorizontalGradient);
|
||||
if Length(lPoints) < 2 then Continue;
|
||||
p := (i-i1) / (i2-i1) * 100.0; // p = "percent"
|
||||
// Use first color below first position
|
||||
if p < Brush.Gradient_colors[0].Position then
|
||||
lColor := Brush.Gradient_colors[0].Color
|
||||
else
|
||||
// Use last color above last position
|
||||
if p > Brush.Gradient_colors[High(Brush.Gradient_colors)].Position then
|
||||
lColor := Brush.Gradient_colors[High(Brush.Gradient_colors)].Color
|
||||
else
|
||||
if (c < High(Brush.Gradient_colors)) then
|
||||
begin
|
||||
// Use current color pair if percentage is below next position
|
||||
if (p < Brush.Gradient_colors[c+1].Position) then
|
||||
lColor := MixColors(lColor2, lColor1, p-p1, p2-p1)
|
||||
else
|
||||
// Next next color pair if percentage is above next position
|
||||
begin
|
||||
inc(c);
|
||||
p1 := p2;
|
||||
p2 := Brush.Gradient_colors[c+1].Position;
|
||||
lColor1 := lColor2;
|
||||
lColor2 := Brush.Gradient_colors[c+1].Color;
|
||||
lColor := MixColors(lColor2, lColor1, p-p1, p2-p1);
|
||||
end;
|
||||
// Find intersection points of gradient line (normal to gradient vector)
|
||||
// with polygon
|
||||
lPoints := GetLinePolygonIntersectionPoints(coord, pts, coordIsX);
|
||||
if Length(lPoints) < 2 then begin
|
||||
coord := coord + dcoord;
|
||||
Continue;
|
||||
end;
|
||||
ADest.Pen.FPColor := lColor;
|
||||
|
||||
// Prepare intersection points for painting
|
||||
case Brush.Kind of
|
||||
bkVerticalGradient:
|
||||
// Add loop variable as mssing y coordinate of intersection points
|
||||
for j := 0 to High(lPoints) do lPoints[j].Y := coord;
|
||||
bkHorizontalGradient:
|
||||
// Add loop variable as mssing x coordinate of intersection points
|
||||
for j := 0 to High(lPoints) do lPoints[j].X := coord;
|
||||
bkOtherLinearGradient:
|
||||
// Rotate back
|
||||
for j := 0 to High(lPoints) do
|
||||
lPoints[j] := Make2DPoint(
|
||||
lPoints[j].X * cosPhi - lPoints[j].Y * sinPhi + p1.x,
|
||||
lPoints[j].X * sinPhi + lPoints[j].Y * cosPhi + p1.y
|
||||
);
|
||||
end;
|
||||
|
||||
// WriteLn(Format('%.3f'#9'%.3f'#9'%.3f'#9'%.3f'#9'%.3f', [coord, lPoints[0].x, lPoints[0].Y, lPoints[1].x, lPoints[1].y]));
|
||||
|
||||
// Determine color from fraction (pf) of path travelled along gradient vector
|
||||
pf := (coord - gstart) * dir / gvlen;
|
||||
ADest.Pen.FPColor := GradientColor(Brush.Gradient_colors, pf);
|
||||
|
||||
// Draw gradient lines between intersection points
|
||||
j := 0;
|
||||
while j < High(lPoints) do
|
||||
begin
|
||||
case Brush.Kind of
|
||||
bkVerticalGradient : ADest.Line(lPoints[j].X, i, lPoints[j+1].X, i);
|
||||
bkHorizontalGradient: ADest.Line(i, lPoints[j].Y, i, lPoints[j+1].Y);
|
||||
end;
|
||||
ADest.Line(round(lPoints[j].X), round(lPoints[j].Y), round(lPoints[j+1].X), round(lPoints[j+1].Y));
|
||||
inc(j, 2);
|
||||
end;
|
||||
|
||||
// Proceed to next line
|
||||
coord := coord + dcoord;
|
||||
end;
|
||||
{
|
||||
end
|
||||
else if Brush.Kind = bkHorizontalGradient then // vertical (!) lines have same color
|
||||
begin
|
||||
for i := x1 to x2 do
|
||||
begin
|
||||
lPoints := GetLinePolygonIntersectionPoints(i, APolyPoints, True);
|
||||
if Length(lPoints) < 2 then Continue;
|
||||
lColor := MixColors(lColor2, lColor1, i-x1, x2-x1);
|
||||
ADest.Pen.FPColor := lColor;
|
||||
j := 0;
|
||||
while (j < High(lPoints)) do
|
||||
begin
|
||||
ADest.Line(i, lPoints[j].Y, i, lPoints[j+1].Y);
|
||||
inc(j , 2);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
}
|
||||
end;
|
||||
|
||||
{ Fills the entity's shape with a gradient.
|
||||
@ -4265,24 +4336,29 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function CompareInt(P1, P2: Pointer): Integer;
|
||||
function CompareDbl(P1, P2: Pointer): Integer;
|
||||
var
|
||||
i1, i2: PtrInt;
|
||||
val1, val2: ^Double;
|
||||
begin
|
||||
i1 := PtrInt(P1);
|
||||
i2 := PtrInt(P2);
|
||||
Result := CompareValue(i1, i2);
|
||||
val1 := P1;
|
||||
val2 := P2;
|
||||
Result := CompareValue(val1^, val2^);
|
||||
end;
|
||||
|
||||
function TPath.GetLinePolygonIntersectionPoints(ACoord: Integer;
|
||||
const APoints: TPointsArray; ACoordIsX: Boolean): TPointsArray;
|
||||
function TPath.GetLinePolygonIntersectionPoints(ACoord: Double;
|
||||
const APoints: T2DPointsArray; ACoordIsX: Boolean): T2DPointsArray;
|
||||
const
|
||||
EPS = 1e-9;
|
||||
var
|
||||
j, dx, dy: Integer;
|
||||
j: Integer;
|
||||
dx, dy: Double;
|
||||
xval, yval: Double;
|
||||
val: ^Double;
|
||||
list: TFPList;
|
||||
begin
|
||||
list := TFPList.Create;
|
||||
if ACoordIsX then
|
||||
begin
|
||||
for j:=0 to High(APoints) - 1 do
|
||||
begin
|
||||
if ((APoints[j].X <= ACoord) and (ACoord < APoints[j+1].X)) or
|
||||
@ -4290,33 +4366,41 @@ begin
|
||||
begin
|
||||
dx := APoints[j+1].X - APoints[j].X; // can't be zero here
|
||||
dy := APoints[j+1].Y - APoints[j].Y;
|
||||
yval := APoints[j].Y + (ACoord - APoints[j].X) * dy / dx;
|
||||
list.Add(pointer(PtrInt(round(yval))));
|
||||
end {else
|
||||
if ((APoints[j].X = ACoord) and (ACoord = APoints[j+1].X)) then
|
||||
list.Add(pointer(PtrInt(APoints[j].Y)));}
|
||||
end
|
||||
else
|
||||
New(val);
|
||||
val^ := APoints[j].Y + (ACoord - APoints[j].X) * dy / dx;
|
||||
list.Add(val);
|
||||
end;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
for j:=0 to High(APoints) - 1 do
|
||||
if ((APoints[j].Y <= ACoord) and (ACoord < APoints[j+1].Y)) or
|
||||
((APoints[j+1].Y <= ACoord) and (ACoord < APoints[j].Y)) then
|
||||
begin
|
||||
dy := APoints[j+1].Y - APoints[j].Y; // can't be zero here
|
||||
dx := APoints[j+1].X - APoints[j].X;
|
||||
xval := APoints[j].X + (ACoord - APoints[j].Y) * dx / dy;
|
||||
list.Add(pointer(PtrInt(round(xval))));
|
||||
end {else
|
||||
if ((APoints[j].Y = ACoord) and (ACoord = APoints[j+1].Y)) then
|
||||
list.Add(pointer(PtrInt(APoints[j].X)))};
|
||||
New(val);
|
||||
val^ := APoints[j].X + (ACoord - APoints[j].Y) * dx / dy;
|
||||
list.Add(val);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Sort intersection coordinates in ascending order
|
||||
list.Sort(@CompareInt);
|
||||
list.Sort(@CompareDbl);
|
||||
SetLength(Result, list.Count);
|
||||
if ACoordIsX then
|
||||
for j:=0 to list.Count-1 do
|
||||
Result[j] := Point(ACoord, PtrInt(list[j]))
|
||||
Result[j] := Make2DPoint(ACoord, Double(list[j]^))
|
||||
else
|
||||
for j:=0 to list.Count-1 do
|
||||
Result[j] := Point(PtrInt(list[j]), ACoord);
|
||||
Result[j] := Make2DPoint(Double(list[j]^), ACoord);
|
||||
|
||||
// Clean-up
|
||||
for j:=list.Count-1 downto 0 do
|
||||
begin
|
||||
val := List[j];
|
||||
Dispose(val);
|
||||
end;
|
||||
list.Free;
|
||||
end;
|
||||
|
||||
@ -4343,12 +4427,12 @@ begin
|
||||
begin
|
||||
seg2D := T2DSegment(seg);
|
||||
if p.X < seg2D.X then begin
|
||||
p1 := Make2DPoint(p.X, p.Y);
|
||||
p2 := Make2DPoint(seg2D.X, seg2D.Y);
|
||||
p1 := Make3DPoint(p.X, p.Y, 0);
|
||||
p2 := Make3DPoint(seg2D.X, seg2D.Y, 0);
|
||||
end else
|
||||
begin
|
||||
p1 := Make2DPoint(seg2D.X, seg2D.Y);
|
||||
p2 := Make2DPoint(p.X, p.Y);
|
||||
p1 := Make3DPoint(seg2D.X, seg2D.Y, 0);
|
||||
p2 := Make3DPoint(p.X, p.Y, 0);
|
||||
end;
|
||||
if (p1.X < ACoord) and (ACoord <= p2.X) then
|
||||
begin
|
||||
@ -4370,12 +4454,12 @@ begin
|
||||
seg2D := T2DSegment(seg);
|
||||
if p.Y < seg2D.Y then
|
||||
begin
|
||||
p1 := Make2DPoint(p.X, p.Y);
|
||||
p2 := Make2DPoint(seg2D.X, seg2D.Y);
|
||||
p1 := Make3DPoint(p.X, p.Y, 0);
|
||||
p2 := Make3DPoint(seg2D.X, seg2D.Y, 0);
|
||||
end else
|
||||
begin
|
||||
p1 := Make2DPoint(seg2D.X, seg2D.Y);
|
||||
p2 := Make2DPoint(p.X, p.Y);
|
||||
p1 := Make3DPoint(seg2D.X, seg2D.Y, 0);
|
||||
p2 := Make3DPoint(p.X, p.Y, 0);
|
||||
end;
|
||||
if (p1.Y < ACoord) and (ACoord <= p2.Y) then
|
||||
begin
|
||||
@ -4402,6 +4486,8 @@ var
|
||||
coordX, coordY: Integer;
|
||||
curSegment: TPathSegment;
|
||||
cur2DSegment: T2DSegment absolute curSegment;
|
||||
lRect: TRect;
|
||||
gv1, gv2: T2DPoint;
|
||||
begin
|
||||
inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
|
||||
|
||||
@ -4443,7 +4529,42 @@ begin
|
||||
{$ENDIF}
|
||||
end;
|
||||
else // gradients
|
||||
DrawPolygonBrushGradient(ADest, FPolyPoints, x1, y1, x2, y2);
|
||||
// Boundary rect of shape filled with a gradient
|
||||
lRect := Rect(x1, y1, x2, y2);
|
||||
// calculate gradient vector
|
||||
gv1 := Make2DPoint(Brush.Gradient_start.X, Brush.Gradient_start.Y);
|
||||
gv2 := Make2DPoint(Brush.Gradient_end.X, Brush.Gradient_end.Y);
|
||||
if (gfRelToUserSpace in Brush.Gradient_flags) then
|
||||
begin
|
||||
if (gfRelStartX in Brush.Gradient_flags) then
|
||||
gv1.X := gv1.X * FPage.Width;
|
||||
if (gfRelStartY in Brush.Gradient_flags) then
|
||||
gv1.Y := gv1.Y * FPage.Height;
|
||||
if (gfRelEndX in Brush.Gradient_flags) then
|
||||
gv2.X := gv2.X * FPage.Width;
|
||||
if (gfRelEndY in Brush.Gradient_flags) then
|
||||
gv2.Y := gv2.Y * FPage.Height;
|
||||
gv1.X := CoordToCanvasX(gv1.X, ADestX, AMulX);
|
||||
gv1.Y := CoordToCanvasY(gv1.Y, ADestY, AMulY);
|
||||
gv2.X := CoordToCanvasX(gv2.X, ADestX, AMulX);
|
||||
gv2.Y := CoordToCanvasY(gv2.Y, ADestY, AMulY);
|
||||
end else
|
||||
begin
|
||||
if (gfRelStartX in Brush.Gradient_flags) then
|
||||
gv1.X := x1 + gv1.X * (x2 - x1) else
|
||||
gv1.X := CoordToCanvasX(gv1.X, ADestX, AMulX);
|
||||
if (gfRelStartY in Brush.Gradient_flags) then
|
||||
gv1.Y := y1 + gv1.Y * (y2 - y1) else
|
||||
gv1.Y := CoordToCanvasY(gv1.Y, ADestY, AMulY);
|
||||
if (gfRelEndX in Brush.Gradient_flags) then
|
||||
gv2.X := x1 + gv2.X * (x2 - x1) else
|
||||
gv2.X := CoordToCanvasX(gv2.X, ADestX, AMulX);
|
||||
if (gfRelEndY in Brush.Gradient_flags) then
|
||||
gv2.Y := y1 + gv2.Y * (y2 - y1) else
|
||||
gv2.Y := CoordToCanvasY(gv2.Y, ADestY, AMulY);
|
||||
end;
|
||||
// Draw the gradient
|
||||
DrawPolygonBrushGradient(ADest, FPolyPoints, lRect, gv1, gv2);
|
||||
// to do: multiple polygons!
|
||||
end;
|
||||
|
||||
|
@ -42,6 +42,7 @@ type
|
||||
function FPColorToRGBHexString(AColor: TFPColor): string;
|
||||
function RGBToFPColor(AR, AG, AB: byte): TFPColor; inline;
|
||||
function MixColors(AColor1, AColor2: TFPColor; APos, AMax: Double): TFPColor;
|
||||
function GradientColor(AColors: TvGradientColors; AValue: Double): TFPColor;
|
||||
// Coordinate Conversion routines
|
||||
function CanvasCoordsToFPVectorial(AY: Integer; AHeight: Integer): Integer; inline;
|
||||
function CanvasTextPosToFPVectorial(AY: Integer; ACanvasHeight, ATextHeight: Integer): Integer;
|
||||
@ -49,6 +50,7 @@ function CoordToCanvasX(ACoord: Double; ADestX: Integer; AMulX: Double): Integer
|
||||
function CoordToCanvasY(ACoord: Double; ADestY: Integer; AMulY: Double): Integer; inline;
|
||||
// Other routines
|
||||
function SeparateString(AString: string; ASeparator: char): T10Strings;
|
||||
function Make2DPoint(AX, AY: Double): T2DPoint;
|
||||
function Make3DPoint(AX, AY, AZ: Double): T3DPoint;
|
||||
// Mathematical routines
|
||||
function LineEquation_GetPointAndTangentForLength(AStart, AEnd: T3DPoint; ADistance: Double; out AX, AY, ATangentAngle: Double): Boolean;
|
||||
@ -115,12 +117,48 @@ begin
|
||||
Result.Alpha := $FFFF;
|
||||
end;
|
||||
|
||||
{@@ Returns AColor1 if APos = 0, AColor2 if APos = AMax, or interpolates between }
|
||||
function MixColors(AColor1, AColor2: TFPColor; APos, AMax: Double): TFPColor;
|
||||
var
|
||||
f1, f2: Double;
|
||||
begin
|
||||
Result.Alpha := Round(AColor1.Alpha * APos / AMax + AColor2.Alpha * (AMax - APos) / AMax);
|
||||
Result.Red := Round(AColor1.Red * APos / AMax + AColor2.Red * (AMax - APos) / AMax);
|
||||
Result.Green := Round(AColor1.Green * APos / AMax + AColor2.Green * (AMax - APos) / AMax);
|
||||
Result.Blue := Round(AColor1.Blue * APos / AMax + AColor2.Blue * (AMax - APos) / AMax);
|
||||
f1 := (AMax - APos) / AMax;
|
||||
f2 := APos / AMax;
|
||||
Result.Alpha := Round(AColor1.Alpha * f1 + AColor2.Alpha * f2);
|
||||
Result.Red := Round(AColor1.Red * f1 + AColor2.Red * f2);
|
||||
Result.Green := Round(AColor1.Green * f1 + AColor2.Green * f2);
|
||||
Result.Blue := Round(AColor1.Blue * f1 + AColor2.Blue * f2);
|
||||
end;
|
||||
|
||||
{@@ Assigns a color to the specified value. The color is interpolated between
|
||||
the colors defined in AColors.
|
||||
}
|
||||
function GradientColor(AColors: TvGradientColors; AValue: Double): TFPColor;
|
||||
var
|
||||
i: Integer;
|
||||
c1, c2: TFPColor;
|
||||
p1, p2: Double;
|
||||
begin
|
||||
// Return first color if AValue is below the first color position
|
||||
if AValue <= AColors[0].Position then
|
||||
Result := AColors[0].Color
|
||||
else
|
||||
// Return last color if AValue is above the last color position
|
||||
if AValue >= AColors[High(AColors)].Position then
|
||||
Result := AColors[High(AColors)].Color
|
||||
else
|
||||
// Find pair of colors positions which bracket the specified value and
|
||||
// interpolate color
|
||||
for i:= High(AColors)-1 downto 0 do
|
||||
if AValue >= AColors[i].Position then
|
||||
begin
|
||||
c1 := AColors[i].Color;
|
||||
c2 := AColors[i+1].Color;
|
||||
p1 := AColors[i].Position;
|
||||
p2 := AColors[i+1].Position;
|
||||
Result := MixColors(c1, c2, AValue - p1, p2 - p1);
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@ Converts the coordinate system from a TCanvas to FPVectorial
|
||||
@ -193,6 +231,12 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function Make2DPoint(AX, AY: Double): T2DPoint;
|
||||
begin
|
||||
Result.X := AX;
|
||||
Result.Y := AY;
|
||||
end;
|
||||
|
||||
function Make3DPoint(AX, AY, AZ: Double): T3DPoint;
|
||||
begin
|
||||
Result.X := AX;
|
||||
@ -616,10 +660,10 @@ begin
|
||||
CoordX3 := CoordToCanvasX(Cur2DBSegment.X3, ADestX, AMulX);
|
||||
CoordY3 := CoordToCanvasY(Cur2DBSegment.Y3, ADestY, AMulY);
|
||||
AddBezierToPoints(
|
||||
Make2DPoint(CoordX, CoordY),
|
||||
Make2DPoint(CoordX2, CoordY2),
|
||||
Make2DPoint(CoordX3, CoordY3),
|
||||
Make2DPoint(CoordX4, CoordY4),
|
||||
Make3DPoint(CoordX, CoordY, 0),
|
||||
Make3DPoint(CoordX2, CoordY2, 0),
|
||||
Make3DPoint(CoordX3, CoordY3, 0),
|
||||
Make3DPoint(CoordX4, CoordY4, 0),
|
||||
Points);
|
||||
end;
|
||||
else
|
||||
|
@ -137,6 +137,7 @@ type
|
||||
function StringWithUnitToFloat(AStr: string; ACoordKind: TSVGCoordinateKind = sckUnknown;
|
||||
ADefaultUnit: TSVGUnit = suPX; ATargetUnit: TSVGUnit = suPX): Double;
|
||||
function StringFloatZeroToOneToWord(AStr: string): Word;
|
||||
function StringWithPercentToFloat(AStr: String): Double;
|
||||
|
||||
procedure ConvertSVGCoordinatesToFPVCoordinates(
|
||||
const AData: TvVectorialPage;
|
||||
@ -1235,7 +1236,7 @@ var
|
||||
lBrushEntity: TvEntityWithPenAndBrush;
|
||||
lCurEntity: TvEntity;
|
||||
lGradientColor: TvGradientColor;
|
||||
x1, x2, y1, y2: string;
|
||||
x1, y1, x2, y2: Double;
|
||||
begin
|
||||
lCurNode := ANode.FirstChild;
|
||||
while Assigned(lCurNode) do
|
||||
@ -1307,24 +1308,76 @@ begin
|
||||
lBrushEntity := TvEntityWithPenAndBrush.Create(nil);
|
||||
|
||||
// <linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
x1 := 0;
|
||||
x2 := 0;
|
||||
y1 := 0;
|
||||
y2 := 0;
|
||||
for i := 0 to lCurNode.Attributes.Length - 1 do
|
||||
begin
|
||||
lAttrName := lCurNode.Attributes.Item[i].NodeName;
|
||||
lAttrValue := lCurNode.Attributes.Item[i].NodeValue;
|
||||
lAttrName := lowercase(lCurNode.Attributes.Item[i].NodeName);
|
||||
lAttrValue := lowercase(lCurNode.Attributes.Item[i].NodeValue);
|
||||
if lAttrName = 'id' then
|
||||
lBrushEntity.Name := lAttrValue
|
||||
else if lAttrName = 'x1' then
|
||||
x1 := lAttrValue
|
||||
else if lAttrName = 'x2' then
|
||||
x2 := lAttrValue
|
||||
else if lAttrName = 'y1' then
|
||||
y1 := lAttrValue
|
||||
else if lAttrName = 'y2' then
|
||||
y2 := lAttrValue;
|
||||
else
|
||||
if lAttrName = 'x1' then
|
||||
begin
|
||||
if lAttrValue[Length(lAttrValue)] = '%' then
|
||||
Include(lBrushEntity.Brush.Gradient_flags, gfRelStartX);
|
||||
x1 := StringWithPercentToFloat(lAttrValue);
|
||||
end else
|
||||
if lAttrName = 'x2' then
|
||||
begin
|
||||
if lAttrValue[Length(lAttrValue)] = '%' then
|
||||
Include(lBrushEntity.Brush.Gradient_flags, gfRelEndX);
|
||||
x2 := StringWithPercentToFloat(lAttrValue);
|
||||
end else
|
||||
if lAttrName = 'y1' then
|
||||
begin
|
||||
if lAttrValue[Length(lAttrValue)] = '%' then
|
||||
Include(lBrushEntity.Brush.Gradient_flags, gfRelStartY);
|
||||
y1 := StringWithPercentToFloat(lAttrValue);
|
||||
end else
|
||||
if lAttrName = 'y2' then
|
||||
begin
|
||||
if lAttrValue[Length(lAttrValue)] = '%' then
|
||||
Include(lBrushEntity.Brush.Gradient_flags, gfRelEndY);
|
||||
y2 := StringWithPercentToFloat(lAttrValue);
|
||||
end else
|
||||
if lAttrName = 'gradientunits' then
|
||||
begin
|
||||
if lAttrValue = 'userspaceonuse' then
|
||||
Include(lBrushEntity.Brush.Gradient_flags, gfRelToUserSpace)
|
||||
else if lAttrValue = 'objectboundingbox' then
|
||||
Exclude(lBrushEntity.Brush.Gradient_flags, gfRelToUserSpace);
|
||||
end;
|
||||
end;
|
||||
if x2 = x1 then
|
||||
lBrushEntity.Brush.Gradient_start.X := x1;
|
||||
lBrushEntity.Brush.Gradient_end.X := x2;
|
||||
lBrushEntity.Brush.Gradient_start.Y := y1;
|
||||
lBrushEntity.Brush.Gradient_end.Y := y2;
|
||||
ConvertSVGCoordinatesToFPVCoordinates(AData, x1, y1, x1, y1);
|
||||
ConvertSVGCoordinatesToFPVCoordinates(AData, x2, y2, x2, y2);
|
||||
if not (gfRelStartX in lBrushEntity.Brush.Gradient_flags) then
|
||||
lBrushEntity.Brush.Gradient_start.X := x1;
|
||||
if not (gfRelEndX in lBrushEntity.Brush.Gradient_flags) then
|
||||
lBrushEntity.Brush.Gradient_end.X := x2;
|
||||
if not (gfRelStartY in lBrushEntity.Brush.Gradient_flags) then
|
||||
lBrushEntity.Brush.Gradient_start.Y := y1;
|
||||
if not (gfRelEndY in lBrushEntity.Brush.Gradient_flags) then
|
||||
lBrushEntity.Brush.Gradient_end.Y := y2;
|
||||
if (lBrushEntity.Brush.Gradient_start.X = 0) and
|
||||
(lBrushEntity.Brush.Gradient_start.Y = 0) and
|
||||
(lBrushEntity.Brush.Gradient_end.X = 0) and
|
||||
(lBrushEntity.Brush.Gradient_end.Y = 0) then
|
||||
begin
|
||||
lBrushEntity.Brush.Gradient_start.X := 0.0;
|
||||
lBrushEntity.Brush.Gradient_start.Y := 0.0;
|
||||
lBrushEntity.Brush.Gradient_end.X := 1.0;
|
||||
lBrushEntity.Brush.Gradient_end.Y := 1.0;
|
||||
end;
|
||||
if lBrushEntity.Brush.Gradient_start.X = lBrushEntity.Brush.Gradient_end.X then
|
||||
lBrushEntity.Brush.Kind := bkVerticalGradient
|
||||
else if y2=y1 then
|
||||
else if lBrushEntity.Brush.Gradient_start.Y = lBrushEntity.Brush.Gradient_end.Y then
|
||||
lBrushEntity.Brush.Kind := bkHorizontalGradient
|
||||
else
|
||||
lBrushEntity.Brush.Kind := bkOtherLinearGradient;
|
||||
@ -1341,7 +1394,8 @@ begin
|
||||
lAttrName := lCurSubNode.Attributes.Item[i].NodeName;
|
||||
lAttrValue := lCurSubNode.Attributes.Item[i].NodeValue;
|
||||
if lAttrName = 'offset' then
|
||||
lGradientColor.Position := StringWithUnitToFloat(lAttrValue)
|
||||
lGradientColor.Position := StringWithPercentToFloat(lAttrValue)
|
||||
// use as fraction 0..1
|
||||
else if lAttrName = 'style' then
|
||||
lGradientColor.Color := ReadSVGGradientColorStyle(lAttrValue)
|
||||
else if lAttrName = 'stop-color' then
|
||||
@ -3027,6 +3081,19 @@ begin
|
||||
Result := Round(StrToFloat(AStr, FPointSeparator) * $FFFF);
|
||||
end;
|
||||
|
||||
{@@ Converts a number string to a floating-point number. If the string has a
|
||||
% character at its end then it is removed, and the numerical value is
|
||||
divided by 100. }
|
||||
function TvSVGVectorialReader.StringWithPercentToFloat(AStr: String): Double;
|
||||
begin
|
||||
if AStr[Length(AStr)] = '%' then
|
||||
begin
|
||||
Delete(AStr, Length(AStr), 1);
|
||||
Result := 0.01 * StrToFloat(trim(AStr), FPointSeparator);
|
||||
end else
|
||||
Result := StrToFloat(AStr, FPointSeparator);
|
||||
end;
|
||||
|
||||
procedure TvSVGVectorialReader.ConvertSVGCoordinatesToFPVCoordinates(
|
||||
const AData: TvVectorialPage; const ASrcX, ASrcY: Double;
|
||||
var ADestX,ADestY: Double; ADoViewBoxAdjust: Boolean = True);
|
||||
|
Loading…
Reference in New Issue
Block a user