mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-01-18 17:19:50 +00:00
parent
b50c07b29d
commit
c59ce34aad
|
@ -30,7 +30,7 @@ Files extracted from source:
|
||||||
## SmartSlicer
|
## SmartSlicer
|
||||||
|
|
||||||
- Upstream: https://github.com/Variable-Interactive/SmartSlicer
|
- Upstream: https://github.com/Variable-Interactive/SmartSlicer
|
||||||
- Version: Based on git commit 5d65d2ff556fed878099c96546ceb307aa3bc030 with a modification on SmartSlicePreview.gd (lines 31-32). Only the contents of addons folder are used and the script SmartSlicePreview.gd is moved to res://src/UI/Dialogs/HelperScripts/ for better organization
|
- Version: Based on git commit fd2b423497a377937dbc988e309cc95afd1436ca with a modification on SmartSlicePreview.gd (lines 31-32). Only the contents of addons folder are used and the script SmartSlicePreview.gd is moved to res://src/UI/Dialogs/HelperScripts/ for better organization
|
||||||
- License: [MIT](https://github.com/Variable-Interactive/SmartSlicer/blob/main/LICENSE)
|
- License: [MIT](https://github.com/Variable-Interactive/SmartSlicer/blob/main/LICENSE)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
class_name RegionUnpacker
|
class_name RegionUnpacker
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
# THIS CLASS TAKES INSPIRATION FROM PIXELORAMA'S FLOOD FILL
|
# THIS CLASS TAKES INSPIRATION FROM PIXELORAMA'S FLOOD FILL
|
||||||
# AND HAS BEEN MODIFIED FOR OPTIMIZATION
|
# AND HAS BEEN MODIFIED FOR OPTIMIZATION
|
||||||
|
|
||||||
|
enum { DETECT_VERTICAL_EMPTY_LINES, DETECT_HORIZONTAL_EMPTY_LINES }
|
||||||
|
|
||||||
var slice_thread := Thread.new()
|
var slice_thread := Thread.new()
|
||||||
|
|
||||||
var _include_boundary_threshold: int ## Τhe size of rect below which merging accounts for boundary
|
var _include_boundary_threshold: int ## Τhe size of rect below which merging accounts for boundary
|
||||||
|
@ -44,10 +45,12 @@ func _init(threshold: int, merge_dist: int) -> void:
|
||||||
_merge_dist = merge_dist
|
_merge_dist = merge_dist
|
||||||
|
|
||||||
|
|
||||||
func get_used_rects(image: Image) -> RectData:
|
func get_used_rects(
|
||||||
|
image: Image, lazy_check := false, scan_dir := DETECT_VERTICAL_EMPTY_LINES
|
||||||
|
) -> RectData:
|
||||||
if ProjectSettings.get_setting("rendering/driver/threads/thread_model") != 2:
|
if ProjectSettings.get_setting("rendering/driver/threads/thread_model") != 2:
|
||||||
# Single-threaded mode
|
# Single-threaded mode
|
||||||
return get_rects(image)
|
return get_rects(image, lazy_check, scan_dir)
|
||||||
else: # Multi-threaded mode
|
else: # Multi-threaded mode
|
||||||
if slice_thread.is_started():
|
if slice_thread.is_started():
|
||||||
slice_thread.wait_to_finish()
|
slice_thread.wait_to_finish()
|
||||||
|
@ -55,30 +58,74 @@ func get_used_rects(image: Image) -> RectData:
|
||||||
if error == OK:
|
if error == OK:
|
||||||
return slice_thread.wait_to_finish()
|
return slice_thread.wait_to_finish()
|
||||||
else:
|
else:
|
||||||
return get_rects(image)
|
return get_rects(image, lazy_check, scan_dir)
|
||||||
|
|
||||||
|
|
||||||
func get_rects(image: Image) -> RectData:
|
func get_rects(
|
||||||
|
image: Image, lazy_check := false, scan_dir := DETECT_VERTICAL_EMPTY_LINES
|
||||||
|
) -> RectData:
|
||||||
|
var skip_amount = 0
|
||||||
# Make a smaller image to make the loop shorter
|
# Make a smaller image to make the loop shorter
|
||||||
var used_rect := image.get_used_rect()
|
var used_rect := image.get_used_rect()
|
||||||
if used_rect.size == Vector2i.ZERO:
|
if used_rect.size == Vector2i.ZERO:
|
||||||
return clean_rects([])
|
return clean_rects([])
|
||||||
var test_image := image.get_region(used_rect)
|
var test_image := image.get_region(used_rect)
|
||||||
# Prepare a bitmap to keep track of previous places
|
# Prepare a bitmap to keep track of previous places
|
||||||
var scanned_area := BitMap.new()
|
var scanned_area := Image.create(
|
||||||
scanned_area.create(test_image.get_size())
|
test_image.get_size().x, test_image.get_size().y, false, Image.FORMAT_LA8
|
||||||
|
)
|
||||||
# Scan the image
|
# Scan the image
|
||||||
var rects: Array[Rect2i] = []
|
var rects: Array[Rect2i] = []
|
||||||
var frame_size := Vector2i.ZERO
|
var frame_size := Vector2i.ZERO
|
||||||
for y in test_image.get_size().y:
|
|
||||||
for x in test_image.get_size().x:
|
var found_pixels_this_line := false
|
||||||
var position := Vector2i(x, y)
|
var has_jumped_last_line := false
|
||||||
|
var scanned_lines := PackedInt32Array()
|
||||||
|
var side_a: int
|
||||||
|
var side_b: int
|
||||||
|
match scan_dir:
|
||||||
|
DETECT_VERTICAL_EMPTY_LINES:
|
||||||
|
side_a = test_image.get_size().x
|
||||||
|
side_b = test_image.get_size().y
|
||||||
|
DETECT_HORIZONTAL_EMPTY_LINES:
|
||||||
|
side_a = test_image.get_size().y
|
||||||
|
side_b = test_image.get_size().x
|
||||||
|
var line := 0
|
||||||
|
while line < side_a:
|
||||||
|
for element: int in side_b:
|
||||||
|
var position := Vector2i(line, element)
|
||||||
|
if scan_dir == DETECT_HORIZONTAL_EMPTY_LINES:
|
||||||
|
position = Vector2i(element, line)
|
||||||
if test_image.get_pixelv(position).a > 0: # used portion of image detected
|
if test_image.get_pixelv(position).a > 0: # used portion of image detected
|
||||||
if !scanned_area.get_bitv(position):
|
found_pixels_this_line = true
|
||||||
|
if scanned_area.get_pixelv(position).a == 0:
|
||||||
var rect := _estimate_rect(test_image, position)
|
var rect := _estimate_rect(test_image, position)
|
||||||
scanned_area.set_bit_rect(rect, true)
|
scanned_area.fill_rect(rect, Color.WHITE)
|
||||||
rect.position += used_rect.position
|
rect.position += used_rect.position
|
||||||
rects.append(rect)
|
rects.append(rect)
|
||||||
|
if lazy_check:
|
||||||
|
if !line in scanned_lines:
|
||||||
|
scanned_lines.append(line)
|
||||||
|
|
||||||
|
if found_pixels_this_line and not has_jumped_last_line:
|
||||||
|
found_pixels_this_line = false
|
||||||
|
line += 1
|
||||||
|
if line in scanned_lines: ## We have scanned all skipped lines, re calculate current index
|
||||||
|
scanned_lines.sort()
|
||||||
|
line = scanned_lines[-1] + 1
|
||||||
|
elif not found_pixels_this_line:
|
||||||
|
## we haven't found any pixels in this line and are assuming next line is empty as well
|
||||||
|
skip_amount += 1
|
||||||
|
has_jumped_last_line = true
|
||||||
|
line += 1 + skip_amount
|
||||||
|
elif found_pixels_this_line and has_jumped_last_line:
|
||||||
|
found_pixels_this_line = false
|
||||||
|
has_jumped_last_line = false
|
||||||
|
## if we skipped a line then go back and make sure it was really empty or not
|
||||||
|
line -= skip_amount
|
||||||
|
skip_amount = 0
|
||||||
|
else:
|
||||||
|
line += 1
|
||||||
var rects_info := clean_rects(rects)
|
var rects_info := clean_rects(rects)
|
||||||
rects_info.rects.sort_custom(sort_rects)
|
rects_info.rects.sort_custom(sort_rects)
|
||||||
return rects_info
|
return rects_info
|
||||||
|
@ -86,7 +133,7 @@ func get_rects(image: Image) -> RectData:
|
||||||
|
|
||||||
func clean_rects(rects: Array[Rect2i]) -> RectData:
|
func clean_rects(rects: Array[Rect2i]) -> RectData:
|
||||||
var frame_size := Vector2i.ZERO
|
var frame_size := Vector2i.ZERO
|
||||||
for i in rects.size():
|
for i: int in rects.size():
|
||||||
var target: Rect2i = rects.pop_front()
|
var target: Rect2i = rects.pop_front()
|
||||||
var test_rect := target
|
var test_rect := target
|
||||||
if (
|
if (
|
||||||
|
@ -96,7 +143,7 @@ func clean_rects(rects: Array[Rect2i]) -> RectData:
|
||||||
test_rect.size += Vector2i(_merge_dist, _merge_dist)
|
test_rect.size += Vector2i(_merge_dist, _merge_dist)
|
||||||
test_rect.position -= Vector2i(_merge_dist, _merge_dist) / 2
|
test_rect.position -= Vector2i(_merge_dist, _merge_dist) / 2
|
||||||
var merged := false
|
var merged := false
|
||||||
for rect_i in rects.size():
|
for rect_i: int in rects.size():
|
||||||
if test_rect.intersects(rects[rect_i]):
|
if test_rect.intersects(rects[rect_i]):
|
||||||
rects[rect_i] = target.merge(rects[rect_i])
|
rects[rect_i] = target.merge(rects[rect_i])
|
||||||
merged = true
|
merged = true
|
||||||
|
@ -215,18 +262,12 @@ func _flood_fill(position: Vector2i, image: Image) -> Rect2i:
|
||||||
_compute_segments_for_image(position, image)
|
_compute_segments_for_image(position, image)
|
||||||
# now actually color the image: since we have already checked a few things for the points
|
# 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.
|
# we'll process here, we're going to skip a bunch of safety checks to speed things up.
|
||||||
|
return _select_segments()
|
||||||
var final_image := Image.new()
|
|
||||||
final_image.copy_from(image)
|
|
||||||
final_image.fill(Color.TRANSPARENT)
|
|
||||||
_select_segments(final_image)
|
|
||||||
|
|
||||||
return final_image.get_used_rect()
|
|
||||||
|
|
||||||
|
|
||||||
func _compute_segments_for_image(position: Vector2i, image: Image) -> void:
|
func _compute_segments_for_image(position: Vector2i, image: Image) -> void:
|
||||||
# initially allocate at least 1 segment per line of image
|
# initially allocate at least 1 segment per line of image
|
||||||
for j in image.get_height():
|
for j: int in image.get_height():
|
||||||
_add_new_segment(j)
|
_add_new_segment(j)
|
||||||
# start flood algorithm
|
# start flood algorithm
|
||||||
_flood_line_around_point(position, image)
|
_flood_line_around_point(position, image)
|
||||||
|
@ -235,7 +276,7 @@ func _compute_segments_for_image(position: Vector2i, image: Image) -> void:
|
||||||
while not done:
|
while not done:
|
||||||
done = true
|
done = true
|
||||||
var max_index := _allegro_flood_segments.size()
|
var max_index := _allegro_flood_segments.size()
|
||||||
for c in max_index:
|
for c: int in max_index:
|
||||||
var p := _allegro_flood_segments[c]
|
var p := _allegro_flood_segments[c]
|
||||||
if p.todo_below: # check below the segment?
|
if p.todo_below: # check below the segment?
|
||||||
p.todo_below = false
|
p.todo_below = false
|
||||||
|
@ -247,11 +288,16 @@ func _compute_segments_for_image(position: Vector2i, image: Image) -> void:
|
||||||
done = false
|
done = false
|
||||||
|
|
||||||
|
|
||||||
func _select_segments(map: Image) -> void:
|
func _select_segments() -> Rect2i:
|
||||||
# short circuit for flat colors
|
# short circuit for flat colors
|
||||||
for c in _allegro_image_segments.size():
|
var used_rect := Rect2i()
|
||||||
|
for c: int in _allegro_image_segments.size():
|
||||||
var p := _allegro_image_segments[c]
|
var p := _allegro_image_segments[c]
|
||||||
var rect := Rect2i()
|
var rect := Rect2i()
|
||||||
rect.position = Vector2i(p.left_position, p.y)
|
rect.position = Vector2i(p.left_position, p.y)
|
||||||
rect.end = Vector2i(p.right_position + 1, p.y + 1)
|
rect.end = Vector2i(p.right_position + 1, p.y + 1)
|
||||||
map.fill_rect(rect, Color.WHITE)
|
if used_rect.size == Vector2i.ZERO:
|
||||||
|
used_rect = rect
|
||||||
|
else:
|
||||||
|
used_rect = used_rect.merge(rect)
|
||||||
|
return used_rect
|
||||||
|
|
Loading…
Reference in a new issue