From ea37de86e4b0f1e1e371fabf37495321c71bd24d Mon Sep 17 00:00:00 2001 From: Oussama Bennaci <9404365+obennaci@users.noreply.github.com> Date: Tue, 20 Sep 2022 21:25:45 +0100 Subject: [PATCH] [@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 --- .changeset/grumpy-monkeys-watch.md | 5 ++++ packages/integrations/image/README.md | 8 ++++-- .../integrations/image/src/loaders/index.ts | 4 ++- .../integrations/image/src/loaders/sharp.ts | 16 +++++++---- .../test/background-color-image-ssg.test.js | 11 ++++++++ .../test/background-color-image-ssr.test.js | 28 +++++++++++++++++-- .../src/pages/index.astro | 10 +++++-- 7 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 .changeset/grumpy-monkeys-watch.md diff --git a/.changeset/grumpy-monkeys-watch.md b/.changeset/grumpy-monkeys-watch.md new file mode 100644 index 000000000000..5eb33f6cefa3 --- /dev/null +++ b/.changeset/grumpy-monkeys-watch.md @@ -0,0 +1,5 @@ +--- +'@astrojs/image': patch +--- + +Prevent background flattening on formats supporting transparency diff --git a/packages/integrations/image/README.md b/packages/integrations/image/README.md index fe3a699011ce..4974793025e2 100644 --- a/packages/integrations/image/README.md +++ b/packages/integrations/image/README.md @@ -195,15 +195,17 @@ A `number` can also be provided, useful when the aspect ratio is calculated at b **Default:** `undefined`

-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 diff --git a/packages/integrations/image/src/loaders/index.ts b/packages/integrations/image/src/loaders/index.ts index 801a19300bfa..50b33b9e475c 100644 --- a/packages/integrations/image/src/loaders/index.ts +++ b/packages/integrations/image/src/loaders/index.ts @@ -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'; diff --git a/packages/integrations/image/src/loaders/sharp.ts b/packages/integrations/image/src/loaders/sharp.ts index fe11295a575b..e7ef57aa02f6 100644 --- a/packages/integrations/image/src/loaders/sharp.ts +++ b/packages/integrations/image/src/loaders/sharp.ts @@ -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 { @@ -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 }); diff --git a/packages/integrations/image/test/background-color-image-ssg.test.js b/packages/integrations/image/test/background-color-image-ssg.test.js index 3c488a3ffc12..6c442361547e 100644 --- a/packages/integrations/image/test/background-color-image-ssg.test.js +++ b/packages/integrations/image/test/background-color-image-ssg.test.js @@ -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); diff --git a/packages/integrations/image/test/background-color-image-ssr.test.js b/packages/integrations/image/test/background-color-image-ssr.test.js index ff4c208f8fe7..148c57a5b923 100644 --- a/packages/integrations/image/test/background-color-image-ssr.test.js +++ b/packages/integrations/image/test/background-color-image-ssr.test.js @@ -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/, @@ -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/, @@ -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/, @@ -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(); diff --git a/packages/integrations/image/test/fixtures/background-color-image/src/pages/index.astro b/packages/integrations/image/test/fixtures/background-color-image/src/pages/index.astro index 3ad137457793..76f1fe936500 100644 --- a/packages/integrations/image/test/fixtures/background-color-image/src/pages/index.astro +++ b/packages/integrations/image/test/fixtures/background-color-image/src/pages/index.astro @@ -9,13 +9,17 @@ import { Image } from '@astrojs/image/components'; named
- hex + hex
- hex-short + hex-short
- rgb + rgb
rgb-spaced
+ rgba +
+ rgba-spaced +