Create an Image Cropping Application in Flash with ActionScript 3


In this Premium Tutorial, we'll learn how to manipulate Bitmaps in ActionScript 3 to produce a helpful image cropping application. Keep reading!

Final Result Preview

Image by Swami Stream licenced under Creative Commons.

Step 1: Overview

Using the Flash Tools we'll create an attractive interface that will be powered by several ActionScript 3 classes like MouseCursor, Bitmap, BitmapData, Rectangle, Point, Tween, FileReference and even external libraries.

The user will be able to crop an image multiple times to later select the best option and save it to disk.

Step 2: Set Up Flash

Open Flash and create a 600 pixels wide, 400 pixels tall document. Set the Frame rate to 24fps.

Step 3: Interface

A nice looking mac-like interface will power our code, this involves multiple timeline based buttons, custom cursors and more.

Continue to the next steps to learn how to create this GUI.

Step 4: Background

No science in the background, just create a 600x400px black rectangle and center it in the stage. You can also change the background color in the Stage Properties.

Step 5: Image

We will need an image to crop, select your prefered image and import it to the stage (Cmd + R).

Image by Swami Stream licenced under Creative Commons.

Step 6: Custom Cursor

We'll make a custom cursor to use it as feedback to the user, letting him know where he can crop.

Select the Rectangle Tool (R) and make a 3px wide, 17px tall white rectangle.

Duplicate the shape (Cmd+D) and rotate it 90 degrees to form a cross.

Repeat this process with a 1px wide, 15 px tall black rectangle.

Convert the final shape to MovieClip (F8) and set its instance name to cursor.

Step 7: Action Buttons

These buttons handle secondary actions based in the crop, these actions are:

  • Save: Saves the current crop.
  • Cancel: Returns the current crop to its position and brings back the original image.
  • Clear: Erases all the crops and brings back the original image.

Select the Rectangle Primitive Tool (R) and change the corner radius to 10, draw a 70x18px rectangle and fill it with #666666.

Duplicate the shape (Cmd + D), resize it to 68x16px and change its color to this linear gradient #222222 to #444444. Use the Gradient Transform Tool (F) to rotate the gradient.

Next, use the Text Tool (T) to write a label for the button. Use this format: Lucida Grande Regular, 12pt, #CCCCCC.

Use the same technique to create the Cancel and Clear buttons.

Step 8: Buttons Timeline

A well made GUI button has 4 states:

  • Up: The normal state of the button.
  • Over: The graphics shown when the Mouse Cursor is over the button.
  • Down: Shown when the Mouse is clicking the button.
  • Hit: The area where the mouse can click.

Convert your buttons to MovieClip and double click them to enter edit mode. Open the Timeline Panel (Window > Timeline).

You'll notice that the Up state is already created, these are the shapes you converted to MovieClip.

Press F6 to create a new Frame and change the center shape gradient fill to #222222, #5B5B5B. This will be the Over state.

For the Down state, select the center shape and flip it vertically (Modify > Tranform > Flip Vertical).

The Hit state is by default, the bigger shape area, as the clickable area we want is the whole button, there will be no need to create a frame for this.

Step 9: Position and Instance Names

When all your buttons are ready, exit edit mode and place them as shown in the following screenshot. The white text indicates the button instance name.

Step 10: Button Bar

A button bar will be used to place and highlight the crop button.

Use the Rectangle Tool to create a 600x50 px rectangle and fill it this gradient fill #AFAFAF to #C4C4C4. Align the bar in the bottom of the stage.

To create a embossed effect, draw two 600x1px lines and place them above the button bar, fill the top one with #858585 and the other with #D8D8D8.

Step 11: Crop Button

The Crop button will start the Main ActionScript function that will manipulate the bitmaps in the stage.

With the Rectangle Tool, create a 33x6px and a 26x6px rectangles and use them to form a right angle.

Duplicate the shape and rotate it 180 degrees to form a square.

Move the duplicated shape 5px up and 5px to the right.

Fill the resulting shape with this gradient fill: #444444 to #222222. Use the Gradient Transform Tool to rotate the fill.

You can add more detail by adding a shadow; duplicate the current shape and move it 1px down and 1px right.

Lastly, convert the shape to MovieClip and name it cropButton.

Step 12: Crop Shape

The crop shape is an image that will be used to indicate the area that will be cropped.

