Archive for 2006

Flash bitmap & printing API

Saturday, December 30th, 2006

To follow up on the first bitmap post, I’m posting some code I wrote a few weeks ago to print a movieclip. The Flash Print API is pretty mind-boggling at times I think. Although this code is in no way a general print-problemsolving formula, it does show how to use the bitmap and the print API’s combined to print a movieclip (for example a screen) sized to fit on whatever paper you are providing.

It is not a utility class, and it can certainly be improved, nevertheless it provides a good starting point. It will probably be refactored and become part of our framework. Note that it does not provide page-breaking and other formatting, its purpose is to implement a PrintScreen kind of mechanism.

//the movieclip to print
var clipToPrint:MovieClip = …….;
//the bounds of clipToPrint, eg {x:clipToPrint._x, …}
var bounds:Rectangle = …….;
//reference to an offscreen clip to create print content in, eg _root.createEmptyMovieClip …
var printLayer:MovieClip = …….;

if (System.capabilities.hasPrinting) {
var my_pj:PrintJob = new PrintJob();

var pages:Number = 0;
if (my_pj.start()) {
//if factor 1, we are printing at screenresolution, if factor is 3, its 3*screenresolution
var factor:Number = 3;

//start with a bitmap image the size of our screen
var bmpContent:BitmapData = new BitmapData(bounds.width*factor, bounds.height*factor);
//and populate the bitmap with our contentpane
var matrix:Matrix = new Matrix();
matrix.scale(factor,factor);
bmpContent.draw(clipToPrint, matrix, null, null, null, true);

//create two containers, a parent and a child container. This allows us to move and scale
//the subcontainer within its parent to allow for easier placement.
var printContainer:MovieClip = printLayer.createEmptyMovieClip(“printContainer”, 0);
var printSubContainer:MovieClip = printContainer.createEmptyMovieClip(“sub”, 1);

//create a white background on the printContainer (white paper)
//use unfactored bounds since we are scaling the sub container and not the main container
var bg:TextField = printContainer.createTextField(“background”, 0,0,0, bounds.width, bounds.height);
bg.background = true;
bg.border = false;
bg.backgroundColor = 0xffffff;
bg.selectable = false;
bg.tabEnabled = false;

printContainer._visible = false; //hide it from the screen
printSubContainer.attachBitmap(bmpContent, 0); //attach the bitmap to our child

var landscape:Boolean = (my_pj.orientation == “landscape”);
//to correctly size our content, we can use the PrintJob.paperWidth/paperHeight properties
//these are in points however, not pixels, and only available after starting the printjob
//in order to relate points to pixels, we need to know two things:
//1: 1 point = 1/72 inch
//2: the screen resolution is dpi, where the dots are pixels
//So 1 point = 1/72 inch * screenDPI = x pixels
//We see if the screenDPI = 72, the conversion ratio is 1:1

//get values needed for pixel to point conversion
var screenDPI:Number = System.capabilities.screenDPI;
var point2Pixel:Number = (1/72)*screenDPI;

//declare x and y scales, since we are going to calculate
//how much we need to scale the clip to fit to the page
var xs:Number;
var ys:Number;

//there are two options (we use option 2)

//OPTION 1: rotate with the page (no matter whether we use landscape or portrait, the print is the same)
//printSubContainer._rotation = (landscape?0:-90);
//if we are landscape, we need to compare paperwidth with bounds width.
//if we are portrait, we will have rotated and need to compare paperwidth with bounds height
//xs = (landscape?bounds.width:bounds.height) / (my_pj.paperWidth * point2Pixel);
//ys = (landscape?bounds.height:bounds.width) / (my_pj.pageHeight * point2Pixel);

//OPTION 2: do not rotate with the page (a landscape sized screen will print bigger and better on a landscape paper)
//easier, compare clip width with paper width and same for height
xs = bounds.width / (my_pj.paperWidth * point2Pixel);
ys = bounds.height / (my_pj.pageHeight * point2Pixel);

//we want to keep the aspect ratio intact, so get the one we need to scale the most
//and use that value for both x and y scaling
var scale:Number = Math.max (xs, ys);

//instead of using this factor, use 90% of it, so we have some margins around the paper
printSubContainer._xscale = (1/scale)*90/factor;
printSubContainer._yscale = (1/scale)*90/factor;
//now get the size based on 100 percent and divide it in order to center the image
printSubContainer._x = (bounds.width/scale-printSubContainer._width)/2;
printSubContainer._y = (bounds.height/scale-printSubContainer._height)/2;

//refer to docs for addPage parameters
if (my_pj.addPage(printContainer, {xMin:0,xMax:bounds.width/scale,yMin:0,yMax:bounds.height/scale}, {printAsBitmap:true},1)) pages++;

if (pages == 0) {
trace(“Flash could not start the printjob.”);
} else {
my_pj.send();
trace(“Document sent to printer.”);
}

bmpContent.dispose();
bmpContent = null;
bg.removeTextField();
bg = null;
printSubContainer.removeMovieClip();
printSubContainer = null;
printContainer.removeMovieClip();
printContainer = null;
} else{
//printjob cancelled
}
} else {
trace(“Flash cannot find your printer.”);
}

