I'm intending to display very large Images in Android. My first solution - to supply them as pdf - fails because not every handheld got a pdf-viewer preinstalled, and I don't want to require the users to install one.
So I have a png now (width = 3998px height=2827px) that I want to display. I downloaded this image to test how it would be displayed the gallery. It was quite painful. It seems that the galery renders this picture only once, and if I Zoom in, I cannot read the text at all.
So I wrote a testActivity which simply has an ImageView nested in a LinearLayout. I put the image into the drawable and set it as ImageView's image-source.
Unforunately the app crashes immediatly, due to an "
ERROR/AndroidRuntime(8906): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget"
I didn't expect that ONE single Image can be too large forVM's memory. I played a little bit around, set ImageViews size to 3998 & 2827px , put the Image to sdCard and read it manually with a fileInputStream.
To my big surprise it now shows my image, but if I turn my Nexus S horizontal I get the same OutOfMemoryError as before.
Can somewone point me the main difference between recieving a Bitmap through a FileInputStream or to set it as ImageView's source.
Also I'm not able to scroll comfortable with two parent scrollViews
I searching for a simple solution to display ONE large image at a time with the ability to scroll horizontal and vertical while able to zoom in and out.
here is a sample of the image I want to display
I know it's an old post but I spent a lot of time on this problem, so here's my solution.
I wanted to display a 2000×3000 picture but I got out of memory or the image was too large to be displayed.
To begin, I get the dimensions of the picture:
o = new BitmapFactory.Options();
o.inJustDecodeBounds=true;
pictures = BitmapFactory.decodeStream(new FileInputStream(f), null, o);
Then I cut it up into four parts and displayed them with four ImageViews.
I tried to load the full picture and cut it into four (using BitmapFactory.create(bitmap,int,int,int,int)) but got out of memory again.
So I decided to use some BitMapRegionDecoder:
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
ImageView iv = new ImageView(this);
InputStream istream = null;
try {
istream = this.getContentResolver().openInputStream(Uri.fromFile(f));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
BitmapRegionDecoder decoder = null;
try {
decoder = BitmapRegionDecoder.newInstance(istream, false);
} catch (IOException e) {
e.printStackTrace();
}
int nw = (j*width/k);
int nh = (i*height/k);
Bitmap bMap = decoder.decodeRegion(new Rect(nw,nh, (nw+width/k),(nh+height/k)), null);
iv.setImageBitmap(bMap);
}
}
This worked.
I know its an old question but I used TileView to do exactly this:
https://github.com/moagrius/TileView
Try to use this one:
https://github.com/davemorrissey/subsampling-scale-image-view
A custom image view for Android, designed for photo galleries and
displaying huge images (e.g. maps and building plans) without
OutOfMemoryErrors. Includes pinch to zoom, panning, rotation and
animation support, and allows easy extension so you can add your own
overlays and touch event detection.
You can use this "easy to integerate" source of WorldMap application:
https://github.com/johnnylambada/WorldMap
This uses a huge image of a world map, and uses cache to display a map.
To integerate, I just copied all the java files (5 i guess) and used the surfaceView in my layout file. Then I went through the small OnCreate() method of ImageViewerActivity.java and used the code in my activity (with sligh alteration, depending on my personal use).
This post is a good demo for zooming a picture using multi touch gestures. It uses Matrix to zoom as well as pan a picture.
To handle scaling etc and using full resolution, You can use MapView of OSM (open street map) and provide it with your tiles (instead of the map). Check this: http://www.haakseth.com/?p=30
We have to follow following steps to remove out of memory exception while loading huge images:
1. Read Bitmap Dimensions and Type
2. Load a Scaled down version into memory
Android Developer's Guide defines how we achieve these.
here is the link
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap
Related
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();
Is there any way to get a high resolution screen shot of a certain view in an activity.
I want to convert html content of my webview to PDF. For that I tried to take screen shot of the webview content and then converted it to PDF using itext. The resulted PDF is not in much more clarity.
My code:
protected void takeimg() {
Picture picture = mWebView.capturePicture();
Bitmap b = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
picture.draw(c);
// byte[] bt = b.getNinePatchChunk();
// Bitmap b;
// View v1 = mWebView.getRootView();
// v1.setDrawingCacheEnabled(true);
// b = Bitmap.createBitmap(v1.getDrawingCache());
// v1.setDrawingCacheEnabled(false);
FileOutputStream fos = null;
try {
File root = new File(Environment.getExternalStorageDirectory(),
"Sample");
if (!root.exists()) {
root.mkdir();
}
String sdcardhtmlpath = root.getPath().toString() + "/"
+ "temp_1.png";
fos = new FileOutputStream(sdcardhtmlpath);
// fos = openFileOutput("samsp_1.jpg", MODE_WORLD_WRITEABLE);
if (fos != null) {
b.compress(Bitmap.CompressFormat.PNG, 100, fos);
// fos.write(bt);
fos.close();
}
} catch (Exception e) {
Log.e("takeimg", e.toString());
e.printStackTrace();
}
}
protected void pdfimg() {
Document mydoc = new Document(PageSize.A3);
try {
File root = new File(Environment.getExternalStorageDirectory(),
"Sample");
if (!root.exists()) {
root.mkdir();
}
String sdcardhtmlpath = root.getPath().toString() + "/";
mydoc.setMargins(0, 0, 0, 0);
PdfWriter.getInstance(mydoc, new FileOutputStream(sdcardhtmlpath
+ PDFfilename));
mydoc.open();
Image image1 = Image.getInstance(sdcardhtmlpath + "temp_1.jpg");
image1.scalePercent(95f);
mydoc.add(image1);
// mydoc.newPage();
mydoc.close();
} catch (Exception e) {
Log.e("pdi name", e.toString());
}
}
Update: See Edit 3 for an answer to op's original question
There are two options:
Use a library to convert the HTML to PDF. This is by far the best option, since it will (probably) preserve text as vectors.
Get a high resolution render of the HTML and save it as a PNG (not PDF surely!).
For HTML to PDF, wkhtmltopdf looks like a good option, but it relies on Qt which you can't really use on Android. There are some other libraries but I doubt they do the PDF rendering very well.
For getting a high-res webview, you could try creating your own WebView and calling onMeasure(...) and onLayout(...) and pass appropriate parameters so the view is really big. Then call onDraw(myOwnCanvas) and the webview will draw itself to your canvas, which can be backed by a Bitmap using Canvas.setBitmap().
You can probably copy the state into the new WebView using something like
screenshotterWebview.onRestoreInstanceState(mWebView.onSaveInstanceState());
Orrr it may even be possible to use the same WebView, just temporarily resize it to be large, onDraw() it to your canvas, and resize it back again. That's getting very hacky though!
You might run into memory issues if you make it too big.
Edit 1
I thought of a third, exactly-what-you-want option, but it's kind of hardcore. You can create a custom Canvas, that writes to a PDF. In fact, it is almost easy, because underlying Canvas is Skia, which actually includes a PDF backend. Unfortunately you don't get access to it on Android, so you'll basically have to build your own copy of it on Android (there are instructions), and duplicate/override all the Canvas methods to point to your Skia instead of Androids. Note that there is a tempting Picture.writeToStream() method which serializes the Skia data, but unfortunately this format is not forwards or backwards compatible so if you use it your code will probably only work on a few versions of Android.
I'll update if/when I have fully working code.
Edit 2
Actually it is impossible to make your own "intercepting" Canvas. I started doing it and went through the tedious process of serializing all function calls. A few you can't do because they are hidden, but those didn't look important. But right at the end I came to serializing Path only to discover that it is write-only. That seems like a killer to me, so the only option is to interpret the result of Picture.writeToStream(). Fortunately there are only two versions of that format in use, and they are nearly identical.
Edit 3 - Really simple way to get a high resolution Bitmap of a view
Ok, it turns out just getting a high res bitmap of a view (which can be the entire app) is trivial. Here is how to get double resolution. Obviously all the bitmaps look a bit crap, but the text is rendered at full resolution:
View window = activity.getWindow().getDecorView()
Canvas bitmapCanvas = new Canvas();
Bitmap bitmap = Bitmap.createBitmap(window.getWidth()*2, window.getHeight()*2, Bitmap.Config.ARGB_8888);
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.scale(2.0f, 2.0f);
window.draw(bitmapCanvas);
bitmap.compress(Bitmap.CompressFormat.PNG, 0, myOutputStream);
Works like a charm. I've now given up on getting a PDF screenshot with vector text. It's certainly possible, but very difficult. Instead I am working on getting a high-res PSD where each draw operation is a separate layer, which should be much easier.
Edit 4
Woa this is getting a bit long, but success! I've generated an .xcf (GIMP) and PDF where each layer is a different canvas drawing operation. It's not quite as fine-grained as I was expecting, but still, pretty useful!
Actually my code just outputs full-size PNGs and I used "Open as layers..." and "Autocrop layer" in GIMP to make these files, but of course you can do that in code if you like. I think I will turn this into a blog post.
Download the GIMP or Photoshop demo file (rendered at 3x resolution).
When you capture the view, just screen bound will capture ( due to control weight and android render pipeline ).
Capturing screenshot for converting to PDF is tricky way. I think two way is more reasonable solutions.
Solution #1
Write a parser ( it's simple ) to convert webview content ( that is HTML ) to iText format.
You can refer to this article for more information.
http://www.vogella.com/articles/JavaPDF/article.html
Also to write a parser you can use REGEX and provide your own methods like parseTable, parseImage, ...
Solution #2 Internet Required
Provide a URL ( or webservice ) to convert HTML to PDF using PHP or C# that has a lot of nice libraries. Next you can send download link to the Client ( Android Device ).
So you can also dynamically add some Tags, Banners, ... to the PDF from server side.
Screen Shot is nothing but picture of your device display which usually depend upon your phone absolute pixels, if your phone is 480x800 screen shot will be same and generally applicable for all scenarios.
Sure, Use this:
Bitmap bitmap;
View v1 = MyView.getRootView();
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
Here MyView is the View you need a screenshot of.
I'm working on a project that needs to use a large image as a map. The image is about 95MB and has a resolution of 12100 x 8000 pixels.
I don't need the whole image at once, I just need a detail of 1000 x 1000 Pixel (it's not always the same detail, just grabbing the same part is not a solution I can use). So I can't just sample it down with the BitmapOptions.
I looked around and found the idea to create a FileInputStream (the image is on the SD-Card) and then I can just load the detail with decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts). That way I wouldn't load the whole thing into the memory. I tried it, but it's just crashing when I try to load the image. Here's my code:
FileInputStream stream = null;
try {
stream = new FileInputStream(path);
} catch(Exception e) {
Log.e("inputstream",e.toString());
}
Rect rect = new Rect(a,b,c,d);
return BitmapFactory.decodeStream(stream, rect, null);
When I try to load the image, the activity closes and LogCat tells me java.lang.outOfMemoryError. Why does it crash? I thought with the stream it should work on the image "on-the-fly", but the only explication I have for the Error is the it trys to load the hole image into the memory. Does anybody have an idea how I can load the detail out of the image, or why this idea doesn't work?
It crashed because all these 95M are sucked into memory for processing. This call will not ignore parts of the stream - it will put the whole thing to memory and then try to manipulate it. The only solution you can have is to have some sort of server side code that does the same sort of manipulation or if you don't want to do it on server - provide thumbnails of your large image. And I would strongly advise against pulling whole 95M at any time anyways
Does BitmapRegionDecoder not help (I realise its level 10)?
I have this working just fine in our iPhone app, but am having problems in Android. I'm using the same urls/data in both apps. When I set my image in my ListView to the bitmap that came from the bytes, the image doesn't appear. The data is there. Here is the code where I assign the view:
if (camera.snapshot != null)
{
bMap = BitmapFactory.decodeByteArray(camera.snapshot, 0, camera.snapshot.length);
image.setImageBitmap(bMap);
}
This is where I convert the string data into bytes:
camera.snapshot = responseData.getBytes();
The images are PNG files. They come in about 4 times the size that I need them for the listview image but I would think they would size perfectly to the bounds I set the ImageView to be.
On iPhone I simply use NSData and then use a prebuilt method in ImageView to turn it into an image. It works perfectly! What am I missing here?
You probably need to use the 4-argument version of decodeByteArray: see http://developer.android.com/reference/android/graphics/BitmapFactory.html#decodeByteArray%28byte[],%20int,%20int,%20android.graphics.BitmapFactory.Options%29.
The options would depend on the type of PNG image, so that you might need to experiment with. For a generic PNG, maybe something like this?
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inDither = true;
opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
You can see http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html and http://developer.android.com/reference/android/graphics/Bitmap.Config.html for more detail.
Everything is fine here. So you need to debug to try and find where else is the issue. i.e. is Camera.snapshot = null ? i.e. you might not be getting the data properly. Or there could also be an issue in the layouts to show the imageview. Try setting a predefined image to imageview and see if it is shown. This way you would be able to track the problem.
I read many discussions about the inSampleSize OutOfMemory dilemma.
Cannot get a good solution so i ask a question about it.
Im currently loading a bitmap with inSampleSize=4.
That will give me a Bitmap with the size 648x388.
Original On disk size is 2592x1592.
Im writing text on 648x388 bitmap and saving it back to disk.
Im writing on the 648x388 because the 2592x1592 give me OutOfMemory .
The way it works is that there can be 1-10 648x388 Bitmaps to be saved in a while loop.
I want to change this loop to save 1-10 2592x1592 Bitmaps.
How can i securely load the 2592x1592?
I don care about the resolution going down 60% or more.
As long as the Bitmap has the same size 2592x1592.
Is there a way to maybe keep the size but make Bitmap thinner,
removing color without making quality bad.
My first thought was going something like this to get the biggest bitmap i could get:
I have not tested this but get a feeling it's a bad way
boolean work = true;
int insample = 2;
BitmapFactory.Options options = new BitmapFactory.Options();
while(work){
try{
options.inSampleSize = insample;
bitmap = BitmapFactory.decodeFile(filePath,options);
work = false;
}catch(Exception e){
insample++;
}
}
any help would be grate.
Image processing requires a lot of memory. you cant use the whole bitmap and just modify it on your phone. use a web service for that. upload, process, download. sorry there is no other way a decoded bitmap just takes a lot of memory.
And by the way you cant catch an outOFMemory Exception. the app just crashes.
There's a hard limit on process size in Android and a 4 mega-pixel image at four bytes a pixel will hit it all by itself (on many devices), without any room for your program.
I think you are going to need to do one of two things: Create a web service to do the image processing on a server/in the cloud; or learn to do your image processing "on-the-fly" by manipulating the data directly instead of using a bitmap.