4

I am making an App with coloring book. I have found a library byronsanchez/coloring-book-android, using which I am able to do the coloring fine. However, I need to implement zoom for the same. I have used ScaleImageView for zooming purpose.

The problem I am facing is, when I zoom the image, I am not able to color to specific areas of image. When the image is not zoomed, it detects touch fine for different areas, but after zooming, it fills color to different area while touching on an area.

I searched for some solutions, but I am not able to understand how to implement it in my code. Here is one of the answers I came across while I was searching for solution. I think I already have scaling functions in my code, but still it is not working.

Please provide me some example with a little explanation. I am really stuck in this, hope I get some help out here, Thanks!!!

Here is my code where I am filling colors in bitmap:

@Override
public boolean onTouch(View paramView, MotionEvent event)
{
    try
    {
        int action = event.getAction();
        switch (action & MotionEvent.ACTION_MASK)
        {
            case MotionEvent.ACTION_DOWN:
                //ScaleImageView img = (ScaleImageView) findViewById(R.id.iv_coloring_figure);
                BitmapDrawable drawable = (BitmapDrawable) mImage.getDrawable();
                bmp = drawable.getBitmap();
                Rect imageBounds = new Rect();
                mImage.getDrawingRect(imageBounds);

                // original height and width of the bitmap
                int intrinsicHeight = drawable.getIntrinsicHeight();
                int intrinsicWidth = drawable.getIntrinsicWidth();

                // height and width of the visible (scaled) image
                int scaledHeight = imageBounds.height();
                int scaledWidth = imageBounds.width();

                // Find the ratio of the original image to the scaled image Should normally be equal unless a disproportionate scaling (e.g. fitXY) is used.
                float heightRatio = (float) intrinsicHeight / scaledHeight;
                float widthRatio = (float) intrinsicWidth / scaledWidth;

                // do whatever magic to get your touch point
                // MotionEvent event;

                // get the distance from the left and top of the image bounds
                float scaledImageOffsetX = event.getX() - imageBounds.left;
                float scaledImageOffsetY = event.getY() - imageBounds.top;

                // scale these distances according to the ratio of your scaling
                // For example, if the original image is 1.5x the size of the scaled image, and your offset is (10, 20), your original image offset values should be (15, 30).
                int originalImageOffsetX = Math.round(scaledImageOffsetX * widthRatio);
                int originalImageOffsetY = Math.round(scaledImageOffsetY * heightRatio);
                int oldColor = bmp.getPixel(originalImageOffsetX, originalImageOffsetY);

                arrX.add(originalImageOffsetX);
                arrY.add(originalImageOffsetY);
                Log.e("arrX", "originalImageOffsetX is " + arrX);
                Log.e("arrY", "originalImageOffsetY is " + arrY);
                index = arrX.size();

                int newColor = 0;
                Log.e("sel", "sel color"+selColor);
                if(selColor == null)
                    {
                        Toast.makeText(getApplicationContext(), "Please select a color to fill!", Toast.LENGTH_LONG).show();
                    }
                newColor = Color.parseColor(selColor);

                if (oldColor != newColor && oldColor != Color.BLACK)
                {
                    FloodFill floodfill = new FloodFill(bmp, oldColor, newColor);
                    floodfill.fill(originalImageOffsetX, originalImageOffsetY);
                    mImage.invalidate();
                }
                break;
            default:
                break;
        }
    } 
    catch (Exception e)
    {
        e.printStackTrace();;
    }

    return false;
}

and here is the code I am using to zoom.

public class ScaleImageView extends ImageView implements OnTouchListener {
private Context mContext;
private float MAX_SCALE = 2f;

private Matrix mMatrix;
private final float[] mMatrixValues = new float[9];

// display width height.
private int mWidth;
private int mHeight;

private int mIntrinsicWidth;
private int mIntrinsicHeight;

private float mScale;
private float mMinScale;

private float mPrevDistance;
private boolean isScaling;

private int mPrevMoveX;
private int mPrevMoveY;
private GestureDetector mDetector;

String TAG = "ScaleImageView";

public ScaleImageView(Context context, AttributeSet attr) {
    super(context, attr);
    this.mContext = context;
    initialize();
}

public ScaleImageView(Context context) {
    super(context);
    this.mContext = context;
    initialize();
}

@Override
public void setImageBitmap(Bitmap bm) {
    super.setImageBitmap(bm);
    this.initialize();
}

@Override
public void setImageResource(int resId) {
    super.setImageResource(resId);
    this.initialize();
}

private void initialize() {
    this.setScaleType(ScaleType.MATRIX);
    this.mMatrix = new Matrix();
    Drawable d = getDrawable();
    if (d != null) {
        mIntrinsicWidth = d.getIntrinsicWidth();
        mIntrinsicHeight = d.getIntrinsicHeight();
        setOnTouchListener(this);
    }
    mDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            maxZoomTo((int) e.getX(), (int) e.getY());
            cutting();
            return super.onDoubleTap(e);
        }
    });

}

