Files
hcie-rust-v3.05/_tools/generate_inner_glow_kra_set.py
T

161 lines
7.2 KiB
Python
Raw Normal View History

2026-07-09 02:59:53 +03:00
#!/usr/bin/env python3
import os
import struct
import zipfile
import xml.etree.ElementTree as ET
# Offsets for InnerGlow style keys inside sultan.kra's Unnamed/annotations/layerstyles.asl:
# Md (blend mode): 1510
# Clr (color) RGBC double offsets:
# R double: 1556
# G double: 1576
# B double: 1596
# Opct (opacity): 1620
# Ckmt (choke/spread): 1672
# blur (size): 1696
BASE_KRA = "_images/_psd_stil_test/sultan_test/sultan.kra"
OUT_DIR = "_tmp/inner_glow_kra_set"
def get_double_bytes(val):
return struct.pack(">d", float(val))
def main():
bases = [
("/mnt/extra/00_PROJECTS/hcie-rust-v4/_tmp/kra_tunes_simple/black_rect_simple.kra", "/mnt/extra/00_PROJECTS/hcie-rust-v4/_tmp/kra_tunes_simple_black/inner_glow_kra_set"),
("/mnt/extra/00_PROJECTS/hcie-rust-v4/_tmp/kra_tunes_simple/red_rect_simple.kra", "/mnt/extra/00_PROJECTS/hcie-rust-v4/_tmp/kra_tunes_simple_red/inner_glow_kra_set")
]
for base_kra, out_dir in bases:
if not os.path.exists(base_kra):
print(f"Error: Base Krita file {base_kra} not found.")
continue
os.makedirs(out_dir, exist_ok=True)
# Read base archive and cache entries in memory
base_zip = zipfile.ZipFile(base_kra, 'r')
files_cache = {}
for name in base_zip.namelist():
files_cache[name] = base_zip.read(name)
asl_path_in_zip = "Unnamed/annotations/layerstyles.asl"
if asl_path_in_zip in files_cache:
asl_data = bytearray(files_cache[asl_path_in_zip])
else:
fallback_paths = [
"/mnt/extra/00_PROJECTS/hcie-rust-v4/_images/kra_style_test/sultan.kra",
"/mnt/extra/00_PROJECTS/hcie-rust-v4/_images/_psd_stil_test/sultan_test/sultan.kra",
"_images/kra_style_test/sultan.kra",
"_images/_psd_stil_test/sultan_test/sultan.kra"
]
sultan_zip = None
for path in fallback_paths:
if os.path.exists(path):
sultan_zip = zipfile.ZipFile(path, 'r')
break
if sultan_zip is None:
print("Error: Could not find sultan.kra fallback to load layerstyles.asl")
return
asl_data = bytearray(sultan_zip.read(asl_path_in_zip))
sultan_zip.close()
xml_data = files_cache["maindoc.xml"]
# Set visibility in XML: both target layer and Background should be visible
# Also ensure the layerstyle attribute is attached to the target layer
ET.register_namespace('', "http://www.calligra.org/DTD/krita")
root = ET.fromstring(xml_data)
for layer in root.findall('.//{http://www.calligra.org/DTD/krita}layer'):
if layer.attrib.get('name') in ['Layer 3', 'Red Rectangle']:
layer.set('visible', '1')
layer.set('layerstyle', '{117f8514-88e4-4553-bccb-44a87b6b9182}')
elif layer.attrib.get('name') == 'Background':
layer.set('visible', '1')
else:
layer.set('visible', '0')
xml_patched = b'<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE DOC PUBLIC \'-//KDE//DTD krita 2.0//EN\' \'http://www.calligra.org/DTD/krita-2.0.dtd\'>\n' + ET.tostring(root, encoding='utf-8')
# Parameter lists
chokes = [0.0, 20.0]
sizes = [10.0, 30.0]
opacities = [0.75, 1.0]
blend_modes = [
(b"Nrml", "normal"),
(b"Scrn", "screen"),
(b"LDdg", "lineardodge"),
]
glow_colors = [
([220, 30, 30, 255], "red"),
([30, 180, 30, 255], "green"),
]
total = len(chokes) * len(sizes) * len(opacities) * len(blend_modes) * len(glow_colors)
print(f"Generating {total} styled KRA files for Inner Glow from {os.path.basename(base_kra)}...")
def patch_untf(asl, start_idx, key, value):
idx = asl.find(key, start_idx)
if idx != -1 and asl[idx+4:idx+8] == b'UntF':
asl[idx+12:idx+20] = get_double_bytes(value)
def patch_enum(asl, start_idx, key, value):
idx = asl.find(key, start_idx)
if idx != -1 and asl[idx+4:idx+8] == b'enum':
asl[idx+20:idx+24] = value
def patch_doub(asl, start_idx, key, value):
idx = asl.find(key, start_idx)
if idx != -1 and asl[idx+4:idx+8] == b'doub':
asl[idx+8:idx+16] = get_double_bytes(value)
count = 0
for choke in chokes:
for size in sizes:
for opacity in opacities:
for bm_bytes, bm_name in blend_modes:
for color, color_name in glow_colors:
count += 1
filename = f"ig_sp{int(choke)}_s{int(size)}_op{int(opacity*100)}_n0_{bm_name}_{color_name}.kra"
kra_path = os.path.join(out_dir, filename)
# Clone and patch ASL
patched_asl = bytearray(asl_data)
start_idx = patched_asl.find(b'IrGl')
patch_untf(patched_asl, start_idx, b'Opct', opacity * 100.0)
patch_untf(patched_asl, start_idx, b'Ckmt', choke)
patch_untf(patched_asl, start_idx, b'blur', size)
patch_enum(patched_asl, start_idx, b'Md ', bm_bytes)
# Color: find Clr after start_idx, then Rd , Grn , Bl
clr_idx = patched_asl.find(b'Clr ', start_idx)
patch_doub(patched_asl, clr_idx, b'Rd ', color[0])
patch_doub(patched_asl, clr_idx, b'Grn ', color[1])
patch_doub(patched_asl, clr_idx, b'Bl ', color[2])
# Write new ZIP archive
with zipfile.ZipFile(kra_path, 'w') as out_zip:
has_asl = False
for item in base_zip.infolist():
if item.filename == "Unnamed/annotations/layerstyles.asl":
out_zip.writestr(item, patched_asl)
has_asl = True
elif item.filename == "maindoc.xml":
out_zip.writestr(item, xml_patched)
elif item.filename == "Unnamed/layers/layer6.defaultpixel":
out_zip.writestr(item, b"\x80\x80\x80\xff") # 50% gray background
else:
out_zip.writestr(item, files_cache[item.filename])
if not has_asl:
out_zip.writestr("Unnamed/annotations/layerstyles.asl", patched_asl)
if count % 20 == 0:
print(f" [{count}/{total}] generated...")
base_zip.close()
print(f"Successfully generated {count} files under {out_dir}.")
if __name__ == "__main__":
main()