Work in progress

May 7th, 2009 by Hans Wichman

Ok small break from work, to post some other stuff.

Here is a detail from a painting I’ve been working on. It’s soo far from finished that it’s depressing but anyway :)

The skintones are coming along quite nicely, but they are still way too cold. The chestpiece and tattoo are done for now, so next up are the shoulders, hair and face. I think I’m going to especially enjoy the long hair, or scream in frustration, painting hair can do that to you :)

Detail

_global in ActionScript 3

May 7th, 2009 by Hans Wichman

Also known as:

  • unobtrusive logging
  • logging in swf without the debug player
  • In ActionScript 2, we had a _global object. Although this opened a world of gruelsome possibilities, it had it’s uses as well. For example it allowed to create reflection mechanism by traversing the package tree and unobtrusive logging by attaching your logger to the _global.log hook.

    This prevented you from having to import your logger everywhere and allowed you to easily switch to other loggers, or disable the current one.

    Although I do not find many uses for a _global object, this logging feature is one of them, and I expect that for some developers, the ability to store properties all over the place is a nice-to-have as well, although I strongly advise against that:).

    In addition, when you are using traces, your traces will not show up in the release player. If you are using FlashDevelop like me, you can redirect to traces to FlashConnect however.

    A simple trick you can employ is something like this:

    package
    {
    import org.flashdevelop.utils.FlashConnect;

    public class _global_com_yourdomain
    {
    public static var storage:Object = new Object();

    public static function log(…arguments):void
    {
    FlashConnect.trace (arguments);
    }

    }

    }

    Enjoy the abuse :)

    You can log, without having to import any classes, and change the logging functionality on the fly whenever you wish!

    Injection Swf Class hook

    May 2nd, 2009 by Hans Wichman

    Also known as:

    • keeping your sources out of the injection swf
    • decoupling your main class from your injection swf

    Ok you probably are not going to run into this situation a lot, but I decided to post it anyway. If you develop a lot of big applications in ActionScript 2 YOU SHOULD MOVE ON TO AS3… err… what I meant to say was: if you do, chances are you are developing your apps in FlashDevelop. In a lot of cases this means you are using an injection swf.

    This injection swf contains all your assets. There are two options to start your program once it’s loaded:

    • tell MTASC to execute your main class once all classes are initialized
    • call your main class method from the swf once everything is loaded

    Note that these two points might not be the same. We’ve been using a simple fla template which contains a preloader etc, and has its classes exported at frame 20 and its main application function called at frame 40. Although you might argue that an external preloader should have been used and the main fla should only have been 1 frame, that offers new problems, and anyway that is not what this post is about.

    Telling MTASC to execute the main class once all classes are initialized will cause initialization to occur at frame 20, using a call on frame 40 of the timeline will cause the application to be initialized at… yes frame 40.

    Another difference is that using the MTASC main class option, causes your class file to be excluded during compilation of the injection swf (which is good), using a class hook causes them to be compiled in the injection swf (which is bad).

    When the classes of your application are already compiled in the injection SWF, you will run into problems when refactoring the classes, and forget to recompile the injection swf. If you use the MTASC keep flag, all your classes will be removed, which is probably not what you want if you are using v2 components in the SWF.

    So the simplest option is not to use:
    com.mydomain.client.MainApplication.main (this);

    But to use:
    _global.com.mydomain.client.MainApplication.main (this);

    When you tell FlashDevelop to include your main class, the class will be injected in the injection swf during compilation, so _global.com.mydomain.client.MainApplication.main (this); will call an existing method and your swf will run just fine. On the other hand _global.com.mydomain.client.MainApplication.main (this); will not cause any application classes to be included in your injection swf during export, which is good too.

    So almost there, the only thing that’s not so good about this is that the injection swf still has knowledge about your application’s main class, since it’s right there in the _global.com..etc statement.

    To fix that you can replace:

    _global.com.mydomain.client.MainApplication.main (this);

    with

    _global.hook(this);

    and include this code in your main class:

    private static var _instance:MainClass = null;
    public static var _installHookTrigger:Boolean = _installHook();

    private static function _installHook():Boolean {
    _global.hook = function () {
    MainClass.main.apply (MainClass, arguments);
    }
    return true;
    }

    public static function main (pParent:MovieClip):Void {
    _instance = new MainClass(pParent);
    }

    Just compiling the main class into your swf will cause the _global.hook to be set, and calling it will cause your MainClass’s main function to be called.

    Spiffy ain’t it :) ?

    The Acoustic Doompriests

    April 20th, 2009 by Hans Wichman

    I just came back from an awesome trip to London, where I was visiting my good friend Patrick and his lovely girlfriend Rosa. Almost a year ago he got a trip to the studio from her as a birthday gift, and I was very lucky to be invited to join them. After leaving here on thursday afternoon around 5 pm, I finally arrived somewhere between 12 and 1 UK time, just in time for a good night’s sleep. Next day was full of good food, cold beer and lots of practice, and yes the more I screamed the less my vocal cords hurt ( or maybe it is ‘the more I drank’, but that part is a bit hazy ;) ). So we ended the day looking forward to the actual studio recording, although we might have been just a tad nervous.

    The next day Patrick and me went to the studio first, and Rosa and Iris (a good friend of hers from Finland) joined us later, cheering us on ( thanks for your support ladies :) ). To make a long story short, it rocked. The song came out more than acceptable, which is more than 2 guys without any real studio experience, who had only been able to rehearse for about a day, can hope for. I’m sure there’s lots of room for improvement, but that’s not the point. What’s more important that even while we didn’t get that much sleep (sorry my fault, even though I’d been drinking until 3, I’m still programmed to wake up at 7 arghgh), I came back home full of energy feeling like practising a lot again, drawing stuff etc etc.

    Since I remember promising somewhere in the first posts on this blog that I wouldn’t post too much personal stuff, I’m not going to tell you that the four of us had a very nice and relaxing afternoon/evening after the recording, having dinner and drinks in the pub, nor that I bought some very cool heavy metal T-shirts the next day in Camden for me and my sons to take back home.

    Anyway, all good things come to an end, as did this trip, and so I had to leave on Sunday, and I was back home again somewhere round 0:00 Amsterdam time welcomed by the three sleeping loves of my life, right on time for work on monday, and back behind the pc to post the results for your enjoyment ;) .

    First some photos:

    And ofcourse the track we recorded, a song called ‘watch over you’ from Alter Bridge, one of the coolest bands at this moment:

    If you are thinking about posting something saying we’re out of tune, don’t bother, we don’t care :) it can always be worse :)

    Include code in wordpress posts, the easy way

    April 11th, 2009 by Hans Wichman

    Although there are different plugins out there to help you display your code, there is a very simple solution as well. It does not provide code colouring, linenumbers and all that, but it does create a nice readable non-overflowing area to display your code.

    Simply put this in your style.css:

    div.codewrapper {
    border: 1px solid #DDD;
    max-height:200px;
    overflow:auto;
    width:450px;
    height: expression(this.scrollHeight > 200 ? “200px” : “auto”);
    white-space:pre;
    line-height: 100%;
    font-family: Verdana, Arial, Sans-Serif;
    font-size:10px;

    }

    Now if you create a div with class codewrapper, it will look like this:

    Although there are different plugins out there to help you display your code, there is a very simple solution as well. It does not provide code colouring, linenumbers and all that, but it does create a nice readable non-overflowing area to display your code.

    Simply put this in your style.css:

    div.codewrapper {
    border: 1px solid #DDD;
    max-height:200px;
    overflow:auto;
    width:450px;
    height: expression(this.scrollHeight > 200 ? “200px” : “auto”);
    white-space:pre;
    line-height: 100%;
    font-family: Verdana, Arial, Sans-Serif;
    font-size:10px;

    }

    arguments.caller.name

    April 10th, 2009 by Hans Wichman

    I was looking for a way to trace the name of the current function or the calling function and I came up with a hackish bit of code.
    Don’t know if anyone already came up with something like this but anyway, I thought it was pretty cool so I’d like to share it with the world. I’m still working on making it more general, and you should take care using it in a production environment (unless the compiler directive is set to false).

    package nl.trimm.lang
    {

    /**
    * @author JC Wichman
    */
    public class DebugUtil
    {

    public static function getCallingInfo(pInfo:String = “”, pFull:Boolean = false):String {
    return getInfo (pInfo, pFull, 2);
    }

    public static function traceCallingInfo(pInfo:String = “”, pFull:Boolean = false):void {
    trace (getCallingInfo (pInfo, pFull));
    }

    public static function getCallerInfo(pInfo:String = “”, pFull:Boolean = false):String {
    return getInfo(pInfo, pFull, 3)
    }

    public static function traceCallerInfo(pInfo:String = “”, pFull:Boolean = false):void {
    trace (getCallerInfo (pInfo, pFull));
    }

    private static function getInfo (pInfo:String = “”, pFull:Boolean = false, pIndex:Number = 0):String {
    CONFIG::EXTENDED_INFO_ON {
    try {
    throw new Error();
    } catch (e:Error) {
    var lTrace:String = e.getStackTrace().split(”\tat “)[pIndex+1];
    lTrace = pFull?lTrace:lTrace.split(”[”)[0];
    return (lTrace + “->” + pInfo);
    }
    }
    return pInfo;
    }
    }

    }

    Usage:
    - define a compiler directive and set it to true or false (can be optimized)

    Example:

    public class Test extends Sprite
    {

    public function Test():void
    {
    init();
    }

    private function init():void
    {
    trace (DebugUtil.getCallerInfo(”here”, true));
    trace (DebugUtil.getCallingInfo(”here”, true));
    }

    }

    Prints:

    Test()[D:\MY_DATA\checkouts\co_as3_library\\src\Test.as:21]->here
    Test/init()[D:\MY_DATA\checkouts\co_as3_library\src\Test.as:42]->here

    If you use DebugUtil.getCallingInfo(”here”, false) the source line is omited.

    In the next version that will be a global flag as well.

    I’m not sure what happens in other languages, you might need to replace \tat with \t

    Have fun!
    JC

    ps to use this in FlashDevelop goto Project->Properties->Compiler Options->Additional Compiler Options:
    and add -define=CONFIG::EXTENDED_INFO_ON,true

    ps2 in the Flash IDE, goto publish settings->actionscript settings->Config Constants and enter CONFIG::EXTENDED_INFO_ON
    In addition you can omit traces in the IDE, but you can’t in FlashDevelop

    ps3 you’ll need to run your swf in the debug player!!

    Grabbing bitmap data from external swf

    March 8th, 2009 by Hans Wichman

    Also known as:

    • alternative localization method
    • grabbing bitmap data from a loaded swf in actionscript 2
    • linkageExportForAS in JSFL not working for bitmaps

    For Heineken, one of TriMM’s clients, I had to work on a project called the photomodule. Originally written by Paul de Jong, it required some adaptions for another site. One of the adaptions was that the module had to be running in different languages, Portugese, English & Thai (and maybe even more I don’t know).

    You can watch it here by the way. Note that all the texts are localized.

    Since there was little time to complete the project, I didn’t want to get involved in playing with thai fonts and embedding the right glyphs. Originally the solution was to include every screen twice, once in English and once in Thai. However that wasn’t really going to work as well, since I needed it for 2 or 3 other languages, and I wanted to simplify/refactor the setup in order to get acqainted with it.

    So I came up with an idea: I would create a large jpg with all the button skins in it, and just load that skin. Then in Flash I’d have a couple component cutting the bitmap data out of the jpg. Well it turned out that most of the buttons were already available as separate images, and creating a large skin jpg was only going to be more work.

    So: external swf, lots of images in it with correct identifier. Brilliant, only there is no way to get to that data except from within that swf. Since in order to get the bitmap data, I have to pass a linkage id, and that won’t work outside the swf.

    However before I tell you how to cross that bridge, I hate doing things that can be automated. For example, right clicking 30 button images, and enabling ‘export for actionscript’ and ‘export in first frame’, which is the first step required if we ever want to be able to get to this data dynamically. So you think: well I’ll be damned, I’ve got JSFL up my sleeve. AHAHAHAHA! Gotcha! Macromedia/Adobe has another bug up their sleeve! Which is that you cannot change these properties for a bitmap through JSFL.

    So first step is to write a script that wraps all the bitmaps in a movieclip, and enables ‘export for actionscript’ and ‘export in first frame’ for those clips. First problem solved. Now the trick to get to the bitmap data is to create a clip within the swf that contains the images and attach a helper class to it, for example BitmapLoaderHelper.

    This class contains the following:

    class BitmapLoaderHelper extends MovieClip  {
    
    	public function loadBitmap (linkageID:String):BitmapData {
    		var tmp:MovieClip = attachMovie (linkageID, "tmp", 0);
    		var bmp:BitmapData = new BitmapData (tmp._width, tmp._height, true);
    		//draw the image
    		bmp.draw (tmp, new Matrix(), null, "normal");
    		//copy the alpha channel
    		bmp.draw (tmp, new Matrix(), null, "alpha");
    		tmp.removeMovieClip();
    		return bmp;
    	}
    }
    

    So from your main swf, the process for getting to the BitmapData becomes very simple:

  • load the appropriate skin swf for a given language code containing your bitmaps with predetermined id’s (eg ’select_button’)
  • after loading, attach the BitmapLoaderHelper clip from that swf, to its own timeline and maintain a reference to it
  • when needed retrieve bitmapdata from the swf through the helper
  • In our case we wrapped this whole thing up in a SkinFactory and SkinnedButtonComponents, which included automatic image retrieval for rollovers etc.

    And here is the JSFL I used:

    var dom = fl.getDocumentDOM();
    var lib = dom.library;
    var items = lib.items;
    var item;
    var newSel = [];
    
    for (var i=0; i<items.length; i++) {
     item = items[i];
     if (item.itemType == "movie clip")  {
      fl.trace ("deleting "+item.name);
      lib.deleteItem (item.name);
     }
    }
    
    for (var i=0; i<items.length; i++) {
     item = items[i];
     if (item.itemType == "bitmap")  {
    
      fl.trace ("Processing "+item.name);
      item.compressionType = "lossless";
      item.allowSmoothing = true;
    
      var newName = item.name.substring (0, item.name.indexOf("."));
      fl.trace ("Creating wrapper clip with name:"+newName);
    
      lib.addNewItem( "movie clip", newName);
      lib.editItem( newName );
    
      var tl = dom.getTimeline();
      tl.layers[0].name = "Wrapped Bitmap";
      lib.addItemToDocument( {x:0, y:0}, item.name );
    
      newSel[0] = tl.layers[ 0 ].frames[ 0 ].elements[ 0 ];
      dom.selectNone();
      dom.selection = newSel;
      var mat = dom.selection[0].matrix;
      mat.tx = 0;
      mat.ty = 0;
      dom.selection[0].matrix = mat;
      dom.selectNone();
      dom.exitEditMode();
    
      lib.selectItem (newName, true);
      lib.setItemProperty( "linkageExportForAS", true);
      lib.setItemProperty( "linkageExportInFirstFrame", true);
     }
    }
    
    lib.addNewItem ("movie clip", "BitmapLoaderHelper");
    lib.selectItem ("BitmapLoaderHelper", true);
    lib.setItemProperty( "linkageExportForAS", true);
    lib.setItemProperty( "linkageExportInFirstFrame", true);
    lib.setItemProperty( "linkageClassName", "!replacethiswithyourpackage!.BitmapLoaderHelper");
    

    The _lockroot of all evil!

    March 5th, 2009 by Hans Wichman

    Also known as: losing your _root reference during asynchronous calls.

    I was browsing through my drafts today, since I haven’t written anything in god knows how long, and I happily started deleting everything not having anything todo with AS3, since I finally moved on. But then I ran into some posts that I thought are still worth writing about, so I’ll try to post something every couple of days for the next couple weeks, to get rid of that backlog.

    For today, the _lockroot of all evil!

    If you don’t know what _lockroot is, stop reading now!

    Really you should stop reading now…

    I’m sure we’ve all heard (all us actionscript programmers that is), that you shouldn’t rely on _lockroot. I’m sure most of us have violated that rule too one time or another, until we finally found a framework that solves it (but that’s another story).

    So why shouldn’t we use _lockroot?

    Well there are a couple of things I’d like to share. I think that using _lockroot from movieclip classes, although it might not be a good thing, is not necessarily problematic, at least I haven’t run into any bugs doing that, but maybe you have.

    The real problem is that _lockroot seems to make it easy to access the _root for your subapplication even from non movieclip classes. Although this may seem to work correct, I’ve noticed on several occasions that _root in that case does not point to the _root of your swf, but to the _root of the swf that loaded the class you are referencing _root from (still with me?).

    Another subtle bug which I have encountered twice, with no way to reproduce in a cleanroom setting, is that _root resets itself from the subswf root to the parent root during an asynchronous to for example a webservice.

    For example:

    trace (_root); //_level0.mysubswf
    //do webservice call here and execute onResult

    onResult () {
    trace (_root); //traces _level0
    }

    So conclusion: if you do use _lockroot, do not use it from non movieclip classes.

    Any solutions?

    Well if we stay in the realm of easy hacking, you might want to create a global table storing your roots under an identifier. If you want a more solid approach, in our framework, each application and subapplication extends Application, which has a getTimeline() method.
    The applications are registered in an ApplicationRegistry, and you can retrieve their instances by passing in a class reference. So to retrieve the root for my sub application I might do:

    Application (Application.getRootApplication().getRegistree(com.objectpainters.SubApplication)).getTimeline();

    I have never needed to yet though, and if I had I’d probably wrap it, cache it or whatever.

    So play with _lockroot in as2 all you want, but keep this possibility of hard to track, incredibly nasty bugs in the back of your head.

    Visual Reflections in AS2

    November 23rd, 2008 by Hans Wichman

    I’m currently working on a project where I’m using a bit of reflection, and although I found a couple of reflection classes on the web, I was looking for something that worked out of the box without too much hassle.

    Basically I was looking for something that allowed me to do new Reflection(myClip) and be done with it 9 times out of 10.
    Making a reflection is not that hard (although I need to work on the alpha and falloff, will post a new version later), but getting it to work right with scaled clips with weird registration points was a little harder.

    Anyway I think I came up with something nice, and it performs quite well. You can download the source and a bunch of demo’s here Reflection demo’s

    Here is a list of features:

  • simple setup by default. In most cases doing new ReflectionManager (sourceClip) will suffice
  • default setup is a clip that fades from 40 to 0 alpha at 40% of the image
  • you can change the falloff height, overall alpha, and the redraw interval (eg to mirror video)
  • retrievable reflection and source clips to apply filters or otherwise control clips
  • redraw interval is disconnected from framerate, specify a low redraw interval to save system resources
  • works with scaled, non top left regpoints, and masked clip, using an autobounds detection algorithm
  • works with negative scaled clips
  • tries to warn you in most cases when the result is likely to be different from what you expect
  • very high performance
  • no dependencies on non flash classes
  • A couple samples:

    The code:

    1. import flash.display.BitmapData;
    2. import flash.geom.Matrix;
    3. import flash.geom.Rectangle;
    4.  
    5. /**
    6. * ReflectionManager manages the reflection for one clip.
    7. *
    8. * Short feature set:
    9. * - simple setup by default. In most cases doing new ReflectionManager (sourceClip) will suffice.
    10. * - default setup is a clip that fades from 40 to 0 alpha at 40% of the image.
    11. * - you can change the falloff height, overall alpha, and the redraw interval (eg to mirror video)
    12. * - retrievable reflection and source clips to apply filters or otherwise control clips
    13. * - redraw interval is disconnected from framerate, specify a low redraw interval to save system resources
    14. * - works with scaled, non top left regpoints, and masked clip, using an autobounds detection algorithm
    15. * - works with negative scaled clips
    16. * - tries to warn you in most cases when the result is likely to be different from what you expect
    17. * - very high performance
    18. * - no dependencies on non flash classes
    19. *
    20. * @author J.C. Wichman, TriMM Interactive Media (www.trimm.nl / j.c.wichman@trimm.nl),
    21. * ObjectPainters.com (blog.objectpainters.com / j.c.wichman@objectpainters.com)
    22. */
    23. class nl.trimm.movieclip.Reflection {
    24.  
    25. private var _sourceClip:MovieClip = null; //reference to the source you want to mirror
    26. private var _sourceClipParentBounds:Rectangle = null; //the source bounds as interpreted by the parent
    27. private var _sourceClipLocalBounds:Rectangle = null; //the source bounds as interpreted by source itself
    28. //same as parentbounds for nonscaled, nontranslated clips
    29.  
    30. private var _reflectionClip:MovieClip = null; //contains _reflectionClipContent and _reflectionClipMask
    31. private var _reflectionClipContent:MovieClip = null; //contains resultbitmap into which source reflection is drawn
    32. private var _reflectionClipMask:MovieClip = null; //contains alpha mask used as fade out effect
    33.  
    34. private var _redrawMatrix:Matrix = null; //contains all transformation necessary to draw a scaled,
    35. //translated and flipped reflection of the source into the dest
    36. private var _redrawRectangle:Rectangle = null; //specifies clip on redrawing method
    37.  
    38. private var _resultBitmap:BitmapData = null; //contains result of drawing source into dest through matrix
    39. private var _updateIntervalId:Number = null; //redraw interval, use null for static images
    40.  
    41. private var _autoClear:Boolean = false; //auto clear the bitmap before redrawing
    42.  
    43. /**
    44. * The purpose of this reflection class is to make it as simple as possible to provide a working reflection,
    45. * yet give you the freedom to adjust what's needed.
    46. *
    47. * Restrictions:
    48. * - source and reflection share the same parent
    49. * - source should not be scaled, if you want to scale, either wrap the scaled clip, or scale the parent
    50. *
    51. * @param pTarget the object you want to create a reflection for
    52. * @param pAlpha the alpha of the reflection 0..1
    53. * @param pHeight the height of the reflection, 1 is the whole image height is used as fall off, 0 is no reflection, and
    54. * everything in between (0 .. 1)
    55. * @param pFrameRate the framerate for the redraw method
    56. * @param pBounds explicit bounds that define the area to be reflected, if these are not passed, bounds are auto detected,
    57. * based on what's visible at the moment of creation. The bounds are relative to the source clips coordinate
    58. * system. In other words, IMAGINE you edit the source clip, draw a rectangle around the area you want to see
    59. * reflected, and write down those bounds, THAT area defines the boundsrectangle.
    60. * @param pCustomClip an empty clip on the same parent as pSource that you would like to display the reflection.
    61. * The only reason I can think of to do so, is that this will allow you to apply filters to this
    62. * clip through the IDE instead of filter classes, which might be convenient for non coders.
    63. * @param pAutoClear often it's not necessary to auto clear the canvas again before redrawing, UNLESS you are moving
    64. * your content, and your content is transparent. By default autoclear is off, but you might want
    65. * to enable it.
    66. */
    67. public function Reflection(pSource:MovieClip, pAlpha:Number, pHeight:Number, pFrameRate:Number, pBounds:Rectangle, pCustomClip:MovieClip, pAutoClear:Boolean) {
    68. if (pAlpha != null && (pAlpha < 0 || pAlpha > 1)) trace ("pAlpha has to be > 0 < 1");
    69. if (pHeight != null && (pHeight < 0 || pHeight > 1)) trace ("pHeight has to be > 0 < 1");
    70.  
    71. if (_global.warn == null) _global.warn = trace;
    72.  
    73. _sourceClip = pSource;
    74. _setupReflection(
    75. pAlpha == null?40:pAlpha * 100,
    76. pHeight == null?0.4:pHeight,
    77. pBounds,
    78. pCustomClip
    79. );
    80. if (pFrameRate != null) setAutoUpdateFrameRate (pFrameRate);
    81. if (pAutoClear != null) _autoClear = pAutoClear;
    82. }
    83.  
    84. /**
    85. * Create a gradient required to create the fading out alpha effect
    86. */
    87. private function _setupReflection(pAlpha:Number, pHeight:Number, pBounds:Rectangle, pCustomClip:MovieClip) {
    88. //store short reference to the parent that contains our source, it will contain the reflection and
    89. //reflection alpha mask as well
    90. var lReflectionParent:MovieClip = _sourceClip._parent;
    91. if (pCustomClip == null) {
    92. _reflectionClip = lReflectionParent.createEmptyMovieClip (
    93. "reflection_[" + _sourceClip._name + "]",
    94. lReflectionParent.getNextHighestDepth()
    95. );
    96. } else {
    97. if (pCustomClip._parent != lReflectionParent) {
    98. _global.warn ("WARNING:the clip you have passed is not on the same timeline as the clip to reflect.");
    99. }
    100. _reflectionClip = pCustomClip;
    101. }
    102.  
    103. if (pBounds == null || (pBounds.width == 0 && pBounds.height == 0)) {
    104. //now get the local bounds
    105. var lBounds:Object = _sourceClip.getBounds(_sourceClip);
    106. _sourceClipLocalBounds = new Rectangle (lBounds.xMin, lBounds.yMin, lBounds.xMax - lBounds.xMin, lBounds.yMax - lBounds.yMin);
    107.  
    108. //an important thing to note is that flash returns a _width and _height based on unmasked content.
    109. //if you have a 100x100 clip, with a topleft mask of 50x50, the bounds will be based on the 100x100 size,
    110. //not on the 50x50. This causes an issue in a number of cases, and reflecting the clip is one of them:)
    111. //therefore we are going to check the visible area using the bounds we just established to check if we need to
    112. //adjust our bounds
    113.  
    114. //we create a bitmap to draw the clip into first
    115. var lBoundsDetectionBitmap:BitmapData = new BitmapData (_sourceClipLocalBounds.width, _sourceClipLocalBounds.height, true, 0x0);
    116. var lBoundMatrix:Matrix = new Matrix();
    117. //enforce top left
    118. lBoundMatrix.translate ( -_sourceClipLocalBounds.x, -_sourceClipLocalBounds.y);
    119. lBoundsDetectionBitmap.draw (_sourceClip, lBoundMatrix);
    120. //now detect visible area in the bitmap, and translate it back to the original origin
    121. var lVisibleBounds:Rectangle = lBoundsDetectionBitmap.getColorBoundsRect(0xff000000, 0xff000000, true);
    122. lVisibleBounds.offset (_sourceClipLocalBounds.x, _sourceClipLocalBounds.y);
    123. //dispose tha bitmap
    124. lBoundsDetectionBitmap.dispose();
    125.  
    126. lVisibleBounds.width = Math.ceil (lVisibleBounds.width);
    127. _sourceClipLocalBounds = lVisibleBounds;
    128.  
    129. } else {
    130. _sourceClipLocalBounds = pBounds;
    131. }
    132.  
    133.  
    134. //we can establish parent bounds using _sourceClip.getBounds(lReflectionParent), but the bounds might have
    135. //been prepassed or altered by getColorBoundRect detection, established etc, so we do it through globalToLocal,
    136. //localToGlobal so instead of
    137. //var lBounds:Object = _sourceClip.getBounds(lReflectionParent);
    138. //_sourceClipParentBounds = new Rectangle (lBounds.xMin, lBounds.yMin, lBounds.xMax - lBounds.xMin, lBounds.yMax - lBounds.yMin);
    139. //we do
    140. var xMinyMin:Object = { x: _sourceClipLocalBounds.x, y: _sourceClipLocalBounds.y };
    141. var xMaxyMax:Object = { x: _sourceClipLocalBounds.x+_sourceClipLocalBounds.width, y: _sourceClipLocalBounds.y+_sourceClipLocalBounds.height};
    142. _sourceClip.localToGlobal (xMinyMin);
    143. _sourceClip.localToGlobal (xMaxyMax);
    144. lReflectionParent._parent.globalToLocal (xMinyMin);
    145. lReflectionParent._parent.globalToLocal (xMaxyMax);
    146.  
    147. var lWidth:Number = xMaxyMax.x - xMinyMin.x;
    148. var lHeight:Number = xMaxyMax.y - xMinyMin.y;
    149. _sourceClipParentBounds = new Rectangle (xMinyMin.x, xMinyMin.y, Math.abs(lWidth), Math.abs(lHeight));
    150. //note that for an unscaled clip with left top registration these bounds will be equal
    151. //we will use the parent bounds as much as possible, in short this means that if you are reflecting a movieclip
    152. //that is 1000x1000 but scaled down to 10%, the reflection will only create a bitmap sized 100x100
    153.  
    154. //beside offsetting, we need to establish a reflection height, since we might want a greater falloff than a complete reflection
    155. var lReflectionHeight:Number = _sourceClipParentBounds.height * pHeight;
    156.  
    157. //we create a clip to be used as mask, and we draw an alpha gradient on it (yes it's possible:))
    158. _reflectionClipMask = _reflectionClip.createEmptyMovieClip ("mask", 0);
    159. _reflectionClipMask.lineStyle (0, 0x0, 0);
    160. var lBottomGradientMatrix:Matrix = new Matrix();
    161. lBottomGradientMatrix.createGradientBox(_sourceClipParentBounds.width, lReflectionHeight, Math.PI/2, 0, 0);
    162. _reflectionClipMask.beginGradientFill ("linear", [0xffffff, 0xffffff], [pAlpha, 0], [0, 255], lBottomGradientMatrix);
    163. _reflectionClipMask.moveTo (0, 0);
    164. _reflectionClipMask.lineTo (_sourceClipParentBounds.width, 0);
    165. _reflectionClipMask.lineTo (_sourceClipParentBounds.width, lReflectionHeight);
    166. _reflectionClipMask.lineTo (0, lReflectionHeight);
    167. _reflectionClipMask.lineTo (0, 0);
    168. _reflectionClipMask.endFill();
    169.  
    170. //now create reflection container
    171. _reflectionClipContent = _reflectionClip.createEmptyMovieClip ("content", 1);
    172. //and we set up our resulting bitmap
    173. _resultBitmap = new BitmapData (_sourceClipParentBounds.width, lReflectionHeight, true, 0x0);
    174. _reflectionClipContent.attachBitmap (_resultBitmap, 0, "none", true);
    175.  
    176. //align the reflection clip to the source
    177. _reflectionClip._x = _sourceClipParentBounds.x;
    178. _reflectionClip._y = _sourceClipParentBounds.y + _sourceClipParentBounds.height;
    179.  
    180. if (lWidth < 0) {
    181. _global.warn (
    182. "Source clip has been scaled negatively horizontally, trying to adjust, this will hurt performance." +
    183. "Try to wrap the source clip in another clip."
    184. );
    185. _reflectionClipContent._xscale = -100;
    186. _reflectionClipContent._x += _reflectionClipContent._width;
    187. _reflectionClip._x -= _reflectionClipContent._width;
    188. }
    189. if (lHeight < 0) {
    190. _global.warn ("Source clip has been scaled negatively vertically, trying to adjust, this will hurt performance." +
    191. "Try to wrap the source clip in another clip."
    192. );
    193. _reflectionClipContent._yscale = -100;
    194. _reflectionClipContent._y += _reflectionClipContent._height;
    195. _reflectionClip._y -= _reflectionClipContent._height;
    196. }
    197.  
    198. //hook up the mask and set cacheAsBitmap to true so the alpha in the mask is applied to
    199. _reflectionClipContent.setMask (_reflectionClipMask);
    200. _reflectionClipContent.cacheAsBitmap = true;
    201. _reflectionClipMask.cacheAsBitmap = true;
    202.  
    203. //the redraw matrix is just one matrix, no matter how many operations you add. So it doesn't hurt
    204. //to keep operations separated for clarity. View the redraw matrix from the perspective of the source clip:
    205. _redrawMatrix = new Matrix();
    206. //first we 'enforce' a top left registration point
    207. _redrawMatrix.translate ( -_sourceClipLocalBounds.x, -_sourceClipLocalBounds.y);
    208. //then we scale the relation between parent and child, if the parent bounds for clip a say the clip is 200 wide,
    209. //and the local bounds say 400, appearently is has been scaled by 50 percent
    210. _redrawMatrix.scale (_sourceClipParentBounds.width/_sourceClipLocalBounds.width, _sourceClipParentBounds.height/_sourceClipLocalBounds.height);
    211. //now flip the whole thing upside down
    212. _redrawMatrix.scale (1, -1);
    213. //and move it down again to we can see it
    214. _redrawMatrix.translate (0, _sourceClipParentBounds.height);
    215.  
    216. //everything is tranformed so it fits within this rectangle:
    217. _redrawRectangle = new Rectangle (0, 0, _sourceClipParentBounds.width, lReflectionHeight);
    218.  
    219. //if original clip has filters attached, warn.
    220. if (_reflectionClip.filters.length > 0) {
    221. _global.warn (
    222. "Warning source clip has filters. You might want to wrap this clip in a parent clip without filters, " +
    223. "or pass correct bounds. You might want to add yourReflection.getReflection().filters = yourSourceClip.filters."
    224. );
    225. }
    226.  
    227. //redraw once to get started
    228. redraw();
    229. }
    230.  
    231. public function redraw() {
    232. if (_autoClear) {
    233. //seems faster than a fillRect but fillRect less processor intensive
    234. //_resultBitmap.dispose();
    235. //_resultBitmap = new BitmapData (_redrawRectangle.width, _redrawRectangle.height, true, 0x0);
    236. //_reflectionClipContent.attachBitmap (_resultBitmap, 0, "none", true);
    237. _resultBitmap.fillRect (_redrawRectangle, 0x0);
    238. }
    239. _resultBitmap.draw (_sourceClip, _redrawMatrix, null, null, _redrawRectangle, true);
    240. }
    241.  
    242. public function setAutoUpdateFrameRate(pFrameRate:Number) {
    243. //clear any old intervals
    244. if (_updateIntervalId != null) {
    245. clearInterval (_updateIntervalId);
    246. _updateIntervalId = null;
    247. }
    248.  
    249. //if no interval specified exit
    250. if (pFrameRate == null) return;
    251.  
    252. _updateIntervalId = setInterval (this, "redraw", Math.round (1000/pFrameRate));
    253. }
    254.  
    255. public function setAutoClear (pAutoClear:Boolean) {
    256. _autoClear = pAutoClear;
    257. }
    258.  
    259. public function getReflection():MovieClip {
    260. return _reflectionClip;
    261. }
    262.  
    263. public function getSourceClip():MovieClip {
    264. return _sourceClip;
    265. }
    266.  
    267. public function finalize() {
    268. setAutoUpdateFrameRate (null);
    269. _sourceClip = null;
    270. _reflectionClipContent.setMask (null);
    271. _reflectionClipContent = null;
    272. _reflectionClipMask = null;
    273.  
    274. _reflectionClip.removeMovieClip();
    275. _reflectionClip.filters = null;
    276. _reflectionClip = null;
    277.  
    278. _resultBitmap.dispose();
    279. }
    280.  
    281. }

    A couple of screenshots of the other demo’s:
    Difference process

    Finding character positions

    October 12th, 2008 by Hans Wichman

    Also known as:

  • finding the position of a character in a dynamic textfield with embedded fonts using actionscript 2
  • One of current projects involves some texteffects and I am loath to do anything on the timeline that can be done quicker by code. So my basic idea was (keeping the designer in me happy):

    Doing the design stuff at design time on stage:

  • put a dynamic textfield on stage
  • apply a font, fontsize, color, anti-aliasing
  • apply effects such as dropshadow, glow etc to make it look good
  • Doing the animate stuff at runtime through code:

  • break apart the stage textfield into little textfields that I could animate
  • At this point I was already thinking about converting the stage textfields to bitmaps but found I was thinking about optimising things too early, so back to KISS, basic principles first.

    Silly me, I thought it would be easy in actionscript 2 to get the position of a character in a dynamic textfield. As in:

    textfield.getCoordsOfChar (pIndex:Number)

    but apparently no such thing exists. Ofcourse if you have a monospaced font, there are other ways to accomplish this, but this solution works for both monospaced and other fonts.

    Although in actionscript 3 there is something like getCharIndexAtPoint, that is not quite what I needed.

    Using a trick I managed to implement it in actionscript 2. Note that this is still under development, being researched etc, so it’s not a general I-will-work-everytime-approach. Anyway the basic principle is this:

  • create a bitmap as large as your textfield
  • create two textformats derivatives of your textfield’s textformat and set the textcolor of one to white, the other to black
    now loop over the characters in your textfield (i = 0 to textfield.length), and:
    • apply first the white textformat to everything, then applying the black textformat to the range (i, i+1)
      (basically in every iteration of the loop you set everything to white except the character whose position you are looking for)
    • copy the textfield to the bitmap
    • perform a getColorBoundsRect on the bitmap and voila an approximation of the character position

    And I say approximation since it’s seems to be off by a couple of pixels, but close enough to be usuable. In addition large amounts of text will slow down the process considerably and small font sizes wreak havoc, but it’s good enough:

    Download the prototype here, it includes the Greensock Tween classes, but you can use anything you like.