7

人気のゲームWords with Friendsは、ゲーム ボードに文字タイルを1 つのエンティティとして描画します-

次のスクリーンショットでは、すべての文字タイルに黄色の線形グラデーションが適用され、エッジにエンボス効果が適用されていることがわかります。

アプリのスクリーンショット

私の単語ゲームでは、同様の効果が欲しいです:

エンボスの例

そこで、 のサイズのゲーム ボードを作成し、そこにすべてのmBitmapタイルを描画し、最後にビットマップをカスタム ビューに描画します。

設定:

setLayerType(View.LAYER_TYPE_SOFTWARE, null);

// create yellow linear gradient
mGradStart = new Point(3 * mWidth / 4, mHeight / 3);
mGradEnd = new Point(mWidth / 4, 2 * mHeight / 3);
LinearGradient gradient = new LinearGradient(
        mGradStart.x,
        mGradStart.y,
        mGradEnd.x,
        mGradEnd.y,
        new int[]{ 0xCCFFCC00, 0xCCFFCC99, 0xCCFFCC00 },
        null,
        TileMode.CLAMP);

// create the big bitmap holding all tiles
mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);

mPaintGrad = new Paint();
mPaintGrad.setShader(gradient);

mPaintEmboss = new Paint();
mPaintEmboss.setShader(gradient);

EmbossMaskFilter filter = new EmbossMaskFilter(
    new float[] { 0f, 1f, 0.5f }, 0.8f, 3f, 3f);
mPaintEmboss.setMaskFilter(filter);

描く:

@Override
protected void onDraw(Canvas canvas) {
    mGameBoard.draw(canvas);

    // draw all tiles as rectangles into big bitmap 
    // (this code will move to onTouchEvent later)
    mBitmap.eraseColor(Color.TRANSPARENT);
    for (SmallTile tile: mTiles) {
        mCanvas.drawRect(
                tile.left, 
                tile.top, 
                tile.left + tile.width, 
                tile.top + tile.height, 
                mPaintGrad);
        tile.draw(mCanvas);
    }
    canvas.drawBitmap(mBitmap, 0, 0, mPaintEmboss); // emboss NOT displayed
    canvas.drawText("TEXT WORKS OK", 400, 400, mPaintEmboss); // ebmoss OK
    canvas.drawRect(300, 600, 800, 1200, mPaintEmboss); // emboss OK
}

このEmbossMaskFilter効果はdrawText()と のdrawRect()呼び出しでは正常に機能しますが、 では機能しませんdrawBitmap():

アプリのスクリーンショット

私の質問: PorterDuff.Mode (およびextractAlpha?)のいくつかの組み合わせを使用して、大きなビットマップの周りにエンボスを描画することは可能ですか?

アップデート:

HolographicOutlineHelper.javaを見ると、外側の影を追加できました。

アプリのスクリーンショット

MyView.javaで次のコードを使用-

設定:

public MyView(Context context, AttributeSet attrs) {
    super(context, attrs);

    mScale = getResources().getDisplayMetrics().density;
    mGradStart = new Point(3 * mWidth / 4, mHeight / 3);
    mGradEnd = new Point(mWidth / 4, 2 * mHeight / 3);
    LinearGradient gradient = new LinearGradient(
            mGradStart.x,
            mGradStart.y,
            mGradEnd.x,
            mGradEnd.y,
            new int[]{ 0xCCFFCC00, 0xCCFFCC99, 0xCCFFCC00 },
            null,
            TileMode.CLAMP);

    mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
    mCanvas = new Canvas(mBitmap);

    mPaintGrad = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
    mPaintGrad.setShader(gradient);

    mPaintBlur = new Paint();
    mPaintBlur.setColor(Color.BLACK);
    BlurMaskFilter blurFilter = new BlurMaskFilter(mScale * 1, Blur.OUTER);
    mPaintBlur.setMaskFilter(blurFilter);
}

描く:

private void prepareBitmaps() {
    mBitmap.eraseColor(Color.TRANSPARENT);
    for (SmallTile tile: mTiles) {
        mCanvas.drawRect(
                tile.left, 
                tile.top, 
                tile.left + tile.width, 
                tile.top + tile.height, 
                mPaintGrad);
        tile.draw(mCanvas);
    }

    mAlphaBitmap = mBitmap.extractAlpha(mPaintBlur, mOffset);
}

@Override
protected void onDraw(Canvas canvas) {
    mGameBoard.draw(canvas);
    canvas.drawBitmap(mAlphaBitmap, mOffset[0], mOffset[1], mPaintBlur);
    canvas.drawBitmap(mBitmap, 0, 0, mPaintGrad);
}

残念ながら、アプリの動作が遅くなりました。ビットマップの周りにエンボス効果を追加する方法がまだわかりません。

4

2 に答える 2

3

必要なものが正確に得られたかどうかはわかりませんが、アルファチャンネルを持つpng文字の周りにEmbossMaskFilterを適用したいだけなら、このトリックをほとんど行うことができます

EmbossMaskFilter filter = new EmbossMaskFilter(new float[]{1, 1, 1}, 0.5f, 0.6f, 2f);

Paint paintEmboss = new Paint();
paintEmboss.setMaskFilter(embossMaskFilter);

Bitmap helperBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas helperCanvas = new Canvas(helperBitmap);

