Question

I have a simple canvas with a mouseover event. As the user moves the mouse, I want to draw a single pixel at the event's x,y coords (in the future it will be more complex than a single pixel). Essentially it's like a custom cursor.

The logic is extremely simple as shown below. Strangely, although I'm cleaning up the old pixel location, there're tiny remnants of the pixel left behind (since I posted this question, I have discovered it's related to the Retina display). I've been able to work around this. Instead of saving 1px and restore 1px, I save 3px and restore 3px. But I don't understand why I need to do this, and in the future when I'm drawing a more complicated cursor, I want the dirty pixel handling to be precise.

Here's a runnable JSFiddle example: http://jsfiddle.net/sbCq3/2/

// cleanup previously drawn pixel
ctx.putImageData(lastImageData, lastImageX, lastImageY);

// save the imageData currently at x,y
lastImageData = ctx.getImageData(x, y, 1, 1);
lastImageX = x;
lastImageY = y;

// draw the dot
var dotData = ctx.createImageData(1, 1);
...
ctx.putImageData(dotData, x, y);

I'm a bit stumped. I'm wondering if it has anything to do with my Retina display. If I draw a single pixel at 5,5, it's a single pixel in the image data - but I can zoom using the DigitalColor Meter (built in zoom tool) and see that single pixel is sub-divided and anti-aliased. Whereas if I view that pixel on a Windows machine it's a nice solid pixel. I haven't tested to see if this problem appears on Windows or non-retina machines yet. (I'm not referring to the normal canvas anti-aliasing problem).


UPDATE: I just tested this on my coworkers non-retina MacBook 17" and it works perfectly fine. So this definitely appears to be related to the Retina display.

Was it helpful?

Solution

I'm not entirely sure, but I think this may have to do with the resolutions of the different screens. If you've ever looked at an html5 canvas on a smartphone, which most have a much greater resolution per sq inch than a monitor, then you'll see that the quality is much degraded because the web browser expands the 100px that you tell it to use to the same physical size, which ends up being around ~130px. I think the same is happening with your retina display because it uses a crazy good resolution. Basically, different screens have different pixel size ratios. To get around this, I added the following to my code.

var PIXEL_RATIO = (function() {
  var ctx = document.createElement("canvas").getContext("2d"),
      dpr = window.devicePixelRatio || 1,
      bsr = ctx.webkitBackingStorePixelRatio ||
            ctx.mozBackingStorePixelRatio ||
            ctx.msBackingStorePixelRatio ||
            ctx.oBackingStorePixelRatio ||
            ctx.backignStorePixelRatio || 1;

  return dpr / bsr;
  })();

Now, whenever you make a canvas or draw anything, multiply your constants by the variable PIXEL_RATIO.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top