1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-02-23 05:53:14 +00:00
Pixelorama/src/UI/Recorder/Recorder.gd
Emmanouil Papadeas 8de9697be0
Layer blend modes (#911)
* Preview blend modes

No support for exporting and layer merging yet. Also need to fix the move tool preview.

* Preview blend modes on tile mode

* Raise layer limit to 1024

* Export images with layer blending modes

* Save blend modes in pxo files

* Merge layers with blending modes

* Fix crash when adding a new layer

* Preview blending in the other canvases

* Update DrawingAlgos.gd

* Move tool preview

* Re-arrange blend menu and add lighten, darken, linear burn and exclusion

* Add divide blend mode

* Add hue, saturation, color & luminosity blend modes

* Undo/redo when changing blend modes
2023-10-22 01:57:45 +03:00

201 lines
5.6 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends PanelContainer
signal frame_saved
enum Mode { CANVAS, PIXELORAMA }
var mode := Mode.CANVAS
var chosen_dir := ""
var save_dir := ""
var project: Project
var cache: Array[Image] = [] ## Images stored during recording
var frame_captured := 0 ## Used to visualize frames captured
var skip_amount := 1 ## Number of "do" actions after which a frame can be captured
var current_frame_no := 0 ## Used to compare with skip_amount to see if it can be captured
var resize := 100
@onready var project_list := $"%TargetProjectOption" as OptionButton
@onready var folder_button := $"%Folder" as Button
@onready var start_button := $"%Start" as Button
@onready var size_label := $"%Size" as Label
@onready var path_field := $"%Path" as LineEdit
func _ready() -> void:
refresh_projects_list()
project = Global.current_project
frame_saved.connect(_on_frame_saved)
# Make a recordings folder if there isn't one
chosen_dir = Global.home_data_directory.path_join("Recordings")
DirAccess.make_dir_recursive_absolute(chosen_dir)
path_field.text = chosen_dir
size_label.text = str("(", project.size.x, "×", project.size.y, ")")
func initialize_recording() -> void:
connect_undo() # connect to detect changes in project
cache.clear() # clear the cache array to store new images
frame_captured = 0
current_frame_no = skip_amount - 1
# disable some options that are not required during recording
folder_button.visible = true
project_list.visible = false
$ScrollContainer/CenterContainer/GridContainer/Captured.visible = true
for child in $Dialogs/Options/PanelContainer/VBoxContainer.get_children():
if !child.is_in_group("visible during recording"):
child.visible = false
save_dir = chosen_dir
# Remove end back-slashes if present
if save_dir.ends_with("/"):
save_dir[-1] = ""
# Create a new directory based on time
var time_dict := Time.get_time_dict_from_system()
var folder := str(project.name, time_dict.hour, "_", time_dict.minute, "_", time_dict.second)
var dir := DirAccess.open(save_dir)
save_dir = save_dir.path_join(folder)
dir.make_dir_recursive(save_dir)
capture_frame() # capture first frame
$Timer.start()
func capture_frame() -> void:
current_frame_no += 1
if current_frame_no != skip_amount:
return
current_frame_no = 0
var image: Image
if mode == Mode.PIXELORAMA:
image = get_tree().root.get_viewport().get_texture().get_image()
else:
var frame := project.frames[project.current_frame]
image = Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
DrawingAlgos.blend_all_layers(image, frame, Vector2i.ZERO, project)
if mode == Mode.CANVAS:
if resize != 100:
image.resize(
image.get_size().x * resize / 100,
image.get_size().y * resize / 100,
Image.INTERPOLATE_NEAREST
)
cache.append(image)
func _on_Timer_timeout() -> void:
# Saves frames little by little during recording
if cache.size() > 0:
save_frame(cache[0])
cache.remove_at(0)
func save_frame(img: Image) -> void:
var save_file := str(project.name, "_", frame_captured, ".png")
img.save_png(save_dir.path_join(save_file))
frame_saved.emit()
func _on_frame_saved() -> void:
frame_captured += 1
$ScrollContainer/CenterContainer/GridContainer/Captured.text = str("Saved: ", frame_captured)
func finalize_recording() -> void:
$Timer.stop()
for img in cache:
save_frame(img)
cache.clear()
disconnect_undo()
folder_button.visible = false
project_list.visible = true
$ScrollContainer/CenterContainer/GridContainer/Captured.visible = false
for child in $Dialogs/Options/PanelContainer/VBoxContainer.get_children():
child.visible = true
if mode == Mode.PIXELORAMA:
size_label.get_parent().visible = false
func disconnect_undo() -> void:
project.undo_redo.version_changed.disconnect(capture_frame)
func connect_undo() -> void:
project.undo_redo.version_changed.connect(capture_frame)
func _on_TargetProjectOption_item_selected(index: int) -> void:
project = Global.projects[index]
func _on_TargetProjectOption_pressed() -> void:
refresh_projects_list()
func refresh_projects_list() -> void:
project_list.clear()
for proj in Global.projects:
project_list.add_item(proj.name)
func _on_Start_toggled(button_pressed: bool) -> void:
if button_pressed:
initialize_recording()
Global.change_button_texturerect(start_button.get_child(0), "stop.png")
else:
finalize_recording()
Global.change_button_texturerect(start_button.get_child(0), "start.png")
func _on_Settings_pressed() -> void:
var settings := $Dialogs/Options as Window
var pos := position
settings.popup(Rect2(pos, settings.size))
func _on_SkipAmount_value_changed(value: float) -> void:
skip_amount = value
func _on_Mode_toggled(button_pressed: bool) -> void:
if button_pressed:
mode = Mode.PIXELORAMA
size_label.get_parent().visible = false
else:
mode = Mode.CANVAS
size_label.get_parent().visible = true
func _on_SpinBox_value_changed(value: float) -> void:
resize = value
var new_size: Vector2 = project.size * (resize / 100.0)
size_label.text = str("(", new_size.x, "×", new_size.y, ")")
func _on_Choose_pressed() -> void:
$Dialogs/Path.popup_centered()
$Dialogs/Path.current_dir = chosen_dir
func _on_Open_pressed() -> void:
OS.shell_open(path_field.text)
func _on_Path_dir_selected(dir: String) -> void:
chosen_dir = dir
path_field.text = chosen_dir
start_button.disabled = false
func _on_Fps_value_changed(value: float) -> void:
var dur_label := $Dialogs/Options/PanelContainer/VBoxContainer/Fps/Duration as Label
var duration := snappedf(1.0 / value, 0.0001)
dur_label.text = str("= ", duration, " sec")
func _on_options_close_requested() -> void:
$Dialogs/Options.hide()