1
0
Fork 0
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:
Emmanouil Papadeas 2022-04-22 21:11:02 +03:00
parent 8eb8c5aade
commit 2da5b1e944
3 changed files with 142 additions and 36 deletions

View 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);
}

View file

@ -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()

View file

@ -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"]