mirror of
https://github.com/tonytins/CozyPixelStudio.git
synced 2025-05-05 16:54:49 -04:00
First option is to affect the selected pixels only. The second it to affect the current cel, or the entire frame (all cels of the frame). Options to affect all frames and all projects will be added next. I also made changes to Canvas.handle_undo() and handle_redo() to make this work. Once all these options are added successfully in HSVDialog, they will also be added in the rest of the Image effect dialogs.
292 lines
10 KiB
GDScript
292 lines
10 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
|
|
|
|
|
|
# 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
|
|
var size : Vector2 = Global.current_project.size
|
|
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)
|
|
|
|
if Global.tile_mode:
|
|
draw_texture(current_cels[i].image_texture, Vector2(location.x, location.y + size.y), modulate_color) # Down
|
|
draw_texture(current_cels[i].image_texture, Vector2(location.x - size.x, location.y + size.y), modulate_color) # Down Left
|
|
draw_texture(current_cels[i].image_texture, Vector2(location.x - size.x, location.y), modulate_color) # Left
|
|
draw_texture(current_cels[i].image_texture, location - size, modulate_color) # Up left
|
|
draw_texture(current_cels[i].image_texture, Vector2(location.x, location.y - size.y), modulate_color) # Up
|
|
draw_texture(current_cels[i].image_texture, Vector2(location.x + size.x, location.y - size.y), modulate_color) # Up right
|
|
draw_texture(current_cels[i].image_texture, Vector2(location.x + size.x, location.y), modulate_color) # Right
|
|
draw_texture(current_cels[i].image_texture, location + size, modulate_color) # Down right
|
|
|
|
if Global.draw_grid:
|
|
draw_grid(Global.grid_type)
|
|
|
|
# Draw rectangle to indicate the pixel currently being hovered on
|
|
if Global.has_focus and Global.can_draw:
|
|
Tools.draw_indicator()
|
|
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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)
|
|
|
|
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.undo_redo.commit_action()
|
|
|
|
|
|
func update_texture(layer_index : int, frame_index := -1) -> void:
|
|
if frame_index == -1:
|
|
frame_index = Global.current_project.current_frame
|
|
var current_cel : Cel = Global.current_project.frames[frame_index].cels[layer_index]
|
|
current_cel.image_texture.create_from_image(current_cel.image, 0)
|
|
|
|
var frame_texture_rect : TextureRect
|
|
frame_texture_rect = Global.find_node_by_name(Global.current_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
|
|
|
|
|
|
func draw_grid(grid_type : int) -> void:
|
|
var size : Vector2 = Global.current_project.size
|
|
if grid_type == Global.Grid_Types.CARTESIAN || grid_type == Global.Grid_Types.ALL:
|
|
for x in range(Global.grid_width, size.x, Global.grid_width):
|
|
draw_line(Vector2(x, location.y), Vector2(x, size.y), Global.grid_color, true)
|
|
|
|
for y in range(Global.grid_height, size.y, Global.grid_height):
|
|
draw_line(Vector2(location.x, y), Vector2(size.x, y), Global.grid_color, true)
|
|
|
|
# Doesn't work properly yet
|
|
if grid_type == Global.Grid_Types.ISOMETRIC || grid_type == Global.Grid_Types.ALL:
|
|
var prev_x := 0
|
|
var prev_y := 0
|
|
for y in range(0, size.y + 1, Global.grid_width):
|
|
var yy1 = y + size.y * tan(deg2rad(26.565)) # 30 degrees
|
|
if yy1 <= (size.y + 0.01):
|
|
draw_line(Vector2(location.x, y), Vector2(size.x, yy1),Global.grid_color)
|
|
else:
|
|
var xx1 = (size.x - y) * tan(deg2rad(90 - 26.565)) # 60 degrees
|
|
draw_line(Vector2(location.x, y), Vector2(xx1, size.y), Global.grid_color)
|
|
for y in range(0, size.y + 1, Global.grid_height):
|
|
var xx2 = y * tan(deg2rad(90 - 26.565)) # 60 degrees
|
|
if xx2 <= (size.x + 0.01):
|
|
draw_line(Vector2(location.x, y), Vector2(xx2, location.y), Global.grid_color)
|
|
prev_y = location.y
|
|
else:
|
|
var distance = (xx2 - prev_x) / 2
|
|
#var yy2 = (size.y - y) * tan(deg2rad(26.565)) # 30 degrees
|
|
var yy2 = prev_y + distance
|
|
draw_line(Vector2(location.x, y), Vector2(size.x, yy2), Global.grid_color)
|
|
prev_y = yy2
|
|
|
|
prev_x = xx2
|
|
|
|
for x in range(0, size.x, Global.grid_width * 2):
|
|
if x == 0:
|
|
continue
|
|
var yy1 = (size.x - x) * tan(deg2rad(26.565)) # 30 degrees
|
|
draw_line(Vector2(x, location.y), Vector2(size.x, yy1), Global.grid_color)
|
|
for x in range(0, size.x, Global.grid_height * 2):
|
|
var yy2 = (size.x - x) * tan(deg2rad(26.565)) # 30 degrees
|
|
draw_line(Vector2(x, size.y), Vector2(size.x, size.y - yy2), Global.grid_color)
|