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}是红框内的大小。

TexturePacker大图还原成小图工具带源码 图1 TexturePacker大图还原成小图工具带源码 图2 TexturePacker大图还原成小图工具带源码 图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;

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

正文到此结束
Loading...