L'objet Color(), the color of sprite (et setPixel)
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()
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 pls 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 38 bit
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.
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).