From cf926942ac062b5fe0ae53c7eec6f26bec634aeb Mon Sep 17 00:00:00 2001 From: Emmanouil Papadeas <35376950+OverloadedOrama@users.noreply.github.com> Date: Mon, 2 May 2022 16:12:00 +0300 Subject: [PATCH] Shader-based gradients (#677) * Add 6 shader-based gradient types * Shaders now respect selection * Fix step gradient * Remove comments * Disable step and dithering shaders in Web version * Fixed a weird bug with dithering shaders, selection and GLES2 Having a selection, applying a dithering gradient, removing the selection and then going to the gradient dialog again causes the dithering shaders to "remember" the previous selection, even if there is no selection currently. This only happens with the two dithering shaders and only with the GLES2 renderer. Removing `uniform sampler2D dither_texture;` from the shader code seems to fix the issue, but that's obviously isn't what we want so a "proper" fix is included in this commit. * Format & lint * Removed old gradient code * Change how centers work on radial step and dithering * Made angle, center and radius option a bit more clear * Rename bayer-matrices directory to dither-matrices * Use DitherMatrix class * Create dithering types programmatically * Remove unneeded code in shaders * Rewrite the step shader without a for loop More optimized and works on the Web version with GLES2 * Rewrite radial step and dithering shaders without for loop Now all shaders work on the Web version and have been optimized. * Fix Linear & Radial size range and remove some unneeded lines * Added size uniform to Radial Step and Radial Dither * Swap colors in the Linear gradient * Make size a percentage * Make the preview look the same as the result Didn't change the dithering shaders because they seemed to give different results. * Remove ratio uniform and divide uvs by the radius instead This makes more sense because the smaller the number, the smaller the radius. * Fix linear gradient * Change Position to percentage and "Size" to "Transition size" * Mix gradients with original color, if the gradient colors have transparency --- assets/dither-matrices/bayer16.png | Bin 0 -> 805 bytes assets/dither-matrices/bayer16.png.import | 35 +++ assets/dither-matrices/bayer2.png | Bin 0 -> 79 bytes assets/dither-matrices/bayer2.png.import | 35 +++ assets/dither-matrices/bayer4.png | Bin 0 -> 120 bytes assets/dither-matrices/bayer4.png.import | 35 +++ assets/dither-matrices/bayer8.png | Bin 0 -> 265 bytes assets/dither-matrices/bayer8.png.import | 35 +++ src/Autoload/DrawingAlgos.gd | 57 ---- src/Shaders/Gradients/Dithering.gdshader | 38 +++ src/Shaders/Gradients/Linear.gdshader | 26 ++ src/Shaders/Gradients/Radial.gdshader | 23 ++ .../Gradients/RadialDithering.gdshader | 39 +++ src/Shaders/Gradients/RadialStep.gdshader | 36 +++ src/Shaders/Gradients/Step.gdshader | 35 +++ src/UI/Dialogs/ImageEffects/GradientDialog.gd | 138 +++++++-- .../Dialogs/ImageEffects/GradientDialog.tscn | 288 ++++++++++++++---- 17 files changed, 683 insertions(+), 137 deletions(-) create mode 100644 assets/dither-matrices/bayer16.png create mode 100644 assets/dither-matrices/bayer16.png.import create mode 100644 assets/dither-matrices/bayer2.png create mode 100644 assets/dither-matrices/bayer2.png.import create mode 100644 assets/dither-matrices/bayer4.png create mode 100644 assets/dither-matrices/bayer4.png.import create mode 100644 assets/dither-matrices/bayer8.png create mode 100644 assets/dither-matrices/bayer8.png.import create mode 100644 src/Shaders/Gradients/Dithering.gdshader create mode 100644 src/Shaders/Gradients/Linear.gdshader create mode 100644 src/Shaders/Gradients/Radial.gdshader create mode 100644 src/Shaders/Gradients/RadialDithering.gdshader create mode 100644 src/Shaders/Gradients/RadialStep.gdshader create mode 100644 src/Shaders/Gradients/Step.gdshader diff --git a/assets/dither-matrices/bayer16.png b/assets/dither-matrices/bayer16.png new file mode 100644 index 0000000000000000000000000000000000000000..0bdd8949df3a5b4a5f76afd5c88352e4550ab746 GIT binary patch literal 805 zcmV+=1KRwFP)$5%p0Fa)ZE-NcrU0uz`$CsIzDK9TyTU$$`(K0eJgqsW zU!Q@2!QS4UySw|?*qE7_+1c5dzrX*$z<`mF(ZRuir>Ez{#Ds-~#l^)%U|=9nR8*|5 zuNM#y$j;8z(9mdaZx<62D=scpR#t9oY!ng_%FWHy($ebe?39p@C@n1ocXxN;;o)<0 zbGEj&Z*On$@$qYGYfes14-XHKk&z1v3-eOG{T)R;sJ3x3;#5ii#E% z7J{#@ujJ(9t*tGJqMn|fqNAgimX_FT_Rr5xYHI4v&W@|A>&weaY;5ex%8H|-BaOje zFc=IB!!QhkVHk#Ck|arzq|@niIvqt(6h$Ejf*^<>2!bHM;NT#NqKAivUS3|4larQ~ zmY0{8K|w+N{r!f9hWq>b9v&X!%huPU#8ylP3+uN|Pu+h;`48u-OPyPJ-rl+T^ ztgNoDuR}sYhKGkuOiYfCk9~Z6fWE$dZ*Q-Rj7(Kk6^q4cYiomHxTvT|S67$E<4H+L zRa8`{sj0QJw1|j^6ciMI-{0S~w6xvbT{kzk*Vos$xVY8TRgxsXzrRybQnt6Zxm@n^ j^K(p0%<}TGgM-5#vlnRAZYc}X00000NkvXXu0mjf6|;g9i^15)v2;3=B4G*dQPvFk!+3h7TV;6ciL(xNspLAb{_ZSYv_g Qn^2HmPgg&ebxsLQ00?p=1^@s6 literal 0 HcmV?d00001 diff --git a/assets/dither-matrices/bayer4.png.import b/assets/dither-matrices/bayer4.png.import new file mode 100644 index 000000000..5a072bef8 --- /dev/null +++ b/assets/dither-matrices/bayer4.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/bayer4.png-2008f73a4dd6b2f9f4ba6aac825a7b9b.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/dither-matrices/bayer4.png" +dest_files=[ "res://.import/bayer4.png-2008f73a4dd6b2f9f4ba6aac825a7b9b.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=1 +flags/filter=false +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=false +svg/scale=1.0 diff --git a/assets/dither-matrices/bayer8.png b/assets/dither-matrices/bayer8.png new file mode 100644 index 0000000000000000000000000000000000000000..45c2072fd738102868dc8f3c5cd2e4066430e7e0 GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1SGw4HSYka3!W~HAr*624+?H+br4}VP;JM` z?I@trk|@C9D8K?@f@ntpuz&;`m<{Cavu966;t2Cn{uY5b}cH2J^r~SP~?M+zW~eWO*5ALtc@3Oy}j+` zw!iI$$0UNiG?#KHwg>OLb1?-Y$}v x{@n6U-F}O{@3qUP?0YZW_Hfs_X{mpi_rH%;e(>t~N}%5uJYD@<);T3K0RT*%VAlWu literal 0 HcmV?d00001 diff --git a/assets/dither-matrices/bayer8.png.import b/assets/dither-matrices/bayer8.png.import new file mode 100644 index 000000000..483cea2ad --- /dev/null +++ b/assets/dither-matrices/bayer8.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/bayer8.png-2c0f9ff189b3d219f822ce84a94f5abd.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/dither-matrices/bayer8.png" +dest_files=[ "res://.import/bayer8.png-2c0f9ff189b3d219f822ce84a94f5abd.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=1 +flags/filter=false +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=false +svg/scale=1.0 diff --git a/src/Autoload/DrawingAlgos.gd b/src/Autoload/DrawingAlgos.gd index de9a3b72f..bcd41ede4 100644 --- a/src/Autoload/DrawingAlgos.gd +++ b/src/Autoload/DrawingAlgos.gd @@ -650,60 +650,3 @@ func generate_outline( image.unlock() new_image.unlock() image.copy_from(new_image) - - -func generate_gradient( - image: Image, - colors: Array, - steps: int, - direction: int, - affect_selection: bool, - project: Project -) -> void: - if colors.size() < 2: - return - - var t = 1.0 / (steps - 1) - for i in range(1, steps - 1): - var color: Color - color = colors[-1].linear_interpolate(colors[0], t * i) - colors.insert(1, color) - - if direction == GradientDirection.BOTTOM or direction == GradientDirection.RIGHT: - colors.invert() - - var draw_rectangle := Rect2() - var selection := affect_selection and project.has_selection - if selection: - draw_rectangle = project.get_selection_rectangle() - else: - draw_rectangle = Rect2(Vector2.ZERO, project.size) - var size := draw_rectangle.size - image.lock() - var gradient_size - - if direction == GradientDirection.TOP or direction == GradientDirection.BOTTOM: - gradient_size = size.y / steps - for i in steps: - for xx in size.x: - var start = i * gradient_size - var end = (i + 1) * gradient_size - for yy in range(start, end): - var pos: Vector2 = Vector2(xx, yy) + draw_rectangle.position - if selection and !project.selection_bitmap.get_bit(pos): - continue - image.set_pixelv(pos, colors[i]) - - else: - gradient_size = size.x / steps - for i in steps: - for yy in size.y: - var start = i * gradient_size - var end = (i + 1) * gradient_size - for xx in range(start, end): - var pos: Vector2 = Vector2(xx, yy) + draw_rectangle.position - if selection and !project.selection_bitmap.get_bit(pos): - continue - image.set_pixelv(pos, colors[i]) - - image.unlock() diff --git a/src/Shaders/Gradients/Dithering.gdshader b/src/Shaders/Gradients/Dithering.gdshader new file mode 100644 index 000000000..f7d5a2636 --- /dev/null +++ b/src/Shaders/Gradients/Dithering.gdshader @@ -0,0 +1,38 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D dither_texture; +uniform sampler2D selection; +uniform vec4 first_color : hint_color = vec4(1.0); +uniform vec4 second_color : hint_color = vec4(0.0, 0.0, 0.0, 1.0); +uniform vec2 image_size = vec2(64.0); +uniform float position : hint_range(-0.5, 0.5) = 0.0; +uniform float size : hint_range(0.01, 2.0) = 1.0; +uniform float angle : hint_range(0.0, 360.0) = 0.0; +uniform int dither_steps : hint_range(2, 257) = 5; +uniform int pixel_size : hint_range(2, 16) = 2; + + +void fragment() { + vec4 original_color = texture(TEXTURE, UV); + vec4 selection_color = texture(selection, UV); + + float pivot = position / size; + vec2 uv = UV - 0.5; + float angle_cos = cos(radians(angle)); + float angle_sin = sin(radians(angle)); + float rotated = uv.x * angle_cos - uv.y * angle_sin; + rotated /= abs(angle_cos) + abs(angle_sin); + rotated /= size; + rotated -= pivot - 0.5; + vec4 dither_value = texture(dither_texture, uv * (image_size / float(pixel_size))); + + float step_v = 1.0 / float(dither_steps); + float step_v_m = 1.0 / float(dither_steps - 1); + float rotated_stepped = floor(rotated / step_v) * step_v_m; // Similar to GDScript's stepify + float dithered = step(rotated_stepped, dither_value.r); + vec4 output = first_color * dithered + second_color * (1.0 - dithered); + output = mix(original_color, output, output.a); + + COLOR = mix(original_color, output, selection_color.a); +} diff --git a/src/Shaders/Gradients/Linear.gdshader b/src/Shaders/Gradients/Linear.gdshader new file mode 100644 index 000000000..e34ff0f91 --- /dev/null +++ b/src/Shaders/Gradients/Linear.gdshader @@ -0,0 +1,26 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D selection; +uniform vec4 first_color : hint_color = vec4(1.0); +uniform vec4 second_color : hint_color = vec4(0.0, 0.0, 0.0, 1.0); +uniform float position : hint_range(-0.5, 0.5) = 0.0; +uniform float size : hint_range(0.01, 2.0) = 1.0; +uniform float angle : hint_range(0.0, 360.0) = 0.0; + +void fragment() { + vec4 original_color = texture(TEXTURE, UV); + vec4 selection_color = texture(selection, UV); + + vec2 tex_size = 1.0 / TEXTURE_PIXEL_SIZE; + vec2 uv = floor(UV * tex_size) / (tex_size - 1.0); + uv -= 0.5; + float rotated = uv.x * cos(radians(angle)) - uv.y * sin(radians(angle)); + float fixed_position = position - 0.5; + float fixed_size = (size / 2.0) + 0.5; + + float pos = smoothstep((1.0 - fixed_size) + fixed_position, fixed_size + fixed_position, rotated); + vec4 output = mix(first_color, second_color, pos); + output = mix(original_color, output, output.a); + COLOR = mix(original_color, output, selection_color.a); +} diff --git a/src/Shaders/Gradients/Radial.gdshader b/src/Shaders/Gradients/Radial.gdshader new file mode 100644 index 000000000..47e439cb9 --- /dev/null +++ b/src/Shaders/Gradients/Radial.gdshader @@ -0,0 +1,23 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D selection; +uniform vec4 first_color : hint_color = vec4(1.0); +uniform vec4 second_color : hint_color = vec4(0.0, 0.0, 0.0, 1.0); +uniform vec2 center = vec2(0.5); +uniform vec2 radius = vec2(1.0); +uniform float size : hint_range(0.01, 2.0) = 1.0; + +void fragment() { + vec4 original_color = texture(TEXTURE, UV); + vec4 selection_color = texture(selection, UV); + + vec2 tex_size = 1.0 / TEXTURE_PIXEL_SIZE; + vec2 uv = floor(UV * tex_size) / (tex_size - 1.0); + uv /= radius; + float fixed_size = (size / 2.0) + 0.5; + float smooth_dist = smoothstep(1.0 - fixed_size, fixed_size, distance(uv, center / radius)); + vec4 output = second_color * smooth_dist + (1.0 - smooth_dist) * first_color; + output = mix(original_color, output, output.a); + COLOR = mix(original_color, output, selection_color.a); +} diff --git a/src/Shaders/Gradients/RadialDithering.gdshader b/src/Shaders/Gradients/RadialDithering.gdshader new file mode 100644 index 000000000..7cbdaf8fb --- /dev/null +++ b/src/Shaders/Gradients/RadialDithering.gdshader @@ -0,0 +1,39 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D dither_texture; +uniform sampler2D selection; +uniform vec4 first_color : hint_color = vec4(1.0); +uniform vec4 second_color : hint_color = vec4(0.0, 0.0, 0.0, 1.0); +uniform vec2 image_size = vec2(64.0); +uniform vec2 center = vec2(0.5); +uniform vec2 radius = vec2(1.0); +uniform float size : hint_range(0.01, 2) = 1.0; +uniform int dither_steps : hint_range(2, 100) = 5; +uniform int pixel_size : hint_range(2, 16) = 2; + +void fragment() { + vec4 original_color = texture(TEXTURE, UV); + vec4 selection_color = texture(selection, UV); + + vec2 uv = UV * 2.0 - 1.0; + uv -= (center * 2.0) - vec2(1.0); + uv /= radius; + vec4 dither_value = texture(dither_texture, UV * (image_size / float(pixel_size))); + + float polar_uv = length(uv); + float step_v = 1.0 / float(dither_steps); + float step_v_m = 1.0 / float(dither_steps - 1); + float uv_stepped = floor(polar_uv / step_v) * step_v_m; // Similar to GDScript's stepify + if (uv_stepped > 0.0) { + // Size should not affect the center, only the steps + polar_uv /= size; + uv_stepped = floor(polar_uv / step_v) * step_v_m; + uv_stepped += step_v_m * step(1.0, (1.0 - uv_stepped)); + } + float dithered = step(uv_stepped, dither_value.r); + vec4 output = first_color * dithered + second_color * (1.0 - dithered); + output = mix(original_color, output, output.a); + + COLOR = mix(original_color, output, selection_color.a); +} diff --git a/src/Shaders/Gradients/RadialStep.gdshader b/src/Shaders/Gradients/RadialStep.gdshader new file mode 100644 index 000000000..a717dd981 --- /dev/null +++ b/src/Shaders/Gradients/RadialStep.gdshader @@ -0,0 +1,36 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D selection; +uniform vec4 first_color : hint_color = vec4(1.0); +uniform vec4 second_color : hint_color = vec4(0.0, 0.0, 0.0, 1.0); +uniform vec2 center = vec2(0.5); +uniform vec2 radius = vec2(1.0); +uniform float size : hint_range(0.01, 2) = 1.0; +uniform int steps : hint_range(2, 257) = 2; + +void fragment() { + vec4 original_color = texture(TEXTURE, UV); + vec4 selection_color = texture(selection, UV); + + vec2 tex_size = 1.0 / TEXTURE_PIXEL_SIZE; + vec2 uv = floor(UV * tex_size) / (tex_size - 1.0); + uv = uv * 2.0 - 1.0; + uv -= (center * 2.0) - vec2(1.0); + uv /= radius; + float polar_uv = length(uv); + float step_v = 1.0 / float(steps); + float step_v_m = 1.0 / float(steps - 1); + float uv_stepped = floor(polar_uv / step_v) * step_v_m; // Similar to GDScript's stepify + if (uv_stepped > 0.0) { + // Size should not affect the center, only the steps + polar_uv /= size; + uv_stepped = floor(polar_uv / step_v) * step_v_m; + uv_stepped += step_v_m * step(1.0, (1.0 - uv_stepped)); + } + uv_stepped = clamp(uv_stepped, 0.0, 1.0); + vec4 output = mix(first_color, second_color, uv_stepped); + output = mix(original_color, output, output.a); + + COLOR = mix(original_color, output, selection_color.a); +} diff --git a/src/Shaders/Gradients/Step.gdshader b/src/Shaders/Gradients/Step.gdshader new file mode 100644 index 000000000..5ce691e7c --- /dev/null +++ b/src/Shaders/Gradients/Step.gdshader @@ -0,0 +1,35 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D selection; +uniform vec4 first_color : hint_color = vec4(1.0); +uniform vec4 second_color : hint_color = vec4(0.0, 0.0, 0.0, 1.0); +uniform float position : hint_range(-0.5, 0.5) = 0.0; +uniform float size : hint_range(0.01, 2.0) = 1.0; +uniform float angle : hint_range(0.0, 360.0) = 0.0; +uniform int steps : hint_range(2, 100) = 2; + + +void fragment() { + vec4 original_color = texture(TEXTURE, UV); + vec4 selection_color = texture(selection, UV); + + float pivot = position / size; + vec2 tex_size = 1.0 / TEXTURE_PIXEL_SIZE; + vec2 uv = floor(UV * tex_size) / (tex_size - 1.0); + uv -= 0.5; + float angle_cos = cos(radians(angle)); + float angle_sin = sin(radians(angle)); + float rotated = uv.x * angle_cos - uv.y * angle_sin; + rotated /= abs(angle_cos) + abs(angle_sin); + rotated /= size; + rotated -= pivot - 0.5; + float step_v = 1.0 / float(steps); + float step_v_m = 1.0 / float(steps - 1); + float rotated_stepped = floor(rotated / step_v) * step_v_m; // Similar to GDScript's stepify + rotated_stepped = clamp(rotated_stepped, 0.0, 1.0); + vec4 output = mix(first_color, second_color, rotated_stepped); + output = mix(original_color, output, output.a); + + COLOR = mix(original_color, output, selection_color.a); +} diff --git a/src/UI/Dialogs/ImageEffects/GradientDialog.gd b/src/UI/Dialogs/ImageEffects/GradientDialog.gd index 05b207bea..3ec6e25e6 100644 --- a/src/UI/Dialogs/ImageEffects/GradientDialog.gd +++ b/src/UI/Dialogs/ImageEffects/GradientDialog.gd @@ -1,17 +1,69 @@ extends ImageEffect -onready var options_cont = $VBoxContainer/OptionsContainer +enum { LINEAR, RADIAL, STEP, RADIAL_STEP, DITHERING, RADIAL_DITHERING } + +var shader_linear: Shader = preload("res://src/Shaders/Gradients/Linear.gdshader") +var shader_radial: Shader = preload("res://src/Shaders/Gradients/Radial.gdshader") +var shader_step: Shader = preload("res://src/Shaders/Gradients/Step.gdshader") +var shader_radial_step: Shader = preload("res://src/Shaders/Gradients/RadialStep.gdshader") +var shader_dither: Shader = preload("res://src/Shaders/Gradients/Dithering.gdshader") +var shader_radial_dither: Shader = preload("res://src/Shaders/Gradients/RadialDithering.gdshader") + +var confirmed := false +var shader: Shader = shader_linear +var dither_matrices := [ + DitherMatrix.new(preload("res://assets/dither-matrices/bayer2.png"), "Bayer 2x2"), + DitherMatrix.new(preload("res://assets/dither-matrices/bayer4.png"), "Bayer 4x4", 16), + DitherMatrix.new(preload("res://assets/dither-matrices/bayer8.png"), "Bayer 8x8", 64), + DitherMatrix.new(preload("res://assets/dither-matrices/bayer16.png"), "Bayer 16x16", 256), +] +var selected_dither_matrix: DitherMatrix = dither_matrices[0] + +onready var options_cont: Container = $VBoxContainer/OptionsContainer +onready var type_option_button: OptionButton = options_cont.get_node("TypeOptionButton") onready var color1: ColorPickerButton = options_cont.get_node("ColorsContainer/ColorPickerButton") onready var color2: ColorPickerButton = options_cont.get_node("ColorsContainer/ColorPickerButton2") +onready var position: SpinBox = options_cont.get_node("PositionSpinBox") +onready var angle: SpinBox = options_cont.get_node("AngleSpinBox") +onready var center_x: SpinBox = options_cont.get_node("CenterContainer/CenterXSpinBox") +onready var center_y: SpinBox = options_cont.get_node("CenterContainer/CenterYSpinBox") +onready var radius_x: SpinBox = options_cont.get_node("RadiusContainer/RadiusXSpinBox") +onready var radius_y: SpinBox = options_cont.get_node("RadiusContainer/RadiusYSpinBox") +onready var size: SpinBox = options_cont.get_node("SizeSpinBox") onready var steps: SpinBox = options_cont.get_node("StepSpinBox") -onready var direction: OptionButton = options_cont.get_node("DirectionOptionButton") +onready var dithering_option_button: OptionButton = options_cont.get_node("DitheringOptionButton") + + +class DitherMatrix: + var texture: Texture + var name: String + var n_of_colors: int + + func _init(_texture: Texture, _name: String, _n_of_colors := 4) -> void: + texture = _texture + name = _name + n_of_colors = _n_of_colors func _ready() -> void: color1.get_picker().presets_visible = false - color1.get_picker().deferred_mode = true color2.get_picker().presets_visible = false - color2.get_picker().deferred_mode = true + var sm := ShaderMaterial.new() + sm.shader = shader + preview.set_material(sm) + + for matrix in dither_matrices: + dithering_option_button.add_item(matrix.name) + + +func _about_to_show() -> void: + confirmed = false + ._about_to_show() + + +func _confirmed() -> void: + confirmed = true + ._confirmed() func set_nodes() -> void: @@ -20,28 +72,80 @@ func set_nodes() -> void: affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton -func commit_action(_cel: Image, _project: Project = Global.current_project) -> void: - DrawingAlgos.generate_gradient( - _cel, - [color1.color, color2.color], - steps.value, - direction.selected, - selection_checkbox.pressed, - _project - ) +func commit_action(cel: Image, project: Project = Global.current_project) -> void: + var selection: Image + var selection_tex := ImageTexture.new() + if selection_checkbox.pressed and project.has_selection: + selection = project.bitmap_to_image(project.selection_bitmap) + else: # This is needed to prevent a weird bug with the dithering shaders and GLES2 + selection = Image.new() + selection.create(project.size.x, project.size.y, false, Image.FORMAT_L8) + selection_tex.create_from_image(selection, 0) + + var dither_texture: Texture = selected_dither_matrix.texture + var dither_steps: int = selected_dither_matrix.n_of_colors + 1 + var pixel_size: int = dither_texture.get_width() + var params := { + "first_color": color1.color, + "second_color": color2.color, + "selection": selection_tex, + "position": (position.value / 100.0) - 0.5, + "angle": angle.value, + "center": Vector2(center_x.value / 100.0, center_y.value / 100.0), + "radius": Vector2(radius_x.value, radius_y.value), + "size": size.value / 100.0, + "steps": steps.value, + "dither_texture": dither_texture, + "image_size": project.size, + "dither_steps": dither_steps, + "pixel_size": pixel_size, + } + + 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_ColorPickerButton_color_changed(_color: Color) -> void: +func _on_TypeOptionButton_item_selected(index: int) -> void: + for child in options_cont.get_children(): + if not child.is_in_group("gradient_common"): + child.visible = false + + match index: + LINEAR: + shader = shader_linear + get_tree().set_group("gradient_linear", "visible", true) + RADIAL: + shader = shader_radial + get_tree().set_group("gradient_radial", "visible", true) + STEP: + shader = shader_step + get_tree().set_group("gradient_step", "visible", true) + RADIAL_STEP: + shader = shader_radial_step + get_tree().set_group("gradient_radial_step", "visible", true) + DITHERING: + shader = shader_dither + get_tree().set_group("gradient_dithering", "visible", true) + RADIAL_DITHERING: + shader = shader_radial_dither + get_tree().set_group("gradient_radial_dithering", "visible", true) update_preview() -func _on_ColorPickerButton2_color_changed(_color: Color) -> void: +func _color_changed(_color: Color) -> void: update_preview() -func _on_StepSpinBox_value_changed(_value: int) -> void: +func _value_changed(_value: float) -> void: update_preview() -func _on_DirectionOptionButton_item_selected(_index: int) -> void: +func _on_DitheringOptionButton_item_selected(index: int) -> void: + selected_dither_matrix = dither_matrices[index] update_preview() diff --git a/src/UI/Dialogs/ImageEffects/GradientDialog.tscn b/src/UI/Dialogs/ImageEffects/GradientDialog.tscn index 1d1f6bba1..4033a0764 100644 --- a/src/UI/Dialogs/ImageEffects/GradientDialog.tscn +++ b/src/UI/Dialogs/ImageEffects/GradientDialog.tscn @@ -4,9 +4,9 @@ [ext_resource path="res://src/UI/TransparentChecker.tscn" type="PackedScene" id=2] [node name="GradientDialog" type="ConfirmationDialog"] -margin_right = 200.0 -margin_bottom = 196.0 -rect_min_size = Vector2( 172, 60.2 ) +margin_right = 334.0 +margin_bottom = 444.0 +rect_min_size = Vector2( 334, 444 ) window_title = "Gradient" resizable = true script = ExtResource( 1 ) @@ -26,14 +26,14 @@ __meta__ = { } [node name="AspectRatioContainer" type="AspectRatioContainer" parent="VBoxContainer"] -margin_right = 285.0 -margin_bottom = 200.0 +margin_right = 318.0 +margin_bottom = 240.0 size_flags_vertical = 3 [node name="Preview" type="TextureRect" parent="VBoxContainer/AspectRatioContainer"] -margin_left = 42.5 -margin_right = 242.5 -margin_bottom = 200.0 +margin_left = 39.0 +margin_right = 279.0 +margin_bottom = 240.0 rect_min_size = Vector2( 200, 200 ) size_flags_horizontal = 5 size_flags_vertical = 3 @@ -48,86 +48,248 @@ margin_right = 0.0 margin_bottom = 0.0 [node name="OptionsContainer" type="GridContainer" parent="VBoxContainer"] -margin_top = 204.0 -margin_right = 285.0 -margin_bottom = 304.0 +margin_top = 244.0 +margin_right = 318.0 +margin_bottom = 400.0 columns = 2 -[node name="Label" type="Label" parent="VBoxContainer/OptionsContainer"] +[node name="TypeLabel" type="Label" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]] margin_top = 3.0 margin_right = 160.0 margin_bottom = 17.0 +text = "Type:" + +[node name="TypeOptionButton" type="OptionButton" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]] +margin_left = 164.0 +margin_right = 278.0 +margin_bottom = 20.0 +mouse_default_cursor_shape = 2 +text = "Linear" +items = [ "Linear", null, false, 0, null, "Radial", null, false, 1, null, "Step", null, false, 2, null, "Radial Step", null, false, 3, null, "Dithering", null, false, 4, null, "Radial Dithering", null, false, 5, null ] +selected = 0 + +[node name="ColorsLabel" type="Label" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]] +margin_top = 27.0 +margin_right = 160.0 +margin_bottom = 41.0 text = "Colors:" -[node name="ColorsContainer" type="HBoxContainer" parent="VBoxContainer/OptionsContainer"] -margin_left = 164.0 -margin_right = 285.0 -margin_bottom = 20.0 - -[node name="ColorPickerButton" type="ColorPickerButton" parent="VBoxContainer/OptionsContainer/ColorsContainer"] -margin_right = 20.0 -margin_bottom = 20.0 -rect_min_size = Vector2( 20, 0 ) -mouse_default_cursor_shape = 2 - -[node name="ColorPickerButton2" type="ColorPickerButton" parent="VBoxContainer/OptionsContainer/ColorsContainer"] -margin_left = 24.0 -margin_right = 44.0 -margin_bottom = 20.0 -rect_min_size = Vector2( 20, 0 ) -mouse_default_cursor_shape = 2 -color = Color( 1, 1, 1, 1 ) - -[node name="Label2" type="Label" parent="VBoxContainer/OptionsContainer"] -margin_top = 29.0 -margin_right = 160.0 -margin_bottom = 43.0 -text = "Steps:" - -[node name="StepSpinBox" type="SpinBox" parent="VBoxContainer/OptionsContainer"] +[node name="ColorsContainer" type="HBoxContainer" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]] margin_left = 164.0 margin_top = 24.0 -margin_right = 285.0 -margin_bottom = 48.0 +margin_right = 278.0 +margin_bottom = 44.0 + +[node name="ColorPickerButton" type="ColorPickerButton" parent="VBoxContainer/OptionsContainer/ColorsContainer"] +margin_right = 55.0 +margin_bottom = 20.0 +rect_min_size = Vector2( 20, 0 ) +mouse_default_cursor_shape = 2 +size_flags_horizontal = 3 + +[node name="ColorPickerButton2" type="ColorPickerButton" parent="VBoxContainer/OptionsContainer/ColorsContainer"] +margin_left = 59.0 +margin_right = 114.0 +margin_bottom = 20.0 +rect_min_size = Vector2( 20, 0 ) +mouse_default_cursor_shape = 2 +size_flags_horizontal = 3 +color = Color( 1, 1, 1, 1 ) + +[node name="PositionLabel" type="Label" parent="VBoxContainer/OptionsContainer" groups=["gradient_dithering", "gradient_linear", "gradient_step"]] +margin_top = 53.0 +margin_right = 160.0 +margin_bottom = 67.0 +text = "Position:" + +[node name="PositionSpinBox" type="SpinBox" parent="VBoxContainer/OptionsContainer" groups=["gradient_dithering", "gradient_linear", "gradient_step"]] +margin_left = 164.0 +margin_top = 48.0 +margin_right = 278.0 +margin_bottom = 72.0 +mouse_default_cursor_shape = 2 +value = 50.0 +suffix = "%" + +[node name="AngleLabel" type="Label" parent="VBoxContainer/OptionsContainer" groups=["gradient_dithering", "gradient_linear", "gradient_step"]] +margin_top = 81.0 +margin_right = 160.0 +margin_bottom = 95.0 +text = "Angle:" + +[node name="AngleSpinBox" type="SpinBox" parent="VBoxContainer/OptionsContainer" groups=["gradient_dithering", "gradient_linear", "gradient_step"]] +margin_left = 164.0 +margin_top = 76.0 +margin_right = 278.0 +margin_bottom = 100.0 +mouse_default_cursor_shape = 2 +max_value = 360.0 +suffix = "°" + +[node name="SizeLabel" type="Label" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]] +margin_top = 109.0 +margin_right = 160.0 +margin_bottom = 123.0 +text = "Transition size:" + +[node name="SizeSpinBox" type="SpinBox" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]] +margin_left = 164.0 +margin_top = 104.0 +margin_right = 278.0 +margin_bottom = 128.0 +mouse_default_cursor_shape = 2 +min_value = 1.0 +max_value = 200.0 +value = 100.0 +suffix = "%" + +[node name="StepsLabel" type="Label" parent="VBoxContainer/OptionsContainer" groups=["gradient_radial_step", "gradient_step"]] +visible = false +margin_top = 137.0 +margin_right = 160.0 +margin_bottom = 151.0 +text = "Steps:" + +[node name="StepSpinBox" type="SpinBox" parent="VBoxContainer/OptionsContainer" groups=["gradient_radial_step", "gradient_step"]] +visible = false +margin_left = 164.0 +margin_top = 132.0 +margin_right = 316.0 +margin_bottom = 156.0 mouse_default_cursor_shape = 2 min_value = 2.0 value = 2.0 -[node name="Label3" type="Label" parent="VBoxContainer/OptionsContainer"] -margin_top = 55.0 +[node name="CenterLabel" type="Label" parent="VBoxContainer/OptionsContainer" groups=["gradient_radial", "gradient_radial_dithering", "gradient_radial_step"]] +visible = false +margin_top = 165.0 margin_right = 160.0 -margin_bottom = 69.0 -text = "Direction:" +margin_bottom = 179.0 +text = "Center:" -[node name="DirectionOptionButton" type="OptionButton" parent="VBoxContainer/OptionsContainer"] -margin_left = 164.0 -margin_top = 52.0 -margin_right = 285.0 -margin_bottom = 72.0 +[node name="CenterContainer" type="HBoxContainer" parent="VBoxContainer/OptionsContainer" groups=["gradient_radial", "gradient_radial_dithering", "gradient_radial_step"]] +visible = false +margin_top = 132.0 +margin_right = 183.0 +margin_bottom = 156.0 + +[node name="XLabel" type="Label" parent="VBoxContainer/OptionsContainer/CenterContainer"] +margin_top = 5.0 +margin_right = 12.0 +margin_bottom = 19.0 +text = "X:" + +[node name="CenterXSpinBox" type="SpinBox" parent="VBoxContainer/OptionsContainer/CenterContainer"] +margin_left = 16.0 +margin_right = 90.0 +margin_bottom = 24.0 mouse_default_cursor_shape = 2 -text = "Top to Bottom" -items = [ "Top to Bottom", null, false, 0, null, "Bottom to Top", null, false, 1, null, "Left to Right", null, false, 2, null, "Right to Left", null, false, 3, null ] -selected = 0 +value = 50.0 +suffix = "%" -[node name="SelectionCheckBox" type="CheckBox" parent="VBoxContainer/OptionsContainer"] -margin_top = 76.0 +[node name="YLabel" type="Label" parent="VBoxContainer/OptionsContainer/CenterContainer"] +margin_left = 94.0 +margin_top = 5.0 +margin_right = 105.0 +margin_bottom = 19.0 +text = "Y:" + +[node name="CenterYSpinBox" type="SpinBox" parent="VBoxContainer/OptionsContainer/CenterContainer"] +margin_left = 109.0 +margin_right = 183.0 +margin_bottom = 24.0 +mouse_default_cursor_shape = 2 +value = 50.0 +suffix = "%" + +[node name="RadiusLabel" type="Label" parent="VBoxContainer/OptionsContainer" groups=["gradient_radial", "gradient_radial_dithering", "gradient_radial_step"]] +visible = false +margin_top = 193.0 margin_right = 160.0 -margin_bottom = 100.0 +margin_bottom = 207.0 +text = "Radius:" + +[node name="RadiusContainer" type="HBoxContainer" parent="VBoxContainer/OptionsContainer" groups=["gradient_radial", "gradient_radial_dithering", "gradient_radial_step"]] +visible = false +margin_left = 164.0 +margin_top = 188.0 +margin_right = 316.0 +margin_bottom = 212.0 + +[node name="XLabel" type="Label" parent="VBoxContainer/OptionsContainer/RadiusContainer"] +margin_left = -164.0 +margin_top = -51.0 +margin_right = -152.0 +margin_bottom = -37.0 +text = "X:" + +[node name="RadiusXSpinBox" type="SpinBox" parent="VBoxContainer/OptionsContainer/RadiusContainer"] +margin_right = 74.0 +margin_bottom = 24.0 +mouse_default_cursor_shape = 2 +min_value = 0.01 +step = 0.01 +value = 1.0 + +[node name="YLabel" type="Label" parent="VBoxContainer/OptionsContainer/RadiusContainer"] +margin_left = -70.0 +margin_top = -51.0 +margin_right = -59.0 +margin_bottom = -37.0 +text = "Y:" + +[node name="RadiusYSpinBox" type="SpinBox" parent="VBoxContainer/OptionsContainer/RadiusContainer"] +margin_left = 78.0 +margin_right = 152.0 +margin_bottom = 24.0 +mouse_default_cursor_shape = 2 +min_value = 0.01 +step = 0.01 +value = 1.0 + +[node name="DitheringLabel" type="Label" parent="VBoxContainer/OptionsContainer" groups=["gradient_dithering", "gradient_radial_dithering"]] +visible = false +margin_top = 219.0 +margin_right = 160.0 +margin_bottom = 233.0 +text = "Dithering pattern:" + +[node name="DitheringOptionButton" type="OptionButton" parent="VBoxContainer/OptionsContainer" groups=["gradient_dithering", "gradient_radial_dithering"]] +visible = false +margin_left = 164.0 +margin_top = 216.0 +margin_right = 316.0 +margin_bottom = 236.0 +mouse_default_cursor_shape = 2 +text = "Bayer 2x2" + +[node name="SelectionCheckBox" type="CheckBox" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]] +margin_top = 132.0 +margin_right = 160.0 +margin_bottom = 156.0 mouse_default_cursor_shape = 2 pressed = true text = "Only affect selection" -[node name="AffectOptionButton" type="OptionButton" parent="VBoxContainer/OptionsContainer"] +[node name="AffectOptionButton" type="OptionButton" parent="VBoxContainer/OptionsContainer" groups=["gradient_common"]] margin_left = 164.0 -margin_top = 76.0 -margin_right = 285.0 -margin_bottom = 100.0 +margin_top = 132.0 +margin_right = 278.0 +margin_bottom = 156.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="color_changed" from="VBoxContainer/OptionsContainer/ColorsContainer/ColorPickerButton" to="." method="_on_ColorPickerButton_color_changed"] -[connection signal="color_changed" from="VBoxContainer/OptionsContainer/ColorsContainer/ColorPickerButton2" to="." method="_on_ColorPickerButton2_color_changed"] -[connection signal="value_changed" from="VBoxContainer/OptionsContainer/StepSpinBox" to="." method="_on_StepSpinBox_value_changed"] -[connection signal="item_selected" from="VBoxContainer/OptionsContainer/DirectionOptionButton" to="." method="_on_DirectionOptionButton_item_selected"] +[connection signal="item_selected" from="VBoxContainer/OptionsContainer/TypeOptionButton" to="." method="_on_TypeOptionButton_item_selected"] +[connection signal="color_changed" from="VBoxContainer/OptionsContainer/ColorsContainer/ColorPickerButton" to="." method="_color_changed"] +[connection signal="color_changed" from="VBoxContainer/OptionsContainer/ColorsContainer/ColorPickerButton2" to="." method="_color_changed"] +[connection signal="value_changed" from="VBoxContainer/OptionsContainer/PositionSpinBox" to="." method="_value_changed"] +[connection signal="value_changed" from="VBoxContainer/OptionsContainer/AngleSpinBox" to="." method="_value_changed"] +[connection signal="value_changed" from="VBoxContainer/OptionsContainer/SizeSpinBox" to="." method="_value_changed"] +[connection signal="value_changed" from="VBoxContainer/OptionsContainer/StepSpinBox" to="." method="_value_changed"] +[connection signal="value_changed" from="VBoxContainer/OptionsContainer/CenterContainer/CenterXSpinBox" to="." method="_value_changed"] +[connection signal="value_changed" from="VBoxContainer/OptionsContainer/CenterContainer/CenterYSpinBox" to="." method="_value_changed"] +[connection signal="value_changed" from="VBoxContainer/OptionsContainer/RadiusContainer/RadiusXSpinBox" to="." method="_value_changed"] +[connection signal="value_changed" from="VBoxContainer/OptionsContainer/RadiusContainer/RadiusYSpinBox" to="." method="_value_changed"] +[connection signal="item_selected" from="VBoxContainer/OptionsContainer/DitheringOptionButton" to="." method="_on_DitheringOptionButton_item_selected"]