/PImageEdit/src/com/faindu/editImage/EditImage.as
ActionScript | 701 lines | 358 code | 58 blank | 285 comment | 37 complexity | c48932176a474c3062de68057585175f MD5 | raw file
- // Copyright (c) 2007-2008,
- // Trevor McCauley, http://www.senocular.com,
- // Alessandro Crugnola, http://www.sephiroth.it,
- // Marc Speck, http://www.faindu.com
- // All Rights Reserved. The following is Source Code and is subject to all restrictions
- // on such code as contained in the license accompanying this product.
- package com.faindu.editImage
- {
- import com.faindu.editImage.base.BaseControl;
- import com.faindu.editImage.base.ControlsPositions;
- import com.faindu.editImage.base.EditImageEvent;
- import com.faindu.editImage.base.IBaseTool;
- import com.faindu.editImage.base.IEditImageTools;
- import com.faindu.editImage.base.ImageLoader;
- import com.faindu.editImage.crop.CropTool;
- import com.faindu.editImage.crop.ICropTool;
- import com.faindu.editImage.flip.FlipTool;
- import com.faindu.editImage.flip.IFlipTool;
- import com.faindu.editImage.manipulations.IImageManipulations;
- import com.faindu.editImage.manipulations.ImageManipulations;
- import com.faindu.editImage.move.IMoveTool;
- import com.faindu.editImage.move.MoveTool;
- import com.faindu.editImage.outline.OutlineTool;
- import com.faindu.editImage.rotation.IRotateTool;
- import com.faindu.editImage.rotation.RotateTool;
- import com.faindu.editImage.scale.ScaleTool;
-
- import flash.display.Bitmap;
- import flash.display.Sprite;
- import flash.events.Event;
- import flash.events.MouseEvent;
- import flash.geom.Matrix;
- import flash.geom.Point;
- import flash.geom.Rectangle;
-
- import mx.logging.ILogger;
-
- /**
- * Dispatched when the user presses any control of a tool.
- *
- * @eventType com.faindu.editImage.base.EditImageEvent.CONTROL_DOWN
- */
- [Event(name="controlDown", type="com.faindu.editImage.base.EditImageEvent")]
-
- /**
- * Dispatched when the user has pressed a control and moves while still pressing
- * the mouse.
- *
- * @eventType com.faindu.editImage.base.EditImageEvent.CONTROL_MOVE
- */
- [Event(name="controlMove", type="com.faindu.editImage.base.EditImageEvent")]
-
- /**
- * EditImage loads one image and allows the user to manipulate it. There are the
- * following tools available:
- *
- * <p>
- * <ul>
- * <li><b>Crop Tool</b>: Cut out parts of the image.</li>
- * <li><b>Scale Tool</b>: Resize the image.</li>
- * <li><b>Rotate Tool</b>: Rotate the image around its center.</li>
- * <li><b>Move Tool</b>: Move the image to an other position.</li>
- * </ul>
- * </p>
- *
- * <p> While cropping corners, scaling corners or rotating the image, hold down the shiftkey
- * to remove the constrains of the transformation.
- * </p>
- */
- public class EditImage extends ImageLoader implements IEditImage, IEditImageTools
- {
- /**
- * Constructor.
- */
- public function EditImage()
- {
- super()
- addEventListener( EVENT_IMAGE_ADDED , imageAddedHandler )
- }
- private var logger: ILogger
-
- //--------------------------------------------------------------------------
- //
- // Variables
- //
- //--------------------------------------------------------------------------
- /**
- * <code>updateControlsEnabled</code> adds all tools like
- * <code>ScaleTool</code> to <code>toolSprites</code>.
- *
- * @see #updateControlsEnabled()
- * @see com.faindu.editImage.base.BaseTool.updateControlContainer()
- */
- private var toolSprites:Sprite = new Sprite();
-
- //--------------------------------------------------------------------------
- //
- // Getter properties
- //
- //--------------------------------------------------------------------------
- //----------------------------------
- // controlsPositions
- //----------------------------------
- private var _controlsPositions: ControlsPositions = new ControlsPositions()
- /**
- * Stores the possible positions for controls. All points are in the inner coordinate
- * system.
- */
- public function get controlsPositions(): ControlsPositions
- {
- return _controlsPositions
- }
-
- //----------------------------------
- // initialized
- //----------------------------------
- /** @private */
- protected var _toolsInitialized: Boolean
- /**
- * @inheritDoc
- */
- public function get toolsInitialized(): Boolean
- {
- return _toolsInitialized
- }
-
- //----------------------------------
- // transform tools
- //----------------------------------
- // all tools are lazily created and loaded because in quite a few cases, only the image
- // is shown witout any tools.
- /** @private */
- protected var _moveTool: IMoveTool
- /**
- * Tool for moving the image around.
- */
- public function get moveTool(): IMoveTool
- {
- if ( ! _toolsInitialized )
- initTools()
- return _moveTool
- }
-
- /** @private */
- protected var _scaleTool: IBaseTool
- /**
- * Tool for changing the displayed size of the image.
- */
- public function get scaleTool(): IBaseTool
- {
- if ( ! _toolsInitialized )
- initTools()
- return _scaleTool
- }
-
- /** @private */
- protected var _rotateTool: IRotateTool
- /**
- * Tool for rotating the image.
- */
- public function get rotateTool(): IRotateTool
- {
- if ( ! _toolsInitialized )
- initTools()
- return _rotateTool
- }
-
- /** @private */
- protected var _cropTool: ICropTool
- /**
- * Tool for rotating the image.
- */
- public function get cropTool(): ICropTool
- {
- if ( ! _toolsInitialized )
- initTools()
- return _cropTool
- }
-
- /** @private */
- protected var _outlineTool: IBaseTool
- /**
- * @private
- */
- public function get outlineTool(): IBaseTool
- {
- if ( ! _toolsInitialized )
- initTools()
- return _outlineTool
- }
-
- /** @private */
- protected var _flipTool: IFlipTool
- /**
- * @private
- */
- public function get flipTool(): IFlipTool
- {
- if ( ! _toolsInitialized )
- initTools()
- return _flipTool
- }
-
- //--------------------------------------------------------------------------
- //
- // Getter/Setter properties
- //
- //--------------------------------------------------------------------------
- //----------------------------------
- // movingAreaWidth
- //----------------------------------
- private var _movingAreaWidth: Number = 500;
- /**
- * @copy com.faindu.editImage.move.IMoveTool#movingAreaWidth
- *
- * @default 500
- */
- [Bindable]
- public function get movingAreaWidth(): Number
- {
- return _movingAreaWidth
- }
- public function set movingAreaWidth( value:Number ): void
- {
- if ( _movingAreaWidth != value )
- {
- _movingAreaWidth = value
- if ( moveTool )
- {
- moveTool.movingAreaWidth = _movingAreaWidth
- }
- }
- }
-
- //----------------------------------
- // movingAreaHeight
- //----------------------------------
- private var _movingAreaHeight: Number = 500;
- /**
- * @copy com.faindu.editImage.move.IMoveTool#movingAreaWidth
- *
- * @default 500
- */
- [Bindable]
- public function get movingAreaHeight(): Number
- {
- return _movingAreaHeight
- }
- public function set movingAreaHeight( value:Number ): void
- {
- if ( _movingAreaHeight != value )
- {
- _movingAreaHeight = value
- if ( moveTool )
- {
- moveTool.movingAreaHeight = _movingAreaHeight
- }
- }
- }
-
- //----------------------------------
- // snapToTop
- //----------------------------------
- /**
- * If <code>snapToTop</code> or <code>snapToLeft</code> is true,
- * the most outer point is stored in <code>snapPoint</code>.
- */
- private var snapPoint: Point
-
- /** @private */
- protected var _snapToTop: Boolean = true
- /**
- * @inheritDoc
- */
- public function get snapToTop(): Boolean
- {
- return _snapToTop
- }
- public function set snapToTop( value:Boolean ): void
- {
- _snapToTop = value
- }
-
- //----------------------------------
- // snapToLeft
- //----------------------------------
- /** @private */
- protected var _snapToLeft: Boolean = true
- /**
- * @inheritDoc
- */
- public function get snapToLeft(): Boolean
- {
- return _snapToLeft
- }
- public function set snapToLeft( value:Boolean ): void
- {
- _snapToLeft = value
- }
-
- //----------------------------------
- // controlSize
- //----------------------------------
- private var _controlSize:uint = 20;
- /**
- * @inheritDoc
- *
- * @default 20
- * @see com.faindu.editImage.base.BaseControl
- */
- public function get controlSize(): uint
- {
- return _controlSize;
- }
- public function set controlSize( value:uint ): void
- {
- if ( _controlSize != value )
- {
- _controlSize = value
- }
- }
-
- //----------------------------------
- // currentControl
- //----------------------------------
- private var _currentControl: BaseControl
- /**
- * The current control being used if being manipulated.
- * This value is null if the user is not transforming the image.
- */
- public function get currentControl():BaseControl
- {
- return _currentControl
- }
-
- //----------------------------------
- // toolMatrix
- //----------------------------------
- private var _toolMatrix:Matrix = new Matrix()
- /**
- * The transform matrix of the tool as it exists in its own coordinate space.
- * This is not a clone, so be carefull with referencing the matrix.
- */
- public function get toolMatrix(): Matrix
- {
- return _toolMatrix
- }
- public function set toolMatrix( value:Matrix ): void
- {
- _toolMatrix = value
- }
-
- //==========================================================================
- //
- // Init
- //
- //--------------------------------------------------------------------------
- /**
- * Initialize the tools such as scale or crop and the manipulations property.
- * The getters of the tools or <code>manipulations</code> property call <code>init</code>.
- * This means that by default, none of the tools and the manipulations is instaniated.
- */
- protected function initTools():void
- {
- _toolsInitialized = true
-
- _moveTool = new MoveTool()
- _scaleTool = new ScaleTool()
- _cropTool = new CropTool()
- _rotateTool = new RotateTool()
- _outlineTool = new OutlineTool()
- _flipTool = new FlipTool()
- _imageManipulations = new ImageManipulations( this as EditImage )
-
- _moveTool.init( this, startInteractionHandler )
- _scaleTool.init( this, startInteractionHandler )
- _cropTool.init( this, startInteractionHandler )
- _rotateTool.init( this, startInteractionHandler )
- _outlineTool.init( this, startInteractionHandler )
- _flipTool.init( this, startInteractionHandler )
-
- if ( _image )
- {
- updateMatrixWithImageMatrix()
- drawToolControls()
- }
- }
-
- /**
- * Update the controls in the <code>toolSprites</code> and add <code>toolSprites</code>
- * to the display list. When the <code>image</code> has been loaded and
- * <code>imageHolder</code> has been added to the child list, <code>imageAddedHandler</code>
- * is called.
- *
- * @see #image
- * @see #imageHolder
- */
- private function imageAddedHandler( event:Event ): void
- {
- addChild( toolSprites )
-
- // check whether the tools have been initialized
- if ( _moveTool )
- {
- updateMatrixWithImageMatrix()
- drawToolControls()
- }
- }
-
- /**
- * Draws all controls in all tools.
- */
- public function drawToolControls(): void
- {
- cropTool.draw()
- scaleTool.draw()
- rotateTool.draw()
- // moveTool.draw() moveTool does not have any controls
- outlineTool.draw()
- }
-
- //==========================================================================
- //
- // Size EditImage
- //
- //--------------------------------------------------------------------------
- /**
- * Set the <code>width</code> and <code>height</code> of EditImage to the
- * most outer points of the image.
- */
- override protected function measure():void
- {
- super.measure()
- measuredMinWidth = 10
- measuredMinHeight = 10
-
- if ( toolsInitialized )
- {
- var topLeft: Point = controlsPositions.topLeftSnapPoint
- var bottomRight: Point = controlsPositions.bottomRightSnapPoint
- measuredWidth = bottomRight.x - topLeft.x
- measuredHeight = bottomRight.y - topLeft.y
- }
- else if ( _image )
- {
- measuredWidth = _image.width/2
- measuredHeight = _image.height/2
- }
- }
-
- //==========================================================================
- //
- // Enable/Disable tool and make them visible/invisble
- //
- //--------------------------------------------------------------------------
- /**
- * @inheritDoc
- *
- * @see #updateToolSprites()
- */
- public function updateToolsEnabled():void
- {
- var aToolIsEnabled: Boolean
-
- // highest arrangement
- aToolIsEnabled = updateToolSprites( moveTool ) //use getter of moveTool in case the tools are not initialized yet.
- aToolIsEnabled = updateToolSprites( _rotateTool ) || aToolIsEnabled
- aToolIsEnabled = updateToolSprites( _cropTool ) || aToolIsEnabled
- aToolIsEnabled = updateToolSprites( _scaleTool ) || aToolIsEnabled
-
- _outlineTool.enabled = aToolIsEnabled
- updateToolSprites( _outlineTool )
- }
-
- /**
- * Depending on <code>BaseTool.enabled</code>, add or remove
- * tools like <code>ScaleTool</code> from the display list.
- *
- * @param tool Add or remove this tool.
- * @return true if the tool is enabled.
- *
- * @see com.faindu.editImage.base.BaseTool.enabled
- */
- private function updateToolSprites( tool:IBaseTool ): Boolean
- {
- var isChild:Boolean = toolSprites.contains( tool as Sprite )
- if ( tool.enabled )
- {
- // add child or sent to bottom if enabled
- if ( isChild )
- {
- toolSprites.setChildIndex( tool as Sprite, 0)
- }
- else
- {
- toolSprites.addChildAt( tool as Sprite, 0)
- }
- return true
- }
- else if ( isChild )
- {
- // removed if disabled
- toolSprites.removeChild( tool as Sprite )
- }
- return false
- }
-
- /**
- * Shows or hides all tools including their controls that are enabled.
- *
- * @param visible If true, show all controls. Otherwise hide all controls.
- *
- * @see com.faindu.editImage.base.BaseTool.enabled
- */
- protected function toolsAllVisible( visible:Boolean=true ): void
- {
- moveTool.visible = visible
- scaleTool.visible = visible
- rotateTool.visible = visible
- cropTool.visible = visible
- outlineTool.visible = visible
- }
-
- //==========================================================================
- //
- // interaction handlers
- //
- //--------------------------------------------------------------------------
- /**
- * When the user clicks and holds (<code>MOUSE_DOWN</code>) a control ,
- * <code>startInteractionHandler</code> is called.
- */
- protected function startInteractionHandler( mouseEvent:MouseEvent ): void
- {
- _currentControl = mouseEvent.currentTarget as BaseControl
- if ( _currentControl )
- {
- // hide all visible tool controls and show them again after the interaction
- toolsAllVisible( false )
-
- // setup interaction
- updateMatrixWithImageMatrix()
-
- // if snapping is enabled, cache the topLeftSnapPoint
- if ( snapToTop || snapToLeft )
- snapPoint = controlsPositions.topLeftSnapPoint
-
- // usually startControlInteraction() of all tools are listening to this event.
- dispatchEvent( new EditImageEvent( EditImageEvent.CONTROL_DOWN,
- mouseEvent.shiftKey,
- new Point( mouseX, mouseY ) ) )
-
- // setup stage events to manage control interaction
- stage.addEventListener( MouseEvent.MOUSE_MOVE, interactionHandler )
- stage.addEventListener( MouseEvent.MOUSE_UP, endInteractionHandler, false )
- stage.addEventListener( MouseEvent.MOUSE_UP, endInteractionHandler, true )
- }
- }
-
- /**
- * When the user clicks and hold on a control and then moves the mouse ,
- * <code>interactionHandler</code> is called.
- */
- protected function interactionHandler( mouseEvent:MouseEvent ): void
- {
- dispatchEvent( new EditImageEvent( EditImageEvent.CONTROL_MOVE,
- mouseEvent.shiftKey,
- new Point( mouseX, mouseY ) ) )
- updateControlsPositions()
- _imageHolder.transform.matrix = _toolMatrix.clone()
- mouseEvent.updateAfterEvent()
- }
-
- /**
- * When the user clicks and hold (<code>MOUSE_DOWN</code>) a control and then
- * releases the mouse button, <code>endInteractionHandler</code> is called.
- */
- protected function endInteractionHandler( event:MouseEvent ): void
- {
- updateControlsPositions()
-
- if ( snapPoint && ( snapToTop || snapToLeft ) )
- {
- var moveDifference: Point = snapPoint.subtract( controlsPositions.topLeftSnapPoint )
- if ( snapToLeft )
- _toolMatrix.tx += moveDifference.x
- if ( snapToTop )
- _toolMatrix.ty += moveDifference.y
-
- updateControlsPositions()
- _imageHolder.transform.matrix = _toolMatrix.clone()
- }
-
- drawToolControls()
- rotateTool.deltaAngle = 0
-
- toolsAllVisible()
-
- stage.removeEventListener(MouseEvent.MOUSE_MOVE, interactionHandler)
- stage.removeEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, false)
- stage.removeEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, true)
-
- _currentControl = null
- invalidateSize()
- }
-
- //==========================================================================
- //
- // Updates
- //
- //--------------------------------------------------------------------------
- /**
- * Updates the matrix of imageHolder. In particular:
- * <p>
- * <ul>
- * <li>Updates <code>toolMatrix</code> with the current matrix of imageHolder.</li>
- * <li>Updates the possible positions of the controls.</li>
- * <li>Assigns tollMatrix to the matrix of imageHolder.</li>
- * </ul>
- * </p>
- */
- protected function updateMatrixWithImageMatrix():void
- {
- _toolMatrix = _imageHolder.transform.concatenatedMatrix.clone()
- // counter transform of the parents of the tool
- var current: Matrix = transform.concatenatedMatrix
- current.invert()
- _toolMatrix.concat( current )
-
- updateControlsPositions()
-
- _imageHolder.transform.matrix = _toolMatrix.clone()
- }
-
- /**
- * Recalculate the possible positions the of the controls.
- * <code>updateControlsPositions</code> does not update
- * the displayed positions of the controls.
- *
- * @param reset The toolMatrix and the cutout shall be reset.
- * @param cutout The new cutout after reset.
- */
- public function updateControlsPositions():void
- {
- if ( _image )
- {
- var imageBounds: Rectangle
-
- var imageHolderScrollRect: Rectangle = _imageHolder.scrollRect
- if ( imageHolderScrollRect )
- {
- imageBounds = imageHolderScrollRect.clone()
- imageBounds.x = 0
- imageBounds.y = 0
- }
- else
- {
- // if imageHolder.scrollRect is reset to null with imageEdits
- imageBounds = new Rectangle( 0, 0, image.bitmapData.width,
- image.bitmapData.height )
- }
-
- _controlsPositions.update( _toolMatrix, imageBounds )
- }
- }
-
- //==========================================================================
- //
- // Image Manipulations
- //
- //--------------------------------------------------------------------------
- /** @private */
- protected var _imageManipulations: IImageManipulations
- /**
- * @inheritDoc
- */
- public function get imageManipulations(): IImageManipulations
- {
- if ( ! _toolsInitialized )
- initTools()
- return _imageManipulations
- }
-
- //==========================================================================
- //
- // Image Manipulations
- //
- //--------------------------------------------------------------------------
- /**
- * @inheritDoc
- */
- public function clone(): IEditImage
- {
- var clonedEditImage: IEditImage = new EditImage()
- var clonedBitmap: Bitmap = new Bitmap( _image.bitmapData ) // Should we call bitmapData.clone() ?
- clonedEditImage.showBitmap( clonedBitmap, _url )
-
- return clonedEditImage
- }
- }
- }