fadeout photochromic のパチモン
カメラの前でポーズをキメたら、"SHOOT" ボタンを押してください。
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/jztz
*/
package {
import com.bit101.components.PushButton;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import net.hires.debug.Stats;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
/**
* fadeout photochromic のパチモン
* @see http://www.youtube.com/watch?v=Mt_4mfuwTAU
* @see http://www.youtube.com/watch?v=5hmmjEhqS8k
* @see http://aquioux.net/blog/?p=2398
* @author Aquioux(Yoshida, Akio)
*/
public class Main extends Sprite {
private var webCam_:WebCamera; // ウェブカメラ
private var viewer_:Bitmap; // ウェブカメラビューア
private var shootButton_:PushButton; // 撮影ボタン
private var fadeoutImage_:FadeoutImage; // フェードアウトイメージ
private const CAMERA_WIDTH:int = 156;
private const CAMERA_HEIGHT:int = 156;
private const DISPLAY_SCALE:int = 3;
public function Main() {
// ウェブカメラ
try {
webCam_ = new WebCamera(CAMERA_WIDTH, CAMERA_HEIGHT, this.stage.frameRate);
} catch (err:Error) {
throw new Error(err.message);
return;
}
// ウェブカメラのイベント登録
webCam_.addEventListener(Event.COMPLETE, completeWebCameraHandler);
// ビューア
viewer_ = new Bitmap(new BitmapData(CAMERA_WIDTH, CAMERA_HEIGHT, true, 0x0));
viewer_.scaleX = DISPLAY_SCALE;
viewer_.scaleY = DISPLAY_SCALE;
viewer_.x = -DISPLAY_SCALE;
viewer_.y = -DISPLAY_SCALE;
addChild(viewer_);
}
// WebCamera の Event.COMPLETE を受け取る
private function completeWebCameraHandler(e:Event):void {
e.target.removeEventListener(Event.ACTIVATE, arguments.callee);
// 撮影ボタン
shootButton_ = new PushButton(this, 0, 0, "SHOOT", buttonHandler);
shootButton_.width = 50;
shootButton_.x = stage.stageWidth - shootButton_.width;
shootButton_.y = stage.stageHeight - shootButton_.height;
addEventListener(Event.ENTER_FRAME, update);
}
// 更新
private function update(e:Event):void {
viewer_.bitmapData = webCam_.update();
}
// ボタンハンドラ
private function buttonHandler(e:MouseEvent):void {
// 諸々後始末
removeChild(shootButton_);
removeEventListener(Event.ENTER_FRAME, update);
removeChild(viewer_);
// フェードアウトイメージ
fadeoutImage_ = new FadeoutImage(viewer_.bitmapData);
fadeoutImage_.addEventListener(Event.COMPLETE, completeFadeoutImageHandler);
fadeoutImage_.displayScale = DISPLAY_SCALE;
fadeoutImage_.x = -Math.ceil(DISPLAY_SCALE / 2);
fadeoutImage_.y = -Math.ceil(DISPLAY_SCALE / 2);
addChild(fadeoutImage_);
}
// 描画
private function completeFadeoutImageHandler(e:Event):void {
fadeoutImage_.draw();
}
}
}
//package {
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.IBitmapDrawable;
import flash.events.ActivityEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Camera;
import flash.media.Video;
/**
* WebCamera
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class WebCamera extends EventDispatcher {
private var bitmapData_:BitmapData; // ウェブカメラの画像
private var video_:Video; // ビデオ
private var matrix_:Matrix; // 鏡像になるようにさせるための Matrix
/**
* コンストラクタ
* @param cameraWidth カメラ幅
* @param cameraHeight カメラ高
*/
public function WebCamera(cameraWidth:int, cameraHeight:int, frameRate:int = 30) {
// 外部へ渡す BitmapData の生成
bitmapData_ = new BitmapData(cameraWidth, cameraHeight, true, 0x0);
// 鏡像になるよう、Matrix で左右反転
matrix_ = new Matrix(-1, 0, 0, 1, cameraWidth, 0);
// カメラ準備
var camera:Camera = Camera.getCamera();
if (camera) {
// camera のセットアップ
camera.setMode(cameraWidth, cameraHeight, frameRate);
// video のセットアップ
video_ = new Video(cameraWidth, cameraHeight);
video_.attachCamera(camera);
// カメラがアクティブになるまで待つ
camera.addEventListener(ActivityEvent.ACTIVITY, activeHandler);
} else {
throw new Error("カメラがありません。");
}
}
// ACTIVITY イベントハンドラ
private function activeHandler(e:ActivityEvent):void {
e.target.removeEventListener(ActivityEvent.ACTIVITY, arguments.callee);
dispatchEvent(new Event(Event.COMPLETE));
}
/**
* 更新
*/
public function update():BitmapData {
bitmapData_.draw(video_, matrix_);
return bitmapData_;
}
}
//}
//package {
//import aquioux.display.colorUtil.CycleRGB;
//import aquioux.display.bitmapDataEffector.GrayScale;
//import aquioux.display.bitmapDataEffector.Posterize;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.geom.Rectangle;
/**
* FadeoutImage
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class FadeoutImage extends Sprite {
/**
* ビューア
*/
public function set viewer(value:Bitmap):void { _viewer = value; }
private var _viewer:Bitmap;
/**
* 表示スケール
*/
public function set displayScale(value:int):void { _displayScale = value; }
private var _displayScale:int = 3;
private const DEGREE:int = 24;
private const FADE_VALUE:Number = 0.97;
private const FADE:ColorTransform = new ColorTransform(FADE_VALUE, FADE_VALUE, FADE_VALUE);
private const ZERO_POINT:Point = new Point();
private var inputBmd_:BitmapData;
private var bufferBmd_:BitmapData;
private var viewBmd_:BitmapData;
private var rect_:Rectangle;
private var color_:uint;
private var vectors_:Vector.<Vector.<uint>>;
private var vector_:Vector.<uint>
private var cntDraw_:int = 0;
private var cntUpdate_:int;
private var cntLen_:int;
public function FadeoutImage(bmd:BitmapData) {
inputBmd_ = bmd;
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
private function addedToStageHandler(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, arguments.callee);
// サイズの取得
var imageWidth:int = inputBmd_.width;
var imageHeight:int = inputBmd_.height;
// 各 BitmapData 生成
bufferBmd_ = new BitmapData(imageWidth, imageHeight, true, 0x00000000);
viewBmd_ = new BitmapData(imageWidth, imageHeight, true, 0xFF000000);
rect_ = inputBmd_.rect;
// ビューア
var viewer:Bitmap = new Bitmap(viewBmd_);
viewer.scaleX = _displayScale;
viewer.scaleY = _displayScale;
viewer.x = (stage.stageWidth - viewer.width) / 2;
viewer.y = (stage.stageHeight - viewer.height) / 2;
addChild(viewer);
// イメージデータフィルター生成と適用
new GrayScale().applyEffect(inputBmd_);
var posterize:Posterize = new Posterize();
posterize.degree = DEGREE;
posterize.applyEffect(inputBmd_);
var gradation:Vector.<uint> = posterize.gradation;
// 表示のためのデータ生成
vectors_ = new Vector.<Vector.<uint>>();
var bmdVector:Vector.<uint> = new Vector.<uint>();
var positionVector:Vector.<uint> = new Vector.<uint>();
for (var i:int = 0; i < DEGREE; i++) {
var threshold:uint = gradation[i];
bufferBmd_.fillRect(bufferBmd_.rect, 0x00000000);
bufferBmd_.threshold(inputBmd_, rect_, ZERO_POINT, "==", threshold, 0xFFFFFFFF, 0xFF, false);
bmdVector = bufferBmd_.getVector(rect_);
var c:int = 0;
for (var y:int = 0; y < imageHeight; y++) {
for (var x:int = 0; x < imageWidth; x++) {
if (bmdVector[c++] == 0xFFFFFFFF) positionVector.push(x, y);
}
}
var vector:Vector.<uint> = new Vector.<uint>();
vector = positionVector.concat();
vector.fixed = true;
vectors_[i] = vector;
positionVector.length = 0;
}
// 入力イメージデータ削除
inputBmd_.dispose();
// 表示色決定
color_ = CycleRGB.getColor32(Math.random() * 360 >> 0);
// COMPLETE イベント発効
dispatchEvent(new Event(Event.COMPLETE));
}
public function draw():void {
bufferBmd_.lock();
viewBmd_.lock();
bufferBmd_.fillRect(rect_, 0x00000000);
viewBmd_.colorTransform(rect_, FADE);
vector_ = vectors_[cntDraw_++];
cntLen_ = vector_.length;
cntUpdate_ = 0;
addEventListener(Event.ENTER_FRAME, update);
bufferBmd_.unlock();
viewBmd_.unlock();
}
private function update(e:Event):void {
var posX:uint;
var posY:uint;
var c:int = 5; // 一度の update で表示する pixel 数
while (--c) {
if (cntUpdate_ < cntLen_) {
posX = vector_[cntUpdate_];
posY = vector_[++cntUpdate_];
bufferBmd_.setPixel32(posX, posY, color_);
++cntUpdate_;
} else {
break;
}
}
viewBmd_.draw(bufferBmd_, null, null, BlendMode.LIGHTEN);
if (cntUpdate_ >= cntLen_) {
removeEventListener(Event.ENTER_FRAME, arguments.callee);
if (cntDraw_ < DEGREE) draw();
}
}
}
//}
//package aquioux.display.colorUtil {
/**
* コサインカーブで色相環的な RGB を計算
* @author Aquioux(YOSHIDA, Akio)
*/
/*public*/ class CycleRGB {
/**
* 32bit カラーのためのアルファ値(0~255)
*/
static public function get alpha():uint { return _alpha; }
static public function set alpha(value:uint):void {
_alpha = (value > 0xFF) ? 0xFF : value;
}
private static var _alpha:uint = 0xFF;
private static const PI:Number = Math.PI; // 円周率
private static const DEGREE120:Number = PI * 2 / 3; // 120度(弧度法形式)
/**
* 角度に応じた RGB を得る
* @param angle HSV のように角度(度数法)を指定
* @return 色(0xNNNNNN)
*/
public static function getColor(angle:Number):uint {
var radian:Number = angle * PI / 180;
var r:uint = (Math.cos(radian) + 1) * 0xFF >> 1;
var g:uint = (Math.cos(radian + DEGREE120) + 1) * 0xFF >> 1;
var b:uint = (Math.cos(radian - DEGREE120) + 1) * 0xFF >> 1;
return r << 16 | g << 8 | b;
}
/**
* 角度に応じた RGB を得る(32bit カラー)
* @param angle HSV のように角度(度数法)を指定
* @return 色(0xNNNNNNNN)
*/
public static function getColor32(angle:Number):uint {
return _alpha << 24 | getColor(angle);
}
}
//}
//package aquioux.display.bitmapDataEffector {
import flash.display.BitmapData;
import flash.filters.ColorMatrixFilter;
import flash.geom.Point;
/**
* ColorMatrixFilter による BitmapData のグレイスケール化(NTSC 系加重平均による)
* 参考:Foundation ActionScript 3.0 Image Effects(P106)
* http://www.amazon.co.jp/gp/product/1430218711?ie=UTF8&tag=laxcomplex-22
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class GrayScale implements IEffector {
private const R:Number = EffectorUtils.LUM_R;
private const G:Number = EffectorUtils.LUM_G;
private const B:Number = EffectorUtils.LUM_B;
private const MATRIX:Array = [
R, G, B, 0, 0,
R, G, B, 0, 0,
R, G, B, 0, 0,
0, 0, 0, 1, 0
];
private const FILTER:ColorMatrixFilter = new ColorMatrixFilter(MATRIX);
private const ZERO_POINT:Point = EffectorUtils.ZERO_POINT;
/*
* 効果適用
* @param value 効果対象 BitmapData
*/
public function applyEffect(value:BitmapData):BitmapData {
value.applyFilter(value, value.rect, ZERO_POINT, FILTER);
return value;
}
}
//}
//package aquioux.display.bitmapDataEffector {
import flash.display.BitmapData;
import flash.geom.Point;
/**
* paletteMap による BitmapData の減色
* 「実践画像処理入門」 培風館 内村圭一・上瀧剛 P16 「2.5 濃度値の量子化による減色処理」
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class Posterize implements IEffector {
/*
* 減色の段階
* @param value 段階
*/
public function set degree(value:uint):void {
// value の有効範囲は 2 ~ 256
if (value < 2) value = 2;
if (value > 256) value = 256;
if (_gradation) {
_gradation.fixed = false;
_gradation.length = 0;
} else {
_gradation = new Vector.<uint>();
}
var prevVal:uint = 0xFF;
for (var i:int = 0; i < 256; i++) {
var val:uint = uint(i / (256 / value)) * 255 / (value - 1);
rArray_[i] = val << 16;
gArray_[i] = val << 8;
bArray_[i] = val;
if (prevVal != val) {
_gradation.push(val);
prevVal = val;
}
}
_gradation.fixed = true;
}
// 減色化によって計算された gradation の値を格納する Vector
// degree を set することではじめて有効になる
// length は degree になる
public function get gradation():Vector.<uint> { return _gradation; }
private var _gradation:Vector.<uint>;
//
// paletteMap の引数となる各 Channel 用の Array
private var rArray_:Array = [];
private var gArray_:Array = [];
private var bArray_:Array = [];
private const ZERO_POINT:Point = EffectorUtils.ZERO_POINT;
/*
* コンストラクタ
*/
public function Posterize() {
degree = 8; // degree のデフォルト
}
/*
* 効果適用
* @param value 効果対象 BitmapData
*/
public function applyEffect(value:BitmapData):BitmapData {
value.paletteMap(value, value.rect, ZERO_POINT, rArray_, gArray_, bArray_);
return value;
}
}
//}
//package aquioux.display.bitmapDataEffector {
import flash.display.BitmapData;
/**
* BitmapDataEffector 用 interface
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ interface IEffector {
function applyEffect(value:BitmapData):BitmapData;
}
//}
//package aquioux.display.bitmapDataEffector {
import flash.geom.Point;
/**
* bitmapDataEffector パッケージ内のクラスで共通に使う定数など
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class EffectorUtils {
// BitmapData が備える各種メソッドの destPoint 用
static public const ZERO_POINT:Point = new Point(0, 0);
// グレイスケール用の各チャンネルの重みづけ
// NTSC系加重平均法(YIQ,YCbCr も同じ)
static public const LUM_R:Number = 0.298912;
static public const LUM_G:Number = 0.586611;
static public const LUM_B:Number = 0.114478;
}
//}