Dither
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/qYJO
*/
package {
import flash.geom.Matrix;
//import aquioux.display.bitmapDataEffector.GrayScale;
//import aquioux.utils.ButtonMediator;
import com.bit101.components.PushButton;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.system.LoaderContext;
/**
* ハーフトーニング(ランダムディザと組織的ディザ)
* @see http://aquioux.net/blog/?p=2688
* @author YOSHIDA, Akio (Aquioux)
*/
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#FFFFFF")]
public class Main extends Sprite {
// ビューア BitmapData
private var viewBmd_:BitmapData;
// BitmapData#getVector で得られる値
private var sourcePixelList_:Vector.<uint>; // ソース画像
private var grayPixelList_:Vector.<uint>; // グレイスケール画像
// BitmapData#setVector 用
private var rect_:Rectangle;
// ディザクラス
private var randomDither_:RandomDither; // ランダムディザ法
private var orderedDither_:OrderedDither; // 組織的ディザ法
// ボタン管理
private var buttonMediator_:ButtonMediator;
// コンストラクタ
public function Main() {
var url:String = "http://assets.wonderfl.net/images/related_images/3/39/39f6/39f64cad1c50914c2c459386feeddf6d915cea06m";
var loader:Loader = new Loader();
loader.load(new URLRequest(url), new LoaderContext(true));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
}
private function completeHandler(event:Event):void {
// ソースイメージ
var loadedBmd:BitmapData = event.target.loader.content.bitmapData;
var sourceBmd:BitmapData = new BitmapData(180, 215);
sourceBmd.draw(loadedBmd, new Matrix(180/200, 0, 0, 215/200));
rect_ = sourceBmd.rect;
sourcePixelList_ = sourceBmd.getVector(rect_);
// グレイスケール BitmapData
var grayBmd:BitmapData = sourceBmd.clone();
new GrayScale().applyEffect(grayBmd);
grayPixelList_ = grayBmd.getVector(rect_);
grayBmd.dispose();
// ビューア
viewBmd_ = sourceBmd;
var viewBm:Bitmap = new Bitmap(viewBmd_);
viewBm.x = (stage.stageWidth - viewBm.width ) / 2 >> 0;
viewBm.y = (stage.stageHeight - viewBm.height ) / 2 >> 0;
addChild(viewBm);
// ディザクラス
randomDither_ = new RandomDither();
orderedDither_ = new OrderedDither();
// ボタン
var buttonWidth:int = 50;
var buttonHeight:int = 20;
var button0:PushButton = new PushButton(this, 0, buttonHeight * 0, "Random", random);
var button1:PushButton = new PushButton(this, 0, buttonHeight * 1, "Bayer", bayer);
var button2:PushButton = new PushButton(this, 0, buttonHeight * 2, "Halftone", halftone);
var button3:PushButton = new PushButton(this, 0, buttonHeight * 3, "Screw", screw);
var button4:PushButton = new PushButton(this, 0, buttonHeight * 4, "Screw2", screw2);
var button5:PushButton = new PushButton(this, 0, buttonHeight * 5, "Center", center);
var button6:PushButton = new PushButton(this, 0, buttonHeight * 6, "Dot", dot);
var button7:PushButton = new PushButton(this, 0, buttonHeight * 7, "Original", originalHander);
button0.width = buttonWidth;
button1.width = buttonWidth;
button2.width = buttonWidth;
button3.width = buttonWidth;
button4.width = buttonWidth;
button5.width = buttonWidth;
button6.width = buttonWidth;
button7.width = buttonWidth;
button0.height = buttonHeight;
button1.height = buttonHeight;
button2.height = buttonHeight;
button3.height = buttonHeight;
button4.height = buttonHeight;
button5.height = buttonHeight;
button6.height = buttonHeight;
button7.height = buttonHeight;
// ボタン管理
buttonMediator_ = new ButtonMediator();
buttonMediator_.prevButton = button7;
}
// オリジナル画像
private function originalHander(event:Event):void {
// ボタンの処理
buttonMediator_.buttonChange(PushButton(event.target));
viewBmd_.setVector(rect_, sourcePixelList_);
}
// ランダムディザ
private function random(event:Event):void {
// ボタンの処理
buttonMediator_.buttonChange(PushButton(event.target));
// ディザ実行
viewBmd_.setVector(rect_, grayPixelList_);
randomDither_.execute(viewBmd_);
}
// 組織的ディザ(Bayer 型)
private function bayer(event:Event):void {
// ボタンの処理
buttonMediator_.buttonChange(PushButton(event.target));
// ディザ実行
viewBmd_.setVector(rect_, grayPixelList_);
orderedDither_.matrix = Vector.<uint>([
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
]);
orderedDither_.execute(viewBmd_);
}
// 組織的ディザ(ハーフトーン型)
private function halftone(event:Event):void {
// ボタンの処理
buttonMediator_.buttonChange(PushButton(event.target));
// ディザ実行
viewBmd_.setVector(rect_, grayPixelList_);
orderedDither_.matrix = Vector.<uint>([
10, 4, 6, 8,
12, 0, 2, 14,
7, 9, 11, 5,
3, 15, 13, 1
]);
orderedDither_.execute(viewBmd_);
}
// 組織的ディザ(screw 型)
private function screw(event:Event):void {
// ボタンの処理
buttonMediator_.buttonChange(PushButton(event.target));
// ディザ実行
viewBmd_.setVector(rect_, grayPixelList_);
orderedDither_.matrix = Vector.<uint>([
13, 7, 6, 12,
8, 1, 0, 5,
9, 2, 3, 4,
14, 10, 11, 15
]);
orderedDither_.execute(viewBmd_);
}
// 組織的ディザ(screw 変形型)
private function screw2(event:Event):void {
// ボタンの処理
buttonMediator_.buttonChange(PushButton(event.target));
// ディザ実行
viewBmd_.setVector(rect_, grayPixelList_);
orderedDither_.matrix = Vector.<uint>([
15, 4, 8, 12,
11, 0, 1, 5,
7, 3, 2, 9,
14, 10, 6, 13
]);
orderedDither_.execute(viewBmd_);
}
// 組織的ディザ(中間調強調型)
private function center(event:Event):void {
// ボタンの処理
buttonMediator_.buttonChange(PushButton(event.target));
// ディザ実行
viewBmd_.setVector(rect_, grayPixelList_);
orderedDither_.matrix = Vector.<uint>([
12, 4, 8, 14,
11, 0, 2, 6,
7, 3, 1, 10,
15, 9, 5, 13
]);
orderedDither_.execute(viewBmd_);
}
// 組織的ディザ(Dot Concentrate 型)
private function dot(event:Event):void {
// ボタンの処理
buttonMediator_.buttonChange(PushButton(event.target));
// ディザ実行
viewBmd_.setVector(rect_, grayPixelList_);
orderedDither_.matrix = Vector.<uint>([
12, 4, 8, 14,
10, 0, 1, 7,
6, 3, 2, 11,
15, 9, 5, 13
]);
orderedDither_.execute(viewBmd_);
}
}
}
//package {
import flash.display.BitmapData;
import flash.geom.Rectangle;
/**
* ランダムディザ
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class RandomDither {
/**
* コンストラクタ
*/
public function RandomDither() {
}
/**
* ディザ実行
*/
public function execute(bmd:BitmapData):void {
var rect:Rectangle = bmd.rect;
var pixelList:Vector.<uint> = bmd.getVector(rect);
// 走査
var len:uint = pixelList.length;
for (var i:int = 0; i < len; i++) {
var threshold:int = Math.random() * 0xFF >> 0;
var gray:uint = pixelList[i] & 0xFF;
pixelList[i] = gray > threshold ? 0xFFFFFFFF : 0xFF000000;
}
bmd.setVector(rect, pixelList);
}
}
//}
//package {
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Rectangle;
/**
* 組織的ディザ
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class OrderedDither extends EventDispatcher {
/**
* ディザ・マトリクス
*/
public function set matrix(value:Vector.<uint>):void {
_matrix = value;
var len:int = _matrix.length;
for (var i:int = 0; i < len; i++) _matrix[i] = _matrix[i] * 16 + 8;
}
private var _matrix:Vector.<uint> = Vector.<uint>([
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
]); // Bayer 型
/**
* コンストラクタ
*/
public function OrderedDither() {
matrix = _matrix;
}
/**
* ディザ実行
*/
public function execute(bmd:BitmapData):void {
var imageWidth:int = bmd.width;
var rect:Rectangle = bmd.rect;
var pixelList:Vector.<uint> = bmd.getVector(rect);
// 走査
var len:uint = pixelList.length;
for (var i:int = 0; i < len; i++) {
var idxX:int = (i % imageWidth) % 4;
var idxY:int = (i / imageWidth >> 0) % 4;
var threshold:int = _matrix[idxY * 4 + idxX];
var gray:uint = pixelList[i] & 0xFF;
pixelList[i] = gray >= threshold ? 0xFFFFFFFF : 0xFF000000;
}
bmd.setVector(rect, pixelList);
dispatchEvent(new Event(Event.COMPLETE));
}
}
//}
//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;
/**
* 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;
//static public const LUM_R:Number = 1/3;
//static public const LUM_G:Number = 1/3;
//static public const LUM_B:Number = 1/3;
// HDTV
//static public const LUM_R:Number = 0.2126;
//static public const LUM_G:Number = 0.7152;
//static public const LUM_B:Number = 0.0722;
}
//}
//package aquioux.utils {
/**
* 複数あるボタンの調停
* 一度押したボタンは、別のボタンが押されるまで無効になる
* @author Aquioux(Yoshida, Akio)
*/
import com.bit101.components.PushButton;
/*public*/ class ButtonMediator {
// 前回押したボタン
public function set prevButton(value:PushButton):void {
_prevButton = value;
_prevButton.enabled = false; // 無効にする
}
private var _prevButton:PushButton;
/**
* コンストラクタ
*/
public function ButtonMediator() {
}
/**
* ボタン切り替え
* @param button 今回押したボタン
*/
public function buttonChange(button:PushButton):void {
// 前回押したボタンを有効に戻す
if (_prevButton != null) _prevButton.enabled = true;
// 今回押したボタンを前回押したボタンにして、無効にする
prevButton = button;
}
}
//}