L'objet Color(), the color of sprite (et setPixel) (D7)
Il est possible que votre navigateur ait quelque mal à afficher
ce Shockwave. Accordez lui un peu plus de Ram et tout devrait rentrer
dans l'ordre.
Cette animation fait un usage intensif du nouveau terme color()
de Director 7. Sur cette nouveauté, l'aide en ligne de Director
est plutôt avare. color() c'est d'abord une fonction qui crée
une entité de type rgb ou paletteIndex selon les paramètres
qui lui sont transmis. Par cet formule "entité" on peut provisoirement
entendre un analogue des rect() ou des point(). Une fois cette entité
constituée, on va bénéficier de plusieurs fonctions
permettant de la manipuler. Voyons cela et d'abord dans la fenêtre
message.
set monMagenta to color(#rgb, 255, 0 , 255)
Si l'on demande à Director de nous montrer "magenta" nous
obtenons
put monMagenta
-- rgb( 255, 0, 255 )
On créé l'entité par la fonction color().
Cette fonction prend les arguments qu'on lui passe et retourne une
entité rgb() ou paletteIndex(). Qu'allons nous bien pouvoir
faire de ce type de données ? Des opérations diverses
comme par exemple celle-ci :
put monMagenta /2
-- rgb( 127, 0, 127 )
Ce qui correspond au violet royal. La possibilité d'effectuer
des opérations arithmétique avec cet objet ne doit
pas nous faire penser qu'il fonctionne comme les types de données
point() ou rect(). Un extraction de liste par exemple n'est pas
possible ici et une expression comme getAt(magenta, 2) provoquera
une erreur. En revanche une entité de ce type dispose de
propriétés et c'est pourquoi l'on dit que c'est un
OBJET color. (L'objet Date repose sur le même principe)
put the green of monMagenta
-- 0
put the blue of monMagenta
--127
put the colorType of monMagenta
-- #rgb
On peut connaître aisément l'équivalent indexé
de notre couleur (propriété paletteIndex)
put the paletteIndex of monMagenta
-- 2
Cette dernière propriété peut être modifiée
pour changer la couleur mais alors la propriété colorType
le sera aussi. Il est plus logique de changer ouvertement le mode
colorimétrique de référence de notre objet
par :
set the colorType of magenta to #paletteIndex
put monMagenta
-- paletteIndex( 2 )
Ici la propriété colorType de l'objet est modifiée
de sorte qu'il est référé à la palette
en cours d'utilisation au moment de la conversion. Ceci dit on
peut toujours connaître la valeur d'une composante RVB.
put monMagenta.red
-- 127
L'objet color possède également une méthode
qui renvoie la notation hexadécimale lui correspondant.
put hexString(magenta)
-- "#FF00FF"
Conclusion
Un objet color, c'est donc une entité qui peut être
exprimée rgb(1,2,3) ou paletteIndex(1) selon l'état
de sa propriété colorType, et qui peut-être
créée par la fonction color(). Ça représente
une couleur - Notez bien ! PAS la couleur de ceci ou de cela, PAS
dans un mode colorimétrique particulier, non , une couleur
abstraitement dont on peut obtenir les composante RVB ou bien l'équivalent
indexé. On peut donc travailler avec
l'objet color et définir des couleurs en RVB même si
l'animation n'utilise que 256 couleurs. La puissance d'un
tel outil apparaît quand on découvre que des mélanges
sont possibles.
set uneCouleur to color(#rgb, 255,200,200)
set uneAutreCouleur to color(#rgb, 150,210,60)
put uneCouleur - uneAutreCouleur
-- rgb(105,10,140)
Si vous connaissez Photoshop vous méditerez cette illustration
où à droite la couleur verte est appliquée
par dessus le rose avec l'encre différence :
Bien sûr rien ne vous empêche de tester les encres
de Director lui-même (Somme, différence,...) mais ne
lui demandez pas trop de rigueur quand-même !
Faire des mélanges de couleurs dans l'abstrait c'est bien
mais que va-t-on faire du résultat si on ne peut finalement
l'affecter aux sprites. Nous ne connaissions jusqu'à présent
que la propriété the foreColor of sprite qui attend
un nombre de 0 à 255 et certes pas un objet. La propriété
the paletteIndex of... permet de connaître à tout instant
l'équivalent indexé de notre objet couleur. On pourra
donc jouer seul dans son coin avec un objet couleur (monMelange),
puis finalement pour que le sprite en profite :
set the foreColor of sprite n to the paletteIndex of monMélange
Mais il y a mieux ! La nouvelle propriété the
color of sprite représente non pas une valeur numérique
indexée mais un entité couleur. Un sprite à
donc une propriété "the color of sprite" qui dans
tous les cas est un objet color. On pourra donc écrire directement
et sans passer par une conversion :
set the color of sprite n to monMélange
On encore et plus respectueusement de la nouvelle notation Lingo
:
sprite(n).color = monMelange
Forecolor, color of sprite... C'est la même chose direz-vous
? Non car l'objet color permet des mélanges lumineux ou même
seulement peut subir des opérations arithmétiques
que la manipulation d'une valeur indexée rend impossible.
Démonstration :
On se donne un objet color bleu pur. Dans la palette système
Mac (seulement) ce primaire est indexé 210 et nous
créons l'objet color avec le type RVB.
set monBleu to color(#rgb, 0,0,255)
put the paletteIndex of monBleu
-- 210
Si je divise maintenant l'intensité lumineuse de ce bleu
par 2, j'obtiens l'Obsidienne. Cette très belle teinte bleu
sombre est indexée 240 dans la palette Mac. Voyons comment
l'objet color se sort d'une division (parenthèses impératives) :
put the paletteIndex of (monBleu/2)
-- 240
Cette simple modification de teinte est quasiment impossible lorsque
l'on ne manipule que des valeurs indexées.
Créer le sélecteur de couleurs
LES CURSEURS (UN CLASSIQUE)
On dispose sur la scène un acteur forme longiligne
et par dessus un bitmap qui constitue notre curseur. 10 piste plus
bas on place un acteur QuickDraw de forme ronde. On associe au curseur
le comportement suivant.
property pDecalageH, pN, pDebutH
on beginSprite
me
pN = the currentSpriteNum
pDebutH = sprite
(pN - 1).locH
-- la position minimum
du curseur c'est l'extrême gauche de la glissière
-- ici pour un acteur forme c'est "the loc"
-- nous avons besoin de connaître cette valeur
pour
-- déterminer la position relative du curseur
lors des
-- déplacements le long de la glissière
sprite(pN).constraint
= pN -1
-- rappel : la glissière
est une piste plus haut que le curseur (pN -1)
end
on mouseDown
pDecalageH = the mouseH
- sprite(pN).locH
-- classique : on mesure l'écart
du bitmap à la souris
-- pour éviter un saut lors du déplacement
repeat while the stilldown
sprite(
pN). locH =
the mouseH - pDecalageH
-- on déplace le
curseur sous la souris
-- et on force le rafraîchissement
updateStage
x = (sprite(pN).locH
- pDebutH)/0.4
-- 100 position sont possibles
(largeur de la glissière)
-- la division par 0.4 permet d'obtenir 255 valeurs
(100/0.4=255)
sendSprite(pN+10, #changeDeValeur,
x)
-- on expédie l'info
à la forme ronde 10 piste plus bas
end repeat
end
Selon le même principe les mêmes acteurs sont de nouveau
placés sur la scène : l'acteur forme en premier, sur
la piste immédiatement dessous l'acteur curseur, dix pistes
plus bas la forme ronde. Le même comportement peut dès
lors être derechef attaché aux deux nouveaux curseurs.
LES CERCLES DE LUMIÈRE ROUGE, VERTE
ET BLEU
Ce sont trois forme QuickDraw placés pistes 13, 15
et 17, la couleur en est indifférente puisque nos curseur
la modifient mais pour obtenir l'effet de lumières se superposant
on affecte au deux derniers cercles (dessus) l'encre "Somme". Chaque
cercle recevra le message #changeDeValeur
lors du déplacement du curseur lui correspondant. Afin que
ces cercles sachent traiter l'instruction, nous devons à
chacun associer le comportement suivant :
property pN
on beginSprite
me
pN = the currentSpriteNum
end
on changeDeValeur me,
laquelle
case pN of
13: -- rouge
sprite(pN).color
= rgb(laquelle,0,0)
-- c'est ici que l'on affecte au cercle
une intensité de rouge
sendSprite(10, #changeDeCouleur,
#rouge, laquelle)
sendSprite(11, #changeDeCouleur,
#rouge, laquelle)
-- on adresse aussi un message et
deux arguments à deux
-- autres sprites pistes 10 et 11, ce sont les témoins
-- rectangulaires voir plus bas
15: -- vert
sprite(pN).color
= rgb(0,laquelle,0)
-- on affecte au cercle une intensité
de vert uniquement
sendSprite(10, #changeDeCouleur,
#vert, laquelle)
sendSprite(11, #changeDeCouleur,
#vert, laquelle)
17: -- bleu
sprite(pN).color
= rgb(0,0,laquelle)
sendSprite(10, #changeDeCouleur,
#bleu, laquelle)
sendSprite(11, #changeDeCouleur,#bleu,
laquelle)
end case
updatestage
end
LE RECTANGLE DE COULEUR COMPOSITE RVB
Un acteur forme là encore (piste 10) qui recevra
des trois cercles le message #changeDeCouleur
joint à ses paramètres. Nous dotons ce rectangle de
ce comportement.
property pN
on beginSprite
me
pN = the currentSpriteNum
end
on changeDeCouleur me,
quelleCouleur, quelleProportion
uneCouleur = sprite(pN).color
-- NOTA : bizarrement "sprite(pN).color.red
= x" ne marche pas !
-- Director exige pour le sprite un changement global d'objet
couleur
-- on créé donc un autre objet color reprenant les
valeurs actuelle de
-- sprite(pN).color. C'est ce nouvel objet (unCouleur) que l'on
modifie
-- puis avec lequel on remplace sprite(pN).color
case quelleCouleur of
#rouge :
uneCouleur.red
= quelleProportion
#vert:
uneCouleur.green
= quelleProportion
#bleu :
uneCouleur.blue
= quelleProportion
end case
sprite(pN).color
= uneCouleur
updateStage
end
LE RECTANGLE D'AFFICHAGE DE COULEUR INDEXÉES
Le grand rectangle d'affichage de la couleur indexée
correspondante (piste 11). Un acteur forme identique au précèdent
et dont le comportement sera très semblable. Une seule ligne
diffère :
property pN
on beginSprite
me
pN = the currentSpriteNum
end
on changeDeCouleur me,
quelleCouleur, quelleProportion
uneCouleur = sprite(pN).color
case quelleCouleur of
#rouge :
uneCouleur.red
= quelleProportion
#vert:
uneCouleur.green
= quelleProportion
#bleu :
uneCouleur.blue
= quelleProportion
end case
uneCouleur.colorType = #paletteIndex
-- on change le type de l'objet avant
-- d'en faire the color of sprite..
sprite(pN).color
= uneCouleur
updateStage
end
Dans ces dernières lignes une autre possibilité s'offrait
qui consistait à récupérer l'équivalent
numérique indexé de notre objet RVB puis à
en nourrir la propriété the foreColor of sprite.
sprite(pN).foreColor
= uneCouleur.paletteIndex
Application : setPixel() et getPixel()
Avertissement : cet article est paru
lors de la sortie de D7. Depuis la version 8, les fonctions getPixel()
et setPixel() sont documentées. D'autres commandes lingo
sont apparues permettant de gérer les images (copyPixel(),
image(),...) qui font partie de ce que l'on appelle l'imaging Lingo.
Reportez vous à la documentation de D8.
Afin d'exploiter l'objet color nous avons créé une
animation mettant en oeuvre deux fonctions non documentées
de D7 : getPixel() et setPixel(). La fonction getPixel() permet
de connaître la valeur indexée d'un pixel d'un un acteur
bitmap 8 bits, La fonction setPixel() permet de changer la couleur
d'un pixel précis. Promenez donc votre curseur sur les Imacs
plus bas... (shockWave 7.0)
getPixel()
GetPixel() requiert 3 arguments : un acteur bitmap et le delta
d'un pixel, noté - sur le Mac - depuis le coin supérieur
gauche de l'acteur ET SUR LE PÉCÉ DEPUIS LE COIN INFÉRIEUR
GAUCHE. Étrangement le premier pixel de l'acteur a les coordonnées
0,0. Si on place une apparition de l'acteur sur la scène,
on s'épargnera une soustraction en le calant sur le coin
supérieur gauche. De la sorte - sur le Mac - le premier pixel
de l'acteur (0,0) à les coordonnées point(1,1) sur
la scène.
A coté du bitmap, sur la scène, on a disposé
49 fois la même forme QuickDraw et demandé en Lingo
que lors d'un mouseDown sur la photo, les 49 formes reprennent la
couleurs des pixels survolés. Pour la forme carrée
centrale (sprite z) 4ème ligne, 4ème carré,
qui doit afficher la couleur du pixel exactement sous le pointeur,
le code est le suivant :
repeat while the stilldown
x = getAt(the
mouseloc,1) - 1
y = getAt(the
mouseloc, 2) -1
w = 1
if the platform contains "Windows" then w = 7
-- Rappel : le système de coordonnées
n'est plus le même sous windows
-- l'acteur 7 ici est une copie du bitmap (Édition/Dupliquer)
à laquelle
-- on a fait subir une symétrie verticale dans la fenêtre
dessin de Director.
-- C'est l'image normale (acteur 1) que l'on a placée sur
la scène, mais c'est
-- cette copie inversée dont on extrait la couleur des pixels.
sprite( z ).forecolor
= getpixel(member
w, x , y )
Les autres pixels de l'image source sont récupérés
en déplaçant le focus de la fonction vers la droite
(x + 1), la gauche (x - 1) ou vers le bas (y + 1). Et ainsi:
sprite (z + 1).forecolor
= getpixel(member
w, x + 3, y )
sprite (z + 7).forecolor
= getpixel(member
w, x, y + 1 )
etc.
On finit par l'inévitable updateStage qui force au sein
d'une boucle repeat le rafraîchissement écran.
updatestage
end repeat
end
ATTENTION ! La fonction getPixel() fonctionne
un peu différemment selon que l'on utilise la version 7.0
ou la mise à jour 7.0.2. Avec cette dernière
version, il faut pour obtenir la référence indexée
de la couleur du pixel effectuer une petite soustraction. La couleur
est dans ce cas 255 moins la valeur retournée. Dans D7.0.2,
on écrira donc :
set the forecolor of sprite z to 255 - getpixel(member w, x , y
)
Surtout ne me demandez pas pourquoi cette différence !
Et ne le demandez pas non plus à Macromedia, ils vous répondront
que getPixel() n'existe pas. Pour réaliser le shockwave ci-dessus,
nous avons utilisé D7.0F parce que le plug-in semble s'y
accorder.
Dernière remarque : GetPixel() retourne aussi une valeur
(entre 0 et 16777215 lorsque l'image est en 16 bits), valeur qu'il
est aisé de convertir en base hexadécimale pour obtenir
un triplet rgb (en 32 bits la valeur retournée sera divisée
par 8). toutefois les différences de plate-formes et de versions
rendent difficile son exploitation. Alors : à vous de jouer.
SetPixel() et... l'objet Color
SetPixel() permet comme on l'aura deviné de modifier la
valeur indexée d'un pixel d'acteur bitmap. L'instruction
requiert 4 arguments. L'acteur bitmap a modifier, le delta du pixel
et la référence indexée de la couleur.
Nos essais nous ont montré que l'exécution sur un
grand nombre de pixels n'était pas des plus rapide aussi
nous avons préféré utiliser une petite image
que nous avons fortement étirée sur la scène
de Director. Les utilisateurs dee version 8 et suivantes regarderont
la fonction copyPixels() et se riront de notre méthode pas
à pas (pixel à pixel).
Lors du clic sur le bouton "+ clair" nous souhaitons que chaque
pixel du nounours soit éclairci. Nous savons que nous pouvons
connaître sa valeur indexée par getPixel() mais pour
connaître la valeur indexée immédiatement plus
claire nous devons en passer par l'objet color. Rappel :
le premier pixel de l'acteur est noté 0,0, nous devons commencer
le recensement à 0 et ainsi écrire :
on mouseUp
repeat with x = 0 to
(the width of
member 1) - 1
repeat with
y = 0 to (the
height of
member 1) - 1
Pour chaque pixel de l'image, pris un à un, nous notons
sa valeur par getPixel puis, au fin de conversion et de calculs,
nous créons un objet color correspondant.
Sous D7.0.2, il aurait fallu écrire unObjetColor =
paletteIndex (255 - getpixel(member 1, x, y)). Maintenant, nous
devons convertir le type de l'objet en RVB afin de rendre possible
les opération arithmétiques :
Nous retrouvons l'équivalent indexée grâce
à la propriété the paletteIndex of unObjetColor
z = unObjetColor.paletteIndex
Et enfin nous convertissons le pixel en cette nouvelle valeur.
setPixel(member
1, x, y, z)
Rappel, sous D7.0.2 : setPixel(member 1, x, y, 255 -z)
end repeat
updateStage
end repeat
end
le dernier updateStage ne rendra certes pas le processus
plus rapide mais il nous permet de voir Director travailler. Nous
renonçons toutefois à le placer dans la boucle intérieure.
Ici, UpdateStage rafraîchira l'écran après chaque
changement d'une colonne.
L'animation "paletteRGB" constitue , une fois placée
dans le dossier Xtras de Director 7, un sélecteur de couleur
rgb fort utile... et en plus, le code de David est ouvert. Cliquez
pour télécharger
le fichier (Mac Stuffit 26 Ko).