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

Shader based invert, desaturate and HSV dialogs (#475)

* Shader based invert, desaturate and HSV dialogs

* Bug fixes
This commit is contained in:
Darshan Phaldesai 2021-04-21 18:01:33 +05:30 committed by GitHub
parent a712f822d7
commit dca76afe17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 404 additions and 73 deletions

View file

@ -99,6 +99,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://src/Classes/Project.gd"
}, {
"base": "Reference",
"class": "ShaderImageEffect",
"language": "GDScript",
"path": "res://src/Classes/ShaderImageEffect.gd"
}, {
"base": "Guide",
"class": "SymmetryGuide",
"language": "GDScript",
@ -123,6 +128,7 @@ _global_script_class_icons={
"PaletteSwatch": "",
"Patterns": "",
"Project": "",
"ShaderImageEffect": "",
"SymmetryGuide": ""
}

View file

@ -359,45 +359,47 @@ func general_undo_centralize() -> void:
project.undo_redo.commit_action()
func invert_image_colors(image : Image, affect_selection : bool, project : Project, red := true, green := true, blue := true, alpha := false) -> void:
image.lock()
for x in project.size.x:
for y in project.size.y:
var pos := Vector2(x, y)
if affect_selection and !project.can_pixel_get_drawn(pos):
continue
var px_color := image.get_pixelv(pos)
# Manually invert each color channel
if red:
px_color.r = 1.0 - px_color.r
if green:
px_color.g = 1.0 - px_color.g
if blue:
px_color.b = 1.0 - px_color.b
if alpha:
px_color.a = 1.0 - px_color.a
image.set_pixelv(pos, px_color)
# TO BE REMOVED
# func invert_image_colors(image : Image, affect_selection : bool, project : Project, red := true, green := true, blue := true, alpha := false) -> void:
# image.lock()
# for x in project.size.x:
# for y in project.size.y:
# var pos := Vector2(x, y)
# if affect_selection and !project.can_pixel_get_drawn(pos):
# continue
# var px_color := image.get_pixelv(pos)
# # Manually invert each color channel
# if red:
# px_color.r = 1.0 - px_color.r
# if green:
# px_color.g = 1.0 - px_color.g
# if blue:
# px_color.b = 1.0 - px_color.b
# if alpha:
# px_color.a = 1.0 - px_color.a
# image.set_pixelv(pos, px_color)
func desaturate_image(image : Image, affect_selection : bool, project : Project, red := true, green := true, blue := true, alpha := false) -> void:
image.lock()
for x in project.size.x:
for y in project.size.y:
var pos := Vector2(x, y)
if affect_selection and !project.can_pixel_get_drawn(pos):
continue
var px_color := image.get_pixelv(pos)
var gray = px_color.v
if red:
px_color.r = gray
if green:
px_color.g = gray
if blue:
px_color.b = gray
if alpha:
px_color.a = gray
# TO BE REMOVED
# func desaturate_image(image : Image, affect_selection : bool, project : Project, red := true, green := true, blue := true, alpha := false) -> void:
# image.lock()
# for x in project.size.x:
# for y in project.size.y:
# var pos := Vector2(x, y)
# if affect_selection and !project.can_pixel_get_drawn(pos):
# continue
# var px_color := image.get_pixelv(pos)
# var gray = px_color.v
# if red:
# px_color.r = gray
# if green:
# px_color.g = gray
# if blue:
# px_color.b = gray
# if alpha:
# px_color.a = gray
image.set_pixelv(pos, px_color)
# image.set_pixelv(pos, px_color)
func generate_outline(image : Image, affect_selection : bool, project : Project, outline_color : Color, thickness : int, diagonal : bool, inside_image : bool) -> void:
@ -539,41 +541,42 @@ func generate_outline(image : Image, affect_selection : bool, project : Project,
image.copy_from(new_image)
func adjust_hsv(img: Image, delta_h : float, delta_s : float, delta_v : float, affect_selection : bool, project : Project) -> void:
img.lock()
for x in project.size.x:
for y in project.size.y:
var pos := Vector2(x, y)
if affect_selection and !project.can_pixel_get_drawn(pos):
continue
var c : Color = img.get_pixelv(pos)
# Hue
var hue = range_lerp(c.h,0,1,-180,180)
hue = hue + delta_h
# TO BE REMOVED
# func adjust_hsv(img: Image, delta_h : float, delta_s : float, delta_v : float, affect_selection : bool, project : Project) -> void:
# img.lock()
# for x in project.size.x:
# for y in project.size.y:
# var pos := Vector2(x, y)
# if affect_selection and !project.can_pixel_get_drawn(pos):
# continue
# var c : Color = img.get_pixelv(pos)
# # Hue
# var hue = range_lerp(c.h,0,1,-180,180)
# hue = hue + delta_h
while(hue >= 180):
hue -= 360
while(hue < -180):
hue += 360
# while(hue >= 180):
# hue -= 360
# while(hue < -180):
# hue += 360
# Saturation
var sat = c.s
if delta_s > 0:
sat = range_lerp(delta_s,0,100,c.s,1)
elif delta_s < 0:
sat = range_lerp(delta_s,-100,0,0,c.s)
# # Saturation
# var sat = c.s
# if delta_s > 0:
# sat = range_lerp(delta_s,0,100,c.s,1)
# elif delta_s < 0:
# sat = range_lerp(delta_s,-100,0,0,c.s)
# Value
var val = c.v
if delta_v > 0:
val = range_lerp(delta_v,0,100,c.v,1)
elif delta_v < 0:
val = range_lerp(delta_v,-100,0,0,c.v)
# # Value
# var val = c.v
# if delta_v > 0:
# val = range_lerp(delta_v,0,100,c.v,1)
# elif delta_v < 0:
# val = range_lerp(delta_v,-100,0,0,c.v)
c.h = range_lerp(hue,-180,180,0,1)
c.s = sat
c.v = val
img.set_pixelv(pos, c)
# c.h = range_lerp(hue,-180,180,0,1)
# c.s = sat
# c.v = val
# img.set_pixelv(pos, c)
func generate_gradient(image : Image, colors : Array, steps : int, direction : int, affect_selection : bool, project : Project) -> void:

View file

@ -639,12 +639,15 @@ func resize_bitmap(bitmap : BitMap, new_size : Vector2) -> BitMap:
# Unexposed BitMap class function - https://github.com/godotengine/godot/blob/master/scene/resources/bit_map.cpp#L622
func bitmap_to_image(bitmap : BitMap) -> Image:
func bitmap_to_image(bitmap : BitMap, square := true) -> Image:
var image := Image.new()
var width := bitmap.get_size().x
var height := bitmap.get_size().y
var square_size = max(width, height)
image.create(square_size, square_size, false, Image.FORMAT_LA8)
if square:
var square_size = max(width, height)
image.create(square_size, square_size, false, Image.FORMAT_LA8)
else:
image.create(width, height, false, Image.FORMAT_LA8)
image.lock()
for x in width:
for y in height:

View file

@ -0,0 +1,45 @@
class_name ShaderImageEffect extends Reference
# Helper class to generate image effects using shaders
signal done
func generate_image(_img : Image,_shaderpath: String, _params : Dictionary , size : Vector2 = Global.current_project.size):
var shader = load(_shaderpath)
_img.unlock()
var viewport_texture := Image.new()
var vp = VisualServer.viewport_create()
var canvas = VisualServer.canvas_create()
VisualServer.viewport_attach_canvas(vp, canvas)
VisualServer.viewport_set_size(vp, size.x, size.y)
VisualServer.viewport_set_disable_3d(vp, true)
VisualServer.viewport_set_usage(vp, VisualServer.VIEWPORT_USAGE_2D)
VisualServer.viewport_set_hdr(vp, true)
VisualServer.viewport_set_active(vp, true)
VisualServer.viewport_set_transparent_background(vp, true)
var ci_rid = VisualServer.canvas_item_create()
VisualServer.viewport_set_canvas_transform(vp, canvas, Transform())
VisualServer.canvas_item_set_parent(ci_rid, canvas)
var texture = ImageTexture.new()
texture.create_from_image(_img)
VisualServer.canvas_item_add_texture_rect(ci_rid, Rect2(Vector2(0, 0), size), texture)
var mat_rid = VisualServer.material_create()
VisualServer.material_set_shader(mat_rid, shader.get_rid())
VisualServer.canvas_item_set_material(ci_rid, mat_rid)
for key in _params:
VisualServer.material_set_param(mat_rid, key, _params[key])
VisualServer.viewport_set_update_mode(vp, VisualServer.VIEWPORT_UPDATE_ONCE)
VisualServer.viewport_set_vflip(vp, true)
VisualServer.force_draw(false)
viewport_texture = VisualServer.texture_get_data(VisualServer.viewport_get_texture(vp))
VisualServer.free_rid(vp)
VisualServer.free_rid(canvas)
VisualServer.free_rid(ci_rid)
VisualServer.free_rid(mat_rid)
viewport_texture.convert(Image.FORMAT_RGBA8)
#Global.canvas.handle_undo("Draw")
_img.copy_from(viewport_texture)
#Global.canvas.handle_redo("Draw")
_img.lock()
emit_signal("done")

View file

@ -0,0 +1,62 @@
shader_type canvas_item;
render_mode unshaded;
uniform bool red;
uniform bool blue;
uniform bool green;
uniform bool alpha;
uniform sampler2D selection;
uniform bool affect_selection;
uniform bool has_selection;
vec3 rgb2hsb(vec3 c){
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz),
vec4(c.gb, K.xy),
step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r),
vec4(c.r, p.yzx),
step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),
d / (q.x + e),
q.x);
}
vec3 hsb2rgb(vec3 c){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
6.0)-3.0)-1.0,
0.0,
1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb);
return c.z * mix(vec3(1.0), rgb, c.y);
}
void fragment() {
// Get color from the sprite texture at the current pixel we are rendering
vec4 original_color = texture(TEXTURE, UV);
vec4 selection_color = texture(selection, UV);
vec3 col = original_color.rgb;
vec3 hsb = rgb2hsb(col);
float gray = hsb.z;
if (red)
col.x = gray;
if (green)
col.y = gray;
if (blue)
col.z = gray;
vec3 output;
if(affect_selection && has_selection)
output = mix(original_color.rgb, col, selection_color.a);
else
output = col;
if (alpha)
COLOR = vec4(output.rgb, gray);
else
COLOR = vec4(output.rgb, original_color.a);
}

71
src/Shaders/HSV.shader Normal file
View file

@ -0,0 +1,71 @@
shader_type canvas_item;
render_mode unshaded;
uniform float hue_shift_amount : hint_range(-1, 1);
uniform float sat_shift_amount : hint_range(-1, 1);
uniform float val_shift_amount : hint_range(-1, 1);
uniform sampler2D selection;
uniform bool affect_selection;
uniform bool has_selection;
vec3 rgb2hsb(vec3 c){
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz),
vec4(c.gb, K.xy),
step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r),
vec4(c.r, p.yzx),
step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),
d / (q.x + e),
q.x);
}
vec3 hsb2rgb(vec3 c){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
6.0)-3.0)-1.0,
0.0,
1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb);
return c.z * mix(vec3(1.0), rgb, c.y);
}
void fragment() {
// Get color from the sprite texture at the current pixel we are rendering
vec4 original_color = texture(TEXTURE, UV);
vec4 selection_color = texture(selection, UV);
vec3 col = original_color.rgb;
vec3 hsb = rgb2hsb(col);
// If not greyscale
if(col[0] != col[1] || col[1] != col[2]) {
// Shift the color by shift_amount, but rolling over the value goes over 1
hsb.x = mod(hsb.x + hue_shift_amount, 1.0);
}
if(sat_shift_amount > 0.0) {
hsb.y = mix(hsb.y, 1 , sat_shift_amount);
}
else if (sat_shift_amount < 0.0) {
hsb.y = mix(0, hsb.y , 1f - abs(sat_shift_amount));
}
if(val_shift_amount > 0.0) {
hsb.z = mix(hsb.z, 1 , val_shift_amount);
}
else if (val_shift_amount < 0.0) {
hsb.z = mix(0, hsb.z , 1f - abs(val_shift_amount));
}
col = hsb2rgb(hsb);
vec3 output;
if(affect_selection && has_selection)
output = mix(original_color.rgb, col, selection_color.a);
else
output = col;
COLOR = vec4(output.rgb, original_color.a);
}

34
src/Shaders/Invert.shader Normal file
View file

@ -0,0 +1,34 @@
shader_type canvas_item;
render_mode unshaded;
uniform bool red;
uniform bool blue;
uniform bool green;
uniform bool alpha;
uniform sampler2D selection;
uniform bool affect_selection;
uniform bool has_selection;
void fragment() {
// Get color from the sprite texture at the current pixel we are rendering
vec4 original_color = texture(TEXTURE, UV);
vec4 selection_color = texture(selection, UV);
vec4 col = original_color;
if (red)
col.r = 1f - col.r;
if (green)
col.g = 1f - col.g;
if (blue)
col.b = 1f - col.b;
if (alpha)
col.a = 1f - col.a;
vec4 output;
if(affect_selection && has_selection)
output = mix(original_color.rgba, col, selection_color.a);
else
output = col;
COLOR = output;
}

View file

@ -6,6 +6,15 @@ var green := true
var blue := true
var alpha := false
var shaderPath : String = "res://src/Shaders/Desaturate.shader"
var confirmed: bool = false
func _about_to_show():
var sm : ShaderMaterial = ShaderMaterial.new()
sm.shader = load(shaderPath)
preview.set_material(sm)
._about_to_show()
func set_nodes() -> void:
preview = $VBoxContainer/Preview
@ -13,8 +22,36 @@ func set_nodes() -> void:
affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton
func _confirmed() -> void:
confirmed = true
._confirmed()
func commit_action(_cel : Image, _project : Project = Global.current_project) -> void:
DrawingAlgos.desaturate_image(_cel, selection_checkbox.pressed, _project, red, green, blue, alpha)
var selection = _project.bitmap_to_image(_project.selection_bitmap, false)
var selection_tex = ImageTexture.new()
selection_tex.create_from_image(selection)
if !confirmed:
preview.material.set_shader_param("red", red)
preview.material.set_shader_param("blue", blue)
preview.material.set_shader_param("green", green)
preview.material.set_shader_param("alpha", alpha)
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 = {
"red": red,
"blue": blue,
"green": green,
"alpha": alpha,
"selection": selection_tex,
"affect_selection": selection_checkbox.pressed,
"has_selection": _project.has_selection
}
var gen: ShaderImageEffect = ShaderImageEffect.new()
gen.generate_image(_cel, shaderPath, params, _project.size)
yield(gen, "done")
func _on_RButton_toggled(button_pressed : bool) -> void:

