1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 17:19:50 +00:00

Add tolerance to the bucket's "similar area" mode, rename "similarity" to "tolerance" and make it work the inverse way

A slightly breaking change for a minor update, but one that needed to be done. The bucket tool's "similarity" (now renamed to "tolerance") used to work the opposite way from all other software, the maximum value meant exact color match and 0 meant no color match. Now it works the inverse way to make it be consistent with other software, and the range is now 0-255 instead of 0-100. 0 means exact color match, 255 means no color match. And tolerance also now works for the "similar area" mode as well.
This commit is contained in:
Emmanouil Papadeas 2024-08-10 18:20:42 +03:00
parent 92d7bbf539
commit b0a284583b
4 changed files with 62 additions and 36 deletions

View file

@ -196,7 +196,7 @@ func get_ellipse_points_filled(pos: Vector2i, size: Vector2i, thickness := 1) ->
return border + filling return border + filling
func scale_3x(sprite: Image, tol := 50.0) -> Image: func scale_3x(sprite: Image, tol := 0.196078) -> Image:
var scaled := Image.create( var scaled := Image.create(
sprite.get_width() * 3, sprite.get_height() * 3, false, Image.FORMAT_RGBA8 sprite.get_width() * 3, sprite.get_height() * 3, false, Image.FORMAT_RGBA8
) )
@ -475,11 +475,15 @@ func nn_rotate(sprite: Image, angle: float, pivot: Vector2) -> void:
sprite.set_pixel(x, y, Color(0, 0, 0, 0)) sprite.set_pixel(x, y, Color(0, 0, 0, 0))
func similar_colors(c1: Color, c2: Color, tol := 100.0) -> bool: ## Compares two colors, and returns [code]true[/code] if the difference of these colors is
var v1 := Vector4(c1.r, c1.g, c1.b, c1.a) ## less or equal to the tolerance [param tol]. [param tol] is in the range of 0-1.
var v2 := Vector4(c2.r, c2.g, c2.b, c2.a) func similar_colors(c1: Color, c2: Color, tol := 0.392157) -> bool:
var dist := v2.distance_to(v1) return (
return dist <= (tol / 255.0) absf(c1.r - c2.r) <= tol
&& absf(c1.g - c2.g) <= tol
&& absf(c1.b - c2.b) <= tol
&& absf(c1.a - c2.a) <= tol
)
# Image effects # Image effects

View file

@ -5,7 +5,7 @@ uniform vec2 size;
uniform vec4 old_color; uniform vec4 old_color;
uniform vec4 new_color; uniform vec4 new_color;
uniform float similarity_percent : hint_range(0.0, 100.0); uniform float tolerance : hint_range(0.0, 1.0);
// Must be the same size as image // Must be the same size as image
// Selected pixels are 1,1,1,1 and unselected 0,0,0,0 // Selected pixels are 1,1,1,1 and unselected 0,0,0,0
@ -16,22 +16,26 @@ uniform sampler2D pattern: repeat_enable;
uniform vec2 pattern_size; uniform vec2 pattern_size;
uniform vec2 pattern_uv_offset; uniform vec2 pattern_uv_offset;
void fragment() { // applies on each pixel separately
vec4 original_color = texture(TEXTURE, UV); // The drawing we have to use on
vec4 selection_color = texture(selection, UV); // use its alpha to get portion we can ignore
vec4 col = original_color; // Innocent till proven guilty bool similar_colors(vec4 c1, vec4 c2, float tol) {
return (
abs(c1.r - c2.r) <= tol
&& abs(c1.g - c2.g) <= tol
&& abs(c1.b - c2.b) <= tol
&& abs(c1.a - c2.a) <= tol
);
}
float max_diff = distance(original_color, old_color); // How much this pixel matches our description
float similarity = abs(2.0 - ((similarity_percent/100.0) * 2.0)); void fragment() {
vec4 original_color = texture(TEXTURE, UV);
if (max_diff <= similarity) // We found our match and pixel is proven guilty (small is precise) vec4 selection_color = texture(selection, UV);
vec4 col = original_color;
if (similar_colors(original_color, old_color, tolerance))
if (has_pattern) if (has_pattern)
col = textureLod(pattern, UV * (size / pattern_size) + pattern_uv_offset, 0.0); col = textureLod(pattern, UV * (size / pattern_size) + pattern_uv_offset, 0.0);
else else
col = new_color; col = new_color;
// Mix selects original color if there is selection or col if there is none
COLOR = mix(original_color, col, selection_color.a); COLOR = mix(original_color, col, selection_color.a);
} }

View file

@ -9,7 +9,7 @@ const PATTERN_FILL_SHADER := preload("res://src/Shaders/PatternFill.gdshader")
var _undo_data := {} var _undo_data := {}
var _prev_mode := 0 var _prev_mode := 0
var _pattern: Patterns.Pattern var _pattern: Patterns.Pattern
var _similarity := 100 var _tolerance := 0.003
var _fill_area: int = FillArea.AREA var _fill_area: int = FillArea.AREA
var _fill_with: int = FillWith.COLOR var _fill_with: int = FillWith.COLOR
var _offset_x := 0 var _offset_x := 0
@ -60,7 +60,7 @@ func _on_FillAreaOptions_item_selected(index: int) -> void:
func _select_fill_area_optionbutton() -> void: func _select_fill_area_optionbutton() -> void:
$FillAreaOptions.selected = _fill_area $FillAreaOptions.selected = _fill_area
$SimilaritySlider.visible = (_fill_area == FillArea.COLORS) $ToleranceSlider.visible = (_fill_area != FillArea.SELECTION)
func _on_FillWithOptions_item_selected(index: int) -> void: func _on_FillWithOptions_item_selected(index: int) -> void:
@ -69,8 +69,8 @@ func _on_FillWithOptions_item_selected(index: int) -> void:
save_config() save_config()
func _on_SimilaritySlider_value_changed(value: float) -> void: func _on_tolerance_slider_value_changed(value: float) -> void:
_similarity = value _tolerance = value / 255.0
update_config() update_config()
save_config() save_config()
@ -102,12 +102,12 @@ func _on_PatternOffsetY_value_changed(value: float) -> void:
func get_config() -> Dictionary: func get_config() -> Dictionary:
if !_pattern: if !_pattern:
return {"fill_area": _fill_area, "fill_with": _fill_with, "similarity": _similarity} return {"fill_area": _fill_area, "fill_with": _fill_with, "tolerance": _tolerance}
return { return {
"pattern_index": _pattern.index, "pattern_index": _pattern.index,
"fill_area": _fill_area, "fill_area": _fill_area,
"fill_with": _fill_with, "fill_with": _fill_with,
"similarity": _similarity, "tolerance": _tolerance,
"offset_x": _offset_x, "offset_x": _offset_x,
"offset_y": _offset_y, "offset_y": _offset_y,
} }
@ -119,7 +119,7 @@ func set_config(config: Dictionary) -> void:
_pattern = Global.patterns_popup.get_pattern(index) _pattern = Global.patterns_popup.get_pattern(index)
_fill_area = config.get("fill_area", _fill_area) _fill_area = config.get("fill_area", _fill_area)
_fill_with = config.get("fill_with", _fill_with) _fill_with = config.get("fill_with", _fill_with)
_similarity = config.get("similarity", _similarity) _tolerance = config.get("tolerance", _tolerance)
_offset_x = config.get("offset_x", _offset_x) _offset_x = config.get("offset_x", _offset_x)
_offset_y = config.get("offset_y", _offset_y) _offset_y = config.get("offset_y", _offset_y)
update_pattern() update_pattern()
@ -128,7 +128,7 @@ func set_config(config: Dictionary) -> void:
func update_config() -> void: func update_config() -> void:
_select_fill_area_optionbutton() _select_fill_area_optionbutton()
$FillWithOptions.selected = _fill_with $FillWithOptions.selected = _fill_with
$SimilaritySlider.value = _similarity $ToleranceSlider.value = _tolerance * 255.0
$FillPattern.visible = _fill_with == FillWith.PATTERN $FillPattern.visible = _fill_with == FillWith.PATTERN
$FillPattern/OffsetX.value = _offset_x $FillPattern/OffsetX.value = _offset_x
$FillPattern/OffsetY.value = _offset_y $FillPattern/OffsetY.value = _offset_y
@ -224,7 +224,7 @@ func fill_in_color(pos: Vector2i) -> void:
"size": project.size, "size": project.size,
"old_color": color, "old_color": color,
"new_color": tool_slot.color, "new_color": tool_slot.color,
"similarity_percent": _similarity, "tolerance": _tolerance,
"selection": selection_tex, "selection": selection_tex,
"pattern": pattern_tex, "pattern": pattern_tex,
"has_pattern": true if _fill_with == FillWith.PATTERN else false "has_pattern": true if _fill_with == FillWith.PATTERN else false
@ -313,23 +313,31 @@ func _add_new_segment(y := 0) -> void:
func _flood_line_around_point( func _flood_line_around_point(
pos: Vector2i, project: Project, image: Image, src_color: Color pos: Vector2i, project: Project, image: Image, src_color: Color
) -> int: ) -> int:
if not image.get_pixelv(pos).is_equal_approx(src_color): if not DrawingAlgos.similar_colors(image.get_pixelv(pos), src_color, _tolerance):
return pos.x + 1 return pos.x + 1
var west := pos var west := pos
var east := pos var east := pos
if project.has_selection: if project.has_selection:
while ( while (
project.can_pixel_get_drawn(west) && image.get_pixelv(west).is_equal_approx(src_color) project.can_pixel_get_drawn(west)
&& DrawingAlgos.similar_colors(image.get_pixelv(west), src_color, _tolerance)
): ):
west += Vector2i.LEFT west += Vector2i.LEFT
while ( while (
project.can_pixel_get_drawn(east) && image.get_pixelv(east).is_equal_approx(src_color) project.can_pixel_get_drawn(east)
&& DrawingAlgos.similar_colors(image.get_pixelv(east), src_color, _tolerance)
): ):
east += Vector2i.RIGHT east += Vector2i.RIGHT
else: else:
while west.x >= 0 && image.get_pixelv(west).is_equal_approx(src_color): while (
west.x >= 0
&& DrawingAlgos.similar_colors(image.get_pixelv(west), src_color, _tolerance)
):
west += Vector2i.LEFT west += Vector2i.LEFT
while east.x < project.size.x && image.get_pixelv(east).is_equal_approx(src_color): while (
east.x < project.size.x
&& DrawingAlgos.similar_colors(image.get_pixelv(east), src_color, _tolerance)
):
east += Vector2i.RIGHT east += Vector2i.RIGHT
# Make a note of the stuff we processed # Make a note of the stuff we processed
var c := pos.y var c := pos.y

View file

@ -1,8 +1,9 @@
[gd_scene load_steps=6 format=3 uid="uid://bbvvkrrjyxugo"] [gd_scene load_steps=7 format=3 uid="uid://bbvvkrrjyxugo"]
[ext_resource type="PackedScene" uid="uid://yjhp0ssng2mp" path="res://src/UI/Nodes/ValueSlider.tscn" id="1"] [ext_resource type="PackedScene" uid="uid://yjhp0ssng2mp" path="res://src/UI/Nodes/ValueSlider.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://ctfgfelg0sho8" path="res://src/Tools/BaseTool.tscn" id="2"] [ext_resource type="PackedScene" uid="uid://ctfgfelg0sho8" path="res://src/Tools/BaseTool.tscn" id="2"]
[ext_resource type="Script" path="res://src/Tools/DesignTools/Bucket.gd" id="3"] [ext_resource type="Script" path="res://src/Tools/DesignTools/Bucket.gd" id="3"]
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="4_1pngp"]
[sub_resource type="StyleBoxFlat" id="1"] [sub_resource type="StyleBoxFlat" id="1"]
bg_color = Color(1, 1, 1, 1) bg_color = Color(1, 1, 1, 1)
@ -46,11 +47,20 @@ popup/item_1/id = 1
popup/item_2/text = "Whole selection" popup/item_2/text = "Whole selection"
popup/item_2/id = 2 popup/item_2/id = 2
[node name="SimilaritySlider" parent="." index="4" instance=ExtResource("1")] [node name="ToleranceSlider" type="TextureProgressBar" parent="." index="4"]
visible = false custom_minimum_size = Vector2(32, 24)
layout_mode = 2 layout_mode = 2
value = 100.0 focus_mode = 2
prefix = "Similarity:" mouse_default_cursor_shape = 2
theme_type_variation = &"ValueSlider"
max_value = 255.0
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource("4_1pngp")
prefix = "Tolerance:"
[node name="FillWith" type="Label" parent="." index="5"] [node name="FillWith" type="Label" parent="." index="5"]
layout_mode = 2 layout_mode = 2
@ -101,7 +111,7 @@ layout_mode = 2
prefix = "Offset Y:" prefix = "Offset Y:"
[connection signal="item_selected" from="FillAreaOptions" to="." method="_on_FillAreaOptions_item_selected"] [connection signal="item_selected" from="FillAreaOptions" to="." method="_on_FillAreaOptions_item_selected"]
[connection signal="value_changed" from="SimilaritySlider" to="." method="_on_SimilaritySlider_value_changed"] [connection signal="value_changed" from="ToleranceSlider" to="." method="_on_tolerance_slider_value_changed"]
[connection signal="item_selected" from="FillWithOptions" to="." method="_on_FillWithOptions_item_selected"] [connection signal="item_selected" from="FillWithOptions" to="." method="_on_FillWithOptions_item_selected"]
[connection signal="pressed" from="FillPattern/Type" to="." method="_on_PatternType_pressed"] [connection signal="pressed" from="FillPattern/Type" to="." method="_on_PatternType_pressed"]
[connection signal="value_changed" from="FillPattern/OffsetX" to="." method="_on_PatternOffsetX_value_changed"] [connection signal="value_changed" from="FillPattern/OffsetX" to="." method="_on_PatternOffsetX_value_changed"]