I'm trying to get around the problem where TextureAtlas would overflow if I make a dynamic label with a svg where I have text elements constantly changing.
My idea was to use and entity as a label, with a BillboardCollection for the background and a LabelCollection for the text. But I'm realising that the Collections are not really part of the entity I put them in which I guess makes sense somehow.
Is there a way to make the billboardCollection and LabelCollection that is part of an entity to inherit the position of that entity if no separate position is specified?
If this doesn't work, do I need to place each graphical element and text into separate entities?
What is the best praxis for a dynamic label with a background of my choice that is expected to be updated very very very very much, with a lot of similar labels without killing Cesium because of an overflow in the TextureAtlas?
Cesium's Entities have labels and billboards, but they typically share a single LabelCollection and BillboardCollection among the active entities. The text of the labels can change frequently, but the TextureAtlas is careful to re-use glyphs it has already seen from the same font. When used correctly, this should prevent it from overflowing.
Allowing each entity to manage its own collection defeats this optimization, and you can get an overflow much more quickly. The whole collection is intended to render with a single draw call, so if each entity manages a collection, then you get separate draw calls per-entity, which gets expensive fast. In the normal flow, all entities with billboards share a single BillboardCollection, and a single draw call gets all the billboards on the screen, and then a second draw call gets all the labels from all the entities on the screen.
Here's a sample of a time-varying label on an entity. This should produce an entity with a label that changes text very rapidly when the animation is playing.
var viewer = new Cesium.Viewer('cesiumContainer', {
navigationInstructionsInitiallyVisible: false,
// These next 5 lines are just to avoid the Bing Key error message.
imageryProvider : Cesium.createTileMapServiceImageryProvider({
url : Cesium.buildModuleUrl('Assets/Textures/NaturalEarthII')
}),
baseLayerPicker : false,
geocoder : false,
// This next line fixes another Stack Snippet error, you may omit
// this setting from production code as well.
infoBox : false
});
var startTime = Cesium.JulianDate.fromIso8601('2016-08-01T00:00:00Z');
var intervalStart = startTime;
var labelText = new Cesium.TimeIntervalCollectionProperty();
for (var x = 0; x < 86400; ++x) {
var intervalStop = Cesium.JulianDate.addSeconds(startTime, x, new Cesium.JulianDate());
labelText.intervals.addInterval(new Cesium.TimeInterval({
start: intervalStart,
stop: intervalStop,
data: 'Time ' + x
}));
intervalStart = intervalStop;
}
var clock = viewer.clock;
clock.startTime = startTime;
clock.stopTime = intervalStart;
clock.currentTime = startTime;
clock.clockRange = Cesium.ClockRange.CLAMPED;
clock.multiplier = 30;
viewer.timeline.zoomTo(clock.startTime, clock.stopTime);
viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222),
label : {
text : labelText
}
});
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
font-family: sans-serif;
}
<link href="http://cesiumjs.org/releases/1.20/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"/>
<script src="http://cesiumjs.org/releases/1.20/Build/Cesium/Cesium.js">
</script>
<div id="cesiumContainer"></div>
Related
I need to add the width property to the canvas .By traceing the value iam geting its value as zero though it contain images and has a width.
//code of adding 5 canvases to the main canvas
public function addcanvas(canarr:Array):void
{
var can_arr:Array=new Array();
can_arr=canarr;
for(var c:int=0;c<can_arr.length;c++)
{
canvasA=new Canvas();
canvasA.id="canvasA";
trace("ccc:"+c);
canvasA.x=(c*10)+10;
canvasA.y=0;2
this.addChild(canvasA);
}
trace("canvasA.width: "+canvasA.width);
}
and next i will add images into each canvas
//enter code here
public function ClubCards(imgarr:Array):void
{
var Gimg_arr:Array = new Array();
Gimg_arr =imgarr;
for(var j:int=0;j < Gimg_arr.length;j++)
{
myimage1 = new Image();
myimage1.x=0+(j*20);
myimage1.y=40;
myimage1.source=gameModule.getStyle(Gimg_arr[j].toString());
GroupingImageListeners(myimage1);
canvasA.addChild(myimage1);
trace("canvasA.width: "+canvasA.width );
}
iam unable to know the width of the canvas which has images in it . can u please help me out.how can i get that canvas width
Thank you in advance
I can't add a comment, so I will add an answer saying this code should be triggered by a call to the updateDisplayList method (during the appropriate phase of the flex component life-cycle). I would suggest reading and understanding this update cycle before doing any further Flex development (it will save you a lot of time in the long-run).
More info can be found here: http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_2.html
UPDATE
Since you mentioned Canvas, I updated the link to point to the 3.x version of the article
You did not make your canvas invalidate its size. Without an order to redraw, I don't think you'll get the correct size. Without Event.RENDER dispatched your component will not calculate its size.
Maybe you should try:
canvas.invalidateSize();
before accessing its width and see the result. This should make your canvas call its method measure() and calculate its size.
I've got a big issue and it's almost a week trying to make it work so any help I would really appreciate - I am trying to create a simple image editor in html5, so I upload an image, load it into canvas and then paint on it -
I also want to be able to zoom in and zoom out- just that I can't figure out how should I save the canvas state - for the paint mouseevents I am using an array which saves canvas.toDataUrl, but this one will save only what it is visible in canvas, only a part of the scaled image, and not the entire one -
if anyone knows how can I un-scale the canvas together with the painting over it and save it in the stack from where I can retrieve it for other painting events, I'll appreciate a lot! Thanks
Saving state
The canvas' save() and restore() is not related to the pixels in the canvas at all. Save() only saves current pen color, fill color, transform, scale, rotation and so forth - parameter values only, not actual pixel data.
And so, the restore() will only restore these parameter values to the previous ones.
The canvas element is passive, meaning it only holds the pixels that you see on the screen. It does not keep a backup of anything so if you change its size, re-size browser window or open dialogs in the browser causing it to clear, you will need to update the canvas yourself.
This also applies when you change a parameter value such as scale. Nothing on the canvas will change setting a new value. The only thing that happens is that your next draw of what-ever will use these parameter values for the drawing (in other words: if you apply rotation nothing rotates, but the next thing you draw will be rotated).
Drawing on existing image
As you need to maintain the content it also means you need to store the image you draw on as well as what you draw.
When you draw for example lines you need to record every stroke to arrays. When the canvas needs an update (ie. zoom) you redraw the original image first at the new scale, then iterate through the arrays with lines and re-render them too.
Same for points, rectangles, circles and what have you..
Think of canvas as just a snapshot of what you have stored elsewhere (image object, arrays, objects) . Canvas is just a view-port for that data.
I would recommend to store as this:
var backgroundImage; //reference to your uploaded image
var renderStack = []; //stores all drawing objects (see below)
//example generic object to hold strokes, shapes etc.
function renderObject() {
this.type = 'stroke'; //or rectangle, or circle, or dot, ...
this.x1;
this.y1;
this.x2;
this.y2;
this.radius;
this.penWidth;
this.penColor;
this.fillColor;
this.points = [];
//... extend as you need or use separate object for each type
}
When you then draw a stroke (pseudo):
var currentRenderObject;
function mouseDown(e) {
//get a new render object for new shape/line etc.
currentRenderObject = new renderObject();
//get type from your selected tool
currentRenderObject.type = 'stroke'; //for example
//do the normal draw operations, mouse position etc.
x =..., y = ...
}
function mouseMove(e) {
//get mouse positions, draw as normal
x = ..., y = ...
//store the points to the array, here:
//we have a line or stroke, so we push the
//values to ourpoint-array in the renderObject
currentRenderObject.points.push(x);
currentRenderObject.points.push(y);
}
function mouseUp(e) {
//when paint is done, push the current renderObject
//to our render stack
renderStack.push(currentRenderObject);
}
Now you can make a redraw function:
function redraw() {
clearCanvas();
drawBackgroundImage();
for(var i = 0, ro; ro = renderStack[i]; i++) {
switch(ro.type) {
case 'stroke':
//... parse through point list
break;
case 'rectangle':
//... draw rectangle
break;
...
}
}
}
function zoom(factor) {
//set new zoom, position (scale/translate if you don't
//want to do it manually). Remember that mouse coords need
//to be recalculated as well to match the zoom factor.
redraw();
}
//when canvas is scaled for some reason, or the window
canvas.onresize = windows.onresize = redraw;
A bonus doing this here is you can use your render stack as a undo/redo stack as well...
Hope this helped to better understand how canvas works.
I have a movieclip in the library that is added to the stage dynamically in the document class's actionscript. This movieclip contains many many child images that were imported directly from photoshop at their original positions (which must be preserved).
I do not want to manually name every single image instance, as there are dozens upon dozens.
I have already gone through and manually converted the images to symbols, as apparently flash won't recognize the "bitmap" objects as children of a parent movieclip in AS3 (numChildren doesn't see the bitmaps, but it sees the symbols).
I have an array filled with references to the dozens of children, and I loop through it, checking if each one is under the mouse when clicked. However, somehow, it is not detecting when I click over the items unless I manually name the child symbols (I tested by manually naming a few of them -- those ones became click-sensitive.)
I have already done trace() debugging all throughout the code, verifying that my array is full of data, that the data is, in fact, the names of the instances (automatically named, IE instance45, instance46, instance47, etc.), verifying that the function is running on click, verifying that the code works properly if I manually name the symbols.
Can any one see what's going wrong, or what aspect of flash I am failing to understand?
Here is the code:
//check each animal to see if it was clicked on
private function check_animal_hits():void
{
var i:int = 0;
var animal:Object = this.animal_container;
for (i=0; i<animal.mussels.length; i++)
{
if (this.instance_under_cursor(animal.mussels[i].name))
{
var animal_data = new Object();
animal_data.animal = "mussel";
this.send_data(animal_data);
}
}
}
Here is the code for the instance_under_cursor() method:
// Used for finding out if a certain instance is underneath the cursor the instance name is a string
private function instance_under_cursor(instance_name)
{
var i:Number;
var pt:Point = new Point(mouseX,mouseY);
var objects:Array = stage.getObjectsUnderPoint(pt);
var buttons:Array = new Array ;
var o:DisplayObject;
var myMovieClip:MovieClip;
// add items under mouseclick to an array
for (i = 0; i < objects.length; i++)
{
o = objects[i];
while (! o.parent is MovieClip)
{
o = o.parent;
}
myMovieClip = o.parent as MovieClip;
buttons.push(myMovieClip.name);
}
if (buttons.indexOf(instance_name) >= 0)
{
return true;
}
return false;
}
Update:
I believe I have narrowed it down to a problem with getObjectsUnderPoint() not detecting the objects unless they are named manually.
That is the most bizarre way to find objects under mouse pointer... There is a built-in function that does exactly that. But, that aside, you shouldn't probably rely on instance names as they are irrelevant / can be changed / kept solely for historical reasons. The code that makes use of this property is a subject to refactoring.
However, what you have observed might be this: when you put images on the scene in Flash CS, Flash will try to optimize it by reducing them all to a shape with a bitmap fill. Once you convert them to symbols, it won't be able to do it (as it assumes you want to use them later), but it will create Bitmpas instead - Bitmap is not an interactive object - i.e. it doesn't register mouse events - no point in adding it into what's returned from getObjectsUnderPoint(). Obviously, what you want to do, is to make them something interactive - like Sprite for example. Thus, your testing for parent being a MovieClip misses the point - as the parent needs not be MovieClip (could be Sprite or SimpleButton or Loader).
But, if you could explain what did you need the instance_under_cursor function for, there may be a better way to do what it was meant to do.
For instance the mx.controls.Image objects are only displayed when i add them directly to the main application object. If i add a "subimage" to the previously created Image object it simply doesnt show. Why ? What concept did i miss ?
What I want to do:
var img : Image = new Image;
var subimg : Image = new Image;
img.source = "images/panel.png";
subimg.source = "images/panel.png";
subimg.x = 10;
subimg.y = 10;
addChild (img);
img.addChild(subimg); // img is displayed, but not the overlapping subimg
OK, and here the code how it by directly adding the subimg to the Application just like img - this one works ofcourse:
var img : Image = new Image;
var subimg : Image = new Image;
img.source = "images/panel.png";
subimg.source = "images/panel.png";
subimg.x = 10;
subimg.y = 10;
addChild (img);
addChild(subimg); // img & subimg is displayed correctly
What exactly is it that you want to do, that the second example isn't doing for you? Generally speaking UIComponents are things with complicated internals, being that they're skinnable and styleable and so on, and they manage their own contents (as with Image, which populates itself with loaded assets).
I'm not familiar enough with Image to say precisely what the problem is - whether the subimg object is being hidden or whether the load is failing, or what. But what you should probably do is to make your own Sprite and add both Images inside it, or make two sprites, add an image to each, and parent them the way you like, so you can have a similar parent-child relationship without mucking around in the internals of a component.
For example:
// ... make img and subimg
var imgContainer:Sprite = new Sprite();
imgContainer.addChild(img);
var subimgContainer:Sprite = new Sprite();
subimgContainer.addChild(subimg);
imgContainer.addChild(subimgContainer);
addChild(imgContainer);
i have
A bunch of fabric patterns (simple jpg files)
An image for every letter of the alphabet(blank white background)
I essentially want to have a page similar to this:
http://www.craftcuts.com/hand-painted-wooden-letters-single-patterns.html
but instead of having it as a static page, i would like a user to be able to:
Type in a name
Choose a pattern (one of the jpg files)
and then have it display that name in that pattern.
Obviously i could create separate jpgs for every combination of letters (right now i have a jpg for every letter with white back color) and patterns but i wanted to find out if there was a more elegant way of coding this up to have it dynamically put one image onto the letter in the other.
EDIT: In my initial post, i assumed that this would have to be a front end thing (javascript), but if it makes it any easier (as a few people asked what is the backend), my back end is an asp.net-mvc so if there is some solution to build this up on the serverside and ship down to the client, i am more than happy using that as well.
You can apply background images to images using CSS, then use a load of transparent .png images for the letters.
I mocked it up:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Untitled Document</title>
<style type="text/css">
img.leaves
{
background:url("leaves.png");
}
img.lights
{
background:url("lights.png");
}
</style>
<script type="text/javascript">
function makeText()
{
var text = document.getElementById('text').value.toUpperCase();
var pattern = document.getElementById('pattern').value;
for(var i=0; i<text.length; i++)
{
var img = document.createElement('img');
img.src=text[i]+'.png';
img.className=pattern;
document.getElementById('textArea').appendChild(img);
}
return false;
}
</script>
</head>
<body>
<form onsubmit="return makeText();">
<p><label for="text">Enter your text</label> <input type="text" name="text" id="text" size="20"></p>
<p><label for="pattern">Choose a pattern</label> <select id="pattern"><option value="leaves">Leaves</option><option value="lights">Lights</option></select></p>
<p><input type="submit" value="Make!"></p>
</form>
<div id="textArea"></div>
</body>
</html>
and you can also see it in action.
alt text http://www.subdimension.co.uk/files/1/SO/A.png < this is one of the letter cutouts (difficult to see!)
alt text http://www.subdimension.co.uk/files/1/SO/leaves.png and one of the background patterns.
I got bored at G, so you can only write words that contain the letters a-g, it's also a little lazy in that it only does upper case and there are only 2 patterns but hopefully it should be enough to give you an idea
You could use the ImageMagick libraries to make any combination of superimpositions your and your users' hearts desire. This way, you're not limited to just something simple that can be achieved with CSS.
ImageMagick examples
The easiest way to do so via Javascript is probably to have an image mask of each letter and apply it on top of a pattern. By image mask I mean a simple image completely black (or white, or whatever you prefer) with a "cut out" transparent part in the shape of the letter. You can simply overlay that over the pattern file to get the wanted effect.
<div style="background: url(pattern.jpg)">
<img src="letter_a.png" />
</div>
You can dynamically change the img src and div background url to switch patterns and letters. You can also dynamically create new divs based on user input.
Note that this won't work easily in IE6 due to the transparent PNG.
Alternatively, you could generate the image dynamically on the server using something like gd, which would be a little more involved, but ultimately more flexible.
For users to be able to type in their own name you'll need a text field. You'll also need a button that fires a Javascript function that checks the value of the text field and renders the letter images.
You can use the following code to create the letters. You will need a textfield with the id 'textfield' and a div to render the results in with the id 'output', which ofcourse you can change. I recommend using a select element to store the pattern options (#patternChooser in this example)
function renderName()
{
// Get the name to be rendered and the chosen pattern
var text = document.getElementById('textfield').value;
var pattern = document.getElementById('patternChooser').value;
// Iterate over the name and create an element for each letter
for(i = 0; i < text.length; i++)
{
var letter = document.createElement('div');
letter.style.backgroundImage = 'url(' + pattern + '_' + text[i] + ')';
document.getElementById('output').appendChild(letter);
}
}
You can use the following CSS to apply some positioning to the letters (adjust the with and height to that of your images):
#output div
{
margin-left: 10px;
width: 50px;
height: 100px;
float: left;
}
You will need to name your images like this: flowerpattern_a.png, brickpattern_j.png, etc.
If you want the letters to appear real time you can use Javascript's onkeyup() to fire a function that checks the last character of the text field's value and creates an element for it.
Sprites
You can also use a sprite to increase performance. Put all the images for the letters into one image. You set this image as a background for every letter.
Quick read on CSS sprites: http://css-tricks.com/css-sprites/
You can add background-image: url(sprite.png); to the CSS snippet above.
Instead of just setting a backgroundImage with Javascript, you will need to set the background position of the letter (letter.style.background-position = '100px 200px')
Font embedding
If you got fonts to use: there are many font embedding options like Typeface and Cufon. The one I find most pleasant to work with is the use of font-face. It is fast and the text will behave like any other text.
If you got your .TTF Truetype font, you'll need to convert the font to .EOT for use with Internet Explorer. You could also add SVG fonts for full browser coverage. It's actually very easy: You just add the following snippet to the top of your stylesheet like this:
#font-face {
font-family: 'GothicCustom';
src: url("LeagueGothic.eot");
src: local('League Gothic'),
url("LeagueGothic.svg#lg") format('svg'),
url("LeagueGothic.otf") format('opentype');
}
The advantage of this technique is that it's easy to use and you got full controll over the rendered text, like any other text on your website. You can set the font-size, letter-spacing etc. Here's a good read about font-face font embedding.
You can use Microsoft WEFT or TTF2EOT to create .EOT fonts.
For example, your code could look like this.
Javascript
function renderName()
{
// Get the name to be rendered and the chosen pattern
var text = document.getElementById('textfield').value;
var pattern = document.getElementById('patternChooser').value;
// Render the text with a class
for(i = 0; i < text.length; i++)
{
var output = document.getElementById('output');
output.style.fontFamily = pattern;
output.innerHTML = text;
}
}
HTML
<form>
<input id="textfield" type="text" />
<select id="patternChooser">
<option>Flowers</option>
<option>Brick</option>
<option>Decorative</option>
</select>
<input type="button" onclick="renderName()" />
</form>
<div id="output"></div>
CSS
#font-face {
font-family: 'Decorative';
src: url("decorative.eot");
src: local('Decorative'),
url("Decorative.svg#lg") format('svg'),
url("Decorative.otf") format('opentype');
}
Now the only thing that is left is converting the fonts and import them in your stylesheet.
Ofcourse you can choose to go with any other font-embedding method. Here's an article about font-embedding options but a quick Google will show you the options as well.
If you are allowed to use something in the back-end, here is a java solution. You will just need the font (name.ttf) with which you type the letters, but I assume you have it:
(this is inside a servlet doGet method)
String text = "A letter, or any text here";
File texture = new File(patternTexturePath);
File fontFile = new File(filePath);
Font font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(font);
BufferedImage buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
g2 = buffer.createGraphics();
g2.clearRect(0, 0, width, height);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
FontRenderContext fc = g2.getFontRenderContext();
Rectangle2D bounds = font.getStringBounds(text, fc);
AttributedString attrStr = new AttributedString(text);
Rectangle r = new Rectangle(0, 0, width, height);
attrStr.addAttribute(TextAttribute.FOREGROUND, new TexturePaint(ImageIO.read(texture), r));
attrStr.addAttribute(TextAttribute.FONT, font);
g2.drawString(attrStr.getIterator(), 0, (int) -bounds.getY());
response.setContentType("image/png");
ImageIO.write(buffer, "png", response.getOutputStream());
response.getOutputStream().close();
Even on the server this is not a trivial issue, as it is not possible to do a direct Fill() using a color mask, which is esentially what you need.
I would sooner loop over the existing pixels, do a lookup and write the pattern pixel to the resulting image if the letter image is non-white:
Graphics g = Graphics.FromImage(letterImg);
Brush brush = new System.Drawing.TextureBrush(patternImg);
for (int x=0;i<letterImg.Width;i++)
for (int y=0;y<letterImg.Height;y++)
{
Color mask = img.GetPixel(x,y);
//Color pattern = patternImg.GetPixel(x % patternImg.Width, y % patternImg.Height);
if (mask != Color.White)
{
g.FillRectagle(brush,x,y,1,1);
}
}
And then find a naming scheme to cache these on the disk after creation so you only need to render each pattern<->letter combination once.
GDI+ works fine under ASP.NET. (WPF used to, but then was broken with a service pack. Not sure if it's working again now).
Building images server-side is pretty easy - the trick is linking them to client-side requests. I made use of a stack representation - my article here:
http://www.hackification.com/2008/10/29/stack-based-processing-part-2/
After confirming with OO (the poster) I wanted to throw Flash in the mix.
Flash has the advantage that it has really nice anti-aliasing and awesome typography support (especially since CS4). You could choose to have the entire interface in Flash where you type the patterned letters directly or just have the swf render the result according to parameters specifying the name and the pattern.
Personally I would make the whole thing in Flash, supplying the interface with the optional patterns with for instance JSON. But the most basic steps you could take are
create a form with input for the name and a select box for the patterns
display the flash file with parameters patterntype.swf?name=flash&pattern=disco.png
The basic concept is having the Text set as the mask for the pattern.
I did a quick test in flash to test the performance of tiling the pattern (and if it would actually work)
package {
import flash.display.MovieClip;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
public class PatternType extends MovieClip {
/* library element, use the textfield to change the font */
public var nameDisplay:NameDisplay;
private var loader:Loader;
/* parameters, you can pass these as request parameters, or make a javascript call to get/set them */
private var pattern:String = "pattern.png";
private var nameString:String = "Heidi Klum";
private var container:MovieClip;
public function PatternType() {
container = new MovieClip();
addChild(container);
nameDisplay = new NameDisplay();
nameDisplay.txt.text = nameString;
nameDisplay.cacheAsBitmap = true; //mask wont work without this.
container.addChild(nameDisplay)
container.mask = nameDisplay;
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handlePatternLoaded);
loader.load(new URLRequest(pattern));
}
private function handlePatternLoaded(e:Event) {
trace("complete");
var bmp:Bitmap = e.target.content as Bitmap;
var rows:int = Math.ceil(stage.stageHeight / bmp.height);
var cols:int = Math.ceil(stage.stageWidth / bmp.width);
//tile the texture
for(var i:int = 0; i < cols; i++) {
for(var j:Number = 0; j < rows; j++) {
var b:Bitmap = new Bitmap(bmp.bitmapData.clone());
b.x = i * bmp.width;
b.y = j * bmp.height;
container.addChild(b);
}
}
}
}
}
When everything is to the users wishes, you can create a new Bitmap object from the result and send the data to the server which can write the data directly to a bitmap file (jpeg is also possible).
A few things that could sell Flash a bit more:
you can rotate the text
use exotic fonts
use different patterns per letter
scale the text
use custom made image filters
move in 3d space (x,y,z translation rotation, perspective)
Save the result as an image
Read the link below here you will find the dynamic loader details alonmg with example
http://thecodecentral.com/2008/02/21/a-useful-javascript-image-loader