Skip to content

Commit

Permalink
Options for trim op must be an Object, add lineArt #2363
Browse files Browse the repository at this point in the history
  • Loading branch information
lovell committed Nov 4, 2023
1 parent 2e7c606 commit 0bd1715
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 125 deletions.
47 changes: 22 additions & 25 deletions docs/api-resize.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,16 +251,15 @@ sharp(input)


## trim
> trim(trim) ⇒ <code>Sharp</code>
> trim([options]) ⇒ <code>Sharp</code>
Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.

Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.

If the result of this operation would trim an image to nothing then no change is made.

The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
will contain `trimOffsetLeft` and `trimOffsetTop` properties.
The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.


**Throws**:
Expand All @@ -270,46 +269,44 @@ will contain `trimOffsetLeft` and `trimOffsetTop` properties.

| Param | Type | Default | Description |
| --- | --- | --- | --- |
| trim | <code>string</code> \| <code>number</code> \| <code>Object</code> | | the specific background colour to trim, the threshold for doing so or an Object with both. |
| [trim.background] | <code>string</code> \| <code>Object</code> | <code>&quot;&#x27;top-left pixel&#x27;&quot;</code> | background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel. |
| [trim.threshold] | <code>number</code> | <code>10</code> | the allowed difference from the above colour, a positive number. |
| [options] | <code>Object</code> | | |
| [options.background] | <code>string</code> \| <code>Object</code> | <code>&quot;&#x27;top-left pixel&#x27;&quot;</code> | Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel. |
| [options.threshold] | <code>number</code> | <code>10</code> | Allowed difference from the above colour, a positive number. |
| [options.lineArt] | <code>boolean</code> | <code>false</code> | Does the input more closely resemble line art (e.g. vector) rather than being photographic? |

**Example**
```js
// Trim pixels with a colour similar to that of the top-left pixel.
sharp(input)
await sharp(input)
.trim()
.toFile(output, function(err, info) {
...
});
.toFile(output);
```
**Example**
```js
// Trim pixels with the exact same colour as that of the top-left pixel.
sharp(input)
.trim(0)
.toFile(output, function(err, info) {
...
});
await sharp(input)
.trim({
threshold: 0
})
.toFile(output);
```
**Example**
```js
// Trim only pixels with a similar colour to red.
sharp(input)
.trim("#FF0000")
.toFile(output, function(err, info) {
...
});
// Assume input is line art and trim only pixels with a similar colour to red.
const output = await sharp(input)
.trim({
background: "#FF0000",
lineArt: true
})
.toBuffer();
```
**Example**
```js
// Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
sharp(input)
const output = await sharp(input)
.trim({
background: "yellow",
threshold: 42,
})
.toFile(output, function(err, info) {
...
});
.toBuffer();
```
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Requires libvips v8.15.0

* Remove `sharp.vendor`.

