Using dpi instead of pixels in canvas.drawBitmap - android

I have read the "screen support API guide "(http://developer.android.com/guide/practices/screens_support.html) and much more resources, but I cannot understand how the dpi works.
I'm developing a game, and I'm not using any layouts (I'm going to draw all myself with functions like canvas.drawbitmap). But when I use the Canvas.Drawbitmap function I need to specify the pixels of the screen where I want to draw the image.
So I'm working now with a fixed resolution (1280x800) and I'm using the drawable-nodpi folder and adjusting the canvas later if the screen of the phone is wider or narrow. The problem with that is that the images look horrible when the resolution is not the native one (1280x800).
What can I do to solve this problem? I've read and read during 3 days, but all explanations and examples are related to Layouts, Nine Patches and so.

Get the density of the device being used and multiply that density by some base size that you pick (how big or small do you actually want it to be drawn?)
Example:
float objectToDrawHeight = 50; //Specified in plain pixels
float objectToDrawWidth = 50; //Specified in plain pixels
float density = getResources().getDisplayMetrics().density;
objectToDrawHeight *= density;
objectToDrawWidth *= density;
//Draw your object using the new (scaled) height and width
//If you are worried about how the object will look on different aspect ratio devices
// you can get the screen dimensions and use that ratio as a multiplier
// just as you did with density
Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
float screenDimensionX = display.getWidth();
float screenDimensionY = display.getHeight();
Using density and possibly screen dimensions should allow you to draw anything and keep it scaled correctly. When using canvas, assume everything is in pixels, and that you must do the dpi conversion.

Related

Scaling for Android devices with starling causing some layout issues

I'm using multi resolution technique number three as written in this article
and to determine the scale factor and stage size, I'm using this piece of code originally written by Jeff :
if (Capabilities.screenDPI >= 200) {
if (Capabilities.screenDPI >= 280) {
AssetFactory.contentScaleFactor = 2;
}
else {
AssetFactory.contentScaleFactor = 1.5;
}
}
else {
AssetFactory.contentScaleFactor = 1;
}
var mViewPort:Rectangle = new Rectangle(0, 0, stage.fullScreenWidth, stage.fullScreenHeight);
mStarling = new Starling(Startup, stage,mViewPort);
mStarling.stage.stageWidth = stage.fullScreenWidth / AssetFactory.contentScaleFactor;
mStarling.stage.stageHeight = stage.fullScreenHeight / AssetFactory.contentScaleFactor;
Then I use this scale factor to determine which sized assets I need to pick.
So now, I have a background which I strech to stage width and size. This technique works great when I'm testing with most devices but then we have devices like the Barnes and Noble Nook Color.
The device has a resolution of 600x1024 with 170 dpi. This means that it's going to pick the smallest assets (320x480) and strech it to 600x1024. Which ofcourse is pixalated. Any ideas on how to get over this issue?
I'm also attaching a test application which shows the issue in detail https://dl.dropbox.com/u/2192209/scaling%20test.zip
What worked for me best so far is not scaling Starling's viewport at all. It should always remain the base size (320x480).
Then you have a scale factor about how big the texture should be. It's a built feature of Starling when creating a texture - passing a scale factor.
What this means is that if your stage is 640x960, your scale factor will be 2. Your image (Bitmap object), will have ACTUAL SCREEN size of 320x480 (fullscreen, 1:1 with stage size), BUT it's texture (loaded asset BitmapData) will be twice as big (640x960, 1:1 with phone size).
For easier understanding - the stage of Starling (320x480) will be scaled up to fit your phone's resolution (320 -> 640). But your images will be scaled down to fit in the stage (640 -> 320). So you get perfectly normal images.
This helps you maintain fixed size of stage. It's really helpful, because otherwise it would be hard to position objects - if you want the object to be in the middle, it's sometimes 160, sometimes 320, etc. This means you always have to set position/size with calculations, which is an overload.
Hope that helps!
edit: Just remembered of a site I've used to determine my main size and assets ratios: http://screensiz.es/phone
How about setting max scale size? If the conclusion of the calculations above takes you to scale that is grater than 2, use bigger assets to reduce the scale ratio.
Your calculation only takes screenDPI in count, try combining them with screen resolution as well.

What is the Android equivalent of [UIScreen mainScreen].bounds.size.width?

While we're at it, what is the Android equivalent of [UIScreen mainScreen].scale?
Are the following equations correct?
[UIScreen mainScreen].bounds.size.width = displayMetrics.widthPixels
[UIScreen mainScreen].scale = displayMetrics.density
where you get displayMetrics like so:
DisplayMetrics displayMetrics = new DisplayMetrics();
Display display = ((WindowManager) someContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
display.getMetrics(displayMetrics);
The thing is that I'm confused by the wording for iOS versus Android.
The definitions for those UIScreen values are these:
bounds
Contains the bounding rectangle of the screen, measured in points.
(read-only)
scale
The natural scale factor associated with the screen. (read-only)
#property(nonatomic, readonly) CGFloat scale
Discussion
This value reflects the scale factor needed to convert from the
default logical coordinate space into the device coordinate space of
this screen. The default logical coordinate space is measured using
points, where one point is approximately equal to 1/160th of an inch.
If a device’s screen has a reasonably similar pixel density, the scale
factor is typically set to 1.0 so that one point maps to one pixel.
However, a screen with a significantly different pixel density may set
this property to a higher value.
I am wondering whether the width of the bounds is equivalent to the widthPixels of the DisplayMetrics and whether the scale value is equivalent to the density value on Android.
[UIScreen mainScreen].bounds.size.width = displayMetrics.widthPixels
Correct, width of the screen (display) in pixels, nothing complicated here.
[UIScreen mainScreen].scale = displayMetrics.density
Not really. They are similar values but definitely not equal.
I'll try to explain what scale is. We have iPad 1 with screen resolution 1024x768 and we have iPad 3 with double resolution (apple marketing calls it Retina) and we want applications to work on both devices. So, the application works on resolution 1024x768 (logical points) but the OS translates to physical pixels on every device using the scale (scale=1.0 on iPad 1, scale=2.0 on iPad 3).
For example, when you draw a rectangle in logical coordinates (1, 1, 20, 40), on iPad 3 it will be drawn on pixels (2, 2, 40, 80).
There are currently only two values defined: 1.0 and 2.0.
The density is a similar scaling factor but calculated differently
This is a scaling factor for the Density Independent Pixel unit, where one DIP is one pixel on an approximately 160 dpi screen
Note that again it converts logical points (called DIP on Android) into screen pixels.
The difference between iOS scale and Android density is that the logical unit is defined differently.
I use this:
private void SetScreenSizes() {
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
intScreenWidth = displaymetrics.widthPixels;
}

Drawing a view on different screen resolution

I am trying to drawing a rectangle view in my application, but its looks different in different devices having different screen resolution. Any idea how to draw same size of view in all different resolution devises. Thanks in advance.
From the documentation (Supporting multiple Screen Sizes):
Multiply your dimensions with scale
// Get the screen's density scale
final float scale = getResources().getDisplayMetrics().density;

Android - Drawing on the different devices

In my app I create a canvas and add some bitmaps on it. The problem is that the objects are adding why touch the screen. So on one screens they appear on the middle on different the same, but their position in pixels are different.
I mean that I got tablet and smartphone. When I touch one the object appear on both devices (multiplayer) but its not in the same place, because its passing the position by x and y.
If someone understand what I mean, can you help me?
Probably it must have something common with counting the ratio.
I am guessing the problem you are having is that the screens are different resolutions and you are passing pixel data. You will need to use dp values and before sending them convert the dp to pixel values. On the receiving device you will need to convert the pixel values being sent back to dp on the given device. Use the methods below for the conversion.
To Convert DP to Pixels:
final float scale = getResources().getDisplayMetrics().density;
int pixelValue = (int) (DESIRED_DP_VALUE * scale + 0.5f);
To Convert Pixels to DP:
final float scale = getResources().getDisplayMetrics().density;
int dpValue = (int) ((DESIRED_PIXEL_VALUE) - 0.5f / scale);
The call to getDisplayMetrics().density is what will give you a scale value based on the current device. The dp value is meant to be density independent.
How do you define the metrics? If you are using pixels, use a density independent solution:
public int GetDipsFromPixel(float pixels)
{
// Get the screen's density scale
final float scale = getResources().getDisplayMetrics().density;
// Convert the dps to pixels, based on density scale
return (int) (pixels * scale + 0.5f);
}
And use it like this in you class:
textView.setHeight(GetDipsFromPixel(50));
This way the the height of the textview will be the same dps on both devices, even if their resolution is different.

Canvas Bitmap drawing and Screen-Density

i'm developing a game for android based on the SurfaceView class. Currently i'm stuck trying to optimize the view for different screen-sizes.
Problem 1: i have PNG-Files in my res folder and i draw them using Canvas.drawBitmap. After reading this article ( http://developer.android.com/guide/practices/screens_support.html ) i thought android resizes the bitmaps if the screen is bigger. Unfortunately i recognized no scaling (or any changes) when launching my app on a tablet vs. launching it on a phone. So how is it done? Should i come up with some fancy strategy to calculate the available space and draw my bitmaps using the source- and destination-Rectangle? Would be nice if someone has a best practice or some hints how to draw bitmaps on a canvas. I already read the pixel<->dip conversion topics but i have to say i don't know how to use that "solution" in my app. So any help appreciated...
Problem 2: I have 3 PNGs in my res folder. One ldpi, one mdpi and one hdpi. Ldpi image has 50px*50px, mdpi has 75px*75px and hdpi has 100px*100px. When i load the app in the emulator (800*480) it uses the 100*100 png. When i load the app on the Acer Iconia Tab it uses the 75*75 image. I thought hdpi is for big screens (=tab) and mdpi or ldpi would be used for the emulator screen. At least i thought android would use the hdpi-image also on the tab but i got surprised... Any explantation for this as well much appreciated
Sorry 4 long text, just trying to be clear...
cya
EffDee
For calculating how big your drawing should be, you can use Canvas.getDensity().
The DPI value of a screen depends on the amount of pixels and on the size of the screen. Tablets do have a high resolution, but most are quite large aswell. This leads to a lower DPI value. For more information have a look at http://developer.android.com/guide/practices/screens_support.html
The best practice is to put images into mdpi folder (pixel aspect 1) and then use density with all horizontal dimensions.
For example:
src.left = offsetX * density;
src.top = 0;
src.right = (offsetX + width - 1) * density;
src.bottom = height - 1;
dst.left = x * density;
dst.top = y;
dst.right = (x + width - 1) * density;
dst.bottom = height - 1;
canvas.drawBitmap(myImageStrip, src, dst, null);
For mdpi density is 1.0, ldpi density=0.75 and for hdpi density=1.5
You can obtain these and other values using
density = getResources().getDisplayMetrics().density;
There is another solution: you can put same image in all folders. The system will take proper one, so you dont need to multiply values with density. But in that case, you'll need 3 or more copies of same image.

Categories

Resources