Android Bitmaps — BitBeauty (Part 1)

Soumya Kanti Kar
wwdablu
Published in
7 min readApr 15, 2018

After reading posts from various developers on Medium, I thought to join the group by posting my own post. The first hurdle was finding a topic on which I can write a post about. Coincidentally, during that time I was working on bitmaps on an android application. Eureka! Why not write a post about it. So, lets dive-in.

I am working on an Android library which would allow developers to perform various actions with the minimal amount of code and easily. The library has been written using Kotlin and has been shared in the GitHub. For image loading and bitmap handling purpose we will be using Glide. Apart from this we will also be using the Bitmap and BitmapFactory.

So, the first thing that we need is, you guessed it, a bitmap. And how do we get one? We can either create it or we can load existing images using Glide and ask it to give us the bitmap for it.

val bmp:Bitmap = Glide.get(context).bitmapPool.get(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)

In the above code, we are getting an empty bitmap from the bitmap pool maintained by Glide. This removes the responsibility from the developer related to the bitmap management. But, do remember not to call recycle() method on this bitmap as it is managed by Glide.

Good, now let us suppose I want to load an existing image and get a bitmap from out. We can use BitBeauty (which uses Glide internally) like:

val simpleTarget = object : SimpleTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
val bitBeautyBitmap = BitBeautyBitmap(resource, Bitmap.Config.ARGB_8888)
emitter.onNext(bitBeautyBitmap)
emitter.onComplete()
}
}
Glide.with(context).asBitmap().load(image).into(simpleTarget)

or call BitBeauty as:

BitBeauty.Creator.createBitmapFromDrawable(this, R.drawable.bridge)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object: DisposableObserver<BitBeautyBitmap>() {
override fun onComplete() {
//
}
override fun onNext(t: BitBeautyBitmap) {
findViewById<ImageView>(R.id.iv_image).setImageBitmap((t.getBitmap()))
}
override fun onError(e: Throwable) {
//
}
})

Once the above code is executed we should receive a bitmap enclosed inside the class BitBeautyBitmap. The reason BitBeauty returns BitBeautyBitmap is because it allows some direct action on the bitmap to be performed and in future too this encapsulation can be leveraged more.

So, we have the bitmap loaded and being displayed on an ImageView.

Actual image loaded

We have already made a good progress of getting a bitmap and displaying it to the user using an ImageView.

Yes I agree, this is not much interesting, so lets continue. Now, what if I want to playing with some filters, you know those fancy effects like, Greyscale, Sepia and so forth. Guess what, we can do it on Android out of the box and BitBeauty makes it more simple to use. It already has some predefined effects already.

To achieve this effect, we need to use ColorMatrix. Using the color matrix we can play around with the ARGB (Alpha-Red-Green-Blue) channels are get the desired effects that we want on the image. For example, to get a negative effect image, we can use the ColorMatrix as:

val invertMatrix: FloatArray = floatArrayOf(
-1F, 0F, 0F, 0F, 255F,
0F, -1F, 0F, 0F, 255F,
0F, 0F, -1F, 0F, 255F,
0F, 0F, 0F, 1F, 0F)

So let us apply the greyscale effect using BitBeauty as:

BitBeauty.Effects.toGrayScale(bridgeBitBeautyBitmap)
Image converted to greyscale

So what do you say now?

Good, so we are making progress. We can modify the ARGB channel values are when applied on the image can modify the image color altogether.

Effects are good, I provide an ARGB matrix as I get the effect. What about cropping and masking, can we do that? To answer that in short, Yes! We can.

Consider that we can to crop a particular region of the image. To do this, we need to use Canvas, Paint and a bitmap. First, let us check the code:

val canvas = Canvas(dstBitBmp)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
canvas.drawCircle(radius, radius, radius, paint)
canvas.drawBitmap(srcBitBmp.getBitmap(), srcRect, dstRect, paint)

The above snippet is the main code which will perform the action of cropping. So what we do is first we create a bitmap of the size of which the original image is to be cropped. Once the bitmap is created, we attach it with a Canvas which would be required for drawing. Like in real life, to draw you need paint, so in the Android too, for drawing on bitmaps we need Paint object. Now, we simply call the drawBitmap method of canvas and command it to draw the source bitmap from the source RECT to the destination RECT. The RECT object specified the region on which the action is to be performed.

So using BitBeauty, we can crop as following:

val croppedBitmap = BitBeauty.Editor.crop(context, srcBitBmp, Rect(top, left, right, bottom)

And the output is:

Greyscale image cropped

Is this getting interesting?

Ok crop is good, what if I want to get a cropped section but in a particular shape? Well… you ask too much, but yes, Android can do it too. No sweat.

Achieving this using BitBeauty is simple. First, we need to create a bitmap which will be used as mask. So let us consider that we want to crop a circular image on the middle car crossing the bridge. To achieve this, we will create a mask bitmap with a circle and paint it black. It should be noted, that we can use any color that we want to use. For an example, we are using the black.

So let us create a bitmap which will hold the circular bitmap.

BitBeauty.Creator.createBitmap(context, 500, 500, Color.TRANSPLANT)

Now in that bitmap let us draw the black circle. The process to draw the circle using Canvas and Paint is as follows:

fun drawCircle(bitBeautyBitmap: BitBeautyBitmap, @ColorInt colorInt: Int, radius: Float, center: Point) {val canvas = Canvas(bitBeautyBitmap.getBitmap())
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.color = colorInt
canvas.drawCircle(xCoor, yCoor, radius, paint)
}

But using BitBeauty we can do it as:

BitBeauty.Shape.createCircle(srcBitBmp, Color.BLACK, radius, centerPoint)

Now, next we need perform the magic. We will be using the circular black image to crop it out in that shape. To achieve this, we need to use Paint class and the setXfermode and use PorterDuff which we will be using for masking mode.

Using PorterDuff we are going to overlap the two images in such a manner that while combining them the black portion will act as a window for the source image, that is, the original image will be viewable by only the region covered by the mask image.

Cool, but what is Mode.SRC_IN option? Well, it is basically defining the 12 options which we can use to work on the bitmaps and perform various composition. The details can be referred on the documentation.

So, if we try to visualize, let us consider that we have two printouts. One the original image printout, and the second a white page with a hole (the black region of the mask). Now if we place the page with the hole on top of the printout of the image, then we can see the portion of the image which we can see through the hole. The same concept if applicable here.

The code which does the same for us is:

val canvas = Canvas(dstBitBeautyBitmap.getBitmap())
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.xfermode = PorterDuffXfermode(mode)
canvas.drawBitmap(srcBitBeautyBitmap.getBitmap(), 0F, 0F, paint)

Using BitBeauty we can achieve the same as:

BitBeauty.Editor.mask(srcBitBmp, maskBitBmp, PorterDuff.Mode.SRC_IN)
Cropped a circular image

And how does it feel now?

Love it. So using it, we can create bitmaps with custom shapes as mask. Nice. But can I paste the new masked bitmap on another bitmap? Well, sure. Basically, it means that we need to draw the smaller bitmap on a certain location on the bigger bitmap.

BitBeauty provides a method to just do that.

BitBeauty.Editor.combine(smallerBitBmp, biggerBitBmp, startPoint)

Sweet. But you know what, can I just rotate a bitmap before I can combine? That would be so super cool. Sure, with Android you can do it and using BitBeauty, well, its a single line of code.

BitBeauty.Editor.rotate(context, srcBitBmp, 90)

Just provide the degree to which the rotation is to be done and that would all be good.

The library is still being developed and is under development. It is still in its infancy. Would love to collaborate with other developers to work with. The features mentioned above are few of the functionalities provided. You can head over to GitHub and check out more details on the wiki page too, also the code is available too.

Let me know your comments and your thoughts.

Second part of the story

--

--

Soumya Kanti Kar
wwdablu
Editor for

Android developer. Interested in working on open source Android libraries and applications.