Recent

Author Topic: text color visible  (Read 579 times)

Paolo

  • Hero Member
  • *****
  • Posts: 718
text color visible
« on: May 13, 2026, 12:25:06 pm »
Hello (win64/laz46/fpc 324RC1)

I have a component of mine for graphical usage (inherited form TGraphicControl).

I have in some cases some colored circle with inside a number (the circle ID). The number is written using Canvas.TextOut.

Now the question: how to change the text color automatically to have max visibility of ID-text inside the colored circle ?

if the circle is white black text (default) is fine, but if the circle is dark the number becames not visible.

There is a pen/brush mode to do that ?

code snippets are welcome,

thanks.

dsiders

  • Hero Member
  • *****
  • Posts: 1633
Re: text color visible
« Reply #1 on: May 13, 2026, 10:06:18 pm »
Hello (win64/laz46/fpc 324RC1)

I have a component of mine for graphical usage (inherited form TGraphicControl).

I have in some cases some colored circle with inside a number (the circle ID). The number is written using Canvas.TextOut.

Now the question: how to change the text color automatically to have max visibility of ID-text inside the colored circle ?

if the circle is white black text (default) is fine, but if the circle is dark the number becames not visible.

There is a pen/brush mode to do that ?

code snippets are welcome,

thanks.

graphics.pp has a routine called InvertColor. But it's aesthetics lack a little.

I generally want text to be either black (for light colors) or white (for dark colors). The attached has ColorIsDark() and ComplementColor() routines which I use. The example ises TPanel... but applies to TCanvas too.

Hope that helps...


speter

  • Hero Member
  • *****
  • Posts: 532
Re: text color visible
« Reply #2 on: May 14, 2026, 01:02:41 am »
If you want a more "hands-on" approach :) look at the attached project.

It creates 2 (almost identical) complementory colours for a given colour.
The first converts the colour to HLS colour space then adds 128 to the "H" value:
Code: Pascal  [Select][+][-]
  1.   [...]
  2.   r := byte(colours[a]);
  3.   g := byte(colours[a] shr 8);
  4.   b := byte(colours[a] shr 16);
  5.  
  6.   RGBtoHLS(r,g,b, ch,cl,cs);
  7.   k := ch;
  8.   ch := byte(k+128);
  9.   brush.color := HLStoColor(cH, cL, cS);
  10.   [...]
and the second simply subtracts each RGB from 255
Code: Pascal  [Select][+][-]
  1.   brush.color := 255 - r + (255 - g) shl 8 + (255 - b) shl 16;

I generally use the second (simpler) technique.

cheers
S.
I climbed mighty mountains, and saw that they were actually tiny foothills. :)

Khrys

  • Sr. Member
  • ****
  • Posts: 456
Re: text color visible
« Reply #3 on: May 14, 2026, 07:14:29 am »
There is a pen/brush mode to do that ?

TPenMode  has  pmXor,  which will invert any underlying colors when setting the pen to white (since  (B xor $FF) = (not B) = (255 - B)  when  B  is a byte, e.g. an RGB channel). I'm not aware of any similiar functionality for  TFont,  though.
You could also draw the text white-on-black into some reserved area and call  TCanvas.CopyRect  with  TCanvas.CopyMode  set to  cmSrcInvert,  which has the same XOR effect.

Personally, when I have to draw text over an arbitrarily colored background such as a photo, I prefer using black/white text with a white/black border (respectively), but that's not easy to accomplish when using  TCanvas.

creaothceann

  • Sr. Member
  • ****
  • Posts: 375
Re: text color visible
« Reply #4 on: May 14, 2026, 09:49:21 am »
A "shadow" effect might already improve readability, for that you simply draw the text with a slanted offset and a darker color first, then without an offset with the actual color.

For an outline there's this: https://forum.lazarus.freepascal.org/index.php?topic=39770.0

Paolo

  • Hero Member
  • *****
  • Posts: 718
Re: text color visible
« Reply #5 on: May 14, 2026, 12:10:14 pm »
Thak you all.
I'll check what proposed.

@Khrys I have already tried pmXor,  but nothing.

I'll come back with my observation.

Thanks again.

