mirror of
https://github.com/Relintai/MemR.git
synced 2024-11-14 10:27:20 +01:00
Added gif support using https://github.com/vbousquet/godot-gif-importer .
This commit is contained in:
parent
483842187f
commit
5cb1a3ce2f
@ -15,26 +15,26 @@ anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
margin_left = 7.0
|
||||
margin_top = 7.0
|
||||
margin_right = 1017.0
|
||||
margin_right = 593.0
|
||||
margin_bottom = 593.0
|
||||
|
||||
[node name="VBoxContainer" parent="Menu" index="0"]
|
||||
margin_right = 1010.0
|
||||
margin_right = 586.0
|
||||
margin_bottom = 586.0
|
||||
|
||||
[node name="Sort" parent="Menu/VBoxContainer" index="0"]
|
||||
margin_top = 239.0
|
||||
margin_right = 1010.0
|
||||
margin_right = 586.0
|
||||
margin_bottom = 259.0
|
||||
|
||||
[node name="Settings" parent="Menu/VBoxContainer" index="1"]
|
||||
margin_top = 283.0
|
||||
margin_right = 1010.0
|
||||
margin_right = 586.0
|
||||
margin_bottom = 303.0
|
||||
|
||||
[node name="Exit" parent="Menu/VBoxContainer" index="2"]
|
||||
margin_top = 327.0
|
||||
margin_right = 1010.0
|
||||
margin_right = 586.0
|
||||
margin_bottom = 347.0
|
||||
|
||||
[node name="Settings" parent="." instance=ExtResource( 3 )]
|
||||
@ -54,7 +54,7 @@ margin_bottom = 593.0
|
||||
[node name="Control" type="Control" parent="."]
|
||||
margin_left = 7.0
|
||||
margin_top = 7.0
|
||||
margin_right = 1017.0
|
||||
margin_right = 593.0
|
||||
margin_bottom = 593.0
|
||||
mouse_filter = 2
|
||||
|
||||
|
41
game/addons/gif-importer/GIF2AnimatedTexturePlugin.gd
Normal file
41
game/addons/gif-importer/GIF2AnimatedTexturePlugin.gd
Normal file
@ -0,0 +1,41 @@
|
||||
# Derived from https://github.com/jegor377/godot-gdgifexporter
|
||||
|
||||
tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
func get_importer_name():
|
||||
return "gif.animated.texture.plugin"
|
||||
|
||||
func get_visible_name():
|
||||
return "Animated Texture"
|
||||
|
||||
func get_recognized_extensions():
|
||||
return ["gif"]
|
||||
|
||||
func get_save_extension():
|
||||
return "tres"
|
||||
|
||||
func get_resource_type():
|
||||
return "AnimatedTexture"
|
||||
|
||||
func get_preset_count():
|
||||
return 1
|
||||
|
||||
func get_preset_name(i):
|
||||
return "Default"
|
||||
|
||||
func get_import_options(i):
|
||||
return [
|
||||
{"name": "Filter", "default_value": false},
|
||||
{"name": "MipMaps", "default_value": false}
|
||||
]
|
||||
|
||||
func import(source_file, save_path, options, platform_variants, gen_files):
|
||||
var reader = GifReader.new()
|
||||
reader.filter = options["Filter"]
|
||||
reader.mipmaps = options["MipMaps"]
|
||||
var tex = reader.read(source_file)
|
||||
if tex == null:
|
||||
return FAILED
|
||||
var filename = save_path + "." + get_save_extension()
|
||||
return ResourceSaver.save(filename, tex)
|
50
game/addons/gif-importer/GIF2SpriteFramesPlugin.gd
Normal file
50
game/addons/gif-importer/GIF2SpriteFramesPlugin.gd
Normal file
@ -0,0 +1,50 @@
|
||||
# Derived from https://github.com/jegor377/godot-gdgifexporter
|
||||
|
||||
tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
func get_importer_name():
|
||||
return "gif.animated.texture.plugin"
|
||||
|
||||
func get_visible_name():
|
||||
return "Sprite Frames"
|
||||
|
||||
func get_recognized_extensions():
|
||||
return ["gif"]
|
||||
|
||||
func get_save_extension():
|
||||
return "tres"
|
||||
|
||||
func get_resource_type():
|
||||
return "SpriteFrames"
|
||||
|
||||
func get_preset_count():
|
||||
return 1
|
||||
|
||||
func get_preset_name(i):
|
||||
return "Default"
|
||||
|
||||
func get_import_options(i):
|
||||
return [
|
||||
{"name": "Filter", "default_value": false},
|
||||
{"name": "MipMaps", "default_value": false}
|
||||
]
|
||||
|
||||
func import(source_file, save_path, options, platform_variants, gen_files):
|
||||
var reader = GifReader.new()
|
||||
reader.filter = options["Filter"]
|
||||
reader.mipmaps = options["MipMaps"]
|
||||
var tex = reader.read(source_file)
|
||||
if tex == null:
|
||||
return FAILED
|
||||
var filename = save_path + "." + get_save_extension()
|
||||
var sf = SpriteFrames.new()
|
||||
var minLength = 1000
|
||||
var maxLength = 0
|
||||
for i in range(0, tex.frames):
|
||||
sf.add_frame("default", tex.get_frame_texture(i))
|
||||
var length = tex.get_frame_delay(i)
|
||||
minLength = min(minLength, length)
|
||||
maxLength = min(maxLength, length)
|
||||
sf.set_animation_speed("default", 2.0 / (minLength + maxLength))
|
||||
return ResourceSaver.save(filename, sf)
|
137
game/addons/gif-importer/GIFReader.gd
Normal file
137
game/addons/gif-importer/GIFReader.gd
Normal file
@ -0,0 +1,137 @@
|
||||
tool
|
||||
extends Reference
|
||||
|
||||
class_name GifReader
|
||||
|
||||
var filter = false
|
||||
var mipmaps = false
|
||||
|
||||
var lzw_module = preload("./gif-lzw/lzw.gd")
|
||||
var lzw = lzw_module.new()
|
||||
|
||||
func read(source_file):
|
||||
var file = File.new()
|
||||
if file.open(source_file, File.READ) != OK:
|
||||
return null
|
||||
var data = file.get_buffer(file.get_len())
|
||||
file.close()
|
||||
var pos = 0
|
||||
# Header 'GIF89a'
|
||||
pos = pos + 6
|
||||
# Logical Screen Descriptor
|
||||
var width = get_int(data, pos)
|
||||
var height = get_int(data, pos + 2)
|
||||
var packed_info = data[pos + 4]
|
||||
var background_color_index = data[pos + 5]
|
||||
pos = pos + 7
|
||||
# Global color table
|
||||
var global_lut
|
||||
if (packed_info & 0x80) != 0:
|
||||
var lut_size = 1 << (1 + (packed_info & 0x07))
|
||||
global_lut = get_lut(data, pos, lut_size)
|
||||
pos = pos + 3 * lut_size
|
||||
# Frames
|
||||
var repeat = -1
|
||||
var img = Image.new()
|
||||
var frame_number = 0
|
||||
var frame_delay = -1
|
||||
var frame_anim_packed_info = -1
|
||||
var frame_transparent_color = -1
|
||||
var animated_texture = AnimatedTexture.new()
|
||||
animated_texture.fps = 0
|
||||
animated_texture.flags = 0
|
||||
if filter:
|
||||
animated_texture.flags = animated_texture.flags | Texture.FLAG_FILTER
|
||||
if mipmaps:
|
||||
animated_texture.flags = animated_texture.flags | Texture.FLAG_MIPMAPS
|
||||
img.create(width, height, false, Image.FORMAT_RGBA8)
|
||||
while pos < data.size():
|
||||
if data[pos] == 0x21: # Extension block
|
||||
var ext_type = data[pos + 1]
|
||||
pos = pos + 2 # 21 xx ...
|
||||
match ext_type:
|
||||
0xF9: # Graphic extension
|
||||
var subblock = get_subblock(data, pos)
|
||||
frame_anim_packed_info = subblock[0]
|
||||
frame_delay = get_int(subblock, 1)
|
||||
frame_transparent_color = subblock[3]
|
||||
0xFF: # Application extension
|
||||
var subblock = get_subblock(data, pos)
|
||||
if subblock != null and subblock.get_string_from_ascii() == "NETSCAPE2.0":
|
||||
subblock = get_subblock(data, pos + 1 + subblock.size())
|
||||
repeat = get_int(subblock, 1)
|
||||
_: # Miscelaneous extension
|
||||
#print("extension ", data[pos + 1])
|
||||
pass
|
||||
var block_len = 0
|
||||
while data[pos + block_len] != 0:
|
||||
block_len = block_len + data[pos + block_len] + 1
|
||||
pos = pos + block_len + 1
|
||||
elif data[pos] == 0x2C: # Image data
|
||||
var img_left = get_int(data, pos + 1)
|
||||
var img_top = get_int(data, pos + 3)
|
||||
var img_width = get_int(data, pos + 5)
|
||||
var img_height = get_int(data, pos + 7)
|
||||
var img_packed_info = get_int(data, pos + 9)
|
||||
pos = pos + 10
|
||||
# Local color table
|
||||
var local_lut = global_lut
|
||||
if (img_packed_info & 0x80) != 0:
|
||||
var lut_size = 1 << (1 + (img_packed_info & 0x07))
|
||||
local_lut = get_lut(data, pos, lut_size)
|
||||
pos = pos + 3 * lut_size
|
||||
# Image data
|
||||
var min_code_size = data[pos]
|
||||
pos = pos + 1
|
||||
var colors = []
|
||||
for i in range(0, 1 << min_code_size):
|
||||
colors.append(i)
|
||||
var block = PoolByteArray()
|
||||
while data[pos] != 0:
|
||||
block.append_array(data.subarray(pos + 1, pos + data[pos]))
|
||||
pos = pos + data[pos] + 1
|
||||
pos = pos + 1
|
||||
var decompressed = lzw.decompress_lzw(block, min_code_size, colors)
|
||||
var disposal = (frame_anim_packed_info >> 2) & 7 # 1 = Keep, 2 = Clear
|
||||
var transparency = frame_anim_packed_info & 1
|
||||
if disposal == 2:
|
||||
if transparency == 0 or background_color_index != frame_transparent_color:
|
||||
img.fill(local_lut[background_color_index])
|
||||
else:
|
||||
img.fill(Color(0,0,0,0))
|
||||
var p = 0
|
||||
img.lock()
|
||||
for y in range(0, img_height):
|
||||
for x in range(0, img_width):
|
||||
var c = decompressed[p]
|
||||
if transparency == 0 or c != frame_transparent_color:
|
||||
img.set_pixel(img_left + x, img_top + y, local_lut[c])
|
||||
p = p + 1
|
||||
img.unlock()
|
||||
var frame = ImageTexture.new()
|
||||
frame.create_from_image(img, 0)
|
||||
animated_texture.set_frame_texture(frame_number, frame)
|
||||
animated_texture.set_frame_delay(frame_number, frame_delay / 100.0)
|
||||
frame_anim_packed_info = -1
|
||||
frame_transparent_color = -1
|
||||
frame_delay = -1
|
||||
frame_number = frame_number + 1
|
||||
elif data[pos] == 0x3B: # Trailer
|
||||
pos = pos + 1
|
||||
animated_texture.frames = frame_number
|
||||
return animated_texture
|
||||
|
||||
func get_subblock(data, pos):
|
||||
if data[pos] == 0:
|
||||
return null
|
||||
else:
|
||||
return data.subarray(pos + 1, pos + data[pos])
|
||||
|
||||
func get_lut(data, pos, size):
|
||||
var colors = Array()
|
||||
for i in range(0, size):
|
||||
colors.append(Color(data[pos + i * 3] / 255.0, data[pos + 1 + i * 3] / 255.0, data[pos + 2 + i * 3] / 255.0))
|
||||
return colors
|
||||
|
||||
func get_int(data, pos):
|
||||
return data[pos] + (data[pos + 1] << 8)
|
1
game/addons/gif-importer/HEAD
Normal file
1
game/addons/gif-importer/HEAD
Normal file
@ -0,0 +1 @@
|
||||
d8645b160b084fc4ea1f09c45ce09bcc15fa0bcb
|
21
game/addons/gif-importer/LICENSE
Normal file
21
game/addons/gif-importer/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Vincent Bousquet
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
18
game/addons/gif-importer/README.md
Normal file
18
game/addons/gif-importer/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Godot GIF importer
|
||||
Plugin for [Godot Engine](https://godotengine.org/) to import GIF image as AnimatedTexture
|
||||
|
||||
## Installation
|
||||
|
||||
Download or clone this repository and copy the contents of the `addons` folder to your own project's `addons` folder.
|
||||
|
||||
Then enable the plugin on the Project Settings. All your project's GIF assets should now appear in the Godot's asset browser as AnimatedTextures.
|
||||
|
||||
## Credits
|
||||
|
||||
This little script uses [godot-gif-lzw](https://github.com/jegor377/godot-gif-lzw) to decompress images, and is entirely based on the content of this [website](http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp).
|
||||
|
||||
The author of godot-gif-lzw also developped a [little godot script](https://github.com/jegor377/godot-gdgifexporter) to compress and export GIF images.
|
||||
|
||||
## License
|
||||
|
||||
[MIT License](LICENSE). Copyright (c) 2021 Vincent Bousquet.
|
21
game/addons/gif-importer/gif-lzw/LICENSE
Normal file
21
game/addons/gif-importer/gif-lzw/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Igor Santarek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
32
game/addons/gif-importer/gif-lzw/lsbbitpacker.gd
Normal file
32
game/addons/gif-importer/gif-lzw/lsbbitpacker.gd
Normal file
@ -0,0 +1,32 @@
|
||||
tool
|
||||
extends Reference
|
||||
|
||||
|
||||
class LSBLZWBitPacker:
|
||||
var bit_index: int = 0
|
||||
var stream: int = 0
|
||||
|
||||
var chunks: PoolByteArray = PoolByteArray([])
|
||||
|
||||
func put_byte():
|
||||
chunks.append(stream & 0xff)
|
||||
bit_index -= 8
|
||||
stream >>= 8
|
||||
|
||||
func write_bits(value: int, bits_count: int) -> void:
|
||||
value &= (1 << bits_count) - 1
|
||||
value <<= bit_index
|
||||
stream |= value
|
||||
bit_index += bits_count
|
||||
while bit_index >= 8:
|
||||
self.put_byte()
|
||||
|
||||
func pack() -> PoolByteArray:
|
||||
if bit_index != 0:
|
||||
self.put_byte()
|
||||
return chunks
|
||||
|
||||
func reset() -> void:
|
||||
bit_index = 0
|
||||
stream = 0
|
||||
chunks = PoolByteArray([])
|
42
game/addons/gif-importer/gif-lzw/lsbbitunpacker.gd
Normal file
42
game/addons/gif-importer/gif-lzw/lsbbitunpacker.gd
Normal file
@ -0,0 +1,42 @@
|
||||
tool
|
||||
extends Reference
|
||||
|
||||
|
||||
class LSBLZWBitUnpacker:
|
||||
var chunk_stream: PoolByteArray
|
||||
var bit_index: int = 0
|
||||
var byte: int
|
||||
var byte_index: int = 0
|
||||
|
||||
func _init(_chunk_stream: PoolByteArray):
|
||||
chunk_stream = _chunk_stream
|
||||
self.get_byte()
|
||||
|
||||
func get_bit(value: int, index: int) -> int:
|
||||
return (value >> index) & 1
|
||||
|
||||
func set_bit(value: int, index: int) -> int:
|
||||
return value | (1 << index)
|
||||
|
||||
func get_byte():
|
||||
byte = chunk_stream[byte_index]
|
||||
byte_index += 1
|
||||
bit_index = 0
|
||||
|
||||
func read_bits(bits_count: int) -> int:
|
||||
var result: int = 0
|
||||
var result_bit_index: int = 0
|
||||
|
||||
for _i in range(bits_count):
|
||||
if self.get_bit(byte, bit_index) == 1:
|
||||
result = self.set_bit(result, result_bit_index)
|
||||
result_bit_index += 1
|
||||
bit_index += 1
|
||||
|
||||
if bit_index == 8:
|
||||
self.get_byte()
|
||||
|
||||
return result
|
||||
|
||||
func remove_bits(bits_count: int) -> void:
|
||||
self.read_bits(bits_count)
|
211
game/addons/gif-importer/gif-lzw/lzw.gd
Normal file
211
game/addons/gif-importer/gif-lzw/lzw.gd
Normal file
@ -0,0 +1,211 @@
|
||||
tool
|
||||
extends Reference
|
||||
|
||||
var lsbbitpacker = preload("./lsbbitpacker.gd")
|
||||
var lsbbitunpacker = preload("./lsbbitunpacker.gd")
|
||||
|
||||
|
||||
class CodeEntry:
|
||||
var sequence: PoolByteArray
|
||||
var raw_array: Array
|
||||
|
||||
func _init(_sequence):
|
||||
raw_array = _sequence
|
||||
sequence = _sequence
|
||||
|
||||
func add(other):
|
||||
return CodeEntry.new(self.raw_array + other.raw_array)
|
||||
|
||||
func to_string():
|
||||
var result: String = ""
|
||||
for element in self.sequence:
|
||||
result += str(element) + ", "
|
||||
return result.substr(0, result.length() - 2)
|
||||
|
||||
|
||||
class CodeTable:
|
||||
var entries: Dictionary = {}
|
||||
var counter: int = 0
|
||||
var lookup: Dictionary = {}
|
||||
|
||||
func add(entry) -> int:
|
||||
self.entries[self.counter] = entry
|
||||
self.lookup[entry.raw_array] = self.counter
|
||||
counter += 1
|
||||
return counter
|
||||
|
||||
func find(entry) -> int:
|
||||
return self.lookup.get(entry.raw_array, -1)
|
||||
|
||||
func has(entry) -> bool:
|
||||
return self.find(entry) != -1
|
||||
|
||||
func get(index) -> CodeEntry:
|
||||
return self.entries.get(index, null)
|
||||
|
||||
func to_string() -> String:
|
||||
var result: String = "CodeTable:\n"
|
||||
for id in self.entries:
|
||||
result += str(id) + ": " + self.entries[id].to_string() + "\n"
|
||||
result += "Counter: " + str(self.counter) + "\n"
|
||||
return result
|
||||
|
||||
|
||||
func log2(value: float) -> float:
|
||||
return log(value) / log(2.0)
|
||||
|
||||
|
||||
func get_bits_number_for(value: int) -> int:
|
||||
if value == 0:
|
||||
return 1
|
||||
return int(ceil(log2(value + 1)))
|
||||
|
||||
|
||||
func initialize_color_code_table(colors: PoolByteArray) -> CodeTable:
|
||||
var result_code_table: CodeTable = CodeTable.new()
|
||||
for color_id in colors:
|
||||
# warning-ignore:return_value_discarded
|
||||
result_code_table.add(CodeEntry.new([color_id]))
|
||||
# move counter to the first available compression code index
|
||||
var last_color_index: int = colors.size() - 1
|
||||
var clear_code_index: int = pow(2, get_bits_number_for(last_color_index))
|
||||
result_code_table.counter = clear_code_index + 2
|
||||
return result_code_table
|
||||
|
||||
|
||||
# compression and decompression done with source:
|
||||
# http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp
|
||||
|
||||
|
||||
func compress_lzw(image: PoolByteArray, colors: PoolByteArray) -> Array:
|
||||
# Initialize code table
|
||||
var code_table: CodeTable = initialize_color_code_table(colors)
|
||||
# Clear Code index is 2**<code size>
|
||||
# <code size> is the amount of bits needed to write down all colors
|
||||
# from color table. We use last color index because we can write
|
||||
# all colors (for example 16 colors) with indexes from 0 to 15.
|
||||
# Number 15 is in binary 0b1111, so we'll need 4 bits to write all
|
||||
# colors down.
|
||||
var last_color_index: int = colors.size() - 1
|
||||
var clear_code_index: int = pow(2, get_bits_number_for(last_color_index))
|
||||
var index_stream: PoolByteArray = image
|
||||
var current_code_size: int = get_bits_number_for(clear_code_index)
|
||||
var binary_code_stream = lsbbitpacker.LSBLZWBitPacker.new()
|
||||
|
||||
# initialize with Clear Code
|
||||
binary_code_stream.write_bits(clear_code_index, current_code_size)
|
||||
|
||||
# Read first index from index stream.
|
||||
var index_buffer: CodeEntry = CodeEntry.new([index_stream[0]])
|
||||
var data_index: int = 1
|
||||
# <LOOP POINT>
|
||||
while data_index < index_stream.size():
|
||||
# Get the next index from the index stream.
|
||||
var k: CodeEntry = CodeEntry.new([index_stream[data_index]])
|
||||
data_index += 1
|
||||
# Is index buffer + k in our code table?
|
||||
var new_index_buffer: CodeEntry = index_buffer.add(k)
|
||||
if code_table.has(new_index_buffer): # if YES
|
||||
# Add k to the end of the index buffer
|
||||
index_buffer = new_index_buffer
|
||||
else: # if NO
|
||||
# Add a row for index buffer + k into our code table
|
||||
binary_code_stream.write_bits(code_table.find(index_buffer), current_code_size)
|
||||
|
||||
# We don't want to add new code to code table if we've exceeded 4095
|
||||
# index.
|
||||
var last_entry_index: int = code_table.counter - 1
|
||||
if last_entry_index != 4095:
|
||||
# Output the code for just the index buffer to our code stream
|
||||
# warning-ignore:return_value_discarded
|
||||
code_table.add(new_index_buffer)
|
||||
else:
|
||||
# if we exceeded 4095 index (code table is full), we should
|
||||
# output Clear Code and reset everything.
|
||||
binary_code_stream.write_bits(clear_code_index, current_code_size)
|
||||
code_table = initialize_color_code_table(colors)
|
||||
# get_bits_number_for(clear_code_index) is the same as
|
||||
# LZW code size + 1
|
||||
current_code_size = get_bits_number_for(clear_code_index)
|
||||
|
||||
# Detect when you have to save new codes in bigger bits boxes
|
||||
# change current code size when it happens because we want to save
|
||||
# flexible code sized codes
|
||||
var new_code_size_candidate: int = get_bits_number_for(code_table.counter - 1)
|
||||
if new_code_size_candidate > current_code_size:
|
||||
current_code_size = new_code_size_candidate
|
||||
|
||||
# Index buffer is set to k
|
||||
index_buffer = k
|
||||
# Output code for contents of index buffer
|
||||
binary_code_stream.write_bits(code_table.find(index_buffer), current_code_size)
|
||||
|
||||
# output end with End Of Information Code
|
||||
binary_code_stream.write_bits(clear_code_index + 1, current_code_size)
|
||||
|
||||
var min_code_size: int = get_bits_number_for(clear_code_index) - 1
|
||||
|
||||
return [binary_code_stream.pack(), min_code_size]
|
||||
|
||||
|
||||
# gdlint: ignore=max-line-length
|
||||
func decompress_lzw(code_stream_data: PoolByteArray, min_code_size: int, colors: PoolByteArray) -> PoolByteArray:
|
||||
var code_table: CodeTable = initialize_color_code_table(colors)
|
||||
var index_stream: PoolByteArray = PoolByteArray([])
|
||||
var binary_code_stream = lsbbitunpacker.LSBLZWBitUnpacker.new(code_stream_data)
|
||||
var current_code_size: int = min_code_size + 1
|
||||
var clear_code_index: int = pow(2, min_code_size)
|
||||
|
||||
# CODE is an index of code table, {CODE} is sequence inside
|
||||
# code table with index CODE. The same goes for PREVCODE.
|
||||
|
||||
# Remove first Clear Code from stream. We don't need it.
|
||||
binary_code_stream.remove_bits(current_code_size)
|
||||
|
||||
# let CODE be the first code in the code stream
|
||||
var code: int = binary_code_stream.read_bits(current_code_size)
|
||||
# output {CODE} to index stream
|
||||
index_stream.append_array(code_table.get(code).sequence)
|
||||
# set PREVCODE = CODE
|
||||
var prevcode: int = code
|
||||
# <LOOP POINT>
|
||||
while true:
|
||||
# let CODE be the next code in the code stream
|
||||
code = binary_code_stream.read_bits(current_code_size)
|
||||
# Detect Clear Code. When detected reset everything and get next code.
|
||||
if code == clear_code_index:
|
||||
code_table = initialize_color_code_table(colors)
|
||||
current_code_size = min_code_size + 1
|
||||
code = binary_code_stream.read_bits(current_code_size)
|
||||
elif code == clear_code_index + 1: # Stop when detected EOI Code.
|
||||
break
|
||||
# is CODE in the code table?
|
||||
var code_entry: CodeEntry = code_table.get(code)
|
||||
if code_entry != null: # if YES
|
||||
# output {CODE} to index stream
|
||||
index_stream.append_array(code_entry.sequence)
|
||||
# let k be the first index in {CODE}
|
||||
var k: CodeEntry = CodeEntry.new([code_entry.sequence[0]])
|
||||
# warning-ignore:return_value_discarded
|
||||
# add {PREVCODE} + k to the code table
|
||||
code_table.add(code_table.get(prevcode).add(k))
|
||||
# set PREVCODE = CODE
|
||||
prevcode = code
|
||||
else: # if NO
|
||||
# let k be the first index of {PREVCODE}
|
||||
var prevcode_entry: CodeEntry = code_table.get(prevcode)
|
||||
var k: CodeEntry = CodeEntry.new([prevcode_entry.sequence[0]])
|
||||
# output {PREVCODE} + k to index stream
|
||||
index_stream.append_array(prevcode_entry.add(k).sequence)
|
||||
# add {PREVCODE} + k to code table
|
||||
# warning-ignore:return_value_discarded
|
||||
code_table.add(prevcode_entry.add(k))
|
||||
# set PREVCODE = CODE
|
||||
prevcode = code
|
||||
|
||||
# Detect when we should increase current code size and increase it.
|
||||
var new_code_size_candidate: int = get_bits_number_for(code_table.counter)
|
||||
if new_code_size_candidate > current_code_size:
|
||||
current_code_size = new_code_size_candidate
|
||||
|
||||
return index_stream
|
7
game/addons/gif-importer/plugin.cfg
Normal file
7
game/addons/gif-importer/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
||||
[plugin]
|
||||
|
||||
name="GIF Importer"
|
||||
description="Editor plugin ti import GIF as AnimatedTexture"
|
||||
author="SoftMotionLabs"
|
||||
version="1.0"
|
||||
script="plugin.gd"
|
17
game/addons/gif-importer/plugin.gd
Normal file
17
game/addons/gif-importer/plugin.gd
Normal file
@ -0,0 +1,17 @@
|
||||
tool
|
||||
extends EditorPlugin
|
||||
|
||||
var import_sprite_frames_plugin
|
||||
var import_animated_texture_plugin
|
||||
|
||||
func _enter_tree():
|
||||
import_sprite_frames_plugin = preload("GIF2SpriteFramesPlugin.gd").new()
|
||||
add_import_plugin(import_sprite_frames_plugin)
|
||||
import_animated_texture_plugin = preload("GIF2AnimatedTexturePlugin.gd").new()
|
||||
add_import_plugin(import_animated_texture_plugin)
|
||||
|
||||
func _exit_tree():
|
||||
remove_import_plugin(import_sprite_frames_plugin)
|
||||
import_sprite_frames_plugin = null
|
||||
remove_import_plugin(import_animated_texture_plugin)
|
||||
import_animated_texture_plugin = null
|
@ -8,6 +8,16 @@
|
||||
|
||||
config_version=4
|
||||
|
||||
_global_script_classes=[ {
|
||||
"base": "Reference",
|
||||
"class": @"GifReader",
|
||||
"language": @"GDScript",
|
||||
"path": "res://addons/gif-importer/GIFReader.gd"
|
||||
} ]
|
||||
_global_script_class_icons={
|
||||
@"GifReader": ""
|
||||
}
|
||||
|
||||
[application]
|
||||
|
||||
config/name="MemR"
|
||||
@ -23,6 +33,10 @@ config/target_folder=""
|
||||
window/size/width=600
|
||||
window/handheld/orientation="sensor"
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PoolStringArray( "res://addons/gif-importer/plugin.cfg" )
|
||||
|
||||
[physics]
|
||||
|
||||
common/enable_pause_aware_picking=true
|
||||
|
15
game/sort/GifLoader.gd
Normal file
15
game/sort/GifLoader.gd
Normal file
@ -0,0 +1,15 @@
|
||||
extends TextureRect
|
||||
|
||||
func load_gif(path : String) -> bool:
|
||||
var reader = GifReader.new()
|
||||
reader.filter = false
|
||||
reader.mipmaps = false
|
||||
|
||||
var tex : AnimatedTexture = reader.read(path)
|
||||
|
||||
if tex == null:
|
||||
return false
|
||||
|
||||
texture = tex
|
||||
|
||||
return true
|
8
game/sort/GifLoader.tscn
Normal file
8
game/sort/GifLoader.tscn
Normal file
@ -0,0 +1,8 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://sort/GifLoader.gd" type="Script" id=1]
|
||||
|
||||
[node name="GifLoader" type="TextureRect"]
|
||||
margin_right = 14.0
|
||||
margin_bottom = 14.0
|
||||
script = ExtResource( 1 )
|
@ -6,6 +6,7 @@ var target_folder : String
|
||||
var shell_script_name : String = "apply.sh"
|
||||
|
||||
var _texture_rect : TextureRect
|
||||
var _gif_rect : TextureRect
|
||||
var _error_label : Label
|
||||
var _categories_ob : OptionButton
|
||||
var _sub_categories_ob : OptionButton
|
||||
@ -104,18 +105,27 @@ func next_image() -> void:
|
||||
if _current_file_index >= _current_folder_files.size():
|
||||
next_folder()
|
||||
return
|
||||
|
||||
var img : Image = Image.new()
|
||||
if img.load(_current_folder_files[_current_file_index]) != OK:
|
||||
_error_label.text = "Error loading file: " + _current_folder_files[_current_file_index]
|
||||
_error_label.show()
|
||||
_texture_rect.hide()
|
||||
|
||||
return
|
||||
|
||||
|
||||
_error_label.hide()
|
||||
_texture_rect.show()
|
||||
_texture_rect.texture.create_from_image(img, 0)
|
||||
_texture_rect.hide()
|
||||
_gif_rect.hide()
|
||||
|
||||
var curr_file : String = _current_folder_files[_current_file_index]
|
||||
|
||||
if curr_file.get_extension().to_lower() != "gif":
|
||||
var img : Image = Image.new()
|
||||
if img.load(curr_file) != OK:
|
||||
_error_label.text = "Error loading file: " + curr_file
|
||||
_error_label.show()
|
||||
else:
|
||||
_texture_rect.show()
|
||||
_texture_rect.texture.create_from_image(img, 0)
|
||||
else:
|
||||
if !_gif_rect.load_gif(curr_file):
|
||||
_error_label.text = "Error loading file: " + curr_file
|
||||
_error_label.show()
|
||||
else:
|
||||
_gif_rect.show()
|
||||
|
||||
func evaluate_folders() -> void:
|
||||
_folders.clear()
|
||||
@ -284,10 +294,11 @@ func _on_NewSubCategoryPopup_confirmed() -> void:
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_READY:
|
||||
_texture_rect = get_node("ScrollContainer/VBoxContainer/TextureRect") as TextureRect
|
||||
_gif_rect = get_node("ScrollContainer/VBoxContainer/GifRect") as TextureRect
|
||||
_error_label = get_node("ScrollContainer/VBoxContainer/ErrorLabel") as Label
|
||||
_categories_ob = get_node("Categories/Categories") as OptionButton
|
||||
_sub_categories_ob = get_node("SubCategoies/SubCategoies") as OptionButton
|
||||
|
||||
|
||||
_categories_popup = get_node("Control/NewCategoryPopup") as ConfirmationDialog
|
||||
_categories_popup_line_edit = get_node("Control/NewCategoryPopup/VBoxContainer/LineEdit") as LineEdit
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://sort/Sort.gd" type="Script" id=1]
|
||||
[ext_resource path="res://sort/GifLoader.tscn" type="PackedScene" id=2]
|
||||
|
||||
[node name="Sort" type="VBoxContainer"]
|
||||
margin_right = 1024.0
|
||||
@ -17,6 +18,12 @@ size_flags_vertical = 3
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/VBoxContainer"]
|
||||
|
||||
[node name="GifRect" parent="ScrollContainer/VBoxContainer" instance=ExtResource( 2 )]
|
||||
visible = false
|
||||
margin_top = 4.0
|
||||
margin_right = 0.0
|
||||
margin_bottom = 4.0
|
||||
|
||||
[node name="ErrorLabel" type="Label" parent="ScrollContainer/VBoxContainer"]
|
||||
visible = false
|
||||
margin_top = 4.0
|
||||
|
Loading…
Reference in New Issue
Block a user