Why does PDFRenderer creates black borders? - android

I try to generate a bitmap from this PDF file.
My code is the following one:
mFileDescriptor = ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY);
// This is the PdfRenderer we use to render the PDF.
if (mFileDescriptor != null) {
mPdfRenderer = new PdfRenderer(mFileDescriptor);
}
if (mPdfRenderer != null) {
for (int i = 0; i < mPdfRenderer.getPageCount(); i++) {
printPage(i);
}
}
....
private void printPage(int index) {
byte[] img;
if (mPdfRenderer.getPageCount() <= index) {
return;
}
// Make sure to close the current page before opening another one.
if (null != mCurrentPage) {
mCurrentPage.close();
}
// Use `openPage` to open a specific page in PDF.
mCurrentPage = mPdfRenderer.openPage(index);
int width = (int)(203.0 / 72.0 * mCurrentPage.getWidth() + 0.5);
int height = (int)(203.0 / 72.0 * mCurrentPage.getHeight() + 0.5);
// Important: the destination bitmap must be ARGB (not RGB).
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_PRINT);
try {
sendImageToPrinter(PrinterEventsProto.Image.parseFrom(bitmap), getApplicationContext());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
And the correspdonding bitmap is this one where I don't understand where this black border comes from. Any idea ?

This is most likely because your width and height of your bitmap do not match the width and height of the screen (the activity window).
Don't set your bitmap height and width to arbitrary values like you're doing here: int width = (int)(203.0 / 72.0 * mCurrentPage.getWidth() + 0.5)
You could try setting the width of the bitmap to mCurrentPage.getWidth() and the height to mCurrentPage.getHeight(). Then, if you set the scale type of the ImageView that you're displaying to fit, it should display to the full page width and height.
Or if you're sending it to a printer, it should print the full page and not cut anything off. Although, you may need to change the height and width of the bitmap to fit on the type of paper you're printing. In that instance, use this info to produce a properly scaled bitmap for A4 paper:
8.27" x 11.69" Equivalent A4 paper dimensions in pixels at 300 DPI and 72 DPI respectively are: 2480 pixels x 3508 pixels (print resolution)
595 pixels x 842 pixels (screen resolution)
http://www.graphic-design-employment.com/a4-paper-dimensions.html

Related

Remove Background of an Image, Split it in same sizes and Change color(overlay)

I've been searching the internet for changing colour or overlaying a specific part of a bitmap. I've a square bitmap and I want to change the colour in a matrix pattern that is in equal 9 blocks which can be understood from the following image. (Cyan colour line here is for demonstration only)
I've read about boundary fill algorithm in College but here for java, I came to know that it is too bulky to perform such an operation for 9 specific parts. And I don't know how to use Paint with Canvas for such a square scenario.
So is there a method or something which can help me figure out how to paint a specific square by providing the size or location without performing a huge task on UI.
Here's what I need to achieve:
I can change the color, location,size by myself if there's something which can help me out.
Also, as there is white background, is there a way to not paint the background or do I have to use PNG?
Update:
I'm successfully able to divide the image in 9 equal parts using following code but PorterDuffColorFilter is not working as expected.
Code:
public void splitBitmap(Bitmap bitmap) {
int width, height;
// Divide the original bitmap width by the desired vertical column count
width = bitmap.getWidth() / 3;
// Divide the original bitmap height by the desired horizontal row count
height = bitmap.getHeight() / 3;
// Loop the array and create bitmaps for each coordinate
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < 3; ++y) {
// Create the sliced bitmap
smallimages.add(Bitmap.createBitmap(bitmap, x * width, y * height, width, height));
}
}
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
The above code provides 9 bitmaps which then set to a GridLayout. But No mode of PorterDuffColorFilter is useful. Either the images remain original or is painted completely. I've tried every one of the modes available and none worked.
I've done something similar to this so after changing my code a bit, I think this is what you want:
Assuming that you don't have PNG,
First, remove the white background from your image Source:
Setting the white color as Transparent, you can use any color you want.
private static final int[] FROM_COLOR = new int[]{255, 255, 255};
private static final int THRESHOLD = 3;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.test_colors);
ImageView iv = (ImageView) findViewById(R.id.img);
Drawable d = getResources().getDrawable(RES);
iv.setImageDrawable(adjust(d));
}
private Drawable adjust(Drawable d)
{
int to = Color.TRANSPARENT;
//Need to copy to ensure that the bitmap is mutable.
Bitmap src = ((BitmapDrawable) d).getBitmap();
Bitmap bitmap = src.copy(Bitmap.Config.ARGB_8888, true);
for(int x = 0;x < bitmap.getWidth();x++)
for(int y = 0;y < bitmap.getHeight();y++)
if(match(bitmap.getPixel(x, y)))
bitmap.setPixel(x, y, to);
return new BitmapDrawable(bitmap);
}
private boolean match(int pixel)
{
//There may be a better way to match, but I wanted to do a comparison ignoring
//transparency, so I couldn't just do a direct integer compare.
return Math.abs(Color.red(pixel) - FROM_COLOR[0]) < THRESHOLD &&
Math.abs(Color.green(pixel) - FROM_COLOR[1]) < THRESHOLD &&
Math.abs(Color.blue(pixel) - FROM_COLOR[2]) < THRESHOLD;
}
Above code will change the color to transparent and below code will split the bitmap into 9 same size bitmaps:
public void splitBitmap(Bitmap bitmap) {
ArrayList<Bitmap> smallimages = new ArrayList<>(9);
int width, height;
// Divide the original bitmap width by the desired vertical column count
width = bitmap.getWidth() / 3;
// Divide the original bitmap height by the desired horizontal row count
height = bitmap.getHeight() / 3;
// Loop the array and create bitmaps for each coordinate
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < 3; ++y) {
// Create the sliced bitmap
smallimages.add(Bitmap.createBitmap(bitmap, x * width, y * height, width, height));
}
}
}
At last, you can use PorterDuffColorFilter on every bitmap:
imageView.setImageDrawable(arrayList.get(0));
imageView.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_ATOP);
There can be problems as it works for me and might not for you but this is the way you can achieve your needed result.
If any problem persists, I can help.

