[@astrojs/image] flatten background only if alpha channel isn't supported (#4800)
* Use sharp flatten only when format doesnt support alpha * Allow rgba for background prop * Add changeset * Adjust doc * Fix tests
This commit is contained in:
parent
a76b0aa27c
commit
ea37de86e4
7 changed files with 66 additions and 16 deletions
5
.changeset/grumpy-monkeys-watch.md
Normal file
5
.changeset/grumpy-monkeys-watch.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@astrojs/image': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Prevent background flattening on formats supporting transparency
|
|
@ -195,15 +195,17 @@ A `number` can also be provided, useful when the aspect ratio is calculated at b
|
||||||
**Default:** `undefined`
|
**Default:** `undefined`
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
The background color to use for replacing the alpha channel with `sharp`'s `flatten` method. In case the output format
|
The background color is used to fill the remaining background when using `contain` for the `fit` property.
|
||||||
|
|
||||||
|
The background color is also used for replacing the alpha channel with `sharp`'s `flatten` method. In case the output format
|
||||||
doesn't support transparency (i.e. `jpeg`), it's advisable to include a background color, otherwise black will be used
|
doesn't support transparency (i.e. `jpeg`), it's advisable to include a background color, otherwise black will be used
|
||||||
as default replacement for transparent pixels.
|
as default replacement for transparent pixels.
|
||||||
|
|
||||||
The parameter accepts a `string` as value.
|
The parameter accepts a `string` as value.
|
||||||
|
|
||||||
The parameter can be a [named HTML color](https://www.w3schools.com/tags/ref_colornames.asp), a hexadecimal
|
The parameter can be a [named HTML color](https://www.w3schools.com/tags/ref_colornames.asp), a hexadecimal
|
||||||
color representation with 3 or 6 hexadecimal characters in the form `#123[abc]`, or an RGB definition in the form
|
color representation with 3 or 6 hexadecimal characters in the form `#123[abc]`, an RGB definition in the form
|
||||||
`rgb(100,100,100)`.
|
`rgb(100,100,100)`, an RGBA definition in the form `rgba(100,100,100, 0.5)`.
|
||||||
|
|
||||||
#### fit
|
#### fit
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,9 @@ export type ColorDefinition =
|
||||||
| NamedColor
|
| NamedColor
|
||||||
| `#${string}`
|
| `#${string}`
|
||||||
| `rgb(${number}, ${number}, ${number})`
|
| `rgb(${number}, ${number}, ${number})`
|
||||||
| `rgb(${number},${number},${number})`;
|
| `rgb(${number},${number},${number})`
|
||||||
|
| `rgba(${number}, ${number}, ${number}, ${number})`
|
||||||
|
| `rgba(${number},${number},${number},${number})`
|
||||||
|
|
||||||
export type CropFit = 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
|
export type CropFit = 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { ColorDefinition, isAspectRatioString, isOutputFormat } from '../loaders/index.js';
|
import {
|
||||||
|
ColorDefinition,
|
||||||
|
isAspectRatioString,
|
||||||
|
isOutputFormat,
|
||||||
|
isOutputFormatSupportsAlpha,
|
||||||
|
} from '../loaders/index.js';
|
||||||
import type { OutputFormat, SSRImageService, TransformOptions } from './index.js';
|
import type { OutputFormat, SSRImageService, TransformOptions } from './index.js';
|
||||||
|
|
||||||
class SharpService implements SSRImageService {
|
class SharpService implements SSRImageService {
|
||||||
|
@ -119,13 +124,12 @@ class SharpService implements SSRImageService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove alpha channel and replace with background color if requested
|
|
||||||
if (transform.background) {
|
|
||||||
sharpImage.flatten({ background: transform.background });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transform.format) {
|
if (transform.format) {
|
||||||
sharpImage.toFormat(transform.format, { quality: transform.quality });
|
sharpImage.toFormat(transform.format, { quality: transform.quality });
|
||||||
|
|
||||||
|
if (transform.background && !isOutputFormatSupportsAlpha(transform.format)) {
|
||||||
|
sharpImage.flatten({ background: transform.background });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, info } = await sharpImage.toBuffer({ resolveWithObject: true });
|
const { data, info } = await sharpImage.toBuffer({ resolveWithObject: true });
|
||||||
|
|
|
@ -106,6 +106,17 @@ describe('SSG image with background - build', function () {
|
||||||
id: '#rgb-spaced',
|
id: '#rgb-spaced',
|
||||||
bg: [105, 105, 105],
|
bg: [105, 105, 105],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: 'RGBA color',
|
||||||
|
id: '#rgba',
|
||||||
|
bg: [105, 105, 105],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'RGBA color with spaces',
|
||||||
|
id: '#rgba-spaced',
|
||||||
|
bg: [105, 105, 105],
|
||||||
|
},
|
||||||
].forEach(({ title, id, bg }) => {
|
].forEach(({ title, id, bg }) => {
|
||||||
it(title, async () => {
|
it(title, async () => {
|
||||||
const image = $(id);
|
const image = $(id);
|
||||||
|
|
|
@ -31,7 +31,7 @@ describe('SSR image with background', function () {
|
||||||
title: 'Hex color',
|
title: 'Hex color',
|
||||||
id: '#hex',
|
id: '#hex',
|
||||||
query: {
|
query: {
|
||||||
f: 'avif',
|
f: 'jpeg',
|
||||||
w: '256',
|
w: '256',
|
||||||
h: '256',
|
h: '256',
|
||||||
href: /^\/assets\/file-icon.\w{8}.png/,
|
href: /^\/assets\/file-icon.\w{8}.png/,
|
||||||
|
@ -42,7 +42,7 @@ describe('SSR image with background', function () {
|
||||||
title: 'Hex color short',
|
title: 'Hex color short',
|
||||||
id: '#hex-short',
|
id: '#hex-short',
|
||||||
query: {
|
query: {
|
||||||
f: 'png',
|
f: 'jpeg',
|
||||||
w: '256',
|
w: '256',
|
||||||
h: '256',
|
h: '256',
|
||||||
href: /^\/assets\/file-icon.\w{8}.png/,
|
href: /^\/assets\/file-icon.\w{8}.png/,
|
||||||
|
@ -53,7 +53,7 @@ describe('SSR image with background', function () {
|
||||||
title: 'RGB color',
|
title: 'RGB color',
|
||||||
id: '#rgb',
|
id: '#rgb',
|
||||||
query: {
|
query: {
|
||||||
f: 'webp',
|
f: 'jpeg',
|
||||||
w: '256',
|
w: '256',
|
||||||
h: '256',
|
h: '256',
|
||||||
href: /^\/assets\/file-icon.\w{8}.png/,
|
href: /^\/assets\/file-icon.\w{8}.png/,
|
||||||
|
@ -71,6 +71,28 @@ describe('SSR image with background', function () {
|
||||||
bg: 'rgb(105, 105, 105)',
|
bg: 'rgb(105, 105, 105)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'RGBA color',
|
||||||
|
id: '#rgba',
|
||||||
|
query: {
|
||||||
|
f: 'jpeg',
|
||||||
|
w: '256',
|
||||||
|
h: '256',
|
||||||
|
href: /^\/assets\/file-icon.\w{8}.png/,
|
||||||
|
bg: 'rgb(105,105,105,0.5)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'RGBA color with spaces',
|
||||||
|
id: '#rgba-spaced',
|
||||||
|
query: {
|
||||||
|
f: 'jpeg',
|
||||||
|
w: '256',
|
||||||
|
h: '256',
|
||||||
|
href: /^\/assets\/file-icon.\w{8}.png/,
|
||||||
|
bg: 'rgb(105, 105, 105, 0.5)',
|
||||||
|
},
|
||||||
|
},
|
||||||
].forEach(({ title, id, query }) => {
|
].forEach(({ title, id, query }) => {
|
||||||
it(title, async () => {
|
it(title, async () => {
|
||||||
const app = await fixture.loadTestAdapterApp();
|
const app = await fixture.loadTestAdapterApp();
|
||||||
|
|
|
@ -9,13 +9,17 @@ import { Image } from '@astrojs/image/components';
|
||||||
<body>
|
<body>
|
||||||
<Image id="named" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="dimgray" alt="named" />
|
<Image id="named" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="dimgray" alt="named" />
|
||||||
<br />
|
<br />
|
||||||
<Image id="hex" src={import('../assets/file-icon.png')} width={256} format="avif" background="#696969" alt="hex" />
|
<Image id="hex" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="#696969" alt="hex" />
|
||||||
<br />
|
<br />
|
||||||
<Image id="hex-short" src={import('../assets/file-icon.png')} width={256} background="#666" alt="hex-short" />
|
<Image id="hex-short" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="#666" alt="hex-short" />
|
||||||
<br />
|
<br />
|
||||||
<Image id="rgb" src={import('../assets/file-icon.png')} width={256} format="webp" background="rgb(105,105,105)" alt="rgb" />
|
<Image id="rgb" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="rgb(105,105,105)" alt="rgb" />
|
||||||
<br />
|
<br />
|
||||||
<Image id="rgb-spaced" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="rgb(105, 105, 105)" alt="rgb-spaced" />
|
<Image id="rgb-spaced" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="rgb(105, 105, 105)" alt="rgb-spaced" />
|
||||||
<br />
|
<br />
|
||||||
|
<Image id="rgba" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="rgb(105,105,105,0.5)" alt="rgba" />
|
||||||
|
<br />
|
||||||
|
<Image id="rgba-spaced" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="rgb(105, 105, 105, 0.5)" alt="rgba-spaced" />
|
||||||
|
<br />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Add table
Reference in a new issue