Home >
It seems that the number one request I get for development work is creating applications that do image manipulation or vector drawing or a combination of the two. This article is about my experiences in building applications in Flex to manipulate images. It will cover the basics of loading an image, saving a reference to it, adjusting color, applying pixel effects, changing its dimensions and orientation and ultimately saving these changes. The aim of this article is not to provide production ready solutions but instead to provide ideas for implementing image manipulation solutions in Flex.
The first thing you need to know about loading images into the Flash Player is the limits the Player has with respect to the maximum size of display objects and the maximum size of bitmap data. Currently the Flash Player has a hard coded limit as to the size of bitmap data you can create in the Flash Player (Flash Player 9 and earlier. I am hoping these limits are removed in Flash Player 10). If you create a new BitmapData object in ActionScript it cannot exceed a size of 2880 x 2880. These values are hard coded into the Flash Player and will result in an ‘Invalid BitmapData’ exception if they are exceeded.
For display objects there are no hard coded values (that I have been able to determine) but through testing I have discovered that if a display object exceeds 8191 x 8191 it won’t be rendered. This also applies to the dimensions of images being loaded. If the dimensions of the image exceed this size it will load but it will not be rendered on the display list. So theoretically you can load an image that is up to 8191 x 8191 but if you plan on accessing its bitmap data it can’t exceed 2880 x 2880. More on this below. One thing to keep in mind is that these limits (in the case of BitmapData anyways.) are related to memory allocation so the larger the image you load the more memory you will consume. Ultimately you are going to want to either limit the size of the image a user can load or scale the image down in your application so let’s look at this now.
For simple projects you could just load the image into an image tag and manipulate it. For more complex applications that require functionality like zoom, pan, multiple copies of same image ( for example a thumbnail and full size version) or non destructive editing a better strategy is to load the image with a Loader and store the bitmap data of the image in a variable. You can then use this data to create multiple bitmaps and apply different manipulations to each. This will greatly decrease the amount of memory used and create a faster more responsive application.
In the sample code below you will see how to load an image with the Loader class and store the bitmap data in a variable, scaling it down if necessary.
Let’s walk through the code.
We first create a variable to store the bitmap data of the image that we are loading. We then create 2 constants for the maximum width and height this bitmap data can be.
In the loadImage method we take the url of the image as a parameter, create a new URLRequest object with it. We then create a new Loader and attach an event listener to its complete event (other listeners can be added but we are only interested in this event right now). We then call the load method of the loader with the URLRequest as its parameter.
In the event handler we need to access the content property of the loader instance that loaded the image. The content in this case is a Bitmap object. Within a Bitmap there is a property called bitmapData which contains an array of pixel data for this Bitmap. This data is an instance of the BitmapData class.
Once we have a reference to the bitmapData we can check its dimensions and if they exceed our maximum size we can draw it to a new bitmapData object at a new size. To accomplish this we get the amount we need to scale and create a new BitmapData object at this new size. We then use the draw method of the BitmapData class to draw the loaded image to the new BitmapData object and use a matrix to provide the new scale. This BitmapData can now be stored in a variable and used to create as many bitmaps you need each with its own transformations. The transformations applied to you bitmaps don’t affect the original BitmapData so you can always use it as a reference. Now that we have a bitmap data instance that we can use let’s do something with it.
One of the things that you are probably going to want to do when manipulating an image is adjust its color. Within the Flash Player API’s there are a number of ways to do this but no matter which you choose what you are basically doing is changing the red, green and blue (and possibly alpha) values of the image. The two main ways to change the values is to either use a ColorTransform or a ColorMatrixFilter. Let’s look at each and how they differ and when to use each.
Using ColorTransform
ColorTransform universally adjust the values of the red, green and blue channels of a display object . Depending on whether you are manipulating a bitmap or a display object the color transform is applied differently. For a display object it is a property of the transform object. For bitmaps it is passed as a parameter to the colorTransform method. To adjust the color you either create a new instance of ColorTransform or get a reference to an existing one and change the red, green and blue values and/or offsets.
Adjusting brightness with a ColorTransform:
*to adjust brightness you change the offsets of the red, green, and blue channels equally
Using ColorMatrixFilter
The ColorMatrix filter uses a 4 x 5 matirx to adjust the values. Unlike the ColorTransform which universally adjusts the red, green and blue channels, the ColorMatrixFilter adjusts each pixel. The speed of the ColorMatrixFilter is proportional to the size of the image (the number of pixels to change).
Adjusting brightness with a ColorMatrixFilter:
or
No matter which you choose you have to remember that each pixel is broken down into its red, green, blue and alpha channels and the range for each channel is 0 to 255. Any values less than 0 will be set to 0 (0x00) and any values greater than 255 will be set to 255 (0xFF). With each of them you can apply multiple effects simultaneously but it is difficult to undo incremental changes made by concatenating multiple ColorTransform objects together. If you use the filter approach undoing one of your effects is as easy as removing it from the filters array and reapplying the array to the display object. With the filter approach you can also apply multiple filters by pushing each of them into the filters array and turning them off by removing them but the filter approach is slower if you have a larger image. More information on the differences can be found in the Flex documentation.
More examples of adjusting color
*Note - there are different formulas that can be used to calculate the values and offsets for these. The ones used here were taken from the Actionscript 3 Cookbook by Joey Lott, Darron Schall and Keith Peters.
Contrast
You adjust brightness by either scaling or offsetting the color values. You change contrast by changing both with all the values being equal and all of the offsets being equal. *value is a number between 0 and 1
Saturation
These are the constants for the luminance contrasts for the red, green and blue channels
Grey Scale
Apply a grey scale effect by red, green and blue values to their luminance contrast values.
Negative
Apply a negative effect by reversing the values of the matrix
Unlike other aspects of image manipulation there is really only one way to apply effects to your images, a ConvolutionFilter. Like the ColorMatrixFilter, the ConvolutionFilter also uses a matrix to change the image but in this case the matrix can be any size. There are a number of factors that affect performance and they are outlined in the Flex documentation. The most common matrix you will use is a 3x3 matrix. Applying effects is basically the same as applying color effects - you create a filter and add it to the filters array of the display object. Here are some common filters (the first two parameters are the dimensions of the matrix and the third is the matrix).
*the values I am using here are also taken from the Actionscript 3 cookbook
Embossing
Use a negative value in the center and opposite values on each side.
Edge Detection
Use a negative value in the center with symmetrical surrounding values
You can change the center value (-3) to apply more or less of an effect. The smaller the number the more of an effect is applied.
Sharpening
This is the opposite of the Edge Detection matrix - a positive value in the center with symmetrical negative values
The larger the center value the less drastic the effect.
There are other parameters and properties of the ConvolutionFilter that can give some very interesting effects so I encourage you to experiment with them.
Like color adjustments there are different ways to rotate and/ or flip (mirror) an image in the Flash Player. The first way would be to simply change the rotation, scaleX, and scaleY properties of the display object. This may be fine for simple applications but for more complex applications you will want to use a matrix to manipulate these properties. As noted in the in the Adjusting Color section, each display object has a property called ‘transform’ which contains all of the transformations applied to the display object. The ‘matrix’ property is used to move, rotate, scale and skew the display object. As an example let’s scale the image to twice its original size.
To use the matrix to make changes you get a reference to the matrix (you can’t change the matrix directly)
One thing of note - If you get a reference to the matrix and then make changes all of your changes will be applied onto any changes that have already been applied. In other words they are applied incrementally. As an example if the matrix for the display object already has a rotation of 30 and you apply a rotation of 10 the result will be a rotation of 40.If this is not your desired effect you can simply create a new Matrix and apply it instead of referencing the matrix that already exists.
The other reason a matrix is the better choice is that you can pass it as a parameter in bitmapData.draw. This allows you to make multiple changes and then using you original bitmap data you can draw a new image with all of these transformations.
If you look at the example under ‘Loading image data’ you will see we used a matrix to scale the image down after it was loaded.
Rotating
To use a matrix to rotate an image you either create a new Matrix with appropriate parameters
When you rotate something in the Flash Player it will rotate around its registration point. This by default is the top left corner. If you want to rotate it around a different point you will need to offset it in the negative direction, do the rotation and then put it back where it was.
Flipping
To flip and image is a 2 step process. The first step is to multiply the current scaleX and/or scaleY by -1 and the second is to adjust the x and y position. When you flip and image the registration point does not change and its drawn in the opposite direction. To compensate you will need to change the x position by its width and its y position by its height.
Like everything else we have looked at, there are different ways to change the area of an image that you see. You could change the x, y, width and height properties on the display object or a Rectangle to mark out the pixels you want to see.
There are a few reasons that changing the size of the display object is not the best solution. If the user zooms in enough to make the display object larger than 8191 x 8191 it simply will no longer be rendered. Also if you are applying filters with a matrix the new value of each pixel needs to be calculated which can have dire consequences on performance. You only want to apply you filters to the pixels that the user actually sees. The better approach is to redraw the area of the image that the user wants to see using bitmapData.draw and a matrix.
Since you are storing a reference to the original bitmap data of the image you can use a Rectangle to draw any area of that data onto a new bitmap and then use a matrix to scale it to fit the area of the screen it is displayed in.
When you first load the image you set the rectangle to the dimensions of the original bitmap data and store it in an instance variable
The properties of zoomArea can be updating if you want to zoom in or out or pan the image around. The rectangle can then be used to draw an area of the original bitmapData onto a new bitmap.
To pan the image around you can adjust the left and top position of the rectangle through its offset property.
To zoom in or out you adjust the size of the rectangle
Once you have made the adjustments you can then draw a new bitmapData with the dimensions of zoomArea using the copyPixels method of the bitmapData class. The copyPixels method takes a rectangle as the second parameter.
The copyPixles method is a fast way to copy bitmap data but it will reset any transformations you have made so these all need to be replied. You could also use the rectangle as the clipRect parameter of the bitmapData.draw method.
Once your user has made all of these changes they are probably going to want to print or save them. The Flex 3 SDK now contains a great way to capture these changes. In the mx.graphics package there is a class called ImageSnapshot and this can be used to capture the bitmapData of you image to print or create a jpeg or png to save. You can event use it to encode a byte stream to send to the server. I am not going to go too deeply into this but will mention that if you used a matrix to draw your bitmap then the resolution will now be at screen resolution regardless of what the dpi was before it was loaded. If you simply send the display object to the printer from the stage it will probably appear pixelatted. You will want to capture the display object with the ImageSnapshot.captureImage and scale it up to about 300 dpi to get a crisper image.
Image manipulation is a broad subject and there are a lot of different approaches you can take. It really comes down to the project requirements. I hope this article gave you some insight into the different approaches you can take. For maximum speed and performance in your application you should familiarize yourself with the Flash Player APIs mentioned in this article. They include Loader, Bitmap, BitmapData, Matrix, Rectangle, Math, ImageSnapshot, JPEGEncoder, PNGEncoder, PrintJob, FlexPrintJob and all of the bitmap filters.
Loading image data
The first thing you need to know about loading images into the Flash Player is the limits the Player has with respect to the maximum size of display objects and the maximum size of bitmap data. Currently the Flash Player has a hard coded limit as to the size of bitmap data you can create in the Flash Player (Flash Player 9 and earlier. I am hoping these limits are removed in Flash Player 10). If you create a new BitmapData object in ActionScript it cannot exceed a size of 2880 x 2880. These values are hard coded into the Flash Player and will result in an ‘Invalid BitmapData’ exception if they are exceeded.
For display objects there are no hard coded values (that I have been able to determine) but through testing I have discovered that if a display object exceeds 8191 x 8191 it won’t be rendered. This also applies to the dimensions of images being loaded. If the dimensions of the image exceed this size it will load but it will not be rendered on the display list. So theoretically you can load an image that is up to 8191 x 8191 but if you plan on accessing its bitmap data it can’t exceed 2880 x 2880. More on this below. One thing to keep in mind is that these limits (in the case of BitmapData anyways.) are related to memory allocation so the larger the image you load the more memory you will consume. Ultimately you are going to want to either limit the size of the image a user can load or scale the image down in your application so let’s look at this now.
For simple projects you could just load the image into an image tag and manipulate it. For more complex applications that require functionality like zoom, pan, multiple copies of same image ( for example a thumbnail and full size version) or non destructive editing a better strategy is to load the image with a Loader and store the bitmap data of the image in a variable. You can then use this data to create multiple bitmaps and apply different manipulations to each. This will greatly decrease the amount of memory used and create a faster more responsive application.
In the sample code below you will see how to load an image with the Loader class and store the bitmap data in a variable, scaling it down if necessary.
private var original:BitmapData;
private static const MAX_WIDTH:uint = 2880;
private static var MAX_HEIGHT:uint = 2880;
private function loadImage(url:String):void
{
var request:URLRequest = new URLRequest(url);
var imageLoader:Loader = new Loader();
imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, image_completeHandler);
// add other listeners here
imageLoader.load(request)
}
private function image_completeHandler(event:Event):void
{
var bmd:BitmapData = Bitmap(event.currentTarget.content).bitmapData;
var originalWidth:Number = bmd.width;
var originalHeight:Number = bmd.height;
var newWidth:Number = originalWidth;
var newHeight:Number = originalHeight;
var m:Matrix = new Matrix();
var scaleX:Number = 1;
var scaleY:Number = 1;
if (originalWidth > MAX_WIDTH || originalHeight > MAX_HEIGHT)
{
sx = MAX_WIDTH / originalWidth;
sy = MAX_HEIGHT / originalHeight;
var scale:Number = Math.min(sx, sy);
newWidth = originalWidth * scale;
newHeight = originalHeight * scale;
}
m.scale(scale, scale);
original = new BitmapData( newWidth, , newHeight);
original.draw(bmd, m);
}
Let’s walk through the code.
We first create a variable to store the bitmap data of the image that we are loading. We then create 2 constants for the maximum width and height this bitmap data can be.
In the loadImage method we take the url of the image as a parameter, create a new URLRequest object with it. We then create a new Loader and attach an event listener to its complete event (other listeners can be added but we are only interested in this event right now). We then call the load method of the loader with the URLRequest as its parameter.
In the event handler we need to access the content property of the loader instance that loaded the image. The content in this case is a Bitmap object. Within a Bitmap there is a property called bitmapData which contains an array of pixel data for this Bitmap. This data is an instance of the BitmapData class.
Once we have a reference to the bitmapData we can check its dimensions and if they exceed our maximum size we can draw it to a new bitmapData object at a new size. To accomplish this we get the amount we need to scale and create a new BitmapData object at this new size. We then use the draw method of the BitmapData class to draw the loaded image to the new BitmapData object and use a matrix to provide the new scale. This BitmapData can now be stored in a variable and used to create as many bitmaps you need each with its own transformations. The transformations applied to you bitmaps don’t affect the original BitmapData so you can always use it as a reference. Now that we have a bitmap data instance that we can use let’s do something with it.
Adjusting Color
One of the things that you are probably going to want to do when manipulating an image is adjust its color. Within the Flash Player API’s there are a number of ways to do this but no matter which you choose what you are basically doing is changing the red, green and blue (and possibly alpha) values of the image. The two main ways to change the values is to either use a ColorTransform or a ColorMatrixFilter. Let’s look at each and how they differ and when to use each.
Using ColorTransform
ColorTransform universally adjust the values of the red, green and blue channels of a display object . Depending on whether you are manipulating a bitmap or a display object the color transform is applied differently. For a display object it is a property of the transform object. For bitmaps it is passed as a parameter to the colorTransform method. To adjust the color you either create a new instance of ColorTransform or get a reference to an existing one and change the red, green and blue values and/or offsets.
Adjusting brightness with a ColorTransform:
*to adjust brightness you change the offsets of the red, green, and blue channels equally
var ct:ColorTransform = new ColorTransform();
ct.redOffset = value;
ct.blueOffset = value;
ct.greenOffset = value;
image.transform.colorTransform = ct; // apply the transform to a display object
Using ColorMatrixFilter
The ColorMatrix filter uses a 4 x 5 matirx to adjust the values. Unlike the ColorTransform which universally adjusts the red, green and blue channels, the ColorMatrixFilter adjusts each pixel. The speed of the ColorMatrixFilter is proportional to the size of the image (the number of pixels to change).
Adjusting brightness with a ColorMatrixFilter:
var cmf:ColorMatrixFilter = new ColorMatrixFilter( [ red, green, blue,0, 0, red, green, blue, 0, 0, red,green, blue, 0, 0, 0, 0, 0, 1, 0 ]);
var filtersArray:Array = new Array();
filtersArray.push(cmf);
image.filters = filtersArray;
var matrix:Array = new Array();
matrix = matrix.concat([1, 0, 0, 0, value]); // red
matrix = matrix.concat([0, 1, 0, 0, value]); // green
matrix = matrix.concat([0, 0, 1, 0, value]); // blue
matrix = matrix.concat([0, 0, 0, 1, 0]); // alpha
var cmf:ColorMatrixFilter = new ColorMatrixFilter(matrix);
var filtersArray:Array = new Array();
filtersArray.push(cmf);
image.filters = filtersArray;
No matter which you choose you have to remember that each pixel is broken down into its red, green, blue and alpha channels and the range for each channel is 0 to 255. Any values less than 0 will be set to 0 (0x00) and any values greater than 255 will be set to 255 (0xFF). With each of them you can apply multiple effects simultaneously but it is difficult to undo incremental changes made by concatenating multiple ColorTransform objects together. If you use the filter approach undoing one of your effects is as easy as removing it from the filters array and reapplying the array to the display object. With the filter approach you can also apply multiple filters by pushing each of them into the filters array and turning them off by removing them but the filter approach is slower if you have a larger image. More information on the differences can be found in the Flex documentation.
More examples of adjusting color
*Note - there are different formulas that can be used to calculate the values and offsets for these. The ones used here were taken from the Actionscript 3 Cookbook by Joey Lott, Darron Schall and Keith Peters.
Contrast
You adjust brightness by either scaling or offsetting the color values. You change contrast by changing both with all the values being equal and all of the offsets being equal. *value is a number between 0 and 1
var a:Number = value * 11;
var b:Number = 63.5 - (value * 698.5);
redValue = greenValue = blueValue = a;
redOffset = greenOffset = blueOffset = b;
var cmf:ColorMatrixFilter = new ColorMatrixFilter(a, 0, 0, 0, b, 0, a, 0, 0, b, 0, 0, a, 0, b, 0, 0, 0, 1, 0);
Saturation
These are the constants for the luminance contrasts for the red, green and blue channels
var red:Number = 0.3086; // luminance contrast value for red
var green:Number = 0.694; // luminance contrast value for green
var blue:Number = 0.0820; // luminance contrast value for blue
var a:Number = (1-value) * red + value;
var b:Number = (1-value) * green;
var c:Number = (1-value) * blue;
var d:Number = (1-value) * red;
var e:Number = (1-value) * green + value;
var f:Number = (1-value) * blue;
var g:Number = (1-value) * red ;
var h:Number = (1-value) * green;
var i:Number = (1-value) * blue + value;
var cmf:ColorMatrixFilter = new ColorMatrixFilter(a, b, c, 0, 0, d, e, f, 0, 0, g, h, i, 0 ,0, 0, 0, 0, 1, 0);
Grey Scale
Apply a grey scale effect by red, green and blue values to their luminance contrast values.
var red:Number = 0.3086; // luminance contrast value for red
var green:Number = 0.694; // luminance contrast value for green
var blue:Number = 0.0820; // luminance contrast value for blue
var cmf:ColorMatrixFilter = new ColorMatrixFilter(red, green, blue, 0, 0, red, green, blue, 0, 0, red, green, blue, 0, 0, 0, 0, 0, 1, 0);
Negative
Apply a negative effect by reversing the values of the matrix
var cmf:ColorMatrixFilter = new ColorMatrixFilter(-1, 0, 0, 0, 255, 0, -1, 0, 0, 255, 0, 0, -1, 0, 255, 0, 0, 0, 1, 0);
Applying Effects
Unlike other aspects of image manipulation there is really only one way to apply effects to your images, a ConvolutionFilter. Like the ColorMatrixFilter, the ConvolutionFilter also uses a matrix to change the image but in this case the matrix can be any size. There are a number of factors that affect performance and they are outlined in the Flex documentation. The most common matrix you will use is a 3x3 matrix. Applying effects is basically the same as applying color effects - you create a filter and add it to the filters array of the display object. Here are some common filters (the first two parameters are the dimensions of the matrix and the third is the matrix).
*the values I am using here are also taken from the Actionscript 3 cookbook
Embossing
Use a negative value in the center and opposite values on each side.
var emboss:ConvolutionFilter = new ConvolutionFilter(3, 3, [-2, -1, 0, -1, 1, 1, 0, 1, 2])
Edge Detection
Use a negative value in the center with symmetrical surrounding values
var edge:ConvolutionFilter = new ConvolutionFilter(3, 3, [0, 1, 0, 1, -3, 1, ,0, 1, 0])
Sharpening
This is the opposite of the Edge Detection matrix - a positive value in the center with symmetrical negative values
var sharpen:ConvolutionFilter = new ConvolutionFilter(3, 3, [0, -1, 0, -1, 5, -1, 0, -1, 0]);
The larger the center value the less drastic the effect.
There are other parameters and properties of the ConvolutionFilter that can give some very interesting effects so I encourage you to experiment with them.
Rotating and Flipping
Like color adjustments there are different ways to rotate and/ or flip (mirror) an image in the Flash Player. The first way would be to simply change the rotation, scaleX, and scaleY properties of the display object. This may be fine for simple applications but for more complex applications you will want to use a matrix to manipulate these properties. As noted in the in the Adjusting Color section, each display object has a property called ‘transform’ which contains all of the transformations applied to the display object. The ‘matrix’ property is used to move, rotate, scale and skew the display object. As an example let’s scale the image to twice its original size.
To use the matrix to make changes you get a reference to the matrix (you can’t change the matrix directly)
var m:Matrix = image.transform.matrix;
//make your changes (scale it in the x and y direction by a factor of 2)
m.scale(2, 2);
//and reapply the matrix to the display object
image.transform.matrix = m;
One thing of note - If you get a reference to the matrix and then make changes all of your changes will be applied onto any changes that have already been applied. In other words they are applied incrementally. As an example if the matrix for the display object already has a rotation of 30 and you apply a rotation of 10 the result will be a rotation of 40.If this is not your desired effect you can simply create a new Matrix and apply it instead of referencing the matrix that already exists.
The other reason a matrix is the better choice is that you can pass it as a parameter in bitmapData.draw. This allows you to make multiple changes and then using you original bitmap data you can draw a new image with all of these transformations.
If you look at the example under ‘Loading image data’ you will see we used a matrix to scale the image down after it was loaded.
Rotating
To use a matrix to rotate an image you either create a new Matrix with appropriate parameters
var q:Number = 30 * Math.PI / 180 // 30 degrees in radians
var m:Matrix = new Matrix(Math.cos(q), Math.sin(q), -1 * Math.sin(q), Math.cos(q));
//or as a shortcut use the rotate method
var m:Matrix = new Matrix();
m.rotate(q) ;
When you rotate something in the Flash Player it will rotate around its registration point. This by default is the top left corner. If you want to rotate it around a different point you will need to offset it in the negative direction, do the rotation and then put it back where it was.
var m:Matrix = new Matrix();
// rotate around the center of the image
var centerX:Number = image.width / 2;
var centerY:Number = image.height /2;
m.translate(-1 * centerX, -1 * centerY);
m.rotate(q);
m.translate(centerX, centrerY);
Flipping
To flip and image is a 2 step process. The first step is to multiply the current scaleX and/or scaleY by -1 and the second is to adjust the x and y position. When you flip and image the registration point does not change and its drawn in the opposite direction. To compensate you will need to change the x position by its width and its y position by its height.
var m:Matrix = new Matrix();
m.scale(-1, 0); // flip horizontal assuming that scaleX is currently 1
m.translate(image.width, 0); // move its x position by its width to put it in the upper left corner again
Cropping, Panning and Zooming
Like everything else we have looked at, there are different ways to change the area of an image that you see. You could change the x, y, width and height properties on the display object or a Rectangle to mark out the pixels you want to see.
There are a few reasons that changing the size of the display object is not the best solution. If the user zooms in enough to make the display object larger than 8191 x 8191 it simply will no longer be rendered. Also if you are applying filters with a matrix the new value of each pixel needs to be calculated which can have dire consequences on performance. You only want to apply you filters to the pixels that the user actually sees. The better approach is to redraw the area of the image that the user wants to see using bitmapData.draw and a matrix.
Since you are storing a reference to the original bitmap data of the image you can use a Rectangle to draw any area of that data onto a new bitmap and then use a matrix to scale it to fit the area of the screen it is displayed in.
When you first load the image you set the rectangle to the dimensions of the original bitmap data and store it in an instance variable
var zoomArea:Rectangle
zoomArea = original.rect // rect is a property of bitmapData containing the rectangle of the data
The properties of zoomArea can be updating if you want to zoom in or out or pan the image around. The rectangle can then be used to draw an area of the original bitmapData onto a new bitmap.
To pan the image around you can adjust the left and top position of the rectangle through its offset property.
zoomArea.offset(panX, panY);
To zoom in or out you adjust the size of the rectangle
zoomArea.inflate(newWidth, newHeight);
Once you have made the adjustments you can then draw a new bitmapData with the dimensions of zoomArea using the copyPixels method of the bitmapData class. The copyPixels method takes a rectangle as the second parameter.
var newBitmapData:BitmapData = new BitmapData(zoomArea.width, zoomArea.height)
newBitmapData.copyPixels(original, zoomArea, new Point(0, 0));
The copyPixles method is a fast way to copy bitmap data but it will reset any transformations you have made so these all need to be replied. You could also use the rectangle as the clipRect parameter of the bitmapData.draw method.
Printing and Encoding
Once your user has made all of these changes they are probably going to want to print or save them. The Flex 3 SDK now contains a great way to capture these changes. In the mx.graphics package there is a class called ImageSnapshot and this can be used to capture the bitmapData of you image to print or create a jpeg or png to save. You can event use it to encode a byte stream to send to the server. I am not going to go too deeply into this but will mention that if you used a matrix to draw your bitmap then the resolution will now be at screen resolution regardless of what the dpi was before it was loaded. If you simply send the display object to the printer from the stage it will probably appear pixelatted. You will want to capture the display object with the ImageSnapshot.captureImage and scale it up to about 300 dpi to get a crisper image.
Where to go from here
Image manipulation is a broad subject and there are a lot of different approaches you can take. It really comes down to the project requirements. I hope this article gave you some insight into the different approaches you can take. For maximum speed and performance in your application you should familiarize yourself with the Flash Player APIs mentioned in this article. They include Loader, Bitmap, BitmapData, Matrix, Rectangle, Math, ImageSnapshot, JPEGEncoder, PNGEncoder, PrintJob, FlexPrintJob and all of the bitmap filters.




Facebook Application Development
That's so interesting because I'm working on an AIR project where user could manipulate images with this apps.
An interesting thing that you could share your experience is how to create a preview with jpegencoder image to allow user to try any quality to optimaze image.
Do you have any suggestions or experience?
Thank you in advance
would be great if I could get Flex to actually load an image so I could follow this article -"A conflict exists with inherited definition mx.core:Application.url in namespace public." - anybody elese seen this problem?
martin:
That error is generated whenever you declare a variable in a sub class that has the same name as one inherited from a super class. Application already has a variable named 'url' that is public so you can't create another one in any class that extends Application. Renaming your variable will fix the problem.
luca:
I am not sure how you plan on implementing this and I have personally not done this yet but it wouldn't be too difficult to implement. JPEGEncoder's encode method returns a ByteArray and Loader has a method called 'loadBytes'. Using the Loader to load the ByteArray generated by the JPEGEncoder would be no different that loading an image from the server. Just use 'loadBytes' instead of 'load'. You could use the ImageSnapshot class to get the bitmapData of a display object to pass the the JPEGEncoder.
Thanks for such a great article.
However I would like to know whether it is possible to upload the image data to a server after resizing?
What I want to do is :
1. Browse for an image present in the local system.
2. Apply some transformation, so as to reduce its file size (which can be accomplished by reducing its dimensions)
3. Then upload the same to the server.
Please suggest.
Hi Andrew,
thank you so much for your reply.
I'll try to implement it :D
Nice work Budda :)
Arunava:
You can't simply upload the same image. The image now exists as a bitmap in the Flash Player and is no longer a jpeg or png. You would need to create a new jpeg with the JPEGEncoder and upload that.The JPEG encoder returns a jpeg encoded ByteArray so when you upload that you can either store it in database directly or use a server side script to create an image from it and either store that on the server or download it back to the client.
Very nice article on bitmaps editing. I am looking forward to one that has a little more vector specific stuff. I have looked at the drawing API quite a bit... and though what I have read gets the job done, very little of my code for drawing tools are elegant as the above code and descriptions you have written.
--Chris
nice information...
i am curious about the 'contrast' formula that is used... i tried to google around.. couldnt get one..
Hi Andrew,
Thank you very much for the reply. I will definitely try to implement what you said.
One more request, if by any chance you have any sample coding for the same or any url demonstrating it, please post it.
Thanks once again
I think there is one small mistake in Flipping section:
m.scale(-1, 0);
after this call image.height will be 0.
Use:
m.scale(-1, 1);
How can I implement a tool to have photo smudging? I would like to have a kid friendly tool to manipulate photos with limit capability.
Is it possible to load a image from the filesystem?
Is it possible to use ImageSnapshot.captureImage and resize the captured UIComponent before sending it to the server for downloading, without actually resizing it on the screen? In other words, I want to give the user an option for saving a chart on the screen in different sizes.
Does anybody know how to merge to or more images into one and then download it to the user's desktop???
Thanks
Todd
Does anyone know how to scale all contents of an application according to the user's screen resolution? My colleague has already tried a technique which re-scales the whole screen, but the problem with this is that all the contents become out of proportion.
Is there any other way? Please email me at russellevanson@innovate-software.co.uk
Many thanks.
@rooz,
Thanks for the comment. I had been trying to get my image to flip correctly for the last 3 hours thinking my code was throwing some weirdness into the mix. Simply changing it from 0 to 1 as you suggested fixed the "flip" issue.
What about if the image is rotated at some angle and then need to flip vertical or horizontal by center?
Hai Andrew Alderson,
I had followed your guidance for image manipulation. The main part i have to do is i want to save the captured image in server side Using Java JAI. I had tried many solutions but i failed to achieve that. I can able to send the JpegEncoded byte array as string (Using Base64Decode) to the server side. But in server side i can't able to convert the encoded string to image format using Java JAI. Can you please send me the java code to save the captured image. When i save the String as jpeg image in server side the file gets saved but no image is visible.
Thanks
Jaibabu
Hi,
I have a project where i would like to save the image in 300dpi. i haven't implemented your code yet. Don.t you think that scaling the image upto 300 dpi will effect the quality specially in a case where i am looking for a very high print quality. It would be great for me if you can reply back to this question
Regards
flashgen
I went through the loader example yesterday and found a couple of things that did not work in Actionscript 3, I dont know if this was written in 2 or 3, but I had a couple of errors thrown with the posted code. First, because I was only loading into memory and not displaying the image in some mxml component, i instantiated a private bindable variable type BitmapData outside the functions. Second, in the load complete handler function I had a dickens of a time getting the loaded data to get assigned to the local bitmapData variable. Your dot notation syntax did not work. Here is how I ended up doing it.
var bitMap:Bitmap = new Bitmap(event.currentTarget.content.bitmapData);
var bmd:BitmapData = bitMap.bitmapData;
The flex compiler did not allow me to do the type coercion that you posted in your example.
Then I assigned the variable back to the module scope bitmapData variable (I called it image):
image = new BitmapData(bitMap.width, bitMap.height, true);
image.draw(bitMap);
Hi all ... i have a big problem
i must dinamicaly load an image ...
how i do that ..
image.source = "http://ispot.interlogic.com.ua/Content/Upload/Kunstindustrimuseets Café.jpg";
but it's not load the image becouse there are danish character in URI
is = "é" in word = "Café"
what i must to do that to load this image ...
urlencode is the magic!
regarding the initial code which creates the bitmap data, how would you then add that to the display?
I tried creating a bitmap from the original bitmap data and adding the new bitmap as a child of a canvas component, but i get the following error:
Type Coercion failed: cannot convert flash.display::Bitmap@67c02e1 to mx.core.IUIComponent.
Hi Randy,
You can't add a Bitmap directly to a Canvas container in Flex. All children of a Canvas must implement IUIComponent which Bitmap doesn't. You would need to put the bitmap into an Image control by setting the source property of it to the bitmap. eg:
var image:Image = new Image();
image.source = myBitmap;
Cool man, cheers... You got some issues with parsing non Arrays into the ColorMatrixFilter constructor.. but otherwise, spot on! cheers! :D
i was searching from a long time how to do the manipulation of image in flex and at last i hit this site and this page in google .
this is cool ..........
@Randy,
Andrew is right about the UIComponent limitation, but you don't have to use an Image instance. Instead, you can choose to just use the UIComponent itself.
I wrote a quick blog piece on how to apply actionscript drawing examples with Flex that might be helpful; http://blog.shortfusion.com/index.cfm/2008/11/30/UIComponent-Instead-of-Sprite--Blending-is-Fun
@Randy,
Andrew is right about the UIComponent limitation, but you don't have to use an Image instance. Instead, you can choose to just use the UIComponent itself.
I wrote a quick blog piece on how to apply actionscript drawing examples to Flex that might be helpful; http://blog.shortfusion.com/index.cfm/2008/11/30/UIComponent-Instead-of-Sprite--Blending-is-Fun
Can anyone post an example of exactly how to display an image on the screen? I have read the comments but am still unsure of how to do this. I must be missing something.
I liked it, this is what I was searching for
one thing to add
I'm zooming/resizing an image on an canvas
but while zooming/resizing the zooming icons (those cross icons used )
goes out of the canvas n I land up loss of those icons
Can u give code / logic so that I stay inside the canvas/container while resizing??
Hi to all,
How to get the text from image.
Good stuff Andrew!
There seems to be an error with the contract example:
var a:Number = value * 11;
var b:Number = 63.5 - (value * 698.5);
redValue = greenValue = blueValue = a;
redOffset = greenOffset = blueOffset = b;
var cmf:ColorMatrixFilter = new ColorMatrixFilter(a, 0, 0, 0, b, 0, a, 0, 0, b, 0, 0, a, 0, b, 0, 0, 0, 1, 0);
specifically:
redValue = greenValue = blueValue = a;
redOffset = greenOffset = blueOffset = b;
Thanks!
Good stuff Andrew!
There seems to be an error with your Contrast example:
var a:Number = value * 11;
var b:Number = 63.5 - (value * 698.5);
redValue = greenValue = blueValue = a;
redOffset = greenOffset = blueOffset = b;
var cmf:ColorMatrixFilter = new ColorMatrixFilter(a, 0, 0, 0, b, 0, a, 0, 0, b, 0, 0, a, 0, b, 0, 0, 0, 1, 0);
specifically, these vars are undeclared:
redValue = greenValue = blueValue = a;
redOffset = greenOffset = blueOffset = b;
Thanks!
I have an app where the user may edit brightness/contrast values for an image. If the user changes these values in one session the changes should be visible in later sessions.
Does anybody know if these values can be read from the image (and how) or whether they would need to be stored as metadata?
Hi Adam,
I would never store a manipulated image. Rather you should store the original image (or the path to it) and then store the filters applied separately. This way the user can revert to the original image anytime.
Some choices would be to create xml that has the image path and then nodes for the filters. Or if you want to store them as binary use the ByteArray class to serialize the metadata (filters) and the image. An example would be.
var ba:ByteArray = new ByteArray();
ba.writeObject(myDrawing);
ba.wirteObject({filters:['an array of filters']};
I am currently using a similar approach to this for a vector drawing application I am working on. You would have to do some testing as to what the best way to store the filters are. Also if you go the ByteArray approach not all built in Flash Player classes are serializable so you need to look at 'registerClassAlias' and add any class that you serialize to your application.
ex : registerClassAlias('dropShadowFilter', flash.filters.DropShadowFileter);
This will ensure that when you deserialize the drawing the filters will be typed and not just generic objects.
Also if you are resierailizing a jpg that has been loaded (or a png) look at the JPGEncoder or PNGEncoder classes in the Flex framework. Actually for that matter another choice is to write your own encoder that does exactly what you want it to do - serializes an image and the filters.
Hi Andrew
Thanks for your helpful comments. I'll try out your recommended approach.
Cheers
Adam
This article is SOOO GREAT !! Thank You for that Andrew !
Great work.
I have a question though, after zooming the image (after copyPixels) what then ? I'm not sure how to display the newly zoomed area.
Any help is appreciated.
Hi Andrew
Thanks a lot for such useful Article..its great help and cleared many doubts.
dev
Hi,
Great tutorial!
You have a mistake in brightness with ColorMatrix, it should be:
var cmf:ColorMatrixFilter = new ColorMatrixFilter( [ 1, 0, 0,0, value, 0, 1, 0, 0, value, 0,0, 1, 0, value, 0, 0, 0, 1, 0 ]);
Any idea how to change the brightness of non-black pixels?
Thanks!
Hi,
Great tutorial!
You have a mistake in brightness with ColorMatrix, it should be:
var cmf:ColorMatrixFilter = new ColorMatrixFilter( [ 1, 0, 0,0, value, 0, 1, 0, 0, value, 0,0, 1, 0, value, 0, 0, 0, 1, 0 ]);
Any idea how to change the brightness of non-black pixels?
Thanks!
Hi guys, I am realy new with Flex... I am trying to use this tips, but i dont know how to load a image... using this tips the first load image data, where goes the image?
thanks
Really great tutorial.
I am working on functionality where user can remove white colors from the image, I am almost done with applying specific ColorMatrixFilter, but sometimes it dulling/removing the colors other than white.
Please have a look at http://cfcdi.org/colormatrixfilter/
Thanks
Hi all
The problem i now encountered is the memory problem. When the 4MB image load completed, the application would occupy around 8x MB memory. However, the issue occur when i going to implement the ColorMatrix which changing the image Brightness, Contrast, etc .. the memory allocation of app. increased significantly (up to 18x MB) MB. As a WEB app., 18x MB is a BIG number and is the serious concern of performance. The way i do is similiar with the above script, just apply the ColorMatrixFilter onto the image.filters.
Hence, hope will get some help from all of you experts ....
or
any alternative way that would able to changing the Brightness, Contrast of the image but with better performance
or
any technical suggestions if i implement like that .. any risks !?
or
is there no other way to solve the problem !?
Please help ........ indeed .....
CX113
Regards
Please can any one tell Where i can get the complete code
This article is just what I need, but as a Newbie I can't get it to work.
I'm using Flex 3, SDK 3.3, and Flash 10.0.22.
Please can someone post the exact code that does allow 'oversized' images to be loaded ? If this can be provided as a mxml file, then copious amounts of beer/liquor would be made available to the supplier, when they visit Florida :)
Any help appreciated, Thanks, Steve.
hi,
thank for the great sharing.
i would like to ask how to apply these effect into drawing?
e.g. i have drawn a shapes or text on the image, how could i apply the "flip" on both the layer and the images?
Hi..
Old programmer new to web programming... ;O)
I am working on a project to learn some of the tools for web applications. This project is mostly on-line, but part requires a desktop app for configuring and gathering resources... one of which is taking various large graphic images and creating thumbnail and display size images. I am useing FlexBuilder and the AIR Framework for this part.
Most of the examples deal with interactively generating things for a display and so are techniques for speed. What I am more interested is in quality over speed. So far what I have found and tried resulted in poor quality...especially with thumbnails..
ISo... I would be interested in knowing what you would suggest as a workflow for generating the highest quality reductions. I am not asking for actual code just point me in the right direction.
Thanks for any help.
Bob
hi,
i m new in flex.i need to undo and redo operation on image after crop, resize, move an image. can anyone help me??
regards
kamrul
Hi!
I've tried to adjust your examples for my own application, but I just can't get it working properly..
The thing that I try to accomplish is this:
When the user presses a button, the multiple selection browser is shown.
After the user selects one or more images, I start to iterate trough each file in the FileListEvent.
For each file i'm calling the loadImage function. Which does his thing like you described.
As addition to the image_CompleteHandler, I'm inserting the image into an ArrayCollection which I use as a dataprovider for my tilelist.
This tile list next shows the selected images.
The problem that I'm encoutering is that when I'm selecting more than one image, the TileList shows one tile for each selected image, but the image is the same image each time.. Say for instance that I've selected 3 images, then the tile list shows three tiles but only with the last image as it's source. instead of three different images
I'm having trouble understanding how to solve this problem, so that the loadImage is performed on each selected image.
I'm quite new to Flex and I would really appreciate if someone could help me out a little...
Thanks in advance!!
Ramin
my mail is: ramstyles AT gmail DOT com
Hello, I have a similar requirement like this. please can u send me the code. its urgent
My id is varunvittal@yahoo.co.in
First, thank you for writing this article. It's very helpful.
I do have a question, though. Putting aside the difficulty of un-transforming the image, isn't it far more efficient to adjust brightness and contrast via ColorTransform than ColorMatrixFilter?
Am I missing something?
I have a similar requirement like this. Please can u send me the code.
my id is: purnima2006@gmail.com
Hello,
First of all thanks for the great article. I'm new to Flex and I need to write an application that can upload, manipulate images, and then save the transformed images to a server. Since I'm so new I have no idea how to get this code to work. I'm trying to get it to work but as you might imagine it's taking me a while. So if someone could please point me in the right direction or perhaps somebody could share so code that would help to get me started I would really appreciate it.
THANKS !!!!!
Mario.
mestevez01@hotmail.com
Thanks for the article, it is helpful. In case anybody else is struggling to get the first code example to work, there are a few problems. The scale variable needs to be initialized to 1 before the if statement, otherwise it will be NaN if the if statement is false.
var scale:Number = 1;
Also, there is an extra comma in the fourth to last line:
original = new BitmapData( newWidth, , newHeight);
Also, if you're trying to figure out how to get this to paint to a canvas, this is what I did in my MXML:
mx:Image source="{myBitmap}" x="77.5" y="10" width="300" height="200" creationComplete="loadImage('http://whatever.gif')"
Then I put the code in the first example in a script tag and defined another variable called myBitmap.
[Bindable]
private var myBitmap:Bitmap;
Then I added the following to the last line of the image_completeHandler method:
myBitmap = new Bitmap(original);