Taking too much time when resizing bitmap using Bi-linear algorithm in android

I am developing an app which is have loading large bitmap into ImageView. here i am using Bi linear algorithm for re-sizing image and it is working fine but it is taking too much time to resize(Bi-linear algorithm). i am adding some code below.
here is the code :
mutableBitmap = biLinear(mutableBitmap, outWidth,outHeight);// Bi-Linear code
mutableBitmap = Bitmap.createBitmap(mutableBitmap, 0, 0, mutableBitmap.getWidth(),
mutableBitmap.getHeight(), m, true);
Bi-Linear algorithm method :
public static Bitmap biLinear(final Bitmap input,int outWidth,int outHeight)
{
final int oldHeight=input.getHeight(),oldWidth=input.getWidth();
final int newHeight=outHeight,newWidth=outWidth;
Bitmap output =Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
// position of the top left pixel of the 4 pixels to use interpolation on
int xTopLeft,yTopLeft;
int x,y,lastTopLefty;
final float xRatio=(float)newWidth/(float)oldWidth,yratio=(float)newHeight/(float)oldHeight;
// Y color ratio to use on left and right pixels for interpolation
float ycRatio2=0,ycRatio1=0;
// pixel target in the src
float xt,yt;
// X color ratio to use on left and right pixels for interpolation
float xcRatio2=0,xcratio1=0;
int rgbTopLeft=0,rgbTopRight=0,rgbBottomLeft=0,rgbBottomRight=0,rgbTopMiddle=0,rgbBottomMiddle=0;
// do the resizing:
for(x=0;x<newWidth;x++)
{
xTopLeft=(int)(xt=x/xRatio);
// when meeting the most right edge, move left a little
if(xTopLeft>=oldWidth-1)
xTopLeft--;
if(xt<=xTopLeft+1)
{
// we are between the left and right pixel
xcratio1=xt-xTopLeft;
// color ratio in favor of the right pixel color
xcRatio2=1-xcratio1;
}
for(y=0,lastTopLefty=Integer.MIN_VALUE;y<newHeight;y++)
{
yTopLeft=(int)(yt=y/yratio);
// when meeting the most bottom edge, move up a little
if(yTopLeft>=oldHeight-1)
yTopLeft--;
// we went down only one rectangle
if(lastTopLefty==yTopLeft-1)
{
rgbTopLeft=rgbBottomLeft;
rgbTopRight=rgbBottomRight;
rgbTopMiddle=rgbBottomMiddle;
rgbBottomLeft=input.getPixel(xTopLeft,yTopLeft+1);
rgbBottomRight=input.getPixel(xTopLeft+1,yTopLeft+1);
rgbBottomMiddle=Color.argb((int)(Color.alpha(rgbBottomLeft)*xcRatio2+Color.alpha(rgbBottomRight)*xcratio1),//
(int)(Color.red(rgbBottomLeft)*xcRatio2+Color.red(rgbBottomRight)*xcratio1),//
(int)(Color.green(rgbBottomLeft)*xcRatio2+Color.green(rgbBottomRight)*xcratio1),//
(int)(Color.blue(rgbBottomLeft)*xcRatio2+Color.blue(rgbBottomRight)*xcratio1));
}
else if(lastTopLefty!=yTopLeft)
{
// we went to a totally different rectangle (happens in every loop start,and might happen more when making the picture smaller)
rgbTopLeft=input.getPixel(xTopLeft,yTopLeft);
rgbTopRight=input.getPixel(xTopLeft+1,yTopLeft);
rgbTopMiddle=Color.argb((int)(Color.alpha(rgbTopLeft)*xcRatio2+Color.alpha(rgbTopRight)*xcratio1),//
(int)(Color.red(rgbTopLeft)*xcRatio2+Color.red(rgbTopRight)*xcratio1),//
(int)(Color.green(rgbTopLeft)*xcRatio2+Color.green(rgbTopRight)*xcratio1),//
(int)(Color.blue(rgbTopLeft)*xcRatio2+Color.blue(rgbTopRight)*xcratio1));
rgbBottomLeft=input.getPixel(xTopLeft,yTopLeft+1);
rgbBottomRight=input.getPixel(xTopLeft+1,yTopLeft+1);
rgbBottomMiddle=Color.argb((int)(Color.alpha(rgbBottomLeft)*xcRatio2+Color.alpha(rgbBottomRight)*xcratio1),//
(int)(Color.red(rgbBottomLeft)*xcRatio2+Color.red(rgbBottomRight)*xcratio1),//
(int)(Color.green(rgbBottomLeft)*xcRatio2+Color.green(rgbBottomRight)*xcratio1),//
(int)(Color.blue(rgbBottomLeft)*xcRatio2+Color.blue(rgbBottomRight)*xcratio1));
}
lastTopLefty=yTopLeft;
if(yt<=yTopLeft+1)
{
// color ratio in favor of the bottom pixel color
ycRatio1=yt-yTopLeft;
ycRatio2=1-ycRatio1;
}
// prepared all pixels to look at, so finally set the new pixel data
output.setPixel(x,y,Color.argb(//
(int)(Color.alpha(rgbTopMiddle)*ycRatio2+Color.alpha(rgbBottomMiddle)*ycRatio1),//
(int)(Color.red(rgbTopMiddle)*ycRatio2+Color.red(rgbBottomMiddle)*ycRatio1),//
(int)(Color.green(rgbTopMiddle)*ycRatio2+Color.green(rgbBottomMiddle)*ycRatio1),//
(int)(Color.blue(rgbTopMiddle)*ycRatio2+Color.blue(rgbBottomRight)*ycRatio1)));
}
}
return output;
}
this algorithm taking nearly 15-20 secs to load the image
is there any possibility to reduce the loading time
if anyone have idea please help me
Thanks in Advance.
Calling getPixel/setPixel methods per pixel may require to much time.
I suggest you to use copyPixelsToBuffer/copyPixelsFromBuffer methods instead.
e.g:
public static Bitmap biLinear(final Bitmap input,int outWidth,int outHeight) {
if(input.getConfig() != Bitmap.Config.ARGB_8888) {
// this example assumes that input bitmap configuration is Bitmap.Config.ARGB_8888
throw new RuntimeException();
}
final int oldHeight=input.getHeight(),oldWidth=input.getWidth();
final int newHeight=outHeight,newWidth=outWidth;
IntBuffer inputBuffer = IntBuffer.allocate(oldWidth * oldHeight);
IntBuffer outputBuffer = IntBuffer.allocate(newWidth * newHeight);
Bitmap output = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
input.copyPixelsToBuffer(inputBuffer);
// position of the top left pixel of the 4 pixels to use interpolation on
int xTopLeft,yTopLeft;
int x,y,lastTopLefty;
final float xRatio=(float)newWidth/(float)oldWidth,yratio=(float)newHeight/(float)oldHeight;
// Y color ratio to use on left and right pixels for interpolation
float ycRatio2=0,ycRatio1=0;
// pixel target in the src
float xt,yt;
// X color ratio to use on left and right pixels for interpolation
float xcRatio2=0,xcratio1=0;
int rgbTopLeft=0,rgbTopRight=0,rgbBottomLeft=0,rgbBottomRight=0,rgbTopMiddle=0,rgbBottomMiddle=0;
// do the resizing:
for(x=0;x<newWidth;x++)
{
xTopLeft=(int)(xt=x/xRatio);
// when meeting the most right edge, move left a little
if(xTopLeft>=oldWidth-1)
xTopLeft--;
if(xt<=xTopLeft+1)
{
// we are between the left and right pixel
xcratio1=xt-xTopLeft;
// color ratio in favor of the right pixel color
xcRatio2=1-xcratio1;
}
for(y=0,lastTopLefty=Integer.MIN_VALUE;y<newHeight;y++)
{
yTopLeft=(int)(yt=y/yratio);
// when meeting the most bottom edge, move up a little
if(yTopLeft>=oldHeight-1)
yTopLeft--;
// we went down only one rectangle
if(lastTopLefty==yTopLeft-1)
{
rgbTopLeft=rgbBottomLeft;
rgbTopRight=rgbBottomRight;
rgbTopMiddle=rgbBottomMiddle;
rgbBottomLeft=inputBuffer.get(xTopLeft + (oldWidth * (yTopLeft+1)));
rgbBottomRight=inputBuffer.get(xTopLeft+1 + (oldWidth * (yTopLeft+1)));
rgbBottomMiddle=Color.argb((int)(Color.alpha(rgbBottomLeft)*xcRatio2+Color.alpha(rgbBottomRight)*xcratio1),//
(int)(Color.red(rgbBottomLeft)*xcRatio2+Color.red(rgbBottomRight)*xcratio1),//
(int)(Color.green(rgbBottomLeft)*xcRatio2+Color.green(rgbBottomRight)*xcratio1),//
(int)(Color.blue(rgbBottomLeft)*xcRatio2+Color.blue(rgbBottomRight)*xcratio1));
}
else if(lastTopLefty!=yTopLeft)
{
// we went to a totally different rectangle (happens in every loop start,and might happen more when making the picture smaller)
rgbTopLeft=inputBuffer.get(xTopLeft + (oldWidth * yTopLeft));
rgbTopRight=inputBuffer.get(xTopLeft+1 + (oldWidth * yTopLeft));
rgbTopMiddle=Color.argb((int)(Color.alpha(rgbTopLeft)*xcRatio2+Color.alpha(rgbTopRight)*xcratio1),//
(int)(Color.red(rgbTopLeft)*xcRatio2+Color.red(rgbTopRight)*xcratio1),//
(int)(Color.green(rgbTopLeft)*xcRatio2+Color.green(rgbTopRight)*xcratio1),//
(int)(Color.blue(rgbTopLeft)*xcRatio2+Color.blue(rgbTopRight)*xcratio1));
rgbBottomLeft=inputBuffer.get(xTopLeft + (oldWidth * (yTopLeft+1)));
rgbBottomRight=inputBuffer.get(xTopLeft+1+(oldWidth * (yTopLeft+1)));
rgbBottomMiddle=Color.argb((int)(Color.alpha(rgbBottomLeft)*xcRatio2+Color.alpha(rgbBottomRight)*xcratio1),//
(int)(Color.red(rgbBottomLeft)*xcRatio2+Color.red(rgbBottomRight)*xcratio1),//
(int)(Color.green(rgbBottomLeft)*xcRatio2+Color.green(rgbBottomRight)*xcratio1),//
(int)(Color.blue(rgbBottomLeft)*xcRatio2+Color.blue(rgbBottomRight)*xcratio1));
}
lastTopLefty=yTopLeft;
if(yt<=yTopLeft+1)
{
// color ratio in favor of the bottom pixel color
ycRatio1=yt-yTopLeft;
ycRatio2=1-ycRatio1;
}
// prepared all pixels to look at, so finally set the new pixel data
outputBuffer.put(x + outWidth * y,Color.argb(//
(int)(Color.alpha(rgbTopMiddle)*ycRatio2+Color.alpha(rgbBottomMiddle)*ycRatio1),//
(int)(Color.red(rgbTopMiddle)*ycRatio2+Color.red(rgbBottomMiddle)*ycRatio1),//
(int)(Color.green(rgbTopMiddle)*ycRatio2+Color.green(rgbBottomMiddle)*ycRatio1),//
(int)(Color.blue(rgbTopMiddle)*ycRatio2+Color.blue(rgbBottomRight)*ycRatio1)));
}
}
output.copyPixelsFromBuffer(outputBuffer);
return output;
}