Select the Rectangle Tool and change the stroke color to white and the fill color to white with 1% alpha (this will help us drag the crop shape). Draw a 10x10px square and center it.

Select a 1px part of the square and change its color to black.

Duplicate the shape and do the same on all the side.

Repeat this process on all the sides.

Convert the result to MovieClip and name it cropShape, mark the Export for ActionScript box.

Double click it to enter edit mode, create a new Keyframe in frame 5 and rotate the shape 180 degrees.

Add 4 frames more and exit edit mode.

This ends the graphic part, let's start with ActionScript!

Step 13: New ActionScript Class

Create a new (Cmd + N) ActionScript 3.0 Class and save it as in your class folder.

Step 14: Package

The package keyword allows you to organize your code into groups that can be imported by other scripts, it's recommended to name them starting with a lowercase letter and use intercaps for subsequent words for example: myClasses. It's also common to name them using your company's website: com.mycompany.classesType.myClass.

In this example, we're using a single class, so there isn't really a need to create a classes folder.


Step 15: Import Directive

These are the classes we'll need to import for our class to work, the import directive makes externally defined classes and packages available to your code.

import flash.display.Sprite; 
import flash.ui.Mouse; 
import flash.ui.MouseCursor; 
import flash.display.BitmapData; 
import flash.display.Bitmap; 
import flash.geom.Rectangle; 
import flash.geom.Point; 
import fl.transitions.Tween; 
import fl.transitions.easing.Strong; 
import flash.utils.ByteArray; 
import com.adobe.images.JPGEncoder; 

Step 16: JPGEncoder

In order to save the image crop, we'll use a class which is part of the as3corelib. Follow the link and download the library.

Step 17: Declaring and Extending

Here we declare the class using the class definition keyword followed by the name that we want for the class, remember that you have to save the file using this name.

The extends keyword defines a class that is a subclass of another class. The subclass inherits all the methods, properties and functions, that way we can use them in our class.

public class Main extends Sprite 

Step 18: Variables

These are the variables we'll use, read the comments in the code to find out more about them.

private var cropShape:CropShape; //This is an instance of the cropShape we created before 
private var pointX:Number; //This var will hold the x position where the crop shape is started 
private var pointY:Number; //This var will hold the y position where the crop shape is started 
private var added:Boolean; //Checks if the crop shape is in stage 
private var images:Array = new Array(); //Stores the images crops 
private var bmd:BitmapData; //A bitmap data object, used to manipulate the main image data 
private var bmd2:BitmapData; //A second bitmap data object, this one will hold the data of the image we want to save 
private var croppedImage:Sprite; //Holds the cropped images 
private var cropThumb:int = 0; //The number of thumbs already created 
private var thumbPosX:Array = [5, 5, 5, 505, 505, 505]; //Stores the x positions of the thumbs 
private var thumbPosY:Array = [37, 135, 233, 37, 135, 233]; //Stores the y positions of the thumbs 
private var tween:Tween; //A tween object, the default animation library in flash 
private var fileReference:FileReference; //Used to save the file to disk 
private var byteArray:ByteArray; //Stores binary data, used to save the file to disk 
private var jpg:JPGEncoder = new JPGEncoder(); //The image encoder 
private var zoomed:Boolean; //Checks if the thumbnail is zoomed in or out 
private var currentCrop:Bitmap; //Holds the current zoomed thumbnail, the one that will be saved 
private var currentSprite:*; //Used to know what thumb is active 
private var latestX:Number; //Checks the latest x of the zoomed thumb 
private var latestY:Number; //Checks the latest y of the zoomed thumb

Step 19: Constructor

The constructor is a function that runs when an object is created from a class, this code is the first to execute when you make an instance of an object or runs using the Document Class.

public function Main():void 

Step 20: Initial Listeners

These are the listeners that will be defined at launch, they basically add Mouse related events to buttons.

image.addEventListener(MouseEvent.MOUSE_DOWN, getStartPoint); 
image.addEventListener(MouseEvent.MOUSE_UP, stopDrawing); 
cropButton.addEventListener(MouseEvent.MOUSE_UP, crop); 
image.addEventListener(MouseEvent.MOUSE_OVER, customCursor); 
image.addEventListener(MouseEvent.MOUSE_OUT, removeCustomCursor); 
saveButton.addEventListener(MouseEvent.MOUSE_UP, saveImage); 
cancelButton.addEventListener(MouseEvent.MOUSE_UP, cancel); 
clearButton.addEventListener(MouseEvent.MOUSE_UP, clearThumbs);

