PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/PImageEdit/src/com/faindu/editImage/EditImage.as

http://ptraco.googlecode.com/
ActionScript | 701 lines | 358 code | 58 blank | 285 comment | 37 complexity | c48932176a474c3062de68057585175f MD5 | raw file
  1. // Copyright (c) 2007-2008,
  2. // Trevor McCauley, http://www.senocular.com,
  3. // Alessandro Crugnola, http://www.sephiroth.it,
  4. // Marc Speck, http://www.faindu.com
  5. // All Rights Reserved. The following is Source Code and is subject to all restrictions
  6. // on such code as contained in the license accompanying this product.
  7. package com.faindu.editImage
  8. {
  9. import com.faindu.editImage.base.BaseControl;
  10. import com.faindu.editImage.base.ControlsPositions;
  11. import com.faindu.editImage.base.EditImageEvent;
  12. import com.faindu.editImage.base.IBaseTool;
  13. import com.faindu.editImage.base.IEditImageTools;
  14. import com.faindu.editImage.base.ImageLoader;
  15. import com.faindu.editImage.crop.CropTool;
  16. import com.faindu.editImage.crop.ICropTool;
  17. import com.faindu.editImage.flip.FlipTool;
  18. import com.faindu.editImage.flip.IFlipTool;
  19. import com.faindu.editImage.manipulations.IImageManipulations;
  20. import com.faindu.editImage.manipulations.ImageManipulations;
  21. import com.faindu.editImage.move.IMoveTool;
  22. import com.faindu.editImage.move.MoveTool;
  23. import com.faindu.editImage.outline.OutlineTool;
  24. import com.faindu.editImage.rotation.IRotateTool;
  25. import com.faindu.editImage.rotation.RotateTool;
  26. import com.faindu.editImage.scale.ScaleTool;
  27. import flash.display.Bitmap;
  28. import flash.display.Sprite;
  29. import flash.events.Event;
  30. import flash.events.MouseEvent;
  31. import flash.geom.Matrix;
  32. import flash.geom.Point;
  33. import flash.geom.Rectangle;
  34. import mx.logging.ILogger;
  35. /**
  36. * Dispatched when the user presses any control of a tool.
  37. *
  38. * @eventType com.faindu.editImage.base.EditImageEvent.CONTROL_DOWN
  39. */
  40. [Event(name="controlDown", type="com.faindu.editImage.base.EditImageEvent")]
  41. /**
  42. * Dispatched when the user has pressed a control and moves while still pressing
  43. * the mouse.
  44. *
  45. * @eventType com.faindu.editImage.base.EditImageEvent.CONTROL_MOVE
  46. */
  47. [Event(name="controlMove", type="com.faindu.editImage.base.EditImageEvent")]
  48. /**
  49. * EditImage loads one image and allows the user to manipulate it. There are the
  50. * following tools available:
  51. *
  52. * <p>
  53. * <ul>
  54. * <li><b>Crop Tool</b>: Cut out parts of the image.</li>
  55. * <li><b>Scale Tool</b>: Resize the image.</li>
  56. * <li><b>Rotate Tool</b>: Rotate the image around its center.</li>
  57. * <li><b>Move Tool</b>: Move the image to an other position.</li>
  58. * </ul>
  59. * </p>
  60. *
  61. * <p> While cropping corners, scaling corners or rotating the image, hold down the shiftkey
  62. * to remove the constrains of the transformation.
  63. * </p>
  64. */
  65. public class EditImage extends ImageLoader implements IEditImage, IEditImageTools
  66. {
  67. /**
  68. * Constructor.
  69. */
  70. public function EditImage()
  71. {
  72. super()
  73. addEventListener( EVENT_IMAGE_ADDED , imageAddedHandler )
  74. }
  75. private var logger: ILogger
  76. //--------------------------------------------------------------------------
  77. //
  78. // Variables
  79. //
  80. //--------------------------------------------------------------------------
  81. /**
  82. * <code>updateControlsEnabled</code> adds all tools like
  83. * <code>ScaleTool</code> to <code>toolSprites</code>.
  84. *
  85. * @see #updateControlsEnabled()
  86. * @see com.faindu.editImage.base.BaseTool.updateControlContainer()
  87. */
  88. private var toolSprites:Sprite = new Sprite();
  89. //--------------------------------------------------------------------------
  90. //
  91. // Getter properties
  92. //
  93. //--------------------------------------------------------------------------
  94. //----------------------------------
  95. // controlsPositions
  96. //----------------------------------
  97. private var _controlsPositions: ControlsPositions = new ControlsPositions()
  98. /**
  99. * Stores the possible positions for controls. All points are in the inner coordinate
  100. * system.
  101. */
  102. public function get controlsPositions(): ControlsPositions
  103. {
  104. return _controlsPositions
  105. }
  106. //----------------------------------
  107. // initialized
  108. //----------------------------------
  109. /** @private */
  110. protected var _toolsInitialized: Boolean
  111. /**
  112. * @inheritDoc
  113. */
  114. public function get toolsInitialized(): Boolean
  115. {
  116. return _toolsInitialized
  117. }
  118. //----------------------------------
  119. // transform tools
  120. //----------------------------------
  121. // all tools are lazily created and loaded because in quite a few cases, only the image
  122. // is shown witout any tools.
  123. /** @private */
  124. protected var _moveTool: IMoveTool
  125. /**
  126. * Tool for moving the image around.
  127. */
  128. public function get moveTool(): IMoveTool
  129. {
  130. if ( ! _toolsInitialized )
  131. initTools()
  132. return _moveTool
  133. }
  134. /** @private */
  135. protected var _scaleTool: IBaseTool
  136. /**
  137. * Tool for changing the displayed size of the image.
  138. */
  139. public function get scaleTool(): IBaseTool
  140. {
  141. if ( ! _toolsInitialized )
  142. initTools()
  143. return _scaleTool
  144. }
  145. /** @private */
  146. protected var _rotateTool: IRotateTool
  147. /**
  148. * Tool for rotating the image.
  149. */
  150. public function get rotateTool(): IRotateTool
  151. {
  152. if ( ! _toolsInitialized )
  153. initTools()
  154. return _rotateTool
  155. }
  156. /** @private */
  157. protected var _cropTool: ICropTool
  158. /**
  159. * Tool for rotating the image.
  160. */
  161. public function get cropTool(): ICropTool
  162. {
  163. if ( ! _toolsInitialized )
  164. initTools()
  165. return _cropTool
  166. }
  167. /** @private */
  168. protected var _outlineTool: IBaseTool
  169. /**
  170. * @private
  171. */
  172. public function get outlineTool(): IBaseTool
  173. {
  174. if ( ! _toolsInitialized )
  175. initTools()
  176. return _outlineTool
  177. }
  178. /** @private */
  179. protected var _flipTool: IFlipTool
  180. /**
  181. * @private
  182. */
  183. public function get flipTool(): IFlipTool
  184. {
  185. if ( ! _toolsInitialized )
  186. initTools()
  187. return _flipTool
  188. }
  189. //--------------------------------------------------------------------------
  190. //
  191. // Getter/Setter properties
  192. //
  193. //--------------------------------------------------------------------------
  194. //----------------------------------
  195. // movingAreaWidth
  196. //----------------------------------
  197. private var _movingAreaWidth: Number = 500;
  198. /**
  199. * @copy com.faindu.editImage.move.IMoveTool#movingAreaWidth
  200. *
  201. * @default 500
  202. */
  203. [Bindable]
  204. public function get movingAreaWidth(): Number
  205. {
  206. return _movingAreaWidth
  207. }
  208. public function set movingAreaWidth( value:Number ): void
  209. {
  210. if ( _movingAreaWidth != value )
  211. {
  212. _movingAreaWidth = value
  213. if ( moveTool )
  214. {
  215. moveTool.movingAreaWidth = _movingAreaWidth
  216. }
  217. }
  218. }
  219. //----------------------------------
  220. // movingAreaHeight
  221. //----------------------------------
  222. private var _movingAreaHeight: Number = 500;
  223. /**
  224. * @copy com.faindu.editImage.move.IMoveTool#movingAreaWidth
  225. *
  226. * @default 500
  227. */
  228. [Bindable]
  229. public function get movingAreaHeight(): Number
  230. {
  231. return _movingAreaHeight
  232. }
  233. public function set movingAreaHeight( value:Number ): void
  234. {
  235. if ( _movingAreaHeight != value )
  236. {
  237. _movingAreaHeight = value
  238. if ( moveTool )
  239. {
  240. moveTool.movingAreaHeight = _movingAreaHeight
  241. }
  242. }
  243. }
  244. //----------------------------------
  245. // snapToTop
  246. //----------------------------------
  247. /**
  248. * If <code>snapToTop</code> or <code>snapToLeft</code> is true,
  249. * the most outer point is stored in <code>snapPoint</code>.
  250. */
  251. private var snapPoint: Point
  252. /** @private */
  253. protected var _snapToTop: Boolean = true
  254. /**
  255. * @inheritDoc
  256. */
  257. public function get snapToTop(): Boolean
  258. {
  259. return _snapToTop
  260. }
  261. public function set snapToTop( value:Boolean ): void
  262. {
  263. _snapToTop = value
  264. }
  265. //----------------------------------
  266. // snapToLeft
  267. //----------------------------------
  268. /** @private */
  269. protected var _snapToLeft: Boolean = true
  270. /**
  271. * @inheritDoc
  272. */
  273. public function get snapToLeft(): Boolean
  274. {
  275. return _snapToLeft
  276. }
  277. public function set snapToLeft( value:Boolean ): void
  278. {
  279. _snapToLeft = value
  280. }
  281. //----------------------------------
  282. // controlSize
  283. //----------------------------------
  284. private var _controlSize:uint = 20;
  285. /**
  286. * @inheritDoc
  287. *
  288. * @default 20
  289. * @see com.faindu.editImage.base.BaseControl
  290. */
  291. public function get controlSize(): uint
  292. {
  293. return _controlSize;
  294. }
  295. public function set controlSize( value:uint ): void
  296. {
  297. if ( _controlSize != value )
  298. {
  299. _controlSize = value
  300. }
  301. }
  302. //----------------------------------
  303. // currentControl
  304. //----------------------------------
  305. private var _currentControl: BaseControl
  306. /**
  307. * The current control being used if being manipulated.
  308. * This value is null if the user is not transforming the image.
  309. */
  310. public function get currentControl():BaseControl
  311. {
  312. return _currentControl
  313. }
  314. //----------------------------------
  315. // toolMatrix
  316. //----------------------------------
  317. private var _toolMatrix:Matrix = new Matrix()
  318. /**
  319. * The transform matrix of the tool as it exists in its own coordinate space.
  320. * This is not a clone, so be carefull with referencing the matrix.
  321. */
  322. public function get toolMatrix(): Matrix
  323. {
  324. return _toolMatrix
  325. }
  326. public function set toolMatrix( value:Matrix ): void
  327. {
  328. _toolMatrix = value
  329. }
  330. //==========================================================================
  331. //
  332. // Init
  333. //
  334. //--------------------------------------------------------------------------
  335. /**
  336. * Initialize the tools such as scale or crop and the manipulations property.
  337. * The getters of the tools or <code>manipulations</code> property call <code>init</code>.
  338. * This means that by default, none of the tools and the manipulations is instaniated.
  339. */
  340. protected function initTools():void
  341. {
  342. _toolsInitialized = true
  343. _moveTool = new MoveTool()
  344. _scaleTool = new ScaleTool()
  345. _cropTool = new CropTool()
  346. _rotateTool = new RotateTool()
  347. _outlineTool = new OutlineTool()
  348. _flipTool = new FlipTool()
  349. _imageManipulations = new ImageManipulations( this as EditImage )
  350. _moveTool.init( this, startInteractionHandler )
  351. _scaleTool.init( this, startInteractionHandler )
  352. _cropTool.init( this, startInteractionHandler )
  353. _rotateTool.init( this, startInteractionHandler )
  354. _outlineTool.init( this, startInteractionHandler )
  355. _flipTool.init( this, startInteractionHandler )
  356. if ( _image )
  357. {
  358. updateMatrixWithImageMatrix()
  359. drawToolControls()
  360. }
  361. }
  362. /**
  363. * Update the controls in the <code>toolSprites</code> and add <code>toolSprites</code>
  364. * to the display list. When the <code>image</code> has been loaded and
  365. * <code>imageHolder</code> has been added to the child list, <code>imageAddedHandler</code>
  366. * is called.
  367. *
  368. * @see #image
  369. * @see #imageHolder
  370. */
  371. private function imageAddedHandler( event:Event ): void
  372. {
  373. addChild( toolSprites )
  374. // check whether the tools have been initialized
  375. if ( _moveTool )
  376. {
  377. updateMatrixWithImageMatrix()
  378. drawToolControls()
  379. }
  380. }
  381. /**
  382. * Draws all controls in all tools.
  383. */
  384. public function drawToolControls(): void
  385. {
  386. cropTool.draw()
  387. scaleTool.draw()
  388. rotateTool.draw()
  389. // moveTool.draw() moveTool does not have any controls
  390. outlineTool.draw()
  391. }
  392. //==========================================================================
  393. //
  394. // Size EditImage
  395. //
  396. //--------------------------------------------------------------------------
  397. /**
  398. * Set the <code>width</code> and <code>height</code> of EditImage to the
  399. * most outer points of the image.
  400. */
  401. override protected function measure():void
  402. {
  403. super.measure()
  404. measuredMinWidth = 10
  405. measuredMinHeight = 10
  406. if ( toolsInitialized )
  407. {
  408. var topLeft: Point = controlsPositions.topLeftSnapPoint
  409. var bottomRight: Point = controlsPositions.bottomRightSnapPoint
  410. measuredWidth = bottomRight.x - topLeft.x
  411. measuredHeight = bottomRight.y - topLeft.y
  412. }
  413. else if ( _image )
  414. {
  415. measuredWidth = _image.width/2
  416. measuredHeight = _image.height/2
  417. }
  418. }
  419. //==========================================================================
  420. //
  421. // Enable/Disable tool and make them visible/invisble
  422. //
  423. //--------------------------------------------------------------------------
  424. /**
  425. * @inheritDoc
  426. *
  427. * @see #updateToolSprites()
  428. */
  429. public function updateToolsEnabled():void
  430. {
  431. var aToolIsEnabled: Boolean
  432. // highest arrangement
  433. aToolIsEnabled = updateToolSprites( moveTool ) //use getter of moveTool in case the tools are not initialized yet.
  434. aToolIsEnabled = updateToolSprites( _rotateTool ) || aToolIsEnabled
  435. aToolIsEnabled = updateToolSprites( _cropTool ) || aToolIsEnabled
  436. aToolIsEnabled = updateToolSprites( _scaleTool ) || aToolIsEnabled
  437. _outlineTool.enabled = aToolIsEnabled
  438. updateToolSprites( _outlineTool )
  439. }
  440. /**
  441. * Depending on <code>BaseTool.enabled</code>, add or remove
  442. * tools like <code>ScaleTool</code> from the display list.
  443. *
  444. * @param tool Add or remove this tool.
  445. * @return true if the tool is enabled.
  446. *
  447. * @see com.faindu.editImage.base.BaseTool.enabled
  448. */
  449. private function updateToolSprites( tool:IBaseTool ): Boolean
  450. {
  451. var isChild:Boolean = toolSprites.contains( tool as Sprite )
  452. if ( tool.enabled )
  453. {
  454. // add child or sent to bottom if enabled
  455. if ( isChild )
  456. {
  457. toolSprites.setChildIndex( tool as Sprite, 0)
  458. }
  459. else
  460. {
  461. toolSprites.addChildAt( tool as Sprite, 0)
  462. }
  463. return true
  464. }
  465. else if ( isChild )
  466. {
  467. // removed if disabled
  468. toolSprites.removeChild( tool as Sprite )
  469. }
  470. return false
  471. }
  472. /**
  473. * Shows or hides all tools including their controls that are enabled.
  474. *
  475. * @param visible If true, show all controls. Otherwise hide all controls.
  476. *
  477. * @see com.faindu.editImage.base.BaseTool.enabled
  478. */
  479. protected function toolsAllVisible( visible:Boolean=true ): void
  480. {
  481. moveTool.visible = visible
  482. scaleTool.visible = visible
  483. rotateTool.visible = visible
  484. cropTool.visible = visible
  485. outlineTool.visible = visible
  486. }
  487. //==========================================================================
  488. //
  489. // interaction handlers
  490. //
  491. //--------------------------------------------------------------------------
  492. /**
  493. * When the user clicks and holds (<code>MOUSE_DOWN</code>) a control ,
  494. * <code>startInteractionHandler</code> is called.
  495. */
  496. protected function startInteractionHandler( mouseEvent:MouseEvent ): void
  497. {
  498. _currentControl = mouseEvent.currentTarget as BaseControl
  499. if ( _currentControl )
  500. {
  501. // hide all visible tool controls and show them again after the interaction
  502. toolsAllVisible( false )
  503. // setup interaction
  504. updateMatrixWithImageMatrix()
  505. // if snapping is enabled, cache the topLeftSnapPoint
  506. if ( snapToTop || snapToLeft )
  507. snapPoint = controlsPositions.topLeftSnapPoint
  508. // usually startControlInteraction() of all tools are listening to this event.
  509. dispatchEvent( new EditImageEvent( EditImageEvent.CONTROL_DOWN,
  510. mouseEvent.shiftKey,
  511. new Point( mouseX, mouseY ) ) )
  512. // setup stage events to manage control interaction
  513. stage.addEventListener( MouseEvent.MOUSE_MOVE, interactionHandler )
  514. stage.addEventListener( MouseEvent.MOUSE_UP, endInteractionHandler, false )
  515. stage.addEventListener( MouseEvent.MOUSE_UP, endInteractionHandler, true )
  516. }
  517. }
  518. /**
  519. * When the user clicks and hold on a control and then moves the mouse ,
  520. * <code>interactionHandler</code> is called.
  521. */
  522. protected function interactionHandler( mouseEvent:MouseEvent ): void
  523. {
  524. dispatchEvent( new EditImageEvent( EditImageEvent.CONTROL_MOVE,
  525. mouseEvent.shiftKey,
  526. new Point( mouseX, mouseY ) ) )
  527. updateControlsPositions()
  528. _imageHolder.transform.matrix = _toolMatrix.clone()
  529. mouseEvent.updateAfterEvent()
  530. }
  531. /**
  532. * When the user clicks and hold (<code>MOUSE_DOWN</code>) a control and then
  533. * releases the mouse button, <code>endInteractionHandler</code> is called.
  534. */
  535. protected function endInteractionHandler( event:MouseEvent ): void
  536. {
  537. updateControlsPositions()
  538. if ( snapPoint && ( snapToTop || snapToLeft ) )
  539. {
  540. var moveDifference: Point = snapPoint.subtract( controlsPositions.topLeftSnapPoint )
  541. if ( snapToLeft )
  542. _toolMatrix.tx += moveDifference.x
  543. if ( snapToTop )
  544. _toolMatrix.ty += moveDifference.y
  545. updateControlsPositions()
  546. _imageHolder.transform.matrix = _toolMatrix.clone()
  547. }
  548. drawToolControls()
  549. rotateTool.deltaAngle = 0
  550. toolsAllVisible()
  551. stage.removeEventListener(MouseEvent.MOUSE_MOVE, interactionHandler)
  552. stage.removeEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, false)
  553. stage.removeEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, true)
  554. _currentControl = null
  555. invalidateSize()
  556. }
  557. //==========================================================================
  558. //
  559. // Updates
  560. //
  561. //--------------------------------------------------------------------------
  562. /**
  563. * Updates the matrix of imageHolder. In particular:
  564. * <p>
  565. * <ul>
  566. * <li>Updates <code>toolMatrix</code> with the current matrix of imageHolder.</li>
  567. * <li>Updates the possible positions of the controls.</li>
  568. * <li>Assigns tollMatrix to the matrix of imageHolder.</li>
  569. * </ul>
  570. * </p>
  571. */
  572. protected function updateMatrixWithImageMatrix():void
  573. {
  574. _toolMatrix = _imageHolder.transform.concatenatedMatrix.clone()
  575. // counter transform of the parents of the tool
  576. var current: Matrix = transform.concatenatedMatrix
  577. current.invert()
  578. _toolMatrix.concat( current )
  579. updateControlsPositions()
  580. _imageHolder.transform.matrix = _toolMatrix.clone()
  581. }
  582. /**
  583. * Recalculate the possible positions the of the controls.
  584. * <code>updateControlsPositions</code> does not update
  585. * the displayed positions of the controls.
  586. *
  587. * @param reset The toolMatrix and the cutout shall be reset.
  588. * @param cutout The new cutout after reset.
  589. */
  590. public function updateControlsPositions():void
  591. {
  592. if ( _image )
  593. {
  594. var imageBounds: Rectangle
  595. var imageHolderScrollRect: Rectangle = _imageHolder.scrollRect
  596. if ( imageHolderScrollRect )
  597. {
  598. imageBounds = imageHolderScrollRect.clone()
  599. imageBounds.x = 0
  600. imageBounds.y = 0
  601. }
  602. else
  603. {
  604. // if imageHolder.scrollRect is reset to null with imageEdits
  605. imageBounds = new Rectangle( 0, 0, image.bitmapData.width,
  606. image.bitmapData.height )
  607. }
  608. _controlsPositions.update( _toolMatrix, imageBounds )
  609. }
  610. }
  611. //==========================================================================
  612. //
  613. // Image Manipulations
  614. //
  615. //--------------------------------------------------------------------------
  616. /** @private */
  617. protected var _imageManipulations: IImageManipulations
  618. /**
  619. * @inheritDoc
  620. */
  621. public function get imageManipulations(): IImageManipulations
  622. {
  623. if ( ! _toolsInitialized )
  624. initTools()
  625. return _imageManipulations
  626. }
  627. //==========================================================================
  628. //
  629. // Image Manipulations
  630. //
  631. //--------------------------------------------------------------------------
  632. /**
  633. * @inheritDoc
  634. */
  635. public function clone(): IEditImage
  636. {
  637. var clonedEditImage: IEditImage = new EditImage()
  638. var clonedBitmap: Bitmap = new Bitmap( _image.bitmapData ) // Should we call bitmapData.clone() ?
  639. clonedEditImage.showBitmap( clonedBitmap, _url )
  640. return clonedEditImage
  641. }
  642. }
  643. }