今回は、透明画像の対応について説明します。
今回、使用する透明画像はこのボール(png形式)になります。
背景画像には、このかわいい猫ちゃんを使います。
前回のプログラムのままでは透明画像に対応していないため、ボールの周りが黒色になってしまいます。
これは、ImageDataをそのまま描画するputImageDataメソッドでは透過されない仕様のためです。
※前回のプログラムでは、透明度を255(不透明)固定してセットしていますが、255以下にしても透過されません。
Javascript における imageData のアルファ(透明度)について - Yahoo!知恵袋 の回答によると
imageData.data[i+3] = 50; // 青い四角形のアルファを下げる
これ↑の解釈なんですが・・・
そのアルファは、イメージの透過率ではなくて、Canvas自体の透過率みたいです。
ためしに 0 にすると、その領域で Canvas は完全に透過になります。
では、透明画像に対応するにはどうするのか?
主に3点の修正が必要となります。
- Canvasを1枚追加し、隠しCanvasとして扱う
- ImageData生成時に透明度を255(不透明)の固定から描画イメージの透明度をセットに変更
- putImageDataで隠しCanvas側に一旦描画し、drawImageで表示側Canvasに転送する
各項目について説明していきます。
■Canvasを1枚追加し、隠しCanvasとして扱う
下記のように、隠しCanvasを追加します。
<canvas id="ctx"></canvas> <canvas id="ctx2" style="visibility:hidden"></canvas>
仕組みを見たい方は、style="visibility:hidden"を外すのと縦のサイズを変更するといいです。
this.height = canvas.height = window.innerHeight; → 400;
■ImageData生成時に透明度を255(不透明)の固定から描画イメージの透明度をセットに変更
setPixel(output, j, i, R, G, B, 255); としていた透明度を変更します。
function drawNearest(ctx, param, sx, sy, w, h) var A = pixelData.A; this.setPixel(output, j, i, R, G, B, A); function drawBilinear(ctx, param, sx, sy, w, h) var A = rgb00.A; this.setPixel(output, j, i, R, G, B, A);
■putImageDataで隠しCanvas側に一旦描画し、drawImageで表示側Canvasに転送する
1.隠しCanvas(this.canvas2)のサイズも生成する画像サイズにしておきます。
2.隠しCanvasのコンテキスト(ctx2)に生成した画像データをputImageDataメソッドで描画します。その際、描画位置は左上の原点(0,0)にします。
3.イメージオブジェクトを生成して、画像のソースに隠しCanvasの画像データ(this.canvas2.toDataURL();)をセットします。
4.drawImageメソッドで表示側Canvas側の指定位置に転送することで、透明部分が反映されます。
render() { var ctx = this.context; var ctx2 = this.context2; var min = new Point(0, 0); var max = new Point(0, 0); var pt = []; // アンカー値をセット for(var i = 0; i < this.markers.length; i++) { pt.push(Anchor.getPoint("p" + (i + 1))); this.markers[i][0] = pt[i].x - this.offset.x; this.markers[i][1] = pt[i].y - this.offset.y; } // 描画用の射影変換パラメータを取得 var inv_param = this.computeH(this.origin, this.markers, min, max); // 画像サイズをセット var w = max.x - min.x; var h = max.y - min.y; // 角度0の時に形状保存 if(this.degrees.value == "0") { // 回転用に各制御点の中心点との差をセット this.center = new Point(w / 2, h / 2); this.vertexs.length = 0; for(var i = 0; i < this.markers.length; i++) { this.vertexs.push(new Point(this.markers[i][0], this.markers[i][1])); } } // 描画クリア ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // 背景画像 ctx.drawImage(this.backimage, 0, 0); // 隠しCanvasのサイズをセット this.canvas2.width = w; this.canvas2.height = h; // 画像処理 if(this.drawType[0].checked) { // ニアレストネイバー this.drawNearest(ctx2, inv_param, 0, 0, w, h); } else { // バイリニア補間 this.drawBilinear(ctx2, inv_param, 0, 0, w, h); } // 隠しCanvasにputImageDataで描画した画像をdrawImageで描画することで透明部分が反映される var newimage = new Image(); newimage.src = this.canvas2.toDataURL(); newimage.onload = function() { ctx.drawImage(newimage, _this.offset.x + min.x, (_this.offset.y - _this.ctlHeight) + min.y); } // 座標位置表示 this.drawInfo(pt); };
この変更により、下記のように透明画像が表示されるようになります。
ただし、描画コストがかなり高くなります。
■プログラム
jsdo.it
Typescriptで組んだソースリストは、github/Homography2に公開しました。