189 lines
7.1 KiB
Python
189 lines
7.1 KiB
Python
import os
|
|
|
|
from art_export import ArtExporter
|
|
from image_export import export_still_image
|
|
from renderable import LAYER_VIS_FULL, LAYER_VIS_NONE
|
|
from ui_art_dialog import ExportOptionsDialog
|
|
from ui_dialog import Field, UIDialog
|
|
|
|
FILE_EXTENSION = "png"
|
|
|
|
DEFAULT_SCALE = 1
|
|
DEFAULT_CRT = False
|
|
|
|
|
|
def get_full_filename(
|
|
in_filename, frame, layer_name, use_frame, use_layer, forbidden_chars
|
|
):
|
|
"Returns properly mutated filename for given frame/layer data"
|
|
# strip out path and extension from filename as we mutate it
|
|
dirname = os.path.dirname(in_filename)
|
|
base_filename = os.path.basename(in_filename)
|
|
base_filename = os.path.splitext(base_filename)[0]
|
|
fn = base_filename
|
|
if use_frame:
|
|
fn += "_{}".format(str(frame).rjust(4, "0"))
|
|
if use_layer:
|
|
fn += "_{}".format(layer_name)
|
|
# strip unfriendly chars from output filename
|
|
for forbidden_char in ["\\", "/", "*", ":"]:
|
|
fn = fn.replace(forbidden_char, "")
|
|
# add path and extension for final mutated filename
|
|
return "{}/{}.{}".format(dirname, fn, FILE_EXTENSION)
|
|
|
|
|
|
class PNGSetExportOptionsDialog(ExportOptionsDialog):
|
|
title = "PNG set export options"
|
|
tile_width = 60 # extra width for filename preview
|
|
field0_label = "Scale factor (%s pixels)"
|
|
field1_label = "CRT filter"
|
|
field2_label = "Export frames"
|
|
field3_label = "Export layers"
|
|
field4_label = "First filename (in set of %s):"
|
|
field5_label = " %s"
|
|
fields = [
|
|
Field(label=field0_label, type=int, width=6, oneline=False),
|
|
Field(label=field1_label, type=bool, width=0, oneline=True),
|
|
Field(label=field2_label, type=bool, width=0, oneline=True),
|
|
Field(label=field3_label, type=bool, width=0, oneline=True),
|
|
Field(label=field4_label, type=None, width=0, oneline=True),
|
|
Field(label=field5_label, type=None, width=0, oneline=True),
|
|
]
|
|
# redraw dynamic labels
|
|
always_redraw_labels = True
|
|
invalid_scale_error = "Scale must be greater than 0"
|
|
|
|
def get_initial_field_text(self, field_number):
|
|
art = self.ui.active_art
|
|
if field_number == 0:
|
|
return str(DEFAULT_SCALE)
|
|
elif field_number == 1:
|
|
return [" ", UIDialog.true_field_text][DEFAULT_CRT]
|
|
elif field_number == 2:
|
|
# default false if only one frame
|
|
return [" ", UIDialog.true_field_text][art.frames > 1]
|
|
elif field_number == 3:
|
|
# default false if only one layer
|
|
return [" ", UIDialog.true_field_text][art.layers > 1]
|
|
|
|
def get_field_label(self, field_index):
|
|
label = self.fields[field_index].label
|
|
if field_index == 0:
|
|
valid, _ = self.is_input_valid()
|
|
if not valid:
|
|
label %= "???"
|
|
else:
|
|
# calculate exported image size
|
|
art = self.ui.active_art
|
|
scale = int(self.field_texts[0])
|
|
width = art.charset.char_width * art.width * scale
|
|
height = art.charset.char_height * art.height * scale
|
|
label %= "{} x {}".format(width, height)
|
|
# show how many images exported set will be
|
|
elif field_index == 4:
|
|
export_frames = bool(self.field_texts[2].strip())
|
|
export_layers = bool(self.field_texts[3].strip())
|
|
art = self.ui.active_art
|
|
if export_frames and export_layers:
|
|
label %= str(art.frames * art.layers)
|
|
elif export_frames:
|
|
label %= str(art.frames)
|
|
elif export_layers:
|
|
label %= str(art.layers)
|
|
else:
|
|
label %= "1"
|
|
# preview frame + layer filename mutations based on current settings
|
|
elif field_index == 5:
|
|
export_frames = bool(self.field_texts[2].strip())
|
|
export_layers = bool(self.field_texts[3].strip())
|
|
art = self.ui.active_art
|
|
fn = get_full_filename(
|
|
self.filename,
|
|
0,
|
|
art.layer_names[0],
|
|
export_frames,
|
|
export_layers,
|
|
self.ui.app.forbidden_filename_chars,
|
|
)
|
|
fn = os.path.basename(fn)
|
|
label %= fn
|
|
return label
|
|
|
|
def is_input_valid(self):
|
|
# scale factor: >0 int
|
|
try:
|
|
int(self.field_texts[0])
|
|
except Exception:
|
|
return False, self.invalid_scale_error
|
|
if int(self.field_texts[0]) <= 0:
|
|
return False, self.invalid_scale_error
|
|
return True, None
|
|
|
|
def confirm_pressed(self):
|
|
valid, reason = self.is_input_valid()
|
|
if not valid:
|
|
return
|
|
self.dismiss()
|
|
# compile options for importer
|
|
options = {
|
|
"scale": int(self.field_texts[0]),
|
|
"crt": bool(self.field_texts[1].strip()),
|
|
"frames": bool(self.field_texts[2].strip()),
|
|
"layers": bool(self.field_texts[3].strip()),
|
|
}
|
|
ExportOptionsDialog.do_export(self.ui.app, self.filename, options)
|
|
|
|
|
|
class PNGSetExporter(ArtExporter):
|
|
format_name = "PNG image set"
|
|
format_description = """
|
|
PNG image set for each frame and/or layer.
|
|
"""
|
|
file_extension = FILE_EXTENSION
|
|
options_dialog_class = PNGSetExportOptionsDialog
|
|
|
|
def run_export(self, out_filename, options):
|
|
export_frames = options["frames"]
|
|
export_layers = options["layers"]
|
|
art = self.app.ui.active_art
|
|
# remember user's active frame/layer/viz settings so we
|
|
# can set em back when done
|
|
start_frame = art.active_frame
|
|
start_layer = art.active_layer
|
|
start_onion = self.app.onion_frames_visible
|
|
start_layer_viz = self.app.inactive_layer_visibility
|
|
self.app.onion_frames_visible = False
|
|
# if multi-player, only show active layer
|
|
self.app.inactive_layer_visibility = (
|
|
LAYER_VIS_NONE if export_layers else LAYER_VIS_FULL
|
|
)
|
|
success = True
|
|
for frame in range(art.frames):
|
|
# if exporting layers but not frames, only export active frame
|
|
if not export_frames and frame != art.active_frame:
|
|
continue
|
|
art.set_active_frame(frame)
|
|
for layer in range(art.layers):
|
|
art.set_active_layer(layer)
|
|
full_filename = get_full_filename(
|
|
out_filename,
|
|
frame,
|
|
art.layer_names[layer],
|
|
export_frames,
|
|
export_layers,
|
|
self.app.forbidden_filename_chars,
|
|
)
|
|
if not export_still_image(
|
|
self.app,
|
|
art,
|
|
full_filename,
|
|
crt=options.get("crt", DEFAULT_CRT),
|
|
scale=options.get("scale", DEFAULT_SCALE),
|
|
):
|
|
success = False
|
|
# put everything back how user left it
|
|
art.set_active_frame(start_frame)
|
|
art.set_active_layer(start_layer)
|
|
self.app.onion_frames_visible = start_onion
|
|
self.app.inactive_layer_visibility = start_layer_viz
|
|
return success
|