fpvectorial: Fix smooth bezier paths if previous segment is a relative bezier path

git-svn-id: trunk@51108 -
This commit is contained in:
wp 2016-01-01 22:47:03 +00:00
parent 491df20f71
commit bc906c5514
3 changed files with 135 additions and 43 deletions

View File

@ -283,7 +283,7 @@ type
st2DEllipticalArc); st2DEllipticalArc);
{@@ {@@
The coordinates in fpvectorial are given in millimiters and The coordinates in fpvectorial are given in millimeters and
the starting point is in the bottom-left corner of the document. the starting point is in the bottom-left corner of the document.
The X grows to the right and the Y grows to the top. The X grows to the right and the Y grows to the top.
} }
@ -521,7 +521,8 @@ type
procedure AssignBrush(ABrush: TvBrush); procedure AssignBrush(ABrush: TvBrush);
procedure DrawBrushGradient(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; procedure DrawBrushGradient(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo;
x1, y1, x2, y2: Integer; x1, y1, x2, y2: Integer;
ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); overload;
// procedure DrawBrushGradient(ADest: TFPCustomCanvas; x1,y1,x2,y2: Integer); overload;
procedure Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; ADestX: Integer = 0; 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; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0; ADoDraw: Boolean = True); override;
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override; function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
@ -561,6 +562,9 @@ type
FCurMoveSubPartIndex: Integer; FCurMoveSubPartIndex: Integer;
FCurMoveSubPartSegment: TPathSegment; FCurMoveSubPartSegment: TPathSegment;
// //
protected
FPolyPoints: TPointsArray;
FPolyStarts: TIntegerDynArray;
public public
Len: Integer; Len: Integer;
Points: TPathSegment; // Beginning of the double-linked list Points: TPathSegment; // Beginning of the double-linked list
@ -3217,6 +3221,7 @@ var
pts: TPointsArray; pts: TPointsArray;
coordX, coordY, coord2X, coord2Y, coord3X, coord3Y, coord4X, coord4Y: Integer; coordX, coordY, coord2X, coord2Y, coord3X, coord3Y, coord4X, coord4Y: Integer;
i, n: Integer; i, n: Integer;
prev: TPoint;
begin begin
if not (Previous is T2DSegment) then if not (Previous is T2DSegment) then
raise Exception.Create('T2DBezierSegment must follow a T2DSegment.'); raise Exception.Create('T2DBezierSegment must follow a T2DSegment.');
@ -3238,13 +3243,21 @@ begin
Make2DPoint(coord4X, coord4Y), Make2DPoint(coord4X, coord4Y),
pts); pts);
if Length(pts) = 0 then
exit;
n := Length(Points); n := Length(Points);
SetLength(Points, n + Length(pts) - 1); // we don't need the start point --> -1 prev := Points[n-1];
for i:=1 to High(pts) do // begin at 1 to skip the start point SetLength(Points, n + Length(pts));
for i:=0 to High(pts) do
begin begin
if (pts[i].X = prev.X) and (pts[i].Y = prev.Y) then // skip subsequent coincident points
Continue;
Points[n] := pts[i]; Points[n] := pts[i];
prev := pts[i];
inc(n); inc(n);
end; end;
SetLength(Points, n);
end; end;
{ T3DSegment } { T3DSegment }
@ -3595,7 +3608,63 @@ begin
Brush.Style := ABrush.Style; Brush.Style := ABrush.Style;
Brush.Color := ABrush.Color; Brush.Color := ABrush.Color;
end; end;
(*
{ Fills the entity with a gradient.
Assumes that the boundary is already in canvas units }
procedure TvEntityWithPenAndBrush.DrawBrushGradient(ADest: TFPCustomCanvas;
x1, y1, x2, y2: Integer);
var
lColor1, lColor2: TFPColor;
i, j: Integer;
begin
if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient]) then
Exit;
lColor1 := Brush.Gradient_colors[1];
lColor2 := Brush.Gradient_colors[0];
if Brush.Kind = bkVerticalGradient then
begin
for i := y1 to y2 do
begin
lPoints := GetLineIntersectionPoints(CanvasToCoordY(i), False);
if Length(lPoints) < 2 then Continue;
lColor := MixColors(lColor1, lColor2, i-y1, y2-y1);
ADest.Pen.FPColor := lColor;
ADest.Pen.Style := psSolid;
j := 0;
while j < Length(lPoints) do
begin
lCanvasPts[0] := CoordToCanvasX(lPoints[j]);
lCanvasPts[1] := CoordToCanvasX(lPoints[j+1]);
ADest.Line(lCanvasPts[0], i, lCanvasPts[1], i);
inc(j, 2);
end;
end;
end
else if Brush.Kind = bkHorizontalGradient then
begin
for i := x1 to x2 do
begin
lPoints := GetLineIntersectionPoints(CanvasToCoordX(i), True);
if Length(lPoints) < 2 then Continue;
lColor := MixColors(lColor1, lColor2, i-x1, x2-x1);
ADest.Pen.FPColor := lColor;
ADest.Pen.Style := psSolid;
j := 0;
while (j+1 < Length(lPoints)) do
begin
lCanvasPts[0] := CoordToCanvasY(lPoints[j]);
lCanvasPts[1] := CoordToCanvasY(lPoints[j+1]);
ADest.Line(i, lCanvasPts[0], i, lCanvasPts[1]);
inc(j , 2);
end;
end;
end;
end; *)
{ Fills the entity's shape with a gradient.
Assumes that the boundary is in fpv units and provides parameters (ADestX,
ADestY, AMulX, AMulY) for conversion to canvas pixels. }
procedure TvEntityWithPenAndBrush.DrawBrushGradient(ADest: TFPCustomCanvas; procedure TvEntityWithPenAndBrush.DrawBrushGradient(ADest: TFPCustomCanvas;
var ARenderInfo: TvRenderInfo; x1, y1, x2, y2: Integer; var ARenderInfo: TvRenderInfo; x1, y1, x2, y2: Integer;
ADestX: Integer; ADestY: Integer; AMulX: Double; AMulY: Double); ADestX: Integer; ADestY: Integer; AMulX: Double; AMulY: Double);
@ -4203,11 +4272,10 @@ end;
procedure TPath.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; procedure TPath.Render(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo;
ADestX, ADestY: Integer; AMulX, AMulY: Double; ADoDraw: Boolean); ADestX, ADestY: Integer; AMulX, AMulY: Double; ADoDraw: Boolean);
var var
polygonPoints: TPointsArray;
polygonStart: TIntegerDynArray;
i: Integer; i: Integer;
j, n: Integer; j, n: Integer;
x1, y1, x2, y2: Integer; x1, y1, x2, y2: Integer;
pts: TPointsArray;
ACanvas: TCanvas absolute ADest; ACanvas: TCanvas absolute ADest;
coordX, coordY: Integer; coordX, coordY: Integer;
curSegment: TPathSegment; curSegment: TPathSegment;
@ -4215,17 +4283,17 @@ var
begin begin
inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw); inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY, ADoDraw);
ConvertPathToPolygons(self, ADestX, ADestY, AMulX, AMulY, polygonPoints, polygonStart); ConvertPathToPolygons(self, ADestX, ADestY, AMulX, AMulY, FPolyPoints, FPolyStarts);
x1 := MaxInt; x1 := MaxInt;
y1 := maxInt; y1 := maxInt;
x2 := -MaxInt; x2 := -MaxInt;
y2 := -MaxInt; y2 := -MaxInt;
for i := 0 to High(polygonPoints) do for i := 0 to High(FPolyPoints) do
begin begin
x1 := min(x1, polygonPoints[i].X); x1 := min(x1, FPolyPoints[i].X);
y1 := min(y1, polygonPoints[i].Y); y1 := min(y1, FPolyPoints[i].Y);
x2 := max(x2, polygonPoints[i].X); x2 := max(x2, FPolyPoints[i].X);
y2 := max(y2, polygonPoints[i].Y); y2 := max(y2, FPolyPoints[i].Y);
end; end;
CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, x1, y1, x2, y2); CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, x1, y1, x2, y2);
@ -4233,23 +4301,24 @@ begin
begin begin
// (1) draw background only // (1) draw background only
ADest.Pen.Style := psClear; ADest.Pen.Style := psClear;
if (Length(polygonPoints) > 2) then if (Length(FPolyPoints) > 2) then
case Brush.Kind of case Brush.Kind of
bkSimpleBrush: bkSimpleBrush:
if Brush.Style <> bsClear then if Brush.Style <> bsClear then
begin begin
{$IFDEF USE_LCL_CANVAS} {$IFDEF USE_LCL_CANVAS}
for i := 0 to High(polygonStart) do for i := 0 to High(FPolyStarts) do
begin begin
j := polygonStart[i]; j := FPolyStarts[i];
if i = High(polygonStart) then if i = High(FPolyStarts) then
n := Length(polygonPoints) - j n := Length(FPolyPoints) - j
else else
n := polygonStart[i+1] - polygonStart[i] + 1; n := FPolyStarts[i+1] - FPolyStarts[i] + 1;
end; end;
ACanvas.Polygon(@polygonPoints[j], n, WindingRule = vcmNonZeroWindingRule); if (FPolyPoints[0].X = FPolyPoints[n-1].X) and (FPolyPoints[0].Y = FPolyPoints[n-1].Y) then
ACanvas.Polygon(@FPolyPoints[j], n, WindingRule = vcmNonZeroWindingRule);
{$ELSE} {$ELSE}
ADest.Polygon(polygonPoints); ADest.Polygon(FPolyPoints);
{$ENDIF} {$ENDIF}
end; end;
else // gradients else // gradients
@ -4291,13 +4360,14 @@ begin
begin begin
coordX := CoordToCanvasX(T2DSegment(curSegment.Previous).X, ADestX, AMulX); coordX := CoordToCanvasX(T2DSegment(curSegment.Previous).X, ADestX, AMulX);
coordY := CoordToCanvasY(T2DSegment(curSegment.Previous).Y, ADestY, AMulY); coordY := CoordToCanvasY(T2DSegment(curSegment.Previous).Y, ADestY, AMulY);
SetLength(PolygonPoints, 1); SetLength(pts, 1);
PolygonPoints[0] := Point(coordX, coordY); pts[0] := Point(coordX, coordY);
curSegment.AddToPoints(ADestX, ADestY, AMulX, AMulY, PolygonPoints); curSegment.AddToPoints(ADestX, ADestY, AMulX, AMulY, pts);
ADest.PolyLine(PolygonPoints); if Length(pts) > 0 then
coordX := PolygonPoints[High(PolygonPoints)].X; begin
coordY := PolygonPoints[High(PolygonPoints)].Y; ADest.PolyLine(pts);
ADest.MoveTo(coordX, coordY); ADest.MoveTo(pts[High(pts)].X, pts[High(pts)].Y);
end;
end; end;
end; end;
end; end;

View File

@ -254,7 +254,7 @@ begin
EllipticalArcToBezier(Xc, Yc, R, R, startAngle, endAngle, P1, P2, P3, P4); EllipticalArcToBezier(Xc, Yc, R, R, startAngle, endAngle, P1, P2, P3, P4);
end; end;
{ This routine converts a Bezier to a Polygon and adds the points of this poligon { This routine converts a Bezier to a Polygon and adds the points of this polygon
to the end of the provided Points output variables } to the end of the provided Points output variables }
procedure AddBezierToPoints(P1, P2, P3, P4: T3DPoint; var Points: TPointsArray); procedure AddBezierToPoints(P1, P2, P3, P4: T3DPoint; var Points: TPointsArray);
var var

View File

@ -221,11 +221,12 @@ var
lToken: TSVGToken; lToken: TSVGToken;
lStr: string; lStr: string;
begin begin
lToken := TSVGToken.Create; // lToken := TSVGToken.Create;
lStr := Trim(AStr); lStr := Trim(AStr);
if lStr = '' then Exit; if lStr = '' then Exit;
lToken := TSVGToken.Create;
// Moves // Moves
if lStr[1] = 'M' then lToken.TokenType := sttMoveTo if lStr[1] = 'M' then lToken.TokenType := sttMoveTo
else if lStr[1] = 'm' then lToken.TokenType := sttRelativeMoveTo else if lStr[1] = 'm' then lToken.TokenType := sttRelativeMoveTo
@ -2001,6 +2002,7 @@ var
lDebugStr: String; lDebugStr: String;
lToken5Before, lToken7Before: TSVGTokenType; lToken5Before, lToken7Before: TSVGTokenType;
lCorrectPreviousToken: Boolean; lCorrectPreviousToken: Boolean;
lPrevRelative, lCurRelative: Boolean;
begin begin
lCurTokenType := ACurTokenType; lCurTokenType := ACurTokenType;
// -------------- // --------------
@ -2043,7 +2045,8 @@ begin
CurY := Y; CurY := Y;
AData.AddLineToPath(CurX, CurY); AData.AddLineToPath(CurX, CurY);
Inc(i, 3); Inc(i, 1);
// Inc(i, 3);
end end
// -------------- // --------------
// Lines // Lines
@ -2091,6 +2094,7 @@ begin
else if lCurTokenType in [sttBezierTo, sttRelativeBezierTo, else if lCurTokenType in [sttBezierTo, sttRelativeBezierTo,
sttSmoothBezierTo, sttRelativeSmoothBezierTo] then sttSmoothBezierTo, sttRelativeSmoothBezierTo] then
begin begin
lPrevRelative := false;
if lCurTokenType in [sttBezierTo, sttRelativeBezierTo] then if lCurTokenType in [sttBezierTo, sttRelativeBezierTo] then
begin begin
X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value; X2 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
@ -2116,10 +2120,11 @@ begin
lCorrectPreviousToken := lToken5Before in [sttSmoothBezierTo, sttRelativeSmoothBezierTo]; lCorrectPreviousToken := lToken5Before in [sttSmoothBezierTo, sttRelativeSmoothBezierTo];
lCorrectPreviousToken := lCorrectPreviousToken or lCorrectPreviousToken := lCorrectPreviousToken or
(lToken7Before in [sttBezierTo, sttRelativeBezierTo]); (lToken7Before in [sttBezierTo, sttRelativeBezierTo]);
lPrevRelative := (lToken5Before = sttRelativeSmoothBezierTo) or (lToken7Before = sttRelativeBezierTo);
end; end;
if (i >= 7) and (lCorrectPreviousToken) then if (i >= 7) and (lCorrectPreviousToken) then
begin begin
if lCurTokenType = sttRelativeSmoothBezierTo then if (lCurTokenType = sttRelativeSmoothBezierTo) or lPrevRelative then
begin begin
X2 := FSVGPathTokenizer.Tokens.Items[i-2].Value - FSVGPathTokenizer.Tokens.Items[i-4].Value; X2 := FSVGPathTokenizer.Tokens.Items[i-2].Value - FSVGPathTokenizer.Tokens.Items[i-4].Value;
Y2 := FSVGPathTokenizer.Tokens.Items[i-1].Value - FSVGPathTokenizer.Tokens.Items[i-3].Value; Y2 := FSVGPathTokenizer.Tokens.Items[i-1].Value - FSVGPathTokenizer.Tokens.Items[i-3].Value;
@ -2138,17 +2143,32 @@ begin
end; end;
// Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates // Careful that absolute coordinates require using ConvertSVGCoordinatesToFPVCoordinates
if lCurTokenType in [sttRelativeBezierTo, sttRelativeSmoothBezierTo] then lCurRelative := lCurTokenType in [sttRelativeBezierTo, sttRelativeSmoothBezierTo];
if lPrevRelative then
begin begin
ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2); ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2);
ConvertSVGDeltaToFPVDelta(AData, X3, Y3, X3, Y3); if lCurRelative then
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y); begin
end ConvertSVGDeltaToFPVDelta(AData, X3, Y3, X3, Y3);
else ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
end else
begin
ConvertSVGCoordinatesToFPVCoordinates(AData, X3, Y3, X3, Y3);
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
end;
end else
begin begin
ConvertSVGCoordinatesToFPVCoordinates(AData, X2, Y2, X2, Y2); if lCurRelative then
ConvertSVGCoordinatesToFPVCoordinates(AData, X3, Y3, X3, Y3); begin
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y); ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2);
ConvertSVGDeltaToFPVDelta(AData, X3, Y3, X3, Y3);
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
end else
begin
ConvertSVGCoordinatesToFPVCoordinates(AData, X2, Y2, X2, Y2);
ConvertSVGCoordinatesToFPVCoordinates(AData, X3, Y3, X3, Y3);
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
end;
end; end;
// Covers the case where there is no valid first control point in smooth bezier // Covers the case where there is no valid first control point in smooth bezier
@ -2177,14 +2197,16 @@ begin
end end
else else
begin begin
AData.AddBezierToPath(X2, Y2, X3, Y3, X, Y); if lPrevRelative then
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X3, Y3, X, Y) else
AData.AddBezierToPath(X2, Y2, X3, Y3, X, Y);
CurX := X; CurX := X;
CurY := Y; CurY := Y;
end; end;
if lCurTokenType in [sttBezierTo, sttRelativeBezierTo] then if lCurTokenType in [sttBezierTo, sttRelativeBezierTo] then
Inc(i, 7) Inc(i, 7) else
else Inc(i, 5); Inc(i, 5);
end end
// -------------- // --------------
// Quadratic Bezier // Quadratic Bezier
@ -3194,7 +3216,7 @@ begin
AData.Height := ly2 - ly; AData.Height := ly2 - ly;
end; end;
// Make sure the latest page size is syncronized with auto-detected // Make sure the latest page size is synchronized with auto-detected
// or ViewBox-only obtained size // or ViewBox-only obtained size
Page_Width := AData.Width; Page_Width := AData.Width;
Page_Height := AData.Height; Page_Height := AData.Height;