EBWin4 の高DPI化について
EBWin4の高DPI化を行ったので、忘備録として作業内容をまとめておこうと思う。
事の発端は、Macbook pro retina 13" Early2015を購入したこと(2016-11-19 - hishidaのblog)。retinaディスプレイではVMWare Fusion上のWindowsの文字が極端に小さくなり、文字サイズを拡大しないと使用できなくなった。
Macbook pro retina 13"の実解像度は2560x1600だが、Mac OS Xでは擬似解像度1280x800にスケーリングされて表示される。だがVMWare 上のWindowsではドットバイドットで表示されるので、2560x1600の実解像度のまま表示され、文字が小さくなってしまう。
「ディスプレイ設定」→「テキスト、アプリ、その他の項目のサイズを変更する:」で150%〜200%に拡大すると、DPIのスケーリングに従ってアプリも拡大されて表示される。だがEBWin4のように高DPIに対応していないアプリだと、下図のように文字がにじんで表示されてしまう。Surface Proなどの高解像度の端末では、以前から問題になっていたと思う。
図:高DPIに対応していない場合、文字サイズを拡大するとにじんで表示される:
Windows Form アプリの高DPI化作業について
高DPI化全般については、下記のマイクロソフトの田中達彦氏のブログが詳しい。
アプリの高DPI(High DPI)対応について 第1回 ~ 高DPIとは ~ – 田中達彦のブログ
WPFで開発したソフトは自動的にdpiAwareになるが、昔ながらのWindows Formで開発したソフトは、デフォルトでは高DPIに非対応となる。
Windwos FormでアプリをdpiAwareにする方法は簡単で、前述のブログにあるように、app.manifestを追加してdpiAwareをtrueにすればいい。
app.manifestは、Visual Studio でプロジェクトで右クリック→追加(D)→新規項目(W)で簡単に追加できる。
ただしdpiAwareの雛型が作られるのはVisual Studio 2013以降なので(現行のVisual Studio Community 2015もOK)、Visual Studio 2010以前のバージョンを使用している場合は、記述を手動で追加する必要がある。
app.manifestに追加した記述:
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> </windowsSettings> </application>
基本的には、これだけでdpiAwareになり、文字サイズを拡大しても文字がにじまなくなる。
ただEBWin4の場合はEPWINGの外字をビットマップイメージで表示しており、文字サイズを変えても外字ビットマップは小さいままなので、表示のバランスが非常に悪くなる。
つまり現在のWindowsの文字サイズの倍率を取得して、ビットマップ画像も拡大しなければならない。
様々なサイトを参考にして、現在のDPIを取得するサポートクラスを追加した:(100%なら96dpiを返す)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Runtime.InteropServices; namespace EBWin4 { public static class ScreenExtensions { public static void GetDpi(this System.Windows.Forms.Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY) { try { var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1); var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/); GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY); } catch { dpiX = 96; dpiY = 96; } } //https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062(v=vs.85).aspx [DllImport("User32.dll")] private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags); //https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx [DllImport("Shcore.dll")] private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY); } //https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx public enum DpiType { Effective = 0, Angular = 1, Raw = 2, } }
上記クラスを使用して、現在の文字サイズのスケールを取得する関数
/// <summary> /// ScreenのDPIスケールを得る /// </summary> /// <returns></returns> private double GetDPIScale() { double dpiScale = 1.0; try { // 現在フォームのあるスクリーンを得る System.Windows.Forms.Screen s = System.Windows.Forms.Screen.FromControl(this); uint x, y; s.GetDpi(DpiType.Effective, out x, out y); dpiScale = ( x / 96.0); } catch { } return dpiScale; }
DPIスケールに合わせて画像を拡大する関数:
/// <summary> /// DPIに応じて拡大したImage画像を作成 /// </summary> /// <param name="image"></param> /// <param name="dpiScale"></param> /// <returns></returns> private Image GetImageStretchedDPI(Image image, double dpiScale) { Size newSize = image.Size; newSize.Width = (int)(newSize.Width * dpiScale); newSize.Height = (int)(newSize.Height * dpiScale); Bitmap newBitmap = new Bitmap(image, newSize); image.Dispose(); return newBitmap; }
これで高DPIで美しく文字が表示されるようになった。
図:高DPI化されたEBWin4
高DPI時代のEPWINGビューアとして、もうしばらく延命できることとなった。