TXCOLOR, UNITÉ EXTENDCOLOR, SPECIALISATION DANS LE TRAITEMENTS DES COULEURS

cs_Delphiprog Messages postés 4297 Date d'inscription samedi 19 janvier 2002 Statut Membre Dernière intervention 9 janvier 2013 - 23 févr. 2006 à 23:10
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 - 7 mars 2006 à 21:55
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/36214-txcolor-unite-extendcolor-specialisation-dans-le-traitements-des-couleurs

f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
7 mars 2006 à 21:55
et voila, reparation du probleme plus quelques nouveaux ajout.
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
7 mars 2006 à 16:26
oh ben zut ... en effet le code est manquant ... lol ... je corrige cela au plus vite.
Utilisateur anonyme
7 mars 2006 à 01:51
Et ben voilà j'ai au moins compris le pourquoi du comment. Je ne le savais même pas
Merci pour l'éclairage de ma lanterne.

Maintenant dans ton code as-tu volontairement oublier d'implémenter les fonctions HighLighted et Shadowed Color où est-ce simplement par manque de temps ?

En tous cas bravo pour le HLS et le reste d'ailleurs
ça merite bien un 10/10

@+
Cirec
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
6 mars 2006 à 23:08
j'ajouterais, que cela ressemble un peu a la nuance entre le binaire intel qui se lit de droite a gauche : 0010 = 2
et le binaire motorola qui se lit de gauche a droite : 0010 = 4

alors l'explication du BGR, peut avoir plusieurs bonnes theories :
- la compatibilité Intel/Motorola au niveau du binaire ...
- le fait de stocker la couleur dans un entier signé plutot que dans un entier non-signé ...
- une etrangetée bizarre qui viens de nos CPU ...

bref ... le fait et que c'est comme ça et qu'il faut le savoir pour ne pas avoir un jours a ce demander pourquoi il y a une erreur.
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
6 mars 2006 à 22:59
haha! bienvenus dans mon univers ...

alors ... j'explique prend par exemple clBlue, clRed, clLime.

un graphiste te repondras : 0000FF, FF0000 et 00FF00

normal, tout les graphistes travailent en RGB.

maintenant soumettons ces couleurs a la fonction Format de delphi :
format('%.6x / %.6x % %.6x',[clblue,clred,cllime]);

il nous renvois : FF0000, 0000FF et 00FF00

normal, en delphi les couleurs sont bien exprimée en RGB mais les octets R et B sont inversés, donc BGR et non RGB.

donc si nous voulons un bleu metallique par exemple :
(RGB : 54749C)
il faudrat tenir compte de cela :
shape1.brush.color := $9C7454;

si on entre $54749C, on obtient un rouge bordeau bizarre et non notre jolie bleu metallique.

idem pour du orange ou l'on aurat beaucoup de rouge et un peu de vert (RGB : FFA000) il faudrat l'ecrire en delphi $00A0FF

la fonction RGB de l'unité windows nous retourne bel et bien une couleur RGB. mais dans certain cas, l'inversion des octets R et B pose probleme et donc la couleur n'est pas bonne.

pour faire un test facile pour demontrer cela :

label1.caption := format('%.2x / %.2x',[byte(clBlue shr 16),byte(clblue)]);

byte(clblue shr 16) est censé nous renvoyer le premier octet de la couleur RGB et donc devrais renvoyer l'octet correspondant au rouge, ici le resultat renverras FF qui est l'octet correspondant au bleu vus que delphi travail en BGR.

byte(clblue) est censé nous renvoyer le dernier octet de la couleur RGB et donc devrais renvoyer l'octet correspondant au bleu. ici le resultat renverras 00 qui est l'octet correspondant au rouge dans une couleur BGR.


alors effectivement, quand on prend une couleur BGR et qu'on utilise GetRValue, GetGValue, GetBValue et RGB, tout vas bien car :

