diff --git a/.gitignore b/.gitignore index 4709183..162691a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ # Godot 4+ specific ignores .godot/ +*.import \ No newline at end of file diff --git a/color_picker.gd b/color_picker.gd new file mode 100644 index 0000000..b911226 --- /dev/null +++ b/color_picker.gd @@ -0,0 +1,17 @@ +extends ColorPicker + +@onready var texture_rect: TextureRect = $"../TextureRect" +@onready var img: Image = texture_rect.img + +func _physics_process(_delta: float) -> void: + await get_tree().create_timer(.750).timeout + if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): + var local_position := texture_rect.get_local_mouse_position() + var rect_size := texture_rect.get_size() + var img_size := Vector2(img.get_size()) + var ratio := rect_size / img_size + var pixel_coord := local_position / ratio + + if pixel_coord.x >= img_size.x or pixel_coord.x < 0: return + if pixel_coord.y >= img_size.y or pixel_coord.y < 0: return + self.color = img.get_pixelv(pixel_coord) diff --git a/control.tscn b/control.tscn deleted file mode 100644 index 7d96258..0000000 --- a/control.tscn +++ /dev/null @@ -1,173 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://bcmdpvny8bj2p"] - -[sub_resource type="GDScript" id="GDScript_grmqm"] -script/source = "extends TextureRect - -var img : Image -var dimension: Vector2i -@onready var spin_x: SpinBox = $'../spin_x' -@onready var spin_y: SpinBox = $'../spin_y' - -var gray_v :PackedFloat64Array= [] - -var hue_v :PackedFloat64Array= [] -@onready var saturation: HSlider = $'../saturation' -var light_v :PackedFloat64Array= [] - -func _ready() -> void: - dimension.x = 8; dimension.y = 16; - img = Image.create(dimension.x*2, dimension.y, false,Image.FORMAT_RGB8) - gen_values(); gen_image() - - -func gen_values(): - gray_v.clear(); hue_v.clear(); light_v.clear(); - - gray_v.append(1) - for x in range(1,dimension.x): - gray_v.append(1 - (x * (1.0/(dimension.x-1)))) - - for y in range(0,dimension.y): - hue_v.append(y * (1.0/dimension.y) + (1.0/dimension.y)) - - #for x in range(1,dimension.x+1): - #light_v.append(x * (1.0/(dimension.x+1))) - light_v.append(.05) - light_v.append(.114583) - light_v.append(.25) - light_v.append(.416667) - light_v.append(.583333) - light_v.append(.75) - light_v.append(.8854167) - light_v.append(.95) - -func gen_image(): - for x in range(0, dimension.x): - var col := Color.from_ok_hsl(0, 0, gray_v[x] ) - for y in range(0, dimension.y): - img.set_pixel(x, y, col) - - for x in range(0, dimension.x): - for y in range(0, dimension.y): - var col := Color.from_ok_hsl(hue_v[y], saturation.value, light_v[x] ) - img.set_pixel(dimension.x+x, y, col) - - self.texture = ImageTexture.create_from_image(img) - - -func _on_saturation_value_changed(_value: float) -> void: - gen_image() - -func _on_spin_x_value_changed(value: float) -> void: - var v := roundi(value) - if v == 0: - spin_x.value = 2; return - dimension.x = roundi(value/2) - img.resize(dimension.x*2, dimension.y) - gen_values(); gen_image() - -func _on_spin_y_value_changed(value: float) -> void: - var v := roundi(value) - if v == 0: - spin_y.value = 2; return - dimension.y = v - img.resize(dimension.x*2, dimension.y) - gen_values(); gen_image() - - -func _on_save_image_pressed() -> void: - img.save_png('./img.png') -" - -[node name="Control" type="Control"] -custom_minimum_size = Vector2(768, 768) -layout_mode = 3 -anchors_preset = 13 -anchor_left = 0.5 -anchor_right = 0.5 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="TextureRect" type="TextureRect" parent="."] -custom_minimum_size = Vector2(448, 448) -layout_mode = 0 -offset_left = 3.0 -offset_top = 4.0 -offset_right = 464.0 -offset_bottom = 760.0 -stretch_mode = 4 -script = SubResource("GDScript_grmqm") - -[node name="ColorPicker" type="ColorPicker" parent="."] -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -298.0 -offset_bottom = 576.0 -grow_horizontal = 0 -color = Color(0.831373, 0.14902, 0.305882, 1) - -[node name="saturation" type="HSlider" parent="."] -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -293.0 -offset_top = 633.0 -offset_right = 1.0 -offset_bottom = 649.0 -grow_horizontal = 0 -max_value = 1.0 -step = 0.1 -value = 0.9 -tick_count = 11 -ticks_on_borders = true - -[node name="Label" type="Label" parent="saturation"] -layout_mode = 0 -offset_left = 6.0 -offset_top = -23.0 -offset_right = 87.0 -text = "Saturation" - -[node name="spin_x" type="SpinBox" parent="."] -layout_mode = 0 -offset_left = 476.0 -offset_top = 659.0 -offset_right = 559.062 -offset_bottom = 690.0 -step = 2.0 -value = 16.0 -rounded = true -allow_greater = true -prefix = "X:" -select_all_on_focus = true - -[node name="spin_y" type="SpinBox" parent="."] -layout_mode = 0 -offset_left = 565.0 -offset_top = 659.0 -offset_right = 648.062 -offset_bottom = 690.0 -step = 2.0 -value = 16.0 -rounded = true -allow_greater = true -prefix = "Y:" -select_all_on_focus = true - -[node name="save_image" type="Button" parent="."] -layout_mode = 2 -offset_left = 669.0 -offset_top = 659.0 -offset_right = 764.0 -offset_bottom = 690.0 -text = "save image -" - -[connection signal="value_changed" from="saturation" to="TextureRect" method="_on_saturation_value_changed"] -[connection signal="value_changed" from="spin_x" to="TextureRect" method="_on_spin_x_value_changed"] -[connection signal="value_changed" from="spin_y" to="TextureRect" method="_on_spin_y_value_changed"] -[connection signal="pressed" from="save_image" to="TextureRect" method="_on_save_image_pressed"] diff --git a/main.gd b/main.gd new file mode 100644 index 0000000..22ad4a1 --- /dev/null +++ b/main.gd @@ -0,0 +1,151 @@ +extends TextureRect + +@export var img: Image + +@onready var home_path := OS.get_environment("USERPROFILE") if OS.has_feature("windows") else OS.get_environment("HOME") +@onready var path_line: LineEdit = $"../path_line" +@onready var image_name_line: LineEdit = $"../image_name_line" + +@onready var hue_slider: HSlider = $"../hue_slider" +@onready var hue_slider_label: Label = $"../hue_slider/Label" +@onready var lightness_slider: HSlider = $"../lightness_slider" +@onready var lightness_slider_label: Label = $"../lightness_slider/Label" +@onready var saturation: HSlider = $'../saturation' +@onready var saturation_label: Label = $"../saturation/Label" +@onready var overlay_lightness: HSlider = $"../overlay_lightness" +@onready var overlay_lightness_label: Label = $"../overlay_lightness/Label" +@onready var overlay_on: CheckBox = $"../overlay_on" + +@onready var hue_slider_label_text := hue_slider_label.text +@onready var lightness_slider_label_text := lightness_slider_label.text +@onready var saturation_label_text := saturation_label.text +@onready var overlay_lightness_label_text := overlay_lightness_label.text + +var num_hues := 12 +var num_lights := 9 + +var gray_v: PackedFloat64Array = [] +var gray_reversed_v: PackedFloat64Array = [] + +var hue_v: PackedFloat64Array = [] +var light_v: PackedFloat64Array = [] + + +func _ready() -> void: + path_line.text = home_path + + img = Image.create((num_lights * 2) - 2, num_hues, false, Image.FORMAT_RGB8) + gen_values(); gen_image() + + hue_slider_label.text = hue_slider_label_text % num_hues + lightness_slider_label.text = lightness_slider_label_text % num_lights + saturation_label.text = saturation_label_text % saturation.value + overlay_lightness_label.text = overlay_lightness_label_text % overlay_lightness.value + +func resize_img(): img.resize((num_lights * 2) - 2, num_hues) + +func gen_values(): + gray_v.clear(); hue_v.clear(); light_v.clear(); + + gray_v.append(1) + for x in range(1, num_lights): + gray_v.append(1 - (x * (1.0 / (num_lights - 1)))) + gray_reversed_v = gray_v.duplicate() + gray_reversed_v.reverse() + + for y in range(0, num_hues): + hue_v.append(1.0 / 12 + y * (1.0 / num_hues)) + + for x in range(1, num_lights + 1): + light_v.append(x * (1.0 / (num_lights + 1))) + +func gen_image(): + var col: Color + var col_overlay: Color + var x_local: int + + for x in range(0, (num_lights * 2) - 2): + for y in range(0, num_hues): + x_local = x % num_lights + if x < num_lights: + col = Color.from_ok_hsl(0, 0, gray_v[x_local]) + else: + if x_local == num_lights - 2: continue + if overlay_on.button_pressed: + # overlay blend mode in righ side + col = Color.from_ok_hsl(0, 0, gray_reversed_v[x_local + 1]) + col_overlay = Color.from_ok_hsl(hue_v[y], saturation.value, overlay_lightness.value) + col = blend_overlay(col, col_overlay) + else: + col = Color.from_ok_hsl(hue_v[y], saturation.value, gray_reversed_v[x_local + 1]) + img.set_pixel(x, y, col) + + self.texture = ImageTexture.create_from_image(img) + +func blend_overlay(a: Color, b: Color) -> Color: + var c := Color() + if a.r < 0.5: c.r = 2 * a.r * b.r + else: c.r = 1 - 2 * (1 - a.r) * (1 - b.r) + if a.g < 0.5: c.g = 2 * a.g * b.g + else: c.g = 1 - 2 * (1 - a.g) * (1 - b.g) + if a.b < 0.5: c.b = 2 * a.b * b.b + else: c.b = 1 - 2 * (1 - a.b) * (1 - b.b) + + return c + + +func _on_saturation_value_changed(_value: float) -> void: + gen_image() + saturation_label.text = saturation_label_text % saturation.value + +func _on_overlay_lightness_value_changed(_value: float) -> void: + gen_image() + overlay_lightness_label.text = overlay_lightness_label_text % overlay_lightness.value + +func _on_overlay_on_toggled(_toggled_on: bool) -> void: + gen_image() + +func _on_hue_slider_value_changed(value: float) -> void: + var v := 3 + for x in range(1, roundi(value)): v += v + num_hues = v + resize_img(); gen_values(); gen_image() + hue_slider_label.text = hue_slider_label_text % num_hues + +func _on_lightness_slider_value_changed(value: float) -> void: + var v := 2 + for x in range(1, roundi(value)): v += v - 1 + num_lights = v + resize_img(); gen_values(); gen_image() + lightness_slider_label.text = lightness_slider_label_text % num_lights + + +func _on_save_image_pressed() -> void: + # Ensure the directory exists + var path := path_line.text + var d := DirAccess.open('res://') + if not d.dir_exists(path): + d.make_dir_recursive(path) + + var image_name := image_name_line.text + if image_name == '': image_name = 'img' + image_name = image_name.trim_suffix('.png') + image_name = image_name.validate_filename() + image_name_line.text = image_name + + var file_path_to_save = path + '/' + image_name + '.png' + img.save_png(file_path_to_save) + + +func _on_edit_path_pressed() -> void: + var dlg = FileDialog.new() + self.add_child(dlg) + dlg.access = FileDialog.ACCESS_FILESYSTEM + dlg.current_dir = path_line.text + dlg.current_file = image_name_line.text + dlg.add_filter('*.png') + dlg.set_size(Vector2(640, 512)) + dlg.popup_centered() + await dlg.file_selected + path_line.text = dlg.current_dir + image_name_line.text = dlg.current_file.trim_suffix('.png') diff --git a/main.tscn b/main.tscn new file mode 100644 index 0000000..7120848 --- /dev/null +++ b/main.tscn @@ -0,0 +1,180 @@ +[gd_scene load_steps=3 format=3 uid="uid://f2imuwxg74oc"] + +[ext_resource type="Script" path="res://main.gd" id="1_32lon"] +[ext_resource type="Script" path="res://color_picker.gd" id="2_tb0mp"] + +[node name="Main" type="Control"] +custom_minimum_size = Vector2(768, 640) +layout_mode = 3 +anchors_preset = 13 +anchor_left = 0.5 +anchor_right = 0.5 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="TextureRect" type="TextureRect" parent="."] +custom_minimum_size = Vector2(448, 448) +layout_mode = 0 +offset_left = 4.0 +offset_right = 452.0 +offset_bottom = 448.0 +script = ExtResource("1_32lon") + +[node name="ColorPicker" type="ColorPicker" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -300.0 +offset_bottom = 471.0 +grow_horizontal = 0 +color = Color(0, 0, 0, 1) +color_mode = 3 +picker_shape = 3 +can_add_swatches = false +color_modes_visible = false +presets_visible = false +script = ExtResource("2_tb0mp") + +[node name="hue_slider" type="HSlider" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -300.0 +offset_top = 507.0 +offset_bottom = 523.0 +grow_horizontal = 0 +min_value = 1.0 +max_value = 4.0 +value = 3.0 +rounded = true +tick_count = 4 +ticks_on_borders = true + +[node name="Label" type="Label" parent="hue_slider"] +layout_mode = 0 +offset_left = 6.0 +offset_top = -23.0 +offset_right = 87.0 +text = "HUE %s" + +[node name="lightness_slider" type="HSlider" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -300.0 +offset_top = 557.0 +offset_bottom = 573.0 +grow_horizontal = 0 +min_value = 1.0 +max_value = 4.0 +value = 4.0 +rounded = true +tick_count = 4 +ticks_on_borders = true + +[node name="Label" type="Label" parent="lightness_slider"] +layout_mode = 0 +offset_left = 6.0 +offset_top = -23.0 +offset_right = 87.0 +text = "lightness %s" + +[node name="saturation" type="HSlider" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -755.0 +offset_top = 557.0 +offset_right = -555.0 +offset_bottom = 573.0 +grow_horizontal = 0 +max_value = 1.0 +step = 0.05 +value = 0.9 +tick_count = 5 +ticks_on_borders = true + +[node name="Label" type="Label" parent="saturation"] +layout_mode = 0 +offset_left = 6.0 +offset_top = -23.0 +offset_right = 87.0 +text = "Saturation %s" + +[node name="overlay_lightness" type="HSlider" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -537.0 +offset_top = 557.0 +offset_right = -337.0 +offset_bottom = 573.0 +grow_horizontal = 0 +max_value = 1.0 +step = 0.05 +value = 0.3 +tick_count = 5 +ticks_on_borders = true + +[node name="Label" type="Label" parent="overlay_lightness"] +layout_mode = 0 +offset_left = 6.0 +offset_top = -23.0 +offset_right = 87.0 +text = "Overlay lightness %s" + +[node name="path_line" type="LineEdit" parent="."] +layout_mode = 1 +offset_left = 4.0 +offset_top = 460.0 +offset_right = 422.0 +offset_bottom = 491.0 + +[node name="edit_path" type="Button" parent="."] +layout_mode = 1 +offset_left = 425.0 +offset_top = 460.0 +offset_right = 453.0 +offset_bottom = 491.0 +text = "... +" + +[node name="image_name_line" type="LineEdit" parent="."] +layout_mode = 1 +offset_left = 4.0 +offset_top = 496.0 +offset_right = 179.0 +offset_bottom = 527.0 +text = "img" + +[node name="save_image" type="Button" parent="."] +layout_mode = 1 +offset_left = 183.0 +offset_top = 496.0 +offset_right = 266.0 +offset_bottom = 527.0 +text = "save png +" + +[node name="overlay_on" type="CheckBox" parent="."] +layout_mode = 0 +offset_left = 151.0 +offset_top = 591.0 +offset_right = 311.0 +offset_bottom = 622.0 +button_pressed = true +text = "Overlay blending" + +[connection signal="value_changed" from="hue_slider" to="TextureRect" method="_on_hue_slider_value_changed"] +[connection signal="value_changed" from="lightness_slider" to="TextureRect" method="_on_lightness_slider_value_changed"] +[connection signal="value_changed" from="saturation" to="TextureRect" method="_on_saturation_value_changed"] +[connection signal="value_changed" from="overlay_lightness" to="TextureRect" method="_on_overlay_lightness_value_changed"] +[connection signal="pressed" from="edit_path" to="TextureRect" method="_on_edit_path_pressed"] +[connection signal="pressed" from="save_image" to="TextureRect" method="_on_save_image_pressed"] +[connection signal="toggled" from="overlay_on" to="TextureRect" method="_on_overlay_on_toggled"] diff --git a/project.godot b/project.godot index fdbc9ef..f56c25c 100644 --- a/project.godot +++ b/project.godot @@ -10,15 +10,15 @@ config_version=5 [application] -config/name="Generate Image" -run/main_scene="res://control.tscn" -config/features=PackedStringArray("4.2", "Forward Plus") +config/name="Generate palette" +run/main_scene="res://main.tscn" +config/features=PackedStringArray("4.3", "Forward Plus") config/icon="res://icon.svg" [display] window/size/viewport_width=768 -window/size/viewport_height=768 +window/size/viewport_height=640 window/stretch/mode="canvas_items" window/stretch/aspect="keep_height"