/* BitmapDissolve.as Lee Felarca http://www.zeropointnine.com/blog 4-7-2007 v0.8 Source code licensed under a Creative Commons Attribution 3.0 License. http://creativecommons.org/licenses/by/3.0/ Some Rights Reserved. */ package { import flash.display.Sprite; import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; import flash.events.EventDispatcher; import flash.geom.Matrix; import BitmapDissolveEvent; public class BitmapDissolve extends Sprite { // CONSTANTS public static var DIR_LEFT:int = 0; public static var DIR_RIGHT:int = 2; public static var DIR_DOWN:int = 1; public static var DIR_UP:int = 3; public static var DEGREE:Number = Math.PI / 180; public static var DISTRIB_FLAT:int = 0; public static var DISTRIB_CONCAVE:int = 1; public static var DISTRIB_CONVEX:int = 2; // (not yet implemented) // PROPERTY VARIABLES private var _numPixels:int = 300; private var _duration:int = 30; private var _accelRate:Number = 5; private var _colBkg:uint = 0x000000; private var _distribType:int = DISTRIB_FLAT; private var _doubleSpeed:Boolean = false; private var _direction:int = DIR_LEFT; // PRIVATE VARIABLES private var _sprMain:Sprite; private var _bmpOrig:BitmapData; private var _bmpSource:BitmapData; private var _bmpDest:BitmapData; private var _sprSource:Sprite; // (not necessary) private var _imgDest:Bitmap; private var _bitmapHeight:int; private var _bitmapWidth:int; private var _pixel:Array = new Array(); private var _xScannedTo:Array = new Array(); private var _yLowerLimit:int; private var _yUpperLimit:int; private var _accelCounter:Number = 5; private var _active_pixels:int = 1; private var _startedAt:int; private var _frames:int = 0; public function get distribType():int { return _distribType; } public function set distribType(i:int):void { _distribType = i; } public function get doubleSpeed():Boolean { return _doubleSpeed; } public function set doubleSpeed(b:Boolean):void { _doubleSpeed = b; } public function get numPixels():int { return _numPixels; } public function set numPixels(i:int):void { _numPixels = i; } public function get direction():int { return _direction; } public function set direction(i:int):void { _direction = i; } public function get duration():int { return _duration; } public function set duration(i:int):void { _duration = i; } public function BitmapDissolve(pBitmap:BitmapData, pColBkg:uint) { _sprMain = new Sprite(); addChild(_sprMain); _bmpOrig = pBitmap; _bmpSource = pBitmap; _colBkg = pColBkg; } public function run():void { // ----------------------------------------- // USEFUL FOR DEBUGGING PURPOSES: //_sprMain.createEmptyMovieClip("_sprSource", _sprMain.getNextHighestDepth()); //_sprMain._sprSource.attachBitmap(_bmpSource, _sprMain._sprSource.getNextHighestDepth()); //_sprMain._sprSource._y = 200; // ----------------------------------------- if (_direction==DIR_LEFT) // DEFAULT { _bmpSource = new BitmapData(_bmpOrig.width, _bmpOrig.height); _bmpSource.draw(_bmpOrig); } else // rotation shenanigans { // [1] make copy of source bitmap var b:BitmapData = new BitmapData(_bmpOrig.width, _bmpOrig.height); b.draw(_bmpOrig); // [2] make rotation matrix var mRot:Matrix = new Matrix(); mRot.rotate(DEGREE * 90 * _direction); var mTrans:Matrix = new Matrix(); if (_direction==DIR_RIGHT) // rotates bitmap 180 degrees, rotates sprite 180 degrees { // [3] make translation matrix, concatenate, and draw back to _sourceBitmap mTrans.translate(_bmpOrig.width,_bmpOrig.height); mRot.concat(mTrans); _bmpSource.draw( b, mRot); // [4] rotate movieclip 180 degrees and adjust mc position _sprMain.rotation = 180; _sprMain.x += _bmpOrig.width; _sprMain.y += _bmpOrig.height; } else if (_direction==DIR_DOWN) // direction is down ( { // same steps [3] and [4] as above, basically ... mTrans.translate(_bmpOrig.height,0); mRot.concat(mTrans); _bmpSource = new BitmapData(b.height, b.width); _bmpSource.draw( b, mRot); _sprMain.rotation = 270; _sprMain.y += _bmpOrig.height; } else if (_direction==DIR_UP) // direction is up ( { // same steps [3] and [4] as above, basically ... mTrans.translate(0, _bmpOrig.width); mRot.concat(mTrans); _bmpSource = new BitmapData(b.height, b.width); _bmpSource.draw( b, mRot); _sprMain.rotation = 90; _sprMain.x += _bmpOrig.width; } } _bitmapHeight = _bmpSource.height; _bitmapWidth = _bmpSource.width; _bmpDest = new BitmapData(_bmpSource.width, _bmpSource.height); _bmpDest.draw(_bmpSource); _imgDest = new Bitmap(_bmpDest); _sprMain.addChild(_imgDest); for (var i:int=0; i<_numPixels; i++) _pixel[i] = new Object(); for (var j:int=0; j < _bitmapHeight; j++) { _xScannedTo[j] = 0; } _yLowerLimit = 0; _yUpperLimit = _bitmapHeight; addEventListener(Event.ENTER_FRAME, onEnterFrame); _startedAt = new Date().getTime(); } public function stop():void { removeEventListener(Event.ENTER_FRAME, onEnterFrame); } // PRIVATE FUNCTIONS private function onEnterFrame(o:Object):void { _frames++; if (_accelCounter < _numPixels) _accelCounter += _accelRate; _active_pixels = 0; for (var i:int=0; i<_accelCounter; i++) { if (_pixel[i].active) { updatePixel(i); _active_pixels++; } else { findNextPixel(i); } } if (_active_pixels == 0 && _accelCounter >= _numPixels) // done { removeEventListener(Event.ENTER_FRAME, onEnterFrame); var ms:int = (new Date().getTime()) - _startedAt; var e:BitmapDissolveEvent = new BitmapDissolveEvent(BitmapDissolveEvent.DISSOLVED); e.milliseconds = ms; e.frames = _frames; dispatchEvent(e); } // note, _yUpperLimit - _yLowerLimit == 1 when last _pixel is eaten } private function updatePixel(i:int):void { _bmpDest.setPixel( _pixel[i].x, _pixel[i].y, _bmpSource.getPixel(_pixel[i].x, _pixel[i].y) ); // erase last position _pixel[i].x -= _pixel[i].velx; _pixel[i].y -= _pixel[i].vely; _pixel[i].lifeleft--; if (_pixel[i].lifeleft && _pixel[i].x) { _bmpDest.setPixel( _pixel[i].x, _pixel[i].y, _pixel[i].color ); // draw new point } else { _pixel[i].active = false; } } private function findNextPixel(i:int):void { // find a y value to scan thru var y0:Number; var y:int; if (_distribType==DISTRIB_CONCAVE) { y0 = Math.random() * 180; // range 0 to 180 y0 = Math.cos( y0 * DEGREE); // range 0 to 1 trace(y0); y0 *= _bitmapHeight; // range -height to height y0 /= 2; // range -halfheight to halfheight y0 += _bitmapHeight/2; // range 0 to height } else if (_distribType==DISTRIB_CONVEX) { // not yet implemented y0 = Math.random() * _bitmapHeight; } else { y0 = Math.random() * _bitmapHeight; } y = int(y0); var yStartedAt:int = y; // scan upward if y is in top half if (y < _bitmapHeight/2) { while (_xScannedTo[y] >= _bitmapWidth && y >= _yLowerLimit) { y--; } if (_xScannedTo[ int(y-1) ] < _xScannedTo[y]) y = y-1; // (helps even out 'jaggies') if (y <= _yLowerLimit) { if (yStartedAt > _yLowerLimit) { _yLowerLimit = yStartedAt; return; } } } else { // mirror image of above -- scan downards if y is in the bottom half while (_xScannedTo[y] >= _bitmapWidth && y < _yUpperLimit) { y++; } if (_xScannedTo[ int(y+1) ] < _xScannedTo[y]) y = int(y+1); if (y >= _yUpperLimit) { if (yStartedAt < _yUpperLimit) { _yUpperLimit = yStartedAt; return; } } } // scan across for nonblank _pixel var x:int = _xScannedTo[y]; for (x = x; x < _bitmapWidth; int(x++) ) { if (_bmpSource.getPixel(x,y) != _colBkg) { // set new point _pixel[i].active = true; _pixel[i].color = _bmpSource.getPixel(x,y); _pixel[i].x = x; _pixel[i].y = y; _pixel[i].wasx = -1; _pixel[i].wasy = -1; _pixel[i].velx = Math.random()*1 + 1; _pixel[i].vely = Math.random()*0.5 - 0.25; _pixel[i].lifeleft = _duration; _bmpSource.setPixel( x,y, _colBkg ); // 'erase' that point in the source if (_doubleSpeed) { _bmpSource.setPixel( int(x+1),y, _colBkg ); _bmpDest.setPixel( int(x+1), y, _colBkg ); } _xScannedTo[y] = x; x = _bitmapWidth+66; // exits for loop! bad! } } // bad! bad! lol. if (x != _bitmapWidth+66+1) _xScannedTo[y] = _bitmapWidth; } } }