Windows 和 macOS 在高 DPI 支持上的差距,真的只是對生態的把控力問題嗎?

還是說,Windows 相關 API 的設計根本就是有問題的?

附註:

雖然很多人一再聲稱 Windows 10 下系統的支持和第三方軟體的支持都有大改善,但實際上還是問題很大。特別是多顯示器的情況,似乎又退回到了古代,而且對於多個顯示器不同 DPI 的接合、拖動處理似乎也有問題。

一個具體的例子可參考windows 10對高分屏的支持如何?,連 VS 都不支持高 DPI。


說到API設計,程序員就要show the code,自行對比(>_<)

macOS(Per-Monitor DPI from OS X 10.8)

AppKit:APIs for Supporting High Resolution

Carbon:已死亡,從OS X 10.8開始API全線已經Deprecated,也沒有Retina支持,估計下個macOS版本二進位直接就沒法跑了吧,畢竟是比MFC還古老的技術啊...

Java(比如說Swing之類):在JDK 8已修復,對開發者透明,JDK-8000629 [macosx] Blurry rendering with Java 7 on Retina display

Windows(Firstly Windows 8, Per-monitor DPI from Windows 8.1)

UWP:Introduction to Universal Windows Platform (UWP) app design (Windows apps)

WPF:Developing a Per-Monitor DPI-Aware WPF Application

WinForm:Online Documentation - Developer Express Inc.

Win32:Writing DPI-Aware Desktop and Win32 Applications

Qt:High DPI Displays

科普

DPI字面上的意思就是每英寸的顯示出來的點數,這個是和肉眼看到的物理尺寸有關,常見於印刷製品。

macOS上,實際上用的Retina的概念(來自iPhone)更為簡單,對所有UI的繪製不以像素(pixel)為基準,而是叫做Point,在Retina 2x設備上就是1point=2pixel,3x設備就是3pixel(現如今Mac還沒有,只有iPhone Plus系列是),而且由於軟硬體一體,Retina只會有整數倍數,類比200% DPI,不會出現類似1.5x這樣的問題。

Retina和解析度:The Ultimate Guide To iPhone Resolutions

Retina詳細解釋:High Resolution Explained: Features and Benefits

Windows上就比較麻煩了,DPI縮放可以設置成100% 125% 150% 200%等等,而且XP時代,8.1時代,10時代還有各自不同的處理方式,整體看起來明顯就相對麻煩了,具體可以看看DPI DIP Effective Pixels(UWP時代)這三者說明。

DPI和DIP:DPI and Device-Independent Pixels

Effective Pixels:Sceen sizes and break points for responsive design

看法

大概掃了一眼各種GUI框架簡介,如果你用的是新的API(AppKit UWP WPF),程序員不用有任何額外處理,單獨讓設計師對所有標量圖切出不同大小的圖,對AppKit是*.@2x.png(Retina 2x) *.@3x.png(Retina 3x,Mac還沒有,對應iPhone Plus系列)。對UWP WPF這種使用XAML的就是*.scale-150.png(150% DPI) *.scale-200(200% DPI)的圖,全部拖到工程目錄裡面放到本身那個png旁邊,剩下的啥事沒有,圖片自動根據設備DPI選擇匹配的那個,UI控制項都是GUI庫提供的,根據DPI繪製,字體也會根據DPI顯示,完美。

但是如果用的舊的那些API,就得手動處理一些Dirty Work,封裝一下介面,比如說自己搞定標量圖選擇呀,自己轉換像素值,繪製對應DPI下的文字,然後還得升級用到的第三方UI組件,成本是有的。所以還得看第三方軟體廠商......天知道他們用了什麼GUI框架......

另外,GUI這樣的東西,除了標準那些組件(類似什麼Label TextView ImageView這些),還有一些是直接繪製的(簡單點說就是描點、畫線、填充這些底層API)的UI,Mac上是基於Core Graphics繪製,在OS X 10.8之後(也就是第一款Retina MacBook 發布時候)就已經全部考慮到DPI,Deprecated了老介面。對於Core Text文本渲染也是對開發者透明的情況下支持了Retina。在Windows上,對應的繪製API應該是GDI(之後有了GDI+和Direct 2D),但GDI是直接基於像素的,而不是像Core Graphics那樣基於Point的概念,因此開發者得搞一些工作支持。字體渲染的話早期也是GDI,這個坑已經說了。不過現在另外有一個DirectWrtie用來渲染字體,對DPI支持的非常不錯。不過現在主要是UWP用,Win32用的軟體不是非常多(比如Chrome Steam for Windows是用的)。

當然,假如你直接用OpenGL或者DirectX這種級別的底層渲染API(雖然很少見用這種東西寫軟體,大部分是做遊戲的,而遊戲通常從不考慮DPI,只考慮像素和解析度),在哪個操作系統上都沒有DPI支持的,全看自己...

關於多屏幕DPI適配

提問者補充追加裡面說到了Per-monitor DPI的問題,這個macOS和Windows都有各自的解決策略,當然是不是方便易用,可以參考兩邊的文檔。

macOS:

OS X 10.8全面支持Retina後,就已經想到這個問題,對AppKit所有上層的坐標系統介面進行了梳理,提出了一個叫做Backing Store的坐標系統,你不管有多少外接屏幕,所有的計算是在一個單獨的坐標系統中計算,渲染的時候把這個Backing Store的坐標映射到當前屏幕上。具體原理在上文的 High Resolution Explained: Features and Benefits 已經介紹過了。開發者本身不需要額外處理什麼消息機制,重繪策略,一切透明通過這個坐標系統處理(當然,對動態生成的標量圖,默認行為是直接拉伸。但是比如圖片應用,你可以手動接收一下通知來處理,因為此時兩個屏幕scale不同,可以在兩個屏幕展示不同尺寸的標量圖)。

另外,為了避免開發者再手動處理這些問題,把所有舊的API全部標記為deprecated並要求遷移到新的API上。如果你使用了新API,那就是可以完全免費得到Per-monitor DPI。可以參考這個介面:NSView | Apple Developer Documentation

Windows:

Windows 8.1之後才提供了Per-monitor DPI的介面,Windows 8及之前是沒有官方介面的。對於UWP應用,本身使用了DPI以外的叫做Effective Pixel的概念,直接映射到另一套坐標系統上,應該比AppKit還簡單一些,畢竟開發者不用操任何心。

但是對於WPF和Win32,雖然我不搞WPF,不過看了那個官方教程文檔Developing a Per-Monitor DPI-Aware WPF Application,還需要一定工作量(取決於你的代碼複雜度)。首先,需要繼承一個子類PerMonitorDPIWindow,然後在接受到DPI更改的消息時候重新對所有child view重繪(那個教程裡面直接加上了一個Transform,但是實際上UI肯定不會這麼寫,會導致鋸齒等,又不是寫3D遊戲。需要真實重新設置scale之類),估計又得搞一堆代碼。總之不是對開發者透明的,意味著有一定開發成本。還有一些問題,比如只有Main window有通知,菜單對話框等系統提供的Win32組件本身不支持這個情況……

在Windows 10 Creator Update之後,新加入了一個Per-monitor DPI Awareness V2(已經2017年了),支持了child window的通知,還有一系列Win32控制項的自動處理,相對開發者好用多了。具體可以參考:High-DPI Scaling Improvements for Desktop Applications in the Windows 10 Creators Update

舊軟體兼容性

macOS:

macOS對於舊軟體,使用了全局的縮放(由macOS的窗口管理程序WindowServer處理),效果應該也就是簡單的插值過濾罷了,模糊點但是布局正常。不過開發者或者高級用戶,可以直接在應用.app包中的info.plist,聲明NSHighResolutionCapable為True,可以開啟強制Retina渲染,也是直接對舊版API(主要是Core Graphics畫的)也強制按Retina渲染(具體原理沒查到,看錶現應該是直接把所有像素值當作pt給你渲染了一次...),有一些應用(比如說公司用的某Qt寫的VPN軟體)就非常完美,也有的比如當年的Word 2011就會掛......所以說也不是絕對,需要嘗試,有個小軟體能方便普通用戶使用這種方式設置強制開啟Retina,參考:http://retinizer.mikelpr.com

Windows:

Windows其實對舊版兼容性好得多,這裡有一個DPI Virtualization的概念(參見上面的Win32文檔),意思是說,如果應用不支持DPI Awareness,那麼和Mac類似,使用暴力全局縮放(由Windows的窗口管理程序DWM處理),看起來模糊但是至少是正確的UI。如果是聲明了DPI Awareness,那麼GDI繪製時會返回偽造的像素值(比如你200% DPI的10像素,GDI就繪製20像素),這樣有一些軟體看上去就會銳利無比,但有些也會掛。有意思的是,普通用戶也可以在exe右鍵打開"在高解析度下禁用縮放"來強制聲明DPI Awareness,一些軟體看著就舒服多啦。不過實際上自己試過有些Win32軟體開始後會有字體模糊,UI正常,不知為何(難道不是用GDI渲染的字體?)……

另外,Windows 10 Creator Update裡面提供了一個對傳統Win32程序的增強的GDI HiDPI支持,開啟後效果更加,前文已提到:Improving the high-DPI experience in GDI based Desktop Apps

總結

個人觀點,還是蘋果對兼容性比較大膽,在第一款Mac Retina設備發布時(搭載OS X 10.8),就直接把舊的API全部Deprecated,讓你沒法用。而且GUI框架生態上來說,基本上是絕對數量的AppKit,很少的Java應用和Qt應用,由於軟硬體一體,Retina的概念比起DPI更好處理(畢竟沒有非整數倍),當年廣大Retina MacBook用戶拿到設備後,集體郵件轟擊軟體商支持,加之Mac App Store審核也是要求Retina支持。因此大波廠商迅速更新支持Retina吸引用戶(不然就淘汰)。

Windows這邊,雖然最早在Windows 7就有識別DPI的API,但是配套GUI框架沒跟上,而且各路GUI框架爭雄(8.1時代應該有Metro(UWP前身) WPF WinForm Win32 MFC Qt好幾個...),開發者用的GUI框架也許更新就不夠及時。而且不是軟硬體一體,OEM硬體廠商不給力,2K 4K屏幕推的太慢,到了Windows 10才大力推廣,API也加上來,結果造成生態沒有及時跟上...不過感覺現如今好多了(比起當年8.0/8.1時代),而且不是要把UWP推上正位嗎,原生支持高分屏和Per-monitor DPI,如果廠商多提供UWP應用,軟體多高分屏的支持肯定越來越好(雖然感覺VS這種應用,不太可能用UWP寫……)

---更新---

關於編程語言

經過評論區提示,編程語言和介面設計的關係也比較大。在macOS上,AppKit本身提供的是Objective-C的介面,而Objective-C是一門嚴格的C超集的動態語言,在ABI上使用Runtime和動態派發,避免了靜態面向對象語言的Fragile Binary Interface問題(參考Wiki:Fragile binary interface problem)問題,因此可以隨便添加成員變數,方法,甚至覆蓋,而不會導致的ABI兼容問題。同時,Core Graphics是純C的介面,也不會有面向對象語言特有的這個問題。

但是Windows上的Win32 API和GDI+,提供的是C++介面,大部分C++程序員都是知道C++的ABI兼容是多麼難做,一不小心(比如沒有通過PIMPL等方式而是直接暴露成員變數的話),添加一個新的變數可能就你的舊的二進位程序鏈接系統動態庫時崩潰,因此得非常慎重。GDI是C介面,不過設計時間比起Core Graphics要早十年(DOS時代……),那時候的C編譯器產生出來的二進位想要ABI兼容也是大問題。

另外,Win32 API一般不太會及時Deprecated老的API,除非到已經維護不下去為止,不像Apple,一旦不滿足最新的需要(比如AppKit的那個NSView的例子)立即Deprecated。用戶依舊能繼續運行舊應用,但可以大大鼓勵開發者儘快遷移新介面。這點也間接影響了軟體商,讓Windows很多軟體一直保留舊的API,隨時間推移更加難遷移,直到最後「重構」(重寫)。


相關API是在高DPI出來十多年前設計的。當然沒法直接支持高DPI。新API出來之後,老程序沒人改也用不上。所以這是個循環。


當然是有問題的,跨屏DPI縮放現在確實沒有好的解決方案。

一個折中的辦法就是盡量在DPI不同的第二屏幕上用UWP……反正閱讀類應用UWP還是可以勝任的


關於我提到的多屏幕不同 DPI 下多種應用(包括 Office 和 Visual Studio)在高 DPI 屏幕下模糊的問題(windows 10對高分屏的支持如何?),基本已經明晰了——Windows 在「創意者更新」之前,根本就沒有對類似行為的良好支持。這得到了微軟本身的更新日誌的確認(High-DPI Scaling Improvements for Desktop Applications in the Windows 10 Creators Update)。

The image above illustrates the types of issues you』ll see in Windows 10 when using multiple displays with different DPI values. In this case a low-DPI primary (「main」) display docked to a high-DPI external display.
Note that these are just a few of the types of issues and that all the items that are too small in this picture could easily be too large if the display topology was reversed (high-DPI primary and low-DPI external display).

值得注意的是,官方特別提到了 Word:

Some applications (Word) render blurry on the high-DPI display

以及 PowerPoint(其實表現同 Photoshop)

Some applications (PowerPoint and Skype for Business) are crisp but render at the wrong size

官方引用的圖:

這證明了我的問題(windows 10對高分屏的支持如何?),以及其他用戶碰到的類似問題(Windows 和 macOS 在高 DPI 支持上的差距,真的只是對生態的把控力問題嗎?,windows 10對高分屏的支持如何?),並不是無中生有,而且是可以廣泛復現的。

微軟官方的態度是坦誠的,然而我對某些具有較強地維護微軟傾向地用戶的評論(以及對其點贊的用戶)表示遺憾:

但是有人聲稱他們的高分筆記本(XPS,Surface 家族)外接低 DPI 顯示器就沒有問題。現在看來,這極有可能不是「沒有問題」,而是問題以另一種不同方式表現出來。下圖就是把高 DPI 的屏幕設置為主屏幕之後低 DPI 屏幕上的表現。Chrome 依然是正常的大小。PowerPoint 更新之後,大小是正常了,但是字體卻顯得很 macOS——也就是微軟支持者聲稱的(像蘋果一樣,而不是那種單純插值放大的)「糊了」。同樣表現的還有 Visual Studio。截圖工具則被放大了,Photoshop 也是,而且菜單欄字體顯得格外的大。

當然官方 UI 也無法倖免。

===============================================

而對於 WPF 應用,根據我試用的一個 fix(參考http://stackoverflow.com/questions/43537990/wpf-clickonce-dpi-awareness-per-monitor-v2),在如此修復了之後(手動禁止 WPF 的默認 DPI Awareness,並且手動設置對於不同操作系統使用最新的策略),WPF 應用並沒有正確地縮放——它依然在 150% 放大的屏幕上保持 100% 的縮放比例,顯得字非常小。而 SO 答案的末尾也提到,也許你需要自己實現 OnDpiChange。

需要改動的地方:

上為 100% 屏幕上的結果,下為 150% 的屏幕上,Chrome 的表現為正常表現:

因此,我謹慎地認為,輪子哥對於 Visual Studio 是使用 WPF 開發,從而能自動支持高 DPI 情形的表述是值得商榷的。(具體言論暫時找不到,不過可以找到類似的對 WPF 的誇讚——「反正WPF的UI排版就算你不知道什麼是dpi縮放也能用好。」——visual basic 如何禁用dpi縮放?)

最終,對這個問題,我的結論是,這不僅僅是生態和 API 設計的問題,同時微軟的支持進度也較為落後,畢竟 DPI Awareness v2 是創意者更新才實現的——之前根本不存在這個東西。


其實mac的設計才是有問題的。

正確的設計是「解析度無關「,UI元素(包括文字)都使用物理尺寸(毫米,磅...),全矢量圖形(例如svg),使得任意像素密度的顯示設備上,UI元素的物理尺寸大致相同。在今天的Linux桌面,例如KDE上就能看到這樣的設計:只要Xwindow的dpi設置正確,在任意屏幕上文字/按鈕/圖標的物理尺寸都一致。

而mac的hidpi,2x,3x之類的hack,在蘋果店裡一字排開的不同型號macbook上表現無遺:每台的字都不一樣大。


1995 年哪來那麼好的顯示屏

另外 win32 是 C++,api 行為變一點點就會讓 app 崩潰。


你要強行說生態的把控力我也沒啥好反駁的

畢竟,Win32應用都這麼多年了

Win32應用不走商店是幾十年前就開始的,那個時候還沒有商店分發的概念

換言之,那個時代根本沒有所謂的生態把控力

現如今Win10通用應用全部走商店,對DPI的支持也很好,無奈很多人都不知道有商店這個東西罷了

如果微軟放棄兼容,Win直接崩潰

如果微軟不放棄兼容,那麼你們又會提出這樣那樣的問題

所以啊,我軟也很無奈


在高DPI支持上Win和MacOS差距還是相當大的,GDI+出來如果一刀切的話怨聲更大,畢竟4K屏直到現在都沒有普及,但目前支持情況已經好很多了,大部分軟體在高分屏上看起來確實爽歪歪。如果要讓我選擇是看高清UI還是要功能多樣化,我還是選擇後者。日常應用中像百度雲和迅雷這樣頑固的也是少數,但你能說不用就不用么?


路過黑一下微軟。本來以為windows 10發布這麼多年以後high dpi的問題應該解決差不多了。沒想到前兩天拿筆記本接外顯一試,第三方軟體倒是基本上沒問題了,字糊的最厲害的要數兩個軟體(系列):第一個是Microsoft Office,第二個是Microsoft Visual Studio(包括最新的2017版)。


不是生態問題,是API設計問題。macOS(和 iOS)的 Core Graphics 從一開始就是解析度無關的,而windows早期的 GDI 是基於物理像素的,也就是解析度相關的。這是導致兩個系統對高DPI支持上的差距的主要原因。

舉個例子,當年 iPhone 3G 屏幕是320x480像素,iPhone4 首次使用 Retina 屏幕,寬度是 640x960像素,解析度增加了一倍,按照windows的經驗,所有的UI、字體、圖標都變成原來的1/2大小,文字小到需要眯著眼睛才能看清。我就不貼圖了,大家都親眼見過。但是當拿到 iPhone4 時發現所有的app的UI元素仍然保持著原來的尺寸,而不是像windows那樣變成原來的一半。

這個技術難么?其實一點都不難。Apple 只是在物理像素之上又加了一個抽象層而已,Core Graphics 裡面使用的單位不是pixel,而是point,一個邏輯單位,程序員指定UI元素坐標和尺寸是用的都是point,然後 Core Graphics 根據物理解析度不同,再把 point 轉換成 pixel。iPhone 3G 上 1point == 1pixel,iPhone4、5、6 上 1point==2pixel,iPhone 6+、7+ 上 1point==2.88pixel。然後用 vector 方式保存線條和顏色過度,這樣可以無損縮放,確保了無論多高解析度看上去都很細膩,針對bitmap,因為是解析度相關的,不同解析度的屏幕使用不同資源,所以有了 1x 2x 3x 的圖片。

這個簡單的轉換就保證了不同 DPI 下的UI元素看起來的尺寸幾乎一樣,技術實現上很簡單,但是Apple考慮到了,微軟沒考慮到。於是產生了今天Windows和macOS(iOS)對高DPI支持上的差距。

Windows的GDI+採用了和Core Graphics 一樣的策略,但是已經來不及了。


mac的dpi系統也很坑,感覺是個hack而不是一個future proof的系統。

macOS的dpi就只能原生支持1x, 2x, 3x。

那麼你就會覺得奇怪了,我在系統設置里確實可以愉快地微調dpi啊,沒有你說的問題啊……我也曾經是這麼覺得的,直到有一天我發現屏幕上的所有東西都有些模糊。在確定不是我瞎了以後我發現mac的dpi系統可能比Windows更坑:任何非整數的dpi倍率都會導致界面模糊,只是屏幕解析度比較高,不太能發現。

這是macOS的DPI設置(late 2016 13" retina),除了我目前使用的,其他的模式下屏幕都會有或多或少的模糊。在那些模式下screen buffer(可以粗略理解為系統模擬的虛擬屏幕)和物理屏幕大小是不一樣的。拿default(looks like 1440x900)舉例:default是模擬了一個2880x1800的屏幕buffer,所有UI以2x繪製在這個buffer上。驗證方法也很簡單,用截圖工具截一張全屏幕截圖保存,會發現圖片大小是2880x1800。

但2016 macbook pro的屏幕的原生解析度並不是2880x1800而是2560x1600,所以必須最後再把這個buffer重新繪製到屏幕上,由於不是1比1的映射,這個過程是有損的。

直接導致的問題就是任何1px的線都不會以1px顯示,而是8/9px,可以用photoshop驗證一下,1px的線永遠是模糊的。

關鍵是Apple沒告訴你這個,把這個坑爹的模式設為default。我一直以為模糊是軟體的bug,沒想到是系統坑。

對於2016 macbook pro 13"只有介於Larger Text和Default之間的模式是1比1的映射,這也是我目前用的模式。


看了很多,說早期的時候微軟沒有考慮這些問題,這是事實。但是,微軟也發明了新的技術來支持高DPI,但是,連微軟自己的很多產品也還在用老的技術。蘋果自己發明了技術,自己就主力支持。所以,問題不是技術本身的事情。


問題並不是黑。

筆記本是4K屏,開250%縮放,效果非常棒。

然而,外接的顯示器是1080P,縮放100%,根本沒法看!

不過,MAC也好不到哪去,2016款MBP,外接1080P顯示器,也會糊,只是糊得沒有win那麼厲害罷了。


我個人認為 API 設計確實有問題。但是當時設計的時候根本沒有高 DPI 這個概念,所以不能怪微軟。

至於 macOS,蘋果的策略很激進,新的 API 不考慮兼容性。這可以說是對生態把控得好。如果把控不好的話,不兼容舊軟體不僅得罪開發者還得罪用戶。


GDI+ 開始,繪圖默認會自適應 DPI,但由於那時候屏幕相差不大,幾乎所有開發者都以繪出的不是真實像素計算不方便為由強制關閉了這個功能,時間一轉到了現在,放不放大你們自己設置里選,然後系統再告訴程序讓程序決定怎麼畫,Windows 也很無奈啊(扶額


解決對話框字體模糊 —— 這個和 DPI 有關,最近解決的問題

解決對話框字體模糊 - UMU Corporation


看上去是個技術題,實際上是個歷史題


應該算是歷史遺留問題導致現在解決會比較麻煩


舊程序沒有適配新版windows 嗎?不要拿windows 當Mac os x 或iPhone版看嗎,windows 歷史包袱很重的


微軟的優勢是兼容性做的好,但是兼容老版本就會出現大量應用其實沒有使用操作系統新特性,在Mac上這類應用會直接崩潰。

我用Mac好幾年了,很喜歡用,但是要承認每年都有程序升級不能用


不 是MacOS只顧消費者體驗 剝削開發者


推薦閱讀:

為什麼 Win 10 在幾乎沒開什麼程序的情況下 CPU 佔用率一直 100% 不降?
看看我這樣能否榨乾windows性能?——關於內存檔,文件讀寫,臨時文件
能否通過微軟賬號直接恢復以前電腦存放在桌面上的文件?
2017 年,Windows 7 有跟不上時代的地方嗎?
為什麼微軟至今不給Win10的虛擬桌面加上重命名的功能?

TAG:微軟Microsoft | RetinaDisplay | DPI | Windows10 | WindowsAPI |