You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi 11.0 FMX框架下TCanvas.DrawLine在Android与Windows平台的绘制差异问题问询

Delphi 11 FMX TCanvas.DrawLine 跨平台线条端点位置差异问题

我来帮你拆解这个Delphi FMX跨平台绘制的兼容性问题,结合你已经做的排查,整理出问题根因和可行的解决方向:

问题复现与表现

你遇到的是Delphi 11跨平台应用中TCanvas.DrawLine的绘制行为差异:当线条厚度大于1且使用TStrokeCap.Round线帽时:

  • Windows平台(默认使用TCanvasD2D):线条端点位于线帽的中心位置,符合预期
  • Android 8.0.0(华为P10)(使用TCanvasGpu):线条端点位于线帽的最外侧,与预期不符
  • 该问题同样出现在TImage组件的绘制场景中

复现代码如下:

procedure TMainForm.FormPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
begin
  Canvas.Stroke.Thickness := 20;
  Canvas.Stroke.Cap := TStrokeCap.Round;
  Canvas.Stroke.Color := TAlphaColorRec.Red;
  Canvas.Stroke.Kind := TBrushKind.Solid;
  Canvas.DrawLine(PointF(20,20), PointF(70,70), 1);
  Canvas.DrawLine(PointF(70,70), PointF(130,70), 1);
end;

根因定位

你已经做了非常精准的排查,这里再梳理一下核心原因:

  1. Canvas实现差异:Windows平台默认使用TCanvasD2D,Android平台使用TCanvasGpu,两者的线条绘制逻辑不一致
  2. 源码bug疑点:FMX.StrokeBuilder.pas中的TStrokeBuilder.InsertDash方法存在问题——即使在实线(TStrokeDash.Solid)模式下,这段代码也会被FMX.Canvas.GPU.pas调用:
    procedure TStrokeBuilder.InsertDash(SrcPos, DestPos: TPointF; const DashDirVec, ThickPerp: TPointF);
    var
      InitIndex, DivIndex, Divisions: Integer;
      SinValue, CosValue: Single;
      RoundShift: TPointF;
    begin
      if FBrush.Cap = TStrokeCap.Round then
      begin
        RoundShift := DashDirVec * FHalfThickness;
        SrcPos := SrcPos + RoundShift;
        DestPos := DestPos - RoundShift;
      end;
    
    这段代码对线条的起始和结束位置做了不必要的偏移,导致TCanvasGpu绘制的线条端点位置偏移到了线帽外侧,而Windows的TCanvasD2D没有这个偏移逻辑。
  3. 验证结论:通过设置FMX.Types.GlobalUseGPUCanvas := True(在Application.Initialize前)让Windows也使用TCanvasGpu,出现了和Android完全一致的问题,确认TCanvasGpu是差异的源头。

另外你提到的Android官方文档说明也很关键:Android原生Paint.Cap的行为应该和Windows一致,说明这是FMX框架封装层的bug,而非Android平台本身的问题。

解决方案建议

1. 优先尝试官方修复路径

  • 升级框架版本:检查RAD Studio 11的最新补丁,或直接升级到Delphi 12,这个bug可能已经在后续版本中被修复
  • 提交官方bug报告:通过Embarcadero的官方bug追踪系统提交问题,附带复现代码、测试环境信息和你的排查结果,推动官方修复

2. 临时规避方案(尽量减少平台专属代码)

如果暂时无法升级或等待官方修复,这里提供两个可行思路:

方案A:条件编译调整坐标

虽然你希望避免,但这是最直接的临时解决办法,针对Android平台对线条端点做反向偏移:

procedure TMainForm.FormPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
var
  HalfThickness: Single;
  P1, P2, P3: TPointF;
  DirVec: TPointF;
  Len: Single;
begin
  Canvas.Stroke.Thickness := 20;
  Canvas.Stroke.Cap := TStrokeCap.Round;
  Canvas.Stroke.Color := TAlphaColorRec.Red;
  Canvas.Stroke.Kind := TBrushKind.Solid;
  
  HalfThickness := Canvas.Stroke.Thickness / 2;
  P1 := PointF(20,20);
  P2 := PointF(70,70);
  P3 := PointF(130,70);

  {$IFDEF ANDROID}
  // 调整第一条线的端点:反向偏移半个厚度
  DirVec := P2 - P1;
  Len := Sqrt(DirVec.X*DirVec.X + DirVec.Y*DirVec.Y);
  if Len > 0 then
  begin
    DirVec := DirVec / Len;
    P1 := P1 + DirVec * HalfThickness;
    P2 := P2 - DirVec * HalfThickness;
  end;
  // 调整第二条线的端点:水平方向反向偏移
  P2 := P2 + PointF(HalfThickness, 0);
  P3 := P3 - PointF(HalfThickness, 0);
  {$ENDIF}

  Canvas.DrawLine(P1, P2, 1);
  Canvas.DrawLine(P2, P3, 1);
end;

方案B:自定义StrokeBuilder

如果熟悉FMX源码,可以尝试重写TStrokeBuilder类,修改InsertDash方法的逻辑——当绘制实线时跳过那段偏移代码。不过这个方案需要对FMX框架有一定了解,且需要全面测试避免影响其他绘制逻辑。

3. 第三方测试呼吁

针对你无法测试更高版本Android的需求,这里呼吁其他开发者帮忙验证:如果有使用Delphi 11/12 + Android 9及以上版本的朋友,麻烦运行上述复现代码,确认该问题是否仍然存在,欢迎在评论区反馈结果。

内容的提问来源于stack exchange,提问作者XylemFlow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 15:07:42