Flex采用遮罩方法图片裁剪

Flex采用遮罩方法图片裁剪

在线预览

  http://rj.8634.com/xiaoshandong/ImageCropperDemo/ImageCropperDemo.html

源码工程下载

 http://files.cnblogs.com/files/ffmpeg/ImageCropper.zip

截图

  

源代码

ImageCropperDemo.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:c="com.flexblocks.imagecropper.*" layout="absolute" width="610" height="500" horizontalScrollPolicy="off" verticalScrollPolicy="off"
                 viewSourceURL="srcview/index.html">
    <mx:Style>
        Label {
            color: #BBBBBB;
        }
        
        CheckBox {
            color: #BBBBBB;
            textRollOverColor: #BBBBBB;
            textSelectedColor: #BBBBBB;
        }                        
    
    
    
    
    
    </mx:Style>
    <mx:Script>
        <![CDATA[
        
            // Index of last image selected using the selectImage ComboBox
            
            private var lastSelectedImage:uint = 0;
            
            // Current values for the cropping rectangle, handle size, and aspect ratio constraint settings
            
            private var currentCropbox:Rectangle;
            private var currentHandleSize:uint = 10;
            private var currentCropConstraint:Boolean = false
            
            // Image 1 ("Image Larger then Component"): Last values for the cropping rectangle, handle size, and aspect ratio constraint settings
            
            private var img1Cropbox:Rectangle;
            private var img1HandleSize:uint = 10;
            private var img1CropConstraint:Boolean = false;
            
            // Image 2 ("Image Smaller than Component"): Last values for the cropping rectangle, handle size, and aspect ratio constraint settings

            private var img2Cropbox:Rectangle;            
            private var img2HandleSize:uint = 10;
            private var img2CropConstraint:Boolean = false;            
            
            // --------------------------------------------------------------------------------------------------
            // doImageSelect - Called when an image is selected using the selectImage ComboBox
            // --------------------------------------------------------------------------------------------------

            private function doImageSelect():void {
                
                // Remove the previously cropped image
                
                croppedImage.source = null;
                croppedImage.width = 0;
                croppedImage.height = 0;
                
                // Save the cropping rectangle, handle size and constraint option for the previous image so that these settings 
                // may be restored when the image is re-selected
                
                if (lastSelectedImage == 0) { 
                    img1Cropbox = imageCropper.getCropRect(true);
                    img1HandleSize = imageCropper.handleSize;
                    img1CropConstraint = constrainToAspectRatio.selected;
                }
                else {
                    img2Cropbox = imageCropper.getCropRect(true);
                    img2HandleSize = imageCropper.handleSize;
                    img2CropConstraint = constrainToAspectRatio.selected;
                }
                
                // Update the saved ComboBox index to the index for the newly selected image
                
                lastSelectedImage = selectImage.selectedIndex;    
                
                // Restore the cropping rectangle, handle size and constraint option for the image just selected
                                            
                currentCropbox = lastSelectedImage == 0 ? img1Cropbox : img2Cropbox;
                currentHandleSize = lastSelectedImage == 0 ? img1HandleSize : img2HandleSize;
                currentCropConstraint = lastSelectedImage == 0 ? img1CropConstraint : img2CropConstraint;
                
                // Tell the ImageCropper component to begin loading the selected image
                
                imageCropper.sourceImage = selectImage.selectedItem.data;                

                // Disable all controls while the image is loading. This is done after the sourceImage is assigned
                // to the ImageCropper because when the ImageCropper is disabled a sourceImage cannot be assigned.
                
                enableControls(false, true);            
            }

            // --------------------------------------------------------------------------------------------------
            // imageReady - Called when the ImageCropper component has loaded and initialized an image
            // --------------------------------------------------------------------------------------------------
            
            private function imageReady():void {
                
                // Enable the controls (including the imageCropper). Note that the imageCropper must be enabled before changing property values or calling setCropRect().
                
                enableControls(true, true);
                            
                // Restore the handle size that was previously saved for this image
                
                imageCropper.handleSize = currentHandleSize;
                handleSize.value = currentHandleSize;
                
                // Restore "Constrain Crop Rectangle to Aspect Ratio" to the setting that was previously saved for the image
                
                imageCropper.constrainToAspectRatio = currentCropConstraint;
                constrainToAspectRatio.selected = currentCropConstraint;

                // If this image was not previously selected then set the cropping rectangle to include the entire image. Otherwise,
                // restore the cropping rectangle to its previous value. Note that the cropping rectangle is relative to the component
                // and not to the image because the componentRelative parameter in the call the to setCropRect is set to true.
                
                if (!currentCropbox) imageCropper.setCropRect();
                else imageCropper.setCropRect(currentCropbox.width, currentCropbox.height, currentCropbox.x, currentCropbox.y, true);
                
                // Get the cropped image 
                
                doCrop();                
            }
            
            // --------------------------------------------------------------------------------------------------
            // doCrop - Get the cropped image from the ImageCropper component
            // --------------------------------------------------------------------------------------------------

            private function doCrop():void {
                
                // Get the cropped BitmapData
                
                var newImage:BitmapData = imageCropper.croppedBitmapData;
                
                // Set the width and height of the croppedImage Image based on the dimensions of the cropped image
                
                croppedImage.width = newImage.width;
                croppedImage.height = newImage.height;

                // Create a new Bitmap from the BitmapData and assign it to the croppedImage Image
                
                croppedImage.source = new Bitmap(newImage);
                
                // Display the cropping rectangle in relative to the ImageCropper component and relative to the image
                
                imageCropperRect.text = imageCropper.getCropRect(true, true).toString();
                sourceImageRect.text = imageCropper.getCropRect(false, true).toString();
            }    
            
            // --------------------------------------------------------------------------------------------------
            // enableControls - Enables or disables the controls
            // --------------------------------------------------------------------------------------------------
            
            private function enableControls(enable:Boolean, includeEnabled:Boolean = false):void {
                
                // The checkbox to enable/disable the ImageCropper component is only affected if includeEnabled is set to true
                
                if (includeEnabled) enableComponent.enabled = enable;
                
                // Set the enabled state for all other controls
                
                imageCropper.enabled = enable;
                selectImage.enabled = enable;
                outlineColor.enabled = enable;
                outlineAlpha.enabled = enable;
                handleColor.enabled = enable;
                handleAlpha.enabled = enable;
                maskColor.enabled = enable;
                maskAlpha.enabled = enable;
                backgroundColor.enabled = enable;
                backgroundAlpha.enabled = enable;
                handleSize.enabled = enable;
                constrainToAspectRatio.enabled = enable;
            }
        ]]>
    </mx:Script>
    <mx:Label x="14" y="11" text="ImageCropper Component" fontWeight="bold" color="#FFFFFF"/>
    <c:ImageCropper id="imageCropper" y="39" left="15" width="280" height="280" enabled="true" sourceImage="large.jpg" handleColor="#FF00FF" maskColor="#660066" maskAlpha="0.6"
                    sourceImageReady="imageReady()" sourceImageLoadError="trace('Error while loading image')" cropRectChanged="doCrop()" cropConstraintDisabled="constrainToAspectRatio.selected = false"
                    cropConstraintChanged="trace('Crop constraint changed'); doCrop()" cropDimensionsChanged="trace('Crop dimensions changed'); doCrop()" cropPositionChanged="trace('Crop position changed'); doCrop()" x="15"/>
    <mx:Label x="314" y="11" text="Cropped Image" fontWeight="bold" color="#FFFFFF"/>
    <mx:Canvas y="37" right="15" width="280" height="280" backgroundColor="#000000" x="315">
        <mx:Image id="croppedImage" scaleContent="false" y="0" x="0"/>
    </mx:Canvas>
    <mx:CheckBox id="enableComponent" x="15" y="338" enabled="false" label="Enable the ImageCropper Component" selected="true" change="enableControls(enableComponent.selected)"/>
    <mx:ComboBox id="selectImage" x="15" y="369" enabled="false" change="doImageSelect()" width="250">
        <mx:dataProvider>
            <mx:Array>
                <mx:Object label="Image Larger then Component Size" data="large.jpg"/>
                <mx:Object label="Image Smaller than Component Size" data="small.jpg"/>
            </mx:Array>
        </mx:dataProvider>
    </mx:ComboBox>
    <mx:Label x="314" y="340" text="Outline"/>
    <mx:ColorPicker id="outlineColor" x="391" y="337" enabled="false" selectedColor="#FFFFFF" change="imageCropper.outlineColor = outlineColor.selectedColor"/>
    <mx:HSlider id="outlineAlpha" x="422" y="340" width="171" enabled="false" allowTrackClick="true" minimum="0" maximum="1" snapInterval=".05" value="1" liveDragging="true" change="imageCropper.outlineAlpha = outlineAlpha.value"/>
    <mx:Label x="314" y="370" text="Handles"/>
    <mx:ColorPicker id="handleColor" x="391" y="369" enabled="false" selectedColor="#FF00FF" change="imageCropper.handleColor = handleColor.selectedColor"/>
    <mx:HSlider id="handleAlpha" x="422" y="371" width="171" enabled="false" allowTrackClick="true" minimum="0" maximum="1" snapInterval=".05" value=".5" liveDragging="true" change="imageCropper.handleAlpha = handleAlpha.value"/>
    <mx:Label x="314" y="404" text="Mask"/>
    <mx:ColorPicker id="maskColor" x="391" y="401" enabled="false" selectedColor="#660066" change="imageCropper.maskColor = maskColor.selectedColor"/>
    <mx:HSlider id="maskAlpha" x="422" y="404" width="171" enabled="false" allowTrackClick="true" minimum="0" maximum="1" value=".6" snapInterval=".05" liveDragging="true" change="imageCropper.maskAlpha = maskAlpha.value"/>
    <mx:Label x="314" y="435" text="Background"/>
    <mx:ColorPicker id="backgroundColor" x="391" y="432" enabled="false" selectedColor="#000000" change="imageCropper.backgroundColor = backgroundColor.selectedColor"/>
    <mx:HSlider id="backgroundAlpha" x="422" y="435" width="171" enabled="false" minimum="0" maximum="1" liveDragging="true" value="1" snapInterval=".05" allowTrackClick="true" change="imageCropper.backgroundAlpha = backgroundAlpha.value"/>
    <mx:Label x="13" y="435" text="Handle Size:"/>
    <mx:HSlider id="handleSize" x="92" y="435" width="177" enabled="false" allowTrackClick="true" minimum="3" maximum="20" value="10" snapInterval="1" liveDragging="true" change="imageCropper.handleSize = handleSize.value"/>
    <mx:CheckBox id="constrainToAspectRatio" x="15" y="402" enabled="false" label="Constrain Crop Rectangle to Aspect Ratio" selected="false" change="imageCropper.constrainToAspectRatio = constrainToAspectRatio.selected"/>
    <mx:Label x="13" y="466" text="ImageCropper Rect:"/>
    <mx:Label id="imageCropperRect" x="133" y="466"/>
    <mx:Label x="314" y="466" text="Crop Rect:"/>
    <mx:Label id="sourceImageRect" x="389" y="466"/>
    <s:Label id="Label1" height="18" width="50" text="Label" x="197" y="130" name="Label"/>
</mx:Application>

ImageCropper.as

