1  /**
     2   * com.sekati.display.BitmapImageSliceClip
     3   * @version 1.0.1
     4   * @author jason m horwitz | sekati.com | tendercreative.com
     5   * Copyright (C) 2007  jason m horwitz, Sekat LLC. All Rights Reserved.
     6   * Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
     7   */
     8  
     9   import com.sekati.core.App;
    10   import com.sekati.display.IBitmapImageClip;
    11   import com.sekati.display.BaseClip;
    12   import com.sekati.utils.Delegate;
    13   import com.sekati.utils.MovieClipUtils;
    14   import com.sekati.except.FatalException;
    15   import flash.display.BitmapData;
    16   import flash.geom.ColorTransform;
    17   import flash.geom.Matrix;
    18   import flash.geom.Rectangle;
    19  
    20  /**
    21   * BitmapImageSliceClip - load a large image, convert it to a set of smoothed BitmapData objects available 
    22   * for manipulation.This works around the BitmapDataObject 2880 pixel width or height limitation.
    23   * NOTE: The clip image is automatically hidden (_alpha:0) until manually tweened in by developer.
    24   * 
    25   * {@code Usage:
    26   * 	import com.sekati.utils.*
    27   * 	import caurina.transitions.Tweener;
    28   * 	
    29   * 	function _onProgress(l:Number, t:Number):Void { trace("img_onProgress: "+l+" / "+t); }
    30   * 	function _onInit():Void { Tweener.addTween(image, {_alpha:100, time:0.3, transition:"easeInOutQuint"}); }
    31   * 	function _onError(code:String):Void { trace("oh noes! The image couldnt be loaded: "+ecode); }
    32   * 	
    33   * 	// create an empty BitmapImageClip
    34   * 	var image:MovieClip =  ClassUtils.createEmptyMovieClip (com.sekati.display.BitmapImageSliceClip, this, "image", {_x:0, _y:0});
    35   * 	
    36   * 	// load an image and define onProgress onInit Delegates.
    37   * 	image.load("test.jpg", Delegate.create(this, _onInit), Delegate.create(this, _onProgress), Delegate.create(this, _onError));
    38   * }
    39   */
    40  class com.sekati.display.BitmapImageSliceClip extends BaseClip implements IBitmapImageClip {
    41  	
    42  	public var img:MovieClip;
    43  	public var bmp:Array;
    44  	private var _uri:String;
    45  	private var _smooth:Boolean;
    46  	private var _progressCb:Function;
    47  	private var _initCb:Function;
    48  	private var _errorCb:Function;
    49  	private var _loader:MovieClipLoader;
    50  	private var _tmp:MovieClip;
    51  	
    52  	/**
    53  	 * Constructor
    54  	 */
    55  	public function BitmapImageSliceClip() {
    56  		super();
    57  		_loader = new MovieClipLoader();
    58  		_this.onLoadInit = Delegate.create(_this, _onInit);
    59  		_this.onLoadError = Delegate.create(_this, _onError);
    60  		_this.onLoadProgress = Delegate.create(_this, _onProgress);
    61  		_loader.addListener(_this);
    62  	}
    63  	
    64  	/**
    65  	 * Load image and convert to bitmap
    66  	 * @param uri (String)
    67  	 * @param isSmoothed (Boolean) - enable smoothing on the bitmap.
    68  	 * @param onInit (Function) - cb when image is loaded, converted, ready for use.
    69  	 * @param onProgress (Function) - cb is passed args: bytesLoaded, bytesTotal.
    70  	 * @param onError (Function) - cb is passed arg errorCode, httpStatus.
    71  	 * @return Void
    72  	 */
    73  	public function load(uri:String, isSmoothed:Boolean, onInit:Function, onProgress:Function, onError:Function):Void {
    74  		_uri = uri;
    75  		_smooth = isSmoothed;
    76  		_progressCb = onProgress;
    77  		_initCb = onInit;
    78  		_errorCb = onError;
    79  		createContainers();
    80  		_loader.loadClip(_uri, _tmp);	
    81  	}
    82  	
    83  	/**
    84  	 * Remove the bitmap and clips.
    85  	 * @return Void
    86  	 */
    87  	public function unload():Void {
    88  		_loader.unloadClip(img);
    89  		bmp.dispose();
    90  		MovieClipUtils.rmClip(_tmp); 
    91  		MovieClipUtils.rmClip(img);
    92  	}
    93  
    94  	/**
    95  	 * Create the temporary image Container
    96  	 * @return Void
    97  	 */
    98  	private function createContainers():Void {
    99  		if (bmp.length > 0) {
   100  			for (var i:Number = 0; i < bmp.length; i++) bmp[i].dispose();
   101  		}
   102  		if (_tmp) MovieClipUtils.rmClip(_tmp); 
   103  		if (img) MovieClipUtils.rmClip(img);
   104  		img = _this.createEmptyMovieClip("img", _this.getNextHighestDepth());
   105  		_tmp = _this.createEmptyMovieClip("_tmp", _this.getNextHighestDepth());
   106  		bmp = new Array();
   107  		//img.cacheAsBitmap = true;
   108  		_tmp._visible = false;
   109  		_this._alpha = 0;
   110  	}
   111  	
   112  	/**
   113  	 * Clear existing callbacks in preparation for another load
   114  	 * @return Void
   115  	 */
   116  	private function clearCallbacks():Void {
   117  		_initCb = undefined;
   118  		_progressCb = undefined;
   119  		_errorCb = undefined;		
   120  	}
   121  	
   122  	/**
   123  	 * Once external image is loaded, convert to smoothed bitmapData object
   124  	 * and run the {@link _initCb} if it was defined during {@link load}.
   125  	 * @return Void
   126  	 */
   127  	private function _onInit():Void {
   128  		var sw:Number = _tmp._width;
   129  		var sh:Number = _tmp._height;
   130  		var sliceWidth:Number = 2000;
   131  		var slices:Number = Math.ceil( sw / sliceWidth );
   132  		var rect:Rectangle = new Rectangle( 0, 0, sliceWidth, sh );
   133  		var ct:ColorTransform = new ColorTransform();
   134  		
   135  		App.log.info("foo", "### Source Image ("+sw+"x"+sh+") results in: "+slices+" slices @ "+sliceWidth+" px/slice (including overflow)");
   136  		
   137  		// draw,attach,place each column
   138  		for (var i:Number = 0; i < slices; i++) {
   139  			var xslice:Number = sliceWidth * i;
   140  			var diff:Number = 0;
   141  			if (i == (slices - 1) && (xslice + sliceWidth) > _tmp._width) {
   142  				App.log.warn(_this, "!!! BITMAP:IMAGE -> BUFFER OVERFLOW !!!");
   143  				diff = sliceWidth-(_tmp._width - xslice);
   144  				rect = new Rectangle(0, 0, sliceWidth-diff, sh);
   145  				App.log.info(_this, "Adjusting for overflow: New sliceWidth: "+(sliceWidth-diff));
   146  			}
   147  			var b:BitmapData = new BitmapData( (sliceWidth-diff), sh, true, 0x00FFFFFF );	
   148  			var m:Matrix = new Matrix();
   149  			m.translate( -i*sliceWidth, 0 );
   150  			b.draw( _tmp, m, ct, "normal", rect, true );	
   151  			var container:MovieClip = img.createEmptyMovieClip( "bmp"+i, img.getNextHighestDepth() );
   152  			container.attachBitmap( b, container.getNextHighestDepth(), "auto", _smooth );
   153  			container._x = xslice;
   154  			bmp.push(b);
   155  			App.log.info(_this, "Attaching bitmapData slice: "+i+" ("+container+"._x:"+container._x+") slice section: "+xslice+" - " +(xslice + sliceWidth - diff));	
   156  		}
   157  		
   158  		App.log.status(_this, ":Bitmap:Image slice assembly completed successfully: "+img._width+"x"+img._height);
   159  		
   160  		MovieClipUtils.rmClip(_tmp);
   161  		_initCb();
   162  		clearCallbacks();
   163  	}
   164  
   165  	/**
   166  	 * Call the onProgress cb 
   167  	 * @param target (MovieClip)
   168  	 * @param bytesLoaded (Number)
   169  	 * @param bytesTotal (Number)
   170  	 * @return Void
   171  	 */
   172  	private function _onProgress(target:MovieClip, bytesLoaded:Number, bytesTotal:Number):Void {
   173  		//trace("loading: "+target + " " + bytesLoaded + "/" + bytesTotal);
   174  		_progressCb(bytesLoaded, bytesTotal);
   175  	}
   176  
   177  	/**
   178  	 * Call the onError cb
   179  	 * @param errorCode (String)
   180  	 * @param httpStatus (String)
   181  	 */
   182  	private function _onError(target_mc:MovieClip, errorCode:String, httpStatus:Number):Void {
   183  		_errorCb(errorCode, httpStatus);
   184  		clearCallbacks();
   185  		throw new FatalException(_this, "Could not load image from url:"+_uri, arguments);
   186  	}
   187  }
   188