Draw text on BitmapData in Flex - apache-flex

Well, the problem may be a simple one but I can't figure it out. I have an image loaded into BitmapData. now I want to take text from a textinput and put it on the BitmapData. Basically it's drawing a text on the BitmapData and get the result as another BitmapData that will consist of the original BitmapData with the text drawn over it on a specified position. What's the best way to achieve this in flex?

To put the text inside a bitmap you can do:
var channelName:TextField = new TextField();
channelName.textColor=0x000000;
channelName.antiAliasType = AntiAliasType.NORMAL;
channelName.alpha=1.0;
var txtFormat:TextFormat = new TextFormat("SansSerif",14,0x000000,true);
channelName.setTextFormat(txtFormat);
var bitmapdata:BitmapData = new BitmapData(
channelName.width, channelName.height, true, 0x000000);
bitmapdata.draw(channelName);

You can't draw over bitmapdata per say, but you could compose it from the data. Since you have BitmapData, it's easy enough to change it to a bitmap (var bitmap:Bitmap = new Bitmap(bitmapData);) and then add it as source for an image.
Now that you have an actual image on the stage, you can now add text above that using what you like (text, label, textarea, etc) and then you can do a Bitmap.draw over the dimensions of the image to get the pixel information back into a BitmapData (under Bitmap.bitmapData).

Related

How can an image be cropped based on a set scale of random image?

Working on allowing the upload of images which can range in a variety of size, then allowing to crop a predefined area of the image for a thumbnail.
The thumbnail size is predefined to 150x150. Using the Jcrop.js tool to select a section of the image.
Problem:
When displaying the uploaded image in a smaller size than the original image by implementing set height/width on the image rendered, then there is a scale factor that comes into play when selecting an area to crop.
You either have to scale down the cropping area proportionately or you have to scale the image in relation to the actual image's size in comparison to its displayed size.
Question:
How do I figure out the scale of the browser displayed image vs. original image? I am currently using the following code to save the image, but how would I take into consideration the scaling?
public static Image CropImage(Image originalImage, int x, int y, int width, int height)
{
var bmp = new Bitmap(width, height);
bmp.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
using (var graphic = Graphics.FromImage(bmp))
{
graphic.SmoothingMode = SmoothingMode.AntiAlias;
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.PixelOffsetMode = PixelOffsetMode.HighSpeed;
graphic.DrawImage(originalImage, new Rectangle(0, 0, width, height), x, y, width, height, GraphicsUnit.Pixel);
return bmp;
}
}
Bonus Question:
Another problem I discovered, is that there seems to be no efficient way to transfer the original file's ImageFormat when creating a new Bitmap which creates a ImageFormatMemoryBMP and when you attempt to call Bitmap.Save(memorystream, original rawformat) it will blow up. And bitmap RawFormat has no setter.
So how can you set the format on a new bitmap?
I think perhaps that this problem is solved purely on the front end, no need to use any server side for this.
Jcrop has a built in scale factor handler.
http://deepliquid.com/content/Jcrop_Sizing_Issues.html
Now you can use this in two ways, as I understand it. Either to 'resize' the image for you on the front end using 'box sizing', or you can tell it the 'truesize' of the image and it will work out the scale factor and handle the coordinates for you on it's own.
Box sizing
$('#cropbox').Jcrop({ boxWidth: 450, boxHeight: 400 });
True Size
$.Jcrop('#cropbox',{ trueSize: [500,370] });
Using the true size method you will need to invoke jcrop using the api method:
http://deepliquid.com/content/Jcrop_API.html#API_Invocation_Method
var jcrop_api,
options = { trueSize: [500,370] };
$('#target').Jcrop(options,function(){
jcrop_api = this;
});
Good luck!

FlashBuilder 4.5 :: Render Text without lifecycle for upsampling

