root/as3/gunyarapaint/branches/gunyarapaint/framework/src/org/libspark/gunyarapaint/framework/LayerBitmapCollection.as

リビジョン 3914, 19.0 kB (コミッタ: hkrn, コミット時期: 3 年 前)

added the limit of pixels to LayerBitmapCollection?.save and more tests

Line 
1 package org.libspark.gunyarapaint.framework
2 {
3     import flash.display.Bitmap;
4     import flash.display.BitmapData;
5     import flash.display.BlendMode;
6     import flash.display.DisplayObject;
7     import flash.display.Sprite;
8     import flash.events.Event;
9     import flash.events.IEventDispatcher;
10     import flash.geom.Point;
11     import flash.geom.Rectangle;
12    
13     import org.libspark.gunyarapaint.framework.errors.AddLayerError;
14     import org.libspark.gunyarapaint.framework.errors.MergeLayersError;
15     import org.libspark.gunyarapaint.framework.errors.RemoveLayerError;
16     import org.libspark.gunyarapaint.framework.errors.TooManyLayersError;
17     import org.libspark.gunyarapaint.framework.i18n.TranslatorRegistry;
18    
19     /**
20      * 複数のレイヤーを管理する
21      *
22      */
23     public class LayerBitmapCollection implements IEventDispatcher
24     {
25         /**
26          * 作成出来る最大レイヤー数
27          */
28         public static const MAX:uint = 8;
29        
30         /**
31          * 連結したレイヤー画像で受け付ける最大ピクセル数
32          */
33         public static const MAX_PIXEL:uint = 2880;
34        
35         public function LayerBitmapCollection(width:int, height:int)
36         {
37             currentIndex = 0;
38             doCompositeAll = true;
39             m_width = width;
40             m_height = height;
41             m_layers = new Vector.<LayerBitmap>();
42             m_sprite = new Sprite();
43             m_drawingSprite = new Sprite();
44             m_drawingSprite.mouseEnabled = false;
45             // 白い背景を作成してレイヤー群に追加する
46             var layer:LayerBitmap = new LayerBitmap(
47                 new BitmapData(width, height, true, uint.MAX_VALUE)
48             );
49             layer.name = TranslatorRegistry.tr("Background");
50             composited = new BitmapData(width, height, true, 0x0);
51             m_layers.push(layer);
52             m_sprite.addChild(layer.displayObject);
53         }
54        
55         /**
56          * レイヤーを作成して追加する
57          *
58          * 完全に透明なレイヤーが作成される。また、現在の番号が一つ上にずれる。
59          *
60          */
61         public function add():void
62         {
63             if (m_layers.length >= MAX)
64                 throw new AddLayerError(MAX);
65             var layer:LayerBitmap = new LayerBitmap(
66                 new BitmapData(m_width, m_height, true, 0x0)
67             );
68             currentIndex++;
69             layer.name = TranslatorRegistry.tr("Layer") + currentIndex;
70             m_layers.splice(currentIndex, 0, layer);
71             m_sprite.addChildAt(layer.displayObject, currentIndex);
72             compositeAll();
73             resetLayersIndex();
74         }
75        
76         /**
77          * 指定された番号のレイヤーを取り出す
78          *
79          * @param index レイヤー番号
80          * @return
81          */
82         public function at(index:int):LayerBitmap
83         {
84             return m_layers[index];
85         }
86        
87         /**
88          * レイヤーをコピーして現在の番号の次に追加する
89          *
90          */
91         public function copy():void
92         {
93             copyAt(currentIndex);
94         }
95        
96         /**
97          * レイヤーをコピーして指定された番号の次に作成する
98          *
99          * コピー元の画像が複製されて追加される。また、現在の番号が一つ上にずれる。
100          *
101          * @param index レイヤー番号
102          */
103         public function copyAt(index:int):void
104         {
105             if (m_layers.length >= MAX)
106                 throw new AddLayerError(MAX);
107             var selected:LayerBitmap = m_layers[index];
108             var layer:LayerBitmap = selected.clone();
109             layer.name = TranslatorRegistry.tr("%s's copy", selected.name);
110             m_layers.splice(index, 0, layer);
111             m_sprite.addChildAt(layer.displayObject, index);
112             compositeAll();
113             resetLayersIndex();
114         }
115        
116         /**
117          * レイヤーを指定された番号と交換する
118          *
119          * @param from 入れ替え元のレイヤー番号
120          * @param to 入れ替え先のレイヤー番号
121          */
122         public function swap(from:int, to:int):void
123         {
124             var layer:LayerBitmap = m_layers[from];
125             m_layers[from] = m_layers[to];
126             m_layers[to] = layer;
127             m_sprite.swapChildrenAt(from, to);
128             compositeAll();
129             resetLayersIndex();
130         }
131        
132         /**
133          * 現在のレイヤーを下のレイヤーと合成する
134          *
135          * @throws MergeLayersError レイヤーが一つ、あるいは対象のうち片方が不可視の場合
136          */
137         public function merge():void
138         {
139             mergeAt(currentIndex);
140         }
141        
142         /**
143          * 指定された番号のレイヤーを下のレイヤーと合成する
144          *
145          * <p>
146          * 両方のレイヤーは可視 (visible=true) である必要がある。
147          * また、合成後は現在のレイヤーは下のレイヤーに変更され、完全に不透明になる。
148          * </p>
149          *
150          * @param index レイヤー番号
151          * @throws MergeLayersError レイヤーが一つ、あるいは対象のうち片方が不可視の場合
152          */
153         public function mergeAt(index:int):void
154         {
155             // レイヤーは必ず2つ以上
156             if (currentIndex > 0) {
157                 var current:LayerBitmap = m_layers[index];
158                 var prev:LayerBitmap = m_layers[index - 1];
159                 // 両方可視である必要がある
160                 if (current.visible && prev.visible) {
161                     current.compositeTo(prev.bitmapData);
162                     // 合成後の LayerBitmap は完全に不透明にしておく
163                     prev.alpha = 1.0;
164                     m_layers.splice(index, 1);
165                     m_sprite.removeChildAt(index);
166                     if (index >= currentIndex)
167                         currentIndex -= 1;
168                     compositeAll();
169                     resetLayersIndex();
170                     return;
171                 }
172             }
173             throw new MergeLayersError();
174         }
175        
176         /**
177          * 現在のレイヤーを削除する
178          *
179          * @throws RemoveLayerError レイヤーが一つの場合
180          */
181         public function remove():void
182         {
183             removeAt(currentIndex);
184         }
185        
186         /**
187          * 指定された番号のレイヤーを削除する
188          *
189          * <p>
190          * レイヤーが削除されると 現在の番号が一つ下にずれる。
191          * </p>
192          *
193          * @param index 現在のレイヤー番号
194          * @throws RemoveLayerError レイヤーが一つの場合
195          */
196         public function removeAt(index:int):void
197         {
198             if (m_layers.length <= 1)
199                 throw new RemoveLayerError();
200             m_layers.splice(index, 1);
201             m_sprite.removeChildAt(index);
202             if (currentIndex > 0 && index >= currentIndex)
203                 currentIndex -= 1;
204             compositeAll();
205             resetLayersIndex();
206         }
207        
208         /**
209          * 表示オブジェクトから現在のビューを削除する
210          *
211          * @param parent 親となる表示オブジェクト
212          */
213         public function removeView(parent:Sprite):void
214         {
215             parent.removeChild(m_sprite);
216         }
217        
218         /**
219          * 現在のビューを表示オブジェクトに設定する
220          *
221          * @param parent 親となる表示オブジェクト
222          */
223         public function setView(parent:Sprite):void
224         {
225             parent.addChild(m_sprite);
226         }
227        
228         /**
229          * 現在の全てのレイヤー情報を dataProvider に適用出来る形で返す
230          *
231          * @return Array dataProvider に適用出来る配列
232          */
233         public function toDataProvider():Array
234         {
235             var ret:Array = [];
236             var count:uint = m_layers.length;
237             for (var i:uint = 0; i < count; i++) {
238                 ret.push(m_layers[i]);
239             }
240             return ret.reverse();
241         }
242        
243         /**
244          * 連結されたレイヤー画像とメタデータから復元する
245          *
246          * @param layerBitmaps 縦に連結されたレイヤー画像
247          * @param metadata メタデータ
248          */
249         public function load(layerBitmap:BitmapData, metadata:Object):void
250         {
251             var width:uint = metadata.width;
252             var height:uint = metadata.height;
253             var layersInfo:Array = metadata.layer_infos;
254             var layerCount:uint = layerBitmap.height / height;
255             var destination:Point = new Point(0, 0);
256             var rectangle:Rectangle = new Rectangle(0, 0, width, height);
257             clear();
258             for (var i:uint = 0; i < layerCount; i++) {
259                 var bitmapData:BitmapData = new BitmapData(width, height);
260                 rectangle.y = i * height;
261                 bitmapData.copyPixels(layerBitmap, rectangle, destination);
262                 var layer:LayerBitmap = new LayerBitmap(bitmapData);
263                 layer.fromJSON(layersInfo[i]);
264                 m_layers.push(layer);
265                 m_sprite.addChild(layer.displayObject);
266             }
267         }
268        
269         /**
270          * 連結されたレイヤー画像とメタデータを保存する
271          *
272          * @param layerBitmaps 縦に連結されたレイヤー画像
273          * @param metadata メタデータ
274          */
275         public function save(layerBitmap:BitmapData, metadata:Object):void
276         {
277             if (layerBitmap.height > MAX_PIXEL) {
278                 var count:uint = Math.min(Math.floor((1.0 * MAX_PIXEL) / height), MAX);
279                 throw new TooManyLayersError(count);
280             }
281             var layersInfo:Array = [];
282             var layerCount:uint = layerBitmap.height / height;
283             var rectangle:Rectangle = new Rectangle(0, 0, width, height);
284             var destination:Point = new Point(0, 0);
285             layerBitmap.lock();
286             for (var i:uint = 0; i < layerCount; i++) {
287                 var layer:LayerBitmap = m_layers[i];
288                 destination.y = i * height;
289                 layerBitmap.copyPixels(layer.bitmapData, rectangle, destination);
290                 layersInfo.push(layer.toJSON());
291             }
292             layerBitmap.unlock();
293             metadata.width = width;
294             metadata.height = height;
295             metadata.layer_infos = layersInfo;
296         }
297        
298         public function addEventListener(type:String,
299                                          listener:Function,
300                                          useCapture:Boolean = false,
301                                          priority:int = 0,
302                                          useWeakReference:Boolean = false):void
303         {
304             m_sprite.addEventListener(type, listener, useCapture, priority, useWeakReference);
305         }
306        
307         public function removeEventListener(type:String,
308                                             listener:Function,
309                                             useCapture:Boolean = false):void
310         {
311             m_sprite.removeEventListener(type, listener, useCapture);
312         }
313        
314         public function dispatchEvent(event:Event):Boolean
315         {
316             return m_sprite.dispatchEvent(event);
317         }
318        
319         public function hasEventListener(type:String):Boolean
320         {
321             return m_sprite.hasEventListener(type);
322         }
323        
324         public function willTrigger(type:String):Boolean
325         {
326             return m_sprite.willTrigger(type);
327         }
328        
329         /**
330          * 全てのレイヤーを削除する
331          *
332          */
333         internal function clear():void
334         {
335             var count:uint = m_layers.length;
336             for (var i:uint = 0; i < count; i++) {
337                 m_sprite.removeChildAt(i);
338             }
339             m_layers.splice(0, count);
340         }
341        
342         /**
343          * 全てのレイヤーの画像を合成する
344          *
345          */
346         internal function compositeAll():void
347         {
348             if (doCompositeAll) {
349                 var c:uint = count;
350                 // 操作毎にBitmapDataが生成されるため、逆にGarbageCollectionを発生させやすくしている。
351                 // そのため、これがないとBitmapDataによるメモリリークが余計にひどくなる。
352                 composited = new BitmapData(m_width, m_height, true, 0x0);
353                 for (var i:uint = 0; i < c; i++) {
354                     var layer:LayerBitmap = m_layers[i];
355                     layer.compositeTo(composited);
356                 }
357             }
358         }
359        
360         /**
361          * saveState で保存したオブジェクトを復元する
362          *
363          */
364         internal function loadState(undoData:Object):void
365         {
366             var i:uint = 0;
367             var layers:Vector.<Object> = undoData.layers;
368             var oldLayerCount:uint = m_layers.length;
369             var newLayerCount:uint = layers.length;
370             // レイヤー切り替えが発生しないケースの対処
371             // 古いレイヤーが一緒に消えないようにする必要がある
372             var c:uint = newLayerCount > oldLayerCount ? oldLayerCount : newLayerCount;
373             for (i = 0; i < c; i++) {
374                 var data:Object = layers[i];
375                 var newLayer:LayerBitmap = new LayerBitmap(data.bitmapData);
376                 newLayer.fromJSON(data);
377                 m_sprite.removeChildAt(i);
378                 m_sprite.addChildAt(newLayer.displayObject, i);
379                 m_layers[i] = newLayer;
380             }
381             currentIndex = undoData.index;
382             compositeAll();
383         }
384        
385         /**
386          * 画像データを含むすべてのレイヤー情報を保存する
387          *
388          */
389         internal function saveState(undoData:Object):void
390         {
391             var c:uint = count;
392             var layers:Vector.<Object> = new Vector.<Object>(c, true);
393             for (var i:uint = 0; i < c; i++) {
394                 var layer:LayerBitmap = m_layers[i];
395                 var data:Object = layer.toJSON();
396                 data.bitmapData = layer.bitmapData;
397                 layers[i] = data;
398             }
399             undoData.index = currentIndex;
400             undoData.layers = layers;
401         }
402        
403         internal function startDrawing(engine:PaintEngine):void
404         {
405             if (m_tempLayer == null) {
406                 var layer:LayerBitmap = currentLayer;
407                 var blendMode:String = layer.blendMode;
408                 engine.resetPen();
409                 m_drawingSprite.blendMode =
410                     blendMode == BlendMode.NORMAL ? BlendMode.LAYER : blendMode;
411                 m_drawingSprite.alpha = layer.alpha;
412                 m_tempLayer = currentLayer.newDisplayObject;
413                 m_tempLayer.blendMode = BlendMode.NORMAL;
414                 m_tempLayer.alpha = 1.0;
415                 m_drawingSprite.addChild(m_tempLayer);
416                 m_drawingSprite.addChild(engine.shape);
417                 m_sprite.removeChild(layer.displayObject);
418                 m_sprite.addChildAt(m_drawingSprite, currentIndex);
419             }
420         }
421        
422         internal function stopDrawing(engine:PaintEngine):void
423         {
424             if (m_tempLayer != null) {
425                 var layer:LayerBitmap = currentLayer;
426                 var blendMode:String = m_drawingSprite.blendMode;
427                 layer.blendMode =
428                     blendMode == BlendMode.LAYER ? BlendMode.NORMAL : blendMode;
429                 layer.alpha = m_drawingSprite.alpha;
430                 m_sprite.removeChild(m_drawingSprite);
431                 m_sprite.addChildAt(layer.displayObject, currentIndex);
432                 engine.clear();
433                 m_drawingSprite.removeChild(m_tempLayer);
434                 m_drawingSprite.removeChild(engine.shape);
435                 m_tempLayer = null;
436             }
437         }
438        
439         /**
440          * 現在のレイヤー画像を設定する
441          *
442          */
443         internal function setCurrentLayer(value:LayerBitmap):void
444         {
445             m_layers[currentIndex] = value;
446         }
447        
448         // LayerBitmap の name で正しい番号で作成されるように調整する
449         private function resetLayersIndex():void
450         {
451             var c:uint = count;
452             for (var i:uint = 0; i < c; i++) {
453                 var layer:LayerBitmap = m_layers[i];
454                 layer.index = i;
455             }
456         }
457        
458         /**
459          * 現在のレイヤー画像の幅を返す
460          *
461          */
462         public function get width():uint
463         {
464             return m_width;
465         }
466        
467         /**
468          * 現在のレイヤー画像の高さを返す
469          *
470          */
471         public function get height():uint
472         {
473             return m_height;
474         }
475        
476         /**
477          * 現在のレイヤー画像を返す
478          *
479          */
480         public function get currentLayer():LayerBitmap
481         {
482             return m_layers[currentIndex];
483         }
484        
485         /**
486          * 現在のレイヤー数を返す
487          *
488          */
489         public function get count():uint
490         {
491             return m_layers.length;
492         }
493        
494         /**
495          * 全てのレイヤーを結合した結果の BitmapData のコピーを返す
496          *
497          */
498         public function get compositedBitmapData():BitmapData
499         {
500             return composited.clone();
501         }
502        
503         /**
504          * レイヤーを保存するために必要な BitmapData を生成する
505          *
506          */
507         public function get newLayerBitmapData():BitmapData
508         {
509             return new BitmapData(width, height * count, true, 0x0);
510         }
511        
512         /**
513          * スプライトオブジェクトを返す
514          *
515          */
516         internal function get view():Sprite
517         {
518             return m_sprite;
519         }
520        
521         /**
522          * 現在のレイヤー番号
523          *
524          * @default 0
525          */
526         public var currentIndex:uint;
527        
528         /**
529          * 全てのレイヤーを合成するかどうか
530          *
531          * @default false
532          */
533         public var doCompositeAll:Boolean;
534        
535         /**
536          * キャンバス用のスプライトオブジェクト
537          *
538          * @default null
539          */
540         private var m_sprite:Sprite;
541        
542         /**
543          * 全てのレイヤーが合成された結果の画像データ
544          *
545          * @default null
546          */
547         internal var composited:BitmapData;
548        
549         private var m_layers:Vector.<LayerBitmap>;
550         private var m_drawingSprite:Sprite;
551         private var m_tempLayer:DisplayObject;
552         private var m_width:uint;
553         private var m_height:uint;
554     }
555 }
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。