Creare un’applicazione Paint in Flash CS3
Domenica 10 Febbraio, 2008Come esposto in Actionscript Flash contest: erase tool non è possibile "cancellare" una particolare zona di uno Sprite, MovieClip o Shape su cui sono state tracciate linee o rettangoli tramite il puntatore graphics. Esiste, infatti, il solo metodo clear() che, tuttavia, ha effetto sull'intera area del nostro oggetto. La soluzione al problema risiede nella possibilità di usare gli oggetti Bitmap e BitmapData. Come vedremo possiamo accedere direttamente ai dati Bitmap e manipolarli in modo da "cancellare" i tratti con un vero e proprio strumento "cancellino".
Presenterò due metodi diversi per realizzare il "cancellino". Il primo "disegna" (cancella) i dati direttamente nella BitmapData, sfruttando il metodo fillRect() - si può usare anche setPixel() per esempio. Il secondo metodo, quello che preferisco, sfrutta i metodi di fusioni (blendMode).
Prima di tutto vediamo che tipo di organizzazione minimale è necessaria per realizzare un semplice Painter in Flash. Lo schema presentato qui sotto vale per entrambe le proposte del "cancellino":

Ho creato tre layer: il primo, MovieClip o Sprite, funziona da sfondo e gestore eventi (MouseDown, MouseMove e MouseUp). Il secondo, la Bitmap, è il nostro layer principale, quello che conterrà effettivamente la grafica disegnata e su cui sarà applicata la funzione "cancellino". Il terzo e ultimo layer, lo Shape, risolve due questioni: la prima è che permette di utilizzare direttamente le funzioni messe a disposizione dal puntatore graphics (come lineStyle, drawRect(), ecc...). Inoltre aumenta le performance durante il tracciamento grafico, come spiegato in dettaglio più sotto.
METODO 1 - Accesso diretto ai dati Bitmap
Questo "sample" usa il metodo fillRect() direttamente sulla BitmapData (layer 2) dopo che il disegno è stato copiato (tramite il metodo draw()) dalla Shape (layer 3) alla Bitmap (layer 2).
Per capire come funziona ecco lo stesso filmato con i layer scomposti e in pseudo 3D - disegnate nel Box colorato - sorgente:
Come vedete si disegna sul layer 3, la Shape. Quando scatta l'evento MouseUp il contenuto della Shape viene copiato sulla Bitmap (e quindi i dati sono presenti nella BitmapData) e successivamente lo Shape viene pulito (clear()).
Nota: quest'ultimo punto, il clear() sulla Shape, non è da sottovalutare. Alcuni esempi di painter in Flash (quasi tutti sprovvisti di un vero "cancellino") disegnano direttamente su un MovieClip o Shape, mantenendo su quest'ultimi i dati disegnati. Dopo un po' che si disegna il sistema si rallenta, in quanto l'area "vettoriale" del MovieClip aumenta. Nel metodo da me presentato questo inconveniente viene risolto in quanto i dati vettoriali della Shape, quando vengono copiati nella Bitmap, sono tasformati in bit e lo Shape viene cancellato, liberando memoria e restituiendo fluidità al disegno!
Tenendo premuto il tasto Ctrl (control) viene "cancellata" direttamente la BitmapData tramite fillRect(). Questo è possibile in quanto fillRect(), come gli altri metodi che agiscono sulla BitmapData, permettono di impostare il "colore" tramite 0xARGB, dove A è il valore alpha, la trasparenza.
Il codice è il seguente - sorgente:
-
import flash.events.MouseEvent;
-
import flash.display.BitmapData;
-
import flash.display.Bitmap;
-
import flash.display.GradientType;
-
//
-
import flash.geom.Matrix;
-
//
-
var md:Boolean = false;
-
//
-
var event_spr:Sprite = new Sprite();
-
addChild (event_spr);
-
//
-
var area_width:Number = event_spr.stage.stageWidth;
-
var area_height:Number = event_spr.stage.stageHeight-32;
-
//
-
var fillType:String = GradientType.LINEAR;
-
var colors:Array = [0xFF0000, 0x00FF00, 0x0000ff];
-
var alphas:Array = [1, 1, 1];
-
var ratios:Array = [0, 128, 255];
-
var spreadMethod:String = SpreadMethod.PAD;
-
var matrix:Matrix = new Matrix();
-
matrix.createGradientBox(area_width, area_height, 1, 0, 0);
-
//
-
with (event_spr.graphics) {
-
beginGradientFill (fillType,colors,alphas,ratios,matrix,spreadMethod);
-
drawRect (0,0,area_width, area_height);
-
endFill ();
-
}
-
// paint event
-
event_spr.addEventListener (MouseEvent.MOUSE_DOWN, _onMouseDown);
-
event_spr.addEventListener (MouseEvent.MOUSE_MOVE, _onMouseMove);
-
event_spr.addEventListener (MouseEvent.MOUSE_UP, _onMouseUp);
-
event_spr.addEventListener (MouseEvent.MOUSE_OUT, _onMouseUp);
-
//
-
var bmpd:BitmapData = new BitmapData(event_spr.width,event_spr.height,true,0);
-
var bmp:Bitmap = new Bitmap(bmpd);
-
addChild (bmp);
-
// shape temporanea
-
var draw_shape:Shape = new Shape();
-
addChild (draw_shape);
-
//
-
function _onMouseDown (e:MouseEvent):void {
-
debug ("_onMouseDown");
-
var c:uint = 0xffffff;
-
draw_shape.graphics.lineStyle (10,c,1);
-
draw_shape.graphics.moveTo (e.localX,e.localY);
-
md = true;
-
}
-
//
-
function _onMouseUp (e:MouseEvent):void {
-
md = false;
-
bmp.bitmapData.draw (draw_shape);
-
draw_shape.graphics.clear ();
-
}
-
//
-
function _onMouseMove (e:MouseEvent):void {
-
debug ("_onMouseMove");
-
if (md && !e.ctrlKey) {
-
draw_shape.graphics.lineTo (e.localX,e.localY);
-
} else if (md && e.ctrlKey) {
-
bmp.bitmapData.fillRect ( new Rectangle(e.target.mouseX-10,e.target.mouseY-10,20,20), 0);
-
}
-
}
-
//
-
function debug (v:String):void {
-
var d:Date = new Date();
-
trace (d.getMinutes()+":"+d.getSeconds()+":"+d.getMilliseconds()+": "+v);
-
}
La riga 61, come mostrato nel codice qui sopra, viene esguita quando il tasto Ctrl è premuto. Come si vede questa sezione di codice disegna direttamente nella BitmapData con un color 0, ovvero 0x0000!
METODO 2 - Uso dei metodi di fusione
Questo, probabilmente, è il metodo migliore in quando permette di usare qualsiasi tipo di "cancellino". A differenza del metodo precedente non si accede direttamente ai dati bitmap presenti in BitmapData, ma si sfruttano i metodi di fusione (blendMode) e la copia di Bitmap tramite il metodo draw().
Versione 3D - sorgente:
Esattamente nello stesso modo con cui copiamo i dati dalla Shape alla Bitmap, il "cancellino" è "simultato" copiando i dati dalla Shape alla Bitmap ma, questa volta, impostando il blendMode del metodo draw() a erase.
Il codice è il seguente - sorgente:
-
import flash.events.MouseEvent;
-
import flash.display.BitmapData;
-
import flash.display.Bitmap;
-
import flash.display.GradientType;
-
//
-
import flash.geom.Matrix;
-
//
-
var md:Boolean = false;
-
//
-
var event_spr:Sprite = new Sprite();
-
addChild (event_spr);
-
//
-
var area_width:Number = event_spr.stage.stageWidth;
-
var area_height:Number = event_spr.stage.stageHeight-32;
-
//
-
var fillType:String = GradientType.LINEAR;
-
var colors:Array = [0xFF0000, 0x00FF00, 0x0000ff];
-
var alphas:Array = [1, 1, 1];
-
var ratios:Array = [0, 128, 255];
-
var spreadMethod:String = SpreadMethod.PAD;
-
var matrix:Matrix = new Matrix();
-
matrix.createGradientBox (area_width, area_height, 1, 0, 0);
-
//
-
with (event_spr.graphics) {
-
beginGradientFill (fillType,colors,alphas,ratios,matrix,spreadMethod);
-
drawRect (0,0,area_width, area_height);
-
endFill ();
-
}
-
// paint event
-
event_spr.addEventListener (MouseEvent.MOUSE_DOWN, _onMouseDown);
-
event_spr.addEventListener (MouseEvent.MOUSE_MOVE, _onMouseMove);
-
event_spr.addEventListener (MouseEvent.MOUSE_UP, _onMouseUp);
-
event_spr.addEventListener (MouseEvent.MOUSE_OUT, _onMouseUp);
-
//
-
var bmpd:BitmapData = new BitmapData(event_spr.width,event_spr.height,true,0);
-
var bmp:Bitmap = new Bitmap(bmpd);
-
addChild (bmp);
-
// shape temporanea
-
var draw_shape:Shape = new Shape();
-
addChild (draw_shape);
-
//
-
function _onMouseDown (e:MouseEvent):void {
-
debug ("_onMouseDown");
-
var c:uint = (!e.ctrlKey?0xffffff:0x000000);
-
draw_shape.graphics.lineStyle (10, c, 1);
-
draw_shape.graphics.moveTo (e.localX,e.localY);
-
md = true;
-
}
-
//
-
function _onMouseUp (e:MouseEvent):void {
-
md = false;
-
bmp.bitmapData.draw (draw_shape,null,null,(e.ctrlKey?"erase":"normal"));
-
draw_shape.graphics.clear ();
-
}
-
//
-
function _onMouseMove (e:MouseEvent):void {
-
debug ("_onMouseMove");
-
if (md) {
-
draw_shape.graphics.lineTo (e.localX,e.localY);
-
}
-
}
-
//
-
function debug (v:String):void {
-
var d:Date = new Date();
-
trace (d.getMinutes()+":"+d.getSeconds()+":"+d.getMilliseconds()+": "+v);
-
}
Questo codice non è molto dissimile dal precedente. La verà differenza è nella riga 52! Questa determina la "modalità di fusione" quando si copia dalla Shape alla Bitmap; quando il tasto Ctrl è premuto viene usato il metodo di fusione erase.
Con questo è tutto! Torneremo a parlare delle Bitmap quanto prima e delle differenze tra MovieClip, Sprite, Shape e Bitmap... tutti oggetti visuali!



















Undolog.com » Blog Archive » Come salvare immagini in Flash CS3 ha detto:
[...] Come salvare immagini in Flash CS3 Tags: ActionScript 3.0, Bitmap, BitmapData, Flash CS3, GD, GD Library, Internet, PHP, Save Flash Image, Sviluppo, TutorialsCon Flash CS3 l’uso delle Bitmap è così migliorato che viene subito voglia di creare un piccolo Paint. Abbiamo già visto come realizzare un piccolo Paint (vedi Creare un’applicazione Paint in Flash CS3 e Painter: semplice applicazione per disegno in Flash CS3 Pro) in grado di supportare un vero e proprio “cancellino” - funzione erase, grazie ad un uso particolare di layer Shape e Bitmap. Diciamo subito che Flash (a differenza di Flex) non permette ne encoding (tipo JPG o PNG) ne salvataggio automatico di immagini Bitmap. Tuttavia si può aggirare l’ostacolo sfruttando uno scripting lato server e la capacità di Flash di inviare dati in POST. [...]
Guida ActionScript 3 in Flash CS3: gli eventi del mouse | Marcello Surdi ha detto:
[...] Creare un’applicazione Paint in Flash CS3 [...]
Gianni ha detto:
Ottima soluzione, adatta per innnumerevoli soluzioni relative anche alle web application grafiche.
Giovambattista Fazioli ha detto:
@Gianni: grazie! Vedi anche Painter: semplice applicazione per disegno in Flash CS3 Pro