JavaScriptで縮小画像の作成

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

スマートフォンなどで撮影した写真をサーバーに転送したい場合、撮影された写真そのままではサイズが大きすぎてアップロードに時間がかかり、縮小してから送りたいといったことをしたいと思っていたら、またまたHTML5 テクニックバイブルに書いてありました。HTML5 テクニックバイブルすごい。

というわけで、上記の書籍を参考に下記のようなコードを書いてみた。

HTML

JavaScript

以下、実行サンプル。
画像を縮小して表示

やっていることは単純で、画像ファイルを選択すると、縮小用の画像サイズと同じcanvasを作成し、canvasオブジェクトのdrawImageメソッドで、画像を埋め込み、toDataURLメソッドでData URIを取得するというもの。こんな簡単にできるとは・・・。

今回は取得したData URIをimgタグで表示するようにしたサンプルを作成しましたが、これをそのままサーバーに送信したり、先日canvasで描いた絵をバイナリ形式でサーバーにPOST送信する方法 | while(isプログラマ)で紹介したやり方でData URIをバイナリ形式にしてから送信するとサーバーに縮小画像を送信することができそうです。

2014年10月21日追記:
JavaScriptで縮小画像を作成してサーバーにPOST送信する方法 | while(isプログラマ)にて試してみました。

canvasで描いた絵をバイナリ形式でサーバーにPOST送信する方法

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

最近、自宅で時間ができるとHTML5 テクニックバイブルという本を読んでいるのですが、初めて知ったことがいろいろ書いてあって驚愕する日々を送っています。

今回は上記書籍を読んで知ったCanvasで描いた絵を、Base64のData URI形式ではなく、バイナリ形式にしてPOST送信する方法のメモ。ただし、IE9では使えないよう。

まずは、受け取り側のPHPを記述。普通にフォームの画像ファイルを受け取ってサーバーにファイルを保存するような記述を書いておきます。ファイルのアップロード | PHP Laboを参考にしました。

上記のPHPでは、acceptImageというプロパティに対応するファイルをtest.pngという名前で保存するようにしたPHPです。試しに、下記のようなHTMLを書いて普通にファイル選択してPOSTするとうまく動きました。

というわけでつづいて、前に作ったCanvasお絵描きツールの投稿先を、上記PHPに変更し、JavaScriptでCanvasのデータをPOST渡しできるよう記述するように投稿ボタンを押した時の処理を下記のようにしてみました。

実行サンプル:canvasを使ったお絵描き投稿システム

HTML5 テクニックバイブルのほうでは、jQueryを使わずに純粋にXMLHttpRequestオブジェクトを使ってAjax送信しているコードが載っていたのですが、自分は最近Ajaxを使う時はjQueryを使うことにしてるので、jQueryを使って書いてみました。実はjQueryを使ったAajxでは最初うまく動かなかったのですが、調べてみるとjQueryでFormDataオブジェクトをAjaxで送信する場合には、contentTypeとprocessDataをfalseにしなければいけないと分かり、そのオプションを変更することによってうまく動きました(参考:jQuery: FormData オブジェクトで送信する – Sarabande.jp)。
HTML5テクニックバイブルに載っているコードでは、そんな記述が必要ない分、もう少しシンプルな記述となっています。

それにしても、上記コードは自分が初めて知ることがたくさんあります。FormDataオブジェクトぐらいなら聞いたことはあったのですが、BlobオブジェクトとかArrayBufferオブジェクトとかUint8Arrayオブジェクトとか、何だそれ!? という思いです。簡単に説明すると、BlobオブジェクトはMIMEタイプを含んだバイナリデータを表し、ArrayBufferオブジェクトは固定長のバイナリデータを表し、Uint8Arrayは8ビットの符号なし整数を表すようです。なお、Uint8Arrayの説明には、C言語での同等タイプに『uint8_t』という記述が。そんなデータ型がC言語にあったか? と思って調べてみると、どうやら『unsinged char』と同じらしいです(参考:整数型 – Wikipedia)。

HTML5はある程度知っているつもりでいましたが、まだまだ知らないことが多くありそうです。

ところで、Blobオブジェクトの取得については、canvasオブジェクトにtoBlobメソッドが実装されていれば、そのメソッドを使うことにより、簡単に取得できるようです。ただ、このメソッドは残念ながら、Firefoxでしか実装されていないよう。html5.jpによると、2011年4月5日の草案にはすでに提案されていたようなんですが・・・(参考:W3C – 『HTML 5 differences from HTML 4』日本語訳 – HTML5.JP)。Chromeですら実装してないとなると、さすがに使うのに躊躇します。

Canvasで描いた絵をクラシックASPでサーバー上に保存する方法

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

