2011年9月16日金曜日

「BufferedImage」の TYPE_4BYTE_ABGR から TYPE_INT_ARGB にする方法

Java の BufferedImage は getType() を使うことでタイプを知ることが出来ます。
byte型でABGRの順に記録されている場合は、BufferedImage.TYPE_4BYTE_ABGR
int型でARGBが記録されている場合は、BufferedImage.TYPE_INT_ARGB になります。

TYPE_4BYTE_ABGR の画像を、 ColorConvertOp(中ではg.drawImage(image, 0, 0, null);) を利用して変換すると、なぜか色が変わる場合があります。
PixelGrabber を利用すれば、色は変わらないです。

ですが、直接変換する方法のサンプルを紹介します。
BufferedImage から int型のデータを取得するサンプルにもなると思います。

BufferedImage.TYPE_4BYTE_ABGR の image から
BufferedImage.TYPE_INT_ARGB の newimage へ

int width = image.getWidth();
int height = image.getHeight();
int pixelsize = width * height;
BufferedImage newimage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
int[] pixels = ((DataBufferInt)(newimage.getRaster().getDataBuffer())).getData();
byte[] binary = ((DataBufferByte)(image.getRaster().getDataBuffer())).getData();
int r,g,b,a;
for(int i=0,j=0;i<pixelsize;i++) {
a = binary[j++]&0xff;
b = binary[j++]&0xff;
g = binary[j++]&0xff;
r = binary[j++]&0xff;
pixels[i] = (a<<24)|(r<<16)|(g<<8)|b;
}
以上です。

BufferedImage.TYPE_3BYTE_BGR の場合は

b = binary[j++]&0xff;
g = binary[j++]&0xff;
r = binary[j++]&0xff;
pixels[i] =0xff000000|(r<<16)|(g<<8)|b;


BufferedImage.TYPE_CUSTOM で取得した DataBuffer のタイプが TYPE_BYTE の場合も
上記のような感じで変換できるようです。
但し、順番が下記のようになるっぽいです。

BufferedImage.TYPE_CUSTOM かつ getColorModel().hasAlpha() で true の場合は

r = binary[j++]&0xff;
g = binary[j++]&0xff;
b = binary[j++]&0xff;
a = binary[j++]&0xff;
pixels[i] = (a<<24)|(r<<16)|(g<<8)|b;
BufferedImage.TYPE_CUSTOM かつ getColorModel().hasAlpha() で false の場合は

r = binary[j++]&0xff;
g = binary[j++]&0xff;
b = binary[j++]&0xff;
pixels[i] = 0xff000000|(r<<16)|(g<<8)|b;
ImageIO.read で読み込んだ時点で、
インデックスカラーとグレースケール以外は、TYPE_INT_ARGB に変換してくれればいいのに。

2011年9月14日水曜日

Anrdoidのエミュレータ起動中にSDをマウント・アンマウントする

エミュレータ起動時にSDカードイメージを指定する例はたくさん見かけたけど、起動後に動的にやるのはあまり見かけなかったのでメモ。
SDからファイルを読むようなアプリで突然SDを抜かれたり、差されたりしたときの挙動を確認したいときに。
* (1)emuratorコマンドでエミュレータを起動する(SDイメージ有りのAVDを使う)
* (2)"adb shell"コマンドでエミュレータに接続
* (3)"sdutil unmount /sdcard" でSDが抜かれる
* (4)"sdutil mount /sdcard"でSDが差される

今更ながらAndroidで3D。GLSurfaceViewを使ってみる。

ビジネス向けのアプリケーションでは
余り使われる事が無い3Dグラフィックスですが、
使いこなすことが出来れば、面白い技術なのではないでしょうか。
3Dプログラミングの概念などはちょっと置いておいて、
「Androidで3Dを描画するには最低限何を書けばいいの?」
という所に着目して、説明をしたいと思います。
新規Androidプロジェクトを作成したと仮定します。
その後、必要なクラスファイルは以下の三つだけです。
・MainActivity.java
・TriangleRenderer.java
・Triangle.java
Eclipseで作成するとこんな感じになります。
パッケージ分けはやらなくても全然OKです。
早速、コードにいってみましょう。
MainActivity.java
  1. public class MainActivity extends Activity {  
  2.   
  3. private GLSurfaceView mGLSurfaceView;  
  4. private TriangleRenderer tr = null;  
  5.   
  6. @Override  
  7. protected void onCreate(Bundle savedInstanceState) {  
  8. super.onCreate(savedInstanceState);  
  9. //GLSurfaceViewを取得  
  10. mGLSurfaceView = new GLSurfaceView(this);  
  11. // Rendererを作成  
  12. tr = new TriangleRenderer();  
  13. //GLSurfaceViewRendererを設定  
  14. mGLSurfaceView.setRenderer(tr);  
  15. setContentView(mGLSurfaceView);  
  16. }  
  17.   
  18. @Override  
  19. protected void onResume() {  
  20. super.onResume();  
  21. mGLSurfaceView.onResume();  
  22. }  
  23.   
  24. @Override  
  25. protected void onPause() {  
  26. super.onPause();  
  27. mGLSurfaceView.onPause();  
  28. }  
  29.   
  30. }  
