7

Lines with thickness > 1 appear to be drawn differently on Windows and Android. I'm using Delphi 11.0. Create a blank multi-platform application and add the following in the FormPaint event.

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;

This results in the following. The same happens when drawing on a TImage.

enter image description here

It seems that in Windows the endpoints of the line are at the center of the line caps, whereas on Android they're at the extremes of the line caps. I'm testing on a Huawei P10 running Android 8.0.0. I'm not currently able to test on a more recent Android version. A Google search doesn't seem to give any results on this issue. I'd appreciate if anyone has any info on this issue and what could be done about it? If someone could test this on a more recent Android version, that would also be appreciated. I could of course add special code for Android to extend the line endpoints by half the line thickness, but I'd like to avoid that if possible.

The Android documentation seem to imply that it shouldn't behave like this. https://developer.android.com/reference/android/graphics/Paint.Cap

The main difference between Windows and Android is that they use different implementations of TCanvas. Windows uses TCanvasD2D, whereas Android is using TCanvasGpu. Looking into the Delphi code. I wonder if the following code in FMX.StrokeBuilder.pas is causing the issue. This code gets runs from FMX.Canvas.GPU.pas, even with TStrokeDash.Solid. I can't work out why it would offset the ends like that.

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; 

I can confirm that TCanvasGpu is the issue by setting FMX.Types.GlobalUseGPUCanvas to True before Application.Initialize. Then TCanvasGpu is used even on Windows instead of TCanvasD2D and I get the same issue that I see on Android.

Rohit Gupta
  • 4,022
  • 20
  • 31
  • 41
XylemFlow
  • 963
  • 5
  • 12
  • 1
    Did you try using SKIA4Delphi ? – SergeGirard Sep 20 '22 at 05:20
  • We use FMXNativeDraw on Android because there are too many problems with the draw quality in the default FMX solution. – Hans Sep 20 '22 at 08:00
  • 1
    Thanks. I hadn't heard of SKIA4Delphi. It looks like that may require me to rewrite my graphics code though. I have had a look at FMX.Graphics.Native already, so I may try that. – XylemFlow Sep 20 '22 at 13:22
  • 2
    @XylemFlow Actually no, SKIA4Delphi doesn't require to rewrite your graphics code. Skia provides a new, common canvas for all supported platforms. It also has many more benefits - it's really worth to give it a try ! – iamjoosy Sep 20 '22 at 15:34

0 Answers0