October 24, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Maintaining Transparency for Image Transformations

  • October 1, 2001
  • By Chunyen Liu
  • Send Email »
  • More Articles »

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.






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel