diff --git a/Translations/Translations.pot b/Translations/Translations.pot index 6656b8bdb..3394e7b1e 100644 --- a/Translations/Translations.pot +++ b/Translations/Translations.pot @@ -956,9 +956,6 @@ msgstr "" msgid "Shadow color:" msgstr "" -msgid "Adjust Hue/Saturation/Value" -msgstr "" - msgid "Gradient" msgstr "" @@ -1012,24 +1009,64 @@ msgstr "" msgid "Dithering pattern:" msgstr "" -msgid "Adjust HSV" -msgstr "" - msgid "Type:" msgstr "" msgid "Angle:" msgstr "" +#. An image effect. Adjusts the hue, saturation and value of the colors of an image. +msgid "Adjust Hue/Saturation/Value" +msgstr "" + +#. HSV stands for Hue, Saturation & Value. +msgid "Adjust HSV" +msgstr "" + +#. Refers to the hue of the colors of an image. msgid "Hue:" msgstr "" +#. Refers to the saturation of the colors of an image. msgid "Saturation:" msgstr "" +#. Refers to the value (as in HSV) of the colors of an image. msgid "Value:" msgstr "" +#. An image effect. Adjusts the brightness and contrast of the colors of an image. +msgid "Adjust Brightness/Contrast" +msgstr "" + +#. Refers to the brightness of the colors of an image. +msgid "Brightness:" +msgstr "" + +#. Refers to the contrast of the colors of an image. +msgid "Contrast:" +msgstr "" + +#. Refers to the red value of the colors of an image. +msgid "Red value:" +msgstr "" + +#. Refers to the green value of the colors of an image. +msgid "Green value:" +msgstr "" + +#. Refers to the blue value of the colors of an image. +msgid "Blue value:" +msgstr "" + +#. Refers to a color that tints an image. +msgid "Tint color:" +msgstr "" + +#. Refers to the factor (how much) a color tints an image. +msgid "Tint effect factor:" +msgstr "" + msgid "Apply" msgstr "" diff --git a/project.godot b/project.godot index ab7123521..505d937bf 100644 --- a/project.godot +++ b/project.godot @@ -677,6 +677,10 @@ adjust_hsv={ "deadzone": 0.5, "events": [] } +adjust_brightness_saturation={ +"deadzone": 0.5, +"events": [] +} gradient={ "deadzone": 0.5, "events": [] diff --git a/src/Autoload/Global.gd b/src/Autoload/Global.gd index 52f5b35a4..a5f8c791f 100644 --- a/src/Autoload/Global.gd +++ b/src/Autoload/Global.gd @@ -59,6 +59,7 @@ enum EffectsMenu { INVERT_COLORS, DESATURATION, HSV, + BRIGHTNESS_SATURATION, PALETTIZE, PIXELIZE, POSTERIZE, @@ -746,6 +747,7 @@ func _initialize_keychain() -> void: &"outline": Keychain.InputAction.new("", "Effects menu", true), &"drop_shadow": Keychain.InputAction.new("", "Effects menu", true), &"adjust_hsv": Keychain.InputAction.new("", "Effects menu", true), + &"adjust_brightness_saturation": Keychain.InputAction.new("", "Effects menu", true), &"gradient": Keychain.InputAction.new("", "Effects menu", true), &"gradient_map": Keychain.InputAction.new("", "Effects menu", true), &"palettize": Keychain.InputAction.new("", "Effects menu", true), diff --git a/src/Shaders/Effects/BrightnessContrast.gdshader b/src/Shaders/Effects/BrightnessContrast.gdshader new file mode 100644 index 000000000..971731eb5 --- /dev/null +++ b/src/Shaders/Effects/BrightnessContrast.gdshader @@ -0,0 +1,60 @@ +// Shader from https://godotshaders.com/shader/color-manipulator/ +// Licensed under CC0 +shader_type canvas_item; +render_mode unshaded; + +uniform float brightness : hint_range(-1, 1) = 0; +uniform float contrast : hint_range(0, 3) = 1.0; +uniform float saturation : hint_range(0, 3) = 1.0; + +uniform float red_value : hint_range(0, 1) = 1.0; +uniform float green_value : hint_range(0, 1) = 1.0; +uniform float blue_value : hint_range(0, 1) = 1.0; + +uniform vec4 tint_color : source_color = vec4(1.0, 1.0, 1.0, 1.0); +uniform float tint_effect_factor : hint_range(0, 1) = 0.0; + +uniform sampler2D selection : filter_nearest; + +mat4 contrastMatrix( float _contrast ) +{ + float t = ( 1.0 - _contrast ) / 2.0; + return mat4( + vec4(_contrast, 0, 0, 0), + vec4(0, _contrast, 0, 0), + vec4(0, 0, _contrast, 0), + vec4(t, t, t, 1)); +} + +mat4 brightnessMatrix( float _brightness ) +{ + return mat4( vec4(1, 0, 0, 0), + vec4(0, 1, 0, 0), + vec4(0, 0, 1, 0), + vec4(_brightness, _brightness, _brightness, 1)); +} + +mat4 saturationMatrix( float _saturation ) +{ + vec3 luminance = vec3( 0.3086, 0.6094, 0.0820 ); + float oneMinusSat = 1.0 - _saturation; + + vec3 red = vec3( luminance.x * oneMinusSat ); + red+= vec3(_saturation, 0, 0) * red_value; + vec3 green = vec3( luminance.y * oneMinusSat ); + green += vec3(0, _saturation, 0) * green_value; + vec3 blue = vec3( luminance.z * oneMinusSat ); + blue += vec3(0, 0, _saturation ) * blue_value; + + return mat4(vec4(red, 0), vec4(green, 0), vec4(blue, 0), vec4(0, 0, 0, 1)); +} + +void fragment() +{ + vec4 original_color = texture(TEXTURE, UV); + vec4 selection_color = texture(selection, UV); + vec4 c2 = original_color * tint_color; + vec4 col = brightnessMatrix(brightness) * contrastMatrix(contrast) * saturationMatrix(saturation) * mix(original_color, c2, tint_effect_factor); + vec3 output = mix(original_color.rgb, col.rgb, selection_color.a); + COLOR = vec4(output.rgb, original_color.a); +} diff --git a/src/UI/Dialogs/ImageEffects/BrightnessContrastDialog.gd b/src/UI/Dialogs/ImageEffects/BrightnessContrastDialog.gd new file mode 100644 index 000000000..3081ef12a --- /dev/null +++ b/src/UI/Dialogs/ImageEffects/BrightnessContrastDialog.gd @@ -0,0 +1,87 @@ +extends ImageEffect + +enum Animate { BRIGHTNESS, CONTRAST, SATURATION, RED, GREEN, BLUE, TINT_EFFECT_FACTOR } + +var shader := preload("res://src/Shaders/Effects/BrightnessContrast.gdshader") + + +func _ready() -> void: + super._ready() + var sm := ShaderMaterial.new() + sm.shader = shader + preview.set_material(sm) + animate_panel.add_float_property("Brightness", $VBoxContainer/BrightnessSlider) + animate_panel.add_float_property("Contrast", $VBoxContainer/ContrastSlider) + animate_panel.add_float_property("Saturation", $VBoxContainer/SaturationSlider) + animate_panel.add_float_property("Red", $VBoxContainer/RedSlider) + animate_panel.add_float_property("Green", $VBoxContainer/GreenSlider) + animate_panel.add_float_property("Blue", $VBoxContainer/BlueSlider) + animate_panel.add_float_property("Tint effect factor", $VBoxContainer/TintSlider) + + +func commit_action(cel: Image, project := Global.current_project) -> void: + var brightness := animate_panel.get_animated_value(commit_idx, Animate.BRIGHTNESS) / 100.0 + var contrast := animate_panel.get_animated_value(commit_idx, Animate.CONTRAST) / 100.0 + var saturation := animate_panel.get_animated_value(commit_idx, Animate.SATURATION) / 100.0 + var red := animate_panel.get_animated_value(commit_idx, Animate.RED) / 100.0 + var green := animate_panel.get_animated_value(commit_idx, Animate.GREEN) / 100.0 + var blue := animate_panel.get_animated_value(commit_idx, Animate.BLUE) / 100.0 + var tint_color: Color = $VBoxContainer/TintColorContainer/TintColor.color + var tint_effect_factor := ( + animate_panel.get_animated_value(commit_idx, Animate.TINT_EFFECT_FACTOR) / 100.0 + ) + var selection_tex: ImageTexture + if selection_checkbox.button_pressed and project.has_selection: + var selection := project.selection_map.return_cropped_copy(project.size) + selection_tex = ImageTexture.create_from_image(selection) + + var params := { + "brightness": brightness, + "contrast": contrast, + "saturation": saturation, + "red_value": red, + "blue_value": blue, + "green_value": green, + "tint_color": tint_color, + "tint_effect_factor": tint_effect_factor, + "selection": selection_tex + } + + if !has_been_confirmed: + for param in params: + preview.material.set_shader_parameter(param, params[param]) + else: + var gen := ShaderImageEffect.new() + gen.generate_image(cel, shader, params, project.size) + + +func _on_brightness_slider_value_changed(_value: float) -> void: + update_preview() + + +func _on_contrast_slider_value_changed(_value: float) -> void: + update_preview() + + +func _on_saturation_slider_value_changed(_value: float) -> void: + update_preview() + + +func _on_red_slider_value_changed(_value: float) -> void: + update_preview() + + +func _on_green_slider_value_changed(_value: float) -> void: + update_preview() + + +func _on_blue_slider_value_changed(_value: float) -> void: + update_preview() + + +func _on_tint_color_color_changed(_color: Color) -> void: + update_preview() + + +func _on_tint_slider_value_changed(_value: float) -> void: + update_preview() diff --git a/src/UI/Dialogs/ImageEffects/BrightnessContrastDialog.tscn b/src/UI/Dialogs/ImageEffects/BrightnessContrastDialog.tscn new file mode 100644 index 000000000..a90cdc44a --- /dev/null +++ b/src/UI/Dialogs/ImageEffects/BrightnessContrastDialog.tscn @@ -0,0 +1,143 @@ +[gd_scene load_steps=4 format=3 uid="uid://7hslmewq0w4a"] + +[ext_resource type="PackedScene" uid="uid://bybqhhayl5ay5" path="res://src/UI/Dialogs/ImageEffects/ImageEffectParent.tscn" id="1_5wfra"] +[ext_resource type="Script" path="res://src/UI/Dialogs/ImageEffects/BrightnessContrastDialog.gd" id="2_msv0o"] +[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="3_2epr4"] + +[node name="BrightnessContrastDialog" instance=ExtResource("1_5wfra")] +title = "Adjust Brightness/Contrast" +size = Vector2i(362, 540) +visible = true +script = ExtResource("2_msv0o") + +[node name="VBoxContainer" parent="." index="3"] +offset_bottom = 491.0 + +[node name="BrightnessSlider" type="TextureProgressBar" parent="VBoxContainer" index="2"] +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" +min_value = -100.0 +nine_patch_stretch = true +stretch_margin_left = 3 +stretch_margin_top = 3 +stretch_margin_right = 3 +stretch_margin_bottom = 3 +script = ExtResource("3_2epr4") +prefix = "Brightness:" + +[node name="ContrastSlider" type="TextureProgressBar" parent="VBoxContainer" index="3"] +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" +max_value = 300.0 +value = 100.0 +nine_patch_stretch = true +stretch_margin_left = 3 +stretch_margin_top = 3 +stretch_margin_right = 3 +stretch_margin_bottom = 3 +script = ExtResource("3_2epr4") +prefix = "Contrast:" + +[node name="SaturationSlider" type="TextureProgressBar" parent="VBoxContainer" index="4"] +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" +max_value = 300.0 +value = 100.0 +nine_patch_stretch = true +stretch_margin_left = 3 +stretch_margin_top = 3 +stretch_margin_right = 3 +stretch_margin_bottom = 3 +script = ExtResource("3_2epr4") +prefix = "Saturation:" + +[node name="RedSlider" type="TextureProgressBar" parent="VBoxContainer" index="5"] +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" +value = 100.0 +nine_patch_stretch = true +stretch_margin_left = 3 +stretch_margin_top = 3 +stretch_margin_right = 3 +stretch_margin_bottom = 3 +script = ExtResource("3_2epr4") +prefix = "Red value:" + +[node name="GreenSlider" type="TextureProgressBar" parent="VBoxContainer" index="6"] +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" +value = 100.0 +nine_patch_stretch = true +stretch_margin_left = 3 +stretch_margin_top = 3 +stretch_margin_right = 3 +stretch_margin_bottom = 3 +script = ExtResource("3_2epr4") +prefix = "Green value:" + +[node name="BlueSlider" type="TextureProgressBar" parent="VBoxContainer" index="7"] +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" +value = 100.0 +nine_patch_stretch = true +stretch_margin_left = 3 +stretch_margin_top = 3 +stretch_margin_right = 3 +stretch_margin_bottom = 3 +script = ExtResource("3_2epr4") +prefix = "Blue value:" + +[node name="TintColorContainer" type="HBoxContainer" parent="VBoxContainer" index="8"] +layout_mode = 2 +alignment = 1 + +[node name="Label" type="Label" parent="VBoxContainer/TintColorContainer" index="0"] +layout_mode = 2 +text = "Tint color:" + +[node name="TintColor" type="ColorPickerButton" parent="VBoxContainer/TintColorContainer" index="1"] +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 +size_flags_horizontal = 3 +color = Color(1, 1, 1, 1) + +[node name="TintSlider" type="TextureProgressBar" parent="VBoxContainer" index="9"] +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" +nine_patch_stretch = true +stretch_margin_left = 3 +stretch_margin_top = 3 +stretch_margin_right = 3 +stretch_margin_bottom = 3 +script = ExtResource("3_2epr4") +prefix = "Tint effect factor:" + +[connection signal="value_changed" from="VBoxContainer/BrightnessSlider" to="." method="_on_brightness_slider_value_changed"] +[connection signal="value_changed" from="VBoxContainer/ContrastSlider" to="." method="_on_contrast_slider_value_changed"] +[connection signal="value_changed" from="VBoxContainer/SaturationSlider" to="." method="_on_saturation_slider_value_changed"] +[connection signal="value_changed" from="VBoxContainer/RedSlider" to="." method="_on_red_slider_value_changed"] +[connection signal="value_changed" from="VBoxContainer/GreenSlider" to="." method="_on_green_slider_value_changed"] +[connection signal="value_changed" from="VBoxContainer/BlueSlider" to="." method="_on_blue_slider_value_changed"] +[connection signal="color_changed" from="VBoxContainer/TintColorContainer/TintColor" to="." method="_on_tint_color_color_changed"] +[connection signal="value_changed" from="VBoxContainer/TintSlider" to="." method="_on_tint_slider_value_changed"] diff --git a/src/UI/Dialogs/ImageEffects/ImageEffectParent.tscn b/src/UI/Dialogs/ImageEffects/ImageEffectParent.tscn index 5dd1c2b87..773962b90 100644 --- a/src/UI/Dialogs/ImageEffects/ImageEffectParent.tscn +++ b/src/UI/Dialogs/ImageEffects/ImageEffectParent.tscn @@ -92,6 +92,7 @@ layout_mode = 2 size_flags_horizontal = 3 mouse_default_cursor_shape = 2 item_count = 4 +selected = 0 popup/item_0/text = "Selected cels" popup/item_0/id = 0 popup/item_1/text = "Current frame" diff --git a/src/UI/Timeline/LayerEffects/LayerEffectsSettings.gd b/src/UI/Timeline/LayerEffects/LayerEffectsSettings.gd index 8690d8f13..32230e239 100644 --- a/src/UI/Timeline/LayerEffects/LayerEffectsSettings.gd +++ b/src/UI/Timeline/LayerEffects/LayerEffectsSettings.gd @@ -12,6 +12,10 @@ var effects: Array[LayerEffect] = [ LayerEffect.new( "Adjust Hue/Saturation/Value", preload("res://src/Shaders/Effects/HSV.gdshader") ), + LayerEffect.new( + "Adjust Brightness/Contrast", + preload("res://src/Shaders/Effects/BrightnessContrast.gdshader") + ), LayerEffect.new("Palettize", preload("res://src/Shaders/Effects/Palettize.gdshader")), LayerEffect.new("Pixelize", preload("res://src/Shaders/Effects/Pixelize.gdshader")), LayerEffect.new("Posterize", preload("res://src/Shaders/Effects/Posterize.gdshader")), diff --git a/src/UI/TopMenuContainer/TopMenuContainer.gd b/src/UI/TopMenuContainer/TopMenuContainer.gd index 7d33726e7..5cdb8d8c2 100644 --- a/src/UI/TopMenuContainer/TopMenuContainer.gd +++ b/src/UI/TopMenuContainer/TopMenuContainer.gd @@ -27,6 +27,9 @@ var desaturate_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/Desaturat var outline_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/OutlineDialog.tscn") var drop_shadow_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/DropShadowDialog.tscn") var hsv_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/HSVDialog.tscn") +var adjust_brightness_saturation_dialog := Dialog.new( + "res://src/UI/Dialogs/ImageEffects/BrightnessContrastDialog.tscn" +) var gradient_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/GradientDialog.tscn") var gradient_map_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/GradientMapDialog.tscn") var palettize_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/PalettizeDialog.tscn") @@ -398,6 +401,7 @@ func _setup_effects_menu() -> void: "Invert Colors": "invert_colors", "Desaturation": "desaturation", "Adjust Hue/Saturation/Value": "adjust_hsv", + "Adjust Brightness/Saturation": "adjust_brightness_saturation", "Palettize": "palettize", "Pixelize": "pixelize", "Posterize": "posterize", @@ -805,6 +809,8 @@ func effects_menu_id_pressed(id: int) -> void: drop_shadow_dialog.popup() Global.EffectsMenu.HSV: hsv_dialog.popup() + Global.EffectsMenu.BRIGHTNESS_SATURATION: + adjust_brightness_saturation_dialog.popup() Global.EffectsMenu.GRADIENT: gradient_dialog.popup() Global.EffectsMenu.GRADIENT_MAP: