From da511460906d67294c4033e35265fb987574eb83 Mon Sep 17 00:00:00 2001 From: UncertainProd Date: Sat, 16 Jul 2022 00:56:45 +0530 Subject: [PATCH] Added option to not merge identical frames + some bufixes --- src/SpritesheetGenSettings.ui | 16 +++++-- src/engine/icongridutils.py | 7 +-- src/engine/xmlpngengine.py | 81 ++++++++++++++++++++++++----------- src/framedata.py | 80 +++++++++++++++++----------------- src/settingswindow.py | 3 ++ src/spritesheetgensettings.py | 9 +++- 6 files changed, 124 insertions(+), 72 deletions(-) diff --git a/src/SpritesheetGenSettings.ui b/src/SpritesheetGenSettings.ui index 0a89b38..b669089 100644 --- a/src/SpritesheetGenSettings.ui +++ b/src/SpritesheetGenSettings.ui @@ -9,14 +9,14 @@ 0 0 - 611 - 520 + 603 + 552 Spritesheet Generation Settings - + @@ -137,6 +137,16 @@ Before Animation Prefix + + + + Do not merge look-alike frames +(WARNING: Can cause extremely large spritesheets which may cause windows to +refuse to open them. +May also cause crashes!) + + + diff --git a/src/engine/icongridutils.py b/src/engine/icongridutils.py index f3cdc9b..967191d 100644 --- a/src/engine/icongridutils.py +++ b/src/engine/icongridutils.py @@ -68,10 +68,11 @@ def appendIconToGrid(icongrid_path, iconpaths, iconsize=150): try: # icongrid = Image.open(icongrid_path) - with Image.open(icongrid_path) as icongrid: + with Image.open(icongrid_path).convert('RGBA') as icongrid: + # icongrid = icongrid.convert('RGBA') for iconpath in iconpaths: # icon_img = Image.open(iconpath) - with Image.open(iconpath) as icon_img: + with Image.open(iconpath).convert('RGBA') as icon_img: # check if icon_img is 150x150 can_fit = _check_icon_size(icon_img) if can_fit == ICON_BIGGER_THAN_AREA: @@ -113,7 +114,7 @@ def makePsychEngineIconGrid(iconpaths, savepath, img_size=150): good_icons = [] for iconpath in iconpaths: try: - icon = Image.open(iconpath) + icon = Image.open(iconpath).convert('RGBA') except Exception as e: exception_msg = f"{e.__class__.__name__} : {str(e)}" continue diff --git a/src/engine/xmlpngengine.py b/src/engine/xmlpngengine.py index 986ac63..fe8e4a4 100644 --- a/src/engine/xmlpngengine.py +++ b/src/engine/xmlpngengine.py @@ -25,6 +25,7 @@ def make_png_xml(frames, save_dir, character_name="Result", progressupdatefn=Non must_use_prefix = settings.get('must_use_prefix', 0) != 0 # use the custom prefix even if frame is from existing spritesheet padding_pixels = settings.get('frame_padding', 0) packing_algorithm = settings.get('packing_algo', 0) # 0 = Growing Packer, 1 = Ordered Packer + no_merge = settings.get('no_merge', 0) != 0 # no merging lookalike frames # print(len(imghashes)) # print(len(frames)) @@ -53,14 +54,23 @@ def make_png_xml(frames, save_dir, character_name="Result", progressupdatefn=Non frame_dict_arr = [] current_img_hashes = set([x.data.img_hash for x in frames]) - # 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) - for imhash, img in imghashes.items(): - if imhash in current_img_hashes: + if no_merge: + for f in frames: frame_dict_arr.append({ - "id": imhash, - "w": img.width + 2*padding_pixels, - "h": img.height + 2*padding_pixels + "id": f.data.img_hash, + "w": imghashes.get(f.data.img_hash).width + 2*padding_pixels, + "h": imghashes.get(f.data.img_hash).height + 2*padding_pixels, + "frame": f # this comes in handy later on }) + else: + # 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) + for imhash, img in imghashes.items(): + if imhash in current_img_hashes: + frame_dict_arr.append({ + "id": imhash, + "w": img.width + 2*padding_pixels, + "h": img.height + 2*padding_pixels + }) if packing_algorithm == 1: packer = OrderedPacker() @@ -84,26 +94,47 @@ def make_png_xml(frames, save_dir, character_name="Result", progressupdatefn=Non prgs += 1 progressupdatefn(prgs, "Adding images to spritesheet...") + if no_merge: + for framedict in frame_dict_arr: + frame = framedict['frame'] + subtexture_element = ET.Element("SubTexture") + subtexture_element.tail = linesep + w, h = imghashes.get(frame.data.img_hash).size + subtexture_element.attrib = { + "name" : frame.data.xml_pose_name, + "x": str(framedict['fit']['x']), + "y": str(framedict['fit']['y']), + "width": str(w + 2*padding_pixels), + "height": str(h + 2*padding_pixels), + "frameX": str(frame.data.framex), + "frameY": str(frame.data.framey), + "frameWidth": str(frame.data.framew), + "frameHeight": str(frame.data.frameh), + } + root.append(subtexture_element) + prgs += 1 + progressupdatefn(prgs, f"Saving {frame.data.xml_pose_name} to XML...") + else: # convert frame_dict_arr into a dict[image_hash -> position in spritesheet]: - imghash_dict = { rect['id']: (rect['fit']['x'], rect['fit']['y']) for rect in frame_dict_arr } - for frame in frames: - subtexture_element = ET.Element("SubTexture") - subtexture_element.tail = linesep - w, h = imghashes.get(frame.data.img_hash).size - subtexture_element.attrib = { - "name" : frame.data.xml_pose_name, - "x": str(imghash_dict[frame.data.img_hash][0]), - "y": str(imghash_dict[frame.data.img_hash][1]), - "width": str(w + 2*padding_pixels), - "height": str(h + 2*padding_pixels), - "frameX": str(frame.data.framex), - "frameY": str(frame.data.framey), - "frameWidth": str(frame.data.framew), - "frameHeight": str(frame.data.frameh), - } - root.append(subtexture_element) - prgs += 1 - progressupdatefn(prgs, f"Saving {frame.data.xml_pose_name} to XML...") + imghash_dict = { rect['id']: (rect['fit']['x'], rect['fit']['y']) for rect in frame_dict_arr } + for frame in frames: + subtexture_element = ET.Element("SubTexture") + subtexture_element.tail = linesep + w, h = imghashes.get(frame.data.img_hash).size + subtexture_element.attrib = { + "name" : frame.data.xml_pose_name, + "x": str(imghash_dict[frame.data.img_hash][0]), + "y": str(imghash_dict[frame.data.img_hash][1]), + "width": str(w + 2*padding_pixels), + "height": str(h + 2*padding_pixels), + "frameX": str(frame.data.framex), + "frameY": str(frame.data.framey), + "frameWidth": str(frame.data.framew), + "frameHeight": str(frame.data.frameh), + } + root.append(subtexture_element) + prgs += 1 + progressupdatefn(prgs, f"Saving {frame.data.xml_pose_name} to XML...") # im.close() print("Saving XML...") xmltree = ET.ElementTree(root) diff --git a/src/framedata.py b/src/framedata.py index 2f4f7c8..9b93e01 100644 --- a/src/framedata.py +++ b/src/framedata.py @@ -18,7 +18,7 @@ def __init__(self, impath, from_single_png, pose_name, **xmlinfo): self.th = None self.from_single_png = from_single_png - img = Image.open(impath) + img = Image.open(impath).convert('RGBA') self.framex = 0 self.framey = 0 self.framew = img.width @@ -134,46 +134,46 @@ def modify_image_to(self, im): self.img_height = im.height # not used as of now -class FrameXMLData: - def __init__(self, pose_name, x, y, w, h, framex, framey, framew, frameh, flipx=False, flipy=False): - self.pose_name = pose_name - self.x = x - self.y = y - self.w = w - self.h = h - self.framex = framex - self.framey = framey - self.framew = framew - self.frameh = frameh - self.is_flip_x = flipx - self.is_flip_y = flipy - self.xml_posename = None - # not exactly relevant to the xml but still - self.from_single_png = False +# class FrameXMLData: +# def __init__(self, pose_name, x, y, w, h, framex, framey, framew, frameh, flipx=False, flipy=False): +# self.pose_name = pose_name +# self.x = x +# self.y = y +# self.w = w +# self.h = h +# self.framex = framex +# self.framey = framey +# self.framew = framew +# self.frameh = frameh +# self.is_flip_x = flipx +# self.is_flip_y = flipy +# self.xml_posename = None +# # not exactly relevant to the xml but still +# self.from_single_png = False - def convert_to_dict(self): - attribs = { - "name": self.pose_name, - "x": self.x, - "y": self.y, - "width": self.w, - "height": self.h - } +# def convert_to_dict(self): +# attribs = { +# "name": self.pose_name, +# "x": self.x, +# "y": self.y, +# "width": self.w, +# "height": self.h +# } - if self.framex: - attribs.update({ - "frameX": self.framex, - "frameY": self.framey, - "frameWidth": self.framew, - "frameHeight": self.frameh, - }) +# if self.framex: +# attribs.update({ +# "frameX": self.framex, +# "frameY": self.framey, +# "frameWidth": self.framew, +# "frameHeight": self.frameh, +# }) - return attribs +# return attribs - def __str__(self): - return f"""Frame XML data: - FrameX: {repr(self.framex)} - FrameY: {repr(self.framey)} - FrameWidth: {repr(self.framew)} - FrameHeight: {repr(self.frameh)} - """ \ No newline at end of file +# def __str__(self): +# return f"""Frame XML data: +# FrameX: {repr(self.framex)} +# FrameY: {repr(self.framey)} +# FrameWidth: {repr(self.framew)} +# FrameHeight: {repr(self.frameh)} +# """ \ No newline at end of file diff --git a/src/settingswindow.py b/src/settingswindow.py index 74c0475..940f583 100644 --- a/src/settingswindow.py +++ b/src/settingswindow.py @@ -45,6 +45,7 @@ def restoreToNormal(self): self.ui.insist_prefix_checkbox.setCheckState(self.must_use_prefix) self.ui.frame_padding_spinbox.setValue(self.frame_padding) self.ui.packingalgo_combobox.setCurrentIndex(self.packing_algo) + self.ui.no_merge_checkbox.setCheckState(self.no_merge) self.close() def saveSettings(self, shouldclose=True): @@ -55,6 +56,7 @@ def saveSettings(self, shouldclose=True): self.must_use_prefix = self.ui.insist_prefix_checkbox.checkState() self.frame_padding = self.ui.frame_padding_spinbox.value() self.packing_algo = self.ui.packingalgo_combobox.currentIndex() + self.no_merge = self.ui.no_merge_checkbox.checkState() # saving to global settings obj g_settings['isclip'] = self.isclip g_settings['prefix_type'] = self.prefix_type @@ -62,6 +64,7 @@ def saveSettings(self, shouldclose=True): g_settings['must_use_prefix'] = self.must_use_prefix g_settings['frame_padding'] = self.frame_padding g_settings['packing_algo'] = self.packing_algo + g_settings['no_merge'] = self.no_merge if shouldclose: self.close() diff --git a/src/spritesheetgensettings.py b/src/spritesheetgensettings.py index 7024597..ce9faff 100644 --- a/src/spritesheetgensettings.py +++ b/src/spritesheetgensettings.py @@ -15,7 +15,7 @@ class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") Form.setWindowModality(QtCore.Qt.ApplicationModal) - Form.resize(611, 520) + Form.resize(603, 552) self.verticalLayout_3 = QtWidgets.QVBoxLayout(Form) self.verticalLayout_3.setObjectName("verticalLayout_3") self.clip_checkbox = QtWidgets.QCheckBox(Form) @@ -78,6 +78,9 @@ def setupUi(self, Form): self.insist_prefix_checkbox.setFont(font) self.insist_prefix_checkbox.setObjectName("insist_prefix_checkbox") self.verticalLayout_3.addWidget(self.insist_prefix_checkbox) + self.no_merge_checkbox = QtWidgets.QCheckBox(Form) + self.no_merge_checkbox.setObjectName("no_merge_checkbox") + self.verticalLayout_3.addWidget(self.no_merge_checkbox) self.frame_4 = QtWidgets.QFrame(Form) self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised) @@ -146,6 +149,10 @@ def retranslateUi(self, Form): self.label_2.setText(_translate("Form", "Custom Prefix:")) self.no_prefix_radiobtn.setText(_translate("Form", "Don\'t use any prefix (what you type in the prefix box is exactly what will show up in the XML)")) self.insist_prefix_checkbox.setText(_translate("Form", "Use Prefix even if frame is imported from existing XML")) + self.no_merge_checkbox.setText(_translate("Form", "Do not merge look-alike frames\n" +"(WARNING: Can cause extremely large spritesheets which may cause windows to\n" +"refuse to open them.\n" +"May also cause crashes!)")) self.frame_padding_spinbox.setSuffix(_translate("Form", "px")) self.frame_padding_label.setText(_translate("Form", "Frame Padding (use this to add empty pixels to the edge of each frame, helps prevent \n" " sprites clipping into each other)"))