Android: how to warp images?
android, image-processing
Solution
There is a much easier way than coding you own. See TransitionDrawable
An extension of LayerDrawables that is intended to cross-fade between the first and second layer. To start the transition, call startTransition(int). To display just the first layer, call resetTransition(). It can be defined in an XML file with the <transition> element. Each Drawable in the transition is defined in a nested <item>
You can find many examples on line, here is one:
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/first_image" />
<item android:drawable="@drawable/second_image" />
</transition>
and the code
final ImageView image = (ImageView) findViewById(R.id.image);
final ToggleButton button = (ToggleButton) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
TransitionDrawable drawable = (TransitionDrawable) image.getDrawable();
if (button.isChecked()) {
drawable.startTransition(500);
} else {
drawable.reverseTransition(500);
}
}
});
Problem
I want to warp images like this: Added 08-04-2013: I used this code but it's not working properly: ``` private static final int WIDTH = 20; private static final int HEIGHT = 20; private static final int COUNT = (WIDTH + 1) * (HEIGHT + 1); private final Bitmap mBitmap; private final float[] mVerts = new float[COUNT*2]; private final float[] mOrig = new float[COUNT*2]; private final Matrix mMatrix = new Matrix(); private final Matrix mInverse = new Matrix(); private static void setXY(float[] array, int index, float x, float y) { array[index*2 + 0] = x; array[index*2 + 1] = y; } public SampleView(Context context) { super(context); setFocusable(true); mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); float w = mBitmap.getWidth(); float h = mBitmap.getHeight(); // construct our mesh int index = 0; for (int y = 0; y <= HEIGHT; y++) { float fy = h * y / HEIGHT; for (int x = 0; x <= WIDTH; x++) { float fx = w * x / WIDTH; setXY(mVerts, index, fx, fy); setXY(mOrig, index, fx, fy); index += 1; } } mMatrix.setTranslate(10, 10); mMatrix.invert(mInverse); } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(0xFFCCCCCC); canvas.concat(mMatrix); canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, mVerts, 0, null, 0, null); } private void warp(float cx, float cy) { final float K = 10000; float[] src = mOrig; float[] dst = mVerts; for (int i = 0; i < COUNT*2; i += 2) { float x = src[i+0]; float y = src[i+1]; float dx = cx - x; float dy = cy - y; float dd = dx*dx + dy*dy; float d = FloatMath.sqrt(dd); float pull = K / (dd + 0.000001f); pull /= (d + 0.000001f); // android.util.Log.d("skia", "index " + i + " dist=" + d + " pull=" + pull); if (pull >= 1) { dst[i+0] = cx; dst[i+1] = cy; } else { dst[i+0] = x + dx * pull; dst[i+1] = y + dy * pull; } } } private int mLastWarpX = -9999; // don't match a touch coordinate private int mLastWarpY; @Override public boolean onTouchEvent(MotionEvent event) { float[] pt = { event.getX(), event.getY() }; mInverse.mapPoints(pt); int x = (int)pt[0]; int y = (int)pt[1]; if (mLastWarpX != x || mLastWarpY != y) { mLastWarpX = x; mLastWarpY = y; warp(pt[0], pt[1]); invalidate(); } return true; } ```