Write standard tagged blocks (lnsr/lyid/clbl/infx/knko/lspf/lclr/fxrp) per layer
Photoshop reads a layer's tagged blocks until it sees a non-8BIM signature. With only 'luni' the next bytes are the channel data, which PS mis-parses and the layer pixels come out empty. PS writes the standard block set, so we now mirror it (verified: blocks list now matches the reference PSD).
This commit is contained in:
1 parent
e07ef4acaa
commit
00f2cbe71a
1 file changed
+29
-3
+29
-3
@@ -463,7 +463,29 @@ def channel_blocks(img: Image.Image, workers: int,
|
||||
return list(zip(ids, enc))
|
||||
|
||||
|
||||
def _layer_record(top, left, bottom, right, name, opacity, visible, channels) -> bytes:
|
||||
def _standard_blocks(layer_id: int) -> bytes:
|
||||
"""Standard tagged blocks Photoshop writes for every layer. PS reads
|
||||
tagged blocks until it hits a non-8BIM signature; with only 'luni' the
|
||||
next bytes are channel data and PS mis-parses the record, so we write
|
||||
the usual block set like PS itself does."""
|
||||
items = [
|
||||
(b"lnsr", b"\x00\x00\x00\x00"), # name source
|
||||
(b"lyid", struct.pack(">I", layer_id)), # layer id
|
||||
(b"clbl", b"\x00\x00\x00\x00"), # blend clipping
|
||||
(b"infx", b"\x00\x00\x00\x00"), # blend interior
|
||||
(b"knko", b"\x00\x00\x00\x00"), # knockout
|
||||
(b"lspf", b"\x00\x00\x00\x00"), # protected
|
||||
(b"lclr", b"\x00" * 8), # sheet color
|
||||
(b"fxrp", b"\x00" * 16), # reference point
|
||||
]
|
||||
out = bytearray()
|
||||
for key, payload in items:
|
||||
out += b"8BIM" + key + struct.pack(">I", len(payload)) + payload
|
||||
return bytes(out)
|
||||
|
||||
|
||||
def _layer_record(top, left, bottom, right, name, opacity, visible, channels,
|
||||
layer_id=1) -> bytes:
|
||||
rec = bytearray()
|
||||
rec += struct.pack(">4i", top, left, bottom, right)
|
||||
rec += struct.pack(">H", len(channels))
|
||||
@@ -489,6 +511,8 @@ def _layer_record(top, left, bottom, right, name, opacity, visible, channels) ->
|
||||
# luni: unicode layer name
|
||||
uni = name.encode("utf-16-be")
|
||||
extra += b"8BIMluni" + struct.pack(">I", len(uni)) + uni
|
||||
# standard blocks Photoshop expects (see _standard_blocks)
|
||||
extra += _standard_blocks(layer_id)
|
||||
rec += struct.pack(">I", len(extra))
|
||||
rec += extra
|
||||
return bytes(rec)
|
||||
@@ -506,11 +530,13 @@ def build_psd(layers, width: int, height: int, out_path: str, workers: int,
|
||||
# layer records + channel payloads
|
||||
records = bytearray()
|
||||
payload = bytearray()
|
||||
for (name, opacity, visible, (lx, ly), img), ch in zip(layers, chan_data):
|
||||
for li, (name, opacity, visible, (lx, ly), img) in enumerate(layers):
|
||||
ch = chan_data[li]
|
||||
l, t = lx, ly
|
||||
r = l + img.width
|
||||
b = t + img.height
|
||||
records += _layer_record(t, l, b, r, name, opacity, visible, ch)
|
||||
records += _layer_record(t, l, b, r, name, opacity, visible, ch,
|
||||
layer_id=li + 1)
|
||||
for _, p in ch:
|
||||
payload += p
|
||||
|
||||
|
||||
Reference in new issue
Block a user