/*----------------------------------------------------------------------------- The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is Fever Framework code. The Initial Developer of the Original Code is Romain Ecarnot. Portions created by Initial Developer are Copyright (C) 2006 the Initial Developer. All Rights Reserved. Contributor(s): Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -----------------------------------------------------------------------------*/ import com.bourre.commands.CommandManagerFPS; import com.bourre.commands.Delegate; import com.bourre.data.collections.Map; import com.bourre.events.BasicEvent; import com.bourre.events.EventBroadcaster; import com.bourre.events.EventType; import com.bourre.log.Logger; import com.bourre.log.LogLevel; import com.bourre.log.PixlibStringifier; import com.bourre.remoting.BasicFaultEvent; import com.bourre.remoting.BasicResultEvent; import com.bourre.remoting.ServiceMethod; import com.bourre.remoting.ServiceProxy; import com.bourre.remoting.ServiceResponder; import flash.display.BitmapData; import flash.filters.ColorMatrixFilter; import flash.geom.Point; /** * Remoting Bitmap saver using AMFPHP. * *
Use Patrick Minault BitmapDataSaver AMFPHP Service to * save a BitmapData object into a PNG image file. * *
Available image depth are : *
Original AS2 implementation by Patrick Minault.
* More informations on http://www.5etdemi.com/
*
*
Remix by Romain Ecarnot to be Pixlib compliant.
* No use of Adobe MX Remoting package.
*
* {@code
* private function _save( ) : Void
* {
* //Just sample to get a BitmapData instance
* var shoot : ScreenShot = new ScreenShot( Fever.stage.root, true );
* shoot.update();
*
* var saver : BitmapSaver = new BitmapSaver( 'http://myServ.com/amf/gateway.php', 'BitmapDataSaver' );
* saver.addEventListener( BitmapSaver.onStartEVENT, this, _onStartExport );
* saver.addEventListener( BitmapSaver.onCompleteEVENT, this, _onComplete );
* saver.addEventListener( BitmapSaver.onErrorEVENT, this, _onError );
*
* saver.save( shoot.getData(), BitmapSaver.DEPTH_24, 'monImage' );
* }
*
* private function _onStartExport( event : BasicEvent ) : Void
* {
* FeverDebug.DEBUG( 'On Start' );
* }
*
* private function _onComplete( event : NumberEvent ) : Void
* {
* FeverDebug.DEBUG( 'On Complete' );
* }
*
* private function _onError( event : BasicFaultEvent ) : Void
* {
* FeverDebug.DEBUG( 'Error : ' + event.getDescription() );
* }
* }
*
* @author Romain Ecarnot
*/
class fever.utils.BitmapSaver
{
//-------------------------------------------------------------------------
// Events definition
//-------------------------------------------------------------------------
/** Event type broadcasted when process starts. */
public static var onStartEVENT : EventType = new EventType( 'onStart' );
/** Event type broadcasted when process is complete. */
public static var onCompleteEVENT : EventType = new EventType( 'onComplete' );
/** Event type broadcasted when error occured. */
public static var onErrorEVENT : EventType = new EventType( 'onError' );
//-------------------------------------------------------------------------
// Private properties
//-------------------------------------------------------------------------
private var _bCancelled : Boolean;
private var _bExported : Boolean;
private var _sBitmapId : String;
private var _sBitmapString : String;
private var _sFileName : String;
private var _nBitmapDepth : Number;
private var _nBlockSize : Number;
private var _nSteps : Number;
private var _nStepIndex : Number;
private var _nStepCount : Number;
private var _nStepLength : Number;
private var _oBitmap : BitmapData;
private var _oService : ServiceProxy;
private var _oEB : EventBroadcaster;
private var _nCommandCount : Number;
private var _dDepthProcessing : Delegate;
private var _dExportProcessing : Delegate;
private var _mDepthMap : Map;
//-------------------------------------------------------------------------
// Public Properties
//-------------------------------------------------------------------------
/** 32 Bits depth mode ( alpha channel support ). */
public static var DEPTH_32 : Number = 32;
/** 24 Bits depth mode. */
public static var DEPTH_24 : Number = 24;
/** 12 Bits depth mode. */
public static var DEPTH_12 : Number =12;
/** Method name for {@code getBitmapId} remoting method. */
public static var GET_BITMAP_ID_METHOD : ServiceMethod = new ServiceMethod( 'getBitmapId' );
/** Method name for {@code saveImagePart} remoting method. */
public static var SAVE_IMAGE_PART : ServiceMethod = new ServiceMethod( 'saveImagePart' );
/** Method name for {@code endSaveImage} remoting method. */
public static var END_SAVE_IMAGE : ServiceMethod = new ServiceMethod( 'endSaveImage' );
/** Defines packet bloc size ( data packet to send ) ( default is 50000 ). */
public function get blockSize() : Number { return _nBlockSize; }
public function set blockSize ( p : Number ) : Void { _nBlockSize = p; }
//-------------------------------------------------------------------------
// Public API
//-------------------------------------------------------------------------
/**
* Constructor.
*
* @param gatewayURL AMFPhp gateway url
* @param serviceName Remoting service name
*/
public function BitmapSaver( gatewayURL : String, serviceName : String )
{
_oEB = new EventBroadcaster( this );
_oService = new ServiceProxy( gatewayURL, serviceName );
_nBlockSize = 50000;
_mDepthMap = new Map();
_mDepthMap.put( DEPTH_12, _prepare12BitsCompression );
_mDepthMap.put( DEPTH_24, _prepare24BitsCompression );
_mDepthMap.put( DEPTH_32, _prepare32BitsCompression );
}
/**
* Saves passed-in {@code bitmap} BitmapData on
* server.
*
* @param bitmap The BitmapData instance to export
* @param depth Image depth
*
Algo from Patrick Minault sources. */ private function _exportOne12( currStep : Number ) : Void { var pix1 : Number; var pix2 : Number; var pix3 : Number; var maxVal : Number = Math.min( ( currStep + 1 ) * _nStepLength, _oBitmap.width * _oBitmap.height ); var buff = ''; var oldBuff = ''; var rle = false; var numRle = 0; var tmpBmpStr : String = ''; var bmpW = _oBitmap.width; var BASE64_CHARS : Array = [ 'A','B','C','D','E','F','G','H', 'I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X', 'Y','Z','a','b','c','d','e','f', 'g','h','i','j','k','l','m','n', 'o','p','q','r','s','t','u','v', 'w','x','y','z','0','1','2','3', '4','5','6','7','8','9','+','/' ]; for( var i : Number = currStep * _nStepLength; i < maxVal; i += 3 ) { pix1 = _oBitmap.getPixel(i % bmpW, Math.floor(i / bmpW)); pix2 = _oBitmap.getPixel( ( i + 1 ) % bmpW, Math.floor( ( i + 1 ) / bmpW ) ); pix3 = _oBitmap.getPixel( ( i + 2 ) % bmpW, Math.floor( ( i + 2 ) / bmpW ) ); buff = BASE64_CHARS[( pix1 >> 14 & 0x3f ) + ( pix1 >> 10 & 0x3 ) ] + BASE64_CHARS[( pix1 >> 4 & 0x3f ) + ( pix1 & 0xf ) ] + BASE64_CHARS[( pix2 >> 14 & 0x3f ) + ( pix2 >> 10 & 0x3 ) ] + BASE64_CHARS[( pix2 >> 4 & 0x3f ) + ( pix2 & 0xf ) ] + BASE64_CHARS[( pix3 >> 14 & 0x3f ) + ( pix3 >> 10 & 0x3 ) ] + BASE64_CHARS[( pix3 >> 4 & 0x3f ) + ( pix3 & 0xf ) ]; if( buff == oldBuff ) { if( rle ) { numRle++; if( numRle == 64 ) { tmpBmpStr += '/#'; numRle = 0; } } else { tmpBmpStr += '#'; rle = true; numRle = 0; } } else { if( rle ) { tmpBmpStr += BASE64_CHARS[ numRle ]; rle = false; } tmpBmpStr += buff; } oldBuff = buff; } if( rle ) { tmpBmpStr += BASE64_CHARS[ numRle ]; rle = false; } _sBitmapString += tmpBmpStr; } /** * Exportation in 24 Bits depth. * *
Algo from Patrick Minault sources. */ private function _exportOne24( currStep : Number ) : Void { var pix1:Number; var pix2:Number; var maxVal:Number = Math.min( ( currStep + 1 ) * _nStepLength, _oBitmap.width * _oBitmap.height ); var buff = ''; var oldBuff = ''; var rle = false; var numRle = 0; var tmpBmpStr:String = ''; var bmpW = _oBitmap.width; var BASE64_CHARS : Array = [ 'A','B','C','D','E','F','G','H', 'I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X', 'Y','Z','a','b','c','d','e','f', 'g','h','i','j','k','l','m','n', 'o','p','q','r','s','t','u','v', 'w','x','y','z','0','1','2','3', '4','5','6','7','8','9','+','/' ]; for( var i : Number = currStep * _nStepLength; i < maxVal; i += 2 ) { pix1 = _oBitmap.getPixel( i % bmpW, Math.floor( i / bmpW ) ); pix2 = _oBitmap.getPixel( ( i + 1) % bmpW, Math.floor( ( i + 1 ) / bmpW ) ); buff = BASE64_CHARS[ pix1 >> 18 ] + BASE64_CHARS[ pix1 >> 12 & 0x3f ] + BASE64_CHARS[ pix1 >> 6 & 0x3f ] + BASE64_CHARS[ pix1 & 0x3f ] + BASE64_CHARS[ pix2 >> 18 ] + BASE64_CHARS[ pix2 >> 12 & 0x3f ] + BASE64_CHARS[ pix2 >> 6 & 0x3f ] + BASE64_CHARS[ pix2 & 0x3f ]; if( buff == oldBuff ) { if( rle ) { numRle++; if(numRle == 64) { tmpBmpStr += '/#'; numRle = 0; } } else { tmpBmpStr += '#'; rle = true; numRle = 0; } } else { if( rle ) { tmpBmpStr += BASE64_CHARS[ numRle ]; rle = false; } tmpBmpStr += buff; } oldBuff = buff; } if( rle ) { tmpBmpStr += BASE64_CHARS[ numRle ]; rle = false; } _sBitmapString += tmpBmpStr; } /** * Exportation in 32 Bits depth. * *
Algo from Patrick Minault sources. */ private function _exportOne32( currStep : Number ) : Void { var pix1 : Number; var pix2 : Number; var pix3 : Number; var maxVal:Number = Math.min( ( currStep + 1 ) * _nStepLength, _oBitmap.width * _oBitmap.height ); var oldBuff = ''; var buff = ''; var rle = false; var numRle = 0; var tmpBmpStr:String = ''; var bmpW = _oBitmap.width; var BASE64_CHARS : Array = [ 'A','B','C','D','E','F','G','H', 'I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X', 'Y','Z','a','b','c','d','e','f', 'g','h','i','j','k','l','m','n', 'o','p','q','r','s','t','u','v', 'w','x','y','z','0','1','2','3', '4','5','6','7','8','9','+','/' ]; for( var i : Number = currStep * _nStepLength; i < maxVal; i += 3 ) { pix1 = _oBitmap.getPixel32( i % bmpW, Math.floor(i / bmpW)); pix2 = _oBitmap.getPixel32( ( i + 1 )% bmpW, Math.floor( ( i + 1 ) / bmpW ) ); pix3 = _oBitmap.getPixel32( ( i + 2 )% bmpW, Math.floor( ( i + 2 ) / bmpW ) ); pix1 = ( ( 255 - ( pix1 >> 24 & 0xff ) ) << 24 ) | ( pix1 & 0xffffff ); pix2 = ( ( 255 - ( pix2 >> 24 & 0xff ) ) << 24 ) | ( pix2 & 0xffffff ); pix3 = ( ( 255 - ( pix3 >> 24 & 0xff ) ) << 24 ) | ( pix3 & 0xffffff ); buff = BASE64_CHARS[pix1 >> 26 & 0x3f] + BASE64_CHARS[ pix1 >> 20 & 0x3f ] + BASE64_CHARS[ pix1 >> 14 & 0x3f ] + BASE64_CHARS[ pix1 >> 8 & 0x3f ] + BASE64_CHARS[ pix1 >> 2 & 0x3f ] + BASE64_CHARS[ ( pix1 & 0x3 )* 16 + ( pix2 >> 28 & 0xf ) ] + BASE64_CHARS[ pix2 >> 22 & 0x3f ] + BASE64_CHARS[ pix2 >> 16 & 0x3f ] + BASE64_CHARS[ pix2 >> 10 & 0x3f ] + BASE64_CHARS[ pix2 >> 4 & 0x3f ] + BASE64_CHARS[ ( pix2 & 0xf ) * 4 + ( pix3 >> 30 & 0x3 ) ] + BASE64_CHARS[ pix3 >> 24 & 0x3f ] + BASE64_CHARS[ pix3 >> 18 & 0x3f ] + BASE64_CHARS[ pix3 >> 12 & 0x3f ] + BASE64_CHARS[ pix3 >> 6 & 0x3f ] + BASE64_CHARS[ pix3 & 0x3f ]; if( buff == oldBuff ) { if(rle) { numRle++; if(numRle == 64) { tmpBmpStr += '/#'; numRle = 0; } } else { tmpBmpStr += '#'; rle = true; numRle = 0; } } else { if(rle) { tmpBmpStr += BASE64_CHARS[numRle]; rle = false; } tmpBmpStr += buff; } oldBuff = buff; } if(rle) { tmpBmpStr += BASE64_CHARS[numRle]; rle = false; } _sBitmapString += tmpBmpStr; } }