TriangleRenderer.java
  1. public class TriangleRenderer  implements GLSurfaceView.Renderer {  
  2.   
  3. private Triangle mTriangle;  
  4. private int size = 0x10000;  
  5.   
  6. public TriangleRenderer() {  
  7. mTriangle = new Triangle(0 , size);  
  8. }  
  9.   
  10. public void onDrawFrame(GL10 gl) {  
  11. //画面をクリア  
  12. gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);  
  13.   
  14. // モデルビューの行列を用いて作業する事を指定する。  
  15. gl.glMatrixMode(GL10.GL_MODELVIEW);  
  16. gl.glLoadIdentity(); // 現在選択されている行列に単位行列をロードする。  
  17.   
  18. //移動(初期カメラ位置  
  19. gl.glTranslatef(00, -3.0f);  
  20.   
  21. //頂点アレイを設定  
  22. gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);  
  23. //カラーアレイを設定  
  24. gl.glEnableClientState(GL10.GL_COLOR_ARRAY);  
  25.   
  26. //カリングの設定  
  27. gl.glFrontFace(GL10.GL_CW);  
  28. //三角形を描画  
  29. mTriangle.draw(gl);  
  30.   
  31. }  
  32.   
  33. /** 
  34. * OpenGLの透視投影の描画領域が変更された時の設定 
  35. */  
  36. @Override  
  37. public void onSurfaceChanged(GL10 gl, int width, int height) {  
  38. /** 
  39. * OpenGLが表示に使う長方形領域を設定します。 
  40. * (x,y)が表示領域の左上の座標、(w,h)が表示領域の幅と高さになります。 
  41. */  
  42. gl.glViewport(00, width, height);  
  43.   
  44. // 透視投影(遠近投影法)の行列を指定する。  
  45. float ratio = (float) width / height;  
  46. gl.glMatrixMode(GL10.GL_PROJECTION);  
  47.   
  48. // 現在選択されている行列に単位行列をロードする。  
  49. gl.glLoadIdentity();  
  50.   
  51. /** 
  52. ビューボリューム(視体積)を指定します。 
  53. 引数は視体積の左、右、下、上、近クリップ面、遠クリップ面の座標を定義 
  54. */  
  55. gl.glFrustumf(-ratio, ratio, -111100);  
  56. }  
  57.   
  58. /** 
  59. * OpenGLの初期設定 
  60. */  
  61. @Override  
  62. public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
  63. /** 
  64. *  DITHERをオフにします。 
  65. *  DITHERとは量子化の誤差を最小にするべく 
  66. *  サンプルデータに意図的に追加される誤った信号・データのこと。 
  67. */  
  68. gl.glDisable(GL10.GL_DITHER);  
  69.   
  70. // OpenGLにスムージングを設定  
  71. gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);  
  72.   
  73. // 背景色  
  74. gl.glClearColor(1,1,1,1);  
  75. //スムースシェーディング:平面のポリゴンを曲面に見せかける処理。  
  76. gl.glShadeModel(GL10.GL_SMOOTH);  
  77.   
  78. /** 
  79. 多角形に影を付けるには、各多角形の前後関係を決定する必要がある。 
  80. これをするのが、デプステストです。 
  81. このデプステストを有効にします。 
  82. */  
  83. gl.glEnable(GL10.GL_DEPTH_TEST);  
  84. }  
  85. }  
Triangle.java
  1. public class Triangle {  
  2.   
  3. private IntBuffer   mVertexBuffer;  
  4. private IntBuffer   mColorBuffer;  
  5. private ByteBuffer  mIndexBuffer;  
  6.   
  7. public Triangle(int p , int size){  
  8. int one = 0x10000;  
  9. //座標リスト  
  10. int vertices[] = {  
  11. p - size , p        , p + size,  // 左下  
  12. p + size , p        , p + size,  // 右下  
  13. p        , p + size , p       ,  //   
  14. };  
  15.   
  16. //頂点色  
  17. int colors[] = {  
  18. 0 ,    0,    0,  one,  
  19. 0 ,    0,    0,  one,  
  20. 0 ,    0,    0,  one,  
  21. };  
  22.   
  23. //頂点の描画順インデックス  
  24. byte indices[] = {  
  25. 012,  
  26. };  
  27.   
  28. //バイトバッファにそれぞれのリストを登録  
  29. ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);  
  30. vbb.order(ByteOrder.nativeOrder());  
  31. mVertexBuffer = vbb.asIntBuffer();  
  32. mVertexBuffer.put(vertices);  
  33. mVertexBuffer.position(0);  
  34.   
  35. ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);  
  36. cbb.order(ByteOrder.nativeOrder());  
  37. mColorBuffer = cbb.asIntBuffer();  
  38. mColorBuffer.put(colors);  
  39. mColorBuffer.position(0);  
  40.   
  41. mIndexBuffer = ByteBuffer.allocateDirect(indices.length);  
  42. mIndexBuffer.put(indices);  
  43. mIndexBuffer.position(0);  
  44. }  
  45.   
  46. //描画  
  47. public void draw(GL10 gl){  
  48. gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);  
  49. gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer);  
  50. gl.glDrawElements(GL10.GL_TRIANGLES, 3, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);  
  51. }  
  52. }  
