drawImageによる画像の挿入
カンバスには図形を描画するのみならず, 既存のラスタ(ベクタ)画像ファイルを描画することが可能です.
CanvasRenderingContext2D.drawImage()
画像を描画する. HTMLImageElement/HTMLVideoElement/HTMLCanvasElement等が描画対象. なお, GIFアニメーションや動画については, 特定のフレームがスナップショット的に描画される.
メソッドの引数のパターンには次の3つが存在します.
drawImage(image,dx,dy)
画像をカンバスの指定した位置を基準に描画したい場合に用いる. カンバスからはみ出した部分は無視される.
drawImage(image,dx,dy,dw,dh)
画像をカンバスの矩形領域に描画したい場合に用いる.
drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh)
画像をトリミングしてカンバスに描画する場合に用いる. (引数の順番に注意する)
引数imageにはHTMLImageElement(Imageオブジェクト), HTMLCanvasElement, HTMLVideoElement, ImageBitmapの何れかを指定します. その他の引数の詳しい意味合いは下記の図を参照して下さい.
画像描画時のマッピング
描画可能な画像のフォーマット
基本的にブラウザがサポートしている(img要素で表示可能な)画像・動画形式であればcanvas要素に描画できますが, ブラウザ互換性を鑑みると, PNG, JPEG, GIF形式のいずれかを利用するのが無難です. なお, JPEG画像についてはCMYK色空間で保存されたものについては扱いに注意が必要です. 環境によっては色味が変化したり, そもそも表示できない事もあります. またWEB環境では基本的に色をRGB値(24bitカラー)で扱うため, canvas要素単体での(カラープロファイルを利用するような)色味の調整は出来ません. 同様にデジタルカメラで撮影したrawデータをcanvas要素に描画する事は出来ず, 別途画像データを24bitカラーに変換する処理を自作する必要があります.
逆に24bitカラーへ変換できるなら, どのような画像データをもcanvas要素に描画できます.
画像挿入の3パターン
下はimg要素で読み込んだ画像ファイルです. これをカンバス要素に描画してみましょう.
座標指定
指定した座標を起点に画像が描画されます. canvasサイズからはみ出た部分は無視されます.
矩形指定
矩形に従って画像が引き伸ばされます.
トリミング指定
画像を指定した矩形範囲で切り取り, その内容をカンバスに描画します.
画像挿入に関わるテクニック
画像の挿入に際して覚えておくと便利なテクニックについて示します.
座標軸変換による画像の変形
drawImageメソッドでは予め座標軸を変形しておくことで画像を変形することができます. 下の例は画像を左右反転させた例です.
canvas要素の参照
drawImageメソッドでは画像の参照先としてcanvas要素が使えます. 例えばアニメーション処理を行う際に前景と背景とを別々に描画し, 最後に一つにまとめるといった構成を採ることも可能です.
また自分自身を書き込むことも出来ます.
画像の大きさを取得する
img要素における画像サイズにおいてもcanvas要素と同じく画像そのものサイズと画像の描画サイズの2つが定義されています.
HTMLImageElement.width/height
画像の見た目のサイズを取得する. CSSによるサイズが指定されていない場合, スクリーンに描画されていない場合は画像そのものサイズを返す.
HTMLImageElement.naturalWidth/naturalHeight
画像そのもののサイズを取得する.
例えば画像の加工を目的としているのであればnaturalWidth/naturalHeightを用います.
画像を読み込む場合の注意点
外部画像を読み込む場合, HTML文書に定義されているimg要素を参照する 動的にHTMLImageElementを生成して参照する ImageBitmapを生成して参照する(詳しくは後述します) の3つの方法が存在しますが, この時注意すべき点があります. 外部の画像ファイルの読込・分析はメイン処理とは別に非同期で行われるため, drawImageメソッドを実行したタイミングによってはまだ画像のロードが終わっていないことがあります.
画像の読み込みと画像の取得
従ってこの状態のままdrawImageメソッドを実行してしまうと画像の書き込みに失敗します. 下の例ではimg要素に画像の参照先を設定した直後にdrawImageメソッドを実行している ため, 画像の描画成否が不定となります(ブラウザキャッシュを参照した場合や, ブラウザの種類によっては成功することがあります).
この問題を解決するにはimg要素の持つonloadイベントでカンバスの描画処理を行うようにするか, img要素のcompleteプロパティを使って画像読み込みの完了を確認するようにします. 画像の読込が確実に完了してからdrawImageメソッドを実行するわけです.
複数の画像ソースを利用する場合
canvas要素に読み込む画像が複数にわたる場合はもう少し細工を施す必要があります. 読み込まれた画像の数を数え全ての画像が読み込まれたことを確認してから描画処理を開始するようにします. HTMLImageElement.completeプロパティは画像を読み込んでいない際にもtrueを返す ためこの用途においては適切ではありません.
必要な画像データをいわゆるスプライト画像として一つのファイルにまとめておく方法もあります.
画像の描画品質
画像をカンバスに描画する際の品質を設定できます. 主にドット絵等の低解像度の画像データを拡大した際に発生するアンチエイリアスの有無・品質を指定するために用います.
CanvasRenderingContext2D.imageSmoothingEnabled
画像を描画する際の品質を指定する. falseでアンチエイリアスを無効とする. (図形描画等には影響しない)
CanvasRenderingContext2D.imageSmoothingQuality
アンチエイリアスの品質を指定する. (low/medium/high)
画像縮小時の品質改善
drawImageメソッドで大きな画像を極端に縮小描画した際, 得られた結果において色の境界のギザギザが目立つ場合は縮小作業を何回かに分けることで見た目が改善します.
画像のモザイク化
ソース画像をモザイク化するには様々な方法がありますが, imageSmoothingEnabledが使えるなら一旦小さなcanvas要素に描画した内容を引き伸ばすだけで実現できます. この方法ならピクセル操作を行う場合に注意すべきクロスオリジンでの制約を考えずに済みます.
補足)CSSを用いた画像のモザイク拡大
なお, CSSを使って縮小した画像を拡大する方法もあります. canvas要素側の処理が単純になる反面, 指定するスタイル値がブラウザ毎にバラバラという難点があります. (これはimage-renderingプロパティの内容が二転三転しているため.) またInternet Explorer9以降では-ms-interpolation-modeプロパティが効きません.
CSS4におけるpixelatedとcrisp-edgesの違いは前者がドットをそのまま(nearest-neighbor法による)拡大するのに対し, 後者はピクセルを拡大する際に色境界を滑らかにする可能性がある 点です. 下はその処理イメージです.
image-renderingプロパティにおけるの動作の違い
元画像 pixelated crisp-edges
なお拡大アルゴリズムの指定はされておらず, pixelatedのように表示しても良いことになっています. 従って互換性の観点からは扱いにくい値と言えます.
画像読み込みの応用
ここまでは単純なURLを用いた画像読み込みを行いましたが, これを応用すると次のような場面においても画像を取得可能です.
Ajaxによる画像データの取得
input[type=file]による画像データの取得
ファイルドロップによる画像データの取得
コピーペースト操作による画像データの取得
Ajax機構で取得したバイナリ画像データをcanvas要素に書き出す
XMLHttpRequestオブジェクトを使い, 画像データをバイナリデータ(Blobオブジェクト)として取得する方法です. 得られたBlobオブジェクトはURL.createObjectURLメソッドでBlobデータスキームに変換することで, img要素に渡すことが可能です.
一旦画像ファイルをBlobとして扱う方法は, GIFアニメーションをフレーム画像に分割すると言った場合に用いられます.
なお, WEBサーバーから画像データを取得するのにfetchAPIを用いる方法もあります. 本項の最後にサンプルコードを記載しています.
ローカル環境の画像ファイルをcanvas要素に書き出す
input[type=file]要素で画像ファイルを取得する方法です. 得られたFileオブジェクトはBlobオブジェクトでもあるため, 画像ファイルが得られた後は先ほどと同じです.
canvas要素にドロップした画像を描画する
dropイベントから画像ファイルオブジェクトを取得する方法です. 画像ファイルが得られた後は先ほどと同じです.
いずれも何らかの手段で画像データに相当するBlob(File)を得る URL.createObjectURLメソッドを用いてBlobをimg要素に表示する img要素には予めonloadイベントにcanvas要素への描画処理を記述しておく 描画処理が完了したらURL.revokeObjectURLメソッドでBlobオブジェクトを開放する と言った手順をとっている点に着目しましょう.
クリップボードを経由した画像の描画
WEBページ上の画像を右クリックした際に表示される「画像のコピー」を使ってコピーした画像を, ペースト操作(キーボード操作を含む)でcanvas要素に画像を描画する事が出来ます. 実現方法には概ねpasteイベントを用いる contenteditable属性を用いる の2つがあります.
なお, クリップボード内の画像の形式や, クリップした環境等によって画像が描画されないケースもあります. また, 異なるWEBページに表示されている画像を描画した場合, 後述するorigin-cleanフラグによって利用可能な機能が制限されます.
コピー用の画像(右クリックでコピーして以下のコードでペーストしてみましょう)
pasteイベントを用いるもの
pasteイベントを使って, クリップボードの中身を参照する方法です. canvas要素そのものはpasteイベントを発生させないので, canvas要素を囲む何らかのHTML要素を用意する必要があります. また, そのままではペースト可能な範囲が不明瞭なので何らかの誘導が必要となるでしょう. 動作する環境はChromeに限られます.
MutationObserverによるもの
contenteditable属性をtrueとした要素の特性をcanvas要素に応用したものです. FireFox/Chrome/InternetExplorerといった広範な環境で動作するので, 通常はこちらを選択すると良いでしょう. 以下に動作原理を示します.
contenteditable=trueを何らかの要素に適用すると, 当該要素において貼り付け操作(右クリックメニュー及びctrl+v)が有効になる. 貼り付け操作を行うと, 配下に子要素が生成される ようになる.
DOM変更のタイミングはMutaionObserverで監視できる.
ペースト操作で追加されたimg要素を抽出し, その内容をcanvas要素に描画する.
例を示します. canvas要素の上に画像のペーストを検知するためのdiv要素を被せ, MutationObserverでDOMの変更を監視しています.
但し, canvas要素に対するコンテキスト(右クリック)メニューが隠されてしまうため, 画像の保存が出来なくなるというデメリットもあります. 何らかの手段で上に被さっているdiv要素を一時的に隠す等の対処が必要でしょう.
canvas要素にcontenteditable属性を設定する方法もありますが, 動作する環境が限られます.
テキストデータの貼り付け
先ほどの例はテキストの貼り付けに応用することができます. この場合, pasteListener内部のテキストを描画するようにします.
クリップボードへの画像転送
逆にcanvas要素の内容をクリップボードに転送する事も出来ます. ctrl+cキーによるコピー操作(コンテキストメニューによるものではない)をcopyイベントで取得し, canvas画像をHTMLコードとしてセットします. コンテキストメニューによる「画像のコピー」がcanvas画像全体をクリップボードに送るのに対して, この方法を用いると画像の一部分を転送可能です. またクリップボードに転送した画像は(HTMLの貼り付けをサポートする)他のアプリケーション(例えばLibreOffice)に直接貼り付けることが出来ます.
下の例では, canvas要素をクリックした後にctrl+cキーを入力することで破線の範囲がクリップボードに転送されます.
WEBブラウザでのクリップボードの操作はセキュリティの観点から動作条件に厳しい制約があります.
video要素をcanvas要素に描画する
drawImageメソッドの引数にはHTMLVideoElementオブジェクトを渡すこともできます.
描画対象のvideo要素
逆にcanvas要素の内容をvideo要素に出力することも出来ます. 詳しくはアニメーションの項で解説します.
getUserMediaを用いてWebカメラの映像をcanvas要素に描画する
WebRTC をサポートする環境では, WEBカメラの映像をcanvas要素に取り込むことが出来ます. navigator.mediaDevices.getUserMediaメソッドを実行するとWEBカメラ映像がMediaStreamオブジェクトとして得られます. このMediaStreamオブジェクトをvideo要素で再生し, canvas要素に転写するのです.
WEBカメラ映像を表示するvideo要素
なおgetUserMediaメソッドの所在はかつてnavigator.getUserMediaとされており, 機能的には同等であるものの使用法が異なります.
Chromeではかつて同様の操作でスクリーンキャプチャ映像を取得することが出来ましたが, 現在ではフラグ設定を含めて機能が削除されています.
getUserMediaメソッド実行の制限
getUserMediaメソッドによるカメラ映像の抽出は使い方によっては重大なセキュリティリスクを伴います. その為, ブラウザではhttps環境でのみ動作可能としているもの(Chrome)があります.
ImageBitmapによる画像読み込み
drawImageメソッドに指定する画像としては通常HTMLImageElement/HTMLCanvasElementを用いますが, ImageBitmapオブジェクトをサポートする環境では画像に類するデータ(例えばImageDataやBlob/File等)をImageBitmapオブジェクトに変換することで統一的にdrawImageメソッドに渡すことが可能です. また, DOMから切り離されたオブジェクトであるため, Worker内部でも画像データを扱えるというメリットもあります. なおImageBitmapオブジェクトは種類の異なるコンテキストオブジェクト間での画像データの授受を目的としていて, 今後バックグラウンドでの画像描画やWebGLにおけるテクスチャ画像の取り扱いと言った場面での活用が期待されています.
ImageBitmapオブジェクトの概観
ImageBitmapFactory(window/worker).createImageBitmap(source, sx, sy, sw, sh, option)
ImageBitmapオブジェクトを生成し, それを利用するためのプロミスオブジェクトを返す. sourceには画像オブジェクトを, sx,sy,sw,shにはその切り出したい範囲を指定する.
sourceとしてはHTMLImageElement, HTMLCanvasElement, HTMLVideoElement, Blob, ImageData, CanvasRenderingContext2D, ImageBitmap, SVGImageElementと言った広範な画像インターフェースを指定可能.
※Window/WorkerオブジェクトはImageBitmapFactoryインターフェースを実装しています.
optionには現状下記値の連想配列(ImageBitmapOptions)を指定可能.
imageOrientation 画像の方向(none/flipY…上下反転)
premultiplyAlpha プレマルチプライ(アルファチャンネルの事前適用の有無)設定の指定(default/premultiply/none)
colorSpaceConversion 色空間の変更(none/default)
resizeWidth リサイズ幅
resizeHeight リサイズ高
resizeQuality リサイズ品質(pixelated/low/medium/high)
※但し全てが動作するわけではありません.
ImageBitmap WEB環境における汎用ビットマップ(ラスタ)画像を表すオブジェクト. CanvasRenderingContxt2D, WebGLRenderingContext, ImageBitmapRenderingContext共通で利用可能. transferableインターフェースを実装しているため, window-worker/worker-worker間でのデータ授受が可能です. なお, データ転送後は画像データの取り出しが出来なくなります.
createImageBitmapメソッドは直接ImageBitmapオブジェクトを返すわけではなく, それを利用するためのPromiseオブジェクトを返します. つまり, 得られたPromiseオブジェクトのthenメソッドの引数にimageBitmapが得られた際の処理を記述していきます.
Promiseはjavascriptでこれまで煩雑だった非同期処理の記述をより簡潔に行えるように新たに追加された仕組みです. Promiseの使い方についてはこちら を参照してください.
promise.then(onfulfilled, onrejected) 処理成功時の処理, 失敗時の処理を指定する.
promise.catch(onrejected) 処理失敗時の処理を指定する.
Promise.all([promise, …]) PromiseオブジェクトをひとくくりとしたPromiseオブジェクトを返す.
なおcloseメソッドを指定することで画像データを明示的に破棄することが可能です.
ImageBitmap.close()
現在保持している画像データを開放する.
ImageBitmap.width
現在保持している画像の幅
ImageBitmap.height
現在保持している画像の高さ
ImageBitmapRenderingContextによる画像データの表示
ImageBitmapが利用可能な環境では通常利用するコンテキストオブジェクトに加え, ImageBitmapRenderingContextというAPIが定義されます. 本オブジェクトはグラフィック描画に伴うメモリ書き換え処理の代わりにImageBitmapが内包するデータを直接参照する ことで画像をスクリーンに出力します.
ImageBitmapRenderingContext.transferFromImageBitmap(bmp)
ImageBitmapの内容をcanvas要素に出力(転送)する. nullを渡すと画像がクリアされる.
※現在transferImageBitmapとの表記揺れがあります
ImageBitmapRenderingContext.canvas
このコンテキストオブジェクトを生成したHTMLCanvasElement
fetchAPIと組み合わせる
Promiseという仕組みを利用するのは一見面倒ですが, 現在検討中のfetchAPI と組み合わせると次のように画像データの取得処理が順序良く簡潔に記述できます.
ImageBitmapによる処理効率化の条件
ImageBitmap/ImageBitmapRenderingContextオブジェクトの仕組みを活かすには幾つか条件があります.
canvas要素に描画すべきグラフィックがimg要素に読み込まれている場合, そのimg要素からImageBitmapオブジェクトを生成する意味はありません. ImageBitmapオブジェクトを生成する際にimg要素の画像を複製するため, 却ってパフォーマンスを損ないます.
ImageDataオブジェクトをcanvas要素に書き戻す場合, ImageBitmapオブジェクトを使う意味はありません. putImageDataメソッドによるデータ転写とImageBitmapオブジェクト生成時のデータ転写のコストがほぼ等価だからです.
canvas要素で加工した画像を複数のcanvas要素に転写する場合は, 描いたグラフィックをImageBitmapオブジェクト化し, ImageBitmapRenderingContextで描画すると無駄なメモリ複写が発生しません.
ImageBitmapオブジェクトをアニメーション用途で繰り返し生成するのは間違った使い方です.
EXIF方向値を加味した画像の描画
JPEG画像にEXIFによる「画像の方向」が設定されている場合, WEBブラウザではこの内容を無視します† . 従ってこの方向を加味した画像の描画を行う場合, 一旦JPEG画像をAjaxによりバイナリデータとして取得し画像の方向データを抽出する必要があります. が, 比較的難度の高い処理となるため, Exif.js と言ったサポートライブラリを用いると良いでしょう.
†img要素を使った場合で, 歴史的経緯から動作を変更出来ないようです. 画像を直接開いた場合と動作が異なる点に注意します. CSS4ではimage-orientationプロパティにfrom-imageを指定することでEXIFによる画像の方向で画像を出力することが可能になります.
EXIF値と画像データの状態
EXIF値 見た目の回転 反転 画像イメージ EXIF値 見た目の回転 反転 画像イメージ
1 なし 実像 ← x,yを交換 (鏡像)
5 左回転 鏡像
2 鏡像 ← x,yを交換 (鏡像)
6 実像 (鏡像の鏡像)
3 180度 実像 ← x,yを交換 (鏡像)
7 右回転 鏡像
4 鏡像 ← x,yを交換 (鏡像)
8 実像 (鏡像の鏡像)
EXIF方向値が得られたら, その内容を元にcanvasのサイズを決定し座標軸を変換します. 下記にサンプルコード(実際の動作例はこちら )を示します.
要するにx,y軸の交換 左右反転 180度回転 の有無から定まる2×2×2=8パターンというわけです. この後描画処理を続けるのであれば, 座標軸変換を元に戻すとよいでしょう.