MemR/game/addons/gif-importer/GIFReader.gd

151 lines
4.5 KiB
GDScript

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'
var sttr : String = ""
for i in range(6):
sttr += char(data[i])
if (sttr != "GIF89a"):
print("Can only open GIF89a!")
return null
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):
if p >= decompressed.size():
break
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)