Android: 如何在缩放后为位图填充颜色?

4
我正在开发一个涂色本功能的App。我找到了一个名为byronsanchez/coloring-book-android的库,使用它可以很好地实现涂色。但是,我需要实现缩放功能。我使用ScaleImageView来实现缩放。

问题在于,当我放大图片时,就无法涂抹到指定区域。当图片没有缩放时,可以正确识别不同区域的触摸,但放大后,在触摸某个区域时会填充到其他区域。

我搜索了一些解决方案,但仍无法理解如何在我的代码中实现。我在寻找解决方法时遇到了这里的一个答案。我认为我已经在代码中添加了缩放函数,但仍然不能正常工作。

请提供一些带有简要说明的示例。我真的卡住了,希望能在这里得到一些帮助,谢谢!

以下是我用于在位图中填充颜色的代码:

@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;
}

这是我正在使用的缩放代码。

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);
}
}

同时,在我的Activity的onCreate中,我正在绘制一个位图,如下所示:
// 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);

我也尝试过在自定义的imageView中的onTouch方法中设置颜色代码,但仍然不起作用。原因只有一个,即底部的原始图像没有随着我在顶部缩放图像而发生缩放。我真的需要解决方案。希望能得到一个。再次感谢!


在填充颜色的onTouch(第一段代码)中,您需要翻译event.getX()和Y。请执行float fixedX = (event.getX - mImage.getTranslateX()) / mImage.getScale()。然后使用fixedX代替event.getX。如果不确定是否需要+-*/,只需记录这些值以查看应如何修复它们。 - talkol
@talkol 好的,谢谢你的回复。我会尝试你的建议,并告诉你它是否奏效! - user1952459
@talkol 它不起作用! - user1952459
我相信这是大致的方向,我们只需要修正一下数学运算(加或减,乘或除)。如果你不想考虑正确的数学运算应该是什么样子,你可以尝试所有的组合:用加法代替减法,用乘法代替除法,先除后减而不是先减再除 :) - talkol
是的,但我的问题是这里有两个位图重叠在一起。下面的位图接收填充颜色的触摸事件,而上面的位图接收缩放的触摸事件。当我缩放图像时,下面的位图只保持在原来的位置,并根据原始图像的触摸事件填充颜色,而不是缩放后的图像的触摸事件。 - user1952459
我知道,这就是为什么我们需要翻译下面位图的触摸事件。我们将计算fixedX和fixedY而不是使用它们的原始x和y,并根据它们进行泛洪填充。 - talkol
1个回答

0

您可以尝试获取顶层位图的矩阵并将其设置为底层位图。您可以参考此答案获取图像的矩阵。


1
这是一个很好的参考例子,虽然我会尝试看看它是否适用于我。谢谢! - user1952459
我也会尝试复制你的代码并与之一起工作,看看我是否能够真正修复并提供给你修复后的代码。一旦我有足够的时间,我就会这样做。 - user3513843

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接