awesome-inc / FontAwesome.Sharp

A library for using Font Awesome in WPF & Windows Forms applications
Apache License 2.0
383 stars 90 forks source link

icon not displayed on high scaling #133

Closed pmaistri6869 closed 1 week ago

pmaistri6869 commented 2 months ago

I have used the library v 6.6.0 in a WinForm application, in which I have enabled the DPI awareness. The app is running on Windows 11.

Both IconButton and IconPictureBox suffer of the same issue when the form is displayed on a screen with scaling >250% (in my case a 6K screen at 400% and a 4K screen at 300%), while there are no issues with scaling <=250%.

The issue is that when there is such high scaling, the font that should be drawn on screen within the DrawIcon() function has a width = 0 hence nothing is plotted. (within the GetTopLeft() function the var iconSize = graphics.GetIconSize(text, font, size); has width = 0).

I have downloaded the github source code and I have tried making this change to the code into the function (FormsIconHelper.cs) to try to fix it. The result could be better (depending on the size used for the font in GetAdjustedIconFont), but at least the icon is displayed.

public static Bitmap ToBitmap(this FontFamily fontFamily, TEnum icon, int width, int height, Color? color = null, double rotation = 0.0, FlipOrientation flip = FlipOrientation.Normal) where TEnum : struct, IConvertible, IComparable, IFormattable { float f = NativeMethods.GetDpiRatio(IntPtr.Zero);

 int wf = width;
 int hf = height;

 if (f >= 3.0) // scaling >= 300%
 {
     f *= 2;  // empirically determined;
     wf = (int)(((float)wf) / f);
     hf = (int)(((float)hf) / f);
 }

 var bitmap = new Bitmap(width, height);

 using (var graphics = Graphics.FromImage(bitmap))
 {
     var text = icon.ToChar();

     Font font;
     SizeF iconSize;

    // find the first font with a non zero size <= of the requested size
     do
     {
         font = graphics.GetAdjustedIconFont(fontFamily, text, new SizeF(wf, hf));

         iconSize = graphics.GetIconSize(text, font, new SizeF(wb, hb));

         //System.Windows.Forms.MessageBox.Show(string.Format("f {2}, width = {0}, wf = {1}", iconSize.Width, wf, f));

         wf = (int)(((float)wf) / f);
         hf = (int)(((float)hf) / f);

     } while ((iconSize.Width == 0) || (iconSize.Height == 0));

     graphics.Rotate(rotation, width, height);
     var brush = color.HasValue ? new SolidBrush(color.Value) : DefaultBrush;
     DrawIcon(graphics, font, text, width, height, brush);
 }

 bitmap.Flip(flip);

 return bitmap;

}

the dpi ratio is evaluated using the following code I have added to nativemethods.cs

public enum DpiType { Effective = 0, Angular = 1, Raw = 2, }

[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);

[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);

public static uint GetDpi(IntPtr hwnd, DpiType dpiType) { var screen = Screen.FromHandle(hwnd); var pnt = new Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1); var mon = MonitorFromPoint(pnt, 2 /MONITOR_DEFAULTTONEAREST/);

 try
 {
     uint dpiX, dpiY;
     GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
     return dpiX;
 }
 catch
 {
     // fallback for Windows 7 and older - not 100% reliable
     using (Graphics graphics = Graphics.FromHwnd(hwnd))
     {
         float dpiXX = graphics.DpiX;
         return Convert.ToUInt32(dpiXX);
     }
 }

}

public static float GetDpiRatio(IntPtr hwnd) { var dpi = GetDpi(hwnd, DpiType.Effective); float ratio = 1; if (dpi > 96) ratio = (float)dpi / 96F;

 return ratio;

}