SF Symbols Module

I’m certainly on a retina for the mac - but my external display is not - same result.

I’ve tried a range of parameters - as illustrated - but typically 20pt, weight.4, scale.medium, color.controlTextColor.

The graphics look exactly double but your routines produce an NSRect of height 28 for a 20 point font.

Can you post the code that is making the images you are showing. Typically I assign the image to a control and it deals with the rest. My routine to generate the image to use as a fallback is as follows:

Var oFolder As FolderItem
Var oPicture As Picture

' Toolbar fallback image:
oFolder = SpecialFolder.Desktop.Child( "ToolbarIcon_New.png" )
oPicture = SystemImage( "macwindow.badge.plus", 36.0, SystemImageWeights.Light, SymbolScale.Small, Color.Black )
oPicture.Save( oFolder, Picture.SaveAsPNG, Picture.QualityMaximum )

' Button fallback image:
oFolder = SpecialFolder.Desktop.Child( "btnNext.png" )
oPicture = SystemImage( "chevron.right", 14.0, SystemImageWeights.Regular, SymbolScale.Medium )
oPicture.Save( oFolder, Picture.SaveAsPNG, Picture.QualityMaximum )

That code doesn’t generate a quarter sized image file.

Basically it is just your SystemImage method…
The code looks a bit convoluted but there was a reason (once) for doing it this way.

//longnames are of the form
//tablecells_1_18_red
//exclamationmark_3_4_20_red


//make the picture only once

If diIcons.HasKey(longName) Then
  Return diIcons.value(LongName)
End If

Dim ns() As String =LongName.Split("_")
Dim ub As Integer = ns.ubound

Dim colour As Color = getColour(ns(ub))
Dim size As Double = ns(ub-1).ToDouble
Dim scale As SymbolScale = SymbolScale.Medium
Dim weight As SystemImageWeights = SystemImageWeights(ns(ub-2).ToInteger)

Redim ns(ub-3)
Dim name As String = Join(ns, ".")

Dim p As picture = SystemImage(name, size, weight, scale, colour)

diIcons.value(LongName) = p

Return p

And this in a paint event.

Dim names() As String = Array("pencil_4_20_controlTextColor", "person_badge_key_4_24_red")

y = y + maxRowHeight
x = 0
maxRowHeight = 0

For Each name As String In names
  
  Dim p As Picture = sfIcons.Get_picture_fromName(name)
  
  maxRowHeight = Max(maxRowHeight, p.height)
  
  If x + p.width > g.width Then 
    x = 0
    y = y + maxRowHeight
  End If
  
  g.DrawPicture(p, x, y)
  
  x = x + p.width
Next

Further investigation shows turning Hi-DPI on/off is what triggers it. With Hi-DPI turned off it creates regular sized pictures. Note, the image inside the pictures is correctly sized for both.

Which is what I was thinking scaling factor wise. If you add use:

x = x + ( p.width / canvas/window.ScaleFactor )
y = y + ( maxRowHeight / canvas/window.ScaleFactor )

It will all work as required.

If x + p.width > g.width . canvas/window.ScaleFactor Then

You would think it would, wouldn’t you. But… with Hi-DPI on, on a Retina monitor - yes - that works, but now move the window over to a regular res monitor and its back to double size again.

I think the scaling issue needs to be dealt with within the SystemImage routines as that is where the double sized picture is being created - unfortunately I’m not much good on Declares so can’t see where scaling would be applied - but presumably within your ImageSize declare.

That’s Javier Menendez’s code. But I’ll take a look. If you needed it to take account of monitors it would need to know where it was going to use the image. Which would be an interesting problem. I’ll take a look.

If you look at the above code and run it in with HiDPI mode on or off it works correctly. Turned off I get a picture which is 17px x 17px. Turned on I get an image that is 34px x 34px. In neither case do I get the icon squashed into the corner of the image.

Ah, you are making the picture only once. Which means you are making it for a given screen resolution. When you switch screens you need to make it again. You also need to do it in the ScaleFactorChanged event.

Yes, I reset the dictionary for a different scaleFactorChanged - but its still making the picture twice the size for the second monitor. This is because although scaleFactorChanged event is triggered, the scalefactor for the Window (where the paint event is fired) remains the same.

Hmm… something is wrong. The image generated is indeed doubled in size when the retina option is turned on, with the small image in the corner. I’m picking through the code now to figure out what the problem is.

I find it very odd that geeing this the image saved still comes out correctly. I’ll keep prodding.

Make sure that the Horizontal and Vertical resolutions of the picture as well as the ScaleFactors in the graphics context of the image are correct. That would account for it being right in the saved image but not when drawing.

There seems to be an issue with Picture.FromData. If I load a valid PNG file into a memory block and then produce a picture the same scaling issue is found. Equally getting the image from the API and using Picture.FromData results in an image 2 x 2 times larger than the original.

The same is true of Pictures created with Picture.Open( file ). Graphics context for both methods reads as nil.

Check the resolutions as I said. I’d bet that the Horizontal and Vertical resolutions are 72 when they should be 144 on a retina screen.

No, they are 144 when app is run as retina (HiDPI) and 72 when not.

I’ve opened an issue:
https://tracker.xojo.com/xojoinc/xojo/-/issues/70193

Example project attached if you want to try it.

According to photoshop the original image is 34px x 34px with a resolution of 144 x 144. When loaded it reads as 34 x 34, 144 bit is positioned in the top left. Setting the resolution to 72 x 72 makes the image occupy the full area of the image, but then doesn’t match the original file.

But you do not load a SF Symbol, only a picture file (using a BinaryStream / same result as with Picture.Open(f)).

Yes, that is a test of what is produced by Picture.FromData / Picture.Open, which is what the SFSymbols module does internally. It generates an SF Symbol and then renders it into PNG format converting that to a Xojo picture (using Picture.FromData). Thus turning the NSImage into a Xojo picture object.

Ian

Thank you for your pursuit of this issue. I have done a temporary fix for my app by setting my own scale variable - dependent not on the window.scaleFactor but by whether HDPI is on.

Dim scale As Double = If( app.AllowHiDPI, 2, 1)

Obviously this doesn’t address the original problem.

When you get the picture you could set the HorizontalResolution and VerticalResolution to 72, which fills the rectangle, you would then have to down scale the image. That said if you simply apply the unmodified image to controls, toolbars etc they would fine. It would only become an issue if you were drawing your own controls using a canvas.