mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-20 12:33:14 +00:00
Further speed up of bucket fill (#672)
* Removed some calls to can_pixel_get_drawn by managing has_selection condition differently * Refactored `_set_pixel` to shortcircuit. This allowed shortcircuiting away some tests to speed up common code paths. * Further refactor of `_set_pixel` Removed unused functions. Made conditions for pattern-fills clearer. * Refactor. * Refactor. * Removed dev timers.
This commit is contained in:
parent
7c6cdadb86
commit
1a19c3a297
1 changed files with 54 additions and 36 deletions
|
@ -256,10 +256,22 @@ func _flood_line_around_point(position: Vector2, project: Project, image: Image,
|
|||
return int(position.x) + 1
|
||||
var west: Vector2 = position
|
||||
var east: Vector2 = position
|
||||
while project.can_pixel_get_drawn(west) && image.get_pixelv(west).is_equal_approx(src_color):
|
||||
west += Vector2.LEFT
|
||||
while project.can_pixel_get_drawn(east) && image.get_pixelv(east).is_equal_approx(src_color):
|
||||
east += Vector2.RIGHT
|
||||
if project.has_selection:
|
||||
while (
|
||||
project.can_pixel_get_drawn(west)
|
||||
&& image.get_pixelv(west).is_equal_approx(src_color)
|
||||
):
|
||||
west += Vector2.LEFT
|
||||
while (
|
||||
project.can_pixel_get_drawn(east)
|
||||
&& image.get_pixelv(east).is_equal_approx(src_color)
|
||||
):
|
||||
east += Vector2.RIGHT
|
||||
else:
|
||||
while west.x >= 0 && image.get_pixelv(west).is_equal_approx(src_color):
|
||||
west += Vector2.LEFT
|
||||
while east.x < project.size.x && image.get_pixelv(east).is_equal_approx(src_color):
|
||||
east += Vector2.RIGHT
|
||||
# Make a note of the stuff we processed
|
||||
var c = int(position.y)
|
||||
var segment = _allegro_flood_segments[c]
|
||||
|
@ -280,16 +292,14 @@ func _flood_line_around_point(position: Vector2, project: Project, image: Image,
|
|||
segment.y = position.y
|
||||
segment.next = 0
|
||||
# Should we process segments above or below this one?
|
||||
if project.has_selection:
|
||||
# when there is a selected area, the pixels above and below the one we started creating this
|
||||
# segment from may be outside it. It's easier to assume we should be checking for segments
|
||||
# above and below this one than to specifically check every single pixel in it, because that
|
||||
# test will be performed later anyway.
|
||||
segment.todo_above = position.y > 0
|
||||
segment.todo_below = position.y < project.size.y - 1
|
||||
else:
|
||||
segment.todo_above = project.can_pixel_get_drawn(position + Vector2.UP)
|
||||
segment.todo_below = project.can_pixel_get_drawn(position + Vector2.DOWN)
|
||||
# when there is a selected area, the pixels above and below the one we started creating this
|
||||
# segment from may be outside it. It's easier to assume we should be checking for segments
|
||||
# above and below this one than to specifically check every single pixel in it, because that
|
||||
# test will be performed later anyway.
|
||||
# On the other hand, this test we described is the same `project.can_pixel_get_drawn` does if
|
||||
# there is no selection, so we don't need branching here.
|
||||
segment.todo_above = position.y > 0
|
||||
segment.todo_below = position.y < project.size.y - 1
|
||||
# this is an actual segment we should be coloring, so we add it to the results for the
|
||||
# current image
|
||||
_allegro_image_segments.append(segment)
|
||||
|
@ -326,8 +336,14 @@ func _flood_fill(position: Vector2) -> void:
|
|||
for image in images:
|
||||
var color: Color = image.get_pixelv(position)
|
||||
if _fill_with == 0 or _pattern == null:
|
||||
# end early if we are filling with the same color
|
||||
if tool_slot.color.is_equal_approx(color):
|
||||
return
|
||||
else:
|
||||
# end early if we are filling with an empty pattern
|
||||
var pattern_size = _pattern.image.get_size()
|
||||
if pattern_size.x == 0 or pattern_size.y == 0:
|
||||
return
|
||||
# init flood data structures
|
||||
_allegro_flood_segments = []
|
||||
_allegro_image_segments = []
|
||||
|
@ -354,31 +370,33 @@ func _flood_fill(position: Vector2) -> void:
|
|||
p.y - 1, p.left_position, p.right_position, project, image, color
|
||||
):
|
||||
done = false
|
||||
# now actually color the image
|
||||
for c in _allegro_image_segments.size():
|
||||
var p = _allegro_image_segments[c]
|
||||
if p.flooding: # sanity check: should always be true
|
||||
# now actually color the image: since we have already checked a few things for the points
|
||||
# we'll process here, we're going to skip a bunch of safety checks to speed things up.
|
||||
if _fill_with == 0 or _pattern == null:
|
||||
# short circuit for flat colors
|
||||
for c in _allegro_image_segments.size():
|
||||
var p = _allegro_image_segments[c]
|
||||
for px in range(p.left_position, p.right_position + 1):
|
||||
_set_pixel(image, px, p.y, tool_slot.color)
|
||||
# We don't have to check again whether the point being processed is within the bounds
|
||||
image.set_pixel(px, p.y, tool_slot.color)
|
||||
else:
|
||||
# shortcircuit tests for patternfills
|
||||
var pattern_size = _pattern.image.get_size()
|
||||
# we know the pattern had a valid size when we began flooding, so we can skip testing that
|
||||
# again for every point in the pattern.
|
||||
for c in _allegro_image_segments.size():
|
||||
var p = _allegro_image_segments[c]
|
||||
for px in range(p.left_position, p.right_position + 1):
|
||||
_set_pixel_pattern(image, px, p.y, pattern_size)
|
||||
|
||||
|
||||
func _set_pixel(image: Image, x: int, y: int, color: Color) -> void:
|
||||
var project: Project = Global.current_project
|
||||
if !project.can_pixel_get_drawn(Vector2(x, y)):
|
||||
return
|
||||
|
||||
if _fill_with == 0 or _pattern == null:
|
||||
image.set_pixel(x, y, color)
|
||||
else:
|
||||
var size := _pattern.image.get_size()
|
||||
if size.x == 0 or size.y == 0:
|
||||
return
|
||||
_pattern.image.lock()
|
||||
var px := int(x + _offset_x) % int(size.x)
|
||||
var py := int(y + _offset_y) % int(size.y)
|
||||
var pc := _pattern.image.get_pixel(px, py)
|
||||
_pattern.image.unlock()
|
||||
image.set_pixel(x, y, pc)
|
||||
func _set_pixel_pattern(image: Image, x: int, y: int, pattern_size: Vector2) -> void:
|
||||
_pattern.image.lock()
|
||||
var px := int(x + _offset_x) % int(pattern_size.x)
|
||||
var py := int(y + _offset_y) % int(pattern_size.y)
|
||||
var pc := _pattern.image.get_pixel(px, py)
|
||||
_pattern.image.unlock()
|
||||
image.set_pixel(x, y, pc)
|
||||
|
||||
|
||||
func commit_undo(action: String, undo_data: Dictionary) -> void:
|
||||
|
|
Loading…
Add table
Reference in a new issue