44
55from krita import Krita , Document , Node , InfoObject
66from builtins import Application
7- from PyQt5 .QtCore import QRect
7+ from PyQt5 .QtCore import QRect , QByteArray
88
99from collections .abc import Iterable
1010from typing import Optional
1919
2020class SpritesheetExporter :
2121 export_path = Path .home ().joinpath ("spritesheet.png" )
22+ unique_frames = True
2223
2324 horizontal = True
2425 size = DEFAULT_SPACE
@@ -146,49 +147,58 @@ def _make_frames_dir(self):
146147
147148 return dir
148149
149- def _copy_frames (self , src : Document , dest : Document ) -> int :
150+ def _copy_frames (self , src : Document , dest : Document ):
150151 """
151152 Copies frames from the source document to the destination.
152153
153154 @param src The source document
154155 @param dest The destination document
155- @returns The number of frames copied
156156 """
157157
158158 root = dest .rootNode ()
159159 width = src .width ()
160160 height = src .height ()
161161
162+ pixel_set : Optional [set [QByteArray ]] = set () if self .unique_frames else None
163+
162164 if self .layers_as_animation :
163165 paint_layers = src .rootNode ().findChildNodes ("" , True , False , "paintlayer" )
164166 visible_layers = [i for i in paint_layers if i .visible ()]
165167
166168 # export each visible layer
167169 for i , layer in enumerate (visible_layers ):
170+ if pixel_set is not None :
171+ pixel_data = layer .pixelData (0 , 0 , width , height )
172+ if pixel_data in pixel_set :
173+ continue # Got a non-unique frame
174+ pixel_set .add (pixel_data )
175+
168176 clone_layer = dest .createCloneLayer (str (i ), layer )
169177 root .addChildNode (clone_layer , None )
178+ else :
179+ if self .end == DEFAULT_TIME or self .start == DEFAULT_TIME :
180+ self ._set_frame_times (src )
170181
171- return len (visible_layers )
172-
173- if self .end == DEFAULT_TIME or self .start == DEFAULT_TIME :
174- self ._set_frame_times (src )
182+ initial_time = src .currentTime ()
183+ frame_range = range (self .start , self .end + 1 , self .step )
175184
176- initial_time = src . currentTime ()
177- frame_range = range ( self . start , self . end + 1 , self . step )
185+ for i in frame_range :
186+ src . setCurrentTime ( i )
178187
179- for i in frame_range :
180- src .setCurrentTime (i )
181- layer = dest .createNode (str (i ), "paintlayer" )
182- root .addChildNode (layer , None )
188+ # Ensure the time has been set before copying the pixel data
189+ src .waitForDone ()
190+ pixel_data = src .pixelData (0 , 0 , width , height )
183191
184- # Ensure the time has been set before copying the pixel data
185- src . waitForDone ()
186- pixel_data = src . pixelData ( 0 , 0 , width , height )
187- layer . setPixelData ( pixel_data , 0 , 0 , width , height )
192+ if pixel_set is not None :
193+ if pixel_data in pixel_set :
194+ continue # Got a non-unique frame
195+ pixel_set . add ( pixel_data )
188196
189- src .setCurrentTime (initial_time ) # reset time
197+ layer = dest .createNode (str (i ), "paintlayer" )
198+ layer .setPixelData (pixel_data , 0 , 0 , width , height )
199+ root .addChildNode (layer , None )
190200
191- return len ( frame_range )
201+ src . setCurrentTime ( initial_time ) # reset time
192202
193203 def _process_frames (self , src : Document , dest : Document ):
194204 width = src .width ()
@@ -197,12 +207,10 @@ def _process_frames(self, src: Document, dest: Document):
197207 frames_dir = self ._make_frames_dir () if self .export_frame_sequence else None
198208 texture_atlas = {"frames" : []} if self .write_texture_atlas else None
199209
200- for layer in dest .rootNode ().childNodes ():
201- name = layer .name ()
202-
210+ for i , layer in enumerate (dest .rootNode ().childNodes ()):
203211 if frames_dir is not None :
204212 file_name = "" .join (
205- [self .base_name , name .zfill (3 ), self .export_path .suffix ]
213+ [self .base_name , str ( i ) .zfill (3 ), self .export_path .suffix ]
206214 )
207215 layer .save (
208216 str (frames_dir .joinpath (file_name )),
@@ -212,17 +220,13 @@ def _process_frames(self, src: Document, dest: Document):
212220 QRect (0 , 0 , width , height ),
213221 )
214222
215- if self .layers_as_animation :
216- index = int (name )
217- else :
218- index = (int (name ) - self .start ) // self .step
219-
223+ # Layers are ordered by when they were added, so using `i` is fine
220224 if self .horizontal :
221- x_pos = (index % self .size ) * width
222- y_pos = (index // self .size ) * height
225+ x_pos = (i % self .size ) * width
226+ y_pos = (i // self .size ) * height
223227 else :
224- x_pos = (index // self .size ) * width
225- y_pos = (index % self .size ) * height
228+ x_pos = (i // self .size ) * width
229+ y_pos = (i % self .size ) * height
226230
227231 layer .move (x_pos , y_pos )
228232
@@ -275,7 +279,10 @@ def export(self, debug=False):
275279 )
276280
277281 sheet .setFileName (str (self .export_path ))
278- num_frames = self ._copy_frames (doc , sheet )
282+ sheet .rootNode ().setChildNodes ([]) # Remove any default layers
283+
284+ self ._copy_frames (doc , sheet )
285+ num_frames = len (sheet .rootNode ().childNodes ())
279286
280287 if self .size == DEFAULT_SPACE :
281288 # Pack the sprites as densely as possible with a square fit
@@ -301,9 +308,6 @@ def export(self, debug=False):
301308 + f"new doc width: { sheet .width ()} "
302309 )
303310
304- # Remove the default Background layer
305- sheet .rootNode ().childNodes ()[0 ].remove ()
306-
307311 # Position frames, and optionally write JSON or export all frames
308312 self ._process_frames (doc , sheet )
309313
0 commit comments