1
0
Fork 0
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:
Variable 2024-09-04 20:07:01 +05:00
parent 4a7f7cbde5
commit 3bd964c5b6
4 changed files with 69 additions and 31 deletions

View file

@ -25,39 +25,39 @@ func blend_layers(
only_selected_cels := false,
only_selected_layers := false,
) -> 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
project.order_layers(frame_index)
var textures: Array[Image] = []
# 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
# 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():
var ordered_index := project.ordered_layers[i]
var layer := project.layers[ordered_index]
var include := true if layer.is_visible_in_hierarchy() else false
var ordered_index: int = project.ordered_layers[i]
var layer: BaseLayer = project.layers[ordered_index]
var include: bool = true if layer.is_visible_in_hierarchy() else false
if only_selected_cels and include:
var test_array := [frame_index, i]
if not test_array in project.selected_cels:
include = false
if only_selected_layers and include:
var layer_is_selected := false
var layer_is_selected: bool = false
for selected_cel in project.selected_cels:
if i == selected_cel[1]:
layer_is_selected = true
break
if not layer_is_selected:
include = false
var cel := frame.cels[ordered_index]
var cel: BaseCel = frame.cels[ordered_index]
if DisplayServer.get_name() == "headless":
blend_layers_headless(image, project, layer, cel, origin)
else:
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)
else:
var cel_image := layer.display_effects(cel)
var cel_image: Image = layer.display_effects(cel)
textures.append(cel_image)
if (
layer.is_blended_by_ancestor()
@ -67,13 +67,13 @@ func blend_layers(
include = false
set_layer_metadata_image(layer, cel, metadata_image, ordered_index, include)
if DisplayServer.get_name() != "headless":
var texture_array := Texture2DArray.new()
var texture_array: Texture2DArray = Texture2DArray.new()
texture_array.create_from_images(textures)
var params := {
"layers": texture_array,
"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()
gen.generate_image(blended, blend_layers_shader, params, project.size)
image.blend_rect(blended, Rect2i(Vector2i.ZERO, project.size), origin)

View file

@ -149,7 +149,14 @@ func process_data(project := Global.current_project) -> void:
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()
# Range of frames determined by tags
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 height := project.size.y * spritesheet_rows
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 hh := 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.x = 0
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)
## 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))
@ -671,7 +692,7 @@ func _blend_layers(
# 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
# 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)
if err == OK:
var frame_index := project.frames.find(frame) + 1
@ -679,8 +700,8 @@ func _blend_layers(
if zip_reader.file_exists(image_path):
# "Include blended" must be toggled on when saving the pxo file
# in order for this to work.
var image_data := zip_reader.read_file(image_path)
var loaded_image := Image.create_from_data(
var image_data: PackedByteArray = zip_reader.read_file(image_path)
var loaded_image: Image = Image.create_from_data(
project.size.x,
project.size.y,
image.has_mipmaps(),
@ -698,8 +719,8 @@ func _blend_layers(
elif export_layers == SELECTED_LAYERS:
DrawingAlgos.blend_layers(image, frame, origin, project, false, true)
else:
var layer := project.layers[export_layers - 2]
var layer_image := Image.new()
var layer: BaseLayer = project.layers[export_layers - 2]
var layer_image: Image = Image.new()
if layer is GroupLayer:
layer_image.copy_from(layer.blend_children(frame, Vector2i.ZERO))
else:

View file

@ -6,7 +6,7 @@ signal about_to_preview(dict: Dictionary)
var preview_current_frame := 0
var preview_frames: Array[Texture2D] = []
var spritesheet_info_cache = {}
# Allow custom exporters to be added
var image_exports: Array[Export.FileFormat] = [
Export.FileFormat.PNG,
@ -31,7 +31,7 @@ var _preview_images: Array[Export.ProcessedImage]
@onready var previews: GridContainer = $"%Previews"
@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 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_bar := %ProgressBar as ProgressBar
@onready var frame_timer: Timer = $FrameTimer
@onready var spritesheet_update_timer: Timer = %SpritesheetUpdateTimer
func _ready() -> void:
@ -84,7 +85,8 @@ func show_tab() -> void:
)
Export.ExportTab.SPRITESHEET:
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_lines_count.max_value = Export.number_of_frames
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 preview := create_preview_rect()
preview.texture = ImageTexture.create_from_image(image)
preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
container.add_child(preview)
if canvas_number != -1:
@ -145,6 +148,7 @@ func add_animated_preview() -> void:
var preview := create_preview_rect()
preview.name = "Preview"
preview.texture = preview_frames[preview_current_frame]
preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
container.add_child(preview)
previews.add_child(container)
@ -305,9 +309,8 @@ func _on_Orientation_item_selected(id: Export.Orientation) -> void:
Export.orientation = id
_handle_orientation_ui()
spritesheet_lines_count.value = Export.frames_divided_by_spritesheet_lines()
Export.process_spritesheet()
update_dimensions_label()
set_preview()
## Due to the above line, we don't have to process the spritesheet again
## the value_changed signal will do this for us.
func _handle_orientation_ui() -> void:
@ -326,9 +329,8 @@ func _handle_orientation_ui() -> void:
func _on_LinesCount_value_changed(value: float) -> void:
Export.lines_count = value
Export.process_spritesheet()
update_dimensions_label()
set_preview()
## Check if spritesheet needs to be updated (This is required when orientation gets changed)
spritesheet_update_timer.start()
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:
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()

View file

@ -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="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="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"]
[node name="ExportDialog" type="ConfirmationDialog"]
@ -84,6 +85,7 @@ item_count = 4
popup/item_0/text = "Columns"
popup/item_0/id = 1
popup/item_1/text = "Rows"
popup/item_1/id = 1
popup/item_2/text = "Tags by column"
popup/item_2/id = 2
popup/item_3/text = "Tags by row"
@ -94,11 +96,12 @@ unique_name_in_owner = true
layout_mode = 2
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
custom_minimum_size = Vector2(0, 0)
layout_mode = 2
size_flags_horizontal = 3
mouse_default_cursor_shape = 2
focus_mode = 0
theme_type_variation = &""
min_value = 1.0
max_value = 1000.0
value = 1.0
@ -358,6 +361,11 @@ size_flags_horizontal = 3
[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="confirmed" from="." to="." method="_on_ExportDialog_confirmed"]
[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="custom_action" from="FileExistsAlert" to="." method="_on_FileExistsAlert_custom_action"]
[connection signal="timeout" from="FrameTimer" to="." method="_on_FrameTimer_timeout"]
[connection signal="timeout" from="SpritesheetUpdateTimer" to="." method="_on_spritesheet_update_timer_timeout"]