120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
# "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 Exception:
|
|
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 {}".format(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 {} {} after {:.3f} seconds".format(
|
|
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 = ["{}/{}".format(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
|