I'm doing some research on making a game that will be able to scale its graphical resources to suit the DPI of whatever device it's on.
In order to do this, I would like to be able to query the DPI of the device, to appropriately scale the assets.
It's a 2D game, and the art style suits arbitrary scaling quite well.
example of what I would do with res/dpi combos:
iPhone/iTouch, at 320x480 (163 dpi) - text will be the normal size
iPhone4, at 640x960 (326 dpi) - text will be twice as large
iPad, at 768x1024 (132 dpi) - text will probably be capped at some minimum size, to take advantage of the greater screen real estate.
So, on iPhone OS, is there a way to query the screen's DPI?
(and as a side note - is this possible on Android devices at all?)
You can fetch this information in Android with the DisplayMetrics class.
I'm not sure if there's a direct way to get the DPI, as you generally don't need to know it. The UIScreen class does expose a scale factor. For iPhone <= 3GS and iPad it should be 1.0, for iPhone 4 it should be 2.0.
Sight compiled:
UIScreen *theScreen = [UIScreen mainScreen];
float scaleFactor = 1.0f;
if ([theScreen respondsToSelector:#selector(getScale)]) {
scaleFactor = theScreen.scale;
}
// use scaleFactor to determine size of fonts/whatever
But, the way the new OS is implemented in regards to differing screen resolutions means you usually won't have to custom scale depending on DPI. Things like images will either be scaled up or it will automatically choose a specially named higher resolution image, and drawing operations are also handled automatically (like drawing a 1point line now draws a 2px line on the Iphone 4). Fonts may be handled similarly, I don't think I came across anything font specific. Read up on the new docs for info:
http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/SupportingResolutionIndependence/SupportingResolutionIndependence.html#//apple_ref/doc/uid/TP40007072-CH10
Related
I have a multiplatform iOS/Android game that I programmed in Cocos2d-x. I have only one set of images to use (I'm only after high res devices) that get scaled according to the device's resolution. For example all images are for the iPad but if you are using a Galaxy S then the image locations and scaling are affected by GalaxyS_screenWidth/iPadscreenWidth.
This works fine for all Android devices and on iPad, iPad2 and old iPhones, but with Retina iPhone there is a problem. On the iPhone 4 all my images look extremely pixelated, a result from the images being scaled to iPhone non-retina resolution then scaled back up to fill the screen.
I tried enabling Retina Mode and the images are half of their intended size (maybe due to the usage of get winsize() which uses points) and scaling manually causes other sorts of problems. I tried playing with a lot of options and attributes but to no avail, so what should I do now?
EDIT:
This is not only a graphics issue, as text gets automatically scaled down then up and appears pixelated.
EDIT 2:
Fonts are bitmaps so my bad. But I don't want to use retina as all images are already retina by default. All of my images are set up for the iPad so for iPhone 4 I just scale them down a bit. This works with Android phones.
For example I have an image, depending on screen resolution obtained through getwinsize():
If current resolution width is 1024 then image stays the same.
If current resolution width is 900 then image gets scaled by 900/1024, no problem.
If device is iPhone 4 resolution width is 480, so image gets scaled by 480/1024, then cocos2d-x automatically scales the resulting image by 2 thus the pixelation. I tried using getwinsizeinpixels, I tried multiplying the screen size, I tried many things but nothing worked out of the box unless I am to redo many of my code.
So the question is, how can I just let the damn engine treat the iPhone 4 as an Android phone with resolution of 960x640?
If you want Retina resolution images to look like Retina resolution images, then those images need to be in Retina resolution (960x480).
If you first scale down the image to 480x320 and then upscale it on the device, it will of course look blurred. You can't magically make the Retina pixels appear from a lower resolution image by scaling it up.
SI couldn't get to the bottom of it so I employed a hack. I enabled Retina Display then I scaled everything x2 through code except for the text. Sounds stupid, but it worked, and pixelation is gone. Thanks everyone who spent time trying to help me.
I tried enabling Retina Mode and the images are half of their intended size
when you enable retina support, cocos2d gets images by appending #"-hd" to their provided filenames. Such images are meant to be double the "visual" size (in iOS terms, pixels vs. points), so that they can be sort of scaled down to make full use of device resolution.
If you have a look at the CCDirectorIOS class, you will find there a couple of methods dealing with this, especially those dealing with the scalingFactor. I don't know what kind of changes you should do to make it work, but if you step into those methods and look at the value of various objects, you might find a way to modify cocos2D default behavior for your specific case.
If this seems to complicated, one thing you could try is changing the CC_RETINA_DISPLAY_FILENAME_SUFFIX so that cocos2d will not look for specially-named files for the retina iPhone, but just use the normal ones.
For example all images are for the iPad but if you are using a Galaxy S then the image locations and scaling are affected by GalaxyS_screenWidth/iPadscreenWidth.
Another thing you might try is not using winSize, but winSizeInPixels, so that when you scale down, you down do it down to the point resolution, but just to the pixel resolution (which is double the point resolution).
Hope this helps.
I have an iPhone Xcode project that currently only contains images for retina display (twice the size as normal and with a #2x.png suffix). When I run the app on the iPhone Simulator (non retina) the images are still being displayed. Does this mean I don't need to worry about including two sets of images: retina and non-retina?
This all seems a bit odd. I would assume that no images would appear on a non retina device if there are no non-#2x files included.
Note: I have not tested my app on a non retina device. Just the simulator.
I'm pretty sure that iOS will just use the #2x and scale it down if you don't have a non-retina graphic. Although that's sub-optimal since you're letting iOS do the scaling at runtime which will be slower than including the non-retina graphic and also iOS might not do as good a job as scaling as your graphics editor of choice.
Even if it works, it's not good practice, and if you have a media heavy app definitely it would impact performance and battery life and memory foot print and ....
By the way, is it just that you don't have the 1x graphics available to you or you are concerned about your apps (download size) or ...
If you are assigning the images in Interface Builder, and you set the image property on a UIImageView to image#2x.png, for example, iOS will not know that it's a high resolution "2x" image. In fact, on a retina display, iOS will look for an image named image#2x#2x.png. Since it won't find it, it will set the scale factor of the image to 1.0.
The contentMode property (just "mode" in XCode) will decide if any scaling of the image occurs to fit the constraints of the UIImageView. You may wish to set the mode to "Aspect Fit" to get the high resolution image to scale as needed for both retina and non-retina displays. In general, the image will display as seen in Interface Builder.
If you are using UIImage's imageNamed or similar function to load the image, and just specify image (where "image.png" doesn't exist, but "image#2x.png" does), then iOS will actually find the image on a non-retina display, though the scale factor will be 1.0. As previously, you'll need to scale it to fit your view. The image will work normally on a retina device, and the scale factor will be set to 2.0, since iOS looks for a "2x" image first, and it doesn't matter if the other file exists or not.
This is from Apple's documentation on imageNamed:
On a device running iOS 4 or later, the behavior is identical if the
device’s screen has a scale of 1.0. If the screen has a scale of 2.0,
this method first searches for an image file with the same filename
with an #2x suffix appended to it. For example, if the file’s name is
button, it first searches for button#2x. If it finds a 2x, it loads
that image and sets the scale property of the returned UIImage object
to 2.0. Otherwise, it loads the unmodified filename and sets the scale
property to 1.0. See iOS App Programming Guide for more information on
supporting images with different scale factors.
If at all possible, you really should include both retina and non-retina images. Using higher-resolution images than necessary negatively affects memory and performance.
I'm currently working on an iPad-specific application.
Since I don't use interface builder, every view element is using CGRectMake with specific numbers for its position and size.
so I was wondering,
if I use the same exact code on iPhone, do these numbers scale accordingly?
or should I re-insert the numbers for all the view elements?
If the latter case, is there any easy way to change it all?
CGRect coordinates are absolute, so your subviews will not automatically scale down to the iPhone. If you run your code on an iPhone, you will see only the upper-left corner of your iPad layout.
You can pretty easily write a helper method to take the original CGRect values and scale them down for the iPhone layout. However, if you're also drawing text you'll need to scale down the font sizes as well (which probably won't work, so you'll still need to deal with the text in a possible not-automated way).
I'm doing essentially the same thing right now, writing a single app that runs on both iPad and iPhone and scales itself in code to the available dimensions. I'm generally setting up the view layouts as a float proportion of the available screen size, and then converting those proportions to CGRect before rendering.
Kudos on not using Interface Builder by the way - IB may be the most ridiculous atrocity I've ever seen in the programming world (even worse than classic ASP, which is saying something).
You'll have to change your numbers for your iPhone app. The points that CGRectMake uses are pixels and don't scale, with the exception of HD devices like the iPhone 4.
Honestly, the iPad is such a different device that your UI probably needs to be heavily modified to take advantage of the extra screen space.
I have the following problem. My application has a tab bar at the bottom and a simple TextView above. In order to fit well TextView above tab bar I had to resize it (I did it through Interface Builder) - I just set height to 401px (usually I hate to do such things).
Then I have tested application on the simulator, to my surprise everything looked o.k. on both standard iPhone display and Retina display. Since I do not have an iPhone I am wondering if everything would look good on the real device as well.
So, my question is: how to set the UIView size so it follows best practices and, what is more important, GUI looks good on both Retina and older display.
BTW: I know how to deal with icons and graphics to make them look good on Retina and older display, I've read all the relevant stackoverflow posts on the subject :)
Size is same on both standard and retina display. It's measured in points. Number of pixels depends on display density.
Pixel != point.
Look at UIScreen scale property. For standard screen it contains 1.0 and 2.0 for retina (iPhone 4). It can contain different values, even not integers.
If you set size UIImageView size to 100x100, it will have same physical size on standard and retina display. But on retina, it will show you much nicer image, because 100 points contains more pixels.
The units in Interface Builder are now interpreted as points not pixels. Therefore it will look the same on both types of displays. On old displays 1 point equates to 1 pixel. On Retina displays 1 point equates to 2 pixels.
I am debugging an application using iPhone 4 Simulator. I have selected it from the simulator menu (device = iPhone 4). When I run the app, the screen size is reported as 480x320 !!??
Is there something I have to modify on this app of mine (originally built for 3G/3gs) in order to make it run on iPhone 4 (yes, I have recreated all artwork as 960x640 and the artwork is on the bundle, but it is scaling it down to half the size... because it is running on 3G/3GS mode, instead of hi res).
running
CGRect cgRect =[[UIScreen mainScreen] bounds];
will result in (0,0,480,320).
Any clues?
thanks for any help.
Recommended read: http://developer.apple.com/iphone/library/documentation/iphone/conceptual/iphoneosprogrammingguide/SupportingResolutionIndependence/SupportingResolutionIndependence.html
One point does not necessarily
correspond to one pixel on the screen.
A sample project, download here.
The iPhone is making the move into resolution independence. This means that things aren't measured in pixels, but in points. Points don't always correspond to pixels.
The screen size of the iPhone 3GS (and previous) is 480x320, in both points an pixels. They correspond on these devices, but in newer devices (like iPhone 4) they do not.
The iPhone 4's screen size is 960x640 in pixels, but its logical screensize is still 480x320 in points.
This allows you to keep your frame, point and size values in their original values and still support larger resolution devices.
On iPhone 4 you need to have #2x somewhere in the name of the image file and it will be used automatically on the higher resolution devices.
When #2x images are loaded, they are loaded as their original resolution, but their size property is halved to be able to work with the logical point measurments.
For instance, an image with an original size of 960x640 will report its size as 480x320 when it's asked for.
instead of bounds use currentMode
CGSize pixelBufferSize = [[[UIScreen mainScreen] currentMode] size];
you will get actual resolution