mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-01-19 09:39:48 +00:00
157 lines
3.8 KiB
GDScript
157 lines
3.8 KiB
GDScript
extends Reference
|
|
|
|
|
|
var converter = preload('../converter.gd').new()
|
|
var transparency := false
|
|
|
|
func longest_axis(colors: Array) -> int:
|
|
var start := [255, 255, 255]
|
|
var end := [0, 0, 0]
|
|
for color in colors:
|
|
for i in 3:
|
|
start[i] = min(color[i], start[i])
|
|
end[i] = max(color[i], end[i])
|
|
|
|
var max_r = end[0] - start[0]
|
|
var max_g = end[1] - start[1]
|
|
var max_b = end[2] - start[2]
|
|
|
|
if max_r > max_g:
|
|
if max_r > max_b:
|
|
return 0
|
|
else:
|
|
if max_g > max_b:
|
|
return 1
|
|
return 2
|
|
|
|
func get_median(colors: Array) -> Vector3:
|
|
return colors[colors.size() >> 1]
|
|
|
|
|
|
func median_cut(colors: Array) -> Array:
|
|
var axis := longest_axis(colors)
|
|
|
|
var axis_sort := []
|
|
for color in colors:
|
|
axis_sort.append(color[axis])
|
|
axis_sort.sort()
|
|
|
|
var cut := axis_sort.size() >> 1
|
|
var median: int = axis_sort[cut]
|
|
axis_sort = []
|
|
|
|
var left_colors := []
|
|
var right_colors := []
|
|
|
|
for color in colors:
|
|
if color[axis] < median:
|
|
left_colors.append(color)
|
|
else:
|
|
right_colors.append(color)
|
|
|
|
return [left_colors, right_colors]
|
|
|
|
func average_color(bucket: Array) -> Array:
|
|
var r := 0
|
|
var g := 0
|
|
var b := 0
|
|
for color in bucket:
|
|
r += color[0]
|
|
g += color[1]
|
|
b += color[2]
|
|
return [r / bucket.size(), g / bucket.size(), b / bucket.size()]
|
|
|
|
func average_colors(buckets: Array) -> Dictionary:
|
|
var avg_colors := {}
|
|
for bucket in buckets:
|
|
if bucket.size() > 0:
|
|
avg_colors[average_color(bucket)] = avg_colors.size()
|
|
return avg_colors
|
|
|
|
func pixels_to_colors(image: Image) -> Array:
|
|
image.lock()
|
|
var result := []
|
|
var data: PoolByteArray = image.get_data()
|
|
|
|
for i in range(0, data.size(), 4):
|
|
if data[i + 3] == 0:
|
|
transparency = true
|
|
continue
|
|
result.append([data[i], data[i + 1], data[i + 2]])
|
|
image.unlock()
|
|
return result
|
|
|
|
func remove_smallest_bucket(buckets: Array) -> Array:
|
|
if buckets.size() == 0:
|
|
return buckets
|
|
var i_of_smallest_bucket := 0
|
|
for i in range(buckets.size()):
|
|
if buckets[i].size() < buckets[i_of_smallest_bucket].size():
|
|
i_of_smallest_bucket = i
|
|
buckets.remove(i_of_smallest_bucket)
|
|
return buckets
|
|
|
|
func remove_empty_buckets(buckets: Array) -> Array:
|
|
if buckets.size() == 0:
|
|
return buckets
|
|
|
|
var i := buckets.find([])
|
|
while i != -1:
|
|
buckets.remove(i)
|
|
i = buckets.find([])
|
|
|
|
return buckets
|
|
|
|
# quantizes to gif ready codes
|
|
func quantize(image: Image) -> Array:
|
|
var pixels = pixels_to_colors(image)
|
|
if pixels.size() == 0:
|
|
return pixels
|
|
|
|
var buckets := [pixels]
|
|
var done_buckets := []
|
|
|
|
# it tells how many times buckets should be divided into two
|
|
var dimensions := 8
|
|
|
|
for i in range(0, dimensions):
|
|
var new_buckets := []
|
|
for bucket in buckets:
|
|
# don't median cut if bucket is smaller than 2, because
|
|
# it won't produce two new buckets.
|
|
if bucket.size() > 1:
|
|
var res := median_cut(bucket)
|
|
# sometimes when you try to median cut a bucket, the result
|
|
# is one with size equal to 0 and other with full size as the
|
|
# source bucket. Because of that it's useless to try to divide
|
|
# it further so it's better to put it into separate list and
|
|
# process only those buckets witch divide further.
|
|
if res[0].size() == 0 or res[1].size() == 0:
|
|
done_buckets += res
|
|
else:
|
|
new_buckets += res
|
|
buckets = []
|
|
buckets = new_buckets
|
|
|
|
var all_buckets := remove_empty_buckets(done_buckets + buckets)
|
|
|
|
buckets = []
|
|
done_buckets = []
|
|
|
|
if transparency:
|
|
if all_buckets.size() == pow(2, dimensions):
|
|
all_buckets = remove_smallest_bucket(all_buckets)
|
|
|
|
# dictionaries are only for speed.
|
|
var color_array := average_colors(all_buckets).keys()
|
|
|
|
# if pixel_to_colors detected that the image has transparent pixels
|
|
# then add transparency color at the beginning so it will be properly
|
|
# exported.
|
|
if transparency:
|
|
color_array = [[0, 0, 0]] + color_array
|
|
|
|
var data: PoolByteArray = converter.get_similar_indexed_datas(image, color_array)
|
|
|
|
return [data, color_array, transparency]
|