SDL中文论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 3702|回复: 1
打印 上一主题 下一主题

[gui2] High DPI

[复制链接]

187

主题

346

帖子

2450

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2450
跳转到指定楼层
楼主
发表于 2016-5-16 22:07:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 ancientcc 于 2017-2-3 09:41 编辑

为什么会出现High DPI问题
  首先解释什么是DPI。DPI是Dots Per Inch缩写,表示每英寸点数,这里的点(Dot)指什么?对面向PC、手机、Pad编程的程序员来说,它等同像素(Pixel),于是DPI就等同PPI。
  当前流行的DPI是多少,让以两款电脑为例有个直观认识。一商务笔记本,13.3x8.3英寸屏,最大分辨率1920x1080,由它可算出水平、垂直DPI大概是144(1920/13.3)、130(1080/8.3)。苹果的一款MacBook Air,13.3x8.3英寸屏,最大分辨率1440x900,它水平、垂直DPI分别是108、108。
  接下说苹果营销出的一个概念:Retina屏(视网膜屏)。单从DPI这个参数来说,Retina屏是指它的DPI超过人眼能识别极限的最高DPI,我们不关心这个DPI是不是真的超过人眼分辨极限,但不得不承认的是,Retina屏的DPI的确是基本做到了普通屏的2倍。举个例子,当前一款MacBook Pro,13.3x8.3英寸屏,最大分辨率2560x1600,那它的水平、垂直DPI到达了192,基本是同期Air的两倍。
  现在来讨论同一数量像素下在Air和Pro分别是多少英寸。假设有一个按钮,程序设定它是360x360像素,那它在Air的实际尺寸是3.33x3.33英寸/8.46cm,在Pro则是1.87x1.87英寸/4.76cm。——到此对High DPI问题会有初步认识了:由于不同DPI,使得同一App在不同显示器上显示时会出现很不一样的物理尺寸。就拿这两款苹果笔记本,Air上看去正常的按钮,换Retina上就小很多。
  以上讨论是电脑屏,手机、Pad屏也一样存在DPI。例如iPhone6,对角线是4.7英寸(屏尺寸大概是4.09x2.3),它的水平、垂直DPI都是326。仍然以360x360按钮为例,在iPhone6上则变成1.1x1.1英寸(2.8cm)。以下表格小结了同一像素数在不同设备上的物理尺寸。
设备DPI像素数物理尺寸
MacBook Air1083603.33英寸/8.46cm
MacBook Pro1923601.87英寸/4.76cm
iPhone63263601.1英寸/2.8cm

  High DPI问题根源是用户在意的是物理单位表示的尺寸,程序员关注的是像素表示的尺寸,而关联物理单位和像素的DPI在各设备不同,使得要能让同一App在这些设备都能正常使用的话须进行额外处理。

解决High DPI方法:放大倍数(hdpi_scale)
  让思考这样一个问题,对同一个按钮,如何让在MacBook Air、MacBook Pro看去是同一物理尺寸?很快能想到办法,按钮的像素数在Pro时是Air像素数乘上2。办法虽然简单,但的确是各操作系统在使用的解决High DPI方法。
  那放大倍数到底是多少,有些是操作系统决定,像iOS,它规定iPhone 5/6是2。Android、Windows则由程序员自个定,不过为缩放图像简单,真正到显示时倍数要取整数。以下是Android定义的density(基本等同hdpi_scale)、DPI值之间关系,它用了一个等式:DPI = density * 160。
density名称DPI
0.75ldpi120
1.0mdpi160
1.5hdpi240
2.0xhdpi320
3.0xxhdpi480
4.0xxxhdpi640

  density是浮点数,转换到hdpi_scale可用四舍五入法,即小数部分>=0.4时向上取整,否则向下取整。
  对Windows系统一般取2,一来是考虑到它的DPI相比手机来说不是很大,二来Windows有个High DPI-unaware概念,对属于High DPI-unaware类型App,它会默认用2去放大。接下就让说说High DPI-unaware。Windows把App分为两类:High DPI-aware和High DPI-unaware,App可调用SetProcessDpiAwareness(Win8.1开始出现)这个API让自个归属到哪种类型。
  High DPI-unaware是告诉系统,我这App不支持高清屏,如遇上高清屏请通过DWM(Desktop Window Manager)虚拟化帮我去实现。此种方式的高DPI支持具体过程是这样的,比如当前系统的DPI是192(200%),程序运行时,系统会告诉你当前DPI仍然是96(100%),所以程序会仍然按照96的方式进行绘画,而且调用系统API时,得到的坐标、窗口尺寸都是按96来的。当我们画完之后,DWM再对整个窗口进行200%放大后画到屏幕上,这样看起来程序就自动支持高DPI了。 这种方式对那些非高清屏上开发却依旧要在清高清屏运行的App很有用,像在XP、Win7开发的App,正因为如此,App默认的工作方式是High DPI-unaware。
  这种方式看起来很美妙,但有严重缺点,经过放大后的内容看起来会变模糊,比如文字会有明显锯齿。既然DWM虚拟化用户效果不怎么好,那么很多时候就得向操系统宣称自个能支持高DPI,请直接告诉我真实DPI,至于遇到的屏是192还是108,我自个去处理,这种类型的App就是High DPI-aware。
  虽然不叫DWM,iOS也有类似这样功能的模块,它也可把App分为那两类。不过由于DWM会导致图像变模糊这个致命缺点,往后程序必将都是High DPI-aware。
  回到方法的源头,这会引入两个概念,配置尺寸和显示尺寸。
  • 配置尺寸(Config Size):定义控件时使用的尺寸。设定值时参考一条规则:能满足最小屏幕分辨率的DPI。
  • 显示尺寸(Display Size):控件最终显示出来的尺寸。是配置尺寸乘上放大倍数,在值上,放大倍数等于1时就是配置尺寸。
回复

使用道具 举报

187

主题

346

帖子

2450

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2450
沙发
 楼主| 发表于 2016-5-19 21:30:00 | 只看该作者
本帖最后由 ancientcc 于 2016-5-30 21:27 编辑

High DPI是窗口系统一部分,各平台以着自个的方式去实现,以下是Rose实现该子系统要点。首先,Rose在顶层分成两个过程,一是计算标称尺寸,二是渲染内容。

一、计算标称尺寸
首先要明确,calculate_best_size算出的就是显示尺寸。以下是原因。
  • 不能简单地在twidget::get_best_size()乘上hdpi_scale计算出显示尺寸。如果当前控件是复合控件中的子控件,它会使复合控件最终出来的尺寸超过hdpi_scale倍数。
  • 有些控件希望x、y、width、height中的一些参数不必放大,这种控制放在calculate_best_size之后就很做到了。

公式中的参数需要配置尺寸;公式计算出的值是配置尺寸。

1、cfg配置
计算标称尺寸要考虑的因素很多,当中一个是控件的定义块,定义块中的值都是配置尺寸。
控件字段
control(所有控件)window_width/height, default_width/height, max_width/height, text_extra_width/height, text_font_size
slider(滑块)minimum_positioner_length, maximum_positioner_length, left_offset, right_offset
report(报表)unit_width, unit_height, gap


窗口定义中的值是配置尺寸。
父块字段描述
[column]border_sizeborder_size *= hdpi_scale


提示定义中的值是配置尺寸。
  1. [tip_definition]
  2.         text_extra_width/height, text_font_size, vertical_gap
  3. [/tip_definition]
复制代码


2、计算
当控件内容有字符串时,要用它的结果计算标称尺寸,这个结果需要是hdpi_scale整数倍。
SDL_Rect tintegrate::get_size() const
{
        ......
        max_x = posix_ceil2(max_x, gui2::twidget::hdpi_scale);
        max_y = posix_ceil2(max_y, gui2::twidget::hdpi_scale);
}

计算场景中的控件,它首先是把屏幕的显示尺寸缩小到配置尺寸,用后者是计算,然后把结果放大到显示尺寸。
  1. gui/dialogs/theme.cpp
  2. calculate_relative_loc(const config& cfg, int screen_w, int screen_h)
  3. screen_w /= hdpi_scale;
  4. screen_h /= hdpi_scale;
  5. ......
  6. relative_loc = create_rect(relative_loc.x * hdpi_scale, relative_loc.y * hdpi_scale, relative_loc.w * hdpi_scale, relative_loc.h * hdpi_scale);

  7. set_resolution2(const config& cfg, int screen_w, int screen_h)
  8. screen_w /= hdpi_scale;
  9. screen_h /= hdpi_scale;
复制代码


二、渲染内容
gui2系统根据标称尺寸确定出渲染尺寸后,要开始渲染内容。
1、cfg配置
图元组成了内容,对每个图元,gui2都支给四个可用于公式的参数。dwidth、dheight、width、height。碰到不要放大的情况时,width/height可能不能被hdpi_scale整除。一个[image]控件,内容是一条高1像素的横线,1不可能被hdpi_scale整除。这意味着width/height必须接收显示尺寸。

图元字段描述
imagenamehdpi不是1时,它会在文件名加上后缀“@<hdpi_scale>”。举个例子,button2-botleft.png,当hdpi=2时,会先尝试使用button2-botleft@2.png,如果不存在则使用button2-botleft.png。
textfont_sizefont_size *= hdpi_scale
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|丽谷软件|libsdl.cn

GMT+8, 2024-4-27 12:17 , Processed in 0.066175 second(s), 19 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表