POPSブログ

CreateJS BitmapData モザイク

279

  Category:  javascript2014/02/17 pops 

BitmapDataクラスを利用し、fillRect()で升目を塗りモザイクを作ります。以前AS3などで流行ったものですが最近は余り見かけません。現在のCanvas処理ではまだ大きな画像を処理するのは難しい様です。
easeljs-0.7 でのテストです。

 

CreateJS BitmapData モザイク テスト


BitmapDataを扱いたいので kudox さんが配布されている BitmapDataクラス を使用しました。下記のページに詳細が有ります。サンプル、マニュアル(日本語)も完備されています。


【参照】kudox.jp記事: BitmapData for EaselJS ver1.00 リリースのお知らせ

EaselJSにActionScript3.0と同様のインターフェイスを備えたBitmapDataクラスを追加する「BitmapData for EaselJS」を作ってみました。MITライセンスで公開していますので、商用でも無料でお使い頂けます。

...と言うことなので早速利用してみました。大変便利です。


 

DEMO


CreateJS BitmapData モザイク テスト、(createJS064.js)

このページはHTML5では有りませんので、デモページでご覧ください。「IE7.8」ではご覧いただけません。
/// 注意、ブラウザ、マシン性能により描画品質などに大きな差が有ります事了承下さい ///



Chrome Firefox Safari(Win) IE9、で動作確認済み。 Safari(Mac)、IE10、は未確認です。


 

HTML JS CSS


使用するライブラリ

easeljs preloadjs tweenjs

配布元 : CreateJS createjs.com


ライブラリの読み込み

ダウンロードしたJSを使用する場合。記述は一例です。(バージョン 0.7.0 使用)


<script type="text/javascript" src="js/easeljs-0.7.0.min.js"></script>
<script type="text/javascript" src="js/preloadjs-0.4.0.min.js"></script>
<script type="text/javascript" src="js/bitmapdata-1.0.0.min.js"></script>

重要、バージョン違いでは動かない場合が有りますので必ず合わせて下さい。
尚、別途にkudoxさんの bitmapdata-1.0.0.min.js が必要です。

このデモでは、tweenjs-0.5.0.min.jsは不要です。


HTML (HTML5)


<div id="demo-wrap">
	<div id="image-box" class="radius">
		<canvas id="mainCanvas" width="640" height="300"></canvas>
	</div>
</div>

JS

createJS064.js、JS名は任意に変更可。注意、easeljs-0.7用です。


//日本語
//createJS064.js
//BitmapDataモザイク
//easeljs-0.7用

//------------------------------------------------------
//初期設定
//タイマーの使用、true false
var timerUse=true;//true
//画像切り替え時間タイマー値 3000-10000
var time=6000;

//Stageの大きさ
var canvasWidth=640;
var canvasHeight=300;
//画像の大きさ
var imageWidth=640;
var imageHeight=300;

//初期モザイクサイズ
var mozaicSize=100;
//モザイク終了サイズ
var endSize=4;//2-5
//モザイクのサイズの縮小率
var small=0.9;//0.8-0.9

//TEXT true false
var textUse=true;

//画像manifestリスト/idは利用していない
var manifest=[
{src:"/main/images/flower01.jpg",id:"PHOTO1"},
{src:"/main/images/flower02.jpg",id:"PHOTO2"},
{src:"/main/images/flower03.jpg",id:"PHOTO3"},
{src:"/main/images/flower04.jpg",id:"PHOTO4"},
{src:"/main/images/flower05.jpg",id:"PHOTO5"},
{src:"/main/images/flower06.jpg",id:"PHOTO6"}
]
//最初の画像0のこと
var image_no=0;
//画像数
var image_max;

//------------------------------------------------------

//ステージ
var stage;
//コンテナ
var mozaiccontainer,loadingcontainer,progresscontainer;
//画像Bitmapインスタンス
var mozaicImage;
//画像result
var mainImage;
//Loading
var loadingShape;
var progressbar;
//TEXT
var viewtext;
//読み込み画像URL保存容器
var assets=[];
//変数
var timerID=null;
//ProgressBar変数
var bar_v=0;
//BitmapData
var bitmapdata;
var set_bmd;
//実行縮小率
var setSmall=1;

//shadowフィルター
var shadow=new createjs.Shadow("#000000",0,0,4);

//------------------------------------------------------

//ステージ周りセット
function init() {

	//STAGE
	stage=new createjs.Stage('mainCanvas');

	//バックRect/最下位色背景層、無くとも良い
	var backrect=new createjs.Shape();
	backrect.graphics.beginFill("#000000").drawRect(0,0,canvasWidth,canvasHeight);
	stage.addChild(backrect);

	//モザイク層
	mozaiccontainer=new createjs.Container();
	//BitmapData表示領域
	set_bmd=new createjs.BitmapData(null,canvasWidth,canvasHeight);
	set_bmd.clearRect(0,0,canvasWidth,canvasHeight);
	//BitmapにBitmapData挿入
	mozaicImage=new createjs.Bitmap(set_bmd.canvas);
	mozaiccontainer.addChild(mozaicImage);
	stage.addChild(mozaiccontainer);

	//loadingコンテナを作る
	loadingcontainer=new createjs.Container();
	stage.addChild(loadingcontainer);
	//LOADINGを作る3
	loadingShape=loadingIndicator3();
	loadingShape.x=canvasWidth/2;
	loadingShape.y=canvasHeight/2;
	//tickを設定
	loadingShape.tick=function (){
		loadingShape.rotation +=5;
	}
	//loading addEventListenerを設定
	createjs.Ticker.addEventListener('tick',loadingShape.tick);
	//コンテナに貼り付け
	loadingcontainer.addChild(loadingShape);

	//ProgressBar
	progresscontainer=new createjs.Container();
	progressbar=new createjs.Shape();
	progtext=new createjs.Text('0','12px Arial','#FFFFFF');
	progtext.x=0;
	progtext.y=15;
	progtext.maxWidth=80;
	progtext.textAlign='center';
	progtext.shadow=shadow;//shadow
	//alpha
	progressbar.alpha=0.5;

	//bar_v判定tickを設定
	//90度是正-Math.PI/2
	progressbar.tick=function (){
		if(bar_v > 0) {
			progressbar.graphics.clear();
			progressbar.graphics.setStrokeStyle(8,'butt').beginStroke('#FF69B4');
			progressbar.graphics.arc(0,0,40,-Math.PI/2,Math.PI*2*bar_v-Math.PI/2);
			progtext.text=Math.floor(bar_v*100);
		}
	}
	//Ticker.Listener設定
	createjs.Ticker.addEventListener('tick',progressbar.tick);
	//addChild
	progresscontainer.x=canvasWidth/2;
	progresscontainer.y=canvasHeight/2;
	progresscontainer.addChild(progressbar,progtext);
	stage.addChild(progresscontainer);

	//簡易TEXT
	viewtext=new createjs.Text("","12px Arial","#FFFFFF");
	viewtext.x=20;
	viewtext.y=15;
	viewtext.maxWidth=canvasWidth-40;
	viewtext.lineHeight=20;
	viewtext.textBaseline="bottom";
	viewtext.shadow=shadow;//SHADOW処理
	stage.addChild(viewtext);
	set_text("Loading Now!");

	//Ticker設定
	createjs.Ticker.setFPS(20);
	createjs.Ticker.addEventListener('tick',tick);

	//遅延
	setTimeout(function() {

		//画像ロードに進む
		bulkload();

	},2000);

}

//progressBar
function progress(event) {

	//loadedのみ効く
	bar_v=event.loaded;
}

//bulk-load、画像一括ロード
function bulkload() {

	//Loaderを作る
	var loader=new createjs.LoadQueue(false);

	//全体、progressがあれば先に設定
	loader.addEventListener("progress",progress);

	//loader EventListener設定
	loader.addEventListener("fileload",fileload);
	loader.addEventListener("complete",complete);

	//Manifestを使用、manifest読み込み開始
	loader.loadManifest(manifest);

}
//各画像読み込み完了
function fileload (event) {

	assets.push(event.result);
}
//全ての画像読み込み完了
function complete (event) {

	//画像数確認、再計算
	image_max=assets.length;
	//簡易TEXT
	set_text("Loading End!");

	//loader Listener削除
	event.target.removeEventListener("fileload",fileload);
	event.target.removeEventListener("complete",complete);

	//loading EventListener削除
	createjs.Ticker.removeEventListener('tick',loadingShape.tick);
	//非表示でも良い
	loadingcontainer.visible=false;

	//画像があれば、最初の画像表示
	if (image_max) {

		//早すぎるので調整
		setTimeout(function() {

			//遅延progressbar最後の前にcompleteが反応する
			event.target.removeEventListener("progress",progress);
			//progressTicker削除
			createjs.Ticker.removeEventListener('tick',progressbar.tick);
			//progress非表示
			progresscontainer.visible=false;
			//簡易TEXTクリア
			set_text("");

			//画像表示に進む
			draw();

		},1000);//500-1000
	}

}

//DRAW、モザイクアニメ
function draw() {

	set_text("Mozaic IN");

	//画像result取得
	mainImage=assets[image_no];
	//色取得のため画像BitmapData変換/モザイク処理終了後再利用する
	bitmapdata=new createjs.BitmapData(mainImage);
	//実行縮小率初期化/1のこと
	setSmall=1;

	//Ticker
	createjs.Ticker.addEventListener('tick',mozaic_in);
}

//モザイク-IN
function mozaic_in (event) {

	//BitmapDataクリア
	set_bmd.clearRect(0,0,canvasWidth,canvasHeight);

	//モザイクの大きさでfillRectする
	var size=Math.floor(mozaicSize*setSmall);
	if (size < endSize) {size=endSize;}
	//Rectangle
	var rect=new createjs.Rectangle(0,0,size,size);

	//サイズ変更とfillRect位置計算して処理
	for (var x=0;x < imageWidth;x +=size) {
		for(var y=0;y < imageHeight;y +=size){
			rect.x=x;
			rect.y=y;
			set_bmd.fillRect(rect,bitmapdata.getPixel32(x,y));
		}
	}

	//縮小率更新
	setSmall *=small;

	//fillRect終了
	if (size == endSize) {

		//BitmapDataクリア
		set_bmd.clearRect(0,0,canvasWidth,canvasHeight);
		//fillRect無しの画像bitmapdataをdraw
		set_bmd.draw(bitmapdata);

		//mozaic完了
		finsh_move();
	}

}

//mozaic完了
function finsh_move () {

	set_text("finsh-IN");

	//EvenListener削除
	createjs.Ticker.removeEventListener('tick',mozaic_in);
	//全て完了
	finsh();
}

//全て完了
function finsh () {

	set_text("FINISH");

	//少し遅延
	setTimeout(function() {

		//タイマー次ぎ開く
		if (timerUse) {
			set_timer();
		}

	},500);

}

//tick
function tick() {
	stage.update();
}

//次ぎの開く要素を計算
function next_set() {

	//次ぎの番号
	image_no +=1;
	if (image_no > (image_max-1)) {image_no=0;}

	set_text("AUTO");

	//次ぎ画像処理
	draw();

}

//タイマー
function set_timer() {

	//タイマー再セット
	if (timerUse) {

		//一旦切ってからセット
		clearTimeout(timerID);
		timerID=setTimeout(next_set,time);
	}

}

//VIEWTEXT
function set_text(t) {

	if (textUse) {
		viewtext.text=t;
		stage.update();
	}
}

//------------------------------------------------------
//簡単なLOADING
function loadingIndicator3 () {

	//graphicのスタイル
	var graphics=new createjs.Graphics();
	graphics.setStrokeStyle(2,"round");//round

	//描画データ
	var alpha_v=1;//透明度
	var alphaback;

	var cx,cy;
	var numNeedles=12;
	var innerR=15;
	var outerR=10;
	var cAngle=-Math.PI/2;
	var nAngle;

	//ライン描画
	nAngle=Math.PI*2/numNeedles;
	for (var i=0; i < numNeedles; i++){

		//回転補正
		rotateflag=cAngle -=nAngle;
		//透明度を描画毎に変更
		if (i > 0) {alpha_v -=0.05;}

		//#CCCCCC
		alphaback=createjs.Graphics.getRGB(204,204,204,alpha_v);
		cx=Math.cos(cAngle)*innerR;
		cy=Math.sin(cAngle)*innerR;
		graphics.beginStroke(alphaback).moveTo(cx,cy);
		cx=Math.cos(cAngle)*outerR;
		cy=Math.sin(cAngle)*outerR;
		graphics.lineTo(cx,cy);

	}
	//Shapeに格納
	var s=new createjs.Shape(graphics);
	return s;
}
//------------------------------------------------------

//START
init();

注釈文を削除すれば、幾分早くなります。
画像使用の場合、登録画像パスは当方の例です。使用の際は環境に合わせて下さい。


CSS

createJS064.css、CSS名は任意に変更可


/*日本語createJS064.css*/

#demo-wrap {
text-align:center;
}

#image-box {
position:relative;
top:0;left:0;
width:640px;
height:300px;
margin:0 auto;
padding:0;
box-shadow:0 0 10px #000;
-moz-box-shadow:0 0 10px #000;
-webkit-box-shadow:0 0 10px #000;
-o-box-shadow:0 0 10px #000;
-ms-box-shadow:0 0 10px #000;
background-color:#000000;
}

#image-box #mainCanvas {
border-radius:10px;
}
canvas {
border-style:none;
background-color:transparent;
}

.radius {
border-radius:10px;
-moz-border-radius:10px;
-webkit-border-radius:10px;
-o-box-border-radius:10px;
-ms-border-radius:10px;
}

当方のサンプルの例です。


簡単な説明


[説明図]

 

fillRect()で升目を塗りモザイクを作りますが、モザイクのサイズを順次小さくして行きます。


1. 予めに「透明BitmapData画面(set_bmd)」を作り「使い回し」します。
2. tick毎、getPixel32で得た色をfillRect()で塗ります。
3. 予め設定している「モザイク終了サイズ」に達したらモザイク処理を終了させます。
(サイズが小さいと負荷が大きくギコチないので、「モザイク終了サイズ(最小サイズ)」を設定しました)
4. 現在大きな画像を処理する能力は有りません。(Canvasが進化すれば将来は可能かも)
5. 一応 easeljs-0.7.1でも可動します。


