1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-02-20 12:33:14 +00:00

Add a Gradient Map image effect, implements the second half of #595

The gradient edit code was taken and modified from Material Maker, MIT license.
This commit is contained in:
Emmanouil Papadeas 2022-06-11 16:15:34 +03:00
parent 872ac62722
commit c9f0301c79
9 changed files with 364 additions and 4 deletions

View file

@ -44,6 +44,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://src/Classes/Frame.gd"
}, {
"base": "TextureRect",
"class": "GradientEditNode",
"language": "GDScript",
"path": "res://src/UI/Nodes/GradientEdit.gd"
}, {
"base": "Line2D",
"class": "Guide",
"language": "GDScript",
@ -127,6 +132,7 @@ _global_script_class_icons={
"Cel": "",
"Drawer": "",
"Frame": "",
"GradientEditNode": "",
"Guide": "",
"ImageEffect": "",
"Layer": "",
@ -830,6 +836,10 @@ about_pixelorama={
"deadzone": 0.5,
"events": [ ]
}
gradient_map={
"deadzone": 0.5,
"events": [ ]
}
[locale]

View file

@ -33,6 +33,7 @@ enum ImageMenu {
DROP_SHADOW,
HSV,
GRADIENT,
GRADIENT_MAP,
SHADER
}
enum SelectMenu { SELECT_ALL, CLEAR_SELECTION, INVERT }
@ -281,6 +282,8 @@ func _initialize_keychain() -> void:
Keychain.MenuInputAction.new("", "Image menu", true, "ImageMenu", ImageMenu.HSV),
"gradient":
Keychain.MenuInputAction.new("", "Image menu", true, "ImageMenu", ImageMenu.GRADIENT),
"gradient_map":
Keychain.MenuInputAction.new("", "Image menu", true, "ImageMenu", ImageMenu.GRADIENT_MAP),
"mirror_view":
Keychain.MenuInputAction.new("", "View menu", true, "ViewMenu", ViewMenu.MIRROR_VIEW),
"show_grid":

View file

@ -0,0 +1,16 @@
shader_type canvas_item;
uniform sampler2D map; // GradientTexture
uniform sampler2D selection;
void fragment() {
vec4 original_color = texture(TEXTURE, UV);
vec4 selection_color = texture(selection, UV);
vec4 output = original_color;
float value = (0.2126 * original_color.r) + (0.7152 * original_color.g) + (0.0722 * original_color.b);
vec4 gradient_color = texture(map, vec2(value, 0.0));
output.rgb = gradient_color.rgb;
output.a *= gradient_color.a;
COLOR = mix(original_color, output, selection_color.a);
}

View file

@ -0,0 +1,49 @@
extends ImageEffect
var shader: Shader = preload("res://src/Shaders/GradientMap.gdshader")
var confirmed := false
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
var sm := ShaderMaterial.new()
sm.shader = shader
preview.set_material(sm)
func set_nodes() -> void:
preview = $VBoxContainer/AspectRatioContainer/Preview
selection_checkbox = $VBoxContainer/OptionsContainer/SelectionCheckBox
affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton
func _about_to_show() -> void:
confirmed = false
._about_to_show()
func _confirmed() -> void:
confirmed = true
._confirmed()
func commit_action(cel: Image, project: Project = Global.current_project) -> void:
var selection_tex := ImageTexture.new()
if selection_checkbox.pressed and project.has_selection:
var selection: Image = project.bitmap_to_image(project.selection_bitmap)
selection_tex.create_from_image(selection, 0)
var params := {"selection": selection_tex, "map": $VBoxContainer/GradientEdit.texture}
if !confirmed:
preview.material.shader = shader
for param in params:
preview.material.set_shader_param(param, params[param])
else:
var gen := ShaderImageEffect.new()
gen.generate_image(cel, shader, params, project.size)
yield(gen, "done")
func _on_GradientEdit_updated(_gradient, _cc) -> void:
update_preview()

View file

@ -0,0 +1,77 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://src/UI/TransparentChecker.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UI/Nodes/GradientEdit.tscn" type="PackedScene" id=2]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/GradientMapDialog.gd" type="Script" id=3]
[node name="GradientMapDialog" type="ConfirmationDialog"]
margin_right = 200.0
margin_bottom = 70.0
window_title = "Gradient Map"
resizable = true
script = ExtResource( 3 )
[node name="VBoxContainer" type="VBoxContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 8.0
margin_top = 8.0
margin_right = -8.0
margin_bottom = -36.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="AspectRatioContainer" type="AspectRatioContainer" parent="VBoxContainer"]
margin_right = 278.0
margin_bottom = 200.0
size_flags_vertical = 3
[node name="Preview" type="TextureRect" parent="VBoxContainer/AspectRatioContainer"]
margin_left = 39.0
margin_right = 239.0
margin_bottom = 200.0
rect_min_size = Vector2( 200, 200 )
size_flags_horizontal = 5
size_flags_vertical = 3
expand = true
stretch_mode = 5
[node name="TransparentChecker" parent="VBoxContainer/AspectRatioContainer/Preview" instance=ExtResource( 1 )]
show_behind_parent = true
anchor_right = 1.0
anchor_bottom = 1.0
margin_right = 0.0
margin_bottom = 0.0
[node name="GradientEdit" parent="VBoxContainer" instance=ExtResource( 2 )]
anchor_right = 0.0
anchor_bottom = 0.0
margin_top = 204.0
margin_right = 278.0
margin_bottom = 234.0
rect_min_size = Vector2( 0, 30 )
[node name="OptionsContainer" type="GridContainer" parent="VBoxContainer"]
margin_top = 238.0
margin_right = 278.0
margin_bottom = 262.0
columns = 2
[node name="SelectionCheckBox" type="CheckBox" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]]
margin_right = 160.0
margin_bottom = 24.0
mouse_default_cursor_shape = 2
pressed = true
text = "Only affect selection"
[node name="AffectOptionButton" type="OptionButton" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]]
margin_left = 164.0
margin_right = 278.0
margin_bottom = 24.0
mouse_default_cursor_shape = 2
text = "Selected cels"
items = [ "Selected cels", null, false, 0, null, "Current frame", null, false, 1, null, "All frames", null, false, 2, null, "All projects", null, false, 3, null ]
selected = 0
[connection signal="updated" from="VBoxContainer/GradientEdit" to="." method="_on_GradientEdit_updated"]

View file

@ -1,9 +1,10 @@
[gd_scene load_steps=12 format=2]
[gd_scene load_steps=13 format=2]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/FlipImageDialog.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/InvertColorsDialog.tscn" type="PackedScene" id=2]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/DesaturateDialog.tscn" type="PackedScene" id=3]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/DropShadowDialog.tscn" type="PackedScene" id=4]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/GradientMapDialog.tscn" type="PackedScene" id=5]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/ResizeCanvas.tscn" type="PackedScene" id=8]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/RotateImage.tscn" type="PackedScene" id=9]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/ShaderEffect.tscn" type="PackedScene" id=10]
@ -14,9 +15,6 @@
[node name="ImageEffects" type="Control"]
mouse_filter = 2
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ScaleImage" parent="." instance=ExtResource( 14 )]
margin_bottom = 127.0
@ -44,4 +42,6 @@ margin_bottom = 106.0
[node name="GradientDialog" parent="." instance=ExtResource( 12 )]
margin_bottom = 214.0
[node name="GradientMapDialog" parent="." instance=ExtResource( 5 )]
[node name="ShaderEffect" parent="." instance=ExtResource( 10 )]

View file

@ -0,0 +1,162 @@
# Code taken and modified from Material Maker, licensed under MIT
# gdlint: ignore=max-line-length
# https://github.com/RodZill4/material-maker/blob/master/material_maker/widgets/gradient_editor/gradient_editor.gd
class_name GradientEditNode
extends TextureRect
signal updated(gradient, cc)
var continuous_change := true
var active_cursor: GradientCursor # Showing a color picker popup to change a cursor's color
onready var x_offset: float = rect_size.x - GradientCursor.WIDTH
onready var gradient: Gradient = texture.gradient
class GradientCursor:
extends Control
const WIDTH := 10
var color: Color
var sliding := false
onready var label: Label = get_parent().get_node("Value")
func _ready() -> void:
rect_position = Vector2(0, 15)
rect_size = Vector2(WIDTH, 15)
func _draw() -> void:
# warning-ignore:integer_division
var polygon := PoolVector2Array(
[
Vector2(0, 5),
Vector2(WIDTH / 2, 0),
Vector2(WIDTH, 5),
Vector2(WIDTH, 15),
Vector2(0, 15),
Vector2(0, 5)
]
)
var c := color
c.a = 1.0
draw_colored_polygon(polygon, c)
draw_polyline(polygon, Color(0.0, 0.0, 0.0) if color.v > 0.5 else Color(1.0, 1.0, 1.0))
func _gui_input(ev: InputEvent) -> void:
if ev is InputEventMouseButton:
if ev.button_index == BUTTON_LEFT:
if ev.doubleclick:
get_parent().select_color(self, ev.global_position)
elif ev.pressed:
get_parent().continuous_change = false
sliding = true
label.visible = true
label.text = "%.03f" % get_cursor_position()
else:
sliding = false
label.visible = false
elif ev.button_index == BUTTON_RIGHT and get_parent().get_sorted_cursors().size() > 2:
var parent = get_parent()
parent.remove_child(self)
parent.continuous_change = false
parent.update_from_value()
queue_free()
elif ev is InputEventMouseMotion and (ev.button_mask & BUTTON_MASK_LEFT) != 0 and sliding:
rect_position.x += get_local_mouse_position().x
if ev.control:
rect_position.x = (
round(get_cursor_position() * 20.0)
* 0.05
* (get_parent().rect_size.x - WIDTH)
)
rect_position.x = min(max(0, rect_position.x), get_parent().rect_size.x - rect_size.x)
get_parent().update_from_value()
label.text = "%.03f" % get_cursor_position()
func get_cursor_position() -> float:
return rect_position.x / (get_parent().rect_size.x - WIDTH)
func set_color(c: Color) -> void:
color = c
get_parent().update_from_value()
update()
static func sort(a, b) -> bool:
return a.get_position() < b.get_position()
func can_drop_data(_position, data) -> bool:
return typeof(data) == TYPE_COLOR
func drop_data(_position, data) -> void:
set_color(data)
func _ready() -> void:
create_cursors()
func create_cursors() -> void:
for c in get_children():
if c is GradientCursor:
remove_child(c)
c.queue_free()
for i in gradient.get_point_count():
var p: float = gradient.get_offset(i)
add_cursor(p * x_offset, gradient.get_color(i))
func _gui_input(ev: InputEvent) -> void:
if ev.is_action_pressed("left_mouse"):
var p = clamp(ev.position.x, 0, x_offset)
add_cursor(p, get_gradient_color(p))
continuous_change = false
update_from_value()
func update_from_value() -> void:
gradient.offsets = []
for c in get_children():
if c is GradientCursor:
var point: float = c.rect_position.x / x_offset
gradient.add_point(point, c.color)
emit_signal("updated", gradient, continuous_change)
continuous_change = true
func add_cursor(x: float, color: Color) -> void:
var cursor := GradientCursor.new()
add_child(cursor)
cursor.rect_position.x = x
cursor.color = color
func select_color(cursor: GradientCursor, position: Vector2) -> void:
active_cursor = cursor
var color_picker = $Popup.get_node("ColorPicker")
color_picker.color = cursor.color
$Popup.rect_position = position
$Popup.popup()
func get_sorted_cursors() -> Array:
var array := []
for c in get_children():
if c is GradientCursor:
array.append(c)
array.sort_custom(GradientCursor, "sort")
return array
func get_gradient_color(x: float) -> Color:
return gradient.interpolate(x / x_offset)
func _on_ColorPicker_color_changed(color: Color) -> void:
active_cursor.set_color(color)
func _on_GradientEdit_resized() -> void:
if not gradient:
return
x_offset = rect_size.x - GradientCursor.WIDTH
create_cursors()

