Search code examples
androidandroid-canvasscalepinchzoom

Draw image on scaled canvas


i'm trying to draw an image on scaled canvas where the user touched the screen but it drawing the image with the wrong scale. I'm using custom scale listener to scale the canvas according to the suggestion here:

public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

    private float lastFocusX;
    private float lastFocusY;

    public ScaleListener() {
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        lastFocusX = detector.getFocusX();
        lastFocusY = detector.getFocusY();
        return true;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        Matrix transformatioMatrix = new Matrix();
        float focusX = detector.getFocusX();
        float focusY = detector.getFocusY();

        transformatioMatrix.postTranslate(-focusX, -focusY);
        transformatioMatrix.postScale(detector.getScaleFactor(), detector.getScaleFactor());

        float focusShiftX = focusX - lastFocusX;
        float focusShiftY = focusY - lastFocusY;
        transformatioMatrix.postTranslate(focusX + focusShiftX, focusY + focusShiftY);

        backgroundMatrix.postConcat(transformatioMatrix);

        lastFocusX = focusX;
        lastFocusY = focusY;

        return true;
    }
}

and SimpleOnGestureListener to scroll and draw the image

public class ScrollListener extends GestureDetector.SimpleOnGestureListener{
    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {           
        float touchX = event.getX();
        float touchY = event.getY();
        Toast.makeText(context, "onSingleTapUp", Toast.LENGTH_SHORT).show();
        playersCanvas.drawBitmap(playerBitmap, touchX, touchY, canvasPaint);
        invalidate();
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2,
            float distanceX, float distanceY) {
        backgroundMatrix.postTranslate(-distanceX, -distanceY);
        invalidate();
        return true;
    }
}

this is my OnDraw:

@Override
protected void onDraw(Canvas canvas) {  
    canvas.drawBitmap(background, backgroundMatrix, canvasPaint);
    canvas.drawBitmap(playersBitmap, backgroundMatrix, canvasPaint);
}

now when i touch the screen the image does draws to the canvas but not where i touched and not in the correct scale. For example: This is the original background: enter image description here

After scale and scroll i touched the screen in the red circle location (added after) and the image was drawn in the right bottom corner enter image description here

And after scaling back to original size you can see the wrong location on the canvas: enter image description here

i found this thread that assume that i have the scale point which i dont due to the "free" scale and scroll options.

how can i draw the image in the correct position with the right scale?


Solution

  • Found the answer with hint from @pskink. now the onSingleTapUp looks like this:

            @Override
        public boolean onSingleTapUp(MotionEvent event) {           
            float touchX = event.getX();
            float touchY = event.getY();
    
            float[] points = new float[2];          
            points[0] = touchX;
            points[1] = touchY;
    
            Matrix inverse = new Matrix();
            backgroundMatrix.invert(inverse);
            inverse.mapPoints(points);
            Bitmap scaledPlayer = Bitmap.createBitmap(playerBitmap, 0, 0, playerBitmap.getWidth(), playerBitmap.getHeight(), inverse, true);
            playersCanvas.drawBitmap(scaledPlayer, points[0], points[1], canvasPaint);
    
            invalidate();
            return true;
        }