GetRValue renvois l'octet bleu et RGB le remets a sa place
GetBValue renvois l'octet rouge et RGB le remets a sa place.

au final pour le developpeur, aucune erreur et pourtant ... si ... il y en a une et une grosse.

dans une suite d'octet BGR nous avons donc :

BB GG RR
B = byte(color shr 16)
G = byte(color shr 8)
R = byte(color)

dans une suite d'octet RGB nous avons :
RR GG BB
B = byte(color)
G = byte(color shr 8)
R = byte(color shr 16)

donc si un developpeur utilise les fonctions de l'unité windows et que dans un algorythme il decide d'appliquer une formule au octet bleu ça donneras cela :

var R,G,B : byte;
begin
R := GetRValue(color); // qui est en fait l'octet correspondant au bleu
G := GetGValue(color);
B := GetBValue(color); // qui est en fait l'octet correspondant au rouge
R := algoRouge(R); // erreur dans le retour
G := algoVert(G);
B := algoBleu(B); // erreur dans le retour
color := RGB(R,G,B); // est on persiste en remettant tout dans le bon ordre
end;

voila comment avoir un jolis bug en ce demandant "mais pourquoi nondidju!?"

et pourtant ... ces fonctions, leurs identifiant et leurs code est parfait ...
parfait mais est en total contradiction avec le stockage des couleurs de delphi qui inverse les bits bleu et les bits rouge... non non c'est pas un bug non plus...

dans l'aide delphi en effet, cette nuance n'est pas citée est pourtant quand on regarde a TColor , on trouve cela :

" Si vous spécifiez une valeur TColor en tant que valeur hexadécimale de 4 octets au lieu d'utiliser les constantes définies dans l'unitéGraphics,les trois premiers octets représentent respectivement l'intensitéRGB des couleurs bleu,vert et rouge.La valeur $00FF0000 (Delphi)ou 0x00FF0000 (C++)représente un bleu pur de pleine intensité,$0000FF00 (Delphi)ou 0x0000FF00 (C++),un vert pur et $000000FF (Delphi)ou 0x000000FF (C++),un rouge pur.$00000000 (Delphi)ou 0x00000000 (C++)représente le noir et $00FFFFFF (Delphi)ou 0x00FFFFFF (C++),le blanc. "

Et ils disent bien : l'intensité RGB des couleurs bleu, vert et rouge!

et aussi cela : La valeur $00FF0000 (Delphi)... représente un bleu pur ...

alors que en HTML, dans photoshop ou tout autre logiciel de graphisme on sait que FF0000 correspond a un rouge pur et idem pour les images bitmap (pas specialement BMP) qui stockent chaque couleur en RGB (RRGGBB) et non en BGR (BBGGRR).

ceci prouve donc cela ...

pensez donc a bien faire la nuance entre une couleur RGB dont le sens des octets est BGR et une couleur RGB dont le sens des octets et RGB.
Utilisateur anonyme
3 mars 2006 à 15:06
Bon alors j'ai refait les testes et en ce qui concerne le HLS les valeur sont correctes
Mais il y a encore un truc qui me chiffone et il est de taille si tu pouvais m'éclairer de tes lumières ce serait cool.
Si je fait le teste suivant avec une couleur simple qui est le bleu donc (R 0 G 0 B = 255):
procedure TForm1.Button1Click(Sender: TObject);
Var Col1, Col2, ColRef : TColor;
begin
ColRef := clBlue;
Col1 := GetRGB(GetRValue(ColRef), GetGValue(ColRef), GetBValue(ColRef));
Col2 := RGB(GetRValue(ColRef), GetGValue(ColRef), GetBValue(ColRef));
Label1.Caption := Format('Col1 : %d, Col2 : %d ', [Col1, Col2]);
Shape1.Brush.Color := Col1;
Shape2.Brush.Color := Col2;
Shape3.Brush.Color := ColRef;
end;

