iOS 超大高清图展示策略 TileLayer 及 levelsOfDetailBias 分析
本次分析针对当下流行的中国地图图片处理,1亿像素,就是下面这张:
原图尺寸:11935x8554 文件大小:22.1MB
原始加载方式

直接加载原图内存占用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
self.largeImage = [UIImage imageWithContentsOfFile:_path];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = self.largeImage;
});
});
分块加载方式

分块加载效果
The number of levels of detail maintained by this layer. Defaults to one. Each LOD is half the resolution of the previous level. If too many levels are specified for the current size of the layer, then the number of levels is clamped to the maximum value (the bottom most LOD must contain at least a single pixel in each dimension).
简单来说就是从UIScrollView的缩小级别重绘设置,每一个层级的分辨率是上一层级的½
levelsOfDetailBias The number of magnified levels of detail for this layer. Defaults t zero. Each previous level of detail is twice the resolution of the later. E.g. specifying 'levelsOfDetailBias' of two means that the layer devotes two of its specified levels of detail to magnification, i.e. 2x and 4x.
简单来说就是,layer的放大级别重绘设置,每一个层级分辨率是后面层级的2倍 不急,以上两个属性,我在后面会有详细的描述
tileSize The maximum size of each tile used to create the layer's content. Defaults to (256, 256). Note that there is a maximum tile size, and requests for tiles larger than that limit will cause a suitable value to be substituted.
这个就简单了,是layer划分视图区域最大尺寸
下面的文章分析很透彻:
iOS开发 - CocoaChina CocoaChina_让移动开发更简单
但是我测试的结果和文章中的结论有比较大的出入,但是方向是一致的:(稍后我会做一些实验展示tileSize和levelsOfDetailBias的关系)
分块加载实现
- (void)btnTapped:(id)btnTapped {
[self.scrollView removeFromSuperview];
self.largeImage = [UIImage imageWithContentsOfFile:_path]; self.scrollView = [[ImageScrollViewNoSlip alloc] initWithFrame:self.view.bounds
image:self.largeImage]; [self.view addSubview:self.scrollView]; }
看到上面的代码,就知道重点肯定在这个ImageScrollViewNoSlip了,其实这个view只是一个UIscrollview,负责图片视图的缩放和位置更新,与我们通常使用图片缩放的手法无异,只是对缩放系数的设置做了简单计算,如果对缩放系数要求不是很细致,可以直接忽略计算过程,初始化内容如下:
-(id)initWithFrame:(CGRect)frame image:(UIImage*)img {
if((self = [super initWithFrame:frame])) {
// Set up the UIScrollView
self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;
self.bouncesZoom = YES;
self.decelerationRate = UIScrollViewDecelerationRateFast;
self.delegate = self;
self.backgroundColor = [UIColor colorWithRed:0.4f green:0.2f blue:0.2f alpha:1.0f]; // 根据图片实际尺寸和屏幕尺寸计算图片视图的尺寸
self.image = img;
CGRect imageRect = CGRectMake(
0.0f,
0.0f,
CGImageGetWidth(image.CGImage),
CGImageGetHeight(image.CGImage));
imageScale = self.frame.size.width/imageRect.size.width; NSLog(@"imageScale: %f",imageScale);
imageRect.size = CGSizeMake(
imageRect.size.width*imageScale,
imageRect.size.height*imageScale); //根据图片的缩放计算scrollview的缩放级别
// 图片相对于视图放大了1/imageScale倍,所以用log2(1/imageScale)得出缩放次数,
// 然后通过pow得出缩放倍数,至于为什么要加1,
// 是希望图片在放大到原图比例时,还可以继续放大一次(即2倍),可以看的更清晰
int level = ceil(log2(/imageScale))+;
CGFloat zoomOutLevels = ;
CGFloat zoomInLevels = pow(, level); self.maximumZoomScale =zoomInLevels;
self.minimumZoomScale = zoomOutLevels; frontTiledView = [[TiledImageViewNoSlip alloc] initWithFrame:imageRect
image:image
scale:imageScale];
[self addSubview:frontTiledView];
}
return self;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return frontTiledView;
}
最后要出场的就是我们的tile layer了,也就是上面scrollview中创建的TiledImageViewNoSlip,其实它也只是个普通的UIView,只是重新指定了Layer为CATiledLayer,然后在tileLayer执行分块绘制时(调用drawrect),根据给定的区域(rect)对应到超大图的区域进行绘制,这个view的关键实现如下:
+ (Class)layerClass {
return [CATiledLayer class];
} -(id)initWithFrame:(CGRect)_frame image:(UIImage*)img scale:(CGFloat)scale {
if ((self = [super initWithFrame:_frame])) {
self.image = img;
imageRect = CGRectMake(0.0f, 0.0f,
CGImageGetWidth(image.CGImage),
CGImageGetHeight(image.CGImage));
imageScale = scale;
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
//根据图片的缩放计算scrollview的缩放次数
// 图片相对于视图放大了1/imageScale倍,所以用log2(1/imageScale)得出缩放次数,
// 然后通过pow得出缩放倍数,至于为什么要加1,
// 是希望图片在放大到原图比例时,还可以继续放大一次(即2倍),可以看的更清晰
int lev = ceil(log2(1/scale))+1;
tiledLayer.levelsOfDetail = 1;
tiledLayer.levelsOfDetailBias = lev;
// tiledLayer.tileSize 此处tilesize使用默认的256x256即可 }
return self;
} -(void)drawRect:(CGRect)rect {
//将视图frame映射到实际图片的frame
CGRect rec = CGRectMake(
rect.origin.x / imageScale,
rect.origin.y / imageScale,
rect.size.width / imageScale,
rect.size.height / imageScale
);
//截取指定图片区域,重绘
CGImageRef cropImg = CGImageCreateWithImageInRect(self.image.CGImage, rec);
UIImage *tileImg = [UIImage imageWithCGImage:cropImg];
[tileImg drawInRect:rect];
}
实现原理分析
tileSize

使用默认的tile size256x256, 则视图会划分成4块逐个加载,经过多次测试,内存峰值有所下降,为310M左右,毕竟这个尺寸相当于一次性绘制大部分的区域,如下图:
设置tile size为{100, 100}, 多次测试内存峰值在110M左右
所以逐个加载的意义,就是分散同时绘制整个视图的内存压力,至此,tile size告一段落!
注:以上默认设置LOD和LODB,进行scrollview缩放时,layer不会进行重绘
接下来继续探索~
levelsOfDetail
这个值表示layer在绘制时缩小层级设置,就是在缩小视图时可以达到的最大缩小级数,可以和下面的levelsOfDetailBias值对应起来,本次重点讨论超大图展示,主要涉及到放大,所以可以参考下面的放大设置分析。
这个值是负责设定视图缩小时的重绘节点, 通过实验确实如此,在LODB默认值为0时, 无论设置LOD的值是多少,在放大图片时,都不会进行重绘操作,视图也就会越来越模糊,而对视图进行缩小操作时,根据LOD的值不同,zoom scale达到触发重绘的值会相应变化,LOD越大,zoom scale的触发值就越小
既然LOD不影响放大的重绘结果,那就先置之不理吧,继续看LODB~
levelsOfDetailBias
这个值表示layer在放大时,触发重新绘制的最大层级设置。
简单来说,就是从最小视图需要放大多少次,才能达到我们需要的清晰度效果,注意是多少次,一次就是2倍,这个和scrollview的zoomScale不同,zoomscale表示的是放大多少倍~
数值越大,视图在放大时可以重新渲染原图的粒度就越细,在失真前视图能呈现的纹理就越清晰,直到显示到像素级别,这时tileSize已经无限接近0了,也就是每个tile负责绘制一个像素;
以上也说明了,绘制视图时会分成多少个tile,除了与我们设置的tileSize有关,还与缩放级别、最大放大层级(也就是levelsOfDetailBias的值)有关。
tile的最大size为设置的tileSize尺寸,默认为256x256, 最小可以无限接近0,至于绘制时最小可以达到多少,就是levelsOfDetailBias说了算了,可以参考下图数据表
说了这么多,有点抽象,下表是我统计的 levelsOfDetailBias值不同时,随着scrollview放大倍数的增加,tile可以达到的最小尺寸,省略了很多中间重绘的记录,只写出了每个tileSize最小时,缩放的倍数:
说明:
- 375x268是图片视图的frame size
- 中国地图尺寸为:11935x8554
- levelsOfDetail = 1(默认值,不影响放大)
- tileSize = 256x256 默认值(为了不考虑tileSize对初始化和缩放的影响,此处也可以设置为375x268)
- 图中放大倍数值为 *, 表示缩放不再影响效果
- 放大倍数的值为手动缩放的模糊值,不代表科学计算的实际值,只作为一个参考边界
- 随着LODB的值变大,相同的tile size时,视图能绘制的最大尺寸会更大
- 值设置10以后,继续放大图片已经失真严重了,所以用灰色表示
- 重点观察tileSize的最小值变化
levelsOfDetailBias与最小tileSize、zoomScale关系
iOS 超大高清图展示策略 TileLayer 及 levelsOfDetailBias 分析的更多相关文章
- SDWebImage -- 封装 (网络状态检测,是否打开手机网络下下载高清图设置)
对SDWebImage 进行封装,为了更好的节省用户手机流量,并保证在移动网络下也展示高清图,对使用SDWebImage 下载图片之前进行逻辑处理,根据本地缓存中是否有缓存原始的图片,用户是否打开移动 ...
- 第二十八篇、自定义的UITableViewCell上有图片需要显示,要求网络网络状态为WiFi时,显示图片高清图;网络状态为蜂窝移动网络时,显示图片缩略图
1)SDWebImage会自动帮助开发者缓存图片(包括内存缓存,沙盒缓存),所以我们需要设置用户在WiFi环境下下载的高清图,下次在蜂窝网络状态下打开应用也应显示高清图,而不是去下载缩略图. 2)许多 ...
- Node JS爬虫:爬取瀑布流网页高清图
原文链接:Node JS爬虫:爬取瀑布流网页高清图 静态为主的网页往往用get方法就能获取页面所有内容.动态网页即异步请求数据的网页则需要用浏览器加载完成后再进行抓取.本文介绍了如何连续爬取瀑布流网页 ...
- [iOS]图片高清度太高, 导致内存过大Crash
先说一下状况, 后台提供的图片太高清了, 每个图片都在2-4MB, iOS上每个页面需要同时下载并展示10-15张. 这个时候, 如果我多滑动collectionView几次, 直接App就崩溃了(r ...
- python爬虫实战(二)--------千图网高清图
相关代码已经修改调试----2017-3-21 实现:千图网上高清图片的爬取 程序运行20小时,爬取大约162000张图片,一共49G,存入百度云.链接:http://pan.baidu.com/s/ ...
- 千金良方说:"我现在奉上179341字的MySQL资料包,还来得及吗?有"代码段、附录、和高清图!!"
上一篇"上发布过"一不小心,我就上传了 279674 字的 MySQL 学习资料到 github 上了",我在更早之前,在微信公众号"老叶茶馆"上发布 ...
- Vim 键盘指令高清图
个人感觉挺好用的 推荐大家使用windows版的vim,个人用着感觉不错,在linux上用惯了vim的朋友可以试试这个.
- Python爬虫——你们要的王者荣耀高清图
曾经144区的王者 学了计算机后 头发逐渐从李白变成了达摩 秀发有何用,变秃亦变强 (emmm徒弟说李白比达摩强,变秃不一定变强) 前言 前几天开了农药的安装包,发现农药是.Net实现的游戏 虽然游戏 ...
- Motorola C118 PCB原理高清图
随机推荐
- 在vmware 中使用桥连接 连接到网络
vMware虚拟机以后,连不上网,通过ifconfig命令,查看结果,如图所示: 然后,我想尝试一下,在虚拟机中ping 本地物理机地址,结果如图. 总结起来,主要有4步: 1.使用chkconfig ...
- SVN.服务器迁移方法
SVN项目, 源服务器 : 10.10.13.48 目标服务器: 10.10.13.129 要把SVN项目从.48上迁移到.129上. 做法: 准备: 版本库:vos 源服务器 : 10.10.1 ...
- 教你用SVG画出一条龙
先看demo,九十七度 其实使用svg画出这条龙很简单,关键不在于怎么使用svg,而在于你的美术功底,哈哈. 好吧,当然基础是不能忽略的,先看下这条龙的代码: <svg id="lon ...
- 在webapi中为Action使用dynamic参数实现Post方式调用
1.在webapi中使用controller/action/id的路径配置,打开文件[App_Start] -[WebApiConfig] config.Routes.MapHttpRoute( na ...
- C# group 子句
group 子句返回一个 IGrouping<TKey,TElement> 对象序列,这些对象包含零个或更多与该组的键值匹配的项. 例如,可以按照每个字符串中的第一个字母对字符串序列进行分 ...
- 使用Identity Server 4建立Authorization Server (5)
预备知识: http://www.cnblogs.com/cgzl/p/7746496.html 第一部分: http://www.cnblogs.com/cgzl/p/7780559.html 第二 ...
- 【深度学习系列】PaddlePaddle之数据预处理
上篇文章讲了卷积神经网络的基本知识,本来这篇文章准备继续深入讲CNN的相关知识和手写CNN,但是有很多同学跟我发邮件或私信问我关于PaddlePaddle如何读取数据.做数据预处理相关的内容.网上看的 ...
- Oracle-2 - :超级适合初学者的入门级笔记--定义更改约束,视图,序列,索引,同义词
接着我上一篇的写,在这感觉到哇 内容好多啊 上一篇,纯手打滴,希望给个赞! 添加约束的语法: 使用 alter table 添加或删除约束,但是不能修改约束 有效化或无效化约束 添加not nul ...
- linux-mv
linux-mv 主要用于文件或者目录的移动或者改动, 命令参数 -i:ruguo目标文件或者目录存在,提示是否覆盖目标文件或目录 -f:无论目标文件是否存在,直接覆盖,不提示, 有好多参数,自己可以 ...
- PHP实现前台页面与MySQL的数据绑定、同步更新
今天我来给大家介绍一个PHP-MySQL的小项目. 使用 PHP和前台Ajax 实现在前台对MySQL数据库中数据的增.删等操作语句功能. 如果有问题,欢迎拍砖~ 首先,我们先做好前台HTML.CSS ...