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ビューアとして、もうしばらく延命できることとなった。