Path: blob/master/src/engine/xmlpngengine.py
254 views
import xml.etree.ElementTree as ET1from PIL import Image, ImageChops2from os import path, linesep34from utils import imghashes, g_settings, clean_filename56from engine.packingalgorithms import GrowingPacker, OrderedPacker7from engine.spritesheetutils import get_true_frame, add_pose_numbers8from engine.imgutils import pad_img91011def fast_image_cmp(im1, im2): # im1 == im2 ?12if im1.size != im2.size:13return False14if im1.tobytes() != im2.tobytes():15return False1617return ImageChops.difference(im1, im2).getbbox() is None1819def make_png_xml(frames, save_dir, character_name="Result", progressupdatefn=None, settings=None):20if settings is None:21settings = g_settings22prefix_type = settings.get('prefix_type', 'charname') # use character name or use a custom prefix instead23custom_prefix = settings.get('custom_prefix', '') # the custom prefix to use24must_use_prefix = settings.get('must_use_prefix', 0) != 0 # use the custom prefix even if frame is from existing spritesheet25padding_pixels = settings.get('frame_padding', 0)26packing_algorithm = settings.get('packing_algo', 0) # 0 = Growing Packer, 1 = Ordered Packer27# no_merge = settings.get('no_merge', 0) != 0 # no merging lookalike frames2829# print(len(imghashes))30# print(len(frames))3132try:33# init XML34root = ET.Element("TextureAtlas")35root.text = "\n"36root.tail = linesep37root.attrib['imagePath'] = f"{character_name}.png"3839new_pose_names = add_pose_numbers(frames)40for f, pose in zip(frames, new_pose_names):41final_pose_name = pose42if f.data.from_single_png or (not f.data.from_single_png and f.modified):43if prefix_type == 'charname':44final_pose_name = f"{character_name} {final_pose_name}"45elif prefix_type == 'custom':46final_pose_name = f"{custom_prefix} {final_pose_name}"47else:48if must_use_prefix and prefix_type == 'custom':49final_pose_name = f"{custom_prefix} {final_pose_name}"5051f.data.xml_pose_name = final_pose_name5253frame_dict_arr = []54current_img_hashes = set([x.data.img_hash for x in frames])5556# Doesn't quite work yet, still a WIP57# if no_merge:58# for f in frames:59# frame_dict_arr.append({60# "id": f.data.img_hash,61# "w": imghashes.get(f.data.img_hash).width + 2*padding_pixels,62# "h": imghashes.get(f.data.img_hash).height + 2*padding_pixels,63# "frame": f # this comes in handy later on64# })65# else:66# pass67# add the padding to width and height, then actually padding the images (kind of a hack but it works TODO: work out a better way to do this)68for imhash, img in imghashes.items():69if imhash in current_img_hashes:70frame_dict_arr.append({71"id": imhash,72"w": img.width + 2*padding_pixels,73"h": img.height + 2*padding_pixels74})7576if packing_algorithm == 1:77packer = OrderedPacker()78else:79packer = GrowingPacker()80frame_dict_arr.sort(key= lambda rect: rect.get("h", -100), reverse=True)8182packer.fit(frame_dict_arr)8384final_img = Image.new("RGBA", (packer.root['w'], packer.root['h']), (0, 0, 0, 0))85# frame_dict_arr.sort(key=lambda x: x['id'].img_xml_data.xml_posename)86prgs = 087for r in frame_dict_arr:88fit = r.get("fit")8990# accounting for user-defined padding91imhash_img = imghashes.get(r['id'])92imhash_img = pad_img(imhash_img, False, padding_pixels, padding_pixels, padding_pixels, padding_pixels)9394final_img.paste( imhash_img, (fit["x"], fit["y"]) )95prgs += 196progressupdatefn(prgs, "Adding images to spritesheet...")9798# Doesn't quite work yet, still a WIP99# if no_merge:100# for framedict in frame_dict_arr:101# frame = framedict['frame']102# subtexture_element = ET.Element("SubTexture")103# subtexture_element.tail = linesep104# w, h = imghashes.get(frame.data.img_hash).size105# subtexture_element.attrib = {106# "name" : frame.data.xml_pose_name,107# "x": str(framedict['fit']['x']),108# "y": str(framedict['fit']['y']),109# "width": str(w + 2*padding_pixels),110# "height": str(h + 2*padding_pixels),111# "frameX": str(frame.data.framex),112# "frameY": str(frame.data.framey),113# "frameWidth": str(frame.data.framew),114# "frameHeight": str(frame.data.frameh),115# }116# root.append(subtexture_element)117# prgs += 1118# progressupdatefn(prgs, f"Saving {frame.data.xml_pose_name} to XML...")119# else:120# pass121# convert frame_dict_arr into a dict[image_hash -> position in spritesheet]:122imghash_dict = { rect['id']: (rect['fit']['x'], rect['fit']['y']) for rect in frame_dict_arr }123for frame in frames:124subtexture_element = ET.Element("SubTexture")125subtexture_element.tail = linesep126w, h = imghashes.get(frame.data.img_hash).size127subtexture_element.attrib = {128"name" : frame.data.xml_pose_name,129"x": str(imghash_dict[frame.data.img_hash][0]),130"y": str(imghash_dict[frame.data.img_hash][1]),131"width": str(w + 2*padding_pixels),132"height": str(h + 2*padding_pixels),133"frameX": str(frame.data.framex),134"frameY": str(frame.data.framey),135"frameWidth": str(frame.data.framew),136"frameHeight": str(frame.data.frameh),137}138root.append(subtexture_element)139prgs += 1140progressupdatefn(prgs, f"Saving {frame.data.xml_pose_name} to XML...")141# im.close()142print("Saving XML...")143xmltree = ET.ElementTree(root)144cleanpath = path.join(save_dir, clean_filename(character_name))145with open(cleanpath + ".xml", 'wb') as f:146xmltree.write(f, xml_declaration=True, encoding='utf-8')147148print("Saving Image...")149final_img = final_img.crop(final_img.getbbox())150final_img.save(cleanpath + ".png")151final_img.close()152153print("Done!")154except Exception as e:155return -1, str(e)156157return 0, None158159def save_img_sequence(frames, savedir, updatefn):160# Saves each frame as a png161newposes = add_pose_numbers(frames)162for i, (frame, pose) in enumerate(zip(frames, newposes)):163try:164im = imghashes.get(frame.data.img_hash)165im = get_true_frame(im, frame.data.framex, frame.data.framey, frame.data.framew, frame.data.frameh)166167cleanpath = path.join(savedir, clean_filename(f"{pose}.png"))168im.save(cleanpath)169im.close()170updatefn(i+1, f"Saving: {pose}.png")171except Exception as e:172return str(e)173return None174175176if __name__ == '__main__':177print("This program is just the engine! To run the actual application, Please type: \npython xmlpngUI.py\nor \npython3 xmlpngUI.py \ndepending on what works")178179