Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AVIF images fail to render in Chrome Canary #3977

Closed
3 tasks done
GeoffreyBooth opened this issue Feb 4, 2024 · 7 comments
Closed
3 tasks done

AVIF images fail to render in Chrome Canary #3977

GeoffreyBooth opened this issue Feb 4, 2024 · 7 comments
Labels

Comments

@GeoffreyBooth
Copy link

GeoffreyBooth commented Feb 4, 2024

Possible bug

Is this a possible bug in a feature of sharp, unrelated to installation?

  • Running npm install sharp completes without error.
  • Running node -e "require('sharp')" completes without error.

Are you using the latest version of sharp?

  • I am using the latest version of sharp as reported by npm view sharp dist-tags.latest.

What is the output of running npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp?

  System:
    OS: macOS 13.6.3
    CPU: (16) x64 Intel(R) Xeon(R) W-3223 CPU @ 3.50GHz
    Memory: 1.24 GB / 48.00 GB
    Shell: 5.9 - /usr/local/bin/zsh
  Binaries:
    Node: 21.6.0 - /usr/local/bin/node
    npm: 10.2.4 - /usr/local/bin/npm
    bun: 1.0.15 - ~/.bun/bin/bun
  npmPackages:
    sharp: ^0.33.2 => 0.33.2

What are the steps to reproduce?

Save the following as test.mjs:

import { Buffer } from 'node:buffer'
import { writeFile } from 'node:fs/promises'
import sharp from 'sharp'

const transparentPixelPngBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
const transparentPixelPng = Buffer.from(transparentPixelPngBase64, 'base64')

const avifImage = await sharp(transparentPixelPng).avif().toBuffer()

writeFile('transparent-pixel.avif', avifImage)

Running this generates a 1x1 transparent pixel AVIF. Previewing it in Finder or in current stable Chrome (121.0.6167.139), it looks as expected. If I open it in Chrome Canary 123.0.6280.0, such as via a file:// URL like file:///Users/Geoffrey/Desktop/transparent-pixel.avif as of this writing, I see this:

image

Obviously this could be a bug in Chrome, but because of #2666 I thought perhaps this might be an issue with Sharp.

What is the expected behaviour?

It should render, like it does for stable Chrome.

Further steps:

  1. Add writeFile('transparent-pixel.png', transparentPixelPng) to the end of the above script and run it again, to generate transparent-pixel.png.
  2. Run avifenc transparent-pixel.png transparent-pixel-2.avif
  3. Load this new transparent-pixel-2.avif in Chrome Canary. For me it displays correctly, unlike the “broken image” result of the Sharp-generated transparent-pixel.avif.

Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem

See above.

Please provide sample image(s) that help explain this problem

Here is the transparent-pixel.avif generated by Sharp:

transparent-pixel.avif.zip

Running this file through https://gpac.github.io/ComplianceWarden-wasm/index.html shows that it contains a compliance error:

[avif][Rule #7] Error: [ItemId=1] The values of the AV1CodecConfigurationBox shall match
the Sequence Header OBU in the AV1 Image Item Data

@lovell
Copy link
Owner

lovell commented Feb 4, 2024

Thanks for reporting, I guess the Chrome team is either intentionally or inadvertently tightening up AVIF validation.

The complete ComplianceWarden output suggests there is a single difference:

  • Sequence Header OBU in the AV1 Image Item Data: chroma_sample_position=0
  • AV1CodecConfigurationBox: chroma_sample_position=2

Taking a quick look at chroma_sample_position handling in the libheif code, a possible discrepancy could arise due to testing against only heif_chroma_420 here:

https://github.com/strukturag/libheif/blob/51da10c84bf32d0f7a77b42540dbbef600350e6e/libheif/avif.cc#L181

...but testing against heif_chroma_420 and heif_chroma_monochrome here:

https://github.com/strukturag/libheif/blob/51da10c84bf32d0f7a77b42540dbbef600350e6e/libheif/plugins/encoder_aom.cc#L767-L773

The next step is probably to see if this can be reproduced using heif-convert at the command line.

@GeoffreyBooth
Copy link
Author

GeoffreyBooth commented Feb 5, 2024

The next step is probably to see if this can be reproduced using heif-convert at the command line.

I’m not sure what you mean. When I run heif-enc transparent-pixel.png transparent-pixel-heif-enc.avif, the resulting AVIF file fails to render in Chrome Canary.
transparent-pixel-heif-enc.avif.zip

Using ImageMagick via convert transparent-pixel.png transparent-pixel-imagemagick.avif shows no errors in the compliance test but still shows a “broken picture” icon in Chrome Canary.

transparent-pixel.png.zip
transparent-pixel-imagemagick.avif.zip

@GeoffreyBooth
Copy link
Author

I opened an issue with Chromium: https://issues.chromium.org/issues/323735410.

I converted the 1x1 transparent pixel PNG to AVIF via avifenc, convert (ImageMagick 7.1.1-22) and Sharp:
transparent-pixel-avifenc-imagemagick-sharp.zip

The avifenc one renders in Chrome Canary while the ImageMagick and Sharp ones don’t.

I tried the same experiment with a 1x1 solid white pixel PNG:

import { Buffer } from 'node:buffer'
import { writeFile } from 'node:fs/promises'
import sharp from 'sharp'

const whitePixelPngBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAADa6r/EAAAADElEQVQIHWP4//8/AAX+Av6fyi0TAAAAAElFTkSuQmCC'
const whitePixelPng = Buffer.from(whitePixelPngBase64, 'base64')

const avifImage = await sharp(whitePixelPng).avif().toBuffer()

writeFile('white-pixel-sharp.avif', avifImage)

All the generated AVIF images, from Sharp and ImageMagick and avifenc, render correctly in Chrome Canary. So this may have something to do with the alpha channel.
white-pixel-avifenc-imagemagick-sharp.zip

@lovell
Copy link
Owner

lovell commented Feb 5, 2024

Sorry, I meant heif-enc rather than heif-convert. The following, where the input is 1x1 RGBA, generates the same (non-compliant) output as sharp:

heif-enc -p chroma=444 in.png out.avif

The ImageMagick example uses chroma subsampling (420) and still breaks in Chrome Canary, so this doesn't appear to be related to subsampling.

The avifenc example differs in that it uses a monochrome colourspace (vs YCbCr), which might help narrow this down.

@wantehchang Are you able to help investigate this?

@GeoffreyBooth
Copy link
Author

A fix has landed in Chrome Canary: https://issues.chromium.org/issues/323735410#comment8

It looks like they just rolled back the change that caused the breakage. I don’t know if they intend to try again, and if there are any issues to address on the Sharp/libheif side, but I think this can be closed for now.

@wantehchang
Copy link

Geoffrey: Thank you for reporting this bug. This is a bug in libavif only. (It validated itemID ordering in the wrong function.) Nothing needs to be addressed on the Sharp/libheif side. My colleague Vignesh (@vigneshvg) has fixed the bug in libavif and updated the libavif snapshot in Chrome Canary.

@lovell
Copy link
Owner

lovell commented Feb 7, 2024

Brilliant, thanks both.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants