Fix merged image data: single compression field + RAW for compatibility

Per Adobe PSD spec, the Image Data Section has ONE 2-byte compression
field followed by all planes in planar order (RRR GGG BBB). The previous
code wrote a compression marker per channel (4 extra bytes) and used RLE,
which third-party viewers parse incorrectly (stretching) and Photoshop
rejected. Now writes RAW data with a single compression marker, verified
by pytoshop (decompresses to 3x4882x9764).
This commit is contained in:
AskaEth committed 2026-08-27 08:37:59 +08:00
1 parent a5757d1f07
commit b76a26c11c
1 file changed
+12 -3
+12 -3
View File
@@ -518,7 +518,17 @@ def build_psd(layers, width: int, height: int, out_path: str, workers: int,
if visible and opacity > 0:
merged.paste(img, (lx, ly), img)
merged_rgb = merged.convert("RGB") # transparent -> black
merged_channels = channel_blocks(merged_rgb, workers, progress)[0:3]
# Merged image data is written RAW (compression=0) for maximum
# compatibility with third-party viewers, which often only parse RAW
# merged data and stretch RLE data. Per spec there is ONE 2-byte
# compression field followed by all planes in planar order (RRR GGG BBB).
if np is not None:
arr = np.asarray(merged_rgb)
merged_block = b"\x00\x00" + b"".join(
arr[..., i].tobytes() for i in range(3))
else:
merged_block = b"\x00\x00" + b"".join(
merged_rgb.getchannel(c).tobytes() for c in "RGB")
# assemble file
fh = open(out_path, "wb")
@@ -534,8 +544,7 @@ def build_psd(layers, width: int, height: int, out_path: str, workers: int,
lam = layer_info + struct.pack(">I", 0) # global layer mask info
fh.write(struct.pack(">I", len(lam)) + lam)
# merged image data (RGB, RLE)
merged_block = b"".join(p for _, p in merged_channels)
# merged image data (RGB, RAW)
fh.write(merged_block)
fh.close()