1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 09:09:47 +00:00

Basic tablet pen pressure sensitivity, brush alpha now gets blended

Instead of replacing the pixels with the new color's alpha value, the alpha values of the selected color and the current pixel color get blended together. This means that, if you have a pixel with 50% alpha and you draw a color over it with 25% alpha, the final result will have 75% alpha, instead of 25% as it used to be.

The pressure sensitivity is still experimental and may not work properly. Works only with Godot 3.2 and above.

draw_pixel() has also been renamed to draw_brush()
This commit is contained in:
OverloadedOrama 2020-02-09 01:34:37 +02:00
parent 5d0b39f14c
commit f28a3a4405
3 changed files with 68 additions and 45 deletions

View file

@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- Image layer rotation! Choose between 2 rotation algorithms, Rotxel and Nearest Neighbour - Thanks to azagaya!
- Tablet pen pressure sensitivity!
- Crowdin integration for contributing translations!
- Spanish translation - thanks to azagaya!
- Chinese Simplified translation - thanks to wcxu21!
@ -16,13 +17,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Lanczos scaling interpolation. (Added because of the Godot 3.2 update)
### Changed
- Updates to the Greek, Russian and Traditional Chinese translations.
- Major changes to alpha blending behavior. The alpha values now get added/blended together instead of just replacing the pixel with the new value.
- Replaced some OS alerts with a custom made error dialog.
- Made the zooming smoother, is toggleable in Preferences whether to keep the new zooming or the old one.
- Made the "X" button on the custom brushes a little smaller.
- The color picker will now have a small white triangle on the top left of the color preview if at least one of its RGB values are above 1 in Raw mode. (Added automatically because of the Godot 3.2 update)
- You can now toggle the visibility of hidden items on and off in the file dialogs. (Added automatically because of the Godot 3.2 update)
- The language buttons in the preferences have their localized names in their hint tooltips. For example, if you hover over the "English" button while the language is Greek, the hint tooltip will be "Αγγλικά", which is the Greek word for English.
- Translation updates.
### Fixed
- Delay the splash screen popup so it shows properly centered (thanks to YeldhamDev)

View file

