需求描述
在开发需求中,有种场景:工程中图片太多,很多png图片,导致程序包很大。尤其是出现在移动平台iOS,Android和游戏平台。这是因为png的头占用资源比较大,如果将所有png图片拼接到一起,就省了png的头部信息;那么程序包应该会少不小!
想法很好!如果程序中有四五百个png图片,手动合成到大图上恐怕不是小工作量。何况每天的资源图片都有可能更新,更新一个就要重新拼图。这个工作量肯定不是人能搞定的了。可不可让计算机帮助我们来拼图。拼图完成后,计算机告诉我们每个图片的坐标,程序运行时候我们根据坐标去拿图片就行了。
文章最后可下载demo
想想,手动拼图的第一步。
首先设定一个很大的空白图片,假如是(1000,1000)这样就够装很多图片。考虑到最大限度利用该空白图片。事先将所有图片的大小排序。最大在最前面。
将最大的图片放到空白图片左上角,如下图所示:那么可以将剩余的区域分为两部分:left和right部分,用于放后继图片。
设计数据模型——二叉树
从刚刚的图片中,很容易想到二叉树的数据结构,如下图所示
看懂这个图片,主要通过三个● 和三块区域
采用二叉树数据结构
按照上图的思路,采用二叉树数据结构。怎么将图片一个个插入到二叉树中呢?有一下几种方法:
● 前序遍历
● 后序遍历
● 中序遍历
● 层序遍历
实现初始化一个数组,存储已经排序好了的图片资源
NSMutableArray* tmp = [NSMutableArray array]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(120, 100) name:@"1.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(120, 100) name:@"2.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(100, 100) name:@"3.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(80, 21) name:@"4.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(70, 40) name:@"5.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(65, 30) name:@"6.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(50, 21) name:@"7.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(50, 21) name:@"8.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(50, 21) name:@"9.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(50, 21) name:@"10.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(40, 40) name:@"11.png"]]; [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(35, 30) name:@"12.png"]];
看一下效果GIF图片,然后再说一下insertPicture的代码。
看看insertPicture的逻辑:
-(bool) insertPicture:(BTreeNode*)node{ //如果图片大小,可以放下这个rect。那么就设置一下这个rect的大小为图片大小,剩余面积分为left和right if (!node.isFull && node.virtualPic.size.width >= _currentPic.size.width && node.virtualPic.size.height >= _currentPic.size.height) { node.isFull = TRUE; /* 拆分剩余rect为left和right两个分支。 ●---------●----------------- | picture | right | | | | ●--------------------------- | | | left | | | | | | | ---------------------------- 例如.picture已经占据了【左上角】区域。剩下的区域分为left和right。点(●)的地方就是CGPoint了。 ● 优化点:如果picture是矩形,那么在生成left和right的时候。可以有两个选择:向下延伸,向右延伸。 通常是向值小的一方延伸,这样保证值大的一方可以放进去更多的图片。 ● 上面的例子是向右延伸。 */ node.left = [[BTreeNode alloc]init]; node.left.point = CGPointMake(node.point.x, node.point.y + _currentPic.size.height); node.left.virtualPic = [[picNode alloc]init]; node.right = [[BTreeNode alloc]init]; node.right.point = CGPointMake(node.point.x+ _currentPic.size.width, node.point.y); node.right.virtualPic = [[picNode alloc]init]; //优化一下 if (_currentPic.size.width >= _currentPic.size.height) { //left的宽度是parent的宽度。高度是parent高度 - 图片的高度 node.left.virtualPic.size = CGSizeMake(node.virtualPic.size.width, node.virtualPic.size.height - _currentPic.size.height); //right的宽度是,parent的宽度-图片的宽度。高度是图片的高度。 node.right.virtualPic.size = CGSizeMake(node.virtualPic.size.width- _currentPic.size.width, _currentPic.size.height); }else{ //left的宽度是图片的宽度。高度是parent高度 - 图片的高度(不变) node.left.virtualPic.size = CGSizeMake(_currentPic.size.width, node.virtualPic.size.height - _currentPic.size.height); //right的宽度是,parent的宽度-图片的宽度。高度parent的高度。 node.right.virtualPic.size = CGSizeMake(node.virtualPic.size.width- _currentPic.size.width, node.virtualPic.size.height); } //将虚拟的pic复制 node.virtualPic = [_currentPic copy]; return YES; } return NO; }
总结:
后序遍历的图片利用率最高,这样的结果也符合人类的思维,假如我们手动排班。那么应该也是最大在上角,此大的往后排。后侧排满后再从左侧开始,即蛇型曲线。
demo下载地方点击打开链接
拼图算法,将零碎小图,整理到一张大图之上,自动合并。二叉树实现
时间: 2024-11-08 11:00:20