CozyPixelStudio/src/Canvas.gd
OverloadedOrama 1ce89c6577 Added affecting options to the HSVDialog
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.
2020-07-24 03:22:12 +03:00

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)