TexturePacker大图还原成小图工具带源码

    TexturePacker是一个把好多小图打成大图的软件,生成的是大图以及小图在大图位置的.plist描述文件,但是不支持把大图还原成小图。网上偷的图一般都是大图和plist,想得到小图比较麻烦,于是乎用python写了个TexturePacker反向工具,把大图导成小图。

  1.python要用到的库

    python的图片处理要用到PIL(Python Image Library),mac10.10下安装PIL会报 fatal error: ‘X11/Xlib.h‘ file not found的错,解决方法在此。而且在装PIL前要先装zlib/libpng/jpeg,安装方法自行百度。

    plist解析用了xml.dom为python自带的库,不用装。

  2.TexturePacker导出的plist结构分析

    plist文件如下所示。 

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3 <plist version="1.0">
 4     <dict>
 5         <key>frames</key>
 6         <dict>
 7             <key>grossini_dance_03.png</key>
 8             <dict>
 9                 <key>frame</key>
10                 <string>{{46,324},{63,109}}</string>
11                 <key>offset</key>
12                 <string>{-6,-1}</string>
13                 <key>rotated</key>
14                 <false/>
15                 <key>sourceColorRect</key>
16                 <string>{{5,7},{63,109}}</string>
17                 <key>sourceSize</key>
18                 <string>{85,121}</string>
19             </dict>
20         </dict>
21         <key>metadata</key>
22         <dict>
23             <key>format</key>
24             <integer>2</integer>
25             <key>realTextureFileName</key>
26             <string>bbb.png</string>
27             <key>size</key>
28             <string>{512,512}</string>
29             <key>smartupdate</key>
30             <string>$TexturePacker:SmartUpdate:ea1bbb1419cd4c346debb54e1a7d5de2:1/1$</string>
31             <key>textureFileName</key>
32             <string>bbb.png</string>
33         </dict>
34     </dict>
35 </plist> 

    frames对应的value是所有小图的信息。key为小图的名字,dict为小图的信息。

    frame为图片小图位置及大小(这个大小是经过Trim的大小,TP会把png的无像素白边剔除,来减小图片的大小,也就是说大图中小图的大小不一定等与小图真正的大小)。如上plist{{46,324},{63,109}},46和324为小图在大图中x,y坐标,{63,109}为经过裁剪的图片大小,图2其实是png格式黄色背景是空的,为了看着方便,加两个黄色背景。{63,109}是红框内的大小。

图1图2图3

    rotated是是否旋转,这个光头没有旋转,大图中的play按钮有旋转。

    sourceSize为小图的大小。{85, 121}为小图整个大小,没有经过Trim。

    sourceColorRect为经过Trim的图在小图中的起始坐标及大小。{{5,7},{63, 109}}中{5,7}为图2中红框左上角的坐标,{63,109}为大图中小图的大小。

    offset为中心坐标偏移。图3中小红点处为原大小图中心点p1,小红点左边的交叉点为经过Trim的图片中心点p2,以p1为原点,p2的坐标就是这个offset{-6,-1},x向负轴偏移6像素,y向负轴偏移1像素,这里比较奇怪,y轴好像是向上为正了。

   不知这个有offset有神马用,下面的代码没有用offset,用sourceColorRect、sourceSize、frame、rotated就可以确定出小图的样子。

  3.代码分析

    代码中用到了PIL,PIL的参考手册在这,程序中用到了open、new、crop、crop、rotate几个api。

    代码分三个文件,主文件TexturePacker.py,运行此来生成小图。PlistToDict.py来解析plist文件,生成map,key为小图名,value为小图信息。BraceParser.py为{{2,3},{4,5}}生成列表[[2,3][4,5]]。

    知道了plist的含义,稍微会用PIL,代码应该很好理解,代码如下,玩具代码,莫要嘲笑。

    TexturePacker.py,类TextureParser第一个参数是plist及png文件位置,第二个为文件名字。

 1 import PIL.Image as Image
 2 import BraceParser
 3 import PlistToDict
 4
 5 class TextureParser(object):
 6     def __init__(self, path, name):
 7         self.__resPath = path;
 8         self._name = name;
 9         self.__plistDict = PlistToDict.PlistToDict(path + "/" + name + ".plist").createDict();
