Files
hcie-rust-v3.05/hcie-io/tools/generate_fx_tests_v3.py
T
2026-07-09 02:59:53 +03:00

175 lines
7.0 KiB
Python

#!/usr/bin/env python3
"""
PSD FX Test Generator v3 - lrFX (legacy) based
Uses ImageMagick for correct PSD base, patches in lrFX effects.
"""
import struct, os, subprocess
from PIL import Image
OUTPUT_DIR = "/home/hc/Pictures/_psd_stil_test"
W, H = 512, 512
PREFIX = "fx_"
def create_l_shape():
img = Image.new('RGBA', (W, H), (0, 0, 0, 0))
for y in range(H):
for x in range(W):
if (100 <= x < 380) and (180 <= y < 230):
img.putpixel((x, y), (70, 130, 180, 255))
elif (140 <= x < 190) and (180 <= y < 400):
img.putpixel((x, y), (70, 130, 180, 255))
return img
def create_diamond():
img = Image.new('RGBA', (W, H), (0, 0, 0, 0))
cx, cy, rx, ry = 256, 250, 160, 120
for y in range(H):
for x in range(W):
if abs(x - cx) / rx + abs(y - cy) / ry <= 1.0:
img.putpixel((x, y), (180, 80, 60, 255))
return img
def create_triangle():
img = Image.new('RGBA', (W, H), (0, 0, 0, 0))
x0, y0, x1, y1, x2, y2 = 200, 140, 380, 320, 150, 350
def sign(px, py, ax, ay, bx, by):
return (px - bx) * (ay - by) - (ax - bx) * (py - by)
for y in range(H):
for x in range(W):
d1 = sign(x, y, x0, y0, x1, y1)
d2 = sign(x, y, x1, y1, x2, y2)
d3 = sign(x, y, x2, y2, x0, y0)
if not ((d1 < 0 or d2 < 0 or d3 < 0) and (d1 > 0 or d2 > 0 or d3 > 0)):
img.putpixel((x, y), (60, 160, 80, 255))
return img
# === lrFX builders ===
def psd_color(r, g, b):
return struct.pack('>H', 0) + struct.pack('>HHHH', r*256, g*256, b*256, 0)
def common_state():
return struct.pack('>I', 7) + struct.pack('>I', 0) + b'\x01\x00\x00'
def lrfx_drop_shadow(color=(0,0,0), opacity=0.75, angle=135, distance=15, size=10):
d = struct.pack('>I', 41) + struct.pack('>I', 0)
d += struct.pack('>i', size) + struct.pack('>i', 0) + struct.pack('>i', int(angle*65536)) + struct.pack('>i', int(distance*65536))
d += psd_color(*color) + b'8BIMnorm' + b'\x01\x01' + bytes([int(opacity*255)])
return b'8BIMdsdw' + d
def lrfx_inner_shadow(color=(0,0,0), opacity=0.75, angle=135, distance=10, size=8):
d = struct.pack('>I', 41) + struct.pack('>I', 0)
d += struct.pack('>i', size) + struct.pack('>i', 0) + struct.pack('>i', int(angle*65536)) + struct.pack('>i', int(distance*65536))
d += psd_color(*color) + b'8BIMnorm' + b'\x01\x01' + bytes([int(opacity*255)])
return b'8BIMisdw' + d
def lrfx_outer_glow(color=(255,200,0), opacity=0.75, size=20):
d = struct.pack('>I', 32) + struct.pack('>I', 0)
d += struct.pack('>i', size) + struct.pack('>i', 0) + psd_color(*color)
d += b'8BIMscrn' + b'\x01' + bytes([int(opacity*255)])
return b'8BIMoglw' + d
def lrfx_inner_glow(color=(255,100,0), opacity=0.80, size=15):
d = struct.pack('>I', 33) + struct.pack('>I', 0)
d += struct.pack('>i', size) + struct.pack('>i', 0) + psd_color(*color)
d += b'8BIMscrn' + b'\x01' + bytes([int(opacity*255)]) + b'\x00'
return b'8BIMiglw' + d
def lrfx_bevel(style_val, angle=120, depth=200, size=10):
d = struct.pack('>I', 58) + struct.pack('>I', 0)
d += struct.pack('>i', int(angle*65536)) + struct.pack('>i', depth) + struct.pack('>i', size)
d += b'8BIMscrn' + b'8BIMmul ' # hl_blend, sh_blend
d += psd_color(255,255,255) + psd_color(0,0,0) # hl_color, sh_color
d += bytes([style_val, int(0.75*255), int(0.75*255), 1, 1, 0])
return b'8BIMbevl' + d
def lrfx_color_overlay(color=(220,50,50), opacity=0.80):
d = struct.pack('>I', 34) + struct.pack('>I', 2)
d += b'8BIMnorm' + psd_color(*color)
d += bytes([int(opacity*255), 1]) + psd_color(*color)
return b'8BIMsofi' + d
def build_lrfx_block(effects):
buf = bytearray()
buf.extend(struct.pack('>I', 0)) # version
buf.extend(struct.pack('>H', len(effects)))
for e in effects:
buf.extend(e)
return bytes(buf)
# === PSD patcher ===
def patch_psd_with_lrfx(base_psd_path, out_path, lrfx_data):
data = open(base_psd_path, 'rb').read()
pos = 26
cm = struct.unpack('>I', data[pos:pos+4])[0]; pos += 4 + cm
ir = struct.unpack('>I', data[pos:pos+4])[0]; pos += 4 + ir
lami_pos = pos
lami_len = struct.unpack('>I', data[pos:pos+4])[0]; pos += 4
li_len_pos = pos
li_len = struct.unpack('>I', data[pos:pos+4])[0]; pos += 4
lc = struct.unpack('>h', data[pos:pos+2])[0]; pos += 2
pos += 16
ch_cnt = struct.unpack('>H', data[pos:pos+2])[0]; pos += 2
for c in range(ch_cnt): pos += 6
pos += 8; pos += 4
extra_len_pos = pos
extra_len = struct.unpack('>I', data[pos:pos+4])[0]; pos += 4
extra_start = pos
tagged = bytearray(b'8BIMlrFX')
tagged.extend(struct.pack('>I', len(lrfx_data)))
tagged.extend(lrfx_data)
if len(tagged) % 2: tagged.append(0)
new_extra_len = extra_len + len(tagged)
new_data = bytearray()
new_data.extend(data[:extra_start+extra_len])
new_data.extend(tagged)
new_data.extend(data[extra_start+extra_len:])
new_data[extra_len_pos:extra_len_pos+4] = struct.pack('>I', new_extra_len)
delta = len(tagged)
new_data[li_len_pos:li_len_pos+4] = struct.pack('>I', li_len + delta)
new_data[lami_pos:lami_pos+4] = struct.pack('>I', lami_len + delta)
with open(out_path, 'wb') as f: f.write(new_data)
return len(new_data)
# === Main ===
def main():
os.makedirs(OUTPUT_DIR, exist_ok=True)
img = create_l_shape()
img.save('/tmp/_fx_shape.png')
# Base PSD from ImageMagick
subprocess.run(['convert', '/tmp/_fx_shape.png', '/tmp/_fx_base.psd'], check=True, capture_output=True)
# All effects
for fname, label, fx_func in [
("drop_shadow", "Drop Shadow", lambda: build_lrfx_block([common_state(), lrfx_drop_shadow()])),
("inner_shadow", "Inner Shadow", lambda: build_lrfx_block([common_state(), lrfx_inner_shadow()])),
("outer_glow", "Outer Glow", lambda: build_lrfx_block([common_state(), lrfx_outer_glow()])),
("inner_glow", "Inner Glow", lambda: build_lrfx_block([common_state(), lrfx_inner_glow()])),
("bevel_inner", "Bevel Inner", lambda: build_lrfx_block([common_state(), lrfx_bevel(1)])),
("bevel_outer", "Bevel Outer", lambda: build_lrfx_block([common_state(), lrfx_bevel(2)])),
("emboss", "Emboss", lambda: build_lrfx_block([common_state(), lrfx_bevel(3)])),
("pillow_emboss", "Pillow Emboss", lambda: build_lrfx_block([common_state(), lrfx_bevel(4)])),
("color_overlay", "Color Overlay", lambda: build_lrfx_block([common_state(), lrfx_color_overlay()])),
]:
out = os.path.join(OUTPUT_DIR, f'{PREFIX}{fname}.psd')
lrfx = fx_func()
sz = patch_psd_with_lrfx('/tmp/_fx_base.psd', out, lrfx)
print(f' {PREFIX}{fname}.psd {sz}B')
# Baseline (no effects)
baseline = os.path.join(OUTPUT_DIR, f'{PREFIX}baseline.psd')
subprocess.run(['cp', '/tmp/_fx_base.psd', baseline], check=True)
print(f' {PREFIX}baseline.psd')
print(f'\nDone! {OUTPUT_DIR}')
if __name__ == '__main__':
main()