@Override
protected boolean setFrame(int l, int t, int r, int b) {
    mWidth = r - l;
    mHeight = b - t;

    mMatrix.reset();
    int r_norm = r - l;
    mScale = (float) r_norm / (float) mIntrinsicWidth;

    int paddingHeight = 0;
    int paddingWidth = 0;
    // scaling vertical
    if (mScale * mIntrinsicHeight > mHeight) {
        mScale = (float) mHeight / (float) mIntrinsicHeight;
        mMatrix.postScale(mScale, mScale);
        paddingWidth = (r - mWidth) / 2;
        paddingHeight = 0;
        // scaling horizontal
    } else {
        mMatrix.postScale(mScale, mScale);
        paddingHeight = (b - mHeight) / 2;
        paddingWidth = 0;
    }
    mMatrix.postTranslate(paddingWidth, paddingHeight);

    setImageMatrix(mMatrix);
    mMinScale = mScale;
    zoomTo(mScale, mWidth / 2, mHeight / 2);
    cutting();
    return super.setFrame(l, t, r, b);
}

protected float getValue(Matrix matrix, int whichValue) {
    matrix.getValues(mMatrixValues);
    return mMatrixValues[whichValue];
}

protected float getScale() {
    return getValue(mMatrix, Matrix.MSCALE_X);
}

public float getTranslateX() {
    return getValue(mMatrix, Matrix.MTRANS_X);
}

protected float getTranslateY() {
    return getValue(mMatrix, Matrix.MTRANS_Y);
}

protected void maxZoomTo(int x, int y) {
    if (mMinScale != getScale() && (getScale() - mMinScale) > 0.1f) {
        // threshold 0.1f
        float scale = mMinScale / getScale();
        zoomTo(scale, x, y);
    } else {
        float scale = MAX_SCALE / getScale();
        zoomTo(scale, x, y);
    }
}

public void zoomTo(float scale, int x, int y) {
    if (getScale() * scale < mMinScale) {
        return;
    }
    if (scale >= 1 && getScale() * scale > MAX_SCALE) {
        return;
    }
    mMatrix.postScale(scale, scale);
    // move to center
    mMatrix.postTranslate(-(mWidth * scale - mWidth) / 2, -(mHeight * scale - mHeight) / 2);

    // move x and y distance
    mMatrix.postTranslate(-(x - (mWidth / 2)) * scale, 0);
    mMatrix.postTranslate(0, -(y - (mHeight / 2)) * scale);
    setImageMatrix(mMatrix);
}

public void cutting() {
    int width = (int) (mIntrinsicWidth * getScale());
    int height = (int) (mIntrinsicHeight * getScale());
    if (getTranslateX() < -(width - mWidth)) {
        mMatrix.postTranslate(-(getTranslateX() + width - mWidth), 0);
    }
    if (getTranslateX() > 0) {
        mMatrix.postTranslate(-getTranslateX(), 0);
    }
    if (getTranslateY() < -(height - mHeight)) {
        mMatrix.postTranslate(0, -(getTranslateY() + height - mHeight));
    }
    if (getTranslateY() > 0) {
        mMatrix.postTranslate(0, -getTranslateY());
    }
    if (width < mWidth) {
        mMatrix.postTranslate((mWidth - width) / 2, 0);
    }
    if (height < mHeight) {
        mMatrix.postTranslate(0, (mHeight - height) / 2);
    }
    setImageMatrix(mMatrix);
}

private float distance(float x0, float x1, float y0, float y1) {
    float x = x0 - x1;
    float y = y0 - y1;
    return FloatMath.sqrt(x * x + y * y);
}

