chore: add jimp dependency, icon padding script, and update configuration
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
# v0.22.8 (Thu May 11 2023)
|
||||
|
||||
:tada: This release contains work from a new contributor! :tada:
|
||||
|
||||
Thank you, Sjoerd ([@sjoerd108](https://github.com/sjoerd108)), for all your work!
|
||||
|
||||
#### 🐛 Bug Fix
|
||||
|
||||
- Fix autocrop mixing up east and west [#1227](https://github.com/jimp-dev/jimp/pull/1227) ([@sjoerd108](https://github.com/sjoerd108))
|
||||
|
||||
#### Authors: 1
|
||||
|
||||
- Sjoerd ([@sjoerd108](https://github.com/sjoerd108))
|
||||
|
||||
---
|
||||
|
||||
# v0.22.1 (Mon Feb 06 2023)
|
||||
|
||||
#### 🏠 Internal
|
||||
|
||||
- rename master to main [#1169](https://github.com/jimp-dev/jimp/pull/1169) ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
#### Authors: 1
|
||||
|
||||
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
---
|
||||
|
||||
# v0.22.0 (Mon Feb 06 2023)
|
||||
|
||||
#### 🏠 Internal
|
||||
|
||||
- switch from should to expect [#1163](https://github.com/jimp-dev/jimp/pull/1163) ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
- Change test matching strategy to include all test files [#1161](https://github.com/jimp-dev/jimp/pull/1161) ([@danielholmes](https://github.com/danielholmes))
|
||||
|
||||
#### Authors: 2
|
||||
|
||||
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
- Daniel Holmes ([@danielholmes](https://github.com/danielholmes))
|
||||
|
||||
---
|
||||
|
||||
# v0.21.0 (Sun Feb 05 2023)
|
||||
|
||||
### Release Notes
|
||||
|
||||
#### Babel Refactor ([#1149](https://github.com/jimp-dev/jimp/pull/1149))
|
||||
|
||||
Marking this as a "breaking release" because it might change what deps need to be installed.
|
||||
|
||||
All modules should be exported as valid cjs and esm
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
#### 💥 Breaking Change
|
||||
|
||||
- Babel Refactor [#1149](https://github.com/jimp-dev/jimp/pull/1149) ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
#### Authors: 1
|
||||
|
||||
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
---
|
||||
|
||||
# v0.16.3 (Sat Feb 04 2023)
|
||||
|
||||
#### 🐛 Bug Fix
|
||||
|
||||
- fixed plugin-crop full width slices math [#1073](https://github.com/jimp-dev/jimp/pull/1073) ([@endreszabo](https://github.com/endreszabo))
|
||||
|
||||
#### ⚠️ Pushed to `main`
|
||||
|
||||
- upgrade prettier ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
#### Authors: 2
|
||||
|
||||
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
- Endre Szabo ([@endreszabo](https://github.com/endreszabo))
|
||||
|
||||
---
|
||||
|
||||
# v0.16.0 (Sat Aug 08 2020)
|
||||
|
||||
#### 🚀 Enhancement
|
||||
|
||||
- added ability to ignore sides on autocrop [#924](https://github.com/oliver-moran/jimp/pull/924) ([@cbanfiel](https://github.com/cbanfiel))
|
||||
|
||||
#### Authors: 1
|
||||
|
||||
- Chad Banfield ([@cbanfiel](https://github.com/cbanfiel))
|
||||
|
||||
---
|
||||
|
||||
# v0.11.0 (Fri May 15 2020)
|
||||
|
||||
#### 🚀 Enhancement
|
||||
|
||||
- Removed Core-JS as a dependency. [#882](https://github.com/oliver-moran/jimp/pull/882) ([@EricRabil](https://github.com/EricRabil))
|
||||
|
||||
#### Authors: 1
|
||||
|
||||
- Eric Rabil ([@EricRabil](https://github.com/EricRabil))
|
||||
|
||||
---
|
||||
|
||||
# v0.9.3 (Tue Nov 26 2019)
|
||||
|
||||
#### 🐛 Bug Fix
|
||||
|
||||
- `@jimp/cli`, `@jimp/core`, `@jimp/custom`, `jimp`, `@jimp/plugin-blit`, `@jimp/plugin-blur`, `@jimp/plugin-circle`, `@jimp/plugin-color`, `@jimp/plugin-contain`, `@jimp/plugin-cover`, `@jimp/plugin-crop`, `@jimp/plugin-displace`, `@jimp/plugin-dither`, `@jimp/plugin-fisheye`, `@jimp/plugin-flip`, `@jimp/plugin-gaussian`, `@jimp/plugin-invert`, `@jimp/plugin-mask`, `@jimp/plugin-normalize`, `@jimp/plugin-print`, `@jimp/plugin-resize`, `@jimp/plugin-rotate`, `@jimp/plugin-scale`, `@jimp/plugin-shadow`, `@jimp/plugin-threshold`, `@jimp/plugins`, `@jimp/test-utils`, `@jimp/bmp`, `@jimp/gif`, `@jimp/jpeg`, `@jimp/png`, `@jimp/tiff`, `@jimp/types`, `@jimp/utils`
|
||||
- Fix regeneratorRuntime errors [#815](https://github.com/oliver-moran/jimp/pull/815) ([@crutchcorn](https://github.com/crutchcorn) [@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
#### Authors: 2
|
||||
|
||||
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
- Corbin Crutchley ([@crutchcorn](https://github.com/crutchcorn))
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Oliver Moran
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<div align="center">
|
||||
<img width="200" height="200"
|
||||
src="https://s3.amazonaws.com/pix.iemoji.com/images/emoji/apple/ios-11/256/crayon.png">
|
||||
<h1>@jimp/plugin-crop</h1>
|
||||
<p>Crop an image.</p>
|
||||
</div>
|
||||
|
||||
## Crop
|
||||
|
||||
Crops the image at a given point to a give size
|
||||
|
||||
- @param {number} x the x coordinate to crop form
|
||||
- @param {number} y the y coordinate to crop form
|
||||
- @param w the width of the crop region
|
||||
- @param h the height of the crop region
|
||||
- @param {function(Error, Jimp)} cb (optional) a callback for when complete
|
||||
|
||||
```js
|
||||
import jimp from "jimp";
|
||||
|
||||
async function main() {
|
||||
const image = await jimp.read("test/image.png");
|
||||
|
||||
image.crop(150, 150);
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
## AutoCrop
|
||||
|
||||
AutoCrop same color borders from this image
|
||||
|
||||
- @param {number} tolerance (optional): a percent value of tolerance for pixels color difference (default: 0.0002%)
|
||||
- @param {boolean} cropOnlyFrames (optional): flag to crop only real frames: all 4 sides of the image must have some border (default: true)
|
||||
|
||||
or
|
||||
|
||||
- @param {object} options object
|
||||
- tolerance (optional): a percent value of tolerance for pixels color difference (default: 0.0002%)
|
||||
- cropOnlyFrames (optional): flag to crop only real frames: all 4 sides of the image must have some border (default: true)
|
||||
- cropSymmetric (optional): flag to force cropping top be symmetric. north and south / east and west are cropped by the same value
|
||||
- leaveBorder (optional): integer of how many pixels of the background color to leave around the image
|
||||
|
||||
```js
|
||||
import jimp from "jimp";
|
||||
|
||||
async function main() {
|
||||
const image = await jimp.read("test/image.png");
|
||||
|
||||
image.autocrop();
|
||||
image.autocrop({ cropOnlyFrames: false, cropSymmetric: true });
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
/* eslint-disable no-labels */
|
||||
|
||||
import { throwError, isNodePattern } from "@jimp/utils";
|
||||
export default function pluginCrop(event) {
|
||||
/**
|
||||
* Crops the image at a given point to a give size
|
||||
* @param {number} x the x coordinate to crop form
|
||||
* @param {number} y the y coordinate to crop form
|
||||
* @param w the width of the crop region
|
||||
* @param h the height of the crop region
|
||||
* @param {function(Error, Jimp)} cb (optional) a callback for when complete
|
||||
* @returns {Jimp} this for chaining of methods
|
||||
*/
|
||||
event("crop", function (x, y, w, h, cb) {
|
||||
if (typeof x !== "number" || typeof y !== "number") return throwError.call(this, "x and y must be numbers", cb);
|
||||
if (typeof w !== "number" || typeof h !== "number") return throwError.call(this, "w and h must be numbers", cb);
|
||||
|
||||
// round input
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
if (x === 0 && w === this.bitmap.width) {
|
||||
// shortcut
|
||||
const start = w * y + x << 2;
|
||||
const end = start + (h * w << 2);
|
||||
this.bitmap.data = this.bitmap.data.slice(start, end);
|
||||
} else {
|
||||
const bitmap = Buffer.allocUnsafe(w * h * 4);
|
||||
let offset = 0;
|
||||
this.scanQuiet(x, y, w, h, function (x, y, idx) {
|
||||
const data = this.bitmap.data.readUInt32BE(idx, true);
|
||||
bitmap.writeUInt32BE(data, offset, true);
|
||||
offset += 4;
|
||||
});
|
||||
this.bitmap.data = bitmap;
|
||||
}
|
||||
this.bitmap.width = w;
|
||||
this.bitmap.height = h;
|
||||
if (isNodePattern(cb)) {
|
||||
cb.call(this, null, this);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
return {
|
||||
class: {
|
||||
/**
|
||||
* Autocrop same color borders from this image
|
||||
* @param {number} tolerance (optional): a percent value of tolerance for pixels color difference (default: 0.0002%)
|
||||
* @param {boolean} cropOnlyFrames (optional): flag to crop only real frames: all 4 sides of the image must have some border (default: true)
|
||||
* @param {function(Error, Jimp)} cb (optional): a callback for when complete (default: no callback)
|
||||
* @returns {Jimp} this for chaining of methods
|
||||
*/
|
||||
autocrop() {
|
||||
const w = this.bitmap.width;
|
||||
const h = this.bitmap.height;
|
||||
const minPixelsPerSide = 1; // to avoid cropping completely the image, resulting in an invalid 0 sized image
|
||||
|
||||
let cb; // callback
|
||||
let leaveBorder = 0; // Amount of pixels in border to leave
|
||||
let tolerance = 0.0002; // percent of color difference tolerance (default value)
|
||||
let cropOnlyFrames = true; // flag to force cropping only if the image has a real "frame"
|
||||
// i.e. all 4 sides have some border (default value)
|
||||
let cropSymmetric = false; // flag to force cropping top be symmetric.
|
||||
// i.e. north and south / east and west are cropped by the same value
|
||||
let ignoreSides = {
|
||||
north: false,
|
||||
south: false,
|
||||
east: false,
|
||||
west: false
|
||||
};
|
||||
|
||||
// parse arguments
|
||||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
for (let a = 0, len = args.length; a < len; a++) {
|
||||
if (typeof args[a] === "number") {
|
||||
// tolerance value passed
|
||||
tolerance = args[a];
|
||||
}
|
||||
if (typeof args[a] === "boolean") {
|
||||
// cropOnlyFrames value passed
|
||||
cropOnlyFrames = args[a];
|
||||
}
|
||||
if (typeof args[a] === "function") {
|
||||
// callback value passed
|
||||
cb = args[a];
|
||||
}
|
||||
if (typeof args[a] === "object") {
|
||||
// config object passed
|
||||
const config = args[a];
|
||||
if (typeof config.tolerance !== "undefined") {
|
||||
({
|
||||
tolerance
|
||||
} = config);
|
||||
}
|
||||
if (typeof config.cropOnlyFrames !== "undefined") {
|
||||
({
|
||||
cropOnlyFrames
|
||||
} = config);
|
||||
}
|
||||
if (typeof config.cropSymmetric !== "undefined") {
|
||||
({
|
||||
cropSymmetric
|
||||
} = config);
|
||||
}
|
||||
if (typeof config.leaveBorder !== "undefined") {
|
||||
({
|
||||
leaveBorder
|
||||
} = config);
|
||||
}
|
||||
if (typeof config.ignoreSides !== "undefined") {
|
||||
({
|
||||
ignoreSides
|
||||
} = config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All borders must be of the same color as the top left pixel, to be cropped.
|
||||
* It should be possible to crop borders each with a different color,
|
||||
* but since there are many ways for corners to intersect, it would
|
||||
* introduce unnecessary complexity to the algorithm.
|
||||
*/
|
||||
|
||||
// scan each side for same color borders
|
||||
let colorTarget = this.getPixelColor(0, 0); // top left pixel color is the target color
|
||||
const rgba1 = this.constructor.intToRGBA(colorTarget);
|
||||
|
||||
// for north and east sides
|
||||
let northPixelsToCrop = 0;
|
||||
let eastPixelsToCrop = 0;
|
||||
let southPixelsToCrop = 0;
|
||||
let westPixelsToCrop = 0;
|
||||
|
||||
// north side (scan rows from north to south)
|
||||
colorTarget = this.getPixelColor(0, 0);
|
||||
if (!ignoreSides.north) {
|
||||
north: for (let y = 0; y < h - minPixelsPerSide; y++) {
|
||||
for (let x = 0; x < w; x++) {
|
||||
const colorXY = this.getPixelColor(x, y);
|
||||
const rgba2 = this.constructor.intToRGBA(colorXY);
|
||||
if (this.constructor.colorDiff(rgba1, rgba2) > tolerance) {
|
||||
// this pixel is too distant from the first one: abort this side scan
|
||||
break north;
|
||||
}
|
||||
}
|
||||
|
||||
// this row contains all pixels with the same color: increment this side pixels to crop
|
||||
northPixelsToCrop++;
|
||||
}
|
||||
}
|
||||
|
||||
// west side (scan columns from west to east)
|
||||
colorTarget = this.getPixelColor(w, 0);
|
||||
if (!ignoreSides.west) {
|
||||
west: for (let x = 0; x < w - minPixelsPerSide; x++) {
|
||||
for (let y = 0 + northPixelsToCrop; y < h; y++) {
|
||||
const colorXY = this.getPixelColor(x, y);
|
||||
const rgba2 = this.constructor.intToRGBA(colorXY);
|
||||
if (this.constructor.colorDiff(rgba1, rgba2) > tolerance) {
|
||||
// this pixel is too distant from the first one: abort this side scan
|
||||
break west;
|
||||
}
|
||||
}
|
||||
|
||||
// this column contains all pixels with the same color: increment this side pixels to crop
|
||||
westPixelsToCrop++;
|
||||
}
|
||||
}
|
||||
|
||||
// south side (scan rows from south to north)
|
||||
colorTarget = this.getPixelColor(0, h);
|
||||
if (!ignoreSides.south) {
|
||||
south: for (let y = h - 1; y >= northPixelsToCrop + minPixelsPerSide; y--) {
|
||||
for (let x = w - eastPixelsToCrop - 1; x >= 0; x--) {
|
||||
const colorXY = this.getPixelColor(x, y);
|
||||
const rgba2 = this.constructor.intToRGBA(colorXY);
|
||||
if (this.constructor.colorDiff(rgba1, rgba2) > tolerance) {
|
||||
// this pixel is too distant from the first one: abort this side scan
|
||||
break south;
|
||||
}
|
||||
}
|
||||
|
||||
// this row contains all pixels with the same color: increment this side pixels to crop
|
||||
southPixelsToCrop++;
|
||||
}
|
||||
}
|
||||
|
||||
// east side (scan columns from east to west)
|
||||
colorTarget = this.getPixelColor(w, h);
|
||||
if (!ignoreSides.east) {
|
||||
east: for (let x = w - 1; x >= 0 + westPixelsToCrop + minPixelsPerSide; x--) {
|
||||
for (let y = h - 1; y >= 0 + northPixelsToCrop; y--) {
|
||||
const colorXY = this.getPixelColor(x, y);
|
||||
const rgba2 = this.constructor.intToRGBA(colorXY);
|
||||
if (this.constructor.colorDiff(rgba1, rgba2) > tolerance) {
|
||||
// this pixel is too distant from the first one: abort this side scan
|
||||
break east;
|
||||
}
|
||||
}
|
||||
|
||||
// this column contains all pixels with the same color: increment this side pixels to crop
|
||||
eastPixelsToCrop++;
|
||||
}
|
||||
}
|
||||
|
||||
// decide if a crop is needed
|
||||
let doCrop = false;
|
||||
|
||||
// apply leaveBorder
|
||||
westPixelsToCrop -= leaveBorder;
|
||||
eastPixelsToCrop -= leaveBorder;
|
||||
northPixelsToCrop -= leaveBorder;
|
||||
southPixelsToCrop -= leaveBorder;
|
||||
if (cropSymmetric) {
|
||||
const horizontal = Math.min(eastPixelsToCrop, westPixelsToCrop);
|
||||
const vertical = Math.min(northPixelsToCrop, southPixelsToCrop);
|
||||
westPixelsToCrop = horizontal;
|
||||
eastPixelsToCrop = horizontal;
|
||||
northPixelsToCrop = vertical;
|
||||
southPixelsToCrop = vertical;
|
||||
}
|
||||
|
||||
// make sure that crops are >= 0
|
||||
westPixelsToCrop = westPixelsToCrop >= 0 ? westPixelsToCrop : 0;
|
||||
eastPixelsToCrop = eastPixelsToCrop >= 0 ? eastPixelsToCrop : 0;
|
||||
northPixelsToCrop = northPixelsToCrop >= 0 ? northPixelsToCrop : 0;
|
||||
southPixelsToCrop = southPixelsToCrop >= 0 ? southPixelsToCrop : 0;
|
||||
|
||||
// safety checks
|
||||
const widthOfRemainingPixels = w - (westPixelsToCrop + eastPixelsToCrop);
|
||||
const heightOfRemainingPixels = h - (southPixelsToCrop + northPixelsToCrop);
|
||||
if (cropOnlyFrames) {
|
||||
// crop image if all sides should be cropped
|
||||
doCrop = eastPixelsToCrop !== 0 && northPixelsToCrop !== 0 && westPixelsToCrop !== 0 && southPixelsToCrop !== 0;
|
||||
} else {
|
||||
// crop image if at least one side should be cropped
|
||||
doCrop = eastPixelsToCrop !== 0 || northPixelsToCrop !== 0 || westPixelsToCrop !== 0 || southPixelsToCrop !== 0;
|
||||
}
|
||||
if (doCrop) {
|
||||
// do the real crop
|
||||
this.crop(westPixelsToCrop, northPixelsToCrop, widthOfRemainingPixels, heightOfRemainingPixels);
|
||||
}
|
||||
if (isNodePattern(cb)) {
|
||||
cb.call(this, null, this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+46
@@ -0,0 +1,46 @@
|
||||
import { Jimp, ImageCallback } from "@jimp/core";
|
||||
|
||||
interface CropClass {
|
||||
crop(
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
cb?: ImageCallback<this>
|
||||
): this;
|
||||
cropQuiet(
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
cb?: ImageCallback<this>
|
||||
): this;
|
||||
autocrop(tolerance?: number, cb?: ImageCallback<this>): this;
|
||||
autocrop(cropOnlyFrames?: boolean, cb?: ImageCallback<this>): this;
|
||||
autocrop(
|
||||
tolerance?: number,
|
||||
cropOnlyFrames?: boolean,
|
||||
cb?: ImageCallback<this>
|
||||
): this;
|
||||
autocrop(
|
||||
options: {
|
||||
tolerance?: number;
|
||||
cropOnlyFrames?: boolean;
|
||||
cropSymmetric?: boolean;
|
||||
leaveBorder?: number;
|
||||
ignoreSides?: {
|
||||
north: boolean;
|
||||
south: boolean;
|
||||
east: boolean;
|
||||
west: boolean;
|
||||
};
|
||||
},
|
||||
cb?: ImageCallback<this>
|
||||
): this;
|
||||
}
|
||||
|
||||
interface Crop {
|
||||
class: CropClass;
|
||||
}
|
||||
|
||||
export default function (): Crop;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@jimp/plugin-crop",
|
||||
"version": "0.22.12",
|
||||
"description": "crop an image.",
|
||||
"main": "dist/index.js",
|
||||
"module": "es/index.js",
|
||||
"repository": "jimp-dev/jimp",
|
||||
"types": "index.d.ts",
|
||||
"scripts": {
|
||||
"test": "cross-env BABEL_ENV=test mocha --require @babel/register --recursive test --extension js",
|
||||
"test:watch": "npm run test -- --reporter min --watch",
|
||||
"test:coverage": "nyc npm run test",
|
||||
"build": "npm run build:node:production && npm run build:module",
|
||||
"build:watch": "npm run build:node:debug -- -- --watch --verbose",
|
||||
"build:debug": "npm run build:node:debug",
|
||||
"build:module": "cross-env BABEL_ENV=module babel src -d es --source-maps --config-file ../../babel.config.js",
|
||||
"build:node": "babel src -d dist --source-maps --config-file ../../babel.config.js",
|
||||
"build:node:debug": "cross-env BABEL_ENV=development npm run build:node",
|
||||
"build:node:production": "cross-env BABEL_ENV=production npm run build:node"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jimp/custom": "^0.22.12",
|
||||
"@jimp/test-utils": "^0.22.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@jimp/custom": ">=0.3.5"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "a4a8d6364bbf97629749e196f3b0a4c94c9a7abc"
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
/* eslint-disable no-labels */
|
||||
|
||||
import { throwError, isNodePattern } from "@jimp/utils";
|
||||
|
||||
export default function pluginCrop(event) {
|
||||
/**
|
||||
* Crops the image at a given point to a give size
|
||||
* @param {number} x the x coordinate to crop form
|
||||
* @param {number} y the y coordinate to crop form
|
||||
* @param w the width of the crop region
|
||||
* @param h the height of the crop region
|
||||
* @param {function(Error, Jimp)} cb (optional) a callback for when complete
|
||||
* @returns {Jimp} this for chaining of methods
|
||||
*/
|
||||
event("crop", function (x, y, w, h, cb) {
|
||||
if (typeof x !== "number" || typeof y !== "number")
|
||||
return throwError.call(this, "x and y must be numbers", cb);
|
||||
if (typeof w !== "number" || typeof h !== "number")
|
||||
return throwError.call(this, "w and h must be numbers", cb);
|
||||
|
||||
// round input
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
|
||||
if (x === 0 && w === this.bitmap.width) {
|
||||
// shortcut
|
||||
const start = (w * y + x) << 2;
|
||||
const end = start + ((h * w) << 2);
|
||||
|
||||
this.bitmap.data = this.bitmap.data.slice(start, end);
|
||||
} else {
|
||||
const bitmap = Buffer.allocUnsafe(w * h * 4);
|
||||
let offset = 0;
|
||||
|
||||
this.scanQuiet(x, y, w, h, function (x, y, idx) {
|
||||
const data = this.bitmap.data.readUInt32BE(idx, true);
|
||||
bitmap.writeUInt32BE(data, offset, true);
|
||||
offset += 4;
|
||||
});
|
||||
|
||||
this.bitmap.data = bitmap;
|
||||
}
|
||||
|
||||
this.bitmap.width = w;
|
||||
this.bitmap.height = h;
|
||||
|
||||
if (isNodePattern(cb)) {
|
||||
cb.call(this, null, this);
|
||||
}
|
||||
|
||||
return this;
|
||||
});
|
||||
|
||||
return {
|
||||
class: {
|
||||
/**
|
||||
* Autocrop same color borders from this image
|
||||
* @param {number} tolerance (optional): a percent value of tolerance for pixels color difference (default: 0.0002%)
|
||||
* @param {boolean} cropOnlyFrames (optional): flag to crop only real frames: all 4 sides of the image must have some border (default: true)
|
||||
* @param {function(Error, Jimp)} cb (optional): a callback for when complete (default: no callback)
|
||||
* @returns {Jimp} this for chaining of methods
|
||||
*/
|
||||
autocrop(...args) {
|
||||
const w = this.bitmap.width;
|
||||
const h = this.bitmap.height;
|
||||
const minPixelsPerSide = 1; // to avoid cropping completely the image, resulting in an invalid 0 sized image
|
||||
|
||||
let cb; // callback
|
||||
let leaveBorder = 0; // Amount of pixels in border to leave
|
||||
let tolerance = 0.0002; // percent of color difference tolerance (default value)
|
||||
let cropOnlyFrames = true; // flag to force cropping only if the image has a real "frame"
|
||||
// i.e. all 4 sides have some border (default value)
|
||||
let cropSymmetric = false; // flag to force cropping top be symmetric.
|
||||
// i.e. north and south / east and west are cropped by the same value
|
||||
let ignoreSides = {
|
||||
north: false,
|
||||
south: false,
|
||||
east: false,
|
||||
west: false,
|
||||
};
|
||||
|
||||
// parse arguments
|
||||
for (let a = 0, len = args.length; a < len; a++) {
|
||||
if (typeof args[a] === "number") {
|
||||
// tolerance value passed
|
||||
tolerance = args[a];
|
||||
}
|
||||
|
||||
if (typeof args[a] === "boolean") {
|
||||
// cropOnlyFrames value passed
|
||||
cropOnlyFrames = args[a];
|
||||
}
|
||||
|
||||
if (typeof args[a] === "function") {
|
||||
// callback value passed
|
||||
cb = args[a];
|
||||
}
|
||||
|
||||
if (typeof args[a] === "object") {
|
||||
// config object passed
|
||||
const config = args[a];
|
||||
|
||||
if (typeof config.tolerance !== "undefined") {
|
||||
({ tolerance } = config);
|
||||
}
|
||||
|
||||
if (typeof config.cropOnlyFrames !== "undefined") {
|
||||
({ cropOnlyFrames } = config);
|
||||
}
|
||||
|
||||
if (typeof config.cropSymmetric !== "undefined") {
|
||||
({ cropSymmetric } = config);
|
||||
}
|
||||
|
||||
if (typeof config.leaveBorder !== "undefined") {
|
||||
({ leaveBorder } = config);
|
||||
}
|
||||
|
||||
if (typeof config.ignoreSides !== "undefined") {
|
||||
({ ignoreSides } = config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All borders must be of the same color as the top left pixel, to be cropped.
|
||||
* It should be possible to crop borders each with a different color,
|
||||
* but since there are many ways for corners to intersect, it would
|
||||
* introduce unnecessary complexity to the algorithm.
|
||||
*/
|
||||
|
||||
// scan each side for same color borders
|
||||
let colorTarget = this.getPixelColor(0, 0); // top left pixel color is the target color
|
||||
const rgba1 = this.constructor.intToRGBA(colorTarget);
|
||||
|
||||
// for north and east sides
|
||||
let northPixelsToCrop = 0;
|
||||
let eastPixelsToCrop = 0;
|
||||
let southPixelsToCrop = 0;
|
||||
let westPixelsToCrop = 0;
|
||||
|
||||
// north side (scan rows from north to south)
|
||||
colorTarget = this.getPixelColor(0, 0);
|
||||
if (!ignoreSides.north) {
|
||||
north: for (let y = 0; y < h - minPixelsPerSide; y++) {
|
||||
for (let x = 0; x < w; x++) {
|
||||
const colorXY = this.getPixelColor(x, y);
|
||||
const rgba2 = this.constructor.intToRGBA(colorXY);
|
||||
|
||||
if (this.constructor.colorDiff(rgba1, rgba2) > tolerance) {
|
||||
// this pixel is too distant from the first one: abort this side scan
|
||||
break north;
|
||||
}
|
||||
}
|
||||
|
||||
// this row contains all pixels with the same color: increment this side pixels to crop
|
||||
northPixelsToCrop++;
|
||||
}
|
||||
}
|
||||
|
||||
// west side (scan columns from west to east)
|
||||
colorTarget = this.getPixelColor(w, 0);
|
||||
if (!ignoreSides.west) {
|
||||
west: for (let x = 0; x < w - minPixelsPerSide; x++) {
|
||||
for (let y = 0 + northPixelsToCrop; y < h; y++) {
|
||||
const colorXY = this.getPixelColor(x, y);
|
||||
const rgba2 = this.constructor.intToRGBA(colorXY);
|
||||
|
||||
if (this.constructor.colorDiff(rgba1, rgba2) > tolerance) {
|
||||
// this pixel is too distant from the first one: abort this side scan
|
||||
break west;
|
||||
}
|
||||
}
|
||||
|
||||
// this column contains all pixels with the same color: increment this side pixels to crop
|
||||
westPixelsToCrop++;
|
||||
}
|
||||
}
|
||||
|
||||
// south side (scan rows from south to north)
|
||||
colorTarget = this.getPixelColor(0, h);
|
||||
|
||||
if (!ignoreSides.south) {
|
||||
south: for (
|
||||
let y = h - 1;
|
||||
y >= northPixelsToCrop + minPixelsPerSide;
|
||||
y--
|
||||
) {
|
||||
for (let x = w - eastPixelsToCrop - 1; x >= 0; x--) {
|
||||
const colorXY = this.getPixelColor(x, y);
|
||||
const rgba2 = this.constructor.intToRGBA(colorXY);
|
||||
|
||||
if (this.constructor.colorDiff(rgba1, rgba2) > tolerance) {
|
||||
// this pixel is too distant from the first one: abort this side scan
|
||||
break south;
|
||||
}
|
||||
}
|
||||
|
||||
// this row contains all pixels with the same color: increment this side pixels to crop
|
||||
southPixelsToCrop++;
|
||||
}
|
||||
}
|
||||
|
||||
// east side (scan columns from east to west)
|
||||
colorTarget = this.getPixelColor(w, h);
|
||||
if (!ignoreSides.east) {
|
||||
east: for (
|
||||
let x = w - 1;
|
||||
x >= 0 + westPixelsToCrop + minPixelsPerSide;
|
||||
x--
|
||||
) {
|
||||
for (let y = h - 1; y >= 0 + northPixelsToCrop; y--) {
|
||||
const colorXY = this.getPixelColor(x, y);
|
||||
const rgba2 = this.constructor.intToRGBA(colorXY);
|
||||
|
||||
if (this.constructor.colorDiff(rgba1, rgba2) > tolerance) {
|
||||
// this pixel is too distant from the first one: abort this side scan
|
||||
break east;
|
||||
}
|
||||
}
|
||||
|
||||
// this column contains all pixels with the same color: increment this side pixels to crop
|
||||
eastPixelsToCrop++;
|
||||
}
|
||||
}
|
||||
|
||||
// decide if a crop is needed
|
||||
let doCrop = false;
|
||||
|
||||
// apply leaveBorder
|
||||
westPixelsToCrop -= leaveBorder;
|
||||
eastPixelsToCrop -= leaveBorder;
|
||||
northPixelsToCrop -= leaveBorder;
|
||||
southPixelsToCrop -= leaveBorder;
|
||||
|
||||
if (cropSymmetric) {
|
||||
const horizontal = Math.min(eastPixelsToCrop, westPixelsToCrop);
|
||||
const vertical = Math.min(northPixelsToCrop, southPixelsToCrop);
|
||||
westPixelsToCrop = horizontal;
|
||||
eastPixelsToCrop = horizontal;
|
||||
northPixelsToCrop = vertical;
|
||||
southPixelsToCrop = vertical;
|
||||
}
|
||||
|
||||
// make sure that crops are >= 0
|
||||
westPixelsToCrop = westPixelsToCrop >= 0 ? westPixelsToCrop : 0;
|
||||
eastPixelsToCrop = eastPixelsToCrop >= 0 ? eastPixelsToCrop : 0;
|
||||
northPixelsToCrop = northPixelsToCrop >= 0 ? northPixelsToCrop : 0;
|
||||
southPixelsToCrop = southPixelsToCrop >= 0 ? southPixelsToCrop : 0;
|
||||
|
||||
// safety checks
|
||||
const widthOfRemainingPixels =
|
||||
w - (westPixelsToCrop + eastPixelsToCrop);
|
||||
const heightOfRemainingPixels =
|
||||
h - (southPixelsToCrop + northPixelsToCrop);
|
||||
|
||||
if (cropOnlyFrames) {
|
||||
// crop image if all sides should be cropped
|
||||
doCrop =
|
||||
eastPixelsToCrop !== 0 &&
|
||||
northPixelsToCrop !== 0 &&
|
||||
westPixelsToCrop !== 0 &&
|
||||
southPixelsToCrop !== 0;
|
||||
} else {
|
||||
// crop image if at least one side should be cropped
|
||||
doCrop =
|
||||
eastPixelsToCrop !== 0 ||
|
||||
northPixelsToCrop !== 0 ||
|
||||
westPixelsToCrop !== 0 ||
|
||||
southPixelsToCrop !== 0;
|
||||
}
|
||||
|
||||
if (doCrop) {
|
||||
// do the real crop
|
||||
this.crop(
|
||||
westPixelsToCrop,
|
||||
northPixelsToCrop,
|
||||
widthOfRemainingPixels,
|
||||
heightOfRemainingPixels
|
||||
);
|
||||
}
|
||||
|
||||
if (isNodePattern(cb)) {
|
||||
cb.call(this, null, this);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
+408
@@ -0,0 +1,408 @@
|
||||
import { Jimp, mkJGD } from "@jimp/test-utils";
|
||||
import configure from "@jimp/custom";
|
||||
|
||||
import crop from "../src";
|
||||
import { expectToBeJGD } from "@jimp/test-utils/src";
|
||||
|
||||
const jimp = configure({ plugins: [crop] }, Jimp);
|
||||
|
||||
describe("Autocrop", () => {
|
||||
it("image with transparent surround color", async () => {
|
||||
const imgSrc = await jimp.read(
|
||||
mkJGD(
|
||||
" ",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
" "
|
||||
)
|
||||
);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.autocrop().getJGDSync(),
|
||||
mkJGD(" ◆◆ ", " ◆▦▦◆ ", "◆▦▦▦▦◆", " ◆▦▦◆ ", " ◆◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("image with opaque surround color", async () => {
|
||||
const imgSrc = await jimp.read(
|
||||
mkJGD(
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥◆◆▥▥▥▥",
|
||||
"▥▥▥◆▦▦◆▥▥▥",
|
||||
"▥▥◆▦▦▦▦◆▥▥",
|
||||
"▥▥▥◆▦▦◆▥▥▥",
|
||||
"▥▥▥▥◆◆▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.autocrop().getJGDSync(),
|
||||
mkJGD("▥▥◆◆▥▥", "▥◆▦▦◆▥", "◆▦▦▦▦◆", "▥◆▦▦◆▥", "▥▥◆◆▥▥")
|
||||
);
|
||||
});
|
||||
|
||||
it("image with one color border", async () => {
|
||||
const imgSrc = await jimp.read(
|
||||
mkJGD(
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥ ◆◆ ▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥",
|
||||
"▥▥ ◆▦▦▦▦◆ ▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥",
|
||||
"▥▥ ◆◆ ▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.autocrop().getJGDSync(),
|
||||
mkJGD(" ◆◆ ", " ◆▦▦◆ ", " ◆▦▦▦▦◆ ", " ◆▦▦◆ ", " ◆◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("image border with small variation", async () => {
|
||||
const imgSrc = await jimp.read(
|
||||
mkJGD(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232"
|
||||
)
|
||||
);
|
||||
expectToBeJGD(
|
||||
imgSrc.clone().autocrop().getJGDSync(),
|
||||
mkJGD(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232"
|
||||
)
|
||||
);
|
||||
expectToBeJGD(
|
||||
imgSrc.clone().autocrop(0.005).getJGDSync(),
|
||||
mkJGD(" ◆◆ ", " ◆▦▦◆ ", " ◆▦▦▦▦◆ ", " ◆▦▦◆ ", " ◆◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("image border with small variation configured by options", async () => {
|
||||
const imgSrc = await Jimp.read(
|
||||
mkJGD(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232"
|
||||
)
|
||||
);
|
||||
expectToBeJGD(
|
||||
imgSrc.clone().autocrop().getJGDSync(),
|
||||
mkJGD(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232"
|
||||
)
|
||||
);
|
||||
expectToBeJGD(
|
||||
imgSrc.clone().autocrop({ tolerance: 0.005 }).getJGDSync(),
|
||||
mkJGD(" ◆◆ ", " ◆▦▦◆ ", " ◆▦▦▦▦◆ ", " ◆▦▦◆ ", " ◆◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("image without frame", async () => {
|
||||
const imgSrc = await Jimp.read(
|
||||
mkJGD(
|
||||
"▥▥ ◆◆ ",
|
||||
"▥▥ ◆▦▦◆ ",
|
||||
"▥▥ ◆▦▦▦▦◆ ",
|
||||
"▥▥ ◆▦▦◆ ",
|
||||
"▥▥ ◆◆ ",
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.autocrop(false).getJGDSync(),
|
||||
mkJGD(" ◆◆ ", " ◆▦▦◆ ", " ◆▦▦▦▦◆ ", " ◆▦▦◆ ", " ◆◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("image without frame configured by options", async () => {
|
||||
const imgSrc = await Jimp.read(
|
||||
mkJGD(
|
||||
"▥▥ ◆◆ ",
|
||||
"▥▥ ◆▦▦◆ ",
|
||||
"▥▥ ◆▦▦▦▦◆ ",
|
||||
"▥▥ ◆▦▦◆ ",
|
||||
"▥▥ ◆◆ ",
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.autocrop({ cropOnlyFrames: false }).getJGDSync(),
|
||||
mkJGD(" ◆◆ ", " ◆▦▦◆ ", " ◆▦▦▦▦◆ ", " ◆▦▦◆ ", " ◆◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("image with symmetric border configured by options", async () => {
|
||||
const imgSrc = await Jimp.read(
|
||||
mkJGD(
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥ ◆◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆◆ ▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.autocrop({ cropSymmetric: true }).getJGDSync(),
|
||||
mkJGD(
|
||||
" ◆◆ ▥▥",
|
||||
" ◆▦▦◆ ▥▥",
|
||||
" ◆▦▦▦▦◆ ▥▥",
|
||||
" ◆▦▦◆ ▥▥",
|
||||
" ◆◆ ▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it("image without frame and with symmetric border configured by options", async () => {
|
||||
const imgSrc = await Jimp.read(
|
||||
mkJGD(
|
||||
"▥▥ ◆◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆◆ ▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
expectToBeJGD(
|
||||
imgSrc
|
||||
.autocrop({ cropSymmetric: true, cropOnlyFrames: false })
|
||||
.getJGDSync(),
|
||||
mkJGD(
|
||||
" ◆◆ ▥▥",
|
||||
" ◆▦▦◆ ▥▥",
|
||||
" ◆▦▦▦▦◆ ▥▥",
|
||||
" ◆▦▦◆ ▥▥",
|
||||
" ◆◆ ▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it("image without frame and with some border left", async () => {
|
||||
const imgSrc = await Jimp.read(
|
||||
mkJGD(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232"
|
||||
)
|
||||
);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc
|
||||
.autocrop({
|
||||
tolerance: 0.005,
|
||||
leaveBorder: 1,
|
||||
})
|
||||
.getJGDSync(),
|
||||
mkJGD(
|
||||
"3232323232",
|
||||
"2 ◆◆ 3",
|
||||
"3 ◆▦▦◆ 2",
|
||||
"2 ◆▦▦▦▦◆ 3",
|
||||
"3 ◆▦▦◆ 2",
|
||||
"2 ◆◆ 3",
|
||||
"3232323232"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('image not cropped given an out of bounds "leaveBorder" value ', async () => {
|
||||
const imgSrc = await Jimp.read(
|
||||
mkJGD(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232"
|
||||
)
|
||||
);
|
||||
expectToBeJGD(
|
||||
imgSrc
|
||||
.autocrop({
|
||||
tolerance: 0.005,
|
||||
leaveBorder: 100,
|
||||
})
|
||||
.getJGDSync(),
|
||||
mkJGD(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it("image with top and bottom frame and leaveBorder", async () => {
|
||||
const imgSrc = await Jimp.read(
|
||||
mkJGD(
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
expectToBeJGD(
|
||||
imgSrc
|
||||
.autocrop({
|
||||
cropSymmetric: true,
|
||||
cropOnlyFrames: false,
|
||||
leaveBorder: 2,
|
||||
})
|
||||
.getJGDSync(),
|
||||
mkJGD(
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it("ignore sides north", async () => {
|
||||
const imgSrc = await jimp.read(
|
||||
mkJGD(
|
||||
" ",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
" "
|
||||
)
|
||||
);
|
||||
expectToBeJGD(
|
||||
imgSrc
|
||||
.autocrop({ cropOnlyFrames: false, ignoreSides: { north: true } })
|
||||
.getJGDSync(),
|
||||
mkJGD(" ", " ◆◆ ", " ◆▦▦◆ ", "◆▦▦▦▦◆", " ◆▦▦◆ ", " ◆◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("ignore sides south and west", async () => {
|
||||
const imgSrc = await jimp.read(
|
||||
mkJGD(
|
||||
" ",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
" "
|
||||
)
|
||||
);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc
|
||||
.autocrop({
|
||||
cropOnlyFrames: false,
|
||||
ignoreSides: { west: true, south: true },
|
||||
})
|
||||
.getJGDSync(),
|
||||
mkJGD(
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
" "
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it("ignore sides east", async () => {
|
||||
const imgSrc = await jimp.read(
|
||||
mkJGD(
|
||||
" ",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
" "
|
||||
)
|
||||
);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc
|
||||
.autocrop({ cropOnlyFrames: false, ignoreSides: { east: true } })
|
||||
.getJGDSync(),
|
||||
mkJGD(" ◆◆ ", " ◆▦▦◆ ", "◆▦▦▦▦◆ ", " ◆▦▦◆ ", " ◆◆ ")
|
||||
);
|
||||
});
|
||||
});
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import { Jimp, mkJGD } from "@jimp/test-utils";
|
||||
import configure from "@jimp/custom";
|
||||
|
||||
import crop from "../src";
|
||||
import { expectToBeJGD } from "@jimp/test-utils/src";
|
||||
|
||||
const jimp = configure({ plugins: [crop] }, Jimp);
|
||||
|
||||
describe("crop", () => {
|
||||
// 6x5 size
|
||||
const testImage = mkJGD(" ◆◆ ", " ◆▦▦◆ ", "◆▦▦▦▦◆", " ◆▦▦◆ ", " ◆◆ ");
|
||||
|
||||
it("full width from top", async () => {
|
||||
const imgSrc = await jimp.read(testImage);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.crop(0, 0, 6, 2).getJGDSync(),
|
||||
mkJGD(" ◆◆ ", " ◆▦▦◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("full width from bottom", async () => {
|
||||
const imgSrc = await jimp.read(testImage);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.crop(0, 3, 6, 2).getJGDSync(),
|
||||
mkJGD(" ◆▦▦◆ ", " ◆◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("full width from middle", async () => {
|
||||
const imgSrc = await jimp.read(testImage);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.crop(0, 2, 6, 2).getJGDSync(),
|
||||
mkJGD("◆▦▦▦▦◆", " ◆▦▦◆ ")
|
||||
);
|
||||
});
|
||||
|
||||
it("full height from left", async () => {
|
||||
const imgSrc = await jimp.read(testImage);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.crop(0, 0, 2, 5).getJGDSync(),
|
||||
mkJGD(" ", " ◆", "◆▦", " ◆", " ")
|
||||
);
|
||||
});
|
||||
|
||||
it("full height from right", async () => {
|
||||
const imgSrc = await jimp.read(testImage);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.crop(4, 0, 2, 5).getJGDSync(),
|
||||
mkJGD(" ", "◆ ", "▦◆", "◆ ", " ")
|
||||
);
|
||||
});
|
||||
|
||||
it("full height from middle", async () => {
|
||||
const imgSrc = await jimp.read(testImage);
|
||||
|
||||
expectToBeJGD(
|
||||
imgSrc.crop(2, 0, 2, 5).getJGDSync(),
|
||||
mkJGD("◆◆", "▦▦", "▦▦", "▦▦", "◆◆")
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user