Thursday, July 28, 2011

Scaling transparent images in Java.

Ever had the need to scale images using Java? If so you would have realized that its a fairly easy task given the amount of support provided by Java Imaging API's as well as the numerous resources available online. Yup I just said it, numerous resources online, so the obvious question why write another blog post? Well you are about to find out.

I've been working on scaling uploaded images using Java for a couple of days now. Everything works fine for an opaque image, but not too well for transparent images. As it turned out most of the articles on scaling images discussed how to scale opaque images but not transparent ones. So I thought I might as well blog about it.

The issue with scaling transparent images are that the resulting images is distorted, and the transparent areas are drawn in black! Check out the two images below.

                                        Original image                           Scaled Image

This obviously wasn't acceptable, as a quick fix we investigated on coloring the transparent images to White color but the that was an ugly fix too.

Most of the blogs and forums that discussed this issue of black background issue suggested setting the ColorModel to BufferedImage.TYPE_INT_ARGB but unfortunately that didn't quite work for me.

The issue is how we paint the image once transformed, make sure to write the image in PNG format so that transparency is preserved. JPEG doesn't support transparency and GIF formats behave differently depending on the background etc. On the other hand using PNG works fine for transparent and opaque images.
          
private static DataHandler scaleImage(DataHandler dataHandler, int height, int width) throws IOException {

        Image image = ImageIO.read(new BufferedInputStream(dataHandler.getInputStream()));
        // Check if the image has transparent pixels
        boolean hasAlpha = ((BufferedImage)image).getColorModel().hasAlpha();

        // Maintain Aspect ratio
        int thumbHeight = height;
        int thumbWidth = width;
        double thumbRatio = (double)width / (double)height;
        double imageRatio = (double)image.getWidth(null) / (double)image.getHeight(null);
        if (thumbRatio < imageRatio) {
            thumbHeight = (int)(thumbWidth / imageRatio);
        } else {
            thumbWidth = (int)(thumbHeight * imageRatio);
        }

        BufferedImage thumb;
        // Check if transparent pixels are available
        // and set the color mode accordingly 
        if (hasAlpha) {
            thumb = new BufferedImage(thumbWidth, thumbHeight,
                    BufferedImage.TYPE_INT_ARGB);
        } else {
            thumb = new BufferedImage(thumbWidth, thumbHeight, 
                    BufferedImage.TYPE_INT_RGB);
        }
        Graphics2D graphics2D = thumb.createGraphics();
        graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);

        // Save the image as PNG so that transparent pixels are rendered
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        ImageIO.write(thumb, "PNG", output);

        DataSource dataSource= new ByteArrayDataSource(output.toByteArray(), 
                               "application/octet-stream");
        return new DataHandler(dataSource);
    }

As you can see the image format is set to PNG when and the final image is returned as a DataHandler.

If you want to iterate each and every pixel of an image and then identify transparent pixels and change the color of that pixel you can do it as follows;

int destWidth = 151;
  int destHeight = 179;
  BufferedImage dest = new BufferedImage(destWidth, destHeight,
    BufferedImage.TYPE_INT_ARGB);
for (int i = 0; i < dest.getHeight(); i++) {
   for (int j = 0; j < dest.getWidth(); j++) {
    int pixel = dest.getRGB(j, i);
    byte alpha=(byte)pixel;
    alpha%=0xff;
    if (pixel == 0) {
     // Set the color of the pixel to White
     dest.setRGB(j, i, Color.WHITE.getRGB());  
    }
   }
  }

More useful links on scaling images are listed below.


[1] - http://developeriq.in/articles/2010/oct/07/playing-with-images-using-java/
[2] - http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html


Tuesday, July 19, 2011

Setting up WSO2 Stratos on a local machine.

Setting up WSO2 Stratos has become as simple as executing a few commands, this post discusses how Startos can be setup on a local machine.

You can checkout carbon from the trunk or branch and build the Stratos services and set it up locally.
Carbon trunk is found at - https://svn.wso2.org/repos/wso2/trunk/carbon/
Carbon 3.2.0 branch at - https://svn.wso2.org/repos/wso2/branches/carbon/3.2.0

Next you need to configure some variables in ~/.bashrc file as follows;
export CARBON_DIR="/home/user/trunk/carbon" => carbon checkout directory 
export STRATOS_DIR=/home/user/stratos/deploy => directory to setup stratos 
export STRATOS_VERSION=1.5.1 => Stratos version
export SSO_ENABLED=true => enable / disable single sign-on
export CREATE_DB=true
export STRATOS_MAIL_TRANSPORT='****.***.***25 false false cloud-test@gmail.com'
export NOTIFICATION_EMAIL=user@gmail.com


If you have all the Stratos services in a single directory can use use the PACKS_DIR option instead of CARBON_DIR. For Example, if all your packs are located at /home/user/services; then set the PACKS_DIR option as follows;
export PACKS_DIR=/home/user/services

In order to execute the setup script you need to install List More Utils, you can do this as follows;
$sudo apt-get install liblist-moreutils-perl
You can find the Stratos setup scripts at ${CARBON_DIR}/build directory. You can execute the setup script - stratos-setup.pl as follws;
$./stratos-setup.pl

Once you execute the script as shown above, the Stratos services will be unpacked and deployed at the directory denoted as STRATOS_DIR. Also several configuration files will be modified under the hood to ensure no port conflicts occur while starting multiple services on one machine, and also include custom URL's based on the service, in this case, esb.cloud-test.wso2.com for the ESB as a Service etc.

Once the script runs, you need to add the following host entries to the /etc/hosts file;
127.0.0.1 cloud-test.wso2.com
127.0.0.1 identity.cloud-test.wso2.com
127.0.0.1 governance.cloud-test.wso2.com
127.0.0.1 appserver.cloud-test.wso2.com
127.0.0.1 bam.cloud-test.wso2.com
127.0.0.1 dss.cloud-test.wso2.com
127.0.0.1 bps.cloud-test.wso2.com
127.0.0.1 brs.cloud-test.wso2.com
127.0.0.1 cep.cloud-test.wso2.com
127.0.0.1 esb.cloud-test.wso2.com
127.0.0.1 gadget.cloud-test.wso2.com
127.0.0.1 mb.cloud-test.wso2.com

127.0.0.1 mashup.cloud-test.wso2.com 

Now you are ready to run Straos on your machine. Simply navigate to the  $STRATOS_DIR and run the stratos.sh file as follows;
$cd $STRATOS_DIR
$sh stratos.sh start all => Starts all the Stratos services
$sh stratos.sh stop all => Stop all the running Stratos services

Alternately you can change to specific service directory and start the respective server. To starts Stratos ESB you can simply do;
$cd $STRATOS_DIR/wso2stratos-esb-1.5.1
$sh bin/wso2server.sh

The option Single Sign-On means that once a user logs in to a single service, eg: Stratos Manager using their credentials they will automatically be signed in to any other service eg: ESB or Governance.

Hope you enjoy using WSO2 Stratos !!!