Qt OpenGL Font 的使用限制

| | 0 Comments| 13:21
Categories:

這篇是之前《使用 QGraphicsScene 繪製 widget、產生 OpenGL Texture》和《使用 QGraphicsScene 繪製 widget、產生 OpenGL Texture:續》這兩篇的延伸,主要是針對 Qt OpenGL 裡面,字型的問題做一些研究紀錄。

老實說,最初一直以為 Qt 應該都處理掉了,應該可以一下就弄完,結果沒想到牽扯的問題很多,一堆問題還是無解…

首先,這邊最基本的需求,就是使用 QOpenGLPaintDevice,透過 QGraphicsScene 把 Qt 的圖形介面(QWidget)以 offscreen 的形式、畫成一個 OpenGL Texture。

而現在碰到的主要問題,則是兩個部分:

  • 文字大小超過一定程度,會變成黑框
  • 部分地方的繪製效果不一致
  • High DPI 設定造成顯示結果與預期不同


Qt OpenGL 字體大小限制

文字會變黑框的問題,在 Heresy 來看算是滿頭大的…

在經過測試後,發現以 Heresy 使用的、預先建置好的 Qt 5.10 來說,在 96dpi 的環境下,只要用到大於等於 48pt 的文字,就會變成黑框。

下面就是測試程式的畫面:

其中,左上方是正常的 QWidget 使用方法,右上方則是丟到 QGraphicsScene 裡面,用 QGraphicsView 來顯示的方法。右下方時丟到 QGraphicsScene 裡面,然後畫成 QPixmap 後,再畫出來的結果;左下方則是使用 QOpenGLPaintDevice 繪製到 Frame buffer object 後,產生 OpenGL Texture 的結果(這個大小沒有做正確的控制,所以會有一些縮放的效果)。

可以看到,大部分的繪製方法的結果都是正常的,僅有左下方的文字在 48pt(含)以後有問題。

而在查了一下資料後,發現這類的問題其實早就有人回報過了(參考);後來又看了好一陣子,個人覺得應該是由於 Qt 預設的字形(glyph)快取不夠大所造成的;如果在建置 Qt 的時候,把設定調高,應該可以在某種程度上避免這個問題(其實也只是延後他發生)。(參考

老實說,如果只是這樣,到還算好;因為只要控制字體大小,就勉強可以解決了…畢竟,大型文字用到的機會其實不算多。

但是,真正的問題,是:一般指定給 Qt 的大小(包含文字、元件),在繪製時其實都還會考量很多東西(主要是 DPI),真正繪製時的大小通常不是指定的大小

像是 Heresy 之前是因為考量到這個貼圖是要用在虛擬實境中,解析到要更高,所以把它放大兩倍來繪製;而這樣的結果會變成右圖,可以看到,現在變成 24pt 開始就出問題了。

如果放大到四倍呢,那則就是會從 12pt 開始就出現問題。

會有這樣的狀況,就是因為 Qt 在繪製時,偵測到要繪製的大小比較大,所以為了繪製品質,會把字體大小也放大,所以變成本來應該沒問題的小字體、也出問題了。

而更糟糕的一點,是 Qt 為了因應 High DPI 的螢幕,所以其實會去偵測系統、螢幕的 DPI,而且也包含了 Windows 的縮放設定

也就是說,如果忽略了這些不同電腦可能會有不同參數的東西的話,就算針對開發用的電腦調整到好,換一台電腦可能也是沒有用的…

像下圖就是同一隻程式,在一台 Windows 縮放(設定、系統、顯示器中的「縮放與版面配置」)中設定為 150% 的顯示結果:

可以看到,這邊連 36pt 的文字也變黑框了。

如果是 200% 的話,他的結果就會和之前放大兩倍的狀況依樣,連 24pt 的字都變黑框了。

也就是說,當要使用 Qt OpenGL 來畫界面的時候,還需要考慮 DPI 的問題才行;完全不考慮的下場,就是在不同的環境會有有不同的結果。

另外,上面也可以看的出來,當 Windows 的縮放提高後,其實整個介面也就出問題了。不但按鈕變小了,文字也被大幅度地裁切,很多根本看不出來是什麼字了!

而這個 High DPI 的問題,其實一直是 Windows 的痛(參考);雖然 Qt 本身在架構上就有試著去支援(參考),但是在 Heresy 來看,還是有很多問題…由於這也是一個很大的題目,所以就先不在這邊討論了。

(附註:這邊的顯示結果都沒有開啟 Qt 的 High DPI Scaling)

不過個人在這邊覺得比較有趣的是,如果是透過 QGraphicsScene 來畫兩倍大的介面的話,實際上並不會有這麼多問題發生。不知道為什麼 Qt 不用這類的方法來解?

另外,雖然透過 QGraphicsScene 來畫兩倍大的介面時,不會有文字過大的問題,但是還是有可能發生文字排列不合適的問題;像右圖就是放大兩倍大後的「Volume」,可以看到,「m」和「e」之間的空隙明顯變得太小了…不過相較前面的問題,這個算是相當小的問題就是了。


使用 OpenGL 繪製的其他狀況

再來,在使用 OpenGL 繪製的時候,QGroupBox 在使用 OpenGL 繪製時,Groupbox 的標題(title)文字後面的框限不會正確地斷掉。

右圖就是一個例子,可以看到「FontSize 10pt」的文字後方,依舊有 groupbox 的灰色框線。在預設的配色下,這個問題還不算明顯,如果有需要換成其他的配色的話,這個問題可能會因為色彩的組合,被放大很多。

而這個問題在使用其他方法繪製時,都不會產生,只有在使用 QOpenGLPaintDevice 繪製時才會發生;這部分估計應該也是 Qt OpenGL 文字繪製時的問題了…

不過,由於這個問題目前只有看到發生在 GroupBox 上,所以某種程度上,應該還算是比較明確、比較好繞過去的了(就不要用這個元件…)。


目前看來,如果要能比較方便使用,應該需要:

  • 調整 QT_MAX_CACHED_GLYPH_SIZE 的數值,自己重新建置 Qt,這樣可以許允比較大的文字。
    • 但是實際上要建置 Qt 還滿麻煩的,實在不是很想這樣搞。
  • 根據系統的 DPI 縮放設定,來調整 Frame buffer 縮放的比例,這樣可以讓可用的字型大小一致。
    • 不過能使用的文字大小依舊很有限。
    • 但是遇到極端的系統的 DPI 縮放設定值(250%),問題還是很頭大。

之後,Heresy 其實也有試著去找看看,有沒有辦法強制忽略系統的 DPI 縮放設定,但是不是很順利…

目前看來,Qt 針對系統縮放設定的部分雖然是有可能可以去做相關設定的(參考),但是它基本上是沒有設計讓使用者可以在執行階段去覆寫的;而主要的設定則是跟著 QApplication 跑,也就是說一個程式就都是單一設定,不能針對特定 widget 修改,而且似乎也沒辦法在程式執行的過程中臨時修改。

也就是說,這邊應該沒辦法設計成讓程式在 Window 顯示的介面,與 offscreen 繪製給 VR 用的介面,採用不同的縮放參數設定…

所以,接下來的主要問題,可能還是要去研究 Qt 的 High DPI 顯示的部分了。(遠目

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *