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文件如下所示。 

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>frames</key>
<dict>
<key>grossini_dance_03.png</key>
<dict>
<key>frame</key>
<string>{{46,324},{63,109}}</string>
<key>offset</key>
<string>{-6,-1}</string>
<key>rotated</key>
<false/>
<key>sourceColorRect</key>
<string>{{5,7},{63,109}}</string>
<key>sourceSize</key>
<string>{85,121}</string>
</dict>
</dict>
<key>metadata</key>
<dict>
<key>format</key>
<integer>2</integer>
<key>realTextureFileName</key>
<string>bbb.png</string>
<key>size</key>
<string>{512,512}</string>
<key>smartupdate</key>
<string>$TexturePacker:SmartUpdate:ea1bbb1419cd4c346debb54e1a7d5de2:1/1$</string>
<key>textureFileName</key>
<string>bbb.png</string>
</dict>
</dict>
</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文件位置,第二个为文件名字。

 import PIL.Image as Image
import BraceParser
import PlistToDict class TextureParser(object):
def __init__(self, path, name):
self.__resPath = path;
self._name = name;
self.__plistDict = PlistToDict.PlistToDict(path + "/" + name + ".plist").createDict(); #return map[picName : {originPoint : {x:, y:}, size : {width:, height:}}]
def __getSmallPicInfos(self):
picInfo = {};
for key, value in self.__plistDict["frames"].items():
size = BraceParser.BraceParser(value["sourceSize"]).createList();
origin = BraceParser.BraceParser(value["frame"]).createList();
sourceSize = BraceParser.BraceParser(value["sourceColorRect"]).createList();
picInfo[key] = {"size" : size,
"origin" : [origin[0][0], origin[0][1]],
"colorOrigin" : [sourceSize[0][0], sourceSize[0][1]],
"colorSize" : [sourceSize[1][0], sourceSize[1][1]],
"isRotated" : value["rotated"]
};
return picInfo; def smallPicsCreate(self, pathToStore = None):
image = Image.open(self.__resPath + "/" + self._name + ".png");
picInfos = self.__getSmallPicInfos();
for k, v in picInfos.items():
if v["isRotated"] == True:
v["size"][0], v["size"][1] = v["size"][1], v["size"][0];
v["colorSize"][0], v["colorSize"][1] = v["colorSize"][1], v["colorSize"][0];
newImage = Image.new("RGBA", (int(v["size"][0]),int(v["size"][1])));
box = (int(v["origin"][0]), int(v["origin"][1]),
int(v["origin"][0] + v["colorSize"][0]), int(v["origin"][1] + v["colorSize"][1]));
region = image.crop(box);
newImage.paste(region, (int(v["colorOrigin"][0]), int(v["colorOrigin"][1])));
if v["isRotated"] == True:
newImage = newImage.rotate(90);
newImage.save(self.__resPath + k); if __name__ == "__main__":
textureUnPacker = TextureParser("/Users/adv/Desktop/", "bbb");
textureUnPacker.smallPicsCreate();
print("success!")

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

 from xml.dom import minidom

 class PlistToDict(object):
def __init__(self, plistPath):
dom = minidom.parse(plistPath);
self.__root = dom.documentElement; # get root dict
def __getFirstDictDoc(self):
children = self.__root.childNodes;
for v in children:
if v.nodeType == v.ELEMENT_NODE and v.nodeName == "dict":
return v;
return None; # get value by key in doc's children
def __getValueDocByKey(self, doc, key):
children = doc.childNodes;
for v in children:
if v.nodeType == v.ELEMENT_NODE and v.nodeName == "key" and v.firstChild.nodeValue == key:
node = v.nextSibling;
while node.nodeType != node.ELEMENT_NODE:
node = node.nextSibling;
if node == None:
return None;
return node;
return None; def __firstElementNodeName(self, doc):
for v in doc.childNodes:
if v.nodeType == v.ELEMENT_NODE:
return v.nodeName; def __docToDict(self, dom, dic):
keys = self.__getAllKeyValuesInDoc(dom);
for key in keys:
valueNode = self.__getValueDocByKey(dom, key);
if valueNode.nodeName == "dict":
dic[key] = {}
self.__docToDict(valueNode, dic[key]);
elif valueNode.nodeName == "false":
dic[key] = False;
elif valueNode.nodeName == "true":
dic[key] = True;
else:
dic[key] = valueNode.firstChild.nodeValue; def __getAllKeyValuesInDoc(self, doc):
ret = [];
for v in doc.childNodes:
if v.nodeName == "key":
ret.append(v.firstChild.nodeValue);
return ret; def createDict(self):
rootDict = self.__getFirstDictDoc();
ret = {};
self.__docToDict(rootDict, ret);
return ret;

    BraceParser.py,用来解析括号。

 class BraceParser(object):
def __init__(self, str):
self.__strToParse = str.replace(" ", ""); def __firstStrIsLeftBrace(self, str):
return True if str[0] == "{" else False; def __subOutBrace(self, str):
return str[1:-1]; def __findAllSeqCommaPos(self, str):
bracketNum = 0;
ret = [];
for i, v in enumerate(str):
if v == "{":
bracketNum += 1;
elif v == "}":
bracketNum -= 1;
elif v == ",":
if bracketNum == 0:
ret.append(i);
return ret; # {111,324},{100,100} return ["{111,324}", "{100,100}"]
def __getAllBraceStrs(self, str):
listStr = [];
posList = self.__findAllSeqCommaPos(str);
lastPos = -1;
for v in posList:
listStr.append(str[lastPos + 1: v]);
lastPos = v;
listStr.append(str[lastPos + 1: ]);
return listStr; def __getValue(self, str):
listStr = str.split(",");
return listStr[0], listStr[1]; def __listCreate(self, str, listIns):
if self.__firstStrIsLeftBrace(str) == True:
braceStrs = self.__getAllBraceStrs(str);
for v in braceStrs:
subList = [];
listIns.append(subList);
self.__listCreate(self.__subOutBrace(v), subList);
else:
x, y = self.__getValue(str);
listIns.append(float(x));
listIns.append(float(y)); def createList(self):
listIns = [];
str = self.__subOutBrace(self.__strToParse);
self.__listCreate(str, listIns);
return listIns;

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

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

  1. Python - 工具:将大图切片成小图,将小图组合成大图

    训练keras时遇到了一个问题,就是内存不足,将 .fit 改成 .fit_generator以后还是放不下一张图(我的图片是8192×8192的大图==64M).于是解决方法是将大图切成小图,把小图 ...

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

    在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码 ...

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

    在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码 ...

  4. Orchard CMS中如何打包不带源码的模块

    在Orchard CMS的官网已经提供了文档说明如何打包,但是如果使用它的打包方式,打好的nuget包是带源代码的.如果是为开源系统写模块,不需要关注源代码是否可见.但是如果是用Orchard CMS ...

  5. 转】MyEclipse使用总结——使用MyEclipse打包带源码的jar包

    原博文出自于: http://www.cnblogs.com/xdp-gacl/p/4136303.html 感谢! 平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不 ...

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

    平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不到jar包里面的类的源码了,所以也就无法调试,要想调试,那么就只能通过关联源代码的形式,这样或多或少也有一些不方便,今 ...

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

    原文:[百度地图API]多家地图API内存消耗对比测验(带源码) 任务描述: 啊,美妙的春节结束了.酸奶小妹和妈妈的山西平遥之旅也宣告成功!距离平遥古城7km,有一个同样身为“世界文化遗产”的寺庙,叫 ...

  8. shiro实现无状态的会话,带源码分析

    转载请在页首明显处注明作者与出处 朱小杰      http://www.cnblogs.com/zhuxiaojie/p/7809767.html 一:说明 在网上都找不到相关的信息,还是翻了大半天 ...

  9. 开源方案搭建可离线的精美矢量切片地图服务-8.mapbox 之sprite大图图标文件生成(附源码)

    项目成果展示(所有项目文件都在阿里云的共享云虚拟主机上,访问地图可以会有点慢,请多多包涵). 01:中国地图:http://test.sharegis.cn/mapbox/html/3china.ht ...

随机推荐

  1. Elasticsearch使用REST API实现全文检索

    通过rest api添加检索数据,阅读官方文档可以发现,elasticsearch支持动态映射,但是其中有不少问题,且听慢慢详解. 本文主要讲述三点内容: 1 Elasticsearch常用的rest ...

  2. 用Latex写学术论文:作者(Author)&摘要(Abstract)

    标题&作者 1.标题 \title{} "Line breaks (\\) may be used to equalize the length of the title lines ...

  3. orleans/Documentation

    福利 奥尔良的主要好处是︰开发人员工作效率,甚至为非专家程序员;和默认的透明可伸缩性与程序员没有特别努力.我们扩大每个下面这些好处. 开发人员的生产力 奥尔良的编程模型通过提供以下关键的抽象. 担保和 ...

  4. VS2012 JSON、XML自动生成对应的类

    在VS编辑下拉框中,选择选择性粘贴(Paste Special)

  5. Emit学习(4) - Dapper解析之数据对象映射(一)

    感觉好久没有写博客了, 这几天有点小忙, 接下来会更忙, 索性就先写一篇吧. 后面估计会有更长的一段时间不会更新博客了. 废话不多说, 先上菜. 一.示例 1. 先建类, 类的名称与读取的表名并没有什 ...

  6. web前端学习笔记---实现雪花飘落的效果

    看了javascript网页特效实例大全中的图片飘下的效果实例,觉得值得动手学习下. 就把图片改成雪花图,完成一个雪花飘下的效果. 并且,其中有些内容比较陈旧了,那么就学者改掉吧. 包括: 1.对le ...

  7. EXCEL countif函数多条件

    在MS EXCEL中,countif如果要满足多个条件怎么办呢? 1.答案就是:使用条件集合{}和sum函数. 即在countif()第二个参数中使用条件集合{},然后用sum()函数求满足这些条件的 ...

  8. 一个十年java程序员的心得

    展望未来,总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告 走过的路,回忆起来是那么曲折,把自己的一些心得体会分享给程序员兄弟姐妹们,虽然时代在变化,但是很可能你也会走我已经做过的1 ...

  9. 2016 大连网赛---Different GCD Subarray Query(GCD离散+树状数组)

    题目链接 http://acm.split.hdu.edu.cn/showproblem.php?pid=5869 Problem Description This is a simple probl ...

  10. 简单可用好实现的 HA 高可用设计

    本文为作者原创,如需转载请注明出处. 1. 实现的功能 一主多备,自动选主 启动记录可查询 2. 前置需求 一台数据库用以记录,如 MySQL.Redis.MongoDB 等.关键是设计中的思想,用啥 ...