Bitmap / Canvas / Shader

Irregular Shapes – Part 2

In the previous article we looked at how we can use an alpha mask to create a round cornered image, although there were a couple of reasons why it wasn’t entirely suitable for our requirements. In this article we’ll look at an alternative mechanism for achieving the same result but without using a second image.

Before we start I claim absolutely no credit for the technique I am going to describe. I first learned of it in a blog post by Romain Guy and it uses a BitmapShader to create a round-cornered image. We’ll go on to explore some other cool things that we can do using this technique so it warrants a brief explanation first.

Shader is something that we’ve touched on briefly before on StylingAndroid – specifically in the post on applying a gradient fill to text. BitmapShader is also something that I’ve spoken in about in my Graphical Magic presentation.

Shaders allow us to define a fill style for drawing objects to Canvas and are set on the Paint object which will be used to render things the drawing operation, and they are incredibly useful and powerful things.

BitmapShader allows us to use a Bitmap in this way and it supports various tiling options which control the behaviour when the area being filled is much larger that the Bitmap being used. However for our requirements the area being filled will match the dimensions of the Bitmap so we don’t be to worry about this.

We’re calculating the corner radius to one eighth the size of the smaller dimension of the image which will produce a near identical result to the previous article:

public Bitmap processImage(Bitmap bitmap) {
    Bitmap bmp;

    bmp = Bitmap.createBitmap(bitmap.getWidth(), 
        bitmap.getHeight(), Bitmap.Config.ARGB_8888);
    BitmapShader shader = new BitmapShader(bitmap, 
        BitmapShader.TileMode.CLAMP, 
        BitmapShader.TileMode.CLAMP);

    float radius = Math.min(bitmap.getWidth(), 
        bitmap.getHeight()) / RADIUS_FACTOR;
    Canvas canvas = new Canvas(bmp);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setShader(shader);

    RectF rect = new RectF(0, 0, 
        bitmap.getWidth(), bitmap.getHeight());
    canvas.drawRoundRect(rect, radius, radius, paint);

    return bmp;
}

That is really easy – it’s simply a case of constructing the BitmapShader from the source Bitmap, adding it to the Paint object, and then drawing a rounded rectangle using the Paint.

If you compare this code to that from the previous article it should be obvious that the memory requirements are much lower as we only require two Bitmap objects rather than the three we used previously. Also the Shader / Path solution scales with the image and so works for different sized images. The mask approach requires that the mask match the size of the source image unless we do some additional scaling. If we look at the results, it’s difficult to see any visual difference from the previous article:

As I suggestd earlier, this technique can actually do an awful lot more than just rounding corners, and in the next article we’ll look at creating a speech bubble shaped image, similar to those used in some messaging apps, such as WhatsApp.

The source code for this article can be found here.

© 2014, Mark Allison. All rights reserved.

Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.