Tuesday, January 4, 2011

Video: Reflections on Android

Everyone's supposed to make a resolution on New Years, so here's mine: post more articles/blogs/videos about Android development. Starting with today's video tutorial.

Here's the first in what I hope will be a series of video tutorials on anything ranging from graphics to animation and back to graphics (hey, it's my show and I might as well talk about the things I enjoy).

For the Devoxx conference last November, I developed a simple picture-viewing application to demonstrate various facets of UI development that Romain Guy and I were talking about that week. You can see the presentations online at parleys.com (you'll have to register for 79 Euros to get access to them for now). But I wanted to deep dive into particular aspects of this application for my blog. Here's the first of these tutorials, in which I talk about the simple reflection effect used in the application. By the way, credit for the beautiful pictures goes to Romain, my source for all of my, er, borrowed images.

The video is in two parts (because YouTube thinks that I talk too much, so I had to split it). This first part introduces the show and talks about the effect at a high level:

Part 2 dives into the code that makes the reflection effect work:

The code in the video is a tad blurry (given the resolution of the video compared to the size of the IDE window), so here's the code from PictureView.java that I walk through for your reading pleasure (note that this code looks slightly different than that in the video due to formatting for a smaller line wrap. Also, the blurryBitmap image is now created to be only as high as the reflection height, as described in the comments):

private Bitmap getReflection(Bitmap bitmap) {
    Bitmap reflection = reflections.get(bitmap);
    if (reflection == null) {
        // We're cropping the height of the reflection to 80
        int reflectionH = 80;
        reflection = Bitmap.createBitmap(bitmap.getWidth(),
                reflectionH, Bitmap.Config.ARGB_8888);

        Bitmap blurryBitmap = Bitmap.createBitmap(bitmap, 0,
                bitmap.getHeight() - reflectionH,
                bitmap.getWidth(), reflectionH);
        // cheap and easy scaling algorithm; down-scale it, then
        // upscale it. The filtering during the scale operations
        // will blur the resulting image
        blurryBitmap = Bitmap.createScaledBitmap(
                Bitmap.createScaledBitmap(
                        blurryBitmap,blurryBitmap.getWidth() / 2,
                        blurryBitmap.getHeight() / 2, true),
                blurryBitmap.getWidth(), blurryBitmap.getHeight(), true);
        // This shader will hold a cropped, inverted,
        // blurry version of the original image
        BitmapShader bitmapShader = new BitmapShader(blurryBitmap,
                TileMode.CLAMP, TileMode.CLAMP);
        Matrix invertMatrix = new Matrix();
        invertMatrix.setScale(1f, -1f);
        invertMatrix.preTranslate(0, -reflectionH);
        bitmapShader.setLocalMatrix(invertMatrix);

        // This shader holds an alpha gradient
        Shader alphaGradient = new LinearGradient(0, 0, 0, reflectionH,
                0x80ffffff, 0x00000000, TileMode.CLAMP);

        // This shader combines the previous two, resulting in a
        // blurred, fading reflection
        ComposeShader compositor = new ComposeShader(bitmapShader,
                alphaGradient, PorterDuff.Mode.DST_IN);

        Paint reflectionPaint = new Paint();
        reflectionPaint.setShader(compositor);

        // Draw the reflection into the bitmap that we will return
        Canvas canvas = new Canvas(reflection);
        canvas.drawRect(0, 0, reflection.getWidth(),
                reflection.getHeight(), reflectionPaint);
    }
    return reflection;
}

And finally, here's the Eclipse project complete with source code, images, and everything you need to build and run the application. The app is targeted at Android 2.2 (Froyo) (and probably could work on earlier versions as well), so you should be able to run it on the emulator or any appropriate device. Note that it's just a demo and not really a full-featured photo viewer; it was written to demonstrate particular effects and techniques, not to be a real application.

Now that my New Year's resolution is fulfilled, I should go back to working on Android framework code...

9 comments:

Unknown said...

Nice effect. I'm looking forward to seeing some stuff about animations in android. Not really seen that much on it so far.

Unknown said...

Hi Chet. Nice work! I tested it and I have a small performance & memory improvement. Instead of scaling the whole bitmap down and up one could take only the important shadow part of the image:

Bitmap blurryBitmap = Bitmap.createBitmap(bitmap, 0, bitmap.getHeight() - reflectionH, bitmap.getWidth(), reflectionH);
blurryBitmap = Bitmap.createScaledBitmap(
Bitmap.createScaledBitmap(blurryBitmap,blurryBitmap.getWidth() / 2, blurryBitmap.getHeight() / 2, true),
blurryBitmap.getWidth(), blurryBitmap.getHeight(), true);
// and of course use invertMatrix.preTranslate(0, -reflectionH);
// instead of invertMatrix.preTranslate(0, -bitmap.getHeight());

Through measurements this improvement was 1.5X to 4X faster while maintaining the same result, not to mention the reduced risk of an OutOfMemoryError. :-)

Greetings from Germany
Marc

PS: Would be nice if Blogger had support for <pre> and <code> tags...

Joe Nuxoll said...

I like how the app is looking now - with those slight detailed improvements. :-)

taf said...

Nice tip on how to add a blur to the bitmap. I did a similar tutorial a while back. In case anyone is interested you can find it here:

http://www.inter-fuser.com/2009/12/android-reflections-with-bitmaps.html

Chet Haase said...

@Marc: Thanks for the suggestion; definitely a nice addition. I've updated the code and the download to 'reflect' this change.
Blogger's definitely not the greatest at code-posting. At least it lets me code-format the main blog entries, if not the comments.

Unknown said...

Hi Chet! Thank you for including my little improvement! :-)

Happy hacking
Marc

Lee Brimelow said...

Awesome tutorial Chet! I had no idea you left Adobe. Been getting heavily into native Android myself as of late.

Chet Haase said...

Hey Lee: Yep, showed up on the Android team last May, been heads-down cranking on the UI toolkit ever since. Good luck with your Android hacking!

Anonymous said...

hi,

how about an example of reflection for something that moves?
for example , have a gallery of thumbnails that you can scroll (and press on each of them, of course) , yet you get a reflection below it?
it seems that your solution is very software-related instead of hardware-related. doesn't it mean that it won't work well for something that moves fast?