Step 21: Hide Buttons

This code hides the buttons that aren't in use at the moment.

saveButton.visible = false; 
cancelButton.visible = false; 
clearButton.visible = false; 
cursor.visible = false;

Step 22: Add Custom Cursor

The following function makes the custom cursor visible when the mouse pointer is over the main image.

private function customCursor(e:MouseEvent):void 
	cursor.visible = true; 

Step 23: Remove Custom Cursor

Hides the custom cursor when the mouse pointer leaves the main image.

private function removeCustomCursor(e:MouseEvent):void 
	cursor.visible = false;; 

Step 24: Get Start Point

A very important function, gets and stores the mouse click coordinates and places the cropShape movieclip in them, the current value will be used later to determine the size of the crop shape.

private function getStartPoint(e:MouseEvent):void 
	pointX = mouseX; 
	pointY = mouseY; 
	if (! added) 
		cropShape = new CropShape(); 
		cropShape.x = pointX; 
		cropShape.y = pointY; 
		added = true; 
		cropShape = new CropShape(); 
		cropShape.x = pointX; 
		cropShape.y = pointY; 

Step 25: Crop Mouse Listeners

Adds a listener in the main image to expand the crop shape on mouse movement and adds the listener to stop expanding on mouse up.

image.addEventListener(MouseEvent.MOUSE_MOVE, drawCropShape); 
cropShape.addEventListener(MouseEvent.MOUSE_UP, stopDrawing);

Step 26: Draw Crop Shape

Expands the cropShape according to the saved coordinates and the current ones.

private function drawCropShape(e:MouseEvent):void 
	cropShape.width = mouseX - pointX; 
	cropShape.height = mouseY - pointY; 

Step 27: Stop Crop Shape

Stops the crop shape expansion and adds some listeners that will be used by the next functions.

private function stopDrawing(e:MouseEvent):void 
	image.removeEventListener(MouseEvent.MOUSE_MOVE, drawCropShape); 
	cropShape.addEventListener(MouseEvent.MOUSE_OVER, showHandCursor); 
	cropShape.addEventListener(MouseEvent.MOUSE_OUT, hideHandCursor); 
	cropShape.addEventListener(MouseEvent.MOUSE_DOWN, dragCrop); 
	cropShape.addEventListener(MouseEvent.MOUSE_UP, stopDragCrop); 

Step 28: Show Hand Cursor

Uses the Mouse class to manipulate the mouse cursor, this code will show the Hand cursor when the mouse is over the crop shape.

private function showHandCursor(e:MouseEvent):void 
	Mouse.cursor = MouseCursor.HAND; 

Step 29: Hide Hand Cursor

Restores mouse cursor original behaviour.

private function hideHandCursor(e:MouseEvent):void 
	Mouse.cursor = MouseCursor.AUTO; 

Step 30: Drag Crop

The crop shape will be draggable, this will help select the area we want easily.

private function dragCrop(e:MouseEvent):void 

Step 31: Stop Drag Crop

Stops the drag on mouse up.

private function stopDragCrop(e:MouseEvent):void 

Step 32: Crop Function

This function will handle the image cropping, using the images bitmap data and the sprite class to create a series of thumbnails that will be elegible to save.

private function crop(e:MouseEvent):void 

Step 33: Bitmap Data

The next lines handle the bitmap data required to crop the image.

bmd = new BitmapData(image.width,image.height,false,0x00000000); //Creates a new bitmap data object using the size of the main image 
bmd.draw(image); //Copies the main image data to the instance 
bmd2 = new BitmapData(cropShape.width,cropShape.height,false,0x00000000); //Creates a new bitmap data using the actual size of the crop shape

Step 34: Copy Image Pixels

The next line copies the pixels of the main Image situated in the crop shape position to the bitmap data object bmp2.

The parameters work as listed below:

  • bmd: The source bitmap data to copy from, the main image
  • Rectangle: Specifies the position of the pixels to copy, the crop shape position
  • Point: Specifies the point to start copying, from the point 0 of the rectangle
bmd2.copyPixels(bmd, new Rectangle(cropShape.x - image.x, cropShape.y - image.y, cropShape.width, cropShape.height), new Point(0, 0));

Step 35: Create Thumbnail

