ブラウザサポート外の画像を扱う方法


ウェブブラウザではPNG, JPG, GIFなど一般的な画像形式を扱えますが、
それら以外の画像形式を使いたいこともあるのではないでしょうか？

3D映像,ゲーム業界で働いているのでTGAやDDSなど一般的ではない画像形式を扱うことが多く、
ウェブアプリを作成する際に画像をPNGなどへコンバートする作業が必要でした。

このような背景もあり今回、ブラウザサポート外の画像を扱えるように挑戦してみました。

プログラム言語はJavaでGWTを使いサーバーサイドは使わずに実装してみます。

##調査

調査の結果、HTML5 Canvasを使うのが簡単そうなので、
W3CのIDLから関係のある部分を抜き出してみます。

http://www.w3.org/TR/2dcontext/

```idl: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を作成するメソッドを実装します。

```java
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画像のキャンバスを作成します。

```java
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では動きませんでした・・・。

![alt text](http://3dtech.jp/wiki/index.php?plugin=attach&refer=TGAReader&openfile=tgaimagedemo.png "TGA Image Canvas")

今回作ったTGA画像オンラインデモページはこちらです。
http://npe-net.appspot.com/npesdk/gwt/tgaimagedemo/index.html

TGAReaderのGitHubリポジトリはこちらです。:octocat: 
https://github.com/npedotnet/TGAReader

TGAReaderの日本語解説はこちらです。
http://3dtech.jp/wiki/index.php?TGAReader

同じ要領でDDS画像をブラウザで表示するとこんな感じです。
![alt text](http://3dtech.jp/wiki/index.php?plugin=attach&refer=DDSReader&openfile=ddsimagedemo.png "DDS Image Canvas")

DDS画像のオンラインページはこちらです。
http://npe-net.appspot.com/npesdk/gwt/ddsimagedemo/index.html

TGAReaderとほぼ同じ仕様のDDSReaderのGitHubリポジトリはこちらです。 :octocat: 
https://github.com/npedotnet/DDSReader

DDSReaderの日本語解説はこちらです。
http://3dtech.jp/wiki/index.php?DDSReader

それでは楽しいGWTライフを！ :shipit: