pre-rendering ver. forked from: forked from: Shining Text
前回のがあまりに重かったのでプリレンダ版です。
画面サイズ約1/4。アウトライン抽出の前に2値化処理を追加したので光線も細身。
ソースコードかなりいじったのでもうほぼ別物です…
/**
* Copyright matacat ( http://wonderfl.net/user/matacat )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/yGc2
*/
// forked from matacat's forked from: Shining Text
// forked from Kay's Shining Text
// 前回のがあまりに重かったのでプリレンダ版です。
// 画面サイズ約1/4。アウトライン抽出の前に2値化処理を追加したので光線も細身。
// ソースコードかなりいじったのでもうほぼ別物です…
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFormat;
[SWF(width="230", height="230", frameRate="30", backgroundColor="0xCFCFCF")]
public class Main extends Sprite
{
// 調整用
private var text:String = "wonderfl";
private var textSize:Number = 32;
private var textColor:uint = 0xFFFF9F;
private var backColor:uint = 0x000000;
// 光線の広がる倍率(X方向を増やすと総フレーム数が増える)
private var multiX:Number = 12;
private var multiY:Number = 10;
// 1フレームあたりの光線の移動量(減らすと総フレーム数が増える)
private var delta:Number = 20;
private var lightColor:uint = 0xFFFFBF;
// 光線の太さ
private var thickness:Number = 3;
private var filter:BlurFilter = new BlurFilter(12, 10, 2);
private var textOnly:Boolean = false;
// 内部処理用
private var screen:Bitmap = new Bitmap();
private var frames:Vector.<BitmapData> = new Vector.<BitmapData>();
private var totalFrames:int = 0;
private var currentFrame:int = 0;
private var message:TextField = new TextField();
private const SW:Number = stage.stageWidth;
private const SH:Number = stage.stageHeight;
private const CX:Number = SW >> 1;
private const CY:Number = SH >> 1;
public function Main()
{
Wonderfl.capture_delay(30);
var fm:TextFormat = new TextFormat(null, null, 0);
message.autoSize = fm.align = "center";
message.defaultTextFormat = fm;
message.selectable = false;
message.text = "Click to start";
message.x = SW - message.width >> 1;
message.y = SH - message.height >> 1;
addChild(message);
stage.addEventListener(MouseEvent.CLICK, init);
}
private function init(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.CLICK, init);
message.text = "Rendering now.\nPlease wait a minute...";
message.y = SH - message.height >> 1;
e.updateAfterEvent();
stage.addEventListener(Event.ENTER_FRAME, render);
}
private function render(e:Event):void
{
stage.removeEventListener(Event.ENTER_FRAME, render);
// initialize text
var tf:TextField = new TextField();
tf.defaultTextFormat = new TextFormat("Arial", textSize, textColor);
tf.autoSize = "left";
tf.selectable = false;
tf.text = this.text;
var tw:Number = tf.width;
var th:Number = tf.height;
tf.x = SW - tw >> 1;
tf.y = SH - th >> 1;
// binarize text picture
var bmp:BitmapData = new BitmapData(tw, th, false, 0);
bmp.draw(tf);
var binPic:Vector.<Vector.<Boolean>> = new Vector.<Vector.<Boolean>>();
var textLum:uint = ((textColor >> 16 & 0xFF) + (textColor >> 8 & 0xFF) + (textColor & 0xFF)) / 3;
var backLum:uint = ((backColor >> 16 & 0xFF) + (backColor >> 8 & 0xFF) + (backColor & 0xFF)) / 3;
var avg:uint = textLum + backLum >> 1;
var dir:Boolean = textLum > avg ? true : false;
for (var i:int = 0; i < tw ; i++) {
binPic[i] = new Vector.<Boolean>();
for (var j:int = 0; j < th; j++) {
var c:uint = bmp.getPixel(i, j);
c = ((c >> 16 & 0xFF) + (c >> 8 & 0xFF) + (c & 0xFF)) / 3;
binPic[i][j] = (dir && (c > avg)) || (!dir && (c < avg));
}
}
// get outline
var backOL:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
var frontOL:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
var nx:Number;
var ny:Number;
var len:int = 0;
for (i = 0; i < tw; i++) {
for (j = 0; j < th; j++) {
// 注目画素が背景色ならスルー
if (!binPic[i][j]) continue;
// 注目画素が端っこでないときで
if (!(i == 0 || i == th - 1 || j == 0 || j == tw - 1)) {
// 4-近傍(上下左右のピクセル)が文字色で埋まっているときスルー
if (binPic[i + 1][j] && binPic[i - 1][j] && binPic[i][j + 1] && binPic[i][j - 1]) continue;
}
// 以上の条件でスルーされなかったものをアウトラインとして抽出
backOL[len] = Vector.<Number>([nx = i - (tw >> 1), ny = j - (th >> 1)]);
frontOL[len] = Vector.<Number>([nx * multiX, ny * multiY]);
backOL[len][0] += CX;
backOL[len][1] += CY;
frontOL[len][0] += CX;
frontOL[len][1] += CY;
len++;
}
}
// rendering
var canvas:Sprite = new Sprite();
var g:Graphics = canvas.graphics;
canvas.addChild(tf);
var max:Number = SW + tw * multiX;
var min:Number = -max;
var temp:BitmapData = new BitmapData(SW, SH , false, backColor);
var rect:Rectangle = new Rectangle(0, 0, SW, SH);
var point:Point = new Point(0, 0);
for (var dx:Number = max; dx >= min; dx -= delta) {
var aAvg:Number = 0;
for (i = 0; i < len; i++) {
var xx:Number = backOL[i][0] - (frontOL[i][0] + dx);
if (xx < 0) xx = -xx;
if (xx <= delta) xx = delta;
xx = delta / xx;
aAvg += xx;
if (!textOnly) {
g.lineStyle(thickness, lightColor, xx);
g.moveTo(backOL[i][0], backOL[i][1]);
g.lineTo(frontOL[i][0] + dx , frontOL[i][1]);
}
}
tf.alpha = aAvg / len * 2;
temp.draw(canvas);
frames[totalFrames] = new BitmapData(SW, SH, false, backColor);
frames[totalFrames].applyFilter(temp, rect, point, filter);
temp.fillRect(rect, backColor);
g.clear();
frames[totalFrames].draw(canvas, null, null, "add");
totalFrames++;
}
// start
removeChild(message);
addChild(screen);
stage.addEventListener(Event.ENTER_FRAME, xAnimation);
}
private function xAnimation(e:Event):void
{
screen.bitmapData = frames[currentFrame];
currentFrame = currentFrame < totalFrames - 1 ? currentFrame + 1 : 0;
}
}
}