• 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 : {, 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;

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

  • 相关阅读:
    正则表达式
    正则表达式-量词
    正则表达式-字符组
    6月学习总结
    利用Magick和gs实现pdf到jpg的转换
    NGS Antenna Calibrations
    VS2017运行旧版本下的C程序工程
    C#.NET SQLite自适应32位/64位系统
    RAID1环境下外挂第三块硬盘
    修复 Fontconfig Error
  • 原文地址:https://www.cnblogs.com/BigFeng/p/4659261.html
Copyright © 2020-2023  润新知