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

发布于 2025-01-10 21:00:07 字数 2124 浏览 0 评论 0原文

我绘制了一条由两条线组成的路径,先向上然后向下返回到同一位置或几乎同一位置,但第一条线画得太高。如果我然后使用 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;

在 Windows 10 中运行代码的结果。我使用的是 Delphi 11,但 Delphi 10 也出现同样的问题。我尝试过切换 GPU,但出现同样的问题。

输入图片此处描述

放大视图:

在此处输入图像描述

I draw a path consisting of 2 lines going up and then back down to the same spot, or almost the same spot, but the first line is drawn too high. If I then draw the same lines using DrawLine I don't see the issue. Why is this happening?

Below is an example. Just drop a 400x400 TImage on a blank multiplatform form. The code draws 2 red paths, one with close to a 180 degree angle between the lines and one with less of an angle. The same lines are then drawn using DrawLine in blue. If the DrawPath function works correctly then the blue lines should completely cover the red lines, but they don't. In this example with a scale of 1.5 the path extends 7 pixels too high for the first path. The extent of the error reduces as the lines get further apart. The issue still happens with a scale of 1, but is less obvious.

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;

Result of the code when run in Windows 10. I'm using Delphi 11, but the same issue happens with Delphi 10. I've tried switching GPU but the same issue occurs.

enter image description here

Enlarged view:

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

笛声青案梦长安 2025-01-17 21:00:07

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

请注意,在接近 180 度的非常尖锐的角度处,斜接连接将变得无限大。然而,它似乎受到某种限制,也许与笔画粗细成比例。我不认为有办法改变德尔福中的斜接限制。

I've come to the conclusion that this isn't a glitch at all. It's because the default setting of TCanvas.Stroke.Join is TStrokeJoin.Miter. The artefact seen is just the sharp spike of the mitred corner. Using MoveTo before each line segment when constructing the path does solve the issue (because there's no join between the separate line segments) but so does setting the TCanvas.Stroke.Join parameter to TStrokeJoin.Round or TStrokeJoin.Bevel.

Note that at very sharp angles approaching 180 degrees, the miter join would become infinite. However, it appears to be limited somehow, perhaps in proportion to the stroke thickness. I don't think there's a way to change this miter limit in delphi.

冷心人i 2025-01-17 21:00:07

这是因为默认情况下 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));

This is because by default TPath is making smooth transitions between different path segments. I'm guessing it might be using Quadratic interpolation for making these smooth transitions.

Yes making smooth transition between two lines doesn't seem logical but it looks this is how it is implemented.

Now you can avoid this by telling the TPath that your two lines are not connected and thus should be treated as two separate lines even thou in reality they are connected. And you can do this by simply calling Path.MoveTo which is intended to shift position so you can create another unconnected line that dos not continue from your last path point.

Here is how modified code for your first sharp cornered line would look like:
NOTE that I'm specifying the exact same position for MoveTo command that was used for rendering of previous path line since you don't want the new line to start at new position.

// 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));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文