Saving large image to PNG - java

I've just encontered a tough problem...
Here's my story:
My device is Samsung Galaxy Note, normally, it takes pictures at a resolution of 3264 * 2448 pixels. I love fine pictures when am tring to find detailed information, but here I hate it because it's huge to handle in Android. I can read and display an image of this big resolution, I need to draw lines on top of it, then I need to save both picture and lines to a png.
My headache is that my Android always gives me Out of memery exception, when am trying to create a mutable bitmap like this:
Bitmap bmp = Bitmap.createBitmap(3264, 2448, Config.RGB_565);
Canvas cv = new Canvas(bmp);
//draw a line and save and restore canvas
// I found an alternative solution yet proven to be uncorrect:
myImageView.setDrawingCacheEnabled(true);
myImageView.buildDrawingCache(true);
Bitmap bmp = myImageView.getDrawingCache();
//save bmp to png
When the
bmp.width * bmp.height * 4 > getScaledMaximumDrawingCacheSize()
exception jumps out. I've tested myImageView.measure(...) and myImageView.layout(...), didn't work for me.
(Things are fine, when I test the same code using smaller images)
Any experts know how to kill this problem or throw a solution to me? Thanks!

Maybe not the definitve solution, but did you try to increase heap size?
For Android 2.2 or lower versions:
dalvik.system.VMRuntime.getRuntime().setMinimumHeapSize(sizeInBytes);
For Android 2.3 or or higher versions:
android:largeHeap="true"

Related

java lang RuntimeException:

so basically, i'm new to java and android studio. I know the basics but I'm not that good yet.
I get this error when I try to run an the app on my phone. Going through other threads didn't help me either as I basically have just one background image in the MainActivity. I have to add one more but when I do it and try to run the app, it crashes.
size of background image: 115kb
size of the image I still have to add: 164 KB (tried to compress it to 74Kb, didn't work.)
java.lang.RuntimeException: Canvas: trying to draw too large(430377192bytes) bitmap.
I saw this in another thread which was supposed to be put in the manifest which hasn't helped either:
android:largeHeap="true"
I hope I have provided enough information needed to answer the question, if you need more please tell me.
Again: I am new to this.
430377192 bytes is the equivalent of a 10372 x 10372 pixel image. This is much too large. Moreover, it is far larger than any Android device screen that you are ever likely to encounter.
So, find this drawable resource, and reduce its resolution to something more reasonable.
If you placed this drawable resource in res/drawable/, please understand that res/drawable/ is a synonym for res/drawable-mdpi/, representing images designed for -mdpi screens (~160 dpi). Those images will be upsampled to higher resolutions on higher-density screens (e.g., double along each axis for -xhdpi screens). Either prepare dedicated drawables for appropriate densities, or move this image into res/drawable-nodpi/.

Applying Effects to HD Images

I am developing an app that deals with images and applying filters, borders etc to the image..(like photo editing app).
I am facing issue when applying filters or borders to HD images (as images will be of different size and resolution).
My Question is how do I apply filters without changing the size or dimension of the Image.
Please help regarding this, Thanks in advance.
As I mentioned in the comment section of the Code, for smaller size images getDrawingCache is returning a value, but for the bigger size images it is returning null.
public Bitmap getBorderAppliedBitmap(PorterShapeImageView imgView , GPUImage gpuImage)
{
Bitmap bmp = gpuImage.getBitmapWithFilterApplied();
imgView.setImageBitmap(bmp);
imgView.setDrawingCacheEnabled(true);
imgView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
imgView.layout(0, 0,
imgView.getMeasuredWidth(), imgView.getMeasuredHeight());
imgView.buildDrawingCache(true);
Bitmap bmap = Bitmap.createBitmap(imgView.getDrawingCache());
<!-- getting null from the method imgView.getDraingCache() only for the bigger size Images -->
imgView.setDrawingCacheEnabled(false);
return bmap;
}
What you must be facing is limited Android resources issue for Image Processing techniques. The best advice for you would be to shift towards NDK and write image filters in JNI using OpenCV .
It is the best option for faster results in HD images. Any how there must be a boundary to the resolution to which an image can be processed because all professional apps create their own boundary parameters.

how can I clear a bitmap memory in real time?

I create 14 Bitmap objects like this:
bmp[0]=BitmapFactory.decodeResource(getResources(), R.drawable.a0000);
bmp[1]=BitmapFactory.decodeResource(getResources(), R.drawable.a0001);
bmp[2]=BitmapFactory.decodeResource(getResources(), R.drawable.a0002);
bmp[3]=BitmapFactory.decodeResource(getResources(), R.drawable.a0003);
bmp[4]=BitmapFactory.decodeResource(getResources(), R.drawable.a0004);
bmp[5]=BitmapFactory.decodeResource(getResources(), R.drawable.a0005);
bmp[6]=BitmapFactory.decodeResource(getResources(), R.drawable.a0006);
bmp[7]=BitmapFactory.decodeResource(getResources(), R.drawable.a0007);
bmp[8]=BitmapFactory.decodeResource(getResources(), R.drawable.a0008);
bmp[9]=BitmapFactory.decodeResource(getResources(), R.drawable.a0009);
bmp[10]=BitmapFactory.decodeResource(getResources(), R.drawable.a0010);
bmp[11]=BitmapFactory.decodeResource(getResources(), R.drawable.a0011);
bmp[12]=BitmapFactory.decodeResource(getResources(), R.drawable.a0012);
bmp[13]=BitmapFactory.decodeResource(getResources(), R.drawable.a0013);
those Bitmaps are used to make an animation of the background, the resources are 14 jpj images of 320x480 pixels. so I created a custom view and then call the bitmaps at the onDraw method. Then I test the app on my phone that have the same resolution of my images, it run smoothly, then I try to use the app on a tablet with a larger resolution(like 1280x720), but my background don't fit the entire screen, so I investigate how fill the entire screen by make a new bitmap with different resolution based on the device resolution and the result was this:
newWidth= context.getResources().getDisplayMetrics().widthPixels;
newHeight= context.getResources().getDisplayMetrics().heightPixels;
int contador=0;
do{
bmp[contador]= Bitmap.createScaledBitmap(bmp[contador], newWidth, newHeight,true);
contador++;
}while(contador<13);
Then I simply put these line on my onDraw method.
canvas.drawBitmap(bmp[i], 0, 0, null);
i++;
If I test the app on my phone it runs fine but when I test it on my tablet throws me the "bitmap size exceeds VM budget" error... So I investigate a little bit more and I had the following solution: Create the original 14 bmp Bitmap objects and make a second array of Bitmaps called bmp2 and then only assign the bitmap when I need it and then clear it like this on my onDraw method:
bmp2[i]= Bitmap.createScaledBitmap(bmp[i], newWidth, newHeight,true);
canvas.drawBitmap(bmp2[i], 0, 0, null);
bmp2[i].recycle();
bmp2[i]=null;
It work on my tablet and my phone but with a really bad performance I assume is because I assign the image in real time (on the onDraw method) but if I use the recycle method and don't assign again the bmp2 there will be not a new bmp2...
So finally my real question is: How can I clear some memory after I show my image and then when I need it again I can use it. Thanks!
Other have mentioned how scaling up small bitmaps to fill a large screen will not give high-quality results. But anyway, to answer your specific question, this is how you can draw your low-res bitmaps scaled up to fill the screen without memory errors.
You need to use one of the alternative Canvas.drawBitmap() methods which can scale your bitmap to the correct size while drawing.
For example:
public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint)
From the Javadoc:
Draw the specified bitmap, scaling/translating automatically to fill
the destination rectangle.
Set the dst rectangle to be the screen size you calculated earlier.
Then, you do not need to create separate scaled bitmaps in memory, so your problem goes away.
I am afraid there is no safe way of doing a fullscreen animation using scaled bitmaps.
Why?
Let's do some counting. Each your initial bitmap is 320x480, yes? That's 320*480*4*14=8601600, which is roughly 8Mb of memory. When you scale to 1280x720, you add up 1280*720*4*14=51609600 bytes, which is 49 megabytes. 49 + 8 = 57 megabytes. Given that the newest tablets have maximum of 64 megabytes allocated for your app VM, no surprise it won't fit. And even if it fits with your tablet, what would happen if a Nexus 10 owner would buy your app? N10 has 2560Ă—1600 resolution (not to mention that scaled bitmap would look pathetic).
Allocating single bitmap for single frame will hardly let you have a smooth animation, I'm afraid. You could try keeping a window of 5 bitmaps at once and recycling others, but still I don't thik it would be enough. Besides that would consume ton of CPU work for constant bitmap scaling and will destroy your battery.
If you really want an animated background, you should probably look at either OpenGL, or TextureView.
Most of the times the GC would do the job.
You should have different bitmap resolutions handled by Android by folder (xhdpi, hdpi, etc.).
You should also check BitmapFactory.Options in order to have lower quality version Bitmaps generated by your BitmapFactory signatures.
Edit
You might also want to consider using WeakReferences to your Bitmaps.
You should design new bitmaps for your Tablets and Other Higher Screens and put them in the xhdpi folder under the res directory. This is the most efficient option. Let The Android OS itself pull the best fitting Resource for you.
See this for more detailed help
http://developer.android.com/training/multiscreen/index.html
This is what the documentation says
Although the system performs scaling and resizing to make your
application work on different screens, you should make the effort to
optimize your application for different screen sizes and densities. In
doing so, you maximize the user experience for all devices and your
users believe that your application was actually designed for their
devices—rather than simply stretched to fit the screen on their
devices.