à l'arrivé avec la méthode RGB Col2 = ColRef
Mais Col1 = Rouge ?

Et j'ai cherché BRG dans l'aide de Delphi resultat inconu ?

Soit tu t'es emellé les pinceaux
où c'est les dev de chez Borland qui ont tout faux et ce depuis le début

ah oui encore une précision avec ton code en partant de la même couleur bleu on trouve bien
R 0 G 0 et B =255 jusque la Ok mais c'est le retour qui donne R = 255 G = 0 B = 0

je ne crompred plus rien es ce que tout ce qu'on fait depuis le début est faux ?
si oui il serait temps de les avertir

@+
Cirec
Utilisateur anonyme
3 mars 2006 à 13:25
Salut,
bon alors pour la nuance entre RGB et BRG non je ne savais pas qu'il y avait un BRG ;-(

Et pour la fonction que j'ai "corrigé" je me suis basé sur celle de l'unité Windows donc si en plus la base est fausse ça ne va pas m'aider. ;-(

Et pour finir Or et + ça je le savais que c'était kif kif au moins un truc que je savais ;-)

Le reste je vais tester le nouveau code et je te tiens au courant

@+
Cirec
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
3 mars 2006 à 06:18
tiens d'ailleur pour le probleme du resultat qui n'est pas bon sur HLS>RGB, j'ai trouver une astuces qui permet de compenser le defaut d'arrondis :

au lieu de (a la fin)

result := n1;

je fait

result := n1 - trunc((_HLSMAX-hue)/100);

c'est con mais ça permet de retrouver par exemple
FF80FF au lieu de FF7FFF (pour HLS = 200 180 240)
ou encore
8181C0 au lieu de 8282C0 (pour HLS = 160 151 81)

par contre j'ai remarquer que la valeur de T (hue) dans colordialog vas de 0..239 au lieu de 0..240, bizarre.
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
3 mars 2006 à 06:01
Et voila, suppression de GraphUtil, j'ai retranscrit des methodes plus fiable directement des methodes C++ du UnrealEngine et avec l'aide des tiennes.

j'ai egalement ajouter de nombreuses methodes en dehors de TXColor.

petite nouveautée dont je suis assé fier, la procedure FX_BMP_Plugin, qui permet de créer des semblant de plugin qui seront utiliser avec la methode ScanLine de TBitmap.

@Cirec :
result := (fRed or (fGreen shl 8) or (fBlue shl 16));

attention a l'ordre des octets, c'est du RGB pas du BGR!
ce qui peu preter a confusion c'est que si on nome une methode comme cela :
RGBToTruc(color : TColor)
pour moi l'ordre des octets de color est en RGB et non BGR (delphi)

et d'ailleur la fonction RGB devrait s'appelée BGR et non RGB

function RGB(r, g, b: Byte): COLORREF;
begin
Result := (r or (g shl 8) or (b shl 16));
end;

on le vois bien, l'ordre des octets est BGR et non RGB!
hehe...
et si on veux l'ordre RGB il faut ecrire :

Result := (B or (g shl 8) or (R shl 16));

* moi je fais la nuance ... pas vous ? *

donc quand je vois RGBToHLS ... je fournis une couleur RGB et non BGR ... d'ou GetRGB plutot que GetBGR...

* bete et con tu connais ? *

en gros sans modifier le code est en remplacant dans la methode GetRGB par GetBGR les resultats seront de nouveau correct.

