From ed2e757c11b14144e175138138be28ada3def79e Mon Sep 17 00:00:00 2001 From: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> Date: Tue, 24 Dec 2019 23:51:08 +0200 Subject: [PATCH] Layer Opacity - Change alpha of each layer Added a slider under the layer add/remove/etc buttons that changes the currently selected layer's transparency. It gets saved in .png and .pxo files, while respecting non-opaque pixels in the image too. Which means, their alpha values aren't being overwritten. --- Main.tscn | 18 +++++++++---- Scripts/Canvas.gd | 25 +++++++++--------- Scripts/FrameButton.gd | 2 +- Scripts/Global.gd | 8 ++++-- Scripts/LayerContainer.gd | 3 ++- Scripts/Main.gd | 54 +++++++++++++++++++++++++++++---------- project.godot | 2 +- 7 files changed, 77 insertions(+), 35 deletions(-) diff --git a/Main.tscn b/Main.tscn index 449d1cb58..93fc305e1 100644 --- a/Main.tscn +++ b/Main.tscn @@ -158,12 +158,14 @@ anchor_bottom = 1.0 custom_constants/separation = 0 [node name="TopMenuContainer" type="Panel" parent="MenuAndUI"] +editor/display_folded = true margin_right = 1152.0 margin_bottom = 28.0 rect_min_size = Vector2( 0, 28 ) custom_styles/panel = ExtResource( 3 ) [node name="MenuItems" type="HBoxContainer" parent="MenuAndUI/TopMenuContainer"] +editor/display_folded = true margin_left = 2.0 margin_top = 4.0 margin_right = 1010.0 @@ -233,7 +235,6 @@ text = "[64×64]" align = 2 [node name="UI" type="HBoxContainer" parent="MenuAndUI"] -editor/display_folded = true margin_top = 28.0 margin_right = 1152.0 margin_bottom = 648.0 @@ -1326,7 +1327,6 @@ margin_right = 224.0 margin_bottom = 419.0 [node name="LayerVBoxContainer" type="VBoxContainer" parent="MenuAndUI/UI/LayerPanel/LayersAndMisc"] -editor/display_folded = true margin_top = 423.0 margin_right = 224.0 margin_bottom = 604.0 @@ -1417,9 +1417,16 @@ texture_normal = ExtResource( 55 ) texture_hover = ExtResource( 56 ) texture_disabled = ExtResource( 57 ) +[node name="OpacitySlider" type="HSlider" parent="MenuAndUI/UI/LayerPanel/LayersAndMisc/LayerVBoxContainer"] +margin_top = 38.0 +margin_right = 224.0 +margin_bottom = 44.0 +value = 100.0 +ticks_on_borders = false + [node name="ScrollLayers" type="ScrollContainer" parent="MenuAndUI/UI/LayerPanel/LayersAndMisc/LayerVBoxContainer"] editor/display_folded = true -margin_top = 38.0 +margin_top = 50.0 margin_right = 224.0 margin_bottom = 181.0 size_flags_horizontal = 3 @@ -1688,9 +1695,9 @@ anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 margin_left = -92.0 -margin_top = -36.0 +margin_top = -27.0 margin_right = 92.0 -margin_bottom = 5.0 +margin_bottom = -4.0 custom_constants/vseparation = 4 custom_constants/hseparation = 4 columns = 2 @@ -1871,6 +1878,7 @@ visible = false [connection signal="pressed" from="MenuAndUI/UI/LayerPanel/LayersAndMisc/LayerVBoxContainer/CenterLayerButtons/LayerButtons/MovwDownLayer" to="." method="change_layer_order" binds= [ -1 ]] [connection signal="pressed" from="MenuAndUI/UI/LayerPanel/LayersAndMisc/LayerVBoxContainer/CenterLayerButtons/LayerButtons/CloneLayer" to="." method="add_layer" binds= [ false ]] [connection signal="pressed" from="MenuAndUI/UI/LayerPanel/LayersAndMisc/LayerVBoxContainer/CenterLayerButtons/LayerButtons/MergeDownLayer" to="." method="_on_MergeLayer_pressed"] +[connection signal="value_changed" from="MenuAndUI/UI/LayerPanel/LayersAndMisc/LayerVBoxContainer/OpacitySlider" to="." method="_on_OpacitySlider_value_changed"] [connection signal="toggled" from="SplitScreenButton" to="." method="_on_SplitScreenButton_toggled"] [connection signal="confirmed" from="CreateNewImage" to="." method="_on_CreateNewImage_confirmed"] [connection signal="popup_hide" from="CreateNewImage" to="." method="_can_draw_true"] diff --git a/Scripts/Canvas.gd b/Scripts/Canvas.gd index 8d6b91cd5..329280c6e 100644 --- a/Scripts/Canvas.gd +++ b/Scripts/Canvas.gd @@ -36,8 +36,8 @@ func _ready() -> void: var tex := ImageTexture.new() tex.create_from_image(sprite, 0) - #Store [Image, ImageTexture, Layer Name, Visibity boolean] - layers.append([sprite, tex, "Layer 0", true]) + #Store [Image, ImageTexture, Layer Name, Visibity boolean, Opacity] + layers.append([sprite, tex, "Layer 0", true, 1]) generate_layer_panels() @@ -407,18 +407,19 @@ func _draw() -> void: #Draw current frame layers for texture in layers: + var modulate_color := Color(1, 1, 1, texture[4]) if texture[3]: #if it's visible - draw_texture(texture[1], location) + draw_texture(texture[1], location, modulate_color) if Global.tile_mode: - draw_texture(texture[1], Vector2(location.x, location.y + size.y)) #Down - draw_texture(texture[1], Vector2(location.x - size.x, location.y + size.y)) #Down Left - draw_texture(texture[1], Vector2(location.x - size.x, location.y)) #Left - draw_texture(texture[1], location - size) #Up left - draw_texture(texture[1], Vector2(location.x, location.y - size.y)) #Up - draw_texture(texture[1], Vector2(location.x + size.x, location.y - size.y)) #Up right - draw_texture(texture[1], Vector2(location.x + size.x, location.y)) #Right - draw_texture(texture[1], location + size) #Down right + draw_texture(texture[1], Vector2(location.x, location.y + size.y), modulate_color) #Down + draw_texture(texture[1], Vector2(location.x - size.x, location.y + size.y), modulate_color) #Down Left + draw_texture(texture[1], Vector2(location.x - size.x, location.y), modulate_color) #Left + draw_texture(texture[1], location - size, modulate_color) #Up left + draw_texture(texture[1], Vector2(location.x, location.y - size.y), modulate_color) #Up + draw_texture(texture[1], Vector2(location.x + size.x, location.y - size.y), modulate_color) #Up right + draw_texture(texture[1], Vector2(location.x + size.x, location.y), modulate_color) #Right + draw_texture(texture[1], location + size, modulate_color) #Down right #Idea taken from flurick (on GitHub) if Global.draw_grid: @@ -476,7 +477,7 @@ func generate_layer_panels() -> void: layer_container.get_child(1).get_child(0).texture = layers[i][1] layer_container.get_child(1).get_child(1).text = layers[i][2] layer_container.get_child(1).get_child(2).text = layers[i][2] - layers[i][3] = true #set visible + layers[i][3] = true # Set visible Global.vbox_layer_container.add_child(layer_container) func pencil_and_eraser(mouse_pos : Vector2, color : Color, current_mouse_button : String, current_action := "None") -> void: diff --git a/Scripts/FrameButton.gd b/Scripts/FrameButton.gd index 8468c0c59..351ceb5d1 100644 --- a/Scripts/FrameButton.gd +++ b/Scripts/FrameButton.gd @@ -46,7 +46,7 @@ func _on_PopupMenu_id_pressed(ID : int) -> void: sprite.lock() var tex := ImageTexture.new() tex.create_from_image(sprite, 0) - new_canvas.layers.append([sprite, tex, layer[2], layer[3]]) + new_canvas.layers.append([sprite, tex, layer[2], layer[3], layer[4]]) Global.undos += 1 Global.undo_redo.create_action("Add Frame") diff --git a/Scripts/Global.gd b/Scripts/Global.gd index 5523d0b9d..be5f0157a 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -189,6 +189,7 @@ var remove_layer_button : BaseButton var move_up_layer_button : BaseButton var move_down_layer_button : BaseButton var merge_down_layer_button : BaseButton +var layer_opacity_slider : HSlider var add_palette_button : TextureButton var remove_palette_button : TextureButton @@ -285,12 +286,15 @@ func _ready() -> void: play_backwards = find_node_by_name(root, "PlayBackwards") frame_container = find_node_by_name(root, "FrameContainer") - var layer_buttons = find_node_by_name(root, "LayerButtons") + var layer_stuff_container = find_node_by_name(root, "LayerVBoxContainer") + var layer_buttons = find_node_by_name(layer_stuff_container, "LayerButtons") remove_layer_button = find_node_by_name(layer_buttons, "RemoveLayer") move_up_layer_button = find_node_by_name(layer_buttons, "MoveUpLayer") move_down_layer_button = find_node_by_name(layer_buttons, "MovwDownLayer") merge_down_layer_button = find_node_by_name(layer_buttons, "MergeDownLayer") - vbox_layer_container = find_node_by_name(root, "VBoxLayerContainer") + + layer_opacity_slider = find_node_by_name(layer_stuff_container, "OpacitySlider") + vbox_layer_container = find_node_by_name(layer_stuff_container, "VBoxLayerContainer") add_palette_button = find_node_by_name(root, "AddPalette") remove_palette_button = find_node_by_name(root, "RemovePalette") diff --git a/Scripts/LayerContainer.gd b/Scripts/LayerContainer.gd index f3950e478..a80a6ef3f 100644 --- a/Scripts/LayerContainer.gd +++ b/Scripts/LayerContainer.gd @@ -43,9 +43,10 @@ func changed_selection() -> void: child.label.visible = true child.line_edit.visible = false child.line_edit.editable = false - if Global.canvas.current_layer_index == child.i: + if Global.canvas.current_layer_index == child.i: # The selected layer child.currently_selected = true child.pressed = true + Global.layer_opacity_slider.value = Global.canvas.layers[child.i][4] * 100 if Global.canvas.current_layer_index < Global.canvas.layers.size() - 1: Global.move_up_layer_button.disabled = false diff --git a/Scripts/Main.gd b/Scripts/Main.gd index c06d95a66..ebc84823e 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -387,8 +387,10 @@ func _on_OpenSprite_file_selected(path : String) -> void: return var current_version : String = ProjectSettings.get_setting("application/config/Version") + var current_version_number = float(current_version.substr(1, 3)) # Example, "0.6" var version := file.get_line() - if current_version != version: + var version_number = float(version.substr(1, 3)) # Example, "0.6" + if current_version_number < 0.5: OS.alert("File is from an older version of Pixelorama, as such it might not work properly") var frame := 0 var frame_line := file.get_line() @@ -403,12 +405,15 @@ func _on_OpenSprite_file_selected(path : String) -> void: while layer_line == "-": #Load layers var buffer := file.get_buffer(width * height * 4) var layer_name := file.get_line() + var layer_transparency := 1.0 + if version_number > 0.5: + layer_transparency = file.get_float() var image := Image.new() image.create_from_data(width, height, false, Image.FORMAT_RGBA8, buffer) image.lock() var tex := ImageTexture.new() tex.create_from_image(image, 0) - canvas.layers.append([image, tex, layer_name, true]) + canvas.layers.append([image, tex, layer_name, true, layer_transparency]) layer_line = file.get_line() var guide_line := file.get_line() #"guideline" no pun intended @@ -475,14 +480,15 @@ func _on_SaveSprite_file_selected(path) -> void: var err := file.open(path, File.WRITE) if err == 0: file.store_line(ProjectSettings.get_setting("application/config/Version")) - for canvas in Global.canvases: #Store frames + for canvas in Global.canvases: # Store frames file.store_line("--") file.store_16(canvas.size.x) file.store_16(canvas.size.y) - for layer in canvas.layers: #Store layers + for layer in canvas.layers: # Store layers file.store_line("-") file.store_buffer(layer[0].get_data()) - file.store_line(layer[2]) + file.store_line(layer[2]) # Layer name + file.store_float(layer[4]) # Layer transparency file.store_line("END_LAYERS") for child in canvas.get_children(): #Store guides @@ -544,8 +550,8 @@ func _on_ImportSprites_files_selected(paths) -> void: image.lock() var tex := ImageTexture.new() tex.create_from_image(image, 0) - #Store [Image, ImageTexture, Layer Name, Visibity boolean] - canvas.layers.append([image, tex, "Layer 0", true]) + #Store [Image, ImageTexture, Layer Name, Visibity boolean, Opacity] + canvas.layers.append([image, tex, "Layer 0", true, 1]) canvas.frame = i Global.canvases.append(canvas) Global.canvas_parent.add_child(canvas) @@ -606,7 +612,16 @@ func save_sprite(canvas : Canvas, path : String) -> void: whole_image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8) whole_image.lock() for layer in canvas.layers: - canvas.blend_rect(whole_image, layer[0], Rect2(canvas.position, canvas.size), Vector2.ZERO) + var img : Image = layer[0] + img.lock() + if layer[4] < 1: # If we have layer transparency + for xx in img.get_size().x: + for yy in img.get_size().y: + var pixel_color := img.get_pixel(xx, yy) + var alpha : float = pixel_color.a * layer[4] + img.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha)) + + canvas.blend_rect(whole_image, img, Rect2(canvas.position, canvas.size), Vector2.ZERO) layer[0].lock() var err = whole_image.save_png(path) if err != OK: @@ -635,8 +650,18 @@ func save_spritesheet() -> void: var dst := Vector2.ZERO for canvas in Global.canvases: for layer in canvas.layers: - canvas.blend_rect(whole_image, layer[0], Rect2(canvas.position, canvas.size), dst) + var img : Image = layer[0] + img.lock() + if layer[4] < 1: # If we have layer transparency + for xx in img.get_size().x: + for yy in img.get_size().y: + var pixel_color := img.get_pixel(xx, yy) + var alpha : float = pixel_color.a * layer[4] + img.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha)) + + canvas.blend_rect(whole_image, img, Rect2(canvas.position, canvas.size), dst) layer[0].lock() + if export_vertical_spritesheet.pressed: dst += Vector2(0, canvas.size.y) else: @@ -691,7 +716,7 @@ func _on_OutlineDialog_confirmed() -> void: new_image.set_pixelv(new_pos, outline_color) new_pos = pos + Vector2.RIGHT * i - if new_pos.x < Global.canvas.size.x - 1: + if new_pos.x < Global.canvas.size.x: var new_pixel = image.get_pixelv(new_pos) if new_pixel.a == 0: new_image.set_pixelv(new_pos, outline_color) @@ -703,7 +728,7 @@ func _on_OutlineDialog_confirmed() -> void: new_image.set_pixelv(new_pos, outline_color) new_pos = pos + Vector2.DOWN * i - if new_pos.y < Global.canvas.size.y - 1: + if new_pos.y < Global.canvas.size.y: var new_pixel = image.get_pixelv(new_pos) if new_pixel.a == 0: new_image.set_pixelv(new_pos, outline_color) @@ -828,7 +853,7 @@ func add_layer(is_new := true) -> void: new_layer_tex.create_from_image(new_layer, 0) var new_layers := Global.canvas.layers.duplicate() - new_layers.append([new_layer, new_layer_tex, null, true]) + new_layers.append([new_layer, new_layer_tex, null, true, 1]) Global.undos += 1 Global.undo_redo.create_action("Add Layer") Global.undo_redo.add_do_property(Global.canvas, "layers", new_layers) @@ -1103,6 +1128,9 @@ func _on_RightHorizontalMirroring_toggled(button_pressed) -> void: func _on_RightVerticalMirroring_toggled(button_pressed) -> void: Global.right_vertical_mirror = button_pressed +func _on_OpacitySlider_value_changed(value) -> void: + Global.canvas.layers[Global.canvas.current_layer_index][4] = value / 100 + func _on_QuitDialog_confirmed() -> void: # Darken the UI to denote that the application is currently exiting # (it won't respond to user input in this state). @@ -1110,7 +1138,7 @@ func _on_QuitDialog_confirmed() -> void: get_tree().quit() -func _on_AddPalette_pressed(): +func _on_AddPalette_pressed() -> void: Global.add_palette_button.get_child(0).popup(Rect2(Global.add_palette_button.rect_global_position, Vector2.ONE)) func _on_RemovePalette_pressed() -> void: diff --git a/project.godot b/project.godot index 9a705a32c..4fc4d2860 100644 --- a/project.godot +++ b/project.godot @@ -51,7 +51,7 @@ boot_splash/image="res://splash.png" boot_splash/bg_color=Color( 0.145098, 0.145098, 0.164706, 1 ) config/icon="res://icon.png" config/description="A free & open-source 2D sprite editor" -config/Version="v0.5" +config/Version="v0.6" [autoload]