Strange retina bug with external non-retina monitors

For some reason, if I have a canvas that displays a picture on a machine with two displays (one retina, one non-retina), the image will display incorrectly on the non-retina display.


  • The picture is redrawn in the paint event, at which point it is recreated using the BitmapForCaching function of the window, which should provide the proper image
  • Everything in the debugger shows correctly. The image shows up perfectly fine when previewing in the debugger, but in the actual canvas it’s showing as blown up.

Here’s how it appears on the retina display (this is a non-retina screenshot just to keep the sizes the same):

But when you drag the window to a second, non-retina display it will incorrectly display the image at retina proportions, even though everything with the width/height/resolution numbers seems to be adjusting properly.

I’ve included a sample project that encounters this issue. Just load it on any retina machine with an external non-retina monitor, and then drag the window to the second monitor and it will display incorrectly.

Here’s the code:

Really at a loss for what to do here since everything on my end seems to be working properly…

Drawing should be done in the paint event so you can access the Graphics object of the Canvas.
Fixed your demo project in two seconds by copying DoDraw to the Paint event, and deleting the line redefining g as the graphics object of the picture.

To keep drawing a separate method you need to know the ScalingFactor of the window when you make the new picture and calculate that into ALL of your drawing. Your separate drawing event should draw everything at the HiDPI size and scale down in the Paint event with DrawPicture for a HiDPI screen. If the window is on a non-HiDPI screen draw at the @1x size.

Thanks Tim.

The problem is that I use functions to cache these images so that they can be reused. That’s why doDraw is in a separate method that creates the applicable picture (with the correct resolution and size) that is then drawn onto the canvas. In theory, since all of the numbers match up, isn’t this a bug?

The picture itself is the correct size, width, resolution, and the canvas has everything correct, but it doesn’t display correctly.

The picture is created using BitmapForCaching which accounts for the scaling factor and anything else necessary for drawing on a retina or non-retina display.

The only thing I can recommend is to use Me.DrawPicture in the Paint event to draw your image at the correct size (me.width and me.height should give you the correct canvas size when it moves screens)

I’ve done a bit custom drawing over the past few months, and this works out well for me (I do re-draw the item at the @1x size for clarity)

Basically you cant cache a 2x and use it on a 1x
You’ll have 2x as many pixels & when you draw it on a 1x its huge (like you see)
And you cant go the other way for similar reasons
Not with bitmapped images

Norman: It re-caches and redraws the picture at 1x if the window moves to a non-retina display. You can see in the second screenshot above that the picture is created at 540x40 with a horizontal and vertical resolution of 72. In the debugger it’s correctly 540x40, and all the attributes show it as a non-retina picture.

It’s just that when it’s drawn to the canvas it’s displayed at the retina size, even though it isn’t the retina size and isn’t a retina bitmap.

  • If I draw the bitmap in the paint event, it draws correctly
  • If I draw the exact same bitmap in a “DoDraw” method called from the paint event using, it draws incorrectly
  • If i pass the g as graphics parameter from the paint event through to the “DoDraw” method, and then paint to that directly, it works properly

Looking at the docs I can see that is deprecated (there’s some old code in my 14 year old project…), so I’m guessing my best bet is to pass the g parameter through to any of the custom drawing functions to fix this issue.