如何在位图周围创建浮雕?
How to create emboss around a Bitmap?
热门游戏Words with Friends在游戏板上绘制字母方块作为一个实体 -
在下面的屏幕截图中,您可以看到黄色线性渐变 应用于所有字母拼贴 以及边缘的浮雕效果:
在my word game我想有类似的效果:
所以我创建了一个大小为 mBitmap
的游戏板,然后将所有图块绘制到其中,最后将位图绘制到 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()
:
无效
我的问题: 是否可以使用 PorterDuff.Mode(和 extractAlpha
?)的某些组合在我的大位图周围绘制浮雕?
更新:
通过查看 HolographicOutlineHelper.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);
}
但不幸的是,应用程序现在运行缓慢 - 我仍然不知道如何在位图周围添加浮雕效果。
这是使用自定义布局的建议。
您需要自己的拼字板布局。因为它是网格,这应该很容易编码。
基本思想是拥有一组 PNG 阴影图像,每个图像对应相邻单元格的每种组合类型。在您的布局 onDraw() 中,先绘制阴影,然后在 onLayout() 中绘制图块。
在 onDraw() 中,遍历图块占位符数组。如果您有瓷砖,则对于每条边,检查相邻的单元格。根据相邻的东西,选择正确的阴影图像并绘制它。
你可以大大减少阴影图像的数量,方法是使用一个恰好是瓷砖宽度的阴影图像,然后专门化角区域:一个用于 270 度,一个用于直线对齐,一个用于 90 度。
我不知道使用 porter-duff 是否有帮助,因为您仍然需要确定所有这些 "edge" 个案例(没有双关语意)。
我不确定我是否确切地得到了你需要的东西,但如果你只是想在一些带有 alpha 通道的 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 repo of first commit:
但后来我意识到,您不需要在字母周围使用 EmbossMaskFilter,您需要在矩形周围使用它们。它可以以几乎相同的方式完成。这是我如何做到的:
- 为浮雕背景创建新的辅助静态位图和 Canvas,就像 mAlphaBitmap
- 在每个 prepareBitmaps() 上绘制辅助位图上的矩形。没有 alpha 的纯色。
- 像这样从创建的位图中提取 alpha
Bitmap alpha = helperCanvas.extractAlpha();
- 使用带浮雕过滤器的绘画在助手上绘制提取的 alpha 位图
helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss);
- 在 onDraw 中打印 helperBitmap,在主位图之前有一些 alpha。
这是没有alpha的截图(因为这样更容易看清形状)
这是我在您的项目中更改的代码的重要部分:
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);
这在具有不同屏幕分辨率的不同设备上看起来会不一样
热门游戏Words with Friends在游戏板上绘制字母方块作为一个实体 -
在下面的屏幕截图中,您可以看到黄色线性渐变 应用于所有字母拼贴 以及边缘的浮雕效果:
在my word game我想有类似的效果:
所以我创建了一个大小为 mBitmap
的游戏板,然后将所有图块绘制到其中,最后将位图绘制到 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()
:
我的问题: 是否可以使用 PorterDuff.Mode(和 extractAlpha
?)的某些组合在我的大位图周围绘制浮雕?
更新:
通过查看 HolographicOutlineHelper.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);
}
但不幸的是,应用程序现在运行缓慢 - 我仍然不知道如何在位图周围添加浮雕效果。
这是使用自定义布局的建议。
您需要自己的拼字板布局。因为它是网格,这应该很容易编码。
基本思想是拥有一组 PNG 阴影图像,每个图像对应相邻单元格的每种组合类型。在您的布局 onDraw() 中,先绘制阴影,然后在 onLayout() 中绘制图块。
在 onDraw() 中,遍历图块占位符数组。如果您有瓷砖,则对于每条边,检查相邻的单元格。根据相邻的东西,选择正确的阴影图像并绘制它。
你可以大大减少阴影图像的数量,方法是使用一个恰好是瓷砖宽度的阴影图像,然后专门化角区域:一个用于 270 度,一个用于直线对齐,一个用于 90 度。
我不知道使用 porter-duff 是否有帮助,因为您仍然需要确定所有这些 "edge" 个案例(没有双关语意)。
我不确定我是否确切地得到了你需要的东西,但如果你只是想在一些带有 alpha 通道的 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 repo of first commit:
但后来我意识到,您不需要在字母周围使用 EmbossMaskFilter,您需要在矩形周围使用它们。它可以以几乎相同的方式完成。这是我如何做到的:
- 为浮雕背景创建新的辅助静态位图和 Canvas,就像 mAlphaBitmap
- 在每个 prepareBitmaps() 上绘制辅助位图上的矩形。没有 alpha 的纯色。
- 像这样从创建的位图中提取 alpha
Bitmap alpha = helperCanvas.extractAlpha();
- 使用带浮雕过滤器的绘画在助手上绘制提取的 alpha 位图
helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss);
- 在 onDraw 中打印 helperBitmap,在主位图之前有一些 alpha。
这是没有alpha的截图(因为这样更容易看清形状)
这是我在您的项目中更改的代码的重要部分:
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);
这在具有不同屏幕分辨率的不同设备上看起来会不一样