Handling Large Bitmaps in Image Editor - android

For the program I am currently writing I need a simple image editor. Essentially the user navigates to this editor by simply selecting an image from a gallery. Upon selection the image editor activity is created and should allow the user to perform simple editing actions such as rotation, brightness adjustment, zoom etc.
At the moment I have managed to implement the aforementioned functionality with relative ease. My problem lies in dynamically adding the image in question to the ImageView. As many of you might know; the Binder transaction buffer has a limited fixed size, currently 1Mb, which is shared by all transactions in progress for the process. Thus I am only able to load compressed versions of the bitmap into my ImageView and this presents a rather large problem for me (my program is mainly designed for use on tablets). I have done significant research on this issue and have found that one can essentially split a Bitmap into several smaller bitmaps and place them in several ImageView to create the illusion of one contiguous image using BitmapRegionDecoder(as suggested in this thread). While this has successfully allowed me to display large images I have no idea how I am supposed to implement zooming functionality using multiple instances of ImageView. Is there a relatively simple way of going about doing this?

Have a look at this video from Google I/O where they develop an Advanced gallery app with image editing.
You can download source code for the application here. This is how it opens the activity for image editing:
private OnItemClickListener mPhotoClickListener = new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// User clicked on photo, open our viewer
final Intent intent = new Intent(AlbumActivity.this, PhotoActivity.class);
final Uri data = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
intent.setData(data);
startActivity(intent);
}
};
The gallery also implements image editing functionality. The code might be helpful.

Did you try this? options.inJustDecodeBounds should be set to true.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Related

Loading Bitmap Efficiently in Android

I have dug insanely deep into this topic and found out a lot of options that makes the bitmap loading easier. But what I want to achieve is a lil far-fetched. I have referred this [video] which helps in loading the bitmaps efficiently. My question is unique cause I am able to load n number of bitmap images without any glitches but the problem is that it loads the SAME image1.
CODE :
for(int i=0;i<10;i++)
{
...
in = httpConn.getInputStream();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inJustDecodeBounds = false;
options.inBitmap = image;
options.inSampleSize = 1;
image = BitmapFactory.decodeStream(in,null,options);
item = new RowItem(image, product_name, cost,product_id, dream_status,username); --> item is an object of holder class which has get and set methods
rowItems.add(item); --> this is an arraylist object of type holder class
}
Here I have a loop that loads 10 images from WebService, and I am setting it in a adapter at onPostExecute of an async task. When I loop through the 10 images, what happens to me is that, only the last image(10th image) gets set in the view and it gets repeated for all the 10 images. This is due to the following line :
options.inBitmap = image;
which recycles the bitmap. My question here is that, how can I use the inBitmap function and set all the images ? I mean the logic or loop that would let me set not only the 10th image but all the other images. Any help would be highly appreciated guys.I'll be happy to help if you guys have any queries.
NOTE : I am well aware of the fact that inSampleSize should always be set in powers of 2 but setting it to 2 reduces the size of my image and setting it to 1 is what meets my expectation.
I suggest creating a BitmapPool class to smarten up the way you cache and re-use bitmaps. In your example, this pool could start off with a size of 10, and will cache 10 bitmaps. The pool size can grow or shrink depending on the implementation.
you may request a bitmap from this Pool, via getBitmap(), and
Bitmap obtained via getBitmap() call can be used as "inBitmap" while decoding the image
you may return the bitmap back to the pool once you are done using/drawing its contents via returnBitmap()
You can extend the use of BitmapPool to facilitate other bitmap requests in your app.

Android- BitmapFactory.decodeFile makes my app slowly

I'm writing app with ActionBar Tabs and ViewPager. I have two tabs and two pages. First page is just Fragmet and second is ListFragment.
In first page i'm inverting infromations about plat like name, interval between water, photo etc. I can add default photo (from resources) and take photo of plant. Then i save photo's data in database like String in two ways: id of image from resources or path to saved photo.
In second page (ListFragment) is ListView with custom adapter and there is the problem.
One row of list contains two TextViews, CheckBox and ImageView.
In method getView of my custom adapter every line works fine, but decoding file to bitmap takes lot of time and it makes my app alowly, even after decoding all photos.
It's my code to set up an image in each list row:
private void setUpImage(int position, ImageView photo)
{
if(plants.get(position).getPhoto().contains("jpg"))
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 10;
Bitmap bitmap = BitmapFactory.decodeFile(plants.get(position).getPhoto(), options);
photo.setImageBitmap(bitmap);
}
else
{
photo.setImageResource(Integer.parseInt(plants.get(position).getPhoto()));
}
}
Do anyone know how to improve speed of decoding and seting image in each list row of my ListView?
First of all! Setting options.inSampleSize = 10; is much greater. Normally, inSampleSize is provided in power of 2. And setting it equal to 2 or 4 is ok.
You can decode your file in background thread or you can use Lazy Loading techniques.
You can get Lazy Loading code here. See this answer.

