5

我有以下场景:一个用作背景的位图和另一个用作覆盖的位图,可以是 50% 透明或不透明(在运行时可更改),第三个位图包含第二个位图的掩码。我尝试了不同的 Xfermodes 配置和绘图顺序,但找不到合适的。

我将掩码用作位图,因为我需要能够在两次程序运行之间或在配置更改之间保存它。它是在用户在屏幕上绘制时创建的,有效地清除了战争迷雾

最佳尝试的代码片段。唯一没有像我希望的那样起作用的是我的面具的透明度。

@Override
protected void onDraw(Canvas canvas) {      
    canvas.drawBitmap(mFogOfWar, mTransformationMatrix, mPaintFog);
    canvas.drawBitmap(mMaskBitmap, mTransformationMatrix, mPaintMask);
    canvas.drawBitmap(mImage, mTransformationMatrix, mPaintImage);
}

Paint对象:

mPaintImage.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
mPaintFog.setAlpha(127);
mPaintMask.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

这是我通过当前配置得到的更清楚的结果: 截屏

我不确定我是否能够在 Paint 对象上设置 alpha;如果不是,我不介意针对 alpha 问题的其他建议或解决方案,最好是不需要将位图重新用作战争迷雾的建议或解决方案。

编辑:

通过执行以下操作,我能够得到我想要的结果:

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawBitmap(mImage, mTransformationMatrix, mPaintImage);
    if (mMaskBitmap != null) {
        canvas.drawBitmap(mFogOfWar, mTransformationMatrix, mPaintFog);
        canvas.drawBitmap(mMaskBitmap, mTransformationMatrix, mPaintMask);
        canvas.drawBitmap(mMaskBitmap, mTransformationMatrix, mPaintImage);
        canvas.drawBitmap(mImage, mTransformationMatrix, mPaintImageSecondPass);
    }

Paint对象:

mPaintImage = new Paint(); // No Xfermode here anymore
mPaintFog.setAlpha(127);
mPaintMask.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mPaintImageSecondPass.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));

但是绘制五次位图似乎是一种浪费。由于 Android 硬件加速,它在 OpenGL 纹理中运行(我将位图重新缩放到设备 GPU 接受的最高分辨率),并且我非常注意invalidates()它在我的 Nexus S 和我的 A500 上运行得非常流畅,但是我不确定其他设备(无论如何项目将是 4.0+)。

但我相信一定有更好的方法来做到这一点。我想要一种避免过多透支的方法,或者至少可以正确地向我解释那些 Xfermode 的含义并且我没有透支的东西。

4

1 回答 1

6

在获得某种顿悟之后,我尝试了一种完全不同的方法——并意识到这个问题的解决方案是一种更简单的方法——就像通常那样。因为我只需要两个位图,所以我需要更少的内存来处理它。

对于绘图:

canvas.drawBitmap(mImage, mTransformationMatrix, mPaintImageRegular);
if (mFogOfWarState != FOG_OF_WAR_HIDDEN) {
    canvas.drawBitmap(mFogOfWar, mTransformationMatrix, mPaintFog);
}

“秘密”是,我没有在蒙版位图上绘图,而是使用另一种油漆擦除战争迷雾:

mFogOfWarCanvas.drawPath(mPath, mEraserPaint);

唯一Paint有一个Xfermode是用于擦除的:

mEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

对于我的面具的加载和保存,我做了以下事情:

private void createFogAndMask(File dataDir) {
    BitmapDrawable tile = (BitmapDrawable) getResources().getDrawable(R.drawable.fog_of_war); 
    tile.setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
    mFogOfWar = Bitmap.createBitmap(mImageBounds.width(), mImageBounds.height(), Config.ARGB_8888);
    mFogOfWarCanvas = new Canvas(mFogOfWar);
    tile.setBounds(mImageBounds);
    tile.draw(mFogOfWarCanvas);   
    tile = null;

    // Try to load an existing mask
    File existingMask = new File(dataDir, getMaskFileName());
    if (existingMask.exists()) {
        Bitmap existingMaskBitmap = BitmapFactory.decodeFile(existingMask.getAbsolutePath());
        mFogOfWarCanvas.drawBitmap(existingMaskBitmap, new Matrix(), mPaintImageRegular);
        mFogOfWarCanvas.drawPaint(mMaskEraserPaint);
        existingMaskBitmap.recycle();
        System.gc();
    }
}

public void saveMask(File folder) throws IOException {
    if (!mReady || mImagePath == null) return;
    mImage.recycle();
    System.gc();
    if (!folder.exists()) {
        folder.mkdirs();
    }
    File savedFile = new File(folder, getMaskFileName());

    // Change all transparent pixels to black and all non-transparent pixels to transparent
    final int length = mImageBounds.width() * mImageBounds.height();        
    final int[] pixels =  new int[length];
    mFogOfWar.getPixels(pixels, 0, mImageBounds.width(), 0, 0, mImageBounds.width(), mImageBounds.height());
    for (int i = 0; i < length; i++) {
        if (pixels[i] == Color.TRANSPARENT) {
            pixels[i] = Color.BLACK;
        } else {
            pixels[i] = Color.TRANSPARENT;              
        }
    }
    mFogOfWar.setPixels(pixels, 0, mImageBounds.width(), 0, 0, mImageBounds.width(), mImageBounds.height());

    FileOutputStream output = new FileOutputStream(savedFile);
    mFogOfWar.compress(CompressFormat.PNG, 80, output);
    output.flush();
    output.close();     
}
于 2012-02-23T18:05:22.593 回答