I need to find a way to "upsample" text from 72dpi (screen) to 300dpi (print) for rendered client generated text. This is a true WYSIWYG application and we're expecting a ton of traffic so client side rendering is a requirement. Our application has several fonts, font sizes, colors, alignments the user can modify in a textarea. The question is how to convert 72dpi to 300dpi. We have the editior complete, we just need to make 300dpi versions of the textarea.
MY IDEA
1) Get textarea and increase the height, width, and font size by 300/72. (if ints are needed on font size I may need to increase the font then down-sample to the height/width)
2) use BitmapUtil.getSnapshot on the textarea to get a rendered version of the text
THE QUESTION
How can I render text inside of a textarea without the component lifecycle? Imagine:
var textArea:TextArea = new TextArea();
textArea.text = "This is a test";
var bmd:BitmapData = textArea.render();
Like Flextras said, width/height has nothing to do with DPI, unless you actually zoom into the application by 4.16X. If your application all has vector based graphics, it shouldn't be a problem. Plus, the concept of DPI is lost in any web application until you're trying to save/print a bitmap.
It's definitely possible, but you'll have to figure it on your own.
To ask a question another way, it is possible to create a TextArea in
memory which I can use the BitmapUtil.getSnapshot() function to
generate a BitmapData object
Technically, all components are in memory. What you want to do, I believe, is render a component without adding it to a container.
We do exactly this for the watermark on Flextras components. Conceptually we created a method to render the instance; like this:
public function render(argInheritingStyles : Object):void{
this.createChildren();
this.childrenCreated();
this.initializationComplete();
this.inheritingStyles = argInheritingStyles;
this.commitProperties();
this.measure();
this.height = this.measuredHeight;
this.width = this.measuredWidth;
this.updateDisplayList(this.unscaledWidth,this.unscaledHeight);
}
The method must be explicitly called. Then you can use the 'standard' procedure for turning the component into a bitmap. I think we use a Label; but the same approach should work on any given component.
Here is the final method I used to solve the problem of creating a printable version of the text and style of a Spark TextArea component. I ended up placing the custom component TextAreaRenderer (see below) in the MXML and setting the visibility to false. Then using the reference to this component to process any text field (renderObject) and get back a BitmapData object.
public class TextAreaRenderer extends TextArea implements IAssetRenderer
{
public function render(renderObject:Object, dpi:int = 300):BitmapData{
// CAST THE OBJECT
//.................
var userTextArea:TextArea = TextArea(renderObject);
// SCALE IS THE DIVISION OF THE NEW DPI OVER THE SCREEN DPI 72
//............................................................
var scale:Number = dpi / 72;
// COPY THE USER'S TEXT AREA INTO THE OFFSCREEN TEXT AREA
//.......................................................
this.text = userTextArea.text; // the actual text
this.height = Math.floor(userTextArea.height * scale); // scaled height
this.width = Math.floor(userTextArea.width * scale); // scaled width
// GET THE LAYOUT FORMATS AND COPY TO OFFSCREEN
// - the user's format = userTextAreaLayoutFormat
// - the hidden format = thisLayoutFormat
//...............................................
var editableLayoutProperties:Array = ['fontSize', 'fontFamily', 'fontWeight', 'fontStyle', 'textAlign', 'textDecoration', 'color']
userTextArea.selectAll();
var userTextAreaLayoutFormat:TextLayoutFormat = userTextArea.getFormatOfRange();
this.selectAll();
var thisLayoutFormat:TextLayoutFormat = this.getFormatOfRange();
for each(var prop:String in editableLayoutProperties){
thisLayoutFormat[prop] = userTextAreaLayoutFormat[prop];
}
// SCALE THE FONT SIZE
//....................
thisLayoutFormat.fontSize = thisLayoutFormat.fontSize * scale;
// SET THE FORMAT BACK IN THE TEXT BOX
//...................................
this.setFormatOfRange(thisLayoutFormat);
// REDRAW THE OFFSCREEN
// RETURN THE BITMAP DATA
//.......................
this.validateNow();
return BitmapUtil.getSnapshot(this);
}
}
Then calling the TextAreaRenderer after the text area is changed to get a scaled up bitmap.
// COPY THE DATA INTO THE OFFSCREEN COMPONENT
//............................................
var renderableComponent:IAssetRenderer = view.offScreenTextArea;
return renderableComponent.render(userTextArea, 300);
Thanks to the advice from www.Flextras.com for working through the issue with me.

Flex saving canvas and dropped image

I have an image loaded from an url and added to canvas as child. Then I am drag and dropping another image on it which also uses the senocular transform so the image can be transformed on the canvas. I have coded in such way that the transform handles shows up only after it's dropped on canvas. The image shows up correctly. But I am trying to save the result image (that is the main image and the dropped image on top of it), I only end up with the main image that was loaded earlier. The dropped image doesn't show up.
Below is the code for handleDrop() that is fired on dragDrop event and prepares the final image. What am I doing wrong?
var dragInitiator:IUIComponent = dragEvent.dragInitiator;
var dropTarget:IUIComponent = dragEvent.currentTarget as IUIComponent;
var tool:TransformTool = new TransformTool(new ControlSetStandard());
var items:String = dragEvent.dragSource.dataForFormat("items") as String;
var img:Image = new Image();
img.x=50;
img.y=50;
img.width=55;
img.height=55;
img.source=items.toString();
var bitmap:Bitmap= Bitmap(img.content);
var tool:TransformTool = new TransformTool(new ControlSetStandard());
var component:UIComponent = new UIComponent( );
tool.target = img;
tool.x=myCanvas.x;
tool.y=myCanvas.y;
addElement(component);
myCanvas.addChild(img);
img.z=myCanvas.z+1;
component.addChild(tool);
original=new BitmapData(bmd.width,bmd.height,true,0x000000FF);
original.draw(myCanvas);
Just because you added the image to the canvas doesn't mean it has drawn already. Either listen for the updateComplete event on the image or do a callLater to a function that then draws the bitmap.

Draw text on shape in ActionScript 3

Is there any way to draw text in a DisplayObject or Shape using only ActionScript? The only way I can find on the web is by creating a TextField, but I can't add a TF to a DisplayObject or Shape.
Edit:
Solved thanks to viatropos.
For anyone that's interested:
DisplayObject implements IBitmapDrawable which can be passed as an argument to the draw function of a BitmapData object, which then can be drawn using graphics.beginBitmapFill.
var textfield:TextField = new TextField;
textfield.text = "text";
var bitmapdata:BitmapData = new BitmapData(theWidth, theHeight, true, 0x00000000);
bitmapdata.draw(textfield);
graphics.beginBitmapFill(bitmapdata);
graphics.drawRect(0, 0, theWidth, theHeight);
graphics.endFill();
Good question. This is beyond anything I've ever needed to do, but I think I know how to do it.
Shape extends DisplayObject, but not DisplayObjectContainer, so you can't add anything to it. But it does have the graphics property, so you can draw things into it. The best way I can think of is to take a Bitmap snapshot of the TextField, and draw that into the Shape. I know this is what Degrafa does for their RasterText (check out the source, it's really helpful).
If you changed your Shape to a Sprite instead, it's a lot easier. Sprite extends DisplayObjectContainer, so you could add your TextField there.
Hope that helps,
Lance

Terminal emulation in Flex

I need to do some emulation of some old DOS or mainframe terminals in Flex. Something like the image below for example.
The different coloured text is easy enough, but the ability to do different background colours, such as the yellow background is beyond the capabilities of the standard Flash text.
I may also need to be able to enter text at certain places and scroll text up the "terminal". Any idea how I'd attack this? Or better still, any existing code/components for this sort of thing?
Use TextField.getCharBoundaries to get a rectangle of the first and last characters in the areas where you want a background. From these rectangles you can construct a rectangle that spans the whole area. Use this to draw the background in a Shape placed behind the text field, or in the parent of the text field.
Update you asked for an example, here is how to get a rectangle from a range of characters:
var firstCharBounds : Rectangle = textField.getCharBoundaries(firstCharIndex);
var lastCharBounds : Rectangle = textField.getCharBoundaries(lastCharIndex);
var rangeBounds : Rectangle = new Rectangle();
rangeBounds.topLeft = firstCharBounds.topLeft;
rangeBounds.bottomRight = lastCharBounds.bottomRight;
If you want to find a rectangle for a whole line you can do this instead:
var charBounds : Rectangle = textField.getCharBoundaries(textField.getLineOffset(lineNumber));
var lineBounds : Rectangle = new Rectangle(0, charBounds.y, textField.width, firstCharBounds.height);
When you have the bounds of the text range you want to paint a background for, you can do this in the updateDisplayList method of the parent of the text field (assuming the text field is positioned at [0, 0] and has white text, and that textRangesWithYellowBackground is an array of rectangles that represent the text ranges that should have yellow backgrounds):
graphics.clear();
// this draws the black background
graphics.beginFill(0x000000);
graphics.drawRect(0, 0, textField.width, textField.height);
graphics.endFill();
// this draws yellow text backgrounds
for each ( var r : Rectangle in textRangesWithYellowBackground )
graphics.beginFill(0xFFFF00);
graphics.drawRect(r.x, r.y, r.width, r.height);
graphics.endFill();
}
The font is fixed width and height, so making a background bitmap dynamically isn't difficult, and is probably the quickest and easiest solution. In fact, if you size it correctly there will only be one stretched pixel per character.
Color the pixel (or pixels) according to the background of the character.
-Adam

Resources