10
11     #return map[picName : {originPoint : {x:, y:}, size : {width:, height:}}]
12     def __getSmallPicInfos(self):
13         picInfo = {};
14         for key, value in self.__plistDict["frames"].items():
15             size = BraceParser.BraceParser(value["sourceSize"]).createList();
16             origin = BraceParser.BraceParser(value["frame"]).createList();
17             sourceSize = BraceParser.BraceParser(value["sourceColorRect"]).createList();
18             picInfo[key] = {"size" : size,
19                             "origin" : [origin[0][0], origin[0][1]],
20                             "colorOrigin" : [sourceSize[0][0], sourceSize[0][1]],
21                             "colorSize" : [sourceSize[1][0], sourceSize[1][1]],
22                             "isRotated" : value["rotated"]
23                             };
24         return picInfo;
25
26     def smallPicsCreate(self, pathToStore = None):
27         image = Image.open(self.__resPath + "/" + self._name + ".png");
28         picInfos = self.__getSmallPicInfos();
29         for k, v in picInfos.items():
30             if v["isRotated"] == True:
31                 v["size"][0], v["size"][1] = v["size"][1], v["size"][0];
32                 v["colorSize"][0], v["colorSize"][1] = v["colorSize"][1], v["colorSize"][0];
33             newImage = Image.new("RGBA", (int(v["size"][0]),int(v["size"][1])));
34             box = (int(v["origin"][0]), int(v["origin"][1]),
35                    int(v["origin"][0] + v["colorSize"][0]), int(v["origin"][1] + v["colorSize"][1]));
36             region = image.crop(box);
37             newImage.paste(region, (int(v["colorOrigin"][0]), int(v["colorOrigin"][1])));
38             if v["isRotated"] == True:
39                 newImage = newImage.rotate(90);
40             newImage.save(self.__resPath + k);
41
42 if __name__ == "__main__":
43     textureUnPacker = TextureParser("/Users/adv/Desktop/", "bbb");
44     textureUnPacker.smallPicsCreate();
45     print("success!")

    PlistToDict.py,用的是dom解析plist。dom怎么用自行百度。

 1 from xml.dom import minidom
 2
 3 class PlistToDict(object):
 4     def __init__(self, plistPath):
 5         dom = minidom.parse(plistPath);
 6         self.__root = dom.documentElement;
 7
 8     # get root dict
 9     def __getFirstDictDoc(self):
10         children = self.__root.childNodes;
11         for v in children:
12             if v.nodeType == v.ELEMENT_NODE and v.nodeName == "dict":
13                 return v;
14         return None;
15
16     # get value by key in doc‘s children
17     def __getValueDocByKey(self, doc, key):
18         children = doc.childNodes;
19         for v in children:
20             if v.nodeType == v.ELEMENT_NODE and v.nodeName == "key" and v.firstChild.nodeValue == key:
21                 node = v.nextSibling;
22                 while node.nodeType != node.ELEMENT_NODE:
23                     node = node.nextSibling;
24                     if node == None:
25                         return None;
26                 return node;
27         return None;
28
29     def __firstElementNodeName(self, doc):
30         for v in doc.childNodes:
31             if v.nodeType == v.ELEMENT_NODE:
32                 return v.nodeName;
33
34     def __docToDict(self, dom, dic):
35         keys = self.__getAllKeyValuesInDoc(dom);
36         for key in keys:
37             valueNode = self.__getValueDocByKey(dom, key);
38             if valueNode.nodeName == "dict":
39                 dic[key] = {}
40                 self.__docToDict(valueNode, dic[key]);
41             elif valueNode.nodeName == "false":
42                 dic[key] = False;
43             elif valueNode.nodeName == "true":
44                 dic[key] = True;
45             else:
46                 dic[key] = valueNode.firstChild.nodeValue;
47
48     def __getAllKeyValuesInDoc(self, doc):
49         ret = [];
50         for v in doc.childNodes:
51             if v.nodeName == "key":
52                 ret.append(v.firstChild.nodeValue);
53         return ret;
54
55     def createDict(self):
56         rootDict = self.__getFirstDictDoc();
57         ret = {};
58         self.__docToDict(rootDict, ret);
59         return ret;

    BraceParser.py,用来解析括号。

 1 class BraceParser(object):
 2     def __init__(self, str):
 3         self.__strToParse = str.replace(" ", "");
 4
 5     def __firstStrIsLeftBrace(self, str):
 6         return True if str[0] == "{" else False;
 7
 8     def __subOutBrace(self, str):
 9         return str[1:-1];
