1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 17:19:50 +00:00
Pixelorama/addons/aimg_io/apng_stream.gd
20kdc 4658b1cfb6
APNG Loading (#797)
* APNG loader: Import addon to take over APNG handling

* APNG loader: Transition code to using the AImgIO addon

* APNG loader: Can now open APNGs.

* AImgIO: Update to fix bugs

* APNG loader: HTML5

* APNG loader: gdformat/gdlint addon

* APNG loader: OpenSave formatting fix

* APNG Loader: Add ignore line to OpenSave because it's too big

* Fix GIFAnimationExporter bug caused by the switch to the addon
2022-12-23 20:08:46 +02:00

96 lines
2.7 KiB
GDScript

tool
class_name AImgIOAPNGStream
extends Reference
# APNG IO context. To be clear, this is still effectively magic.
# Quite critical we preload this. Preloading creates static variables.
# (Which GDScript doesn't really have, but we need since we have no tree access)
var crc32: AImgIOCRC32 = preload("apng_crc32.tres")
var chunk_type: String
var chunk_data: PoolByteArray
# The reason this must be a StreamPeerBuffer is simple:
# 1. We need to support in-memory IO for HTML5 to really work
# 2. We need get_available_bytes to be completely accurate in all* cases
# * A >2GB file doesn't count. Godot limitations.
# because get_32 can return arbitrary nonsense on error.
# It might have been worth trying something else if StreamPeerFile was a thing.
# Though even then that's betting the weirdness of corrupt files against the
# benefits of using less memory.
var _target: StreamPeerBuffer
func _init(t: PoolByteArray = PoolByteArray()):
crc32.ensure_ready()
_target = StreamPeerBuffer.new()
_target.big_endian = true
_target.data_array = t
# Reading
# Reads the magic number. Returns the method of failure or null for success.
func read_magic():
if _target.get_available_bytes() < 8:
return "Not enough bytes in magic number"
var a := _target.get_32() & 0xFFFFFFFF
if a != 0x89504E47:
return "Magic number start not 0x89504E47, but " + str(a)
a = _target.get_32() & 0xFFFFFFFF
if a != 0x0D0A1A0A:
return "Magic number end not 0x0D0A1A0A, but " + str(a)
return null
# Reads a chunk into chunk_type and chunk_data. Returns an error code.
func read_chunk() -> int:
if _target.get_available_bytes() < 8:
return ERR_FILE_EOF
var dlen := _target.get_32()
var a := char(_target.get_8())
var b := char(_target.get_8())
var c := char(_target.get_8())
var d := char(_target.get_8())
chunk_type = a + b + c + d
if _target.get_available_bytes() >= dlen:
chunk_data = _target.get_data(dlen)[1]
else:
return ERR_FILE_EOF
# we don't care what this reads anyway, so don't bother checking it
_target.get_32()
return OK
# Writing
# Writes the PNG magic number.
func write_magic():
_target.put_32(0x89504E47)
_target.put_32(0x0D0A1A0A)
# Creates a big-endian StreamPeerBuffer for writing PNG data into.
func start_chunk() -> StreamPeerBuffer:
var result := StreamPeerBuffer.new()
result.big_endian = true
return result
# Writes a PNG chunk.
func write_chunk(type: String, data: PoolByteArray):
_target.put_32(len(data))
var at := type.to_ascii()
_target.put_data(at)
_target.put_data(data)
var crc := crc32.update(crc32.mask, at)
crc = crc32.end(crc32.update(crc, data))
_target.put_32(crc)
# Returns the data_array of the stream (to be used when you're done writing the file)
func finish() -> PoolByteArray:
return _target.data_array