/** * koko さんおたんじょうびおめでとーございまーす :-) * happy birthday な画像を使って happy birthday を作るよ。 * * @see http://www.flickr.com/groups/happybirthday/ */ package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageQuality; import flash.display.StageScaleMode; import flash.events.Event; import com.flashdynamix.utils.SWFProfiler; import org.libspark.thread.EnterFrameThreadExecutor; import org.libspark.thread.Thread; [SWF(width=465, height=465, frameRate=30, backgroundColor=0x000000)] /** * document class. */ public class HappyBirthdayKoko extends Sprite { /** * constructor. */ public function HappyBirthdayKoko() { Wonderfl.disable_capture(); addEventListener(Event.ADDED_TO_STAGE, initialize); } /** * initialize. */ private function initialize(evt:Event):void { removeEventListener(Event.ADDED_TO_STAGE, initialize); if (!Thread.isReady) { // setup stage. stage.align = StageAlign.TOP_LEFT; stage.quality = StageQuality.HIGH; stage.scaleMode = StageScaleMode.NO_SCALE; // Thread.initialize(new EnterFrameThreadExecutor()); // setup debugger. SWFProfiler.init(this); } new MainThread(this).start(); } } } import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BlendMode; import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.display.Graphics; import flash.display.Loader; import flash.display.PixelSnapping; import flash.display.Shape; import flash.display.Sprite; import flash.display.Stage; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Matrix; import flash.net.URLLoader; import flash.net.URLRequest; import flash.net.URLVariables; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.system.LoaderContext; import com.adobe.serialization.json.JSON; import org.libspark.betweenas3.BetweenAS3; import org.libspark.betweenas3.easing.*; import org.libspark.betweenas3.tweens.ITween; import org.libspark.thread.Monitor; import org.libspark.thread.Thread; import org.libspark.thread.threads.display.LoaderThread; import org.libspark.thread.threads.net.URLLoaderThread; import org.libspark.thread.threads.tweener.TweenerThread; import org.libspark.thread.utils.ParallelExecutor; /** * main thread. */ internal class MainThread extends Thread { private var _layer:DisplayObjectContainer; private var _jsonLoader:URLLoader; /** * constructor. */ public function MainThread(layer:DisplayObjectContainer) { _layer = layer; } /** * show waiting message. */ override protected function run():void { var th:Thread; th = new FixBackgroundThread(_layer); th.start(); th = new WaitAnimationThread(_layer); th.start(); th.join(); next(loadData); interrupted(empty); } /** * laod data from YQL. */ private function loadData():void { var req:URLRequest, data:URLVariables, th:Thread; _jsonLoader = new URLLoader(); data = new URLVariables(); data['format'] = "json"; data['diagnostics'] = "false"; data['q'] = "SELECT * FROM flickr.photos.search(50) WHERE group_id = '62402008@N00'"; req = new URLRequest(); req.url = "http://query.yahooapis.com/v1/public/yql"; req.data = data; th = new URLLoaderThread(req, _jsonLoader); th.start(); th.join(); next(animate); interrupted(empty); } /** * */ private function animate():void { var json:Object, row:Object, urls:Vector.<String>, pool:ImagePool, tasks:ParallelExecutor; json = JSON.decode(_jsonLoader.data); urls = new Vector.<String>(); for each (row in json.query.results.photo) { urls.push("http://farm" + row.farm + ".static.flickr.com/" + row.server + "/" + row.id + "_" + row.secret + "_s.jpg"); } Wonderfl.capture_delay(5); pool = new ImagePool(); tasks = new ParallelExecutor(); tasks.addThread(new LoadImageThread(urls, pool)); tasks.addThread(new LoadImageThread(urls, pool)); tasks.addThread(new LoadImageThread(urls, pool)); tasks.addThread(new LoadImageThread(urls, pool)); tasks.addThread(new LoadImageThread(urls, pool)); tasks.addThread(new LoadImageThread(urls, pool)); tasks.addThread(new DisplayImageThread(_layer, pool)); tasks.start(); } /** * empty task. */ private function empty(...args):void { // nothing to do. } /** * finalize the object. */ override protected function finalize():void { _layer = null; } } /** * */ internal class WaitAnimationThread extends Thread { private var _layer:DisplayObjectContainer; private var _message:DisplayObject; /** * */ public function WaitAnimationThread(layer:DisplayObjectContainer) { _layer = layer; } /** * */ override protected function run():void { var txt:TextField, data:BitmapData, stage:Stage; stage = _layer.stage; txt = new TextField(); txt.autoSize = TextFieldAutoSize.LEFT; txt.defaultTextFormat = new TextFormat("Lucida Grande", 36); txt.text = "click to start"; data = new BitmapData(txt.width, txt.height, true, 0); data.draw(txt); _message = new Bitmap(data, PixelSnapping.AUTO, true); _message.blendMode = BlendMode.INVERT; _message.x = (stage.stageWidth - _message.width) >> 1; _message.y = (stage.stageHeight - _message.height) >> 1; _layer.addChild(_message); event(stage, MouseEvent.CLICK, clickHandler); } private function clickHandler(evt:MouseEvent):void { var th:Thread; th = new HideMessageThread(_message); th.start(); } /** * */ override protected function finalize():void { _layer = null; _message = null; } } /** * */ internal class HideMessageThread extends Thread { private var _message:DisplayObject; /** * constructor. */ public function HideMessageThread(message:DisplayObject) { _message = message; } /** * hide message. */ override protected function run():void { var th:Thread; th = new TweenerThread(_message, { alpha: 0, time: 1, transition: "easeOutCubic" }); th.start(); th.join(); next(complete); interrupted(empty); } /** * remove message from parent. */ private function complete():void { var parent:DisplayObjectContainer; parent = _message.parent; if (parent) parent.removeChild(_message); } /** * empty task. */ private function empty(...args):void { // do nothing. } /** * finalize the object. */ override protected function finalize():void { _message = null; } } /** * load image from flickr. */ internal class LoadImageThread extends Thread { private var _data:Vector.<String>; private var _pool:ImagePool; private var _loader:Loader; /** * constructor. */ public function LoadImageThread(data:Vector.<String>, pool:ImagePool) { _data = data; _pool = pool; } /** * load image. */ override protected function run():void { var req:URLRequest, ctx:LoaderContext, th:Thread; if (!_data.length) return; _loader = new Loader(); req = new URLRequest(_data.shift()); ctx = new LoaderContext(true); th = new LoaderThread(req, ctx, _loader); th.start(); th.join(); next(complete); error(Error, failure); } /** * stack data to queue and rewind. */ private function complete():void { var data:BitmapData; data = new BitmapData(_loader.width, _loader.height, false, 0); data.draw(_loader); _pool.offer(data); _loader.unload(); _loader = null; next(run); } /** * error fallback. */ private function failure(e:Error, t:Thread):void { _loader.unload(); _loader = null; next(run); } /** * finalize the object. */ override protected function finalize():void { _data = null; _pool = null; } } /** * show image. */ internal class DisplayImageThread extends Thread { private var _layer:DisplayObjectContainer; private var _container:Sprite; private var _pool:ImagePool; private var _map:Vector.<uint>; private var _width:uint; private var _height:uint; private var _index:uint; /** * constructor. */ public function DisplayImageThread(layer:DisplayObjectContainer, pool:ImagePool) { _layer = layer; _pool = pool; _index = 0; } /** * create map. */ override protected function run():void { var i:uint, j:uint, l:uint, m:uint, txt:TextField, map:BitmapData, stage:Stage; stage = _layer.stage; txt = new TextField(); txt.autoSize = TextFieldAutoSize.LEFT; txt.defaultTextFormat = new TextFormat("Arial", 18, 0x000000);//, true); txt.text = "Happy\nBirthday\nKoko"; _width = txt.width >> 0; _height = txt.height >> 0; map = new BitmapData(_width, _height, true, 0); map.draw(txt); _map = new Vector.<uint>(); for (i=0; i<_height; i++) { for (j=0; j<_width; j++) { if (map.getPixel32(j, i)) { _map.push(i*_width+j); } } } map.dispose(); _container = new Sprite(); _container.transform.matrix = new Matrix(.08, 0, 0, .08, stage.stageWidth>>1, stage.stageHeight>>1); _layer.addChild(_container); next(update); } private function update():void { var i:uint, data:BitmapData, bitmap:Bitmap, th:Thread, tw:ITween, angle:Number, strength:Number, tx:Number, ty:Number; if (_pool.check()) { bitmap = new Bitmap(_pool.pickup()); i = Math.random() * _map.length >> 0; i = _map.splice(i, 1).pop(); tx = int(i % _width) * bitmap.width - _width * bitmap.width / 2; ty = int(i / _width) * bitmap.height - _height * bitmap.height / 2; angle = Math.random() * Math.PI * 2; strength = Math.random() * 200 / _container.scaleX; bitmap.alpha = 0; bitmap.x = tx + Math.cos(angle) * strength; bitmap.y = ty + Math.sin(angle) * strength; bitmap.scaleX = bitmap.scaleY = 2 / _container.scaleX; tw = BetweenAS3.serial( BetweenAS3.addChild(bitmap, _container), BetweenAS3.to(bitmap, { x: tx, y: ty, scaleX: 1, scaleY: 1, alpha: 1 }, 1.5, Expo.easeIn) ); tw.play(); } next(_map.length ? update : complete); } private function complete():void { var tw:ITween; tw = BetweenAS3.tween(_container, { transform: { colorTransform: { redMultiplier: 5, greenMultiplier: 5, blueMultiplier: 5 } }, _glowFilter: { color: 0xFFFFFF, alpha: .5, blurX: 32, blurY: 32 } }, { _glowFilter: { color: 0xFFFFFF, alpha: 0, blurX: 0, blurY: 0 } }, 1.6, Cubic.easeIn); tw = BetweenAS3.serial( BetweenAS3.delay(tw, 1.6), BetweenAS3.reverse(tw) ); tw.play(); } /** * */ override protected function finalize():void { _layer = null; _container = null; _pool = null; } } /** * fix background layer. */ internal class FixBackgroundThread extends Thread { private var _layer:DisplayObjectContainer; private var _background:Shape; /** * constructor. */ public function FixBackgroundThread(layer:DisplayObjectContainer) { _layer = layer; } /** * */ override protected function run():void { _background = new Shape(); _layer.addChild(_background); update(); } private function update():void { var stage:Stage, gr:Graphics; stage = _layer.stage; if (!stage) return; gr = _background.graphics; gr.clear(); gr.beginFill(0); gr.drawRect(0, 0, stage.stageWidth, stage.stageHeight); gr.endFill(); event(stage, Event.RESIZE, resizeHandler); interrupted(empty); } private function resizeHandler(evt:Event):void { update(); } private function empty(...args):void { // nothing to do. } /** * finalize the object. */ override protected function finalize():void { _layer.removeChild(_background); _layer = null; _background = null; } } /** * image pool */ internal class ImagePool { private var _pool:Vector.<BitmapData>; private var _monitor:Monitor; /** * check pool is empty. */ public function get isEmpty():Boolean { return _pool.length == 0; } /** * constructor. */ public function ImagePool() { _pool = new Vector.<BitmapData>(); _monitor = new Monitor(); } /** * check data available. */ public function check():Boolean { var f:Boolean = true; if (isEmpty) { f = false; _monitor.wait(); } return f; } /** * pickup image. */ public function pickup():BitmapData { return _pool[int(Math.random() * _pool.length)]; } /** * stack new data. */ public function offer(data:BitmapData):void { _pool.push(data); _monitor.notifyAll(); } } Happy Birthday Koko ;-)