10
11     def __findAllSeqCommaPos(self, str):
12         bracketNum = 0;
13         ret = [];
14         for i, v in enumerate(str):
15             if v == "{":
16                 bracketNum += 1;
17             elif v == "}":
18                 bracketNum -= 1;
19             elif v == ",":
20                 if bracketNum == 0:
21                     ret.append(i);
22         return ret;
23
24     # {111,324},{100,100} return ["{111,324}", "{100,100}"]
25     def __getAllBraceStrs(self, str):
26         listStr = [];
27         posList = self.__findAllSeqCommaPos(str);
28         lastPos = -1;
29         for v in posList:
30             listStr.append(str[lastPos + 1: v]);
31             lastPos = v;
32         listStr.append(str[lastPos + 1: ]);
33         return listStr;
34
35     def __getValue(self, str):
36         listStr = str.split(",");
37         return listStr[0], listStr[1];
38
39
40     def __listCreate(self, str, listIns):
41         if self.__firstStrIsLeftBrace(str) == True:
42             braceStrs = self.__getAllBraceStrs(str);
43             for v in braceStrs:
44                 subList = [];
45                 listIns.append(subList);
46                 self.__listCreate(self.__subOutBrace(v), subList);
47         else:
48             x, y = self.__getValue(str);
49             listIns.append(float(x));
50             listIns.append(float(y));
51
52     def createList(self):
53         listIns = [];
54         str = self.__subOutBrace(self.__strToParse);
55         self.__listCreate(str, listIns);
56         return listIns;

    最后,我想问博客园怎么上传附件?

时间: 2024-10-07 05:45:16

TexturePacker大图还原成小图工具带源码的相关文章

Texture的渲染大图裁剪成小图并保存下来

我们今天就简单说下 cocos2d的Texture的简单用法,并将一张大图裁剪成小图并保存起来 我们先准备一张大图,如下: 只看图,表太在意内容啊. 我们的目的就是将这张大图裁剪成一张张的小图并保存下来. 首先,我们要将它裁剪成一张张的小图,小图的大小自己计算哈,我就不告诉你w=Width/5,h=(Height-108)/8的: 然后,将裁剪出来的小图一张张的绘制出来: 最后,将绘制的每一张保存起来. 我怎么这么多话啊,说了这么多,一行代码也写呢,好了,直接上代码: auto size = D

可视化工具gephi源码探秘(二)---导入netbeans

在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码的可行性不高以及熟悉netbeans,并把原本基于netbeans平台开发的gephi源码导入进netbeans后启动正常运行的过程,其中有遇到的不少问题和相应的解决方法. 前日工作梗概(还是沿着想把源码导入myeclipse的思路): 经过从各大子模块的pom.xml中筛选出符合条件的jar包写入项目下的p

可视化工具gephi源码探秘(二)

在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码的可行性不高以及熟悉netbeans,并把原本基于netbeans平台开发的gephi源码导入进netbeans后启动正常运行的过程,其中有遇到的不少问题和相应的解决方法. 前日工作梗概(还是沿着想把源码导入myeclipse的思路): 经过从各大子模块的pom.xml中筛选出符合条件的jar包写入项目下的p

VC++环境下多文档模板应用程序开发(带源码)

我们以前见到的关于VC++环境下利用APP Wizard 自动生成的MDI的应用程序只支持打开同一文档类型的多个文件,但是不能够同时打开不同种类型的文档.网上的这种公开资料比较少,我在知网上查阅了相关的资料.下面我根据这些资料做一下总结(辛辛苦苦一个一个字码出来的). 下面设计一个能够处理两种文档类型的应用程序:第一种文档类型:TXT文档,第二种文档类型:Bub的文档类型. 第一种的文档类型TXT,是利用APP Wizard自动生成的,只不过是在设置的时候将文档类型设置为TXT类型,并且使生成的

MyEclipse使用总结——使用MyEclipse打包带源码的jar包

平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不到jar包里面的类的源码了,所以也就无法调试,要想调试,那么就只能通过关联源代码的形式,这样或多或少也有一些不方便,今天教大家一种打包jar包的方式,让jar包当中既有编译好的class,又有class对应的源代码,这样在其他项目中使用打包好的jar包时,就可以直接看到class的源代码了,也可以直接进行源码调试,不再需要额外关联源代码了,具体做法如下: 假如有一个如下图所示的项目 现在我们需要将src目录下的ja

用Diff和Patch工具维护源码

在Unix系统下,维护源码版本可以使用很多方法,其中最常用的当然是大名鼎鼎的CVS,但实际上,简单的版本维护工作并没有必要使用复杂的CVS等专门的版本维护工具,Unix标配中的diff和patch工具就完全可以完成代码的简单备份和升级工作. diff以"行"为单位比较两个文本文件(也可以是目录比较),并将不同之处以某种格式输出到标准输出上:patch可以读入这种输出,并按照一定指令使源文件(目录)按照目标文件(目录)更新.Linux内核源码就是按照这种方式保持更新的,我们在www.ke

【百度地图API】多家地图API内存消耗对比测验(带源码)

原文:[百度地图API]多家地图API内存消耗对比测验(带源码) 任务描述: 啊,美妙的春节结束了.酸奶小妹和妈妈的山西平遥之旅也宣告成功!距离平遥古城7km,有一个同样身为“世界文化遗产”的寺庙,叫做“双林寺”.双林寺的精致.纯木质结构.保存完好等特点,让我不由得为之一振.这让我想到了,万事万物都需要对比,“取其精华,去其糟粕”. 双林寺如是,API也如是.这不,上班第一天,我就迫不及待地做起了API性能测试. 如何实现: 使用不同家的API,分别以随机新增覆盖物为测试用例,观察内存消耗变化.

一大波资源来袭:工具,源码,职场攻略

又要一年了,不要再做个傻傻的程序员,然后不停的问:为什么技术好.工作认真却还败给那些不如自己的人,觉得很是不公平, No No No,你还修炼的不够,不过听Dev的刘说移动应用开发者要涨工资啦,有木有很幸福的赶脚,还有人说由于科技人才短缺,美国放松了留美毕业生工作政策,但也有其它报道称美国技术类绿卡申请越来越难,奥巴马新宣布的移民新政也不利于留学生的发展,这到底是怎么回事呢?难道在美国的留学生会回来抢国内旭元的饭碗,还是去看看是不是真得吧. 对了,刚才说到修炼不够,我是来送福利的,给你的一大波资

使用MyEclipse打包带源码的jar包

平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不到jar包里面的类的源码了,所以也就无法调试,要想调试,那么就只能通过关联源代码的形式,这样或多或少也有一些不方便,今天教大家一种打包jar包的方式,让jar包当中既有编译好的class,又有class对应的源代码,这样在其他项目中使用打包好的jar包时,就可以直接看到class的源代码了,也可以直接进行源码调试,不再需要额外关联源代码了,具体做法如下: 假如有一个如下图所示的项目 现在我们需要将src目录下的ja