How to emboss around a bitmap?

The popular game Words with Friends draws tiles on the playing field as a whole -

You can see the yellow linear gradient applied to all letter fragments in the following screenshot, as well as the embossing effect on the edge:

app screenshot

In my pun, I would like to have similar effects:

emboss example

So, I create a mBitmap sized game board, then draw all the tiles and finally draw a bitmap onto my user view -

Setup:

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

Drawing

 @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 } 

The EmbossMaskFilter effect works fine with drawText() and drawRect() calls, but it does NOT work for drawBitmap() :

app screenshot

My question is: can I use some combinations of PorterDuff.Mode (and extractAlpha ?) To draw embossing around my large bitmap?

UPDATE:

By looking at HolographicOutlineHelper.java I was able to add an outer shadow:

app screenshot

with the following code in MyView.java -

Setup:

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

Drawing

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

but, unfortunately, the application is slow - and I still don't know how to add an embossing effect around a bitmap.

+7
android bitmap android-bitmap porter-duff
source share
2 answers

I'm not sure I got what you need, but if you just want to apply EmbossMaskFilter around some png letter with alpha channel, you can pretty much do this trick with

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

You will never need all this code in 1 onDraw, because it creates many objects in memory. And src.extractAlpha(); creates a new bitmap every time. (Btw, I always extract an error from memory from your git project. Added mAlphaBitmap.recycle(); and it can at least load, but it still lags like hell)

So, I played with your git repository and got some results. Here is a demo and git repo of the first commit :

enter image description here

But then I realized that you do not need EmbossMaskFilter around letters, you need them around rectangles. And this can be done in much the same way. Here is how I did it:

  • Create a new auxiliary static bitmap and canvas for the background of the album, like mAlphaBitmap
  • In each picture, paintBitmaps () on the auxiliary bitmap. Solid color without alpha.
  • Extract alpha from created bitmap like this Bitmap alpha = helperCanvas.extractAlpha();
  • Draw the selected alpha-bitmap image on the auxiliary paint medium with the embossing filter helperCanvas.drawBitmap(alpha, 0, 0, paintEmboss);
  • In onDraw print helperBitmap with some alpha in front of the main bitmap.

Here is a screenshot without alpha (because itโ€™s much easier to see the shapes this way) enter image description here

Below is the git version of this version: https://github.com/varren/AndroidEmbossMaskFilterForPng/blob/1d692d576e78bd434252a8a6c6ad2ee9f4c6dbd8/app/src/main/java/de/afarber/mytiles2/Myava

And here is the important part of the code that I changed in your project:

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

And the last 3rd step I took is to move everything from onDraw to prepareBitmaps() , and the preliminary work is fine, but we have text destruction when resizing. so here is the source code for this step.

And here is some wonderful work the final decision . Moving all the paints with filters solved the problems with the preliminary preparation, but I think there are even better options for implementing this. As I said, I donโ€™t know what you need, but this code pretty much creates Emboss around Bitmap

PS: cool effect when splitting and adding cells together

PS2: new EmbossMaskFilter(new float[] { 0f, 1f, 0.5f }, 0.8f, 3f, 3f); it will not look the same on different devices with different screen resolutions

+5
source share

It is suggested that you use a custom layout.

You will need your own layout for the scrabble panel. Since this is a grid, it should be fairly easy to code.

The basic idea is to have a set of shadow PNG images, one for each type of combination of neighboring cells. In your onDraw () layout, first draw the shadows, then draw the tiles in onLayout ().

In onDraw (), iterates through an array of table placeholders. If you have a tile, then check adjacent cells for each edge. Depending on what is adjacent, select the correct shadow image and draw it.

You can reduce the number of shadow images essentially by having a shadow image that is exactly the width of the tile, and then specializing in the area of โ€‹โ€‹the angle: one at 270 degrees, one for direct alignment, one at 90 degrees.

I don't know if using porter-duff can help, since you still need to identify all these โ€œextremeโ€ cases (no pun intended).

+2
source share

All Articles