mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-08 14:18:17 +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;
|
||||
X3, Y3: Double;
|
||||
procedure Move(ADeltaX, ADeltaY: Double); override;
|
||||
function GenerateDebugTree(ADestRoutine: TvDebugAddItemProc; APageItem: Pointer): Pointer; override;
|
||||
end;
|
||||
|
||||
{ T3DSegment }
|
||||
@ -1325,9 +1326,10 @@ end;
|
||||
function TPathSegment.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc;
|
||||
APageItem: Pointer): Pointer;
|
||||
var
|
||||
lStr: string;
|
||||
lStr, lTypeStr: string;
|
||||
begin
|
||||
lStr := Format('[%s]', [Self.ClassName]);
|
||||
lTypeStr := GetEnumName(TypeInfo(TSegmentType), integer(SegmentType));
|
||||
lStr := Format('[%s] Type=%s', [Self.ClassName, lTypeStr]);
|
||||
Result := ADestRoutine(lStr, APageItem);
|
||||
end;
|
||||
|
||||
@ -1352,9 +1354,10 @@ end;
|
||||
function T2DSegment.GenerateDebugTree(ADestRoutine: TvDebugAddItemProc;
|
||||
APageItem: Pointer): Pointer;
|
||||
var
|
||||
lStr: string;
|
||||
lStr, lTypeStr: string;
|
||||
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);
|
||||
end;
|
||||
|
||||
@ -1369,6 +1372,15 @@ begin
|
||||
Y3 := Y3 + ADeltaY;
|
||||
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 }
|
||||
|
||||
procedure T3DSegment.Move(ADeltaX, ADeltaY: Double);
|
||||
@ -1920,6 +1932,8 @@ var
|
||||
t: Double;
|
||||
// For polygons
|
||||
Points: array of TPoint;
|
||||
MultiPoints: array of array of TPoint;
|
||||
lCurPoligon, lCurPoligonStartIndex: Integer;
|
||||
// for elliptical arcs
|
||||
BoxLeft, BoxTop, BoxRight, BoxBottom: Double;
|
||||
EllipseRect: TRect;
|
||||
@ -1964,6 +1978,9 @@ begin
|
||||
//
|
||||
// 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
|
||||
begin
|
||||
PrepareForSequentialReading;
|
||||
@ -1974,25 +1991,43 @@ begin
|
||||
ADest.Brush.Style := Brush.Style;
|
||||
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
|
||||
begin
|
||||
//WriteLn('j = ', j);
|
||||
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);
|
||||
CoordY := CoordToCanvasY(Cur2DSegment.Y);
|
||||
|
||||
Points[j].X := CoordX;
|
||||
Points[j].Y := CoordY;
|
||||
MultiPoints[lCurPoligon][j-lCurPoligonStartIndex].X := CoordX;
|
||||
MultiPoints[lCurPoligon][j-lCurPoligonStartIndex].Y := CoordY;
|
||||
|
||||
{$ifdef FPVECTORIAL_TOCANVAS_DEBUG}
|
||||
Write(Format(' P%d,%d', [CoordY, CoordY]));
|
||||
{$endif}
|
||||
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}
|
||||
Write(' Now the details ');
|
||||
|
@ -1262,12 +1262,15 @@ begin
|
||||
lLastCommandToken := lCurTokenType;
|
||||
ReadNextPathCommand(lCurTokenType, i, CurX, CurY, AData, ADoc);
|
||||
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
|
||||
begin
|
||||
lTmpTokenType := lLastCommandToken;
|
||||
if lLastCommandToken = sttMoveTo then lTmpTokenType := sttLineTo;
|
||||
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);
|
||||
end;
|
||||
end;
|
||||
@ -1292,16 +1295,19 @@ begin
|
||||
begin
|
||||
X := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||
Y := FSVGPathTokenizer.Tokens.Items[i+2].Value;
|
||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||
|
||||
// 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
|
||||
ConvertSVGDeltaToFPVDelta(AData, X, Y, X, Y);
|
||||
CurX := CurX + X;
|
||||
CurY := CurY + Y;
|
||||
end
|
||||
else
|
||||
begin
|
||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||
CurX := X;
|
||||
CurY := Y;
|
||||
end;
|
||||
@ -1397,8 +1403,16 @@ begin
|
||||
end;
|
||||
if (i >= 7) and (lCorrectPreviousToken) then
|
||||
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;
|
||||
if lCurTokenType = sttRelativeSmoothBezierTo then
|
||||
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;
|
||||
// Now the non-missing items
|
||||
X3 := FSVGPathTokenizer.Tokens.Items[i+1].Value;
|
||||
@ -1421,14 +1435,25 @@ begin
|
||||
ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y);
|
||||
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
|
||||
((i < 7) or (not lCorrectPreviousToken)) then
|
||||
begin
|
||||
X2 := CurX;
|
||||
Y2 := CurY;
|
||||
if lCurTokenType = sttRelativeSmoothBezierTo then
|
||||
begin
|
||||
X2 := CurX;
|
||||
Y2 := CurY;
|
||||
end
|
||||
else
|
||||
begin
|
||||
X2 := 0;
|
||||
Y2 := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
if lCurTokenType = sttRelativeBezierTo then
|
||||
// The final step
|
||||
if lCurTokenType in [sttRelativeBezierTo, sttRelativeSmoothBezierTo] then
|
||||
begin
|
||||
AData.AddBezierToPath(X2 + CurX, Y2 + CurY, X3 + CurX, Y3 + CurY, X + CurX, Y + CurY);
|
||||
CurX := CurX + X;
|
||||
|
@ -56,6 +56,7 @@ begin
|
||||
InflateGZ(AFileName, DataStream);
|
||||
DataStream.Position := 0;
|
||||
ReadFromStream(DataStream, AData);
|
||||
//DataStream.SaveToFile('/tmp/foo.svg');
|
||||
finally
|
||||
DataStream.Free;
|
||||
end;
|
||||
|
Loading…
Reference in New Issue
Block a user