このブログでCanvasについてよく扱っていたタイミングで、会社からCanvasを用いた編集ツールを作ることになりました。ただし、うちの会社で使っているサーバーは少し古めのIISで、現状サーバープログラミングはクラシックASPしか使えないようです。
ただ、Canvasで作った画像はサーバーに保存しなければいけないので、その方法を調べる必要がありました。Canvasの画像データ自体はBase64にして得られるものの、そのBase64の文字列をデコードしてファイルとして保存する必要があります。そこで見つけたのが、BASP21 DLLというASP上で扱えるコンポーネント。どうやら、Base64でファイルをデコードする機能もあるようです(わざわざ、Base64の文字列を入れたテキストファイルを作る必要があるようですが)。とりあえずやってみたものの、小さい画像ではうまくいきましたが、ちょっと大きくなるとダメでした(確か、200px*200pxぐらいだともうダメだったかと)。

正直ちょっと諦めかけて、LhaplusみたいなBase64デコード対応のソフトをWScript.Shellで操作するのがいいんじゃないか。なんて思っていた所、下記のサイトを発見。
ScrapCode/VBS/BASE64 – 備忘録
ASPはVBSで書かれているので、この方法でうまくいくかもしれません。なぜXMLを読み込んだり書き込んだりするためのMicrosoft.XMLDOMというオブジェクトでBase64のデコードができるのか謎なのですが、とりあえず試してみました。

以下、サンプルコード。

CanvasのHTMLは、前に作った、canvasを使ったお絵描き投稿システムを利用し、投稿先URLを、”image-accept.php”ではなく、”image-accept.asp”として試してみました。acceptImageというパラメータで、Canvasから得られたBase64 URLを渡すようにしています
結果、うまくいったようです。実際に使う時にはファイルパスとBase64の文字列を渡して、ファイルを作成する関数を作ったほうがいいかもしれませんね。

canvasのお絵描きツールに『戻る』『進む』ボタンをつけてみる

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

描き間違いがあったときに一つ前の状態に戻る機能がほしくなったので、その機能を追加することに。とりあえず、五回分を保存できるようにしてみた。塗りつぶし機能よりは簡単に実装できました。

以下、JavaScriptのコード

以下、動作サンプル。
canvasを使ったお絵描き投稿システム

saveImageDataという関数は、初期状態やキャンバス上でマウスボタンやタッチが離された時に実行するようにしています。やっていることは単純で、17行目のgetImageDataメソッドで現在のキャンバスの状態をimageMemory という配列に保存し、戻るボタンや進むボタンをおした時に現在の番号(flagMemory)の前や次に対応したimageMemoryに入っているキャンバスのデータをputImageDataでキャンバス上に描画しています。
何度か戻った後に新しくキャンバスに絵を書いた場合、次の画像というものがないのが自然な気はしますが、わざわざ消す必要もないだろうと思い、消してません(面倒だっただけだろと突っ込まないでください)。

なんというか、本当思いついたらその分追加するだけのコードになっているので、汚い気はします。もっとキレイなコードを書けるようになりたいです。

canvasのお絵描きツールに塗りつぶし機能をつけてみる

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

塗りつぶし機能を作ってみようと思ったけど、いろいろ苦戦。再帰を使えば簡単にできると思ったのですが、小さい範囲ならともかく、ちょっと大きくなるとブラウザに怒られました。

というわけで、JavaScriptに以下のようなコードを記述。

上記関数を呼び出す時には下記のような記述。

以下、動作サンプル。
canvasを使ったお絵描き投稿システム

なんだか汚いコードですみません。似たような記述が二箇所にあったりしてますしね・・・。
ようは、ピクセルごとに色を取得して、選択したピクセルが塗りつぶす色と違うようであればそのピクセルを塗りつぶし、さらにその点の上下左右の点を見て先ほど選択したピクセルの元の色と同じであれば塗りつぶすということを繰り返しています。
あまりいいやり方とはいえないかも。多分、下記のようなアルゴリズムを使ったほうが効率的かもしれません。
塗りつぶしアルゴリズム(scanline seed fill algorithm) – jsdo.it – Share JavaScript, HTML5 and CSS
ペイント・ルーチン (2)アルゴリズムの高速化
ただ、自分には何をやってるのか全く分からなかったので、もっと分かりやすい方法で実装しています。その分、ちょっと遅いとは思います・・・。

線にアンチエイリアスがかかって塗りつぶしが思いもよらない結果になる時があるようです。てっきり、アンチエイリアスはアルファ値で調節してるだけだと思ったので、アルファ値を考慮しない(というより0かそうでないかしか見ていない)アルゴリズムにしたのだけれども、そういうわけではないよう。どうして、lineToの機能にアンチエイリアスをかけないようにする指定がないのだろうか・・・。
ctx.translate(0.5,0.5);としたらいいとどこかに書いてあったのだけれども、あまり関係なさそうだった。