mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-22 21:43:13 +00:00
Performance boost
This commit is contained in:
parent
4a7f7cbde5
commit
3bd964c5b6
4 changed files with 69 additions and 31 deletions
|
@ -25,39 +25,39 @@ func blend_layers(
|
||||||
only_selected_cels := false,
|
only_selected_cels := false,
|
||||||
only_selected_layers := false,
|
only_selected_layers := false,
|
||||||
) -> void:
|
) -> void:
|
||||||
var frame_index := project.frames.find(frame)
|
var frame_index: int = project.frames.find(frame)
|
||||||
var previous_ordered_layers: Array[int] = project.ordered_layers
|
var previous_ordered_layers: Array[int] = project.ordered_layers
|
||||||
project.order_layers(frame_index)
|
project.order_layers(frame_index)
|
||||||
var textures: Array[Image] = []
|
var textures: Array[Image] = []
|
||||||
# Nx4 texture, where N is the number of layers and the first row are the blend modes,
|
# Nx4 texture, where N is the number of layers and the first row are the blend modes,
|
||||||
# the second are the opacities, the third are the origins and the fourth are the
|
# the second are the opacities, the third are the origins and the fourth are the
|
||||||
# clipping mask booleans.
|
# clipping mask booleans.
|
||||||
var metadata_image := Image.create(project.layers.size(), 4, false, Image.FORMAT_R8)
|
var metadata_image: Image = Image.create(project.layers.size(), 4, false, Image.FORMAT_R8)
|
||||||
for i in project.layers.size():
|
for i in project.layers.size():
|
||||||
var ordered_index := project.ordered_layers[i]
|
var ordered_index: int = project.ordered_layers[i]
|
||||||
var layer := project.layers[ordered_index]
|
var layer: BaseLayer = project.layers[ordered_index]
|
||||||
var include := true if layer.is_visible_in_hierarchy() else false
|
var include: bool = true if layer.is_visible_in_hierarchy() else false
|
||||||
if only_selected_cels and include:
|
if only_selected_cels and include:
|
||||||
var test_array := [frame_index, i]
|
var test_array := [frame_index, i]
|
||||||
if not test_array in project.selected_cels:
|
if not test_array in project.selected_cels:
|
||||||
include = false
|
include = false
|
||||||
if only_selected_layers and include:
|
if only_selected_layers and include:
|
||||||
var layer_is_selected := false
|
var layer_is_selected: bool = false
|
||||||
for selected_cel in project.selected_cels:
|
for selected_cel in project.selected_cels:
|
||||||
if i == selected_cel[1]:
|
if i == selected_cel[1]:
|
||||||
layer_is_selected = true
|
layer_is_selected = true
|
||||||
break
|
break
|
||||||
if not layer_is_selected:
|
if not layer_is_selected:
|
||||||
include = false
|
include = false
|
||||||
var cel := frame.cels[ordered_index]
|
var cel: BaseCel = frame.cels[ordered_index]
|
||||||
if DisplayServer.get_name() == "headless":
|
if DisplayServer.get_name() == "headless":
|
||||||
blend_layers_headless(image, project, layer, cel, origin)
|
blend_layers_headless(image, project, layer, cel, origin)
|
||||||
else:
|
else:
|
||||||
if layer is GroupLayer and layer.blend_mode != BaseLayer.BlendModes.PASS_THROUGH:
|
if layer is GroupLayer and layer.blend_mode != BaseLayer.BlendModes.PASS_THROUGH:
|
||||||
var cel_image := (layer as GroupLayer).blend_children(frame)
|
var cel_image: Image = (layer as GroupLayer).blend_children(frame)
|
||||||
textures.append(cel_image)
|
textures.append(cel_image)
|
||||||
else:
|
else:
|
||||||
var cel_image := layer.display_effects(cel)
|
var cel_image: Image = layer.display_effects(cel)
|
||||||
textures.append(cel_image)
|
textures.append(cel_image)
|
||||||
if (
|
if (
|
||||||
layer.is_blended_by_ancestor()
|
layer.is_blended_by_ancestor()
|
||||||
|
@ -67,13 +67,13 @@ func blend_layers(
|
||||||
include = false
|
include = false
|
||||||
set_layer_metadata_image(layer, cel, metadata_image, ordered_index, include)
|
set_layer_metadata_image(layer, cel, metadata_image, ordered_index, include)
|
||||||
if DisplayServer.get_name() != "headless":
|
if DisplayServer.get_name() != "headless":
|
||||||
var texture_array := Texture2DArray.new()
|
var texture_array: Texture2DArray = Texture2DArray.new()
|
||||||
texture_array.create_from_images(textures)
|
texture_array.create_from_images(textures)
|
||||||
var params := {
|
var params := {
|
||||||
"layers": texture_array,
|
"layers": texture_array,
|
||||||
"metadata": ImageTexture.create_from_image(metadata_image),
|
"metadata": ImageTexture.create_from_image(metadata_image),
|
||||||
}
|
}
|
||||||
var blended := Image.create(project.size.x, project.size.y, false, image.get_format())
|
var blended: Image = Image.create(project.size.x, project.size.y, false, image.get_format())
|
||||||
var gen := ShaderImageEffect.new()
|
var gen := ShaderImageEffect.new()
|
||||||
gen.generate_image(blended, blend_layers_shader, params, project.size)
|
gen.generate_image(blended, blend_layers_shader, params, project.size)
|
||||||
image.blend_rect(blended, Rect2i(Vector2i.ZERO, project.size), origin)
|
image.blend_rect(blended, Rect2i(Vector2i.ZERO, project.size), origin)
|
||||||
|
|
|
@ -149,7 +149,14 @@ func process_data(project := Global.current_project) -> void:
|
||||||
process_spritesheet(project)
|
process_spritesheet(project)
|
||||||
|
|
||||||
|
|
||||||
func process_spritesheet(project := Global.current_project) -> void:
|
func process_spritesheet(project := Global.current_project, info_cache: Dictionary = {}) -> void:
|
||||||
|
## info_cache stores the position of frame in the spritesheet and recycles the last created
|
||||||
|
## spritesheet to construct a new one (only if info_cache is provided).
|
||||||
|
## This causes significant performance boost for higher row or column values
|
||||||
|
## (no significant increase in lower values).
|
||||||
|
var old_spritesheet: Image
|
||||||
|
if not info_cache.is_empty():
|
||||||
|
old_spritesheet = processed_images[0].image
|
||||||
processed_images.clear()
|
processed_images.clear()
|
||||||
# Range of frames determined by tags
|
# Range of frames determined by tags
|
||||||
var frames := _calculate_frames(project)
|
var frames := _calculate_frames(project)
|
||||||
|
@ -192,6 +199,11 @@ func process_spritesheet(project := Global.current_project) -> void:
|
||||||
var width := project.size.x * spritesheet_columns
|
var width := project.size.x * spritesheet_columns
|
||||||
var height := project.size.y * spritesheet_rows
|
var height := project.size.y * spritesheet_rows
|
||||||
var whole_image := Image.create(width, height, false, Image.FORMAT_RGBA8)
|
var whole_image := Image.create(width, height, false, Image.FORMAT_RGBA8)
|
||||||
|
if not info_cache.is_empty() and old_spritesheet:
|
||||||
|
whole_image.blend_rect(
|
||||||
|
old_spritesheet, Rect2i(Vector2i.ZERO, old_spritesheet.get_size()), Vector2i.ZERO
|
||||||
|
)
|
||||||
|
var old_origins = info_cache.values()
|
||||||
var origin := Vector2i.ZERO
|
var origin := Vector2i.ZERO
|
||||||
var hh := 0
|
var hh := 0
|
||||||
var vv := 0
|
var vv := 0
|
||||||
|
@ -252,8 +264,17 @@ func process_spritesheet(project := Global.current_project) -> void:
|
||||||
origin.y = project.size.y * tag_origins[0]
|
origin.y = project.size.y * tag_origins[0]
|
||||||
origin.x = 0
|
origin.x = 0
|
||||||
tag_origins[0] += 1
|
tag_origins[0] += 1
|
||||||
|
old_origins.erase(origin)
|
||||||
|
if frame in info_cache:
|
||||||
|
if info_cache[frame] == origin:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
whole_image.fill_rect(Rect2i(origin, project.size), Color.TRANSPARENT)
|
||||||
|
info_cache[frame] = origin
|
||||||
_blend_layers(whole_image, frame, origin)
|
_blend_layers(whole_image, frame, origin)
|
||||||
|
## remove redundant origins from image
|
||||||
|
for useless in old_origins:
|
||||||
|
whole_image.fill_rect(Rect2i(useless, project.size), Color.TRANSPARENT)
|
||||||
processed_images.append(ProcessedImage.new(whole_image, 0))
|
processed_images.append(ProcessedImage.new(whole_image, 0))
|
||||||
|
|
||||||
|
|
||||||
|
@ -671,7 +692,7 @@ func _blend_layers(
|
||||||
# Attempt to read the image data directly from the pxo file, without having to blend
|
# Attempt to read the image data directly from the pxo file, without having to blend
|
||||||
# This is mostly useful for when running Pixelorama in headless mode
|
# This is mostly useful for when running Pixelorama in headless mode
|
||||||
# To handle exporting from a CLI
|
# To handle exporting from a CLI
|
||||||
var zip_reader := ZIPReader.new()
|
var zip_reader: ZIPReader = ZIPReader.new()
|
||||||
var err := zip_reader.open(project.save_path)
|
var err := zip_reader.open(project.save_path)
|
||||||
if err == OK:
|
if err == OK:
|
||||||
var frame_index := project.frames.find(frame) + 1
|
var frame_index := project.frames.find(frame) + 1
|
||||||
|
@ -679,8 +700,8 @@ func _blend_layers(
|
||||||
if zip_reader.file_exists(image_path):
|
if zip_reader.file_exists(image_path):
|
||||||
# "Include blended" must be toggled on when saving the pxo file
|
# "Include blended" must be toggled on when saving the pxo file
|
||||||
# in order for this to work.
|
# in order for this to work.
|
||||||
var image_data := zip_reader.read_file(image_path)
|
var image_data: PackedByteArray = zip_reader.read_file(image_path)
|
||||||
var loaded_image := Image.create_from_data(
|
var loaded_image: Image = Image.create_from_data(
|
||||||
project.size.x,
|
project.size.x,
|
||||||
project.size.y,
|
project.size.y,
|
||||||
image.has_mipmaps(),
|
image.has_mipmaps(),
|
||||||
|
@ -698,8 +719,8 @@ func _blend_layers(
|
||||||
elif export_layers == SELECTED_LAYERS:
|
elif export_layers == SELECTED_LAYERS:
|
||||||
DrawingAlgos.blend_layers(image, frame, origin, project, false, true)
|
DrawingAlgos.blend_layers(image, frame, origin, project, false, true)
|
||||||
else:
|
else:
|
||||||
var layer := project.layers[export_layers - 2]
|
var layer: BaseLayer = project.layers[export_layers - 2]
|
||||||
var layer_image := Image.new()
|
var layer_image: Image = Image.new()
|
||||||
if layer is GroupLayer:
|
if layer is GroupLayer:
|
||||||
layer_image.copy_from(layer.blend_children(frame, Vector2i.ZERO))
|
layer_image.copy_from(layer.blend_children(frame, Vector2i.ZERO))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -6,7 +6,7 @@ signal about_to_preview(dict: Dictionary)
|
||||||
|
|
||||||
var preview_current_frame := 0
|
var preview_current_frame := 0
|
||||||
var preview_frames: Array[Texture2D] = []
|
var preview_frames: Array[Texture2D] = []
|
||||||
|
var spritesheet_info_cache = {}
|
||||||
# Allow custom exporters to be added
|
# Allow custom exporters to be added
|
||||||
var image_exports: Array[Export.FileFormat] = [
|
var image_exports: Array[Export.FileFormat] = [
|
||||||
Export.FileFormat.PNG,
|
Export.FileFormat.PNG,
|
||||||
|
@ -31,7 +31,7 @@ var _preview_images: Array[Export.ProcessedImage]
|
||||||
@onready var previews: GridContainer = $"%Previews"
|
@onready var previews: GridContainer = $"%Previews"
|
||||||
|
|
||||||
@onready var spritesheet_orientation: OptionButton = $"%Orientation"
|
@onready var spritesheet_orientation: OptionButton = $"%Orientation"
|
||||||
@onready var spritesheet_lines_count: SpinBox = $"%LinesCount"
|
@onready var spritesheet_lines_count: ValueSlider = $"%LinesCount"
|
||||||
@onready var spritesheet_lines_count_label: Label = $"%LinesCountLabel"
|
@onready var spritesheet_lines_count_label: Label = $"%LinesCountLabel"
|
||||||
|
|
||||||
@onready var frames_option_button: OptionButton = $"%Frames"
|
@onready var frames_option_button: OptionButton = $"%Frames"
|
||||||
|
@ -51,6 +51,7 @@ var _preview_images: Array[Export.ProcessedImage]
|
||||||
@onready var export_progress_popup: Window = $ExportProgressBar
|
@onready var export_progress_popup: Window = $ExportProgressBar
|
||||||
@onready var export_progress_bar := %ProgressBar as ProgressBar
|
@onready var export_progress_bar := %ProgressBar as ProgressBar
|
||||||
@onready var frame_timer: Timer = $FrameTimer
|
@onready var frame_timer: Timer = $FrameTimer
|
||||||
|
@onready var spritesheet_update_timer: Timer = %SpritesheetUpdateTimer
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
@ -84,7 +85,8 @@ func show_tab() -> void:
|
||||||
)
|
)
|
||||||
Export.ExportTab.SPRITESHEET:
|
Export.ExportTab.SPRITESHEET:
|
||||||
frame_timer.stop()
|
frame_timer.stop()
|
||||||
Export.process_spritesheet()
|
spritesheet_info_cache.clear()
|
||||||
|
Export.process_spritesheet(Global.current_project, spritesheet_info_cache)
|
||||||
spritesheet_orientation.selected = Export.orientation
|
spritesheet_orientation.selected = Export.orientation
|
||||||
spritesheet_lines_count.max_value = Export.number_of_frames
|
spritesheet_lines_count.max_value = Export.number_of_frames
|
||||||
spritesheet_lines_count.value = Export.lines_count
|
spritesheet_lines_count.value = Export.lines_count
|
||||||
|
@ -121,6 +123,7 @@ func add_image_preview(image: Image, canvas_number: int = -1) -> void:
|
||||||
var container := create_preview_container()
|
var container := create_preview_container()
|
||||||
var preview := create_preview_rect()
|
var preview := create_preview_rect()
|
||||||
preview.texture = ImageTexture.create_from_image(image)
|
preview.texture = ImageTexture.create_from_image(image)
|
||||||
|
preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
container.add_child(preview)
|
container.add_child(preview)
|
||||||
|
|
||||||
if canvas_number != -1:
|
if canvas_number != -1:
|
||||||
|
@ -145,6 +148,7 @@ func add_animated_preview() -> void:
|
||||||
var preview := create_preview_rect()
|
var preview := create_preview_rect()
|
||||||
preview.name = "Preview"
|
preview.name = "Preview"
|
||||||
preview.texture = preview_frames[preview_current_frame]
|
preview.texture = preview_frames[preview_current_frame]
|
||||||
|
preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
container.add_child(preview)
|
container.add_child(preview)
|
||||||
|
|
||||||
previews.add_child(container)
|
previews.add_child(container)
|
||||||
|
@ -305,9 +309,8 @@ func _on_Orientation_item_selected(id: Export.Orientation) -> void:
|
||||||
Export.orientation = id
|
Export.orientation = id
|
||||||
_handle_orientation_ui()
|
_handle_orientation_ui()
|
||||||
spritesheet_lines_count.value = Export.frames_divided_by_spritesheet_lines()
|
spritesheet_lines_count.value = Export.frames_divided_by_spritesheet_lines()
|
||||||
Export.process_spritesheet()
|
## Due to the above line, we don't have to process the spritesheet again
|
||||||
update_dimensions_label()
|
## the value_changed signal will do this for us.
|
||||||
set_preview()
|
|
||||||
|
|
||||||
|
|
||||||
func _handle_orientation_ui() -> void:
|
func _handle_orientation_ui() -> void:
|
||||||
|
@ -326,9 +329,8 @@ func _handle_orientation_ui() -> void:
|
||||||
|
|
||||||
func _on_LinesCount_value_changed(value: float) -> void:
|
func _on_LinesCount_value_changed(value: float) -> void:
|
||||||
Export.lines_count = value
|
Export.lines_count = value
|
||||||
Export.process_spritesheet()
|
## Check if spritesheet needs to be updated (This is required when orientation gets changed)
|
||||||
update_dimensions_label()
|
spritesheet_update_timer.start()
|
||||||
set_preview()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_Direction_item_selected(id: Export.AnimationDirection) -> void:
|
func _on_Direction_item_selected(id: Export.AnimationDirection) -> void:
|
||||||
|
@ -478,3 +480,9 @@ func _on_Layers_item_selected(id: int) -> void:
|
||||||
|
|
||||||
func _on_SeparatorCharacter_text_changed(new_text: String) -> void:
|
func _on_SeparatorCharacter_text_changed(new_text: String) -> void:
|
||||||
Export.separator_character = new_text
|
Export.separator_character = new_text
|
||||||
|
|
||||||
|
|
||||||
|
func _on_spritesheet_update_timer_timeout() -> void:
|
||||||
|
Export.process_spritesheet(Global.current_project, spritesheet_info_cache)
|
||||||
|
update_dimensions_label()
|
||||||
|
set_preview()
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
[gd_scene load_steps=5 format=3 uid="uid://clgu8wb5o6oup"]
|
[gd_scene load_steps=6 format=3 uid="uid://clgu8wb5o6oup"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://src/UI/Dialogs/ExportDialog.gd" id="1"]
|
[ext_resource type="Script" path="res://src/UI/Dialogs/ExportDialog.gd" id="1"]
|
||||||
[ext_resource type="PackedScene" uid="uid://3pmb60gpst7b" path="res://src/UI/Nodes/TransparentChecker.tscn" id="2"]
|
[ext_resource type="PackedScene" uid="uid://3pmb60gpst7b" path="res://src/UI/Nodes/TransparentChecker.tscn" id="2"]
|
||||||
[ext_resource type="Script" path="res://src/UI/Nodes/CollapsibleContainer.gd" id="3"]
|
[ext_resource type="Script" path="res://src/UI/Nodes/CollapsibleContainer.gd" id="3"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://yjhp0ssng2mp" path="res://src/UI/Nodes/ValueSlider.tscn" id="3_u6gtq"]
|
||||||
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="4"]
|
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="4"]
|
||||||
|
|
||||||
[node name="ExportDialog" type="ConfirmationDialog"]
|
[node name="ExportDialog" type="ConfirmationDialog"]
|
||||||
|
@ -84,6 +85,7 @@ item_count = 4
|
||||||
popup/item_0/text = "Columns"
|
popup/item_0/text = "Columns"
|
||||||
popup/item_0/id = 1
|
popup/item_0/id = 1
|
||||||
popup/item_1/text = "Rows"
|
popup/item_1/text = "Rows"
|
||||||
|
popup/item_1/id = 1
|
||||||
popup/item_2/text = "Tags by column"
|
popup/item_2/text = "Tags by column"
|
||||||
popup/item_2/id = 2
|
popup/item_2/id = 2
|
||||||
popup/item_3/text = "Tags by row"
|
popup/item_3/text = "Tags by row"
|
||||||
|
@ -94,11 +96,12 @@ unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Columns:"
|
text = "Columns:"
|
||||||
|
|
||||||
[node name="LinesCount" type="SpinBox" parent="VBoxContainer/VSplitContainer/VBoxContainer/GridContainer" groups=["ExportSpritesheetOptions"]]
|
[node name="LinesCount" parent="VBoxContainer/VSplitContainer/VBoxContainer/GridContainer" groups=["ExportSpritesheetOptions"] instance=ExtResource("3_u6gtq")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(0, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
focus_mode = 0
|
||||||
mouse_default_cursor_shape = 2
|
theme_type_variation = &""
|
||||||
min_value = 1.0
|
min_value = 1.0
|
||||||
max_value = 1000.0
|
max_value = 1000.0
|
||||||
value = 1.0
|
value = 1.0
|
||||||
|
@ -358,6 +361,11 @@ size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="FrameTimer" type="Timer" parent="."]
|
[node name="FrameTimer" type="Timer" parent="."]
|
||||||
|
|
||||||
|
[node name="SpritesheetUpdateTimer" type="Timer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
wait_time = 0.2
|
||||||
|
one_shot = true
|
||||||
|
|
||||||
[connection signal="about_to_popup" from="." to="." method="_on_ExportDialog_about_to_show"]
|
[connection signal="about_to_popup" from="." to="." method="_on_ExportDialog_about_to_show"]
|
||||||
[connection signal="confirmed" from="." to="." method="_on_ExportDialog_confirmed"]
|
[connection signal="confirmed" from="." to="." method="_on_ExportDialog_confirmed"]
|
||||||
[connection signal="tab_clicked" from="VBoxContainer/TabBar" to="." method="_on_Tabs_tab_clicked"]
|
[connection signal="tab_clicked" from="VBoxContainer/TabBar" to="." method="_on_Tabs_tab_clicked"]
|
||||||
|
@ -384,3 +392,4 @@ size_flags_horizontal = 3
|
||||||
[connection signal="confirmed" from="FileExistsAlert" to="." method="_on_FileExistsAlert_confirmed"]
|
[connection signal="confirmed" from="FileExistsAlert" to="." method="_on_FileExistsAlert_confirmed"]
|
||||||
[connection signal="custom_action" from="FileExistsAlert" to="." method="_on_FileExistsAlert_custom_action"]
|
[connection signal="custom_action" from="FileExistsAlert" to="." method="_on_FileExistsAlert_custom_action"]
|
||||||
[connection signal="timeout" from="FrameTimer" to="." method="_on_FrameTimer_timeout"]
|
[connection signal="timeout" from="FrameTimer" to="." method="_on_FrameTimer_timeout"]
|
||||||
|
[connection signal="timeout" from="SpritesheetUpdateTimer" to="." method="_on_spritesheet_update_timer_timeout"]
|
||||||
|
|
Loading…
Add table
Reference in a new issue