Textured text in Actionscript - apache-flex

There is any way to make a text "textured" - with a result similar to this: http://www.entheosweb.com/fireworks/patterned_text.asp
In Flex (so using AS3)?
I though it was a simple issue, but I'm stuck: i'm googling it from days and was not able to find aa way or workaround to do it...

The rough idea is this:
Create a TextField with whatever font you need, and write whatever text you want in there.
Create a new BitmapData object and draw() the TextField so that you get a bitmap representation of it.
Get your texture as a BitmapData object (if you load in a jpg, it'll be in the Bitmap that you get after it's loaded)
Create a new BitmapData object and use copyPixels() to draw your texture, while using the first BitmapData object (the blitted TextField) as an alpha mask: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#copyPixels()
Voila, textured text.
Let me know if you need some code, but it should be pretty straightforward
Edit with code:
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
public class TestTexturedText extends Sprite
{
private var m_text:TextField = null; // the TextField that we use to write our text
private var m_texture:BitmapData = null; // the texture that we're going to texture our TextField with
private var m_textBMD:BitmapData = null; // the BitmapData that we use to draw our TextField
private var m_drawPoint:Point = new Point; // Point used in drawing the final BitmapData
private var m_drawRect:Rectangle = new Rectangle;// the Rectangle we use to determine which part of the texture to take
// the texture we're using
[Embed(source="texture.jpg")]
private var m_textureImage:Class;
public function TestTexturedText()
{
this._createText( "Trebuchet Ms", 50.0 ); // create our textfield
this._getTexture(); // get our texture
// create textured text 1
var bmd:BitmapData = this.getTexturedText( "hello world" );
var b:Bitmap = new Bitmap( bmd );
b.x = 250.0;
b.y = 50.0;
this.addChild( b );
// create textured text 2
bmd = this.getTexturedText( "testing" );
b = new Bitmap( bmd );
b.x = 250.0;
b.y = 100.0;
this.addChild( b );
}
/**
* Get a BitmapData of the text we want, textured
* #param text The text we're looking at
* #param randomPos Should we take a random position from our text, or just start at (0,0)?
* #return A BitmapData object containing our textured text
*/
public function getTexturedText( text:String, randomPos:Boolean = true ):BitmapData
{
// set the text
this.m_text.text = text;
var tw:int = int( this.m_text.width + 0.5 ); // quick conver to int without clipping
var th:int = int( this.m_text.height + 0.5 );
// reuse our previous BitmapData if we can, rather than always creating a new one
if ( this.m_textBMD == null || this.m_textBMD.width < tw || this.m_textBMD.height < th )
this.m_textBMD = new BitmapData( tw, th, true, 0x00000000 );
else
this.m_textBMD.fillRect( this.m_textBMD.rect, 0x00000000 ); // clear the bitmapdata of the old rendering
// draw our text
this.m_textBMD.draw( this.m_text, null, null, null, null, true );
// set our draw rect position
this.m_drawRect.x = ( randomPos ) ? Math.random() * ( this.m_texture.width - tw ) : 0.0;
this.m_drawRect.y = ( randomPos ) ? Math.random() * ( this.m_texture.height - tw ) : 0.0;
this.m_drawRect.width = tw;
this.m_drawRect.height = th;
// get a new bitmap data (that we'll return) and copy our pixels, using the first bmd as an alpha mask
var ret:BitmapData = new BitmapData( tw, th, true, 0x00000000 );
ret.copyPixels( this.m_texture, this.m_drawRect, this.m_drawPoint, this.m_textBMD, this.m_drawPoint );
return ret;
}
// creates the TextField that we'll use to write our text
private function _createText( font:String, size:Number ):void
{
var tf:TextFormat = new TextFormat( font, size );
this.m_text = new TextField;
this.m_text.defaultTextFormat = tf;
this.m_text.autoSize = TextFieldAutoSize.LEFT;
// debug add it to the stage
this.m_text.x = 250.0;
this.addChild( this.m_text );
}
// gets the texture that we'll use to create our text
private function _getTexture():void
{
this.m_texture = ( ( new this.m_textureImage ) as Bitmap ).bitmapData;
// debug add it to the stage
var debug:Bitmap = new Bitmap( this.m_texture );
debug.scaleX = debug.scaleY = 0.2;
this.addChild( debug );
}
}
}
Some points:
It'll take a minor bit of work to turn it into a class - this is a main class (as in I use it to run the application)
Edit the m_textureImage embed to point to the texture you want to use, or alternatively, load one in.
Just call the function getTexturedText() to get a BitmapData with the text that you want
You can remove where I add the different items (texture, textfield, result) to the stage. That's just to show you what's going on
This is pure AS3, but it'll also work in flex

