Retina and mask

Somehow I keep on running into little things with Retina and the Picture Sets.

I’m trying to change the color of a glyph/icon image in a Canvas.Paint event.

Because I’ve enabled Retina/HiDPI support, the Image resource I drag into the project becomes a Picture Set (I named it: ‘pReload’).
I’ve properly added a 1x 72DPI and a 2x 144DPI

Now if I draw the image direactly with: g.DrawPicture(pReload, 0, 0)
…all works fine. It picks the correct image for both Retina and non-Retina.

However, when I do this:

Dim newImg As New Picture(pReload.Width, pReload.Height)
newImg.Graphics.DrawPicture(pReload, 0, 0)

Dim newMask As Picture = newImg.CopyMask
newImg.Graphics.ForeColor = RGB(90, 90, 90)
newImg.Graphics.FillRect(0, 0, newImg.Width, newImg.Height)
g.DrawPicture(newImg, 0, 0)

it always shows the non-Retina image.

I assume it goes wrong when either declaring newImg or assigning pReload to newImg but I can’t seem to figure it out.
How can I make sure it picks the right one?

It’s working correctly; the trouble is your rendering it to a 1x bitmap. Try with a 2x bitmap.

I don’t understand. pReload has a 1x and a 2x.
You mean make newImg a 2x? How do I do that?

Dim newImage as New Picture( width, height )

Try using BitmapForCaching, that’ll setup it up 1x or 2x as appropriate

Dim newImg As Picture = BitmapForCaching(pReload.Width, pReload.Height)

To setup a 2x manually from a properly built Image/Picture Set

dim newImg As new Picture(pReload.Width*2, pReload.Height*2) newImg.VerticalResolution = 144 newImg.HorizontalResolution = 144 newImg.Graphics.ScaleX = 2 newImg.Graphics.ScaleY = 2

The trick is that an Image Set and BitmapForCaching use Width and height in points, while New Picture is in pixels.

Great! Thanks!
This works but it surprises me that it’s working on non-Retina as well. Or is it using the scaled 2x image for non-Retina now?
I’m really glad it works but if it’s still switching correctly between non-Retina and Retina, then I don’t understand why.

For non-retina that code is wasteful. It’s making a 2x Picture (when only 1x is needed) but 2x Pictures still draw in the same space because their Resolution is 144.

When you draw a Picture there’s two scalings that happen. 1 The pixel size of the Picture is scaled by it’s Resolutions to yield a point size. 2 That is then scaled by ScaleXY of the Graphics being drawn upon to yield destination pixel size.

So if your Canvas is 100x100 Points, in non-retina ScaleXY is 1 and the buffer is 100x100 Pixels, while in retina ScaleXY is 2 and it’s buffer is 200x200 Pixels.

Now make a Picture
dim pic As new Picture(200, 200)
pic.VerticalResolution = 144
pic.HorizontalResolution = 144

Setting the resolution is important. That Picture is 200x200 Pixels but because resolution is 144 it represents 100x100 Points. It’s 100x100 Points whether you’re retina or non-retina.

When that Picture is drawn it’s Point size is scaled by ScaleXY to Pixels. In non-retina 100x100 Points corresponds to 100x100 Pixels, so the 200x200 Pixel Picture is drawn scaled down into 100x100 Pixels. In retina, 100x100 Points corresponds to 200x200 Pixels, and because ScaleXY is 2 the 100x100 Point size of the Picture gets drawn in 200x200 Pixels.

picturesPixelWidth * (72 / HorizontalResolution) * ScaleX = destinationPixels

Something to note is that New Picture takes Pixels, but most places Points are used. I’ve been working up a cheat sheet to explain all the mechanics because I’ve been confused many times too but think I’ve finally got a grasp on it. There’s a few more permutations to test out but it’s pretty straightforward for the most part, once I got a handle on the two scalings.

Oh, and use BitmapForCaching, it’ll give you the right number of pixels when retina or not.

Very well explained and very helpful.

I think I incorrectly assuming that when in Retina mode, newImg would default to 144DPI with 2x scaling.

Same thing for the dimensions in: Dim newImg As new Picture(pReload.Width, pReload.Height)
When in Retina mode, I assumed it would pass the width and height of the 2x Picture in the image set.

