// forked from nengafl's nengafl /** * DisplacementMapFilterの練習 * 前フレームをフィードバックする、 * オーディオプレーヤのビジュアルプラグインでよくあるあれです。 */ package { import flash.display.Sprite; import flash.text.TextFormat; import flash.text.TextField; [SWF(width="465", height="465", frameRate="30")] public class Nengafl extends Sprite { public function Nengafl() { var width:uint = stage.stageWidth; var height:uint = stage.stageHeight; var nengaText:String = "<b>これからActionScript3.0を始める人が、あなたの作品を通してFlashの楽しさを知り<br>" + "ASの世界に足を踏み入れるきっかけになるような課題作品をつくってください!<br><br>" + "一番FORKされた方に、wonderfl からお年玉、なんと現金10万円を贈呈します! <br><br>" + "単純に数字を変えるだけで、面白い動きがでるのも OK! <br>" + "真面目な課題ももちろん OK! <br>" + "「面白いAS 」の定義はみなさんにおまかせします!<br>" + "Flash仲間が増え、Flash界が盛り上がるような作品の応募をお待ちしております。</b>"; var format:TextFormat = new TextFormat(); format.size = 12; format.color = 0x000000; var nengaTextBox:TextField = new TextField(); nengaTextBox.width = 465; nengaTextBox.height = 200; nengaTextBox.y = 100; nengaTextBox.multiline = true; nengaTextBox.wordWrap = true; nengaTextBox.border = false; nengaTextBox.defaultTextFormat = format; nengaTextBox.htmlText = nengaText; var dm:Deltamap = new Deltamap(width, height, nengaTextBox); addChild(dm); } } } import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.DisplayObject; import flash.display.Shape; import flash.filters.DisplacementMapFilter; import flash.filters.DisplacementMapFilterMode; import flash.filters.ColorMatrixFilter; import flash.geom.Point; import flash.geom.Rectangle; import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.events.Event; class DeltamapEvent { public static const SWAP:String = "swapped"; } interface IDeltaUnit { function init():void; /** * 入力座標にセットする前フレームの座標を指定する * x:入力座標x(0.0 - 1.0) * y:入力座標y(0.0 - 1.0) * source[0]:出力座標x(0.0 - 1.0) * source[1]:出力座標y(0.0 - 1.0) */ function source(x:Number, y:Number, source:Vector.<Number>):void; } class SimpleDeltaUnit implements IDeltaUnit { public function init():void {} public function source( x:Number, y:Number, source:Vector.<Number>):void { source[0] = x; source[1] = y; } } class XYScroll implements IDeltaUnit { private var dx:Number; private var dy:Number; public function init():void { dx = random(); dy = random(); trace(dx + ", " + dy); } public function source( x:Number, y:Number, source:Vector.<Number>):void { source[0] = x + dx; source[1] = y + dy; } private function random():Number { var d:Number = Math.random() * 0.01 - 0.005; if(0.0 < d) d += 0.01; else d -= 0.01; return d; } } class Spread implements IDeltaUnit { public function init():void {} public function source( x:Number, y:Number, source:Vector.<Number>):void { source[0] = (x - 0.5)*0.9 + 0.5; source[1] = (y - 0.5)*0.9 + 0.5; } } class SpreadTile implements IDeltaUnit { public function init():void {} public function source( x:Number, y:Number, source:Vector.<Number>):void { var ox:Number = Math.floor(x * 10) * 0.1; var oy:Number = Math.floor(y * 10) * 0.1; x -= ox; y -= oy; source[0] = ox + (x - 0.05)*0.9 + 0.05; source[1] = oy + (y - 0.05)*0.9 + 0.05; } } class SimpleSpin implements IDeltaUnit { private var sin:Number; private var cos:Number; public function init():void { var th:Number = 0.4; sin = Math.sin(th); cos = Math.cos(th); } public function source( x:Number, y:Number, source:Vector.<Number>):void { var tx:Number = x - 0.5; var ty:Number = y - 0.5; source[0] = (tx*cos - ty*sin)*0.9 + 0.5; source[1] = (tx*sin + ty*cos)*0.9 + 0.5; } } class NengaDeltaUnit implements IDeltaUnit { public function init():void {} public function source( x:Number, y:Number, source:Vector.<Number>):void { if(0.2 < y && 0.75 > y) { source[0] = x; source[1] = y; } else { source[0] = (x - 0.5)*0.95 + 0.5; source[1] = (y - 0.5)*0.95 + 0.5; } } } interface IPaletteUnit { function init():void; /** * パレットの色をHLSで指定する * t:入力パレットインデックス(0.0 - 1.0) * hls[0]:出力色相H(0.0 - 1.0) * hls[1]:出力輝度L(0.0 - 1.0) * hls[2]:出力彩度S(0.0 - 1.0) */ function hls(t:Number, hls:Vector.<Number>):void; } // 輝度を暗から明へ:グレースケール class SimplePaletteUnit implements IPaletteUnit { public function init():void {} public function hls(t:Number, hls:Vector.<Number>):void { hls[0] = 0.0; hls[1] = t; hls[2] = 0.0; } } // 色相を赤から黄へ、輝度を暗から明へ class Fire implements IPaletteUnit { public function init():void {} public function hls(t:Number, hls:Vector.<Number>):void { var tt:Number = t * t; hls[0] = 0.15 * tt; hls[1] = tt; hls[2] = 1.0; } } // 色相を黄から赤へ、輝度を明から暗へ class NegativeFire implements IPaletteUnit { public function init():void {} public function hls(t:Number, hls:Vector.<Number>):void { var ntt:Number = 1.0 - t * t; hls[0] = 0.15 * ntt; hls[1] = ntt; hls[2] = 1.0; } } // 色相環1周 class Rainbow implements IPaletteUnit { public function init():void {} public function hls(t:Number, hls:Vector.<Number>):void { var h:Number = 0.5 + 1.5 * t; if(1.0 < h) h -= 1.0; hls[0] = h; hls[1] = 0.5 * t * t; hls[2] = 1.0; } } class NengaPaletteUnit implements IPaletteUnit { public function init():void {} public function hls(t:Number, hls:Vector.<Number>):void { hls[0] = 0.0; hls[1] = 0.5*t + 0.5; hls[2] = 1.0; } } class Deltamap extends Bitmap { private static const R:Number = 10; private var point:Point; private var rect:Rectangle; private var frontBitmapData:BitmapData; private var backBitmapData:BitmapData; private var displacementMapFilter:DisplacementMapFilter; private var deltamapData:DeltamapData; private var palette:Palette; private var colorTransform:ColorTransform; private var shape:Shape; private var r:Number; public function Deltamap( width:uint, height:uint, firstFrame:DisplayObject=null) { point = new Point(0, 0); rect = new Rectangle(0, 0, width, height); bitmapData = new BitmapData(width, height, false); frontBitmapData = new BitmapData(width, height, false); backBitmapData = new BitmapData(width, height, false); if(null !== firstFrame) { frontBitmapData.draw( firstFrame, new Matrix(1, 0, 0, 1, firstFrame.x, firstFrame.y)); } // 使用するDeltaUnitの指定 // 最初は先頭のDeltaUnitが使用され、その後はランダム deltamapData = new DeltamapData( width, height, [ new NengaDeltaUnit(), new SimpleDeltaUnit(), new XYScroll(), new SimpleSpin(), new Spread(), new SpreadTile()]); // 使用するPaletteUnitの指定 // 最初は先頭のPaletteUnitが使用され、その後はランダム palette = new Palette([ new NengaPaletteUnit(), new SimplePaletteUnit(), new Rainbow(), new Fire(), new NegativeFire()]); displacementMapFilter = new DisplacementMapFilter( deltamapData.data, new Point(0, 0), BitmapDataChannel.GREEN, BitmapDataChannel.BLUE, width*2., height*2., DisplacementMapFilterMode.COLOR, 0xff000000, 1.0); colorTransform = new ColorTransform(1, 1, 1, 1, -1, -1, -1, -1); deltamapData.addEventListener(DeltamapEvent.SWAP, onSwapDeltamap); deltamapData.swap(); shape = new Shape(); with(shape.graphics) { beginFill(0xffffff); drawCircle(R, R, R); endFill(); } for each(var fun:Function in [ onEnterFrame, deltamapData.onEnterFrame, palette.onEnterFrame]) { addEventListener(Event.ENTER_FRAME, fun); } } private function onEnterFrame(event:Event):void { backBitmapData.applyFilter( frontBitmapData, rect, point, displacementMapFilter); backBitmapData.draw( shape, new Matrix(1, 0, 0, 1, mouseX-R, mouseY-R)); var rgba:Vector.<Array> = palette.rgba(); bitmapData.paletteMap( backBitmapData, rect, point, rgba[0], rgba[1], rgba[2], rgba[3]); frontBitmapData.draw(backBitmapData, null, colorTransform); } private function onSwapDeltamap(event:Event):void { displacementMapFilter.mapBitmap = deltamapData.data; } } import flash.events.EventDispatcher; class DeltamapData extends EventDispatcher { private var width:uint; private var height:uint; private var length:uint; private var resolution:Number; private var rect:Rectangle; private var frontBitmapData:BitmapData; private var backBitmapData:BitmapData; private var backVector:Vector.<uint>; private var backLine:uint; private var backSource:Vector.<Number>; private var backDeltaUnit:IDeltaUnit; private var deltaUnitArray:Array; public function DeltamapData( width:uint, height:uint, deltaUnits:Array=null) { this.width = width; this.height = height; length = width * height; resolution = 1.0 / width; rect = new Rectangle(0, 0, width, height); deltaUnitArray = deltaUnits || [new SimpleDeltaUnit()]; frontBitmapData = new BitmapData(width, height, false, 0xff000000); backBitmapData = new BitmapData(width, height, false, 0xff000000); backVector = new Vector.<uint>(length, true); backLine = 0; backSource = new Vector.<Number>(2, true); backDeltaUnit = deltaUnitArray[0]; backDeltaUnit.init(); } public function get data():BitmapData { return frontBitmapData; } public function swap():void { while(false === process()) {} } public function onEnterFrame(event:Event):void { process(); } private function process():Boolean { var swapped:Boolean = false; if(height === backLine) { backLine = 0; swapChannel(); swapped = true; dispatchEvent(new Event(DeltamapEvent.SWAP)); } else { var y:Number = resolution * backLine; var round:Function = Math.round; for(var i:uint=backLine*width, x:Number=0.0, end:uint=i+width; i<end; ++i, x+=resolution) { backDeltaUnit.source(x, y, backSource); var sx:Number = backSource[0]; if(0.0 > sx) sx = 0.0; else if(1.0 < sx) sx = 1.0; var sy:Number = backSource[1]; if(0.0 > sy) sy = 0.0; else if(1.0 < sy) sy = 1.0; backVector[i] &= 0xffff0000; backVector[i] |= (round(127.5*(backSource[0]-x+1.)) << 8) | (round(127.5*(backSource[1]-y+1.))); } ++backLine; } return swapped; } private function swapChannel():void { backBitmapData.setVector(rect, backVector); var tmpBitmapData:BitmapData = frontBitmapData; frontBitmapData = backBitmapData; backBitmapData = tmpBitmapData; backDeltaUnit = deltaUnitArray[ Math.floor(deltaUnitArray.length * Math.random())] backDeltaUnit.init(); } } class Palette { public static const LENGTH:uint = 256; public static const H60:Number = 1.0 / 6.0; public static const H120:Number = 1.0 / 3.0; public static const H180:Number = 0.5; public static const H240:Number = 2.0 / 3.0; private var frontPalette:Vector.<Vector.<Number>>; private var backPalette:Vector.<Vector.<Number>>; private var paletteUnitArray:Array; private var rgbaPalete:Vector.<Array>; private var rPalette:Array; private var gPalette:Array; private var bPalette:Array; private var aPalette:Array; private var frameCount:uint; private var swapFrames:uint; private var transitFrames:uint; public function Palette( paletteUnitArray:Array=null, swapFrames:uint=900, transitFrames:uint = 300) { this.paletteUnitArray = paletteUnitArray || [new SimplePaletteUnit()]; this.swapFrames = swapFrames; this.transitFrames = (swapFrames < transitFrames? swapFrames: transitFrames); frontPalette = new Vector.<Vector.<Number>>(LENGTH, true); backPalette = new Vector.<Vector.<Number>>(LENGTH, true); rgbaPalete = new Vector.<Array>(4); rPalette = new Array(LENGTH); gPalette = new Array(LENGTH); bPalette = new Array(LENGTH); aPalette = new Array(LENGTH); rgbaPalete[0] = rPalette; rgbaPalete[1] = gPalette; rgbaPalete[2] = bPalette; rgbaPalete[3] = aPalette; var paletteUnit:IPaletteUnit = paletteUnitArray[0]; paletteUnit.init(); for(var i:uint=0; i<LENGTH; ++i) { var t:Number = i / (LENGTH - 1); frontPalette[i] = new Vector.<Number>(3, true); backPalette[i] = new Vector.<Number>(3, true); paletteUnit.hls(t, frontPalette[i]); paletteUnit.hls(t, backPalette[i]); aPalette[i] = 0xff000000; } frameCount = 0; } public function rgba():Vector.<Array> { var t:Number = frameCount / transitFrames; if(1.0 >= t) { for(var i:uint=0; i<LENGTH; ++i) { var frontHls:Vector.<Number> = frontPalette[i]; var backHls:Vector.<Number> = backPalette[i]; var front:Number = frontHls[0]; var back:Number = backHls[0]; var h:Number = back + (front - back) * t; front = frontHls[1]; back = backHls[1]; var l:Number = back + (front - back) * t; front = frontHls[2]; back = backHls[2]; var s:Number = back + (front - back) * t; var max:Number; if(0.5 > l) max = l * (1.0 + s); else max = l * (1.0 - s) + s; var min:Number = 2.0 * l - max; var th:Number = h + H120; if(1.0 < th) th -= 1.0; var max_min:Number = max - min; var r:Number; if(H60 > th) r = min + max_min * th * 6.0; else if(H180 > th) r = max; else if(H240 > th) r = min + max_min*(H240 - th)*6.0; else r = min; th = h; var g:Number; if(H60 > th) g = min + max_min * th * 6.0; else if(H180 > th) g = max; else if(H240 > th) g = min + max_min*(H240 - th)*6.0; else g = min; th = h - H120; if(0.0 > th) th += 1.0; var b:Number; if(H60 > th) b = min + max_min * th * 6.0; else if(H180 > th) b = max; else if(H240 > th) b = min + max_min*(H240 - th)*6.0; else b = min; rPalette[i] = uint(Math.round(255 * r) << 16); gPalette[i] = uint(Math.round(255 * g) << 8); bPalette[i] = uint(Math.round(255 * b)); } } return rgbaPalete; } public function onEnterFrame(event:Event):void { if(transitFrames === frameCount) { var paletteUnit:IPaletteUnit = paletteUnitArray[ Math.floor(paletteUnitArray.length*Math.random())]; paletteUnit.init(); for(var i:uint=0, t:Number=0.0, dt:Number=1.0/LENGTH; i<LENGTH; ++i, t+=dt) { paletteUnit.hls(t, backPalette[i]); } } if(swapFrames === frameCount) { frameCount = 0; var tmpPalette:Vector.<Vector.<Number>> = frontPalette; frontPalette = backPalette; backPalette = tmpPalette; } else { ++frameCount } } } DisplacementMapFilterの練習