2016年10月20日木曜日

GLSurfaceViewを使ってみる2

私が使用しているスマホが、4年前のF-10Dなので、OpenGL ES 1.0 にしか対応していない。
今時、1.0で作るのもどうかとは思うが、テストで作ってるだけだし。

でもソースコードに、

GL10

という記述をしているため、あとでバージョンを上げると、この辺りを全部差し替える作業が発生するな…。

などと思いつつ、背景部分の移植完了。
背景は、良く使う手として、小さい画像をタイル状に敷き詰めて、スクロールさせている。

Canvasを用いた描画では、画像を何枚も並べて描画していたが、GLを用いると、1枚のポリゴンに、UVを設定するだけでいい。

具体的には、u = 0.0 ~ 1.0 とすれば、1枚分が表示されるが、
u = 0.0 ~ 5.0 にすれば、5枚分が表示される。

ところが、パターンが細かい画像を使用したので発覚したのだが、フルスクリーンのうち、数か所がたまにちらついて見える。

これは…いわゆる、ダブルバッファを使わないといけない案件か?
しかし、OpenGL ES1.0には、フレームバッファはなく、
1.1のextensionを使わないといけないらしい。

しかも extensionだから、どの端末でも実装されているという保証はない。
いやまあ、1.0で作ってるのが悪いんだけど。

ところが、だ。
テストを重ねると、どうやら、描画が追い付かずにちらついているわけではないらしい。

スクロールさせるために、uvをそれぞれ、0~1の範囲で動かしているわけだが、

u= 0.184f;
v = 0.816f;

と設定すると、描画が崩れる。

これは…単なる float誤差か?

しかも、全体の一部の描画のときだけ??

面倒くさいのきたー。

2016年10月14日金曜日

GLSurfaceViewを使ってみる1

基本、2Dのゲームしか作らないので、ずっとSurfaceViewを使っているが、実行速度を上げようと思い、試しにOpenGLを使ってみることにした。

ネットを見てみると、SurfaceViewとViewの速度について、ハードウェアアクセラレーションやら GPU使用やら、条件でどちらが速いか変わるし、デフォルトの設定がどうなっているかも機種によって違うとか何とか。
面倒くさいのう。

手持ちのメイン機種 F-10D (4年前のモデル)では、SurfaceViewの方が速かった。
GPUのON/OFFでも比べたが、OFF(デフォルト)の方が速い。

SurfaceView では、40FPS程度のものが、GPU使用使用にすると、30FPSくらいに落ちる。
ほぼ何も表示しない状態でも、50FPSを超えるかどうか、というのが最速。

それに対し、それよりももっと負荷が高い表示(128x128の画像100個)でも、GLSurfaceViewは、60fps前後を維持できた。
やはり高速だ。

ただ問題もあって、GL系だと、フォント表示が面倒になる。
そもそも、画像1枚表示するにも、テクスチャバッファ作って、頂点バッファ作って…とか、色々面倒だし。

この辺りの手順、どうせ誰が作っても一緒なんだし、そろそろスマートになってもいいと思うんだけど…ならないねぇ。

逆にGLで楽になるのは、タイルパターンの描画かな。

通常のdrawBitmapでタイルペイントすると、始点の変更ができないが、GLだとuvをいじるだけで表現できるしね。

2015年1月22日木曜日

カメラのプレビューとSurfaceViewのサイズ

カメラのプレビューサイズと、SurfaceViewのサイズについては、カメラアプリを作るときに必須の知識で、すでに多くのサイトで解説がなされているので、さらっとおさらい。

カメラのプレビューサイズは、任意に決定できない
サポートされているサイズセットの中から選ぶ

プレビューサイズは、端末の画面サイズと同じとは限らない
(というか、一致しない方がデフォルト)

プレビューサイズを設定しても、最終的には、SurfaceViewのサイズになる
(viewのサイズに、ViewGroup.LayoutParams.WRAP_CONTENTを指定した場合など)

なので、16:9のアスペクト比を持つ画面いっぱいのViewに、4:3のプレビューを表示すると、横に伸びた映像になる。
この辺りを、どう解決するかで、みんながググる事になる。

一般的な解決方法は、SurfaceViewを作って、レイアウトにaddView() する際に、
プレビューの比率に合わせてサイズを指定することである。

しかしこのSurfaceViewのサイズ、一旦決めたら、変更するのがなかなか難しい。
もし、画面サイズが変わる端末があったら、プレビューが見切れたりしてしまう。

そんな、恐ろしい端末が、s5110bである!


画面下のアクションバーを(アプリ起動中でも)いつでも出し入れできるのだ。
アクションバーが消えているときは、画面一番下を横にスワイプすることで出てくる。

アクションバーが表示された状態。⏬をタッチすると消える。

アクションバーが消えた状態。背景画像も隠れていた部分が表示されている。

ちなみに画面サイズは、[800x480] [800x432]で、アクションバーの高さは48ピクセル、
割合にして、10%も変化してしまう。

アクションバーを出した状態で、カメラアプリを起動し、アクションバーを消すと、変な空白が生まれる。

この微妙な見た目に対応するためには、どうにかしてSurfaceViewのサイズを更新する必要がある。

画面サイズが変更されるので、onSizeChanged()が呼ばれるが、名前の通り、サイズが変更"された"ときに呼ばれるメソッドで、ここで何をしても、何も変化は起こらない。

そこで、見つけたのが、onMeasure() だ。

これは、画面サイズ更新時(だけではないが)に呼ばれ、
各Viewのサイズを算出して設定するメソッドだ。
ここで更新後の新サイズが取得でき、かつ、まだ反映される前のタイミングで取得できる。
試しに100x100を設定したら、そのサイズで反映された。

まあ、自力で算出しろってことだ。

そんなこんなで、実際のコード。
カメラのプレビューは、アスペクト比4:3から変更するつもりがないので、固定で書き込んでいる。


@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

  int widthMode= MeasureSpec.getMode(widthMeasureSpec);
  int heightMode= MeasureSpec.getMode(heightMeasureSpec);
  //新サイズ
  int w = MeasureSpec.getSize(widthMeasureSpec);
  int h = MeasureSpec.getSize(heightMeasureSpec);
  //変更後のサイズ
  int dw = w;
  int dh = h;

  switch(widthMode) {
  case MeasureSpec.EXACTLY:// 確定
    break;

  case MeasureSpec.UNSPECIFIED:// 自由
    dw = h *4 /3;
    break;

  case MeasureSpec.AT_MOST:// 以下
    dw = h *4 /3;

    if ( dw > w ) {
      dw = w;
    }
    break;
  }

  switch(heightMode) {
  case MeasureSpec.EXACTLY:// 確定
    break;

  case MeasureSpec.UNSPECIFIED:// 自由
    break;

  case MeasureSpec.AT_MOST:// 以下
    dh = dw *3 /4;
    if ( dh > h )
     dh = h;

    break;
  }

  //サイズ反映
  setMeasuredDimension(dw, dh);

}