1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| import os import re from PIL import Image
def parse_plist(content): """ 精准解析 plist 格式,只匹配真正的图片项 """ # 更加精确的匹配:寻找嵌套在 dict 里的 key,且 key 必须以 .png 结尾 # 排除掉外层的 <key>frames</key> frames = {} # 1. 先定位到 <key>frames</key> 之后的内容,避免抓到头部的 frames 标签 frames_block_match = re.search(r"<key>frames</key>\s*<dict>(.*?)</dict>\s*<key>metadata</key>", content, re.DOTALL) if not frames_block_match: # 如果没找到特定的块,尝试全局搜索 block = content else: block = frames_block_match.group(1)
# 2. 匹配具体的图片条目 pattern = re.compile(r"<key>(.*?\.png)</key>\s*<dict>(.*?)</dict>", re.DOTALL) items = pattern.findall(block)
for name, data in items: # 清洗文件名:去掉可能存在的路径、换行符或空格 clean_name = os.path.basename(name.strip()) # 提取 frame: {{x,y},{w,h}} frame_match = re.search(r"<key>frame</key>\s*<string>\{(.*?)\}</string>", data) # 提取 rotated rotated_match = re.search(r"<key>rotated</key>\s*<(true|false)/>", data) if frame_match: # 提取数字 coords = [int(n) for n in re.findall(r"\d+", frame_match.group(1))] is_rotated = (rotated_match.group(1) == "true") if rotated_match else False frames[clean_name] = { "x": coords[0], "y": coords[1], "w": coords[2], "h": coords[3], "rotated": is_rotated } return frames
def split_atlas(img_path, plist_path, output_dir): if not os.path.exists(output_dir): os.makedirs(output_dir)
with open(plist_path, 'r', encoding='utf-8') as f: content = f.read()
frames = parse_plist(content) if not frames: print("未能解析到任何有效图片信息,请检查 plist 格式。") return
try: atlas_img = Image.open(img_path) except Exception as e: print(f"无法打开图片文件: {e}") return
for name, info in frames.items(): # TexturePacker 逻辑:如果旋转了,plist 里的 w/h 是原始尺寸, # 但在合图中它是倒下来的,所以裁剪框的宽高要交换。 if info['rotated']: rect_w, rect_h = info['h'], info['w'] else: rect_w, rect_h = info['w'], info['h'] # 裁剪 (left, upper, right, lower) box = (info['x'], info['y'], info['x'] + rect_w, info['y'] + rect_h) sub_img = atlas_img.crop(box)
# 旋转恢复 if info['rotated']: sub_img = sub_img.rotate(90, expand=True)
# 保存 try: output_path = os.path.join(output_dir, name) sub_img.save(output_path) print(f"成功保存: {name}") except Exception as e: print(f"保存 {name} 失败: {e}")
if __name__ == "__main__": split_atlas("menu/menu.png", "menu/menu.plist", "menu/output_fish")
|