Im developing an app that shows big images.. There are a lot of images which size is around 700x8100.. I can create an object of type EncodedImage, without throwing an exception, but when I try to execute getBitmap I receive an OutOfMemory error.
This is the line that occurs the exception:
Bitmap imgBtm = encodedImagePng.getBitmap();
Is there any kind of resolution size limit?
Does anyone already had to handle big images?
Any help will be very useful..
Tks
You have to resize the encoded Image before using the getBitmap() function. First construct your EncodedImage and then rescale it using the following:
private static EncodedImage rescaleEncodedImage (EncodedImage image, int width, int height) {
EncodedImage result = null;
try {
int currentWidthFixed32 = Fixed32.toFP(image.getWidth());
int currentHeightFixed32 = Fixed32.toFP(image.getHeight());
int requiredWidthFixed32 = Fixed32.toFP(width);
int requiredHeightFixed32 = Fixed32.toFP(height);
int scaleXFixed32 = Fixed32.div(currentWidthFixed32, requiredWidthFixed32);
int scaleYFixed32 = Fixed32.div(currentHeightFixed32, requiredHeightFixed32);
result = image.scaleImage32(scaleXFixed32, scaleYFixed32);
}
catch (Exception ex) {
}
return result;
}
And then after resizing get the Bitmap from it
You can't read images that are so large into device memory. There are built-in methods to decode an image while scaling it down, which will avoid loading the original image into memory. Try EncodedImage.scaleImage32 to set a more reasonable dimension before getting a bitmap.
Related
I am calling this class which is PopupScreen.
public class Custom_LoadingScreen extends PopupScreen {
private VerticalFieldManager vfm;
private Util_AnimateGifField anmtFldCycle = null;
private GIFEncodedImage gifImgCycle;
public Custom_LoadingScreen() {
super(new VerticalFieldManager());
Background bg = BackgroundFactory.createSolidTransparentBackground(
Color.BLACK, 190);
setBackground(bg);
setBorder(BorderFactory.createSimpleBorder(new XYEdges(),
Border.STYLE_TRANSPARENT));
gifImgCycle = (GIFEncodedImage) GIFEncodedImage
.getEncodedImageResource("LoadingSpinner.gif");
anmtFldCycle = new Util_AnimateGifField(gifImgCycle,
Field.FIELD_HCENTER);
vfm = new VerticalFieldManager(USE_ALL_WIDTH) {
protected void sublayout(int maxWidth, int maxHeight) {
super.sublayout(Display.getWidth(), Display.getHeight());
setExtent(Display.getWidth(), Display.getHeight());
}
};
int padding = (Display.getHeight() - 16) / 2;
if (padding > 0) {
anmtFldCycle.setPadding(padding, 0, 0, 0);
}
vfm.add(anmtFldCycle);
add(vfm);
}
//public void Popupscreen() {
//Main.getUiApplication().popScreen(this);
//}
public boolean keyDown(int keycode, int status) {
if (Keypad.key(keycode) == Keypad.KEY_ESCAPE) {
Main.getUiApplication().popScreen(this);
return true;
}
return super.keyDown(keycode, status);
}
}
In a button, I pushed it before goes to next screen.
financebtn = new Custom_ButtonField(finance, financeactive,
financeactive) {
protected boolean navigationClick(int status, int time) {
Main.getUiApplication().pushScreen(new Custom_LoadingScreen());
Main.getUiApplication().invokeLater(new Runnable() {
public void run() {
// Main.getUiApplication().popScreen();
Main.getUiApplication().pushScreen(
new Main_NewsDetail());
}
}, 1 * 1000, false);
return true;
}
};
add(financebtn);
The result give me Uncaught:ClassCastException. I can call another class which is similar to custom_loadingscreen also popupscreen. It work fine.
I also tried call this class in another button yet still same problem.
If you take a look at your Custom_LoadingScreen code, there's only one place where you are doing a cast:
gifImgCycle = (GIFEncodedImage) GIFEncodedImage
.getEncodedImageResource("LoadingSpinner.gif");
So, that's a good place to start looking. If you Google for "BlackBerry GIFEncodedImage ClassCastException", you'll find this thread:
http://supportforums.blackberry.com/t5/Java-Development/GIFEncodedImage-in-BlackBerry-OS7/td-p/1228959
The problem is that, for optimization, BlackBerry likes to convert images to the PNG format, which most smartphones work best with. So, what's happening here is that your GIF image is actually being converted to a PNG image. Therefore, when you call the getEncodedImageResource() method, the object you are getting back may actually be of type PNGEncodedImage, not GIFEncodedImage, and you get the exception. Sneaky, huh?
You can solve it a few ways.
In the Blackberry_App_Descriptor.xml file, you can uncheck the setting that specifies that images are converted to PNG (Build tab -> Convert image files to png)
You can trick the build system by renaming your GIF file to something like LoadingSpinner.agif. The toolset doesn't recognize the .agif extension, and therefore won't try to convert it. If you do this, of course, remember to change the filename in your Java code, too, when loading it.
You can change the code to use PNGEncodedImage, or test the object like this:
EncodedImage img = EncodedImage.getEncodedImageResource("LoadingSpinner.gif");
if (img instanceof GIFEncodedImage) {
// cast to GIFEncodedImage
} else if (img instanceof PNGEncodedImage) {
// cast to PNGEncodedImage
}
Number (1) will lose the non-PNG to PNG conversion optimization for all your non-PNG images, not just this one.
Number (2) does look a little ugly. The benefit of doing this, though, is that you can disable this behaviour for just this one image. If most of your images are not PNG images, it might be valuable to let BlackBerry optimize for you, for the other images. But, maybe this one needs to be a GIF. So, #2 lets you handle this one as a special case.
I'm just guessing that this image might be an animated GIF? Is that right? If so, you probably want to keep it as a GIF, so you won't want to do number (3), which lets it be converted to a PNG, and uses it as such.
How can I re-size the image stored in resource folder using code according to the display width and height of blackberry screen?
Firstly create a encoded image like this
EncodedImage ei = EncodedImage.getEncodedImageResource("res/helpscreen.png");
then pass this encoded image to the below function
EncodedImage ei1= scaleImage(ei,reqWidth,requiredHeight);
public EncodedImage scaleImage(EncodedImage source, int requiredWidth, int requiredHeight)
{
int currentWidthFixed32 = Fixed32.toFP(source.getWidth());
int requiredWidthFixed32 = Fixed32.toFP(requiredWidth);
int scaleXFixed32 = Fixed32.div(currentWidthFixed32, requiredWidthFixed32);
int currentHeightFixed32 = Fixed32.toFP(source.getHeight());
int requiredHeightFixed32 = Fixed32.toFP(requiredHeight);
int scaleYFixed32 = Fixed32.div(currentHeightFixed32, requiredHeightFixed32);
return source.scaleImage32(scaleXFixed32, scaleYFixed32);
}
this will give you a encoded image. then convert it to Bitmap using
BitmapField logoBitmap = new BitmapField(ei1.getBitmap());
To scale a Bitmap image try Bitmap.scaleInto(...). API Link.
Bitmap targetBm = new Bitmap(Display.getWidth(), Display.getHeight());
srcBitmap.scaleInto(targetBm, Bitmap.FILTER_BILINEAR, Bitmap.SCALE_STRETCH);
I'm drawing a Bitmap as a background for a custom field but it doesn't draw the bitmap in the entire field's area. I've resized the image too but it always leaves out some space to the right and bottom. This is what it looks like:
Here's some code for painting:
public int getPreferredWidth(){
return phoneWidth;
}
public int getPreferredHeight(){
return cellHeight;
}
protected void paint(Graphics g) {
Bitmap img = Bitmap.getBitmapResource("cell_bg.png");
img.scaleInto(new Bitmap(phoneWidth, cellHeight), Bitmap.SCALE_TO_FIT);//or other scaling methods
g.drawBitmap(0, 0, phoneWidth, cellHeight, img, 0, 0);//draw background
//other steps
}
It works fine if I set the Background as follows-
Background bg = BackgroundFactory.createBitmapBackground(Bitmap.getBitmapResource("cell_bg.png"),0,0,Background.REPEAT_SCALE_TO_FIT);
this.setBackground(bg);
but this way, the background image is not visible onFocus.
What am I doing wrong here?
The problem here is you've used scaleInto() incorrectly.
Try it like this:
Bitmap img = Bitmap.getBitmapResource("cell_bg.png");
Bitmap scaled = new Bitmap(phoneWidth, cellHeight);
img.scaleInto(scaled, Bitmap.SCALE_TO_FIT);//or other scaling methods
g.drawBitmap(0, 0, phoneWidth, cellHeight, scaled, 0, 0);// here draw the scaled image(You here draw the old one)
the scaled bitmap image is given as output, NOT the original image .
create an encoded image then pass this encoded image to function scaleImage()
EncodedImage ei = EncodedImage.getEncodedImageResource("res/helpscreen.png");
EncodedImage ei1= scaleImage(ei,Graphics.getScreenWidth(),Graphics.getScreenHeight());
public EncodedImage scaleImage(EncodedImage source, int requiredWidth, int requiredHeight)
{
int currentWidthFixed32 = Fixed32.toFP(source.getWidth());
int requiredWidthFixed32 = Fixed32.toFP(requiredWidth);
int scaleXFixed32 = Fixed32.div(currentWidthFixed32, requiredWidthFixed32);
int currentHeightFixed32 = Fixed32.toFP(source.getHeight());
int requiredHeightFixed32 = Fixed32.toFP(requiredHeight);
int scaleYFixed32 = Fixed32.div(currentHeightFixed32, requiredHeightFixed32);
return source.scaleImage32(scaleXFixed32, scaleYFixed32);
}
then get this bitmap
BitmapField logoBitmap = new BitmapField(ei1.getBitmap());
Use this method to scale the image in your code:
private Bitmap getScaledBitmap(String imageName, int width, int height)
{
try {
EncodedImage image = EncodedImage.getEncodedImageResource(imageName);
int currentWidthFixed32 = Fixed32.toFP(image.getWidth());
int currentHeightFixed32 = Fixed32.toFP(image.getHeight());
int requiredWidthFixed32 = Fixed32.toFP(width);
int requiredHeightFixed32 = Fixed32.toFP(height);
int scaleXFixed32 = Fixed32.div(currentWidthFixed32, requiredWidthFixed32);
int scaleYFixed32 = Fixed32.div(currentHeightFixed32, requiredHeightFixed32);
image = image.scaleImage32(scaleXFixed32, scaleYFixed32);
return image.getBitmap();
} catch(Exception e) {
return null; // unable to resize the image
}
}
I've managed to get images into a custom drawListRow:
public void drawListRow(ListField listField, Graphics graphics, int index, int y, int width) {
graphics.drawBitmap(0, (index) * listField.getRowHeight(), firstrowPostion, rowHeight, thing.image, 0, 0);
graphics.setFont(titleFont);
graphics.drawText(thing.title, firstrowPostion, y, (DrawStyle.LEFT | DrawStyle.ELLIPSIS | DrawStyle.TOP ), 250);
}
The first time though everything works perfectly but once I get to the bottom of the list and start to scroll up again, the pictures have disappeared.
Any suggestions?
Edit: I've figured out the second time through this code:
try {
InputStream inputStream = Connector.openInputStream(ImagePath);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
int i = 0;
while ((i = inputStream.read()) != -1) {
outputStream.write(i);
}
byte[] data = outputStream.toByteArray();
EncodedImage eimg = EncodedImage.createEncodedImage(data, 0,
data.length);
Bitmap image = eimg.getBitmap();
inputStream.close();
outputStream.close();
return ImageUtility.resizeBitmap(image, 70, 70);
} catch (IOException e) {
return null;
} catch (IllegalArgumentException ex) {
return null;
}
}
that InputStream inputStream = Connector.openInputStream(ImagePath); is throwing an IOException. I understand from here
that IO will be thrown under these conditions: but I don't know which is the cause:
1. more than one openInputStream() on single instance of fileconnection.
2. openInputStream() on already closed fileconnection.
3. openInputStream() on a directory.
again any suggestions?
I figured out it's best to just create a separate array of fully formed images and send both that and the thing array to drawlistrow, instead of trying to form and draw them as every row is drawn.
I am using below code to resize image in blackberry but it resulting into poor quality of image. Please advise on the same.
{
JPEGEncodedImage encoder = null;
encode_image = sizeImage(encode_image, (int)newWidth,(int)newHeight);
encoder=JPEGEncodedImage.encode(encode_image.getBitmap(),100);
}
public EncodedImage sizeImage(EncodedImage image, int width,
int height) {
EncodedImage result = null;
int currentWidthFixed32 = Fixed32.toFP(image.getWidth());
int currentHeightFixed32 = Fixed32.toFP(image.getHeight());
int requiredWidthFixed32 = Fixed32.toFP(width);
int requiredHeightFixed32 = Fixed32.toFP(height);
int scaleXFixed32 = Fixed32.div(currentWidthFixed32,
requiredWidthFixed32);
int scaleYFixed32 = Fixed32.div(currentHeightFixed32,
requiredHeightFixed32);
result = image.scaleImage32(scaleXFixed32, scaleYFixed32);
return result;
}
The default image scaling done by BlackBerry is quite primitive and generally doesn't look very good. If you are building for 5.0 there is a new API to do much better image scaling using filters such as bilinear or Lanczos.
You've probably already fixed your issue, but for other coders that need to write code that's compatible with the older API's you can also use the code from this post: Strange out of memory issue while loading an image to a Bitmap object
The thread is about android, but the image scaling math and logics are the same :)