View file

@ -0,0 +1,38 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://src/UI/Nodes/GradientEdit.gd" type="Script" id=1]
[sub_resource type="Gradient" id=1]
[sub_resource type="GradientTexture" id=2]
gradient = SubResource( 1 )
[node name="GradientEdit" type="TextureRect"]
anchor_right = 1.0
anchor_bottom = 1.0
texture = SubResource( 2 )
expand = true
script = ExtResource( 1 )
[node name="Popup" type="PopupPanel" parent="."]
margin_right = 316.0
margin_bottom = 470.0
[node name="ColorPicker" type="ColorPicker" parent="Popup"]
margin_left = 4.0
margin_top = 4.0
margin_right = 312.0
margin_bottom = 466.0
[node name="Value" type="Label" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -20.0
margin_top = -7.0
margin_right = 20.0
margin_bottom = 7.0
[connection signal="resized" from="." to="." method="_on_GradientEdit_resized"]
[connection signal="color_changed" from="Popup/ColorPicker" to="." method="_on_ColorPicker_color_changed"]

View file

@ -252,6 +252,7 @@ func _setup_image_menu() -> void:
"Drop Shadow",
"Adjust Hue/Saturation/Value",
"Gradient",
"Gradient Map",
# "Shader"
]
var image_menu: PopupMenu = image_menu_button.get_popup()
@ -620,6 +621,10 @@ func image_menu_id_pressed(id: int) -> void:
Global.control.get_node("Dialogs/ImageEffects/GradientDialog").popup_centered()
Global.dialog_open(true)
Global.ImageMenu.GRADIENT_MAP:
Global.control.get_node("Dialogs/ImageEffects/GradientMapDialog").popup_centered()
Global.dialog_open(true)
# Global.ImageMenu.SHADER:
# Global.control.get_node("Dialogs/ImageEffects/ShaderEffect").popup_centered()
# Global.dialog_open(true)