Drawing Images: Accounting for Device DPI
Drawing Images: Accounting for Device DPI
Over the next few examples, our goal will be to show a car animating across the screen. At the same time, we will be exploring the impact of device DPI on various versions of the Android OS, and some strategies that can be used to deal with this.
To get started, once again create an HTML file to contain the application, but with a minor difference this time:
<html> <head>
<title>Simple Car Animation</title> <meta name="viewport" content="target-densitydpi=device-dpi; width=device-width;
user-scalable=0;" /> <link rel="stylesheet" href="../../css/proui.css" /> <script type="text/javascript" src="../../js/jquery-1.4.2.min.js"></script> <script type="text/javascript" src="../../js/prowebapps.js"></script> <script type="text/javascript" src="car.js"></script>
</head> <body>
<canvas id="main"></canvas>
CHAPTER 7: Exploring Interactivity
</body> </html>
In Chapter 2, we looked at the various values that can be specified in the viewport meta tag. Here is an example where setting the target-densitydpi actually makes a difference to what is displayed in the browser. Figure 7–5 illustrates the difference between specifying and not specifying the target-densitydpi setting when using a high DPI device.
Figure 7–5. The difference between including and not including the target-densitydpi (on the left, there is no setting; on the right, it is included)
Since a target-densitydpi setting has not been included in the viewport meta tag, the emulator has automatically scaled up the image. This isn’t really what is desired, as this can make the car start to look a little pixelated.
Once the device is instructed to use the device-dpi, it no longer scales, and the quality of the image is improved. There is still more work to do regarding device pixel ratios in our JavaScript, but that’s a start.
Speaking of JavaScript, here is our car.js file: (function() {
var canvas = null, context = null, car = null, carX = 0, endPos = null;
function resetCanvas() {
CHAPTER 7: Exploring Interactivity
... } // resetContext
function animate() { context.save(); try {
if (endPos && car && car.complete) { // clear the drawing surface context.clearRect(0, 0, canvas.width, canvas.height);
// draw the car
context.drawImage(car, carX - car.width, endPos.y - car.height);
// draw an indicator to highlight the difference between the car and
context.beginPath(); context.arc(carX, endPos.y, 5, 0, Math.PI * 2, false); context.fill();
// increment the car x carX += 3;
// if the car x is greater than the end pos, then remove it if (carX > endPos.x) {
endPos = null; } // if } // if
} finally {
context.restore(); } // try..finally } // animate
$(window).bind("resize", resetCanvas).bind("reorient", resetCanvas);
$(document).ready(function() { window.scrollTo(0, 1); resetCanvas();
document.body.addEventListener("touchstart", function(evt) {
endPos = { x: evt.touches[0].pageX, y: evt.touches[0].pageY
carX = 0;
// prevent screen scrolling evt.preventDefault();
}, false);
// load our car image car = new Image(); car.src = "car.png";
setInterval(animate, 40); }); })();
CHAPTER 7: Exploring Interactivity
In the first version of this file, we included a marker to help us understand the impact of device DPI when rendering images. To gain an understanding of how this works, run an emulator using an Android OS 2.1 AVD image with a high-resolution screen DPI skin (something like WVGA800—see Chapter 1 for details on how to do this). This will allow us to compare positioning in an emulator running in medium DPI vs. high DPI mode.
NOTE: You may be wondering why a specific version of the Android emulator is required to demonstrate the difference between a standard resolution and a high-resolution display. This is due to some differences in behavior between different versions of the Android OS, and it is explained in more detail soon.
Figures 7–6 and 7–7 illustrate the difference between the two device pixel ratios and the impact on drawing images.
Figure 7–6. A device pixel ratio of 1 means that both our marker and image are drawn at the position of the touch.
CHAPTER 7: Exploring Interactivity
Figure 7–7. A device pixel ratio of 1.5 shows that images require adjustment. Guidelines have been added. Not surprisingly, the position at which the screen was touched and the end position of
the car differ by a factor of 1.5, while the marker is drawn right where it’s meant to be. For this reason, when we are drawing images to the canvas, we will need to apply some scaling to ensure that those images appear in the correct location.
The following code demonstrates the adjustments required to display the image in the correct location:
// draw the car context.drawImage(car,
(carX / window.devicePixelRatio) - car.width, (endPos.y / window.devicePixelRatio) - car.height);
// draw an indicator to highlight the difference between the car and context.beginPath(); context.arc(carX, endPos.y, 5, 0, Math.PI * 2, false); context.fill();
With this code modification made, the car image is drawn in the correct location and appears at a position in line with the marker.
CHAPTER 7: Exploring Interactivity
NOTE: For the moment, if you are targeting 2.1 as an application platform we would recommend that you look at including appropriate windowDevicePixel ratio tweaks (plus some browser detection code). If you feel comfortable targeting 2.2 and above only, then you are able to let the Android browser deal with things rather than have to account for this behavior yourself.
Additionally, if you are working on an Android 2.2 development platform, then adjust the sample code in this chapter, removing any instance that we divide by the window.devicePixelRatio.