Paolo

  • Hero Member
  • *****
  • Posts: 718
Re: text color visible
« Reply #6 on: May 20, 2026, 03:12:19 pm »
Applied what suggested by dsiders. It is enough since I know the background color.
For more general case I'll see.

wp

  • Hero Member
  • *****
  • Posts: 13558
Re: text color visible
« Reply #7 on: May 20, 2026, 05:53:48 pm »
I generally want text to be either black (for light colors) or white (for dark colors). The attached has ColorIsDark() and ComplementColor() routines which I use.
Yes, I do that too, but my "color is dark or bright" routine of using the red/gree/blue mean value is more stupid:
Code: Pascal  [Select][+][-]
  1. function ContrastColor(AColor: TColor): TColor;
  2. var
  3.   sum: Integer;
  4. begin
  5.   sum := GetRValue(AColor) + GetGValue(AColor) + GetBValue(AColor);
  6.   if sum > 3*128 then
  7.     Result := clBlack
  8.   else
  9.     Result := clWhite;
  10. end;

In most cases the result is visually correct, but there are a few colors, e.g. clLime, where obviously the wrong color is picked - your solution utilizing the visually correct grayscale conversion is better. Don, would you allow me to add your functions to the GraphUtil unit? I would take the freedom, though, to rename the "ComplementColor" to "ContrastColor" because the word "complement" indicates the opposite color on the color wheel to me while the function just returns black or white.
« Last Edit: May 20, 2026, 06:29:12 pm by wp »

dsiders

  • Hero Member
  • *****
  • Posts: 1633
Re: text color visible
« Reply #8 on: May 20, 2026, 06:30:50 pm »
Would you allow me to add your functions to the GraphUtil unit?

Of course. 

I would take the freedom, though, to rename the "ComplementColor" to "ContrastColor" because it indicates the opposite color on the color wheel to me while it just returns black or white.

That is a better / more accurate name for it.

wp

  • Hero Member
  • *****
  • Posts: 13558
Re: text color visible
« Reply #9 on: May 20, 2026, 07:02:24 pm »
Would you allow me to add your functions to the GraphUtil unit?

Of course. 

I would take the freedom, though, to rename the "ComplementColor" to "ContrastColor" because it indicates the opposite color on the color wheel to me while it just returns black or white.

That is a better / more accurate name for it.
Done (https://gitlab.com/freepascal.org/lazarus/lazarus/-/commit/61dbd5c6967644682858572d85366476da2ee7e6).

hedgehog

  • Full Member
  • ***
  • Posts: 121
Re: text color visible
« Reply #10 on: May 21, 2026, 10:36:48 am »
I use this function
Code: Pascal  [Select][+][-]
  1. function GetContrastColor(C: TColor): TColor;
  2. var
  3.   R, G, B: Integer;
  4.   Y: double;
  5. begin
  6.   R := C and $ff;
  7.   G := (C shr 8) and $ff;
  8.   B := (C shr 16) and $ff;
  9.   Y := 0.299*R + 0.587*G + 0.114*B;
  10.   if Y>128 then
  11.     Result:= clBlack
  12.   else
  13.     Result:= clWhite;
  14. end;

Edited:

This function uses muted colors, which is useful for dark mode.

Code: Pascal  [Select][+][-]
  1. function GetContrastColor(C: TColor; MutedColor: boolean): TColor;
  2. const
  3.   ColorsArray: array [0..3] of byte = (255, 208, 0, 48);
  4. var
  5.   R, G, B: Integer;
  6.   Y: integer;
  7.   k: integer = 0;
  8. begin
  9.   R := C and $ff;
  10.   G := (C shr 8) and $ff;
  11.   B := (C shr 16) and $ff;
  12.   Y := trunc(0.299*R + 0.587*G + 0.114*B) - 128;
  13.   if Y>0 then k:= 2;
  14.   if MutedColor and (abs(Y)> 80) then inc(k);
  15.   B:= ColorsArray[k];
  16.   Result := ((B and $ff) shl 16) or ((B and $ff) shl 8) or (B and $ff);
  17. end;
« Last Edit: May 21, 2026, 11:48:12 am by hedgehog »

 

TinyPortal © 2005-2018