Skip to content

Convert YCbCr images to sRGB when decoding with Imagick#1502

Merged
olivervogel merged 1 commit into
Intervention:developfrom
nlemoine:fix/imagick-ycbcr-colorspace
Jun 23, 2026
Merged

Convert YCbCr images to sRGB when decoding with Imagick#1502
olivervogel merged 1 commit into
Intervention:developfrom
nlemoine:fix/imagick-ycbcr-colorspace

Conversation

@nlemoine

Copy link
Copy Markdown
Contributor

Reading pixels from an AVIF (or HEIF) fails with the Imagick driver on older ImageMagick. Decoding is fine, the first colorAt() throws.

Older ImageMagick decodes AVIF/HEIF into the YCbCr colorspace and doesn't normalize it. The Imagick ColorspaceAnalyzer has no YCbCr branch, so the match falls through to the OKLAB/OKLCH arms. Those reference Imagick::COLORSPACE_OKLAB, which doesn't exist before the ImageMagick release that added OKLAB, so analysis fails with "Undefined constant Imagick::COLORSPACE_OKLAB". Newer ImageMagick returns sRGB for the same image, so it only shows up on older builds.

Repro: decode an AVIF with the Imagick driver on an ImageMagick without OKLAB, then call colorAt() or colorspace().

Measured inside the failing environment:

ImageMagick OKLAB defined decoded AVIF colorspace
6.9.11-60 no 7 (COLORSPACE_YCBCR)
6.9.13-40 no 7 (COLORSPACE_YCBCR)
7.1.2 no sRGB

Every decoded AVIF reported YCbCr regardless of the encoder (aom/libavif, vips). The colorspace enum values differ between ImageMagick majors (YCbCr is 7 on IM6, 27 on IM7), so the fix uses the Imagick::COLORSPACE_YCBCR constant, not a literal.

Fix:

  1. NativeObjectDecoder converts YCbCr to sRGB on decode, next to the existing GRAY handling. It uses transformImageColorspace() (a real conversion), not setImageColorspace(), otherwise pixels come back as raw luma/chroma values. This is a no-op on newer ImageMagick where the image is already sRGB.

  2. ColorspaceAnalyzer guards the OKLAB/OKLCH constants with defined() before using them, the same way ColorspaceModifier already does. Unknown colorspaces now reach the default arm instead of throwing on the missing constant.

Tests: added a regression test in NativeObjectDecoderTest that forces an image to YCbCr, decodes it, and checks the result is sRGB and colorAt() returns the original color (small tolerance, so it also catches a relabel-instead-of-convert mistake). It forces YCbCr explicitly so it runs the same on ImageMagick 6 and 7.

Older ImageMagick decodes AVIF/HEIF as YCbCr and does not normalize it,
so the Imagick ColorspaceAnalyzer reaches the OKLAB/OKLCH match arms and
references Imagick::COLORSPACE_OKLAB, which is undefined before OKLAB
support and throws. Convert YCbCr to sRGB on decode (next to the existing
GRAY handling), and guard the OKLAB/OKLCH constants with defined() the
same way ColorspaceModifier already does.
@olivervogel olivervogel merged commit e5256d3 into Intervention:develop Jun 23, 2026
6 checks passed
@olivervogel

Copy link
Copy Markdown
Member

Thanks.

@nlemoine nlemoine deleted the fix/imagick-ycbcr-colorspace branch June 23, 2026 13:31
@nlemoine

nlemoine commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Thank you @olivervogel!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants