WordPress / WordPress/performance
Image Placeholders: average pixels in linear light to avoid gamma-skewed dominant color
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 461
- Forks
- 165
- Avg merge
- 11h 12m
- Merged PRs (30d)
- 28
Description
Feature Description
The Image Placeholders plugin (formerly Dominant Color Images) extracts the dominant color of an image by resizing it down to a single 1x1 pixel and reading that pixel's color. This is done in both the Imagick and GD editors:
Dominant_Color_Image_Editor_Imagick::get_dominant_color()(class-dominant-color-image-editor-imagick.php#L38) usesImagick::resizeImage( 1, 1, ... ).Dominant_Color_Image_Editor_GD::get_dominant_color()(class-dominant-color-image-editor-gd.php#L46) usesimagecopyresampled()to downscale to 1x1.
A community member (@MarcinDudekDev) pointed out in this comment that both Imagick and GD average pixels in gamma-encoded sRGB space, which skews the resulting color darker and less saturated than what the eye actually perceives. This is the classic "gamma error in picture scaling" problem.
Quick illustration: a 50/50 black/white checkerboard averaged in gamma space gives
#808080. Averaged in linear light (what you'd see squinting at the image from a distance) it's#BCBCBC- about 20 L* lighter. Real photos land at 4-9 L* off, worst on high contrast - skies with dark foregrounds, foliage, speculars.
Side-by-side demo with sample photos (you can drop in your own test images): https://marcindudekdev.github.io/img2gradient/gamma-demo.html
Proposed Fix
The fix is cheap for this use case (a single 1x1 average):
- Imagick: transform to linear colorspace before the resize and back to sRGB after, i.e. the
transformImageColorspace( Imagick::COLORSPACE_RGB )→resize 1x1→transformImageColorspace( Imagick::COLORSPACE_SRGB )recipe. - GD: GD has no colorspace support, but since the current implementation can iterate pixels anyway, the fix is to decode each pixel through a 256-entry sRGB→linear LUT, average in linear light, then re-encode to sRGB. A few extra lines, negligible cost. (Note: this would replace the current
imagecopyresampled()1x1 downscale, which itself averages in gamma space.) - libvips (for reference):
vipsthumbnailhas a--linearflag for exactly this, off by default.
The reporter ran into this while building a gradient placeholder generator (MarcinDudekDev/img2gradient) and noted the linear-light fix was visibly better on photos immediately.
Considerations
- The change would shift extracted placeholder colors lighter/more saturated, so existing stored
dominant_colorpost meta would not match newly computed values. Regeneration would be needed for consistency, though placeholder colors are not critical so a gradual migration is fine. - Worth adding test coverage with a high-contrast fixture (e.g. a black/white checkerboard) where the gamma vs. linear difference is largest and easy to assert on.
Reported via community feedback from @MarcinDudekDev.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with class-dominant-color-image-editor-imagick.php at get_dominant_color() and class-dominant-color-image-editor-gd.php at get_dominant_color(), then inspect existing tests for both editors. Verify the 1x1 color is averaged in linear light for Imagick and GD, and add high-contrast checkerboard coverage showing the expected difference from gamma-space averaging.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- performance
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100