Add unflatten operation to create an alpha channel (#3461)

This commit is contained in:
Anton Marsden 2023-04-07 22:01:29 +12:00 committed by GitHub
parent b9c3851515
commit a4c6eba7d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 93 additions and 0 deletions

View File

@ -217,6 +217,7 @@ const Sharp = function (input, options) {
tintB: 128, tintB: 128,
flatten: false, flatten: false,
flattenBackground: [0, 0, 0], flattenBackground: [0, 0, 0],
unflatten: false,
negate: false, negate: false,
negateAlpha: true, negateAlpha: true,
medianSize: 0, medianSize: 0,

7
lib/index.d.ts vendored
View File

@ -427,6 +427,13 @@ declare namespace sharp {
*/ */
flatten(flatten?: boolean | FlattenOptions): Sharp; flatten(flatten?: boolean | FlattenOptions): Sharp;
/**
* Unflatten - add an alpha channel to the image if required, and make white pixels fully transparent. Alpha for non-white pixels will be unchanged/opaque.
* @param unflatten true to enable and false to disable (defaults to true)
* @returns A sharp instance that can be used to chain operations
*/
unflatten(unflatten?: boolean): Sharp;
/** /**
* Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of 1/gamma then increasing the encoding (brighten) post-resize at a factor of gamma. * Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of 1/gamma then increasing the encoding (brighten) post-resize at a factor of gamma.
* This can improve the perceived brightness of a resized image in non-linear colour spaces. * This can improve the perceived brightness of a resized image in non-linear colour spaces.

View File

@ -405,6 +405,25 @@ function flatten (options) {
return this; return this;
} }
/**
* Unflatten - add an alpha channel to the image if required, and make white pixels fully transparent. Alpha for non-white pixels will be unchanged/opaque.
*
* @example
* await sharp(rgbInput)
* .unflatten()
* .toBuffer();
*
* @example
* await sharp(rgbInput)
* .threshold(128, { grayscale: false }) // converter bright pixels to white
* .unflatten()
* .toBuffer();
*/
function unflatten (options) {
this.options.unflatten = is.bool(options) ? options : true;
return this;
}
/** /**
* Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma` * Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
* then increasing the encoding (brighten) post-resize at a factor of `gamma`. * then increasing the encoding (brighten) post-resize at a factor of `gamma`.
@ -875,6 +894,7 @@ module.exports = function (Sharp) {
median, median,
blur, blur,
flatten, flatten,
unflatten,
gamma, gamma,
negate, negate,
normalise, normalise,

View File

@ -342,6 +342,19 @@ namespace sharp {
} }
} }
/*
* Unflatten
*/
VImage Unflatten(VImage image) {
if (HasAlpha(image)) {
VImage alpha = image[image.bands() - 1];
VImage noAlpha = RemoveAlpha(image);
return noAlpha.bandjoin(alpha & (noAlpha.colourspace(VIPS_INTERPRETATION_B_W) < 255));
} else {
return image.bandjoin(image.colourspace(VIPS_INTERPRETATION_B_W) < 255);
}
}
/* /*
* Ensure the image is in a given colourspace * Ensure the image is in a given colourspace
*/ */

View File

@ -86,6 +86,11 @@ namespace sharp {
*/ */
VImage Linear(VImage image, std::vector<double> const a, std::vector<double> const b); VImage Linear(VImage image, std::vector<double> const a, std::vector<double> const b);
/*
* Unflatten
*/
VImage Unflatten(VImage image);
/* /*
* Recomb with a Matrix of the given bands/channel size. * Recomb with a Matrix of the given bands/channel size.
* Eg. RGB will be a 3x3 matrix. * Eg. RGB will be a 3x3 matrix.

View File

@ -550,7 +550,9 @@ class PipelineWorker : public Napi::AsyncWorker {
if (baton->medianSize > 0) { if (baton->medianSize > 0) {
image = image.median(baton->medianSize); image = image.median(baton->medianSize);
} }
// Threshold - must happen before blurring, due to the utility of blurring after thresholding // Threshold - must happen before blurring, due to the utility of blurring after thresholding
// Threshold - must happen before unflatten to enable non-white unflattening
if (baton->threshold != 0) { if (baton->threshold != 0) {
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale); image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
} }
@ -560,6 +562,11 @@ class PipelineWorker : public Napi::AsyncWorker {
image = sharp::Blur(image, baton->blurSigma); image = sharp::Blur(image, baton->blurSigma);
} }
// Unflatten the image
if (baton->unflatten) {
image = sharp::Unflatten(image);
}
// Convolve // Convolve
if (shouldConv) { if (shouldConv) {
image = sharp::Convolve(image, image = sharp::Convolve(image,
@ -1460,6 +1467,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
// Operators // Operators
baton->flatten = sharp::AttrAsBool(options, "flatten"); baton->flatten = sharp::AttrAsBool(options, "flatten");
baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground"); baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
baton->unflatten = sharp::AttrAsBool(options, "unflatten");
baton->negate = sharp::AttrAsBool(options, "negate"); baton->negate = sharp::AttrAsBool(options, "negate");
baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha"); baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma"); baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");

View File

@ -73,6 +73,7 @@ struct PipelineBaton {
double tintB; double tintB;
bool flatten; bool flatten;
std::vector<double> flattenBackground; std::vector<double> flattenBackground;
bool unflatten;
bool negate; bool negate;
bool negateAlpha; bool negateAlpha;
double blurSigma; double blurSigma;
@ -239,6 +240,7 @@ struct PipelineBaton {
tintB(128.0), tintB(128.0),
flatten(false), flatten(false),
flattenBackground{ 0.0, 0.0, 0.0 }, flattenBackground{ 0.0, 0.0, 0.0 },
unflatten(false),
negate(false), negate(false),
negateAlpha(true), negateAlpha(true),
blurSigma(0.0), blurSigma(0.0),

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

37
test/unit/unflatten.js Normal file
View File

@ -0,0 +1,37 @@
'use strict';
const sharp = require('../../');
const fixtures = require('../fixtures');
// const assert = require('assert');
describe('Unflatten', function () {
it('unflatten white background', function (done) {
sharp(fixtures.inputPng).unflatten()
.toBuffer(function (err, data) {
if (err) throw err;
fixtures.assertSimilar(fixtures.expected('unflatten-white-transparent.png'), data, { threshold: 0 }, done);
});
});
it('unflatten transparent image', function (done) {
sharp(fixtures.inputPngTrimSpecificColourIncludeAlpha).unflatten()
.toBuffer(function (err, data) {
if (err) throw err;
fixtures.assertSimilar(fixtures.expected('unflatten-flag-white-transparent.png'), data, { threshold: 0 }, done);
});
});
it('unflatten using threshold', function (done) {
sharp(fixtures.inputPngPalette).unflatten(true).threshold(128, { grayscale: false })
.toBuffer(function (err, data) {
if (err) throw err;
fixtures.assertSimilar(fixtures.expected('unflatten-swiss.png'), data, { threshold: 1 }, done);
});
});
it('no unflatten', function (done) {
sharp(fixtures.inputPng).unflatten(false)
.toBuffer(function (err, data) {
if (err) throw err;
fixtures.assertSimilar(fixtures.inputPng, data, { threshold: 0 }, done);
});
});
});