I am using the following code to get a Bitmap image of the html webpage that is loaded in WebView, however getting crashes due to running out of memory. how do i avoid memory problems if it is directly converting the picturedrawable into a Bitmap.
Is there a way to load file directly into a compressed format lke jpg? or is there some other way to handle this problem?
using Bitmapfactory to downsize the image does not look like it is possible, because the Bitmap is already created so it would be too late to do anything about it.
wv = (WebView)findViewById(R.id.webView1);
wv.loadUrl("http://www.google.com");
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
Picture picture = wv.capturePicture();
HTMLBitmap = pictureDrawable2Bitmap(picture);
Toast.makeText(HTMLActivity.this, "bitmap " + HTMLBitmap.toString(), Toast.LENGTH_LONG).show();
Bitmap bmp = Bitmap.createScaledBitmap(HTMLBitmap, 500, 300, false);
// the imageView that is being set to the bitmap
imageOne.setImageBitmap(bmp);
private static Bitmap pictureDrawable2Bitmap(Picture picture){
PictureDrawable pictureDrawable = new PictureDrawable(picture);
Bitmap bitmap = Bitmap.createBitmap(pictureDrawable.getIntrinsicWidth(),pictureDrawable.getIntrinsicHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawPicture(pictureDrawable.getPicture());
return bitmap;
}
EDIT:
two possible solutions
1
one idea I just thought of is somehow creating the Image on external memory like the SD card and using BitmapFactory to drop the size if it is large, before putting on the internal memory, how to actually do that is another question. the bitmap would have to be originally created on the external memory.
2
or is the memory problem only caused by loading it into an imageView. In that case I can drop the image with BitmapFactory Options before loading it in the imageView.
Bitmap bmp = Bitmap.createScaledBitmap(HTMLBitmap, 500, 300, false);
Related
I Have a recycler view that shows multi bitmap from the SQLite database. When the number of bitmaps increases, recycler view scrolled with lag, In addition, The whole program slows down.
I save bitmap from a view to a database like this (convert bitmap to byte):
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getWidth(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, stream);
DatabaseHelper.updateData(id, stream.toByteArray());
And convert the byte to a bitmap in onBindViewHolder adapter like this:
byte[] byte = items.get(position).getImageByte();
Bitmap bitmap = BitmapFactory.decodeByteArray(byte, 0, byte.length);
holder.ivShot.setImageBitmap(bitmap);
And this is my fragment that initializing recycler view:
ModelArrayList = DatabaseHelper.getData();
Adapter = new Adapter(ModelArrayList, DatabaseHelper);
recycler.setHasFixedSize(true);
recycler.setLayoutManager(new GridLayoutManager(context, 2));
recycler.setAdapter(Adapter);
How I can fix this error?
I don't know how you can reduce the size of the bitmap for better performance, But I tested it with the Glide library and the lag issue was solved.
Glide.with(context).load("your byte")
.placeholder("default image")
.into(viewHolder.imageView);
Bitmap loading is a very sensitive and complicated topic, as bitmap size gets larger you will face more memory issues, and hence the lag.
Your best way to load bitmaps in recyclerview is to keep your bitmaps in Files and load them using some well known efficient libraries like Glide.
You can create a less memory-hungry version of the image called thumbnail.
Then pass the thumbnail to the Recycler-View. Android has an API for this
import android.media.ThumbnailUtils
Bitmap thumbnailBitmap = ThumbnailUtils.extractThumbnail(originalBitmap, width, height);
Make sure to get rid of the original Bitmap afterwards.
originalBitmap.recycle();
I'm trying to merge several png files during runtime and displaying the resulting Bitmap. But the performance seems bad (it takes around half a second to display the image). Are there are alternate methods to improve the performance?
All the images are Full Screen PNG images where some images have different transparent areas so the order of merging is important.
I have tried pre-loading the resources by decodeResource but I'm running out of memory after loading at around 40 PNG files into Bitmap files. Even though the PNG file is just 10KB when I decode it into a Bitmap its size is in couple of MB. I have hundreds of PNG files so pre-loading the resources is not possible and crashing the app due to memory exhaustion.
Sample code:
Bitmap background = decodeResource(context.getResources(), R.drawable.background);
Bitmap bmp1 = decodeResource(context.getResources(), R.drawable.png1);
Bitmap bmp2 = decodeResource(context.getResources(), R.drawable.png2);
Bitmap bmp3 = decodeResource(context.getResources(), R.drawable.png3);
Bitmap bmp4 = decodeResource(context.getResources(), R.drawable.png4);
Bitmap bmp5 = decodeResource(context.getResources(), R.drawable.png5);
Bitmap bmp6 = decodeResource(context.getResources(), R.drawable.png6);
Bitmap bmp7 = decodeResource(context.getResources(), R.drawable.png7);
Bitmap bmp8 = decodeResource(context.getResources(), R.drawable.png8);
Bitmap bmp9 = decodeResource(context.getResources(), R.drawable.png9);
Bitmap bmp10 = decodeResource(context.getResources(), R.drawable.png10);
Bitmap bmp11 = decodeResource(context.getResources(), R.drawable.png11);
Bitmap bmp12 = decodeResource(context.getResources(), R.drawable.png12);
Bitmap bmOverlay = Bitmap.createBitmap(background.getWidth(), background.getHeight(), background.getConfig());
canvas = new Canvas(bmOverlay);
canvas.drawBitmap(background, new Matrix(), null);
canvas.drawBitmap(bmp1, 0, 0, null);
canvas.drawBitmap(bmp2, 0, 0, null);
canvas.drawBitmap(bmp3, 0, 0, null);
canvas.drawBitmap(bmp4, 0, 0, null);
canvas.drawBitmap(bmp5, 0, 0, null);
canvas.drawBitmap(bmp6, 0, 0, null);
canvas.drawBitmap(bmp7, 0, 0, null);
canvas.drawBitmap(bmp8, 0, 0, null);
canvas.drawBitmap(bmp9, 0, 0, null);
canvas.drawBitmap(bmp10, 0, 0, null);
canvas.drawBitmap(bmp11, 0, 0, null);
canvas.drawBitmap(bmp12, 0, 0, null);
Drawable drawable =new BitmapDrawable(context.getResources(),bmOverlay);
ImageView image.setImageDrawable(drawable);
Any other clever techniques that I can use to solve this problem?
First things first, pngs and jpegs are compressed format of image and hence they are smaller in size but bitmaps, as name suggests, are mapping of each bit of the image and its size depends on the actual resolution of the image and hence a 10kb jpeg can turn into 10mb bitmap.
Now the solution part,
As you mentioned that your pngs have transparency and order of merging is important, you can make a method something like
public static Bitmap mergePng(Bitmap baseBitmap, Bitmap newBitmap){
Bitmap targetBitmap; //use target bitmap to store the result of merging
//your login to merge the newBitmap on top of base bitmap
newBitmap.recycle;
baseBitmap.recycle;
return targetBitmap;
}
if you dont want this method to be used from multiple classes, then make the target bitmap as global in your class and make the method as private void, this will save more memory.
And then call this method in a loop passing two Bitmaps at a time. This approach will help you to merge the Bitmaps in your desired order that too by having at most 3 instance of active Bitmaps at a time.
Do not create so many bitmaps at once from resource. On!y one. Then draw it. And recycle. Then take the next from resource, draw and recycle.
You should of course make a loop for it.
The file size of your png does not say all. Its the resolution of the image which determines the amount of memory needed for the bitmap.
A very peculiar problem seen when loading an image from the following URL:
https://codechef_shared.s3.amazonaws.com/sites/default/files/uploads/landing_page_banners/small-banner-updatedcook51.png
However I tried loading images from other sources and they loaded successfully. And there is no error reported!
I have used both picasso and volley's network image view for this URL and both are unable to load!
Picasso.with(context).load("https://codechef_shared.s3.amazonaws.com/sites/default/files/uploads/landing_page_banners/small-banner-updatedcook51.png")
.resize(200, 200).centerCrop()
.into(viewHolder.ivEventImage);
Try downgrade image if is of large in size with the method :
ImageView myImgView = (ImageView) findViewById(R.id.imageView);
Bitmap bMap = BitmapFactory.decodeResource(getResources(), R.drawable.picture);
Bitmap bMapScaled = Bitmap.createScaledBitmap(bMap, newWidth, newHeight, true);
myImgView .setImageBitmap(bMapScaled);
I use the following code. With large image files it returns null, small ones display. Does anyone know if there is a size limitation with BitmapFactory.decodeFile?
File imgFile = new File(path);
if(imgFile.exists())
{
Bitmap myBitmap=null;
myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
ImageView imageView=(ImageView)findViewById(R.id.imageView1);
if(myBitmap!=null) {
imageView.setImageBitmap(myBitmap);
}
else {
Toast.makeText(MainActivity.this, "NULL", Toast.LENGTH_LONG).show();
}
} else Toast.makeText(MainActivity.this, "NO FILE", Toast.LENGTH_LONG).show();
how large is it? how many MBs?
do you get an OutOfMemoryException because each mobile app have specific amount of memory (heap size) and a large bitmap might exceed this limit, so u get a null
EDIT:
unless the big bitmap by chance, is corrupted of some weird format that the app could not decode? try another large bitmap
From experience there is a limit and when you hit that resolution limit for openGL you will see the following type of error message:
Bitmap too large to be uploaded into a texture (4128x2322, max=4096x4096)
You may also get a OutOfMemoryException error like many have noted.
First of all you should do this:
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, options);
final int width = options.outWidth;
final int height = options.outHeight;
Now that you have the width and height, it's time to make an intelligent decision about how to rescale it.
I would at this point look at the full code example (really only two methods to copy/paste) from Google at Loading Large Bitmaps Efficiently.
I'm currently working on a steganogrpahy android app as a class project. I've created an object that will encode an image with in another image and return an encoded bitmap. This code is run in a seprate thread.
new Thread(new Runnable()
{
public void run()
{
Bitmap encoded_image = null;
Encryptor encryptor = new Encryptor();
encoded_image = encryptor.encode_image_in_image(
image_location,message_image_location);
}
}).start();
After encoding the bitmap I was passing the bitmap to a file browser activity that I've created to save the bitmap as a png image. This method works for smaller images however, when a large image is encoded and passed to the sub activity the application freezes and returns to the main activity.
private void pass_image_to_file_browser( Bitmap image )
{
Intent intent = new Intent(Encrypt.this,FileBrowser.class);
intent.putExtra( Intent.EXTRA_STREAM, image );
startActivity( intent );
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = this.getIntent().getExtras();
Bitmap image = bundle.getParacable(Intent.EXTRA_STREAM);
}
I was assuming a large bitmap was to large to send between activities using an intent so I decided to simply save the image in a temporary location and pass the image's location to the sub activity. Which then saves the png image where the user specifies and deletes the temporary image file.
private void save_bitmap( Bitmap image, String location )
{
FileOutputStream fileOutputStream = new FileOutputStream(location);
BufferedOutputStream buffered_output_stream = new
BufferOutputStream(fileOutputStream);
image.compress(CompressFormat.PNG, 0, buffered_output_stream);
buffered_output_stream.flush();
buffered_output_stream.close();
}
This solves the problem of sending a large bitmap from one activity to another however, has created a new problem that I haven't been able to solve. Both the temporary image that is saved before passing the file location to the sub activity and the image file after re saving the image using the file browser have both had their color changed slightly. This color change is unrecognizable to the naked eye but, when decoding the image it causes lots of problems.
One thought I had was that the Bitmap.Config was changing from ARGB_8888 to ARGB_4444 or RGB_565 however, after debug this is not the case. The bitmap is instantiated as an ARGB_8888 and saved as an ARGB_8888 bitmap and never changes in between. The code still works if I pass the entire bitmap to the file browser activity and I'm saving the bitmaps exactly the same in both places. I do not have any thoughts on what else could be causing this. I'm looking for suggestions on what else could be causing the problem. Sorry I was going to post images on the output in both cases however, stack overflow wouldn't let me at my reputation level Thanks.
Ok, after many long wasted hours worrying about the thread and the algorithm I was using to encode the the bitmap the problem was a little simpler. While decoding the image file that was to be encoded with a message I was using options.inPreferredConfig = Config.ARGB_8888; During debugging I was checking to make sure this didn't change to RGB_565. Although the bitmap object was loaded as ARBG_8888 the image file did not contain an alpha channel and thus even though the bitmap had a byte for the alpha level and would allow me to edit the alpha byte of a pixel through Bitmap.setPixel( x, y, color) the bitmap object never recognized that it had alpha values set. When compressing the bitmap compressed to RGB_565 since the object thought that there was no alpha channel. Somehow this problem was resolved by passing the bitmap to a sub activity and parsing it. I'm guessing when object was being recreated the alpha values I had set were recognized. To solve the problem with out passing the bitmap to a subactivity one must add an alpha channel after decoding a bitmap from a file. I found a function to do so here.
private Bitmap adjustOpacity( Bitmap bitmap )
{
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
dest.setPixels(pixels, 0, width, 0, 0, width, height);
return dest;
}
I'm not sure if there is a more efficient method
For anyone who stumbles upon it, like me, wondering why bitnap colors change once you write it out in PNG. I have found out that using bitmap.setPreMultiplied(false) before calling bitmap.compress(PNG, 100, fileout) preserves the color of your image if it has alpha channel values. Setpremultiplied prevents the pixels written onto disk from being multiplied by alpha channel, thus generating different pixel color values.