精华内容
下载资源
问答
  • iOS: 如何正确的绘制1像素的线

    千次阅读 2015-06-29 10:30:00
    看了上述一通解释,我们了解了1像素宽的线条失真的原因,及解决办法。 至此问题貌似都解决了?再想想为什么在非Retina和Retina屏幕上调整位置时值不一样,前者为0.5Point,后者为0.25Point,那么scale为3的6 Plus...

    一、Point Vs Pixel

        iOS中当我们使用Quartz,UIKit,CoreAnimation等框架时,所有的坐标系统采用Point来衡量。系统在实际渲染到设置时会帮助我们处理Point到Pixel的转换。
        这样做的好处隔离变化,即我们在布局的事后不需要关注当前设备是否为Retina,直接按照一套坐标系统来布局即可。实际使用中我们需要牢记下面这一点:

    One point does not necessarily correspond to one physical pixel.

    1 Point的线在非Retina屏幕则是一个像素,在Retina屏幕上则可能是2个或者3个,取决于系统设备的DPI。
    iOS系统中,UIScreen,UIView,UIImage,CALayer类都提供相关属性来获取scale factor。原生的绘制技术天然的帮我们处理了scale factor,例如在drawRect:方法中,UIKit自动的根据当前运行的设备设置了正切的scale factor。所以我们在drawRect: 方法中绘制的任何内容都会被自动缩放到设备的物理屏幕上。
    基于以上信息可以看出,我们大部分情况下都不需要去关注pixel,然而存在部分情况需要考虑像素的转化。
    例如画1个像素的分割线
    看到这个问题你的第一想法可能是,直接根据当前屏幕的缩放因子计算出1 像素线对应的Point,然后设置线宽即可。
    代码如下:
    1.0f / [UIScreen mainScreen].scale
    表面上看着一切正常了,但是通过实际的设备测试你会发现渲染出来的线宽并不是1个像素。

    Why?

    为了获得良好的视觉效果,绘图系统通常都会采用一个叫“antialiasing(反锯齿)”的技术,iOS也不例外。
    显示屏幕有很多小的显示单元组成,可以接单的理解为一个单元就代表一个像素。如果要画一条黑线,条线刚好落在了一列或者一行显示显示单元之内,将会渲染出标准的一个像素的黑线。
    但如果线落在了两个行或列的中间时,那么会得到一条“失真”的线,其实是两个像素宽的灰线。如下图所示:



        Positions defined by whole-numbered points fall at the midpoint between pixels. For example, if you draw a one-pixel-wide vertical line from (1.0, 1.0) to (1.0, 10.0), you get a fuzzy grey line. If you draw a two-pixel-wide line, you get a solid black line because it fully covers two pixels (one on either side of the specified point). As a rule, lines that are an odd number of physical pixels wide appear softer than lines with widths measured in even numbers of physical pixels unless you adjust their position to make them cover pixels fully.
    官方解释如上,简单翻译一下:
    规定:奇数像素宽度的线在渲染的时候将会表现为柔和的宽度扩展到向上的整数宽度的线,除非你手动的调整线的位置,使线刚好落在一行或列的显示单元内。如何对齐呢?
        On a low-resolution display (with a scale factor of 1.0), a one-point-wide line is one pixel wide. To avoid antialiasing when you draw a one-point-wide horizontal or vertical line, if the line is an odd number of pixels in width, you must offset the position by 0.5 points to either side of a whole-numbered position. If the line is an even number of points in width, to avoid a fuzzy line, you must not do so.
    On a high-resolution display (with a scale factor of 2.0), a line that is one point wide is not antialiased at all because it occupies two full pixels (from -0.5 to +0.5). To draw a line that covers only a single physical pixel, you would need to make it 0.5 points in thickness and offset its position by 0.25 points. A comparison between the two types of screens is shown in Figure 1-4.
    翻译一下
    在非高清屏上,一个Point对应一个像素。为了防止“antialiasing”导致的奇数像素的线渲染时出现失真,你需要设置偏移0.5 Point。
    在高清屏幕上,要绘制一个像素的线,
    需要设置线宽为0.5个Point,同时设置偏移为0.25 Point。
    如果线宽为偶数Point的话,则不要去设置偏移,否则线条也会失真。
    如下图所示:


    看了上述一通解释,我们了解了1像素宽的线条失真的原因,及解决办法。
    至此问题貌似都解决了?再想想为什么在非Retina和Retina屏幕上调整位置时值不一样,前者为0.5Point,后者为0.25Point,那么scale为3的6 Plus设备又该调整多少呢?
    要回答这个问题,我们需要理解调整多少依旧什么原则。


    再回过头来看看这上面的图片,图片中每一格子代表一个像素,而顶部标记的则代码我们布局时的坐标。

    可以看到左边的非Retina屏幕,我们要在(3,0)这个位置画一条一个像素宽的竖线时,由于渲染的最小单位是像素,而(3,0)这个坐标恰好位于两个像素中间,此时系统会对坐标3左右两列的像素对填充,为了不至于线显得太宽,为对线的颜色淡化。那么根据上述信息我们可以得出,如果要画出一个像素宽的线,就得把绘制的坐标移动到(2.5, 0)或者(3.5,0)这个位置,这样系统渲染的时候刚好可以填充一列像素,也就是标准的一个像素的线。

    基于上面的分析,我们可以得出“Scale为3的6 Plus”设备如果要绘制1个像素宽的线条时,位置调整也应该是0.5像素,对应该的Point计算如下:

    (1.0f / [UIScreen mainScreen].scale) / 2;
    奉上一个画一像素线的一个宏:
    #define SINGLE_LINE_WIDTH           (1 / [UIScreen mainScreen].scale)
    #define SINGLE_LINE_ADJUST_OFFSET   ((1 / [UIScreen mainScreen].scale) / 2)
    使用代码如下:
    CGFloat xPos = 5;
    UIView *view = [[UIView alloc] initWithFrame:CGrect(x - SINGLE_LINE_ADJUST_OFFSET, 0, SINGLE_LINE_WIDTH, 100)];

    二、正确的绘制Grid线条

    贴上一个写的GridView的代码,代码中对Grid线条的奇数像素做了偏移,防止出现线条模糊的情况。
    SvGridView.h

    //
    //  SvGridView.h
    //  SvSinglePixel
    //
    //  Created by xiaoyong.cxy on 6/23/15.
    //  Copyright (c) 2015 smileEvday. All rights reserved.
    //
    #import @interface SvGridView : UIView
    /**
     * @brief 网格间距,默认30
     */
    @property (nonatomic, assign) CGFloat   gridSpacing;
    /**
     * @brief 网格线宽度,默认为1 pixel (1.0f / [UIScreen mainScreen].scale)
     */
    @property (nonatomic, assign) CGFloat   gridLineWidth;
    /**
     * @brief 网格颜色,默认蓝色
     */
    @property (nonatomic, strong) UIColor   *gridColor;
    @end

    SvGridView.m

    //
    //  SvGridView.m
    //  SvSinglePixel
    //
    //  Created by xiaoyong.cxy on 6/23/15.
    //  Copyright (c) 2015 smileEvday. All rights reserved.
    //
    #import "SvGridView.h"
    #define SINGLE_LINE_WIDTH           (1 / [UIScreen mainScreen].scale)
    #define SINGLE_LINE_ADJUST_OFFSET   ((1 / [UIScreen mainScreen].scale) / 2)
    @implementation SvGridView
    @synthesize gridColor = _gridColor;
    @synthesize gridSpacing = _gridSpacing;
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            self.backgroundColor = [UIColor clearColor];
             
            _gridColor = [UIColor blueColor];
            _gridLineWidth = SINGLE_LINE_WIDTH;
            _gridSpacing = 30;
        }
         
        return self;
    }
    - (void)setGridColor:(UIColor *)gridColor
    {
        _gridColor = gridColor;
         
        [self setNeedsDisplay];
    }
    - (void)setGridSpacing:(CGFloat)gridSpacing
    {
        _gridSpacing = gridSpacing;
         
        [self setNeedsDisplay];
    }
    - (void)setGridLineWidth:(CGFloat)gridLineWidth
    {
        _gridLineWidth = gridLineWidth;
         
        [self setNeedsDisplay];
    }
    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    - (void)drawRect:(CGRect)rect
    {
        CGContextRef context = UIGraphicsGetCurrentContext();
         
        CGContextBeginPath(context);
        CGFloat lineMargin = self.gridSpacing;
         
        /**
         *  https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html
         * 仅当要绘制的线宽为奇数像素时,绘制位置需要调整
         */
        CGFloat pixelAdjustOffset = 0;
        if (((int)(self.gridLineWidth * [UIScreen mainScreen].scale) + 1) % 2 == 0) {
            pixelAdjustOffset = SINGLE_LINE_ADJUST_OFFSET;
        }
         
        CGFloat xPos = lineMargin - pixelAdjustOffset;
        CGFloat yPos = lineMargin - pixelAdjustOffset;
        while (xPos < self.bounds.size.width) {
            CGContextMoveToPoint(context, xPos, 0);
            CGContextAddLineToPoint(context, xPos, self.bounds.size.height);
            xPos += lineMargin;
        }
         
        while (yPos < self.bounds.size.height) {
            CGContextMoveToPoint(context, 0, yPos);
            CGContextAddLineToPoint(context, self.bounds.size.width, yPos);
            yPos += lineMargin;
        }
         
        CGContextSetLineWidth(context, self.gridLineWidth);
        CGContextSetStrokeColorWithColor(context, self.gridColor.CGColor);
        CGContextStrokePath(context);
    }
    @end

    使用方法如下:

    SvGridView *gridView = [[SvGridView alloc] initWithFrame:self.view.bounds];
    gridView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    gridView.alpha = 0.6;
    gridView.gridColor = [UIColor greenColor];
    [self.view addSubview:gridView];

    三、一个问题

    这里我使用相同的比例缩放,对比一下。使用偏移量和不实用偏移量的差别:


    (上图没有使用偏移量)


    (上图使用了偏移量)

    差别还是很明显的


    三、一个问题

    好了,到这儿本文的全部知识就结束了,最后我还有一个问题。


    设计师为什么一定要一个像素的线?
    一个像素的线可能在非Retina设备上显示宽度看着合适,在Retina屏幕上显示可能会比较细。是不是一定需要一个像素的线,需要根据情况来处理。

    参考文档:
    https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html
    注:smileEvday保留本文的一切权利

    展开全文
  • 最近看了很多这方面的文章,能搜到的基本看了个遍,但感觉...虚拟像素,可以理解为“直觉”像素,CSS和JS使用的抽象单位,浏览器内的一切长度都是以CSS像素为单位的,CSS像素的单位是px。 1.2 注意 在CSS规范中

    最近看了很多这方面的文章,能搜到的基本看了个遍,但感觉还是似懂非懂,知道这个东西,很难说出这是个什么东西,先整理一些概念,慢慢消化,以后慢慢探索其中的原因。

    1、PX(CSS pixels)

    1.1 定义

    虚拟像素,可以理解为“直觉”像素,CSSJS使用的抽象单位,浏览器内的一切长度都是以CSS像素为单位的,CSS像素的单位是px

    1.2 注意

    CSS规范中,长度单位可以分为两类,绝对(absolute)单位以及相对(relative)单位。px是一个相对单位,相对的是设备像素(device pixel)。

    在同样一个设备上,每1个CSS像素所代表的物理像素是可以变化的(即CSS像素的第一方面的相对性); 

    在不同的设备之间,每1个CSS像素所代表的物理像素是可以变化的(即CSS像素的第二方面的相对性);

    1.3 那么PX到底是什么?

    px实际是pixel(像素)的缩写,根据 维基百科的解释,它是图像显示的基本单元,既不是一个确定的物理量,也不是一个点或者小方块,而是一个抽象概念。所以在谈论像素时一定要清楚它的上下文!一定要清楚它的上下文!一定要清楚它的上下文!

    不同的设备,图像基本采样单元是不同的,显示器上的物理像素等于显示器的点距,而打印机的物理像素等于打印机的墨点。而衡量点距大小和打印机墨点大小的单位分别称为ppidpi

    ppi:每英寸多少像素数,放到显示器上说的是每英寸多少物理像素及显示器设备的点距。

    dpi:每英寸多少点。

    关于打印机的点距我们不去关心,只要知道 当用于描述显示器设备时ppi与dpi是同一个概念 。

    1.4 CSS像素的真正含义

    由于不同的物理设备的物理像素的大小是不一样的,所以css认为浏览器应该对css中的像素进行调节,使得浏览器中 1css像素的大小在不同物理设备上看上去大小总是差不多 ,目的是为了保证阅读体验一致。为了达到这一点浏览器可以直接按照设备的物理像素大小进行换算,而css规范中使用**"参考像素"**来进行换算。

    1参考像素即为从一臂之遥看解析度为96DPI的设备输出(即1英寸96点)时,1点(即1/96英寸)的视角。它并不是1/96英寸长度,而是从一臂之遥的距离处看解析度为96DPI的设备输出一单位(即1/96英寸)时视线与水平线的夹角。通常认为常人臂长为28英寸,所以它的视角是:
    (1/96)in / (28in * 2 * PI / 360deg) = 0.0213度。

    由于css像素是一个视角单位,所以在真正实现时,为了方便基本都是根据设备像素换算的。浏览器根据硬件设备能够直接获取css像素

    1.5 举个栗子来理解css像素的相对性

    作为Web开发者,我们接触的更多的是用于控制元素样式的样式单位像素。这里的像素我们称之为CSS像素。

    CSS像素有什么特别的地方?我们可以借用quirksmode中的这个例子:

    假设我们用PC浏览器打开一个页面,浏览器此时的宽度为800px,页面上同时有一个400px宽的块级元素容器。很明显此时块状容器应该占页面的一半。

    但如果我们把页面放大(通过“Ctrl键”加上“+号键”),放大为200%,也就是原来的两倍。此时块状容器则横向占满了整个浏览器。

    吊诡的是此时我们既没有调整浏览器窗口大小,也没有改变块状元素的css宽度,但是它看上去却变大了一倍——这是因为我们把CSS像素放大为了原来的两倍。

    CSS像素与屏幕像素1:1同样大小时:

    CSS像素(黑色边框)开始被拉伸,此时1个CSS像素大于1个屏幕像素

    也就是说默认情况下一个CSS像素应该是等于一个物理像素的宽度的,但是浏览器的放大操作让一个CSS像素等于了两个设备像素宽度。在后面你会看到更复杂的情况,在高PPI的设备上,CSS像素甚至在默认状态下就相当于多个物理像素的尺寸。

    从上面的例子可以看出,CSS像素从来都只是一个相对值。

    2、DP(device pixels)

    2.1 定义

    设备像素(物理像素),顾名思义,显示屏是由一个个物理像素点组成的,通过控制每个像素点的颜色,使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位pt

    2.2 注意

    ptcss单位中属于真正的绝对单位,1pt = 1/72(inch),inch及英寸,而1英寸等于2.54厘米。

    不同的设备,其图像基本单位是不同的,比如显示器的点距,可以认为是显示器的物理像素。现在的液晶显示器的点距一般在0.25mm0.29mm之间。而打印机的墨点,也可以认为是打印机的物理像素,300DPI就是0.085mm600DPI就是0.042mm

    注意,我们通常所说的显示器分辨率,其实是指桌面设定的分辨率,而不是显示器的物理分辨率。只不过现在液晶显示器成为主流,由于液晶的显示原理与CRT不同,只有在桌面分辨率与物理分辨率一致的情况下,显示效果最佳,所以现在我们的桌面分辨率几乎总是与显示器的物理分辨率一致了。

    2.3 小知识

    小知识:屏幕普遍采用RGB色域(红、绿、蓝三个子像素构成),而印刷行业普遍使用CMYK色域(青、品红、黄和黑)

    2.4 设备像素(DP)与CSS像素之间的关系

    获得设备像素比(dpr)后,便可得知设备像素与CSS像素之间的比例。当这个比率为1:1时,使用1个设备像素显示1个CSS像素。当这个比率为2:1时,使用4个设备像素显示1个CSS像素,当这个比率为3:1时,使用9(3*3)个设备像素显示1个CSS像素。
    所以,有如下公式:

    DPR = 设备像素/CSS像素

    3、DIP(Device independent Pixel)

    设备独立像素,也称为逻辑像素,简称dip
    根据上述设备像素与CSS像素之间的关系、及DPR的官方定义,我们可以推断出:

    CSS像素 =设备独立像素 = 逻辑像素

    下面,还是引用 http://www.cnblogs.com/2050/p/3877280.html 文中的内容说明:

    在移动端浏览器中以及某些桌面浏览器中,window对象有一个devicePixelRatio属性,它的官方的定义为:设备物理像素和设备独立像素的比例,也就是 devicePixelRatio = 物理像素 / 独立像素
    CSS像素就可以看做是设备的独立像素,所以通过devicePixelRatio,我们可以知道该设备上一个css像素代表多少个物理像素。例如,在Retina屏的iphone上,devicePixelRatio的值为2,也就是说1个css像素相当于2个物理像素。但是要注意的是,devicePixelRato在不同的浏览器中还存在些许的兼容性问题,所以我们现在还并不能完全信赖这个东西,具体的情况可以看下这篇文章

    为什么是“每四个一组”?而且要让这四个一组来显示“原来屏幕的一个像素”?这大概就是 Retina 显示技术的一种表现吧。而这“每四个一组”的“大像素”,可以被称作“设备独立像素”,device independent pixel ,或者 density-independentpixel ,它可以是系统中的一个点,这个点代表一个可以由程序使用的虚拟像素,然后由相关系统转换为物理像素。

    “设备独立像素”也有人称为“CSS像素”,一种形象的说法,更倾向于表明与 CSS 中尺寸的对应。

    设备独立像素与物理像素的对应关系,可以这样看:

    类似的每四个一组的对应关系,也许正是 Retina 显示技术所做的。

    4、DPR(device pixels ratio)

    4.1 定义

    设备像素比(dpr 描述的是未缩放状态下,物理像素CSS像素的初始比例关系,计算方法如下图。

    4.2 理解

    设备像素比(dpr) 是指在移动开发中1个css像素占用多少设备像素,如2代表1个css像素用2x2个设备像素来绘制。

    设备像素比(dpr),公式为1px = (dpr)^2 * 1dp,可以理解为1px由多少个设备像素组成;

    5、PPI(pixels per inch)

    5.1 定义

    每英寸像素取值,更确切的说法应该是像素密度,也就是衡量单位物理面积内拥有像素值的情况。

    5.2 ppi是如何计算出来的呢?

    顾名思义,每英寸的像素点(设备像素),已知屏幕分辨率和主对角线的尺寸,则ppi等于
    以爱疯6为例:

    var 斜边尺寸 = V(1920^2+1080^2) V代表开根号 
    var ppi = 斜边尺寸/5.5 
    ppi = 401ppi
    

    我们知道,ppi越高,每英寸像素点越多,图像越清晰;我们可以类比物体的密度,密度越大,单位体积的质量就越大,ppi越高,单位面积的像素越多。

    5.3 ppi和dpr到底什么关系?

    毕竟这些参数是外国人先发明的,他们会优先选择自己熟悉的计量单位作为显示设备的工厂标准参数,因此ppi就用作显示设备的工业标准;

    告诉业界人士,ppi达到多少是高清屏,此时对应的dpr是多少,而不直接告诉你我现在的显示设备dpr是多少,毕竟人们直接听到像素分辨率会更加有反应。

    设备像素比与ppi相关,一般是ppi/160的整数倍:

    6、倍率与逻辑像素

    6.1 基本关系


    用iPhone 3gs和4s来举例。假设有个邮件列表界面,我们不妨按照PC端网页设计的思维来想象。3gs上大概只能显示4-5行,4s就能显示9-10行,而且每行会变得特别宽。但两款手机其实是一样大的。如果照这种方式显示,3gs上刚刚好的效果,在4s上就会小到根本看不清字。

    在现实中,这两者效果却是一样的。这是因为Retina屏幕把2x2个像素当1个像素使用。比如原本44像素高的顶部导航栏,在Retina屏上用了88个像素的高度来显示。导致界面元素都变成2倍大小,反而和3gs效果一样了。画质却更清晰。

    在以前,iOS应用的资源图片中,同一张图通常有两个尺寸。你会看到文件名有的带@2x字样,有的不带。其中不带@2x的用在普通屏上,带@2x的用在Retina屏上。只要图片准备好,iOS会自己判断用哪张,Android道理也一样。

    由此可以看出,苹果以普通屏为基准,给Retina屏定义了一个2倍的倍率(iPhone 6plus除外,它达到了3倍)。实际像素除以倍率,就得到逻辑像素尺寸。只要两个屏幕逻辑像素相同,它们的显示效果就是相同的。

    6.2 Retina显示屏

    这是一种显示技术,可以将把更多的像素点压缩至一块屏幕里,从而达到更高的分辨率并提高屏幕显示的细腻程度,这种分辨率在正常观看距离下足以使人肉眼无法分辨其中的单独像素。

    最先使用retina屏幕是iphone 4,屏幕分辨率为960 * 640(326ppi)。

    对比如下两幅图,可以清晰地看出是否 Retina 屏的显示差异:

    图2 iPhone 3GS


    图3 iPhone 4

    两代iPhone 的物理尺寸(屏幕宽高有多少英寸)是一样的,从上图可以看出,iphone 4的显示效果要明显好于iphone 3GS,虽然 iPhone 4 分辨率提高了,但它不同于普通的电脑显示器那样为了显示更多的内容,而是提升显示相同内容时的画面精细程度。这种提升方式是靠提升单位面积屏幕的像素数量,即像素密度来提升分辨率,这样做的主要目的是为了提高屏幕显示画面的精细程度。以第三代 MacBook Pro with Retina Display为例, 工作时显卡渲染出的2880x1880个像素每四个一组,输出原来屏幕的一个像素显示的大小区域内的图像。这样一来,用户所看到的图标与文字的大小与原来的1440x900分辨率显示屏相同,但精细度是原来的4倍。

    注意:在桌面显示器中,我们调整了显示分辨率,比如从 800 * 600 调整到 1024 * 768 时,屏幕的文字图标会变小,显示的内容更多了。但 Retina 显示方式不会产生这样的问题,或者说, Retina 显示技术解决的是显示画面精细程度的问题,而不是解决显示内容容量的问题。

    7、分辨率、像素和屏幕尺寸

    PPI 说的是像素密度,而分辨率说的是块屏幕的像素尺寸,譬如说 1334*750 就是 iPhone(6~7)的分辨率,说 iPhone(6~7)的分辨率是 326 是错误的表述,326 是它的像素密度,单位是 PPI

    询问别人一粒像素有多大是一个非常鸡贼的问题(小心面试遇到这样的题),虽然我们说像素是构成屏幕的发光的点,是物理的,但是像素在脱离了屏幕尺寸之后是没有大小可言的,你可以将 1920 * 1080 颗像素放到一台 40 寸的小米电视机里面,也可以将同样多的像素全部塞到一台 5.5 寸的 iPhone7 Plus 手机里面去,那么对于 40 寸的电视而言,每个像素颗粒当然会大于 5.5 寸的手机的像素。

    所以光看屏幕的分辨率对于设计师来说是不具备多少实际意义的,通过分辨率计算得出的像素密度(PPI)才是设计师要关心的问题,我们通过屏幕分辨率和屏幕尺寸就能计算出屏幕的像素密度的。

    再次使用 iPhone(6~7)作为例子。我们知道该屏幕的横向物理尺寸为 2.3 英寸 ,且横向具有 750 颗像素,根据下面的公式,我们能够算出 iPhone(6~7)的屏幕是 326 PPI,意为每寸存在 326 颗像素。

    其实不论我们怎么除,计算得出来的像素密度(PPI)都会是这个数,宽存在像素除以宽物理长度,高存在像素除以高物理长度,得数都接近于 326。

    8、Viewport

    8.1 PPK的关于三个viewport的理论

    ppk大神对于移动设备上的viewport有着非常多的研究(第一篇第二篇第三篇),有兴趣的同学可以去看一下,本文中有很多数据和观点也是出自那里。ppk认为,移动设备上有三个viewport。

    首先,移动设备上的浏览器认为自己必须能让所有的网站都正常显示,即使是那些不是为移动设备设计的网站。但如果以浏览器的可视区域作为viewport的话,因为移动设备的屏幕都不是很宽,所以那些为桌面浏览器设计的网站放到移动设备上显示时,必然会因为移动设备的viewport太窄,而挤作一团,甚至布局什么的都会乱掉。也许有人会问,现在不是有很多手机分辨率都非常大吗,比如768x1024,或者1080x1920这样,那这样的手机用来显示为桌面浏览器设计的网站是没问题的吧?前面我们已经说了,css中的1px并不是代表屏幕上的1px,你分辨率越大,css中1px代表的物理像素就越多,devicePixelRatio的值也越大,这很好理解,因为你分辨率增大了,但屏幕尺寸并没有变大多少,必须让css中的1px代表更多的物理像素,才能让1px的东西在屏幕上的大小与那些低分辨率的设备差不多,不然就会因为太小而看不清。所以在1080x1920这样的设备上,在默认情况下,也许你只要把一个div的宽度设为300多px(视devicePixelRatio的值而定),就是满屏的宽度了。回到正题上来,如果把移动设备上浏览器的可视区域设为viewport的话,某些网站就会因为viewport太窄而显示错乱,所以这些浏览器就决定默认情况下把viewport设为一个较宽的值,比如980px,这样的话即使是那些为桌面设计的网站也能在移动浏览器上正常显示了。ppk把这个浏览器默认的viewport叫做 layout viewport
       
    这个layout viewport的宽度可以通过document.documentElement.clientWidth 来获取。

    然而,layout viewport 的宽度是大于浏览器可视区域的宽度的,所以我们还需要一个viewport来代表 浏览器可视区域的大小,ppk把这个viewport叫做 visual viewport 。visual viewport的宽度可以通过window.innerWidth 来获取,但在Android 2, Oprea mini 和 UC 8中无法正确获取。

    现在我们已经有两个viewport了:layout viewport 和  visual viewport。但浏览器觉得还不够,因为现在越来越多的网站都会为移动设备进行单独的设计,所以必须还要有一个能完美适配移动设备的viewport。所谓的完美适配指的是,首先不需要用户缩放和横向滚动条就能正常的查看网站的所有内容;第二,显示的文字的大小是合适,比如一段14px大小的文字,不会因为在一个高密度像素的屏幕里显示得太小而无法看清,理想的情况是这段14px的文字无论是在何种密度屏幕,何种分辨率下,显示出来的大小都是差不多的。当然,不只是文字,其他元素像图片什么的也是这个道理。ppk把这个viewport叫做 ideal viewport,也就是第三个viewport——移动设备的理想viewport

    ideal viewport并没有一个固定的尺寸,不同的设备拥有有不同的ideal viewport。所有的iphoneideal viewport宽度都是320px,无论它的屏幕宽度是320还是640,也就是说,在iphone中,css中的320px就代表iphone屏幕的宽度。

    但是安卓设备就比较复杂了,有320px的,有360px的,有384px的等等,关于不同的设备ideal viewport的宽度都为多少,可以到http://viewportsizes.com去查看一下,里面收集了众多设备的理想宽度。

    再总结一下:ppk把移动设备上的viewport分为 layout viewport 、 visual viewport 和 ideal viewport 三类,其中的ideal viewport是最适合移动设备的viewportideal viewport的宽度等于移动设备的屏幕宽度,只要在css中把某一元素的宽度设为ideal viewport的宽度(单位用px),那么这个元素的宽度就是设备屏幕的宽度了,也就是宽度为100%的效果。ideal viewport 的意义在于,无论在何种分辨率的屏幕下,那些针对ideal viewport而设计的网站,不需要用户手动缩放,也不需要出现横向滚动条,都可以完美的呈现给用户。

     

    8.2 利用meta标签对viewport进行控制

    移动设备默认的viewportlayout viewport,也就是那个比屏幕要宽的viewport,但在进行移动设备网站的开发时,我们需要的是ideal viewport。那么怎么才能得到ideal viewport呢?这就该轮到meta标签出场了。

    我们在开发移动设备的网站时,最常见的的一个动作就是把下面这个东西复制到我们的head标签中:

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">

    meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。也许允不允许用户缩放不同的网站有不同的要求,但让viewport的宽度等于设备的宽度,这个应该是大家都想要的效果,如果你不这样的设定的话,那就会使用那个比屏幕宽的默认viewport,也就是说会出现横向滚动条。

    这个name为viewportmeta标签到底有哪些东西呢,又都有什么作用呢?

    meta viewport 标签首先是由苹果公司在其safari浏览器中引入的,目的就是解决移动设备的viewport问题。后来安卓以及各大浏览器厂商也都纷纷效仿,引入对meta viewport的支持,事实也证明这个东西还是非常有用的。

    在苹果的规范中,meta viewport 有6个属性(暂且把content中的那些东西称为一个个属性和值),如下:

    这些属性可以同时使用,也可以单独使用或混合使用,多个属性同时使用时用逗号隔开就行了。

    此外,在安卓中还支持  target-densitydpi  这个私有属性,它表示目标设备的密度等级,作用是决定css中的1px代表多少物理像素

    特别说明的是,当 target-densitydpi=device-dpi 时, css中的1px会等于物理像素中的1px。

    因为这个属性只有安卓支持,并且安卓已经决定要废弃<strike>target-densitydpi</strike>  这个属性了,所以这个属性我们要避免进行使用  。

    8.3 把当前的viewport宽度设置为 ideal viewport 的宽度

    要得到ideal viewport就必须把默认的layout viewport的宽度设为移动设备的屏幕宽度。因为meta viewport中的width能控制layout viewport的宽度,所以我们只需要把width设为width-device这个特殊的值就行了。

    <meta name="viewport" content="width=device-width">

    下图是这句代码在各大移动端浏览器上的测试结果:

    可以看到通过width=device-width,所有浏览器都能把当前的viewport宽度变成ideal viewport的宽度,但要注意的是,在iphone和ipad上,无论是竖屏还是横屏,宽度都是竖屏时ideal viewport的宽度。

    这样的写法看起来谁都会做,没吃过猪肉,谁还没见过猪跑啊~,确实,我们在开发移动设备上的网页时,不管你明不明白什么是viewport,可能你只需要这么一句代码就够了。

    可是你肯定不知道

    <meta name="viewport" content="initial-scale=1">

    这句代码也能达到和前一句代码一样的效果,也可以把当前的的viewport变为 ideal viewport

    呵呵,傻眼了吧,因为从理论上来讲,这句代码的作用只是不对当前的页面进行缩放,也就是页面本该是多大就是多大。那为什么会有 width=device-width 的效果呢?

    要想清楚这件事情,首先你得弄明白这个缩放是相对于什么来缩放的,因为这里的缩放值是1,也就是没缩放,但却达到了 ideal viewport 的效果,所以,那答案就只有一个了,缩放是相对于 ideal viewport来进行缩放的,当对ideal viewport进行100%的缩放,也就是缩放值为1的时候,不就得到了 ideal viewport 吗?事实证明,的确是这样的。下图是各大移动端的浏览器当设置了<meta name="viewport" content="initial-scale=1">后是否能把当前的viewport 宽度变成 ideal viewport 的宽度的测试结果。

    测试结果表明 initial-scale=1 也能把当前的 viewport 宽度变成 ideal viewport 的宽度,但这次轮到了windows phone 上的IE 无论是竖屏还是横屏都把宽度设为竖屏时 ideal viewport 的宽度。但这点小瑕疵已经无关紧要了。

    但如果 width 和 initial-scale=1 同时出现,并且还出现了冲突呢?比如:

    <meta name="viewport" content="width=400, initial-scale=1">

    width=400 表示把当前 viewport 的宽度设为400pxinitial-scale=1 则表示把当前 viewport 的宽度设为ideal viewport的宽度,那么浏览器到底该服从哪个命令呢?是书写顺序在后面的那个吗?不是。当遇到这种情况时,浏览器会取它们两个中较大的那个值。例如,当width=400ideal viewport 的宽度为320时,取的是400;当width=400, ideal viewport的宽度为480时,取的是ideal viewport的宽度。(ps:在uc9浏览器中,当initial-scale=1时,无论width属性的值为多少,此时viewport的宽度永远都是ideal viewport的宽度)

    最后,总结一下,要把当前的viewport宽度设为ideal viewport的宽度,既可以设置 width=device-width,也可以设置 initial-scale=1,但这两者各有一个小缺陷,就是iphone、ipad以及IE 会横竖屏不分,通通以竖屏的ideal viewport宽度为准。所以,最完美的写法应该是,两者都写上去,这样就 initial-scale=1 解决了 iphone、ipad的毛病,width=device-width则解决了IE的毛病:

    <meta name="viewport" content="width=device-width, initial-scale=1">

    8.4 关于meta viewport的更多知识

    8.4.1 关于缩放以及initial-scale的默认值

    首先我们先来讨论一下缩放的问题,前面已经提到过,缩放是相对于 ideal viewport 缩放的,缩放值越大,当前viewport的宽度就会越小,反之亦然。例如在iphone中,ideal viewport 的宽度是320px,如果我们设置 initial-scale=2 ,此时 viewport 的宽度会变为只有160px了,这也好理解,放大了一倍嘛,就是原来1px的东西变成2px了,但是1px变为2px并不是把原来的320px变为640px了,而是在实际宽度不变的情况下,1px变得跟原来的2px的长度一样了,所以放大2倍后原来需要320px才能填满的宽度现在只需要160px就做到了。因此,我们可以得出一个公式:

    visual viewport宽度 = ideal viewport宽度 / 当前缩放值

    当前缩放值 = ideal viewport宽度 / visual viewport宽度

    ps: visual viewport 的宽度指的是浏览器可视区域的宽度。

    大多数浏览器都符合这个理论,但是安卓上的原生浏览器以及IE有些问题。安卓自带的webkit浏览器只有在 initial-scale = 1 以及没有设置width属性时才是表现正常的,也就相当于这理论在它身上基本没用;而IE则根本不甩initial-scale这个属性,无论你给他设置什么,initial-scale表现出来的效果永远是1。

    好了,现在再来说下 initial-scale 的默认值问题,就是不写这个属性的时候,它的默认值会是多少呢?很显然不会是1,因为当 initial-scale = 1 时,当前的 layout viewport 宽度会被设为 ideal viewport 的宽度,但前面说了,各浏览器默认的 layout viewport 宽度一般都是980啊,1024啊,800啊等等这些个值,没有一开始就是 ideal viewport 的宽度的,所以 initial-scale 的默认值肯定不是1。安卓设备上的 initial-scale 默认值好像没有方法能够得到,或者就是干脆它就没有默认值,一定要你显示的写出来这个东西才会起作用,我们不管它了,这里我们重点说一下iphone和ipad上的 initial-scale 默认值。

    根据测试,我们可以在iphone和ipad上得到一个结论,就是无论你给 layout viewpor 设置的宽度是多少,而又没有指定初始的缩放值的话,那么iphone和ipad会自动计算 initial-scale 这个值,以保证当前 layout viewport 的宽度在缩放后就是浏览器可视区域的宽度,也就是说不会出现横向滚动条。比如说,在iphone上,我们不设置任何的 viewport meta 标签,此时 layout viewport 的宽度为980px,但我们可以看到浏览器并没有出现横向滚动条,浏览器默认的把页面缩小了。根据上面的公式,当前缩放值 = ideal viewport宽度 / visual viewport宽度,我们可以得出:

    当前缩放值 = 320 / 980

    也就是当前的 initial-scale 默认值应该是 0.33这样子。当你指定了 initial-scale 的值后,这个默认值就不起作用了。

    总之记住这个结论就行了:在iphone和ipad上,无论你给viewport设的宽的是多少,如果没有指定默认的缩放值,则iphone和ipad会自动计算这个缩放值,以达到当前页面不会出现横向滚动条(或者说viewport的宽度就是屏幕的宽度)的目的。

    8.4.2 动态改变meta viewport标签

    第一种方法

    可以使用 document.write 来动态输出 meta viewport 标签,例如:

    document.write('<meta name="viewport" content="width=device-width,initial-scale=1">')

    第二种方法

    通过 setAttribute 来改变

    <meta id="testViewport" name="viewport" content="width = 380">
    <script>
    var mvp = document.getElementById('testViewport');
    mvp.setAttribute('content','width=480');
    </script>

    安卓2.3自带浏览器上的一个 bug

    <meta name="viewport" content="width=device-width">
    
    <script type="text/javascript">
    alert(document.documentElement.clientWidth); //弹出600,正常情况应该弹出320
    </script>
    
    <meta name="viewport" content="width=600">
    
    <script type="text/javascript">
    alert(document.documentElement.clientWidth); //弹出320,正常情况应该弹出600
    </script>

    测试的手机 ideal viewport 宽度为320px,第一次弹出的值是600,但这个值应该是第行meta标签的结果啊,然后第二次弹出的值是320,这才是第一行meta标签所达到的效果啊,所以在安卓2.3(或许是所有2.x版本中)的自带浏览器中,对 meta viewport 标签进行覆盖或更改,会出现让人非常迷糊的结果。

    最后我们来看一个栗子来加深上面概念的印象:

    一只笔的像素如下:

    这只笔在屏幕c,d,e下的显示效果如下:

    看到同一张图片在各屏幕显示大小不一。
    我们希望不同屏幕显示图片的大小要一致。
    我们要计算图片缩放比例。
    计算公式:
    (图片逻辑像素大小px1) / (图片缩放后实际像素大小px2) = (设备像素dp) / (设备独立像素dips)
    px2 = px1 * (dp / dips)
    px2 = px1 * dpr
    此时,这只笔在屏幕c,d,e下的显示效果如下:

    通过上面的我们可以看到,不同的 DPR (设备像素比)要想显示大小一样,必须准备三张不同分辨率的图片,那么,我想一张图片就在三种不同的屏幕下显示一样的大小,能做到吗?当然能做到,这就需要缩放了,要自己计算缩放多麻烦,那有没有一种简单的方式呢?当然有,那就是你在熟悉不过的px,你会发现设置图片宽度为50px以后,在各个移动终端的大小看起来都一样,这是什么原因呢。

    按照 CSS 规范的定义,CSS 中的 px 是一个相对长度,它相对的,是 viewing device 的分辨率。这个viewing device,通常就是电脑显示器。典型的电脑显示器的分辨率是96DPI,也就是1像素为1/96英寸(实际上,假设我们的显示器分辨率都与物理分辨率一致,而液晶点距其实是0.25mm到0.29mm之间,所以不太可能是正好1/96英寸,而只是接近)。

    一般来说,px 就是对应设备的物理像素,然而如果输出设备的解析度与电脑显示器大不相同,输出效果就会有问题。例如打印机输出到纸张上,其解析度比电脑屏幕要高许多,如果不缩放,直接使用设备的物理像素,那电脑上的照片由 600DPI 的打印机打出来就比用显示器看小了约6倍。

    所以 CSS 规定,在这种情况下,浏览器应该对像素值进行缩放调节,以保持阅读体验的大体一致。也就是要保持一定像素的长度在不同设备输出上看上去的大小总是差不多。

    怎样确保这一点呢?直接按照设备物理像素的大小进行换算当然是一个方式,但是CSS考虑得更多,它建议,转换应按照“参考像素”(reference pixel)来进行。

    眼睛看到的大小,取决于可视角度。而可视角度取决于物体的实际大小以及物体与眼睛的距离。10米远处一个1米见方的东西,与1米远处的10厘米见方的东西,看上去的大小差不多是一样的,所谓一叶障目不见泰山,讲的就是这个常识。

    因此CSS规范使用视角来定义“参考像素”,1参考像素即为从一臂之遥看解析度为96DPI的设备输出(即1英寸96点)时,1点(即1/96英寸)的视角。

    请注意这个差别——CSS规范定义的参考像素并不是1/96英寸,而是1/96英寸在一臂之遥的看起来的视角。通常认为常人臂长为28英寸,所以其视角可以计算出来是0.0213度。(即(1/96)in / (28in * 2 * PI / 360deg) )

    我们在使用不同设备输出时,眼睛与设备输出的典型距离是不同的。比如电脑显示器,通常是一臂之距,而看书和纸张时(对应于打印机的设备输出),则通常会更近一些。看电视时则会更远,比如一般建议是电视机屏幕对角线的2.5到3倍长——如果你是个42'彩电,那就差不多是3米远。看电影的话……我就不知道多远了,您自己量吧。

    因此,1参考像素:
    对于电脑显示器是0.26mm(即1/96英寸);
    对于激光打印机是0.20mm(假设阅读距离通常为55cm,即21英寸);

    而换算时,对于300DPI的打印机(即每个点是1/300英寸),1px通常会四舍五入到3dots,也就是0.25mm左右;而对于600DPI的打印机,则可能四舍五入到5dots,也就是0.21mm。

    上图中,左边的屏幕(可以认为是电脑屏幕)的典型视觉距离是71厘米即28英寸,其1px对应了0.28mm;
    而右边的屏幕(可以认为是你的42寸高清电视)的典型视觉距离是3.5米即120英寸,其1px对应1.3mm。42寸的1080p电视,分辨率是1920*1080,则其物理像素只有0.5mm左右,可见确实是高清哦。

    综上,px 是一个相对单位,而且在特定设备上总是一个近似值(原则是尽量接近参考像素)。

    然而,如果你把绝对单位理解为对输出效果的绝对掌控,事情却大相径庭。就网页输出的最主要对象——电脑屏幕来说,px 可被视为一个基准单位——与桌面分辨率一致,如果是液晶屏,则几乎总是与液晶屏物理分辨率一致——也就是说网页设计者设定的1px,就是“最终看到这个网页的用户的显示器上的1个点距”!反倒是那些绝对单位,其实一点也不绝对。


    展开全文
  • 怎么理解物理像素和CSS像素

    千次阅读 2017-09-25 09:54:48
    后来随着技术的发展,移动设备的屏幕像素密度越来越高,从iphone4开始,苹果公司便推出了所谓的Retina屏,分辨率提高了一倍,变成640x960,但屏幕尺寸却没变化,这就意味着同样大小的屏幕上,像素却多了一倍,这时,...

    很不理解这段话:

    后来随着技术的发展,移动设备的屏幕像素密度越来越高,从iphone4开始,苹果公司便推出了所谓的Retina屏,分辨率提高了一倍,变成640x960,但屏幕尺寸却没变化,这就意味着同样大小的屏幕上,像素却多了一倍,这时,一个css像素是等于两个物理像素的。

    还有一个因素也会引起css中px的变化,那就是用户缩放。例如,当用户把页面放大一倍,那么css中1px所代表的物理像素也会增加一倍;反之把页面缩小一倍,css中1px所代表的物理像素也会减少一倍。

    为什么放大后CSS里面1PX对应的物理像素反而增加了?不应该是减少了吗?

    解释:

    0

    只讨论宽或高,面积自己乘。
    用户把页面放大(手指向外滑动,看到元素变大)一倍,那么css中1px所代表的物理像素也会增加一倍。
    1.100px的div不管放大缩小永远都是100px,因为这就是元素单位。
    2.200物理像素的设备,不管放大缩小永远都是200物理像素,因为屏幕硬件是死的。
    3.200物理像素刚好放下两个100px的div时,1px=1物理像素。
    4.页面放大,200物理像素只能看到1个100px的div时,请问1px等于多少物理像素。

    展开全文
  • 像素显示

    千次阅读 2013-06-19 00:08:30
    来源:... 2009年1月20日 发表评论 阅读评论 ...被拆分的像素:当像素不是一个像素的时候 LCD显示器上最小的图像单元(一个”像素”)实际上是由三个”亚像素(sub-pixel)”组成的:一个红色、一

    来源:http://www.xieyidian.com/1057

    2009年1月20日 发表评论 阅读评论

    这是一篇翻译文章,原文地址:http://www.grc.com/cleartype.htm

    工作原理

    被拆分的像素:当像素不是一个像素的时候

    LCD显示器上最小的图像单元(一个”像素”)实际上是由三个”亚像素(sub-pixel)”组成的:一个红色、一个绿色,还有一个蓝色(R-G-B)。这三个亚像素组合在一起就形成了我们看到的一个像素。

    我们用眼睛看到的一个像素是这样的:

    但实际上将它放大后我们看到的内容是这样的:

    这意味着对于水平分辨率为800像素的LCD显示器,实际上在水平方向上有800个红色、绿色和蓝色的亚像素,交替排列(R-G-B-R-G-B-R-G-B……),因此横向上的总共有2400个单色的亚像素。

    如果我们的眼睛看到一条白线:

    实际上这条白线被显示为:

    因此这意味着,如果我们单独看待实际的亚像素,也就是说,忽略亚像素不同的颜色,那么实际上这条线在水平方向上的分辨率将会是实际LCD上显示的分辨率的三倍。

    但是人眼并不能”看见”不同亚像素之间颜色的区别,因为我们的视觉系统会有意将这三种主要的颜色混合为中间色。你可能已经想到了,我们的眼睛只能识别这三种颜色,而对其他颜色的感知都是通过这些颜色的混色产生的。

    这有什么用?看看下面的例子 . .

    假设我们需要绘制一个具有倾斜对角线边缘的对象。

    如果使用标准的”整块”白像素,那么最佳情况也只能创建出锯齿状的边缘。

    所有数字显示系统与生俱来的”像素化”工作原理都导致只能创建类似左侧的显示结果(术语上这叫空间采样,Spatial Sampling)。

    但是对于LCD面板,我们可以充分利用每个LCD显示像素中都有的独立的R-G-B亚像素。

    因此,如做图所示,通过使用非白色的像素绘制对象的边缘,我们可以创建出更加平滑的边缘,减小锯齿的存在。

    虽然对象边缘的像素不是白色的,不过我们的眼睛依然会将其当作相对的白色对待,这是因为亚像素的色彩是和其他原色毗邻的(从上图中就可以看出来)。

    因此我们就得到了一个纯白色具有平滑边缘的对象。

    这和增强显示质量有什么关系

    在数字显示设备上增强小字体的显示质量有很大难度,因此字体设计人员总是希望能有更高分辨率的显示设备。

    例如,这是一个微软的标准Times New Roman 字体显示的8pt的常规(非斜体、非粗体)字母”A”,如果仔细看这个字母就可以发现,它很不清楚,而且不如同样的字打印到纸上后锐利。

    当我们放大显示以像素为单位的字母”A”的时候,我们只能看到一些色块,根本无法识别这是什么。

    从图中可以看出,字体设计人员似乎很希望有真正的斜线,但是限于显示器的分辨率,不得不使用一些折衷的方式,看左侧的图片就知道了。

    在上述问题之后,人们开发出一种”抗锯齿”技术,字体设计人员会使用灰色的渐变效果显示像素的”一部分”。这样做的目的在于,我们的眼睛会将两个毗邻的灰色像素看作是居中的一个。

    然而,这种技术的作用并不大。对于小尺寸字体,抗锯齿功能会让文字模糊并且降低可读性。

    但如果垂直分辨率也可以分成三部分,那就有办法解决了。

    如果在垂直方向上有三倍的像素可用,那么字体设计人员就可以创建出的效果接近纸张上的印刷效果的显示字体。

    (下面我会告诉你在LCD面板上这种效果到底是怎样的)

    通过从毗邻的完整像素那里”借来”亚像素,我们可以用是以前三倍垂直分辨率的方式优化文字的显示。

    在这种情况下我们的眼睛只能看到白色和黑色,这是因为”借来”的亚像素总是显示为临近像素颜色的互补色,因此我们的眼睛会将这些亚像素看作是白色。

    亚像素字体平滑还有其他三个好处

    字符间距调整

    “间距调整(Kerning)”是指对字符之间的间隔的处理,间距处理的好的字体可以通过平滑字体的密度以增强可读性。

    但是和大尺寸文字相比,小尺寸文字的间距处理是相当麻烦的:如果两个字符之间只有一个像素的间隔似乎有些拥挤,而间隔太多像素又显得有些松散。但是在我们看来,亚像素平滑可以让字符之间的空间缩短为三分之一个像素,这极大增强了文字的可读性。

    字体加粗

    字体设计师讨厌的还有粗体字。作为计算机用户,你可能已经注意到了,有时候粗体显得太”粗”了(比预想中的粗),而有时候似乎又粗的不明显。

    这也是由水平像素之间较大的宽度导致的:把字符加粗一个像素的宽度会使得字符太粗,但是不增加这个像素则显得似乎没有什么变化。亚像素平滑可以让字体设计师设计字体以创建出最恰当的效果。

    字体倾斜

    印刷出来的斜体字似乎显得很漂亮,而且可以用于突出显示某些文字,但同时又比粗体字更具有可读性。但是数字系统在处理斜体字的时候则会遇到很大的麻烦,因为所有文字都倾斜了,而从中也可以看出大像素在处理倾斜文字时的不足。

    这时候,亚像素平滑就可以通过调整每个像素的位置和宽度的方式完美完成文字的平滑工作,这样我们就可以更加广泛地利用斜体字。

    亚像素平滑到底是什么样的

    对眼睛看来更加平滑

    虽然你可以使用任何显示设备查看下列图像,不过CRT显示器并不能实现亚像素,因此在CRT显示器上并不能看到亚像素抗锯齿和平滑功能,除非使用LCD显示器观看。

    另外,正如我在下一节”亚像素平滑的不足”中指出的,并非所有LCD都有同样的线性R-G-B亚像素排列顺序(有些是B-G-R)。因此,我准备了两个图像,一个是针对R-G-B亚像素顺序的,另一个是针对B-G-R的,符合你LCD显示器类型的图像效果会更好,而不符合的那一个则可能看起来很糟糕,并且边缘会出现杂色:

    传统的整像素图形

    传统的整像素抗锯齿

    R-G-B LCD亚像素平滑

    B-G-R LCD亚像素平滑

    不管使用LCD或CRT显示器观看,你都应该可以看见传统的整像素抗锯齿(上面第二张图片)是如何平滑边缘的锯齿的,不过所需的代价则是图像的模糊,就像失去了焦点。可以想象,”散焦”后的效果已经偏离了增强显示质量提高可读性的本意。因此,整像素抗锯齿并不是用于解决小尺寸文字平滑的好的方案。

    透过玻璃看到的结果

    如果你没有LCD显示器或者珠宝匠人的放大镜,我将我的Toshiba Tecra的屏幕显示内容拍摄后放大显示,下面两张图片是不同平滑方式下看到的结果:

    整像素平滑

    亚像素平滑

    仔细观察右侧的亚像素平滑图像可以看出,这种方式使用有色的亚像素填充锯齿,如果你从远处观看这张图片,就可以看出这种方式产生的文字更加平滑,同时边缘依然很锐利。另外,在正常的大小和分辨率下观看的时候,也看不到任何杂色(你可以在任何LCD显示器上自己证明这一点)。

    那么亚像素平滑对于文字的效果如何?

    当然了!该技术对文字效果的改进就和上面列举的图像效果一样有效。在用于平滑文字的时候亚像素一样可以生效。为了能让你在自己的计算机上体验这一效果,我用一个很小(35KB)的32位Windows免费软件Free & Clear来演示亚像素技术的效果。

    Free & Clear需要32位Windows操作系统环境(Windows 95/98/NT或者在Mac上运行Virtual PC),Free & Clear说明页面详细介绍了这个程序,或者你可以直接下载(35KB)

    如果你手头没有Windows环境,或者如果你只是想简单看看亚像素平滑文本的显示效果,那么请查看下面使用Free & Clear产生的文字范例:

    标准的整像素Windows TrueType字体平滑:

    美观的平滑文本:

    注意:即使第二张图片在CRT上也能看到效果,不过效果并不明显,除非你使用LCD显示器观看。而在LCD显示器上,正如我所希望的,效果非常明显!

    无论是否下载了Free & Clear演示,请将你自己加入到我的电子邮件提醒系统(单击该链接即可),因为这个免费的演示程序可能还会继续开发。同时不用担心,如果你因为某些原因后悔加入了,我发送的每封邮件中都会包含退出的方法。

    MAC用户请注意:Free & Clear无法在MAC上虚拟机中运行的Windows 95/98里使用,同时我也不打算专门为该软件编写一个Mac版本,不过你依然可以(从上面的图片中)看到该技术的效果,同时你可以在Virtual PC中实验。

    亚像素平滑的局限

    虽然亚像素平滑很实用,不过依然有一些局限需要注意:

    只能用于LCD,不能用于CRT

    虽然亚像素平滑显示技术可以用于所有LCD显示器用户,不过需要注意,该技术无法用于高分辨率的阴极管射线(CRT)显示器。没错,我知道上面的图片在你的CRT显示器上看一样有效果,但那是因为任何成熟的亚像素平滑技术同时都吸收了抗锯齿技术(下文中将会提到)的精华。虽然抗锯齿技术可以改善文字的显示效果,因此尽管亚像素平滑比CRT上什么相关技术都不用,不过这种”有色”的方式真的只适合于LCD显示器。

    黑白图像的质量最好

    正如上面提到的,亚像素平滑的成功取决于使用毗邻的部分像素扩展现有的亚像素,这样眼睛就会感知为稍微有些变色的黑或者白。同时这一技术也可以用于低饱和度的颜色,不过在显示高对比度的黑白图像的时候效果是最好的。白色背景上的黑色文字可以获得最好的效果,同样,黑色背景上的白色文字效果也是最好的。

    仅横向,非纵向

    从上文可以知道,亚像素平滑只能改善LCD面板的横向分辨率,而令人难以置信的是,”横向”的改善正是文本最需要的!加粗的文字只是作用在横向上,而文字间距的调整也只是将文字做横向的移动,同时文字的倾斜也是在横向的位置上进行的。然而,这意味着目前的彩色LCD面板技术,在面板被用于垂直或者纵向的方向后将不能使用亚像素平滑。

    对我来说,令我对当前硬件的这种应用的激动降温的重要原因是电子书,因为电子书使用了类似传统书籍的纵向操作方式,而这完全无法兼容于现有的彩色LCD结构下的亚像素平滑技术。

    然而,没有什么能阻止为电子书专门研制的新型的LCD面板。这类面板垂直方向要比水平方向更长(用于纵向操作),同时可以像现有LCD面板那样确定亚像素的方向。只要能够清晰显示彩色图像,并同时可以使用亚像素平滑技术改善文字显示质量,这类新式的显示器就会被尽快用于电子书。

    亚像素顺序敏感度

    因为亚像素平滑严重依赖在物理上毗邻的亚像素,因此平滑机制必须知道像素的排列顺序是红-蓝-绿还是蓝-绿-红(目前现有的LCD面板只有这两种排列顺序)。

    如果亚像素平滑机制的顺序弄错了(如做图所示),那么最终的结果将会更糟糕。

    因为很少有LCD面板的亚像素排列是B-G-R,大部分都是R-G-B,因此任何正式投入使用的亚像素平滑技术都要求有用户设置的(或操作系统可读的)选项以告诉系统的LCD平滑引擎亚像素是”正序”还是”逆序”排列的。

    使用方式

    如果你还没有阅读或者完全了解上文的内容,那么请首先仔细阅读上文。下文会假设你已经了解了所涉及的概念。

    如果你可以接触到任何32位Windows环境(Windows 95/98/NT4,包括在Mac上运行的Virtual PC),那么你可能会希望在阅读下文的同时使用Free & Clear进行一些测试,这样你就可以按照文中的介绍自己尝试了。

    “简单”的理论有点太简单

    为了最好地理解有关亚像素平滑的基本理论(见上文)和现实之间的冲突,让我们先返回去看看一些现实世界中的例子。在我们的测试中,我们会使用”Sample Text String”这个字符串,并设置Windows 14 pt的Times New Roman字体:

    我故意选择这个字体和字号是因为这样能清除地说明问题。如果我们将该字符串转换为简单的亚像素,那么结果应该是这样的:

    正如你所看到的,在任何计算机的显示设备上,这个结果中都有很明显的”杂色”问题。为了能通过增强的基本理论解决这一问题,让我们首先了解一下问题出在哪里。

    正如我们所知道的,亚像素平滑技术实际上是利用LCD显示器可以直接寻址(directly addressable)的亚像素将显示器的有效水平分辨率扩大三倍。因此,我们首先需要三倍宽度的文本图案,并要包含三倍的平滑信息。实现这一目标的一种方法是将文字在水平方向上拉宽三倍,并转换成标准的白色和黑色,像这样:

    让我们把字母”m”放大仔细查看:

    当以三倍水平分辨率进行平滑的时候,字母”m”的三条垂直的腿被转换为五个像素的宽度。当然,当我们将结果转换回显示器上并使用亚像素显示(这时候三倍宽度的文字也会显示为正常尺寸)的时候,这实际上是指五个亚像素的宽度。以三倍像素方式显示(上图)的五个像素宽的”m”的腿告诉我们,当在正常分辨率下显示的时候,TrueType轮廓平滑引擎会将这些腿显示为一又三分之二个正常像素(也就是5个亚像素),但是TrueType又必须将其转换为两个标准像素。这就是导致”m”的腿显得更粗的原因。不足的显示分辨率,这正是亚像素平滑技术要解决的问题。

    目前为止似乎一切正常,当时看看当我们将三倍像素宽度的文字转换为LCD显示面板上的R-G-B亚像素后发生的情况:

    因为字母”m”的三条腿的宽度不是三的整数倍,因此腿的左右两侧边缘都是红色的亚像素(从上图很容易看出这一点),这也解释了当我们在正常尺寸下看到的经过亚像素平滑后的文字边缘呈红色的原因。

    另外需要注意的是,字母”m”的腿显得很”黑”,那是因为腿的显示使用了下列亚像素顺序:G-B-R-G-B,这意味着有两个绿色和两个蓝色的亚像素被关闭了,但是只有一个红色亚像素被关闭。这就形成了一种叫做本地色彩失衡(Local Color Imbalance)的现象,而我们的眼睛对这种本地色彩失衡是很敏感的。因为因为字母”m”的周围有更多红色,因此我们的眼睛为了平衡亚像素颜色,就让我们看到了红色。

    标准的整像素平滑并不存在这样的问题,因为额外的三倍R-G-B像素都被当作完整的单元来对待。但一旦我们开始单独对待亚像素,而这才是亚像素平滑的目的,我们就得面对本地色彩失衡的现象。

    那么该怎么解决这一问题

    其实解决的办法也很简单和直接,而且只需要对策略进行一些微小的改变。我们只需要将每个亚像素的”能量”分散到毗邻的两个亚像素即可:

    换句话说,当一个亚像素被”打开”后,我们只将其打开1/3,然后将左右毗邻的两个亚像素各打开1/3。这样,不再是每个单一的亚像素独自承担所有”能量”,我们会让这个亚像素的邻居一起承担。

    这样为什么有效?因为按照定义,每个亚像素左右两侧的邻居都是互补的两种基色,因此这种”能量分散”方式就可以有效地重新平衡失衡的颜色。如果仔细考虑一下就会发现,在同一时间,我们总是会打开R-G-B(或G-B-R或者B-R-G)亚像素集,因此就更不可能出现本地色彩失衡的问题。

    下面是一张大图,显示了未经筛选的亚像素线,以及经过分散后筛选了的结果:

    用语言描述就是:”筛选”后的亚像素的强度和其左右两侧毗邻的亚像素的亮度都是该像素原先亮度的1/3。因此,如果一个亚像素以及它的邻居都是”打开”的,那么就是全亮;而如果该亚像素或者它的一个或两个邻居都关闭了,那么实际亮度将会是2/3、1/3或者彻底关闭。

    这种解决方式的有趣之处在于三倍宽度的”水平分辨率”信息依然会呈现在显示结果中,同时保证本地色彩相当平衡。

    这种”能量分散”方式的一个不足在于可能会使显示结果有一点模糊,不过我们依然可以通过一些方法解决这一问题。模糊是由毗邻的亚像素比中间的主要亚像素具有更多的能量导致的,因此我们只需要降低两侧亚像素的能量但依旧维持本地色彩平衡即可。

    我们可以通过再次进行”筛选”做到这一点:让这三个相关联的亚像素将自己的能量分散给它们的三个邻居即可,形象的例子是这样:

    仔细研究上面的图可以发现,因为我们再次将一个亚像素的能量分散到三个毗邻的不同颜色的亚像素上,这一操作必定能维持完美的颜色平衡,但同时因为将自己的能量分散到毗邻的亚像素上,这必然导致亮度的进一步下降。

    因为两次分散影响了三组亚像素,因此能量的分散结果导致的亮度变化如下:

    图像处理大师可能会把这叫做是系数为[1/9, 2/9, 3/9, 2/9, 1/9]的五行低通窗口过滤器(five-element low-pass windowing filter)。你可能已经注意到了,这五个系数的总和是1.0,因此最初中央亚像素的全部能量都被分散的五个亚像素完全表现了出来。

    通过将原始亚像素的能量分散在五个最毗邻的亚像素上,我们可以在保持最完美的本地色彩平衡的基础上将大部分能量都保留在中央亚像素上,这是一个相当酷的解决方案。

    结果是什么样的

    下面是单词”Sample”使用未筛选的亚像素方式平滑后的结果,原始大小和放大后的结果:


    如图所示,”m”的竖直部分在原始大小下显得非常红,放大后查看就更加明显了。但是通过过滤产生本地色彩平衡可以完全解决这一问题。下面是同样的亚像素平滑结果,只不过这个结果进行过五行低通过滤,是这样的:


    最后,我们上面提到的示例,过滤和未过滤后的结果是这样的:


    如果在LCD显示器上看过滤后的结果,结果显然会非常令人满意。

    直观的感受

    很多(相当多的)人说我的亚像素平滑文字范例在自己的特丽珑或者一般的CRT显示器上效果确实很好,而我却还坚持说亚像素平滑结果在LCD面板上才能看到效果。幸好这个”直观”的感受解释了为什么亚像素平滑技术在非LCD显示器上也能起到部分效果。

    这是一个标准的TrueType显示,12pt,Times New Roman字体,标准的字母”S”。这是在整像素方式下可以实现的最好效果,因为所有像素要么完全”打开”要么完全”关闭”(也就是不包含任何抗锯齿技术)。

    这是同样的字母”S”显示在同样的网格中,但是这个字母使用亚像素平滑技术进行了三倍水平分辨率上的平滑。

    请仔细将其与上图对比,并注意本图中像素的位置和宽度又怎样的变化。请注意两幅图中第2行和第3行的第一个像素(左侧),注意当TrueType平滑提供了更大的水平分辨率后这两个像素是如何变为原先的1/3的。同时请从远处看这个字母的整体,注意第二张图片中的字母”S”的显示有什么改进。

    这张图片和上图内容相同,只不过将像素中缺少的部分用灰色填充。请注意,如果一个像素只被填充了1/3的黑色后剩余部分是如何用浅灰色填充的,同时如果一个像素被填充了2/3的黑色后剩余部分会使用深灰色填充。这就是抗锯齿的本质。

    接着对比着查看第二和第三张图片,你可以发现抗锯齿技术是如何将像素的”能量”分散到毗邻的两个像素以显示1/3黑色像素(上图中)旁边的区域的。我们的眼睛会尽量将它们均衡在一起。

    这张图片显示了完整的技术,包括对字母”S”的多重过滤以及亚像素平滑。也许这种技术最合适的名称是”有色抗锯齿”。

    有色抗锯齿技术最值得注意的是,因为LCD显示器的亚像素顺序是R-G-B,因此实际的颜色等同于垂直位置的视觉能量。

    例如,再次查看第二和第三行的前两个像素,分别是红色和蓝色。而因为像素的元素是R-G-B,左侧的像素通过关闭绿色和蓝色亚像素可以获得红色,因此这个像素的”暗”边缘就会显示到像素的右侧。在蓝色的像素中,通过关闭红色和绿色亚像素可以获得蓝色,因此它的”暗”边缘就被移动到左侧。而这样的像素移动就是TrueType平滑引擎要做的。

    有关亚像素平滑技术,我需要指出的是:该技术在直观上类似于在R-G-B方式的LCD面板上实现的色彩抗锯齿(colorized anti-aliasing)技术,亚像素左右两侧的颜色可以帮助我们看到更加平滑的显示结果。

    那么,为什么这种技术在CRT显示器上一样可以看到效果?

    左图显示了亚像素平滑技术为什么会在CRT显示器上生效(只不过效果不那么明显)。

    在这幅图中,我们隐藏了亚像素原本的颜色,而是将每个像素最终的能量结果(而非其颜色)显示出来。最终我们得到了一个灰度图像,那么我们得到了什么?最古老的抗锯齿技术,类似上面的第三个”S”。

    因此我的观点是,就算是CRT显示器(哪怕是特丽珑)也无法正确处理亚像素技术带来的色彩抗锯齿信息,因此最终只能显示最古老的标准抗锯齿图像,这当然要比没有任何抗锯齿技术时的效果要好,但因为LCD独有的将色彩转换到不同位置的功能,因此LCD上的显示效果要更好。

    就是这么简单

    我的希望是,任何对该技术有兴趣的人都可以并真正在自己的显示器上使用亚像素平滑技术,正如你所看到的,这真的没什么复杂的,因为显示结果本身就证明了这一点。

    感谢你的关注!


    展开全文
  • 设备像素比(devicePixelRatio)

    千次阅读 2019-06-12 11:49:37
    设备像素比(devicePixelRatio) 定义 window.devicePixelRatio是设备的物理像素和设备独立像素device-independent pixels (dips)之间的比率。window.devicePixelRatio = physical pixels / dips dips 是一个抽象...
  • 像素显示详解

    千次阅读 2015-05-02 15:47:43
     本文转自:http://blog.csdn.net/Armily/article/details/9142773 转自 http://www.xieyidian.com/1057 ... ...被拆分的像素:当像素不是一个像素的时候 LCD显示器上最小的图像单元(一个”
  • 图像像素字节数

    千次阅读 2015-09-30 09:21:09
    计算图像像素字节数的纠结 Code: int imgBuffSize = (m_imgWidth*m_nBitCount/8+3)/4*4*m_imgHeight;  就是后面的+3让我纠结了一阵子。原来是图像的像素占的字节数为4的整数倍。 从网上找了一下得到...
  • OpenGL ES像素着色器

    千次阅读 2016-07-21 09:04:43
    OpenGL ES像素着色器 原文 http://www.tairan.com/archives/7509 目 录 准备开始 像素着色器 vs 顶点/片段着色器 像素着色器101:渐变 像素着色器几何学 像素着色器程序生成纹理:Perlin...
  • GitHub标星7000+,快速恢复像素化图像,效果惊人

    万次阅读 多人点赞 2020-12-09 19:06:12
    整理 |高卫华出品 | AI科技大本营像素化(类似于马赛克)被许多领域用来加密图像中的重要信息, 例如很多公司会将内部文档中的密码像素化以加密数据,但之后并没有工具来恢复被像素化的图像...
  • 1px像素问题(一):真正原因

    千次阅读 2019-04-01 17:58:58
    关于,前端1px像素的问题,网上已经有很多相关的文章了, 但是,关于这个问题的原因网上没有几个说的是正确的,甚至还大谈dpr。。。 其实,原因很简单: 因为UI给设计图的时候基本上都是给的二倍图甚至三倍图,以...
  • AWTRIX2.0像素时钟制作

    千次阅读 2020-07-18 23:47:06
    像素钟 文章目录项目官方网站材料准备ESP8266固件烧写准备工作安装python3环境安装git环境安装pyserial下载esptool安装platformIO IDE安装vscode安装platformIO插件方法一:官方推荐方法下载固件擦除ESP8266Flash...
  • Android进程保活 Android进程 首先你要知道Android中的进程以及它的优先级,下面来说明它进程 前台进程 (Foreground process) 可见进程 (Visible process) ...下面进行解释: 前台进程(Foregr...
  • 设备像素比devicePixelRatio简单介绍

    千次阅读 2017-08-17 14:19:09
    今天分享设备像素比devicePixelRatio简单介绍,我个人觉得这个很重要有必要去进行了解,今天我分享给大家。 本文所说devicePixelRatio其实指的是window.devicePixelRatio, 被所有WebKit浏览器以及Opera所支持,随着...
  • 在做移动端网页开发时,首先要搞清楚三种viewport和三种像素,只有明白了这些概念,才能更好地开发出适配不同分辨率设备的网页。 但参阅的博客越多,对这些概念越是混乱,同样的一个概念,在一篇博客中是这种说法,...
  • PS-像素与分辨率

    千次阅读 2011-10-11 13:31:03
    像素与分辨率 像素说明 简单的说,我们通常所说的像素,就是CCD/CMOS上光电感应元件的数量,一个感光元件经过感光,光电信号转换,A/D转换等步骤以后,在输出的照片上就形成一个点,我们如果把影像放大数倍,会...
  • OpenCV从入门到放弃(五):像素

    万次阅读 2016-09-11 19:07:04
    矩阵中的一个元素对应一个像素。 2.对于灰度图像(黑白图像),像素是8位无符号数(CV_8U)。0表示黑色,255表示白色。对于彩色图像,是用三原色数据合成彩色。3个8位(CV_8UC3)的数值组成矩阵的一个元素。而且...
  • 本文介绍了图像中像素的邻域关系,通过邻域关系中基于限定灰度值的集合定义了邻接关系,通过邻接可以构建像素间的通路,基于通路构建了像素间的连通关系和连通集,并进一步定义了图像的区域和边界。 如果一个函数...
  • 概要:续接上文,本文进一步讲解与分析了上文未讲完的Unity5中...如有解释不妥当之处,还请各位及时指出。 依然是附上一组本文配套工程的运行截图之后,便开始我们的正文。 傍晚的野外(with 屏幕像素化特效):
  • OpenGL ES像素着色器教程

    千次阅读 2015-01-29 16:41:00
    http://www.tairan.com/archives/7509 原文地址:...在这个像素着色器(pixel shaders)教程里,你将学到如何把你的iPhone变成一块全屏的GPU画
  • dpr(设备像素比)与 移动端适配

    千次阅读 2019-09-29 11:32:35
    在上一篇文章中(使用 rem 实现移动端的自适应布局),我们讲到 iphone6s 的屏幕宽度为 375px,我们拿到的视觉稿也是基于iphone6s 设计的,但是设计稿的...一个物理像素是显示器(手机屏幕)上最小的物理显示单元,在...
  • iOS 开发:绘制像素到屏幕

    千次阅读 2015-03-31 22:41:19
    像素是如何绘制到屏幕上面的?把数据输出到屏幕的方法有很多,通过调用很多不同的framework和不同的函数。这里我们讲一下这个过程背后的东西。希望能够帮助大家了解什么时候该使用什么API,特别是当遇到性能问题需要...
  •  可我想表达的并不是这个,上述这个情况确实是正确的。但是,我相信这个世界上90%的开源代码是垃圾代码,还有9%的代码是理想代码,能够有1%的是优质代码就已经是相当不错了。基本上我们拿到的网络中的某个参考代码...
  • 计算图像像素占字节数的纠结

    万次阅读 2011-01-07 23:51:00
    Code: int imgBuffSize = (m_imgWidth*m_...原来是图像的像素占的字节数为4的整数倍。从网上找了一下得到结果:我看到一个根据位图的每行像素点数和位数计算每行字节数的公式,请问为什么这样计算?(mImageW
  • 2020年3月11日,我在看学生提交的中期报告时,再次遇到“字号,行距,磅,像素……”之类概念,然后搜了很多资料,看我的脑壳痛,眼冒金星。之前,在2017年7月16日,我在B站上传了《排版的艺术》,当时主要关注的是...
  • 前几天想快速了解一下什么事置信图,结果就百度了一下,发现完全没有相关的大佬出来发帖解释。没办法了,只能去找原文看一看了。先给大家总结一下关于置信图(confidence map)的知识点,并一句话概括confidence map...
  • 像素颜色和相关运算的理解 我们常说某件物体是某种颜色的,仿佛颜色是物体的自有属性。其实这样的理解并不正确。颜色是物体反射的光进入眼睛以后给我们的一种感觉。也就是说,除了物体本身的属性,光照条件也是最终...
  • 概要:续接上文,本文进一步讲解与分析了上文未讲完的Unity5中Standard Shader正向基础渲染通道源码的片段着色实现部分,以及对屏幕像素化后期特效进行了实现。   同样需要声明的是。本文中对Stardard Shader源码...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,248
精华内容 14,099
关键字:

像素的正确解释