private float dispDistance() {
    return FloatMath.sqrt(mWidth * mWidth + mHeight * mHeight);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (mDetector.onTouchEvent(event)) {
        return true;
    }
    int touchCount = event.getPointerCount();
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
    case MotionEvent.ACTION_POINTER_1_DOWN:
    case MotionEvent.ACTION_POINTER_2_DOWN:
        if (touchCount >= 2) {
            float distance = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
            mPrevDistance = distance;
            isScaling = true;
        } else {
            mPrevMoveX = (int) event.getX();
            mPrevMoveY = (int) event.getY();
        }
    case MotionEvent.ACTION_MOVE:
        if (touchCount >= 2 && isScaling) {
            float dist = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
            float scale = (dist - mPrevDistance) / dispDistance();
            mPrevDistance = dist;
            scale += 1;
            scale = scale * scale;
            zoomTo(scale, mWidth / 2, mHeight / 2);
            cutting();
        } else if (!isScaling) {
            int distanceX = mPrevMoveX - (int) event.getX();
            int distanceY = mPrevMoveY - (int) event.getY();
            mPrevMoveX = (int) event.getX();
            mPrevMoveY = (int) event.getY();
            mMatrix.postTranslate(-distanceX, -distanceY);
            cutting();
        }
        break;
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_POINTER_UP:
    case MotionEvent.ACTION_POINTER_2_UP:
        if (event.getPointerCount() <= 1) {
            isScaling = false;
        }
        break;
    }
    return true;
}

@Override
public boolean onTouch(View v, MotionEvent event) {
    return super.onTouchEvent(event);
}
}

Also I am drawing a bitmap in onCreate in my Activity as below:

// Coloring Image
    mImage = (ScaleImageView) findViewById(R.id.iv_coloring_figure);
    BitmapDrawable drawable = (BitmapDrawable) mImage.getDrawable();
    bmp = drawable.getBitmap();
    _alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(_alteredBitmap);
    Paint paint = new Paint();
    Matrix matrix = new Matrix();
    canvas.drawBitmap(bmp, matrix, paint);
    mImage.setImageBitmap(_alteredBitmap);
    mImage.setOnTouchListener(this);

I have also tried to set the coloring code inside onTouch method in the custom imageView that I am using for zooming, but still not working. The reason is only one that the original image laying under is not getting scaled even when I zoom the image on the top. I really need a solution for this. Hope I get one. Thanks again!!!

Community
  • 1
  • 1
  • In your onTouch for filling the color (1st code), you need to translate event.getX() and Y. Do float fixedX = (event.getX - mImage.getTranslateX()) / mImage.getScale(). Then use fixedX instead of event.getX. Not sure about if you need +-*/ just log the values to see how they should be fixed – talkol Sep 03 '13 at 21:35
  • @talkol Okay, thanks for the response. I will try your suggestion and will let you know if it did the trick! –  Sep 04 '13 at 04:54
  • @talkol It's not working! –  Sep 04 '13 at 06:53
  • I'm pretty sure this is the general direction, we just need to fix the math (add or subtract, divide or multiply). If you don't want to think about how the correct math should look like, you can always try all combinations: add instead of subtract, multiply instead of divide, divide first and then subtract instead of subtracting and then dividing :) – talkol Sep 04 '13 at 09:48
  • Yes but my problem is I have two bitmaps overlapping each other here. The bitmap below takes touch events for filling in colors, while the one above gets touch events for zooming. When I zoom the image, the bitmap below just stays in its original position and fills colors according to the touch events on original image and not the zoomed one. –  Sep 04 '13 at 10:28
  • I know, thats why we need to translate the touch events of the bitmap below. Instead of using their original x and y we will calculate fixedX and fixedY and do the flood fill based on them – talkol Sep 04 '13 at 12:29

1 Answers1

0

You can try to get the Matrix of your top layer bitmap and set it to the bottom layer bitmap. You can refer to this answer for getting the matrix of an image.

Community
  • 1
  • 1
  • 1
    That is a good example for reference, even though I will try to see if it works for me. Thanks! –  Sep 04 '13 at 11:28
  • I will also try to copy your code and work with it and see if I can actually fix and provide you the fixed code. I will do that once I get enough time. –  Sep 04 '13 at 12:21