package com.flexblocks.imagecropper {
    
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Loader;
    import flash.display.Shape;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.MouseEvent;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.net.URLRequest;
    
    import mx.core.UIComponent;
    import mx.managers.CursorManager;
    
    // Events
    
    /**
     * This event is dispatched when the component begins loading an image from a URL.
     */ 
    
    [Event(name="sourceImageLoading")]
    
    /**
     * This event is dispatched if an I/O error occurs while loading an image from a URL.
     */ 
    
    [Event(name="sourceImageLoadError")]
    
    /**
     * This event is dispatched when the component has completed loading an image from a URL or when a <code>BitmapData</code> object is specified for the <code>sourceImage</code> parameter.
     */ 
    
    [Event(name="sourceImageReady")]
    
    /**
     * This event is dispatched whenever the cropping rectangle is resized or repositioned using the mouse.
     */
     
    [Event(name="cropRectChanged")]
    
    /**
     * This event is dispatched whenever <code>constrainToAspectRatio</code> is set to <code>true</code> and the component alters the aspect ratio of the cropping rectangle.
     * 
     * <p>One situation where the component will alter the aspect ratio of the cropping rectangle is if the cropping rectangle handle size is increased and the cropping rectangle is
     * not large enough to contain the increased size of the handles. The other situation where the component will alter the aspect ratio of the cropping rectangle is when the cropping rectangle is initialized following
     * a call to <code>setCropRect</code> and the parameters passed to <code>setCropRect</code> necessitate that the component change the cropping rectangle in order for it to be properly displayed within the component 
     * (e.g., the coordinates passed for the cropping rectangle place part of the cropping rectangle outside of the component's display area).</p>
     */
     
    [Event(name="cropConstraintChanged")]
    
    /**
     * If <code>constrainToAspectRatio</code> is set to <code>true</code> and a call to <code>setCropRect</code> is executed with a <code>width</code> or <code>height</code> value less than or equal to zero, 
     * then <code>constrainToAspectRatio</code> will be disabled and this event will be dispatched.
     */    
    
    [Event(name="cropConstraintDisabled")]
    
    /**
     * This event is dispatched whenever the component alters the position of the cropping rectangle.
     * 
     * <p>If the cropping rectangle handle size is increased by setting the <code>handleSize</code> property and there is not enough room within the cropping rectangle to contain the larger handles, 
     * then the component will increase the size of the cropping rectangle. If the position of the cropping rectangle changes when its size is increased, then this event will be dispatched.</p>
     * 
     * <p>This event is also dispatched if the position of the cropping rectangle is specified in a call to <code>setCropRect</code> but the cropping rectangle cannot be properly displayed in the component
     * at the specified position. In this case the component will adjust the position of the cropping rectangle and dispatch the <code>cropPositionChanged</code> event.
     */    
    
    [Event(name="cropPositionChanged")]

    /**
     * This event is dispatched whenever the component alters the dimensions of the cropping rectangle.
     * 
     * <p>If the cropping rectangle handle size is increased by setting the <code>handleSize</code> property and there is not enough room within the cropping rectangle to contain the larger handles, 
     * then the component will increase the size of the cropping rectangle and dispatch this event.</p>
     * 
     * <p>This event is also dispatched if the width and height of the cropping rectangle are specified in a call to <code>setCropRect</code> but the cropping rectangle cannot be properly displayed in the component
     * using the specified dimensions. In this case the component will adjust the dimensions of the cropping rectangle and dispatch the <code>cropDimensionsChanged</code> event.
     */    
    
    [Event(name="cropDimensionsChanged")]
    
    /**
     * The ImageCropper component accepts a <code>String</code> (URL) pointing to an image file, or a <code>BitmapData</code> object that contains an image, and displays the image within the 
     * component. If the size of the image exceeds the component's dimensions then the image is scaled so that it will entirely fit within the component.
     *  
     * <p>The image may be visually cropped by adjusting the boundries of a cropping rectangle using any of the four handles on the corners 
     * of the rectangle. At any time a <code>BitmapData</code> object containing the cropped portion of the image may be retrieved, or 
     * a <code>Rectangle</code> may be retrieved that defines the cropped portion of the image.</p>
     * 
     * <p>The cropping rectangle may be initialized using coordinates and dimensions relative to the component's display or relative to the source image.</p>
     */
    
    public class ImageCropper extends UIComponent {
        
        // Track the enabled state of the component
        
        private var componentEnabled:Boolean = true;
        
        // Hand cursor for moving the cropping rectangle
        
        [Embed(source="hand.gif")]
        private var handCursor:Class;
        private var handCursorID:int = -1;
        
        // Source image object (can be a String object or a BitmapData object)
        
        private var imageSource:Object = null;
                
        // Component dimensions
        
        private var componentWidth:Number;
        private var componentHeight:Number;

        // Component bitmap variables

        private var componentBitmap:Bitmap;
        private var componentBitmapData:BitmapData;

        // Variables for loading an image
        
        private var newImageLoaded:Boolean = false;
        private var currentSource:Loader;
        
        // Component colors and alpha values
        
        private var bkgndColor:uint = 0xFF000000;
        private var bkgndAlpha:uint = 0xFF000000;
        private var cropMaskColor:uint = 0x4CFF0000;
        private var cropMaskAlpha:uint = 0x4C000000;
        private var cropHandleColor:uint = 0xFFFF0000;
        private var cropHandleAlpha:Number = .5;
        private var cropSelectionOutlineColor:uint = 0xFFFFFFFF;
        private var cropSelectionOutlineAlpha:Number = 1.; 
        
        // Image scaling variables        
        
        private var imageLocation:Point;
        private var imageScaleFactor:Number;
        private var imageScaledWidth:Number;
        private var imageScaledHeight:Number;
        private var imageBitmapData:BitmapData;
        private var scaledImageBitmapData:BitmapData;
        
        // Image cropping variables
        
        private var cropX:Number;
        private var cropY:Number;
        private var cropWidth:Number;
        private var cropHeight:Number;        
        private var newCroppingRect:Boolean = false;
        private var cropRatioActive:Boolean = false;
        private var cropRatio:Number = 0;
        private var cropRect:Rectangle;    
        private var croppingRectBitmapData:BitmapData;
        private var anchorX:Number;
        private var anchorY:Number;
        private var activeHandle:int;
        private var croppingRectIsImageScale:Boolean;
        private var cropMaskChanged:Boolean = false;
        
        // Crop sizing variables
        
        private var cropHandleSize:Number = 10;
        private var cropRectMinimumSize:Number = 20;
        
        // Flag used to prevent a null object reference if updateDisplayList() is called by the Flex callLaterDispatcher() after destroy() is executed
        
        private var destroyed:Boolean = false;
        
        // Flag to indicate whether mouse listeners are active
        
        private var mouseListenersActive:Boolean = false;
        
        // Event constants
        
        /**
         * Constant value for the <code>sourceImageLoading</code> event.
         */
        
        public const SOURCE_IMAGE_LOADING:String = "sourceImageLoading";
        
        /**
         * Constant value for the <code>sourceImageLoadError</code> event.
         */
        
        public const SOURCE_IMAGE_LOAD_ERROR:String = "sourceImageLoadError";
        
        /**
         * Constant value for the <code>sourceImageReady</code> event.
         */

        public const SOURCE_IMAGE_READY:String = "sourceImageReady";

        /**
         * Constant value for the <code>cropRectChanged</code> event.
         */

        public const CROP_RECT_CHANGED:String = "cropRectChanged";

        /**
         * Constant value for the <code>cropConstraintChanged</code> event.
         */

        public const CROP_CONSTRAINT_CHANGED:String = "cropConstraintChanged";

        /**
         * Constant value for the <code>cropConstraintDisabled</code> event.
         */

        public const CROP_CONSTRAINT_DISABLED:String = "cropConstraintDisabled";
        
        /**
         * Constant value for the <code>cropPositionChanged</code> event.
         */

        public const CROP_POSITION_CHANGED:String = "cropPositionChanged";
                
        /**
         * Constant value for the <code>cropDimensionsChanged</code> event.
         */

        public const CROP_DIMENSIONS_CHANGED:String = "cropDimensionsChanged";        

        /**
         * The version number of the ImageCropper component.
         */

        public const VERSION:Number = 1.0;        
        
        // --------------------------------------------------------------------------------------------------
        // ImageCropper - Constructor
        // --------------------------------------------------------------------------------------------------

        /**
         * Class constructor.
         */
        
        public function ImageCropper() {
            
            // Initialize the superclass
                    
            super();
        }
        
        // --------------------------------------------------------------------------------------------------
        // destroy - Call this function when finished using the component to release resources for garbage collection
        // --------------------------------------------------------------------------------------------------

        /**
         * Call this method to dereference resources when the <code>ImageCropper</code> component is no longer needed. For example, if the <code>ImageCropper</code> component is
         * used in a pop-up window and the window is closed, call <code>destroy</code> when removing the window. 
         */
        
        public function destroy():void {
            
            // Set to prevent a null object reference if updateDisplayList() is called by the Flex callLaterDispatcher() after destroy() is executed
            
            destroyed = true;

            // Release memory used by Bitmaps and BitmapData objects
            
            if (componentBitmapData != null) {
                componentBitmapData.dispose();
                componentBitmapData = null;
                componentBitmap = null;
            }
            
            if (imageBitmapData != null) {
                imageBitmapData.dispose();
                imageBitmapData = null;
            }            
            
            if (scaledImageBitmapData != null) {
                scaledImageBitmapData.dispose();
                scaledImageBitmapData = null;
            }

            // Remove hand cursor if active
            
            if (handCursorID >= 0) {
                CursorManager.removeCursor(handCursorID);
                handCursorID = -1;
            }
            
            // Release any active listeners 
            
            activateListeners(false); 
        }
        
        // --------------------------------------------------------------------------------------------------
        // GET enabled - Returns whether or not the component is enabled
        // --------------------------------------------------------------------------------------------------

        /**
         * Whether the component can accept user interaction or changes to properties. If the <code>enabled</code> property is set to <code>false</code> then the cropping rectangle is removed
         * and all component properties become read-only (except for the <code>enabled</code> property). In addition, calls to the <code>setCropRect</code> method are ignored. 
         * 
         * <p>The component may be re-enabled by setting the <code>enabled</code> property to <code>true</code>. 
         * 
         * @default true
         */
                
        public override function get enabled():Boolean {
            
            return componentEnabled;
        }    

        // --------------------------------------------------------------------------------------------------
        // SET enabled - Sets whether or not the component is enabled
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */
        
        public override function set enabled(value:Boolean):void {
            
            // If the requested enabled state is different than the current enabled state
            
            if (componentEnabled != value) {
            
                // Call the super method
                
                super.enabled = value;
                    
                // Save the new enbled state
                
                componentEnabled = value;
                
                // If an image is being displayed, activate or deactive the mouse listeners depending on the new enabled state
                
                if (imageBitmapData != null) activateListeners(componentEnabled);
                
                // Remove hand cursor if active
                
                if (handCursorID >= 0) {
                    CursorManager.removeCursor(handCursorID);
                    handCursorID = -1;
                }                                
            
                // Schedule a redraw
            
                invalidateDisplayList();
            }
        }

        // --------------------------------------------------------------------------------------------------
        // GET backgroundColor - Returns the component's background color (behind the image)
        // --------------------------------------------------------------------------------------------------

        /**
         * The background color for the component. The component background will be visible only when an image does not entirely fill the component area.
         * 
         * @default 0x00000000
         */
                
        public function get backgroundColor():uint {
            return (bkgndColor & 0x00FFFFFF);
        }    

        // --------------------------------------------------------------------------------------------------
        // SET backgroundColor - Sets the component's background color (behind the image)
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */
        
        public function set backgroundColor(value:uint):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {

                bkgndColor = value;
                
                // Mask off high-order 8 bits and replace with the background alpha value
                
                bkgndColor &= 0x00FFFFFF;
                bkgndColor |= bkgndAlpha;
                
                // Schedule a redraw
                
                invalidateDisplayList();
            }
        }
                        
        // --------------------------------------------------------------------------------------------------
        // GET backgroundAlpha - Returns the alpha level for the component's background color
        // --------------------------------------------------------------------------------------------------

        /**
         * The level of transparency (0 to 1) for the component's background. The component background will be visible only when an image does not entirely fill the component area.
         * 
         * @default 0
         */
        
        public function get backgroundAlpha():Number {
            if (bkgndAlpha > 0) return (bkgndAlpha >> 48) / 100;
            else return 0;
        }

        // --------------------------------------------------------------------------------------------------
        // SET backgroundAlpha - Sets the alpha level for the component's background color
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set backgroundAlpha(value:Number):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {
    
                // Value must be between 0 and 1
                
                if (value > 1) value = 1;
                else if (value < 0) value = 0;
                
                // Convert the Number to an 8 bit value and position the bits as the high order 8 bits
                
                var a:uint = Math.round(value * 255);
                bkgndAlpha = (Math.round(value * 255)) << 24;
                
                // Set the high order 8 bits of the background color to the alpha value
                
                bkgndColor &= 0x00FFFFFF;
                bkgndColor |= bkgndAlpha;
                
                // Schedule a redraw
                
                invalidateDisplayList();
            }
        }
                
        // --------------------------------------------------------------------------------------------------
        // GET maskColor - Returns the color used to mask unselected crop area
        // --------------------------------------------------------------------------------------------------

        /**
         * The color of the mask used to indicate the non-selected portion of the cropped image. The mask will be visible only when the dimensions of the cropping rectangle are smaller than the dimensions of the image.
         * 
         * @default 0x00FF0000
         */

        public function get maskColor():uint {
            return (cropMaskColor & 0x00FFFFFF);
        }    

        // --------------------------------------------------------------------------------------------------
        // SET maskColor - Sets the color used to mask unselected crop area
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set maskColor(value:uint):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {
            
                cropMaskColor = value;
                
                // Mask off high-order 8 bits and replace with the mask alpha value
                
                cropMaskColor &= 0x00FFFFFF;
                cropMaskColor |= cropMaskAlpha;
                
                // Set a flag to indicate that a mask property has changed and schedule a property update
                
                cropMaskChanged = true;
                invalidateProperties();
            }
        }
                                
        // --------------------------------------------------------------------------------------------------
        // GET maskAlpha - Returns the alpha level for the unselected crop area
        // --------------------------------------------------------------------------------------------------

        /**
         * The level of transparency (0 to 1) for the mask that is used to indicate the non-selected portion of the cropped image. The mask will only be visible when the dimensions of the cropping rectangle are smaller than the dimensions of the image.
         * 
         * @default 0.3
         */
        
        public function get maskAlpha():Number {
            if (cropMaskAlpha > 0) return (cropMaskAlpha >> 48) / 100;
            else return 0;
        }                

        // --------------------------------------------------------------------------------------------------
        // SET maskAlpha - Sets the alpha level for the unselected crop area
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set maskAlpha(value:Number):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {
    
                // Value must be between 0 and 1
                
                if (value > 1) value = 1;
                else if (value < 0) value = 0;
                
                // Convert the Number to an 8 bit value and position the bits as the high order 8 bits
                
                var a:uint = Math.round(value * 255);
                cropMaskAlpha = (Math.round(value * 255)) << 24;
                
                // Set the high order 8 bits of the mask color to the alpha value
                
                cropMaskColor &= 0x00FFFFFF;
                cropMaskColor |= cropMaskAlpha;
                
                // Set a flag to indicate that a mask property has changed and schedule a property update
                
                cropMaskChanged = true;
                invalidateProperties();
            }
        }        

        // --------------------------------------------------------------------------------------------------
        // GET handleColor - Returns the color of the cropping handles
        // --------------------------------------------------------------------------------------------------

        /**
         * The color used for the four corner handles of the cropping rectangle.
         * 
         * @default 0x00FF0000
         */

        public function get handleColor():uint {
            return (cropHandleColor & 0x00FFFFFF);
        }    

        // --------------------------------------------------------------------------------------------------
        // SET handleColor - Sets the color of the cropping handles
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set handleColor(value:uint):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {
    
                cropHandleColor = value;
                
                // Mask off high-order 8 bits and replace with the crop handle alpha value
                
                cropHandleColor &= 0x00FFFFFF;
                cropHandleColor |= cropHandleAlpha;
                
                // Set a flag to indicate that a mask property has changed and schedule a property update
                
                cropMaskChanged = true;
                invalidateProperties();
            }
        }        
                                
        // --------------------------------------------------------------------------------------------------
        // GET handleAlpha - Returns the alpha level for the crop handles
        // --------------------------------------------------------------------------------------------------

        /**
         * The level of transparency (0 to 1) for the four corner handles of the cropping rectangle.
         * 
         * @default 0.5
         */

        public function get handleAlpha():Number {
            return cropHandleAlpha;
        }

        // --------------------------------------------------------------------------------------------------
        // SET handleAlpha - Sets the alpha level for the crop handles
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set handleAlpha(value:Number):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {

                // Value must be between 0 and 1
                
                if (value > 1) value = 1;
                else if (value < 0) value = 0;
                
                // Save the new alpha value
                
                cropHandleAlpha = value;
                
                // Set a flag to indicate that a mask property has changed and schedule a property update
                
                cropMaskChanged = true;
                invalidateProperties();
            }
        }
                                
        // --------------------------------------------------------------------------------------------------
        // GET outlineColor - Returns the color of the crop area outline
        // --------------------------------------------------------------------------------------------------

        /**
         * The color used for single pixel outline drawn around the cropping rectangle and around the four corner handles of the cropping rectangle.
         * 
         * @default 0x00FFFFFF
         */

        public function get outlineColor():uint {
            return cropSelectionOutlineColor;
        }
        
        // --------------------------------------------------------------------------------------------------
        // SET outlineColor - Sets the color of the crop area outline
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set outlineColor(value:uint):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {

                cropSelectionOutlineColor = value;
                
                // Set a flag to indicate that a mask property has changed and schedule a property update
                
                cropMaskChanged = true;
                invalidateProperties();
            }
        }        
        
        // --------------------------------------------------------------------------------------------------
        // GET outlineAlpha - Returns the alpha level for the crop area outline
        // --------------------------------------------------------------------------------------------------

        /**
         * The level of transparency (0 to 1) used for the single pixel outline drawn around the cropping rectangle and around the four corner handles of the cropping rectangle.
         * 
         * @default 0
         */

        public function get outlineAlpha():Number {
            return cropSelectionOutlineAlpha;
        }
                
        // --------------------------------------------------------------------------------------------------
        // SET outlineAlpha - Sets the alpha level for the crop area outline
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set outlineAlpha(value:Number):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {

                // Value must be between 0 and 1
                
                if (value > 1) value = 1;
                else if (value < 0) value = 0;
                
                // Save the new alpha value
                
                cropSelectionOutlineAlpha = value;
                
                // Set a flag to indicate that a mask property has changed and schedule a property update
                
                cropMaskChanged = true;
                invalidateProperties();
            }
        }
        
        // --------------------------------------------------------------------------------------------------
        // GET handleSize - Returns the size of the cropping handles
        // --------------------------------------------------------------------------------------------------

        /**
         * The size of each of the four corner handles of the cropping rectangle. The minimum allowed handle size is 3. 
         * 
         * @default 10
         */

        public function get handleSize():Number {
            return cropHandleSize;
        }
        
        // --------------------------------------------------------------------------------------------------
        // SET handleSize - Sets the size of the cropping handles (the minimum handle size is 3)
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set handleSize(value:Number):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {
    
                // Remove hand cursor if active
                
                if (handCursorID >= 0) {
                    CursorManager.removeCursor(handCursorID);
                    handCursorID = -1;
                }
                
                // Minimum habdle size is 3
                
                cropHandleSize = value < 3 ? 3 : value;
                
                // Set the minimum allowed size for the cropping rectangle to twice the size of a handle
                
                cropRectMinimumSize = value * 2;
                
                // Set a flag to indicate that a mask property has changed and schedule a property update
                
                cropMaskChanged = true;
                invalidateProperties();
            }
        }
        
        // --------------------------------------------------------------------------------------------------
        // GET constrainToAspectRatio - Returns whether or not the dimensions of the cropping rectangle are constrained to the ratio of cropping rectangle's current width and height
        // --------------------------------------------------------------------------------------------------

        /**
         * If set to <code>true</code> then the cropping rectangle will always maintain the aspect ratio that was active when the <code>constrainToAspectRatio</code> property was set.
         * For example, suppose the cropping rectangle has dimensions of 200 x 100 (an aspect ratio of 2:1). If <code>constrainToAspectRatio</code> is set to <code>true</code> then 
         * dragging any of the handles will cause the cropping rectangle to maintain a 2:1 relationship between the width and the height. If dragging a handle causes the width to increase 
         * to 400, then the height of the cropping rectangle will be adjusted to 200. If the height is changed to 50, then the width will be adjusted to 100.
         * 
         * <p>Enabling <code>constrainToAspectRatio</code> can be useful if you wish to crop an image so that it can be scaled to fixed dimensions without distortion. For example,
         * suppose that you want to crop an image so that it will exactly fit a target area that is 400 pixels wide and 600 pixels high. Since the target area has an aspect ratio of 2:3
         * you'll want to call the <code>setCropRect</code> method to set an initial cropping rectangle with dimensions that conform to the target aspect ratio. In this case, let's say that 
         * we set the cropping rectangle to a width of 80 and to a height of 120 (a ratio of 2:3). Now when we set <code>constrainToAspectRatio</code>
         * to <code>true</code> the cropping rectangle will always maintain a width to height ratio of 2:3. Once the portion of the image to crop is selected, all that needs to be
         * done is to retrieve the <code>BitmapData</code> from the <code>croppedBitmapData</code> parameter and then scale the <code>BitmapData</code> to the final size (400 x 600).<p>
         * 
         * Setting <code>constrainToAspectRatio</code> to <code>false</code> allows the width and height of the cropping rectangle to be adjusted independently. 
         * 
         * @default false
         */

        public function get constrainToAspectRatio():Boolean {
            return cropRatioActive;
        }
        
        // --------------------------------------------------------------------------------------------------
        // SET constrainToAspectRatio - Sets whether or not to constrain the dimensions of the cropping rectangle to the ratio of cropping rectangle's current width and height
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set constrainToAspectRatio(constrain:Boolean):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {
            
                // If constrainToAspectRatio is true and both the width and height of the cropping rectangle is non-zero, calculate the cropRatio
                
                if (constrain) {                
                    if (cropRect != null) cropRatio = cropRect.width / cropRect.height;
                    else cropRatio = 0;
                }
                else cropRatio = 0;
                
                // Set a flag to indicate if the cropping rectangle is constained to the aspect ratio
                
                cropRatioActive = constrain;
            }
        }        

        // --------------------------------------------------------------------------------------------------
        // GET sourceImage - Returns the source image as a BitmapData object
        // --------------------------------------------------------------------------------------------------

        /**
         * Either a <code>String</code> that contains a URL pointing to an image or a <code>BitmapData</code> object that contains an image.
         * 
         * <p>If a URL <code>String</code> is assigned to this parameter, then a <code>sourceImageLoading</code> event will be dispatched and the 
         * component will begin loading the referenced image. Once the image has been loaded then a <code>sourceImageReady</code> event will be dispatched
         * and the image will be displayed in the component. If an error occurs while loading the image, then a <code>sourceImageLoadError</code> event will be dispatched</p>
         * 
         * <p>If a <code>BitmapData</code> object is assigned to this parameter, then a <code>sourceImageReady</code> event will immediately be dispatched
         * and the image contained in the <code>BitmapData</code> object will be displayed in the component.</p>
         * 
         * <p>When reading this parameter an <code>Object</code> will be returned of type <code>String</code> or of type <code>BitmapData</code>, depending upon what type of
         * object was last assigned to the <code>sourceImage</code> parameter. If no assignment was made to the <code>sourceImage</code> parameter, then <code>null</code> is returned.</p>
         */

        public function get sourceImage():Object {
            return imageSource;
        }
                                
        // --------------------------------------------------------------------------------------------------
        // SET sourceImage - Sets the source image to be cropped
        // --------------------------------------------------------------------------------------------------

        /**
         * @private
         */

        public function set sourceImage(value:Object):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {
        
                // If a URL String or a BitmapData object has been passed
                
                if (value != null && (value is String || value is BitmapData)) {
                    
                    // Remove hand cursor if active
                    
                    if (handCursorID >= 0) {
                        CursorManager.removeCursor(handCursorID);
                        handCursorID = -1;
                    }                
                
                    // If an image has already been loaded, erase the image from the display and make sure listeners are disabled
                    
                    if (imageBitmapData != null) {
                        if (componentBitmap != null) componentBitmapData.fillRect(componentBitmapData.rect, bkgndColor);        
                        imageBitmapData.dispose();
                        imageBitmapData = null;
                        imageSource = null;
                        activateListeners(false);
                    }
    
                    // If a URL was passed
                    
                    if (value is String) {
                                                
                        // Create a loader for the requested image
                        
                        currentSource = new Loader();
                        
                        // Add listeners
                        
                        sourceLoadListeners(true);
    
                        // Begin loading the requested image
                        
                        currentSource.load(new URLRequest(String(value)));
                        
                        // Dispatch load event
                        
                        dispatchEvent(new Event(SOURCE_IMAGE_LOADING));                    
                    }
                    
                    // Else a BitmapData object was passed
                    
                    else {
                    
                        // Clone passed BitmapData for the image
                        
                        imageBitmapData = BitmapData(value).clone();
                        
                        // Set flag that new image has been loaded
                        
                        newImageLoaded = true;            
                        
                        // Mark the component so that updateDisplayList() is  called during the next screen update
            
                        invalidateDisplayList();
                        
                        // Save image source object
                    
                        imageSource = imageBitmapData;
                    
                        // Add event listeners
                        
                        activateListeners(true);
                        
                        // Dispatch image loaded notification event
                    
                        dispatchEvent(new Event(SOURCE_IMAGE_READY));                    
                    }
                }
            }
        }
        
        // --------------------------------------------------------------------------------------------------
        // GET croppedBitmapData - Returns the bitmap data for the cropped image at actual size
        // --------------------------------------------------------------------------------------------------

        /**
         * The cropped source image as a <code>BitmapData</code> object. The cropped portion of the source image is defined by the position and the dimensions of the cropping rectangle.
         */

        public function get croppedBitmapData():BitmapData {
        
            // If this function is called before the component has a chance to create componentBitmapData, then call initializeDisplay()
            
            if (componentBitmapData == null) initializeDisplay(this.width, this.height);
                    
            // If the image has not been rendered yet, create a scaled version of the image that will fit within the component
                    
            if (newImageLoaded) createScaledImage();        

            // If the cropping rectangle has changed but has not yet been initialized, then initialize the cropping rectangle before proceeding
            
            if (newCroppingRect) initializeCroppingRect();
            
            // Extract cropped area from image
            
            var croppedBitmap:BitmapData = null;
            var sourceImageRect:Rectangle = getCropRect();
            
            if (imageBitmapData != null && sourceImageRect != null && sourceImageRect.width > 0 && sourceImageRect.height > 0) {
                croppedBitmap = new BitmapData(sourceImageRect.width, sourceImageRect.height, false);
                croppedBitmap.copyPixels(imageBitmapData, sourceImageRect, new Point(0, 0));
            }
            
            return croppedBitmap;
        }
        
        // --------------------------------------------------------------------------------------------------
        // setCropRect - Set the initial crop area
        // --------------------------------------------------------------------------------------------------
        
        /**
         * This method defines the position and the dimensions of the cropping rectangle within the component.
         * 
         * <p>Note that values specified for the <code>width</code>, <code>height</code>, <code>x</code> and <code>y</code> parameters can be relative to either the component (if <code>componentRelative</code> is <code>true</code>)
         * or to the source image (if <code>componentRelative</code> is <code>false</code>).</p>
         * 
           * <p>For example, suppose the component has dimensions of 250x250 but the source image has dimensions of 500x500. In this case the component will automatically scale the source image so that it fits within
           * the component area. If <code>componentRelative</code> is set to <code>false</code> then setting a cropping rectangle with dimensions of 100x50 will result in a cropping rectangle being drawn in the component
           * area with dimensions of 50x25 (i.e. the cropping rectangle dimensions are relative to the source image). If <code>componentRelative</code> is set to <code>true</code> then the cropping rectangle will be drawn with 
           * dimensions of 100x50 (i.e. the cropping rectangle dimensions are relative to the component area).</p>
           * 
           * <p>If the <code>width</code> or the <code>height</code> parameter is assigned a value of zero, then the cropping rectangle will be set to the full size of the image and any values specified for 
           * the <code>x</code> and <code>y</code> parameters will be ignored.</p>
         * 
         * @param width Sets the width of the cropping rectangle. Setting <code>width</code> to zero will result in both the width and the height of the cropping rectangle being set to the size of the image displayed in the component.
         * 
         * @param height Sets the height of the cropping rectangle. Setting <code>height</code> to zero will result in both the width and the height of the cropping rectangle being set to the size of the image displayed in the component.
         * 
         * @param x Sets the horizontal position of the cropping rectangle. Setting <code>x</code> to -1 will result in the cropping rectangle being centered vertically and horizontally in the component.
         * 
         * @param y Sets the vertical position of the cropping rectangle. Setting <code>y</code> to -1 will result in the cropping rectangle being centered vertically and horizontally in the component.
         * 
         * @param componentRelative When set to <code>true</code> then the <code>width</code>, <code>height</code>, <code>x</code> and <code>y</code> parameters are relative to the image 
         * displayed in the component. When <code>componentRelative</code> is set to <code>false</code> then the parameters are relative to the source image. If the source image completely fits within
         * the component without scaling, then <code>componentRelative</code> in essence has no effect since the component image and the source image are identical.
         */

        public function setCropRect(Number = 0, height:Number = 0, x:Number = -1, y:Number = -1, componentRelative:Boolean = false):void {
            
            // If the ImageCropper component is enabled ...
            
            if (componentEnabled) {

                // Remove hand cursor if active
                
                if (handCursorID >= 0) {
                    CursorManager.removeCursor(handCursorID);
                    handCursorID = -1;
                }        
                
                // Make sure crop area values are not less than minimum allowed values
                
                if (width < 0) width = 0;
                if (height < 0) height = 0;
                if (x < -1) x = -1;
                if (y < -1) y = -1;
                
                // Save the initial position coordinates. If they are greater than or equal to zero then they are used to position the top-left corner of the cropping rectangle. 
                
                cropX = x;
                cropY = y;
                
                // Set the dimensions of the cropping rectangle
                
                cropWidth = width;
                cropHeight = height;
    
                // If either dimension is zero than the crop area will be set to the scaled image area. If the image has not yet been loaded the cropRatioActive will be set to false
                // because there is no way to detemine an aspect ratio.
    
                if (cropWidth == 0 || cropHeight == 0) {
                    
                    // If one dimension is zero, then set the other dimension to zero
                    
                    cropWidth = 0;
                    cropHeight = 0;
                    
                    // If the cropping rectangle is constrained to the aspect ratio
                    
                    if (cropRatioActive) {
                        
                        // If there is an image then set the cropping rectangle's dimensions equal to the scaled image size (the ratio is set below)
                        
                        if (scaledImageBitmapData != null) {
                            cropWidth = scaledImageBitmapData.width - 1;
                            cropHeight = scaledImageBitmapData.height - 1;
                        }
                        
                        // Else there is no image so there cannot be an aspect ratio constraint on the cropping rectangle
                        
                        else {
                            dispatchEvent(new Event(CROP_CONSTRAINT_DISABLED));
                            cropRatioActive = false;
                        }
                    }
                }
                
                // If componentRelative is set to true, then the cropping rectangle size will be set to the actual size specified (assuming it will fit within the image area).
                // If componentRelative is set to false, then the cropping rectangle size represents the size of the final image so the cropping rectangle will be scaled to match the scale of the selected image
                
                croppingRectIsImageScale = !componentRelative;
                
                // If constrainToAspectRatio is true and both the width and height of the cropping rectangle is non-zero, calculate the cropRatio
                
                if (cropRatioActive) cropRatio = cropWidth / cropHeight;
                else cropRatio = 0;
                
                // Set the "newCroppingRect" flag so that a BitMapData object will be constructed when updateDisplayList() is next called
                
                newCroppingRect = true;
                
                // Invalidate the display list in order to schedule a call to updateDisplayList()
                            
                invalidateDisplayList();
            }        
        }    
                
        // --------------------------------------------------------------------------------------------------
        // getCropRect - Return the selected crop area
        // --------------------------------------------------------------------------------------------------

        /**
         * Returns the position and the dimensions of the cropped portion of the image as a <code>Rectangle</code>.
         * 
         * @param componentRelative If set to <code>true</code> then the <code>Rectangle</code> returned represents the position and dimensions of the cropping rectangle in the component.
         * If set to <code>false</code> then the <code>Rectangle</code> returned represents the position and dimensions of the crop area in the source image.
         * 
         * @param roundValues If set to <code>true</code> then all values in the returned <code>Rectangle</code> are rounded to integer values.
         * 
         * @return The position and dimensions of the crop area relative to the component (if <code>componentRelative</code> is <code>true</code>) or relative to the source image 
         * (if <code>componentRelative</code> is <code>false</code>). If a cropping rectangle has not been defined, then <code>null</code> will be returned.
         */
        
        public function getCropRect(componentRelative:Boolean = false, roundValues:Boolean = false):Rectangle {
        
            // The cropRect must be defined
            
            if (cropRect != null) {
                
                var requestedRect:Rectangle;
            
                // If the position and size of the cropping area in the component is requested, return the current cropRect
                
                if (componentRelative) requestedRect = cropRect.clone();
                
                // Else the cropRect is translated back to unscaled coordinates and dimensions and returned
                
                else requestedRect = new Rectangle(cropRect.x / imageScaleFactor, cropRect.y / imageScaleFactor, cropRect.width / imageScaleFactor, cropRect.height / imageScaleFactor);
                
                // If rounding was requested, round each rectangle value
                
                if (roundValues) {
                    requestedRect.x = Math.round(requestedRect.x);
                    requestedRect.y = Math.round(requestedRect.y);
                    requestedRect.width = Math.round(requestedRect.width);
                    requestedRect.height = Math.round(requestedRect.height);
                }
                
                // Return the Rectangle
                
                return requestedRect; 
            }
            
            else return null;
        }                                
        
        // --------------------------------------------------------------------------------------------------
        // sourceLoadSuccess - Called when the source image has been loaded and initialized
        // --------------------------------------------------------------------------------------------------

        private function sourceLoadSuccess(event:Event):void {
            
            // Get the  bitmap data for the loaded image 
            
            imageBitmapData = Bitmap(currentSource.content).bitmapData.clone();
                        
            // Remove the event listeners for loading the image and release the Loader
            
            sourceLoadListeners(false);
            
            // Save image source object
                
            imageSource = imageBitmapData;
            
            // Set flag that new image has been loaded
            
            newImageLoaded = true;
            
            // Mark the component so that updateDisplayList() is  called during the next screen update

            invalidateDisplayList();
                            
            // Add event listeners
                    
            activateListeners(true);

            // Dispatch image loaded notification event
                
            dispatchEvent(new Event(SOURCE_IMAGE_READY));                    
        }        
        
        // --------------------------------------------------------------------------------------------------
        // sourceLoadFailure - Called when an error occurs loading the source image 
        // --------------------------------------------------------------------------------------------------

        private function sourceLoadFailure(event:IOErrorEvent):void {

            // Remove the event listeners
            
            sourceLoadListeners(false);

            // Dispatch image load error notification event
                
            dispatchEvent(new Event(SOURCE_IMAGE_LOAD_ERROR));                                
        }        
        
        // --------------------------------------------------------------------------------------------------
        // sourceLoadListeners - Adds or removes listeners to monitor the source image load process
        // --------------------------------------------------------------------------------------------------

        private function sourceLoadListeners(addListeners:Boolean):void {
                    
            // If listeners are being added
            
            if (addListeners) {
            
                // Request to be notified when the image has been loaded and initialized
                        
                currentSource.contentLoaderInfo.addEventListener(Event.INIT, sourceLoadSuccess);
                        
                // Request to be notified if an error occurs during the image load process
                        
                currentSource.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, sourceLoadFailure);
            }
            
            // Else release resources
            
            else {
                
                // Remove the listeners
                
                currentSource.contentLoaderInfo.removeEventListener(Event.INIT, sourceLoadSuccess);
                currentSource.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, sourceLoadFailure);
                
                // Release the loader
            
                try {
                    currentSource.close();
                }
                catch(e:Error) {};
                
                currentSource.unload();
                currentSource = null;                
            }    
        }        
                                
        // --------------------------------------------------------------------------------------------------
        // activateListeners - Called to assign or release listeners for the component (mouse listeners should only be active when a source image is assigned) 
        // --------------------------------------------------------------------------------------------------

        private function activateListeners(addListeners:Boolean):void {

            if (addListeners && !mouseListenersActive) {
                this.addEventListener(MouseEvent.MOUSE_DOWN, doMouseDown);
                this.addEventListener(MouseEvent.MOUSE_MOVE, doMouseMove);
                mouseListenersActive = true;
            }
            
            // Remove event listeners
            
            else if (!addListeners && mouseListenersActive) {
                this.removeEventListener(MouseEvent.MOUSE_MOVE, doMouseMove);
                this.removeEventListener(MouseEvent.MOUSE_DOWN, doMouseDown);
                this.removeEventListener(MouseEvent.MOUSE_UP, doMouseUp);
                systemManager.stage.removeEventListener(Event.MOUSE_LEAVE, doMouseUp);
                systemManager.stage.removeEventListener(MouseEvent.MOUSE_UP, doMouseUp);
                mouseListenersActive = false;
                if (currentSource != null) sourceLoadListeners(false);
            }        
        }
        
        // --------------------------------------------------------------------------------------------------
        // mouseLocation - Given a mouse event, returns -1 if the mouse is outside of the cropping rectangle, 1 if the mouse is in the top-left handle, 2 if the mouse is in the top-right handle,
        //                 3 if the mouse is in the bottom-left handle, 4 if the mouse is in the bottom-right handle, or 0 if the mouse is in the interior of the cropping rectangle
        // --------------------------------------------------------------------------------------------------

        private function mouseLocation(event:MouseEvent):int {
            
            // Get the location of the mouse relative to the image area
            
            var mouseXLoc:Number = event.localX - imageLocation.x;
            var mouseYLoc:Number = event.localY - imageLocation.y;
                        
            // If the cropping rectangle contains the mouse location point
            
            if (cropRect != null && cropRect.contains(mouseXLoc, mouseYLoc)) {
            
                // Calculate the distance between the top-left corner of the cropping rectangle to the mouse location
                
                var mouseDeltaX:Number = mouseXLoc - cropRect.x;
                var mouseDeltaY:Number = mouseYLoc - cropRect.y;
                
                // Top-left handle?
                
                if (mouseDeltaX <= cropHandleSize && mouseDeltaY <= cropHandleSize) return 1;
                
                // Top-right handle?
                
                else if (mouseDeltaX >= cropRect.width - cropHandleSize && mouseDeltaY <= cropRect.width && mouseDeltaY <= cropHandleSize) return 2;
                
                // Bottom-left handle?
                                
                else if (mouseDeltaX <= cropHandleSize && mouseDeltaY >= cropRect.height - cropHandleSize && mouseDeltaY <= cropRect.height) return 3;

                // Bottom-right handle?

                else if (mouseDeltaX >= cropRect.width - cropHandleSize && mouseDeltaY <= cropRect.height && mouseDeltaY >= cropRect.height - cropHandleSize && mouseDeltaY <= cropRect.height) return 4;
                
                // Cropping rectangle interior
                
                else return 0;
            }
            
            // The mouse is not in the cropping rectangle area
            
            else return -1;
        }                                
                
        // --------------------------------------------------------------------------------------------------
        // doMouseDown
        // --------------------------------------------------------------------------------------------------

        private function doMouseDown(event:MouseEvent):void {
            
            // Find out where the mouse down even occurred
            
            var mouseLoc:int = mouseLocation(event);            
            
            // If the cropping rectangle contains the mouse location point
            
            if (mouseLoc >= 0) {
            
                // Calculate the distance between the top-left corner of the cropping rectangle to the mouse location
                
                var mouseDeltaX:Number = event.localX - imageLocation.x - cropRect.x;
                var mouseDeltaY:Number = event.localY - imageLocation.y - cropRect.y;

                // Top-left handle?
                
                if (mouseLoc == 1) {
                    anchorX = mouseDeltaX;
                    anchorY = mouseDeltaY;                    
                    activeHandle = 1;
                }                    
                
                // Top-right handle?
                
                else if (mouseLoc == 2) {
                    anchorX = mouseDeltaX - cropRect.width;
                    anchorY = mouseDeltaY;    
                    activeHandle = 2;
                }                    
                
                // Bottom-left handle?
                                
                else if (mouseLoc == 3) {
                    anchorX = mouseDeltaX;
                    anchorY = mouseDeltaY - cropRect.height;    
                    activeHandle = 3;
                }

                // Bottom-right handle?

                else if (mouseLoc == 4) {
                    anchorX = mouseDeltaX - cropRect.width;
                    anchorY = mouseDeltaY - cropRect.height;    
                    activeHandle = 4;
                }
                
                // Cropping rectangle interior
                
                else {
                    anchorX = mouseDeltaX;
                    anchorY = mouseDeltaY;
                    activeHandle = 0;
                }
                
                // Add listener for mouse events
                
                this.addEventListener(MouseEvent.MOUSE_UP, doMouseUp);
                systemManager.stage.addEventListener(Event.MOUSE_LEAVE, doMouseExit);
                systemManager.stage.addEventListener(MouseEvent.MOUSE_UP, doMouseUp);
            }
        }
            
        // --------------------------------------------------------------------------------------------------
        // doMouseExit - Called when the mouse is outside of the Player area
        // --------------------------------------------------------------------------------------------------

        private function doMouseExit(event:Event):void {
            doMouseUp(null);
        }
        
        // --------------------------------------------------------------------------------------------------
        // doMouseUp
        // --------------------------------------------------------------------------------------------------

        private function doMouseUp(event:MouseEvent):void {
        
            // Clear active handle tracking variable
            
            activeHandle = -1;
            
            // Remove listeners
            
            this.removeEventListener(MouseEvent.MOUSE_UP, doMouseUp);
            systemManager.stage.removeEventListener(Event.MOUSE_LEAVE, doMouseUp);
            systemManager.stage.removeEventListener(MouseEvent.MOUSE_UP, doMouseUp);
            
            // Send event that the crop area has been updated 
            
            dispatchEvent(new Event(CROP_RECT_CHANGED));
        }        
                    
        // --------------------------------------------------------------------------------------------------
        // doMouseMove - Follow the mouse and move or resize the cropping rectangle
        // --------------------------------------------------------------------------------------------------

        private function doMouseMove(event:MouseEvent):void {
            
            // If a cropping rectangle has been set and the mouse button is down
            
            if (croppingRectBitmapData != null && event.buttonDown) {
                
                // Local variables
                
                var topX:Number;
                var topY:Number;
                var btmX:Number;
                var btmY:Number;
                
                var scaledW:Number;
                var scaledH:Number; 
            
                // Get the location of the mouse relative to the cropping rectangle bitmap
                
                var mouseX:Number = event.localX - imageLocation.x;
                var mouseY:Number = event.localY - imageLocation.y;
                
                // Drag cropping rectangle
                
                if (activeHandle == 0) {
                
                    // Calculate the new top-left corner of the cropping rectangle
                    
                    cropRect.x = mouseX - anchorX;
                    cropRect.y = mouseY - anchorY;
                    
                    // Make sure that the entire cropping rectangle stays within the image area
                    
                    if (cropRect.x < 0) cropRect.x = 0;
                    else {
                        var maxX:Number = Math.floor(imageScaledWidth - cropRect.width - 1);
                        if (cropRect.x > maxX) cropRect.x = maxX;
                    }
                    
                    if (cropRect.y < 0) cropRect.y = 0;
                    else {
                        var maxY:Number = Math.floor(imageScaledHeight - cropRect.height - 1);
                        if (cropRect.y > maxY) cropRect.y = maxY;
                    }
                }
                
                // Top-left Handle
                
                else if (activeHandle == 1) {
                    
                    // Get the new position of the top-left corner of the cropping rectangle
                    
                    topX = mouseX - anchorX;
                    topY = mouseY - anchorY;
                    
                    // If the new position of the top-left corner of the cropping rectangle is outside the top or left margin, then set the corner to the top or left margin
                    
                    if (topX < 0) topX = 0;
                    if (topY < 0) topY = 0;
                    
                    // Get the current position of the bottom-right corner of the cropping rectangle
                                        
                    btmX = cropRect.x + cropRect.width;
                    btmY = cropRect.y + cropRect.height;
                    
                    // Calculate the new width of the cropping rectangle
                    
                    cropRect.width = btmX - topX;
                    
                    // If the new width is less than the minimum allowed size then set the new width to the minimum
                    
                    if (cropRect.width < cropRectMinimumSize) {
                        cropRect.width = cropRectMinimumSize;
                        topX = btmX - cropRectMinimumSize;
                    }
                    
                    // Set the new x position for the top-left corner of the cropping rectangle
                    
                    cropRect.x = topX;

                    // Calculate the new height of the cropping rectangle
                    
                    cropRect.height = btmY - topY;
                    
                    // If the new height is less than the minimum allowed size then set the new height to the minimum
                    
                    if (cropRect.height < cropRectMinimumSize) {
                        cropRect.height = cropRectMinimumSize;
                        cropRect.y = btmY - cropRectMinimumSize;
                    }
                    else cropRect.y = topY;
                    
                    // If a cropping ratio is defined
                    
                    if (cropRatioActive) {
                        
                        // If the cropping rectangle is not at the correct ratio
                    
                        if (cropRect.width / cropRect.height != cropRatio) {
                            
                            // Get a new scaled width for the cropping rectangle
                            
                            scaledW = cropRect.height * cropRatio;
                            
                            // If the new width is less then the minimum allowed width
                            
                            if (scaledW < cropRectMinimumSize) {
                                
                                // Set the width to the minimum
                                
                                scaledW = cropRectMinimumSize;
                                
                                // Scale the height for the new width
                                
                                scaledH = scaledW / cropRatio;
                                
                                // Adjust the y coordinate of the cropping rectangle for the new scaled height
                                
                                cropRect.y = cropRect.y + (cropRect.height - scaledH);
                                
                                // Set the new height of the cropping rectangle
                                
                                cropRect.height = scaledH;
                            }
                            
                            // Move the horizontal position to compensate for the scaled width
                            
                            cropRect.x += cropRect.width - scaledW;
                            
                            // If the new width is to the left of the left margin
                            
                            if (cropRect.x < 0) {
                                
                                // Reduce the width of the cropping rectangle by the amount that the cropping rectangle extends past the left margin
                                
                                scaledW += cropRect.x;
                                
                                // Set the left edge of the cropping rectangle to the left margin
                                
                                cropRect.x = 0;

                                // Scale the height for the new width
                                
                                scaledH = scaledW / cropRatio;
                                
                                // Adjust the y coordinate of the cropping rectangle for the new scaled height
                                
                                cropRect.y = cropRect.y + (cropRect.height - scaledH);
                                
                                // Set the new height of the cropping rectangle
                                
                                cropRect.height = scaledH;
                            }
                            
                            // Set the scaled width for the cropping rectangle
                            
                            cropRect.width = scaledW;                                                        
                        }
                    }                    
                }
                
                // Top-right Handle
                
                else if (activeHandle == 2) {
                    
                    // Get the new position of the top-right corner of the cropping rectangle
                    
                    topX = mouseX - anchorX;
                    topY = mouseY - anchorY;
                                        
                    // Get the current position of the bottom-left corner of the cropping rectangle
                                        
                    btmX = cropRect.x;
                    btmY = cropRect.y + cropRect.height;
                    
                    // If the new position of the top-right corner of the cropping rectangle is outside the top or right margin, then set the corner to the top or right margin
                    
                    if (topX > imageScaledWidth - 1) topX = imageScaledWidth - 1;
                    if (topY < 0) topY = 0;
                    
                    // Calculate the new width of the cropping rectangle
                    
                    cropRect.width = topX - btmX;
                    
                    // If the new width is less than the minimum allowed size then set the new width to the minimum
                    
                    if (cropRect.width < cropRectMinimumSize) {
                        cropRect.width = cropRectMinimumSize;
                        topX = btmX + cropRectMinimumSize;
                    }
                    
                    // Set the new x position for the top-left corner of the cropping rectangle
                    
                    cropRect.x = btmX;
                    
                    // Calculate the new height of the cropping rectangle
                    
                    cropRect.height = btmY - topY;
                    
                    // If the new height is less than the minimum allowed size then set the new height to the minimum
                    
                    if (cropRect.height < cropRectMinimumSize) {
                        cropRect.height = cropRectMinimumSize;
                        cropRect.y = btmY - cropRectMinimumSize;
                    }
                    else cropRect.y = topY;
                    
                    // If a cropping ratio is defined
                    
                    if (cropRatioActive) {
                        
                        // If the cropping rectangle is not at the correct ratio
                    
                        if (cropRect.width / cropRect.height != cropRatio) {
                            
                            // Get a new scaled width for the cropping rectangle
                            
                            scaledW = cropRect.height * cropRatio;
                            
                            // If the new width is less then the minimum allowed width
                            
                            if (scaledW < cropRectMinimumSize) {
                                
                                // Set the width to the minimum
                                
                                scaledW = cropRectMinimumSize;
                                
                                // Scale the height for the new width
                                
                                scaledH = scaledW / cropRatio;
                                
                                // Adjust the y coordinate of the cropping rectangle for the new scaled height
                                
                                cropRect.y = cropRect.y + (cropRect.height - scaledH);
                                
                                // Set the new height of the cropping rectangle
                                
                                cropRect.height = scaledH;
                            }
                            
                            // If the new width exceeds the maximum allowed width
                            
                            if (cropRect.x + scaledW > imageScaledWidth - 1) {
                                
                                // Set the width to the maximum allowed
                                
                                scaledW = imageScaledWidth - 1 - cropRect.x;
                                
                                // Scale the height for the new width
                                
                                scaledH = scaledW / cropRatio;
                                
                                // Adjust the y coordinate of the cropping rectangle for the new scaled height
                                
                                cropRect.y = cropRect.y + (cropRect.height - scaledH);
                                
                                // Set the new height of the cropping rectangle
                                
                                cropRect.height = scaledH;
                            }
                            
                            // Set the scaled width for the cropping rectangle
                            
                            cropRect.width = scaledW;                            
                        }
                    }
                }
                
                // Bottom-left Handle
                
                else if (activeHandle == 3) {
                    
                    // Get the new position of the bottom-left corner of the cropping rectangle
                    
                    btmX = mouseX - anchorX;
                    btmY = mouseY - anchorY;

                    // If the new position of the bottom-left corner of the cropping rectangle is outside the bottom or left margin, then set the corner to the bottom or left margin
                    
                    if (btmX < 0) btmX = 0;
                    if (btmY > imageScaledHeight - 1) btmY = imageScaledHeight - 1;

                    // Get the current position of the top-right corner of the cropping rectangle
                                        
                    topX = cropRect.x + cropRect.width;
                    topY = cropRect.y;

                    // Calculate the new width of the cropping rectangle
                    
                    cropRect.width = topX - btmX;
                    
                    // If the new width is less than the minimum allowed size then set the new width to the minimum
                    
                    if (cropRect.width < cropRectMinimumSize) {
                        cropRect.width = cropRectMinimumSize;
                        btmX = topX - cropRectMinimumSize;
                    }
                    
                    // Set the new x position for the top-left corner of the cropping rectangle
                    
                    cropRect.x = btmX;
                    
                    // Calculate the new height of the cropping rectangle
                    
                    cropRect.height = btmY - topY;
                    
                    // If the new height is less than the minimum allowed size then set the new height to the minimum
                    
                    if (cropRect.height < cropRectMinimumSize) cropRect.height = cropRectMinimumSize;

                    // Set the new y position for the top-left corner of the cropping rectangle
                    
                    cropRect.y = topY;
                    
                    // If a cropping ratio is defined
                    
                    if (cropRatioActive) {
                        
                        // If the cropping rectangle is not at the correct ratio
                    
                        if (cropRect.width / cropRect.height != cropRatio) {
                            
                            // Get a new scaled width for the cropping rectangle
                            
                            scaledW = cropRect.height * cropRatio;
                            
                            // If the new width is less then the minimum allowed width
                            
                            if (scaledW < cropRectMinimumSize) {
                                
                                // Set the width to the minimum
                                
                                scaledW = cropRectMinimumSize;
                                
                                // Scale the height for the new width
                                
                                scaledH = scaledW / cropRatio;
                                                                
                                // Set the new height of the cropping rectangle
                                
                                cropRect.height = scaledH;
                            }
                            
                            // Move the horizontal position to compensate for the scaled width
                            
                            cropRect.x += cropRect.width - scaledW;
                            
                            // If the new width is to the left of the left margin
                            
                            if (cropRect.x < 0) {
                                
                                // Reduce the width of the cropping rectangle by the amount that the cropping rectangle extends past the left margin
                                
                                scaledW += cropRect.x;
                                
                                // Set the left edge of the cropping rectangle to the left margin
                                
                                cropRect.x = 0;

                                // Scale the height for the new width
                                
                                scaledH = scaledW / cropRatio;
                                
                                // Set the new height of the cropping rectangle
                                
                                cropRect.height = scaledH;
                            }
                            
                            // Set the scaled width for the cropping rectangle
                            
                            cropRect.width = scaledW;                                                        
                        }
                    }                    
                }
                
                // Bottom-right Handle                
                
                else if (activeHandle == 4) {
                    
                    // Get the new position of the bottom-left corner of the cropping rectangle
                    
                    btmX = mouseX - anchorX;
                    btmY = mouseY - anchorY;
                    
                    // If the new position of the bottom-right corner of the cropping rectangle is outside the bottom or right margin, then set the corner to the bottom or right margin
                    
                    if (btmX > imageScaledWidth - 1) btmX = imageScaledWidth - 1;
                    if (btmY > imageScaledHeight - 1) btmY = imageScaledHeight - 1;

                    // Get the current position of the top-left corner of the cropping rectangle
                                        
                    topX = cropRect.x;
                    topY = cropRect.y;
                    
                    // Calculate the new width of the cropping rectangle
                    
                    cropRect.width = btmX - topX;
                    
                    // If the new width is less than the minimum allowed size then set the new width to the minimum
                    
                    if (cropRect.width < cropRectMinimumSize) {
                        cropRect.width = cropRectMinimumSize;
                        btmX = topX + cropRectMinimumSize;
                    }
                                        
                    // Calculate the new height of the cropping rectangle
                    
                    cropRect.height = btmY - topY;
                    
                    // If the new height is less than the minimum allowed size then set the new height to the minimum
                    
                    if (cropRect.height < cropRectMinimumSize) cropRect.height = cropRectMinimumSize;
                    
                    // If a cropping ratio is defined
                    
                    if (cropRatioActive) {
                        
                        // If the cropping rectangle is not at the correct ratio
                    
                        if (cropRect.width / cropRect.height != cropRatio) {
                            
                            // Get a new scaled width for the cropping rectangle
                            
                            scaledW = cropRect.height * cropRatio;
                            
                            // If the new width is less then the minimum allowed width
                            
                            if (scaledW < cropRectMinimumSize) {
                                
                                // Set the width to the minimum
                                
                                scaledW = cropRectMinimumSize;
                                
                                // Scale the height for the new width
                                
                                scaledH = scaledW / cropRatio;
                                                                
                                // Set the new height of the cropping rectangle
                                
                                cropRect.height = scaledH;
                            }
                            
                            // If the new width exceeds the maximum allowed width
                            
                            if (cropRect.x + scaledW > imageScaledWidth - 1) {
                                
                                // Set the width to the maximum allowed
                                
                                scaledW = imageScaledWidth - 1 - cropRect.x;
                                
                                // Scale the height for the new width
                                
                                scaledH = scaledW / cropRatio;
                                                                
                                // Set the new height of the cropping rectangle
                                
                                cropRect.height = scaledH;
                            }
                            
                            // Set the scaled width for the cropping rectangle
                            
                            cropRect.width = scaledW;                            
                        }
                    }                    
                }        
                        
                // Render the cropping rectangle on top of the image
                
                drawCroppingRect();
                
                // Invalidate the display list to redraw the component
                
                invalidateDisplayList();                
            }
            
            // Else a handle is not active
            
            else {
                
                // If the cropping rectangle is smaller than the image dimensions and if the mouse is within the cropping rectangle but not in a handle then show the hand cursor
                
                if ((cropRect.width + 1 < Math.floor(imageScaledWidth) || cropRect.height + 1 < Math.floor(imageScaledHeight)) && mouseLocation(event) == 0) {
                    if (handCursorID < 0) handCursorID = CursorManager.setCursor(handCursor, 2, -6, -3);
                }
                
                // Else the mouse is in a handle or outside of the cropping rectangle
                
                else {
                    
                    // If the hand cursor is active, remove it
                    
                    if (handCursorID >= 0) {
                        CursorManager.removeCursor(handCursorID);
                        handCursorID = -1;
                    }                                        
                }
            }
        }
        
        // --------------------------------------------------------------------------------------------------
        // drawCroppingRect
        // --------------------------------------------------------------------------------------------------

        private function drawCroppingRect():void {
            
            // If the BitmapData object has been constructed, proceed to draw the cropping rectangle
            
            if (croppingRectBitmapData && componentEnabled) {
            
                // Clear the previous cropping rectangle by filling the bitmap with the mask color
                
                croppingRectBitmapData.fillRect(croppingRectBitmapData.rect, cropMaskColor);
                
                // Draw the transparent cropping rectangle area
                
                croppingRectBitmapData.fillRect(cropRect, 0x00FFFFFF);
                
                // Draw boarder around the cropping rectangle
                
                var border:Shape = new Shape();
                border.graphics.lineStyle(1, cropSelectionOutlineColor, cropSelectionOutlineAlpha);
                border.graphics.drawRect(cropRect.x, cropRect.y, cropRect.width, cropRect.height);
                croppingRectBitmapData.draw(border);    
                
                // Draw corner handles
                
                var handles:Shape = new Shape();
                handles.graphics.lineStyle(1, cropSelectionOutlineColor, cropSelectionOutlineAlpha);
                
                handles.graphics.beginFill(cropHandleColor, cropHandleAlpha);
                handles.graphics.drawRect(cropRect.x, cropRect.y, cropHandleSize, cropHandleSize);
                handles.graphics.endFill();
                
                handles.graphics.beginFill(cropHandleColor, cropHandleAlpha);
                handles.graphics.drawRect(cropRect.x + cropRect.width - cropHandleSize, cropRect.y, cropHandleSize, cropHandleSize);
                handles.graphics.endFill();
                
                handles.graphics.beginFill(cropHandleColor, cropHandleAlpha);
                handles.graphics.drawRect(cropRect.x, cropRect.y + cropRect.height - cropHandleSize, cropHandleSize, cropHandleSize);
                handles.graphics.endFill();
                
                handles.graphics.beginFill(cropHandleColor, cropHandleAlpha);
                handles.graphics.drawRect(cropRect.x + cropRect.width - cropHandleSize, cropRect.y + cropRect.height - cropHandleSize, cropHandleSize, cropHandleSize);
                handles.graphics.endFill();
                
                croppingRectBitmapData.draw(handles);
            }
        }        

        // --------------------------------------------------------------------------------------------------
        // commitProperties - Handle cropping rectangle property change (called by Flex when invalidateProperties is called after a change is made to a cropping rectangle property)
        // --------------------------------------------------------------------------------------------------
        
        /**
         * @private
         */
        
        override protected function commitProperties():void {
            
            super.commitProperties();
            
            // If a property for the cropping rectangle has been changed
            
            if (cropMaskChanged) {
                
                // Reset property changed flag
                
                cropMaskChanged = false;
                
                // Check the cropping rectangle size to make sure that there is enough space to display the handles with no overlap; if there isn't, then increase the size of the cropping rectangle.

                if (cropRect != null && (cropRectMinimumSize > cropRect.width || cropRectMinimumSize > cropRect.height)) {
                    
                    var origRect:Rectangle = cropRect.clone();
                        
                    if (cropRect.y > imageScaledHeight - 1 - cropRectMinimumSize) cropRect.y = imageScaledHeight - 1 - cropRectMinimumSize;
                    if (cropRect.x > imageScaledWidth - 1 - cropRectMinimumSize) cropRect.x = imageScaledWidth - 1 - cropRectMinimumSize;
                    if (cropRect.height < cropRectMinimumSize) cropRect.height = cropRectMinimumSize;
                    if (cropRect.width < cropRectMinimumSize) cropRect.width = cropRectMinimumSize;
                    
                    // If the aspect ratio is constrained, make sure that it doesn't change unless it's not possible to maintain the aspect ratio in the available space (given the current crop handle size)
                    
                    if (constrainToAspectRatio && cropRect.width != cropRect.height * cropRatio) {
                        
                        // Calculate what the new cropping rectangle width or the new cropping rectangle height will have to be in order to maintain the correct aspect ratio
                        // (the dimension that is not less than the cropping rectangle cropRectMinimumSize will be used)
                        
                        var newWidth:Number = cropRect.height * cropRatio;
                        var newHeight:Number = cropRect.width / cropRatio;
                        
                        // Increase width
                        
                        if (newWidth > cropRectMinimumSize) {
                            
                            // Set the cropping rectangle to the width that will maintain the aspect ratio
                            
                            cropRect.width = newWidth;
                            
                            // If the new width causes the crop box to extend beyond the right edge ...
                            
                            if (cropRect.x + cropRect.width > imageScaledWidth - 1) {
                                
                                // Find out how far off the edge the cropping rectangle extends
                                
                                var wDelta:Number = (cropRect.x + cropRect.width) - (imageScaledWidth - 1);
                                
                                // If there is room to the left of the cropping rectangle, adjust the left edge of the cropping rectangle so it fits in the available space
                                
                                if (cropRect.x - wDelta >= 0) cropRect.x = cropRect.x - wDelta;
                                
                                // Else there is no way to maintain the aspect ratio so extend the cropping rectangle from the left edge to the right edge, calculate the new aspect ratio, and dispatch an event indicating that the ratio has been altered
                                
                                else {
                                    cropRect.x = 0;
                                    cropRect.width = imageScaledWidth - 1;
                                    cropRatio = cropRect.width / cropRect.height;
                                    dispatchEvent(new Event(CROP_CONSTRAINT_CHANGED));
                                }
                            }
                        }
                        
                        // Increase height
                        
                        else {
                            
                            // Set the cropping rectangle to the height that will maintain the aspect ratio
                            
                            cropRect.height = newHeight;
                            
                            // If the new height causes the crop box to extend beyond the bottom edge ...
                            
                            if (cropRect.y + cropRect.height > imageScaledHeight - 1) {
                                
                                // Find out how far off the edge the cropping rectangle extends
                                
                                var hDelta:Number = (cropRect.y + cropRect.height) - (imageScaledHeight - 1);
                                
                                // If there is room above the cropping rectangle, adjust the top edge of the cropping rectangle so it fits in the available space
                                
                                if (cropRect.y - hDelta >= 0) cropRect.y = cropRect.y - hDelta;
                                
                                // Else there is no way to maintain the aspect ratio so extend the cropping rectangle from the top to the bottom, calculate the new aspect ratio, and dispatch an event indicating that the ratio has been altered
                                
                                else {
                                    cropRect.y = 0;
                                    cropRect.height = imageScaledHeight - 1;
                                    cropRatio = cropRect.width / cropRect.height;
                                    dispatchEvent(new Event(CROP_CONSTRAINT_CHANGED));
                                    
                                }
                            }                            
                        }
                    }

                    // Dispatch events if the size of the handles cause the position or dimensions of the cropping rectangle to change
                    
                    if (cropRect.x != origRect.x || cropRect.y != origRect.y) dispatchEvent(new Event(CROP_POSITION_CHANGED));
                    if (cropRect.width != origRect.width || cropRect.height != origRect.height) dispatchEvent(new Event(CROP_DIMENSIONS_CHANGED));
                }
                
                // Redraw the cropping rectangle area
                
                drawCroppingRect();
                
                // Schedule a display list update
                
                invalidateDisplayList();
            }
        }

        // --------------------------------------------------------------------------------------------------
        // measure - Sets the default component size and the component's minimum size in pixels 
        // --------------------------------------------------------------------------------------------------
        
        /**
         * @private
         */
        
        override protected function measure():void {
            
            super.measure();
            
            if (!isNaN(componentWidth) && !isNaN(componentHeight)) {
            
                // Set default measurements
                
                measuredWidth = componentWidth;
                measuredHeight = componentHeight;
                
                // Set optional minimum size measurements
                
                measuredMinWidth = componentWidth;
                measuredMinHeight = componentHeight;
            }            
        }    

        // --------------------------------------------------------------------------------------------------
        // updateDisplayList - This method is called to size and position the children of the component based on all previous property and style settings.
        //                     It also draws any skins or graphic elements that the component uses. Note that the parent container determines the size of the component itself.
        // --------------------------------------------------------------------------------------------------
                            
        /**
         * @private
         */

        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {

            // The Flex callLaterDispatcher() can call updateDisplayList() after this component's destroy() method has been called but before it is garbage collected.
            // When destroy() is called the "destroyed" flag is set to prevent a null object reference error if updateDisplayList() is subsequently called.
            
            if (!destroyed) {
            
                super.updateDisplayList(unscaledWidth, unscaledHeight);
                
                // If the dimensions of the component have changed, create a new bitmap to match the components new size
                
                if (unscaledWidth != componentWidth || unscaledHeight != componentHeight) initializeDisplay(unscaledWidth, unscaledHeight);
                
                // If an image is loaded
                
                if (imageBitmapData != null) {    
                    
                    // If the image has not been rendered yet, create a scaled version of the image that will fit within the component
                    
                    if (newImageLoaded) createScaledImage();
                                    
                    if (newCroppingRect) initializeCroppingRect();
                    
                    // Clear the current content
                    
                    componentBitmapData.fillRect(componentBitmapData.rect, bkgndColor);
                    
                    // Draw the scaled image in the component's display
                    
                    componentBitmapData.copyPixels(scaledImageBitmapData, scaledImageBitmapData.rect, imageLocation, null, null, true);
                    
                    // Draw the cropping overlay if the component is enabled
                    
                    if (componentEnabled && croppingRectBitmapData != null) componentBitmapData.copyPixels(croppingRectBitmapData, croppingRectBitmapData.rect, imageLocation, null, null, true);
                }
            }            
        }

        // --------------------------------------------------------------------------------------------------
        // initializeDisplay - Create the bitmap that represents the component's display
        // --------------------------------------------------------------------------------------------------
        
        private function initializeDisplay(newWidth:int, newHeight:int):void {
            
            if (newWidth > 0 && newHeight > 0) {
            
                // If a bitmap already exists (i.e., if the display area has been resized), remove the previous bitmap
    
                if (componentBitmap != null) {
                    removeChild(componentBitmap);
                    componentBitmapData.dispose();
                }
                
                // Create a Bitmap with smoothing enabled that matches the component's size and add it to the display list
                
                componentBitmapData = new BitmapData(newWidth, newHeight, true, bkgndColor);
                componentBitmap = new Bitmap(componentBitmapData);
                addChild(componentBitmap);
                
                // Save the current size of the component
                
                componentWidth = newWidth;
                componentHeight = newHeight;    
            }
        }        
                    
        // --------------------------------------------------------------------------------------------------
        // initializeCroppingRect
        // --------------------------------------------------------------------------------------------------
        
        private function initializeCroppingRect():void {
            
            // The cropping rectangle cannot be initialized unless the imageCropper component is enabled and createScaledImage() has been called
            
            if (componentEnabled && scaledImageBitmapData != null) {
                
                // Determine if cropping rectangle is to be centered
                
                var centerCropRect:Boolean = (cropX == -1) || (cropY == -1);
            
                // Clear the initialization flag that was set when newCroppingRect() was called
                
                newCroppingRect = false;
                
                // If a cropping rectangle was previously defined, dispose of the BitmapData
                
                if (croppingRectBitmapData != null) croppingRectBitmapData.dispose();
                
                // Create the BitmapData for the cropping rectangle (the display area should match the image's display area)
                
                croppingRectBitmapData = new BitmapData(scaledImageBitmapData.width, scaledImageBitmapData.height, true, 0xAA000000);
                
                // If the width or height is zero, then the cropping rectangle will be set to the size of the image
                
                if (cropWidth == 0 || cropHeight == 0) {
                    
                    // Set width and height to the size of the image
                    
                    cropWidth = scaledImageBitmapData.width - 1;
                    cropHeight = scaledImageBitmapData.height - 1;                    
            
                    // Save original cropping rectangle to determine if it had to be repositioned or resized in order to be valid
                
                    var origRect:Rectangle = new Rectangle(cropX, cropY, cropWidth, cropHeight);
                    
                    // If the crop ratio is active, calculate the ratio and save it as the original ratio (in case it has to be changed later in this function)
                    
                    if (cropRatioActive) {
                        cropRatio = cropWidth / cropHeight;
                        var origCropRatio:Number = cropRatio;
                    }
                }
                    
                // If croppingRectIsImageScale is true, then scale the cropping rectangle dimensions so that it is drawn at the same scale as the selected image
                
                else if (croppingRectIsImageScale) {
                    
                    // If the crop ratio is active, save it as the original ratio (in case it has to be changed later in this function)
                    
                    if (cropRatioActive) origCropRatio = cropRatio;
                    
                    // If a specific location is defined for the cropping rectangle, convert the X and Y coordinates

                    if (cropX >= 0 && cropY >= 0) {
                        cropX *= imageScaleFactor;
                        cropY *= imageScaleFactor;
                    }
                    
                    // Convert width and height of the cropping rectangle
                    
                    cropWidth *= imageScaleFactor;
                    cropHeight *= imageScaleFactor;
                    
                    // Save original cropping rectangle to determine if it had to be repositioned or resized in order to be valid
                
                    origRect = new Rectangle(cropX, cropY, cropWidth, cropHeight);                    
                    
                    // Make sure that the dimensions are not smaller than the size of the cropping rectangle handles
                    
                    if (cropWidth < cropRectMinimumSize) {
                        cropHeight = cropHeight * (cropRectMinimumSize / cropWidth)
                        cropWidth = cropRectMinimumSize;
                    }
                    
                    if (cropHeight < cropRectMinimumSize) {
                        cropWidth = cropWidth * (cropRectMinimumSize / cropHeight)
                        cropHeight = cropRectMinimumSize;
                    }
                }
                
                // Esle save original cropping rectangle to determine if it had to be repositioned or resized in order to be valid
                
                else origRect = new Rectangle(cropX, cropY, cropWidth, cropHeight);                    
                
                // If the cropping rectangle is positioned absolutely (i.e., cropX and cropY > -1)
                
                if (!centerCropRect) {
                                        
                    // If the absolute positioning causes the cropping rectangle to extend beyond the image area, try to reposition the cropping rectangle so that it touches rather than exceeds the image boundry

                    if (cropX + cropWidth + 1 > scaledImageBitmapData.width) cropX = scaledImageBitmapData.width - cropWidth - 1;
                    if (cropY + cropHeight + 1 > scaledImageBitmapData.height) cropY = scaledImageBitmapData.height - cropHeight - 1;
                    
                    // If the top or left-edge of the cropping rectangle extends beyond the top or left edge of the image area then decrease the height or width so that it fits within the image area
                    // If a dimension of the cropping rectangle cannot fit, then set that dimension equal to the width or height of the image.
                    
                    if (cropX < 0) {
                        cropWidth += cropX;
                        if (cropWidth <= 0) cropWidth = scaledImageBitmapData.width - 1;
                        cropX = 0;
                    }
                    
                    if (cropY < 0) {
                        cropHeight += cropY;
                        if (cropHeight <= 0) cropHeight = scaledImageBitmapData.height - 1;
                        cropY = 0;
                    }
                }
            
                // Make sure that neither dimension exceeds the image dimensions
                
                if (cropWidth + 1 > scaledImageBitmapData.width) {
                    cropWidth = scaledImageBitmapData.width - 1;
                    if (cropRatioActive) cropHeight = cropWidth / cropRatio;
                }
                
                if (cropHeight + 1 > scaledImageBitmapData.height) {
                    cropHeight = scaledImageBitmapData.height - 1;
                    if (cropRatioActive) cropWidth = cropHeight * cropRatio;
                }
                
                // If the cropping rectangle is not absolutely positioned, then center the cropping rectangle in the image area
                
                if (centerCropRect) {
                    cropX = (scaledImageBitmapData.width - cropWidth) / 2;
                    cropY = (scaledImageBitmapData.height - cropHeight) / 2;
                }
                                
                // If the absolute positioning causes the cropping rectangle to extend beyond the image area, try to reposition the cropping rectangle so that it touches rather than exceeds the image boundry

                if (cropX + cropWidth + 1 > scaledImageBitmapData.width) cropX = scaledImageBitmapData.width - cropWidth - 1;
                if (cropY + cropHeight + 1 > scaledImageBitmapData.height) cropY = scaledImageBitmapData.height - cropHeight - 1;
                
                // If adjusting cropX and cropY results in zero or negative space, reset the cropping rectangle to the image area
                
                if (scaledImageBitmapData.width - 1 <= cropX || scaledImageBitmapData.height - 1 <= cropY) {
                    cropX = 0;
                    cropY = 0;
                    cropWidth = scaledImageBitmapData.width - 1
                    cropHeight = scaledImageBitmapData.height - 1;
                }
                
                // Create the initial cropping rectangle centered in the display area
                
                cropRect = new Rectangle(cropX, cropY, cropWidth, cropHeight); 
                
                // Draw the initial cropping rectangle
                
                drawCroppingRect();
                
                // Dispatch events if the position, size, and/or aspect ratio of the cropping rectangle was changed
                
                if (!centerCropRect && (cropX != origRect.x || cropY != origRect.y)) dispatchEvent(new Event(CROP_POSITION_CHANGED));
                if (cropWidth != origRect.width || cropHeight != origRect.height) dispatchEvent(new Event(CROP_DIMENSIONS_CHANGED));
                if (cropRatioActive && !isNaN() && cropRatio != origCropRatio) dispatchEvent(new Event(CROP_CONSTRAINT_CHANGED));
            }            
        }
        
        // --------------------------------------------------------------------------------------------------
        // createScaledImage - Create a scaled version of the source image that will fit in the component's display area
        // --------------------------------------------------------------------------------------------------
        
        private function createScaledImage():void {

            if (imageBitmapData != null) {
                
                var imageWidth:Number = imageBitmapData.width;
                var imageHeight:Number = imageBitmapData.height;

                // Clear the flag that indicates that the image has not been processed yet by the createScaledImage() method
                
                newImageLoaded = false;                
                
                // Initialize the scaling factor to 1 (unscaled)
                
                imageScaleFactor = 1;
            
                // If the image size is larger than the component size
                
                if (imageWidth > componentWidth || imageHeight > componentHeight) {
                
                    // Determine the ratio of the size of the loaded image to the component's size
                    
                    var newXScale:Number = imageWidth == 0 ? 1 : componentWidth / imageWidth;
                    var newYScale:Number = imageHeight == 0 ? 1 : componentHeight / imageHeight;
                    
                    // Calculate the scaling factor based on which dimension must be scaled in order for the image to fit within the component
                    
                    var x:Number = 0;
                    var y:Number = 0;
                    
                    if (newXScale > newYScale) {
                        x = Math.floor((componentWidth - imageWidth * newYScale));
                        imageScaleFactor = newYScale;
                    }
                    else {
                        y = Math.floor((componentHeight - imageHeight * newXScale));
                        imageScaleFactor = newXScale;
                    }
                    
                    // Create a matrix to perform the image scaling
                    
                    var scaleMatrix:Matrix = new Matrix();
                    scaleMatrix.scale(imageScaleFactor, imageScaleFactor);
                    
                    // Calculate the scaled size of the image
                    
                    imageScaledWidth = Math.ceil(imageBitmapData.width * imageScaleFactor);
                    imageScaledHeight = Math.ceil(imageBitmapData.height * imageScaleFactor);
                    
                    // Calculate the new coordinates for the image so that it is centered within the component
                    
                    imageLocation = new Point(x - ((unscaledWidth - imageScaledWidth) / 2), y - ((unscaledHeight - imageScaledHeight) / 2))            
                    
                    // If there is a scaled BitmapData object from a previous image, dispose of the data
                    
                    if (scaledImageBitmapData != null) scaledImageBitmapData.dispose();
                    
                    // Create a new BitmapData object to hold the scaled image
                    
                    scaledImageBitmapData = new BitmapData(imageScaledWidth, imageScaledHeight, true, bkgndColor);
                    
                    // Create the scaled image (use smoothing)
                    
                    scaledImageBitmapData.draw(imageBitmapData, scaleMatrix, null, null, null, true);
                }
                
                // Else the image size is equal to or smaller than the component size
                
                else {
                    
                    // The scaled size is the actual size of the image
                    
                    imageScaledWidth = imageWidth;
                    imageScaledHeight = imageHeight;
                    
                    // Set the new coordinates for the image so that it is centered within the component
                    
                    imageLocation = new Point((componentWidth - imageWidth) / 2, (componentHeight - imageHeight) / 2);
                    
                    // The image is unscaled, so just clone the BitmapData
                    
                    scaledImageBitmapData = imageBitmapData.clone();                    
                }
            }            
        }
    }
}
原文地址:https://www.cnblogs.com/ffmpeg/p/4381183.html