Create Shadow and Blur Images

by BlackBerry Development Advisor on ‎04-21-2011 07:35 PM (5,581 Views)

Summary

 

This article applies to the following:

 

  • BlackBerry® smartphones based on Java® technology

 

Description

 

I've recently been using Graphics.drawFilledPath to produced a background shadow effect. Although this function works flawlessly, it does not provide the developer with the ability to apply a linear gradient with an Alpha Channel.

 

After doing some research and several hours of programming I've managed to produced a set of functions that provides a Gaussian Distribution Blur Filter. The function provided allows the user to blur an image with a given radius. It also allows the user to produce shadows.

 

 

/****************************************************************************************
*
* File Name: ImageTools.java
*
* Purpose: Collection of Bitmap Image Processing Tools
*
* Created By: Mitchell Romanuik.
* Date Created: Wednesday December 22, 2010.
* Last Modified: Thursday January 05, 2011.
*
****************************************************************************************/
package program;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Graphics;

public class ImageTools
{
// Variables and Fields
private static int _radius;
private static int _kernelSize;
private static int[] _kernel;
private static int[][] _productTable;

/*************************************************************************************
*
* Required by blur() function to process images. This function merely builds a suitable
* data kernel which is in the form of a Gaussian Distribution. This kernel can be
* changed which will result in a different type of blur produced.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !!!!! THIS IS NOT THREAD SAFE !!!!!!!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* @param radius Radius of Blur
* @return void
*
***************************************************************************************/
public static void buildKernel(int radius){
// Determine if Radius Selected is Valid (Values 64, 128, and 256 are Valid Maxima Values for Radius)
radius = Math.min(64, Math.max(1, radius));

// Initialize Global Variables
_radius = radius;
_kernelSize = 1 + 2*radius; // This is the range for Pixel Selection
_kernel= new int[_kernelSize]; // This is a Gaussian Distribution Dictionary

// This is an added Optimization for Embedded Devices (Reduces CPU Stress)
_productTable = new int[_kernelSize][256];

// Augment Values of Gaussian Curve Excluding Radius Pixel
for(int i=1; i<radius; i++){
// Initialize Temp Variables
int increment = radius - i;

// Gaussian Curve is Symmetric
_kernel[radius+i] = _kernel[increment] = increment*increment;

// Produce Product Table for 'each' Pixel Color 0-255
for(int j=0; j<256; j++)
_productTable[radius+i][j] = _productTable[increment][j] = _kernel[increment]*j;
}

// Augment Values of Gaussian Curve for Radius Pixel
_kernel[radius] = radius*radius;
for(int j=0; j<256; j++)
_productTable[radius][j] = _kernel[radius]*j;
}


/*************************************************************************************
*
* Blurs a Given Bitmap with the Option of Alpha Channel PreProcessing
* ** It is recommended that the AlphaChannel is Processed Before Blurring the Image
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !!!!! THIS IS NOT THREAD SAFE !!!!!!!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* @param image Source Bitmap
* @param left,top Start Position of Blur
* @param width,height Amount of Blur to Produce
* @param isAlpha This is an added feature which blurs only the edges of an
* image. Typically the boundary parameters are the entire
* image (not required).
* @return The Blurred Bitmap in a New Object.
*
***************************************************************************************/
public static Bitmap blur(Bitmap image, int left, int top, int width, int height, boolean isAlpha){
// Determine Bitmap Properties
int imgWidth = image.getWidth();
int imgHeight = image.getHeight();
int numPixels = width*height; // Only select the Region Size (Optimization)

// Check Image if Alpha Channel Exists
if(!image.hasAlpha() && isAlpha)
isAlpha = false;

// Determine Region to Blur on 'image'
int topYAxis = 0;
top = Math.min(imgHeight, Math.max(0, top));
left = Math.min(imgWidth, Math.max(0, left));
width = Math.min(imgWidth, Math.max(0, width));
height = Math.min(imgHeight, Math.max(0, height));

// Obtain Bitmap Pixels in ARGB from that Region
int[] data = new int[numPixels];
image.getARGB(data, 0, width, top, left, width, height); // Obtain pixel data from 'image' only the Region Specified

// Separate Color Channels
int a[] = new int[numPixels];
int r[] = new int[numPixels];
int g[] = new int[numPixels];
int b[] = new int[numPixels];
for(int i=0; i<numPixels; i++){
// Initialize Temp Variables
int pixel = data[i];
if(isAlpha)
a[i] = Math.abs((pixel&0xFF000000)>>24);
r[i] = (pixel&0x00FF0000)>>16;
g[i] = (pixel&0x0000FF00)>>8;
b[i] = (pixel&0x000000FF);
}

// Clone Array(s) of Color Channels
int a2[] = new int[numPixels];
int r2[] = new int[numPixels];
int g2[] = new int[numPixels];
int b2[] = new int[numPixels];

// Produce Clone of Bitmap with Horizontal Blurring
for(int y=0; y<height; y++){
for(int x=0; x<width; x++){
// Initialize Temp Variables
int alphaChannel=0, redChannel=0, greenChannel=0, blueChannel=0, summation=0;
int pixel = x - _radius;

// Collect RGB Data from the range [radius-x, radius+x]
for(int i=0; i<_kernelSize; i++){
// Initialize Temp Variables
int tmpPixel = pixel + i;

// Determine if Selected Pixel is within Boundary
if(tmpPixel >= 0 && tmpPixel < width){
// Collect RGB or Alpha Data over Radius
tmpPixel += topYAxis;
if(!isAlpha){
redChannel += _productTable[i][r[tmpPixel]];
greenChannel += _productTable[i][g[tmpPixel]];
blueChannel += _productTable[i][b[tmpPixel]];
}
else
if(a[tmpPixel] != 0)
alphaChannel += _productTable[i][255];

// Increase Color Average
summation += _kernel[i];
}
}
// Store Processed Data into Clone Array(s)
if(isAlpha)
a2[x+topYAxis] = alphaChannel/summation;
else{
r2[x+topYAxis] = redChannel/summation;
g2[x+topYAxis] = greenChannel/summation;
b2[x+topYAxis] = blueChannel/summation;
}
}
// Set New Data Position for Pixel Array
topYAxis += width;
}

// Produce Clone of Bitmap with Vertical Blurring
for(int x=0; x<width; x++){
for(int y=0; y<height; y++){
// Initialize Temp Variables
int alphaChannel=0, redChannel=0, greenChannel=0, blueChannel=0, summation=0;
int pixel = y - _radius;

// Collect RGB Data from the range [radius-y, radius+y]
for(int i=0; i<_kernelSize; i++){
// Initialize Temp Variables
int tmpPixel = (pixel + i)*width + x;

// Determine if Selected Pixel is within Boundary
if(tmpPixel < numPixels && tmpPixel >= 0){
// Collect RGB or Alpha Data over Radius
if(!isAlpha){
redChannel += _productTable[i][r2[tmpPixel]];
greenChannel += _productTable[i][g2[tmpPixel]];
blueChannel += _productTable[i][b2[tmpPixel]];
}
else
if(a[tmpPixel] != 0)
alphaChannel += _productTable[i][a2[tmpPixel]];

// Increase Color Average
summation += _kernel[i];
}
}
// Recombine Color Channels with Full Opacity
if(isAlpha)
data[x+y*width] = (alphaChannel/summation)<<24 | r[x+y*width]<<16 | g[x+y*width]<<8 | b[x+y*width];
else
data[x+y*width] = 0xFF000000 | (redChannel/summation)<<16 | (greenChannel/summation)<<8 | (blueChannel/summation);
}
}

// Replace 'image' Data with PreProcessed Data
image.setARGB(data, 0, width, top, left, width, height);
return image;
}
}

 

^^ code looks ugly Smiley Sad

 

How to Blur an image.

 

 

ImageTools.buildKernel(5);
Bitmap _target = Bitmap.getBitmapResource("firefox.png");
_target = ImageTools.blur(_target, 0, 0, 50, 50, false);

 How to create Shadow.

 

 

// Create Bitmap and Alpha Channel
Bitmap _target = new Bitmap(50, 50);
_target.createAlpha(Bitmap.ALPHA_BITDEPTH_8BPP);

// Initialize Alpha Channel
int[] data = new int[50*50];
for(int i=0; i<50*50; i++)
data[i] = 0x00000000;

// Insert Bitmap Data
_target.setARGB(data, 0, 50, 0, 0, 50, 50);

// Prepare Drawing Canvas
Graphics canvas = Graphics.create(_target);

// Shape Properties
canvas.setColor(0x000000);
canvas.setGlobalAlpha(255);
canvas.fillRoundRect(10, 10, 30, 30, 8, 8);

ImageTools.buildKernel(5);
_target = ImageTools.blur(_target, 0, 0, 50, 50, true);

 

Blur.png

 

*Known Issues: Bitmap.createAlpha() creates funky colors.

 

 

Have fun!

 

Mitchell Romanuik.