Monthly Archives: December 2016

Black Paths with FMX D2D Canvas

Have you ever used the RiverSoftAVG SVG Component Library (RSCL) and it just shows a black SVG?  Have you ever had a Delphi FMX application on Windows where a TPath just displays as black?  You know that the path works in other applications or with VCL or with other FMX TCanvas but not in Windows?  If so, you have encountered a bug in the TCanvasD2D.DoDrawPath method.

Users of the RSCL are most likely to encounter this bug as SVGs use paths so heavily.  Earlier versions of the RSCL had clumsy and heavyweight hacks to ensure the black path did not occur (since removed).  However, I finally got frustrated enough with the bug to track it down and find a fix.  It is surprisingly easy to cause this bug even with adding your own paths to a TPath.

To recreate this bug:

  1. Create a new FireMonkey application
  2. Drop a TPath on the form
  3. Set the Path1.Data := ‘M 2,3 z m -1,1’; // this is a point path, nothing should draw (But notice how the path bounding rectangle disappears from the form)
  4. Set the Path1.Stroke.Thickness to 2
    (Notice how the path is filled with black)

The bug and fix has been posted to Embarcadero.  I would hope the fix will be in their next release.

This bug occurs when a path is specified with more than one close path and the last segment on the path does not have a close path on it. The TCanvasD2D.DoDrawPath method mistakenly doesn’t close the path when passing it to DirectX, causing DirectX to fill in the space of the current clipping rectangle.

To get around this for now, obviously you can just append a ClosePath to the end of your path.  However, if you do not want to edit the path or cannot guarantee what is in the path, you need to patch the FMX.Canvas.D2D.pas.  The bug fix link above has the whole fix, but the short answer is change the following line at the end of the DoDrawPath method from:

 if not Closed then
   Path.EndFigure(D2D1_FIGURE_END_OPEN);

to

 if (APath.Count < 1) or (APath[APath.Count - 1].Kind <> TPathPointKind.Close) then
   Path.EndFigure(D2D1_FIGURE_END_OPEN);

This is actually exactly what the DoFillPath does in its code but its fix was not copied over to DoDrawPath.

I wanted to document this bug for others.  This is a bug that can occasionally happen especially if you are using lots of paths like the SVG component library does.

Happy Holidays and Happy CodeSmithing!