So, I'm updating an old e-book reader. The problem I'm facing is the size of the bitmaps (the pages). The way it works is: the app downloads a file, extract it, decode it with our DRM then I can access all pages. The thing is, each page its a 35mb bitmap.
I need to reduce this by a lot. I can't convert the file to any other format.
I tried this:
byte[] rawBytes = intToByteArray(b.getByteCount());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bit = BitmapFactory.decodeByteArray(rawBytes, 0,rawBytes.length, options);
But the Bitmap keeps returning null, and for what I gathered its when the Bitmap facorty can't decode the bitmap.
So, there is any other way to reduce the impact of this big bitmaps ?
Related
I have a project on Android and iOS, there is a section where it shows a 360 view of a car, in iOS I just get the 60 images from internal storage that were previously downloaded from the internet, and these images are showed when the user starts to swipe the car to the right or to the left. And this process takes just 200ms to be ready to interact with the user.
But in Android I have to take first the 60 images and convert them in bitmaps, and then the process is similar to the iOS process. Nevertheless, this first process, convert all of them to bitmap takes about 3 seconds o more. I have read the process to show an image in Android in many sources of information and seems like is necessary use a bitmap for this task, so is possible to reduce the time to get all bitmaps?, or could I save a bitmap to avoid converting the PNG file again to a new bitmap when the users open the same view again?
The code that I'm using to convert png file to a bitmap object is the same that Android developers documentation recommend:
https://developer.android.com/topic/performance/graphics/load-bitmap
My source code for get bitmap is:
public static Bitmap decodeSampledBitmapFromFile(String filePath, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
options.inSampleSize = 1;
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bitmap =BitmapFactory.decodeFile(filePath, options);
Log.d("img360","bitmap res "+bitmap.getWidth()+ " h "+bitmap.getHeight());
return bitmap;
}
I'm really thankful for your help and support.
If you want to download images from Internet and display them on android
Just put an imageview and use picaso or glide library
It is faster and easier
I have 3 or 4 image paths that I use to load an image so I set it to an imageview. Why does it take long? Or better asking is there a way to make it faster? At the end of the day I am loading to fit an imageview of less than 60 dp hight and width
Uri mainImgeUri = Uri.parse(imagePath);
InputStream imageStream;
try {
imageStream = mActiviy.getContentResolver().openInputStream(mainImgeUri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap yourSelectedImage = BitmapFactory.decodeStream(imageStream, null, options);
mainImageIV.setImageBitmap(yourSelectedImage);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
USE CASE:
What happens is that a user will add 5 images (and he get to choose them from Gallery which is mostly taken by phone camera). He hit save and my app stores the path to them in an sqlite database. Then when the user opens the app again to see them, my app query the db to get the paths to all the images and executes the code above x number of times so all the image views are loaded with the intended images
Take a look at http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
It explains how to calculate the correct inSampleSize based on the required dimensions of the output image. It also explains how to reference large bitmaps without having to load all their pixel data into memory.
The idea is that you resample bigger images and only load the smaller ones into memory making the whole process much more efficient. The example code is accessing a bitmap from resources, but this can easily be modified for your needs.
The important things to look out for in the example are inJustDecodeBounds and calculateInSampleSize.
By following this link, I have written the following code to show a large image bitmap from sdcard.
try {
InputStream lStreamToImage = context.getContentResolver().openInputStream(Uri.parse(imagePath));
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
options.inSampleSize = 8; //Decrease the size of decoded image
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(lStreamToImage, null, options);
} catch(Exception e){}
image.setImageBitmap(bitmap);
But it is not returning the bitmap(I mean it returns null). In logcat it is showing the below message repeatedly
08-02 17:21:04.389: D/skia(19359): --- SkImageDecoder::Factory returned null
If I will comment the options.inJustDecodeBounds line and rerun it, it works fine but slowly. The developer guide link I provided above says to use inJustDecodeBounds to load bitmaps efficiently.
Please tell me where I am doing wrong.
inJustDecodeBounds does not load bitmaps. That's the point of it. It loads the dimensions of the bitmap without loading the actual bitmap so you can do any pre-processing or checking on the bitmap before you actually load it. This is helpful is you, say, were having memory issues and you needed to check if loading a bitmap would crash you program.
The reason your bitmap might be loading slowly is because it's probably very large and SD cards are very slow.
EDIT:
From the documentation:
If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.
Edit 2:
Looking at your code with the example provided by Google, it looks like you are doing relatively the same thing. The reason it's returning null is possibly your InputStream has been modified in the first decoding and thus not starting at the beginning of the bitmap's memory address (they use a resource ID rather than InputStream.
From the code you supplied here, here's what I've figured. You are ALWAYS setting a sample size to 8 regardless of what the first decoding gives you. The reason Google decodes the first time is to figure out what the actual size of the bitmap is versus what they want. They determine that the bitmap is ZxZ dimensions and they want YxY dimensions, so they calculate the samplesize that they should use from the second decoding. You are not doing this. You are simply retrieving the dimensions of the bitmap and not using them. THEN, you set the sample size to a hard-coded 8, swapping it to a hard-coded ARGB_4444 bitmap, then decoding the full bitmap in to memory. In other words, these three lines are not being used:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
Setting inJustDecodeBounds merely gives you the bitmap's dimensions without putting the bitmap in to memory. It doesn't make it more efficient. It's meant to allow you to load bitmaps in a smaller memory space if they are too big because you can pre-decide what size it should be without decoding the whole thing).
The reason decoding the bitmap is slow might merely be a CPU thing. Depending on the size of your bitmap, you're loading the bitmap from an InputStream from the SDcard which is a slow operation in itself.
I have a photo on disk with dimensions 2560 x 1920. This is often too large to load into memory, so I'm trying to use BitmapFactory.Options.inSampleSize to conserve memory. From the docs:
inSampleSize: If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
This is how I use it:
BitmapFactory.Options optsDownSample = new BitmapFactory.Options();
optsDownSample.inSampleSize = 3;
Bitmap bmp = BitmapFactory.decodeFile(path, optsDownSample);
but the app still sometimes crashes on the last line there, and from logcat I can see it's trying to allocate ~5mb, and I suspect this is because the downsampling is not really being honored.
Anyone else know what could be going on here, am I using inSampleSize incorrectly?
Thanks
I'm also struggling understanding how to use the BitmapFactory.Options, based on all the documentation I've read I believe you are just missing the optsDownSample.inJustDecodeBounds = false; as indicated on the Android Developers site.
Best of lucks!
I need to load lots of big images (500 .png files) from the SD card to my app. Do I always have to convert images to Bitmap and make Bitmap files? I don't want to resize the Heap.
Is there another way to read the images from SD card?
If you're displaying them in a view, then you have to load them into memory in their entirety.
You didn't mention how large your images will get, but what we do in our photo gallery is to keep a list of SoftReferences to these bitmaps, so that the garbage collector can throw them away when they're not visible (i.e. when the view displaying them gets discarded--make sure that this actually happens, e.g. by using AdapterView). Combine this with lazy loading of these bitmaps and you should be good.
The internal representation of the image in your app is a collection of bits and bytes - not an image of any specific format (png, bmp, etc).
The image is converted to this internal representation when the image is loaded by the BitmapFactory.
It is usually not a good idea to load all the bitmaps at once, you will quickly run out of memory...
If your image's dimension is very big, you must to resize them before loading in to ImageView. Otherwise, even one picture can easily cause out of memory problem. I don't know how many images you want to display concurrently and how big they are. But I suggest you to resize them before displaying them.
To resize image and show it, you can use this code:
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(fileInputStream, null, bitmapOptions);
//reduce the image size
int imageWidth = bitmapOptions.outWidth;
int imageHeight = bitmapOptions.outHeight;
int scale = 1;
while (imageWidth/scale >= screenWidth && imageHeight/scale >= screenHeight) {
imageWidth = imageWidth / 2;
imageHeight = imageHeight / 2;
scale = scale * 2;
}
//decode the image with necessary size
fileInputStream = new FileInputStream(cacheFile);
bitmapOptions.inSampleSize = scale;
bitmapOptions.inJustDecodeBounds = false;
imageBitmap = BitmapFactory.decodeStream(fileInputStream, null, bitmapOptions);
ImageView imageView = (ImageView)this.findViewById(R.id.preview);
imageView.setImageBitmap(imageBitmap);
In my android project, I am using this piece of code to resize my HD wallpaper to review it.
Android Save And Load Downloading File Locally