mirror of
https://github.com/tonytins/CozyPixelStudio.git
synced 2025-12-18 03:34:43 -05:00
* Add shrink option that allow change default shrink Shrink option allow to scale the godot application. This path enable dynamic change of this value in the options. * Fix the computation of cursor location For an unknown reason the function get_local_mouse_position return incorrect current_pixel when shrink is not 1. This path compute the transformation manually and povide correct values for any shrink value.
245 lines
7.8 KiB
GDScript
245 lines
7.8 KiB
GDScript
class_name Canvas
|
|
extends Node2D
|
|
|
|
|
|
var location := Vector2.ZERO
|
|
var fill_color := Color(0, 0, 0, 0)
|
|
var current_pixel := Vector2.ZERO # pretty much same as mouse_pos, but can be accessed externally
|
|
var can_undo := true
|
|
var cursor_image_has_changed := false
|
|
var sprite_changed_this_frame := false # for optimization purposes
|
|
|
|
onready var grid = $Grid
|
|
onready var tile_mode = $TileMode
|
|
onready var indicators = $Indicators
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready() -> void:
|
|
var frame : Frame = new_empty_frame(true)
|
|
Global.current_project.frames.append(frame)
|
|
yield(get_tree().create_timer(0.2), "timeout")
|
|
camera_zoom()
|
|
|
|
|
|
func _draw() -> void:
|
|
Global.second_viewport.get_child(0).get_node("CanvasPreview").update()
|
|
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
|
|
if Global.onion_skinning:
|
|
onion_skinning()
|
|
|
|
# Draw current frame layers
|
|
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, location, modulate_color)
|
|
|
|
tile_mode.update()
|
|
|
|
|
|
func _input(event : InputEvent) -> void:
|
|
# Don't process anything below if the input isn't a mouse event, or Shift/Ctrl.
|
|
# This decreases CPU/GPU usage slightly.
|
|
if not event is InputEventMouse:
|
|
if not event is InputEventKey:
|
|
return
|
|
elif not event.scancode in [KEY_SHIFT, KEY_CONTROL]:
|
|
return
|
|
# elif not get_viewport_rect().has_point(event.position):
|
|
# return
|
|
|
|
#current_pixel = get_local_mouse_position() + location
|
|
var tmp_transform = get_canvas_transform().affine_inverse()
|
|
var tmp_position = Global.main_viewport.get_local_mouse_position()
|
|
current_pixel = tmp_transform.basis_xform(tmp_position)+tmp_transform.origin
|
|
|
|
if Global.has_focus:
|
|
update()
|
|
|
|
sprite_changed_this_frame = false
|
|
|
|
var current_project : Project = Global.current_project
|
|
|
|
if Global.has_focus:
|
|
if !cursor_image_has_changed:
|
|
cursor_image_has_changed = true
|
|
if Global.show_left_tool_icon:
|
|
Global.left_cursor.visible = true
|
|
if Global.show_right_tool_icon:
|
|
Global.right_cursor.visible = true
|
|
else:
|
|
if cursor_image_has_changed:
|
|
cursor_image_has_changed = false
|
|
Global.left_cursor.visible = false
|
|
Global.right_cursor.visible = false
|
|
|
|
Tools.handle_draw(current_pixel.floor(), event)
|
|
|
|
if sprite_changed_this_frame:
|
|
update_texture(current_project.current_layer)
|
|
|
|
|
|
func camera_zoom() -> void:
|
|
# Set camera zoom based on the sprite size
|
|
var bigger_canvas_axis = max(Global.current_project.size.x, Global.current_project.size.y)
|
|
var zoom_max := Vector2(bigger_canvas_axis, bigger_canvas_axis) * 0.01
|
|
var cameras = [Global.camera, Global.camera2, Global.camera_preview]
|
|
for camera in cameras:
|
|
if zoom_max > Vector2.ONE:
|
|
camera.zoom_max = zoom_max
|
|
else:
|
|
camera.zoom_max = Vector2.ONE
|
|
|
|
if camera == Global.camera_preview:
|
|
Global.preview_zoom_slider.max_value = -camera.zoom_min.x
|
|
Global.preview_zoom_slider.min_value = -camera.zoom_max.x
|
|
|
|
camera.fit_to_frame(Global.current_project.size)
|
|
camera.save_values_to_project()
|
|
|
|
Global.transparent_checker._ready() # To update the rect size
|
|
|
|
|
|
func new_empty_frame(first_time := false, single_layer := false, size := Global.current_project.size) -> Frame:
|
|
var frame := Frame.new()
|
|
for l in Global.current_project.layers: # Create as many cels as there are layers
|
|
# The sprite itself
|
|
var sprite := Image.new()
|
|
if first_time:
|
|
if Global.config_cache.has_section_key("preferences", "default_image_width"):
|
|
Global.current_project.size.x = Global.config_cache.get_value("preferences", "default_image_width")
|
|
if Global.config_cache.has_section_key("preferences", "default_image_height"):
|
|
Global.current_project.size.y = Global.config_cache.get_value("preferences", "default_image_height")
|
|
if Global.config_cache.has_section_key("preferences", "default_fill_color"):
|
|
fill_color = Global.config_cache.get_value("preferences", "default_fill_color")
|
|
sprite.create(size.x, size.y, false, Image.FORMAT_RGBA8)
|
|
sprite.fill(fill_color)
|
|
sprite.lock()
|
|
frame.cels.append(Cel.new(sprite, 1))
|
|
|
|
if single_layer:
|
|
break
|
|
|
|
return frame
|
|
|
|
|
|
func handle_undo(action : String, project : Project = Global.current_project, layer_index := -2, frame_index := -2) -> void:
|
|
if !can_undo:
|
|
return
|
|
|
|
if layer_index <= -2:
|
|
layer_index = project.current_layer
|
|
if frame_index <= -2:
|
|
frame_index = project.current_frame
|
|
|
|
var cels := []
|
|
var frames := []
|
|
var layers := []
|
|
if frame_index == -1:
|
|
frames = project.frames
|
|
else:
|
|
frames.append(project.frames[frame_index])
|
|
|
|
if layer_index == -1:
|
|
layers = project.layers
|
|
else:
|
|
layers.append(project.layers[layer_index])
|
|
|
|
for f in frames:
|
|
for l in layers:
|
|
var index = project.layers.find(l)
|
|
cels.append(f.cels[index])
|
|
|
|
project.undos += 1
|
|
project.undo_redo.create_action(action)
|
|
for cel in cels:
|
|
# If we don't unlock the image, it doesn't work properly
|
|
cel.image.unlock()
|
|
var data = cel.image.data
|
|
cel.image.lock()
|
|
project.undo_redo.add_undo_property(cel.image, "data", data)
|
|
project.undo_redo.add_undo_method(Global, "undo", frame_index, layer_index, project)
|
|
|
|
can_undo = false
|
|
|
|
|
|
func handle_redo(_action : String, project : Project = Global.current_project, layer_index := -2, frame_index := -2) -> void:
|
|
can_undo = true
|
|
if project.undos < project.undo_redo.get_version():
|
|
return
|
|
|
|
if layer_index <= -2:
|
|
layer_index = project.current_layer
|
|
if frame_index <= -2:
|
|
frame_index = project.current_frame
|
|
|
|
var cels := []
|
|
var frames := []
|
|
var layers := []
|
|
if frame_index == -1:
|
|
frames = project.frames
|
|
else:
|
|
frames.append(project.frames[frame_index])
|
|
|
|
if layer_index == -1:
|
|
layers = project.layers
|
|
else:
|
|
layers.append(project.layers[layer_index])
|
|
|
|
for f in frames:
|
|
for l in layers:
|
|
var index = project.layers.find(l)
|
|
cels.append(f.cels[index])
|
|
|
|
for cel in cels:
|
|
project.undo_redo.add_do_property(cel.image, "data", cel.image.data)
|
|
project.undo_redo.add_do_method(Global, "redo", frame_index, layer_index, project)
|
|
project.undo_redo.commit_action()
|
|
|
|
|
|
func update_texture(layer_index : int, frame_index := -1, project : Project = Global.current_project) -> void:
|
|
if frame_index == -1:
|
|
frame_index = project.current_frame
|
|
var current_cel : Cel = project.frames[frame_index].cels[layer_index]
|
|
current_cel.image_texture.create_from_image(current_cel.image, 0)
|
|
|
|
if project == Global.current_project:
|
|
var frame_texture_rect : TextureRect
|
|
frame_texture_rect = Global.find_node_by_name(project.layers[layer_index].frame_container.get_child(frame_index), "CelTexture")
|
|
frame_texture_rect.texture = current_cel.image_texture
|
|
|
|
|
|
func onion_skinning() -> void:
|
|
# Past
|
|
if Global.onion_skinning_past_rate > 0:
|
|
var color : Color
|
|
if Global.onion_skinning_blue_red:
|
|
color = Color.blue
|
|
else:
|
|
color = Color.white
|
|
for i in range(1, Global.onion_skinning_past_rate + 1):
|
|
if Global.current_project.current_frame >= i:
|
|
var layer_i := 0
|
|
for layer in Global.current_project.frames[Global.current_project.current_frame - i].cels:
|
|
if Global.current_project.layers[layer_i].visible:
|
|
color.a = 0.6 / i
|
|
draw_texture(layer.image_texture, location, color)
|
|
layer_i += 1
|
|
|
|
# Future
|
|
if Global.onion_skinning_future_rate > 0:
|
|
var color : Color
|
|
if Global.onion_skinning_blue_red:
|
|
color = Color.red
|
|
else:
|
|
color = Color.white
|
|
for i in range(1, Global.onion_skinning_future_rate + 1):
|
|
if Global.current_project.current_frame < Global.current_project.frames.size() - i:
|
|
var layer_i := 0
|
|
for layer in Global.current_project.frames[Global.current_project.current_frame + i].cels:
|
|
if Global.current_project.layers[layer_i].visible:
|
|
color.a = 0.6 / i
|
|
draw_texture(layer.image_texture, location, color)
|
|
layer_i += 1
|