Since that’s clearly not the case, I’m back to old-style manual juggling and might as well not use the Image Sets. :slight_smile:
But to do things correctly (not wasteful) I will try to somehow use BestRepresentation , ScaleFactor or BitmapForCaching so it does things properly for both non-Retina or Retina.

Many thanks!

I find it all very confusing and still stick with RetinaKit. I hoped for a few more Methods/Functions for making it easier for the rest of us. :slight_smile:

Just use BitmapForCaching instead of New Picture and the rest works out transparently. There’s cases where something more is needed but I don’t see that in what you’re trying to do.

To go back on my words, it looks like CopyMask returns a Picture with the wrong ScaleXY. Depending what/how you draw that may need to be fixed.

Dim newMask As Picture = newImg.CopyMask newMask.Graphics.ScaleX = newImg.Graphics.ScaleX newMask.Graphics.ScaleY = newImg.Graphics.ScaleY

Will did a brilliant explaination.

On phone at the moment, which is hard to type out long things, but re-reading the original question; there is an easier way and that’s compatible with image sets, but it’s OS X only.

There’s a function in the Retina Kit for doing just this without having to worry about scaling or even bit maps. I could probably port the code over so it uses Xojo’s ‘retina’ images.

Xojo’s retina support is appreciated, and I’m working on trying to extend it with a special version of the Retina Kit.


100,100 @ 144 dpi is different than 200,200 @ 72 dpi.

If you see them the same it is because you use a wrong display method.

Load both images with Preview and modify the preference, tab images *: click the other RadioButton, close it load the two images and watch what is onscreen.

Another method to know what is what is to set an image to a folder background (Finder): if you place the 144 dpi image, it will be displayed at half its size whatever your screen is Retina or not.

There is a misunderstanding here since the dawn of time…

  • You will find the same kind of preferences in Gimp and Photoshop and every well created image editor.

We went to great lend this to make sure that the transition to Retina would be relatively painless. Could you be more specific about what’s missing?

To be fair (and i hurt myself by writing this now) “no, i can’t”. I’ve read only Forum Posts and thought all the times “Oh, that sounds complicated. I better stay with RetinaKit because it just works”.

Maybe i should give it a try to make my own experiences. But OTOH, as long as Sam’s RetinaKit is performing SO WELL and EASY, i am not ready to use some of my spare time for such tests. :slight_smile:

In that case, you should go read:

We worked very hard so that for the most part users don’t need to do anything special for Retina. If you start using Images instead of Pictures in the IDE and turn on the Supports Retina / HiDPI in the Shared settings, things should just work.

If you’re loading images from disk by hand, there’s a little more work, but if you create a few little methods it gets very easy.

NOTE: Using our method will ensure that your code will be compatible with our Windows HiDPI framework as well.

Than you Greg. I will do so and not “complain” anymore about this before i did not make my own experiences. :smiley:

I cannot make the BitmapForCaching work. It keeps crashing on me at ApplyMask. I’ve included a little sample project.
The best way I could come up with (to make it more dynamic) is by using ScaleFactor:

  Dim newImg As New Picture(pTest.Width * ScaleFactor, pTest.Height * ScaleFactor)
  newImg.VerticalResolution = 72 * ScaleFactor
  newImg.HorizontalResolution = 72 * ScaleFactor
  newImg.Graphics.ScaleX = ScaleFactor
  newImg.Graphics.ScaleY = ScaleFactor

This juggling seems a bit pre- 2016R1 to me though (when selecting the @1x or @2x picture). Maybe that was even easier. And I’m not sure if the above even works on Windows with the Zoom option and it’s 96PPI.

I very much appreciate the Retina/HiDPI work. Many things are much easier and I’m trying really hard to do things how they are supposed to be done. But agree with Sacha where he says that things can be confusing.
Maybe I’m a little slow but I have issues wrapping my head around the things that are supposed to be automated and things that aren’t.
My example is the issue above. And to pick one thing out: Dim p As New Picture(imageSet.Width, imageSet.Height) assigns the width and height of the 1x @ 72PPI Picture from imageSet. Why? I’m in Retina mode.
Maybe there should be a way to copy the properties of the (correct) picture from the Image Set at the Dim statement? I don’t know but I’ll keep trying. :slight_smile:

(Too late to edit)
To make the above code work on a Containercontrol use TrueWindow.ScaleFactor.
Not sure if it’s a bug but on ContainerControls, ScaleFactor always returns 1.