forked from: forked from: マンデルブロ高速化のやる気のない高速化

by 9re forked from forked from: マンデルブロ高速化のやる気のない高速化 (diff: 307)
view in fullscreen mode!

@author kobayashi-taro @ kayac inc.
♥0 | Line 227 | Modified 2009-12-06 21:28:53 | MIT License
play

ActionScript3 source code

/**
 * Copyright 9re ( http://wonderfl.net/user/9re )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/6zCM
 */

// forked from uwi's forked from: マンデルブロ高速化のやる気のない高速化
// forked from toyoshim's マンデルブロ高速化
// forked from toyoshim's マンデルブロ @ Frocessing
package  
{
    // view in fullscreen mode!
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.ShaderJob;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.ShaderEvent;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.utils.ByteArray;
	import flash.utils.getTimer;
	import flash.utils.setTimeout;
	import org.libspark.betweenas3.BetweenAS3;
	/**
	 * @author kobayashi-taro @ kayac inc.
	 */
	public class Mandelbrot extends Sprite
	{
		
		private const WONDERFL:int = 465;
		private var _job:ShaderJob;
		private var _bd:BitmapData;
		private var _colors:ByteArray = new ByteArray;
		private var _timer:int;
		private var _kernel:MandelbrotRenderingKernel;
		private var _images:Array;
		private var _buffers:Array;
		private var _lock:Boolean;
		private var _jobSuspended:Boolean;
		private var _bufferIndex:int = 0;
		private var _basePoint:Point = new Point(-2.5, -2);
		private var _rectLength:Number = 4.0;
		private var _benchInfo:TextField;
		private var _imageLayer:Sprite;
		
		public function Mandelbrot() 
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
			stage.addEventListener(Event.MOUSE_LEAVE, onMouseLeave);
			
			addChild(_imageLayer = new Sprite);
			addChild(_benchInfo = new TextField);
			
			_benchInfo.defaultTextFormat = new TextFormat("_sans", 12, 0xffffff);
			_benchInfo.mouseEnabled = _benchInfo.mouseWheelEnabled = _benchInfo.selectable = false;
			_benchInfo.background = true;
			_benchInfo.backgroundColor = 0;
			
			buttonMode = true;
			tabEnabled = false;
			
			_images = [];
			_buffers = [];
			
			var image:Image;
			var bd:BitmapData;
			for (var i:int = 0; i < 2; ++i) {
				_images.push(image = new Image);
				_buffers.push(bd = new BitmapData(WONDERFL, WONDERFL));
				image.bitmapData = bd;
			}
			
			var spMask:Sprite = new Sprite;
			spMask.graphics.beginFill(0);
			spMask.graphics.drawRect(0, 0, WONDERFL, WONDERFL);
			spMask.graphics.endFill();
			mask = spMask;
			
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			
			_bd = new BitmapData(WONDERFL, WONDERFL);
			_kernel = new MandelbrotRenderingKernel;
			_kernel.addEventListener(ShaderEvent.COMPLETE, onJobComplete);
			
			doJob();
		}
		
		private function onJobComplete($event:ShaderEvent):void 
		{
			var t:Number = getTimer() - _timer;
			_benchInfo.text = "rendering time: " + t + " ms";
			_benchInfo.width = _benchInfo.textWidth + 4;
			_benchInfo.height = _benchInfo.textHeight + 4;
			
			_lock = false;
			var image:Image;
			_bufferIndex = 1 - _bufferIndex;
			image = getCurrentImage();
			image.x = 0;
			image.y = 0;
			image.scaleX = image.scaleY = 1;
			image.alpha = 0;
			BetweenAS3.to(image, { alpha:1 }, 0.25).play();
			setTimeout(clearBackBuffer, 250);
			image.bitmapData = _bd;
			_imageLayer.addChild(image);
		}
		
		private function clearBackBuffer():void
		{
			_images[1 - _bufferIndex].alpha = 0;
		}
		
		private function doJob():void {
			if (_lock) return;
			
			var image:Image = getCurrentImage();
			_basePoint.x += -image.x * _rectLength / WONDERFL;
			_basePoint.y += -image.y * _rectLength / WONDERFL;
			
			var scale:Number = WONDERFL / _rectLength;
			
			if (_kernel.baseX == _basePoint.x && _kernel.baseY == _basePoint.y && _kernel.scale == scale)
				return;
			
			image.alpha = 0;
			_kernel.basePoint = _basePoint;
			_kernel.scale = scale;
			
			_kernel.target = _bd;

			_lock = true;
			_timer = getTimer();
			_kernel.start();
		}
		
		private function onMouseWheel(e:MouseEvent):void 
		{
			if (_lock) return;
			
			var oldLength:Number = _rectLength;
			var scale:Number = (e.delta > 0) ? 0.625 : 1.6;
			_rectLength *= scale;
			scale = (oldLength - _rectLength) / WONDERFL;
			
			var image:Image = getCurrentImage();
			image.alpha = 0;
			
			_basePoint.x += scale * mouseX;
			_basePoint.y += scale * mouseY;
			
			doJob();
		}
		
		private function onMouseDown(e:MouseEvent):void 
		{
			getCurrentImage().startDrag();
		}
		
		private function onMouseUp(e:MouseEvent):void 
		{
			stopDragging();
		}
		
		private function onMouseLeave(e:Event):void 
		{
			stopDragging();
		}
		
		private function stopDragging():void {
			getCurrentImage().stopDrag();
			doJob();
		}
		
		private function getCurrentImage():Image {
			return _images[_bufferIndex];
		}
	}
}

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.ShaderEvent;
import flash.events.EventDispatcher;
import flash.geom.Point;

