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

Merge pull request #143 from azagaya/master

Added 2 rotation algorithms and popup for rotating current layer
This commit is contained in:
Manolis Papadeas 2020-02-04 18:14:32 +02:00 committed by GitHub
commit c4229e1759
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 359 additions and 9 deletions

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=64 format=2]
[gd_scene load_steps=65 format=2]
[ext_resource path="res://Themes & Styles/Dark Theme/Dark Theme.tres" type="Theme" id=1]
[ext_resource path="res://Scripts/Main.gd" type="Script" id=2]
@ -53,6 +53,7 @@
[ext_resource path="res://Prefabs/EditPalettePopup.tscn" type="PackedScene" id=51]
[ext_resource path="res://Prefabs/NewPaletteDialog.tscn" type="PackedScene" id=52]
[ext_resource path="res://Prefabs/PaletteImportFileDialog.tscn" type="PackedScene" id=53]
[ext_resource path="res://Prefabs/Dialogs/RotateImage.tscn" type="PackedScene" id=54]
[sub_resource type="InputEventKey" id=1]
scancode = 88
@ -1401,17 +1402,18 @@ columns = 5
[node name="SplashDialog" parent="." instance=ExtResource( 43 )]
[node name="CreateNewImage" parent="." instance=ExtResource( 44 )]
window_title = "Confirmá, por favor..."
[node name="OpenSprite" type="FileDialog" parent="."]
margin_right = 515.0
margin_bottom = 348.0
window_title = "Open a File"
window_title = "Abrir un Archivo"
resizable = true
mode = 0
access = 2
filters = PoolStringArray( "*.pxo ; Pixelorama Project" )
current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama"
current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/"
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
[node name="SaveSprite" type="FileDialog" parent="."]
anchor_left = 0.5
@ -1426,12 +1428,17 @@ window_title = "Save Sprite as .pxo"
resizable = true
access = 2
filters = PoolStringArray( "*.pxo ; Pixelorama Project" )
current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama"
current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/"
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
[node name="ImportSprites" parent="." instance=ExtResource( 45 )]
window_title = "Abrir Archivo(s)"
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
[node name="ExportSprites" parent="." instance=ExtResource( 46 )]
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
[node name="ScaleImage" parent="." instance=ExtResource( 47 )]
@ -1439,12 +1446,14 @@ current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρι
[node name="OutlineDialog" parent="." instance=ExtResource( 49 )]
visible = false
window_title = "Confirmá, por favor..."
[node name="AboutDialog" parent="." instance=ExtResource( 50 )]
[node name="QuitDialog" type="ConfirmationDialog" parent="."]
margin_right = 200.0
margin_bottom = 70.0
window_title = "Confirmá, por favor..."
resizable = true
dialog_text = "QUIT_LABEL"
@ -1460,15 +1469,19 @@ visible = false
[node name="NewPaletteDialog" parent="." instance=ExtResource( 52 )]
[node name="PaletteImportFileDialog" parent="." instance=ExtResource( 53 )]
window_title = "Abrir un Archivo"
filters = PoolStringArray( "*.json ; JavaScript Object Notation", "*.gpl ; Gimp Palette Library" )
current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama"
current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/"
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
[node name="LeftCursor" type="Sprite" parent="."]
visible = false
[node name="RightCursor" type="Sprite" parent="."]
visible = false
[node name="RotateImage" parent="." instance=ExtResource( 54 )]
visible = false
[connection signal="pressed" from="MenuAndUI/UI/ToolPanel/Tools/ColorAndToolOptions/ColorButtonsVertical/ColorSwitchCenter/ColorSwitch" to="." method="_on_ColorSwitch_pressed"]
[connection signal="color_changed" from="MenuAndUI/UI/ToolPanel/Tools/ColorAndToolOptions/ColorButtonsVertical/ColorPickersCenter/ColorPickersHorizontal/LeftColorPickerButton" to="." method="_on_LeftColorPickerButton_color_changed"]
[connection signal="popup_closed" from="MenuAndUI/UI/ToolPanel/Tools/ColorAndToolOptions/ColorButtonsVertical/ColorPickersCenter/ColorPickersHorizontal/LeftColorPickerButton" to="." method="_can_draw_true"]

View file

@ -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

View file

@ -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"]

View file

@ -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)

View file

@ -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,
@ -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: