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

Add a Posterize image effect, with optional dithering

Seems to produce the same result as GIMP's Posterize color filter. Should be useful for reducing the colors of an image, and it could work together with 3D lighting. Thanks to https://godotshaders.com/shader/color-reduction-and-dither/ for the shader.
This commit is contained in:
Emmanouil Papadeas 2023-04-20 16:08:06 +03:00
parent 9607981567
commit ec2dcae8f7
8 changed files with 178 additions and 11 deletions

View file

@ -855,16 +855,16 @@ msgstr ""
msgid "Steps:"
msgstr ""
msgid "Top to Bottom"
#. An image effect. For more details about what it does, you can refer to GIMP's documentation https://docs.gimp.org/2.8/en/gimp-tool-posterize.html
msgid "Posterize"
msgstr ""
msgid "Bottom to Top"
#. An option for the posterize image effect. For more details about what it does, you can refer to GIMP's documentation https://docs.gimp.org/2.8/en/gimp-tool-posterize.html
msgid "Posterize levels:"
msgstr ""
msgid "Left to Right"
msgstr ""
msgid "Right to Left"
#. An option for the posterize image effect.
msgid "Dither intensity:"
msgstr ""
msgid "View Splash Screen"

View file

@ -988,6 +988,14 @@ gradient={
"deadzone": 0.5,
"events": [ ]
}
gradient_map={
"deadzone": 0.5,
"events": [ ]
}
posterize={
"deadzone": 0.5,
"events": [ ]
}
view_splash_screen={
"deadzone": 0.5,
"events": [ ]
@ -1008,10 +1016,6 @@ about_pixelorama={
"deadzone": 0.5,
"events": [ ]
}
gradient_map={
"deadzone": 0.5,
"events": [ ]
}
left_paint_selection_tool={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":73,"physical_scancode":0,"unicode":0,"echo":false,"script":null)

View file

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

View file

@ -0,0 +1,27 @@
// https://godotshaders.com/shader/color-reduction-and-dither/
shader_type canvas_item;
uniform sampler2D selection;
uniform float colors : hint_range(1.0, 255.0) = 2.0;
uniform float dither : hint_range(0.0, 0.5) = 0.0;
void fragment()
{
vec4 color = texture(TEXTURE, UV);
vec4 selection_color = texture(selection, UV);
float a = floor(mod(UV.x / TEXTURE_PIXEL_SIZE.x, 2.0));
float b = floor(mod(UV.y / TEXTURE_PIXEL_SIZE.y, 2.0));
float c = mod(a + b, 2.0);
vec4 col;
col.r = (round(color.r * colors + dither) / colors) * c;
col.g = (round(color.g * colors + dither) / colors) * c;
col.b = (round(color.b * colors + dither) / colors) * c;
c = 1.0 - c;
col.r += (round(color.r * colors - dither) / colors) * c;
col.g += (round(color.g * colors - dither) / colors) * c;
col.b += (round(color.b * colors - dither) / colors) * c;
col.a = color.a;
vec4 output = mix(color.rgba, col, selection_color.a);
COLOR = output;
}

View file

@ -1,10 +1,11 @@
[gd_scene load_steps=13 format=2]
[gd_scene load_steps=14 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/Posterize.tscn" type="PackedScene" id=6]
[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]
@ -44,4 +45,6 @@ margin_bottom = 214.0
[node name="GradientMapDialog" parent="." instance=ExtResource( 5 )]
[node name="Posterize" parent="." instance=ExtResource( 6 )]
[node name="ShaderEffect" parent="." instance=ExtResource( 10 )]

View file

@ -0,0 +1,43 @@
extends ImageEffect
var shader: Shader = preload("res://src/Shaders/Posterize.gdshader")
var levels := 2.0
var dither := 0.0
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 commit_action(cel: Image, project: Project = Global.current_project) -> void:
var selection_tex := ImageTexture.new()
if selection_checkbox.pressed and project.has_selection:
selection_tex.create_from_image(project.selection_map, 0)
var params := {"colors": levels, "dither": dither, "selection": selection_tex}
if !confirmed:
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_LevelsSlider_value_changed(value: float) -> void:
levels = value - 1.0
update_preview()
func _on_DitherSlider_value_changed(value: float) -> void:
dither = value
update_preview()

View file

@ -0,0 +1,83 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/ImageEffectParent.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UI/Nodes/ValueSlider.gd" type="Script" id=2]
[ext_resource path="res://src/UI/Dialogs/ImageEffects/Posterize.gd" type="Script" id=3]
[node name="Posterize" instance=ExtResource( 1 )]
window_title = "Posterize"
script = ExtResource( 3 )
[node name="VBoxContainer" parent="." index="3"]
margin_bottom = 292.0
[node name="AspectRatioContainer" parent="VBoxContainer" index="0"]
margin_right = 278.0
[node name="Preview" parent="VBoxContainer/AspectRatioContainer" index="0"]
margin_left = 39.0
margin_right = 239.0
[node name="LevelsSlider" type="TextureProgress" parent="VBoxContainer" index="1"]
margin_top = 204.0
margin_right = 278.0
margin_bottom = 228.0
rect_min_size = Vector2( 0, 24 )
mouse_default_cursor_shape = 2
theme_type_variation = "ValueSlider"
min_value = 2.0
max_value = 256.0
step = 0.01
value = 3.0
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 2 )
prefix = "Posterize levels:"
snap_by_default = true
[node name="DitherSlider" type="TextureProgress" parent="VBoxContainer" index="2"]
margin_top = 232.0
margin_right = 278.0
margin_bottom = 256.0
rect_min_size = Vector2( 0, 24 )
mouse_default_cursor_shape = 2
theme_type_variation = "ValueSlider"
max_value = 0.5
step = 0.01
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 2 )
prefix = "Dither intensity:"
snap_step = 0.1
[node name="OptionsContainer" parent="VBoxContainer" index="3"]
margin_top = 260.0
margin_right = 278.0
margin_bottom = 284.0
[node name="AffectOptionButton" parent="VBoxContainer/OptionsContainer" index="1"]
margin_right = 278.0
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 ]
[node name="AnimationOptions" parent="VBoxContainer" index="4"]
visible = false
margin_right = 278.0
[node name="PanelContainer" parent="VBoxContainer/AnimationOptions" index="1"]
margin_right = 157.0
[node name="AnimateMenu" parent="VBoxContainer/AnimationOptions/PanelContainer" index="0"]
margin_right = 88.0
[node name="InitalButton" parent="VBoxContainer/AnimationOptions" index="2"]
margin_left = 161.0
margin_right = 278.0
[connection signal="value_changed" from="VBoxContainer/LevelsSlider" to="." method="_on_LevelsSlider_value_changed"]
[connection signal="value_changed" from="VBoxContainer/DitherSlider" to="." method="_on_DitherSlider_value_changed"]

View file

@ -285,6 +285,7 @@ func _setup_image_menu() -> void:
"Adjust Hue/Saturation/Value",
"Gradient",
"Gradient Map",
"Posterize",
# "Shader"
]
var image_menu: PopupMenu = image_menu_button.get_popup()
@ -680,6 +681,9 @@ func image_menu_id_pressed(id: int) -> void:
Global.ImageMenu.GRADIENT_MAP:
_popup_dialog(Global.control.get_node("Dialogs/ImageEffects/GradientMapDialog"))
Global.ImageMenu.POSTERIZE:
_popup_dialog(Global.control.get_node("Dialogs/ImageEffects/Posterize"))
# Global.ImageMenu.SHADER:
# _popup_dialog(Global.control.get_node("Dialogs/ImageEffects/ShaderEffect"))