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

[@astrojs/image] flatten background only if alpha channel isn't supported #4800

Merged
merged 5 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/grumpy-monkeys-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/image': patch
---

Prevent background flattening on formats supporting transparency
8 changes: 5 additions & 3 deletions packages/integrations/image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,17 @@ A `number` can also be provided, useful when the aspect ratio is calculated at b
**Default:** `undefined`
</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
as default replacement for transparent pixels.

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
color representation with 3 or 6 hexadecimal characters in the form `#123[abc]`, or an RGB definition in the form
`rgb(100,100,100)`.
color representation with 3 or 6 hexadecimal characters in the form `#123[abc]`, an RGB definition in the form
`rgb(100,100,100)`, an RGBA definition in the form `rgba(100,100,100, 0.5)`.

#### fit

Expand Down
4 changes: 3 additions & 1 deletion packages/integrations/image/src/loaders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export type ColorDefinition =
| NamedColor
| `#${string}`
| `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';

Expand Down
16 changes: 10 additions & 6 deletions packages/integrations/image/src/loaders/sharp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
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';

class SharpService implements SSRImageService {
Expand Down Expand Up @@ -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) {
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 });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ describe('SSG image with background - build', function () {
id: '#rgb-spaced',
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 }) => {
it(title, async () => {
const image = $(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('SSR image with background', function () {
title: 'Hex color',
id: '#hex',
query: {
f: 'avif',
f: 'jpeg',
w: '256',
h: '256',
href: /^\/assets\/file-icon.\w{8}.png/,
Expand All @@ -42,7 +42,7 @@ describe('SSR image with background', function () {
title: 'Hex color short',
id: '#hex-short',
query: {
f: 'png',
f: 'jpeg',
w: '256',
h: '256',
href: /^\/assets\/file-icon.\w{8}.png/,
Expand All @@ -53,7 +53,7 @@ describe('SSR image with background', function () {
title: 'RGB color',
id: '#rgb',
query: {
f: 'webp',
f: 'jpeg',
w: '256',
h: '256',
href: /^\/assets\/file-icon.\w{8}.png/,
Expand All @@ -71,6 +71,28 @@ describe('SSR image with background', function () {
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 }) => {
it(title, async () => {
const app = await fixture.loadTestAdapterApp();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import { Image } from '@astrojs/image/components';
<body>
<Image id="named" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="dimgray" alt="named" />
<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 />
<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 />
<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 />
<Image id="rgb-spaced" src={import('../assets/file-icon.png')} width={256} format="jpeg" background="rgb(105, 105, 105)" alt="rgb-spaced" />
<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>
</html>