JavaMaintaining Transparency for Image Transformations

Maintaining Transparency for Image Transformations

Introduction

This article will show you how to keep transparent pixels transparent
after an image has been transformed, e.g., scaled, rotated, color-tuned.
Most of the issues can be expected to be resolved in the Java2D package
in Java 2. However, as most Java developers understand, current popular
browsers have not supported the latest Java Virtual Machine (JVM) as of now.

So, why not wait until all the browsers start to support the new JVM and let the
problems go away automatically? Here are the reasons: First, tackling
the issues is much simpler than you think. In fact, it is just a matter
of a few lines of codes that can be easily incorporated into your applications.
Second, the support of JVMs in browsers is always behind the latest Java
iteration. If they can even keep up, there are still many of your Java clients
using older versions of browsers. Third, it never hurts to know what is done
or how it is done to resolve problems. Besides, there are many applications
that need this solution immediately, such as virtual clothes fitting on the Web, games, etc.

In this article, I will briefly walk through how colors of image pixels are
encoded in Java. Then I’ll offer transparency examples, and I will show
what they look like if the issue is not taken care of. Lastly, I will
provide the code and the result.

The following fully working source code demonstrates my
implementation. Please let me know when you find bugs or add useful
features to it. Please also note, though, I have not added code
for double buffering but focused on the transparency issues.
When you drag the transparent object, flickering caused by repaint()
will become more pronounced.



Understanding Color Encoding of Pixels in Java

There are four bytes in an integer used in Java to encode the color of a pixel.
The highest eight bits are the alpha channel and then the subsequent
twenty-four bits for the red, green, and blue channels,
each containing eight bits in this order. Each channel has a value from 0 to
255. Java provides classes to extract and encode the channels, although
it is very straightforward to write them yourself.

Channel alpha red green blue
Bits 24 .. 31 16 .. 23 8 .. 15 0 .. 7

Therefore, the maximum possible number of colors is 256 x 256 x 256 (over 16 million)!
alpha is used for transparency. In some other graphics formats, you may see
another channel called the Z channel, which usually represents how close a pixel
is to the viewer in a 3D scene.


Composing Transparent Images without Transformations

Now, we are going to take a look at a real example.
Suppose we have a transparent object, say, a motorcycle helmet,
to be placed on top of a human face (the background). Without doing
any geometric or color transformation, we simply load the background
and object images with the object drawn last. The result is illustrated
as follows in Result 1. It looks perfectly normal, doesn’t it?
This means when we load the images and store them into Image objects
in Java, the transparent pixels are automatically taken care of.
So, what is the work we need to do? In the event you only want to lay
one transparent image on top of the other, you can sit back and enjoy
without doing anything.


For those who are not satisfied, you may think the helmet is too large
and too bright just like me. Okay, we then need to scale the helmet and adjust
the brightness. It sounds simple. That is what we will do in the
next example.

+ =
Background Transparent Object Result 1



Composing Transparent Images with Incorrect Transformations

To scale an image, we can load it into an integer array
and apply the simple 2D geometric transformation. Adjusting the
brightness can be done at the same time, while we extract individual channels.

Here is a brief description of the following code: oldwidth and oldheight are the dimensions of the transparent object before transformations. oldcx and oldcy
is the center. olddata is an integer array holding all the pixels.
All the variables starting with new are for the object after transformations. We resize the transparent object by scale and adjust the brightness by * 8 / 10 for 80 percent in the code. PixelGrabber is used to load an image into an array while MemoryImageSource is for creating the transformed image. The scaling algorithm we use is the simplest one, called nearest neighbor.


The result is in Result 2. Where are our transparent pixels? This is
definitely not acceptable in this application or the like. We will see how
we can tackle this problem in the next section.

float scale = 0.8f;
int oldwidth = objimage.getWidth(this);
int oldheight = objimage.getHeight(this); 
int newwidth = (int)(scale * oldwidth);
int newheight = (int)(scale * oldheight); 
int oldcx = oldwidth / 2;
int oldcy = oldheight / 2;   
int newcx = newwidth / 2;
int newcy = newheight / 2;
int [] olddata = new int[oldheight * oldwidth];
int [] newdata = new int[newheight * newwidth];