透明BitmapData画面の作成

最初に、透明BitmapData画面を入れたBitmap()を作成して、モザイク縮小毎にBitmapDataのみ更新します
空Bitmap()で、後でBitmapDataを入れるより簡単になります。全体をレーヤー代わりのコンテナに収容。

サンプルでは、モザイク処理後の未処理画像表示にdraw()を使用しています。



//モザイク層
mozaiccontainer=new createjs.Container();
//BitmapData表示領域
set_bmd=new createjs.BitmapData(null,canvasWidth,canvasHeight);
set_bmd.clearRect(0,0,canvasWidth,canvasHeight);
//BitmapにBitmapData挿入
mozaicImage=new createjs.Bitmap(set_bmd.canvas);
mozaiccontainer.addChild(mozaicImage);
stage.addChild(mozaiccontainer);

ここで利用している「BitmapDataクラス」の特性、クセを理解しておくことが重要のようです。(AS3ではない...)


画像の色情報を取得

Load画像のresult値より画像大のBitmapDataを作成しておきます。
fillRect()で染めるときに、升目に該当する部分の色を取得します。(升目左上)
この画像BitmapDataである、bitmapdata はモザイク処理終了時にdraw()して再利用しています。
(fillRectでも処理出来ますが、drawを使ってみました)


● 画像をBitmapDataに変換するのは、画像Load時のresult値を利用すれば簡単だ。
実際のモザイクは 次ぎのEventListener mozaic_in で行います。


//DRAW
function draw() {

	略す

	//画像result取得
	mainImage=assets[image_no];
	//色取得のため画像BitmapData変換/モザイク処理終了後再利用する
	bitmapdata=new createjs.BitmapData(mainImage);
	//実行縮小率初期化/1のこと
	setSmall=1;

	//Ticker
	createjs.Ticker.addEventListener('tick',mozaic_in);
}

● モザイクは、tick毎にfillRect()で画像BitmapDataである bitmapdata の該当箇所の色を取得して塗り込む。
順次小さくしてゆくが、2x2 3x3 位の大きさになると負荷が大きくギコチない。(当方マシンは古く貧弱です)
その為に「モザイク終了サイズ(最小サイズ)」を設定した。マシン環境により処理能力が違いますのでその辺を考慮して設定します。


fillRect()はキャッシュとかないので、以下の様に処理が楽です。


//モザイク-IN
function mozaic_in (event) {

	//BitmapDataクリア
	set_bmd.clearRect(0,0,canvasWidth,canvasHeight);

	//モザイクの大きさでfillRectする
	var size=Math.floor(mozaicSize*setSmall);
	if (size < endSize) {size=endSize;}
	//Rectangle
	var rect=new createjs.Rectangle(0,0,size,size);

	//サイズ変更とfillRect位置計算して処理
	for (var x=0;x < imageWidth;x +=size) {
		for(var y=0;y < imageHeight;y +=size){
			rect.x=x;
			rect.y=y;
			set_bmd.fillRect(rect,bitmapdata.getPixel32(x,y));
		}
	}

	//縮小率更新
	setSmall *=small;

	//fillRect終了
	if (size == endSize) {

		//BitmapDataクリア
		set_bmd.clearRect(0,0,canvasWidth,canvasHeight);
		//fillRect無しの画像bitmapdataをdraw
		set_bmd.draw(bitmapdata);

		//mozaic完了
		finsh_move();
	}

}

モザイク-OUT

INの反対動作になります。サイズ変更を逆にすれば可能ですが、省略します。


マシン環境により処理能力が違います

モザイク升目が小さくなると、処理能力のないマシンは厳しいので「モザイク終了サイズ」endSize を設定しました。状況に応じて変更します。
2 - 5 位の値です。1 は無意味です。(最近のマシンなら 2 で問題は有りません)
画像サイズは「デモ」では 640x300 です。現在CanvasはFlash並の処理能力は有りませんので、最大 700x400 位ではないでしょうか。逆に画像が小さければ軽快に処理します。


var endSize=4;//2-5

各種設定

JSの先頭をご覧下さい。


使用画像

「デモ」では画像サイズ、640x300 に成ります。実際の画像サイズ枚数などは任意です。


これらの改造などは自由です。



 

一応、完動しますが全てテストです。効率化のため、予告無くJSなど修正する場合がありますので了承下さい。

CreateJSは結構「仕様」が変わりますので特に注意が必要です。


BitmapDataクラスは実に便利です。以上です。

 


[ この記事のURL ]


 

ブログ記事一覧

年別アーカイブ一覧



[1]