ウェブブラウザではPNG, JPG, GIFなど一般的な画像形式を扱えますが、
それら以外の画像形式を使いたいこともあるのではないでしょうか?
3D映像,ゲーム業界で働いているのでTGAやDDSなど一般的ではない画像形式を扱うことが多く、
ウェブアプリを作成する際に画像をPNGなどへコンバートする作業が必要でした。
このような背景もあり今回、ブラウザサポート外の画像を扱えるように挑戦してみました。
プログラム言語はJavaでGWTを使いサーバーサイドは使わずに実装してみます。
##調査
調査の結果、HTML5 Canvasを使うのが簡単そうなので、
W3CのIDLから関係のある部分を抜き出してみます。
interface CanvasRenderingContext2D {
ImageData createImageData(unrestricted double sw, unrestricted double sh);
void putImageData(ImageData imagedata, double dx, double dy);
};
interface ImageData {
readonly attribute unsigned long width;
readonly attribute unsigned long height;
readonly attribute Uint8ClampedArray data;
};
CanvasからContext2Dを取得してcreateImageData()でImageDataを作成し、
ImageData.dataに色要素(RGBA[0, 255])を詰めて
putImageData()でCanvas領域に描画する方向で実装したいと思います。
##実装
TGA画像をGWTを使ってブラウザ上へ表示するプログラムを作りたいと思います。
TGA画像の読み込みには私がGitHubで公開しているTGAReaderを使いました。
TGAReaderの仕様上、TGA画像のバイナリデータをbyte配列で用意して、
RGBA各要素8ビットのint配列を受け取る形になります。
まず始めに画像のピクセル配列、幅、高さからCanvasを作成するメソッドを実装します。
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.CanvasPixelArray;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.ImageData;
private Canvas createImageCanvas(int [] pixels, int width, int height) {
Canvas canvas = Canvas.createIfSupported();
canvas.setCoordinateSpaceWidth(width);
canvas.setCoordinateSpaceHeight(height);
Context2d context = canvas.getContext2d();
ImageData data = context.createImageData(width, height);
CanvasPixelArray array = data.getData();
for(int i=0; i<width*height; i++) {
array.set(4*i+0, pixels[i] & 0xFF); // R
array.set(4*i+1, (pixels[i] >> 8) & 0xFF); // G
array.set(4*i+2, (pixels[i] >> 16) & 0xFF); // B
array.set(4*i+3, (pixels[i] >> 24) & 0xFF); // A
}
context.putImageData(data, 0, 0);
return canvas;
}
続いてXMLHttpRequestでTGA画像をArrayBufferで受け取ります。
受け取ったArrayBufferをbyte配列へ変換してTGAReaderで画像のピクセル配列を作成します。
最後に先ほど作ったcreateImageCanvasメソッドでTGA画像のキャンバスを作成します。
import com.google.gwt.typedarrays.client.Uint8ArrayNative;
import com.google.gwt.typedarrays.shared.ArrayBuffer;
import com.google.gwt.xhr.client.ReadyStateChangeHandler;
import com.google.gwt.xhr.client.XMLHttpRequest;
import com.google.gwt.xhr.client.XMLHttpRequest.ResponseType;
private void addTGACanvas(String url) {
XMLHttpRequest request = XMLHttpRequest.create();
request.open("GET", url);
request.setResponseType(ResponseType.ArrayBuffer);
request.setOnReadyStateChange(new ReadyStateChangeHandler() {
@Override
public void onReadyStateChange(XMLHttpRequest xhr) {
if(xhr.getReadyState() == XMLHttpRequest.DONE) {
if(xhr.getStatus() >= 400) {
// error
System.out.println("Error");
}
else {
try {
// ArrayBufferからbyte配列へ変換します
ArrayBuffer arrayBuffer = xhr.getResponseArrayBuffer();
Uint8ArrayNative u8array = Uint8ArrayNative.create(arrayBuffer);
byte [] buffer = new byte[u8array.length()];
for(int i=0; i<buffer.length; i++) {
buffer[i] = (byte)u8array.get(i);
}
// TGA画像の読み込みを行います
int pixels [] = TGAReader.read(buffer, TGAReader.ABGR);
int width = TGAReader.getWidth(buffer);
int height = TGAReader.getHeight(buffer);
// TGA画像キャンバスを作ります
Canvas canvas = createImageCanvas(pixels, width, height);
// 作ったキャンバスをお好きなように・・・
panel.add(canvas);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
}
});
request.send();
}
##結果
GoogleChrome, FireFox, Safariでの動作確認はできましたが、
残念なことにIEでは動きませんでした・・・。
今回作ったTGA画像オンラインデモページはこちらです。
http://npe-net.appspot.com/npesdk/gwt/tgaimagedemo/index.html
TGAReaderのGitHubリポジトリはこちらです。
https://github.com/npedotnet/TGAReader
TGAReaderの日本語解説はこちらです。
http://3dtech.jp/wiki/index.php?TGAReader
DDS画像のオンラインページはこちらです。
http://npe-net.appspot.com/npesdk/gwt/ddsimagedemo/index.html
TGAReaderとほぼ同じ仕様のDDSReaderのGitHubリポジトリはこちらです。
https://github.com/npedotnet/DDSReader
DDSReaderの日本語解説はこちらです。
http://3dtech.jp/wiki/index.php?DDSReader
それでは楽しいGWTライフを!