Given a full path of an image file, how to get its thumbnail?

It seems the answers I searched online (including stackoverflow.com) get the image file id through gallery selection.
I have created my own file explorer.
Then how to do that?
I can create my own small size image; but I think it would be faster if we can make use of an exisiting thumbnail; and if it does not exist, I would prefer to create a thumbnail that is saved for later use.
[Update:]
OK, thanks for advice. So I will not create a thumbnail in the device, to avoid to use too much space.
Then is is better to do two steps:
Step 1: look for an exisiting thumbnail for the image file if it exists.
Step 2: if no thumbnail exists, then create my own small size bitmap (not save the it).
Then how to do Step 1, if I do not use the Gallery intent?
[Update 2:]
I also want to get the thumbnail of a video file.
I can use MediaMetadataRetriever to get a frame at any point of time, and rescale the image to a thumbnail. But I find it is very slow: I have 4 video files in the folder, and I can sense the delay.
So I think the better way to retrieve an existing thumbnail.
If I only know the file path and file name, how can I get it?
I think this question is the same as my original one, just it has more sense to do so.
You shouldn't be using specific files for tumbnail, especially not creating tumbnails. What if the user has a lot of images and you store a tumbnail of each picture which gets viewed in your explorer. That would generated a whole lot of duplicated and unwanted data. The calculations from resizing the images each time overweighs the amount of data that would need to be stored.
I would suggest you have a default icon on images in the explorer and then resizing the images in a diffrent thread, replacing your default tumbnail as they are resized.
You could downsize the existing images on the fly:
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(image_path, opts);
int width = opts.outWidth;
int height = opts.outHeight;
then
opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
opts.inDither = true;
opts.inJustDecodeBounds = false;
opts.inSampleSize = (int)Math.pow(2.0,Math.floor(Math.log(scale_factor)/Math.log(2)));//for example double scale_factor=(double)width/desired_dimension;
and finally:
Bitmap bm = BitmapFactory.decodeFile(image_path,opts);
You could load in a separate thread ranges of existing files and update only those ones when needed.
You can use ThumbnailUtils. Look up the this utility method. Probably fits your need without much hassles. Creating duplicate downsized images is a bad design as that it will end up unnecessary data.
Bitmap resizedImage = ThumbnailUtils.extractThumbnail(rescaledImage, imagePixel, imagePixel);

Android ListView showing images results in out of memory

Okay,
I have what I think is an odd problem.
I load photos into a list view as the user scroll (meaning if picked up from disk or online, it takes a little time and they "pop in")
1)
I ensure that I only fetch one image online + resize to 1/3 width at a time (a lock) -- this is to ensure not e.g. 5 threads each are converting a .jpg to bitmap at the same time. (Imagine a user scrolling very fast, it would then be conceivable this could happen.)
2)
All images on disk are only 1/3 width
3)
While the bitmaps are scaled up to fit device width, I call recycle and set bitmaps to null when used in the getView. (I am converting bitmaps to a BitmapDrawable object which the listitem imageview uses in a setImageDrawable call)
4)
I reuse view in getView if not null
5)
I load bitmaps like this:
BitmapFactory.Options o = new BitmapFactory.Options();
o.inPurgeable = false; // I do not use this since I resize/scale down images myself later
o.inInputShareable = false;
o.inPreferredConfig = Config.RGB_565;
res.bitmap = BitmapFactory.decodeStream(is, null, o);
6)
I also clean scrap items
public void onMovedToScrapHeap(View view) {
final TextView description = (TextView)view.findViewById(R.id.listitem_news_content);
description.setText("");
final ImageView image = (ImageView)view.findViewById(R.id.listitem_news_image);
image.setImageBitmap(null);
}
I am testing on a Samsung Galaxy II. Do you guys have any ideas on what more I could try? Since I only max need to show 10 items, I would think it should be possible...
Try using widely used library Universal Image Loader https://github.com/nostra13/Android-Universal-Image-Loader
It is simple and straight forward and you will never have to care about decoding images yourself.
Option two: There's a powerful new API for loading image is available from the team of Square http://square.github.io/picasso/
Try this, it worked for me in case of OutOfMemory...using System.gc()
bitmap = null;
imageView.setImageBitmap(null);
System.gc();