It's been a while since I used ActionScript, but I could give some ideas.
Simplest solution I can think of is that you can load an image in a sprite and then mask it using another sprite which has embedded text.
Other option, more difficult, is to render text as shapes by manually defining each letter as points. Then you could use Papervision3D to texture these shapes. I have not used Papervision3D before but as any other game engine allows texturing this should be possible.

As others have answered, using the text as a mask for an image is how this can be achieved in Flash or Flex.
The answer by divillysausages is a good one, but since you comment there that you are confused by the absence of Flex markup in that example, here is a minimal example in MXML markup:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application creationComplete="img.mask=txt" xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Canvas>
<mx:Text id="txt" cacheAsBitmap="true" fontSize="48" fontWeight="bold" text="LOREM IPSUM" />
<mx:Image id="img" cacheAsBitmap="true" autoLoad="true" source="http://farm3.static.flickr.com/2783/4092743591_3fb90fa599.jpg" />
</mx:Canvas>
</mx:Application>
The principle is the same as in the other answers, the text has to be a bitmap to be used as a mask for the image (here achieved by cacheAsBitmap="true").

Related

errorTip on Flex ComboBox

I have a tooltip on a ComboBox but I much prefer the styling of the errorTip
(with the "tail").
I have replaced the tooltip with an errorTip, but now the ComboBox has a red
border.
I'm still pretty much a newb... is there a way to override the red border on the
ComboBox so that its' border is back to good ol' black?
thanks,
Mark
You'd probably want to create your own custom tooltip as errorTip is reserved for the validation system.
There's some info here about how to create your own. It's fairly straightforward if you want something simple.
Here's something I've used in the past:
The Actionscript:
private var infoToolTip:ToolTip;
private function showToolTip(evt:MouseEvent, text:String):void
{
var pt:Point = new Point(evt.currentTarget.x, evt.currentTarget.y);
// Convert the targets 'local' coordinates to 'global' -- this fixes the
// tooltips positioning within containers.
pt = evt.currentTarget.parent.contentToGlobal(pt);
infoToolTip = ToolTipManager.createToolTip(text, pt.x, pt.y, "errorTipAbove") as ToolTip;
infoToolTip.setStyle("borderColor", "#87B846");
infoToolTip.setStyle("color", "white");
var yOffset:int = infoToolTip.height + 5;
infoToolTip.y -= yOffset;
infoToolTip.x -= 5
}
// Remove the tooltip
private function killToolTip():void
{
ToolTipManager.destroyToolTip(infoToolTip);
}
Using the toolTip:
<mx:Image source="{myImageSource}" mouseOver="showToolTip(event, 'Hello there!')" mouseOut="killToolTip()" />

Adobe Air application and {StageScaleMode.SHOW_ALL StageDisplayState.FULL_SCREEN}

I have problem with using next two properties together in one Air application, I need some functionality for show my application in full screen and scale for different displays. I mean , if user has 17" and other has 24" display my app should save proportionals. So, I've start to use these two properties StageScaleMode.SHOW_ALL StageDisplayState.FULL_SCREEN and see that internal canvas (buffer) is bigger than external, please see on the pictures the firs just StageDisplayState.FULL_SCREEN and the second StageDisplayState.FULL_SCREEN and StageScaleMode.SHOW_ALL.
Could you help me and say How to fix this problem?
Thanks.
StageScaleMode.SHOW_ALL ensures the entire content of your Flash Movie is always displayed. Depending on how large your content is, and where you place your images, this might well exceed the actual display width of your screen.
If you want your movie to always center on screen, but not scale its content, do it like this (fullscreen on click):
package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageDisplayState;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
public class Test extends MovieClip
{
private var content : Sprite;
public function Test ()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
content = new Sprite( );
content.graphics.beginFill( 0, 1 );
content.graphics.drawRect( 0, 0, 200, 100 );
content.graphics.endFill( );
addChild( content );
stage.addEventListener( Event.RESIZE, onResize );
content.addEventListener( MouseEvent.CLICK, onMouseClick );
}
public function onMouseClick (ev : Event) : void
{
stage.displayState = StageDisplayState.FULL_SCREEN;
}
public function onResize ( ev : Event ) : void
{
content.x = (stage.stageWidth - content.width) * .5;
content.y = (stage.stageHeight - content.height) * .5;
}
}
}
Then attach all your elements to the content Sprite instead of the stage.
I have developed my own approach for re-sizing without using SHOW_ALL property, but currently it correctly works only on the Windows systems.
The real issue here is that anything except stage.scaleMode = StageScaleMode.NO_SCALE; will not report the actual stage.stageWidth and stage.stageHeight, but will instead return the width and height of the authored dimensions.

