android - 如何在位图周围创建浮雕?

标签 android bitmap android-bitmap porter-duff emboss

热门游戏Words with Friends在游戏板上绘制字母方 block 作为一个实体 -

您可以在以下屏幕截图中看到黄色线性渐变应用于所有字母拼贴,并且边缘有浮雕效果:

app screenshot

my word game我想要类似的效果:

emboss example

所以我创建了一个游戏板大小的mBitmap,然后将所有的图 block 绘制到其中,最后将位图绘制到my custom view中。 -

设置:

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 ():

app screenshot

我的问题:是否可以使用 PorterDuff.Mode 的某些组合? (和 extractAlpha?)在我的大位图周围绘制浮雕?

更新:

通过查看 HolographicOutlineHelper.java我已经能够添加外部阴影:

app screenshot

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

但不幸的是,应用程序现在运行缓慢 - 我仍然不知道如何在位图周围添加浮雕效果。

最佳答案

我不确定我是否得到了你所需要的东西,但如果你只是想在一些带有 alpha channel 的 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(); 每次都会创建新的 Bitmap。 (顺便说一句,我总是从你的项目 git 中得到内存不足的错误。添加了 mAlphaBitmap.recycle(); 并且它至少可以启动。但它仍然像 hell 一样落后)

因此,我使用了您的 git 存储库并获得了一些结果。这是演示图像和 git repo of first commit :

enter image description here

但后来我意识到,您不需要在字母周围使用 EmbossMaskFilter,您需要在矩形周围使用它们。它可以以几乎相同的方式完成。这是我如何做到的:

  1. 为浮雕背景创建新的辅助静态位图和 Canvas ,就像 mAlphaBitmap
  2. 在每个 prepareBitmaps() 上绘制辅助位图上的矩形。没有 alpha 的纯色。
  3. 像这样从创建的位图中提取 alpha Bitmap alpha = helperCanvas.extractAlpha();
  4. 使用带浮雕过滤器的绘画在助手上绘制提取的 alpha 位图 helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss);
  5. 在 onDraw 中打印 helperBitmap,在主位图之前有一些 alpha。

这是没有 alpha 的屏幕截图(因为这样更容易看到形状) enter image description here

这是这个版本的 git 演示:https://github.com/varren/AndroidEmbossMaskFilterForPng/blob/1d692d576e78bd434252a8a6c6ad2ee9f4c6dbd8/app/src/main/java/de/afarber/mytiles2/MyView.java

这是我在您的项目中更改的代码的重要部分:

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() 并且性能现在很好,但是我们在调整大小时出现文本失真。所以这是source code对于这一步。

这里的工作还不错 final solution .使用过滤器移动所有油漆解决了性能问题,但我认为仍然有更好的选择来实现这一点。正如我之前所说,我不知道这是不是你需要的,但这段代码几乎可以在位图周围创建浮雕

PS:拆分和添加单元格时的效果有点酷

PS2:new EmbossMaskFilter(new float[] { 0f, 1f, 0.5f }, 0.8f, 3f, 3f);这在不同屏幕分辨率的不同设备上看起来会不一样

关于android - 如何在位图周围创建浮雕?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30697466/

相关文章:

android - 动画 ImageView 的位图位置

Android 位图到 Base64 字符串

android - 是否可以在 android2.2 应用程序中从谷歌地图获取建议路线?

Android:原始资源是否存储在本地文件系统上?

android - 为什么保存已加载的位图会增加其大小?

android - 背景中的灰色 fragment

android - 带有离线 JQueryMobile 的 PhoneGap

android - 为媒体播放器导入原始 mp3 资源

image - 在黑莓中使用 Bitmap 还是 EncodedImage 更好?

c++ - 如何保存24位BMP