10-16-2010
03:37 PM
- last edited on
12-23-2010
10:20 AM
by
MSohm
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:
Let me know if you find this useful, all comments are appreciated
.
Cyril
Solved! Go to Solution.
10-18-2010 12:29 AM
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.
10-24-2010
11:19 PM
- last edited on
12-23-2010
10:04 AM
by
MSohm
Thank you
. 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-transparen
Moderator Note: I have updated the code in the original post to match the latest version.
10-26-2010 01:45 PM
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.
12-17-2010 05:48 PM
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!!!
12-17-2010 06:13 PM
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?
12-17-2010 06:41 PM
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!!!!
06-20-2011 11:06 AM
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
08-09-2011 01:04 PM
Thanks for the excellent solution!
08-17-2011 04:49 PM - edited 08-17-2011 04:53 PM
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
.
Take care,
Patchou
PS: I have recently tested the same code in JDE 7 and I can confirm it still works fine.