vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.53k stars 26.92k forks source link

<Image /> placeholder image appears "washed out" due to feathered edges #52548

Closed arturbien closed 1 year ago

arturbien commented 1 year ago

Verify canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 22.5.0: Mon Apr 24 20:53:19 PDT 2023; root:xnu-8796.121.2~5/RELEASE_ARM64_T6020
    Binaries:
      Node: 19.7.0
      npm: 9.5.0
      Yarn: 1.22.19
      pnpm: 8.6.6
    Relevant Packages:
      next: 13.4.10-canary.3
      eslint-config-next: 13.0.4
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 4.9.3
    Next.js Config:
      output: N/A

Which area(s) of Next.js are affected? (leave empty if unsure)

Image optimization (next/image, next/legacy/image)

Link to the code that reproduces this issue or a replay of the bug

https://codesandbox.io/p/sandbox/competent-agnesi-sw4tsq?file=%2Fpages%2Findex.tsx%3A30%2C9

To Reproduce

Describe the Bug

When placeholder image file type is different than JPEG, placeholder images have feathered / washed out edges around them:

Screenshot 2023-07-11 at 14 30 45

Expected Behavior

Placeholder images should keep hard/solid edges around them no matter what type of file extensions is used for the placeholder image (JPEG, JPG, GIF, WEBP).

Here's a comparison between how it is now, and how it could be when fixed. Having hard edges around the image makes UI look crispier.

Feathered edges (current state):

Screenshot 2023-07-11 at 14 36 36

Hard edges (expected):

Screenshot 2023-07-11 at 14 36 47

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

arturbien commented 1 year ago

I've already investigated the issue and I've found the line that causes it:

https://github.com/vercel/next.js/blob/56fcd7ac33e700236e6a37a8dac5ed9c378e0823/packages/next/src/shared/lib/image-blur-svg.ts#L22C1-L24C9

The "feComponentTransfer" primitive is used to make semi-transparent pixels from the blurred image solid (fixing the feathered edges issue), but only for JPEG images. The "JPEG" check however makes sense because this technique would completely break images with transparent backgrounds (by turning all transparent pixels to pitch black).

Why fixing this is important

I suspect that most images loaded in apps built with Next do not have transparency in them, so it would be worth fixing the feathered edges issue. Image-heavy websites will look much better. Especially when libraries like "lqip-modern" or "plaiceholder" output WEBP placeholders by default.

Solution

I've already found a possible solution to this issue by updating the underlying SVG filter. Here's a little demo of the filter that works for any type of image no matter if it has transparenct pixels or not:

(placeholder image, current blur technique, my solution) Image with transparency:

Screenshot 2023-07-11 at 15 05 43

Image without transparency

Screenshot 2023-07-11 at 15 07 31

I'll submit a PR shortly so pls tag me as an assignee 🤓

github-actions[bot] commented 1 year ago

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.