初学python 解析atlas拆解spine图片

时间:2023-01-08 04:08:30

python作为脚本语言,还是十分的不错的,拥有强大的模块库供开发者使用。开发过程中,解释器会将python代码解析为字节码或字节码文件.pyc,有时候会将部分字节码进行优化为二进制指令,虽然无法跟c,c++在性能上相比较,除非你对性能的要求比较高,python已然足够,即当你需要性能或者发现性能的时候再去考虑性能,或许也是为时不晚的。python同时也是oop面向对象的,这对于熟悉c++的开发人员来说,python其实在代码编写和设计上是十分熟悉的,能够快速的掌握。

大多数情况下,我们用python都只是用其写一个脚本,帮助我们完成一些重复的自动化的工作,比如cocos中的setup.py环境变量设置,ndk编译和资源拷贝都可以让python去处理,甚至是生成c++代码,辅助我们去编写游戏。在游戏中,大多重复的劳动是耗费在ui界面上,但是大多ui界面基本都大同小异,比如大多数界面都需要一串按钮,点击关闭就关闭界面,点击领取就给服务端发送相关的解析,各个功能之间的差别极小,往往只需要添加一点点条件或者状态判断即可。这时候脚本就显得闪闪发光了!(GP泛型编程也能解决代码复用的问题,甚至性能和效率上更优于cwithclass的做法,这里不做考虑)

接下来,我简单介绍下python中常用的功能、模块(2.7.7):

#coding=utf-8整个文件以utf-8编码保存

os,sys,time,platform,Queue,shutil(PIL,Tkinter),光作为脚本以上模块已经基本够用,如果做python服务器或者web,threading,mutiprocess或许用的更多(但是我想用python做web的应该不算多吧- -)

print,type,isinstance,reload,raw_input,input,fopen方法也是比较重要

list,tuple,dict,int,float,str等作为基本的数据类型,使用度更不用说(注意:切片功能十分重要,enumerate可生成key,value成对的list,整型、字符串、tuple是不可变,作为参数传入的时候传的其实是原始值的拷贝,list可变)

还有更多强大的功能可以去看python教程(*arg,**kw,metaclass,reduce,map,iter,@property):

接下来是解析atlas:

atlas: spine骨骼动画导出的类似于plist功能的json格式文件,用来表示打包图中碎块的位置,大小等信息,网上有很多拆plist,缺好像没见拆atlas的数据,所以这里用python实现了一下:

kbtools.py (简单的功能模块,不依赖于类对象的放在tools模块中)

#coding=utf-8
import os,sys,platform,time
from os.path import *
from tkMessageBox import *
__auther__='cjsjy123'

global Res,Total_Times,Logs_Times,Total_File_Nums,M_RES_LIST,ALL_FILE_LIST
#总共消耗的时间
Total_Times =[float(0),]
#单个函数消耗的时间字符串
Logs_Times=[]
#总文件数
Total_File_Nums =[0,]
#for search
Res =[0,]

M_RES_LIST =[]
ALL_FILE_LIST=[]

def getinfo():
getTFnums()
getTtimes()
getTFtimes()

def getTFnums():
result_print('@F total file nums : %d \n' % Total_File_Nums[0])
def getTtimes():
result_print('@T total times is : %f seconds \n' % Total_Times[0])
def getTFtimes():
result_print('@f total fun times info : %s \n' % Logs_Times)

def log_time(fun):
def take_time(*arg,**kw):
start =time.time()
val = fun(*arg,**kw)
end =time.time()
result_print('Function: %s ,it takes %f seconds'% (fun.__name__,end-start))
Logs_Times.append('Function: %s ,it takes %f seconds'% (fun.__name__,end-start))
Total_Times[0] += end-start
return val
return take_time

def checkVer():
if int(platform.python_version().split('.')[0])>=3:
errinfo = 'Now you are using %s,plz refer to 2.7(2.x)!' % platform.python_version()
showerror('ERROR',errinfo)
return False
else:
result_print('It\'s Ok. The version is %s' % (platform.python_version()))
return True

def file_switch(file,fullpath):
global Res,Total_File_Nums
Res[0] =1
Total_File_Nums[0] +=1
print 'ENTER!!!!! %s == %s' % (file,fullpath)
# print 'test %s' % file
if isdir(fullpath):
print 'enter 111'
ALL_FILE_LIST.append(fullpath)
result_print ('found it\'s dir: %s' % fullpath)
elif isfile(fullpath):
print 'enter 222'
ALL_FILE_LIST.append(fullpath)
result_print ('found it\'s %s file: %s' % (splitext(fullpath)[1],fullpath))
else:
print 'enter 333'
ALL_FILE_LIST.append(fullpath)
result_print ('found it\'s Unknown File :%s' % fullpath)

def check_utf8(str):
if isinstance(str,unicode):
print 'str :%s is unicode'% str
else:
print 'str :%s is utf8' % str

# @log_time
def FileSearch(str ='',search_path =''):
def visit(arg,dirname,files):
for file in files:
full_path = join(dirname,file)
print 'full=== %s'% full_path
if arg.upper() in file.upper():
file_switch(file,full_path)
name =str
global Total_File_Nums
Total_File_Nums =[0,]
if str =='':
name = raw_input('input something in order to search in the curent dir and subs:\n')
name = name.encode('utf-8')

if search_path == '':
print '-1'
walk(os.getcwd(),visit,name)
else:
print '-2 %s' % name
walk(search_path,visit,name)
#check result
if Res[0]==0:
result_print('Not Found!')
else:
info ='Found %s Files'% Total_File_Nums[0]
showinfo('FIND_INFO',info)

def result_print(str):
global M_RES_LIST
M_RES_LIST.append(str+'\n')

if __name__ == '__main__':
print checkVer()

#coding=utf-8
import os,sys,time,platform
from os.path import *
from tkCommonDialog import Dialog
from Tkinter import *
from tkFileDialog import *
from tkMessageBox import *
from ScrolledText import *
from kbtools import *
try:
import Image
except ImportError:
showerror('Error','Not Found Image module')

__auther__='cjsjy123'

#数据格式 元表
SPINE_STRUCT =('png_path','format','filter','size_wid','size_hei','repeat')


class AntiAtlas:
'''
@ATLAS_STRUCT,pnglist
girl.png
size: 1017,510
format: RGBA8888
filter: Linear,Linear
repeat: none
attment
rotate: false
xy: 541, 159
size: 37, 23
orig: 39, 25
offset: 1, 1
index: -1
'''
def __init__(self,file_path='.'):
self.ATLAS_STRUCT ={}
self.pnglist =[]
self.file_path =file_path;
self.ResourcePath =''
self.flag=False

@log_time
def start_analyze(self,m_view=None):
if checkVer():
print '\n----start analyzing------\n'
print 'The current platform is %s' % sys.platform
#windows
if sys.platform =='win32':
if self.file_path.endswith('.atlas')== False:
showerror('Error','Not Atlas File ! plz check it !')
elif exists(self.file_path) ==False:
showerror('Error','Not Dound Atlas File ! plz check it !')
else:
with open(self.file_path,'r') as f:
# print f.read()
# self.oldatlas =f.read()
# linenum=0;
for lines in f.readlines():
# linenum +=1
# print ' len %d num : %d %s' %(len(lines),linenum,lines)
#去除空格
self.analyze(''.join(lines.split()))
# self.ATLAS_STRUCT['pnglist'] = self.pnglist
self.img =Image.open(self.ResourcePath)
self.split_png()
if m_view != None:
m_view.result_text.insert(END,M_RES_LIST)
#mac
elif sys.plaform == 'darwin':
pass
'''
warning: attment cant include ':'
rotate
x y
width height
orig_x orig_y
offset_x offset_y
index
'''
def singlePng(self,str):
if ':' in str:
if str.startswith('rotate'):
if str.split(':')[-1] =='false':
self.pnglist[-1]['rotate'] = False
else:
self.pnglist[-1]['rotate'] = True
elif str.startswith('xy'):
self.pnglist[-1]['x'] = int(str.split(':')[-1].split(',')[0])
self.pnglist[-1]['y'] = int(str.split(':')[-1].split(',')[-1])
elif str.startswith('size'):
self.pnglist[-1]['width'] = int(str.split(':')[-1].split(',')[0])
self.pnglist[-1]['height'] = int(str.split(':')[-1].split(',')[-1])
elif str.startswith('orig'):
self.pnglist[-1]['orig_x'] = int(str.split(':')[-1].split(',')[0])
self.pnglist[-1]['orig_y'] = int(str.split(':')[-1].split(',')[-1])
elif str.startswith('offset'):
self.pnglist[-1]['offset_x'] = int(str.split(':')[-1].split(',')[0])
self.pnglist[-1]['offset_y'] = int(str.split(':')[-1].split(',')[-1])
elif str.startswith('index'):
self.pnglist[-1]['index'] = int(str.split(':')[-1])
else:
newDic={}
newDic['attmentname'] =str
self.pnglist.append(newDic)

def analyze(self,str):
# print '==== %s --- %d ~~ %s **** %s'% (str,len(str),self.flag,str.find('png'))
if str != '':
if self.flag ==False:
if str.find('.png') != -1:
self.ATLAS_STRUCT[SPINE_STRUCT[0]] = str
self.ResourcePath = str
elif str.startswith('format'):
self.ATLAS_STRUCT[SPINE_STRUCT[1]] = str.split(':')[-1]
elif str.startswith('filter'):
self.ATLAS_STRUCT[SPINE_STRUCT[2]] = str.split(':')[-1]
elif str.startswith('size'):
self.ATLAS_STRUCT[SPINE_STRUCT[3]] = int(str.split(':')[-1].split(',')[0])
self.ATLAS_STRUCT[SPINE_STRUCT[4]] = int(str.split(':')[-1].split(',')[-1])
elif str.startswith('repeat'):
self.ATLAS_STRUCT[SPINE_STRUCT[5]] = str.split(':')[-1]
self.flag= True
else:
self.singlePng(str)

def split_png(self):
if self.ResourcePath =='':
showerror('Error','png path error in atlas ! plz check atlas or *.png')
else:
print self.img.format,self.img.size,self.img.mode
# print self.ResourcePath
for each in self.pnglist:
print 'each --%s' % each
org_x = int(each['x'])
org_y = int(each['y'])
wid = int(each['width'])
height = int(each['height'])
if each['rotate'] ==True:
region = (org_x,org_y,org_x+height,org_y+wid)
elif each['rotate'] ==False:
region = (org_x,org_y,org_x+wid,org_y+height)
print '============ %d %d %d %d' %(org_x,org_y,wid,height)
#裁切图片
cropImg= self.img.crop(region)
export_dir_name = self.ResourcePath.split('.')[0] +'_export'
export_full_path =os.path.join(os.getcwd(),export_dir_name)
print '----- %s %s' %( exists(export_full_path),isdir(export_full_path))
if exists(export_full_path) and isdir(export_full_path):
print 'it has created !'
else:
os.mkdir(export_full_path)
#保存裁切后的图片
if each['rotate'] ==True:
cropImg =cropImg.rotate(270)
print type(cropImg)
elif each['rotate'] ==False:
cropImg =cropImg.rotate(0)

print 'path ----- %s' % (os.path.join(export_full_path,each['attmentname']+'.png'))
cropImg.save(os.path.join(export_full_path,each['attmentname']+'.png'))

#view
class View(Frame):
def __init__(self,parent,master =None):
Frame.__init__(self,parent,master)
self.parent = parent
#680,384

self.pack(fill=BOTH,expand =1)
self.btlist = []
self.lbllist = []
self.editlist= []
self.searchpath =os.getcwd()
self.atlat_searchpath =os.getcwd()
self.createWindow()

def createWindow(self):
scnWidth = self.parent.winfo_screenwidth()
scnHeight = self.parent.winfo_screenheight()
info ='%sx%s+%s+%s' %(600,500,scnWidth/2 -250,scnHeight/2-250)
self.parent.geometry(info)

self.label_1 =Label(self,text ='文件搜索功能:')
self.label_1.grid(row =0,column=0,sticky=E+W+S+N)

self.a_label_1 =Label(self,text ='atlas拆分:')
self.a_label_1.grid(row =2,column=0,sticky=E+W+S+N)
# self.label_1.pack(fill=BOTH,side =LEFT)
self.search_bt =Button(self,text ='开始搜索',command=self.cbk_start_search)
self.search_bt.grid(row=0,column=4,sticky=E+W+S+N)

self.a_search_bt =Button(self,text ='开始解析',command=self.cbk_start_analize)
self.a_search_bt.grid(row=2,column=4,sticky=E+W+S+N)

self.search_bt =Button(self,text ='清空',command=self.clear)
self.search_bt.grid(row=0,column=10,sticky=E+W+S+N,ipadx=30)

self.check_val =[0,]
self.check_bt=Checkbutton(self,text ='只搜索atlas',command=self.cbk_check)
self.check_bt.grid(row=3, column=8)

self.s_defalut_name =StringVar()
self.s_defalut_name.set('请输入关键字 ')
self.s_Input = Entry(self,textvariable =self.s_defalut_name)
self.s_Input.grid(row=0,column=2,columnspan=2,padx =10,sticky=E+W+S+N,ipadx=50)

self.s_choice_b =Button(self,text ='...',command=self.cbk_search)
self.s_choice_b.grid(row=0, column=8,sticky=E+W+S+N,padx=10,ipadx =10)

self.a_choice_b =Button(self,text ='...',command=self.cbk_atlas_search)
self.a_choice_b.grid(row=2,column =8,sticky=E+W,padx=10,ipadx=10)

self.a_defalut_name =StringVar()
self.a_defalut_name.set('请输入atlas和png所在的文件夹 ')
self.a_Input = Entry(self,textvariable =self.a_defalut_name)
self.a_Input.grid(row=2,column=2,columnspan=2,padx =10,sticky=E+W+S+N,ipadx=50)

self.result_text = ScrolledText(bg='white', height=35)
self.result_text.focus_set()
self.result_text.pack(fill=BOTH)

def clear(self):
print 'size %s' % self.result_text.size

def cbk_check(self):
if self.check_val[0] == 1:
self.check_val[0] = 0
elif self.check_val[0] == 0:
self.check_val[0] =1

def cbk_search(self):
self.searchpath = askdirectory()

def cbk_atlas_search(self):
self.atlat_searchpath = askdirectory()

def cbk_start_search(self):
global ALL_FILE_LIST
st=self.s_defalut_name.get().encode('utf-8')
print '============================== %s' % st
FileSearch(st,self.searchpath)
print '%s --- %s' % (st,self.searchpath)

if self.check_val[0] ==1:
temp_list=[]
print '3'
for each in ALL_FILE_LIST:
if each.find('.atlas') !=-1:
temp_list.append(each)
ALL_FILE_LIST=temp_list
for val in ALL_FILE_LIST:
info='Found '+splitext(val)[-1]+' file '+val+'\n'
self.result_text.insert(END,info)

def cbk_start_analize(self):
for each in ALL_FILE_LIST:
print each
anti =AntiAtlas(each)
anti.start_analyze()

if __name__ == '__main__':
root = Tk()
App = View(root)
App.master.title('AntiAtlas 工具')
App.mainloop()

# FileSearch('.atlas')
# getinfo()
# anti =AntiAtlas('spineboy.atlas')
# anti.start_analyze()
# print anti.ATLAS_STRUCT
# showinfo('INFO','it finished')
# raw_input()

log_time装饰器和time模块用来测试函数时间,os.path去调整路径,encode去转换编码,tkinter显示ui界面,golbal 表示全局变量,PIL拆解图片(PIL不是内置模块,需要额外下载安装)

最后搞了个冻结二进制(将PVM和代码全部打包成一个exe文件),用pyinstaller。简单工具,bug还是有的~不过影响不大。注意要先安装PIL哦,先放出EXE:Antiatlas