I'm trying to add one bitmap (png file) on top of another and then save it to the device. Everything works fine except for the first part.
Bits of my code:
1 converting loaded file to bitmap
loadedBitmap = e.target.content as Bitmap;
clonedBitmap = new Bitmap(loadedBitmap.bitmapData.clone());
bitmapData = new BitmapData(e.target.content.width, e.target.content.height);
bitmapData.draw(clonedBitmap);
2 second Bitmap generated within the app
genBitmapData = new BitmapData(bgWidth, bgHeight, false);
genBitmap = new Bitmap(genBitmapData);
3 adding clonedBitmap (external file) to the bitmapdata
var positionMatrix : Matrix = new Matrix(1,0,0,1,0,0);
genBitmapData.draw(clonedBitmap, positionMatrix);
The problem with the step three is that the loaded file clonedBitmap end up being under the bitmap generated by the app upon saving it to the device's storage. When I reverse the order:
bitmapData.draw(genBitmap, positionMatrix);
Only the generated bitmap is displayed/visible while the loaded bitmap isn't visible at all upon saving the file. What I'd like to achieve is to have clonedBitmap (external png file) to be on top of the genBitmap and saved withing a BitmapData object, so I could be able to save it to my device.
I'd be grateful for any tips.
When you draw a bitmap over a bitmap, you need the bitmap being overlayed to be transparent somewhere, otherwise all the lower bitmap's pixels would be overwritten with the upper layer. Therefore, create those bitmaps with alpha channel (transparency=true), make sure the upper layer bitmap is also transparent after you draw a something on it (at least somewhere), then draw first lower layer then upper layer on a single BitmapData object.
Note that since you don't transform any bitmaps, you'd better use BitmapData.copyPixels() method.
It turns out the code works fine. I just overlooked and placed it in the wrong function, which caused the issue.
Related
I am attempting to hide a small file within a bitmap. I am setting the least significant bit of ARGB to each bit in the file (the first few pixels of the bitmap I have reserved for file size).
For this debugging purpose, I am comparing the encoded int[] to the decoded int[]. Every few hundred bytes, there is an incorrect bit.
I am saving the picture through the Bitmap.compress method like so...
OutputStream out = getContentResolver().openOutputStream(fileUri);
picture.compress(Bitmap.CompressFormat.PNG,100,out);
Then, when extracting the file from the image...
pic = MediaStore.Images.Media.getBitmap(this.getContentResolver(), fileUri);
pic.getPixels(intArr, 0, width, 0, 0, width, height);
I took the liberty of examining one of these pngs, and I am indeed using argb8 as the config, and, using tweakPNG, have discovered that the only chuncks in the generated file are
IHDR: 8 bits/sample, truecolor+alpha, noninterlaced
sBIT: RGBA8
IDAT
IEND
The PNG photo looks fine, and has no issues.
Edit: progress!
I tracked the bug down to this basic issue.
Bitmap.setPixels is not functioning identically to Bitmap.getpixels.
The following code shows a slight difference between converted and converted2. Roughly 1 or two bits per hundred ints. This seems shocking to me. Is that an android bug?
picture3 = Bitmap.createBitmap(picture1.getWidth(), picture1.getHeight(), Bitmap.Config.ARGB_8888);
picture3.setPixels(converted,0,picture1.getWidth(),0,0,picture1.getWidth(), picture1.getHeight());
int[] converted2 = new int[converted.length];
picture3.getPixels(converted2,0,picture1.getWidth(),0,0,picture1.getWidth(), picture1.getHeight());
This is the same issue as in this thread.
I have a simple solution, just increment and decriment the off A, R, G, or B value until the lsb if each is correct. Obviously this will result in a more distorted bitmap, but it seems to be the only solution I can think of.
In the app I am developing, I have an activity in which I show two ImageViews made of two bitmaps, ca.bm and ca.tempBm respectively. It works fine on my device but produces OutOfMemoryError on some devices when trying to copy the original bitmap to the bitmap I will modify later:
ca.tempBm = Bitmap.createBitmap(ca.bm); // out of memory error
I would like to create a temp file instead to hold the original image and work with a single bitmap, but the temp file must be preserved when changing from the Camera Activity to the next activity and should be destroyed only when the second activity does.
How can I achieve this?
You can write a Bitmap to the cache directory with:
FileOutputStream outStream = new FileOutputStream(new File(getCacheDir(), "tempBMP"));
myBitmap.compress(Bitmap.CompressFormat.JPEG, 75, outStream);
outStream.close();
And read back in with:
Bitmap myBitmap = BitmapFactory.decodeFile(bitmapPath.getAbsolutePath());
But you might be better off just saving memory by creating a smaller Bitmap, drawing the larger Bitmap to the smaller Bitmap's canvas, then passing around the smaller Bitmap between Activities.
I am working with a customizable database with pictures. Right now I am taking pictures as it is from the sdcard and encoding it in base64 String and then putting it in the database. but whenever I am trying decoding it and showing it in my view, I am getting Out of memory error. Can any one one tell me what is the best procedure to do it? Shall I change the size of the pictures before encoding it?
I want to re-size all of the pictures into 512*512.
Image to Base64 is very heavy operation in android. Consider saving the images on the external/internal memory and save the file path in the sqlite database.
You can convert your image to byte array then store values in sql by using BLOB type and vice versa.
As you mentioned you want to resize the images to 512*512, you can scale the image using below code,
Create bitmap from captured image and then use below line
Bitmap resizedBitmap = Bitmap.createScaledBitmap(myBitmap, 512, 512, false);
It will give you a smaller image, you can also consider compressing the image to reduce in size,
OutputStream imagefile = new FileOutputStream("/your/file/name.jpg");
// Write 'bitmap' to file using JPEG and 50% quality hint for JPEG:
bitmap.compress(CompressFormat.JPEG, 50, imagefile);
Now, you have two options,
Save the scaled and compressed image into a file and save the path of that file in db. (Better way)
Convert the scaled and compressed image to base64 string and save in db.
Althought base64 is , as many answers said, a heavy operation for android, if done properly, it should not be a problem.
There are many reasons a bitmap could be required to be saved to a DB , (photo of a invoice ticket, for example?), and this is the way i do it.
first, create a new , smaller bitmap like #Swapnil commented.
and second, correctly use the bitmap transformation methods, i've been using these (look below) two so far and haven't had any memory issue on many different devices.
link to my BitmapUtils transformation methods
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)?