View file

@ -9,6 +9,16 @@ onready var hue_spinbox = $VBoxContainer/HBoxContainer/TextBoxes/Hue
onready var sat_spinbox = $VBoxContainer/HBoxContainer/TextBoxes/Saturation
onready var val_spinbox = $VBoxContainer/HBoxContainer/TextBoxes/Value
var shaderPath : String = "res://src/Shaders/HSV.shader"
var confirmed: bool = false
func _about_to_show():
reset()
var sm : ShaderMaterial = ShaderMaterial.new()
sm.shader = load(shaderPath)
preview.set_material(sm)
._about_to_show()
func set_nodes() -> void:
preview = $VBoxContainer/Preview
@ -17,12 +27,35 @@ func set_nodes() -> void:
func _confirmed() -> void:
confirmed = true
._confirmed()
reset()
func commit_action(_cel : Image, _project : Project = Global.current_project) -> void:
DrawingAlgos.adjust_hsv(_cel, hue_slider.value, sat_slider.value, val_slider.value, selection_checkbox.pressed, _project)
var selection = _project.bitmap_to_image(_project.selection_bitmap, false)
var selection_tex = ImageTexture.new()
selection_tex.create_from_image(selection)
if !confirmed:
preview.material.set_shader_param("hue_shift_amount", hue_slider.value /360)
preview.material.set_shader_param("sat_shift_amount", sat_slider.value /100)
preview.material.set_shader_param("val_shift_amount", val_slider.value /100)
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 = {
"hue_shift_amount": hue_slider.value /360,
"sat_shift_amount": sat_slider.value /100,
"val_shift_amount": val_slider.value /100,
"selection": selection_tex,
"affect_selection": selection_checkbox.pressed,
"has_selection": _project.has_selection
}
var gen: ShaderImageEffect = ShaderImageEffect.new()
gen.generate_image(_cel, shaderPath, params, _project.size)
yield(gen, "done")
func reset() -> void:
@ -34,6 +67,7 @@ func reset() -> void:
sat_spinbox.value = 0
val_spinbox.value = 0
reconnect_signals()
confirmed = false
func disconnect_signals() -> void:

View file

@ -6,6 +6,14 @@ var green := true
var blue := true
var alpha := false
var shaderPath : String = "res://src/Shaders/Invert.shader"
var confirmed: bool = false
func _about_to_show():
var sm : ShaderMaterial = ShaderMaterial.new()
sm.shader = load(shaderPath)
preview.set_material(sm)
._about_to_show()
func set_nodes() -> void:
preview = $VBoxContainer/Preview
@ -13,8 +21,36 @@ func set_nodes() -> void:
affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton
func _confirmed() -> void:
confirmed = true
._confirmed()
func commit_action(_cel : Image, _project : Project = Global.current_project) -> void:
DrawingAlgos.invert_image_colors(_cel, selection_checkbox.pressed, _project, red, green, blue, alpha)
var selection = _project.bitmap_to_image(_project.selection_bitmap, false)
var selection_tex = ImageTexture.new()
selection_tex.create_from_image(selection)
if !confirmed:
preview.material.set_shader_param("red", red)
preview.material.set_shader_param("blue", blue)
preview.material.set_shader_param("green", green)
preview.material.set_shader_param("alpha", alpha)
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 = {
"red": red,
"blue": blue,
"green": green,
"alpha": alpha,
"selection": selection_tex,
"affect_selection": selection_checkbox.pressed,
"has_selection": _project.has_selection
}
var gen: ShaderImageEffect = ShaderImageEffect.new()
gen.generate_image(_cel, shaderPath, params, _project.size)
yield(gen, "done")
func _on_RButton_toggled(button_pressed : bool) -> void: