需求描述
在开发需求中,有种场景:工程中图片太多,很多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下载地方点击打开链接
原文:http://blog.csdn.net/hherima/article/details/38560929