mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-20 12:33:14 +00:00
Outline generation with shaders
This results in both better and faster results. Unfortunately, the shader does not work in the Web version, so we have to rely on the old method for that platform.
This commit is contained in:
parent
8eb8c5aade
commit
2da5b1e944
3 changed files with 142 additions and 36 deletions
49
src/Shaders/OutlineInline.gdshader
Normal file
49
src/Shaders/OutlineInline.gdshader
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Based on https://godotshaders.com/shader/2d-outline-inline/
|
||||
shader_type canvas_item;
|
||||
render_mode unshaded;
|
||||
|
||||
uniform vec4 color : hint_color = vec4(1.0);
|
||||
uniform float width : hint_range(0, 10) = 1.0;
|
||||
uniform int pattern : hint_range(0, 2) = 0; // diamond, circle, square
|
||||
uniform bool inside = false;
|
||||
uniform sampler2D selection;
|
||||
|
||||
|
||||
bool has_contrary_neighbour(vec2 uv, vec2 texture_pixel_size, sampler2D tex) {
|
||||
for (float i = -ceil(width); i <= ceil(width); i++) {
|
||||
float x = abs(i) > width ? width * sign(i) : i;
|
||||
float offset;
|
||||
|
||||
if (pattern == 0) {
|
||||
offset = width - abs(x);
|
||||
} else if (pattern == 1) {
|
||||
offset = floor(sqrt(pow(width + 0.5, 2) - x * x));
|
||||
} else if (pattern == 2) {
|
||||
offset = width;
|
||||
}
|
||||
|
||||
for (float j = -ceil(offset); j <= ceil(offset); j++) {
|
||||
float y = abs(j) > offset ? offset * sign(j) : j;
|
||||
vec2 xy = uv + texture_pixel_size * vec2(x, y);
|
||||
|
||||
if ((xy != clamp(xy, vec2(0.0), vec2(1.0)) || texture(tex, xy).a == 0.0) == inside) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 original_color = texture(TEXTURE, UV);
|
||||
vec4 selection_color = texture(selection, UV);
|
||||
vec4 output = texture(TEXTURE, UV);
|
||||
|
||||
if ((output.a > 0.0) == inside && has_contrary_neighbour(UV, TEXTURE_PIXEL_SIZE, TEXTURE)) {
|
||||
output.rgb = inside ? mix(output.rgb, color.rgb, color.a) : color.rgb;
|
||||
output.a += (1.0 - output.a) * color.a;
|
||||
}
|
||||
|
||||
COLOR = mix(original_color, output, selection_color.a);
|
||||
}
|
|
@ -2,27 +2,76 @@ extends ImageEffect
|
|||
|
||||
var color := Color.red
|
||||
var thickness := 1
|
||||
var diagonal := false
|
||||
var pattern := 0
|
||||
var inside_image := false
|
||||
var confirmed := false
|
||||
var shader: Shader
|
||||
|
||||
onready var outline_color = $VBoxContainer/OptionsContainer/OutlineColor
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if OS.get_name() == "HTML5" and OS.get_current_video_driver() == OS.VIDEO_DRIVER_GLES2:
|
||||
$VBoxContainer/OptionsContainer/PatternOptionButton.disabled = true
|
||||
else:
|
||||
shader = load("res://src/Shaders/OutlineInline.gdshader")
|
||||
outline_color.get_picker().presets_visible = false
|
||||
color = outline_color.color
|
||||
|
||||
|
||||
func _about_to_show() -> void:
|
||||
confirmed = false
|
||||
if shader:
|
||||
var sm := ShaderMaterial.new()
|
||||
sm.shader = shader
|
||||
preview.set_material(sm)
|
||||
._about_to_show()
|
||||
|
||||
|
||||
func _confirmed() -> void:
|
||||
confirmed = true
|
||||
._confirmed()
|
||||
|
||||
|
||||
func set_nodes() -> void:
|
||||
preview = $VBoxContainer/AspectRatioContainer/Preview
|
||||
selection_checkbox = $VBoxContainer/OptionsContainer/SelectionCheckBox
|
||||
affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton
|
||||
|
||||
|
||||
func commit_action(_cel: Image, _project: Project = Global.current_project) -> void:
|
||||
DrawingAlgos.generate_outline(
|
||||
_cel, selection_checkbox.pressed, _project, color, thickness, diagonal, inside_image
|
||||
)
|
||||
func commit_action(cel: Image, project: Project = Global.current_project) -> void:
|
||||
if !shader: # Web version
|
||||
DrawingAlgos.generate_outline(
|
||||
cel, selection_checkbox.pressed, project, color, thickness, false, inside_image
|
||||
)
|
||||
return
|
||||
|
||||
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)
|
||||
|
||||
if !confirmed:
|
||||
preview.material.set_shader_param("color", color)
|
||||
preview.material.set_shader_param("width", thickness)
|
||||
preview.material.set_shader_param("pattern", pattern)
|
||||
preview.material.set_shader_param("inside", inside_image)
|
||||
preview.material.set_shader_param("selection", selection_tex)
|
||||
preview.material.set_shader_param("affect_selection", selection_checkbox.pressed)
|
||||
preview.material.set_shader_param("has_selection", project.has_selection)
|
||||
else:
|
||||
var params := {
|
||||
"color": color,
|
||||
"width": thickness,
|
||||
"pattern": pattern,
|
||||
"inside": inside_image,
|
||||
"selection": selection_tex,
|
||||
"affect_selection": selection_checkbox.pressed,
|
||||
"has_selection": project.has_selection
|
||||
}
|
||||
var gen := ShaderImageEffect.new()
|
||||
gen.generate_image(cel, shader, params, project.size)
|
||||
yield(gen, "done")
|
||||
|
||||
|
||||
func _on_ThickValue_value_changed(value: int) -> void:
|
||||
|
@ -35,11 +84,11 @@ func _on_OutlineColor_color_changed(_color: Color) -> void:
|
|||
update_preview()
|
||||
|
||||
|
||||
func _on_DiagonalCheckBox_toggled(button_pressed: bool) -> void:
|
||||
diagonal = button_pressed
|
||||
update_preview()
|
||||
|
||||
|
||||
func _on_InsideImageCheckBox_toggled(button_pressed: bool) -> void:
|
||||
inside_image = button_pressed
|
||||
update_preview()
|
||||
|
||||
|
||||
func _on_PatternOptionButton_item_selected(index: int) -> void:
|
||||
pattern = index
|
||||
update_preview()
|
||||
|
|
|
@ -25,13 +25,13 @@ __meta__ = {
|
|||
|
||||
[node name="AspectRatioContainer" type="AspectRatioContainer" parent="VBoxContainer"]
|
||||
margin_right = 527.0
|
||||
margin_bottom = 359.0
|
||||
margin_bottom = 339.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Preview" type="TextureRect" parent="VBoxContainer/AspectRatioContainer"]
|
||||
margin_left = 84.0
|
||||
margin_right = 443.0
|
||||
margin_bottom = 359.0
|
||||
margin_left = 94.0
|
||||
margin_right = 433.0
|
||||
margin_bottom = 339.0
|
||||
rect_min_size = Vector2( 200, 200 )
|
||||
expand = true
|
||||
stretch_mode = 5
|
||||
|
@ -44,7 +44,7 @@ margin_right = 0.0
|
|||
margin_bottom = 0.0
|
||||
|
||||
[node name="OptionsContainer" type="GridContainer" parent="VBoxContainer"]
|
||||
margin_top = 363.0
|
||||
margin_top = 343.0
|
||||
margin_right = 527.0
|
||||
margin_bottom = 467.0
|
||||
custom_constants/vseparation = 4
|
||||
|
@ -56,12 +56,12 @@ __meta__ = {
|
|||
|
||||
[node name="ThickLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 5.0
|
||||
margin_right = 160.0
|
||||
margin_right = 148.0
|
||||
margin_bottom = 19.0
|
||||
text = "Thickness:"
|
||||
|
||||
[node name="ThickValue" type="SpinBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 164.0
|
||||
margin_left = 152.0
|
||||
margin_right = 312.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
|
@ -72,46 +72,54 @@ suffix = "px"
|
|||
|
||||
[node name="OutlineColorLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 31.0
|
||||
margin_right = 160.0
|
||||
margin_right = 148.0
|
||||
margin_bottom = 45.0
|
||||
text = "Fill with color:"
|
||||
|
||||
[node name="OutlineColor" type="ColorPickerButton" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 164.0
|
||||
margin_left = 152.0
|
||||
margin_top = 28.0
|
||||
margin_right = 312.0
|
||||
margin_bottom = 48.0
|
||||
rect_min_size = Vector2( 64, 20 )
|
||||
color = Color( 1, 0, 0, 1 )
|
||||
|
||||
[node name="DiagonalCheckBox" type="CheckBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 52.0
|
||||
margin_right = 160.0
|
||||
margin_bottom = 76.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Diagonal"
|
||||
[node name="PatternLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 55.0
|
||||
margin_right = 148.0
|
||||
margin_bottom = 69.0
|
||||
text = "Pattern:"
|
||||
|
||||
[node name="InsideImageCheckBox" type="CheckBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 164.0
|
||||
[node name="PatternOptionButton" type="OptionButton" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 152.0
|
||||
margin_top = 52.0
|
||||
margin_right = 312.0
|
||||
margin_bottom = 76.0
|
||||
margin_bottom = 72.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Diamond"
|
||||
items = [ "Diamond", null, false, 0, null, "Circle", null, false, 1, null, "Square", null, false, 2, null ]
|
||||
selected = 0
|
||||
|
||||
[node name="InsideImageCheckBox" type="CheckBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 76.0
|
||||
margin_right = 148.0
|
||||
margin_bottom = 100.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Place inside image"
|
||||
|
||||
[node name="SelectionCheckBox" type="CheckBox" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_top = 80.0
|
||||
margin_right = 160.0
|
||||
margin_bottom = 104.0
|
||||
margin_left = 152.0
|
||||
margin_top = 76.0
|
||||
margin_right = 312.0
|
||||
margin_bottom = 100.0
|
||||
mouse_default_cursor_shape = 2
|
||||
pressed = true
|
||||
text = "Only affect selection"
|
||||
|
||||
[node name="AffectOptionButton" type="OptionButton" parent="VBoxContainer/OptionsContainer"]
|
||||
margin_left = 164.0
|
||||
margin_top = 80.0
|
||||
margin_right = 312.0
|
||||
margin_bottom = 104.0
|
||||
margin_top = 104.0
|
||||
margin_right = 148.0
|
||||
margin_bottom = 124.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 ]
|
||||
|
@ -119,5 +127,5 @@ selected = 0
|
|||
|
||||
[connection signal="value_changed" from="VBoxContainer/OptionsContainer/ThickValue" to="." method="_on_ThickValue_value_changed"]
|
||||
[connection signal="color_changed" from="VBoxContainer/OptionsContainer/OutlineColor" to="." method="_on_OutlineColor_color_changed"]
|
||||
[connection signal="toggled" from="VBoxContainer/OptionsContainer/DiagonalCheckBox" to="." method="_on_DiagonalCheckBox_toggled"]
|
||||
[connection signal="item_selected" from="VBoxContainer/OptionsContainer/PatternOptionButton" to="." method="_on_PatternOptionButton_item_selected"]
|
||||
[connection signal="toggled" from="VBoxContainer/OptionsContainer/InsideImageCheckBox" to="." method="_on_InsideImageCheckBox_toggled"]
|
||||
|
|
Loading…
Add table
Reference in a new issue