/** * ノスタルジックな雰囲気になった。かな? * またも ActionScript3 Thread Library 使って作りました :-) * * コメントが壊れたカタコトの英語でごめんなさい :-( */ 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=0xffffff)] /** * document class. */ public class Deploy extends Sprite { /** * constructor. */ public function Deploy() { addEventListener(Event.ADDED_TO_STAGE, initialize); } /** * initialize the object. */ private function initialize(evt:Event):void { // check is thread system already initialized? if (!Thread.isReady) { // fixed stage settings. stage.align = StageAlign.TOP_LEFT; stage.quality = StageQuality.MEDIUM; stage.scaleMode = StageScaleMode.NO_SCALE; SWFProfiler.init(this); // initialize the thread system. Thread.initialize(new EnterFrameThreadExecutor()); } // wake up main task thread. 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.Sprite; import flash.display.Stage; import flash.errors.IOError; import flash.events.Event; import flash.events.MouseEvent; import flash.filters.BitmapFilter; import flash.filters.BitmapFilterQuality; import flash.filters.BlurFilter; 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.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.utils.ParallelExecutor; /** * main task thread. */ internal class MainThread extends Thread { private var layer:DisplayObjectContainer; private var loader:LoadDataThread; /** * constructor. */ public function MainThread(layer:DisplayObjectContainer) { this.layer = layer; } /** * first executable. */ override protected function run():void { var waitor:Thread; // wake up handle background task. new HandleBackgroundThread(layer).start(); // wake up wait animation task. waitor = new WaitAnimationThread(layer); waitor.start(); waitor.join(); // stack next task(s). next(loadData); } /** * load data from flickr. */ private function loadData():void { loader = new LoadDataThread(); loader.start(); loader.join(); // stack next task(s). next(loadComplete); } /** * laod and display images. */ private function loadComplete():void { var data:Array, queue:FlickrImageQueue, container:Sprite; data = loader.data; // create photo image queue. queue = new FlickrImageQueue(); // create images container. container = new Sprite(); container.scaleX = 2; container.scaleY = 2; layer.addChild(container); // wake up fix position task thread. new FollowMousePositionThread(container).start(); // wake up display image task thread. new DisplayImageThread(container, queue).start(); // wake up load image task thread(s). new LoadImageThread(data, queue).start(); new LoadImageThread(data, queue).start(); new LoadImageThread(data, queue).start(); } /** * finalize the object. */ override protected function finalize():void { layer = null; } } /** * handle background noise task. */ internal class HandleBackgroundThread extends Thread { public static const TICK_THRESHOLD:uint = 3; private var layer:DisplayObjectContainer; private var background:Bitmap; private var tick:uint; /** * constructor. */ public function HandleBackgroundThread(layer:DisplayObjectContainer) { this.layer = layer; this.tick = 0; } /** * first executable. */ override protected function run():void { var s:Stage; s = layer.stage; // create background noise canvas. var bmd:BitmapData = new BitmapData(s.stageWidth / 2, s.stageHeight / 2, true, 0); // create bitmap for background. background = new Bitmap(bmd); background.scaleX = 2; background.scaleY = 2; background.alpha = .15; // add background to layer. layer.addChild(background); // update background immediately. update(); } /** * update background. */ private function update():void { var s:Stage; s = background.stage; // if removed background from stage or interrupted, halt process. if (!s || checkInterrupted()) return; // check triggerable? if (tick % TICK_THRESHOLD == 0) { // regenerate noise. background.bitmapData.noise(Math.random() * 100); } // increment tick in range. tick = ++tick % TICK_THRESHOLD; // stack next task(s). next(update); event(s, Event.RESIZE, resized); } private function resized(evt:Event):void { var s:Stage, pb:BitmapData, nb:BitmapData; // cast target as stage object. s = evt.target as Stage; // if missing target or interrupted, halt process. if (!s || checkInterrupted()) return; // recreate noise bitmap data. pb = background.bitmapData; nb = new BitmapData(s.stageWidth / 2, s.stageHeight / 2, true, 0); background.bitmapData = nb; // release old data resource. pb.dispose(); // reset tick counter. tick = 0; // update background noise immediately. update(); } /** * finalize the object. */ override protected function finalize():void { if (layer.contains(background)) { layer.removeChild(background); } layer = null; background.bitmapData.dispose(); background = null; } } /** * wait animation to til user's first action. */ internal class WaitAnimationThread extends Thread { private var layer:DisplayObjectContainer; private var message:DisplayObject; private var handlers:ParallelExecutor; /** * constructor. */ public function WaitAnimationThread(layer:DisplayObjectContainer) { this.layer = layer; } /** * first executable. */ override protected function run():void { var txt:TextField, fmt:TextFormat, bmd:BitmapData; // create format for message text. fmt = new TextFormat(); fmt.color = 0x000000; fmt.size = 48; fmt.font = 'sans-serif'; // create message text. txt = new TextField(); txt.autoSize = TextFieldAutoSize.LEFT; txt.defaultTextFormat = fmt; txt.text = 'click to start.'; // create message data. bmd = new BitmapData(txt.textWidth, txt.textHeight, true, 0); // draw text to bitmap data. bmd.draw(txt); // create message. message = new Sprite(); // add bitmap to message layer. Sprite(message).addChild(new Bitmap(bmd)); // hide message. will disappear later. message.visible = false; // add message to layer. layer.addChild(message); // create sub processes handler. handlers = new ParallelExecutor(); // stack fixed position task. handlers.addThread(new FixCenterPositionThread(message)); // wake up handling focus/blur task. handlers.addThread(new HandleFocusThread(message)); // start each sub processes. handlers.start(); // stack next task(s). event(message, MouseEvent.CLICK, hideMessage); } /** * hide message. * will execute when message is clicked. */ private function hideMessage(evt:MouseEvent):void { // interrupt sub processes. handlers.interrupt(); handlers = null; // wake up hide message task. new HideMessageThread(message).start(); } /** * finalize the object. */ override protected function finalize():void { layer = null; message = null; } } /** * hide message task. */ internal class HideMessageThread extends Thread { private var message:DisplayObject; /** * constructor. */ public function HideMessageThread(message:DisplayObject) { this.message = message; } /** * first executable. */ override protected function run():void { message.alpha *= .95; if (message.alpha < .02) { message.alpha = 0; } else { next(run); } } /** * finalize the object. */ override protected function finalize():void { var p:DisplayObjectContainer; p = message.parent; if (p) { p.removeChild(message); } message = null; } } /** * fix center position task thread. */ internal class FixCenterPositionThread extends Thread { private var target:DisplayObject; /** * constructor. */ public function FixCenterPositionThread(target:DisplayObject) { this.target = target; } /** * first executable. */ override protected function run():void { var s:Stage; s = target.stage; // if missing stage, halt process. if (!s) return; // fix target position. target.x = (s.stageWidth - target.width) / 2; target.y = (s.stageHeight - target.height) / 2; // if hide object yet, disappear. if (!target.visible) { target.visible = true; } // stack next task(s). event(s, Event.RESIZE, resized); interrupted(shutdown); } /** * will execute when stage is resized. */ private function resized(evt:Event):void { var s:Stage; s = target.stage; // if missing stage, halt process. if (!s) return; // fix target position. target.x = (s.stageWidth - target.width) / 2; target.y = (s.stageHeight - target.height) / 2; // stack next task(s). event(s, Event.RESIZE, resized); interrupted(shutdown); } /** * default interrupted handler. */ private function shutdown(...args):void { // do nothing. } override protected function finalize():void { target = null; } } /** * handling focus/blur task thread. */ internal class HandleFocusThread extends Thread { private var target:DisplayObject; /** * constructor. */ public function HandleFocusThread(target:DisplayObject) { this.target = target; } /** * first executable. */ override protected function run():void { // reset filters. target.filters = [ new BlurFilter(8, 8, BitmapFilterQuality.LOW), ]; // bind each events. events(); } /** * bind each events. */ private function events():void { // stack next task(s). event(target, MouseEvent.ROLL_OVER, focus); event(target, MouseEvent.ROLL_OUT, blur); interrupted(shutdown); } /** * will execute when target is focused. */ private function focus(evt:MouseEvent):void { // reset filters. target.filters = [ new BlurFilter(2, 2, BitmapFilterQuality.LOW), ]; // bind each events. events(); } /** * will execute when target is unfocused. */ private function blur(evt:MouseEvent):void { // reset filters. target.filters = [ new BlurFilter(8, 8, BitmapFilterQuality.LOW), ]; // bind each events. events(); } /** * default shutdown handler. */ private function shutdown(...args):void { // do nothing. } /** * finalize the object. */ override protected function finalize():void { target = null; } } /** * load data from flickr task thread. */ internal class LoadDataThread extends Thread { public static const YQL_URL:String = 'http://query.yahooapis.com/v1/public/yql'; private var loader:URLLoaderThread; private var _data:Array; public function get data():Array { return _data.concat(); } /** * constructor. */ public function LoadDataThread() { _data = []; } /** * first executable. */ override protected function run():void { var req:URLRequest, data:URLVariables; data = new URLVariables(); data['q'] = 'select * from flickr.photos.recent(64)'; data['format'] = 'json'; req = new URLRequest(); req.url = YQL_URL; req.data = data; loader = new URLLoaderThread(req); loader.start(); loader.join(); next(loadComplete); error(IOError, loadFailure); error(SecurityError, loadFailure); } /** * will execute when load data complete. */ private function loadComplete():void { var json:Object, row:Object; json = JSON.decode(loader.loader.data); if (!json.query || !json.query.results) return; for each (row in json.query.results.photo) { _data.push(new FlickrPhoto(row)); } } /** * will execute when load data failure. */ private function loadFailure(e:Error, t:Thread):void { trace("CAUGHT EXCEPTION: ", e); //trace(e.getStackTrace()); } } /** * */ internal class DisplayImageThread extends Thread { private var layer:DisplayObjectContainer; private var queue:FlickrImageQueue; /** * constructor. */ public function DisplayImageThread(layer:DisplayObjectContainer, queue:FlickrImageQueue) { this.layer = layer; this.queue = queue; } /** * first executable. */ override protected function run():void { if (queue.checkPoll()) { var i:uint, wrapper:Sprite; i = layer.numChildren; wrapper = new Sprite(); wrapper.addChild(queue.poll()); wrapper.x = Math.floor(i % 8) * 80; wrapper.y = Math.floor(i / 8) * 80; wrapper.blendMode = BlendMode.HARDLIGHT; new HandleFocusThread(wrapper).start(); layer.addChild(wrapper); } // stack next task(s). next(run); } /** * finalize the object. */ override protected function finalize():void { layer = null; queue = null; } } /** * load each image task thread. */ internal class LoadImageThread extends Thread { private var data:Array; private var queue:FlickrImageQueue; private var loader:LoaderThread; /** * constructor. */ public function LoadImageThread(data:Array, queue:FlickrImageQueue) { this.data = data; this.queue = queue; } /** * first executable. */ override protected function run():void { if (!data.length || checkInterrupted()) return; var row:FlickrPhoto, req:URLRequest, ctx:LoaderContext; row = data.shift() as FlickrPhoto; req = new URLRequest(row.thumbnailURL); ctx = new LoaderContext(true); loader = new LoaderThread(req, ctx); loader.start(); loader.join(); next(loadComplete); error(IOError, loadFailure); error(SecurityError, loadFailure); interrupted(shutdown); } /** * will execute when load data complete. */ private function loadComplete():void { // push new task into queue. queue.offer(Bitmap(loader.loader.content)); // stack next task(s). next(run); } /** * will execute when load data failure. */ private function loadFailure(e:Error, t:Thread):void { loader = null; // stack next task(s). next(run); } /** * default interrupted handler. */ private function shutdown(...args):void { // do nothing. } /** * finalize the object. */ override protected function finalize():void { loader = null; data = null; queue = null; } } /** * follow the mouse point */ internal class FollowMousePositionThread extends Thread { public static const MARGIN:Number = 150; private var target:DisplayObject; /** * constructor. */ public function FollowMousePositionThread(target:DisplayObject) { this.target = target; } /** * first executable. */ override protected function run():void { var s:Stage; s = target.stage; if (!s) return; target.x = MARGIN; target.y = MARGIN; event(s, MouseEvent.MOUSE_MOVE, moved); interrupted(shutdown); } /** * will execute when mouse moved. */ private function moved(evt:MouseEvent):void { update(); } /** * update target position. */ private function update():void { var s:Stage, ax:Number, ay:Number, dx:Number, dy:Number; s = target.stage; // if missing stage, halt process. if (!s) return; ax = s.mouseX / s.stageWidth; ay = s.mouseY / s.stageHeight; dx = MARGIN - (target.width - s.stageWidth + MARGIN * 2) * ax; dy = MARGIN - (target.height - s.stageHeight + MARGIN * 2) * ay; target.x += (dx - target.x) / 20; target.y += (dy - target.y) / 20; if (Math.abs(dx - target.x) < 1 && Math.abs(dy - target.y) < 1) { target.x = dx; target.y = dy; event(s, MouseEvent.MOUSE_MOVE, moved); interrupted(shutdown); } else { next(update); } } /** * default interrupted handler. */ private function shutdown(...args):void { // do nothing. } /** * finalize the object. */ override protected function finalize():void { target = null; } } /** * stack flickr image queue. */ internal class FlickrImageQueue { private var queue:Array; private var monitor:Monitor; /** * checking is task empty in queue. */ public function get isEmpty():Boolean { return queue.length <= 0; } /** * constructor. */ public function FlickrImageQueue() { queue = []; monitor = new Monitor(); } /** * checking already stack new task into queue. */ public function checkPoll():Boolean { var f:Boolean = true; if (isEmpty) { f = false; monitor.wait(); } return f; } /** * shift stack task. */ public function poll():Bitmap { return queue.shift(); } /** * */ public function offer(v:Bitmap):void { // push new data into queue. queue.push(v); // notify all wait task. monitor.notifyAll(); } } internal class FlickrPhoto { private var _id:String; public function get id():String { return _id; } private var _secret:String; public function get secret():String { return _secret; } private var _server:String; public function get server():String { return _server; } private var _farm:String; public function get farm():String { return _farm; } public function get thumbnailURL():String { return ['http://farm', farm, '.static.flickr.com/', server, '/', id, '_', secret, '_s.jpg'].join(''); } public function FlickrPhoto(data:Object) { this._id = data['id'] || ''; this._secret = data['secret'] || ''; this._server = data['server'] || ''; this._farm = data['farm'] || ''; } public function toString():String { return thumbnailURL; } } ノスタルジック?な感じ?