Bitmap alpha = src.extractAlpha();
helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss);
alpha.recycle();

...
canvas.drawBitmap(helperBitmap, 0, 0, anyPaint);

メモリ内に多数のオブジェクトが作成されるため、このコードのすべてを 1 つの onDraw に含めることは決してありません。毎回src.extractAlpha();新しいビットマップを作成します。(ところで、私はいつもあなたのプロジェクト git からメモリ不足エラーを起こします。追加さ mAlphaBitmap.recycle(); れ、少なくとも起動できました。しかし、それでも地獄のように遅れます)

それで、私はあなたのgitリポジトリで遊んで、いくつかの結果を得ました。最初のコミットのデモ イメージとgit リポジトリは次のとおりです。

ここに画像の説明を入力

しかし、文字の周りに EmbossMaskFilter は必要なく、長方形の周りに必要であることに気付きました。そして、それはほとんど同じ方法で行うことができます。これが私がこれを行った方法です:

  1. mAlphaBitmap と同様に、エンボス背景用の新しいヘルパー静的ビットマップとキャンバスを作成します。
  2. 各 prepareBitmaps() で、ヘルパー ビットマップに四角形を描画します。アルファなしの単色。
  3. このように作成されたビットマップからアルファを抽出しますBitmap alpha = helperCanvas.extractAlpha();
  4. 抽出されたアルファ ビットマップをエンボス フィルター付きのペイントでヘルパーに描画するhelperCanvas.drawBitmap(alpha, 0, 0, paintEmboss);
  5. onDraw では、メイン ビットマップの前にいくつかのアルファを含む helperBitmap を印刷します。

これはアルファなしのスクリーンショットです(この方法で形状を見やすくするため) ここに画像の説明を入力

このバージョンの git デモは次のとおりです

そして、ここに私があなたのプロジェクトで変更したコードの重要な部分があります:

private static final EmbossMaskFilter filter = 
                 new EmbossMaskFilter(new float[]{1, 1, 1}, 0.5f, 0.6f, 2f);
private static Canvas helperCanvas;
private static Paint paintEmboss;

public  Canvas getHelperCanvas(int width, int height){
    if (mAlphaBitmap == null) {
        mAlphaBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        helperCanvas = new Canvas(mAlphaBitmap);
        paintEmboss = new Paint();
        paintEmboss.setColor(Color.BLACK);
    }
    return helperCanvas;
}

private void prepareBitmaps() {
    mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    helperCanvas = getHelperCanvas(mBitmap.getWidth(),mBitmap.getHeight());
    helperCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    paintEmboss.setMaskFilter(null);
    paintEmboss.setAlpha(255);

    for (SmallTile tile: mTiles) {
        if (!tile.visible) continue;
        helperCanvas.drawRect(tile.left,tile.top,tile.left + tile.width,
                tile.top + tile.height,paintEmboss);
        mCanvas.drawRect(tile.left, tile.top,tile.left + tile.width, 
                tile.top + tile.height, mPaintGrad);
        tile.draw(mCanvas);
    }

    paintEmboss.setMaskFilter(filter);
    Bitmap alpha = mAlphaBitmap.extractAlpha();
    helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss);
}

protected void onDraw(Canvas canvas) {
     // ...
     paintEmboss.setAlpha(255); //todo change alpha here
    if(mAlphaBitmap!= null)canvas.drawBitmap(mAlphaBitmap, 0,0, paintEmboss);
    if(mBitmap!= null)canvas.drawBitmap(mBitmap, 0, 0, mPaintGrad);
    // ...
}

そして、私が行った最後の 3-d ステップは、すべてを onDraw から に移動することであり、パフォーマンスprepareBitmaps()は今では問題ありませんが、サイズ変更時にテキストの歪みがあります。このステップのソース コードは次のとおりです。

そして、ここにちょっとうまくいく最終的な解決策があります。フィルターを使用してすべてのペイントを移動すると、パフォーマンスの問題が解決されましたが、これを実装するためのより良いオプションがまだあると思います。前に言ったように、必要なものかどうかはわかりませんが、このコードはビットマップの周りにエンボスを作成します

PS: セルを分割して追加するときのクールな効果

PS2:new EmbossMaskFilter(new float[] { 0f, 1f, 0.5f }, 0.8f, 3f, 3f);これは、異なる画面解像度の異なるデバイスでは同じに見えません。

于 2015-06-13T17:45:38.180 に答える
1

カスタム レイアウトを使用した提案を次に示します。

スクラブル ボードには独自のレイアウトが必要です。これはグリッドなので、コーディングは非常に簡単です。

基本的な考え方は、隣接するセルの組み合わせの種類ごとに 1 つずつ、一連​​の PNG 影画像を用意することです。レイアウト onDraw() で、最初に影を描画してから、onLayout() でタイルを描画します。

onDraw() で、タイル プレースホルダーの配列を反復処理します。タイルがある場合は、エッジごとに隣接するセルを調べます。隣接するものに応じて、正しい影のイメージを選択して描画します。

タイルの幅と正確に一致する影イメージを作成し、コーナー領域を特殊化することによって、影イメージの数を大幅に減らすことができます。

これらすべての「エッジ」ケースをまだ判断する必要があるため、porter-duff を使用することが役立つかどうかはわかりません (しゃれは意図されていません)。

于 2015-06-07T21:10:26.343 に答える