mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-01 10:05:34 +00:00
Build script
This commit is contained in:
parent
30d0365ae1
commit
1ed08d6a58
12 changed files with 505 additions and 5 deletions
274
CreateIcon.gd
Normal file
274
CreateIcon.gd
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
class_name CreateIcon
|
||||
extends SceneTree
|
||||
|
||||
var error_callable: Callable
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
var arguments = OS.get_cmdline_args()
|
||||
if arguments.size() != 4 and arguments.size() != 9:
|
||||
print(
|
||||
"Usage:\n godot -s CreateIcon.gd name <file>...\n",
|
||||
"\n",
|
||||
"Creates uncompressed windows ico file.\n",
|
||||
"Add --headless to hide Godot console.\n",
|
||||
"\n",
|
||||
"Arguments:\n",
|
||||
" godot path to Godot 4 beta2+ executable\n",
|
||||
" name path to created icon\n",
|
||||
" <file> provide one or six files. If one provided it will be scaled for all\n",
|
||||
" icon resolutions. Multiple files should be 16x16, 32x32, 48x48, 64x64,\n",
|
||||
" 128x128\n and 256x256 pixels big."
|
||||
)
|
||||
quit()
|
||||
return
|
||||
var images := []
|
||||
if arguments.size() == 9:
|
||||
var names := [arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8]]
|
||||
var check_names := {}
|
||||
for name in names:
|
||||
if check_names.has(name):
|
||||
printerr("File ", name, " was added more than once")
|
||||
return
|
||||
check_names[name] = true
|
||||
images = load_images(names)
|
||||
else:
|
||||
images = prepare_images(arguments[3])
|
||||
if not images.is_empty():
|
||||
save_icon(arguments[2], images)
|
||||
quit()
|
||||
|
||||
|
||||
func load_images(paths: PackedStringArray) -> Array:
|
||||
var images := []
|
||||
for path in paths:
|
||||
var image := Image.new()
|
||||
var error = image.load(path)
|
||||
if error:
|
||||
print_error(str("Could not load image: ", path))
|
||||
return []
|
||||
image.convert(Image.FORMAT_RGBA8)
|
||||
images.append(image)
|
||||
images.sort_custom(sort_images_by_size)
|
||||
var index := 0
|
||||
for size in [16, 32, 48, 64, 128, 256]:
|
||||
var image: Image = images[index]
|
||||
if image.get_width() != size:
|
||||
print_error(str("Image has incorrect width: ", image.get_width(), " expected: ", size))
|
||||
return []
|
||||
if image.get_height() != size:
|
||||
print_error(str("Image has incorrect height: ", image.get_height(), " expected: ", size))
|
||||
return []
|
||||
index += 1
|
||||
return images
|
||||
|
||||
|
||||
func prepare_images(path: String) -> Array:
|
||||
var images := []
|
||||
for size in [16, 32, 48, 64, 128, 256]:
|
||||
var image := Image.new()
|
||||
var error = image.load(path)
|
||||
if error:
|
||||
print_error(str("Could not load image: ", path))
|
||||
return []
|
||||
image.convert(Image.FORMAT_RGBA8)
|
||||
image.resize(size, size)
|
||||
images.append(image)
|
||||
return images
|
||||
|
||||
|
||||
func save_icon(destination_path: String, images: Array) -> void:
|
||||
var file = FileAccess.open(destination_path, FileAccess.WRITE)
|
||||
if not file:
|
||||
print_error(str("Could not open file for writing!\n", FileAccess.get_open_error()))
|
||||
return
|
||||
var icon_creator := IconCreator.new()
|
||||
file.store_buffer(icon_creator.generate_icon(images))
|
||||
|
||||
|
||||
func print_error(error_message: String) -> void:
|
||||
printerr(error_message)
|
||||
if error_callable:
|
||||
error_callable.call(error_message)
|
||||
|
||||
|
||||
static func sort_images_by_size(a: Image, b: Image) -> bool:
|
||||
return a.get_width() < b.get_width()
|
||||
|
||||
|
||||
class IconCreator:
|
||||
const ADLER_MOD := 65521
|
||||
const ZLIB_BLOCK_SIZE := 16384
|
||||
const CRC_TABLE_SIZE := 256
|
||||
const ICON_ENTRY_SIZE := 16
|
||||
var PNG_SIGNATURE := PackedByteArray([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0a])
|
||||
var IHDR_SIGNATURE := PackedByteArray([0x49, 0x48, 0x44, 0x52])
|
||||
var IDAT_SIGNATURE := PackedByteArray([0x49, 0x44, 0x41, 0x54])
|
||||
var IEND_SIGNATURE := PackedByteArray([0x49, 0x45, 0x4e, 0x44])
|
||||
|
||||
var crc_table: Array
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
crc_table = generate_crc_table()
|
||||
|
||||
|
||||
func generate_icon(images: Array) -> PackedByteArray:
|
||||
var result := PackedByteArray()
|
||||
result.append_array(generate_icon_header(images.size()))
|
||||
var offset := result.size() + images.size() * ICON_ENTRY_SIZE
|
||||
var pngs := []
|
||||
for image in images:
|
||||
assert(image.get_format() == Image.FORMAT_RGBA8)
|
||||
var png := generate_png(image)
|
||||
pngs.append(png)
|
||||
var icon_entry := generate_icon_entry(image, png.size(), offset)
|
||||
result.append_array(icon_entry)
|
||||
offset += png.size()
|
||||
for png in pngs:
|
||||
result.append_array(png)
|
||||
return result
|
||||
|
||||
|
||||
func generate_icon_header(size: int) -> PackedByteArray:
|
||||
var result := PackedByteArray()
|
||||
result.append_array(lsb_first(0x0, 2)) # reserved
|
||||
result.append_array(lsb_first(0x1, 2)) # icon type
|
||||
result.append_array(lsb_first(size, 2)) # image count
|
||||
return result
|
||||
|
||||
|
||||
func generate_icon_entry(image: Image, size: int, offset: int) -> PackedByteArray:
|
||||
var result := PackedByteArray()
|
||||
result.append(image.get_width()) # width
|
||||
result.append(image.get_height()) # height
|
||||
result.append(0x0) # size of color palette
|
||||
result.append(0x0) # reserved
|
||||
result.append_array(lsb_first(0, 2)) # no color planes
|
||||
result.append_array(lsb_first(32, 2)) # bits per pixel
|
||||
result.append_array(lsb_first(size)) # size of embedded png
|
||||
result.append_array(lsb_first(offset))
|
||||
return result
|
||||
|
||||
|
||||
func generate_png(image: Image) -> PackedByteArray:
|
||||
var result := PackedByteArray()
|
||||
var header_chunk := generate_header_chunk(image.get_width(), image.get_height())
|
||||
var data_chunk := generate_data_chunk(image)
|
||||
var end_chunk := generate_end_chunk()
|
||||
result.append_array(PNG_SIGNATURE)
|
||||
result.append_array(generate_chunk(header_chunk))
|
||||
result.append_array(generate_chunk(data_chunk))
|
||||
result.append_array(generate_chunk(end_chunk))
|
||||
return result
|
||||
|
||||
|
||||
func generate_chunk(chunk: PackedByteArray) -> PackedByteArray:
|
||||
var result := PackedByteArray()
|
||||
result.append_array(msb_first(chunk.size() - 4))
|
||||
result.append_array(chunk)
|
||||
result.append_array(msb_first(crc(chunk)))
|
||||
return result
|
||||
|
||||
|
||||
func generate_header_chunk(width: int, height: int) -> PackedByteArray:
|
||||
var result = PackedByteArray()
|
||||
result.append_array(IHDR_SIGNATURE)
|
||||
result.append_array(msb_first(width))
|
||||
result.append_array(msb_first(height))
|
||||
result.append(0x8) # bit depth
|
||||
result.append(0x6) # color type 32bit RGBA
|
||||
result.append(0x0) # compression method
|
||||
result.append(0x0) # filter method
|
||||
result.append(0x0) # interlace method
|
||||
return result
|
||||
|
||||
|
||||
func generate_data_chunk(image: Image) -> PackedByteArray:
|
||||
@warning_ignore("shadowed_variable")
|
||||
var filtered_pixels := filtered_pixels(image.get_width(), image.get_height(), image.get_data())
|
||||
@warning_ignore("integer_division")
|
||||
var zlib_block_count := filtered_pixels.size() / ZLIB_BLOCK_SIZE + (1 if filtered_pixels.size() % ZLIB_BLOCK_SIZE else 0)
|
||||
var result := PackedByteArray()
|
||||
result.append_array(IDAT_SIGNATURE)
|
||||
result.append(0x78) # CMF
|
||||
result.append(0x1) # FLG
|
||||
for i in range(zlib_block_count):
|
||||
var last_block := i == zlib_block_count - 1
|
||||
result.append(0x1 if last_block else 0x0)
|
||||
@warning_ignore("shadowed_variable")
|
||||
var block_size := filtered_pixels.size() % ZLIB_BLOCK_SIZE if last_block else ZLIB_BLOCK_SIZE
|
||||
result.append_array(block_size(block_size))
|
||||
for b in range(block_size):
|
||||
result.append(filtered_pixels[i * ZLIB_BLOCK_SIZE + b])
|
||||
result.append_array(msb_first(adler(filtered_pixels)))
|
||||
return result
|
||||
|
||||
|
||||
func generate_end_chunk() -> PackedByteArray:
|
||||
return IEND_SIGNATURE
|
||||
|
||||
|
||||
func filtered_pixels(width: int, height: int, pixels: PackedByteArray) -> PackedByteArray:
|
||||
var result = PackedByteArray()
|
||||
for row in range(height):
|
||||
result.append(0x0)
|
||||
for column in range(width * 4):
|
||||
result.append(pixels[row * width * 4 + column])
|
||||
return result
|
||||
|
||||
|
||||
func generate_crc_table() -> Array:
|
||||
var result = []
|
||||
var c: int
|
||||
for n in range(CRC_TABLE_SIZE):
|
||||
c = n
|
||||
for _i in range(8):
|
||||
if (c & 1) != 0:
|
||||
c = 0xedb88320 ^ (c >> 1)
|
||||
else:
|
||||
c = c >> 1
|
||||
result.append(c)
|
||||
return result
|
||||
|
||||
|
||||
func crc(bytes: PackedByteArray) -> int:
|
||||
var c := 0xffffffff
|
||||
for i in range(bytes.size()):
|
||||
c = crc_table[(c ^ bytes[i]) & 0xff] ^ (c >> 8)
|
||||
return c ^ 0xffffffff
|
||||
|
||||
|
||||
func adler(bytes: PackedByteArray) -> int:
|
||||
var a := 1
|
||||
var b := 0
|
||||
for byte in bytes:
|
||||
a = (a + byte) % ADLER_MOD
|
||||
b = (a + b) % ADLER_MOD
|
||||
return b << 16 | a
|
||||
|
||||
|
||||
func msb_first(i: int) -> PackedByteArray:
|
||||
var result := PackedByteArray()
|
||||
result.append((i >> 24) & 0xff)
|
||||
result.append((i >> 16) & 0xff)
|
||||
result.append((i >> 8) & 0xff)
|
||||
result.append(i & 0xff)
|
||||
return result
|
||||
|
||||
|
||||
func lsb_first(i: int, size = 4) -> PackedByteArray:
|
||||
var result := PackedByteArray()
|
||||
for _s in range(size):
|
||||
result.append(i & 0xff)
|
||||
i = i >> 8
|
||||
return result
|
||||
|
||||
|
||||
func block_size(i: int) -> PackedByteArray:
|
||||
var result := PackedByteArray()
|
||||
result.append(i & 0xff)
|
||||
result.append((i >> 8) & 0xff)
|
||||
result.append((i & 0xff) ^ 0xff)
|
||||
result.append(((i >> 8) & 0xff) ^ 0xff)
|
||||
return result
|
||||
Loading…
Add table
Add a link
Reference in a new issue