PSD: layers with a "real user supplied layer mask" (channel -3) stored as Raw fail to load ("unsupported compression")

クローズ
#5,659 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 2 日以内に返信

@linsen458-spec がすでに取り組んでいます。

2026年11月5日 から。

  • #5666 @linsen458-spec による — マージ済み

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
72/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
cpp
領域
backend

調査の方向性

Start with src/psd.imageio/psdinput.cpp, especially PSDInput::load_layer_channel() and its handling of channel ID -3 and declared channel lengths. Use the attached repro_raw.psd and repro_rle.psd files with oiiotool --info to compare behavior. Done means the Raw file opens without misreading the next channel, while existing mask handling continues to work.

索引モデルが issue の本文から書いたものです。

説明

Environment

  • OpenImageIO 3.1.7.0 (as bundled with Blender 5.0 / 5.1 / 5.2.2), macOS
  • Also reproduced with the PyPI wheels 3.1.7.0, 3.1.19.0 and 3.2.2.0 (Linux)

Description

A PSD fails to open when all of the following are true:

  • A layer has both a layer mask (channel ID -2) and a "real user supplied
    layer mask" (channel ID -3).
  • The -3 channel is stored uncompressed (compression = 0).
  • The -3 mask rectangle differs in size from the layer's bounds.
  • At least one more layer follows it.

The file is valid per the PSD spec (all channel lengths in the layer records
are correct). Photoshop itself writes this structure, e.g. for a shape layer
(vector mask) that also has a pixel layer mask.

Steps to reproduce

oiiotool repro_raw.psd --info

Expected

The file opens, like repro_rle.psd.

Actual

[Layer Channel] unsupported compression 65535
failed to open "repro_raw.psd": failed load_layers

With real Photoshop files, the reported compression value varies from file to
file (e.g. 41616).

Attachments

repro_psd.zip contains two minimal files generated with Python
(64×64, RGB, 8-bit, 2 layers, no metadata):

  • repro_raw.psd: the -3 channel is Raw → fails
  • repro_rle.psd: identical, except the -3 channel is RLE → loads

Layout of "Layer 1" in repro_raw.psd:

rectangle (t, l, b, r) size declared channel length
layer bounds 8, 8, 40, 40 32×32 1026 per color/alpha channel
-2 (layer mask) 12, 12, 28, 36 24×16 386 = 2 + 384
-3 (real user mask) 16, 20, 48, 30 10×32 322 = 2 + 320

Analysis

In PSDInput::load_layer_channel() (v3.1.7.0 L1624-1653; same logic in
main around L1780 / L1814), only ChannelID_LayerMask (-2) uses its own
rectangle (mask_data). Channel -3 falls back to the layer bounds. The real
user mask rectangle exists in the layer mask data, but it is skipped
(// skip "real" fields).

For Compression_Raw, the declared channel length is then overwritten with
row_length * height, and the stream skips that many bytes:

https://github.com/AcademySoftwareFoundation/OpenImageIO/blob/v3.1.7.0/src/psd.imageio/psdinput.cpp#L1624-L1653

For the attached file, the reader skips 32 × 32 = 1024 bytes instead of 320,
which overshoots by 704 bytes. It then reads two bytes of unrelated pixel data
(0xFFFF) as the compression of the next channel.

RLE works in 3.1.x because the skip there is based on the declared
data_length. With 3.2.2.0, the RLE case also fails when the -3 height
differs from the layer height
(RLE row lengths (...) exceed available data). In that case, the row-count
table is read with the wrong number of rows, and the new check from #5591
rejects it. This does not happen with the attached RLE file, because its -3
height equals the layer height.

I also confirmed that a variant whose -3 rectangle equals the layer bounds
loads fine with Raw. This is the same class of bug that #1714 fixed for
channel -2.

Possible fix (untested)

Masks are never exposed as image channels, so -3 could be skipped using the
declared length:

+    // The real user mask (-3) has its own rectangle (the "real" fields of the
+    // layer mask data), which is not parsed. Its pixels are never exposed,
+    // so skip it using the length declared in the layer record.
+    if (channel_info.channel_id == ChannelID_UserMask)
+        return ioseek(start_pos + channel_info.data_length);

Alternatives:

  • Parse the real user mask rectangle and use it for -3.
  • In general, never let the Raw path override the declared data_length.

repro_psd.zip

主要言語
C++
スター
2.4k
フォーク
718
平均マージ
2日 18時間
マージ済み PR(30日)
85

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

AcademySoftwareFoundation/OpenImageIO のほかの issue

AcademySoftwareFoundation/OpenImageIO の issue をすべて見る

似ている issue

C++ の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。