Patchou's Cabana

The home page and blog of Cyril Paciullo

Resizing transparent bitmaps with the BlackBerry JDE

Here’s a post I made yesterday on the BlackBerry’s Java Development forum. See the original post here. I updated the code since then, the latest version can be found below.

I recently tried to use Bitmap.scaleInto() to resize transparent bitmaps in my application. Although this function is a nice addition to JDE 5, it tends to produce bad-looking results on transparent bitmaps such as PNG24 files. Forum posts I found on the web for this problem talk about using Bitmap.createAlpha() which does restore transparency but still produces artifacts in the final bitmap such as white edges and wicked pixel colors.

To solve this without the use of an external library, I created the following function. It still uses scaleInto() but in a different manner. I added some comments in the function and created a screenshot showing before and after results (don’t mind the pictures, they’re part of the project I’m currently working on). I’m posting the result here in hope it will help other people searching to achieve the same thing. The code was tested in JDE 5 and JDE 6 and the sample pictures were created using different combinations of filter types and aspect ratios.

package com.patchou.ui;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Graphics;
/**
 *
 * @author    Patchou
 * @version   1.01
 *
 */
public class GPATools
{
    /**
     * Resizes a bitmap with an alpha channel (transparency) without the artifacts introduced
     *   by <code>scaleInto()</code>.
     *
     * @param bmpSrc        Source Bitmap
     * @param nWidth        New Width
     * @param nHeight       New Height
     * @param nFilterType   Filter quality to use. Can be <code>Bitmap.FILTER_LANCZOS</code>,
     *                           <code>Bitmap.FILTER_BILINEAR</code> or
     *                           <code>Bitmap.FILTER_BOX</code>.
     * @param nAspectRatio  Specifies how the picture is resized. Can be
     *                           <code>Bitmap.SCALE_TO_FIT</code>,
     *                           <code>Bitmap.SCALE_TO_FILL</code> or
     *                           <code>Bitmap.SCALE_STRETCH</code>.
     * @return              The resized Bitmap in a new object.
     */
    public static Bitmap ResizeTransparentBitmap(Bitmap bmpSrc, int nWidth, int nHeight, int nFilterType, int nAspectRatio)
    {
        if(bmpsrc== null)
            return null;

        //Get the original dimensions of the bitmap
        int nOriginWidth = bmpSrc.getWidth();
        int nOriginHeight = bmpSrc.getHeight();
        if(nWidth == nOriginWidth && nHeight == nOriginHeight)
            return bmpSrc;

        //Prepare a drawing bitmap and graphic object
        Bitmap bmpOrigin = new Bitmap(nOriginWidth, nOriginHeight);
        Graphics graph = Graphics.create(bmpOrigin);

        //Create a line of transparent pixels for later use
        int[] aEmptyLine = new int[nWidth];
        for(int x = 0; x < nWidth; x++)
            aEmptyLine[x] = 0x00000000;
        //Create two scaled bitmaps
        Bitmap[] bmpScaled = new Bitmap[2];
        for(int i = 0; i < 2; i++)
        {
            //Draw the bitmap on a white background first, then on a black background
            graph.setColor((i == 0) ? Color.WHITE : Color.BLACK);
            graph.fillRect(0, 0, nOriginWidth, nOriginHeight);
            graph.drawBitmap(0, 0, nOriginWidth, nOriginHeight, bmpSrc, 0, 0);

            //Create a new bitmap with the desired size
            bmpScaled[i] = new Bitmap(nWidth, nHeight);
            if(nAspectRatio == Bitmap.SCALE_TO_FIT)
            {
                //Set the alpha channel of all pixels to 0 to ensure transparency is
                //applied around the picture, if needed by the transformation
                for(int y = 0; y < nHeight; y++)
                    bmpScaled[i].setARGB(aEmptyLine, 0, nWidth, 0, y, nWidth, 1);
            }

            //Scale the bitmap
            bmpOrigin.scaleInto(bmpScaled[i], nFilterType, nAspectRatio);
        }

        //Prepare objects for final iteration
        Bitmap bmpFinal = bmpScaled[0];
        int[][] aPixelLine = new int[2][nWidth];

        //Iterate every line of the two scaled bitmaps
        for(int y = 0; y < nHeight; y++)
        {
            bmpScaled[0].getARGB(aPixelLine[0], 0, nWidth, 0, y, nWidth, 1);
            bmpScaled[1].getARGB(aPixelLine[1], 0, nWidth, 0, y, nWidth, 1);

            //Check every pixel one by one
            for(int x = 0; x < nWidth; x++)
            {
                //If the pixel was untouched (alpha channel still at 0), keep it transparent
                if(((aPixelLine[0][x] >> 24) & 0xff) == 0)
                    aPixelLine[0][x] = 0x00000000;
                else
                {
                    //Compute the alpha value based on the difference of intensity
                    //in the red channel
                    int nAlpha = ((aPixelLine[1][x] >> 16) & 0xff) -
                                    ((aPixelLine[0][x] >> 16) & 0xff) + 255;
                    if(nAlpha == 0)
                        aPixelLine[0][x] = 0x00000000; //Completely transparent
                    else if(nAlpha >= 255)
                        aPixelLine[0][x] |= 0xff000000; //Completely opaque
                    else
                    {
                        //Compute the value of the each channel one by one
                        int nRed = ((aPixelLine[0][x] >> 16 ) & 0xff);
                        int nGreen = ((aPixelLine[0][x] >> 8 ) & 0xff);
                        int nBlue = (aPixelLine[0][x] & 0xff);

                        nRed = (int)(255 + (255.0 * ((double)(nRed-255)/(double)nAlpha)));
                        nGreen = (int)(255 + (255.0 * ((double)(nGreen-255)/(double)nAlpha)));
                        nBlue = (int)(255 + (255.0 * ((double)(nBlue-255)/(double)nAlpha)));

                        if(nRed < 0) nRed = 0;
                        if(nGreen < 0) nGreen = 0;
                        if(nBlue < 0) nBlue = 0;
                        aPixelLine[0][x] = nBlue | (nGreen<<8) | (nRed<<16) | (nAlpha<<24);
                    }
                }
            }

            //Change the pixels of this line to their final value
            bmpFinal.setARGB(aPixelLine[0], 0, nWidth, 0, y, nWidth, 1);
        }
        return bmpFinal;
    }
} 

Here’s an example of how to call the function:

Bitmap bmp = Bitmap.getBitmapResource("picture.png");
Bitmap bmpResized = GPATools.ResizeTransparentBitmap(bmp, 30, 60,
    Bitmap.FILTER_LANCZOS, Bitmap.SCALE_TO_FIT);

Here’s the result:

One Response to “Resizing transparent bitmaps with the BlackBerry JDE”


  1. Teban says:

    It’s really inspiring that when the predefined function didn’t work as you expected, you just made one yourself that did a great work.
    I’m starting in programming, and at least at first I would have become just really frustrated.

    Thanks for sharing :) The results are excellent!




  • Archive

  • Categories

  • Blogroll