mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-19 07:59:32 +02:00
fpvectorial: Now smooth bezier seams 100% correct =)
git-svn-id: trunk@41660 -
This commit is contained in:
parent
915f42b9b9
commit
17439e0e35
@ -201,6 +201,7 @@ type
|
|||||||
X2, Y2: Double;
|
X2, Y2: Double;
|
||||||
X3, Y3: Double;
|
X3, Y3: Double;
|
||||||
procedure Move(ADeltaX, ADeltaY: Double); override;
|
procedure Move(ADeltaX, ADeltaY: Double); override;
|
||||||
|
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ T3DSegment }
|
{ T3DSegment }
|
||||||
@ -1325,9 +1326,10 @@ end;
|
|||||||
function TPathSegment.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc;
|
function TPathSegment.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc;
|
||||||
APageItem: Pointer): Pointer;
|
APageItem: Pointer): Pointer;
|
||||||
var
|
var
|
||||||
lStr: string;
|
lStr, lTypeStr: string;
|
||||||
begin
|
begin
|
||||||
lStr := Format('[%s]', [Self.ClassName]);
|
lTypeStr := GetEnumName(TypeInfo(TSegmentType), integer(SegmentType));
|
||||||
|
lStr := Format('[%s] Type=%s', [Self.ClassName, lTypeStr]);
|
||||||
Result := ADestRoutine(lStr, APageItem);
|
Result := ADestRoutine(lStr, APageItem);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1352,9 +1354,10 @@ end;
|
|||||||
function T2DSegment.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc;
|
function T2DSegment.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc;
|
||||||
APageItem: Pointer): Pointer;
|
APageItem: Pointer): Pointer;
|
||||||
var
|
var
|
||||||
lStr: string;
|
lStr, lTypeStr: string;
|
||||||
begin
|
begin
|
||||||
lStr := Format('[%s] X=%f Y=%f', [Self.ClassName, X, Y]);
|
lTypeStr := GetEnumName(TypeInfo(TSegmentType), integer(SegmentType));
|
||||||
|
lStr := Format('[%s] Type=%s X=%f Y=%f', [Self.ClassName, lTypeStr, X, Y]);
|
||||||
Result := ADestRoutine(lStr, APageItem);
|
Result := ADestRoutine(lStr, APageItem);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1369,6 +1372,15 @@ begin
|
|||||||
Y3 := Y3 + ADeltaY;
|
Y3 := Y3 + ADeltaY;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function T2DBezierSegment.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc;
|
||||||
|
APageItem: Pointer): Pointer;
|
||||||
|
var
|
||||||
|
lStr: string;
|
||||||
|
begin
|
||||||
|
lStr := Format('[%s] X=%f Y=%f CX2=%f CY2=%f CX3=%f CY3=%f', [Self.ClassName, X, Y, X2, Y2, X3, Y3]);
|
||||||
|
Result := ADestRoutine(lStr, APageItem);
|
||||||
|
end;
|
||||||
|
|
||||||
{ T3DSegment }
|
{ T3DSegment }
|
||||||
|
|
||||||
procedure T3DSegment.Move(ADeltaX, ADeltaY: Double);
|
procedure T3DSegment.Move(ADeltaX, ADeltaY: Double);
|
||||||
@ -1920,6 +1932,8 @@ var
|
|||||||
t: Double;
|
t: Double;
|
||||||
// For polygons
|
// For polygons
|
||||||
Points: array of TPoint;
|
Points: array of TPoint;
|
||||||
|
MultiPoints: array of array of TPoint;
|
||||||
|
lCurPoligon, lCurPoligonStartIndex: Integer;
|
||||||
// for elliptical arcs
|
// for elliptical arcs
|
||||||
BoxLeft, BoxTop, BoxRight, BoxBottom: Double;
|
BoxLeft, BoxTop, BoxRight, BoxBottom: Double;
|
||||||
EllipseRect: TRect;
|
EllipseRect: TRect;
|
||||||
@ -1964,6 +1978,9 @@ begin
|
|||||||
//
|
//
|
||||||
// For solid paths, draw a polygon for the main internal area
|
// For solid paths, draw a polygon for the main internal area
|
||||||
//
|
//
|
||||||
|
// If there is a move-to in the middle of the path, we should
|
||||||
|
// draw then multiple poligons
|
||||||
|
//
|
||||||
if Brush.Style <> bsClear then
|
if Brush.Style <> bsClear then
|
||||||
begin
|
begin
|
||||||
PrepareForSequentialReading;
|
PrepareForSequentialReading;
|
||||||
@ -1974,25 +1991,43 @@ begin
|
|||||||
ADest.Brush.Style := Brush.Style;
|
ADest.Brush.Style := Brush.Style;
|
||||||
ADest.Pen.Style := psClear;
|
ADest.Pen.Style := psClear;
|
||||||
|
|
||||||
SetLength(Points, Len);
|
SetLength(MultiPoints, 1);
|
||||||
|
SetLength(MultiPoints[0], Len);
|
||||||
|
lCurPoligon := 0;
|
||||||
|
lCurPoligonStartIndex := 0;
|
||||||
|
|
||||||
for j := 0 to Len - 1 do
|
for j := 0 to Len - 1 do
|
||||||
begin
|
begin
|
||||||
//WriteLn('j = ', j);
|
//WriteLn('j = ', j);
|
||||||
CurSegment := TPathSegment(Next());
|
CurSegment := TPathSegment(Next());
|
||||||
|
if (j > 0) and (CurSegment.SegmentType = stMoveTo) then
|
||||||
|
begin
|
||||||
|
SetLength(MultiPoints[lCurPoligon], j-lCurPoligonStartIndex);
|
||||||
|
Inc(lCurPoligon);
|
||||||
|
SetLength(MultiPoints, lCurPoligon+1);
|
||||||
|
SetLength(MultiPoints[lCurPoligon], Len);
|
||||||
|
lCurPoligonStartIndex := j;
|
||||||
|
end;
|
||||||
|
|
||||||
CoordX := CoordToCanvasX(Cur2DSegment.X);
|
CoordX := CoordToCanvasX(Cur2DSegment.X);
|
||||||
CoordY := CoordToCanvasY(Cur2DSegment.Y);
|
CoordY := CoordToCanvasY(Cur2DSegment.Y);
|
||||||
|
|
||||||
Points[j].X := CoordX;
|
MultiPoints[lCurPoligon][j-lCurPoligonStartIndex].X := CoordX;
|
||||||
Points[j].Y := CoordY;
|
MultiPoints[lCurPoligon][j-lCurPoligonStartIndex].Y := CoordY;
|
||||||
|
|
||||||
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
|
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
|
||||||
Write(Format(' P%d,%d', [CoordY, CoordY]));
|
Write(Format(' P%d,%d', [CoordY, CoordY]));
|
||||||
{$endif}
|
{$endif}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ADest.Polygon(Points);
|
// Cut off excess from the last poligon
|
||||||
|
SetLength(MultiPoints[lCurPoligon], Len-lCurPoligonStartIndex);
|
||||||
|
|
||||||
|
// Draw each polygon now
|
||||||
|
for j := 0 to lCurPoligon do
|
||||||
|
begin
|
||||||
|
ADest.Polygon(MultiPoints[j]);
|
||||||
|
end;
|
||||||
|
|
||||||
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
|
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
|
||||||
Write(' Now the details ');
|
Write(' Now the details ');
|
||||||
|
@ -1262,12 +1262,15 @@ begin
|
|||||||
lLastCommandToken := lCurTokenType;
|
lLastCommandToken := lCurTokenType;
|
||||||
ReadNextPathCommand(lCurTokenType, i, CurX, CurY, AData, ADoc);
|
ReadNextPathCommand(lCurTokenType, i, CurX, CurY, AData, ADoc);
|
||||||
end
|
end
|
||||||
|
// In this case we are getting a command without a starting letter
|
||||||
|
// It is a copy of the last one, or something related to it
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
lTmpTokenType := lLastCommandToken;
|
lTmpTokenType := lLastCommandToken;
|
||||||
if lLastCommandToken = sttMoveTo then lTmpTokenType := sttLineTo;
|
if lLastCommandToken = sttMoveTo then lTmpTokenType := sttLineTo;
|
||||||
if lLastCommandToken = sttRelativeMoveTo then lTmpTokenType := sttRelativeLineTo;
|
if lLastCommandToken = sttRelativeMoveTo then lTmpTokenType := sttRelativeLineTo;
|
||||||
Dec(i);// because there is command token
|
// For bezier I checked that a sttBezierTo upon repetition expects a sttBezierTo
|
||||||
|
Dec(i);// because there is no command token in this command
|
||||||
ReadNextPathCommand(lTmpTokenType, i, CurX, CurY, AData, ADoc);
|
ReadNextPathCommand(lTmpTokenType, i, CurX, CurY, AData, ADoc);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -1292,16 +1295,19 @@ begin
|
|||||||
begin
|
begin
|
||||||
X := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
X := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||||
Y := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
Y := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
|
||||||
|
|
||||||
// take care of relative or absolute
|
// take care of relative or absolute
|
||||||
if lCurTokenType = sttRelativeMoveTo then
|
// Idiotism in SVG: If the path starts with a relative move to,
|
||||||
|
// the coordinates are absolute =o source: http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
|
||||||
|
if (lCurTokenType = sttRelativeMoveTo) and (i > 0) then
|
||||||
begin
|
begin
|
||||||
|
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
||||||
CurX := CurX + X;
|
CurX := CurX + X;
|
||||||
CurY := CurY + Y;
|
CurY := CurY + Y;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||||
CurX := X;
|
CurX := X;
|
||||||
CurY := Y;
|
CurY := Y;
|
||||||
end;
|
end;
|
||||||
@ -1397,8 +1403,16 @@ begin
|
|||||||
end;
|
end;
|
||||||
if (i >= 7) and (lCorrectPreviousToken) then
|
if (i >= 7) and (lCorrectPreviousToken) then
|
||||||
begin
|
begin
|
||||||
X2 := 2*FSVGPathTokenizer.Tokens.Items[i-2].Value - FSVGPathTokenizer.Tokens.Items[i-4].Value;
|
if lCurTokenType = sttRelativeSmoothBezierTo then
|
||||||
Y2 := 2*FSVGPathTokenizer.Tokens.Items[i-1].Value - FSVGPathTokenizer.Tokens.Items[i-3].Value;
|
begin
|
||||||
|
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;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
X2 := 2*FSVGPathTokenizer.Tokens.Items[i-2].Value - FSVGPathTokenizer.Tokens.Items[i-4].Value;
|
||||||
|
Y2 := 2*FSVGPathTokenizer.Tokens.Items[i-1].Value - FSVGPathTokenizer.Tokens.Items[i-3].Value;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
// Now the non-missing items
|
// Now the non-missing items
|
||||||
X3 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
X3 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||||
@ -1421,14 +1435,25 @@ begin
|
|||||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Covers the case where there is no valid first control point in smooth bezier
|
||||||
|
// The code is here to be after the conversions
|
||||||
if (lCurTokenType in [sttSmoothBezierTo, sttRelativeSmoothBezierTo]) and
|
if (lCurTokenType in [sttSmoothBezierTo, sttRelativeSmoothBezierTo]) and
|
||||||
((i < 7) or (not lCorrectPreviousToken)) then
|
((i < 7) or (not lCorrectPreviousToken)) then
|
||||||
begin
|
begin
|
||||||
X2 := CurX;
|
if lCurTokenType = sttRelativeSmoothBezierTo then
|
||||||
Y2 := CurY;
|
begin
|
||||||
|
X2 := CurX;
|
||||||
|
Y2 := CurY;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
X2 := 0;
|
||||||
|
Y2 := 0;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if lCurTokenType = sttRelativeBezierTo then
|
// The final step
|
||||||
|
if lCurTokenType in [sttRelativeBezierTo, sttRelativeSmoothBezierTo] then
|
||||||
begin
|
begin
|
||||||
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X3 + CurX, Y3 + CurY, X + CurX, Y + CurY);
|
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X3 + CurX, Y3 + CurY, X + CurX, Y + CurY);
|
||||||
CurX := CurX + X;
|
CurX := CurX + X;
|
||||||
|
@ -56,6 +56,7 @@ begin
|
|||||||
InflateGZ(AFileName, DataStream);
|
InflateGZ(AFileName, DataStream);
|
||||||
DataStream.Position := 0;
|
DataStream.Position := 0;
|
||||||
ReadFromStream(DataStream, AData);
|
ReadFromStream(DataStream, AData);
|
||||||
|
//DataStream.SaveToFile('/tmp/foo.svg');
|
||||||
finally
|
finally
|
||||||
DataStream.Free;
|
DataStream.Free;
|
||||||
end;
|
end;
|
||||||
|
Loading…
Reference in New Issue
Block a user