Android カメラプレビューを縦向きにすると画面描画の座標が訳わからなくなる対応

1.カメラプレビューを縦向きに表示させる

さて、Android のカメラ周りを触ろうと思うと、毎回すんなりいかなくて楽しめるのですが、今回は、カメラプレビュ-の上にテキストやら図形やらを描画させてみようとしてはまりました。

カメラプレビューが横向きに表示されてしまう!の対応」 にて、カメラプレビューでは、アクティビティを縦向きに設定し、カメラプレビューを90°回転させることで、縦向きのプレビューを実現しています。

android_camera_preview01

2.座標系がよくわからなくなってしまう

ところが、これをすると、左上が、(0,0) で右下が、(画面幅、画面高さ) という、座標が意味をなさなくなってしまうので、画像を表示する程度ならトライアンドエラーで何とかなるのですが、ちょっと細かいことをやろうとすると、もう何が何だか・・・

 

3.感触をつかむ

わかっている人には当たり前なんでしょうが、基本業務系の開発者なので、グラフィック周りはちんぷんかんぷん。トライアンドエラーで、どう座標が変換されているか考えてみました。

android_camera_preview02

RectF の値を変えながら、drawRect をしてみると、上図の様な感じに矩形が描画されました。何となく、座標が反転している様な感触をえました。

4.想定

さて、まぁ右に90°回転させているのだから、Matrix 使って、-90°回転させてあげればいいんじゃないかな~ なんて軽く考えて、試し、試すも、なかなか思ったような描画ができないよぅ。。。

と、かれこれ半日位格闘した結果、

android_camera_preview03

本来、カメラは上図の右側の様に横向きの状態。それを90°回転させるということは、第4象限から、第3象限に原点を起点に90°回転して移っているのであろうということがわかった。

本来は、上図右のように描画してあげればいいのだが、直感的ではないので、上図左、しかも画面の左上を(0,0)として描画(座標を取得)して、上図右に変換してあげれば良さそうだ。

 

5.対処法のイメージ

android_camera_preview04

ということは、

① まず、通常通り、画面の左上を原点(第4象限) として位置を取得

① で、画面高さ(横向き基準) 分、左へ移動(第3象限)

② 原点を起点に、-90°回転(第4象限にもどり、ほしい位置が得られる)

これでいけそう。

6.ソースコード

こんな感じ。prepareRestorePortrait で左に移動して、canvas.rotate(-90) で、回転。

/**
 * カメラプレビューのために、縦向きにビューを設定(90度回転)した場合、
 * 元に戻して(-90度)描画を行うが、縦向を想定した位置を、元に戻した場合に
 * 同じ位置に来るように事前に調整する
 * @param rect
 * @param degrees
 * @return
 */
private RectF prepareRestorePortrait(RectF rect) {
    float left   = rect.left;
    float top    = rect.top;
    float right  = rect.right;
    float bottom = rect.bottom;
    
    float[] src = {left, top, right, top, right, bottom, left, bottom};
    float[] dst = new float[8];
    Matrix matrix = new Matrix();
    // 高さ(画面横向き基準)分
    matrix.preTranslate(-getHeight(),0);
    matrix.mapPoints(dst, src);
    //               left    top     right   bottom
    return new RectF(dst[0], dst[1], dst[4],dst[5]);
}

@Override
protected void onDraw(Canvas canvas) {
    final float FONT_MARGIN = 10f;
    final float TEXT_LEFT   = 40f;
    final float MEMO_LEFT   = 20f;
    
    // キャンバスを-90度 回転
    canvas.rotate(-90);
             
             :
    // 縦の画面を基準とした位置(left,top,right,bottom)を元に変換後の位置(newRect)を得る
    RectF newRect = prepareRestorePortrait(new RectF(
                                              left, 
                                              top, 
                                              right,
                                              bottom));
             :
             
    // 変換後の位置で矩形描画したり・・・
    canvas.drawRect(newRect, paint);
    
    // テキストを表示したり・・・
    canvas.drawText(msg, newRect.left, newRect.top + txtHeight ,paint); 
             :
                                              

7. カメラプレビューにかぶせることができました!

android_camera_preview05 

 

また一歩野望に近づいた!

以上。