flex htmlText in tooltip created by TooltipManager

I need to show htmlText in custom tooltip created by TooltipManager.
Below is the code.
myToolTip = ToolTipManager.createToolTip(text, pt.x, pt.y, "errorTipAbove") as ToolTip;
myToolTip.setStyle("borderColor", "#FAF8CC");
myToolTip.setStyle("color", "black");
myToolTip.setStyle("fontSize","9");
I have tried the following thing.
http://flexscript.wordpress.com/2008/08/19/flex-html-tooltip-component/
But that works if we set htmlText to a tooltip eg: button.
Please help.
Just look inside the code of ToolTipManagerImpl and you'll get your answer. Here's how createToolTip function creates toolTip:
public function createToolTip(text:String, x:Number, y:Number,
errorTipBorderStyle:String = null, context:IUIComponent = null):IToolTip{
var toolTip:ToolTip = new ToolTip();
var sm:ISystemManager = context ? context.systemManager as ISystemManager:
ApplicationGlobals.application.systemManager as ISystemManager;
sm.topLevelSystemManager.addChildToSandboxRoot(
"toolTipChildren", toolTip as DisplayObject);
if (errorTipBorderStyle){
toolTip.setStyle("styleName", "errorTip");
toolTip.setStyle("borderStyle", errorTipBorderStyle);
}
toolTip.text = text;
sizeTip(toolTip);
toolTip.move(x, y);
// Ensure that tip is on screen?
// Should x and y for error tip be tip of pointy border?
// show effect?
return toolTip as IToolTip;
}
So your answer is:
Create your own utility class with your own implementation of createToolTip function. Copy all the code from Adobe's implementation and change
var toolTip:ToolTip = new ToolTip(); -> var toolTip:ToolTip = new HTMLToolTip();
using component from the page you mentioned.
PS: You need to copy sizeTip function as well.

Trying to draw a Rectangle to a Custom Container in Flex4/AS3

So below is the code I have so far. For now I simply want to make it draw a square and have it show up. Right now when I click the area defined in MXML as <components:PaintArea width="100%" height="100%" id="paint-a"></PaintArea> it shows nothing; however, the actionlistener is getting triggered and an element is being added to the group. Not sure exactly what is going on... perhaps for some reason it doesn't think the element is drawable? Anyways thanks for the help!
public class PaintArea extends SkinnableContainer
{
private var canvas:Group;
public function PaintArea()
{
super();
canvas = new Group();
canvas.clipAndEnableScrolling = true;
canvas.percentHeight = 100;
canvas.percentWidth = 100;
canvas.addEventListener(MouseEvent.MOUSE_UP,drawRectangle);
this.addElement(canvas);
}
private function drawRectangle(e:MouseEvent):void{
var r:Rect = new Rect();
r.fill = new SolidColor(0x00ff00,.5);
canvas.addElement(r);
}
}
You should probably set the width and height of the rectangle r.
You could also use a BorderContainer (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/components/BorderContainer.html) - its a SkinnableContainer with a styleable border and fill

Styling UITextField

I've been playing around with different methods of determining at runtime the width of a "label" so that I can resize the "label" because I don't want it to truncate. I've finally found an easy solution through UITextField which allows me to set the .autoSize which is great! However, now I'm trying to "style" (simply adjust font and font size) of the UITextField but it seems that I have to do it manually with '.htmlText' (which I'll gladly accept if that is the ONLY way).
I'm using the .text to set the value of the label.
My test case involves a HBox (I'm actually using a Grid but they should be the same and I've done testing on both):
I style the HBox and the style carries through to the UITextField. I don't believe this will work for me because I have other components inside that I need to style differently.
I've tried: UITextFormat and TextFormat (I see that the .htmlText being updated accordingly but the output doesn't update. Then I noticed that whenever I called hbox.addChild(myUITextField) it would override the .htmlText
I've tried setting the style with myUITextField.setStyle("fontSize", 20) before and/or after the call to addChild neither of which made an impact on the display as per what I noted above.
Changes are being made but they seem to be overrided when I add it to the display.
So what do I need to do in order to style the UITextField aside from manually setting it along with my contents in .htmlText? Solutions not using UITextField is fine as long as there is some easy way of not truncating the text.
EDIT: I want to just do textField.setStyle('fontSize', 20) and expect that every time I change the text, I wouldn't need to use HTML to go with it (so I can just do textField.text = 'something else' and expect that it will still have a font size of 20). This is what I meant by not using .htmlText (sorry if I wasn't clear before).
2nd EDIT: I guess I should present the whole issue and maybe that'll clarify what I did wrong or couldn't achieve.
My intent is to have a Grid and add text into it. I do not want it to wrap or scroll so I add it to the next row in the Grid when the current row's children total width exceeds some number. In order to add it to the next row, I need to be able to calculate the width of the text. I would like to be able to style that text individually based on cases and there might be other components (like a TextInput). Essentially what I'm trying to accomplish is "Fill in the Blank".
I've included code to show what I'm currently doing and it works somewhat. It might be un-related to the original issue of styling but I can't figure out how to adjust the distance between each UITextField but aside from that this fits what I would like to accomplish. Relevant to the question is: I would like to change the way I style each UITextField (currently setting .htmlText) into something a bit straightforward though like I previously mentioned I'll gladly accept using .htmlText if that's the only solution.
So I have a Grid with x Rows in it and in each row, I have exactly one GridItem. Based on the input, I add UITextField and TextInput into the GridItem going on to the next GridItem when necessary. If you have a better way of doing so then that would be better but I guess what I really want is to find a different way of styling.
Also another problem, I'm not sure of the exact way to add a TextField into the display. I tried:
var t : TextField = new TextField();
t.text = "I'm a TextField";
hBox.addChild(t); // doesn't work
//this.addChild(t); // doesn't work either
But I get the following error:
TypeError: Error #1034: Type Coercion failed: cannot convert flash.text::TextField#172c8f9 to mx.core.IUIComponent.
Here's what I have that's working.
private function styleQuestionString(str : String) : String {
return '<FONT leading="1" face="verdana" size="20">' + str + '</FONT>';
}
private function loadQuestion(str : String) : void {
/* Split the string */
var tmp : Array = str.split("_");
/* Track the current width of the GridItem */
var curWidth : int = 0;
/* Display components that we will add */
var txtField : UITextField = null;
var txtInput : TextInput = null;
/* Track the current GridItem */
var curGridItem : GridItem = null;
/* Track the GridItem we can use */
var gridItemAC : ArrayCollection = new ArrayCollection();
var i : int = 0;
/* Grab the first GridItem from each GridRow of Grid */
var tmpChildArray : Array = questionGrid.getChildren();
for (i = 0; i < tmpChildArray.length; i++) {
gridItemAC.addItem((tmpChildArray[i] as GridRow).getChildAt(0));
}
curGridItem = gridItemAC[0];
gridItemAC.removeItemAt(0);
/* Used to set the tab index of the TextInput */
var txtInputCounter : int = 1;
var txtFieldFormat : UITextFormat = new UITextFormat(this.systemManager);
txtFieldFormat.leading = "1";
//var txtFieldFormat : TextFormat = new TextFormat();
//txtFieldFormat.size = 20;
/* Proper Order
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
*/
var txtLineMetrics : TextLineMetrics = null;
var tmpArray : Array = null;
curGridItem.setStyle("leading", "1");
var displayObj : DisplayObject = null;
for (var curItem : int= 0; curItem < tmp.length; curItem++) {
/* Using UITextField because it can be auto-sized! */
/** CORRECT BLOCK (ver 1)
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
***/
tmpArray = (tmp[curItem] as String).split(" ");
for (i = 0; i < tmpArray.length; i++) {
if (tmpArray[i] as String != "") {
txtField = new UITextField();
txtField.htmlText = styleQuestionString(tmpArray[i] as String);
//txtField.setTextFormat(txtFieldFormat); // No impact on output
txtLineMetrics = curGridItem.measureHTMLText(txtField.htmlText);
curWidth += txtLineMetrics.width + 2;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
curGridItem.setStyle("leading", "1");
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Configure the proper gap distance
curWidth = txtLineMetrics.width + 2;
}
displayObj = curGridItem.addChild(txtField);
}
}
//txtField.setColor(0xFF0000); // WORKS
if (curItem != tmp.length - 1) {
txtInput = new TextInput();
txtInput.tabIndex = txtInputCounter;
txtInput.setStyle("fontSize", 12);
txtInputCounter++;
txtInput.setStyle("textAlign", "center");
txtInput.width = TEXT_INPUT_WIDTH;
curWidth += TEXT_INPUT_WIDTH;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Decide if we need to add a buffer
curWidth = TEXT_INPUT_WIDTH + 2;
}
curGridItem.addChild(txtInput);
txtInputAC.addItem(txtInput);
/* Adds event listener so that we can perform dragging into the TextInput */
txtInput.addEventListener(DragEvent.DRAG_ENTER, dragEnterHandler);
txtInput.addEventListener(DragEvent.DRAG_DROP, dragDropHandler);
txtInput.addEventListener(DragEvent.DRAG_EXIT, dragExitHandler);
}
/* Add event so that this label can be dragged */
//txtField.addEventListener(MouseEvent.MOUSE_MOVE, dragThisLabel(event, txtField.text));
}
}
After about 8 hours of searching for a solution to what would seem to be such a simple issue I FINALLY stumbled on your posts here... Thankyou!!!
I have been stumbling around trying to get TextField to work and had no Joy, Label was fine, but limited formatting, and I need to be able to use embedded fonts and rotate. After reading the above this finally worked for me:
var myFormat:TextFormat = new TextFormat();
myFormat.align = "center";
myFormat.font = "myFont";
myFormat.size = 14;
myFormat.color = 0xFFFFFF;
var newTxt:UITextField = new UITextField();
newTxt.text = "HELLO";
addChild(newTxt);
newTxt.validateNow();
newTxt.setTextFormat(myFormat);
The order of addChild before the final 2 steps was critical! (myFont is an embedded font I am using).
One again... a thousand thankyou's...
John
EDIT BASED ON THE ASKERS FEEDBACK:
I didn't realize you wanted to just apply one style to the whole textfield, I thought you wanted to style individual parts. This is even simpler for you, won't give you any trouble at all :)
var textFormat: TextFormat = new TextFormat("Arial", 12, 0xFF0000);
myText.setTextFormat(textFormat);
Be aware that this sets the style to the text that is in the TextField, not necessarily future text you put in. So have your text in the field before you call setTextFormat, and set it again every time you change it just to be sure it stays.
It's probably best if you use a normal TextField as opposed to the component. If you still want the component you may need to call textArea.validateNow() to get it to update with the new style (not 100% sure on that one though) Adobe components are notoriously bad, and should be avoided. :(
To see all available options on the TextFormat object see here
END EDIT ---------
This is easy enough to just do with CSS in a normal old TextField.
var myCSS: String = "Have some CSS here, probably from a loaded file";
var myHTML: String = "Have your HTML text here, and have it use the CSS styles";
// assuming your textfield's name is myText
var styleSheet: StyleSheet = new StyleSheet();
styleSheet.parseCSS(myCSS);
myText.autoSize = TextFieldAutoSize.LEFT;
myText.styleSheet = styleSheet;
myText.htmlText = myHTML;
Supported HTML tags can be found here
Supported CSS can be found here
The reason you have a problem adding Textfield to containers is that it doesn't implement the IUIComponent interface. You need to use UITextField if you want to add it. However, that's presenting me with my own styling issues that brought me to this question.
A few things I know:
TextField is styled using the TextFormat definition, and applying it to the textfield. As Bryan said, order matters.
setStyle does nothing on IUITextField, and the TextFormat method doesn't seem to work the same as in normal TextFields. (Edit #2: Ahah. You need to override the "validateNow" function on UITextFields to use the setTextFormat function)
To autosize a TextArea, you need to do something like this (inheriting from TextArea):
import mx.core.mx_internal;
use namespace mx_internal;
...
super.mx_internal::getTextField().autoSize = TextFieldAutoSize.LEFT;
this.height = super.mx_internal::getTextField().height;
Found this code on, I think, on StackOverflow a while back. Apologies to the original author. But the idea is that you need to access the "mx_internal" raw textfield.
Text and TextArea have wrapping options. (Label does not). So if you set the explicit width of a Text object, you might be able to size using the measuredHeight option and avoid truncation.
(edit: That was #4, but stackoverflow parsed it into a 1...)

Resources