We are going to let the user crop six times and save those previews at the side of the main image, for that, we create a new bitmap that will store the bitmap data of the cropped thumbnail and then we add it as a 90x70px sprite.

We also add a listener to zoom in and out the thumbnail.

if(cropThumb < 6) 
	var bmp:Bitmap = new Bitmap(bmd2); 
	var thumb:Sprite = new Sprite(); 
	thumb.width = 90; 
	thumb.height = 70; 
	thumb.addEventListener(MouseEvent.MOUSE_UP, zoomThumb); 

Step 36: Access Thumbnail

As we create a new sprite in every crop, the last sprite will be inaccesible using its instance name, time to use the array we created before.

This array will store the created thumbnails to access them outside the function.


Step 37: Tween Thumbnails

When the user presses the crop button a thumbnail is created using the cropped image, this thumbnail is later animated to the sides of the application depending on a variable.

The thumbnail position is determined by this variable and the thumbPos arrays.

tween = new Tween(thumb, "x", Strong.easeOut, cropShape.x, thumbPosX[cropThumb], 0.5, true); 
tween = new Tween(thumb, "y", Strong.easeOut, cropShape.y, thumbPosY[cropThumb], 0.5, true); 
cropThumb++; // Adds one to the number of thumbs

Step 38: Save Image

The next function is used to save the image to disk.

First, it uses the jpg.encode() method and saves the resulting data to the byte array, then the fileReference object is used to display a save dialog where the user can rename the file created (although a default name is set).

private function saveImage(e:MouseEvent):void 
	fileReference = new FileReference(); 
	byteArray = jpg.encode(currentCrop.bitmapData);, "Crop.jpg"); 
	fileReference.addEventListener(Event.COMPLETE, saveComplete); 

Step 39: Save Complete

Once the image is saved, the thumbnails are removed from the stage and from the array. The buttons are hidden and the main image returns.

private function saveComplete(e:Event):void 
	for(var i:int = 0; i < images.length; i++) 
	cropThumb = 0; 
	images = []; 
	tween = new Tween(image,"y",Strong.easeOut,image.y,stage.stageHeight / 2 - image.height / 2 - 30,0.5,true); 
	added = false; 
	saveButton.visible = false; 
	cancelButton.visible = false; 
	clearButton.visible = false; 

Step 40: Cancel

There is also a cancel button that will let you browse through the thumbnails without saving any of them.

When this button is pressed it returns all the thumbnails to postition and brings back the main image to crop more (if possible).

private function cancel(e:MouseEvent):void 
	tween = new Tween(currentSprite,"width",Strong.easeOut,currentSprite.width,90,0.5,true); 
	tween = new Tween(currentSprite,"height",Strong.easeOut,currentSprite.height,70,0.5,true); 
	tween = new Tween(currentSprite,"x",Strong.easeOut,currentSprite.x,latestX,0.5,true); 
	tween = new Tween(currentSprite,"y",Strong.easeOut,currentSprite.y,latestY,0.5,true); 
	tween = new Tween(image,"y",Strong.easeOut,image.y,stage.stageHeight / 2 - image.height / 2 - 30,0.5,true); 
	added = false; 
	saveButton.visible = false; 
	cancelButton.visible = false; 
	clearButton.visible = false; 
	cropButton.visible = true; 
	zoomed = false; 

Step 41: Clear Thumbnails

If the user has cropped the 6 times allowed by the app, and none of them is the expected result, there's always a way to crop even more times. This is the task that the clear button will achieve.

It removes all thumbnails from the stage and from the array, returns the main image to stage and hides the unused buttons.

private function clearThumbs(e:MouseEvent):void 
	for(var i:int = 0; i < images.length; i++) 
	cropThumb = 0; 
	images = []; 
	tween = new Tween(image,"y",Strong.easeOut,image.y,stage.stageHeight / 2 - image.height / 2 - 30,0.5,true); 
	added = false; 
	saveButton.visible = false; 
	cancelButton.visible = false; 
	clearButton.visible = false; 

Step 42: Document Class

We'll make use of the Document Class in this tutorial, if you don't know how to use it or are a bit confused please read this QuickTip.


The final result is a helpful Image Cropping application that teaches you how to manipulate bitmaps to modify them, and almost without knowing it, you also learned how to create and implement a very nice interface.

I hope you liked this tutorial, thank you for reading!

Related Posts