Open Source & Free  

TIP: Understand Image Masking Performance

TIP: Understand Image Masking Performance

Header Image

Image masking allows us to adapt an image which we acquired from an external source to fit our design e.g. if we want to show an image cropped to a circle we could apply a mask to it in order to get an intelligent crop. This is a very powerful tool as a designer can supply a hardcoded mask image and build some pretty complex shapes that include an alpha channel as well (making it superior to shape clipping).

The most simple type of image masking can be achieved using code like this taken from this old post:

try {
    int width = Display.getInstance().getDisplayWidth();
    Image capturedImage = Image.createImage(Capture.capturePhoto()).fill(width, width);
    Image roundMask = Image.createImage(width, capturedImage.getHeight(), 0xff000000);
    Graphics gr = roundMask.getGraphics();
    gr.setColor(0xffffff);
    gr.setAntiAliased(true);
    gr.fillArc(0, 0, width, width, 0, 360);
    Object mask = roundMask.createMask();
    capturedImage = capturedImage.applyMask(mask);
    result.setIcon(capturedImage);
} catch(IOException err) {
    Log.e(err);
}
I made some minor improvements to the code

Masking Approaches

There are several other ways to implement masking besides the low level approach illustrated above

URLImage Masking

URLImage can be created with a mask. Since masking replaces the underlying image with the masked image if you would use it with a URLImage you would effectively lose the implicit download functionality of the URLImage. The builtin masking allows us to workaround that behavior and still apply a mask to an image that is fetched implicitly.

To paraphrase the previous example this can be used like:

Image roundMask = Image.createImage(placeholder.getWidth(), placeholder.getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.setAntiAliased(true);
gr.fillArc(0, 0, placeholder.getWidth(), placeholder.getHeight(), 0, 360);

URLImage.ImageAdapter ada = URLImage.createMaskAdapter(roundMask);
Image i = URLImage.createToStorage(placeholder, "fileNameInStorage", "http://xxx/myurl.jpg", ada);

Theme Masking

Label and its subclasses can have a mask associated with an icon thru a theme constant. This means that all images set to the given icon will be implicitly masked within the set method. That can be very convenient as it allows us to decouple the masking behavior into the theme rather than code it into the application. However, as stated above this won’t work with tools such as URLImage.

To apply a theme mask we define a theme constant image representing the mask and then invoke Label.setMaskName(String) with the name of the theme constant. We can also set the mask object directly into the label which can be very beneficial.

The SocialBoo demo uses this approach to round up the images of the avatars using rounded borders…​

Performance Tradeoffs

Masking is expensive, it works by literally reviewing every pixel in the mask against every pixel in the image to apply alpha. Applying a mask during rendering is prohibitively expensive.

URLImage solves this by applying the mask as the image data is downloaded. It saves an image object with the mask already applied thus removes the cost of masking from future usage. It does make a tradeoff of encoding and saving the image which is more expensive than just saving the image but that benefits future executions. Other mask types don’t have that level of seamless usage. E.g. when we use the mask builtin to label we might run into a performance overhead if we call setIcon on more than one occasion.

We might also trigger a performance issue when invoking setIcon in a performance critical section of the code. E.g. if we optimize performance by lazily loading images while scrolling this masking might produce an overhead. It’s important to be vigilant when you apply masking to elements so you don’t do it more than once.

2 Comments

Leave a Reply