これだけでもう3Dが出来ています。
早速実行してみましょう。
期待通りに黒い三角形が描画されていますね。
さて、何故このような黒い三角形が描けたのか?
解説していきます。
まず注目すべきは、Triangle.javaです。
Triangle.javaのこの部分で頂点と色を決めています。
(分かりやすいように修正しています。)
  1. 引数p = 0 , size = 0x10000;  
  2. //座標リスト  
  3. int vertices[] = {  
  4. -size , 0      , size,  // 左下  
  5. size , 0      , size,  // 右下  
  6. 0      , size , 0    ,  //   
  7. };  
  8.   
  9. //頂点色  
  10. int colors[] = {  
  11. 0 ,    0,    0,  one,  
  12. 0 ,    0,    0,  one,  
  13. 0 ,    0,    0,  one,  
  14. };  
int vertices[]が頂点です。
三次元の頂点は(x軸 , y軸 , z軸)の三つの点で指定します。
よって、
  1. int vertices[] = {  
  2. // (左下x座標 左下y座標 左下z座標)  
  3. -size , 0      , size,  
  4. // (右下x座標 右下y座標 右下z座標)  
  5. size , 0      , size,  
  6. // (x座標 y座標 z座標)  
  7. 0      , size , 0    ,  
  8. }  
ただのint型の配列ですが、実はこのような意味があります。
こうして見ると分かりやすいのですが、実は上の点は
奥に傾いているため、45度斜めに描画されています。
色も同様です。が、RGBAのため、最後にalpha値を指定するため、
配列4つで一点になります。
  1. //頂点色  
  2. int colors[] = {  
  3. // (左下Red , 左下Green , 左下Blue , 左下Alpha)  
  4. 0 ,    0,    0,  one,  
  5. // (右下Red , 右下Green , 右下Blue , 右下Alpha)  
  6. 0 ,    0,    0,  one,  
  7. // (Red , Green , Blue , Alpha)  
  8. 0 ,    0,    0,  one,  
  9. };  
このようになります。試しに
上部だけ真っ赤にしてみましょう
  1. //頂点色  
  2. int colors[] = {  
  3. // (左下Red , 左下Green , 左下Blue , 左下Alpha)  
  4. 0 ,    0,    0,  one,  
  5. // (右下Red , 右下Green , 右下Blue , 右下Alpha)  
  6. 0 ,    0,    0,  one,  
  7. // (Red , Green , Blue , Alpha)  
  8. one ,    0,    0,  one,  
  9. };  
これで上だけ赤になるはずです。
実行してみましょう。
おお、赤くなってますね。
期待通りです。
では、次に3Dであることを
認識するためにこの三角形をグルグルと
回してみましょう。
今度はTriangleRenderer.javaを編集します。
といってもすることは少ないです。
TriangleRenderer.java
  1. float mView = 0.0f; ←追加  
  2.   
  3. public void onDrawFrame(GL10 gl) {  
  4. //画面をクリア  
  5. gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);  
  6.   
  7. // モデルビューの行列を用いて作業する事を指定する。  
  8. gl.glMatrixMode(GL10.GL_MODELVIEW);  
  9. gl.glLoadIdentity(); // 現在選択されている行列に単位行列をロードする。  
  10.   
  11. //移動(初期カメラ位置  
  12. gl.glTranslatef(00, -3.0f);  
  13.   
  14. //頂点アレイを設定  
  15. gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);  
  16. //カラーアレイを設定  
  17. gl.glEnableClientState(GL10.GL_COLOR_ARRAY);  
  18.   
  19. //gl.glRotatef(mView , 0, 1, 0); //横回転 ←追加  
  20. //gl.glRotatef(mView , 1, 0, 0); //縦回転 ←追加  
  21.   
  22. //カリングの設定  
  23. gl.glFrontFace(GL10.GL_CW);  
  24. //三角形を描画  
  25. mTriangle.draw(gl);  
  26.   
  27. mView += 1.2f; ←追加  
  28. }  
回転はコメントアウトしてあるので、片方ずつ外して
動作を確かめてみてください。
もちろん、同時に動かすのもアリだと思います。
これで凄くザックリですが説明を終わります。
本当はまだまだ解説し足りません。
もし、これで3Dに興味を持った方は、
OpenGLについて調べてみては如何でしょうか?
このプログラムを元に、
複数の三角形を書いてみたり、
三角形から四角形に変えてみたりしてみてください。
回転操作をフリック化してみたりするのもいいですね。