Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added 2 rotation algorithms and popup for rotating current layer #143

Merged
merged 5 commits into from
Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 35 additions & 59 deletions Main.tscn

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions Prefabs/Dialogs/RotateImage.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
extends ConfirmationDialog

var texture : ImageTexture
var aux_img : Image
var layer : Image

func _ready():
texture = ImageTexture.new()
texture.flags = 0
aux_img = Image.new()
$VBoxContainer/HBoxContainer2/OptionButton.add_item("Rotxel")
$VBoxContainer/HBoxContainer2/OptionButton.add_item("Nearest neighbour")

func set_sprite(sprite : Image):
aux_img.copy_from(sprite)
layer = sprite
texture.create_from_image(aux_img, 0)
$VBoxContainer/TextureRect.texture = texture


func _on_HSlider_value_changed(value):
rotate()
$VBoxContainer/HBoxContainer/SpinBox.value = $VBoxContainer/HBoxContainer/HSlider.value


func _on_SpinBox_value_changed(value):
$VBoxContainer/HBoxContainer/HSlider.value = $VBoxContainer/HBoxContainer/SpinBox.value


func _on_RotateImage_confirmed():
Global.canvas.handle_undo("Draw")
match $VBoxContainer/HBoxContainer2/OptionButton.text:
"Rotxel":
Global.rotxel(layer,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
"Nearest neighbour":
Global.nn_rotate(layer,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
Global.canvas.handle_redo("Draw")
$VBoxContainer/HBoxContainer/HSlider.value = 0

func rotate():
var sprite : Image = Image.new()
sprite.copy_from(aux_img)
match $VBoxContainer/HBoxContainer2/OptionButton.text:
"Rotxel":
Global.rotxel(sprite,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
"Nearest neighbour":
Global.nn_rotate(sprite,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
texture.create_from_image(sprite, 0)


func _on_OptionButton_item_selected(id):
rotate()


func _on_RotateImage_about_to_show():
$VBoxContainer/HBoxContainer/HSlider.value = 0
81 changes: 81 additions & 0 deletions Prefabs/Dialogs/RotateImage.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
[gd_scene load_steps=2 format=2]

[ext_resource path="res://Prefabs/Dialogs/RotateImage.gd" type="Script" id=1]

[node name="RotateImage" type="ConfirmationDialog"]
margin_right = 245.0
margin_bottom = 241.0
window_title = "Confirmá, por favor..."
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}

[node name="VBoxContainer" type="VBoxContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 8.0
margin_top = 8.0
margin_right = -8.0
margin_bottom = -36.0
__meta__ = {
"_edit_use_anchors_": false
}

[node name="TextureRect" type="TextureRect" parent="VBoxContainer"]
margin_right = 229.0
margin_bottom = 145.0
size_flags_vertical = 3
expand = true
stretch_mode = 6

[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"]
margin_top = 149.0
margin_right = 229.0
margin_bottom = 169.0

[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer2"]
margin_top = 3.0
margin_right = 34.0
margin_bottom = 17.0
text = "Type:"

[node name="OptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer2"]
margin_left = 38.0
margin_right = 229.0
margin_bottom = 20.0
size_flags_horizontal = 3
size_flags_vertical = 3

[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
margin_top = 173.0
margin_right = 229.0
margin_bottom = 197.0

[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"]
margin_top = 5.0
margin_right = 44.0
margin_bottom = 19.0
text = "Angle: "

[node name="HSlider" type="HSlider" parent="VBoxContainer/HBoxContainer"]
margin_left = 48.0
margin_right = 151.0
margin_bottom = 24.0
size_flags_horizontal = 3
size_flags_vertical = 3
max_value = 359.0
__meta__ = {
"_edit_use_anchors_": false
}

[node name="SpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer"]
margin_left = 155.0
margin_right = 229.0
margin_bottom = 24.0
max_value = 359.0
[connection signal="about_to_show" from="." to="." method="_on_RotateImage_about_to_show"]
[connection signal="confirmed" from="." to="." method="_on_RotateImage_confirmed"]
[connection signal="item_selected" from="VBoxContainer/HBoxContainer2/OptionButton" to="." method="_on_OptionButton_item_selected"]
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/HSlider" to="." method="_on_HSlider_value_changed"]
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/SpinBox" to="." method="_on_SpinBox_value_changed"]
195 changes: 195 additions & 0 deletions Scripts/Global.gd
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,201 @@ func plot_circle(r : int) -> Array:
err += x * 2 + 1
return circle_points

func scale3X(sprite : Image, tol : float = 50) -> Image:
var scaled = Image.new()
scaled.create(sprite.get_width()*3, sprite.get_height()*3, false, Image.FORMAT_RGBA8)
scaled.lock()
sprite.lock()
var a : Color
var b : Color
var c : Color
var d : Color
var e : Color
var f : Color
var g : Color
var h : Color
var i : Color

for x in range(1,sprite.get_width()-1):
for y in range(1,sprite.get_height()-1):
var xs : float = 3*x
var ys : float = 3*y

a = sprite.get_pixel(x-1,y-1)
b = sprite.get_pixel(x,y-1)
c = sprite.get_pixel(x+1,y-1)
d = sprite.get_pixel(x-1,y)
e = sprite.get_pixel(x,y)
f = sprite.get_pixel(x+1,y)
g = sprite.get_pixel(x-1,y+1)
h = sprite.get_pixel(x,y+1)
i = sprite.get_pixel(x+1,y+1)

var db : bool = similarColors(d, b, tol)
var dh : bool = similarColors(d, h, tol)
var bf : bool = similarColors(f, b, tol)
var ec : bool = similarColors(e, c, tol)
var ea : bool = similarColors(e, a, tol)
var fh : bool = similarColors(f, h, tol)
var eg : bool = similarColors(e, g, tol)
var ei : bool = similarColors(e, i, tol)

scaled.set_pixel(xs-1, ys-1, d if (db and !dh and !bf) else e )
scaled.set_pixel(xs, ys-1, b if (db and !dh and !bf and !ec) or
(bf and !db and !fh and !ea) else e)
scaled.set_pixel(xs+1, ys-1, f if (bf and !db and !fh) else e)
scaled.set_pixel(xs-1, ys, d if (dh and !fh and !db and !ea) or
(db and !dh and !bf and !eg) else e)
scaled.set_pixel(xs, ys, e);
scaled.set_pixel(xs+1, ys, f if (bf and !db and !fh and !ei) or
(fh and !bf and !dh and !ec) else e)
scaled.set_pixel(xs-1, ys+1, d if (dh and !fh and !db) else e)
scaled.set_pixel(xs, ys+1, h if (fh and !bf and !dh and !eg) or
(dh and !fh and !db and !ei) else e)
scaled.set_pixel(xs+1, ys+1, f if (fh and !bf and !dh) else e)

scaled.unlock()
sprite.unlock()
return scaled

func rotxel(sprite : Image, angle : float):

# If angle is simple, then nn rotation is the best

if angle == 0 || angle == PI/2 || angle == PI || angle == 2*PI:
nn_rotate(sprite, angle)
return

var aux : Image = Image.new()
aux.copy_from(sprite)
var center : Vector2 = Vector2(sprite.get_width()/2, sprite.get_height()/2)
var ox : int
var oy : int
var p : Color
aux.lock()
sprite.lock()
for x in range(sprite.get_width()):
for y in range(sprite.get_height()):
var dx = 3*(x - center.x)
var dy = 3*(y - center.y)
var found_pixel : bool = false
for k in range(9):
var i = -1 + k % 3
var j = -1 + int(k / 3)
var dir = atan2(dy + j, dx + i)
var mag = sqrt(pow(dx + i, 2) + pow(dy + j, 2))
dir -= angle
ox = round(center.x*3 + 1 + mag*cos(dir))
oy = round(center.y*3 + 1 + mag*sin(dir))

if (sprite.get_width() % 2 != 0):
ox += 1
oy += 1

if (ox >= 0 && ox < sprite.get_width()*3
&& oy >= 0 && oy < sprite.get_height()*3):
found_pixel = true
break

if !found_pixel:
sprite.set_pixel(x, y, Color(0,0,0,0))
continue

var fil : int = oy % 3
var col : int = ox % 3
var index : int = col + 3*fil

ox = round((ox - 1)/3.0);
oy = round((oy - 1)/3.0);
var a : Color
var b : Color
var c : Color
var d : Color
var e : Color
var f : Color
var g : Color
var h : Color
var i : Color
if (ox == 0 || ox == sprite.get_width() - 1 ||
oy == 0 || oy == sprite.get_height() - 1):
p = aux.get_pixel(ox, oy)
else:
a = aux.get_pixel(ox-1,oy-1);
b = aux.get_pixel(ox,oy-1);
c = aux.get_pixel(ox+1,oy-1);
d = aux.get_pixel(ox-1,oy);
e = aux.get_pixel(ox,oy);
f = aux.get_pixel(ox+1,oy);
g = aux.get_pixel(ox-1,oy+1);
h = aux.get_pixel(ox,oy+1);
i = aux.get_pixel(ox+1,oy+1);

match(index):
0:
p = d if (similarColors(d,b) && !similarColors(d,h)
&& !similarColors(b,f)) else e;
1:
p = b if ((similarColors(d,b) && !similarColors(d,h) &&
!similarColors(b,f) && !similarColors(e,c)) ||
(similarColors(b,f) && !similarColors(d,b) &&
!similarColors(f,h) && !similarColors(e,a))) else e;
2:
p = f if (similarColors(b,f) && !similarColors(d,b) &&
!similarColors(f,h)) else e;
3:
p = d if ((similarColors(d,h) && !similarColors(f,h) &&
!similarColors(d,b) && !similarColors(e,a)) ||
(similarColors(d,b) && !similarColors(d,h) &&
!similarColors(b,f) && !similarColors(e,g))) else e;
4:
p = e
5:
p = f if((similarColors(b,f) && !similarColors(d,b) &&
!similarColors(f,h) && !similarColors(e,i))
|| (similarColors(f,h) && !similarColors(b,f) &&
!similarColors(d,h) && !similarColors(e,c))) else e;
6:
p = d if (similarColors(d,h) && !similarColors(f,h) &&
!similarColors(d,b)) else e;
7:
p = h if ((similarColors(f,h) && !similarColors(f,b) &&
!similarColors(d,h) && !similarColors(e,g))
|| (similarColors(d,h) && !similarColors(f,h) &&
!similarColors(d,b) && !similarColors(e,i))) else e;
8:
p = f if (similarColors(f,h) && !similarColors(f,b) &&
!similarColors(d,h)) else e;
sprite.set_pixel(x, y, p)
sprite.unlock()
aux.unlock()

func nn_rotate(sprite : Image, angle : float):
var aux : Image = Image.new()
aux.copy_from(sprite)
sprite.lock()
aux.lock()
var ox: int
var oy: int
var center : Vector2 = Vector2(sprite.get_width()/2, sprite.get_height()/2)
for x in range(sprite.get_width()):
for y in range(sprite.get_height()):
ox = (x - center.x)*cos(angle) + (y - center.y)*sin(angle) + center.x
oy = -(x - center.x)*sin(angle) + (y - center.y)*cos(angle) + center.y
if ox >= 0 && ox < sprite.get_width() && oy >= 0 && oy < sprite.get_height():
sprite.set_pixel(x, y, aux.get_pixel(ox, oy))
else:
sprite.set_pixel(x, y, Color(0,0,0,0))
sprite.unlock()
aux.unlock()

func similarColors(c1 : Color, c2 : Color, tol : float = 100) -> bool:
var dist = colorDistance(c1, c2)
return dist <= tol

func colorDistance(c1 : Color, c2 : Color) -> float:
return sqrt(pow((c1.r - c2.r)*255, 2) + pow((c1.g - c2.g)*255, 2)
+ pow((c1.b - c2.b)*255, 2) + pow((c1.a - c2.a)*255, 2))

func _exit_tree() -> void:
config_cache.set_value("window", "screen", OS.current_screen)
config_cache.set_value("window", "maximized", OS.window_maximized || OS.window_fullscreen)
Expand Down
7 changes: 6 additions & 1 deletion Scripts/Main.gd
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ func _ready() -> void:
"Flip Vertical" : KEY_MASK_SHIFT + KEY_V,
"Invert colors" : 0,
"Desaturation" : 0,
"Outline" : 0
"Outline" : 0,
"Rotate Image" : 0
}
var help_menu_items := {
"View Splash Screen" : 0,
Expand Down Expand Up @@ -346,6 +347,10 @@ func image_menu_id_pressed(id : int) -> void:
Global.canvas.handle_redo("Draw")
6: # Outline
$OutlineDialog.popup_centered()
7: # Rotate
var image : Image = Global.canvas.layers[Global.canvas.current_layer_index][0]
$RotateImage.set_sprite(image)
$RotateImage.popup_centered()

func help_menu_id_pressed(id : int) -> void:
match id:
Expand Down