mirror of
https://github.com/tonytins/CozyPixelStudio.git
synced 2025-06-25 21:44:42 -04:00
Bring refactoring changes to master (#253)
* Refactoring image_menu_id_pressed method in Main.gd (#243) * Refactoring image_menu_id_pressed method in Main.gd I've moved the code from each "match" case into a seperate method to make it more readable. Co-authored-by: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> * Refactoring Main.gd. Mostly cutting big methods into smaller ones. (#244) * Refactoring Main.gd. Mostly cutting big methods into smaller one. - Reduced size of _ready method in Main.gd - Moved code from certain parts of old _ready method into seperate methods - Fixed the translation bug related to CurrentFrame node in TopMenuContainer scene. The CurrentFrame node wasn't updating the language when I was changing language. I've also changed the translation file for this. - Fixed Global.palette_option_button.selected related warning. Because of some unknown reasons, git didn't push completed line there. - Moved code from file_menu_id_pressed and view_menu_id_pressed method in Main.gd to separate methods to make it more readable. * Removed window_title changes from Main.tscn Co-authored-by: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> * Fixed TextureRect images of the circle brushes in BrushesPopup They all had the pixel brush image in their TextureRect * Split code from PreferencesDialog.gd to HandleLanguages.gd Also moved PreferencesDialog script & scene to src/Preferences. More Preferences code splitting will follow. * Split theme related code from PreferencesDialog into HandleThemes.gd * Moved shortcuts code from PreferencesDialog * Created DrawingAlgos.gd and moved a lot of drawing code there Moved code from Global.gd and Canvas.gd to DrawingAlgos.gd. Will also move the fill_gaps and draw_brush methods of Canvas.gd next. Maybe even refactor the inside of them a bit to make them easier to read. * Connected "files_dropped" signal to a method This lets the user drag and drop files into Pixelorama, while it runs, to open them. This doesn't work properly and will crash when it can't open the files. It will get merged into master soon. * Renamed handle_running_pixelorama_with_arguments() to handle_loading_files() handle_loading_files() is also used for _on_files_dropped() * Moved draw_brush() and fill_gaps() from Canvas.gd to DrawingAlgos.gd draw_brush() is currently very ugly and probably needs inside refactoring * Removed coord clamping from fill_gaps() This should make line making behave as expected when the mouse is outside of canvas boundaries * Drawing is no longer limited by the canvas boundaries his means that, if you have a brush largen than 1px, you can draw on the edges of the canvas. All pixels that are being drawn outside of the canvas will still have no effect. * Use enums instead of strings for tools This could be a slight increase in performance * Fixed line making with Shift and don't let color picker pick colors outside of canvas * Changed Global node variables to arrays for left/right Instead of having 2 variables for left & right nodes, use an array instead. This will help with better looking code, automation and less repetitive code, as seen in ToolButtons.gd. Move related refactoring will follow. * More Global left/right variables became Arrays Global.update_left_custom_brush() and its right counterpart have also now become Global.update_custom_brush(mouse_button : int) * Use Global.Mouse_Button instead of strings for comparison This should be a slight increase in performance * Refactoring perferences dialog (#251) * Added ItemList to themes * Language and theme checkboxes are now radio buttons * Even more Global left/right variables became arrays ColorAndToolOptions has now the same code for left and right tool options, with more similar refactoring coming soon to places like Canvas and DrawingAlgos * Refactored Canvas.gd * Refactored DrawingAlgos.draw_brush(), made draw_pixel() method This also fixes alpha blending and lighting/darkening issues when drawing pixels with mirroring. * Remove draw_pixel(), use draw_pixel_blended() instead * Ignore warnings I don't know what else to do about them, they seem trivial anyway * Use enum instead of strings for Global.theme_type Another potential small performance boost when changing themes. * Use a new Layer class to handle layer information This replaces the old Global.layers nested array mess, and makes the code easier to read and to understand. * Fixed linked cel crash and layer naming * Created a new Cel class, to handle cel information Like the Layer class, it is used in place of Canvas.layers nested array mess. It hasn't been tested thoroughly yet, so there may be crashes. * Fixed issue where if you moved a frame to the start (move left), it was invisible * Added AnimationTag class Replaces nested Global.animation_tags arrays. Also replaced array.duplicate(true) with looping through the array and creating a new class for each array element, because duplicate(true) does not create new classes, unfortunately, which was causing issues with undo/redo. Co-authored-by: Igor Santarek <jegor377@gmail.com> Co-authored-by: Kinwailo <lokinwai@gmail.com>
This commit is contained in:
parent
7a8ae9428b
commit
881e53dadc
48 changed files with 4037 additions and 4151 deletions
646
src/Autoload/DrawingAlgos.gd
Normal file
646
src/Autoload/DrawingAlgos.gd
Normal file
|
@ -0,0 +1,646 @@
|
|||
extends Node
|
||||
|
||||
|
||||
const Drawer = preload("res://src/Classes/Drawers.gd").Drawer
|
||||
const SimpleDrawer = preload("res://src/Classes/Drawers.gd").SimpleDrawer
|
||||
const PixelPerfectDrawer = preload("res://src/Classes/Drawers.gd").PixelPerfectDrawer
|
||||
|
||||
var pixel_perfect_drawer := PixelPerfectDrawer.new()
|
||||
var pixel_perfect_drawer_h_mirror := PixelPerfectDrawer.new()
|
||||
var pixel_perfect_drawer_v_mirror := PixelPerfectDrawer.new()
|
||||
var pixel_perfect_drawer_hv_mirror := PixelPerfectDrawer.new()
|
||||
var simple_drawer := SimpleDrawer.new()
|
||||
|
||||
var mouse_press_pixels := [] # Cleared after mouse release
|
||||
var mouse_press_pressure_values := [] # Cleared after mouse release
|
||||
|
||||
|
||||
func draw_pixel_blended(sprite : Image, pos : Vector2, color : Color, pen_pressure : float, current_mouse_button := -1, current_action := -1, drawer : Drawer = simple_drawer) -> void:
|
||||
var west_limit = Global.canvas.west_limit
|
||||
var east_limit = Global.canvas.east_limit
|
||||
var north_limit = Global.canvas.north_limit
|
||||
var south_limit = Global.canvas.south_limit
|
||||
if !point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
|
||||
return
|
||||
|
||||
var pos_floored := pos.floor()
|
||||
var current_pixel_color = sprite.get_pixelv(pos)
|
||||
var saved_pixel_index := mouse_press_pixels.find(pos_floored)
|
||||
if current_action == Global.Tools.PENCIL && color.a < 1:
|
||||
color = blend_colors(color, current_pixel_color)
|
||||
|
||||
if current_pixel_color != color && (saved_pixel_index == -1 || pen_pressure > mouse_press_pressure_values[saved_pixel_index]):
|
||||
if current_action == Global.Tools.LIGHTENDARKEN:
|
||||
var ld : int = Global.ld_modes[current_mouse_button]
|
||||
var ld_amount : float = Global.ld_amounts[current_mouse_button]
|
||||
if ld == Global.Lighten_Darken_Mode.LIGHTEN:
|
||||
color = current_pixel_color.lightened(ld_amount)
|
||||
else:
|
||||
color = current_pixel_color.darkened(ld_amount)
|
||||
|
||||
if saved_pixel_index == -1:
|
||||
mouse_press_pixels.append(pos_floored)
|
||||
mouse_press_pressure_values.append(pen_pressure)
|
||||
else:
|
||||
mouse_press_pressure_values[saved_pixel_index] = pen_pressure
|
||||
drawer.set_pixel(sprite, pos, color)
|
||||
|
||||
|
||||
func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_button : int, pen_pressure : float, current_action := -1) -> void:
|
||||
if Global.can_draw && Global.has_focus:
|
||||
var west_limit = Global.canvas.west_limit
|
||||
var east_limit = Global.canvas.east_limit
|
||||
var north_limit = Global.canvas.north_limit
|
||||
var south_limit = Global.canvas.south_limit
|
||||
|
||||
if Global.pressure_sensitivity_mode == Global.Pressure_Sensitivity.ALPHA:
|
||||
if current_action == Global.Tools.PENCIL:
|
||||
color.a *= pen_pressure
|
||||
elif current_action == Global.Tools.ERASER: # This is not working
|
||||
color.a *= (1.0 - pen_pressure)
|
||||
|
||||
var brush_size : int = Global.brush_sizes[current_mouse_button]
|
||||
var brush_type : int = Global.current_brush_types[current_mouse_button]
|
||||
|
||||
var horizontal_mirror : bool = Global.horizontal_mirror[current_mouse_button]
|
||||
var vertical_mirror : bool = Global.vertical_mirror[current_mouse_button]
|
||||
|
||||
if brush_type == Global.Brush_Types.PIXEL || current_action == Global.Tools.LIGHTENDARKEN:
|
||||
var start_pos_x = pos.x - (brush_size >> 1)
|
||||
var start_pos_y = pos.y - (brush_size >> 1)
|
||||
var end_pos_x = start_pos_x + brush_size
|
||||
var end_pos_y = start_pos_y + brush_size
|
||||
|
||||
for cur_pos_x in range(start_pos_x, end_pos_x):
|
||||
for cur_pos_y in range(start_pos_y, end_pos_y):
|
||||
var pixel_perfect : bool = Global.pixel_perfect[current_mouse_button]
|
||||
# warning-ignore:incompatible_ternary
|
||||
var drawer : Drawer = pixel_perfect_drawer if pixel_perfect else simple_drawer
|
||||
draw_pixel_blended(sprite, Vector2(cur_pos_x, cur_pos_y), color, pen_pressure, current_mouse_button, current_action, drawer)
|
||||
|
||||
# Handle mirroring
|
||||
var mirror_x = east_limit + west_limit - cur_pos_x - 1
|
||||
var mirror_y = south_limit + north_limit - cur_pos_y - 1
|
||||
if horizontal_mirror:
|
||||
# warning-ignore:incompatible_ternary
|
||||
var drawer_h_mirror : Drawer = pixel_perfect_drawer_h_mirror if pixel_perfect else simple_drawer
|
||||
draw_pixel_blended(sprite, Vector2(mirror_x, cur_pos_y), color, pen_pressure, current_mouse_button, current_action, drawer_h_mirror)
|
||||
if vertical_mirror:
|
||||
# warning-ignore:incompatible_ternary
|
||||
var drawer_v_mirror : Drawer = pixel_perfect_drawer_v_mirror if pixel_perfect else simple_drawer
|
||||
draw_pixel_blended(sprite, Vector2(cur_pos_x, mirror_y), color, pen_pressure, current_mouse_button, current_action, drawer_v_mirror)
|
||||
if horizontal_mirror && vertical_mirror:
|
||||
# warning-ignore:incompatible_ternary
|
||||
var drawer_hv_mirror : Drawer = pixel_perfect_drawer_hv_mirror if pixel_perfect else simple_drawer
|
||||
draw_pixel_blended(sprite, Vector2(mirror_x, mirror_y), color, pen_pressure, current_mouse_button, current_action, drawer_hv_mirror)
|
||||
|
||||
Global.canvas.sprite_changed_this_frame = true
|
||||
|
||||
elif brush_type == Global.Brush_Types.CIRCLE || brush_type == Global.Brush_Types.FILLED_CIRCLE:
|
||||
plot_circle(sprite, pos.x, pos.y, brush_size, color, brush_type == Global.Brush_Types.FILLED_CIRCLE)
|
||||
|
||||
# Handle mirroring
|
||||
var mirror_x = east_limit + west_limit - pos.x
|
||||
var mirror_y = south_limit + north_limit - pos.y
|
||||
if horizontal_mirror:
|
||||
plot_circle(sprite, mirror_x, pos.y, brush_size, color, brush_type == Global.Brush_Types.FILLED_CIRCLE)
|
||||
if vertical_mirror:
|
||||
plot_circle(sprite, pos.x, mirror_y, brush_size, color, brush_type == Global.Brush_Types.FILLED_CIRCLE)
|
||||
if horizontal_mirror && vertical_mirror:
|
||||
plot_circle(sprite, mirror_x, mirror_y, brush_size, color, brush_type == Global.Brush_Types.FILLED_CIRCLE)
|
||||
|
||||
Global.canvas.sprite_changed_this_frame = true
|
||||
|
||||
else:
|
||||
var brush_index : int = Global.custom_brush_indexes[current_mouse_button]
|
||||
var custom_brush_image : Image
|
||||
if brush_type != Global.Brush_Types.RANDOM_FILE:
|
||||
custom_brush_image = Global.custom_brush_images[current_mouse_button]
|
||||
else: # Handle random brush
|
||||
var brush_button = Global.file_brush_container.get_child(brush_index + 3)
|
||||
var random_index = randi() % brush_button.random_brushes.size()
|
||||
custom_brush_image = Image.new()
|
||||
custom_brush_image.copy_from(brush_button.random_brushes[random_index])
|
||||
var custom_brush_size = custom_brush_image.get_size()
|
||||
custom_brush_image.resize(custom_brush_size.x * brush_size, custom_brush_size.y * brush_size, Image.INTERPOLATE_NEAREST)
|
||||
custom_brush_image = Global.blend_image_with_color(custom_brush_image, color, Global.interpolate_spinboxes[current_mouse_button].value / 100)
|
||||
custom_brush_image.lock()
|
||||
|
||||
var custom_brush_size := custom_brush_image.get_size() - Vector2.ONE
|
||||
pos = pos.floor()
|
||||
var dst := rectangle_center(pos, custom_brush_size)
|
||||
var src_rect := Rect2(Vector2.ZERO, custom_brush_size + Vector2.ONE)
|
||||
# Rectangle with the same size as the brush, but at cursor's position
|
||||
var pos_rect := Rect2(dst, custom_brush_size + Vector2.ONE)
|
||||
|
||||
# The selection rectangle
|
||||
# If there's no rectangle, the whole canvas is considered a selection
|
||||
var selection_rect := Rect2()
|
||||
selection_rect.position = Vector2(west_limit, north_limit)
|
||||
selection_rect.end = Vector2(east_limit, south_limit)
|
||||
# Intersection of the position rectangle and selection
|
||||
var pos_rect_clipped := pos_rect.clip(selection_rect)
|
||||
# If the size is 0, that means that the brush wasn't positioned inside the selection
|
||||
if pos_rect_clipped.size == Vector2.ZERO:
|
||||
return
|
||||
|
||||
# Re-position src_rect and dst based on the clipped position
|
||||
var pos_difference := (pos_rect.position - pos_rect_clipped.position).abs()
|
||||
# Obviously, if pos_rect and pos_rect_clipped are the same, pos_difference is Vector2.ZERO
|
||||
src_rect.position = pos_difference
|
||||
dst += pos_difference
|
||||
src_rect.end -= pos_rect.end - pos_rect_clipped.end
|
||||
# If the selection rectangle is smaller than the brush, ...
|
||||
# ... make sure pixels aren't being drawn outside the selection by adjusting src_rect's size
|
||||
src_rect.size.x = min(src_rect.size.x, selection_rect.size.x)
|
||||
src_rect.size.y = min(src_rect.size.y, selection_rect.size.y)
|
||||
|
||||
# Handle mirroring
|
||||
var mirror_x = east_limit + west_limit - pos.x - (pos.x - dst.x)
|
||||
var mirror_y = south_limit + north_limit - pos.y - (pos.y - dst.y)
|
||||
if int(pos_rect_clipped.size.x) % 2 != 0:
|
||||
mirror_x -= 1
|
||||
if int(pos_rect_clipped.size.y) % 2 != 0:
|
||||
mirror_y -= 1
|
||||
# Use custom blend function cause of godot's issue #31124
|
||||
if color.a > 0: # If it's the pencil
|
||||
blend_rect(sprite, custom_brush_image, src_rect, dst)
|
||||
if horizontal_mirror:
|
||||
blend_rect(sprite, custom_brush_image, src_rect, Vector2(mirror_x, dst.y))
|
||||
if vertical_mirror:
|
||||
blend_rect(sprite, custom_brush_image, src_rect, Vector2(dst.x, mirror_y))
|
||||
if horizontal_mirror && vertical_mirror:
|
||||
blend_rect(sprite, custom_brush_image, src_rect, Vector2(mirror_x, mirror_y))
|
||||
|
||||
else: # if it's transparent - if it's the eraser
|
||||
var custom_brush := Image.new()
|
||||
custom_brush.copy_from(Global.custom_brushes[brush_index])
|
||||
custom_brush_size = custom_brush.get_size()
|
||||
custom_brush.resize(custom_brush_size.x * brush_size, custom_brush_size.y * brush_size, Image.INTERPOLATE_NEAREST)
|
||||
var custom_brush_blended = Global.blend_image_with_color(custom_brush, color, 1)
|
||||
|
||||
sprite.blit_rect_mask(custom_brush_blended, custom_brush, src_rect, dst)
|
||||
if horizontal_mirror:
|
||||
sprite.blit_rect_mask(custom_brush_blended, custom_brush, src_rect, Vector2(mirror_x, dst.y))
|
||||
if vertical_mirror:
|
||||
sprite.blit_rect_mask(custom_brush_blended, custom_brush, src_rect, Vector2(dst.x, mirror_y))
|
||||
if horizontal_mirror && vertical_mirror:
|
||||
sprite.blit_rect_mask(custom_brush_blended, custom_brush, src_rect, Vector2(mirror_x, mirror_y))
|
||||
|
||||
sprite.lock()
|
||||
Global.canvas.sprite_changed_this_frame = true
|
||||
|
||||
Global.canvas.previous_mouse_pos_for_lines = pos.floor() + Vector2(0.5, 0.5)
|
||||
Global.canvas.previous_mouse_pos_for_lines.x = clamp(Global.canvas.previous_mouse_pos_for_lines.x, Global.canvas.location.x, Global.canvas.location.x + Global.canvas.size.x)
|
||||
Global.canvas.previous_mouse_pos_for_lines.y = clamp(Global.canvas.previous_mouse_pos_for_lines.y, Global.canvas.location.y, Global.canvas.location.y + Global.canvas.size.y)
|
||||
if Global.canvas.is_making_line:
|
||||
Global.canvas.line_2d.set_point_position(0, Global.canvas.previous_mouse_pos_for_lines)
|
||||
|
||||
|
||||
# Bresenham's Algorithm
|
||||
# Thanks to https://godotengine.org/qa/35276/tile-based-line-drawing-algorithm-efficiency
|
||||
func fill_gaps(sprite : Image, end_pos : Vector2, start_pos : Vector2, color : Color, current_mouse_button : int, pen_pressure : float, current_action := -1) -> void:
|
||||
var previous_mouse_pos_floored = start_pos.floor()
|
||||
var mouse_pos_floored = end_pos.floor()
|
||||
var dx := int(abs(mouse_pos_floored.x - previous_mouse_pos_floored.x))
|
||||
var dy := int(-abs(mouse_pos_floored.y - previous_mouse_pos_floored.y))
|
||||
var err := dx + dy
|
||||
var e2 := err << 1 # err * 2
|
||||
var sx = 1 if previous_mouse_pos_floored.x < mouse_pos_floored.x else -1
|
||||
var sy = 1 if previous_mouse_pos_floored.y < mouse_pos_floored.y else -1
|
||||
var x = previous_mouse_pos_floored.x
|
||||
var y = previous_mouse_pos_floored.y
|
||||
while !(x == mouse_pos_floored.x && y == mouse_pos_floored.y):
|
||||
draw_brush(sprite, Vector2(x, y), color, current_mouse_button, pen_pressure, current_action)
|
||||
e2 = err << 1
|
||||
if e2 >= dy:
|
||||
err += dy
|
||||
x += sx
|
||||
if e2 <= dx:
|
||||
err += dx
|
||||
y += sy
|
||||
|
||||
|
||||
# Algorithm based on http://members.chello.at/easyfilter/bresenham.html
|
||||
func plot_circle(sprite : Image, xm : int, ym : int, r : int, color : Color, fill := false) -> void:
|
||||
var radius := r # Used later for filling
|
||||
var x := -r
|
||||
var y := 0
|
||||
var err := 2 - r * 2 # II. Quadrant
|
||||
while x < 0:
|
||||
var quadrant_1 := Vector2(xm - x, ym + y)
|
||||
var quadrant_2 := Vector2(xm - y, ym - x)
|
||||
var quadrant_3 := Vector2(xm + x, ym - y)
|
||||
var quadrant_4 := Vector2(xm + y, ym + x)
|
||||
draw_pixel_blended(sprite, quadrant_1, color, Global.canvas.pen_pressure)
|
||||
draw_pixel_blended(sprite, quadrant_2, color, Global.canvas.pen_pressure)
|
||||
draw_pixel_blended(sprite, quadrant_3, color, Global.canvas.pen_pressure)
|
||||
draw_pixel_blended(sprite, quadrant_4, color, Global.canvas.pen_pressure)
|
||||
|
||||
r = err
|
||||
if r <= y:
|
||||
y += 1
|
||||
err += y * 2 + 1
|
||||
if r > x || err > y:
|
||||
x += 1
|
||||
err += x * 2 + 1
|
||||
|
||||
if fill:
|
||||
for j in range (-radius, radius + 1):
|
||||
for i in range (-radius, radius + 1):
|
||||
if i * i + j * j <= radius * radius:
|
||||
var draw_pos := Vector2(i + xm, j + ym)
|
||||
draw_pixel_blended(sprite, draw_pos, color, Global.canvas.pen_pressure)
|
||||
|
||||
|
||||
# Thanks to https://en.wikipedia.org/wiki/Flood_fill
|
||||
func flood_fill(sprite : Image, pos : Vector2, target_color : Color, replace_color : Color) -> void:
|
||||
var west_limit = Global.canvas.west_limit
|
||||
var east_limit = Global.canvas.east_limit
|
||||
var north_limit = Global.canvas.north_limit
|
||||
var south_limit = Global.canvas.south_limit
|
||||
pos = pos.floor()
|
||||
var pixel = sprite.get_pixelv(pos)
|
||||
if target_color == replace_color:
|
||||
return
|
||||
elif pixel != target_color:
|
||||
return
|
||||
else:
|
||||
|
||||
if !point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
|
||||
return
|
||||
|
||||
var q = [pos]
|
||||
for n in q:
|
||||
# If the difference in colors is very small, break the loop (thanks @azagaya on GitHub!)
|
||||
if target_color == replace_color:
|
||||
break
|
||||
var west : Vector2 = n
|
||||
var east : Vector2 = n
|
||||
while west.x >= west_limit && sprite.get_pixelv(west) == target_color:
|
||||
west += Vector2.LEFT
|
||||
while east.x < east_limit && sprite.get_pixelv(east) == target_color:
|
||||
east += Vector2.RIGHT
|
||||
for px in range(west.x + 1, east.x):
|
||||
var p := Vector2(px, n.y)
|
||||
# Draw
|
||||
sprite.set_pixelv(p, replace_color)
|
||||
replace_color = sprite.get_pixelv(p)
|
||||
var north := p + Vector2.UP
|
||||
var south := p + Vector2.DOWN
|
||||
if north.y >= north_limit && sprite.get_pixelv(north) == target_color:
|
||||
q.append(north)
|
||||
if south.y < south_limit && sprite.get_pixelv(south) == target_color:
|
||||
q.append(south)
|
||||
|
||||
Global.canvas.sprite_changed_this_frame = true
|
||||
|
||||
|
||||
func pattern_fill(sprite : Image, pos : Vector2, pattern : Image, target_color : Color, var offset : Vector2) -> void:
|
||||
var west_limit = Global.canvas.west_limit
|
||||
var east_limit = Global.canvas.east_limit
|
||||
var north_limit = Global.canvas.north_limit
|
||||
var south_limit = Global.canvas.south_limit
|
||||
pos = pos.floor()
|
||||
if !point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
|
||||
return
|
||||
|
||||
pattern.lock()
|
||||
var pattern_size := pattern.get_size()
|
||||
var q = [pos]
|
||||
|
||||
for n in q:
|
||||
var west : Vector2 = n
|
||||
var east : Vector2 = n
|
||||
while west.x >= west_limit && sprite.get_pixelv(west) == target_color:
|
||||
west += Vector2.LEFT
|
||||
while east.x < east_limit && sprite.get_pixelv(east) == target_color:
|
||||
east += Vector2.RIGHT
|
||||
|
||||
for px in range(west.x + 1, east.x):
|
||||
var p := Vector2(px, n.y)
|
||||
var xx : int = int(px + offset.x) % int(pattern_size.x)
|
||||
var yy : int = int(n.y + offset.y) % int(pattern_size.y)
|
||||
var pattern_color : Color = pattern.get_pixel(xx, yy)
|
||||
if pattern_color == target_color:
|
||||
continue
|
||||
sprite.set_pixelv(p, pattern_color)
|
||||
|
||||
var north := p + Vector2.UP
|
||||
var south := p + Vector2.DOWN
|
||||
if north.y >= north_limit && sprite.get_pixelv(north) == target_color:
|
||||
q.append(north)
|
||||
if south.y < south_limit && sprite.get_pixelv(south) == target_color:
|
||||
q.append(south)
|
||||
|
||||
pattern.unlock()
|
||||
Global.canvas.sprite_changed_this_frame = true
|
||||
|
||||
|
||||
func blend_colors(color_1 : Color, color_2 : Color) -> Color:
|
||||
var color := Color()
|
||||
color.a = color_1.a + color_2.a * (1 - color_1.a) # Blend alpha
|
||||
if color.a != 0:
|
||||
# Blend colors
|
||||
color.r = (color_1.r * color_1.a + color_2.r * color_2.a * (1-color_1.a)) / color.a
|
||||
color.g = (color_1.g * color_1.a + color_2.g * color_2.a * (1-color_1.a)) / color.a
|
||||
color.b = (color_1.b * color_1.a + color_2.b * color_2.a * (1-color_1.a)) / color.a
|
||||
return color
|
||||
|
||||
|
||||
# Custom blend rect function, needed because Godot's issue #31124
|
||||
func blend_rect(bg : Image, brush : Image, src_rect : Rect2, dst : Vector2) -> void:
|
||||
var brush_size := brush.get_size()
|
||||
var clipped_src_rect := Rect2(Vector2.ZERO, brush_size).clip(src_rect)
|
||||
if clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0:
|
||||
return
|
||||
var src_underscan := Vector2(min(0, src_rect.position.x), min(0, src_rect.position.y))
|
||||
var dest_rect := Rect2(0, 0, bg.get_width(), bg.get_height()).clip(Rect2(dst - src_underscan, clipped_src_rect.size))
|
||||
|
||||
for x in range(0, dest_rect.size.x):
|
||||
for y in range(0, dest_rect.size.y):
|
||||
var src_x := clipped_src_rect.position.x + x;
|
||||
var src_y := clipped_src_rect.position.y + y;
|
||||
|
||||
var dst_x := dest_rect.position.x + x;
|
||||
var dst_y := dest_rect.position.y + y;
|
||||
|
||||
brush.lock()
|
||||
var brush_color := brush.get_pixel(src_x, src_y)
|
||||
var bg_color := bg.get_pixel(dst_x, dst_y)
|
||||
var out_color := blend_colors(brush_color, bg_color)
|
||||
if out_color.a != 0:
|
||||
bg.set_pixel(dst_x, dst_y, out_color)
|
||||
brush.unlock()
|
||||
|
||||
|
||||
func scale3X(sprite : Image, tol : float = 50) -> Image:
|
||||
var scaled = Image.new()
|
||||
scaled.create(sprite.get_width()*3, sprite.get_height()*3, false, Image.FORMAT_RGBA8)
|
||||
scaled.lock()
|
||||
sprite.lock()
|
||||
var a : Color
|
||||
var b : Color
|
||||
var c : Color
|
||||
var d : Color
|
||||
var e : Color
|
||||
var f : Color
|
||||
var g : Color
|
||||
var h : Color
|
||||
var i : Color
|
||||
|
||||
for x in range(1,sprite.get_width()-1):
|
||||
for y in range(1,sprite.get_height()-1):
|
||||
var xs : float = 3*x
|
||||
var ys : float = 3*y
|
||||
|
||||
a = sprite.get_pixel(x-1,y-1)
|
||||
b = sprite.get_pixel(x,y-1)
|
||||
c = sprite.get_pixel(x+1,y-1)
|
||||
d = sprite.get_pixel(x-1,y)
|
||||
e = sprite.get_pixel(x,y)
|
||||
f = sprite.get_pixel(x+1,y)
|
||||
g = sprite.get_pixel(x-1,y+1)
|
||||
h = sprite.get_pixel(x,y+1)
|
||||
i = sprite.get_pixel(x+1,y+1)
|
||||
|
||||
var db : bool = similarColors(d, b, tol)
|
||||
var dh : bool = similarColors(d, h, tol)
|
||||
var bf : bool = similarColors(f, b, tol)
|
||||
var ec : bool = similarColors(e, c, tol)
|
||||
var ea : bool = similarColors(e, a, tol)
|
||||
var fh : bool = similarColors(f, h, tol)
|
||||
var eg : bool = similarColors(e, g, tol)
|
||||
var ei : bool = similarColors(e, i, tol)
|
||||
|
||||
scaled.set_pixel(xs-1, ys-1, d if (db and !dh and !bf) else e )
|
||||
scaled.set_pixel(xs, ys-1, b if (db and !dh and !bf and !ec) or
|
||||
(bf and !db and !fh and !ea) else e)
|
||||
scaled.set_pixel(xs+1, ys-1, f if (bf and !db and !fh) else e)
|
||||
scaled.set_pixel(xs-1, ys, d if (dh and !fh and !db and !ea) or
|
||||
(db and !dh and !bf and !eg) else e)
|
||||
scaled.set_pixel(xs, ys, e);
|
||||
scaled.set_pixel(xs+1, ys, f if (bf and !db and !fh and !ei) or
|
||||
(fh and !bf and !dh and !ec) else e)
|
||||
scaled.set_pixel(xs-1, ys+1, d if (dh and !fh and !db) else e)
|
||||
scaled.set_pixel(xs, ys+1, h if (fh and !bf and !dh and !eg) or
|
||||
(dh and !fh and !db and !ei) else e)
|
||||
scaled.set_pixel(xs+1, ys+1, f if (fh and !bf and !dh) else e)
|
||||
|
||||
scaled.unlock()
|
||||
sprite.unlock()
|
||||
return scaled
|
||||
|
||||
|
||||
func rotxel(sprite : Image, angle : float) -> 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)
|
||||
return
|
||||
|
||||
var aux : Image = Image.new()
|
||||
aux.copy_from(sprite)
|
||||
# warning-ignore:integer_division
|
||||
# warning-ignore:integer_division
|
||||
var center : Vector2 = Vector2(sprite.get_width() / 2, sprite.get_height() / 2)
|
||||
var ox : int
|
||||
var oy : int
|
||||
var p : Color
|
||||
aux.lock()
|
||||
sprite.lock()
|
||||
for x in range(sprite.get_width()):
|
||||
for y in range(sprite.get_height()):
|
||||
var dx = 3*(x - center.x)
|
||||
var dy = 3*(y - center.y)
|
||||
var found_pixel : bool = false
|
||||
for k in range(9):
|
||||
var i = -1 + k % 3
|
||||
# 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))
|
||||
|
||||
if (sprite.get_width() % 2 != 0):
|
||||
ox += 1
|
||||
oy += 1
|
||||
|
||||
if (ox >= 0 && ox < sprite.get_width()*3
|
||||
&& oy >= 0 && oy < sprite.get_height()*3):
|
||||
found_pixel = true
|
||||
break
|
||||
|
||||
if !found_pixel:
|
||||
sprite.set_pixel(x, y, Color(0,0,0,0))
|
||||
continue
|
||||
|
||||
var fil : int = oy % 3
|
||||
var col : int = ox % 3
|
||||
var index : int = col + 3*fil
|
||||
|
||||
ox = round((ox - 1)/3.0);
|
||||
oy = round((oy - 1)/3.0);
|
||||
var a : Color
|
||||
var b : Color
|
||||
var c : Color
|
||||
var d : Color
|
||||
var e : Color
|
||||
var f : Color
|
||||
var g : Color
|
||||
var h : Color
|
||||
var i : Color
|
||||
if (ox == 0 || ox == sprite.get_width() - 1 ||
|
||||
oy == 0 || oy == sprite.get_height() - 1):
|
||||
p = aux.get_pixel(ox, oy)
|
||||
else:
|
||||
a = aux.get_pixel(ox-1,oy-1);
|
||||
b = aux.get_pixel(ox,oy-1);
|
||||
c = aux.get_pixel(ox+1,oy-1);
|
||||
d = aux.get_pixel(ox-1,oy);
|
||||
e = aux.get_pixel(ox,oy);
|
||||
f = aux.get_pixel(ox+1,oy);
|
||||
g = aux.get_pixel(ox-1,oy+1);
|
||||
h = aux.get_pixel(ox,oy+1);
|
||||
i = aux.get_pixel(ox+1,oy+1);
|
||||
|
||||
match(index):
|
||||
0:
|
||||
p = d if (similarColors(d,b) && !similarColors(d,h)
|
||||
&& !similarColors(b,f)) else e;
|
||||
1:
|
||||
p = b if ((similarColors(d,b) && !similarColors(d,h) &&
|
||||
!similarColors(b,f) && !similarColors(e,c)) ||
|
||||
(similarColors(b,f) && !similarColors(d,b) &&
|
||||
!similarColors(f,h) && !similarColors(e,a))) else e;
|
||||
2:
|
||||
p = f if (similarColors(b,f) && !similarColors(d,b) &&
|
||||
!similarColors(f,h)) else e;
|
||||
3:
|
||||
p = d if ((similarColors(d,h) && !similarColors(f,h) &&
|
||||
!similarColors(d,b) && !similarColors(e,a)) ||
|
||||
(similarColors(d,b) && !similarColors(d,h) &&
|
||||
!similarColors(b,f) && !similarColors(e,g))) else e;
|
||||
4:
|
||||
p = e
|
||||
5:
|
||||
p = f if((similarColors(b,f) && !similarColors(d,b) &&
|
||||
!similarColors(f,h) && !similarColors(e,i))
|
||||
|| (similarColors(f,h) && !similarColors(b,f) &&
|
||||
!similarColors(d,h) && !similarColors(e,c))) else e;
|
||||
6:
|
||||
p = d if (similarColors(d,h) && !similarColors(f,h) &&
|
||||
!similarColors(d,b)) else e;
|
||||
7:
|
||||
p = h if ((similarColors(f,h) && !similarColors(f,b) &&
|
||||
!similarColors(d,h) && !similarColors(e,g))
|
||||
|| (similarColors(d,h) && !similarColors(f,h) &&
|
||||
!similarColors(d,b) && !similarColors(e,i))) else e;
|
||||
8:
|
||||
p = f if (similarColors(f,h) && !similarColors(f,b) &&
|
||||
!similarColors(d,h)) else e;
|
||||
sprite.set_pixel(x, y, p)
|
||||
sprite.unlock()
|
||||
aux.unlock()
|
||||
|
||||
|
||||
func fake_rotsprite(sprite : Image, angle : float) -> void:
|
||||
sprite.copy_from(scale3X(sprite))
|
||||
nn_rotate(sprite,angle)
|
||||
# warning-ignore:integer_division
|
||||
# warning-ignore:integer_division
|
||||
sprite.resize(sprite.get_width() / 3, sprite.get_height() / 3, 0)
|
||||
|
||||
|
||||
func nn_rotate(sprite : Image, angle : float) -> void:
|
||||
var aux : Image = Image.new()
|
||||
aux.copy_from(sprite)
|
||||
sprite.lock()
|
||||
aux.lock()
|
||||
var ox: int
|
||||
var oy: int
|
||||
# warning-ignore:integer_division
|
||||
# warning-ignore:integer_division
|
||||
var center : Vector2 = Vector2(sprite.get_width() / 2, sprite.get_height() / 2)
|
||||
for x in range(sprite.get_width()):
|
||||
for y in range(sprite.get_height()):
|
||||
ox = (x - center.x)*cos(angle) + (y - center.y)*sin(angle) + center.x
|
||||
oy = -(x - center.x)*sin(angle) + (y - center.y)*cos(angle) + center.y
|
||||
if ox >= 0 && ox < sprite.get_width() && oy >= 0 && oy < sprite.get_height():
|
||||
sprite.set_pixel(x, y, aux.get_pixel(ox, oy))
|
||||
else:
|
||||
sprite.set_pixel(x, y, Color(0,0,0,0))
|
||||
sprite.unlock()
|
||||
aux.unlock()
|
||||
|
||||
|
||||
func similarColors(c1 : Color, c2 : Color, tol : float = 100) -> bool:
|
||||
var dist = colorDistance(c1, c2)
|
||||
return dist <= tol
|
||||
|
||||
|
||||
func colorDistance(c1 : Color, c2 : Color) -> float:
|
||||
return sqrt(pow((c1.r - c2.r)*255, 2) + pow((c1.g - c2.g)*255, 2)
|
||||
+ pow((c1.b - c2.b)*255, 2) + pow((c1.a - c2.a)*255, 2))
|
||||
|
||||
|
||||
func adjust_hsv(img: Image, id : int, delta : float) -> void:
|
||||
var west_limit = Global.canvas.west_limit
|
||||
var east_limit = Global.canvas.east_limit
|
||||
var north_limit = Global.canvas.north_limit
|
||||
var south_limit = Global.canvas.south_limit
|
||||
img.lock()
|
||||
|
||||
match id:
|
||||
0: # Hue
|
||||
for i in range(west_limit, east_limit):
|
||||
for j in range(north_limit, south_limit):
|
||||
var c : Color = img.get_pixel(i,j)
|
||||
var hue = range_lerp(c.h,0,1,-180,180)
|
||||
hue = hue + delta
|
||||
|
||||
while(hue >= 180):
|
||||
hue -= 360
|
||||
while(hue < -180):
|
||||
hue += 360
|
||||
c.h = range_lerp(hue,-180,180,0,1)
|
||||
img.set_pixel(i,j,c)
|
||||
|
||||
1: # Saturation
|
||||
for i in range(west_limit, east_limit):
|
||||
for j in range(north_limit, south_limit):
|
||||
var c : Color = img.get_pixel(i,j)
|
||||
var sat = c.s
|
||||
if delta > 0:
|
||||
sat = range_lerp(delta,0,100,c.s,1)
|
||||
elif delta < 0:
|
||||
sat = range_lerp(delta,-100,0,0,c.s)
|
||||
c.s = sat
|
||||
img.set_pixel(i,j,c)
|
||||
|
||||
2: # Value
|
||||
for i in range(west_limit, east_limit):
|
||||
for j in range(north_limit, south_limit):
|
||||
var c : Color = img.get_pixel(i,j)
|
||||
var val = c.v
|
||||
if delta > 0:
|
||||
val = range_lerp(delta,0,100,c.v,1)
|
||||
elif delta < 0:
|
||||
val = range_lerp(delta,-100,0,0,c.v)
|
||||
|
||||
c.v = val
|
||||
img.set_pixel(i,j,c)
|
||||
|
||||
img.unlock()
|
||||
|
||||
|
||||
# Checks if a point is inside a rectangle
|
||||
func point_in_rectangle(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool:
|
||||
return p.x > coord1.x && p.y > coord1.y && p.x < coord2.x && p.y < coord2.y
|
||||
|
||||
|
||||
# Returns the position in the middle of a rectangle
|
||||
func rectangle_center(rect_position : Vector2, rect_size : Vector2) -> Vector2:
|
||||
return (rect_position - rect_size / 2).floor()
|
|
@ -1,26 +1,28 @@
|
|||
extends Node
|
||||
|
||||
|
||||
enum Grid_Types {CARTESIAN, ISOMETRIC, ALL}
|
||||
enum Pressure_Sensitivity {NONE, ALPHA, SIZE, ALPHA_AND_SIZE}
|
||||
enum Brush_Types {PIXEL, CIRCLE, FILLED_CIRCLE, FILE, RANDOM_FILE, CUSTOM}
|
||||
|
||||
var root_directory := "."
|
||||
var window_title := "" setget title_changed # Why doesn't Godot have get_window_title()?
|
||||
var config_cache := ConfigFile.new()
|
||||
var XDGDataPaths = preload("res://src/XDGDataPaths.gd")
|
||||
var directory_module : Reference
|
||||
enum Direction {UP, DOWN, LEFT, RIGHT}
|
||||
enum Mouse_Button {LEFT, RIGHT}
|
||||
enum Tools {PENCIL, ERASER, BUCKET, LIGHTENDARKEN, RECTSELECT, COLORPICKER, ZOOM}
|
||||
enum Theme_Types {DARK, BLUE, CARAMEL, LIGHT}
|
||||
enum Fill_Area {SAME_COLOR_AREA, SAME_COLOR_PIXELS}
|
||||
enum Fill_With {COLOR, PATTERN}
|
||||
enum Lighten_Darken_Mode {LIGHTEN, DARKEN}
|
||||
enum Zoom_Mode {ZOOM_IN, ZOOM_OUT}
|
||||
|
||||
# Stuff for arrowkey-based canvas movements nyaa ^.^
|
||||
const low_speed_move_rate := 150.0
|
||||
const medium_speed_move_rate := 750.0
|
||||
const high_speed_move_rate := 3750.0
|
||||
|
||||
enum Direction {
|
||||
UP = 0,
|
||||
DOWN = 1,
|
||||
LEFT = 2,
|
||||
RIGHT = 3
|
||||
}
|
||||
var root_directory := "."
|
||||
var window_title := "" setget title_changed # Why doesn't Godot have get_window_title()?
|
||||
var config_cache := ConfigFile.new()
|
||||
var XDGDataPaths = preload("res://src/XDGDataPaths.gd")
|
||||
var directory_module : Reference
|
||||
|
||||
# Indices are as in the Direction enum
|
||||
# This is the total time the key for
|
||||
|
@ -51,10 +53,10 @@ var right_cursor_tool_texture : ImageTexture
|
|||
|
||||
var selected_pixels := []
|
||||
var image_clipboard : Image
|
||||
var animation_tags := [] setget animation_tags_changed # [Name, Color, From, To]
|
||||
var animation_tags := [] setget animation_tags_changed
|
||||
var play_only_tags := true
|
||||
|
||||
var theme_type := "Dark"
|
||||
var theme_type : int = Theme_Types.DARK
|
||||
var is_default_image := true
|
||||
var default_image_width := 64
|
||||
var default_image_height := 64
|
||||
|
@ -68,45 +70,30 @@ var checker_size := 10
|
|||
var checker_color_1 := Color(0.47, 0.47, 0.47, 1)
|
||||
var checker_color_2 := Color(0.34, 0.35, 0.34, 1)
|
||||
|
||||
var autosave_interval := 5.0
|
||||
var enable_autosave := true
|
||||
|
||||
# Tools & options
|
||||
var current_left_tool := "Pencil"
|
||||
var current_right_tool := "Eraser"
|
||||
var current_tools := [Tools.PENCIL, Tools.ERASER]
|
||||
var show_left_tool_icon := true
|
||||
var show_right_tool_icon := true
|
||||
var left_square_indicator_visible := true
|
||||
var right_square_indicator_visible := false
|
||||
|
||||
# 0 for area of same color, 1 for all pixels of the same color
|
||||
var left_fill_area := 0
|
||||
var right_fill_area := 0
|
||||
var fill_areas := [Fill_Area.SAME_COLOR_AREA, Fill_Area.SAME_COLOR_AREA]
|
||||
var fill_with := [Fill_With.COLOR, Fill_With.COLOR]
|
||||
var fill_pattern_offsets := [Vector2.ZERO, Vector2.ZERO]
|
||||
|
||||
var left_fill_with := 0
|
||||
var right_fill_with := 0
|
||||
var ld_modes := [Lighten_Darken_Mode.LIGHTEN, Lighten_Darken_Mode.LIGHTEN]
|
||||
var ld_amounts := [0.1, 0.1]
|
||||
|
||||
var left_fill_pattern_offset := Vector2.ZERO
|
||||
var right_fill_pattern_offset := Vector2.ZERO
|
||||
var color_picker_for := [Mouse_Button.LEFT, Mouse_Button.RIGHT]
|
||||
|
||||
# 0 for lighten, 1 for darken
|
||||
var left_ld := 0
|
||||
var right_ld := 0
|
||||
var left_ld_amount := 0.1
|
||||
var right_ld_amount := 0.1
|
||||
var zoom_modes := [Zoom_Mode.ZOOM_IN, Zoom_Mode.ZOOM_OUT]
|
||||
|
||||
# 0 for the left, 1 for the right
|
||||
var left_color_picker_for := 0
|
||||
var right_color_picker_for := 1
|
||||
|
||||
# 0 for zoom in, 1 for zoom out
|
||||
var left_zoom_mode := 0
|
||||
var right_zoom_mode := 1
|
||||
|
||||
var left_horizontal_mirror := false
|
||||
var left_vertical_mirror := false
|
||||
var right_horizontal_mirror := false
|
||||
var right_vertical_mirror := false
|
||||
|
||||
var left_pixel_perfect := false
|
||||
var right_pixel_perfect := false
|
||||
var horizontal_mirror := [false, false]
|
||||
var vertical_mirror := [false, false]
|
||||
var pixel_perfect := [false, false]
|
||||
|
||||
# View menu options
|
||||
var tile_mode := false
|
||||
|
@ -122,29 +109,23 @@ var onion_skinning_future_rate := 1.0
|
|||
var onion_skinning_blue_red := false
|
||||
|
||||
# Brushes
|
||||
var left_brush_size := 1
|
||||
var right_brush_size := 1
|
||||
var current_left_brush_type = Brush_Types.PIXEL
|
||||
var current_right_brush_type = Brush_Types.PIXEL
|
||||
var brush_sizes := [1, 1]
|
||||
var current_brush_types := [Brush_Types.PIXEL, Brush_Types.PIXEL]
|
||||
|
||||
var brush_type_window_position := "left"
|
||||
var brush_type_window_position : int = Mouse_Button.LEFT
|
||||
var left_circle_points := []
|
||||
var right_circle_points := []
|
||||
|
||||
var brushes_from_files := 0
|
||||
var custom_brushes := []
|
||||
var custom_left_brush_index := -1
|
||||
var custom_right_brush_index := -1
|
||||
var custom_left_brush_image : Image
|
||||
var custom_right_brush_image : Image
|
||||
var custom_left_brush_texture := ImageTexture.new()
|
||||
var custom_right_brush_texture := ImageTexture.new()
|
||||
var custom_brush_indexes := [-1, -1]
|
||||
var custom_brush_images := [Image.new(), Image.new()]
|
||||
var custom_brush_textures := [ImageTexture.new(), ImageTexture.new()]
|
||||
|
||||
# Patterns
|
||||
var patterns := []
|
||||
var pattern_window_position := "left"
|
||||
var pattern_left_image : Image
|
||||
var pattern_right_image : Image
|
||||
var pattern_window_position : int = Mouse_Button.LEFT
|
||||
var pattern_images := [Image.new(), Image.new()]
|
||||
|
||||
# Palettes
|
||||
var palettes := {}
|
||||
|
@ -176,65 +157,48 @@ var zoom_level_label : Label
|
|||
|
||||
var import_sprites_dialog : FileDialog
|
||||
var export_dialog : AcceptDialog
|
||||
var preferences_dialog : AcceptDialog
|
||||
|
||||
var left_color_picker : ColorPickerButton
|
||||
var right_color_picker : ColorPickerButton
|
||||
var color_pickers := []
|
||||
|
||||
var color_switch_button : BaseButton
|
||||
|
||||
var left_tool_options_container : Container
|
||||
var right_tool_options_container : Container
|
||||
var tool_options_containers := []
|
||||
|
||||
var left_brush_type_container : Container
|
||||
var right_brush_type_container : Container
|
||||
var left_brush_type_button : BaseButton
|
||||
var right_brush_type_button : BaseButton
|
||||
var brush_type_containers := []
|
||||
var brush_type_buttons := []
|
||||
var brushes_popup : Popup
|
||||
var file_brush_container : GridContainer
|
||||
var project_brush_container : GridContainer
|
||||
var patterns_popup : Popup
|
||||
|
||||
var left_brush_size_edit : SpinBox
|
||||
var left_brush_size_slider : HSlider
|
||||
var right_brush_size_edit : SpinBox
|
||||
var right_brush_size_slider : HSlider
|
||||
var brush_size_edits := []
|
||||
var brush_size_sliders := []
|
||||
|
||||
var left_pixel_perfect_container : VBoxContainer
|
||||
var right_pixel_perfect_container : VBoxContainer
|
||||
var pixel_perfect_containers := []
|
||||
|
||||
var left_color_interpolation_container : Container
|
||||
var right_color_interpolation_container : Container
|
||||
var left_interpolate_spinbox : SpinBox
|
||||
var left_interpolate_slider : HSlider
|
||||
var right_interpolate_spinbox : SpinBox
|
||||
var right_interpolate_slider : HSlider
|
||||
var color_interpolation_containers := []
|
||||
var interpolate_spinboxes := []
|
||||
var interpolate_sliders := []
|
||||
|
||||
var left_fill_area_container : Container
|
||||
var left_fill_pattern_container : Container
|
||||
var right_fill_area_container : Container
|
||||
var right_fill_pattern_container : Container
|
||||
var fill_area_containers := []
|
||||
var fill_pattern_containers := []
|
||||
|
||||
var left_ld_container : Container
|
||||
var left_ld_amount_slider : HSlider
|
||||
var left_ld_amount_spinbox : SpinBox
|
||||
var right_ld_container : Container
|
||||
var right_ld_amount_slider : HSlider
|
||||
var right_ld_amount_spinbox : SpinBox
|
||||
var ld_containers := []
|
||||
var ld_amount_sliders := []
|
||||
var ld_amount_spinboxes := []
|
||||
|
||||
var left_colorpicker_container : Container
|
||||
var right_colorpicker_container : Container
|
||||
var colorpicker_containers := []
|
||||
|
||||
var left_zoom_container : Container
|
||||
var right_zoom_container : Container
|
||||
var zoom_containers := []
|
||||
|
||||
var left_mirror_container : Container
|
||||
var right_mirror_container : Container
|
||||
var mirror_containers := []
|
||||
|
||||
var animation_timeline : Panel
|
||||
|
||||
var animation_timer : Timer
|
||||
var frame_ids : HBoxContainer
|
||||
var current_frame_label : Label
|
||||
var current_frame_mark_label : Label
|
||||
var onion_skinning_button : BaseButton
|
||||
var loop_animation_button : BaseButton
|
||||
var play_forward : BaseButton
|
||||
|
@ -313,58 +277,59 @@ func _ready() -> void:
|
|||
|
||||
import_sprites_dialog = find_node_by_name(root, "ImportSprites")
|
||||
export_dialog = find_node_by_name(root, "ExportDialog")
|
||||
preferences_dialog = find_node_by_name(root, "PreferencesDialog")
|
||||
|
||||
left_tool_options_container = find_node_by_name(root, "LeftToolOptions")
|
||||
right_tool_options_container = find_node_by_name(root, "RightToolOptions")
|
||||
tool_options_containers.append(find_node_by_name(root, "LeftToolOptions"))
|
||||
tool_options_containers.append(find_node_by_name(root, "RightToolOptions"))
|
||||
|
||||
left_color_picker = find_node_by_name(root, "LeftColorPickerButton")
|
||||
right_color_picker = find_node_by_name(root, "RightColorPickerButton")
|
||||
color_pickers.append(find_node_by_name(root, "LeftColorPickerButton"))
|
||||
color_pickers.append(find_node_by_name(root, "RightColorPickerButton"))
|
||||
color_switch_button = find_node_by_name(root, "ColorSwitch")
|
||||
|
||||
left_brush_type_container = find_node_by_name(left_tool_options_container, "LeftBrushType")
|
||||
right_brush_type_container = find_node_by_name(right_tool_options_container, "RightBrushType")
|
||||
left_brush_type_button = find_node_by_name(left_brush_type_container, "LeftBrushTypeButton")
|
||||
right_brush_type_button = find_node_by_name(right_brush_type_container, "RightBrushTypeButton")
|
||||
brush_type_containers.append(find_node_by_name(tool_options_containers[0], "LeftBrushType"))
|
||||
brush_type_containers.append(find_node_by_name(tool_options_containers[1], "RightBrushType"))
|
||||
brush_type_buttons.append(find_node_by_name(brush_type_containers[0], "LeftBrushTypeButton"))
|
||||
brush_type_buttons.append(find_node_by_name(brush_type_containers[1], "RightBrushTypeButton"))
|
||||
brushes_popup = find_node_by_name(root, "BrushesPopup")
|
||||
file_brush_container = find_node_by_name(brushes_popup, "FileBrushContainer")
|
||||
project_brush_container = find_node_by_name(brushes_popup, "ProjectBrushContainer")
|
||||
patterns_popup = find_node_by_name(root, "PatternsPopup")
|
||||
|
||||
left_brush_size_edit = find_node_by_name(root, "LeftBrushSizeEdit")
|
||||
left_brush_size_slider = find_node_by_name(root, "LeftBrushSizeSlider")
|
||||
right_brush_size_edit = find_node_by_name(root, "RightBrushSizeEdit")
|
||||
right_brush_size_slider = find_node_by_name(root, "RightBrushSizeSlider")
|
||||
brush_size_edits.append(find_node_by_name(root, "LeftBrushSizeEdit"))
|
||||
brush_size_sliders.append(find_node_by_name(root, "LeftBrushSizeSlider"))
|
||||
brush_size_edits.append(find_node_by_name(root, "RightBrushSizeEdit"))
|
||||
brush_size_sliders.append(find_node_by_name(root, "RightBrushSizeSlider"))
|
||||
|
||||
left_pixel_perfect_container = find_node_by_name(root, "LeftBrushPixelPerfectMode")
|
||||
right_pixel_perfect_container = find_node_by_name(root, "RightBrushPixelPerfectMode")
|
||||
pixel_perfect_containers.append(find_node_by_name(root, "LeftBrushPixelPerfectMode"))
|
||||
pixel_perfect_containers.append(find_node_by_name(root, "RightBrushPixelPerfectMode"))
|
||||
|
||||
left_color_interpolation_container = find_node_by_name(root, "LeftColorInterpolation")
|
||||
right_color_interpolation_container = find_node_by_name(root, "RightColorInterpolation")
|
||||
left_interpolate_spinbox = find_node_by_name(root, "LeftInterpolateFactor")
|
||||
left_interpolate_slider = find_node_by_name(root, "LeftInterpolateSlider")
|
||||
right_interpolate_spinbox = find_node_by_name(root, "RightInterpolateFactor")
|
||||
right_interpolate_slider = find_node_by_name(root, "RightInterpolateSlider")
|
||||
color_interpolation_containers.append(find_node_by_name(root, "LeftColorInterpolation"))
|
||||
color_interpolation_containers.append(find_node_by_name(root, "RightColorInterpolation"))
|
||||
interpolate_spinboxes.append(find_node_by_name(root, "LeftInterpolateFactor"))
|
||||
interpolate_sliders.append(find_node_by_name(root, "LeftInterpolateSlider"))
|
||||
interpolate_spinboxes.append(find_node_by_name(root, "RightInterpolateFactor"))
|
||||
interpolate_sliders.append(find_node_by_name(root, "RightInterpolateSlider"))
|
||||
|
||||
left_fill_area_container = find_node_by_name(root, "LeftFillArea")
|
||||
left_fill_pattern_container = find_node_by_name(root, "LeftFillPattern")
|
||||
right_fill_area_container = find_node_by_name(root, "RightFillArea")
|
||||
right_fill_pattern_container = find_node_by_name(root, "RightFillPattern")
|
||||
fill_area_containers.append(find_node_by_name(root, "LeftFillArea"))
|
||||
fill_pattern_containers.append(find_node_by_name(root, "LeftFillPattern"))
|
||||
fill_area_containers.append(find_node_by_name(root, "RightFillArea"))
|
||||
fill_pattern_containers.append(find_node_by_name(root, "RightFillPattern"))
|
||||
|
||||
left_ld_container = find_node_by_name(root, "LeftLDOptions")
|
||||
left_ld_amount_slider = find_node_by_name(root, "LeftLDAmountSlider")
|
||||
left_ld_amount_spinbox = find_node_by_name(root, "LeftLDAmountSpinbox")
|
||||
right_ld_container = find_node_by_name(root, "RightLDOptions")
|
||||
right_ld_amount_slider = find_node_by_name(root, "RightLDAmountSlider")
|
||||
right_ld_amount_spinbox = find_node_by_name(root, "RightLDAmountSpinbox")
|
||||
ld_containers.append(find_node_by_name(root, "LeftLDOptions"))
|
||||
ld_amount_sliders.append(find_node_by_name(root, "LeftLDAmountSlider"))
|
||||
ld_amount_spinboxes.append(find_node_by_name(root, "LeftLDAmountSpinbox"))
|
||||
ld_containers.append(find_node_by_name(root, "RightLDOptions"))
|
||||
ld_amount_sliders.append(find_node_by_name(root, "RightLDAmountSlider"))
|
||||
ld_amount_spinboxes.append(find_node_by_name(root, "RightLDAmountSpinbox"))
|
||||
|
||||
left_colorpicker_container = find_node_by_name(root, "LeftColorPickerOptions")
|
||||
right_colorpicker_container = find_node_by_name(root, "RightColorPickerOptions")
|
||||
colorpicker_containers.append(find_node_by_name(root, "LeftColorPickerOptions"))
|
||||
colorpicker_containers.append(find_node_by_name(root, "RightColorPickerOptions"))
|
||||
|
||||
left_zoom_container = find_node_by_name(root, "LeftZoomOptions")
|
||||
right_zoom_container = find_node_by_name(root, "RightZoomOptions")
|
||||
zoom_containers.append(find_node_by_name(root, "LeftZoomOptions"))
|
||||
zoom_containers.append(find_node_by_name(root, "RightZoomOptions"))
|
||||
|
||||
left_mirror_container = find_node_by_name(root, "LeftMirrorButtons")
|
||||
right_mirror_container = find_node_by_name(root, "RightMirrorButtons")
|
||||
mirror_containers.append(find_node_by_name(root, "LeftMirrorButtons"))
|
||||
mirror_containers.append(find_node_by_name(root, "RightMirrorButtons"))
|
||||
|
||||
animation_timeline = find_node_by_name(root, "AnimationTimeline")
|
||||
|
||||
|
@ -372,7 +337,7 @@ func _ready() -> void:
|
|||
frames_container = find_node_by_name(animation_timeline, "FramesContainer")
|
||||
animation_timer = find_node_by_name(animation_timeline, "AnimationTimer")
|
||||
frame_ids = find_node_by_name(animation_timeline, "FrameIDs")
|
||||
current_frame_label = find_node_by_name(control, "CurrentFrame")
|
||||
current_frame_mark_label = find_node_by_name(control, "CurrentFrameMark")
|
||||
onion_skinning_button = find_node_by_name(animation_timeline, "OnionSkinning")
|
||||
loop_animation_button = find_node_by_name(animation_timeline, "LoopAnim")
|
||||
play_forward = find_node_by_name(animation_timeline, "PlayForward")
|
||||
|
@ -401,13 +366,11 @@ func _ready() -> void:
|
|||
|
||||
error_dialog = find_node_by_name(root, "ErrorDialog")
|
||||
|
||||
# Store [Layer name (0), Layer visibility boolean (1), Layer lock boolean (2), Frame container (3),
|
||||
# will new cels be linked boolean (4), Array of linked cels (5)]
|
||||
layers.append([tr("Layer") + " 0", true, false, HBoxContainer.new(), false, []])
|
||||
layers.append(Layer.new())
|
||||
|
||||
|
||||
# Thanks to https://godotengine.org/qa/17524/how-to-find-an-instanced-scene-by-its-name
|
||||
func find_node_by_name(root, node_name) -> Node:
|
||||
func find_node_by_name(root : Node, node_name : String) -> Node:
|
||||
if root.get_name() == node_name:
|
||||
return root
|
||||
for child in root.get_children():
|
||||
|
@ -467,6 +430,7 @@ func undo(_canvases : Array, layer_index : int = -1) -> void:
|
|||
canvas_parent.move_child(_canvases[0], _canvases[0].frame)
|
||||
elif action_name == "Change Frame Order":
|
||||
canvas_parent.move_child(_canvases[0], _canvases[0].frame)
|
||||
canvas_parent.move_child(canvas_parent.get_node("TransparentChecker"), 0)
|
||||
|
||||
canvas.update()
|
||||
if !project_has_changed:
|
||||
|
@ -498,6 +462,7 @@ func redo(_canvases : Array, layer_index : int = -1) -> void:
|
|||
animation_timer.stop()
|
||||
elif action_name == "Change Frame Order":
|
||||
canvas_parent.move_child(_canvases[0], _canvases[0].frame)
|
||||
canvas_parent.move_child(canvas_parent.get_node("TransparentChecker"), 0)
|
||||
|
||||
canvas.update()
|
||||
if !project_has_changed:
|
||||
|
@ -523,7 +488,7 @@ func canvases_changed(value : Array) -> void:
|
|||
frame_id.queue_free()
|
||||
|
||||
for i in range(layers.size() - 1, -1, -1):
|
||||
frames_container.add_child(layers[i][3])
|
||||
frames_container.add_child(layers[i].frame_container)
|
||||
|
||||
for j in range(canvases.size()):
|
||||
var label := Label.new()
|
||||
|
@ -536,9 +501,9 @@ func canvases_changed(value : Array) -> void:
|
|||
var cel_button = load("res://src/UI/Timeline/CelButton.tscn").instance()
|
||||
cel_button.frame = j
|
||||
cel_button.layer = i
|
||||
cel_button.get_child(0).texture = Global.canvases[j].layers[i][1]
|
||||
cel_button.get_child(0).texture = Global.canvases[j].layers[i].image_texture
|
||||
|
||||
layers[i][3].add_child(cel_button)
|
||||
layers[i].frame_container.add_child(cel_button)
|
||||
|
||||
# This is useful in case tagged frames get deleted DURING the animation is playing
|
||||
# otherwise, this code is useless in this context, since these values are being set
|
||||
|
@ -547,9 +512,9 @@ func canvases_changed(value : Array) -> void:
|
|||
animation_timeline.last_frame = canvases.size() - 1
|
||||
if play_only_tags:
|
||||
for tag in animation_tags:
|
||||
if current_frame + 1 >= tag[2] && current_frame + 1 <= tag[3]:
|
||||
animation_timeline.first_frame = tag[2] - 1
|
||||
animation_timeline.last_frame = min(canvases.size() - 1, tag[3] - 1)
|
||||
if current_frame + 1 >= tag.from && current_frame + 1 <= tag.to:
|
||||
animation_timeline.first_frame = tag.from - 1
|
||||
animation_timeline.last_frame = min(canvases.size() - 1, tag.to - 1)
|
||||
|
||||
|
||||
func clear_canvases() -> void:
|
||||
|
@ -591,27 +556,27 @@ func layers_changed(value : Array) -> void:
|
|||
for i in range(layers.size() - 1, -1, -1):
|
||||
var layer_container = load("res://src/UI/Timeline/LayerButton.tscn").instance()
|
||||
layer_container.i = i
|
||||
if !layers[i][0]:
|
||||
layers[i][0] = tr("Layer") + " %s" % i
|
||||
if layers[i].name == tr("Layer") + " 0":
|
||||
layers[i].name = tr("Layer") + " %s" % i
|
||||
|
||||
layers_container.add_child(layer_container)
|
||||
layer_container.label.text = layers[i][0]
|
||||
layer_container.line_edit.text = layers[i][0]
|
||||
layer_container.label.text = layers[i].name
|
||||
layer_container.line_edit.text = layers[i].name
|
||||
|
||||
frames_container.add_child(layers[i][3])
|
||||
frames_container.add_child(layers[i].frame_container)
|
||||
for j in range(canvases.size()):
|
||||
var cel_button = load("res://src/UI/Timeline/CelButton.tscn").instance()
|
||||
cel_button.frame = j
|
||||
cel_button.layer = i
|
||||
cel_button.get_child(0).texture = Global.canvases[j].layers[i][1]
|
||||
cel_button.get_child(0).texture = Global.canvases[j].layers[i].image_texture
|
||||
|
||||
layers[i][3].add_child(cel_button)
|
||||
layers[i].frame_container.add_child(cel_button)
|
||||
|
||||
var layer_button = layers_container.get_child(layers_container.get_child_count() - 1 - current_layer)
|
||||
layer_button.pressed = true
|
||||
self.current_frame = current_frame # Call frame_changed to update UI
|
||||
|
||||
if layers[current_layer][2]:
|
||||
if layers[current_layer].locked:
|
||||
disable_button(remove_layer_button, true)
|
||||
|
||||
if layers.size() == 1:
|
||||
|
@ -619,13 +584,13 @@ func layers_changed(value : Array) -> void:
|
|||
disable_button(move_up_layer_button, true)
|
||||
disable_button(move_down_layer_button, true)
|
||||
disable_button(merge_down_layer_button, true)
|
||||
elif !layers[current_layer][2]:
|
||||
elif !layers[current_layer].locked:
|
||||
disable_button(remove_layer_button, false)
|
||||
|
||||
|
||||
func frame_changed(value : int) -> void:
|
||||
current_frame = value
|
||||
current_frame_label.text = tr("Current frame:") + " %s/%s" % [str(current_frame + 1), canvases.size()]
|
||||
current_frame_mark_label.text = "%s/%s" % [str(current_frame + 1), canvases.size()]
|
||||
|
||||
var i := 0
|
||||
for c in canvases: # De-select all the other canvases/frames
|
||||
|
@ -633,24 +598,24 @@ func frame_changed(value : int) -> void:
|
|||
c.is_making_line = false
|
||||
c.line_2d.set_point_position(1, c.line_2d.points[0])
|
||||
var text_color := Color.white
|
||||
if theme_type == "Caramel" || theme_type == "Light":
|
||||
if theme_type == Theme_Types.CARAMEL || theme_type == Theme_Types.LIGHT:
|
||||
text_color = Color.black
|
||||
frame_ids.get_child(i).add_color_override("font_color", text_color)
|
||||
for layer in layers:
|
||||
if i < layer[3].get_child_count():
|
||||
layer[3].get_child(i).pressed = false
|
||||
if i < layer.frame_container.get_child_count():
|
||||
layer.frame_container.get_child(i).pressed = false
|
||||
i += 1
|
||||
|
||||
# Select the new canvas/frame
|
||||
canvas = canvases[current_frame]
|
||||
canvas.visible = true
|
||||
frame_ids.get_child(current_frame).add_color_override("font_color", control.theme.get_color("Selected Color", "Label"))
|
||||
if current_frame < layers[current_layer][3].get_child_count():
|
||||
layers[current_layer][3].get_child(current_frame).pressed = true
|
||||
if current_frame < layers[current_layer].frame_container.get_child_count():
|
||||
layers[current_layer].frame_container.get_child(current_frame).pressed = true
|
||||
|
||||
if canvases.size() == 1:
|
||||
disable_button(remove_frame_button, true)
|
||||
elif !layers[current_layer][2]:
|
||||
elif !layers[current_layer].locked:
|
||||
disable_button(remove_frame_button, false)
|
||||
|
||||
Global.transparent_checker._ready() # To update the rect size
|
||||
|
@ -658,8 +623,8 @@ func frame_changed(value : int) -> void:
|
|||
|
||||
func layer_changed(value : int) -> void:
|
||||
current_layer = value
|
||||
layer_opacity_slider.value = canvas.layers[current_layer][2] * 100
|
||||
layer_opacity_spinbox.value = canvas.layers[current_layer][2] * 100
|
||||
layer_opacity_slider.value = canvas.layers[current_layer].opacity * 100
|
||||
layer_opacity_spinbox.value = canvas.layers[current_layer].opacity * 100
|
||||
|
||||
for container in layers_container.get_children():
|
||||
container.pressed = false
|
||||
|
@ -681,7 +646,7 @@ func layer_changed(value : int) -> void:
|
|||
disable_button(merge_down_layer_button, true)
|
||||
|
||||
if current_layer < layers.size():
|
||||
if layers[current_layer][2]:
|
||||
if layers[current_layer].locked:
|
||||
disable_button(remove_layer_button, true)
|
||||
else:
|
||||
if layers.size() > 1:
|
||||
|
@ -711,8 +676,8 @@ func disable_button(button : BaseButton, disable : bool) -> void:
|
|||
|
||||
if button is Button:
|
||||
var theme := theme_type
|
||||
if theme == "Caramel":
|
||||
theme = "Dark"
|
||||
if theme == Theme_Types.CARAMEL:
|
||||
theme = Theme_Types.DARK
|
||||
for c in button.get_children():
|
||||
if c is TextureRect:
|
||||
var normal_file_name = c.texture.resource_path.get_file().trim_suffix(".png").replace("_disabled", "")
|
||||
|
@ -739,13 +704,13 @@ func animation_tags_changed(value : Array) -> void:
|
|||
tag_container.add_child(tag_c)
|
||||
var tag_position := tag_container.get_child_count() - 1
|
||||
tag_container.move_child(tag_c, tag_position)
|
||||
tag_c.get_node("Label").text = tag[0]
|
||||
tag_c.get_node("Label").modulate = tag[1]
|
||||
tag_c.get_node("Line2D").default_color = tag[1]
|
||||
tag_c.get_node("Label").text = tag.name
|
||||
tag_c.get_node("Label").modulate = tag.color
|
||||
tag_c.get_node("Line2D").default_color = tag.color
|
||||
|
||||
tag_c.rect_position.x = (tag[2] - 1) * 39 + tag[2]
|
||||
tag_c.rect_position.x = (tag.from - 1) * 39 + tag.from
|
||||
|
||||
var size : int = tag[3] - tag[2]
|
||||
var size : int = tag.to - tag.from
|
||||
tag_c.rect_min_size.x = (size + 1) * 39
|
||||
tag_c.get_node("Line2D").points[2] = Vector2(tag_c.rect_min_size.x, 0)
|
||||
tag_c.get_node("Line2D").points[3] = Vector2(tag_c.rect_min_size.x, 32)
|
||||
|
@ -757,9 +722,9 @@ func animation_tags_changed(value : Array) -> void:
|
|||
animation_timeline.last_frame = canvases.size() - 1
|
||||
if play_only_tags:
|
||||
for tag in animation_tags:
|
||||
if current_frame + 1 >= tag[2] && current_frame + 1 <= tag[3]:
|
||||
animation_timeline.first_frame = tag[2] - 1
|
||||
animation_timeline.last_frame = min(canvases.size() - 1, tag[3] - 1)
|
||||
if current_frame + 1 >= tag.from && current_frame + 1 <= tag.to:
|
||||
animation_timeline.first_frame = tag.from - 1
|
||||
animation_timeline.last_frame = min(canvases.size() - 1, tag.to - 1)
|
||||
|
||||
|
||||
func update_hint_tooltips() -> void:
|
||||
|
@ -862,8 +827,8 @@ func create_brush_button(brush_img : Image, brush_type := Brush_Types.CUSTOM, hi
|
|||
|
||||
|
||||
func remove_brush_buttons() -> void:
|
||||
current_left_brush_type = Brush_Types.PIXEL
|
||||
current_right_brush_type = Brush_Types.PIXEL
|
||||
current_brush_types[0] = Brush_Types.PIXEL
|
||||
current_brush_types[1] = Brush_Types.PIXEL
|
||||
for child in project_brush_container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
|
@ -884,56 +849,32 @@ func redo_custom_brush(_brush_button : BaseButton = null) -> void:
|
|||
project_brush_container.remove_child(_brush_button)
|
||||
|
||||
|
||||
func update_left_custom_brush() -> void:
|
||||
if current_left_brush_type == Brush_Types.PIXEL:
|
||||
func update_custom_brush(mouse_button : int) -> void:
|
||||
if current_brush_types[mouse_button] == Brush_Types.PIXEL:
|
||||
var pixel := Image.new()
|
||||
pixel = preload("res://assets/graphics/pixel_image.png")
|
||||
left_brush_type_button.get_child(0).texture.create_from_image(pixel, 0)
|
||||
elif current_left_brush_type == Brush_Types.CIRCLE:
|
||||
brush_type_buttons[mouse_button].get_child(0).texture.create_from_image(pixel, 0)
|
||||
elif current_brush_types[mouse_button] == Brush_Types.CIRCLE:
|
||||
var pixel := Image.new()
|
||||
pixel = preload("res://assets/graphics/circle_9x9.png")
|
||||
left_brush_type_button.get_child(0).texture.create_from_image(pixel, 0)
|
||||
left_circle_points = plot_circle(left_brush_size)
|
||||
elif current_left_brush_type == Brush_Types.FILLED_CIRCLE:
|
||||
brush_type_buttons[mouse_button].get_child(0).texture.create_from_image(pixel, 0)
|
||||
left_circle_points = plot_circle(brush_sizes[0])
|
||||
right_circle_points = plot_circle(brush_sizes[1])
|
||||
elif current_brush_types[mouse_button] == Brush_Types.FILLED_CIRCLE:
|
||||
var pixel := Image.new()
|
||||
pixel = preload("res://assets/graphics/circle_filled_9x9.png")
|
||||
left_brush_type_button.get_child(0).texture.create_from_image(pixel, 0)
|
||||
left_circle_points = plot_circle(left_brush_size)
|
||||
brush_type_buttons[mouse_button].get_child(0).texture.create_from_image(pixel, 0)
|
||||
left_circle_points = plot_circle(brush_sizes[0])
|
||||
right_circle_points = plot_circle(brush_sizes[1])
|
||||
else:
|
||||
var custom_brush := Image.new()
|
||||
custom_brush.copy_from(custom_brushes[custom_left_brush_index])
|
||||
custom_brush.copy_from(custom_brushes[custom_brush_indexes[mouse_button]])
|
||||
var custom_brush_size = custom_brush.get_size()
|
||||
custom_brush.resize(custom_brush_size.x * left_brush_size, custom_brush_size.y * left_brush_size, Image.INTERPOLATE_NEAREST)
|
||||
custom_left_brush_image = blend_image_with_color(custom_brush, left_color_picker.color, left_interpolate_spinbox.value / 100)
|
||||
custom_left_brush_texture.create_from_image(custom_left_brush_image, 0)
|
||||
custom_brush.resize(custom_brush_size.x * brush_sizes[mouse_button], custom_brush_size.y * brush_sizes[mouse_button], Image.INTERPOLATE_NEAREST)
|
||||
custom_brush_images[mouse_button] = blend_image_with_color(custom_brush, color_pickers[mouse_button].color, interpolate_spinboxes[mouse_button].value / 100)
|
||||
custom_brush_textures[mouse_button].create_from_image(custom_brush_images[mouse_button], 0)
|
||||
|
||||
left_brush_type_button.get_child(0).texture = custom_left_brush_texture
|
||||
|
||||
|
||||
func update_right_custom_brush() -> void:
|
||||
if current_right_brush_type == Brush_Types.PIXEL:
|
||||
var pixel := Image.new()
|
||||
pixel = preload("res://assets/graphics/pixel_image.png")
|
||||
right_brush_type_button.get_child(0).texture.create_from_image(pixel, 0)
|
||||
elif current_right_brush_type == Brush_Types.CIRCLE:
|
||||
var pixel := Image.new()
|
||||
pixel = preload("res://assets/graphics/circle_9x9.png")
|
||||
right_brush_type_button.get_child(0).texture.create_from_image(pixel, 0)
|
||||
right_circle_points = plot_circle(right_brush_size)
|
||||
elif current_right_brush_type == Brush_Types.FILLED_CIRCLE:
|
||||
var pixel := Image.new()
|
||||
pixel = preload("res://assets/graphics/circle_filled_9x9.png")
|
||||
right_brush_type_button.get_child(0).texture.create_from_image(pixel, 0)
|
||||
right_circle_points = plot_circle(right_brush_size)
|
||||
else:
|
||||
var custom_brush := Image.new()
|
||||
custom_brush.copy_from(custom_brushes[custom_right_brush_index])
|
||||
var custom_brush_size = custom_brush.get_size()
|
||||
custom_brush.resize(custom_brush_size.x * right_brush_size, custom_brush_size.y * right_brush_size, Image.INTERPOLATE_NEAREST)
|
||||
custom_right_brush_image = blend_image_with_color(custom_brush, right_color_picker.color, right_interpolate_spinbox.value / 100)
|
||||
custom_right_brush_texture.create_from_image(custom_right_brush_image, 0)
|
||||
|
||||
right_brush_type_button.get_child(0).texture = custom_right_brush_texture
|
||||
brush_type_buttons[mouse_button].get_child(0).texture = custom_brush_textures[mouse_button]
|
||||
|
||||
|
||||
func blend_image_with_color(image : Image, color : Color, interpolate_factor : float) -> Image:
|
||||
|
@ -979,212 +920,6 @@ func plot_circle(r : int) -> Array:
|
|||
return circle_points
|
||||
|
||||
|
||||
func scale3X(sprite : Image, tol : float = 50) -> Image:
|
||||
var scaled = Image.new()
|
||||
scaled.create(sprite.get_width()*3, sprite.get_height()*3, false, Image.FORMAT_RGBA8)
|
||||
scaled.lock()
|
||||
sprite.lock()
|
||||
var a : Color
|
||||
var b : Color
|
||||
var c : Color
|
||||
var d : Color
|
||||
var e : Color
|
||||
var f : Color
|
||||
var g : Color
|
||||
var h : Color
|
||||
var i : Color
|
||||
|
||||
for x in range(1,sprite.get_width()-1):
|
||||
for y in range(1,sprite.get_height()-1):
|
||||
var xs : float = 3*x
|
||||
var ys : float = 3*y
|
||||
|
||||
a = sprite.get_pixel(x-1,y-1)
|
||||
b = sprite.get_pixel(x,y-1)
|
||||
c = sprite.get_pixel(x+1,y-1)
|
||||
d = sprite.get_pixel(x-1,y)
|
||||
e = sprite.get_pixel(x,y)
|
||||
f = sprite.get_pixel(x+1,y)
|
||||
g = sprite.get_pixel(x-1,y+1)
|
||||
h = sprite.get_pixel(x,y+1)
|
||||
i = sprite.get_pixel(x+1,y+1)
|
||||
|
||||
var db : bool = similarColors(d, b, tol)
|
||||
var dh : bool = similarColors(d, h, tol)
|
||||
var bf : bool = similarColors(f, b, tol)
|
||||
var ec : bool = similarColors(e, c, tol)
|
||||
var ea : bool = similarColors(e, a, tol)
|
||||
var fh : bool = similarColors(f, h, tol)
|
||||
var eg : bool = similarColors(e, g, tol)
|
||||
var ei : bool = similarColors(e, i, tol)
|
||||
|
||||
scaled.set_pixel(xs-1, ys-1, d if (db and !dh and !bf) else e )
|
||||
scaled.set_pixel(xs, ys-1, b if (db and !dh and !bf and !ec) or
|
||||
(bf and !db and !fh and !ea) else e)
|
||||
scaled.set_pixel(xs+1, ys-1, f if (bf and !db and !fh) else e)
|
||||
scaled.set_pixel(xs-1, ys, d if (dh and !fh and !db and !ea) or
|
||||
(db and !dh and !bf and !eg) else e)
|
||||
scaled.set_pixel(xs, ys, e);
|
||||
scaled.set_pixel(xs+1, ys, f if (bf and !db and !fh and !ei) or
|
||||
(fh and !bf and !dh and !ec) else e)
|
||||
scaled.set_pixel(xs-1, ys+1, d if (dh and !fh and !db) else e)
|
||||
scaled.set_pixel(xs, ys+1, h if (fh and !bf and !dh and !eg) or
|
||||
(dh and !fh and !db and !ei) else e)
|
||||
scaled.set_pixel(xs+1, ys+1, f if (fh and !bf and !dh) else e)
|
||||
|
||||
scaled.unlock()
|
||||
sprite.unlock()
|
||||
return scaled
|
||||
|
||||
|
||||
func rotxel(sprite : Image, angle : float) -> 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)
|
||||
return
|
||||
|
||||
var aux : Image = Image.new()
|
||||
aux.copy_from(sprite)
|
||||
var center : Vector2 = Vector2(sprite.get_width()/2, sprite.get_height()/2)
|
||||
var ox : int
|
||||
var oy : int
|
||||
var p : Color
|
||||
aux.lock()
|
||||
sprite.lock()
|
||||
for x in range(sprite.get_width()):
|
||||
for y in range(sprite.get_height()):
|
||||
var dx = 3*(x - center.x)
|
||||
var dy = 3*(y - center.y)
|
||||
var found_pixel : bool = false
|
||||
for k in range(9):
|
||||
var i = -1 + k % 3
|
||||
var j = -1 + int(k / 3)
|
||||
var dir = atan2(dy + j, dx + i)
|
||||
var mag = sqrt(pow(dx + i, 2) + pow(dy + j, 2))
|
||||
dir -= angle
|
||||
ox = round(center.x*3 + 1 + mag*cos(dir))
|
||||
oy = round(center.y*3 + 1 + mag*sin(dir))
|
||||
|
||||
if (sprite.get_width() % 2 != 0):
|
||||
ox += 1
|
||||
oy += 1
|
||||
|
||||
if (ox >= 0 && ox < sprite.get_width()*3
|
||||
&& oy >= 0 && oy < sprite.get_height()*3):
|
||||
found_pixel = true
|
||||
break
|
||||
|
||||
if !found_pixel:
|
||||
sprite.set_pixel(x, y, Color(0,0,0,0))
|
||||
continue
|
||||
|
||||
var fil : int = oy % 3
|
||||
var col : int = ox % 3
|
||||
var index : int = col + 3*fil
|
||||
|
||||
ox = round((ox - 1)/3.0);
|
||||
oy = round((oy - 1)/3.0);
|
||||
var a : Color
|
||||
var b : Color
|
||||
var c : Color
|
||||
var d : Color
|
||||
var e : Color
|
||||
var f : Color
|
||||
var g : Color
|
||||
var h : Color
|
||||
var i : Color
|
||||
if (ox == 0 || ox == sprite.get_width() - 1 ||
|
||||
oy == 0 || oy == sprite.get_height() - 1):
|
||||
p = aux.get_pixel(ox, oy)
|
||||
else:
|
||||
a = aux.get_pixel(ox-1,oy-1);
|
||||
b = aux.get_pixel(ox,oy-1);
|
||||
c = aux.get_pixel(ox+1,oy-1);
|
||||
d = aux.get_pixel(ox-1,oy);
|
||||
e = aux.get_pixel(ox,oy);
|
||||
f = aux.get_pixel(ox+1,oy);
|
||||
g = aux.get_pixel(ox-1,oy+1);
|
||||
h = aux.get_pixel(ox,oy+1);
|
||||
i = aux.get_pixel(ox+1,oy+1);
|
||||
|
||||
match(index):
|
||||
0:
|
||||
p = d if (similarColors(d,b) && !similarColors(d,h)
|
||||
&& !similarColors(b,f)) else e;
|
||||
1:
|
||||
p = b if ((similarColors(d,b) && !similarColors(d,h) &&
|
||||
!similarColors(b,f) && !similarColors(e,c)) ||
|
||||
(similarColors(b,f) && !similarColors(d,b) &&
|
||||
!similarColors(f,h) && !similarColors(e,a))) else e;
|
||||
2:
|
||||
p = f if (similarColors(b,f) && !similarColors(d,b) &&
|
||||
!similarColors(f,h)) else e;
|
||||
3:
|
||||
p = d if ((similarColors(d,h) && !similarColors(f,h) &&
|
||||
!similarColors(d,b) && !similarColors(e,a)) ||
|
||||
(similarColors(d,b) && !similarColors(d,h) &&
|
||||
!similarColors(b,f) && !similarColors(e,g))) else e;
|
||||
4:
|
||||
p = e
|
||||
5:
|
||||
p = f if((similarColors(b,f) && !similarColors(d,b) &&
|
||||
!similarColors(f,h) && !similarColors(e,i))
|
||||
|| (similarColors(f,h) && !similarColors(b,f) &&
|
||||
!similarColors(d,h) && !similarColors(e,c))) else e;
|
||||
6:
|
||||
p = d if (similarColors(d,h) && !similarColors(f,h) &&
|
||||
!similarColors(d,b)) else e;
|
||||
7:
|
||||
p = h if ((similarColors(f,h) && !similarColors(f,b) &&
|
||||
!similarColors(d,h) && !similarColors(e,g))
|
||||
|| (similarColors(d,h) && !similarColors(f,h) &&
|
||||
!similarColors(d,b) && !similarColors(e,i))) else e;
|
||||
8:
|
||||
p = f if (similarColors(f,h) && !similarColors(f,b) &&
|
||||
!similarColors(d,h)) else e;
|
||||
sprite.set_pixel(x, y, p)
|
||||
sprite.unlock()
|
||||
aux.unlock()
|
||||
|
||||
|
||||
func fake_rotsprite(sprite : Image, angle : float) -> void:
|
||||
sprite.copy_from(scale3X(sprite))
|
||||
nn_rotate(sprite,angle)
|
||||
sprite.resize(sprite.get_width()/3,sprite.get_height()/3,0)
|
||||
|
||||
|
||||
func nn_rotate(sprite : Image, angle : float) -> void:
|
||||
var aux : Image = Image.new()
|
||||
aux.copy_from(sprite)
|
||||
sprite.lock()
|
||||
aux.lock()
|
||||
var ox: int
|
||||
var oy: int
|
||||
var center : Vector2 = Vector2(sprite.get_width()/2, sprite.get_height()/2)
|
||||
for x in range(sprite.get_width()):
|
||||
for y in range(sprite.get_height()):
|
||||
ox = (x - center.x)*cos(angle) + (y - center.y)*sin(angle) + center.x
|
||||
oy = -(x - center.x)*sin(angle) + (y - center.y)*cos(angle) + center.y
|
||||
if ox >= 0 && ox < sprite.get_width() && oy >= 0 && oy < sprite.get_height():
|
||||
sprite.set_pixel(x, y, aux.get_pixel(ox, oy))
|
||||
else:
|
||||
sprite.set_pixel(x, y, Color(0,0,0,0))
|
||||
sprite.unlock()
|
||||
aux.unlock()
|
||||
|
||||
|
||||
func similarColors(c1 : Color, c2 : Color, tol : float = 100) -> bool:
|
||||
var dist = colorDistance(c1, c2)
|
||||
return dist <= tol
|
||||
|
||||
|
||||
func colorDistance(c1 : Color, c2 : Color) -> float:
|
||||
return sqrt(pow((c1.r - c2.r)*255, 2) + pow((c1.g - c2.g)*255, 2)
|
||||
+ pow((c1.b - c2.b)*255, 2) + pow((c1.a - c2.a)*255, 2))
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
config_cache.set_value("window", "screen", OS.current_screen)
|
||||
config_cache.set_value("window", "maximized", OS.window_maximized || OS.window_fullscreen)
|
||||
|
|
|
@ -249,19 +249,19 @@ func import_patterns(priority_ordered_search_path: Array) -> void:
|
|||
if Global.patterns.size() > 0:
|
||||
var image_size = Global.patterns[0].get_size()
|
||||
|
||||
Global.pattern_left_image = Global.patterns[0]
|
||||
Global.pattern_images[0] = Global.patterns[0]
|
||||
var pattern_left_tex := ImageTexture.new()
|
||||
pattern_left_tex.create_from_image(Global.pattern_left_image, 0)
|
||||
Global.left_fill_pattern_container.get_child(0).get_child(0).texture = pattern_left_tex
|
||||
Global.left_fill_pattern_container.get_child(2).get_child(1).max_value = image_size.x - 1
|
||||
Global.left_fill_pattern_container.get_child(3).get_child(1).max_value = image_size.y - 1
|
||||
pattern_left_tex.create_from_image(Global.pattern_images[0], 0)
|
||||
Global.fill_pattern_containers[0].get_child(0).get_child(0).texture = pattern_left_tex
|
||||
Global.fill_pattern_containers[0].get_child(2).get_child(1).max_value = image_size.x - 1
|
||||
Global.fill_pattern_containers[0].get_child(3).get_child(1).max_value = image_size.y - 1
|
||||
|
||||
Global.pattern_right_image = Global.patterns[0]
|
||||
Global.pattern_images[1] = Global.patterns[0]
|
||||
var pattern_right_tex := ImageTexture.new()
|
||||
pattern_right_tex.create_from_image(Global.pattern_right_image, 0)
|
||||
Global.right_fill_pattern_container.get_child(0).get_child(0).texture = pattern_right_tex
|
||||
Global.right_fill_pattern_container.get_child(2).get_child(1).max_value = image_size.x - 1
|
||||
Global.right_fill_pattern_container.get_child(3).get_child(1).max_value = image_size.y - 1
|
||||
pattern_right_tex.create_from_image(Global.pattern_images[1], 0)
|
||||
Global.fill_pattern_containers[1].get_child(0).get_child(0).texture = pattern_right_tex
|
||||
Global.fill_pattern_containers[1].get_child(2).get_child(1).max_value = image_size.x - 1
|
||||
Global.fill_pattern_containers[1].get_child(3).get_child(1).max_value = image_size.y - 1
|
||||
|
||||
|
||||
func import_gpl(path : String) -> Palette:
|
||||
|
|
|
@ -3,7 +3,6 @@ extends Node
|
|||
var current_save_path := ""
|
||||
# Stores a filename of a backup file in user:// until user saves manually
|
||||
var backup_save_path = ""
|
||||
var default_autosave_interval := 5 # Minutes
|
||||
|
||||
onready var autosave_timer : Timer
|
||||
|
||||
|
@ -14,8 +13,7 @@ func _ready() -> void:
|
|||
autosave_timer.process_mode = Timer.TIMER_PROCESS_IDLE
|
||||
autosave_timer.connect("timeout", self, "_on_Autosave_timeout")
|
||||
add_child(autosave_timer)
|
||||
set_autosave_interval(default_autosave_interval)
|
||||
toggle_autosave(true) # Gets started from preferences dialog
|
||||
update_autosave()
|
||||
|
||||
|
||||
func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
||||
|
@ -62,9 +60,8 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
|||
var layer_new_cels_linked := file.get_8()
|
||||
linked_cels.append(file.get_var())
|
||||
|
||||
# Store [Layer name (0), Layer visibility boolean (1), Layer lock boolean (2), Frame container (3),
|
||||
# will new cels be linked boolean (4), Array of linked cels (5)]
|
||||
Global.layers.append([layer_name, layer_visibility, layer_lock, HBoxContainer.new(), layer_new_cels_linked, []])
|
||||
var l := Layer.new(layer_name, layer_visibility, layer_lock, HBoxContainer.new(), layer_new_cels_linked, [])
|
||||
Global.layers.append(l)
|
||||
global_layer_line = file.get_line()
|
||||
|
||||
var frame_line := file.get_line()
|
||||
|
@ -82,21 +79,18 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
|||
if file_major_version == 0 and file_minor_version < 7:
|
||||
var layer_name_old_version = file.get_line()
|
||||
if frame == 0:
|
||||
# Store [Layer name (0), Layer visibility boolean (1), Layer lock boolean (2), Frame container (3),
|
||||
# will new frames be linked boolean (4), Array of linked frames (5)]
|
||||
Global.layers.append([layer_name_old_version, true, false, HBoxContainer.new(), false, []])
|
||||
var l := Layer.new(layer_name_old_version)
|
||||
Global.layers.append(l)
|
||||
var layer_transparency := 1.0
|
||||
if file_major_version >= 0 and file_minor_version > 5:
|
||||
layer_transparency = file.get_float()
|
||||
var image := Image.new()
|
||||
image.create_from_data(width, height, false, Image.FORMAT_RGBA8, buffer)
|
||||
image.lock()
|
||||
var tex := ImageTexture.new()
|
||||
tex.create_from_image(image, 0)
|
||||
canvas.layers.append([image, tex, layer_transparency])
|
||||
canvas.layers.append(Cel.new(image, layer_transparency))
|
||||
if file_major_version >= 0 and file_minor_version >= 7:
|
||||
if frame in linked_cels[layer_i]:
|
||||
Global.layers[layer_i][5].append(canvas)
|
||||
Global.layers[layer_i].linked_cels.append(canvas)
|
||||
|
||||
layer_i += 1
|
||||
layer_line = file.get_line()
|
||||
|
@ -128,19 +122,19 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
|||
Global.current_frame = frame - 1
|
||||
Global.layers = Global.layers # Just to call Global.layers_changed
|
||||
# Load tool options
|
||||
Global.left_color_picker.color = file.get_var()
|
||||
Global.right_color_picker.color = file.get_var()
|
||||
Global.left_brush_size = file.get_8()
|
||||
Global.left_brush_size_edit.value = Global.left_brush_size
|
||||
Global.right_brush_size = file.get_8()
|
||||
Global.right_brush_size_edit.value = Global.right_brush_size
|
||||
Global.color_pickers[0].color = file.get_var()
|
||||
Global.color_pickers[1].color = file.get_var()
|
||||
Global.brush_sizes[0] = file.get_8()
|
||||
Global.brush_size_edits[0].value = Global.brush_sizes[0]
|
||||
Global.brush_sizes[1] = file.get_8()
|
||||
Global.brush_size_edits[1].value = Global.brush_sizes[1]
|
||||
if file_major_version == 0 and file_minor_version < 7:
|
||||
var left_palette = file.get_var()
|
||||
var right_palette = file.get_var()
|
||||
for color in left_palette:
|
||||
Global.left_color_picker.get_picker().add_preset(color)
|
||||
Global.color_pickers[0].get_picker().add_preset(color)
|
||||
for color in right_palette:
|
||||
Global.right_color_picker.get_picker().add_preset(color)
|
||||
Global.color_pickers[1].get_picker().add_preset(color)
|
||||
|
||||
# Load custom brushes
|
||||
Global.custom_brushes.resize(Global.brushes_from_files)
|
||||
|
@ -164,7 +158,7 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
|||
var tag_color : Color = file.get_var()
|
||||
var tag_from := file.get_8()
|
||||
var tag_to := file.get_8()
|
||||
Global.animation_tags.append([tag_name, tag_color, tag_from, tag_to])
|
||||
Global.animation_tags.append(AnimationTag.new(tag_name, tag_color, tag_from, tag_to))
|
||||
Global.animation_tags = Global.animation_tags # To execute animation_tags_changed()
|
||||
tag_line = file.get_line()
|
||||
|
||||
|
@ -187,12 +181,12 @@ func save_pxo_file(path : String, autosave : bool) -> void:
|
|||
# Store Global layers
|
||||
for layer in Global.layers:
|
||||
file.store_line(".")
|
||||
file.store_line(layer[0]) # Layer name
|
||||
file.store_8(layer[1]) # Layer visibility
|
||||
file.store_8(layer[2]) # Layer lock
|
||||
file.store_8(layer[4]) # Future cels linked
|
||||
file.store_line(layer.name)
|
||||
file.store_8(layer.visible)
|
||||
file.store_8(layer.locked)
|
||||
file.store_8(layer.new_cels_linked)
|
||||
var linked_cels := []
|
||||
for canvas in layer[5]:
|
||||
for canvas in layer.linked_cels:
|
||||
linked_cels.append(canvas.frame)
|
||||
file.store_var(linked_cels) # Linked cels as cel numbers
|
||||
|
||||
|
@ -205,8 +199,8 @@ func save_pxo_file(path : String, autosave : bool) -> void:
|
|||
file.store_16(canvas.size.y)
|
||||
for layer in canvas.layers: # Store canvas layers
|
||||
file.store_line("-")
|
||||
file.store_buffer(layer[0].get_data())
|
||||
file.store_float(layer[2]) # Layer transparency
|
||||
file.store_buffer(layer.image.get_data())
|
||||
file.store_float(layer.opacity)
|
||||
file.store_line("END_LAYERS")
|
||||
|
||||
# Store guides
|
||||
|
@ -224,10 +218,10 @@ func save_pxo_file(path : String, autosave : bool) -> void:
|
|||
file.store_line("END_FRAMES")
|
||||
|
||||
# Save tool options
|
||||
var left_color : Color = Global.left_color_picker.color
|
||||
var right_color : Color = Global.right_color_picker.color
|
||||
var left_brush_size : int = Global.left_brush_size
|
||||
var right_brush_size : int = Global.right_brush_size
|
||||
var left_color : Color = Global.color_pickers[0].color
|
||||
var right_color : Color = Global.color_pickers[1].color
|
||||
var left_brush_size : int = Global.brush_sizes[0]
|
||||
var right_brush_size : int = Global.brush_sizes[1]
|
||||
file.store_var(left_color)
|
||||
file.store_var(right_color)
|
||||
file.store_8(left_brush_size)
|
||||
|
@ -245,10 +239,10 @@ func save_pxo_file(path : String, autosave : bool) -> void:
|
|||
# Store animation tags
|
||||
for tag in Global.animation_tags:
|
||||
file.store_line(".T/")
|
||||
file.store_line(tag[0]) # Tag name
|
||||
file.store_var(tag[1]) # Tag color
|
||||
file.store_8(tag[2]) # Tag "from", the first frame
|
||||
file.store_8(tag[3]) # Tag "to", the last frame
|
||||
file.store_line(tag.name)
|
||||
file.store_var(tag.color)
|
||||
file.store_8(tag.from)
|
||||
file.store_8(tag.to)
|
||||
file.store_line("END_FRAME_TAGS")
|
||||
|
||||
file.close()
|
||||
|
@ -271,16 +265,11 @@ func save_pxo_file(path : String, autosave : bool) -> void:
|
|||
Global.notification_label("File failed to save")
|
||||
|
||||
|
||||
func toggle_autosave(enable : bool) -> void:
|
||||
if enable:
|
||||
func update_autosave() -> void:
|
||||
autosave_timer.stop()
|
||||
autosave_timer.wait_time = Global.autosave_interval * 60 # Interval parameter is in minutes, wait_time is seconds
|
||||
if Global.enable_autosave:
|
||||
autosave_timer.start()
|
||||
else:
|
||||
autosave_timer.stop()
|
||||
|
||||
|
||||
func set_autosave_interval(interval : float) -> void:
|
||||
autosave_timer.wait_time = interval * 60 # Interval parameter is in minutes, wait_time is seconds
|
||||
autosave_timer.start()
|
||||
|
||||
|
||||
func _on_Autosave_timeout() -> void:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue