FMX:TCanvas.DrawPath 的奇怪故障。为什么?

FMX: Strange glitch with TCanvas.DrawPath. Why?

我画了一条由 2 条线组成的路径,向上然后向下回到同一个点,或几乎同一个点,但第一条线画得太高了。如果我然后使用 DrawLine 绘制相同的线,我看不到问题。为什么会这样?

下面是一个例子。只需将 400x400 TImage 放在空白的多平台表单上。该代码绘制了 2 条红色路径,一条线之间的角度接近 180 度,另一条角度较小。然后使用蓝色的 DrawLine 绘制相同的线。如果 DrawPath 函数工作正常,那么蓝线应该完全覆盖红线,但它们没有。在这个比例为 1.5 的示例中,路径延伸了 7 个像素,对于第一条路径来说太高了。错误的程度随着线条的距离越来越远而减小。问题仍然发生在比例为 1 时,但不太明显。

procedure TForm1.FormActivate(Sender: TObject);
var
  LPath1, LPath2 : TPathData;
  i : Integer;
begin
  // A path of 2 lines going up and then back down to almost the same spot
  LPath1 := TPathData.Create;
  LPath1.MoveTo(PointF(100,200));
  LPath1.LineTo(PointF(100,50 ));
  LPath1.LineTo(PointF(105,200));
  // A path of 2 lines going up and then back down at a wider angle
  LPath2 := TPathData.Create;
  LPath2.MoveTo(PointF(200,200));
  LPath2.LineTo(PointF(200,50 ));
  LPath2.LineTo(PointF(260,200));
  Image1.Bitmap.BitmapScale := 1.5; // The issue shows up more at larger scales
  Image1.Bitmap.SetSize(Trunc(Image1.Width), Trunc(Image1.Height));
  with Image1.Bitmap.Canvas do if BeginScene then begin
    Clear(TAlphaColorRec.White);

    // Draw the paths using DrawPath in red
    Stroke.Color := TAlphaColorRec.Red;
    Stroke.Thickness := 1;
    DrawPath(LPath1, 1);
    DrawPath(LPath2, 1);

    // Draw the paths using DrawLine in blue over the top
    // The red lines should be completely hidden under the blue
    Stroke.Color := TAlphaColorRec.Blue;
    for i := 1 to LPath1.Count - 1 do
      DrawLine(LPath1.Points[i-1].Point, LPath1.Points[i].Point, 1);
    for i := 1 to LPath2.Count - 1 do
      DrawLine(LPath2.Points[i-1].Point, LPath2.Points[i].Point, 1);

    EndScene;
  end;
  LPath1.Free;
  LPath2.Free;

  Image1.Bitmap.SaveToFile('test.png');
end;

运行 in Windows 10 时的代码结果。我使用的是 Delphi 11,但是 Delphi 10 也会出现同样的问题。我已经尝试切换 GPU 但出现同样的问题。

放大图:

这是因为默认情况下 TPath 在不同路径段之间进行平滑过渡。我猜它可能使用二次插值来实现这些平滑过渡。

是的,在两条线之间进行平滑过渡似乎不合逻辑,但看起来这就是它的实现方式。

现在您可以通过告诉 TPath 您的两条线没有连接并因此应被视为两条单独的线来避免这种情况,即使实际上它们是连接的。你可以通过简单地调用 Path.MoveTo 来实现这一点,它旨在改变位置,这样你就可以创建另一条不从你的最后一个路径点继续的未连接的线。

这是您的第一条尖角线的修改后代码的样子: 请注意,我为 MoveTo 命令指定了与用于呈现先前路径线的完全相同的位置,因为您不希望新线从新位置开始。

// A path of 2 lines going up and then back down to almost the same spot
  LPath1 := TPathData.Create;
  LPath1.MoveTo(PointF(100,200));
  LPath1.LineTo(PointF(100,50 ));
  //Add move to command to prevent Path from trying to make smooth transitions between two lines
  LPath1.MoveTo(PointF(100,50));
  LPath1.LineTo(PointF(105,200));

我得出的结论是,这根本不是故障。这是因为 TCanvas.Stroke.Join 的默认设置是 TStrokeJoin.Miter。看到的人工制品只是斜角的尖锐尖刺。在构造路径时在每个线段之前使用 MoveTo 确实解决了这个问题(因为单独的线段之间没有连接)但是将 TCanvas.Stroke.Join 参数设置为 TStrokeJoin.Round 或 TStrokeJoin.Bevel 也是如此。

请注意,在接近 180 度的非常锐角处,斜接会变得无限大。然而,它似乎以某种方式受到限制,可能与笔画粗细成比例。我认为没有办法改变 delphi 中的斜接限制。