PSD: layers with a "real user supplied layer mask" (channel -3) stored as Raw fail to load ("unsupported compression")
メンテナーはふだん 2 日以内に返信
評価
調査の方向性
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 → failsrepro_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:
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.
- 主要言語
- C++
- スター
- 2.4k
- フォーク
- 718
- 平均マージ
- 2日 18時間
- マージ済み PR(30日)
- 85
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
AcademySoftwareFoundation/OpenImageIO のほかの issue
-
bug: get_image_info returns true after copying an uninitialised buffer into the caller's output when every UDIM tile is unreadable対応中かも @lgritz が 1 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
AcademySoftwareFoundation/OpenImageIO#5662 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 2 日以内に返信
-
bug: copy() writes destination channels the source does not have, taking the values from past the source's pixel対応中かも @lgritz が 1 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 35/100
AcademySoftwareFoundation/OpenImageIO#5661 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 2 日以内に返信
-
bug: a malformed filename throws std::regex_error out of ImageCache, and catching it deadlocks the file cache対応中かも @lgritz が 1 日前に担当しました。 オープン
AcademySoftwareFoundation/OpenImageIO#5660 · 担当者 1 名 ·
メンテナーはふだん 2 日以内に返信
-
bug: deep paste wraps into the next scanline instead of clipping, and ignores chbegin対応中かも @lgritz が 1 日前に担当しました。 オープン
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
AcademySoftwareFoundation/OpenImageIO#5649 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 2 日以内に返信
-
bug: make_kernel("binomial", ...) indexes its tap arrays past their allocation for even or fractional extents対応中かも @lgritz が 3 日前に担当しました。 オープン
難易度 3/5 半日 初心者へのやさしさ 62/100
AcademySoftwareFoundation/OpenImageIO#5648 ·
メンテナーはふだん 2 日以内に返信
AcademySoftwareFoundation/OpenImageIO の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
xiaoyifang/goldendict-ng#3148 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 91/100
triton-inference-server/server#9069 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 4 日以内に返信
-
documentation
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100