名词解释
-
1 英寸=2.54 厘米
-
PPI:Pixels Per Inch也叫像素密度,所表示的是每英寸所拥有的像素数量
-
DPI:Dots Per Inch,每英寸点数,图像每英寸长度内的像素点
-
pt (point,磅):是一个物理长度单位,指的是72分之一英寸。
-
px (pixel,像素):是一个虚拟长度单位,是计算机系统的数字化图像长度单位,如果px要换算成物理长度,需要指定精度DPI(Dots Per Inch,每英寸像素数),
-
DPR:设备像素比,DPR = 单位长度内(pixel/point)
-
当像素密度超过300ppi时,人眼就无法区分出单独的像素,retina屏的像素密度达到326ppi,肉眼观看的时候不会再出现颗粒感
-
重采样技术:一种影像数据处理方法。即影像数据重新组织过程中的灰度处理方法。影像采样是按一定间隔采集影像灰度数值的,当阈值不位于采样点上的原始函数的数值时,就需要利用已采样点进行内插,称为重采样
背景
-
在15年的时候,有个设计是加一条分隔线,当时在两倍屏上线的高度如果设置小于0.5时,在有些情况下显示不出来,当时理解为两倍屏的0.5宽度对应的就是一个物理像素的宽度,当小于一个物理像素的时候就显示不出来了。当时网上流行两种种处理方法,比如加盐、设置偏移。通常设置一像素的线大家采用的是定义一个宏,
#define LINEWIDTH (1/[UIScreen main].scale)
这样能保证不论是在两倍屏还是三倍屏行线都是一像素的。 -
最近搞Flutter开发,惊奇的发现设置一条线的高度为0.1,Flutter竟然也能显示,很好奇Flutter是怎么做到的。于是乎开始了我的探索之路
Flutter 测试
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("测试"),
),
body: Container(
color: Colors.white,
child: Center(
child: Column(
children: <Widget>[
_line(0.1),
_line(0.2),
_line(0.3),
_line(0.4),
_line(0.5),
_line(0.6),
_line(0.9),
_line(1.0),
_line(1.1),
_line(1.4),
_line(1.5),
_line(1.9),
_line(2.0),
_line(5.0),
],
),
),
),
);
}
Widget _line(double height){
return Container(
child: Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20,right: 10),
child: Text("$height"),
),
// _containerLine(height),
_drawLine(height),
],
),
);
}
// 测试用container 做的line
Widget _containerLine(double height){
return Container(
height: 15,
width: 200,
color: Colors.white,
child: Center(
child: Container(
height: height,
width: 375,
color: Colors.red,
),
),
);
}
// 测试画出来的线
Widget _drawLine(double height){
return CustomPaint(
size: Size(250, 20),
painter:TestPainter(height) ,
);
}
}
-
首先用flutter Container做个line测试效果
下图是总体效果
WechatIMG22.jpeg


- 然后使用flutter 绘制出一根线做测试
下图看起来和使用Container做的线显示效果一样