Rescaling png bitmap in android

So i have this image of a green ball in png format(103x104px is the size), and im trying to resize it before i draw it on the screen. Im trying this code, which i found in many threads and also in the android documentation this function is specified as exactly what i should need, but it doesnt work.
Bitmap a=BitmapFactory.decodeResource(getResources(), R.drawable.example);
Bitmap b=Bitmap.createScaledBitmap(a,30,30,false);
and then i draw it with
canvas.drawBitmap(b, x, y ,paint);
But no matter which numbers i give into the createScaledBitmap() the image is still the same on the screen.
Ok, so the solution was in the end pretty simple :D i overlooked one line in the code that i forget to erase when i decided to resize it and that line always backed it up, so this code indeed works. My fault, should have searched my code longer before posting.

Jagged edges on images rendered in Android

I'm currently developing my first Android app and am having some issues rendering images. The image itself is great quality to begin with, but upon rendering it the quality drastically lowers. Edges become jagged and it just looks poorly done. Everyone I've showed it to thus far has almost immediately noticed it, without any prompting about it. [start on left, end on right:]
I'm trying everything I am aware of and every tip I've been able to find by looking around online, but nothing seems to fix it.
Currently, I get the image as a Bitmap and scale it:
Bitmap holeImage = BitmapFactory.decodeResource(res, R.drawable.hole_image);
Bitmap holeImageBMP = Bitmap.createScaledBitmap(holeImage, width, height, true);
Once I have the image, I create a Paint, set a few smoothing attributes to true, and then draw it on the canvas:
Paint smoothingPaint = new Paint();
smoothingPaint.setAntiAlias(true);
smoothingPaint.setFilterBitmap(true);
smoothingPaint.setDither(true);
canvas.drawBitmap(holeImageBMP, 0, 0, smoothingPaint);
Yet, as you can obviously see above, the image quality drastically decreases. I've seen plenty of images being rendered beautifully and I'm honestly just not sure what's going on so any advice would be great!
Other notes: I'm using a SurfaceView method to handle the drawing, similar in nature to the LunarLander example given in the SDK.
Thanks again!
If you aren't restricted to much less colors than the original picture has (Does Android have 256 color modes?), I'd suggest to disable dithering, if you zoom into your picture, it does have a visible effect that perhaps destroys a smooth look.
I think in your case, dithering infers with anti-aliasing by destroying the additional colors that anti-aliasing needs for a smooth look. A quick color count on your pictures (left one about 850, right one about 140) confirms this.
That is probably related to converting images from one format to another. Also, android screens vary from device to device. Try to use another device and it might look better... Almost for sure it will have a different tone.
Try to read this great article on this problem (and banding and dithering) and consider adapting the image you created for it to work better in android devices: http://www.curious-creature.org/2010/12/08/bitmap-quality-banding-and-dithering/

Categories

Resources