// Load pixels into an array
PixelGrabber imagegrabber = new PixelGrabber(objimage, 0, 0, 
   oldwidth, oldheight, olddata, 0, oldwidth);
try
   {
   imagegrabber.grabPixels();
   }
catch(InterruptedException e) {}

int R = 0, G = 0, B = 0;
int oldX = 0, oldY = 0;
for (int y = 0; y < newheight; y++)
   for (int x = 0; x < newwidth; x++)
      {
      oldX = (int)((x - newcx) / scale + oldcx);
      oldY = (int)((y - newcy) / scale + oldcy);
      if (oldX >= 0 && oldX < oldwidth && oldY >= 0 && oldY < oldheight)
         {               
         R = ((olddata[oldY * oldwidth + oldX] & 0x00FF0000) >> 16) * 8 / 10;
         G = ((olddata[oldY * oldwidth + oldX] & 0x0000FF00) >> 8) * 8 / 10;
         B = (olddata[oldY * oldwidth + oldX] & 0x000000FF) * 8 / 10;               
         }
      else
         {
         R = G = B = 0;
         }
      newdata[y * newwidth + x] = (new Color(R, G, B)).getRGB();     
      }    
     
objimage2 = createImage(new MemoryImageSource(newwidth, newheight, 
   newdata, 0, newwidth));

Result 2



Composing Transparent Images with Correct Transformations

You may guess something obviously went wrong with the last example,
and it must have been the alpha channel. And you are
exactly right. So, we modify the code a little bit to preserve the
old alpha channel. Reconstruct the channels in their
order and we have the result in Result 3. The transparency problem is
gone! As I mentioned before, it is simple, and there is no need to
wait for new JVM support from the browser to make this work
for your applications.

int A = 0, R = 0, G = 0, B = 0;
for (int y = 0; y < newheight; y++)
   for (int x = 0; x < newwidth; x++)
      {
      oldX = (int)((x - newcx) / scale + oldcx);
      oldY = (int)((y - newcy) / scale + oldcy);
      A = (olddata[oldY * oldwidth + oldX] & 0xFF000000) >> 24;
      if (oldX >= 0 && oldX < oldwidth && oldY >= 0 && oldY < oldheight)
         {               
         R = ((olddata[oldY * oldwidth + oldX] & 0x00FF0000) >> 16) * 8 / 10;
         G = ((olddata[oldY * oldwidth + oldX] & 0x0000FF00) >> 8) * 8 / 10;
         B = (olddata[oldY * oldwidth + oldX] & 0x000000FF) * 8 / 10;               
         }
      else
         {
         R = G = B = 0;
         }
      newdata[y * newwidth + x] = B & 0x000000FF;
      newdata[y * newwidth + x] |= ((G & 0x000000FF) << 8);
      newdata[y * newwidth + x] |= ((R & 0x000000FF) << 16);
      newdata[y * newwidth + x] |= ((A & 0x000000FF) << 24);
      }    

Result 3



Summary



In this tutorial, I briefly described when the transparency issue
occurs and how we can deal with it in current browsers.
Some basic knowledge about Java color coding is also
mentioned for a better understanding of the issue. After reading this tutorial, now we should be more familiar with the colors. It is, in fact, very simple to alpha-blend the colors to achieve any level of transparency or opacity while composing multiple images. The same technology can be applied to fading transition effects as well.

Now you can probably start a Java project that allows your clients
to click and drag some clothes and accessories and try them on their
digital pictures after appropriately scaling and rotating those
objects. I am sure your clients will be very impressed by your work!

Download



References




  1. Ken Arnold and James Gosling, The Java Programming Language, Addison-Wesley, 1997.

  2. Jonathan Knudsen, Java 2D Graphics, O’Reilly and Associates Inc, 1999.

  3. Jerry Jackson and Alan McClellan, Java by Example, Sun Microsystems Press, 1999.



About the Author


Chunyen Liu is a software
engineer at a global positioning company, GARMIN
International
. Some of his 100-plus Java programs have won major programming
contests. Check out his personal page for more details. He also owns a Java-intensive site called The J Maker.

Latest Posts

Related Stories