- 通过上面的测试结果我们可以看出
将图片放大后发现线的height 0.1~1.0 渲染出来的高度是一样的,只不过显示颜色深浅的不一样,从1.0~2.0的过程是线上下两边颜色逐渐由浅变深的过程,2.0时可以明显看出线的高度是1.0的两倍。
ios原生测试
#import "ViewController.h"
@interface RenderView: UIView
@end
@implementation RenderView
-(void)drawRect:(CGRect)rect{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawLine:context lineHight:0.1 startPoint:CGPointMake(50, 50) endPoint:CGPointMake(200, 50)];
[self drawLine:context lineHight:0.2 startPoint:CGPointMake(50, 60) endPoint:CGPointMake(200, 60)];
[self drawLine:context lineHight:0.3 startPoint:CGPointMake(50, 70) endPoint:CGPointMake(200, 70)];
[self drawLine:context lineHight:0.5 startPoint:CGPointMake(50, 80) endPoint:CGPointMake(200, 80)];
[self drawLine:context lineHight:0.7 startPoint:CGPointMake(50, 90) endPoint:CGPointMake(200, 90)];
[self drawLine:context lineHight:0.9 startPoint:CGPointMake(50, 100) endPoint:CGPointMake(200, 100)];
[self drawLine:context lineHight:1.0 startPoint:CGPointMake(50, 110) endPoint:CGPointMake(200, 110)];
[self drawLine:context lineHight:1.05 startPoint:CGPointMake(50, 120) endPoint:CGPointMake(200, 120)];
[self drawLine:context lineHight:1.2 startPoint:CGPointMake(50, 130) endPoint:CGPointMake(200, 130)];
[self drawLine:context lineHight:1.4 startPoint:CGPointMake(50, 140) endPoint:CGPointMake(200, 140)];
[self drawLine:context lineHight:1.5 startPoint:CGPointMake(50, 150) endPoint:CGPointMake(200, 150)];
[self drawLine:context lineHight:1.7 startPoint:CGPointMake(50, 160) endPoint:CGPointMake(200, 160)];
[self drawLine:context lineHight:1.9 startPoint:CGPointMake(50, 170) endPoint:CGPointMake(200, 170)];
[self drawLine:context lineHight:2.0 startPoint:CGPointMake(50, 180) endPoint:CGPointMake(200, 180)];
[self drawLine:context lineHight:5.0 startPoint:CGPointMake(50, 190) endPoint:CGPointMake(200, 190)];
}
-(void)drawLine:(CGContextRef )context lineHight:(CGFloat)height startPoint:(CGPoint)start endPoint:(CGPoint)end{
CGContextMoveToPoint(context, start.x, start.y);
CGContextAddLineToPoint(context, end.x, end.y);
CGContextSetLineWidth(context,height);
CGContextClosePath(context);
[[UIColor redColor]setStroke];
CGContextDrawPath(context, kCGPathStroke);
NSString *string = [NSString stringWithFormat:@"%.2f",height];
UIFont *fount = [UIFont systemFontOfSize:9];
[string drawInRect:CGRectMake(20, start.y-10, 30, 20) withAttributes: @{NSFontAttributeName:fount}];
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// RenderView *v = [[RenderView alloc] init];
// v.frame = CGRectMake(0, 0, 300, 500);
// v.backgroundColor = [UIColor whiteColor];
// [self.view addSubview:v];
//
float left = 50;
[self lineRect:CGRectMake(left, 50, 100, 1)];
[self lineRect:CGRectMake(left, 60, 100, 0.1)];
[self lineRect:CGRectMake(left, 70, 100, 0.2)];
[self lineRect:CGRectMake(left, 80, 100, 0.3)];
[self lineRect:CGRectMake(left, 90, 100, 0.5)];
[self lineRect:CGRectMake(left, 100, 100, 0.7)];
[self lineRect:CGRectMake(left, 110, 100, 0.8)];
[self lineRect:CGRectMake(left, 120, 100, 0.9)];
[self lineRect:CGRectMake(left, 130, 100, 1.0)];
[self lineRect:CGRectMake(left, 140, 100, 1.05)];
[self lineRect:CGRectMake(left, 150, 100, 1.2)];
[self lineRect:CGRectMake(left, 160, 100, 1.4)];
[self lineRect:CGRectMake(left, 170, 100, 1.5)];
[self lineRect:CGRectMake(left, 180, 100, 1.7)];
[self lineRect:CGRectMake(left, 190, 100, 1.9)];
[self lineRect:CGRectMake(left, 200, 100, 2.0)];
[self lineRect:CGRectMake(left, 210, 100, 5.0)];
}
-(void)lineRect:(CGRect) rect{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, rect.origin.y - 10, 30, 20)];
label.text = [NSString stringWithFormat:@"%.1f",rect.size.height];
label.font = [UIFont systemFontOfSize:9];
[self.view addSubview:label];
UIView *v = [[UIView alloc] initWithFrame:rect];
v.backgroundColor = [UIColor redColor];
[self.view addSubview:v];
}
@end
- 我们先测试一下使用UIView做根线的效果



-
我们在用ios原生画一条线测试一下
WechatIMG27.jpeg


总结
通过测试对比发现Flutter在渲染小于1逻辑像素的线的时候,其高度是按1逻辑像素去展示的,只不过看到的颜色会不一样,当设置线的高度不是整数逻辑像素时,线的高度是线上取整后的逻辑像素,只不过看到的线上下边缘的颜色会随着小数部分的值越大颜色越接近设置的颜色。也就是说flutter是以1逻辑像素的整数倍去显示的,如果是非整数倍逻辑像素线上下边缘的颜色会跟设置的不一样(以灰度的方式显示出来)。
ios 原生目前也可以展示小于1物理像素的点,但是以UIView的控件做的线,其高度有个临界值,低于临界值的线会显示不出来,而且其显示的线边缘比较锐利,其高度在到某一临界值后会直接变高,而不是在线上下边缘做灰度展示。而用原生绘制出来的线展示小于以逻辑像素的时候其高度均展示为1逻辑像素,不过颜色值会以灰度的方式展示出来,在达到逻辑像素的整数倍时展示设置的颜色,这是和flutter一样的。
结语
- 这次探究推翻了我之前的认知,以前我先是把逻辑像素转换成对应的物理像素去展示,认为小于一物理像素的点是不能展示出来的。现在认识到不要强行将物理像素和逻辑像素联系在一起,当线小于一逻辑像素的时候,渲染的时候并不是物理像素的点就是暗的,其实每一个物理像素是有红黄蓝三种颜色组成,每种颜色有8位来控制,也就是一个物理像素可以展示2的24次方种颜色,当逻辑像素小于1时,渲染的时候内部会有自己的算法将其对应的物理像素点以灰度的方式展示,这样就能展示小于1逻辑像素的点了。
比如拿iPhone 6s 举例,其屏幕宽度是2.3英寸,横向上有750颗像素点,ppi = 750/2.3 = 326,既每寸上有326个物理像素,换算成厘米也就是 326/2.54 = 128.35,也就是说每厘米上有128.35个物理像素点,每毫米有13个物理像素点,也就是每个物理像素点的宽度是0.077毫米。6s 的逻辑宽度是375,横向上每厘米有375/(2.3*2.54) = 64逻辑像素/cm,也就是6s上每个逻辑像素的宽度是0.156毫米。当我们设置一逻辑像素宽度时其在6s上对应的宽度是0.156毫米。
网友评论