forked from Orama-Interactive/Pixelorama
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix for Orama-Interactive#341. :) (Orama-Interactive#488)
- Loading branch information
Showing
4 changed files
with
433 additions
and
526 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,292 @@ | ||
extends Reference | ||
|
||
|
||
enum Error { | ||
OK = 0, | ||
EMPTY_IMAGE = 1, | ||
BAD_IMAGE_FORMAT = 2 | ||
} | ||
|
||
|
||
var little_endian = preload('./little_endian.gd').new() | ||
var lzw = preload('./gif-lzw/lzw.gd').new() | ||
var converter = preload("./converter.gd") | ||
|
||
var last_color_table := [] | ||
var last_transparency_index := -1 | ||
|
||
# File data and Header | ||
var data := PoolByteArray([]) | ||
|
||
|
||
func _init(_width: int, _height: int): | ||
add_header() | ||
add_logical_screen_descriptor(_width, _height) | ||
add_application_ext("NETSCAPE", "2.0", [1, 0, 0]) | ||
|
||
func export_file_data() -> PoolByteArray: | ||
return data + PoolByteArray([0x3b]) | ||
|
||
func add_header() -> void: | ||
data += 'GIF'.to_ascii() + '89a'.to_ascii() | ||
|
||
func add_logical_screen_descriptor(width: int, height: int) -> void: | ||
# not Global Color Table Flag | ||
# Color Resolution = 8 bits | ||
# Sort Flag = 0, not sorted. | ||
# Size of Global Color Table set to 0 | ||
# because we'll use only Local Tables | ||
var packed_fields: int = 0b01110000 | ||
var background_color_index: int = 0 | ||
var pixel_aspect_ratio: int = 0 | ||
|
||
data += little_endian.int_to_2bytes(width) | ||
data += little_endian.int_to_2bytes(height) | ||
data.append(packed_fields) | ||
data.append(background_color_index) | ||
data.append(pixel_aspect_ratio) | ||
|
||
func add_application_ext(app_iden: String, app_auth_code: String, _data: Array) -> void: | ||
var extension_introducer := 0x21 | ||
var extension_label := 0xff | ||
|
||
var block_size := 11 | ||
|
||
data.append(extension_introducer) | ||
data.append(extension_label) | ||
data.append(block_size) | ||
data += app_iden.to_ascii() | ||
data += app_auth_code.to_ascii() | ||
data.append(_data.size()) | ||
data += PoolByteArray(_data) | ||
data.append(0) | ||
|
||
# finds the image color table. Stops if the size gets larger than 256. | ||
func find_color_table(image: Image) -> Dictionary: | ||
image.lock() | ||
var result: Dictionary = {} | ||
var image_data: PoolByteArray = image.get_data() | ||
|
||
for i in range(0, image_data.size(), 4): | ||
var color: Array = [ | ||
int(image_data[i]), | ||
int(image_data[i + 1]), | ||
int(image_data[i + 2]), | ||
int(image_data[i + 3])] | ||
if not color in result: | ||
result[color] = result.size() | ||
if result.size() > 256: | ||
break | ||
|
||
image.unlock() | ||
return result | ||
|
||
func find_transparency_color_index(color_table: Dictionary) -> int: | ||
for color in color_table: | ||
if color[3] == 0: | ||
return color_table[color] | ||
return -1 | ||
|
||
func change_colors_to_codes(image: Image, | ||
color_palette: Dictionary, | ||
transparency_color_index: int) -> PoolByteArray: | ||
image.lock() | ||
var image_data: PoolByteArray = image.get_data() | ||
var result: PoolByteArray = PoolByteArray([]) | ||
|
||
for i in range(0, image_data.size(), 4): | ||
var color: Array = [ | ||
image_data[i], | ||
image_data[i + 1], | ||
image_data[i + 2], | ||
image_data[i + 3]] | ||
|
||
if color in color_palette: | ||
if color[3] == 0 and transparency_color_index != -1: | ||
result.append(transparency_color_index) | ||
else: | ||
result.append(color_palette[color]) | ||
else: | ||
result.append(0) | ||
push_warning('change_colors_to_codes: color not found! [%d, %d, %d, %d]' % color) | ||
|
||
image.unlock() | ||
return result | ||
|
||
# makes sure that the color table is at least size 4. | ||
func make_proper_size(color_table: Array) -> Array: | ||
var result := [] + color_table | ||
if color_table.size() < 4: | ||
for i in range(4 - color_table.size()): | ||
result.append([0, 0, 0, 0]) | ||
return result | ||
|
||
func calc_delay_time(frame_delay: float) -> int: | ||
return int(ceil(frame_delay / 0.01)) | ||
|
||
func color_table_to_indexes(colors: Array) -> PoolByteArray: | ||
var result: PoolByteArray = PoolByteArray([]) | ||
for i in range(colors.size()): | ||
result.append(i) | ||
return result | ||
|
||
func add_frame(image: Image, frame_delay: float, quantizator: Script) -> int: | ||
# check if image is of good format | ||
if image.get_format() != Image.FORMAT_RGBA8: | ||
return Error.BAD_IMAGE_FORMAT | ||
|
||
# check if image isn't empty | ||
if image.is_empty(): | ||
return Error.EMPTY_IMAGE | ||
|
||
var found_color_table: Dictionary = find_color_table(image) | ||
|
||
var image_converted_to_codes: PoolByteArray | ||
var transparency_color_index: int = -1 | ||
var color_table: Array | ||
if found_color_table.size() <= 256: # we don't need to quantize the image. | ||
# try to find transparency color index. | ||
transparency_color_index = find_transparency_color_index(found_color_table) | ||
# if didn't found transparency color index but there is atleast one | ||
# place for this color then add it artificially. | ||
if transparency_color_index == -1 and found_color_table.size() <= 255: | ||
found_color_table[[0, 0, 0, 0]] = found_color_table.size() | ||
transparency_color_index = found_color_table.size() - 1 | ||
image_converted_to_codes = change_colors_to_codes( | ||
image, found_color_table, transparency_color_index) | ||
color_table = make_proper_size(found_color_table.keys()) | ||
else: # we have to quantize the image. | ||
var quantization_result: Array = quantizator.new().quantize(image) | ||
image_converted_to_codes = quantization_result[0] | ||
color_table = quantization_result[1] | ||
# transparency index should always be as the first element of color table. | ||
transparency_color_index = 0 if quantization_result[2] else -1 | ||
|
||
last_color_table = color_table | ||
last_transparency_index = transparency_color_index | ||
|
||
var delay_time := calc_delay_time(frame_delay) | ||
|
||
var color_table_indexes := color_table_to_indexes(color_table) | ||
var compressed_image_result: Array = lzw.compress_lzw( | ||
image_converted_to_codes, | ||
color_table_indexes) | ||
var compressed_image_data: PoolByteArray = compressed_image_result[0] | ||
var lzw_min_code_size: int = compressed_image_result[1] | ||
|
||
add_graphic_constrol_ext(delay_time, transparency_color_index) | ||
add_image_descriptor( | ||
Vector2.ZERO, | ||
image.get_size(), | ||
color_table_bit_size(color_table)) | ||
add_local_color_table(color_table) | ||
add_image_data_block(lzw_min_code_size, compressed_image_data) | ||
|
||
return Error.OK | ||
|
||
# adds frame with last color informations | ||
func add_frame_with_lci(image: Image, frame_delay: float) -> int: | ||
# check if image is of good format | ||
if image.get_format() != Image.FORMAT_RGBA8: | ||
return Error.BAD_IMAGE_FORMAT | ||
|
||
# check if image isn't empty | ||
if image.is_empty(): | ||
return Error.EMPTY_IMAGE | ||
|
||
var image_converted_to_codes: PoolByteArray = converter.new().get_similar_indexed_datas(image, last_color_table) | ||
|
||
var color_table_indexes := color_table_to_indexes(last_color_table) | ||
var compressed_image_result: Array = lzw.compress_lzw( | ||
image_converted_to_codes, | ||
color_table_indexes) | ||
var compressed_image_data: PoolByteArray = compressed_image_result[0] | ||
var lzw_min_code_size: int = compressed_image_result[1] | ||
|
||
var delay_time := calc_delay_time(frame_delay) | ||
|
||
add_graphic_constrol_ext(delay_time, last_transparency_index) | ||
add_image_descriptor( | ||
Vector2.ZERO, | ||
image.get_size(), | ||
color_table_bit_size(last_color_table)) | ||
add_local_color_table(last_color_table) | ||
add_image_data_block(lzw_min_code_size, compressed_image_data) | ||
|
||
return Error.OK | ||
|
||
func add_graphic_constrol_ext(_delay_time: float, tci: int = -1) -> void: | ||
var extension_introducer: int = 0x21 | ||
var graphic_control_label: int = 0xf9 | ||
|
||
var block_size: int = 4 | ||
var packed_fields: int = 0b00001000 | ||
if tci != -1: | ||
packed_fields = 0b00001001 | ||
|
||
var delay_time: int = _delay_time | ||
var transparent_color_index: int = tci if tci != -1 else 0 | ||
|
||
data.append(extension_introducer) | ||
data.append(graphic_control_label) | ||
|
||
data.append(block_size) | ||
data.append(packed_fields) | ||
data += little_endian.int_to_2bytes(delay_time) | ||
data.append(transparent_color_index) | ||
|
||
data.append(0) | ||
|
||
func add_image_descriptor(pos: Vector2, | ||
size: Vector2, | ||
l_color_table_size: int) -> void: | ||
var image_separator: int = 0x2c | ||
var packed_fields: int = 0b10000000 | (0b111 & l_color_table_size) | ||
|
||
var little_endian = preload('./little_endian.gd').new() | ||
|
||
data.append(image_separator) | ||
data += little_endian.int_to_2bytes(int(pos.x)) # left pos | ||
data += little_endian.int_to_2bytes(int(pos.y)) # top pos | ||
data += little_endian.int_to_2bytes(int(size.x)) # width | ||
data += little_endian.int_to_2bytes(int(size.y)) # height | ||
data.append(packed_fields) | ||
|
||
func color_table_bit_size(color_table: Array) -> int: | ||
if color_table.size() <= 1: | ||
return 0 | ||
var bit_size := int(ceil(log(color_table.size()) / log(2.0))) | ||
return bit_size - 1 | ||
|
||
func add_local_color_table(color_table: Array) -> void: | ||
for color in color_table: | ||
data.append(color[0]) | ||
data.append(color[1]) | ||
data.append(color[2]) | ||
|
||
var size := color_table_bit_size(color_table) | ||
var proper_size := int(pow(2, size + 1)) | ||
|
||
if color_table.size() != proper_size: | ||
for i in range(proper_size - color_table.size()): | ||
data += PoolByteArray([0, 0, 0]) | ||
|
||
func add_image_data_block(lzw_min_code_size: int, _data: PoolByteArray) -> void: | ||
data.append(lzw_min_code_size) | ||
|
||
var block_size_index: int = 0 | ||
var i: int = 0 | ||
var data_index: int = 0 | ||
while data_index < _data.size(): | ||
if i == 0: | ||
data.append(0) | ||
block_size_index = data.size() - 1 | ||
data.append(_data[data_index]) | ||
data[block_size_index] += 1 | ||
data_index += 1 | ||
i += 1 | ||
if i == 254: | ||
i = 0 | ||
|
||
if not _data.empty(): | ||
data.append(0) |
Oops, something went wrong.