[Event(name = "complete", type = "flash.events.ShaderEvent")]
class MandelbrotRenderingKernel extends EventDispatcher {
	private var _baseX:Number;
	private var _baseY:Number;
	private var _scale:Number;
	private var _target:BitmapData;
	private var _hsvs:Array = [
	16711680, 16711723, 16711765, 16711808, 16711850, 16711892,
    16711935, 13893887, 11141375,  8388863,  5570815,  2818303,
         255,    11007,    22015,    33023,    43775,    54783,
       65535,    65493,    65450,    65408,    65365,    65322,
       65280,  2883328,  5635840,  8453888, 11206400, 13958912,
    16776960, 16766208, 16755200, 16744448, 16733440, 16722432, 0];

	public function MandelbrotRenderingKernel() {
	}
	
	public function set basePoint(value:Point):void {
		_baseX = value.x;
		_baseY = value.y;
	}
	
	public function start():void {
		_target.lock();
		var cx:Number, cy:Number;
		var p:Number;
		var test0:Number, test1:Number;
		var w:int = _target.width;
		var h:int = _target.height;
		for (var y:int = 0; y < h; y++) {
			for (var x:int = 0; x < w; x++) {
				cx = _baseX + x / _scale;
				cy = _baseY + y / _scale;
				_target.setPixel(x, y, _hsvs[calcDepth(cx, cy)]);
			}
		}
		_target.unlock();
		
		dispatchEvent(new ShaderEvent(ShaderEvent.COMPLETE));
	}
	
	private function calcDepth(cx:Number, cy:Number):int {
		// optimization technique
		var p:Number = Math.sqrt((cx - 0.25) * (cx - 0.25) + cy * cy);
		if (cx < (p - 2 * p * p + 0.25)) return 36;
		if ((cx + 1) * (cx + 1) + cy * cy < 0.0625) return 36;
		
		var r:Number = 0.0;
		var i:Number = 0.0;
		var n:int;
		for (n = 0; n <= 36 && r * r + i * i <= 4; n++) {
			var nr:Number = r * r - i * i + cx;
			var ni:Number = 2.0 * r * i + cy;
			r = nr;
			i = ni;
		}
		return n;
	}
	
	public function set target(value:BitmapData):void {
		_target = value;	
	}
	
	public function set scale(value:Number):void 
	{
		_scale = value;
	}
	public function get scale():Number { return _scale; }
	
	public function get baseX():Number { return _baseX; }
	
	public function get baseY():Number { return _baseY; }
}

class Image extends Sprite {
	private var _bitmap:Bitmap;
	
	public function Image() {
		addChild(_bitmap = new Bitmap);
	}
	
	public function get bitmapData():BitmapData {
		return _bitmap.bitmapData;
	}
	
	public function set bitmapData(value:BitmapData):void {
		_bitmap.bitmapData = value;
	}
}