[Solved] How to obtain the complementary color of a TColor ? lazarus / 39-Graphics ---------------------------------------------------------------------- devEric69 · 2019-07-17 12:45 Hi, I was using the Jedi's method JvgUtils.ComplementaryColor(AColor: TColor): TColor; , under Delphi. Is there an equivalent in FPC or LCL packages? ---------------------------------------------------------------------- marcov · 2019-07-17 12:51 jedi is third party, just port the relevant parts? ---------------------------------------------------------------------- wp · 2019-07-17 16:14 I was search through all the sources in jvcl (https://github.com/project-jedi/jvcl/tree/master/jvcl/run). The current repository does not contain a unit JvgUtils any more; an older one which I have on my HD has it but it is lacking a function "ComplementaryColor". Please be more specific where to find it. But when my understanding of "complementary color" is the same as theirs then why don't you just simply use Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  Graphics; function ComplementaryColor(AColor: TColor): TColor;begin  Result := RGBToColor(255 - Red(AColor), 255- Green(AColor), 255 - Blue(AColor));end; A bit more refined version is "InvertColor" in unit Graphics. ---------------------------------------------------------------------- circular · 2019-07-17 19:59 Or this: Code: Delphi  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function ComplementaryColor(AColor: TColor): TColor;begin  Result := clWhite - ColorToRGB(AColor);end; ---------------------------------------------------------------------- garlar27 · 2019-07-17 21:20 Quote from: circular on July 17, 2019, 09:59:16 pmOr this: Code: Delphi  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function ComplementaryColor(AColor: TColor): TColor;begin  Result := clWhite - ColorToRGB(AColor);end;This is in case you need the complementary of the light color palette. If you need the complementary for the ink color palette then I think you should use CMYK (Cyan Magenta Yellow and Ink) colors, and I don't know well how to use them since I'd never programed using that palette. ---------------------------------------------------------------------- winni · 2019-07-17 22:22 RGB and CMYK are linear converted. In theory you need only CMY to paint (print) everything, but 100% of CMY only gives an ugly gray- brown. So for printing you need the K (Kontrast = ink). For converting RGB to CMYK see the following procedure. Notice that the resulting values of C,M,Y and K are in the range of 0..255 - not 0..100!! Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  procedure RGBtoCMYK(r,g,b: byte; var c,m,y,k : byte);inline;var r1,g1,b1,c1,m1,y1,k1 : single;     begin      r1 := r/255;      g1 := g/255;      b1 := b/255;      k1 := max(r1,g1);      k1 := max(k1,b1);      k1 := 1-k1;      if k1 = 1 then         begin        c1 := 0; m1 := 0; y1 := 0;         end else         begin           c1 := (1-r1-k1) / (1-k1);           m1 := (1-g1-k1) / (1-k1);         y1 := (1-b1-k1) / (1-k1);         end;      c := round(c1*255);    m := round(m1*255);      y := round(y1*255);      k := round(k1*255);    end;        Enough info? Winni ---------------------------------------------------------------------- devEric69 · 2019-07-18 09:54 @wp: sorry for forgetting to mention the Jedi version number. @others: thank you for your answers. I had the same result (but not as well, in 3..a few lines ): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses   Graphics, FPImage;  function CompementaryColor2(AColor: TColor): TColor;var  aRGB: record    iRed, iGreen, iBlue: Byte;  end;  aFPColor: TFPColor;  aColorRef: TColorRef;begin  aFPColor:= TColorToFPColor(AColor);  aColorRef:= FPColorToTColorRef(aFPColor);  //(1) verification of RGB components, from the basic AColor composite  with aRGB do begin    iRed:= Graphics.Red(aColorRef);    iGreen:= Graphics.Green(aColorRef);    iBlue:= Graphics.Blue(aColorRef);  end;  Form1.lblRedBase.Caption:= 'RedBase: ' + IntToStr(aRGB.iRed);  Form1.lblGreenBase.Caption:= 'GreenBase: ' + IntToStr(aRGB.iGreen);  Form1.lblBlueBase.Caption:= 'BlueBase: ' + IntToStr(aRGB.iBlue);  //(2) obtain the complements of the RGB components, from the basic AColor composite  with aRGB do begin    iRed:= 255 - Graphics.Red(aColorRef);    iGreen:= 255 - Graphics.Green(aColorRef);    iBlue:= 255 - Graphics.Blue(aColorRef);  end;  (* Form1.lblRedCompl:= 'RedCompl: ' + ; Form1.lblGreenCompl:= 'GreenCompl: ' + ; Form1.lblBlueCompl:= 'BlueCompl: ' + ; *)  result:= RGBToColor(aRGB.iRed, aRGB.iGreen, aRGB.iBlue);end;         But, I have the following comprehension problem; when I code...: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button2Click(Sender: TObject);var  aBaseColor: TColor;begin  aBaseColor:= TColor($AA6239); //with $00AA6239, I get the same result; //with $AA623900, this color is a light grey, not the brown displayed by websites  Panel1.Color:= aBaseColor; end;... then, I will check on https://www.colorhexa.com/aa6239: I don't see the same color, as the one on the TForm??! However, when I take a calculator, I get: Red = $aa = 170. Green = $62 = 98. Blue = $39 = 57. Nevertheless, Free Pascal tells me, that: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i with aRGB do begin    iRed:= Graphics.Red(aColorRef); // == 57.    iGreen:= Graphics.Green(aColorRef); // == 98.    iBlue:= Graphics.Blue(aColorRef); // == 170.  end; ==> So, iRed and iBlue are reversed (!!?), compared to all tested web sites of colors verification (by entering #aa6239, as in the *.css files). ➔ My question is ( using only simple RGB; no Hue, sensitivity, ..., ink that I don't know yet ): why this inversion ( i.e. how to get from $aa6239 in Free Pascal, the same color on a TForm as #aa6239 displayed by the *.css and Javascript )? ---------------------------------------------------------------------- circular · 2019-07-18 11:27 Indeed TColor written in hex is swaped compared to #... In binary that makes sense because the first bits are the ones on the right of the number. You need to swap red and blue if you write as if it was #... Code: Delphi  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function SwapRedBlue(AColor: TColor): TColor;begin  Result := RGBToColor(Blue(AColor), Green(AColor), Red(AColor));end; ... Panel1.Color := SwapRedBlue($AA6239); ---------------------------------------------------------------------- devEric69 · 2019-07-18 12:05 Thank you very much, circular, for your technical clarification, and your function\trick... ...which poses 2 problems for a basic Lazarus user, with the semantic: - the TColor type could have a synonym named TBGRColor for exemple (but it's falsely clever, right?). - some graphics.pas functions are bug traps, always because of semantics: they are called function xxxRGByyyy(R, G, B: Byte): TThis;, eg function RGBToColor(R, G, B: Byte): TColor; (the naming \ order of parameters is also a trap). So - I'm asking the moderators (I have no vision at all of the involvement of these methods in the LCL) - first of all, what do I do? A bug (or modification?) report with this hyperlinked thread? Nothing? ---------------------------------------------------------------------- wp · 2019-07-18 12:24 This leads to the elemental issue with "little endian" and "big endian" byte order: look at the number 256 - this is $01FF in hex. However, when this value is stored on a file by an Intel processor you'll the the bytes in the order FF 01. When you store to file with a Motorola processor you'll find the bytes in the "correct" order 01 FF. Some file formats store the rgb bytes in the order r-g-b, others in b-g-r, and when there is a forth alpha channel byte it gets even more compilcated. Confusing? Yes. So, please: no error in the graphics unit. It's just a matter of convention - and to listen to the alarm clock in your head when you try to determine the rgb components out of a "color". ---------------------------------------------------------------------- devEric69 · 2019-07-18 12:43 All right: thank you for the explanations \ arguments. I will think about increasing the volume of the alarm clock in my head, when I'll use a TColor (should I resonate contextually with the TColor , thinking about TRGBColor or TBGRColor ?). PS: by the way, your complementary color algorithm is correct. ---------------------------------------------------------------------- wp · 2019-07-18 13:37 Quote from: devEric69 on July 18, 2019, 02:43:31 pmbby the way, your complementary color algorithm is correct. In this case you should use circular's routine (reply #3), it is much more efficient and does exactly the same. ---------------------------------------------------------------------- Thaddy · 2019-07-18 14:38 You can probably use a cast a mask and the not operator? Should be faster than subtraction. It does a bit-wise negation. ---------------------------------------------------------------------- devEric69 · 2019-07-18 16:32 QuoteYou can probably use a cast a mask and the not operator? Too complicated for me . The main thing, is that I didn't understand that ( déjà vu ): To call or not to call SwapRedBlue depends from my material context, and the purpose of this call: - depending on the CPU processor type (Motorola versus Intel), I will write - in a file - AColor's RGB in that order ie RGB; or inversed ie BGR (cross-compilation problems are not yet fully in my head). - depending on the logic of use: let's say I found a pretty #aabbcc color on a website, i.e. a hexdecimal representation in @string format ==> it means that Red='#aa', Green='#bb', and Blue='#cc'. If you want to assign this same compiled color, then $aabbcc to a Canvas property, it will not work: indeed, the binary bits are read from right to left (as @circular pointed out). So, the compiled equivalent will have to be: Red=$cc, Green=$bb Blue=$aa (lately, I've been developing with interpreted Javascript code). PS: another simple manipulation of the bytes, to obtain 3 differents colors - which contrast with each other at most - is the "triadic color scheme": if color_base=aabbcc ==> triad_1=bbccaa, triad_2=ccaabb. ---------------------------------------------------------------------- winni · 2019-07-18 16:54 You have too check first, which convention is used: * HTML writes the hex code in Motorola format * The GIMP shows the color hex code in Motorola Format So if you take a color code from HTML or the  GIMP you have to swap red and blue. Easy test: Fill something with color = $FF If it is red you are on an Intel machine. Otherwise if it is blue you are on a motorala maschine Winni ---------------------------------------------------------------------- circular · 2019-07-19 06:05 Quote from: Thaddy on July 18, 2019, 04:38:53 pmYou can probably use a cast a mask and the not operator? Should be faster than subtraction. It does a bit-wise negation. I was worried by that in the past but in practice it made no difference. Subtracting is not so complicated, for example if you compare that to multiplication. Quote from: devEric69 on July 18, 2019, 06:32:23 pmThe main thing, is that I didn't understand that ( déjà vu ): To call or not to call SwapRedBlue depends from my material context, and the purpose of this call: - depending on the CPU processor type (Motorola versus Intel), I will write - in a file - AColor's RGB in that order ie RGB; or inversed ie BGR (cross-compilation problems are not yet fully in my head). - depending on the logic of use: let's say I found a pretty #aabbcc color on a website, i.e. a hexdecimal representation in @string format ==> it means that Red='#aa', Green='#bb', and Blue='#cc'. If you want to assign this same compiled color, then $aabbcc to a Canvas property, it will not work: indeed, the binary bits are read from right to left (as @circular pointed out). So, the compiled equivalent will have to be: Red=$cc, Green=$bb Blue=$cc (lately, I've been developing with interpreted Javascript code). Yep. Note that when writing a file, if you write each byte individually, then you can just follow the specification of the file format. Otherwise, well TColor contains 4 bytes, so what we're saying here about swaping red and blue will be incomplete. QuotePS: another simple manipulation of the bytes, to obtain 3 differents colors - which contrast with each other at most - is the "triadic color scheme": if color_base=aabbcc ==> triad_1=bbccaa, triad_2=ccaabb. Indeed. ---------------------------------------------------------------------- Thaddy · 2019-07-19 07:28 Quote from: circular on July 19, 2019, 08:05:14 amQuote from: Thaddy on July 18, 2019, 04:38:53 pmYou can probably use a cast a mask and the not operator? Should be faster than subtraction. It does a bit- wise negation. I was worried by that in the past but in practice it made no difference. Subtracting is not so complicated, for example if you compare that to multiplication. Well, not or negation operates on only one register, subtraction on two.... On arm it makes a difference. That was my ratio. But I never applied this on graphics, only on audio. ---------------------------------------------------------------------- wp · 2019-07-19 07:35 Quote from: devEric69 on July 18, 2019, 06:32:23 pmPS: another simple manipulation of the bytes, to obtain 3 differents colors - which contrast with each other at most - is the "triadic color scheme": if color_base=aabbcc ==> triad_1=bbccaa, triad_2=ccaabb. In this algorithm, the original and modified colors will have no or little contrast when aa, bb and cc are equal or almost equal. ---------------------------------------------------------------------- devEric69 · 2019-07-19 08:14 Indeed. But the complementary and trid colors are the only 2 algorithms, to obtain colors by simple manipulations of R, G and B. Afterwards, the algorithms become more complicated (need to use the chromatic circle and to use [radians] angles, shadow degrees i.e. gray percentage, highlighting...). I saw that there was an impressive library of objects \ methods to manage these concepts, once they were understood (which is not yet my case): https://wiki.lazarus.freepascal.org/mbColorLib . In fact I use the complementary color sparingly (contrast is what tires the eyes the most): it's just to "massively" affect the color of the fonts in opposition to the color of the Canvas of their parent component, with recursive RTTI (I happily tested my Delphi RTTI routines: they work very well with Lazarus). ---------------------------------------------------------------------- wp · 2019-07-19 14:29 Quote from: devEric69 on July 19, 2019, 10:14:44 amBut the complementary and trid colors are the only 2 algorithms, to obtain colors by simple manipulations of R, G and B. Be careful with absolute statements ("the only 2 algorithms"). I know a third one, the result is less distracting than the multicolored ComplemantaryColor: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function HighContrastColor(AColor: TColor): TColor;var  sum: Integer;begin  sum := Red(AColor) + Green(AColor + Blue(AColor);  if sum < 3*128 then   // AColor is rather "dark" ---> return white    Result := clWhite  else                    // AColor is rather "bright" --> return black    Result := clBlack;end; I am attaching a demo showing the high-contrast, complementary color and two byte-swapping functions. ---------------------------------------------------------------------- devEric69 · 2019-07-19 15:20 Yeah: it's true that the "complementary" is rather ineffective in the middle of a grey environment . Thank you for sharing. ---------------------------------------------------------------------- circular · 2019-07-19 17:13 Quote from: Thaddy on July 19, 2019, 09:28:43 amWell, not or negation operates on only one register, subtraction on two.... On arm it makes a difference. That was my ratio. But I never applied this on graphics, only on audio.Not sure how you could use only one register here as the not is applied to 3 bytes, not the highest byte. ---------------------------------------------------------------------- lucamar · 2019-07-19 17:34 Quote from: circular on July 19, 2019, 07:13:29 pmNot sure how you could use only one register here as the not is applied to 3 bytes, not the highest byte. Processor registers are four (or eight) bytes long, so no problem, is there? I mean, the highest pixel format is 32 bit: it just fits in a 386 register. ---------------------------------------------------------------------- circular · 2019-07-20 15:18 Quote from: lucamar on July 19, 2019, 07:34:37 pmProcessor registers are four (or eight) bytes long, so no problem, is there? I mean, the highest pixel format is 32 bit: it just fits in a 386 register. Sure but the not instruction cannot be applied on 3 bytes. To invert only part of the value, one could do a xor with a mask, but where does the mask come from? Either a register or an immediate value. ---------------------------------------------------------------------- Thaddy · 2019-07-20 19:48 Quote from: circular on July 20, 2019, 05:18:35 pmSure but the not instruction cannot be applied on 3 bytes. To invert only part of the value, one could do a xor with a mask, but where does the mask come from? Either a register or an immediate value. Hence the mask on a 32 bit  values, masking out the least significant byte.. You can also not the individual  bytes of course, but that would be slower. ---------------------------------------------------------------------- circular · 2019-07-22 10:18 Yes ---------------------------------------------------------------------- Bazzao · 2019-07-24 21:41 Quote from: devEric69 on July 17, 2019, 02:45:54 pmHi, I was using the Jedi's method JvgUtils.ComplementaryColor(AColor: TColor): TColor; , under Delphi. Is there an equivalent in FPC or LCL packages? I found this years ago and it works quite well. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var I,R,G,B:integer;    R:=Red(Memo1.Color);  G:=Green(Memo1.Color);  B:=Blue(Memo1.Color);  I:=Round(Sqrt(R*R*0.241+G*G*0.691+B*B*0.068));  if I>128 then    Memo1.Font.Color:=clBlack  else    Memo1.Font.Color:=clWhite;  The result colour is only black or white. B