In un precedente tutorial è stato trattato come dividere un'immagine con Actionscript 3, tuttavia le potenzialità della classe BitmapData permettono molte altre operazioni: in questo articolo vedremo come agire sui colori di un'immagine caricata esternamente, sia andando a ricavare il colore dei pixel di un'immagine, sia andando eventualmente a colorare l'immagine con una nuova tinta.
L'articolo è relativo all'uso di Actionscript 3 e per aprire i file di esempio è necessario Flash CS3.
Poiché opereremo tramite una classe esterna, creiamo un file Actionscript con nome colora.as e un file Actionscript 3 con nome colora.fla, che abbia impostata come Document Class la classe colora.
Impostazione della classe
Le prime righe di codice da inserire nella classe colora.as sono relative ai package da importare: in questo caso useremo flash.display.*
(per estendere la classe MovieClip, per il Loader e per usare la classe BitmapData), flash.events.*
(per associare gli eventi di click del mouse sull'immagine) e infine flash.net.URLRequest
che verrà utilizzato per caricare l'immagine all'interno del file SWF.
Ecco quindi il codice con cui iniziare la classe:
package{
// importiamo il package flash.display
import flash.display.*;
// importiamo il package flash.events
import flash.events.*
// importiamo il comando URLRequest del package flash.net
import flash.net.URLRequest
public class colora extends MovieClip { }
Una volta importati i package da utilizzare possiamo passare alla scrittura delle funzioni che gestiranno le nostre operazioni: la prima operazione da fare è ovviamente il caricamento dell'immagine all'interno del nostro filmato.
Caricare l'immagine
Per caricare l'immagine nel nostro file SWF possiamo avvalerci di un oggetto di tipo Loader e di una variabile URLRequest
che conterrà il percorso del file; per rendere versatile il nostro script la funzione prevederà un parametro grazie al quale potremo impostare di volta in volta la foto da caricare.
Andiamo allora a creare la prima funzione della nostra classe, che sarà la funzione carica.
public function carica(quale:String):void{
// creiamo l'oggetto Loader
var carica_foto:Loader = new Loader()
// impostiamo la richiesta del file
var richiesta:URLRequest = new URLRequest(quale)
// carichiamo il file nel loader
carica_foto.load(richiesta)
// aggiungiamo la foto allo stage
addChild(carica_foto)
}
Il codice è molto semplice: il file identificato, tramite il parametro, viene utilizzato per creare una URLRequest, che viene processata tramite un oggetto di tipo Loader (nel nostro esempio carica_foto), tale loader viene quindi aggiunto sullo stage per mostrare la foto all'utente.
Una volta che la nostra immagine viene caricata, e mostrata nello stage, diventa accessibile e utilizzabile per ricavare, ed eventualmente manipolare, i valori di colore di ogni suo singolo pixel.
Ricavare il colore di un pixel
Actionscript 3 mette a disposizione due differenti comandi per ricevere il colore di un pixel: getPixel
e getPixel32
; la differenza sta nel fatto che getPixel
restituisce i valori RGB (Red - Green - Blue) del pixel cliccato, mentre getPixel32
restituisce i valori ARGB (Alpha - Red - Green - Blue), in poche parole con getPixel32
siamo in grado di ottenere anche l'informazione relativa alla trasparenza del pixel cliccato.
Facciamo un rapido esempio: impostiamo un evento MouseEvent.MOUSE_DOWN
per il loader carica_foto e impostiamo il trace del colore del pixel cliccato:
public function carica(quale:String):void{
// creiamo l'oggetto Loader
var carica_foto:Loader = new Loader()
// impostiamo la richiesta del file
var richiesta:URLRequest = new URLRequest(quale)
// carichiamo il file nel loader
carica_foto.load(richiesta)
// quando clicchiamo sulla foto viene eseguita la funzione "colore"
carica_foto.addEventListener(MouseEvent.MOUSE_DOWN,colore)
// aggiungiamo la foto allo stage
addChild(carica_foto)
}
private function colore(pixel:Event):void{
var oggetto = pixel.target.content.bitmapData
trace(oggetto.getPixel(pixel.target.mouseX,pixel.target.mouseY).toString(16))
trace(oggetto.getPixel32(pixel.target.mouseX,pixel.target.mouseY).toString(16))
}
Andiamo quindi nel FLA e inseriamo come codice Actionscript:
carica_foto("Tramonto.jpg")
Testando il filmato potremo notare che il primo valore sarà del tipo a41239
, mentre il secondo restituirà un valore tipo ffa41239
. Notiamo come nel secondo caso vi siano due caratteri in più all'inizio del valore, che indicano appunto la trasparenza. Usando il comando toString(16)
abbiamo convertito i valori passati dai metodi getPixel
e getPixel32
in RGB.
Vediamo che la funzione colore ha un funzionamento molto semplice: per prima cosa memorizza l'oggetto cliccato (pixel.target
stabilisce il Loader cliccato, content
ne indica il contenuto e bitmapData
ci permette di ricavare le informazioni bitmap dell'immagine, fondamentali per l'uso con i metodi getPixel
e getPixel32
), quindi basandosi sul punto cliccato (pixel.target.mouseX
e pixel.target.mouseY
) individua l'esatto pixel su cui si trovava il mouse al momento del click.
Colorare un pixel di un'immagine
Actionscript non solo permette di ricavare il colore di ogni pixel di un'immagine, ma consente anche di modificare il colore di ogni pixel che compone un oggetto BitmapData
; a tale scopo sono disponibili i comandi setPixel
e setPixel32
, dove la differenza è la medesima vista per i comandi getPixel
e getPixel32
, difatti tramite setPixel
passeremo un normale valore RGB mentre con setPixel32
povremo impostare anche la trasparenza, ma solo per immagini che prevedano un canale alpha (quindi ad esempio i PNG), altrimenti questa informazione verrà ignorata e ci troveremo comunque con colore pieno.
Per osservare il comportamento di questo comando, variamo la funzione colore della nostra classe come di seguito:
private function colore(pixel:Event):void{
var oggetto = pixel.target.content.bitmapData
oggetto.setPixel32(pixel.target.mouseX,pixel.target.mouseY,0x00FF0000)
}
Torniamo nel FLA e carichiamo questa volta un'immagine di tipo PNG, quindi testiamo il filmato. Cliccando sull'immagine noteremo un risultato come quello in figura, con la cancellazione dei pixel cliccati (o meglio, la loro resa come pixel trasparenti).
Diversamente, qualora usassimo un'immagine priva di canale di trasparenza, il risultato sarebbe il seguente:
Sostituire un colore
Grazie a questi comandi possiamo facilmente creare una piccola funzione che sostituisca tutti i pixel di un determinato colore con un altro colore: ad esempio supponiamo di voler sostituire i pixel del colore corrispondente a quello cliccato dall'utente con un colore blu: ecco come variare la funzione colore e come potremmo impostare la funzione sostituisci.
private function colore(pixel:Event):void{
var oggetto = pixel.target.content.bitmapData
// impostiamo il colore da sostituire
var col_sorg = oggetto.getPixel(pixel.target.mouseX,pixel.target.mouseY).toString(16)
// avviamo la funzione sostituisci
sostituisci(col_sorg,000099,oggetto)
}
private function sostituisci(sorg,dest,target):void{
// stabiliamo larghezza e altezza dell'immagine
var largh = target.width
var alt = target.height
// percorriamo tutti i pixel dell'immagine
for(var l=0;l<largh;l++){
for(var a=0; a<alt; a++){
// ricaviamo il colore di ogni pixel
var colore_attuale = target.getPixel(l,a).toString(16)
// se corrisponde al colore cliccato
if(colore_attuale == sorg){
// lo sostituiamo con il colore di destinazione
target.setPixel(l,a,dest)
}
}
}
}
In figura è possibile vedere il risultato dopo alcuni click sull'immagine originale.
Volendo è possibile aumentare la tolleranza di colori non andando a cercare quelli precisamente uguali, ma utilizzando un range più ampio di valori.
Ottimizzare le prestazioni per le operazioni di analisi delle immagini
Per evitare che ogni istanza dell'oggetto BitmapData
che manipoliamo venga aggiornata (consumando quindi memoria) è consigliabile utilizzare i comandi lock
e unlock
, che rispettivamente bloccano e sbloccano gli aggiornamenti di un oggetto BitmapData
.
È quindi molto utile, specialmente in caso di modifiche complesse, richiamare il comando lock prima delle modifiche e usare poi il comando unlock dopo che lo script avrà concluso le sue operazioni sull'oggetto BitmapData; ad esempio la nostra funzione sostituisci, usando i comandi lock
e unlock
, diventerebbe così:
private function sostituisci(sorg,dest,target):void{
// stabiliamo larghezza e altezza dell'immagine
var largh = target.width
var alt = target.height
// percorriamo tutti i pixel dell'immagine
target.lock()
for(var l=0;l<largh;l++){
for(var a=0; a<alt; a++){
// ricaviamo il colore di ogni pixel
var colore_attuale = target.getPixel(l,a).toString(16)
// se corrisponde al colore cliccato
if(colore_attuale == sorg){
// lo sostituiamo con il colore di destinazione
target.setPixel(l,a,dest)
}
}
}
target.unlock()
}