de toute façon, avec les nouvelles methodes pour les convertions RGB>HLS et HLS>RGB tout est propres et les resultats sont parfait aprés plusieurs tests (photoshop et colordialog a l'appuis).
et pour ceux qui, comme moi, font la nuance entre BGR et RGB, toutes les methodes de convertions sont faite pour les deux : RGBToHLS, BGRToHLS, HLSToRGB, HLSToBGR etc...

par contre, entre
(C or C or C) et (C + C + C)
aucune difference, d'ailleur le symbole de OR en logique est + ... je dirais que c'est donc kifkif.

ASM C + C + C =
and eax,$0000FF
and edx,$0000FF
shl edx,$08
add eax,edx
and ecx,$0000FF
shl ecx,$10
add eax,ecx
mov result,eax

ASM C or C or C =
and eax,$0000FF
and edx,$0000FF
shl edx,$08
or eax,edx
and ecx,$0000FF
shl ecx,$10
or eax,ecx
mov result,eax

kif kif ... la seule difference c'est l'instruction Add qui change pour Or ...
Utilisateur anonyme
2 mars 2006 à 21:37
Bon alors j'ai trouvé un truc qui cloche dans ton code :

Tu as écris :
function TXColor.GetRGB : tcolor;
begin
result := (fRed shl 16) + (fGreen shl 8) + (fBlue);
end;

au lieu de :
function TXColor.GetRGB : tcolor;
begin
result := (fRed or (fGreen shl 8) or (fBlue shl 16));
end;

ce qui provoque déjà une erreur de résultat lors du calcul du HLS
les résultats sont déjà meilleurs mais il perciste toujours un petit décalage par raport aux valeurs obtenues dans un ColorDialog;
Les fonctions de GraphUtil ne sont pas fiables

@+
Cirec
Utilisateur anonyme
1 mars 2006 à 23:51
ah moi j'ai fait les comparaisons entre ton prog et les valeurs dans le TColorDialog et les tiennes sont fausses mais celles que j'ai testé avec ce code correspondait à celles du ColorDialog ????

@+
Cirec
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
1 mars 2006 à 23:48
haaa cool!

moi j'utilise, si tu as bien regarder, les methodes de l'unitée graphutil...

pour le probleme du RGB 128 qui reviens en RGB 127 c'est le probleme de :

1 on arrondis les calculs pour le retour en HLS
2 on arrondis les valeurs arrondis des calcul des retours HLS vers RGB

d'ou le manque de 1

il faudrait garder les valeurs en flottant pour ne pas avoir de perte.
chose qui serait possible a faire.

mais par contre le resultat est identique au methodes de GraphUtil ... donc ... pas vraiment d'interet.

mais merci quand meme.
Utilisateur anonyme
28 févr. 2006 à 22:11
Salut,

Essaye avec ce code, je l'avais récupérer je ne sais plus ou et après quelques modifications et corrections j'en ai fait une unité avec deux procédure qui permettent de calculer le RGB en TSL et inversement. Les résultats sont corrects mais j'ai toute fois remarqué encore une petite chose :
si tu donnes comme valeur R 128 G 128 B 128 et que tu convertis en TSL et à nouveau en RGB tu obtiens R 127 G 127 B 127.
Bon je n'ai pas cherché plus loin puisque pour moi il n'y avait que la conversion RGB en TSL qui m'intéressai. Mais je pense que ce n'est pas un problème pour toi.


Unit URGBToTSL;

Interface

Procedure RGB_To_TSL(R, G, B: Integer; Out T, S, L: Integer);
Procedure TSL_To_RGB(T, S, L: Integer; Out R, G, B: Integer);

Implementation

Function Htorgb(n1, n2, hue: word): word;
Const
HLSMAX = 240;
Begin

If hue < 0 Then hue := HLSMAX + hue;
If hue > HLSMAX Then hue := hue - HLSMAX;

If hue < ((HLSMAX * 2) Div 3) Then
Begin
If hue < (HLSMAX Div 2) Then
Begin
If hue < (HLSMAX Div 6) Then
result := (n1 + (((n2 - n1) * hue + (HLSMAX Div 12)) Div (HLSMAX Div
6)))
Else result := n2;
End
Else
result := (n1 + (((n2 - n1) * (((HLSMAX * 2) Div 3) - hue) + (HLSMAX Div
12)) Div (HLSMAX Div 6)));
End
Else
result := n1;
End;


Procedure RGB_To_TSL(R, G, B: Integer; Out T, S, L: Integer);
Const
HLSMAX = 240;
RGBMAX = 255;
Var
rgb : Array[1..3] Of byte;
cMax, cMin : byte;
i : byte;
Rdelta, Gdelta, Bdelta: integer;
Begin
rgb[1] := r;
rgb[2] := g;
rgb[3] := b;

cMax := rgb[1];
cMin := rgb[1];

For i := 2 To 3 Do
Begin
If cMax < rgb[i] Then cMax := rgb[i];
If cMin > rgb[i] Then cMin := rgb[i];
End;
S := (((cMax + cMin) * HLSMAX) + RGBMAX) Div (2 * RGBMAX);
If cMax = Cmin Then
Begin
L := 0;
T := 160;
End
Else
Begin
If (S <= (HLSMAX Div 2)) Then
Begin
L := (((cMax - cMin) * HLSMAX) + ((cMax + cMin) Div 2)) Div (cMax +
cMin);
End
Else
Begin
L := (((cMax - cMin) * HLSMAX) + ((2 * RGBMAX - cMax - cMin) Div 2))
Div (2 * RGBMAX - cMax - cMin);
End;

Rdelta := (((cMax - r) * (HLSMAX Div 6)) + ((cMax - cMin) Div 2)) Div (cMax
- cMin);
Gdelta := (((cMax - g) * (HLSMAX Div 6)) + ((cMax - cMin) Div 2)) Div (cMax
- cMin);
Bdelta := (((cMax - b) * (HLSMAX Div 6)) + ((cMax - cMin) Div 2)) Div (cMax
- cMin);

If r = cMax Then T := Bdelta - Gdelta
Else If g = cMax Then T := (HLSMAX Div 3) + Rdelta - Bdelta
Else T := ((2 * HLSMAX) Div 3) + Gdelta - Rdelta;
If T < 0 Then T := HLSMAX + T;
End;
End;


Procedure TSL_To_RGB(T, S, L: Integer; Out R, G, B: Integer);
Const
HLSMAX = 240;
RGBMAX = 255;
Var
Magic1, Magic2 : word;
Begin
If S = 0 Then
Begin
r := (L * RGBMAX) Div HLSMAX;
g := r;
b := r;
End
Else
Begin
If L <= (HLSMAX Div 2) Then
Begin
Magic2 := (L * (HLSMAX + S) + (HLSMAX Div 2)) Div HLSMAX;
End
Else
Begin
Magic2 := L + S - ((L * S) + (HLSMAX Div 2)) Div HLSMAX;
End;

Magic1 := 2 * L - Magic2;
r := (Htorgb(Magic1, Magic2, T + (HLSMAX Div 3)) * RGBMAX + (HLSMAX Div 2))
Div HLSMAX;
g := (Htorgb(Magic1, Magic2, T) * RGBMAX + (HLSMAX Div 2)) Div HLSMAX;
b := (Htorgb(Magic1, Magic2, T - (HLSMAX Div 3)) * RGBMAX + (HLSMAX Div 2))
Div HLSMAX;
End;
End;


End.


@+
Cirec
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
28 févr. 2006 à 18:01
voila voila, correction des quelques petites imperfection selon vos pertinente remarque.
nombreux ajout egalement.

certaines methodes necessiterais toute fois que je me penche serieusement dessus car les resultats me semble etrange voir erronés (sur le HLS).

si quelque a des solutions alternative, proposez!
"Plus rapide" : c'est toi qui vois.
Plus propre, j'en suis pas convaincu.

Pour l'exemple, tu peux compter le nombre de caractères, tu verras que c'est pas forcément plus rapide (après ça dépend: si tu as un nom de classe à rallonge comme TMaSuperClasseQuiGereLesCouleurs là je te dis pas).

Les constructeurs sont là pour ça, ça ne sert à rien de creer une méthode qui appele le constructeur et qui remplit les propriétés. Si tu as un nombre important de parametres, tu surcharge même la pile (même si on est pas à quelques octets près).

Mais après, tu fais comme tu veux.
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
25 févr. 2006 à 11:41
salut!

parce que :

declaration normale :

mavariable := typeclasse.create;
ou
mavariable := typeclasse.create(parametres);


declaration rapide :

CreateClasse(mavariable[,parametres]);


je trouve cela plus propre, plus rapide. tu ne trouve pas ?
d'ailleur cela me fait penser que ça ne devrait pas etre des procedures mais des fonctions avec retour booléen selon si la construction a eu lieu ou non.
Ah, une autre suggestion: pourquoi ne pas coder plusieurs constructeurs au lieu de creer ces procédure CreateExtCol() ?
Comme ça, tout est inclus dans une sule et même classe.

Allez, je te laisse défier tes neurones ...
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
25 févr. 2006 à 02:16
alors pour l'histoire du create en published c'est une habitude que j'ai pris de separer dans des zones bien distincte certains elements des classes.
et je mets toujours constructeur et destructeur de classe dans published ... pourquoi ? je sais pas ... certains passe 5 lignes entre Then et Begin ou Do et Begin (j'en passe et des meilleures) moi je mets en published ...
une petite fantaisie pas bien mechante toute fois.

mais pour si peu de code ... c'est une remarque pertinente que je corrigerais.

Sinon pour cette classe, mon idée est venue du fait que j'utilisais une unitée qui faisait le meme travail ... probleme beaucoup de fonction a retenir blablabla...
alors je me suis dis ... tiens ... pourquoi ne pas en faire une classe qui rendrais la tache completement transparente et automatisée...
et pouf ...


Autre reflexion majeur a la creation de cette classe (c'est un exercice aprés tout non ?) a ete de comment stocker ( dans private ) les données ...

Dois-je utiliser de nombreuses variable pour chaque renvois ?
> non trop lourd ...

Dois-je utiliser uniquement un TColor BGR ?
> non, ça ne faciliterais pas les convertions dans le methodes et surtout il faut aussi stocker l'alpha ...

ni une ni deux, mon choix c'est porté sur 4 variables de types byte. cela m'a parus le plus logique et adapté a la situation.

l'exercice de transformer mon unité de gestion des couleurs en classe automatisée est donc je pense pleinement reussi.

pour ce qui est des divers traitement sur les couleurs ... c'est une idée a laquelle je pense.
(un nouveau defi pour mes neuronnes! hehe)
Je trouve ça tout simplement génial.
C'est vrai que le constructeur "poublished" ne sert à rien mais bon ...

A côté de ça, on a une classe performante qui permet de gérer pas mal de trucs qui ne sont pas si simples à faire. (et surtout très répétitifs).
J'aurais juste une suggestion : pourquoi ne pas rajouter des méthodes d'addition, de sustraction, de multiplication, de division entre les différentes couleurs ?

J'en profite pour faire un petite paranthèse: avec .NET, ce serait tellement pratique. On pourrait carement rempacer le type TColor par celui-ci, ce qui ne ferait que des avantages.
L'ennsui, sous Win32, est qu'il faut construire et détruire les instances des classes et si l'on a besoin d'utiliser plein d'instances, on est vite perdu et le code devient très lourd. Vive le garbage collector.

++ Flo (10 /10 quand meme)
cs_Delphiprog Messages postés 4297 Date d'inscription samedi 19 janvier 2002 Statut Membre Dernière intervention 9 janvier 2013 32
23 févr. 2006 à 23:10
Quel est le but de mettre le constructeur en portée Published ?

Je n'ai pas regardé l'implémentation mais le sérieux et la réflexion menée pour définir la partie interface me semblent intéressantes.
Encore une belle démonstration de la puissance de la POO.
Bravo mon cher Foxi.
Rejoignez-nous