5

次のシナリオがあります。背景として使用されるビットマップと、50%透明または不透明(実行時に変更可能)のオーバーレイとして使用される別のビットマップ、およびこの2番目のビットマップのマスクを含む3番目のビットマップ。さまざまなXfermodes構成と描画順序を試しましたが、正しいものを見つけることができませんでした。

プログラムの2回の実行間、または構成の変更間でマスクを保存できる必要があるため、マスクをビットマップとして使用しています。これは、ユーザーが画面に描画するときに作成され、戦争の霧を効果的にクリーンアップします。

最善の試みによるからのコードスニペット。思ったように動かなかったのは、マスクの透明度だけでした。

@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オブジェクトにアルファを設定してこれを実行できるかどうかはわかりません。そうでない場合は、アルファ問題の別の提案や解決策、できれば戦争の霧として使用されているビットマップの再現が必要ないものを気にしません。

編集:

次のようにすることで、希望する結果を得ることができました。

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

しかし、ビットマップを5回描画するのは無駄のようです。これはAndroidハードウェアの高速化によりOpenGLテクスチャで実行され(ビットマップをデバイスのGPUで受け入れられる最高の解像度に再スケーリングします)invalidates()、Nexus SとA500の両方で驚くほどスムーズに実行されるように細心の注意を払っていますが、他のデバイスについてはよくわかりません(プロジェクトはとにかく4.0以降になる予定です)。

しかし、私はそれを行うためのより良い方法があるに違いないと確信しています。私は、その多くの過剰描画を回避する方法、または少なくともそれらのXfermodesが何を意味するのかを正しく説明でき、私がものを過剰描画していない方法を望んでいます。

4

1 に答える 1

6

ある種のひらめきを経験した後、まったく異なるアプローチを試みましたが、この問題の解決策は、通常のように、はるかに単純なアプローチであることに気付きました。また、必要なビットマップは 2 つだけなので、作業に必要なメモリははるかに少なくてすみます。

描画用:

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 に答える