* Options for `trim` operation must be an Object, add new `lineArt` option.
[#2363](https://github.com/lovell/sharp/issues/2363)

* Ensure all `Error` objects contain a `stack` property.
[#3653](https://github.com/lovell/sharp/issues/3653)

Expand Down
2 changes: 1 addition & 1 deletion docs/search-index.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion lib/constructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ const Sharp = function (input, options) {
threshold: 0,
thresholdGrayscale: true,
trimBackground: [],
trimThreshold: 0,
trimThreshold: -1,
trimLineArt: false,
gamma: 0,
gammaOut: 0,
greyscale: false,
Expand Down
10 changes: 6 additions & 4 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -847,11 +847,11 @@ declare namespace sharp {
* Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.
* Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
* The info response Object will contain trimOffsetLeft and trimOffsetTop properties.
* @param trim The specific background colour to trim, the threshold for doing so or an Object with both.
* @param options trim options
* @throws {Error} Invalid parameters
* @returns A sharp instance that can be used to chain operations
*/
trim(trim?: string | number | TrimOptions): Sharp;
trim(options?: TrimOptions): Sharp;

//#endregion
}
Expand Down Expand Up @@ -1342,10 +1342,12 @@ declare namespace sharp {
}

interface TrimOptions {
/** background colour, parsed by the color module, defaults to that of the top-left pixel. (optional) */
/** Background colour, parsed by the color module, defaults to that of the top-left pixel. (optional) */
background?: Color | undefined;
/** the allowed difference from the above colour, a positive number. (optional, default `10`) */
/** Allowed difference from the above colour, a positive number. (optional, default 10) */
threshold?: number | undefined;
/** Does the input more closely resemble line art (e.g. vector) rather than being photographic? (optional, default false) */
lineArt?: boolean | undefined;
}

interface RawOptions {
Expand Down
87 changes: 42 additions & 45 deletions lib/resize.js
Original file line number Diff line number Diff line change
Expand Up @@ -494,70 +494,67 @@ function extract (options) {
*
* If the result of this operation would trim an image to nothing then no change is made.
*
* The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
* will contain `trimOffsetLeft` and `trimOffsetTop` properties.
* The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
*
* @example
* // Trim pixels with a colour similar to that of the top-left pixel.
* sharp(input)
* await sharp(input)
* .trim()
* .toFile(output, function(err, info) {
* ...
* });
* .toFile(output);
*
* @example
* // Trim pixels with the exact same colour as that of the top-left pixel.
* sharp(input)
* .trim(0)
* .toFile(output, function(err, info) {
* ...
* });
* await sharp(input)
* .trim({
* threshold: 0
* })
* .toFile(output);
*
* @example
* // Trim only pixels with a similar colour to red.
* sharp(input)
* .trim("#FF0000")
* .toFile(output, function(err, info) {
* ...
* });
* // Assume input is line art and trim only pixels with a similar colour to red.
* const output = await sharp(input)
* .trim({
* background: "#FF0000",
* lineArt: true
* })
* .toBuffer();
*
* @example
* // Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
* sharp(input)
* const output = await sharp(input)
* .trim({
* background: "yellow",
* threshold: 42,
* })
* .toFile(output, function(err, info) {
* ...
* });
* .toBuffer();
*
* @param {string|number|Object} trim - the specific background colour to trim, the threshold for doing so or an Object with both.
* @param {string|Object} [trim.background='top-left pixel'] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
* @param {number} [trim.threshold=10] - the allowed difference from the above colour, a positive number.
* @param {Object} [options]
* @param {string|Object} [options.background='top-left pixel'] - Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
* @param {number} [options.threshold=10] - Allowed difference from the above colour, a positive number.
* @param {boolean} [options.lineArt=false] - Does the input more closely resemble line art (e.g. vector) rather than being photographic?
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function trim (trim) {
if (!is.defined(trim)) {
this.options.trimThreshold = 10;
} else if (is.string(trim)) {
this._setBackgroundColourOption('trimBackground', trim);
this.options.trimThreshold = 10;
} else if (is.number(trim)) {
if (trim >= 0) {
this.options.trimThreshold = trim;
} else {
throw is.invalidParameterError('threshold', 'positive number', trim);
}
} else if (is.object(trim)) {
this._setBackgroundColourOption('trimBackground', trim.background);
if (!is.defined(trim.threshold)) {
this.options.trimThreshold = 10;
} else if (is.number(trim.threshold) && trim.threshold >= 0) {
this.options.trimThreshold = trim.threshold;
function trim (options) {
this.options.trimThreshold = 10;
if (is.defined(options)) {
if (is.object(options)) {
if (is.defined(options.background)) {
this._setBackgroundColourOption('trimBackground', options.background);
}
if (is.defined(options.threshold)) {
if (is.number(options.threshold) && options.threshold >= 0) {
this.options.trimThreshold = options.threshold;
} else {
throw is.invalidParameterError('threshold', 'positive number', options.threshold);
}
}
if (is.defined(options.lineArt)) {
this._setBooleanOption('trimLineArt', options.lineArt);
}
} else {
throw is.invalidParameterError('threshold', 'positive number', trim);
throw is.invalidParameterError('trim', 'object', options);
}
} else {
throw is.invalidParameterError('trim', 'string, number or object', trim);
}
if (isRotationExpected(this.options)) {
this.options.rotateBeforePreExtract = true;
Expand Down
4 changes: 3 additions & 1 deletion src/operations.cc
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ namespace sharp {
/*
Trim an image
*/
VImage Trim(VImage image, std::vector<double> background, double threshold) {
VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt) {
if (image.width() < 3 && image.height() < 3) {
throw VError("Image to trim must be at least 3x3 pixels");
}
Expand All @@ -287,13 +287,15 @@ namespace sharp {
int left, top, width, height;
left = image.find_trim(&top, &width, &height, VImage::option()
->set("background", background)
->set("line_art", lineArt)
->set("threshold", threshold));
if (HasAlpha(image)) {
// Search alpha channel (A)
int leftA, topA, widthA, heightA;
VImage alpha = image[image.bands() - 1];
leftA = alpha.find_trim(&topA, &widthA, &heightA, VImage::option()
->set("background", backgroundAlpha)
->set("line_art", lineArt)
->set("threshold", threshold));
if (widthA > 0 && heightA > 0) {
if (width > 0 && height > 0) {
Expand Down
2 changes: 1 addition & 1 deletion src/operations.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ namespace sharp {
/*
Trim an image
*/
VImage Trim(VImage image, std::vector<double> background, double const threshold);
VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt);

/*
* Linear adjustment (a * in + b)
Expand Down
10 changes: 5 additions & 5 deletions src/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ class PipelineWorker : public Napi::AsyncWorker {
}

// Trim
if (baton->trimThreshold > 0.0) {
if (baton->trimThreshold >= 0.0) {
MultiPageUnsupported(nPages, "Trim");
image = sharp::StaySequential(image, access);
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold);
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold, baton->trimLineArt);
baton->trimOffsetLeft = image.xoffset();
baton->trimOffsetTop = image.yoffset();
}
Expand Down Expand Up @@ -182,7 +182,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// - trimming or pre-resize extract isn't required;
// - input colourspace is not specified;
bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0 &&
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold < 0.0 &&
baton->colourspaceInput == VIPS_INTERPRETATION_LAST && !shouldRotateBefore;

if (shouldPreShrink) {
Expand Down Expand Up @@ -1248,11 +1248,10 @@ class PipelineWorker : public Napi::AsyncWorker {
info.Set("attentionX", static_cast<int32_t>(baton->attentionX));
info.Set("attentionY", static_cast<int32_t>(baton->attentionY));
}
if (baton->trimThreshold > 0.0) {
if (baton->trimThreshold >= 0.0) {
info.Set("trimOffsetLeft", static_cast<int32_t>(baton->trimOffsetLeft));
info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
}

if (baton->input->textAutofitDpi) {
info.Set("textAutofitDpi", static_cast<uint32_t>(baton->input->textAutofitDpi));
}
Expand Down Expand Up @@ -1519,6 +1518,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
baton->trimBackground = sharp::AttrAsVectorOfDouble(options, "trimBackground");
baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
baton->trimLineArt = sharp::AttrAsBool(options, "trimLineArt");
baton->gamma = sharp::AttrAsDouble(options, "gamma");
baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
Expand Down
4 changes: 3 additions & 1 deletion src/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ struct PipelineBaton {
bool thresholdGrayscale;
std::vector<double> trimBackground;
double trimThreshold;
bool trimLineArt;
int trimOffsetLeft;
int trimOffsetTop;
std::vector<double> linearA;
Expand Down Expand Up @@ -260,7 +261,8 @@ struct PipelineBaton {
threshold(0),
thresholdGrayscale(true),
trimBackground{},
trimThreshold(0.0),
trimThreshold(-1.0),
trimLineArt(false),
trimOffsetLeft(0),
trimOffsetTop(0),
linearA{},
Expand Down
4 changes: 2 additions & 2 deletions test/types/sharp.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,8 +572,8 @@ const nohalo: string = sharp.interpolators.nohalo;
const vertexSplitQuadraticBasisSpline: string = sharp.interpolators.vertexSplitQuadraticBasisSpline;

// Triming
sharp(input).trim('#000').toBuffer();
sharp(input).trim(10).toBuffer();
sharp(input).trim({ background: '#000' }).toBuffer();
sharp(input).trim({ threshold: 10, lineArt: true }).toBuffer();
sharp(input).trim({ background: '#bf1942', threshold: 30 }).toBuffer();

// Text input
Expand Down
Loading

0 comments on commit 0bd1715

Please sign in to comment.