When I create a Canvas from a bitmap that is generated with the BitmapFactory.decodeResources from a drawable resource that is a png image file of 400 x 400 pixels, the Canvas height and width are not 400 but 600!
In the code below, the drawable resource wind_scale is a png file that I generated with PHP as a 400 x 400 image.
Bitmap workingBitmap=BitmapFactory.decodeResource(getResources(), R.drawable.wind_scale, options);
Bitmap mutableBitmap = workingBitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas mCanvas = new Canvas(mutableBitmap);
int mHeight = mCanvas.getHeight(); int mWidth = mCanvas.getWidth(); both 600!!!!
Is this behavior expected? If so, why is there a 50% increase?
I am not 100% sure of the problem here but it's similar to another SO question (Getting weird font size after setting it programmatically). The problem there was also a 50% difference in values, and it was due to a comparison of sp units to pixels on the OP's device with a screen density ratio of 1.5.
You might check into BitmapFactory.Options; there are several scale and density flags that may help. My best guess is that the image is being scaled for the device and this is causing a measurement mismatch.
Canvas it's not related to the bitmap size but to the device available screen size. If you try that code on different devices (or virtual devices) you will find different results.
Canvas and Drawables
Related
I’m working on an app that creates a PDF file and I need to draw some small images on it.
The problem is that the drawn images on the PDF are blurry and have very low quality.
I've also tried to draw the images without scaling them and the same happens, they are blurry.
This is the snippet where I draw one image (I'm not using iText)
Bitmap calendarBitmap = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.icon_200ppp_calendar);
pdfCanvas.drawBitmap(scaleToFitHeight(calendarBitmap, 16), xCoord, yCoord, null);
public Bitmap scaleToFitHeight(Bitmap b, int height) {
float factor = height / (float) b.getHeight();
return Bitmap.createScaledBitmap(b, (int) (b.getWidth() * factor), height, true);
}
I have the images in PNG and SVG (converted to VectorDrawable) and the result is the same.
This is the PNG:
This is the XML generated from the SVG file through Android Studio: SVG converted to VectorDrawable
These are the results:
You have to set the Density of the image bigger by an X factor.
For example:
Bitmap bm = new Bitmap(..);
bm.setDensity(bm.getDensity()*4);
// try with different X's: 2, 3, 4, 5, 6.
This loss in quality happens because when you write to PDF it will automatically resize your bitmap to fit that Canvas resolution. Also if you resize your image, bitmap you have to be careful if you have a 50X50 image and you want to resize to 50X10 it will mess up the image. Instead try to resize the Bitmap by a factor:
bm.getWidth()/4,
bm.getHeight()/4
and so on.
My application load images from server via HTTP and save it into files. Then I use this code to get Drawable and view it:
File f=new File(path);
if(f.exists()) {
Bitmap bmp = BitmapFactory.decodeFile(f.getAbsolutePath());
if(bmp!=null) {
DisplayMetrics dm = m_activity.getResources().getDisplayMetrics();
bmp.setDensity(dm.densityDpi);
Drawable drawable=new BitmapDrawable(context.getResources(), bmp);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
return drawable;
}
}
It working fine on Samsung Galaxy S2. But on Samsung Galaxy S4 this images too small. For example:
On S2:
On S4:
I need to display it equally on all devices with various display's resolution. Is it possible? How to implement this?
I think I can help you. I had this same kind of problem myself. What you need to do is find how much larger this new screen is than your old one, by first getting the dimensions of your device and putting them in for widthOfStandardDevice and heightOfStandardDevice.Once you know how much larger the new screen is then your old one, you would make two multipliers to multiply everything by. You can now say set the size of the bitmap to bitmap_width and bitmap_height.
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
height = displaymetrics.heightPixels;
width = displaymetrics.widthPixels;
float widthMultiplier = width/widthOfStandardDevice;
float heightMultiplier = height/heightOfStandardDevice;
int bitmap_width = (int)(600 * widthMultiplier);
int bitmap_height = (int)(800 * heightMultiplier);
The basic problem is that devices with a HIGHER resolution will make the images appear SMALLER. You need to scale the "intrinsic" size proportionately to your device resolution.
One good solution (as Sheldon suggested) is to use a Scaled Bitmap:
How to scale bitmap to screen size?
Android image on tablets and phones
you don't have to scale the bitmap, since scaling it up will mean it will use more memory.
instead, you can just tell the imageView how large you wish it to be , and let it show the bitmap in the exact size you wish it to be (use dp).
if you do wish to set the bitmap size yourself, check out this link if image quality is important or this link if speed is more important
I'm making a game that is Pixel-based in Android. I have several 8x8 sprites that need to be resized to fit on a 100x100 area. As a test to see if it would work, I tried to just make the image fill the entire canvas. It kind of worked, but it made the 8x8 sprite turn into a 12x12 sprite, making the pixels look really odd and distorted. Here's what I have so far:
Bitmap grass = BitmapFactory.decodeResource(getResources(), R.drawable.small);
Paint configuration = new Paint();
configuration.setDither(false);
configuration.setAntiAlias(false);
Matrix myMatrix = new Matrix();
myMatrix.postScale(canvas.getWidth() / grass.getWidth(), canvas.getWidth() / grass.getHeight());
Bitmap resizedBitmap = Bitmap.createBitmap(grass, 0, 0, grass.getWidth(), grass.getHeight(), myMatrix, false);
canvas.drawBitmap(resizedBitmap, 0, 0, null);
If you work on bitmaps then you simply can't. You'd minimize the distortion by scaling up by non-fractional factor so each pixed would be repeated same number of times (i.e. image 10x10 to 20x20 is scaled by factor of two, but 8x8 to 12x12 is 1,5 so no luck). The solution would be to have all assets in vector form (i.e. SVG) and then render them on run-time according to device density and other specs or prepare separate assets for various type of devices (which would blow application size up a bit)
I just can't figure out how to draw on a bitmap (with the help of a canvas) so that the result won't be device density dependent.
Here's the code that makes the drawing:
ImageView iv = (ImageView)findViewById(R.id.container);
Bitmap result = BitmapFactory.decodeResource(getResources(), R.drawable.big_picture).copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(result);
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.small_picture), actualX, actualY, null);
iv.setImageBitmap(result);
I load a big_picture in the Bitmap result and i want to draw a small_picture on that but at a specified position. If i set actualX and actualY it's ok on a device with the same density. However devices with different densities "scale" the canvas.
The interesting part is that only the small_picture is got "scaled" and goes out of the screen but the pig_picture behind it just fits the screen well on any density screen.
Why is that? How can i draw the small_picture on the big_picture densitiy independently?
Thank you!
I've found out what's the case after debugging the Bitmap. Unless you're putting images to drawable-nodpi the image will be re-sized to match the nominal device density. (Which are 120, 160, 240, 320) If you "load" an image to a Bitmap and Canvas they will have one of these densities regardless of the original image's.
So one solution is to put the images to drawable-nodpi. After that they will behave the same on all densities.
Another solution is to multiply the coordinates based on the ratios of the densities above.
...or you can just make custom images and coordinates for all qualifiers.
I am running into a strange issue. I have multiple images in my Android project which I stored as .png files under res\drawable. I was able to easily extract the images at runtime and convert them to a bitmap like this:
Drawable d = getResources().getDrawable(R.drawable.imageId);
Bitmap bitmap = ((BitmapDrawable)d).getBitmap();
This works great and the image gets scaled correctly no matter what screen density the device has. All my images are 200 pixels by 200 pixels and my image layout is configured as 200 dip x 200 dip.
Now, I have stored all images as blobs in an SQlite database due to scalability issues and I am extracting them at runtime and converting to a bitmap like this:
byte[] bb = cursor.getBlob(columnIndex);
Bitmap bitmap = BitmapFactory.decodeByteArray(bb, 0, bb.length);
The image displays fine if the screen density is standard 160 dip. But if the density is any less or more, the image doesn't scale and remains 200 pixels x 200 pixels for 160 dip. So basically, on a smaller screen (120 dip), the image takes more space than it should and on a larger screen (240 dip), it takes less space than it should.
Has anyone else run into this bizarre issue? Any explanation, workaround, solution will be really appreciated.
Thanks much in advance!
Okay, I finally got it to work by using createScaledBitmap().
After creating a bitmap from the blob, I calculate the scaling factor and then calculate the new width and height. I then use those in the createScaledBitmap() function. Here's the code that works:
public static Bitmap getImageFromBlob(byte[] mBlob)
{
byte[] bb = mBlob;
Bitmap b = BitmapFactory.decodeByteArray(bb, 0, bb.length);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int newWidth = b.getWidth()*metrics.densityDpi)/DisplayMetrics.DENSITY_DEFAULT;
int newHeight = b.getHeight()*metrics.densityDpi)/DisplayMetrics.DENSITY_DEFAULT;
return Bitmap.createScaledBitmap(b, newWidth, newHeight, true);
}
This link provided the clue:
difference between methods to scale a bitmap
Use decodeByteArray(byte[] data, int offset, int length, BitmapFactory.Options opts) and in opts set inDesity to let say 160.
Worked for me, see this. But I have to say, I think SQLite is doing it right, and the android drawable is incorrectly sized.