1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 17:19:50 +00:00
Pixelorama/addons/gdgifexporter/quantization/median_cut.gd
2020-08-09 00:16:03 +03:00

149 lines
3.6 KiB
GDScript

extends Node
var converter = preload('../converter.gd').new()
var color_table: Dictionary = {}
var transparency: bool = false
var tree: TreeNode
var leaf: Array = []
class TreeNode:
var colors: Array
var average_color: Array
var axis: int
var median: int
var parent: TreeNode
var left: TreeNode
var right: TreeNode
func _init(_parent: TreeNode, _colors: Array):
self.parent = _parent
self.colors = _colors
func median_cut() -> void:
var start: Array = [255, 255, 255]
var end: Array = [0, 0, 0]
var delta: Array = [0, 0, 0]
for color in colors:
for i in 3:
if color[i] < start[i]:
start[i] = color[i]
if color[i] > end[i]:
end[i] = color[i]
for i in 3:
delta[i] = end[i] - start[i]
axis = 0
if delta[1] > delta[0]:
axis = 1
if delta[2] > delta[axis]:
axis = 2
var axis_sort: Array = []
for i in colors.size():
axis_sort.append(colors[i][axis])
axis_sort.sort()
var cut = colors.size() >> 1
median = axis_sort[cut]
var left_colors: Array = []
var right_colors: Array = []
for color in colors:
if color[axis] < median:
left_colors.append(color)
else:
right_colors.append(color)
left = TreeNode.new(self, left_colors)
right = TreeNode.new(self, right_colors)
colors = []
func calculate_average_color(color_table: Dictionary) -> void:
average_color = [0, 0, 0]
var total: int = 0
for color in colors:
var weight = color_table[color]
for i in 3:
average_color[i] += color[i] * weight
total += weight
for i in 3:
average_color[i] /= total
func fill_color_table(image: Image) -> void:
image.lock()
var data: PoolByteArray = image.get_data()
for i in range(0, data.size(), 4):
if data[i + 3] == 0:
transparency = true
continue
var color: Array = [data[i], data[i + 1], data[i + 2]]
var count = color_table.get(color, 0)
color_table[color] = count + 1
image.unlock()
func convert_image(image: Image, colors: Array) -> PoolByteArray:
image.lock()
var data: PoolByteArray = image.get_data()
var nearest_lookup: Dictionary = {}
var result: PoolByteArray = PoolByteArray()
for i in colors.size():
colors[i] = Vector3(colors[i][0], colors[i][1], colors[i][2])
for i in range(0, data.size(), 4):
if data[i + 3] == 0:
result.append(0)
continue
var current: Vector3 = Vector3(data[i], data[i + 1], data[i + 2])
var nearest_index: int = 0 + int(transparency)
if current in nearest_lookup:
nearest_index = nearest_lookup[current]
else:
var nearest_distance: float = current.distance_squared_to(colors[nearest_index])
for j in range(1 + int(transparency), colors.size()):
var distance: float = current.distance_squared_to(colors[j])
if distance < nearest_distance:
nearest_index = j
nearest_distance = distance
nearest_lookup[current] = nearest_index
result.append(nearest_index)
image.unlock()
return result
func quantize_and_convert_to_codes(image: Image) -> Array:
color_table.clear()
transparency = false
fill_color_table(image)
tree = TreeNode.new(null, color_table.keys())
leaf = [tree]
var num = 254 if transparency else 255
while leaf.size() <= num:
var node = leaf.pop_front()
if node.colors.size() > 1:
node.median_cut()
leaf.append(node.left)
leaf.append(node.right)
if leaf.size() <= 0:
break
var color_quantized: Dictionary = {}
for node in leaf:
node.calculate_average_color(color_table)
color_quantized[node.average_color] = color_quantized.size()
var color_array: Array = color_quantized.keys()
if transparency:
color_array.push_front([0, 0, 0])
var data: PoolByteArray = converter.get_similar_indexed_datas(image, color_array)
return [data, color_array, transparency]