How to implement an image gallery (local images)

Currently im making an app to show images from SD card. Basicly you make an album and add pictures to it, be it from camera of the MediaStore picker.
I tried implementing 2 methods:
The standard gallery app with a custom BaseAdapter to return a view
A viewpager with custom PagerAdapter
I dont want to display a grid view, so it should go fullscreen right away. After that i want to disable swipe left and right and only listen for clicks.
Atm both methods work in portrait mode. When i switch over to landscape some images just drop
03-20 12:20:56.515: W/OpenGLRenderer(17398): Bitmap too large to be uploaded into a texture
followed by Out of memory errors. Stack overflow if full with problems about the OOM and the gallery, you should recycle the views to make them work because convertView is always null in the getView from BaseAdapter.
So i used a recycler for the views, i limit it to 2 views and portrait mode worked for method 1 (using the gallery). Landscape still gives me the same problem.
For method 2 (viewflipper) it handles the views by
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Wich is never called btw... However portrait mode works here. Landscape still crashes.
My method for getting the bitmap:
public static Bitmap getBitmap(Context ctx, int imageId, ImageView target) {
String file = getPath(ctx, imageId);
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, bmOptions);
WindowManager mgr = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
int scaleFactor = 1;
if (mgr != null) {
// Get the dimensions of the View
int targetW = mgr.getDefaultDisplay().getWidth();
int targetH = mgr.getDefaultDisplay().getHeight();
Log.d(TAG, "Image width + height=" + targetW + "," + targetH);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
scaleFactor = Math.min(photoW / targetW, photoH / targetH);
} else {
Log.d(TAG, "Target is null");
}
// Get the dimensions of the bitmap
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
logHeap(ImageHelper.class);
Bitmap bm = BitmapFactory.decodeFile(file, bmOptions);
if (target != null) {
target.setImageBitmap(bm);
}
return bm;
}
Works fine, i know i use the window manager to get a screensize but that is because my ImageView is still size(0,0) when i inflate it. Afterwards i call
imgView.setLayoutParams(new Gallery.LayoutParams(
Gallery.LayoutParams.FILL_PARENT,
Gallery.LayoutParams.FILL_PARENT));
imgView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
Nothing seems to work...
Please note that i dont use drawables or any other resources included in the APK.
This gallery should be able to load pictures from SD card or from camera in whatever quality they were taken. Obvously the gallery should be able to handle as much images as there are in the directory.
Could someone please help me out with the following questions?
Is there any way to make the default Gallery go fullscreen right away and block out the grid view? This way i only need an adapter to provide images instead of making my own views. (maybe this solves the OOM crashes)
Is my Bitmap decoding function ok? Do i need to built in some hacks to catch the landscape changes?
Whats the best way to make the kind of gallery i need, using a viewpager or the gallery?
Does anyone have some sample code of a fullscreen gallery that doesnt crash?
This is an issue i faced a few times in the past. Google actually posted an article about this.
The problem is in the inSampleSize that u use to decode the image. Since different devices have different screens and memory sizes (VM Heap Sizes that may go from 12 to 64 or more Mb) you cant decode the images the same way in all of them. For example, an image in a 320x240 device with a 12Mb Heap Memory should u a given inSampleSize while a 1280x720 64Mb Heap Memory device should user another inSampleSize (a bigger one).
The article i attack shows an efficient way to render the images for a given height and width. Take a special attention to the last code segment, they "pre-open" the file and calculate the inSampleSize for a "full-decode" later on.
I would also like to add this recommendations.
Use the "getBitmap" function in an async way, call a new thread or an asyncTask (this is done because using the SD takes time and this will cause your application to hang, to be slow and give a bad impression on low-mid end devices.
Try to always use the same Bitmap object. Avoid creating multiple "Bitmap bitmap = new Bitmap()". You have to realize that loaded bitmaps are stored in ram and once assigned to a view its "harder" for the garbage collector to get them.
When you have finished using a Bitmap over make it null. This of this, imagine your have 12Mb max heap RAM. your app is using 8Mb now. Garbage collector starts and cant get any more unused RAM. you 10 Bitmaps fast never make them null after assigning the data to the ImageView and somehow you retain their reference. Taken that each bitmap take 500Kb of Ram your app will crash. Always make Bitmaps null after using.
Finally use the DDMS provided in the google platform-tools (you can access it with the eclipse to debug and see your memory usage, you can also force Garbage Collection to see how your app behaves).

Categories

Resources