drawBitmapMeshで画像の変形


Pocket

drawBitmapMeshを使って画像を変形する方法を試してみました。

今回やってみたのは、こんな感じにユーザーがタッチした位置にみょ〜〜んと画像を歪ませて描画します。

歪ませ画像

使用するのはCanvas#drawBitmapMeshです。
はじめにメッシュを設定し、その上に画像を貼り付けると言った感じでしょうか。

メッシュの説明1
メッシュの説明2

メッシュデータを作る

まず初めにメッシュデータを作ります。
データはfloat型の配列で

float[] verts = {x0, y0, x1, y1, x2, y2, ... xn, yn}; //X座標とY座標をサイズ分
//よってメッシュ配列のサイズは(幅+1)×(高さ+1)×2になります。
int MESH_WIDTH = 10;
int MESH_HEIGHT = 10;
int MESH_SIZE = (MESH_WIDTH+1)*(MESH_HEIGHT+1)*2;

 

画像を描画

普通にonDrawです。

@Override
protected void onDraw(Canvas canvas) {
  canvas.drawBitmapMesh(bmp, MESH_WIDTH, MESH_HEIGHT, verts, 0, null, 0, null);
}

第5引数のvertOffsetは、使用するメッシュ配列のオフセットで、おそらく1枚のメッシュに複数画像を描画するときなどに使用するのだと思います。
第6,7引数のcolorsは・・・May be nullらしいです。対応するビットマップの色に掛け合わされる?・・・今回はパスで。

 

早速試してみる

冒頭のタッチした位置に画像が歪むやつを作ってみました。
かなり適当に書いたコードですが、動かしてみるとBitmapMeshの感じがつかめると思います。

public class MeshView extends View implements View.OnTouchListener{
    private final int MESH_WIDTH = 20;
    private final int MESH_HEIGHT = 20;
    private final int MESH_SIZE = (MESH_WIDTH+1)*(MESH_HEIGHT+1)*2;

    private Bitmap mBmp;
    private float[] mVerts;
    private Paint mPaint;


    public MeshView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mBmp = BitmapFactory.decodeResource(getResources(), R.drawable.a0810_000016);
        setInitialMesh();
        setOnTouchListener(this);
        mPaint = new Paint();
        mPaint.setStrokeWidth(2);
        mPaint.setColor(Color.WHITE);
    }


    public void setInitialMesh() {
        mVerts = new float[MESH_SIZE];

        for (int i=0;i<=MESH_HEIGHT;i++) {
            for (int j=0;j<=MESH_WIDTH;j++) {
                int pos = i * (MESH_WIDTH + 1) + j;
                mVerts[2 * pos] = j * mBmp.getWidth()/MESH_WIDTH;
                mVerts[2 * pos + 1] = i * mBmp.getHeight()/MESH_HEIGHT;
            }
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction()==MotionEvent.ACTION_MOVE) {
           stepNearerTouchPoint(event.getX(), event.getY());
            invalidate();
        }
        return true;
    }

    private void stepNearerTouchPoint(float x, float y) {
        //メッシュの外周は固定したままにする
        for (int i=1;i<MESH_HEIGHT;i++) {
            for (int j=1;j<MESH_WIDTH;j++) {
                int pos = i * (MESH_WIDTH + 1) + j;
                float dist = (float)Math.sqrt((mVerts[2 * pos] - x) * (mVerts[2 * pos] - x) + (mVerts[2 * pos + 1] - y) * (mVerts[2 * pos + 1] - y));
                mVerts[2 * pos] += (x - mVerts[2 * pos]) / dist;
                mVerts[2 * pos + 1] += (y - mVerts[2 * pos + 1]) / dist;
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmapMesh(mBmp, MESH_WIDTH, MESH_HEIGHT-1, mVerts, (MESH_WIDTH+1)*2, null, 0, null);

        // 分り易いようにメッシュを表示
        for (int i=0;i<=MESH_HEIGHT;i++) {
            for (int j=0;j<=MESH_WIDTH;j++) {
                int pos = i * (MESH_WIDTH + 1) + j;
                canvas.drawPoints(mVerts, mPaint);
            }
        }
    }
}

以上です。

 

あとがき

前からちょっと気になっていたメソッドなので試してみたら、結構使えそうだなと。
あと、Canvas#drawVerticesも気になるんですよね。
そのうち試します。

ではでは。
 
 

Leave a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>