mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-20 12:33:14 +00:00
New selection system (#474)
* Basic move tool * Added marching ants effect on the selection borders * Rename SelectionRectangle to SelectionShape, make it have non-rectangular shape and multiple SelectionShapes can exist - Create multiple selection rectangles - Merge them together if they intersect - Move the selections (without contents as of right now) - Gizmos are being drawn but they are not functional yet Code is very ugly. * Sort vectors counter-clockwise to be used as polygon borders I did this, no idea if it works properly, probably won't be used but I thought I'd keep it saved somewhere * More experiments I may or may not need Trying to generate a polygon from the individual selected pixels * Change default rectangle select behavior and ability to clip polygons using Control * Fix rectangle selection clipping * Split polygon into two with selection subtracting * Move selection with contents with the move tool Code is still a mess, don't bother looking. * Move some methods from SelectionShape.gd to Selection.gd The purpose of this is to generalize some selection code, so that it applies to all polygons, the entire selection. More will follow. * UndoRedo for border moving Nothing else in the selections system works properly in UndoRedo right now. Needs: - UR support for creating selections - UR support for modifying selections (merging and cutting selections together) - UR support for removing selection - UR support for moving content & for all the rest of the remaining features * Moving all of the selection shape logic to Selection.gd Handle all of the polygons there instead of having them as individual nodes. Should be easier to handle undo/redo this way. This commit probably breaks move tool + selection tool and undo/redo. Code is still a mess. For your sanity, I hope you are not reading this. I promise I will clean up. * Move tool works again Buggy and messy, of course. * Remove unneeded code and restore selection move undoredo logic * Made Selection.gd have one big preview_image for when moving content, instead of each polygon having its own image Could be further optimized for some specific cases. We could also remove selected_pixels from SelectionPolygon. * UndoRedo support for creating, deleting, merging and clipping selections UndoRedo support for moving content not added in this commit. Should work but needs more testing. This PR also removes selected_pixels from the SelectionPolygon class. * Confirm & cancel selection movement, should support undoredo properly too Press Enter or do any editing to confirm movement, Escape to cancel. I will most likely add UI buttons for confirm and cancel too. * Mirror View affects selection * Restore Cut, Copy, Paste and Clear Selection Pasting now no longer requires a pre-existing selection and instead copies the selections themselves too. * Created a new Select menu, which has Select All and Clear Selection as options Clear Selection now also confirms content moving. TopMenuContainer code has changed to no longer rely on Global for the menu buttons. * Draw gizmos as rectangles No functionality yet. They may need to be turned to nodes, so that they can easily resize based on zoom level and check for mouse enter/exit events. * Made gizmos get drawn in the sides and corners of the big bounding rectangle instead of individual selection parts Still no functionality yet. * Restore label text * Minor optimization when clipping selections This will execute the for loop less times * Made a Gizmo class, cursor change on hover, has_focus = false on mouse click Now I should actually make them resize when dragged, aye? * Very basic gizmo resizing, still a WIP, does not work properly yet * Start replacing the array of selected pixels with a BitMap This should optimize the selection making a lot, and it also allows for easy border drawing without having to deal with polygons, thanks to the MarchingAntsOutline.shader Still commit is still a WIP, image effects and brushes may not work properly yet. Because the BitMap has a fixed size, the size of the project, moving the selection outside of canvas boundaries has proven to be a bit tricky. I did implement a hacky way of handling it, but it may be buggy and problematic. I'm still unsure whether this is the best way to handle the situation. * Selection works with mirror view * Draw a black rectangle when the user is making a rectangular selection After they release the mouse, the black rectangle becomes the selection * Make Selection.gd update when undoing/redoing * Fix brushes not working properly with non-rectangular selections * Added invert selection * Cache has_selection as a variable for a speedup * Fix conflict issues with the shape tools * Made the bitmap image squared so the marching ants effect will be the same on both dimensions There may be a better way to fix the issue, perhaps inside the shader itself. * Some optimizations to call selection_bitmap_changed() less times * Restored almost all of the image effects Left to do: - Change gradient's behavior. Unsure of how it will work with non-rectangular selections yet, but it should not generate pixels outside of the selection. - Restore rotation - Resize bitmap on image resize - Remove the `pixels` array from the ImageEffect * Fix Selection.gd not updating when changing project * Resize the selection bitmap along with image resize * Restored rotation's old behavior and finally got rid of the selected_pixels array The rotation does not yet work properly with selections, but at least it now "works". * Resize selection too when using gizmos Left to do for gizmos: - Proper cancel transformation - Begin transformation (currently named move_content_start but it should be renamed to something more general) when resizing gizmos - Keep the original image and selection in memory and resize them. Meaning, gizmos should not resize the already resized data, but only resize the original. This is less destructive as there is no danger of data loss. - Always resize on InputEventMouseMotion. This is going to be worse for performance, but it will look better for the user. * Image and bitmap resizing now uses the original data and begin transformation on gizmo click No matter how many times the user resizes on the current transformation, the original data will not be lost until they either confirm or cancel, so there is no data loss before confirmation/cancel. * Cancel transformation now works properly when the selection has been resized * Made gizmos resize on mouse motion, fix issues with negative bounding rectangle and when combined with the move tool * Resizing can now get out of positive bounds, clearing and inverting now gets limited to the canvas bounds Resizing currently does not work properly with negative (left & up) canvas boundaries * Flip image when resizing and the bounding rectangle gets flipped * Call move_content_confirm() when inverting selection * Attempt to implement selection resizing that goes outside of the canvas boundaries (not working properly yet) * Flip selection when resizing to negative bounding rectangle sizes And fix preview_image vertical flipping * Fix rotation so that it works (almost) properly with selections Rotation algorithms now accept and only work with a given image, and the pivot has been added as a parameter * Experimental gizmo rotation - does not work properly yet Transforming the selection outside of the canvas is still broken. * Fix some issues with moving selection out of canvas bounds * Fix more issues with selection getting resized outside of canvas bounds * Update marching ants effect properly when switching between projects And make sure the frequency of the marching ants effect always looks roughly the same on all project sizes * Made the rotation gizmo part of the gizmos array and resize them based on camera zoom * Remove unneeded parameter from move_bitmap_values() * Remove more unneeded parameters * Move the selection only if the cursor is above it and neither shift nor control are currently pressed * Gradient generation now works on non-rectangular selections Although this behavior might not be the intended one * Copy/paste marching ants effect offset Useful for when the selection is in negative coords * Fix issue with clear selection & UndoRedo * Restore the ability to move selection when it's in negative coords * Made the marching ants offset a Project variable This fixes the issue of project switching and keeping the previous project's offset. Again, this is only relevant for when the selection is in negative coords. * Made the "from current selection" palette preset work with the new selection system * Fix out of bounds error when using the rectangular select tool on negative coords * Some code cleanup * Comment out the rotation gizmo for now, since it does not work properly * Update marching ants shader params and gizmo sizes when the bitmap changes * Move some methods around in Selection.gd
This commit is contained in:
parent
0c54470209
commit
27cb0d2d2f
40 changed files with 1468 additions and 784 deletions
|
@ -488,6 +488,26 @@ right_ellipsetool_tool={
|
|||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":true,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":67,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
left_move_tool={
|
||||
"deadzone": 0.5,
|
||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":84,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
right_move_tool={
|
||||
"deadzone": 0.5,
|
||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":true,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":84,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
select_all={
|
||||
"deadzone": 0.5,
|
||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":true,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
invert_selection={
|
||||
"deadzone": 0.5,
|
||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":true,"pressed":false,"scancode":73,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[locale]
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ enum GradientDirection {TOP, BOTTOM, LEFT, RIGHT}
|
|||
|
||||
|
||||
func scale3X(sprite : Image, tol : float = 50) -> Image:
|
||||
var scaled = Image.new()
|
||||
var scaled := Image.new()
|
||||
scaled.create(sprite.get_width()*3, sprite.get_height()*3, false, Image.FORMAT_RGBA8)
|
||||
scaled.lock()
|
||||
sprite.lock()
|
||||
|
@ -62,163 +62,142 @@ func scale3X(sprite : Image, tol : float = 50) -> Image:
|
|||
return scaled
|
||||
|
||||
|
||||
func rotxel(sprite : Image, angle : float, pixels : Array) -> void:
|
||||
func rotxel(sprite : Image, angle : float, pivot : Vector2) -> void:
|
||||
# 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, pixels)
|
||||
nn_rotate(sprite, angle, pivot)
|
||||
return
|
||||
|
||||
var aux : Image = Image.new()
|
||||
aux.copy_from(sprite)
|
||||
var selection_rectangle := Rect2(pixels[0].x, pixels[0].y, pixels[-1].x - pixels[0].x + 1, pixels[-1].y - pixels[0].y + 1)
|
||||
var center : Vector2 = selection_rectangle.position + ((selection_rectangle.end - selection_rectangle.position) / 2)
|
||||
var ox : int
|
||||
var oy : int
|
||||
var p : Color
|
||||
aux.lock()
|
||||
sprite.lock()
|
||||
for pix in pixels:
|
||||
var x = pix.x
|
||||
var y = pix.y
|
||||
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
|
||||
for x in sprite.get_size().x:
|
||||
for y in sprite.get_size().y:
|
||||
var dx = 3*(x - pivot.x)
|
||||
var dy = 3*(y - pivot.y)
|
||||
var found_pixel : bool = false
|
||||
for k in range(9):
|
||||
var i = -1 + k % 3
|
||||
# warning-ignore:integer_division
|
||||
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))
|
||||
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(pivot.x*3 + 1 + mag*cos(dir))
|
||||
oy = round(pivot.y*3 + 1 + mag*sin(dir))
|
||||
|
||||
if (sprite.get_width() % 2 != 0):
|
||||
ox += 1
|
||||
oy += 1
|
||||
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 (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
|
||||
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
|
||||
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);
|
||||
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)
|
||||
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 fake_rotsprite(sprite : Image, angle : float, pixels : Array) -> void:
|
||||
var selection_rectangle := Rect2(pixels[0].x, pixels[0].y, pixels[-1].x - pixels[0].x + 1, pixels[-1].y - pixels[0].y + 1)
|
||||
func fake_rotsprite(sprite : Image, angle : float, pivot : Vector2) -> void:
|
||||
var selected_sprite := Image.new()
|
||||
selected_sprite = sprite.get_rect(selection_rectangle)
|
||||
selected_sprite.copy_from(sprite)
|
||||
selected_sprite.copy_from(scale3X(selected_sprite))
|
||||
nn_rotate(selected_sprite, angle, [])
|
||||
nn_rotate(selected_sprite, angle, pivot * 3)
|
||||
# warning-ignore:integer_division
|
||||
# warning-ignore:integer_division
|
||||
selected_sprite.resize(selected_sprite.get_width() / 3, selected_sprite.get_height() / 3, 0)
|
||||
sprite.blit_rect(selected_sprite, Rect2(Vector2.ZERO, selected_sprite.get_size()), selection_rectangle.position)
|
||||
sprite.blit_rect(selected_sprite, Rect2(Vector2.ZERO, selected_sprite.get_size()), Vector2.ZERO)
|
||||
|
||||
|
||||
func nn_rotate(sprite : Image, angle : float, pixels : Array) -> void:
|
||||
func nn_rotate(sprite : Image, angle : float, pivot : Vector2) -> void:
|
||||
var aux : Image = Image.new()
|
||||
aux.copy_from(sprite)
|
||||
sprite.lock()
|
||||
aux.lock()
|
||||
var ox: int
|
||||
var oy: int
|
||||
var center : Vector2
|
||||
if pixels:
|
||||
var selection_rectangle := Rect2(pixels[0].x, pixels[0].y, pixels[-1].x - pixels[0].x + 1, pixels[-1].y - pixels[0].y + 1)
|
||||
center = selection_rectangle.position + ((selection_rectangle.end - selection_rectangle.position) / 2)
|
||||
for pix in pixels:
|
||||
var x = pix.x
|
||||
var y = pix.y
|
||||
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
|
||||
for x in range(sprite.get_width()):
|
||||
for y in range(sprite.get_height()):
|
||||
ox = (x - pivot.x)*cos(angle) + (y - pivot.y)*sin(angle) + pivot.x
|
||||
oy = -(x - pivot.x)*sin(angle) + (y - pivot.y)*cos(angle) + pivot.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))
|
||||
else:
|
||||
# warning-ignore:integer_division
|
||||
# warning-ignore:integer_division
|
||||
center = 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()
|
||||
|
||||
|
@ -256,6 +235,7 @@ func scale_image(width : int, height : int, interpolation : int) -> void:
|
|||
|
||||
|
||||
func centralize() -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
# Find used rect of the current frame (across all of the layers)
|
||||
var used_rect := Rect2()
|
||||
for cel in Global.current_project.frames[Global.current_project.current_frame].cels:
|
||||
|
@ -277,6 +257,7 @@ func centralize() -> void:
|
|||
|
||||
|
||||
func crop_image(image : Image) -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
# Use first cel as a starting rectangle
|
||||
var used_rect : Rect2 = image.get_used_rect()
|
||||
|
||||
|
@ -325,82 +306,101 @@ func resize_canvas(width : int, height : int, offset_x : int, offset_y : int) ->
|
|||
|
||||
|
||||
func general_do_scale(width : int, height : int) -> void:
|
||||
var x_ratio = Global.current_project.size.x / width
|
||||
var y_ratio = Global.current_project.size.y / height
|
||||
var new_x_symmetry_point = Global.current_project.x_symmetry_point / x_ratio
|
||||
var new_y_symmetry_point = Global.current_project.y_symmetry_point / y_ratio
|
||||
var new_x_symmetry_axis_points = Global.current_project.x_symmetry_axis.points
|
||||
var new_y_symmetry_axis_points = Global.current_project.y_symmetry_axis.points
|
||||
var project := Global.current_project
|
||||
var size := Vector2(width, height).floor()
|
||||
var x_ratio = project.size.x / width
|
||||
var y_ratio = project.size.y / height
|
||||
|
||||
var bitmap : BitMap
|
||||
bitmap = project.resize_bitmap(project.selection_bitmap, size)
|
||||
|
||||
var new_x_symmetry_point = project.x_symmetry_point / x_ratio
|
||||
var new_y_symmetry_point = project.y_symmetry_point / y_ratio
|
||||
var new_x_symmetry_axis_points = project.x_symmetry_axis.points
|
||||
var new_y_symmetry_axis_points = project.y_symmetry_axis.points
|
||||
new_x_symmetry_axis_points[0].y /= y_ratio
|
||||
new_x_symmetry_axis_points[1].y /= y_ratio
|
||||
new_y_symmetry_axis_points[0].x /= x_ratio
|
||||
new_y_symmetry_axis_points[1].x /= x_ratio
|
||||
|
||||
Global.current_project.undos += 1
|
||||
Global.current_project.undo_redo.create_action("Scale")
|
||||
Global.current_project.undo_redo.add_do_property(Global.current_project, "size", Vector2(width, height).floor())
|
||||
Global.current_project.undo_redo.add_do_property(Global.current_project, "x_symmetry_point", new_x_symmetry_point)
|
||||
Global.current_project.undo_redo.add_do_property(Global.current_project, "y_symmetry_point", new_y_symmetry_point)
|
||||
Global.current_project.undo_redo.add_do_property(Global.current_project.x_symmetry_axis, "points", new_x_symmetry_axis_points)
|
||||
Global.current_project.undo_redo.add_do_property(Global.current_project.y_symmetry_axis, "points", new_y_symmetry_axis_points)
|
||||
project.undos += 1
|
||||
project.undo_redo.create_action("Scale")
|
||||
project.undo_redo.add_do_property(project, "size", size)
|
||||
project.undo_redo.add_do_property(project, "selection_bitmap", bitmap)
|
||||
project.undo_redo.add_do_property(project, "x_symmetry_point", new_x_symmetry_point)
|
||||
project.undo_redo.add_do_property(project, "y_symmetry_point", new_y_symmetry_point)
|
||||
project.undo_redo.add_do_property(project.x_symmetry_axis, "points", new_x_symmetry_axis_points)
|
||||
project.undo_redo.add_do_property(project.y_symmetry_axis, "points", new_y_symmetry_axis_points)
|
||||
|
||||
|
||||
func general_undo_scale() -> void:
|
||||
Global.current_project.undo_redo.add_undo_property(Global.current_project, "size", Global.current_project.size)
|
||||
Global.current_project.undo_redo.add_undo_property(Global.current_project, "x_symmetry_point", Global.current_project.x_symmetry_point)
|
||||
Global.current_project.undo_redo.add_undo_property(Global.current_project, "y_symmetry_point", Global.current_project.y_symmetry_point)
|
||||
Global.current_project.undo_redo.add_undo_property(Global.current_project.x_symmetry_axis, "points", Global.current_project.x_symmetry_axis.points)
|
||||
Global.current_project.undo_redo.add_undo_property(Global.current_project.y_symmetry_axis, "points", Global.current_project.y_symmetry_axis.points)
|
||||
Global.current_project.undo_redo.add_undo_method(Global, "undo")
|
||||
Global.current_project.undo_redo.add_do_method(Global, "redo")
|
||||
Global.current_project.undo_redo.commit_action()
|
||||
var project := Global.current_project
|
||||
project.undo_redo.add_undo_property(project, "size", project.size)
|
||||
project.undo_redo.add_undo_property(project, "selection_bitmap", project.selection_bitmap)
|
||||
project.undo_redo.add_undo_property(project, "x_symmetry_point", project.x_symmetry_point)
|
||||
project.undo_redo.add_undo_property(project, "y_symmetry_point", project.y_symmetry_point)
|
||||
project.undo_redo.add_undo_property(project.x_symmetry_axis, "points", project.x_symmetry_axis.points)
|
||||
project.undo_redo.add_undo_property(project.y_symmetry_axis, "points", project.y_symmetry_axis.points)
|
||||
project.undo_redo.add_undo_method(Global, "undo")
|
||||
project.undo_redo.add_do_method(Global, "redo")
|
||||
project.undo_redo.commit_action()
|
||||
|
||||
|
||||
func general_do_centralize() -> void:
|
||||
Global.current_project.undos += 1
|
||||
Global.current_project.undo_redo.create_action("Centralize")
|
||||
var project := Global.current_project
|
||||
project.undos += 1
|
||||
project.undo_redo.create_action("Centralize")
|
||||
|
||||
|
||||
func general_undo_centralize() -> void:
|
||||
Global.current_project.undo_redo.add_undo_method(Global, "undo")
|
||||
Global.current_project.undo_redo.add_do_method(Global, "redo")
|
||||
Global.current_project.undo_redo.commit_action()
|
||||
var project := Global.current_project
|
||||
project.undo_redo.add_undo_method(Global, "undo")
|
||||
project.undo_redo.add_do_method(Global, "redo")
|
||||
project.undo_redo.commit_action()
|
||||
|
||||
|
||||
func invert_image_colors(image : Image, pixels : Array, red := true, green := true, blue := true, alpha := false) -> void:
|
||||
func invert_image_colors(image : Image, affect_selection : bool, project : Project, red := true, green := true, blue := true, alpha := false) -> void:
|
||||
image.lock()
|
||||
for i in pixels:
|
||||
var px_color := image.get_pixelv(i)
|
||||
# Manually invert each color channel
|
||||
if red:
|
||||
px_color.r = 1.0 - px_color.r
|
||||
if green:
|
||||
px_color.g = 1.0 - px_color.g
|
||||
if blue:
|
||||
px_color.b = 1.0 - px_color.b
|
||||
if alpha:
|
||||
px_color.a = 1.0 - px_color.a
|
||||
image.set_pixelv(i, px_color)
|
||||
for x in project.size.x:
|
||||
for y in project.size.y:
|
||||
var pos := Vector2(x, y)
|
||||
if affect_selection and !project.can_pixel_get_drawn(pos):
|
||||
continue
|
||||
var px_color := image.get_pixelv(pos)
|
||||
# Manually invert each color channel
|
||||
if red:
|
||||
px_color.r = 1.0 - px_color.r
|
||||
if green:
|
||||
px_color.g = 1.0 - px_color.g
|
||||
if blue:
|
||||
px_color.b = 1.0 - px_color.b
|
||||
if alpha:
|
||||
px_color.a = 1.0 - px_color.a
|
||||
image.set_pixelv(pos, px_color)
|
||||
|
||||
|
||||
func desaturate_image(image : Image, pixels : Array, red := true, green := true, blue := true, alpha := false) -> void:
|
||||
func desaturate_image(image : Image, affect_selection : bool, project : Project, red := true, green := true, blue := true, alpha := false) -> void:
|
||||
image.lock()
|
||||
for i in pixels:
|
||||
var px_color := image.get_pixelv(i)
|
||||
var gray = px_color.v
|
||||
if red:
|
||||
px_color.r = gray
|
||||
if green:
|
||||
px_color.g = gray
|
||||
if blue:
|
||||
px_color.b = gray
|
||||
if alpha:
|
||||
px_color.a = gray
|
||||
for x in project.size.x:
|
||||
for y in project.size.y:
|
||||
var pos := Vector2(x, y)
|
||||
if affect_selection and !project.can_pixel_get_drawn(pos):
|
||||
continue
|
||||
var px_color := image.get_pixelv(pos)
|
||||
var gray = px_color.v
|
||||
if red:
|
||||
px_color.r = gray
|
||||
if green:
|
||||
px_color.g = gray
|
||||
if blue:
|
||||
px_color.b = gray
|
||||
if alpha:
|
||||
px_color.a = gray
|
||||
|
||||
image.set_pixelv(i, px_color)
|
||||
image.set_pixelv(pos, px_color)
|
||||
|
||||
|
||||
func generate_outline(image : Image, pixels : Array, outline_color : Color, thickness : int, diagonal : bool, inside_image : bool) -> void:
|
||||
func generate_outline(image : Image, affect_selection : bool, project : Project, outline_color : Color, thickness : int, diagonal : bool, inside_image : bool) -> void:
|
||||
if image.is_invisible():
|
||||
return
|
||||
var new_image := Image.new()
|
||||
|
@ -408,169 +408,175 @@ func generate_outline(image : Image, pixels : Array, outline_color : Color, thic
|
|||
new_image.lock()
|
||||
image.lock()
|
||||
|
||||
for pos in pixels:
|
||||
var current_pixel := image.get_pixelv(pos)
|
||||
if current_pixel.a == 0:
|
||||
continue
|
||||
for x in project.size.x:
|
||||
for y in project.size.y:
|
||||
var pos := Vector2(x, y)
|
||||
var current_pixel := image.get_pixelv(pos)
|
||||
if affect_selection and !project.can_pixel_get_drawn(pos):
|
||||
continue
|
||||
if current_pixel.a == 0:
|
||||
continue
|
||||
|
||||
for i in range(1, thickness + 1):
|
||||
if inside_image:
|
||||
var outline_pos : Vector2 = pos + Vector2.LEFT # Left
|
||||
if outline_pos.x < 0 || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.RIGHT * (i - 1)
|
||||
if new_pos.x < Global.current_project.size.x:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
for i in range(1, thickness + 1):
|
||||
if inside_image:
|
||||
var outline_pos : Vector2 = pos + Vector2.LEFT # Left
|
||||
if outline_pos.x < 0 || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.RIGHT * (i - 1)
|
||||
if new_pos.x < Global.current_project.size.x:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + Vector2.RIGHT # Right
|
||||
if outline_pos.x >= Global.current_project.size.x || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.LEFT * (i - 1)
|
||||
outline_pos = pos + Vector2.RIGHT # Right
|
||||
if outline_pos.x >= Global.current_project.size.x || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.LEFT * (i - 1)
|
||||
if new_pos.x >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + Vector2.UP # Up
|
||||
if outline_pos.y < 0 || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.DOWN * (i - 1)
|
||||
if new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + Vector2.DOWN # Down
|
||||
if outline_pos.y >= Global.current_project.size.y || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.UP * (i - 1)
|
||||
if new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
if diagonal:
|
||||
outline_pos = pos + (Vector2.LEFT + Vector2.UP) # Top left
|
||||
if (outline_pos.x < 0 && outline_pos.y < 0) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.RIGHT + Vector2.DOWN) * (i - 1)
|
||||
if new_pos.x < Global.current_project.size.x && new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + (Vector2.LEFT + Vector2.DOWN) # Bottom left
|
||||
if (outline_pos.x < 0 && outline_pos.y >= Global.current_project.size.y) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.RIGHT + Vector2.UP) * (i - 1)
|
||||
if new_pos.x < Global.current_project.size.x && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + (Vector2.RIGHT + Vector2.UP) # Top right
|
||||
if (outline_pos.x >= Global.current_project.size.x && outline_pos.y < 0) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.LEFT + Vector2.DOWN) * (i - 1)
|
||||
if new_pos.x >= 0 && new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + (Vector2.RIGHT + Vector2.DOWN) # Bottom right
|
||||
if (outline_pos.x >= Global.current_project.size.x && outline_pos.y >= Global.current_project.size.y) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.LEFT + Vector2.UP) * (i - 1)
|
||||
if new_pos.x >= 0 && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
else:
|
||||
var new_pos : Vector2 = pos + Vector2.LEFT * i # Left
|
||||
if new_pos.x >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + Vector2.UP # Up
|
||||
if outline_pos.y < 0 || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.DOWN * (i - 1)
|
||||
if new_pos.y < Global.current_project.size.y:
|
||||
new_pos = pos + Vector2.RIGHT * i # Right
|
||||
if new_pos.x < Global.current_project.size.x:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + Vector2.DOWN # Down
|
||||
if outline_pos.y >= Global.current_project.size.y || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + Vector2.UP * (i - 1)
|
||||
new_pos = pos + Vector2.UP * i # Up
|
||||
if new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
if diagonal:
|
||||
outline_pos = pos + (Vector2.LEFT + Vector2.UP) # Top left
|
||||
if (outline_pos.x < 0 && outline_pos.y < 0) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.RIGHT + Vector2.DOWN) * (i - 1)
|
||||
if new_pos.x < Global.current_project.size.x && new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
new_pos = pos + Vector2.DOWN * i # Down
|
||||
if new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + (Vector2.LEFT + Vector2.DOWN) # Bottom left
|
||||
if (outline_pos.x < 0 && outline_pos.y >= Global.current_project.size.y) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.RIGHT + Vector2.UP) * (i - 1)
|
||||
if new_pos.x < Global.current_project.size.x && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + (Vector2.RIGHT + Vector2.UP) # Top right
|
||||
if (outline_pos.x >= Global.current_project.size.x && outline_pos.y < 0) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.LEFT + Vector2.DOWN) * (i - 1)
|
||||
if new_pos.x >= 0 && new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
outline_pos = pos + (Vector2.RIGHT + Vector2.DOWN) # Bottom right
|
||||
if (outline_pos.x >= Global.current_project.size.x && outline_pos.y >= Global.current_project.size.y) || image.get_pixelv(outline_pos).a == 0:
|
||||
var new_pos : Vector2 = pos + (Vector2.LEFT + Vector2.UP) * (i - 1)
|
||||
if diagonal:
|
||||
new_pos = pos + (Vector2.LEFT + Vector2.UP) * i # Top left
|
||||
if new_pos.x >= 0 && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a > 0:
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
else:
|
||||
var new_pos : Vector2 = pos + Vector2.LEFT * i # Left
|
||||
if new_pos.x >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
new_pos = pos + (Vector2.LEFT + Vector2.DOWN) * i # Bottom left
|
||||
if new_pos.x >= 0 && new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + Vector2.RIGHT * i # Right
|
||||
if new_pos.x < Global.current_project.size.x:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
new_pos = pos + (Vector2.RIGHT + Vector2.UP) * i # Top right
|
||||
if new_pos.x < Global.current_project.size.x && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + Vector2.UP * i # Up
|
||||
if new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + Vector2.DOWN * i # Down
|
||||
if new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
if diagonal:
|
||||
new_pos = pos + (Vector2.LEFT + Vector2.UP) * i # Top left
|
||||
if new_pos.x >= 0 && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + (Vector2.LEFT + Vector2.DOWN) * i # Bottom left
|
||||
if new_pos.x >= 0 && new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + (Vector2.RIGHT + Vector2.UP) * i # Top right
|
||||
if new_pos.x < Global.current_project.size.x && new_pos.y >= 0:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
new_pos = pos + (Vector2.RIGHT + Vector2.DOWN) * i # Bottom right
|
||||
if new_pos.x < Global.current_project.size.x && new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
new_pos = pos + (Vector2.RIGHT + Vector2.DOWN) * i # Bottom right
|
||||
if new_pos.x < Global.current_project.size.x && new_pos.y < Global.current_project.size.y:
|
||||
var new_pixel = image.get_pixelv(new_pos)
|
||||
if new_pixel.a == 0:
|
||||
new_image.set_pixelv(new_pos, outline_color)
|
||||
|
||||
image.unlock()
|
||||
new_image.unlock()
|
||||
image.copy_from(new_image)
|
||||
|
||||
|
||||
func adjust_hsv(img: Image, delta_h : float, delta_s : float, delta_v : float, pixels : Array) -> void:
|
||||
func adjust_hsv(img: Image, delta_h : float, delta_s : float, delta_v : float, affect_selection : bool, project : Project) -> void:
|
||||
img.lock()
|
||||
for i in pixels:
|
||||
var c : Color = img.get_pixelv(i)
|
||||
# Hue
|
||||
var hue = range_lerp(c.h,0,1,-180,180)
|
||||
hue = hue + delta_h
|
||||
for x in project.size.x:
|
||||
for y in project.size.y:
|
||||
var pos := Vector2(x, y)
|
||||
if affect_selection and !project.can_pixel_get_drawn(pos):
|
||||
continue
|
||||
var c : Color = img.get_pixelv(pos)
|
||||
# Hue
|
||||
var hue = range_lerp(c.h,0,1,-180,180)
|
||||
hue = hue + delta_h
|
||||
|
||||
while(hue >= 180):
|
||||
hue -= 360
|
||||
while(hue < -180):
|
||||
hue += 360
|
||||
while(hue >= 180):
|
||||
hue -= 360
|
||||
while(hue < -180):
|
||||
hue += 360
|
||||
|
||||
# Saturation
|
||||
var sat = c.s
|
||||
if delta_s > 0:
|
||||
sat = range_lerp(delta_s,0,100,c.s,1)
|
||||
elif delta_s < 0:
|
||||
sat = range_lerp(delta_s,-100,0,0,c.s)
|
||||
# Saturation
|
||||
var sat = c.s
|
||||
if delta_s > 0:
|
||||
sat = range_lerp(delta_s,0,100,c.s,1)
|
||||
elif delta_s < 0:
|
||||
sat = range_lerp(delta_s,-100,0,0,c.s)
|
||||
|
||||
# Value
|
||||
var val = c.v
|
||||
if delta_v > 0:
|
||||
val = range_lerp(delta_v,0,100,c.v,1)
|
||||
elif delta_v < 0:
|
||||
val = range_lerp(delta_v,-100,0,0,c.v)
|
||||
# Value
|
||||
var val = c.v
|
||||
if delta_v > 0:
|
||||
val = range_lerp(delta_v,0,100,c.v,1)
|
||||
elif delta_v < 0:
|
||||
val = range_lerp(delta_v,-100,0,0,c.v)
|
||||
|
||||
c.h = range_lerp(hue,-180,180,0,1)
|
||||
c.s = sat
|
||||
c.v = val
|
||||
img.set_pixelv(i,c)
|
||||
|
||||
img.unlock()
|
||||
c.h = range_lerp(hue,-180,180,0,1)
|
||||
c.s = sat
|
||||
c.v = val
|
||||
img.set_pixelv(pos, c)
|
||||
|
||||
|
||||
func generate_gradient(image : Image, colors : Array, steps := 2, direction : int = GradientDirection.TOP, pixels = Global.current_project.selected_pixels) -> void:
|
||||
func generate_gradient(image : Image, colors : Array, steps : int, direction : int, affect_selection : bool, project : Project) -> void:
|
||||
if colors.size() < 2:
|
||||
return
|
||||
|
||||
|
@ -584,8 +590,13 @@ func generate_gradient(image : Image, colors : Array, steps := 2, direction : in
|
|||
if direction == GradientDirection.BOTTOM or direction == GradientDirection.RIGHT:
|
||||
colors.invert()
|
||||
|
||||
var selection_rectangle := Rect2(pixels[0].x, pixels[0].y, pixels[-1].x - pixels[0].x + 1, pixels[-1].y - pixels[0].y + 1)
|
||||
var size := selection_rectangle.size
|
||||
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
|
||||
|
||||
|
@ -596,7 +607,9 @@ func generate_gradient(image : Image, colors : Array, steps := 2, direction : in
|
|||
var start = i * gradient_size
|
||||
var end = (i + 1) * gradient_size
|
||||
for yy in range(start, end):
|
||||
var pos : Vector2 = Vector2(xx, yy) + pixels[0]
|
||||
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:
|
||||
|
@ -606,5 +619,7 @@ func generate_gradient(image : Image, colors : Array, steps := 2, direction : in
|
|||
var start = i * gradient_size
|
||||
var end = (i + 1) * gradient_size
|
||||
for xx in range(start, end):
|
||||
var pos : Vector2 = Vector2(xx, yy) + pixels[0]
|
||||
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])
|
||||
|
|
|
@ -196,7 +196,7 @@ func export_processed_images(ignore_overwrites: bool, export_dialog: AcceptDialo
|
|||
# Store settings for quick export and when the dialog is opened again
|
||||
was_exported = true
|
||||
Global.current_project.was_exported = true
|
||||
Global.file_menu.get_popup().set_item_text(6, tr("Export") + " %s" % (file_name + file_format_string(file_format)))
|
||||
Global.top_menu_container.file_menu.set_item_text(6, tr("Export") + " %s" % (file_name + file_format_string(file_format)))
|
||||
|
||||
# Only show when not exporting gif - gif export finishes in thread
|
||||
if not (current_tab == ExportTab.ANIMATION and animation_type == AnimationType.ANIMATED):
|
||||
|
|
|
@ -114,16 +114,10 @@ var small_preview_viewport : ViewportContainer
|
|||
var camera : Camera2D
|
||||
var camera2 : Camera2D
|
||||
var camera_preview : Camera2D
|
||||
var selection_rectangle : Polygon2D
|
||||
var horizontal_ruler : BaseButton
|
||||
var vertical_ruler : BaseButton
|
||||
var transparent_checker : ColorRect
|
||||
|
||||
var file_menu : MenuButton
|
||||
var edit_menu : MenuButton
|
||||
var view_menu : MenuButton
|
||||
var image_menu : MenuButton
|
||||
var help_menu : MenuButton
|
||||
var cursor_position_label : Label
|
||||
var zoom_level_label : Label
|
||||
|
||||
|
@ -201,7 +195,7 @@ func _ready() -> void:
|
|||
# XDGDataDirs depends on it nyaa
|
||||
directory_module = XDGDataPaths.new()
|
||||
image_clipboard = Image.new()
|
||||
Input.set_custom_mouse_cursor(Global.cursor_image, Input.CURSOR_CROSS, Vector2(15, 15))
|
||||
Input.set_custom_mouse_cursor(cursor_image, Input.CURSOR_CROSS, Vector2(15, 15))
|
||||
|
||||
var root = get_tree().get_root()
|
||||
control = find_node_by_name(root, "Control")
|
||||
|
@ -218,16 +212,10 @@ func _ready() -> void:
|
|||
camera = find_node_by_name(main_viewport, "Camera2D")
|
||||
camera2 = find_node_by_name(root, "Camera2D2")
|
||||
camera_preview = find_node_by_name(root, "CameraPreview")
|
||||
selection_rectangle = find_node_by_name(root, "SelectionRectangle")
|
||||
horizontal_ruler = find_node_by_name(root, "HorizontalRuler")
|
||||
vertical_ruler = find_node_by_name(root, "VerticalRuler")
|
||||
transparent_checker = find_node_by_name(root, "TransparentChecker")
|
||||
|
||||
file_menu = find_node_by_name(root, "FileMenu")
|
||||
edit_menu = find_node_by_name(root, "EditMenu")
|
||||
view_menu = find_node_by_name(root, "ViewMenu")
|
||||
image_menu = find_node_by_name(root, "ImageMenu")
|
||||
help_menu = find_node_by_name(root, "HelpMenu")
|
||||
cursor_position_label = find_node_by_name(root, "CursorPosition")
|
||||
zoom_level_label = find_node_by_name(root, "ZoomLevel")
|
||||
|
||||
|
@ -362,7 +350,7 @@ func general_redo(project : Project = current_project) -> void:
|
|||
func undo(_frame_index := -1, _layer_index := -1, project : Project = current_project) -> void:
|
||||
general_undo(project)
|
||||
var action_name : String = project.undo_redo.get_current_action_name()
|
||||
if action_name == "Draw" or action_name == "Draw Shape" or action_name == "Rectangle Select" or action_name == "Scale" or action_name == "Centralize" or action_name == "Merge Layer" or action_name == "Link Cel" or action_name == "Unlink Cel":
|
||||
if action_name == "Draw" or action_name == "Draw Shape" or action_name == "Rectangle Select" or action_name == "Move Selection" or action_name == "Scale" or action_name == "Centralize" or action_name == "Merge Layer" or action_name == "Link Cel" or action_name == "Unlink Cel":
|
||||
if _layer_index > -1 and _frame_index > -1:
|
||||
canvas.update_texture(_layer_index, _frame_index, project)
|
||||
else:
|
||||
|
@ -370,11 +358,12 @@ func undo(_frame_index := -1, _layer_index := -1, project : Project = current_pr
|
|||
for j in project.layers.size():
|
||||
canvas.update_texture(j, i, project)
|
||||
|
||||
canvas.selection.update()
|
||||
if action_name == "Scale":
|
||||
canvas.camera_zoom()
|
||||
Global.canvas.grid.update()
|
||||
Global.canvas.pixel_grid.update()
|
||||
Global.cursor_position_label.text = "[%s×%s]" % [project.size.x, project.size.y]
|
||||
canvas.grid.update()
|
||||
canvas.pixel_grid.update()
|
||||
cursor_position_label.text = "[%s×%s]" % [project.size.x, project.size.y]
|
||||
|
||||
elif "Frame" in action_name:
|
||||
# This actually means that frames.size is one, but it hasn't been updated yet
|
||||
|
@ -396,7 +385,7 @@ func undo(_frame_index := -1, _layer_index := -1, project : Project = current_pr
|
|||
func redo(_frame_index := -1, _layer_index := -1, project : Project = current_project) -> void:
|
||||
general_redo(project)
|
||||
var action_name : String = project.undo_redo.get_current_action_name()
|
||||
if action_name == "Draw" or action_name == "Draw Shape" or action_name == "Rectangle Select" or action_name == "Scale" or action_name == "Centralize" or action_name == "Merge Layer" or action_name == "Link Cel" or action_name == "Unlink Cel":
|
||||
if action_name == "Draw" or action_name == "Draw Shape" or action_name == "Rectangle Select" or action_name == "Move Selection" or action_name == "Scale" or action_name == "Centralize" or action_name == "Merge Layer" or action_name == "Link Cel" or action_name == "Unlink Cel":
|
||||
if _layer_index > -1 and _frame_index > -1:
|
||||
canvas.update_texture(_layer_index, _frame_index, project)
|
||||
else:
|
||||
|
@ -404,11 +393,12 @@ func redo(_frame_index := -1, _layer_index := -1, project : Project = current_pr
|
|||
for j in project.layers.size():
|
||||
canvas.update_texture(j, i, project)
|
||||
|
||||
canvas.selection.update()
|
||||
if action_name == "Scale":
|
||||
canvas.camera_zoom()
|
||||
Global.canvas.grid.update()
|
||||
Global.canvas.pixel_grid.update()
|
||||
Global.cursor_position_label.text = "[%s×%s]" % [project.size.x, project.size.y]
|
||||
canvas.grid.update()
|
||||
canvas.pixel_grid.update()
|
||||
cursor_position_label.text = "[%s×%s]" % [project.size.x, project.size.y]
|
||||
|
||||
elif "Frame" in action_name:
|
||||
if project.frames.size() == 1: # Stop animating
|
||||
|
@ -432,6 +422,7 @@ func title_changed(value : String) -> void:
|
|||
|
||||
|
||||
func project_changed(value : int) -> void:
|
||||
canvas.selection.move_content_confirm()
|
||||
current_project_index = value
|
||||
current_project = projects[value]
|
||||
current_project.change_project()
|
||||
|
@ -477,9 +468,10 @@ func change_button_texturerect(texture_button : TextureRect, new_file_name : Str
|
|||
|
||||
|
||||
func update_hint_tooltips() -> void:
|
||||
var root = get_tree().get_root()
|
||||
var root = control
|
||||
var tool_buttons = root.find_node("ToolButtons")
|
||||
|
||||
var rect_select : BaseButton = find_node_by_name(root, "RectSelect")
|
||||
var rect_select : BaseButton = tool_buttons.find_node("RectSelect")
|
||||
rect_select.hint_tooltip = tr("""Rectangular Selection
|
||||
|
||||
%s for left mouse button
|
||||
|
@ -487,6 +479,13 @@ func update_hint_tooltips() -> void:
|
|||
|
||||
Press %s to move the content""") % [InputMap.get_action_list("left_rectangle_select_tool")[0].as_text(), InputMap.get_action_list("right_rectangle_select_tool")[0].as_text(), "Shift"]
|
||||
|
||||
var move_select : BaseButton = tool_buttons.find_node("Move")
|
||||
move_select.hint_tooltip = tr("""Move
|
||||
|
||||
%s for left mouse button
|
||||
%s for right mouse button""") % [InputMap.get_action_list("left_move_tool")[0].as_text(), InputMap.get_action_list("right_move_tool")[0].as_text()]
|
||||
|
||||
|
||||
var zoom_tool : BaseButton = find_node_by_name(root, "Zoom")
|
||||
zoom_tool.hint_tooltip = tr("""Zoom
|
||||
|
||||
|
@ -616,7 +615,7 @@ func save_project_to_recent_list(path : String) -> void:
|
|||
|
||||
|
||||
func update_recent_projects_submenu() -> void:
|
||||
for project in Global.recent_projects:
|
||||
for project in recent_projects:
|
||||
recent_projects_submenu.add_item(project.get_file())
|
||||
|
||||
func use_osx_shortcuts() -> void:
|
||||
|
|
|
@ -119,8 +119,8 @@ func open_pxo_file(path : String, untitled_backup : bool = false, replace_empty
|
|||
new_project.directory_path = Export.directory_path
|
||||
new_project.file_name = Export.file_name
|
||||
Export.was_exported = false
|
||||
Global.file_menu.get_popup().set_item_text(4, tr("Save") + " %s" % path.get_file())
|
||||
Global.file_menu.get_popup().set_item_text(6, tr("Export"))
|
||||
Global.top_menu_container.file_menu.set_item_text(4, tr("Save") + " %s" % path.get_file())
|
||||
Global.top_menu_container.file_menu.set_item_text(6, tr("Export"))
|
||||
|
||||
Global.save_project_to_recent_list(path)
|
||||
|
||||
|
@ -339,7 +339,7 @@ func save_pxo_file(path : String, autosave : bool, use_zstd_compression := true,
|
|||
Export.directory_path = path.get_base_dir()
|
||||
Export.was_exported = false
|
||||
project.was_exported = false
|
||||
Global.file_menu.get_popup().set_item_text(4, tr("Save") + " %s" % path.get_file())
|
||||
Global.top_menu_container.file_menu.set_item_text(4, tr("Save") + " %s" % path.get_file())
|
||||
|
||||
Global.save_project_to_recent_list(path)
|
||||
|
||||
|
|
|
@ -124,7 +124,12 @@ func create_new_palette_from_current_palette(name: String, comment: String) -> v
|
|||
func create_new_palette_from_current_selection(name: String, comment: String, width: int, height: int, add_alpha_colors: bool, get_colors_from: int):
|
||||
var new_palette: Palette = Palette.new(name, width, height, comment)
|
||||
var current_project = Global.current_project
|
||||
var pixels = current_project.selected_pixels.duplicate()
|
||||
var pixels := []
|
||||
for x in current_project.size.x:
|
||||
for y in current_project.size.y:
|
||||
var pos := Vector2(x, y)
|
||||
if current_project.selection_bitmap.get_bit(pos):
|
||||
pixels.append(pos)
|
||||
fill_new_palette_with_colors(pixels, new_palette, add_alpha_colors, get_colors_from)
|
||||
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ signal color_changed(color, button)
|
|||
|
||||
var _tools = {
|
||||
"RectSelect" : "res://src/Tools/RectSelect.tscn",
|
||||
"Move" : "res://src/Tools/Move.tscn",
|
||||
"Zoom" : "res://src/Tools/Zoom.tscn",
|
||||
"Pan" : "res://src/Tools/Pan.tscn",
|
||||
"ColorPicker" : "res://src/Tools/ColorPicker.tscn",
|
||||
|
|
|
@ -75,19 +75,9 @@ func set_pixel(image: Image, position: Vector2, color: Color) -> void:
|
|||
var mirror_y = project.y_symmetry_point - position.y
|
||||
var mirror_x_inside : bool
|
||||
var mirror_y_inside : bool
|
||||
var entire_image_selected : bool = project.selected_pixels.empty()
|
||||
if entire_image_selected:
|
||||
mirror_x_inside = mirror_x >= 0 and mirror_x < project.size.x
|
||||
mirror_y_inside = mirror_y >= 0 and mirror_y < project.size.y
|
||||
else:
|
||||
var selected_pixels_x := []
|
||||
var selected_pixels_y := []
|
||||
for i in project.selected_pixels:
|
||||
selected_pixels_x.append(i.x)
|
||||
selected_pixels_y.append(i.y)
|
||||
mirror_x_inside = project.can_pixel_get_drawn(Vector2(mirror_x, position.y))
|
||||
mirror_y_inside = project.can_pixel_get_drawn(Vector2(position.x, mirror_y))
|
||||
|
||||
mirror_x_inside = mirror_x in selected_pixels_x
|
||||
mirror_y_inside = mirror_y in selected_pixels_y
|
||||
|
||||
if horizontal_mirror and mirror_x_inside:
|
||||
drawers[1].set_pixel(image, Vector2(mirror_x, position.y), color, color_op)
|
||||
|
|
|
@ -6,7 +6,6 @@ class_name ImageEffect extends AcceptDialog
|
|||
enum {CEL, FRAME, ALL_FRAMES, ALL_PROJECTS}
|
||||
|
||||
var affect : int = CEL
|
||||
var pixels := []
|
||||
var current_cel : Image
|
||||
var current_frame : Image
|
||||
var preview_image : Image
|
||||
|
@ -33,6 +32,7 @@ func _ready() -> void:
|
|||
|
||||
|
||||
func _about_to_show() -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
current_cel = Global.current_project.frames[Global.current_project.current_frame].cels[Global.current_project.current_layer].image
|
||||
current_frame.resize(Global.current_project.size.x, Global.current_project.size.y)
|
||||
current_frame.fill(Color(0, 0, 0, 0))
|
||||
|
@ -48,39 +48,31 @@ func _about_to_show() -> void:
|
|||
func _confirmed() -> void:
|
||||
if affect == CEL:
|
||||
Global.canvas.handle_undo("Draw")
|
||||
commit_action(current_cel, pixels)
|
||||
commit_action(current_cel)
|
||||
Global.canvas.handle_redo("Draw")
|
||||
elif affect == FRAME:
|
||||
Global.canvas.handle_undo("Draw", Global.current_project, -1)
|
||||
for cel in Global.current_project.frames[Global.current_project.current_frame].cels:
|
||||
commit_action(cel.image, pixels)
|
||||
commit_action(cel.image)
|
||||
Global.canvas.handle_redo("Draw", Global.current_project, -1)
|
||||
|
||||
elif affect == ALL_FRAMES:
|
||||
Global.canvas.handle_undo("Draw", Global.current_project, -1, -1)
|
||||
for frame in Global.current_project.frames:
|
||||
for cel in frame.cels:
|
||||
commit_action(cel.image, pixels)
|
||||
commit_action(cel.image)
|
||||
Global.canvas.handle_redo("Draw", Global.current_project, -1, -1)
|
||||
|
||||
elif affect == ALL_PROJECTS:
|
||||
for project in Global.projects:
|
||||
var _pixels := []
|
||||
if selection_checkbox.pressed and project.selected_pixels:
|
||||
_pixels = project.selected_pixels.duplicate()
|
||||
else:
|
||||
for x in project.size.x:
|
||||
for y in project.size.y:
|
||||
_pixels.append(Vector2(x, y))
|
||||
|
||||
Global.canvas.handle_undo("Draw", project, -1, -1)
|
||||
for frame in project.frames:
|
||||
for cel in frame.cels:
|
||||
commit_action(cel.image, _pixels, project)
|
||||
commit_action(cel.image, project)
|
||||
Global.canvas.handle_redo("Draw", project, -1, -1)
|
||||
|
||||
|
||||
func commit_action(_cel : Image, _pixels : Array, _project : Project = Global.current_project) -> void:
|
||||
func commit_action(_cel : Image, _project : Project = Global.current_project) -> void:
|
||||
pass
|
||||
|
||||
|
||||
|
@ -88,15 +80,7 @@ func set_nodes() -> void:
|
|||
pass
|
||||
|
||||
|
||||
func _on_SelectionCheckBox_toggled(button_pressed : bool) -> void:
|
||||
pixels.clear()
|
||||
if button_pressed and Global.current_project.selected_pixels:
|
||||
pixels = Global.current_project.selected_pixels.duplicate()
|
||||
else:
|
||||
for x in Global.current_project.size.x:
|
||||
for y in Global.current_project.size.y:
|
||||
pixels.append(Vector2(x, y))
|
||||
|
||||
func _on_SelectionCheckBox_toggled(_button_pressed : bool) -> void:
|
||||
update_preview()
|
||||
|
||||
|
||||
|
@ -111,7 +95,8 @@ func update_preview() -> void:
|
|||
preview_image.copy_from(current_cel)
|
||||
_:
|
||||
preview_image.copy_from(current_frame)
|
||||
commit_action(preview_image, pixels)
|
||||
commit_action(preview_image)
|
||||
preview_image.unlock()
|
||||
preview_texture.create_from_image(preview_image, 0)
|
||||
preview.texture = preview_texture
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
class_name Project extends Reference
|
||||
# A class for project properties.
|
||||
|
||||
|
||||
var name := "" setget name_changed
|
||||
var size : Vector2 setget size_changed
|
||||
var undo_redo : UndoRedo
|
||||
|
@ -23,8 +22,10 @@ var y_symmetry_point
|
|||
var x_symmetry_axis : SymmetryGuide
|
||||
var y_symmetry_axis : SymmetryGuide
|
||||
|
||||
var selected_pixels := []
|
||||
var selected_rect := Rect2(0, 0, 0, 0) setget _set_selected_rect
|
||||
var selection_bitmap := BitMap.new()
|
||||
# This is useful for when the selection is outside of the canvas boundaries, on the left and/or above (negative coords)
|
||||
var selection_offset := Vector2.ZERO setget _selection_offset_changed
|
||||
var has_selection := false
|
||||
|
||||
# For every camera (currently there are 3)
|
||||
var cameras_zoom := [Vector2(0.15, 0.15), Vector2(0.15, 0.15), Vector2(0.15, 0.15)] # Array of Vector2
|
||||
|
@ -41,6 +42,7 @@ func _init(_frames := [], _name := tr("untitled"), _size := Vector2(64, 64)) ->
|
|||
frames = _frames
|
||||
name = _name
|
||||
size = _size
|
||||
selection_bitmap.create(size)
|
||||
update_tile_mode_rects()
|
||||
|
||||
undo_redo = UndoRedo.new()
|
||||
|
@ -74,20 +76,33 @@ func _init(_frames := [], _name := tr("untitled"), _size := Vector2(64, 64)) ->
|
|||
directory_path = OS.get_system_dir(OS.SYSTEM_DIR_DESKTOP)
|
||||
|
||||
|
||||
func select_all_pixels() -> void:
|
||||
clear_selection()
|
||||
for x in size.x:
|
||||
for y in size.y:
|
||||
selected_pixels.append(Vector2(x, y))
|
||||
func commit_undo() -> void:
|
||||
if Global.canvas.selection.is_moving_content:
|
||||
Global.canvas.selection.move_content_cancel()
|
||||
else:
|
||||
undo_redo.undo()
|
||||
|
||||
|
||||
func clear_selection() -> void:
|
||||
selected_pixels.clear()
|
||||
func commit_redo() -> void:
|
||||
Global.control.redone = true
|
||||
undo_redo.redo()
|
||||
Global.control.redone = false
|
||||
|
||||
|
||||
func _set_selected_rect(value : Rect2) -> void:
|
||||
selected_rect = value
|
||||
Global.selection_rectangle.set_rect(value)
|
||||
func selection_bitmap_changed() -> void:
|
||||
var image := Image.new()
|
||||
var image_texture := ImageTexture.new()
|
||||
has_selection = selection_bitmap.get_true_bit_count() > 0
|
||||
if has_selection:
|
||||
image = bitmap_to_image(selection_bitmap)
|
||||
image_texture.create_from_image(image, 0)
|
||||
Global.canvas.selection.marching_ants_outline.texture = image_texture
|
||||
|
||||
|
||||
func _selection_offset_changed(value : Vector2) -> void:
|
||||
selection_offset = value
|
||||
Global.canvas.selection.marching_ants_outline.offset = selection_offset
|
||||
Global.canvas.selection.update_on_zoom(Global.camera.zoom.x)
|
||||
|
||||
|
||||
func change_project() -> void:
|
||||
|
@ -146,9 +161,6 @@ func change_project() -> void:
|
|||
|
||||
self.animation_tags = animation_tags
|
||||
|
||||
# Change the selection rectangle
|
||||
Global.selection_rectangle.set_rect(selected_rect)
|
||||
|
||||
# Change the guides
|
||||
for guide in Global.canvas.get_children():
|
||||
if guide is Guide:
|
||||
|
@ -167,21 +179,12 @@ func change_project() -> void:
|
|||
for brush in brushes:
|
||||
Brushes.add_project_brush(brush)
|
||||
|
||||
var cameras = [Global.camera, Global.camera2, Global.camera_preview]
|
||||
var i := 0
|
||||
for camera in cameras:
|
||||
camera.zoom = cameras_zoom[i]
|
||||
camera.offset = cameras_offset[i]
|
||||
i += 1
|
||||
Global.zoom_level_label.text = str(round(100 / Global.camera.zoom.x)) + " %"
|
||||
Global.canvas.update()
|
||||
Global.canvas.grid.update()
|
||||
Global.canvas.pixel_grid.update()
|
||||
Global.transparent_checker._ready()
|
||||
Global.animation_timeline.fps_spinbox.value = fps
|
||||
Global.horizontal_ruler.update()
|
||||
Global.vertical_ruler.update()
|
||||
Global.preview_zoom_slider.value = -Global.camera_preview.zoom.x
|
||||
Global.cursor_position_label.text = "[%s×%s]" % [size.x, size.y]
|
||||
|
||||
Global.window_title = "%s - Pixelorama %s" % [name, Global.current_version]
|
||||
|
@ -192,9 +195,9 @@ func change_project() -> void:
|
|||
if save_path != "":
|
||||
Global.open_sprites_dialog.current_path = save_path
|
||||
Global.save_sprites_dialog.current_path = save_path
|
||||
Global.file_menu.get_popup().set_item_text(4, tr("Save") + " %s" % save_path.get_file())
|
||||
Global.top_menu_container.file_menu.set_item_text(4, tr("Save") + " %s" % save_path.get_file())
|
||||
else:
|
||||
Global.file_menu.get_popup().set_item_text(4, tr("Save"))
|
||||
Global.top_menu_container.file_menu.set_item_text(4, tr("Save"))
|
||||
|
||||
Export.directory_path = directory_path
|
||||
Export.file_name = file_name
|
||||
|
@ -202,13 +205,27 @@ func change_project() -> void:
|
|||
Export.was_exported = was_exported
|
||||
|
||||
if !was_exported:
|
||||
Global.file_menu.get_popup().set_item_text(6, tr("Export"))
|
||||
Global.top_menu_container.file_menu.set_item_text(6, tr("Export"))
|
||||
else:
|
||||
Global.file_menu.get_popup().set_item_text(6, tr("Export") + " %s" % (file_name + Export.file_format_string(file_format)))
|
||||
Global.top_menu_container.file_menu.set_item_text(6, tr("Export") + " %s" % (file_name + Export.file_format_string(file_format)))
|
||||
|
||||
for j in Global.TileMode.values():
|
||||
Global.tile_mode_submenu.set_item_checked(j, j == tile_mode)
|
||||
|
||||
# Change selection effect & bounding rectangle
|
||||
Global.canvas.selection.marching_ants_outline.offset = selection_offset
|
||||
selection_bitmap_changed()
|
||||
Global.canvas.selection.big_bounding_rectangle = get_selection_rectangle()
|
||||
Global.canvas.selection.big_bounding_rectangle.position += selection_offset
|
||||
Global.canvas.selection.update()
|
||||
|
||||
var i := 0
|
||||
for camera in [Global.camera, Global.camera2, Global.camera_preview]:
|
||||
camera.zoom = cameras_zoom[i]
|
||||
camera.offset = cameras_offset[i]
|
||||
camera.zoom_changed()
|
||||
i += 1
|
||||
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
var layer_data := []
|
||||
|
@ -363,7 +380,6 @@ func name_changed(value : String) -> void:
|
|||
func size_changed(value : Vector2) -> void:
|
||||
size = value
|
||||
update_tile_mode_rects()
|
||||
Global.selection_rectangle.set_rect(Global.selection_rectangle.get_rect())
|
||||
|
||||
|
||||
func frames_changed(value : Array) -> void:
|
||||
|
@ -582,3 +598,135 @@ func update_tile_mode_rects() -> void:
|
|||
|
||||
func is_empty() -> bool:
|
||||
return frames.size() == 1 and layers.size() == 1 and frames[0].cels[0].image.is_invisible() and animation_tags.size() == 0
|
||||
|
||||
|
||||
func can_pixel_get_drawn(pixel : Vector2) -> bool:
|
||||
if pixel.x < 0 or pixel.y < 0 or pixel.x >= size.x or pixel.y >= size.y:
|
||||
return false
|
||||
var selection_position : Vector2 = Global.canvas.selection.big_bounding_rectangle.position
|
||||
if selection_position.x < 0:
|
||||
pixel.x -= selection_position.x
|
||||
if selection_position.y < 0:
|
||||
pixel.y -= selection_position.y
|
||||
if has_selection:
|
||||
return selection_bitmap.get_bit(pixel)
|
||||
else:
|
||||
return true
|
||||
|
||||
|
||||
func invert_bitmap(bitmap : BitMap) -> void:
|
||||
for x in bitmap.get_size().x:
|
||||
for y in bitmap.get_size().y:
|
||||
var pos := Vector2(x, y)
|
||||
bitmap.set_bit(pos, !bitmap.get_bit(pos))
|
||||
|
||||
|
||||
# Unexposed BitMap class function - https://github.com/godotengine/godot/blob/master/scene/resources/bit_map.cpp#L605
|
||||
func resize_bitmap(bitmap : BitMap, new_size : Vector2) -> BitMap:
|
||||
if new_size == bitmap.get_size():
|
||||
return bitmap
|
||||
var new_bitmap := BitMap.new()
|
||||
new_bitmap.create(new_size)
|
||||
var lw = min(bitmap.get_size().x, new_size.x)
|
||||
var lh = min(bitmap.get_size().y, new_size.y)
|
||||
for x in lw:
|
||||
for y in lh:
|
||||
new_bitmap.set_bit(Vector2(x, y), bitmap.get_bit(Vector2(x, y)))
|
||||
|
||||
return new_bitmap
|
||||
|
||||
|
||||
# Unexposed BitMap class function - https://github.com/godotengine/godot/blob/master/scene/resources/bit_map.cpp#L622
|
||||
func bitmap_to_image(bitmap : BitMap) -> Image:
|
||||
var image := Image.new()
|
||||
var width := bitmap.get_size().x
|
||||
var height := bitmap.get_size().y
|
||||
var square_size = max(width, height)
|
||||
image.create(square_size, square_size, false, Image.FORMAT_LA8)
|
||||
image.lock()
|
||||
for x in width:
|
||||
for y in height:
|
||||
var pos := Vector2(x, y)
|
||||
var color = Color(1, 1, 1, 1) if bitmap.get_bit(pos) else Color(0, 0, 0, 0)
|
||||
image.set_pixelv(pos, color)
|
||||
image.unlock()
|
||||
return image
|
||||
|
||||
|
||||
func get_selection_rectangle(bitmap : BitMap = selection_bitmap) -> Rect2:
|
||||
var rect := Rect2(Vector2.ZERO, Vector2.ZERO)
|
||||
if bitmap.get_true_bit_count() > 0:
|
||||
var image : Image = bitmap_to_image(bitmap)
|
||||
rect = image.get_used_rect()
|
||||
return rect
|
||||
|
||||
|
||||
func move_bitmap_values(bitmap : BitMap) -> void:
|
||||
var selection_node = Global.canvas.selection
|
||||
var selection_position : Vector2 = selection_node.big_bounding_rectangle.position
|
||||
var selection_end : Vector2 = selection_node.big_bounding_rectangle.end
|
||||
|
||||
var image : Image = bitmap_to_image(bitmap)
|
||||
var selection_rect := image.get_used_rect()
|
||||
var smaller_image := image.get_rect(selection_rect)
|
||||
image.lock()
|
||||
image.fill(Color(0))
|
||||
var dst := selection_position
|
||||
var x_diff = selection_end.x - size.x
|
||||
var y_diff = selection_end.y - size.y
|
||||
var nw = max(size.x, size.x + x_diff)
|
||||
var nh = max(size.y, size.y + y_diff)
|
||||
|
||||
if selection_position.x < 0:
|
||||
nw -= selection_position.x
|
||||
self.selection_offset.x = selection_position.x
|
||||
dst.x = 0
|
||||
else:
|
||||
self.selection_offset.x = 0
|
||||
if selection_position.y < 0:
|
||||
nh -= selection_position.y
|
||||
self.selection_offset.y = selection_position.y
|
||||
dst.y = 0
|
||||
else:
|
||||
self.selection_offset.y = 0
|
||||
|
||||
if nw <= image.get_size().x:
|
||||
nw = image.get_size().x
|
||||
if nh <= image.get_size().y:
|
||||
nh = image.get_size().y
|
||||
|
||||
image.crop(nw, nh)
|
||||
image.blit_rect(smaller_image, Rect2(Vector2.ZERO, Vector2(nw, nh)), dst)
|
||||
bitmap.create_from_image_alpha(image)
|
||||
|
||||
|
||||
func resize_bitmap_values(bitmap : BitMap, new_size : Vector2, flip_x : bool, flip_y : bool) -> BitMap:
|
||||
var selection_node = Global.canvas.selection
|
||||
var selection_position : Vector2 = selection_node.big_bounding_rectangle.position
|
||||
var dst := selection_position
|
||||
var new_bitmap_size := size
|
||||
new_bitmap_size.x = max(size.x, abs(selection_position.x) + new_size.x)
|
||||
new_bitmap_size.y = max(size.y, abs(selection_position.y) + new_size.y)
|
||||
var new_bitmap := BitMap.new()
|
||||
var image : Image = bitmap_to_image(bitmap)
|
||||
var selection_rect := image.get_used_rect()
|
||||
var smaller_image := image.get_rect(selection_rect)
|
||||
if selection_position.x <= 0:
|
||||
self.selection_offset.x = selection_position.x
|
||||
dst.x = 0
|
||||
if selection_position.y <= 0:
|
||||
self.selection_offset.y = selection_position.y
|
||||
dst.y = 0
|
||||
image.lock()
|
||||
image.fill(Color(0))
|
||||
smaller_image.resize(new_size.x, new_size.y, Image.INTERPOLATE_NEAREST)
|
||||
if flip_x:
|
||||
smaller_image.flip_x()
|
||||
if flip_y:
|
||||
smaller_image.flip_y()
|
||||
if new_bitmap_size != size:
|
||||
image.crop(new_bitmap_size.x, new_bitmap_size.y)
|
||||
image.blit_rect(smaller_image, Rect2(Vector2.ZERO, new_bitmap_size), dst)
|
||||
new_bitmap.create_from_image_alpha(image)
|
||||
|
||||
return new_bitmap
|
||||
|
|
23
src/Main.gd
23
src/Main.gd
|
@ -162,24 +162,21 @@ func _input(event : InputEvent) -> void:
|
|||
|
||||
# The section of code below is reserved for Undo and Redo! Do not place code for Input below, but above.
|
||||
if !event.is_echo(): # Checks if the action is pressed down
|
||||
if event.is_action_pressed("redo_secondary"): # Done, so that "redo_secondary" hasn't
|
||||
redone = true # a slight delay before it starts. The "redo" and "undo" action don't have a slight delay,
|
||||
Global.current_project.undo_redo.redo() # The "redo" and "undo" action don't have a slight delay,
|
||||
redone = false # because they get called as an accelerator once pressed (TopMenuContainer.gd / Line 152).
|
||||
if event.is_action_pressed("redo_secondary"):
|
||||
# Done, so that "redo_secondary" hasn't a slight delay before it starts.
|
||||
# The "redo" and "undo" action don't have a slight delay,
|
||||
# because they get called as an accelerator once pressed (TopMenuContainer.gd / Line 152).
|
||||
Global.current_project.commit_redo()
|
||||
return
|
||||
|
||||
if event.is_action("redo"): # Ctrl + Y
|
||||
redone = true
|
||||
Global.current_project.undo_redo.redo()
|
||||
redone = false
|
||||
Global.current_project.commit_redo()
|
||||
|
||||
if event.is_action("redo_secondary"): # Shift + Ctrl + Z
|
||||
redone = true
|
||||
Global.current_project.undo_redo.redo()
|
||||
redone = false
|
||||
Global.current_project.commit_redo()
|
||||
|
||||
if event.is_action("undo") and !event.shift: # Ctrl + Z and check if shift isn't pressed
|
||||
Global.current_project.undo_redo.undo() # so "undo" isn't accidentaly triggered while using "redo_secondary"
|
||||
Global.current_project.commit_undo() # so "undo" isn't accidentaly triggered while using "redo_secondary"
|
||||
|
||||
|
||||
func setup_application_window_size() -> void:
|
||||
|
@ -349,8 +346,8 @@ func _on_BackupConfirmation_confirmed(project_paths : Array, backup_paths : Arra
|
|||
Export.file_name = OpenSave.current_save_paths[0].get_file().trim_suffix(".pxo")
|
||||
Export.directory_path = OpenSave.current_save_paths[0].get_base_dir()
|
||||
Export.was_exported = false
|
||||
Global.file_menu.get_popup().set_item_text(4, tr("Save") + " %s" % OpenSave.current_save_paths[0].get_file())
|
||||
Global.file_menu.get_popup().set_item_text(6, tr("Export"))
|
||||
Global.top_menu_container.file_menu.set_item_text(4, tr("Save") + " %s" % OpenSave.current_save_paths[0].get_file())
|
||||
Global.top_menu_container.file_menu.set_item_text(6, tr("Export"))
|
||||
|
||||
|
||||
func _on_BackupConfirmation_delete(project_paths : Array, backup_paths : Array) -> void:
|
||||
|
|
|
@ -1,192 +0,0 @@
|
|||
extends Polygon2D
|
||||
|
||||
|
||||
var _selected_rect := Rect2(0, 0, 0, 0)
|
||||
var _clipped_rect := Rect2(0, 0, 0, 0)
|
||||
var _move_image := Image.new()
|
||||
var _move_texture := ImageTexture.new()
|
||||
var _clear_image := Image.new()
|
||||
var _move_pixel := false
|
||||
var _clipboard := Image.new()
|
||||
var _undo_data := {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_clear_image.create(1, 1, false, Image.FORMAT_RGBA8)
|
||||
_clear_image.fill(Color(0, 0, 0, 0))
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if _move_pixel:
|
||||
draw_texture(_move_texture, _clipped_rect.position, Color(1, 1, 1, 0.5))
|
||||
|
||||
|
||||
func has_point(position : Vector2) -> bool:
|
||||
return _selected_rect.has_point(position)
|
||||
|
||||
|
||||
func get_rect() -> Rect2:
|
||||
return _selected_rect
|
||||
|
||||
|
||||
func set_rect(rect : Rect2) -> void:
|
||||
_selected_rect = rect
|
||||
polygon[0] = rect.position
|
||||
polygon[1] = Vector2(rect.end.x, rect.position.y)
|
||||
polygon[2] = rect.end
|
||||
polygon[3] = Vector2(rect.position.x, rect.end.y)
|
||||
visible = not rect.has_no_area()
|
||||
|
||||
var project : Project = Global.current_project
|
||||
if rect.has_no_area():
|
||||
project.selected_pixels = []
|
||||
else:
|
||||
project.clear_selection()
|
||||
for x in range(rect.position.x, rect.end.x):
|
||||
for y in range(rect.position.y, rect.end.y):
|
||||
if x < 0 or x >= project.size.x:
|
||||
continue
|
||||
if y < 0 or y >= project.size.y:
|
||||
continue
|
||||
project.selected_pixels.append(Vector2(x, y))
|
||||
|
||||
|
||||
func move_rect(move : Vector2) -> void:
|
||||
_selected_rect.position += move
|
||||
_clipped_rect.position += move
|
||||
set_rect(_selected_rect)
|
||||
|
||||
|
||||
func select_rect() -> void:
|
||||
var undo_data = _get_undo_data(false)
|
||||
Global.current_project.selected_rect = _selected_rect
|
||||
commit_undo("Rectangle Select", undo_data)
|
||||
|
||||
|
||||
func move_start(move_pixel : bool) -> void:
|
||||
if not move_pixel:
|
||||
return
|
||||
|
||||
_undo_data = _get_undo_data(true)
|
||||
var project := Global.current_project
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
|
||||
var rect = Rect2(Vector2.ZERO, project.size)
|
||||
_clipped_rect = rect.clip(_selected_rect)
|
||||
_move_image = image.get_rect(_clipped_rect)
|
||||
_move_texture.create_from_image(_move_image, 0)
|
||||
|
||||
var size := _clipped_rect.size
|
||||
rect = Rect2(Vector2.ZERO, size)
|
||||
_clear_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST)
|
||||
image.blit_rect(_clear_image, rect, _clipped_rect.position)
|
||||
Global.canvas.update_texture(project.current_layer)
|
||||
|
||||
_move_pixel = true
|
||||
update()
|
||||
|
||||
|
||||
func move_end() -> void:
|
||||
var undo_data = _undo_data if _move_pixel else _get_undo_data(false)
|
||||
|
||||
if _move_pixel:
|
||||
var project := Global.current_project
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
var size := _clipped_rect.size
|
||||
var rect = Rect2(Vector2.ZERO, size)
|
||||
image.blit_rect_mask(_move_image, _move_image, rect, _clipped_rect.position)
|
||||
_move_pixel = false
|
||||
update()
|
||||
|
||||
Global.current_project.selected_rect = _selected_rect
|
||||
commit_undo("Rectangle Select", undo_data)
|
||||
_undo_data.clear()
|
||||
|
||||
|
||||
func copy() -> void:
|
||||
if _selected_rect.has_no_area():
|
||||
return
|
||||
|
||||
var project := Global.current_project
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
_clipboard = image.get_rect(_selected_rect)
|
||||
if _clipboard.is_invisible():
|
||||
return
|
||||
var brush = _clipboard.get_rect(_clipboard.get_used_rect())
|
||||
project.brushes.append(brush)
|
||||
Brushes.add_project_brush(brush)
|
||||
|
||||
func cut() -> void: # This is basically the same as copy + delete
|
||||
if _selected_rect.has_no_area():
|
||||
return
|
||||
|
||||
var undo_data = _get_undo_data(true)
|
||||
var project := Global.current_project
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
var size := _selected_rect.size
|
||||
var rect = Rect2(Vector2.ZERO, size)
|
||||
_clipboard = image.get_rect(_selected_rect)
|
||||
if _clipboard.is_invisible():
|
||||
return
|
||||
|
||||
_clear_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST)
|
||||
var brush = _clipboard.get_rect(_clipboard.get_used_rect())
|
||||
project.brushes.append(brush)
|
||||
Brushes.add_project_brush(brush)
|
||||
move_end() # The selection_rectangle can be used while is moving, this prevents malfunctioning
|
||||
image.blit_rect(_clear_image, rect, _selected_rect.position)
|
||||
commit_undo("Draw", undo_data)
|
||||
|
||||
func paste() -> void:
|
||||
if _clipboard.get_size() <= Vector2.ZERO:
|
||||
return
|
||||
|
||||
var undo_data = _get_undo_data(true)
|
||||
var project := Global.current_project
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
var size := _selected_rect.size
|
||||
var rect = Rect2(Vector2.ZERO, size)
|
||||
image.blend_rect(_clipboard, rect, _selected_rect.position)
|
||||
move_end() # The selection_rectangle can be used while is moving, this prevents malfunctioning
|
||||
commit_undo("Draw", undo_data)
|
||||
|
||||
|
||||
func delete() -> void:
|
||||
var undo_data = _get_undo_data(true)
|
||||
var project := Global.current_project
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
var size := _selected_rect.size
|
||||
var rect = Rect2(Vector2.ZERO, size)
|
||||
_clear_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST)
|
||||
image.blit_rect(_clear_image, rect, _selected_rect.position)
|
||||
move_end() # The selection_rectangle can be used while is moving, this prevents malfunctioning
|
||||
commit_undo("Draw", undo_data)
|
||||
|
||||
|
||||
func commit_undo(action : String, undo_data : Dictionary) -> void:
|
||||
var redo_data = _get_undo_data("image_data" in undo_data)
|
||||
var project := Global.current_project
|
||||
|
||||
project.undos += 1
|
||||
project.undo_redo.create_action(action)
|
||||
project.undo_redo.add_do_property(project, "selected_rect", redo_data["selected_rect"])
|
||||
project.undo_redo.add_undo_property(project, "selected_rect", undo_data["selected_rect"])
|
||||
if "image_data" in undo_data:
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
project.undo_redo.add_do_property(image, "data", redo_data["image_data"])
|
||||
project.undo_redo.add_undo_property(image, "data", undo_data["image_data"])
|
||||
project.undo_redo.add_do_method(Global, "redo", project.current_frame, project.current_layer)
|
||||
project.undo_redo.add_undo_method(Global, "undo", project.current_frame, project.current_layer)
|
||||
project.undo_redo.commit_action()
|
||||
|
||||
|
||||
func _get_undo_data(undo_image : bool) -> Dictionary:
|
||||
var data = {}
|
||||
var project := Global.current_project
|
||||
data["selected_rect"] = Global.current_project.selected_rect
|
||||
if undo_image:
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
image.unlock()
|
||||
data["image_data"] = image.data
|
||||
image.lock()
|
||||
return data
|
52
src/Shaders/MarchingAntsOutline.shader
Normal file
52
src/Shaders/MarchingAntsOutline.shader
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Taken and modified from https://godotshaders.com/shader/2d-outline-inline/
|
||||
// Also thanks to https://andreashackel.de/tech-art/stripes-shader-1/ for the stripe tutorial
|
||||
shader_type canvas_item;
|
||||
|
||||
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 bool animated = true;
|
||||
uniform float width : hint_range(0, 2) = 0.2;
|
||||
uniform float frequency = 50.0;
|
||||
uniform float stripe_direction : hint_range(0, 1) = 0.5;
|
||||
|
||||
|
||||
bool hasContraryNeighbour(vec2 uv, vec2 texture_pixel_size, sampler2D texture) {
|
||||
for (float i = -ceil(width); i <= ceil(width); i++) {
|
||||
float x = abs(i) > width ? width * sign(i) : i;
|
||||
float offset = width;
|
||||
|
||||
for (float j = -ceil(offset); j <= ceil(offset); j++) {
|
||||
float y = abs(j) > offset ? offset * sign(j) : j;
|
||||
vec2 xy = uv + texture_pixel_size * vec2(x, y);
|
||||
|
||||
if ((xy != clamp(xy, vec2(0.0), vec2(1.0)) || texture(texture, xy).a == 0.0) == true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec2 uv = UV;
|
||||
COLOR = texture(TEXTURE, uv);
|
||||
|
||||
if ((COLOR.a > 0.0) == true && hasContraryNeighbour(uv, TEXTURE_PIXEL_SIZE, TEXTURE)) {
|
||||
vec4 final_color = first_color;
|
||||
// Generate diagonal stripes
|
||||
if(animated)
|
||||
uv -= TIME / frequency;
|
||||
float pos = mix(uv.x, uv.y, stripe_direction) * frequency;
|
||||
float value = floor(fract(pos) + 0.5);
|
||||
if (mod(value, 2.0) == 0.0)
|
||||
final_color = second_color;
|
||||
|
||||
COLOR.rgb = mix(COLOR.rgb, final_color.rgb, final_color.a);
|
||||
COLOR.a += (1.0 - COLOR.a) * final_color.a;
|
||||
}
|
||||
else {
|
||||
// Erase the texture's pixels in order to only keep the outline visible
|
||||
COLOR.a = 0.0;
|
||||
}
|
||||
}
|
|
@ -81,11 +81,10 @@ func draw_preview() -> void:
|
|||
|
||||
|
||||
func _get_draw_rect() -> Rect2:
|
||||
if Global.current_project.selected_pixels.empty():
|
||||
return Global.current_project.tile_mode_rects[Global.TileMode.NONE]
|
||||
if Global.current_project.has_selection:
|
||||
return Global.current_project.get_selection_rectangle()
|
||||
else:
|
||||
var selected_pixels = Global.current_project.selected_pixels
|
||||
return Rect2(selected_pixels[0].x, selected_pixels[0].y, selected_pixels[-1].x - selected_pixels[0].x + 1, selected_pixels[-1].y - selected_pixels[0].y + 1)
|
||||
return Global.current_project.tile_mode_rects[Global.TileMode.NONE]
|
||||
|
||||
|
||||
func _get_draw_image() -> Image:
|
||||
|
|
|
@ -94,9 +94,10 @@ func update_pattern() -> void:
|
|||
|
||||
|
||||
func draw_start(position : Vector2) -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
if Global.current_project.layers[Global.current_project.current_layer].locked or !Global.current_project.tile_mode_rects[Global.TileMode.NONE].has_point(position):
|
||||
return
|
||||
if Global.current_project.selected_pixels and not position in Global.current_project.selected_pixels:
|
||||
if Global.current_project.has_selection and not Global.current_project.can_pixel_get_drawn(position):
|
||||
return
|
||||
var undo_data = _get_undo_data()
|
||||
if _fill_area == 0:
|
||||
|
@ -123,17 +124,14 @@ func fill_in_color(position : Vector2) -> void:
|
|||
return
|
||||
|
||||
image.lock()
|
||||
var pixels := []
|
||||
if project.selected_pixels:
|
||||
pixels = project.selected_pixels.duplicate()
|
||||
else:
|
||||
for x in Global.current_project.size.x:
|
||||
for y in Global.current_project.size.y:
|
||||
pixels.append(Vector2(x, y))
|
||||
|
||||
for i in pixels:
|
||||
if image.get_pixelv(i).is_equal_approx(color):
|
||||
_set_pixel(image, i.x, i.y, tool_slot.color)
|
||||
for x in Global.current_project.size.x:
|
||||
for y in Global.current_project.size.y:
|
||||
var pos := Vector2(x, y)
|
||||
if project.has_selection and not project.can_pixel_get_drawn(pos):
|
||||
continue
|
||||
if image.get_pixelv(pos).is_equal_approx(color):
|
||||
_set_pixel(image, x, y, tool_slot.color)
|
||||
|
||||
|
||||
func fill_in_area(position : Vector2) -> void:
|
||||
|
@ -145,19 +143,9 @@ func fill_in_area(position : Vector2) -> void:
|
|||
var mirror_y = project.y_symmetry_point - position.y
|
||||
var mirror_x_inside : bool
|
||||
var mirror_y_inside : bool
|
||||
var entire_image_selected : bool = project.selected_pixels.empty()
|
||||
if entire_image_selected:
|
||||
mirror_x_inside = mirror_x >= 0 and mirror_x < project.size.x
|
||||
mirror_y_inside = mirror_y >= 0 and mirror_y < project.size.y
|
||||
else:
|
||||
var selected_pixels_x := []
|
||||
var selected_pixels_y := []
|
||||
for i in project.selected_pixels:
|
||||
selected_pixels_x.append(i.x)
|
||||
selected_pixels_y.append(i.y)
|
||||
|
||||
mirror_x_inside = mirror_x in selected_pixels_x
|
||||
mirror_y_inside = mirror_y in selected_pixels_y
|
||||
mirror_x_inside = project.can_pixel_get_drawn(Vector2(mirror_x, position.y))
|
||||
mirror_y_inside = project.can_pixel_get_drawn(Vector2(position.x, mirror_y))
|
||||
|
||||
if tool_slot.horizontal_mirror and mirror_x_inside:
|
||||
_flood_fill(Vector2(mirror_x, position.y))
|
||||
|
@ -202,13 +190,8 @@ func _flood_fill(position : Vector2) -> void:
|
|||
|
||||
func _set_pixel(image : Image, x : int, y : int, color : Color) -> void:
|
||||
var project : Project = Global.current_project
|
||||
var entire_image_selected : bool = project.selected_pixels.empty()
|
||||
if entire_image_selected:
|
||||
if not _get_draw_rect().has_point(Vector2(x, y)):
|
||||
return
|
||||
else:
|
||||
if not Vector2(x, y) in project.selected_pixels:
|
||||
return
|
||||
if !project.can_pixel_get_drawn(Vector2(x, y)):
|
||||
return
|
||||
|
||||
if _fill_with == 0 or _pattern == null:
|
||||
image.set_pixel(x, y, color)
|
||||
|
|
|
@ -100,7 +100,6 @@ func update_brush() -> void:
|
|||
else:
|
||||
var random = randi() % _brush.random.size()
|
||||
_brush_image = _create_blended_brush_image(_brush.random[random])
|
||||
_brush_image.lock()
|
||||
_brush_texture.create_from_image(_brush_image, 0)
|
||||
update_mirror_brush()
|
||||
_indicator = _create_brush_indicator()
|
||||
|
@ -114,7 +113,6 @@ func update_random_image() -> void:
|
|||
return
|
||||
var random = randi() % _brush.random.size()
|
||||
_brush_image = _create_blended_brush_image(_brush.random[random])
|
||||
_brush_image.lock()
|
||||
_brush_texture.create_from_image(_brush_image, 0)
|
||||
_indicator = _create_brush_indicator()
|
||||
update_mirror_brush()
|
||||
|
@ -273,34 +271,43 @@ func draw_tool_brush(position : Vector2) -> void:
|
|||
return
|
||||
var src_rect := Rect2(dst_rect.position - dst, dst_rect.size)
|
||||
dst = dst_rect.position
|
||||
|
||||
_draw_brush_image(_brush_image, src_rect, dst)
|
||||
var brush_image : Image = remove_unselected_parts_of_brush(_brush_image, dst)
|
||||
_draw_brush_image(brush_image, src_rect, dst)
|
||||
|
||||
# Handle Mirroring
|
||||
var mirror_x = (project.x_symmetry_point + 1) - dst.x - src_rect.size.x
|
||||
var mirror_y = (project.y_symmetry_point + 1) - dst.y - src_rect.size.y
|
||||
var mirror_x_inside : bool
|
||||
var mirror_y_inside : bool
|
||||
var entire_image_selected : bool = project.selected_pixels.empty()
|
||||
if entire_image_selected:
|
||||
mirror_x_inside = mirror_x >= 0 and mirror_x < project.size.x
|
||||
mirror_y_inside = mirror_y >= 0 and mirror_y < project.size.y
|
||||
else:
|
||||
var selected_pixels_x := []
|
||||
var selected_pixels_y := []
|
||||
for i in project.selected_pixels:
|
||||
selected_pixels_x.append(i.x)
|
||||
selected_pixels_y.append(i.y)
|
||||
|
||||
mirror_x_inside = mirror_x in selected_pixels_x
|
||||
mirror_y_inside = mirror_y in selected_pixels_y
|
||||
if tool_slot.horizontal_mirror:
|
||||
var x_dst := Vector2(mirror_x, dst.y)
|
||||
var mirror_brush_x : Image = remove_unselected_parts_of_brush(_mirror_brushes.x, x_dst)
|
||||
_draw_brush_image(mirror_brush_x, _flip_rect(src_rect, size, true, false), x_dst)
|
||||
if tool_slot.vertical_mirror:
|
||||
var xy_dst := Vector2(mirror_x, mirror_y)
|
||||
var mirror_brush_xy : Image = remove_unselected_parts_of_brush(_mirror_brushes.xy, xy_dst)
|
||||
_draw_brush_image(mirror_brush_xy, _flip_rect(src_rect, size, true, true), xy_dst)
|
||||
if tool_slot.vertical_mirror:
|
||||
var y_dst := Vector2(dst.x, mirror_y)
|
||||
var mirror_brush_y : Image = remove_unselected_parts_of_brush(_mirror_brushes.y, y_dst)
|
||||
_draw_brush_image(mirror_brush_y, _flip_rect(src_rect, size, false, true), y_dst)
|
||||
|
||||
if tool_slot.horizontal_mirror and mirror_x_inside:
|
||||
_draw_brush_image(_mirror_brushes.x, _flip_rect(src_rect, size, true, false), Vector2(mirror_x, dst.y))
|
||||
if tool_slot.vertical_mirror and mirror_y_inside:
|
||||
_draw_brush_image(_mirror_brushes.xy, _flip_rect(src_rect, size, true, true), Vector2(mirror_x, mirror_y))
|
||||
if tool_slot.vertical_mirror and mirror_y_inside:
|
||||
_draw_brush_image(_mirror_brushes.y, _flip_rect(src_rect, size, false, true), Vector2(dst.x, mirror_y))
|
||||
|
||||
func remove_unselected_parts_of_brush(brush : Image, dst : Vector2) -> Image:
|
||||
var project : Project = Global.current_project
|
||||
var size := brush.get_size()
|
||||
var new_brush := Image.new()
|
||||
new_brush.copy_from(_mirror_brushes.x)
|
||||
if !project.has_selection:
|
||||
return new_brush
|
||||
|
||||
new_brush.lock()
|
||||
for x in size.x:
|
||||
for y in size.y:
|
||||
var pos := Vector2(x, y) + dst
|
||||
if !project.selection_bitmap.get_bit(pos):
|
||||
new_brush.set_pixel(x, y, Color(0))
|
||||
new_brush.unlock()
|
||||
return new_brush
|
||||
|
||||
|
||||
func draw_indicator() -> void:
|
||||
|
@ -337,13 +344,8 @@ func _set_pixel(position : Vector2) -> void:
|
|||
if project.tile_mode and project.get_tile_mode_rect().has_point(position):
|
||||
position = position.posmodv(project.size)
|
||||
|
||||
var entire_image_selected : bool = project.selected_pixels.empty()
|
||||
if entire_image_selected:
|
||||
if not _get_draw_rect().has_point(position):
|
||||
return
|
||||
else:
|
||||
if not position in project.selected_pixels:
|
||||
return
|
||||
if !project.can_pixel_get_drawn(position):
|
||||
return
|
||||
|
||||
var image := _get_draw_image()
|
||||
var i := int(position.x + position.y * image.get_size().x)
|
||||
|
|
|
@ -24,6 +24,7 @@ func _init() -> void:
|
|||
|
||||
|
||||
func draw_start(position : Vector2) -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
update_mask()
|
||||
_changed = false
|
||||
_drawer.color_op.changed = false
|
||||
|
|
|
@ -200,6 +200,7 @@ func update_strength() -> void:
|
|||
|
||||
|
||||
func draw_start(position : Vector2) -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
update_mask(false)
|
||||
_changed = false
|
||||
_drawer.color_op.changed = false
|
||||
|
|
40
src/Tools/Move.gd
Normal file
40
src/Tools/Move.gd
Normal file
|
@ -0,0 +1,40 @@
|
|||
extends BaseTool
|
||||
|
||||
|
||||
var _starting_pos : Vector2
|
||||
var _offset : Vector2
|
||||
|
||||
|
||||
func draw_start(position : Vector2) -> void:
|
||||
_starting_pos = position
|
||||
_offset = position
|
||||
if Global.current_project.has_selection:
|
||||
Global.canvas.selection.move_content_start()
|
||||
|
||||
|
||||
func draw_move(position : Vector2) -> void:
|
||||
if Global.current_project.has_selection:
|
||||
Global.canvas.selection.move_content(position - _offset)
|
||||
_offset = position
|
||||
else:
|
||||
Global.canvas.move_preview_location = position - _starting_pos
|
||||
_offset = position
|
||||
|
||||
|
||||
func draw_end(position : Vector2) -> void:
|
||||
if _starting_pos != Vector2.INF:
|
||||
var pixel_diff : Vector2 = position - _starting_pos
|
||||
var project : Project = Global.current_project
|
||||
var image : Image = _get_draw_image()
|
||||
|
||||
if !project.has_selection:
|
||||
Global.canvas.move_preview_location = Vector2.ZERO
|
||||
var image_copy := Image.new()
|
||||
image_copy.copy_from(image)
|
||||
Global.canvas.handle_undo("Draw")
|
||||
image.fill(Color(0, 0, 0, 0))
|
||||
image.blit_rect(image_copy, Rect2(Vector2.ZERO, project.size), pixel_diff)
|
||||
|
||||
Global.canvas.handle_redo("Draw")
|
||||
|
||||
_starting_pos = Vector2.INF
|
23
src/Tools/Move.tscn
Normal file
23
src/Tools/Move.tscn
Normal file
|
@ -0,0 +1,23 @@
|
|||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://src/Tools/BaseTool.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://src/Tools/Move.gd" type="Script" id=2]
|
||||
|
||||
|
||||
[node name="ToolOptions" instance=ExtResource( 1 )]
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="PixelPerfect" parent="." index="1"]
|
||||
visible = false
|
||||
margin_top = 126.0
|
||||
margin_bottom = 150.0
|
||||
|
||||
[node name="EmptySpacer" parent="." index="2"]
|
||||
visible = false
|
||||
margin_top = 126.0
|
||||
margin_bottom = 138.0
|
||||
|
||||
[node name="Mirror" parent="." index="3"]
|
||||
visible = false
|
||||
margin_top = 126.0
|
||||
margin_bottom = 143.0
|
|
@ -46,6 +46,7 @@ func update_config() -> void:
|
|||
|
||||
|
||||
func draw_start(position : Vector2) -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
update_mask()
|
||||
_changed = false
|
||||
_drawer.color_op.changed = false
|
||||
|
|
|
@ -1,56 +1,63 @@
|
|||
extends BaseTool
|
||||
|
||||
|
||||
var _rect := Rect2(0, 0, 0, 0)
|
||||
var _start := Rect2(0, 0, 0, 0)
|
||||
var _offset := Vector2.ZERO
|
||||
var _drag := false
|
||||
var _move := false
|
||||
var undo_data : Dictionary
|
||||
|
||||
|
||||
func draw_start(position : Vector2) -> void:
|
||||
if Global.selection_rectangle.has_point(position):
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
undo_data = Global.canvas.selection._get_undo_data(false)
|
||||
var selection_position : Vector2 = Global.canvas.selection.big_bounding_rectangle.position
|
||||
var offsetted_pos := position
|
||||
if selection_position.x < 0:
|
||||
offsetted_pos.x -= selection_position.x
|
||||
if selection_position.y < 0:
|
||||
offsetted_pos.y -= selection_position.y
|
||||
|
||||
if offsetted_pos.x >= 0 and offsetted_pos.y >= 0 and Global.current_project.selection_bitmap.get_bit(offsetted_pos) and !Tools.control and !Tools.shift:
|
||||
# Move current selection
|
||||
_move = true
|
||||
_offset = position
|
||||
Global.selection_rectangle.move_start(Tools.shift)
|
||||
_set_cursor_text(Global.selection_rectangle.get_rect())
|
||||
Global.canvas.selection.move_borders_start()
|
||||
|
||||
else:
|
||||
_drag = true
|
||||
_start = Rect2(position, Vector2.ZERO)
|
||||
Global.selection_rectangle.set_rect(_start)
|
||||
|
||||
|
||||
func draw_move(position : Vector2) -> void:
|
||||
if _move:
|
||||
Global.selection_rectangle.move_rect(position - _offset)
|
||||
Global.canvas.selection.move_borders(position - _offset)
|
||||
_offset = position
|
||||
_set_cursor_text(Global.selection_rectangle.get_rect())
|
||||
_set_cursor_text(Global.canvas.selection.big_bounding_rectangle)
|
||||
else:
|
||||
var rect := _start.expand(position).abs()
|
||||
rect = rect.grow_individual(0, 0, 1, 1)
|
||||
Global.selection_rectangle.set_rect(rect)
|
||||
_set_cursor_text(rect)
|
||||
_rect = _start.expand(position).abs()
|
||||
_rect = _rect.grow_individual(0, 0, 1, 1)
|
||||
_set_cursor_text(_rect)
|
||||
Global.canvas.selection.drawn_rect = _rect
|
||||
Global.canvas.selection.update()
|
||||
|
||||
|
||||
func draw_end(_position : Vector2) -> void:
|
||||
if _move:
|
||||
Global.selection_rectangle.move_end()
|
||||
Global.canvas.selection.move_borders_end()
|
||||
else:
|
||||
Global.selection_rectangle.select_rect()
|
||||
_drag = false
|
||||
if !Tools.shift and !Tools.control:
|
||||
Global.canvas.selection.clear_selection()
|
||||
if _rect.size == Vector2.ZERO and Global.current_project.has_selection:
|
||||
Global.canvas.selection.commit_undo("Rectangle Select", undo_data)
|
||||
if _rect.size != Vector2.ZERO:
|
||||
Global.canvas.selection.select_rect(_rect, !Tools.control)
|
||||
Global.canvas.selection.commit_undo("Rectangle Select", undo_data)
|
||||
|
||||
_move = false
|
||||
cursor_text = ""
|
||||
|
||||
|
||||
func cursor_move(position : Vector2) -> void:
|
||||
if _drag:
|
||||
_cursor = Vector2.INF
|
||||
elif Global.selection_rectangle.has_point(position):
|
||||
_cursor = Vector2.INF
|
||||
Global.main_viewport.mouse_default_cursor_shape = Input.CURSOR_MOVE
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
else:
|
||||
_cursor = position
|
||||
Global.main_viewport.mouse_default_cursor_shape = Input.CURSOR_CROSS
|
||||
_rect = Rect2(0, 0, 0, 0)
|
||||
Global.canvas.selection.drawn_rect = _rect
|
||||
Global.canvas.selection.update()
|
||||
|
||||
|
||||
func _set_cursor_text(rect : Rect2) -> void:
|
||||
|
|
|
@ -178,6 +178,9 @@ func zoom_changed() -> void:
|
|||
update_rulers()
|
||||
for guide in Global.current_project.guides:
|
||||
guide.width = zoom.x * 2
|
||||
|
||||
Global.canvas.selection.update_on_zoom(zoom.x)
|
||||
|
||||
elif name == "CameraPreview":
|
||||
Global.preview_zoom_slider.value = -zoom.x
|
||||
|
||||
|
|
|
@ -7,12 +7,14 @@ var current_pixel := Vector2.ZERO
|
|||
var can_undo := true
|
||||
var cursor_image_has_changed := false
|
||||
var sprite_changed_this_frame := false # for optimization purposes
|
||||
var move_preview_location := Vector2.ZERO
|
||||
|
||||
onready var currently_visible_frame : Viewport = $CurrentlyVisibleFrame
|
||||
onready var current_frame_drawer = $CurrentlyVisibleFrame/CurrentFrameDrawer
|
||||
onready var tile_mode = $TileMode
|
||||
onready var pixel_grid = $PixelGrid
|
||||
onready var grid = $Grid
|
||||
onready var tile_mode = $TileMode
|
||||
onready var selection = $Selection
|
||||
onready var indicators = $Indicators
|
||||
onready var previews = $Previews
|
||||
|
||||
|
@ -30,7 +32,7 @@ func _draw() -> void:
|
|||
Global.small_preview_viewport.get_child(0).get_node("CanvasPreview").update()
|
||||
|
||||
var current_cels : Array = Global.current_project.frames[Global.current_project.current_frame].cels
|
||||
|
||||
var current_layer : int = Global.current_project.current_layer
|
||||
var _position := position
|
||||
var _scale := scale
|
||||
if Global.mirror_view:
|
||||
|
@ -41,7 +43,10 @@ func _draw() -> void:
|
|||
for i in range(Global.current_project.layers.size()):
|
||||
var modulate_color := Color(1, 1, 1, current_cels[i].opacity)
|
||||
if Global.current_project.layers[i].visible: # if it's visible
|
||||
draw_texture(current_cels[i].image_texture, Vector2.ZERO, modulate_color)
|
||||
if i == current_layer:
|
||||
draw_texture(current_cels[i].image_texture, move_preview_location, modulate_color)
|
||||
else:
|
||||
draw_texture(current_cels[i].image_texture, Vector2.ZERO, modulate_color)
|
||||
|
||||
if Global.onion_skinning:
|
||||
onion_skinning()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=9 format=2]
|
||||
[gd_scene load_steps=12 format=2]
|
||||
|
||||
[ext_resource path="res://src/UI/Canvas/Canvas.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/UI/Canvas/Grid.gd" type="Script" id=2]
|
||||
|
@ -7,10 +7,21 @@
|
|||
[ext_resource path="res://src/UI/Canvas/CurrentFrameDrawer.gd" type="Script" id=5]
|
||||
[ext_resource path="res://src/UI/Canvas/PixelGrid.gd" type="Script" id=6]
|
||||
[ext_resource path="res://src/UI/Canvas/Previews.gd" type="Script" id=7]
|
||||
[ext_resource path="res://src/UI/Canvas/Selection.gd" type="Script" id=8]
|
||||
[ext_resource path="res://src/Shaders/MarchingAntsOutline.shader" type="Shader" id=9]
|
||||
|
||||
[sub_resource type="CanvasItemMaterial" id=1]
|
||||
blend_mode = 4
|
||||
|
||||
[sub_resource type="ShaderMaterial" id=2]
|
||||
shader = ExtResource( 9 )
|
||||
shader_param/first_color = Color( 1, 1, 1, 1 )
|
||||
shader_param/second_color = Color( 0, 0, 0, 1 )
|
||||
shader_param/animated = true
|
||||
shader_param/width = 0.05
|
||||
shader_param/frequency = 200.0
|
||||
shader_param/stripe_direction = 0.5
|
||||
|
||||
[node name="Canvas" type="Node2D"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
|
@ -36,6 +47,13 @@ script = ExtResource( 6 )
|
|||
[node name="Grid" type="Node2D" parent="."]
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="Selection" type="Node2D" parent="."]
|
||||
script = ExtResource( 8 )
|
||||
|
||||
[node name="MarchingAntsOutline" type="Sprite" parent="Selection"]
|
||||
material = SubResource( 2 )
|
||||
centered = false
|
||||
|
||||
[node name="Indicators" type="Node2D" parent="."]
|
||||
script = ExtResource( 3 )
|
||||
|
||||
|
|
498
src/UI/Canvas/Selection.gd
Normal file
498
src/UI/Canvas/Selection.gd
Normal file
|
@ -0,0 +1,498 @@
|
|||
extends Node2D
|
||||
|
||||
|
||||
class Clipboard:
|
||||
var image := Image.new()
|
||||
var selection_bitmap := BitMap.new()
|
||||
var big_bounding_rectangle := Rect2()
|
||||
var selection_offset := Vector2.ZERO
|
||||
|
||||
|
||||
class Gizmo:
|
||||
enum Type {SCALE, ROTATE}
|
||||
|
||||
var rect : Rect2
|
||||
var direction := Vector2.ZERO
|
||||
var type : int
|
||||
|
||||
func _init(_type : int = Type.SCALE, _direction := Vector2.ZERO) -> void:
|
||||
type = _type
|
||||
direction = _direction
|
||||
|
||||
|
||||
func get_cursor() -> int:
|
||||
var cursor := Input.CURSOR_MOVE
|
||||
if direction == Vector2.ZERO:
|
||||
return Input.CURSOR_POINTING_HAND
|
||||
elif direction == Vector2(-1, -1) or direction == Vector2(1, 1): # Top left or bottom right
|
||||
cursor = Input.CURSOR_FDIAGSIZE
|
||||
elif direction == Vector2(1, -1) or direction == Vector2(-1, 1): # Top right or bottom left
|
||||
cursor = Input.CURSOR_BDIAGSIZE
|
||||
elif direction == Vector2(0, -1) or direction == Vector2(0, 1): # Center top or center bottom
|
||||
cursor = Input.CURSOR_VSIZE
|
||||
elif direction == Vector2(-1, 0) or direction == Vector2(1, 0): # Center left or center right
|
||||
cursor = Input.CURSOR_HSIZE
|
||||
return cursor
|
||||
|
||||
|
||||
var clipboard := Clipboard.new()
|
||||
var is_moving_content := false
|
||||
var big_bounding_rectangle := Rect2() setget _big_bounding_rectangle_changed
|
||||
var temp_rect := Rect2()
|
||||
var original_big_bounding_rectangle := Rect2()
|
||||
var original_preview_image := Image.new()
|
||||
var original_bitmap := BitMap.new()
|
||||
var original_offset := Vector2.ZERO
|
||||
var preview_image := Image.new()
|
||||
var preview_image_texture := ImageTexture.new()
|
||||
var undo_data : Dictionary
|
||||
var drawn_rect := Rect2(0, 0, 0, 0)
|
||||
var gizmos := [] # Array of Gizmos
|
||||
var dragged_gizmo : Gizmo = null
|
||||
var prev_angle := 0
|
||||
var mouse_pos_on_gizmo_drag := Vector2.ZERO
|
||||
|
||||
onready var marching_ants_outline : Sprite = $MarchingAntsOutline
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
gizmos.append(Gizmo.new(Gizmo.Type.SCALE, Vector2(-1, -1))) # Top left
|
||||
gizmos.append(Gizmo.new(Gizmo.Type.SCALE, Vector2(0, -1))) # Center top
|
||||
gizmos.append(Gizmo.new(Gizmo.Type.SCALE, Vector2(1, -1))) # Top right
|
||||
gizmos.append(Gizmo.new(Gizmo.Type.SCALE, Vector2(1, 0))) # Center right
|
||||
gizmos.append(Gizmo.new(Gizmo.Type.SCALE, Vector2(1, 1))) # Bottom right
|
||||
gizmos.append(Gizmo.new(Gizmo.Type.SCALE, Vector2(0, 1))) # Center bottom
|
||||
gizmos.append(Gizmo.new(Gizmo.Type.SCALE, Vector2(-1, 1))) # Bottom left
|
||||
gizmos.append(Gizmo.new(Gizmo.Type.SCALE, Vector2(-1, 0))) # Center left
|
||||
|
||||
# gizmos.append(Gizmo.new(Gizmo.Type.ROTATE)) # Rotation gizmo (temp)
|
||||
|
||||
|
||||
func _input(event : InputEvent) -> void:
|
||||
if event is InputEventKey:
|
||||
if is_moving_content: # Temporary code
|
||||
if event.scancode == 16777221: # Enter
|
||||
move_content_confirm()
|
||||
elif event.scancode == 16777217: # Escape
|
||||
move_content_cancel()
|
||||
elif event is InputEventMouse:
|
||||
var gizmo : Gizmo
|
||||
for g in gizmos:
|
||||
if g.rect.has_point(Global.canvas.current_pixel):
|
||||
gizmo = g
|
||||
break
|
||||
if gizmo:
|
||||
Global.main_viewport.mouse_default_cursor_shape = gizmo.get_cursor()
|
||||
elif !dragged_gizmo:
|
||||
Global.main_viewport.mouse_default_cursor_shape = Input.CURSOR_CROSS
|
||||
|
||||
if event is InputEventMouseButton and event.button_index == BUTTON_LEFT:
|
||||
if event.pressed:
|
||||
if gizmo:
|
||||
Global.has_focus = false
|
||||
mouse_pos_on_gizmo_drag = Global.canvas.current_pixel
|
||||
dragged_gizmo = gizmo
|
||||
temp_rect = big_bounding_rectangle
|
||||
move_content_start()
|
||||
Global.current_project.selection_offset = Vector2.ZERO
|
||||
if gizmo.type == Gizmo.Type.ROTATE:
|
||||
var img_size := max(original_preview_image.get_width(), original_preview_image.get_height())
|
||||
original_preview_image.crop(img_size, img_size)
|
||||
|
||||
elif dragged_gizmo:
|
||||
Global.has_focus = true
|
||||
dragged_gizmo = null
|
||||
|
||||
if dragged_gizmo:
|
||||
if dragged_gizmo.type == Gizmo.Type.SCALE:
|
||||
gizmo_resize()
|
||||
else:
|
||||
gizmo_rotate()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
var _position := position
|
||||
var _scale := scale
|
||||
if Global.mirror_view:
|
||||
_position.x = _position.x + Global.current_project.size.x
|
||||
_scale.x = -1
|
||||
draw_set_transform(_position, rotation, _scale)
|
||||
draw_rect(drawn_rect, Color.black, false)
|
||||
if big_bounding_rectangle.size != Vector2.ZERO:
|
||||
for gizmo in gizmos: # Draw gizmos
|
||||
draw_rect(gizmo.rect, Color.black)
|
||||
var filled_rect : Rect2 = gizmo.rect
|
||||
var filled_size : Vector2 = gizmo.rect.size * Vector2(0.2, 0.2)
|
||||
filled_rect.position += filled_size
|
||||
filled_rect.size -= filled_size * 2
|
||||
draw_rect(filled_rect, Color.white) # Filled white square
|
||||
|
||||
if is_moving_content and !preview_image.is_empty():
|
||||
draw_texture(preview_image_texture, big_bounding_rectangle.position, Color(1, 1, 1, 0.5))
|
||||
draw_set_transform(position, rotation, scale)
|
||||
|
||||
|
||||
func _big_bounding_rectangle_changed(value : Rect2) -> void:
|
||||
big_bounding_rectangle = value
|
||||
update_gizmos()
|
||||
|
||||
|
||||
func update_gizmos() -> void:
|
||||
var rect_pos : Vector2 = big_bounding_rectangle.position
|
||||
var rect_end : Vector2 = big_bounding_rectangle.end
|
||||
var size := Vector2.ONE * Global.camera.zoom * 10
|
||||
# Clockwise, starting from top-left corner
|
||||
gizmos[0].rect = Rect2(rect_pos - size, size)
|
||||
gizmos[1].rect = Rect2(Vector2((rect_end.x + rect_pos.x - size.x) / 2, rect_pos.y - size.y), size)
|
||||
gizmos[2].rect = Rect2(Vector2(rect_end.x, rect_pos.y - size.y), size)
|
||||
gizmos[3].rect = Rect2(Vector2(rect_end.x, (rect_end.y + rect_pos.y - size.y) / 2), size)
|
||||
gizmos[4].rect = Rect2(rect_end, size)
|
||||
gizmos[5].rect = Rect2(Vector2((rect_end.x + rect_pos.x - size.x) / 2, rect_end.y), size)
|
||||
gizmos[6].rect = Rect2(Vector2(rect_pos.x - size.x, rect_end.y), size)
|
||||
gizmos[7].rect = Rect2(Vector2(rect_pos.x - size.x, (rect_end.y + rect_pos.y - size.y) / 2), size)
|
||||
|
||||
# Rotation gizmo (temp)
|
||||
# gizmos[8].rect = Rect2(Vector2((rect_end.x + rect_pos.x - size.x) / 2, rect_pos.y - size.y - (size.y * 2)), size)
|
||||
update()
|
||||
|
||||
|
||||
func update_on_zoom(zoom : float) -> void:
|
||||
var size := max(Global.current_project.selection_bitmap.get_size().x, Global.current_project.selection_bitmap.get_size().y)
|
||||
marching_ants_outline.material.set_shader_param("width", zoom)
|
||||
marching_ants_outline.material.set_shader_param("frequency", (1.0 / zoom) * 10 * size / 64)
|
||||
for gizmo in gizmos:
|
||||
if gizmo.rect.size == Vector2.ZERO:
|
||||
return
|
||||
update_gizmos()
|
||||
|
||||
|
||||
func gizmo_resize() -> void:
|
||||
var diff : Vector2 = (Global.canvas.current_pixel - mouse_pos_on_gizmo_drag) * dragged_gizmo.direction
|
||||
var dir := dragged_gizmo.direction
|
||||
if diff != Vector2.ZERO:
|
||||
mouse_pos_on_gizmo_drag = Global.canvas.current_pixel
|
||||
var left := 0.0 if dir.x >= 0 else diff.x
|
||||
var top := 0.0 if dir.y >= 0 else diff.y
|
||||
var right := diff.x if dir.x >= 0 else 0.0
|
||||
var bottom := diff.y if dir.y >= 0 else 0.0
|
||||
temp_rect = temp_rect.grow_individual(left, top, right, bottom)
|
||||
big_bounding_rectangle = temp_rect.abs()
|
||||
big_bounding_rectangle.position = big_bounding_rectangle.position.ceil()
|
||||
self.big_bounding_rectangle.size = big_bounding_rectangle.size.ceil()
|
||||
var size = big_bounding_rectangle.size.abs()
|
||||
preview_image.copy_from(original_preview_image)
|
||||
preview_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST)
|
||||
if temp_rect.size.x < 0:
|
||||
preview_image.flip_x()
|
||||
if temp_rect.size.y < 0:
|
||||
preview_image.flip_y()
|
||||
preview_image_texture.create_from_image(preview_image, 0)
|
||||
Global.current_project.selection_bitmap = Global.current_project.resize_bitmap_values(original_bitmap, size, temp_rect.size.x < 0, temp_rect.size.y < 0)
|
||||
Global.current_project.selection_bitmap_changed()
|
||||
update()
|
||||
|
||||
|
||||
func gizmo_rotate() -> void: # Does not work properly yet
|
||||
var angle := Global.canvas.current_pixel.angle_to_point(mouse_pos_on_gizmo_drag)
|
||||
angle = deg2rad(floor(rad2deg(angle)))
|
||||
if angle == prev_angle:
|
||||
return
|
||||
prev_angle = angle
|
||||
# var img_size := max(original_preview_image.get_width(), original_preview_image.get_height())
|
||||
# warning-ignore:integer_division
|
||||
# warning-ignore:integer_division
|
||||
# var pivot = Vector2(original_preview_image.get_width() / 2, original_preview_image.get_height() / 2)
|
||||
var pivot = Vector2(big_bounding_rectangle.size.x / 2, big_bounding_rectangle.size.y / 2)
|
||||
preview_image.copy_from(original_preview_image)
|
||||
if original_big_bounding_rectangle.position != big_bounding_rectangle.position:
|
||||
preview_image.fill(Color(0, 0, 0, 0))
|
||||
var pos_diff := (original_big_bounding_rectangle.position - big_bounding_rectangle.position).abs()
|
||||
# pos_diff.y = 0
|
||||
preview_image.blit_rect(original_preview_image, Rect2(Vector2.ZERO, preview_image.get_size()), pos_diff)
|
||||
DrawingAlgos.nn_rotate(preview_image, angle, pivot)
|
||||
preview_image_texture.create_from_image(preview_image, 0)
|
||||
|
||||
var bitmap_image = Global.current_project.bitmap_to_image(original_bitmap)
|
||||
var bitmap_pivot = original_big_bounding_rectangle.position + ((original_big_bounding_rectangle.end - original_big_bounding_rectangle.position) / 2)
|
||||
DrawingAlgos.nn_rotate(bitmap_image, angle, bitmap_pivot)
|
||||
Global.current_project.selection_bitmap.create_from_image_alpha(bitmap_image)
|
||||
Global.current_project.selection_bitmap_changed()
|
||||
self.big_bounding_rectangle = bitmap_image.get_used_rect()
|
||||
update()
|
||||
|
||||
|
||||
func select_rect(rect : Rect2, select := true) -> void:
|
||||
var project : Project = Global.current_project
|
||||
var selection_bitmap_copy : BitMap = project.selection_bitmap.duplicate()
|
||||
var offset_position := Vector2.ZERO # Used only if the selection is outside of the canvas boundaries, on the left and/or above (negative coords)
|
||||
if big_bounding_rectangle.position.x < 0:
|
||||
rect.position.x -= big_bounding_rectangle.position.x
|
||||
offset_position.x = big_bounding_rectangle.position.x
|
||||
if big_bounding_rectangle.position.y < 0:
|
||||
rect.position.y -= big_bounding_rectangle.position.y
|
||||
offset_position.y = big_bounding_rectangle.position.y
|
||||
|
||||
if offset_position != Vector2.ZERO:
|
||||
big_bounding_rectangle.position -= offset_position
|
||||
project.move_bitmap_values(selection_bitmap_copy)
|
||||
|
||||
selection_bitmap_copy.set_bit_rect(rect, select)
|
||||
big_bounding_rectangle = project.get_selection_rectangle(selection_bitmap_copy)
|
||||
|
||||
if offset_position != Vector2.ZERO:
|
||||
big_bounding_rectangle.position += offset_position
|
||||
project.move_bitmap_values(selection_bitmap_copy)
|
||||
|
||||
project.selection_bitmap = selection_bitmap_copy
|
||||
self.big_bounding_rectangle = big_bounding_rectangle # call getter method
|
||||
|
||||
|
||||
|
||||
func move_borders_start() -> void:
|
||||
undo_data = _get_undo_data(false)
|
||||
|
||||
|
||||
func move_borders(move : Vector2) -> void:
|
||||
marching_ants_outline.offset += move
|
||||
self.big_bounding_rectangle.position += move
|
||||
update()
|
||||
|
||||
|
||||
func move_borders_end() -> void:
|
||||
var selected_bitmap_copy = Global.current_project.selection_bitmap.duplicate()
|
||||
Global.current_project.move_bitmap_values(selected_bitmap_copy)
|
||||
|
||||
Global.current_project.selection_bitmap = selected_bitmap_copy
|
||||
commit_undo("Rectangle Select", undo_data)
|
||||
update()
|
||||
|
||||
|
||||
func move_content_start() -> void:
|
||||
if !is_moving_content:
|
||||
is_moving_content = true
|
||||
undo_data = _get_undo_data(true)
|
||||
get_preview_image()
|
||||
original_bitmap = Global.current_project.selection_bitmap.duplicate()
|
||||
original_big_bounding_rectangle = big_bounding_rectangle
|
||||
original_offset = Global.current_project.selection_offset
|
||||
update()
|
||||
|
||||
|
||||
func move_content(move : Vector2) -> void:
|
||||
move_borders(move)
|
||||
|
||||
|
||||
func move_content_confirm() -> void:
|
||||
if !is_moving_content:
|
||||
return
|
||||
var project : Project = Global.current_project
|
||||
var cel_image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
cel_image.blit_rect_mask(preview_image, preview_image, Rect2(Vector2.ZERO, Global.current_project.selection_bitmap.get_size()), big_bounding_rectangle.position)
|
||||
var selected_bitmap_copy = Global.current_project.selection_bitmap.duplicate()
|
||||
Global.current_project.move_bitmap_values(selected_bitmap_copy)
|
||||
Global.current_project.selection_bitmap = selected_bitmap_copy
|
||||
|
||||
original_preview_image = Image.new()
|
||||
preview_image = Image.new()
|
||||
original_bitmap = BitMap.new()
|
||||
is_moving_content = false
|
||||
commit_undo("Move Selection", undo_data)
|
||||
update()
|
||||
|
||||
|
||||
func move_content_cancel() -> void:
|
||||
if preview_image.is_empty():
|
||||
return
|
||||
var project : Project = Global.current_project
|
||||
project.selection_offset = original_offset
|
||||
|
||||
is_moving_content = false
|
||||
self.big_bounding_rectangle = original_big_bounding_rectangle
|
||||
project.selection_bitmap = original_bitmap
|
||||
project.selection_bitmap_changed()
|
||||
preview_image = original_preview_image
|
||||
var cel_image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
cel_image.blit_rect_mask(preview_image, preview_image, Rect2(Vector2.ZERO, Global.current_project.selection_bitmap.get_size()), big_bounding_rectangle.position)
|
||||
Global.canvas.update_texture(project.current_layer)
|
||||
original_preview_image = Image.new()
|
||||
preview_image = Image.new()
|
||||
original_bitmap = BitMap.new()
|
||||
update()
|
||||
|
||||
|
||||
func commit_undo(action : String, _undo_data : Dictionary) -> void:
|
||||
var redo_data = _get_undo_data("image_data" in _undo_data)
|
||||
var project := Global.current_project
|
||||
|
||||
project.undos += 1
|
||||
project.undo_redo.create_action(action)
|
||||
project.undo_redo.add_do_property(project, "selection_bitmap", redo_data["selection_bitmap"])
|
||||
project.undo_redo.add_do_property(self, "big_bounding_rectangle", redo_data["big_bounding_rectangle"])
|
||||
project.undo_redo.add_do_property(project, "selection_offset", redo_data["outline_offset"])
|
||||
|
||||
project.undo_redo.add_undo_property(project, "selection_bitmap", _undo_data["selection_bitmap"])
|
||||
project.undo_redo.add_undo_property(self, "big_bounding_rectangle", _undo_data["big_bounding_rectangle"])
|
||||
project.undo_redo.add_undo_property(project, "selection_offset", _undo_data["outline_offset"])
|
||||
|
||||
|
||||
if "image_data" in _undo_data:
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
project.undo_redo.add_do_property(image, "data", redo_data["image_data"])
|
||||
project.undo_redo.add_undo_property(image, "data", _undo_data["image_data"])
|
||||
project.undo_redo.add_do_method(Global, "redo", project.current_frame, project.current_layer)
|
||||
project.undo_redo.add_do_method(project, "selection_bitmap_changed")
|
||||
project.undo_redo.add_undo_method(Global, "undo", project.current_frame, project.current_layer)
|
||||
project.undo_redo.add_undo_method(project, "selection_bitmap_changed")
|
||||
project.undo_redo.commit_action()
|
||||
|
||||
undo_data.clear()
|
||||
|
||||
|
||||
func _get_undo_data(undo_image : bool) -> Dictionary:
|
||||
var data := {}
|
||||
var project := Global.current_project
|
||||
data["selection_bitmap"] = project.selection_bitmap
|
||||
data["big_bounding_rectangle"] = big_bounding_rectangle
|
||||
data["outline_offset"] = Global.current_project.selection_offset
|
||||
|
||||
if undo_image:
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
image.unlock()
|
||||
data["image_data"] = image.data
|
||||
image.lock()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
func cut() -> void:
|
||||
copy()
|
||||
delete()
|
||||
|
||||
|
||||
func copy() -> void:
|
||||
var project := Global.current_project
|
||||
if !project.has_selection:
|
||||
return
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
var to_copy := Image.new()
|
||||
to_copy = image.get_rect(big_bounding_rectangle)
|
||||
to_copy.lock()
|
||||
# Only remove unincluded pixels if the selection is not a single rectangle
|
||||
for x in to_copy.get_size().x:
|
||||
for y in to_copy.get_size().y:
|
||||
var pos := Vector2(x, y)
|
||||
var offset_pos = big_bounding_rectangle.position
|
||||
if offset_pos.x < 0:
|
||||
offset_pos.x = 0
|
||||
if offset_pos.y < 0:
|
||||
offset_pos.y = 0
|
||||
if not project.selection_bitmap.get_bit(pos + offset_pos):
|
||||
to_copy.set_pixelv(pos, Color(0))
|
||||
to_copy.unlock()
|
||||
clipboard.image = to_copy
|
||||
clipboard.selection_bitmap = project.selection_bitmap.duplicate()
|
||||
clipboard.big_bounding_rectangle = big_bounding_rectangle
|
||||
clipboard.selection_offset = project.selection_offset
|
||||
|
||||
|
||||
func paste() -> void:
|
||||
if !clipboard.image:
|
||||
return
|
||||
var _undo_data = _get_undo_data(true)
|
||||
var project := Global.current_project
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
clear_selection()
|
||||
project.selection_bitmap = clipboard.selection_bitmap.duplicate()
|
||||
self.big_bounding_rectangle = clipboard.big_bounding_rectangle
|
||||
project.selection_offset = clipboard.selection_offset
|
||||
image.blend_rect(clipboard.image, Rect2(Vector2.ZERO, project.size), big_bounding_rectangle.position)
|
||||
commit_undo("Draw", _undo_data)
|
||||
|
||||
|
||||
func delete() -> void:
|
||||
var project := Global.current_project
|
||||
if !project.has_selection:
|
||||
return
|
||||
var _undo_data = _get_undo_data(true)
|
||||
var image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
for x in big_bounding_rectangle.size.x:
|
||||
for y in big_bounding_rectangle.size.y:
|
||||
var pos := Vector2(x, y) + big_bounding_rectangle.position
|
||||
if project.can_pixel_get_drawn(pos):
|
||||
image.set_pixelv(pos, Color(0))
|
||||
commit_undo("Draw", _undo_data)
|
||||
|
||||
|
||||
func select_all() -> void:
|
||||
var project := Global.current_project
|
||||
var _undo_data = _get_undo_data(false)
|
||||
clear_selection()
|
||||
var full_rect = Rect2(Vector2.ZERO, project.size)
|
||||
select_rect(full_rect)
|
||||
commit_undo("Rectangle Select", _undo_data)
|
||||
|
||||
|
||||
func invert() -> void:
|
||||
move_content_confirm()
|
||||
var project := Global.current_project
|
||||
var _undo_data = _get_undo_data(false)
|
||||
var selection_bitmap_copy : BitMap = project.selection_bitmap.duplicate()
|
||||
selection_bitmap_copy = project.resize_bitmap(selection_bitmap_copy, project.size)
|
||||
project.invert_bitmap(selection_bitmap_copy)
|
||||
project.selection_bitmap = selection_bitmap_copy
|
||||
Global.current_project.selection_bitmap_changed()
|
||||
self.big_bounding_rectangle = project.get_selection_rectangle(selection_bitmap_copy)
|
||||
project.selection_offset = Vector2.ZERO
|
||||
commit_undo("Rectangle Select", _undo_data)
|
||||
|
||||
|
||||
func clear_selection(use_undo := false) -> void:
|
||||
var project := Global.current_project
|
||||
if !project.has_selection:
|
||||
return
|
||||
move_content_confirm()
|
||||
var _undo_data = _get_undo_data(false)
|
||||
var selection_bitmap_copy : BitMap = project.selection_bitmap.duplicate()
|
||||
selection_bitmap_copy = project.resize_bitmap(selection_bitmap_copy, project.size)
|
||||
var full_rect = Rect2(Vector2.ZERO, selection_bitmap_copy.get_size())
|
||||
selection_bitmap_copy.set_bit_rect(full_rect, false)
|
||||
project.selection_bitmap = selection_bitmap_copy
|
||||
|
||||
self.big_bounding_rectangle = Rect2()
|
||||
project.selection_offset = Vector2.ZERO
|
||||
update()
|
||||
if use_undo:
|
||||
commit_undo("Clear Selection", _undo_data)
|
||||
|
||||
|
||||
func get_preview_image() -> void:
|
||||
var project : Project = Global.current_project
|
||||
var cel_image : Image = project.frames[project.current_frame].cels[project.current_layer].image
|
||||
if original_preview_image.is_empty():
|
||||
# original_preview_image.copy_from(cel_image)
|
||||
original_preview_image = cel_image.get_rect(big_bounding_rectangle)
|
||||
original_preview_image.lock()
|
||||
# For non-rectangular selections
|
||||
for x in range(0, big_bounding_rectangle.size.x):
|
||||
for y in range(0, big_bounding_rectangle.size.y):
|
||||
var pos := Vector2(x, y)
|
||||
if !project.can_pixel_get_drawn(pos + big_bounding_rectangle.position):
|
||||
original_preview_image.set_pixelv(pos, Color(0, 0, 0, 0))
|
||||
|
||||
original_preview_image.unlock()
|
||||
preview_image.copy_from(original_preview_image)
|
||||
preview_image_texture.create_from_image(preview_image, 0)
|
||||
|
||||
var clear_image := Image.new()
|
||||
clear_image.create(original_preview_image.get_width(), original_preview_image.get_height(), false, Image.FORMAT_RGBA8)
|
||||
cel_image.blit_rect_mask(clear_image, original_preview_image, Rect2(Vector2.ZERO, Global.current_project.selection_bitmap.get_size()), big_bounding_rectangle.position)
|
||||
Global.canvas.update_texture(project.current_layer)
|
||||
|
||||
|
||||
func get_big_bounding_rectangle() -> Rect2:
|
||||
# Returns a rectangle that contains the entire selection, with multiple polygons
|
||||
var project : Project = Global.current_project
|
||||
var rect := Rect2()
|
||||
var image : Image = project.bitmap_to_image(project.selection_bitmap)
|
||||
rect = image.get_used_rect()
|
||||
return rect
|
|
@ -222,6 +222,7 @@ func set_export_progress_bar(value: float) -> void:
|
|||
|
||||
|
||||
func _on_ExportDialog_about_to_show() -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
# If we're on HTML5, don't let the user change the directory path
|
||||
if OS.get_name() == "HTML5":
|
||||
path_container.visible = false
|
||||
|
|
|
@ -13,8 +13,8 @@ func set_nodes() -> void:
|
|||
affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton
|
||||
|
||||
|
||||
func commit_action(_cel : Image, _pixels : Array, _project : Project = Global.current_project) -> void:
|
||||
DrawingAlgos.desaturate_image(_cel, _pixels, red, green, blue, alpha)
|
||||
func commit_action(_cel : Image, _project : Project = Global.current_project) -> void:
|
||||
DrawingAlgos.desaturate_image(_cel, selection_checkbox.pressed, _project, red, green, blue, alpha)
|
||||
|
||||
|
||||
func _on_RButton_toggled(button_pressed : bool) -> void:
|
||||
|
|
|
@ -11,8 +11,8 @@ func set_nodes() -> void:
|
|||
affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton
|
||||
|
||||
|
||||
func commit_action(_cel : Image, _pixels : Array, project : Project = Global.current_project) -> void:
|
||||
flip_image(_cel, _pixels, project)
|
||||
func commit_action(_cel : Image, project : Project = Global.current_project) -> void:
|
||||
flip_image(_cel, selection_checkbox.pressed, project)
|
||||
|
||||
|
||||
func _on_FlipHorizontal_toggled(_button_pressed : bool) -> void:
|
||||
|
@ -23,14 +23,8 @@ func _on_FlipVertical_toggled(_button_pressed : bool) -> void:
|
|||
update_preview()
|
||||
|
||||
|
||||
func _on_SelectionCheckBox_toggled(button_pressed : bool) -> void:
|
||||
._on_SelectionCheckBox_toggled(button_pressed)
|
||||
update_preview()
|
||||
|
||||
|
||||
func flip_image(image : Image, _pixels : Array, project : Project = Global.current_project) -> void:
|
||||
var entire_image_selected : bool = _pixels.size() == project.size.x * project.size.y
|
||||
if entire_image_selected:
|
||||
func flip_image(image : Image, affect_selection : bool, project : Project = Global.current_project) -> void:
|
||||
if !(affect_selection and project.has_selection):
|
||||
if flip_h.pressed:
|
||||
image.flip_x()
|
||||
if flip_v.pressed:
|
||||
|
@ -41,10 +35,13 @@ func flip_image(image : Image, _pixels : Array, project : Project = Global.curre
|
|||
selected_image.create(image.get_width(), image.get_height(), false, Image.FORMAT_RGBA8)
|
||||
selected_image.lock()
|
||||
image.lock()
|
||||
for i in _pixels:
|
||||
var color : Color = image.get_pixelv(i)
|
||||
selected_image.set_pixelv(i, color)
|
||||
image.set_pixelv(i, Color(0, 0, 0, 0))
|
||||
for x in image.get_width():
|
||||
for y in image.get_width():
|
||||
var pos := Vector2(x, y)
|
||||
if project.can_pixel_get_drawn(pos):
|
||||
var color : Color = image.get_pixelv(pos)
|
||||
selected_image.set_pixelv(pos, color)
|
||||
image.set_pixelv(pos, Color(0, 0, 0, 0))
|
||||
|
||||
if flip_h.pressed:
|
||||
selected_image.flip_x()
|
||||
|
|
|
@ -18,8 +18,8 @@ func set_nodes() -> void:
|
|||
affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton
|
||||
|
||||
|
||||
func commit_action(_cel : Image, _pixels : Array, _project : Project = Global.current_project) -> void:
|
||||
DrawingAlgos.generate_gradient(_cel, [color1.color, color2.color], steps.value, direction.selected, _pixels)
|
||||
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 _on_ColorPickerButton_color_changed(_color : Color) -> void:
|
||||
|
|
|
@ -21,8 +21,8 @@ func _confirmed() -> void:
|
|||
reset()
|
||||
|
||||
|
||||
func commit_action(_cel : Image, _pixels : Array, _project : Project = Global.current_project) -> void:
|
||||
DrawingAlgos.adjust_hsv(_cel, hue_slider.value, sat_slider.value, val_slider.value, _pixels)
|
||||
func commit_action(_cel : Image, _project : Project = Global.current_project) -> void:
|
||||
DrawingAlgos.adjust_hsv(_cel, hue_slider.value, sat_slider.value, val_slider.value, selection_checkbox.pressed, _project)
|
||||
|
||||
|
||||
func reset() -> void:
|
||||
|
|
|
@ -13,8 +13,8 @@ func set_nodes() -> void:
|
|||
affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton
|
||||
|
||||
|
||||
func commit_action(_cel : Image, _pixels : Array, _project : Project = Global.current_project) -> void:
|
||||
DrawingAlgos.invert_image_colors(_cel, _pixels, red, green, blue, alpha)
|
||||
func commit_action(_cel : Image, _project : Project = Global.current_project) -> void:
|
||||
DrawingAlgos.invert_image_colors(_cel, selection_checkbox.pressed, _project, red, green, blue, alpha)
|
||||
|
||||
|
||||
func _on_RButton_toggled(button_pressed : bool) -> void:
|
||||
|
|
|
@ -20,8 +20,8 @@ func set_nodes() -> void:
|
|||
affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton
|
||||
|
||||
|
||||
func commit_action(_cel : Image, _pixels : Array, _project : Project = Global.current_project) -> void:
|
||||
DrawingAlgos.generate_outline(_cel, _pixels, color, thickness, diagonal, inside_image)
|
||||
func commit_action(_cel : Image, _project : Project = Global.current_project) -> void:
|
||||
DrawingAlgos.generate_outline(_cel, selection_checkbox.pressed, _project, color, thickness, diagonal, inside_image)
|
||||
|
||||
|
||||
func _on_ThickValue_value_changed(value : int) -> void:
|
||||
|
|
|
@ -16,6 +16,7 @@ onready var preview_rect : TextureRect = $VBoxContainer/Preview
|
|||
|
||||
|
||||
func _on_ResizeCanvas_about_to_show() -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
image = Image.new()
|
||||
image.create(Global.current_project.size.x, Global.current_project.size.y, false, Image.FORMAT_RGBA8)
|
||||
image.lock()
|
||||
|
|
|
@ -23,15 +23,38 @@ func _about_to_show() -> void:
|
|||
angle_hslider.value = 0
|
||||
|
||||
|
||||
func commit_action(_cel : Image, _pixels : Array, _project : Project = Global.current_project) -> void:
|
||||
func commit_action(_cel : Image, _project : Project = Global.current_project) -> void:
|
||||
var angle : float = deg2rad(angle_hslider.value)
|
||||
# warning-ignore:integer_division
|
||||
# warning-ignore:integer_division
|
||||
var pivot = Vector2(_cel.get_width() / 2, _cel.get_height() / 2)
|
||||
var image := Image.new()
|
||||
image.copy_from(_cel)
|
||||
if _project.has_selection and selection_checkbox.pressed:
|
||||
var selection_rectangle : Rect2 = _project.get_selection_rectangle()
|
||||
pivot = selection_rectangle.position + ((selection_rectangle.end - selection_rectangle.position) / 2)
|
||||
image.lock()
|
||||
_cel.lock()
|
||||
for x in _project.size.x:
|
||||
for y in _project.size.y:
|
||||
var pos := Vector2(x, y)
|
||||
if !_project.can_pixel_get_drawn(pos):
|
||||
image.set_pixelv(pos, Color(0, 0, 0, 0))
|
||||
else:
|
||||
_cel.set_pixelv(pos, Color(0, 0, 0, 0))
|
||||
image.unlock()
|
||||
_cel.unlock()
|
||||
match type_option_button.text:
|
||||
"Rotxel":
|
||||
DrawingAlgos.rotxel(_cel, angle, _pixels)
|
||||
DrawingAlgos.rotxel(image, angle, pivot)
|
||||
"Nearest neighbour":
|
||||
DrawingAlgos.nn_rotate(_cel, angle, _pixels)
|
||||
DrawingAlgos.nn_rotate(image, angle, pivot)
|
||||
"Upscale, Rotate and Downscale":
|
||||
DrawingAlgos.fake_rotsprite(_cel, angle, _pixels)
|
||||
DrawingAlgos.fake_rotsprite(image, angle, pivot)
|
||||
if _project.has_selection and selection_checkbox.pressed:
|
||||
_cel.blend_rect(image, Rect2(Vector2.ZERO, image.get_size()), Vector2.ZERO)
|
||||
else:
|
||||
_cel.blit_rect(image, Rect2(Vector2.ZERO, image.get_size()), Vector2.ZERO)
|
||||
|
||||
|
||||
func _confirmed() -> void:
|
||||
|
|
|
@ -12,6 +12,7 @@ onready var ratio_box : BaseButton = find_node("AspectRatioButton")
|
|||
|
||||
|
||||
func _on_ScaleImage_about_to_show() -> void:
|
||||
Global.canvas.selection.move_content_confirm()
|
||||
width_value.value = Global.current_project.size.x
|
||||
height_value.value = Global.current_project.size.y
|
||||
width_value_perc.value = 100
|
||||
|
|
|
@ -4,6 +4,7 @@ extends VBoxContainer
|
|||
# Node, shortcut
|
||||
onready var tools := [
|
||||
[$RectSelect, "rectangle_select"],
|
||||
[$Move, "move"],
|
||||
[$Zoom, "zoom"],
|
||||
[$Pan, "pan"],
|
||||
[$ColorPicker, "colorpicker"],
|
||||
|
|
|
@ -2,21 +2,37 @@ extends Panel
|
|||
|
||||
|
||||
enum FileMenuId {NEW, OPEN, OPEN_LAST_PROJECT, SAVE, SAVE_AS, EXPORT, EXPORT_AS, QUIT}
|
||||
enum EditMenuId {UNDO, REDO, COPY, CUT, PASTE, DELETE, CLEAR_SELECTION, PREFERENCES}
|
||||
enum EditMenuId {UNDO, REDO, COPY, CUT, PASTE, DELETE, PREFERENCES}
|
||||
enum ViewMenuId {TILE_MODE, WINDOW_TRANSPARENCY, PANEL_LAYOUT, MIRROR_VIEW, SHOW_GRID, SHOW_PIXEL_GRID, SHOW_RULERS, SHOW_GUIDES, SHOW_ANIMATION_TIMELINE, ZEN_MODE, FULLSCREEN_MODE}
|
||||
enum ImageMenuId {SCALE_IMAGE,CENTRALIZE_IMAGE, CROP_IMAGE, RESIZE_CANVAS, FLIP, ROTATE, INVERT_COLORS, DESATURATION, OUTLINE, HSV, GRADIENT, SHADER}
|
||||
enum ImageMenuId {SCALE_IMAGE, CENTRALIZE_IMAGE, CROP_IMAGE, RESIZE_CANVAS, FLIP, ROTATE, INVERT_COLORS, DESATURATION, OUTLINE, HSV, GRADIENT, SHADER}
|
||||
enum SelectMenuId {SELECT_ALL, CLEAR_SELECTION, INVERT}
|
||||
enum HelpMenuId {VIEW_SPLASH_SCREEN, ONLINE_DOCS, ISSUE_TRACKER, CHANGELOG, ABOUT_PIXELORAMA}
|
||||
|
||||
var file_menu_button : MenuButton
|
||||
var edit_menu_button : MenuButton
|
||||
var view_menu_button : MenuButton
|
||||
var image_menu_button : MenuButton
|
||||
var select_menu_button : MenuButton
|
||||
var help_menu_button : MenuButton
|
||||
|
||||
var file_menu : PopupMenu
|
||||
var view_menu : PopupMenu
|
||||
var zen_mode := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
file_menu_button = find_node("FileMenu")
|
||||
edit_menu_button = find_node("EditMenu")
|
||||
view_menu_button = find_node("ViewMenu")
|
||||
image_menu_button = find_node("ImageMenu")
|
||||
select_menu_button = find_node("SelectMenu")
|
||||
help_menu_button = find_node("HelpMenu")
|
||||
|
||||
setup_file_menu()
|
||||
setup_edit_menu()
|
||||
setup_view_menu()
|
||||
setup_image_menu()
|
||||
setup_select_menu()
|
||||
setup_help_menu()
|
||||
|
||||
|
||||
|
@ -32,7 +48,7 @@ func setup_file_menu() -> void:
|
|||
"Export as..." : InputMap.get_action_list("export_file_as")[0].get_scancode_with_modifiers(),
|
||||
"Quit" : InputMap.get_action_list("quit")[0].get_scancode_with_modifiers(),
|
||||
}
|
||||
file_menu = Global.file_menu.get_popup()
|
||||
file_menu = file_menu_button.get_popup()
|
||||
var i := 0
|
||||
|
||||
for item in file_menu_items.keys():
|
||||
|
@ -65,10 +81,9 @@ func setup_edit_menu() -> void:
|
|||
"Cut" : InputMap.get_action_list("cut")[0].get_scancode_with_modifiers(),
|
||||
"Paste" : InputMap.get_action_list("paste")[0].get_scancode_with_modifiers(),
|
||||
"Delete" : InputMap.get_action_list("delete")[0].get_scancode_with_modifiers(),
|
||||
"Clear Selection" : InputMap.get_action_list("clear_selection")[0].get_scancode_with_modifiers(),
|
||||
"Preferences" : 0
|
||||
}
|
||||
var edit_menu : PopupMenu = Global.edit_menu.get_popup()
|
||||
var edit_menu : PopupMenu = edit_menu_button.get_popup()
|
||||
var i := 0
|
||||
|
||||
for item in edit_menu_items.keys():
|
||||
|
@ -92,7 +107,7 @@ func setup_view_menu() -> void:
|
|||
"Zen Mode" : InputMap.get_action_list("zen_mode")[0].get_scancode_with_modifiers(),
|
||||
"Fullscreen Mode" : InputMap.get_action_list("toggle_fullscreen")[0].get_scancode_with_modifiers(),
|
||||
}
|
||||
view_menu = Global.view_menu.get_popup()
|
||||
view_menu = view_menu_button.get_popup()
|
||||
|
||||
var i := 0
|
||||
for item in view_menu_items.keys():
|
||||
|
@ -147,7 +162,7 @@ func setup_image_menu() -> void:
|
|||
"Gradient" : 0,
|
||||
# "Shader" : 0
|
||||
}
|
||||
var image_menu : PopupMenu = Global.image_menu.get_popup()
|
||||
var image_menu : PopupMenu = image_menu_button.get_popup()
|
||||
|
||||
var i := 0
|
||||
for item in image_menu_items.keys():
|
||||
|
@ -159,6 +174,22 @@ func setup_image_menu() -> void:
|
|||
image_menu.connect("id_pressed", self, "image_menu_id_pressed")
|
||||
|
||||
|
||||
func setup_select_menu() -> void:
|
||||
var select_menu_items := { # order as in EditMenuId enum
|
||||
"Select All" : InputMap.get_action_list("select_all")[0].get_scancode_with_modifiers(),
|
||||
"Clear Selection" : InputMap.get_action_list("clear_selection")[0].get_scancode_with_modifiers(),
|
||||
"Invert" : InputMap.get_action_list("invert_selection")[0].get_scancode_with_modifiers(),
|
||||
}
|
||||
var select_menu : PopupMenu = select_menu_button.get_popup()
|
||||
var i := 0
|
||||
|
||||
for item in select_menu_items.keys():
|
||||
select_menu.add_item(item, i, select_menu_items[item])
|
||||
i += 1
|
||||
|
||||
select_menu.connect("id_pressed", self, "select_menu_id_pressed")
|
||||
|
||||
|
||||
func setup_help_menu() -> void:
|
||||
var help_menu_items := { # order as in HelpMenuId enum
|
||||
"View Splash Screen" : 0,
|
||||
|
@ -167,7 +198,7 @@ func setup_help_menu() -> void:
|
|||
"Changelog" : 0,
|
||||
"About Pixelorama" : 0
|
||||
}
|
||||
var help_menu : PopupMenu = Global.help_menu.get_popup()
|
||||
var help_menu : PopupMenu = help_menu_button.get_popup()
|
||||
|
||||
var i := 0
|
||||
for item in help_menu_items.keys():
|
||||
|
@ -259,22 +290,17 @@ func on_recent_projects_submenu_id_pressed(id : int) -> void:
|
|||
func edit_menu_id_pressed(id : int) -> void:
|
||||
match id:
|
||||
EditMenuId.UNDO:
|
||||
Global.current_project.undo_redo.undo()
|
||||
Global.current_project.commit_undo()
|
||||
EditMenuId.REDO:
|
||||
Global.control.redone = true
|
||||
Global.current_project.undo_redo.redo()
|
||||
Global.control.redone = false
|
||||
Global.current_project.commit_redo()
|
||||
EditMenuId.COPY:
|
||||
Global.selection_rectangle.copy()
|
||||
Global.canvas.selection.copy()
|
||||
EditMenuId.CUT:
|
||||
Global.selection_rectangle.cut()
|
||||
Global.canvas.selection.cut()
|
||||
EditMenuId.PASTE:
|
||||
Global.selection_rectangle.paste()
|
||||
Global.canvas.selection.paste()
|
||||
EditMenuId.DELETE:
|
||||
Global.selection_rectangle.delete()
|
||||
EditMenuId.CLEAR_SELECTION:
|
||||
Global.selection_rectangle.set_rect(Rect2(0, 0, 0, 0))
|
||||
Global.selection_rectangle.select_rect()
|
||||
Global.canvas.selection.delete()
|
||||
EditMenuId.PREFERENCES:
|
||||
Global.preferences_dialog.popup_centered(Vector2(400, 280))
|
||||
Global.dialog_open(true)
|
||||
|
@ -343,6 +369,12 @@ func window_transparency(value :float) -> void:
|
|||
|
||||
func toggle_mirror_view() -> void:
|
||||
Global.mirror_view = !Global.mirror_view
|
||||
Global.canvas.selection.marching_ants_outline.scale.x = -Global.canvas.selection.marching_ants_outline.scale.x
|
||||
if Global.mirror_view:
|
||||
Global.canvas.selection.marching_ants_outline.position.x = Global.canvas.selection.marching_ants_outline.position.x + Global.current_project.size.x
|
||||
else:
|
||||
Global.canvas.selection.marching_ants_outline.position.x = 0
|
||||
Global.canvas.selection.update()
|
||||
view_menu.set_item_checked(ViewMenuId.MIRROR_VIEW, Global.mirror_view)
|
||||
|
||||
|
||||
|
@ -476,6 +508,16 @@ func show_hsv_configuration_popup() -> void:
|
|||
Global.dialog_open(true)
|
||||
|
||||
|
||||
func select_menu_id_pressed(id : int) -> void:
|
||||
match id:
|
||||
SelectMenuId.SELECT_ALL:
|
||||
Global.canvas.selection.select_all()
|
||||
SelectMenuId.CLEAR_SELECTION:
|
||||
Global.canvas.selection.clear_selection(true)
|
||||
SelectMenuId.INVERT:
|
||||
Global.canvas.selection.invert()
|
||||
|
||||
|
||||
func help_menu_id_pressed(id : int) -> void:
|
||||
match id:
|
||||
HelpMenuId.VIEW_SPLASH_SCREEN:
|
||||
|
|
|
@ -21,7 +21,7 @@ __meta__ = {
|
|||
|
||||
[node name="FileMenu" type="MenuButton" parent="MenuItems"]
|
||||
margin_right = 35.0
|
||||
margin_bottom = 23.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "File"
|
||||
switch_on_hover = true
|
||||
|
@ -29,7 +29,7 @@ switch_on_hover = true
|
|||
[node name="EditMenu" type="MenuButton" parent="MenuItems"]
|
||||
margin_left = 39.0
|
||||
margin_right = 75.0
|
||||
margin_bottom = 23.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Edit"
|
||||
switch_on_hover = true
|
||||
|
@ -37,7 +37,7 @@ switch_on_hover = true
|
|||
[node name="ViewMenu" type="MenuButton" parent="MenuItems"]
|
||||
margin_left = 79.0
|
||||
margin_right = 121.0
|
||||
margin_bottom = 23.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "View"
|
||||
switch_on_hover = true
|
||||
|
@ -45,15 +45,23 @@ switch_on_hover = true
|
|||
[node name="ImageMenu" type="MenuButton" parent="MenuItems"]
|
||||
margin_left = 125.0
|
||||
margin_right = 177.0
|
||||
margin_bottom = 23.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Image"
|
||||
switch_on_hover = true
|
||||
|
||||
[node name="HelpMenu" type="MenuButton" parent="MenuItems"]
|
||||
[node name="SelectMenu" type="MenuButton" parent="MenuItems"]
|
||||
margin_left = 181.0
|
||||
margin_right = 223.0
|
||||
margin_bottom = 23.0
|
||||
margin_right = 232.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Select"
|
||||
switch_on_hover = true
|
||||
|
||||
[node name="HelpMenu" type="MenuButton" parent="MenuItems"]
|
||||
margin_left = 236.0
|
||||
margin_right = 278.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Help"
|
||||
switch_on_hover = true
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
[ext_resource path="res://src/UI/TransparentChecker.tscn" type="PackedScene" id=5]
|
||||
[ext_resource path="res://src/UI/Canvas/Rulers/HorizontalRuler.gd" type="Script" id=6]
|
||||
[ext_resource path="res://src/UI/Canvas/CameraMovement.gd" type="Script" id=7]
|
||||
[ext_resource path="res://src/SelectionRectangle.gd" type="Script" id=8]
|
||||
[ext_resource path="res://src/Shaders/TransparentChecker.shader" type="Shader" id=9]
|
||||
[ext_resource path="res://assets/graphics/dark_themes/tools/bucket.png" type="Texture" id=10]
|
||||
[ext_resource path="res://assets/graphics/dark_themes/tools/colorpicker.png" type="Texture" id=11]
|
||||
|
@ -25,6 +24,7 @@
|
|||
[ext_resource path="res://src/UI/ViewportContainer.gd" type="Script" id=23]
|
||||
[ext_resource path="res://assets/graphics/dark_themes/tools/rectangletool.png" type="Texture" id=24]
|
||||
[ext_resource path="res://assets/graphics/dark_themes/tools/ellipsetool.png" type="Texture" id=25]
|
||||
[ext_resource path="res://assets/graphics/dark_themes/tools/move.png" type="Texture" id=26]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id=1]
|
||||
shader = ExtResource( 9 )
|
||||
|
@ -88,7 +88,7 @@ __meta__ = {
|
|||
margin_left = 7.0
|
||||
margin_top = 7.0
|
||||
margin_right = 39.0
|
||||
margin_bottom = 363.0
|
||||
margin_bottom = 399.0
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 0
|
||||
script = ExtResource( 1 )
|
||||
|
@ -113,7 +113,7 @@ __meta__ = {
|
|||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Zoom" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
[node name="Move" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 36.0
|
||||
|
@ -123,6 +123,24 @@ rect_min_size = Vector2( 32, 32 )
|
|||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="ToolPanel/PanelContainer/ToolButtons/Move"]
|
||||
margin_right = 32.0
|
||||
margin_bottom = 32.0
|
||||
texture = ExtResource( 26 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Zoom" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 72.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 104.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="ToolPanel/PanelContainer/ToolButtons/Zoom"]
|
||||
margin_right = 32.0
|
||||
margin_bottom = 32.0
|
||||
|
@ -134,9 +152,9 @@ __meta__ = {
|
|||
[node name="Pan" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 72.0
|
||||
margin_top = 108.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 104.0
|
||||
margin_bottom = 140.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
@ -152,9 +170,9 @@ __meta__ = {
|
|||
[node name="ColorPicker" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 108.0
|
||||
margin_top = 144.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 140.0
|
||||
margin_bottom = 176.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
@ -170,9 +188,9 @@ __meta__ = {
|
|||
[node name="Pencil" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 144.0
|
||||
margin_top = 180.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 176.0
|
||||
margin_bottom = 212.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
@ -188,9 +206,9 @@ __meta__ = {
|
|||
[node name="Eraser" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 180.0
|
||||
margin_top = 216.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 212.0
|
||||
margin_bottom = 248.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
@ -206,9 +224,9 @@ __meta__ = {
|
|||
[node name="Bucket" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 216.0
|
||||
margin_top = 252.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 248.0
|
||||
margin_bottom = 284.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
@ -224,9 +242,9 @@ __meta__ = {
|
|||
[node name="LightenDarken" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 252.0
|
||||
margin_top = 288.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 284.0
|
||||
margin_bottom = 320.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
@ -242,9 +260,9 @@ __meta__ = {
|
|||
[node name="RectangleTool" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 288.0
|
||||
margin_top = 324.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 320.0
|
||||
margin_bottom = 356.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
@ -260,9 +278,9 @@ __meta__ = {
|
|||
[node name="EllipseTool" type="Button" parent="ToolPanel/PanelContainer/ToolButtons" groups=[
|
||||
"UIButtons",
|
||||
]]
|
||||
margin_top = 324.0
|
||||
margin_top = 360.0
|
||||
margin_right = 32.0
|
||||
margin_bottom = 356.0
|
||||
margin_bottom = 392.0
|
||||
rect_min_size = Vector2( 32, 32 )
|
||||
mouse_default_cursor_shape = 2
|
||||
button_mask = 3
|
||||
|
@ -367,15 +385,6 @@ current = true
|
|||
zoom = Vector2( 0.15, 0.15 )
|
||||
script = ExtResource( 7 )
|
||||
|
||||
[node name="SelectionRectangle" type="Polygon2D" parent="CanvasAndTimeline/ViewportAndRulers/HSplitContainer/ViewportandVerticalRuler/ViewportContainer/Viewport"]
|
||||
visible = false
|
||||
z_index = 1
|
||||
color = Color( 0.0823529, 0.694118, 0.623529, 0.592157 )
|
||||
invert_enable = true
|
||||
invert_border = 0.5
|
||||
polygon = PoolVector2Array( 0, 0, 0, 0, 0, 0, 0, 0 )
|
||||
script = ExtResource( 8 )
|
||||
|
||||
[node name="ViewportContainer2" type="ViewportContainer" parent="CanvasAndTimeline/ViewportAndRulers/HSplitContainer"]
|
||||
margin_left = 902.0
|
||||
margin_right = 902.0
|
||||
|
|
Loading…
Add table
Reference in a new issue