# "convert folder of images to animation" # heavy lifting still done by ImageConverter, this mainly coordinates # conversion of multiple frames import os import time import formats.in_bitmap as bm import image_convert class ImageSequenceConverter: def __init__(self, app, image_filenames, art, bicubic_scale): self.init_success = False self.app = app self.start_time = time.time() self.image_filenames = image_filenames # App.update_window_title uses image_filename for titlebar self.image_filename = "" # common name of sequence self.image_name = os.path.splitext(self.image_filename)[0] self.art = art self.bicubic_scale = bicubic_scale # queue up first frame self.next_image(first=True) self.init_success = True def next_image(self, first=False): # pop last image off stack if not first: self.image_filenames.pop(0) # done? if len(self.image_filenames) == 0: self.finish() return # next frame self.art.set_active_frame(self.art.active_frame + 1) try: self.current_frame_converter = image_convert.ImageConverter( self.app, self.image_filenames[0], self.art, self.bicubic_scale, self ) except: self.fail() return if not self.current_frame_converter.init_success: self.fail() return self.image_filename = self.image_filenames[0] self.preview_sprite = self.current_frame_converter.preview_sprite self.app.update_window_title() def fail(self): self.app.log("Bad frame %s" % self.image_filenames[0], error=True) self.finish(True) def update(self): # create converter for new frame if current one is done, # else update current one if self.current_frame_converter.finished: self.next_image() else: self.current_frame_converter.update() def finish(self, cancelled=False): time_taken = time.time() - self.start_time (verb, error) = ("cancelled", True) if cancelled else ("finished", False) self.app.log( "Conversion of image sequence %s %s after %.3f seconds" % (self.image_name, verb, time_taken), error, ) self.app.converter = None self.app.update_window_title() class ConvertImageSequenceChooserDialog(bm.ConvertImageChooserDialog): title = "Convert folder" confirm_caption = "Choose First Image" class BitmapImageSequenceImporter(bm.BitmapImageImporter): format_name = "Bitmap image folder" format_description = """ Converts a folder of Bitmap images (PNG, JPEG, or BMP) into an animation. Dimensions will be based on first image chosen. """ file_chooser_dialog_class = ConvertImageSequenceChooserDialog # options_dialog_class = bm.ConvertImageOptionsDialog def run_import(self, in_filename, options={}): palette = self.app.load_palette(options["palette"]) self.art.set_palette(palette) width, height = options["art_width"], options["art_height"] self.art.resize(width, height) # Importer.init will adjust UI bicubic_scale = options["bicubic_scale"] # get dir listing with full pathname in_dir = os.path.dirname(in_filename) in_files = ["%s/%s" % (in_dir, f) for f in os.listdir(in_dir)] in_files.sort() # assume numeric sequence starts from chosen file in_files = in_files[in_files.index(in_filename) :] # remove files from end of list if they don't end in a number while ( not os.path.splitext(in_files[-1])[0][-1].isdecimal() and len(in_files) > 0 ): in_files.pop() # add frames to art as needed while self.art.frames < len(in_files): self.art.add_frame_to_end(log=False) self.art.set_active_frame(0) # create converter isc = ImageSequenceConverter(self.app, in_files, self.art, bicubic_scale) # bail on early failure if not isc.init_success: return False self.app.converter = isc self.app.update_window_title() return True