I am trying to run a sketch that is supposed to show images (png´s, between 100kb and 1,5mb in size, 55.4mb total) in a coverflow animation. it works with about 10 images, but using more I get a out of memory error. I am loading the images file names into an string array like so:
String[] names = {"00.jpg", "01.jpg", "02.jpg"};
and then they get loaded into the sketch like so:
covers = new Cover[names.length];
for (int i = 0; i < covers.length; i++ ) {
covers[i] = new Cover(names[i]);
}
initCovers();
covers class:
class Cover {
PImage img;
Cover( String name ) {
img = loadImage(name);
public void drawCover() {
beginShape();
textureMode(NORMALIZED);
texture(img);
vertex(-300, -300, 0, 0, 0);
vertex( 300, -300, 0, 1, 0);
vertex( 300, 300, 0, 1, 1);
vertex(-300, 300, 0, 0, 1);
endShape();
when I run the sketch, my ram (8gb) gets filled within seconds, and the sketch doesn´t even load, it just crashes. when I start the sketch with about 10 images, everything works fine ( bout 1,5gb of ram usage).
my question is: why is it using so much memory? is it normal? is there a way to make it run more memory efficient (e.g. freeup memory of images that are not currently displayed because we can only see about 3 images at once on screen).
EDIT: I think the problem is that in the cover class, each time it gets called a new PImage is created. could that be possible?
image size in memory: width * height * (color depth/8), so for my images (1575y1969, 24bit) that woul be 8,9mb. times 91 images: about 807mb of memory usage just for the images.
Now that I understand the use-case better, I recommend to change the entire approach.
The types of applications (e.g. seen above) that you are trying to emulate do not load the entire slew of images as soon as the app. is presented. Instead, they read the small group of images that they are going to present to the user first. As the user approaches the end of that group, the software flushes some of the images at the start of the sequence (if memory is tight) and loads some more at the end.
Try increasing the JVM heap space
java -Xmx1024m
(Yes, I know, 1gig is a 'little' excessive, but after some experimentation, this value can be trimmed down)
As #millimoose states, the images loaded by Java are uncompressed into memory when they are loaded, so even a small image of 100kb on disk can suddenly occupy mb's of RAM when uncompressed. It becomes even more complicated when you start dealing with the alpha channel as well.
The size of compressed images isn't a good guide to the memory requirements.
The size in pixels is better. For example a modern camera photo with 8 megapixel resolution requires at least 32mb of memory to represent. If you are manipulating images this size with swing, double or triple that, at least. It's easy to gobble up a lot of memory.
Also, Java's internal memory management isn't very good at dealing with chunks this size.
I would say try painting them to a component using a single temp Cover as a handle as opposed to having N-many objects hanging around? And if you need interactions with images after draw time just hold back some simple meta data about their draw positions and dimensions, then use click event x,y etc to look up a a given image to allow you to work with it.
Related
I would like to know if there are any optimizations that could be used to improve the speed when using a large quantity of bitmaps drawn on a screen.
I use a canvas which I load all my resources at the initialization and use createBitmap when I need to update the bitmap. I use ~10-15kb files with my Galaxy Note 3 and notice a lag (xxhdpi) when I reach around 20 bitmaps which gets nearly unusable around 35+.
I am using createbitmap constantly because the bitmaps use frame animation and matrix to rotate.
So far the only thing i've tried that i've noticed a difference is inBitmap which gives about 5-10% increase in the GC_FOR_ALLOC freed.
Anyone care to chime in on a good answer for what is better? I've heard flash AIR is a good choice to go with using cacheAsBitmapMatrix, but I would like a different option (just personal pref).
EDIT:
(rectf bounds = bitmap bounds)
matrix.setRotate(rotation, rectf.centerX(), rectf.centerY());
ship1 = Bitmap.createBitmap(ship1, 0, 0, ship1.getWidth(), ship1.getHeight(), matrix, true);
I think I understand my problem, I should be calling
canvas.drawBitmap(ship1, matrix, paint);
But in my onDraw method I am using
canvas.drawBitmap(ship1, srcRectf, dstRectf, paint); //srcRectf = null
I use dstRectf to move my bitmap around, but I suppose this can be replaced with setTranslate. I'll try it out, thanks Mehmet!
Bitmap stores the pixel data in the native heap*, not in the java heap, and takes care of managing it by itself. That would mean GC shouldn't give you any serious headaches.
The problem is probably constantly using createBitmap(), which is usually a really costly operation. This will make a disk IO call at worst, or a relatively big memory allocation at best. You would like to use it as little as possible, i.e. only when initially reading them from the disk.
Instead I advise you to use a Matrix in conjunction with a Canvas. Just change your Matrix and with each step repaint your Bitmaps with it.
EDIT:
*Only correct for Android 2.3.3 <-> Android 3.0
I am trying to write a SWT component, that is able to take and draw an instance of java.awt.BufferedImage. My problem is that SWT's Image and AWT's BufferedImage are incompatible: SWT components can't draw java.awt.Image, AWT/Swing components can't draw org.eclipse.swt.graphics.Image.
There are several approaches that try to solve this problem in other ways (which also may have some variations, but basically there are these two):
Convert between SWT Image and AWT BufferedImage
Swing/SWT Integration
They all have shortcomings and didn't satisfy my expectations:
The first approach, to convert an SWT Image to a BufferedImage, results in poor performance for large images due to the creation of a new RGB instance for every Pixel.
The second approach has several shortcomings in usability. See the "workarounds" at the end of the linked article.
This lead to the conclusion that I'd try my best to write a component (based on org.eclipse.swt.widgets.Canvas or org.eclipse.swt.widgets.Composite) which allows to draw a BufferedImage directly without any conversion of images.
My approach was to draw it pixel by pixel. Therefore I simply had to get an instance of GC, walk the source BufferedImage line by line, left-to-right and drawing the corresponding Color using GC.setForeground(Color color) and GC.drawPoint(int x, int y).
First, I created a new instance of Color for every pixel, which uses quite a lot of memory and adds an additional delay, since new Color acquires system resources and creating a new object for every pixel also takes its time.
Then I tried to pre-load all possible (24 bit) Colors into an array before drawing the image. This lead to an explosion of memory usage (>= 600 MB), which was clear before I was trying it, but I had to verify it.
Caching only the used Colors also lead to more memory consumption than would have been required.
I think there has to be a more low-level approach that doesn't require that much memory, since SWT is able to draw whole (SWT) Images without consuming that much memory.
I would appreciate any ideas or solutions.
I found out there's a way to "convert" an BufferedImage to an Image by using the original image's data buffer if it is 24 bit RGB. This is possible, since the image formats are compatible.
final BufferedImage original = ImageIO.read(new File("some-image.jpg");
final PaletteData palette =
new PaletteData(0x0000FF, 0x00FF00, 0xFF0000);
// the last argument contains the byte[] with the image data
final ImageData data = new ImageData(original.getWidth(), original.getHeight(),
24, palette, 4,
((DataBufferByte) original.getData().getDataBuffer()).getData());
final Image converted = new Image(getDevice(), data);
This way, one doesn't have to create thousands of new objects. This approach comes with the disadvantage that one needs to ensure that the original image is of type RGB 24 bit. Otherwise the image has to be converted to this format.
After that, an image can be drawn with the following code:
// get the GC of your component
gc.drawImage(image, 0, 0);
Probably other bit depths can be converted in a similar way, but this is all I need for the moment.
I have an application that need to show a very large image (from a png on disk) with only a small part of the image is visible on screen at any time. However the visible section can move quickly around the large image.
Loading the whole image into a BufferedImage at once is not a good idea as it can be 10 000 - 100 000 pixels wide, but the size on disk is not very large (a few MB perhaps) so it is a question of loading only the relevant sections to be displayed.
I've tried creating an ImageReader like this:
FileImageInputStream is = new FileImageInputStream(imageFile);
ImageReader imageReader = ImageIO.getImageReaders(is).next();
ImageReader.setInput(is, false, true);
ImageReadParam readParameters = imageReader.getDefaultReadParam();
And then a method for getting a subimage something like this:
private BufferedImage loadFrame(int x, int y, int w, int h) {
readParameters.setSourceRegion(new Rectangle(x,y,w,h));
try {
return imageReader.read(0, readParameters);
} catch (IOException ex) {
return null;
}
}
This works in principle, but it is far too slow. So when moving fast around the image it lags way too much.
I also tried splitting up the source image beforehand so I had a bunch of smaller images on disk that I then loaded as needed using ImageIO.read(getImageFile(x,y)) where getImageFile(x,y) would return the appropriate File for that location. This method was actually much faster and fully usable.
So I guess I have a way to make this work, but it just seems a bit awkward to do it this way. Besides needing some preparation of the source image, it also requires a lot of disk access (although I guess this is probably buffered somewhere).
So my question is: What would be the best way to do this? (And why is it faster to load an image from disk than to load a part of an image from an ImageReader?)
PNG is a compressed format, you can't just open the file and seek to a specific location to start reading the region (like you can with a bitmap ~after reading the file header of course). The whole PNG needs to be loaded (parsed/decompressed) before you can start extracting regions of it. (http://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header)
If you want to sacrifice disk space to improved memory (RAM) usage and performance...
You can divide the image up and load only those grid chunks that you need to build the view for the user.
1x1.png, 1x2.png, 2x1.png, 2x2.png - if the user is looking at the top left corner you only need to load 1x1.png etc etc.
You can convert the image to a bitmap BMP the image will be much larger on the disk, but you'll be able to extract specific regions of it without having to process the whole file.
I want to load images with big size, but i have problem to make it. it Crashes, because OutOfMemoryError. here is my array :
Integer[] img = {
R.drawble.pic1, R.drawble.pic2, R.drawble.pic3, R.drawble.pic4
};
how to convert img[0],img[1],img[2] and img[3] to bitmap?
Please help.. Sorry my bad english.
Try this code, you'll convert an array of ids into an array of Bitmap:
Integer[] img = { R.drawble.pic1, R.drawble.pic2, R.drawble.pic3,
R.drawble.pic4 };
Bitmap[] bitmap = new Bitmap[img.length];
for (int i = 0; i < img.length; i++)
bitmap[i] = BitmapFactory.decodeResource(getResources(), img[i]);
If you have problems to convert big size image take a look at Displaying Bitmaps Efficiently.
Check here: http://developer.android.com/training/building-graphics.html
and the class BitmapFactory: http://developer.android.com/reference/android/graphics/BitmapFactory.html
Android apps have a limited heap size (minimum on old devices is as little as 16MB). This varies per device, so be careful during QA. Since a bitmap can take as much as 4 bytes per pixel, large bitmaps can easily fill up your heap (1000 x 1000 pixels = 3.8MB)
What this means is that you have to be very careful and use several tactics to avoid wasting memory:
Don't load all the bitmaps at the same time to memory. If you're showing them for example in a ViewPager you can load only 1-2 pages at a time. When a page is hidden, destroy its bitmap and call bitmap.recycle() to make sure the memory is freed immediately.
If the bitmap is larger than your screen size, you can scale it down to save memory. The technique here will let you load it initially reduced by 2/3/4.. If you need more fine-tuning, you can load it full size and then rescale using Bitmap.createScaledBitmap. Just don't forget to recycle() the original large bitmap immediately.
Make sure to explore your app's memory usage by using memory inspection tools such as DDMS or Eclipse MAT. I recommend to do most explorations on Android 4+ since 2.x saved bitmaps in the native heap which can't be explored easily. If you can keep your app under 16MB peak memory consumption, you should never hit an OutOfMemoryError again.
I'm trying to load a background image for a game as well as some smaller images, placing them on a Canvas, and letting people interact with the smaller overlayed images (like move, rotate)
In order to maintain aspect ratio (e.g. 5:3) I tried loading in the images as a bitmap and resizing them myself. The idea was to do cropping/letter-boxing for the background according to the canvas's width/height, and maintain the correct ratio of size for the smaller images.
Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), resourceImg);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg2, 0, 0, width, height, matrix, true);
In order to cater for tablets/phones i have a background PNG background image at 1600x1000 and 200kb.
However I am now struggling with out of memory issues due to the bitmap being 1600x1000x4byte=6.4 mb of ram and more when it tries to resize.
(I am using the emulator at the moment when these issues occur)
I decided to change it to use canvas.setBackgroundResource
SceneCanvas sceneCanvas = (SceneCanvas) findViewById(R.id.the_canvas);
sceneCanvas.setBackgroundResource(R.drawable.my_scene_1600x900);
This works well, except it fills the screen and does not maintain aspect ratio.
Is there a way to set the background maintaining aspect ratio? Or have I just gone down the wrong route completely and should use ImageViews and render to the canvas somehow to avoid OutOfMemory issues
Given that Java code is only allowed a heap size of around 20MB or so, you’re always going to have trouble with large bitmaps (unless you resort to native code in C/C++/etc).
One option is to use a BitmapFactory to load your image, and in the Options you can specify an inSampleSize to downsample the image as it’s being read in. This should avoid chewing up memory by trying to load the entire original image. There is even an inJustDecodeBounds option, so your code can check the dimensions of the image, instead of having them hard-wired into the code.
It seems that the memory limit on Android is somewhere between 16 - 24 MB memory (depending on device). This is regardless of whether the device has a lot more memory. Also, the memory used by Bitmaps is included in the limit, resulting in lang.OutOfMemoryError: bitmap size exceeds VM budget. After some searching, there are 3 options I could find:
Allocate memory from native code using the NDK (native development kit) and JNI
For images one can also use OpenGL textures, the texture memory is not counted towards the limit.
take advantage of certain bitmap options to reduce size; http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html
To see how much memory your app has allocated one can use, android.os.Debug.getNativeHeapAllocatedSize().