@ -22,11 +22,12 @@ var north_limit := location.y
var south_limit := location.y + size.y
var mouse_inside_canvas := false # used for undo
var sprite_changed_this_frame := false # for optimization purposes
var lighten_darken_pixels := [] # Cleared after mouse release
var mouse_press_pixels := [] # Cleared after mouse release
var is_making_line := false
var made_line := false
var is_making_selection := "None"
var line_2d : Line2D
var pen_pressure := 1.0 # For tablet pressure sensitivity
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
@ -119,6 +120,13 @@ func _input(event : InputEvent) -> void:
if Global.has_focus:
update()
# Godot 3.2 and above only code
if Engine.get_version_info().major == 3 && Engine.get_version_info().minor >= 2:
if event is InputEventMouseMotion:
pen_pressure = event.pressure
if pen_pressure == 0: # Aka drawing with mouse
pen_pressure = 1
sprite_changed_this_frame = false
var mouse_pos := current_pixel
var mouse_pos_floored := mouse_pos.floor()
@ -203,16 +211,16 @@ func _input(event : InputEvent) -> void:
handle_undo("Draw")
elif (Input.is_action_just_released("left_mouse") && !Input.is_action_pressed("right_mouse")) || (Input.is_action_just_released("right_mouse") && !Input.is_action_pressed("left_mouse")):
made_line = false
lighten_darken_pixels.clear()
mouse_press_pixels.clear()
if can_handle || Global.undos == Global.undo_redo.get_version():
if previous_action != "None" && previous_action != "RectSelect" && current_action != "ColorPicker":
handle_redo("Draw")
match current_action: # Handle current tool
"Pencil":
pencil_and_eraser(mouse_pos, current_color, current_mouse_button)
pencil_and_eraser(mouse_pos, current_color, current_mouse_button, current_action)
"Eraser":
pencil_and_eraser(mouse_pos, Color(0, 0, 0, 0), current_mouse_button)
pencil_and_eraser(mouse_pos, Color(0, 0, 0, 0), current_mouse_button, current_action)
"Bucket":
if can_handle:
if fill_area == 0: # Paint the specific area of the same color
@ -539,19 +547,19 @@ func pencil_and_eraser(mouse_pos : Vector2, color : Color, current_mouse_button
return
if is_making_line:
fill_gaps(line_2d.points[1], previous_mouse_pos_for_lines, color, current_mouse_button, current_action)
draw_pixel(line_2d.points[1], color, current_mouse_button, current_action)
draw_brush(line_2d.points[1], color, current_mouse_button, current_action)
made_line = true
else:
if point_in_rectangle(mouse_pos, location, location + size):
mouse_inside_canvas = true
# Draw
draw_pixel(mouse_pos, color, current_mouse_button, current_action)
draw_brush(mouse_pos, color, current_mouse_button, current_action)
fill_gaps(mouse_pos, previous_mouse_pos, color, current_mouse_button, current_action) #Fill the gaps
# If mouse is not inside bounds but it used to be, fill the gaps
elif point_in_rectangle(previous_mouse_pos, location, location + size):
fill_gaps(mouse_pos, previous_mouse_pos, color, current_mouse_button, current_action)
func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String, current_action := "None") -> void:
func draw_brush(pos : Vector2, color : Color, current_mouse_button : String, current_action := "None") -> void:
if Global.can_draw && Global.has_focus:
var brush_size := 1
var brush_type = Global.BRUSH_TYPES.PIXEL
@ -561,6 +569,11 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String, cur
var vertical_mirror := false
var ld := 0
var ld_amount := 0.1
if Global.pressure_sensitivity_mode == Global.PRESSURE_SENSITIVITY.ALPHA:
if current_action == "Pencil":
color.a *= pen_pressure
elif current_action == "Eraser":
color.a *= (1.0 - pen_pressure)
if current_mouse_button == "left_mouse":
brush_size = Global.left_brush_size
brush_type = Global.current_left_brush_type
@ -618,17 +631,22 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String, cur
var pos_floored := Vector2(cur_pos_x, cur_pos_y).floor()
# Don't draw the same pixel over and over and don't re-lighten/darken it
var current_pixel_color : Color = layers[current_layer_index][0].get_pixel(cur_pos_x, cur_pos_y)
if current_pixel_color != color && !(pos_floored in lighten_darken_pixels):
if current_action == "LightenDarken":
color = current_pixel_color
if color.a > 0:
if ld == 0: # Lighten
color = current_pixel_color.lightened(ld_amount)
else: # Darken
color = current_pixel_color.darkened(ld_amount)
lighten_darken_pixels.append(pos_floored)
var _c := color
if current_action == "Pencil" && color.a < 1:
# Blend alpha
_c.a = color.a + current_pixel_color.a * (1 - color.a)
layers[current_layer_index][0].set_pixel(cur_pos_x, cur_pos_y, color)
if current_pixel_color != _c && !(pos_floored in mouse_press_pixels):
if current_action == "LightenDarken":
_c = current_pixel_color
if _c.a > 0:
if ld == 0: # Lighten
_c = current_pixel_color.lightened(ld_amount)
else: # Darken
_c = current_pixel_color.darkened(ld_amount)
mouse_press_pixels.append(pos_floored)
layers[current_layer_index][0].set_pixel(cur_pos_x, cur_pos_y, _c)
sprite_changed_this_frame = true
# Handle mirroring
@ -636,39 +654,39 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String, cur
var mirror_y := south_limit + north_limit - cur_pos_y - 1
if horizontal_mirror:
current_pixel_color = layers[current_layer_index][0].get_pixel(mirror_x, cur_pos_y)
if current_pixel_color != color: # don't draw the same pixel over and over
if current_pixel_color != _c: # don't draw the same pixel over and over
if current_action == "LightenDarken":
if ld == 0: # Lighten
color = current_pixel_color.lightened(ld_amount)
_c = current_pixel_color.lightened(ld_amount)
else:
color = current_pixel_color.darkened(ld_amount)
lighten_darken_pixels.append(pos_floored)
_c = current_pixel_color.darkened(ld_amount)
layers[current_layer_index][0].set_pixel(mirror_x, cur_pos_y, color)
mouse_press_pixels.append(pos_floored)
layers[current_layer_index][0].set_pixel(mirror_x, cur_pos_y, _c)
sprite_changed_this_frame = true
if vertical_mirror:
current_pixel_color = layers[current_layer_index][0].get_pixel(cur_pos_x, mirror_y)
if current_pixel_color != color: # don't draw the same pixel over and over
if current_pixel_color != _c: # don't draw the same pixel over and over
if current_action == "LightenDarken":
if ld == 0: # Lighten
color = current_pixel_color.lightened(ld_amount)
_c = current_pixel_color.lightened(ld_amount)
else:
color = current_pixel_color.darkened(ld_amount)
lighten_darken_pixels.append(pos_floored)
_c = current_pixel_color.darkened(ld_amount)
layers[current_layer_index][0].set_pixel(cur_pos_x, mirror_y, color)
mouse_press_pixels.append(pos_floored)
layers[current_layer_index][0].set_pixel(cur_pos_x, mirror_y, _c)
sprite_changed_this_frame = true
if horizontal_mirror && vertical_mirror:
current_pixel_color = layers[current_layer_index][0].get_pixel(mirror_x, mirror_y)
if current_pixel_color != color: # don't draw the same pixel over and over
if current_pixel_color != _c: # don't draw the same pixel over and over
if current_action == "LightenDarken":
if ld == 0: # Lighten
color = current_pixel_color.lightened(ld_amount)
_c = current_pixel_color.lightened(ld_amount)
else:
color = current_pixel_color.darkened(ld_amount)
lighten_darken_pixels.append(pos_floored)
_c = current_pixel_color.darkened(ld_amount)
layers[current_layer_index][0].set_pixel(mirror_x, mirror_y, color)
mouse_press_pixels.append(pos_floored)
layers[current_layer_index][0].set_pixel(mirror_x, mirror_y, _c)
sprite_changed_this_frame = true
elif brush_type == Global.BRUSH_TYPES.CIRCLE || brush_type == Global.BRUSH_TYPES.FILLED_CIRCLE:
@ -773,7 +791,7 @@ func fill_gaps(mouse_pos : Vector2, prev_mouse_pos : Vector2, color : Color, cur
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_pixel(Vector2(x, y), color, current_mouse_button, current_action)
draw_brush(Vector2(x, y), color, current_mouse_button, current_action)
e2 = err << 1
if e2 >= dy:
err += dy
@ -830,14 +848,11 @@ func plot_circle(sprite : Image, xm : int, ym : int, r : int, color : Color, fil
var quadrant_2 := Vector2(xm - y, ym - x)
var quadrant_3 := Vector2(xm + x, ym - y)
var quadrant_4 := Vector2(xm + y, ym + x)
if point_in_rectangle(quadrant_1, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
sprite.set_pixelv(quadrant_1, color)
if point_in_rectangle(quadrant_2, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
sprite.set_pixelv(quadrant_2, color)
if point_in_rectangle(quadrant_3, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
sprite.set_pixelv(quadrant_3, color)
if point_in_rectangle(quadrant_4, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
sprite.set_pixelv(quadrant_4, color)
draw_pixel_blended(sprite, quadrant_1, color)
draw_pixel_blended(sprite, quadrant_2, color)
draw_pixel_blended(sprite, quadrant_3, color)
draw_pixel_blended(sprite, quadrant_4, color)
r = err
if r <= y:
y += 1
@ -851,8 +866,14 @@ func plot_circle(sprite : Image, xm : int, ym : int, r : int, color : Color, fil
for i in range (-radius, radius + 1):
if i * i + j * j <= radius * radius:
var draw_pos := Vector2(i + xm, j + ym)
if point_in_rectangle(draw_pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
sprite.set_pixelv(draw_pos, color)
draw_pixel_blended(sprite, draw_pos, color)
func draw_pixel_blended(sprite : Image, pos : Vector2, color : Color) -> void:
if point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
if color.a > 0 && color.a < 1:
# Blend alpha
color.a = color.a + sprite.get_pixelv(pos).a * (1 - color.a)
sprite.set_pixelv(pos, color)
# Checks if a point is inside a rectangle
func point_in_rectangle(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool:

View file

@ -16,7 +16,7 @@ var canvases := []
# warning-ignore:unused_class_variable
var hidden_canvases := []
enum PRESSURE_SENSITIVITY {NONE, ALPHA, SIZE}
var pressure_sensitivity_mode = PRESSURE_SENSITIVITY.NONE
var pressure_sensitivity_mode = PRESSURE_SENSITIVITY.ALPHA
var smooth_zoom := true
var left_cursor_tool_texture : ImageTexture
var right_cursor_tool_texture : ImageTexture