Flash Bitmap API basics

Thursday, December 28th, 2006

I’ve been delving into the new Flash bitmap API. First off, lets take a quick look at this API in general terms. Basically it allows you to convert movieclips to bitmaps, wrap bitmaps in movieclips, and operate on these bitmaps at the pixel level. At the pixel level means, that you can retrieve and set pixel values, apply bitmap filters and do a whole lot more.

For example to create a bitmap, you’d do:

var bitmapData1:BitmapData =
new BitmapData(200, 200, false, 0x000000);
bitmapData1.draw(myMC,new Matrix());

Line 1 creates a bitmap 200×200 pixels, tells it to be opaque, and the last parameter specifies the color of the opaqueness. In effect a black square.
Using line 2 you could draw another clip or bitmap into the newly created bitmap.

To show the bitmap you could do:

_root.attachBitmap(bitmapData1, _root.getNextHighestDepth());

So in effect, with just two lines:

var bitmapData1:BitmapData =
new BitmapData(200, 200, false, 0x000000);
_root.attachBitmap(bitmapData1, _root.getNextHighestDepth());

you have created a black bitmap and shown it on the stage. Boring, but we all need to start somewhere, and why not simply show a black void instead of ‘Hello World’ now and then.

This opens up a world of possibilities, and allows you to use movieclip and bitmap tricks vice versa. For example for a radial mask you can draw a very short very thick line with round caps, or you can create a radial gradient, or you could create a circle pixel by pixel by determining whether a certain pixel falls within a certain radius. Whether you are working with bitmap data or not depends on your choice. To conclude this post, I’ll just show these 3 options:

Creating a circle through a very thick line:

_root.lineStyle(500, 0x000000, 100);
_root.moveTo (130,130);
_root.lineTo(130.5,130.5);

The trick is making the line very thick and very short.

Creating a circle through a gradient through drawing api (copied from flash help):

var myMatrix:Matrix = new Matrix();
myMatrix.createGradientBox(200, 200, 0, 0, 0);
trace(myMatrix.toString());
// (a=0.1220703125, b=0, c=0, d=0.1220703125, tx=150, ty=150)
var depth:Number = _root.getNextHighestDepth();
var mc:MovieClip = _root.createEmptyMovieClip("mc_" + depth, depth);
var colors:Array = [0xFF0000, 0x0000FF];
var alphas:Array = [100, 100];
var ratios:Array = [0, 0xFF];
mc.beginGradientFill("radial", colors, alphas, ratios, myMatrix);
mc.lineTo(0, 300);
mc.lineTo(300, 300);
mc.lineTo(300, 0);
mc.lineTo(0, 0);

Creating a circle through bitmap api:

var d:Number = 100;
var bitmapCircle:BitmapData =
  new BitmapData(d*2,d*2,false, 0xffffff);
  for (var x:Number = 0;x < bitmapCircle.width;x++) {
   for (var y:Number = 0;y < bitmapCircle.height;y++) {
    var value:Number = 0x000000;
    if (Math.sqrt(Math.pow(d-x,2)+Math.pow(d-y,2)) > d) {
     value = 0xffffff;
    }
    bitmapCircle.setPixel(x, y, value);
   }
  }
  _root.attachBitmap(bitmapCircle, 1);

The trick here is determining whether the pixel should be black or white based on its distance from the centre. The Math.sqrt etc is nothing more than a simple Pythagoras calculation. Note that in that last example, if you had used new BitmapData(d*2,d*2,true, 0xffffff); you would have had to use setPixel32 instead of setPixel. I like the last example, because it shows how to create images through mathematical calculations.

Ok that’s it for the basics.