Fix PSD structure: add missing LayerInfo length field and correct channel order
- Layer info section was missing its 4-byte length field, causing all subsequent offsets to shift and Photoshop to fail with 'unexpected end of file' - Layer channel data was ordered [R,G,B,A] with ids [-1,0,1,2], shifting every channel by one (alpha held red, red held green, ...) - fixed to [A,R,G,B] so id -1 is the alpha mask - Verified: psd_tools opens the PSD, per-layer pixels match the rendered originals with 0.000 mean difference
This commit is contained in:
1 parent
60624f01c2
commit
a5757d1f07
1 file changed
+16
-6
+16
-6
@@ -432,9 +432,17 @@ def channel_blocks(img: Image.Image, workers: int,
|
||||
# zero RGB where alpha==0 for better compression
|
||||
arr = arr.copy()
|
||||
arr[arr[..., 3] == 0] = 0
|
||||
planes = [arr[..., i].tobytes() for i in range(nch)]
|
||||
# PSD layer channel ids: -1=alpha mask, 0=R, 1=G, 2=B
|
||||
planes = [arr[..., 3].tobytes()] + [arr[..., i].tobytes() for i in range(3)]
|
||||
else:
|
||||
planes = [arr[..., i].tobytes() for i in range(nch)]
|
||||
else:
|
||||
planes = [img.getchannel(c).tobytes() for c in img.getbands()]
|
||||
bands = img.getbands()
|
||||
if bands == ("R", "G", "B", "A"):
|
||||
planes = [img.getchannel("A").tobytes()] + \
|
||||
[img.getchannel(c).tobytes() for c in "RGB"]
|
||||
else:
|
||||
planes = [img.getchannel(c).tobytes() for c in bands]
|
||||
w = img.width
|
||||
jobs = [(plane, w) for plane in planes]
|
||||
if workers > 1 and len(jobs) > 1:
|
||||
@@ -518,10 +526,12 @@ def build_psd(layers, width: int, height: int, out_path: str, workers: int,
|
||||
fh.write(struct.pack(">I", 0)) # color mode data
|
||||
fh.write(struct.pack(">I", 0)) # image resources
|
||||
|
||||
layer_info = bytes(records) + bytes(payload)
|
||||
count_payload = struct.pack(">h", len(layers)) + layer_info
|
||||
lam = struct.pack(">I", len(count_payload) + 4) + count_payload
|
||||
lam += struct.pack(">I", 0) # global layer mask info
|
||||
# Layer info section: [Length(4)] + [layer count(2) + records + channel data]
|
||||
layer_info_body = struct.pack(">h", len(layers)) + bytes(records) + bytes(payload)
|
||||
layer_info = struct.pack(">I", len(layer_info_body)) + layer_info_body
|
||||
# layer & mask section: length = layer info + global layer mask info
|
||||
# (the outer length does NOT include its own 4-byte length field)
|
||||
lam = layer_info + struct.pack(">I", 0) # global layer mask info
|
||||
fh.write(struct.pack(">I", len(lam)) + lam)
|
||||
|
||||
# merged image data (RGB, RLE)
|
||||
|
||||
Reference in new issue
Block a user