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))
|
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 = bytearray()
|
||||||
rec += struct.pack(">4i", top, left, bottom, right)
|
rec += struct.pack(">4i", top, left, bottom, right)
|
||||||
rec += struct.pack(">H", len(channels))
|
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
|
# luni: unicode layer name
|
||||||
uni = name.encode("utf-16-be")
|
uni = name.encode("utf-16-be")
|
||||||
extra += b"8BIMluni" + struct.pack(">I", len(uni)) + uni
|
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 += struct.pack(">I", len(extra))
|
||||||
rec += extra
|
rec += extra
|
||||||
return bytes(rec)
|
return bytes(rec)
|
||||||
@@ -506,11 +530,13 @@ def build_psd(layers, width: int, height: int, out_path: str, workers: int,
|
|||||||
# layer records + channel payloads
|
# layer records + channel payloads
|
||||||
records = bytearray()
|
records = bytearray()
|
||||||
payload = 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
|
l, t = lx, ly
|
||||||
r = l + img.width
|
r = l + img.width
|
||||||
b = t + img.height
|
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:
|
for _, p in ch:
|
||||||
payload += p
|
payload += p
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user