Recently there was some great posts about how to resize and center images. Which involved using the Graphics
class directly in the Paint
event of a Canvas
, as well as returning a new resized Picture from a Method. Thank you @Javier Menendez, @Thom McGrath and @David Cox.
Because the bulk of my programming experience is SQL Server and web apps, there are still some holes in my knowledge when it comes to Desktop apps, like working with images dynamically.
So, with the above posts in mind, I was playing with how to resize some icons when the ScaleFactor
is not 1.00 or 2.00, such as 1.25, 1.50 or 1.75 which is common on Windows.
The following screen-shot shows the same icon “ImageSet” (includes x1, x2 & x3 sizes), where the top Canvas resizes the icon from x2 down to fit ScaleFactor 1.25 (rendered using the Graphics class directly in the Canvas Paint Event) and the bottom one is resized to the same proportions using a Method that returns a new resized Picture.
Both are using more-or-less the same resizing logic, but the bottom image (returned from a method) does not have the same level of quality. To me it looks blurry. Why is that?
The logic for resizing directly in the Paint Event is basically the following:
Var pic As Picture
If Self.mPolarBear <> Nil Then
pic = Self.mPolarBear.BestRepresentation(Me.Width, Me.Height, Me.ScaleFactor)
End If
If pic <> Nil Then
Var ratio As Double = Min(Me.Width / pic.Width, Me.Height / pic.Height, 1.0)
Var w As Int32 = Floor(pic.Width * ratio)
Var h As Int32 = Floor(pic.Height * ratio)
Var x As Int32 = Floor((Me.Width - w) / 2)
Var y As Int32 = Floor((Me.Height - h) / 2)
g.DrawPicture(pic, x, y, w, h, 0, 0, pic.Width, pic.Height)
End If
The logic for resizing in a separate Method and returning a new Picture is basically the following:
// Method called like the following
If Self.mPolarBear <> Nil Then
g.DrawPicture(Self.ImageResize(Me.Width, Me.Height, Me.ScaleFactor, Self.mPolarBear), 0, 0)
End If
// Method definition
Private Function ImageResize(targetWidth As Double, targetHeight As Double, scale As Double, img As Picture) as Picture
If img <> Nil Then
Var best As Picture = img.BestRepresentation(targetWidth, targetHeight, scale)
If best <> Nil Then
If best.Width = targetWidth And best.Height = targetHeight Then
// selected image does not need resizing
Return best
Else
// resize image
Var pic As Picture
Var ratio As Double = Min(targetWidth / best.Width, targetHeight / best.Height, 1.0)
Var w As Int32 = Floor(best.Width * ratio)
Var h As Int32 = Floor(best.Height * ratio)
// resized image fits given target size, so place in top-left corner and fill width & height
Var x As Int32 = 0
Var y As Int32 = 0
pic = New Picture(targetWidth, targetHeight)
Var g As Graphics = pic.Graphics
g.AntiAliasMode = Graphics.AntiAliasModes.HighQuality
g.DrawPicture(best, x, y, w, h, 0, 0, best.Width, best.Height)
Return pic
End If
End If
Return Nil
Else
Return Nil
End If
End Function
But why should using a Method to return the same resized image be blurrier? Is there something about returning dynamic Pictures like this that I’m missing? Or is this a known consequence of doing things this way?
My Ultimate Goal
The reason for exploring the Method approach is to fix how Icons are rendered in pull-down Menus on Windows.
Because as I’ve found, on Windows when the ScaleFactor
is a fraction of a whole number, e.g., 1.25, there is sometimes some weird upscaling or sizing problems that make the icons look non-standard. The results get even weirder if you have multiple monitors, where not all screens are using the same scaling size and you move your app from screen-to-screen.
For example:
The good news, is the Method I made appears to fix the Menu size issue on Windows, but I’m still concerned about the quality of the resized icon.
The following is how I’m assigning the Icon to the MenuItem:
Var w As Double = 16 * Me.ScaleFactor
Var h As Double = 16 * Me.ScaleFactor
EditPaste.Icon = Me.ImageResize(w, h, Me.ScaleFactor, Me.mPasteIcon)
If anyone is interested, here is a link to my test project (Xojo 2019r3.1)
Any insight would be appreciated. Thank you.