4

To my surprise I have found out that rendering text repeatedly on a TCanvas is somehow "additive". I realise that the setting Canvas.Brush.Style:=bsClear is the cause of the problem, but I really do need to render the text transparently and repeatedly (i.e in the OnPaint event). After doing this the text doesn't look good.

How can I avoid that?

Here is some sample code; you can see the effect if you make several clicks on a TButton called btn1.

procedure TForm1.btn1Click(Sender: TObject);
begin
Form1.Canvas.Brush.Style:=bsClear; //if you omit this, everything is OK.
Form1.Canvas.Font.Color:=clRed;
Form1.Canvas.Font.Name:='Times new Roman';
Form1.Canvas.Font.Style:=[fsBold];
Form1.Canvas.Font.Size:=12;
Form1.Canvas.TextOut(50,50,'www.stackoverflow.com');
end;
AndrewC
  • 32,300
  • 7
  • 79
  • 115
lyborko
  • 2,571
  • 3
  • 26
  • 54
  • i think what you do here is very wrong from windows concepts POV. Canvas is volatile thing to be redrawn on demand. It is not persistent. Moving the form outside the screen or hovering it with some another window should just erase that picture. To follow Windows way use TPaintBox and draw what you need on demand, To force anti-Windows persistent ways use TImage to keep your drawings. – Arioch 'The Sep 18 '12 at 07:37
  • @Arioch> of course... but this is very simple example... just for demonstration – lyborko Sep 18 '12 at 07:39
  • 3
    Stop painting in button on click handlers and start painting in response to `WM_PAINT`. In VCL you would normally use `TPaintBox` to shield yourself from Win32 API. I repeat, only ever paint in response to `WM_PAINT`. – David Heffernan Sep 18 '12 at 08:15
  • @Arioch: you are right... in my Paintbox1.OnPaint event I continued to render on Form1.Canvas (mistake in my program), thats why I have got a false impression, that the text is rendered incorrectly. Nevertheless this strange effect persists, so the question still remain. I removed this bug in my application, so it seems to be everything OK (like you said) – lyborko Sep 18 '12 at 08:39
  • if you "fixed the bug" then change the source you show to the one you actually use. And what the outcome, "strange effect persists" or "everything OK" ? You are given two answers by now, try them and report the outcomes. – Arioch 'The Sep 18 '12 at 08:46
  • @lyborko, that strange effect persists because the [`ExtTextOut`](http://msdn.microsoft.com/en-us/library/windows/desktop/dd162713(v=vs.85).aspx) function just renders the text that way. And it's probably a side effect of some sort of antialiasing applied by GDI at rendering. – TLama Sep 18 '12 at 08:56
  • @ all : All of you provided good solution (Arioch) and explanation (TLama, Sertac). "strange effect persists" only when I do not put the rendering function in OnPaint event. I just wondered then why it is rendered that way, but TLama gave the answer. Thank you all. – lyborko Sep 18 '12 at 09:07

2 Answers2

7

That is because the GDI applies some antialiasing when drawing the text, for the text to look better. This causes some pixels of the background outside the drawn text to be painted red/reddish near the text. When you next draw your text, if you don't clear the background, antialiasing causes these reddish pixels to become more red.

You would either clear the background as Arioch 'The stated in his answer, or if you really need to render the text transparently and repeatedly, you can turn off antialiasing.

TOndrej has provided a nice function in this answer for specifying text output quality. Using it, the code becomes:

begin
Form1.Canvas.Brush.Style:=bsClear;
Form1.Canvas.Font.Color:=clRed;
Form1.Canvas.Font.Name:='Times new Roman';
Form1.Canvas.Font.Style:=[fsBold];
Form1.Canvas.Font.Size:=12;
SetFontQuality(Form1.Canvas.Font, NONANTIALIASED_QUALITY);  // <--
Form1.Canvas.TextOut(50,50,'www.stackoverflow.com');
end;
Community
  • 1
  • 1
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
3

You should clean the background before rendering.

If such messages/events are not exposed by the control, then you have to remember the last rendered text TRect (function TCanvas.TextExtent or like that) and clean it before rendering next text.

Also sometimes possible and more simple (though is kind of "dirty and ugly" workaround) would be to keep just a transparent TLabel floating above the canvas in proper place and change its caption when needed.

Arioch 'The
  • 15,799
  • 35
  • 62