Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Java Development

Reply
Contributor
Patchou
Posts: 11
Registered: ‎10-02-2010
My Device: Curve 8900
Accepted Solution

Solution for properly resizing transparent bitmaps

[ Edited ]

Hello everybody,

 

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:

GPATools.png

 

Let me know if you find this useful, all comments are appreciated :smileyhappy:.

Cyril

Developer
m-romanuik
Posts: 114
Registered: ‎04-28-2010
My Device: Storm 9530

Re: Solution for properly resizing transparent bitmaps

Bravo, Im definitely going to load this into my blackberry app to test it. It really has given me more insight into transformations on bitmaps and ARGB channel's.

Contributor
Patchou
Posts: 11
Registered: ‎10-02-2010
My Device: Curve 8900

Re: Solution for properly resizing transparent bitmaps

[ Edited ]

Thank you :smileyhappy:. I made a minor adjustement to the code (to verify if the size of the original bitmap is the same as the desired size). For some reason I can't edit my original post so I posted the latest version on my blog. Link: http://www.patchou.com/2010/10/resizing-transparent-bitmaps-with-the-blackberry-jde/ .

 

Moderator Note:  I have updated the code in the original post to match the latest version.

Developer
tombz
Posts: 24
Registered: ‎04-22-2009
My Device: Not Specified

Re: Solution for properly resizing transparent bitmaps

Very nice work. scaleInto() kept adding random color noise to the background of my transparent images, but your code works perfectly. Thanks for posting this.

 

-Tom B.

Developer
JamesColeman
Posts: 266
Registered: ‎04-24-2009
My Device: Not Specified

Re: Solution for properly resizing transparent bitmaps

Thanks for the code. I really appreciate the help. However, I got an Uncaught exception with your code on the 6.0 JDE. I thought you may know the answer to.

 

Graphics graph = Graphics.create(bmpOrigin); Bitmap is too large for graphics surface

 

I need to resize a full resolution 3.0 BlackBerry image(2054 x 1536). Is that too large to be resized or am I experienceing some other type of problem.

 

Thanks!!!

Contributor
Patchou
Posts: 11
Registered: ‎10-02-2010
My Device: Curve 8900

Re: Solution for properly resizing transparent bitmaps

Interesting, I didn't know about this limitation of the Graphics class. I guess that's related to the amount of memory available to BB apps. In memory, your picture would easily use 10MB.

 

I suggest you simply resize different parts of your picture separately. For example, in a loop, you could create Bitmaps that are roughly 500 x 500, then use Graphics.create(Bitmap) and Graphics.drawBitmap() with your original big bitmap as parameter to draw a part of your picture, then use the result wherever you want. So you would start at corner 0,0 of your large bitmap, then 500,0 then 1000,0 then when the first line is finished: 0,500 , 500,500 , 1000,500 ...

 

This should be doable with only a couple of lines of code, the end result will be about the same and even if the BB allowed you to manipulate such big files, at least your app will use a lot less memory than it would have otherwise. What do you think?

Developer
JamesColeman
Posts: 266
Registered: ‎04-24-2009
My Device: Not Specified

Re: Solution for properly resizing transparent bitmaps

Bummer I was hoping it was something else other than an OS limitation.

 

However, Brilliant idea about breaking it into smaller chunks. I ran into a simular limitation going from SVG graphics to Bitmaps. Hopefuully I can easily modify that code. If not, sounds simple anyway.  I will try to rember to post the final code on this thread.

 

Thanks for the quick responce!!!!

 

Developer
JChow
Posts: 68
Registered: ‎09-04-2008
My Device: Not Specified

Re: Solution for properly resizing transparent bitmaps

Hi Patchou,

 

thanks for your code. It is nice. Is there any restriction modify or redistribte this piece of code? of if it is under some kind of license? 

 

thank you

JChow

New Contributor
sarnab2
Posts: 8
Registered: ‎08-09-2011
My Device: montana

Re: Solution for properly resizing transparent bitmaps

Thanks for the excellent solution!

Contributor
Patchou
Posts: 11
Registered: ‎10-02-2010
My Device: Curve 8900

Re: Solution for properly resizing transparent bitmaps

[ Edited ]

Hello JChow,

 

sorry for the late answer, I never got notified about your post. You can consider the entire code to be free of any restriction. Use it in commercial projects, modify anything you want in there and even claim it was all your idea, I have no problem with that :smileyhappy:.

 

Take care,

Patchou

 

PS: I have recently tested the same code in JDE 7 and I can confirm it still works fine.