Rubbish drawn instead of transparent color when using BitmapRegionDecoder

So, simple task: I have a big picture, which has transparent parts. I want to decode rectangle part of it as a bitmap using BitmapRegionDecoder method decodeRegion. While non-transparent parts of picture are decoded well, I get some crap instead of transparent parts. Did anyone face something familiar? The code I use:
// colNumber, rowNumber are some proper integers from 0 to 4
InputStream is = playingTable.getResources().openRawResource(R.drawable.picture);
try {
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
int cellWidth = decoder.getWidth() / 5;
int cellHeight = decoder.getHeight() / 5;
Rect rect = new Rect(colNumber * cellWidth, rowNumber * cellHeight, (colNumber + 1) * cellWidth, (rowNumber + 1) * cellHeight);
bmp = decoder.decodeRegion(rect, null);
is.close();
} catch (IOException e) {
Log.i("Exception: ", e.toString());
}
// in draw method:
canvas.drawBitmap(bmp, x, y, null);
Use a Paint object for drawing.It is capable of alpha channels.You may need to run through the image pixel by pixel in a nested loop.
Android Developers - Paint

"Bitmap too large to be uploaded into a texture"

I'm loading a bitmap into an ImageView, and seeing this error. I gather this limit relates to a size limit for OpenGL hardware textures (2048x2048). The image I need to load is a pinch-zoom image of about 4,000 pixels high.
I've tried turning off hardware acceleration in the manifest, but no joy.
<application
android:hardwareAccelerated="false"
....
>
Is it possible to load an image larger than 2048 pixels into an ImageView?
This isn't a direct answer to the question (loading images >2048), but a possible solution for anyone experiencing the error.
In my case, the image was smaller than 2048 in both dimensions (1280x727 to be exact) and the issue was specifically experienced on a Galaxy Nexus. The image was in the drawable folder and none of the qualified folders. Android assumes drawables without a density qualifier are mdpi and scales them up or down for other densities, in this case scaled up 2x for xhdpi. Moving the culprit image to drawable-nodpi to prevent scaling solved the problem.
I have scaled down the image in this way:
ImageView iv = (ImageView)waypointListView.findViewById(R.id.waypoint_picker_photo);
Bitmap d = new BitmapDrawable(ctx.getResources() , w.photo.getAbsolutePath()).getBitmap();
int nh = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
iv.setImageBitmap(scaled);
All rendering is based on OpenGL, so no you can't go over this limit (GL_MAX_TEXTURE_SIZE depends on the device, but the minimum is 2048x2048, so any image lower than 2048x2048 will fit).
With such big images, if you want to zoom in out, and in a mobile, you should setup a system similar to what you see in google maps for example. With the image split in several pieces, and several definitions.
Or you could scale down the image before displaying it (see user1352407's answer on this question).
And also, be careful to which folder you put the image into, Android can automatically scale up images. Have a look at Pilot_51's answer below on this question.
Instead of spending hours upon hours trying to write/debug all this downsampling code manually, why not use Picasso? It was made for dealing with bitmaps of all types and/or sizes.
I have used this single line of code to remove my "bitmap too large...." problem:
Picasso.load(resourceId).fit().centerCrop().into(imageView);
Addition of the following 2 attributes in (AndroidManifest.xml) worked for me:
android:largeHeap="true"
android:hardwareAccelerated="false"
Changing the image file to drawable-nodpi folder from drawable folder worked for me.
I used Picasso and had the same problem. image was too large at least in on size, width or height. finally I found the solution here. you can scale the large image down according to display size and also keep the aspect ratio:
public Point getDisplaySize(Display display) {
Point size = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display.getSize(size);
} else {
int width = display.getWidth();
int height = display.getHeight();
size = new Point(width, height);
}
return size;
}
and use this method for loading image by Picasso:
final Point displySize = getDisplaySize(getWindowManager().getDefaultDisplay());
final int size = (int) Math.ceil(Math.sqrt(displySize.x * displySize.y));
Picasso.with(this)
.load(urlSource)
.resize(size, size)
.centerInside()
.into(imageViewd);
also for better performance you can download the image according to width and height of the display screen, not whole the image:
public String reviseImageUrl(final Integer displayWidth, final Integer displayHeight,
final String originalImageUrl) {
final String revisedImageUrl;
if (displayWidth == null && displayHeight == null) {
revisedImageUrl = originalImageUrl;
} else {
final Uri.Builder uriBuilder = Uri.parse(originalImageUrl).buildUpon();
if (displayWidth != null && displayWidth > 0) {
uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_WIDTH, String.valueOf(displayWidth));
}
if (displayHeight != null && displayHeight > 0) {
uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_HEIGHT, String.valueOf(displayHeight));
}
revisedImageUrl = uriBuilder.toString();
}
return revisedImageUrl;
}
final String newImageUlr = reviseImageUrl(displySize.x, displySize.y, urlSource);
and then:
Picasso.with(this)
.load(newImageUlr)
.resize(size, size)
.centerInside()
.into(imageViewd);
EDIT: getDisplaySize()
display.getWidth()/getHeight() is deprecated. Instead of Display use DisplayMetrics.
public Point getDisplaySize(DisplayMetrics displayMetrics) {
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
return new Point(width, height);
}
BitmapRegionDecoder does the trick.
You can override onDraw(Canvas canvas), start a new Thread and decode the area visible to the user.
As pointed by Larcho, starting from API level 10, you can use BitmapRegionDecoder to load specific regions from an image and with that, you can accomplish to show a large image in high resolution by allocating in memory just the needed regions. I've recently developed a lib that provides the visualisation of large images with touch gesture handling. The source code and samples are available here.
View level
You can disable hardware acceleration for an individual view at runtime with the following code:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
I ran through same problem, here is my solution. set the width of image same as android screen width and then scales the height
Bitmap myBitmap = BitmapFactory.decodeFile(image.getAbsolutePath());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Log.e("Screen width ", " "+width);
Log.e("Screen height ", " "+height);
Log.e("img width ", " "+myBitmap.getWidth());
Log.e("img height ", " "+myBitmap.getHeight());
float scaleHt =(float) width/myBitmap.getWidth();
Log.e("Scaled percent ", " "+scaleHt);
Bitmap scaled = Bitmap.createScaledBitmap(myBitmap, width, (int)(myBitmap.getWidth()*scaleHt), true);
myImage.setImageBitmap(scaled);
This is better for any size android screen. let me know if it works for you.
Scale down image:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// Set height and width in options, does not return an image and no resource taken
BitmapFactory.decodeStream(imagefile, null, options);
int pow = 0;
while (options.outHeight >> pow > reqHeight || options.outWidth >> pow > reqWidth)
pow += 1;
options.inSampleSize = 1 << pow;
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(imagefile, null, options);
The image will be scaled down at the size of reqHeight and reqWidth. As I understand inSampleSize only take in a power of 2 values.
Use Glide library instead of directly loading into imageview
Glide : https://github.com/bumptech/glide
Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);
I tried all the solutions above, one-after-the-other, for quite many hours, and none seemed to work! Finally, I decided to look around for an official example concerning capturing images with Android's camera, and displaying them. The official example (here), finally gave me the only method that worked. Below I present the solution I found in that example app:
public void setThumbnailImageAndSave(final ImageView imgView, File imgFile) {
/* There isn't enough memory to open up more than a couple camera photos */
/* So pre-scale the target bitmap into which the file is decoded */
/* Get the size of the ImageView */
int targetW = imgView.getWidth();
int targetH = imgView.getHeight();
/* Get the size of the image */
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
/* Figure out which way needs to be reduced less */
int scaleFactor = 1;
if ((targetW > 0) || (targetH > 0)) {
scaleFactor = Math.min(photoW/targetW, photoH/targetH);
}
/* Set bitmap options to scale the image decode target */
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
/* Decode the JPEG file into a Bitmap */
Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
/* Associate the Bitmap to the ImageView */
imgView.setImageBitmap(bitmap);
imgView.setVisibility(View.VISIBLE);
}
NOTE FOR THOSE WHO WANT TO PUT IMAGES OF SMALL SIZE:
Pilot_51's solution (moving your images to drawable-nodpi folder) works, but has another problem:
It makes images TOO SMALL on screen unless the images are resized to a very large (like 2000 x 3800) resolution to fit screen -- then it makes your app heavier.
SOLUTION: put your image files in drawable-hdpi -- It worked like a charm for me.
Using the correct drawable subfolder solved it for me. My solution was to put my full resolution image (1920x1200) into the drawable-xhdpi folder, instead of the drawable folder.
I also put a scaled down image (1280x800) into the drawable-hdpi folder.
These two resolutions match the 2013 and 2012 Nexus 7 tablets I'm programming. I also tested the solution on some other tablets.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
///*
if (requestCode == PICK_FROM_FILE && resultCode == RESULT_OK && null != data){
uri = data.getData();
String[] prjection ={MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(uri,prjection,null,null,null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(prjection[0]);
ImagePath = cursor.getString(columnIndex);
cursor.close();
FixBitmap = BitmapFactory.decodeFile(ImagePath);
ShowSelectedImage = (ImageView)findViewById(R.id.imageView);
// FixBitmap = new BitmapDrawable(ImagePath);
int nh = (int) ( FixBitmap.getHeight() * (512.0 / FixBitmap.getWidth()) );
FixBitmap = Bitmap.createScaledBitmap(FixBitmap, 512, nh, true);
// ShowSelectedImage.setImageBitmap(BitmapFactory.decodeFile(ImagePath));
ShowSelectedImage.setImageBitmap(FixBitmap);
}
}
This code is work

How do I scale a streaming bitmap in-place without reading the whole image first?

I have an Android application that is very image intensive. I'm currently using Bitmap.createScaledBitmap() to scale the image to a desired size. However, this method requires that I already have the original bitmap in memory, which can be quite sizable.
How can I scale a bitmap that I'm downloading without first writing the entire thing out to local memory or file system?
This method will read the header information from the image to determine its size, then read the image and scale it to the desired size in place without allocating memory for the full original sized image.
It also uses BitmapFactory.Options.inPurgeable, which seems to be a sparsely documented but desirable option to prevent OoM exceptions when using lots of bitmaps. UPDATE: no longer uses inPurgeable, see this note from Romain
It works by using a BufferedInputStream to read the header information for the image before reading the entire image in via the InputStream.
/**
* Read the image from the stream and create a bitmap scaled to the desired
* size. Resulting bitmap will be at least as large as the
* desired minimum specified dimensions and will keep the image proportions
* correct during scaling.
*/
protected Bitmap createScaledBitmapFromStream( InputStream s, int minimumDesiredBitmapWith, int minimumDesiredBitmapHeight ) {
final BufferedInputStream is = new BufferedInputStream(s, 32*1024);
try {
final Options decodeBitmapOptions = new Options();
// For further memory savings, you may want to consider using this option
// decodeBitmapOptions.inPreferredConfig = Config.RGB_565; // Uses 2-bytes instead of default 4 per pixel
if( minimumDesiredBitmapWidth >0 && minimumDesiredBitmapHeight >0 ) {
final Options decodeBoundsOptions = new Options();
decodeBoundsOptions.inJustDecodeBounds = true;
is.mark(32*1024); // 32k is probably overkill, but 8k is insufficient for some jpgs
BitmapFactory.decodeStream(is,null,decodeBoundsOptions);
is.reset();
final int originalWidth = decodeBoundsOptions.outWidth;
final int originalHeight = decodeBoundsOptions.outHeight;
// inSampleSize prefers multiples of 2, but we prefer to prioritize memory savings
decodeBitmapOptions.inSampleSize= Math.max(1,Math.min(originalWidth / minimumDesiredBitmapWidth, originalHeight / minimumDesiredBitmapHeight));
}
return BitmapFactory.decodeStream(is,null,decodeBitmapOptions);
} catch( IOException e ) {
throw new RuntimeException(e); // this shouldn't happen
} finally {
try {
is.close();
} catch( IOException ignored ) {}
}
}
Here is my version, based on #emmby solution (thanks man!)
I've included a second phase where you take the reduced bitmap and scale it again to match exactly your desired dimensions.
My version takes a file path rather than a stream.
protected Bitmap createScaledBitmap(String filePath, int desiredBitmapWith, int desiredBitmapHeight) throws IOException, FileNotFoundException {
BufferedInputStream imageFileStream = new BufferedInputStream(new FileInputStream(filePath));
try {
// Phase 1: Get a reduced size image. In this part we will do a rough scale down
int sampleSize = 1;
if (desiredBitmapWith > 0 && desiredBitmapHeight > 0) {
final BitmapFactory.Options decodeBoundsOptions = new BitmapFactory.Options();
decodeBoundsOptions.inJustDecodeBounds = true;
imageFileStream.mark(64 * 1024);
BitmapFactory.decodeStream(imageFileStream, null, decodeBoundsOptions);
imageFileStream.reset();
final int originalWidth = decodeBoundsOptions.outWidth;
final int originalHeight = decodeBoundsOptions.outHeight;
// inSampleSize prefers multiples of 2, but we prefer to prioritize memory savings
sampleSize = Math.max(1, Math.max(originalWidth / desiredBitmapWith, originalHeight / desiredBitmapHeight));
}
BitmapFactory.Options decodeBitmapOptions = new BitmapFactory.Options();
decodeBitmapOptions.inSampleSize = sampleSize;
decodeBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565; // Uses 2-bytes instead of default 4 per pixel
// Get the roughly scaled-down image
Bitmap bmp = BitmapFactory.decodeStream(imageFileStream, null, decodeBitmapOptions);
// Phase 2: Get an exact-size image - no dimension will exceed the desired value
float ratio = Math.min((float)desiredBitmapWith/ (float)bmp.getWidth(), (float)desiredBitmapHeight/ (float)bmp.getHeight());
int w =(int) ((float)bmp.getWidth() * ratio);
int h =(int) ((float)bmp.getHeight() * ratio);
return Bitmap.createScaledBitmap(bmp, w,h, true);
} catch (IOException e) {
throw e;
} finally {
try {
imageFileStream.close();
} catch (IOException ignored) {
}
}
}

Categories

Resources