BerndGabriel / HtmlViewer

The well-known Delphi/Lazarus HtmlViewer/FrameViewer
Other
398 stars 147 forks source link

Work-around required for FPC / macOS #346

Open manliomazzon opened 8 months ago

manliomazzon commented 8 months ago

I discovered that when compiling for macOS on Apple Silicon machines (M1, M2, etc) and targeting x86_64, there is a compiler bug that causes Canvas.TextOut to ignore the Brush.color, even if Brush.style is NOT bsClear.

In other words, if you set a Brush.Color, and then set Brush.Style := bsSolid, and then call TextOut, the background color will NOT be applied. The result will be as if Brush.Style = bsClear.

This bug affects THtmlViewer, for example here: \<span style="background-color:yellow;">Highlighted text\</span> because ultimately that background color is applied with Brush.Style := bsSolid.

I have made my own crude work-around like this:

  1. I Declare a global variable: var BuggyCanvas: TCanvas = nil;

  2. In procedure DrawTheText (unit HTMLSubs), right under the line Canvas.Brush.Style := bsSolid; I add this new line: BuggyCanvas := Canvas;

  3. When the time comes to actually write the text, in unit HtmlMisc, function ExtTextOutW(...), just before calling ExtTextOut, I do this:

{$IFDEF DARWIN} {$IFDEF CPUX86_64} if BuggyCanvas <> nil then begin BuggyCanvas.FillRect(X, Y, X + BuggyCanvas.TextWidth(s), Y + BuggyCanvas.TextHeight(s)); BuggyCanvas := nil; end; {$ENDIF} {$ENDIF} // next line is the original code Result := ExtTextOut(DC, X, Y, etc...

Explanation:

  1. Whenever the situation arises in which text must be written with a non-transparent background color (point 2 above), then I save the canvas to the variable BuggyCanvas.
  2. Just before writing text with TextOut/ExtTextOut, I check if BuggyCanvas is not nil, and in that case, knowing that TextOut will not do the job, I paint myself a rectangle of the correct size (TextWidth and TextHeight) at the correct position (X, Y) where the text is about to be painted. This work-around works perfectly for me.
  3. The two conditional defines ensure that the work-around is only applied when necessary.

Again this only happens when compiling on an Apple Silicon machine, targeting x86_64 i.e. Intel Apple machines.

Figuring this whole thing out took me a lot of time and effort, and so I hope that if you come across the same problem you'll find this solution quickly as well.

BerndGabriel commented 1 month ago

Thanks for this detailed description and workaround.