Initial commit
This commit is contained in:
commit
e49726988c
5445 files changed
+1089914
No files matched your search
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "../src",
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext"
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "./build.json",
|
||||
"include": [
|
||||
"../src/**/*.ts",
|
||||
"../src/**/*.cts",
|
||||
"../src/**/*.tsx",
|
||||
"../src/**/*.json"
|
||||
],
|
||||
"exclude": [
|
||||
"../**/*.test.ts",
|
||||
"../src/**/*.mts",
|
||||
"../src/package.json"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"outDir": "../.tshy-build/commonjs"
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "./build.json",
|
||||
"include": [
|
||||
"../src/**/*.ts",
|
||||
"../src/**/*.mts",
|
||||
"../src/**/*.tsx",
|
||||
"../src/**/*.json"
|
||||
],
|
||||
"exclude": [
|
||||
"../**/*.test.ts",
|
||||
"../src/package.json"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"outDir": "../.tshy-build/esm"
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
> @jimp/plugin-crop@1.5.0 build /Users/andrewlisowski/Documents/jimp/plugins/plugin-crop
|
||||
> tshy
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
> @jimp/plugin-crop@1.1.2 lint /Users/andrewlisowski/Documents/jimp/plugins/plugin-crop
|
||||
> eslint .
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
> @jimp/plugin-crop@1.1.2 test:browser /Users/andrewlisowski/Documents/jimp/plugins/plugin-crop
|
||||
> vitest --config vitest.config.browser.mjs "--watch=false"
|
||||
|
||||
Port 5173 is in use, trying another one...
|
||||
Port 5174 is in use, trying another one...
|
||||
Port 5175 is in use, trying another one...
|
||||
|
||||
[7m[1m[36m RUN [39m[22m[27m [36mv2.0.5[39m [90m/Users/andrewlisowski/Documents/jimp/plugins/plugin-crop[39m
|
||||
[2m[32m Browser runner started at http://localhost:5176/[39m[22m
|
||||
|
||||
[2m1:39:24 AM[22m [36m[1m[vite][22m[39m [32m✨ new dependencies optimized: [33mvite-plugin-node-polyfills/shims/buffer, vite-plugin-node-polyfills/shims/global, vite-plugin-node-polyfills/shims/process, path[32m[39m
|
||||
[2m1:39:24 AM[22m [36m[1m[vite][22m[39m [32m✨ optimized dependencies changed. reloading[39m
|
||||
[?25l [90m·[39m [2msrc/[22mautocrop[2m.test.ts[22m[2m (15)[22m
|
||||
[90m·[39m [2msrc/[22mcrop[2m.test.ts[22m[2m (6)[22m
|
||||
[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[2K[1A[2K[1A[2K[G [32m✓[39m [2msrc/[22mautocrop[2m.test.ts[22m[2m (15)[22m
|
||||
[32m✓[39m [2msrc/[22mcrop[2m.test.ts[22m[2m (6)[22m
|
||||
[2K[1A[2K[1A[2K[G [32m✓[39m [2msrc/[22mautocrop[2m.test.ts[22m[2m (15)[22m
|
||||
[32m✓[39m [2msrc/[22mcrop[2m.test.ts[22m[2m (6)[22m
|
||||
|
||||
[2m Test Files [22m [1m[32m2 passed[39m[22m[90m (2)[39m
|
||||
[2m Tests [22m [1m[32m21 passed[39m[22m[90m (21)[39m
|
||||
[2m Start at [22m 01:39:14
|
||||
[2m Duration [22m 12.94s[2m (transform 0ms, setup 0ms, collect 1.07s, tests 11ms, environment 0ms, prepare 4.31s)[22m
|
||||
|
||||
[?25h[?25h
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
|
||||
|
||||
> @jimp/plugin-crop@1.1.2 test /Users/andrewlisowski/Documents/jimp/plugins/plugin-crop
|
||||
> vitest "--watch=false"
|
||||
|
||||
|
||||
[7m[1m[36m RUN [39m[22m[27m [36mv2.0.5[39m [90m/Users/andrewlisowski/Documents/jimp/plugins/plugin-crop[39m
|
||||
|
||||
[?25l [90m·[39m [2msrc/[22mcrop[2m.test.ts[22m[2m (6)[22m
|
||||
[?25l[?25l[?25l[?25l[?25l[?25l[2K[1A[2K[G [90m·[39m [2msrc/[22mautocrop[2m.test.ts[22m[2m (15)[22m
|
||||
[90m·[39m [2msrc/[22mcrop[2m.test.ts[22m[2m (6)[22m
|
||||
[?25l[2K[1A[2K[1A[2K[G [90m·[39m [2msrc/[22mautocrop[2m.test.ts[22m[2m (15)[22m
|
||||
[32m✓[39m [2msrc/[22mcrop[2m.test.ts[22m[2m (6)[22m
|
||||
[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[?25l[2K[1A[2K[1A[2K[G [32m✓[39m [2msrc/[22mautocrop[2m.test.ts[22m[2m (15)[22m
|
||||
[32m✓[39m [2msrc/[22mcrop[2m.test.ts[22m[2m (6)[22m
|
||||
[?25l[?25l[?25l[?25l[2K[1A[2K[1A[2K[G [32m✓[39m [2msrc/[22mautocrop[2m.test.ts[22m[2m (15)[22m
|
||||
[32m✓[39m [2msrc/[22mcrop[2m.test.ts[22m[2m (6)[22m
|
||||
|
||||
[2m Test Files [22m [1m[32m2 passed[39m[22m[90m (2)[39m
|
||||
[2m Tests [22m [1m[32m21 passed[39m[22m[90m (21)[39m
|
||||
[2m Start at [22m 01:33:33
|
||||
[2m Duration [22m 2.26s[2m (transform 465ms, setup 0ms, collect 963ms, tests 196ms, environment 0ms, prepare 730ms)[22m
|
||||
|
||||
[?25h[?25h
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
# v1.1.3 (Mon Sep 02 2024)
|
||||
|
||||
#### 🐛 Bug Fix
|
||||
|
||||
- Update deps [#1319](https://github.com/jimp-dev/jimp/pull/1319) ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
#### Authors: 1
|
||||
|
||||
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
---
|
||||
|
||||
# v1.1.0 (Sun Sep 01 2024)
|
||||
|
||||
#### ⚠️ Pushed to `main`
|
||||
|
||||
- upgrade tshy ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
- update more docs ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
#### Authors: 1
|
||||
|
||||
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
---
|
||||
|
||||
# v1.0.3 (Sat Aug 31 2024)
|
||||
|
||||
#### 🐛 Bug Fix
|
||||
|
||||
- Fix build [#1303](https://github.com/jimp-dev/jimp/pull/1303) ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
#### ⚠️ Pushed to `main`
|
||||
|
||||
- add clean script ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
#### Authors: 1
|
||||
|
||||
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
---
|
||||
|
||||
# v1.0.2 (Sat Aug 31 2024)
|
||||
|
||||
#### ⚠️ Pushed to `main`
|
||||
|
||||
- set side effects ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
- fix versions ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
- add publish config ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
|
||||
#### Authors: 1
|
||||
|
||||
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
|
||||
+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.
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<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](http://jimp-dev.github.io/jimp/api/jimp/classes/jimp#crop)
|
||||
- [autocrop](http://jimp-dev.github.io/jimp/api/jimp/classes/jimp#autocrop)
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import { JimpClass } from "@jimp/types";
|
||||
import { z } from "zod";
|
||||
export declare const CropOptionsSchema: z.ZodObject<{
|
||||
/** the x position to crop form */
|
||||
x: z.ZodNumber;
|
||||
/** the y position to crop form */
|
||||
y: z.ZodNumber;
|
||||
/** the width to crop form */
|
||||
w: z.ZodNumber;
|
||||
/** the height to crop form */
|
||||
h: z.ZodNumber;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
}, {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
}>;
|
||||
export type CropOptions = z.infer<typeof CropOptionsSchema>;
|
||||
declare const AutocropComplexOptionsSchema: z.ZodObject<{
|
||||
/** percent of color difference tolerance (default value) */
|
||||
tolerance: z.ZodOptional<z.ZodNumber>;
|
||||
/** flag to force cropping only if the image has a real "frame" i.e. all 4 sides have some border (default value) */
|
||||
cropOnlyFrames: z.ZodOptional<z.ZodBoolean>;
|
||||
/** force cropping top be symmetric */
|
||||
cropSymmetric: z.ZodOptional<z.ZodBoolean>;
|
||||
/** Amount of pixels in border to leave */
|
||||
leaveBorder: z.ZodOptional<z.ZodNumber>;
|
||||
ignoreSides: z.ZodOptional<z.ZodObject<{
|
||||
north: z.ZodOptional<z.ZodBoolean>;
|
||||
south: z.ZodOptional<z.ZodBoolean>;
|
||||
east: z.ZodOptional<z.ZodBoolean>;
|
||||
west: z.ZodOptional<z.ZodBoolean>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
north?: boolean | undefined;
|
||||
south?: boolean | undefined;
|
||||
east?: boolean | undefined;
|
||||
west?: boolean | undefined;
|
||||
}, {
|
||||
north?: boolean | undefined;
|
||||
south?: boolean | undefined;
|
||||
east?: boolean | undefined;
|
||||
west?: boolean | undefined;
|
||||
}>>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
tolerance?: number | undefined;
|
||||
cropOnlyFrames?: boolean | undefined;
|
||||
cropSymmetric?: boolean | undefined;
|
||||
leaveBorder?: number | undefined;
|
||||
ignoreSides?: {
|
||||
north?: boolean | undefined;
|
||||
south?: boolean | undefined;
|
||||
east?: boolean | undefined;
|
||||
west?: boolean | undefined;
|
||||
} | undefined;
|
||||
}, {
|
||||
tolerance?: number | undefined;
|
||||
cropOnlyFrames?: boolean | undefined;
|
||||
cropSymmetric?: boolean | undefined;
|
||||
leaveBorder?: number | undefined;
|
||||
ignoreSides?: {
|
||||
north?: boolean | undefined;
|
||||
south?: boolean | undefined;
|
||||
east?: boolean | undefined;
|
||||
west?: boolean | undefined;
|
||||
} | undefined;
|
||||
}>;
|
||||
export type AutocropComplexOptions = z.infer<typeof AutocropComplexOptionsSchema>;
|
||||
export type AutocropOptions = number | AutocropComplexOptions;
|
||||
export declare const methods: {
|
||||
/**
|
||||
* Crops the image at a given point to a give size.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.crop(150, 100);
|
||||
* ```
|
||||
*/
|
||||
crop<I extends JimpClass>(image: I, options: CropOptions): I;
|
||||
/**
|
||||
* Autocrop same color borders from this image.
|
||||
* This function will attempt to crop out transparent pixels from the image.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.autocrop();
|
||||
* ```
|
||||
*/
|
||||
autocrop<I extends JimpClass>(image: I, options?: AutocropOptions): I;
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB;IAC5B,kCAAkC;;IAElC,kCAAkC;;IAElC,6BAA6B;;IAE7B,8BAA8B;;;;;;;;;;;;EAE9B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,QAAA,MAAM,4BAA4B;IAChC,4DAA4D;;IAE5D,oHAAoH;;IAEpH,sCAAsC;;IAEtC,0CAA0C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAU1C,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAC1C,OAAO,4BAA4B,CACpC,CAAC;AACF,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,sBAAsB,CAAC;AAE9D,eAAO,MAAM,OAAO;IAClB;;;;;;;;;;OAUG;SACE,CAAC,SAAS,SAAS,SAAS,CAAC,WAAW,WAAW;IAiCxD;;;;;;;;;;;OAWG;aACM,CAAC,SAAS,SAAS,SAAS,CAAC,YAAW,eAAe;CAqLjE,CAAC"}
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.methods = exports.CropOptionsSchema = void 0;
|
||||
const utils_1 = require("@jimp/utils");
|
||||
const zod_1 = require("zod");
|
||||
exports.CropOptionsSchema = zod_1.z.object({
|
||||
/** the x position to crop form */
|
||||
x: zod_1.z.number(),
|
||||
/** the y position to crop form */
|
||||
y: zod_1.z.number(),
|
||||
/** the width to crop form */
|
||||
w: zod_1.z.number(),
|
||||
/** the height to crop form */
|
||||
h: zod_1.z.number(),
|
||||
});
|
||||
const AutocropComplexOptionsSchema = zod_1.z.object({
|
||||
/** percent of color difference tolerance (default value) */
|
||||
tolerance: zod_1.z.number().min(0).max(1).optional(),
|
||||
/** flag to force cropping only if the image has a real "frame" i.e. all 4 sides have some border (default value) */
|
||||
cropOnlyFrames: zod_1.z.boolean().optional(),
|
||||
/** force cropping top be symmetric */
|
||||
cropSymmetric: zod_1.z.boolean().optional(),
|
||||
/** Amount of pixels in border to leave */
|
||||
leaveBorder: zod_1.z.number().optional(),
|
||||
ignoreSides: zod_1.z
|
||||
.object({
|
||||
north: zod_1.z.boolean().optional(),
|
||||
south: zod_1.z.boolean().optional(),
|
||||
east: zod_1.z.boolean().optional(),
|
||||
west: zod_1.z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
exports.methods = {
|
||||
/**
|
||||
* Crops the image at a given point to a give size.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.crop(150, 100);
|
||||
* ```
|
||||
*/
|
||||
crop(image, options) {
|
||||
let { x, y, w, h } = exports.CropOptionsSchema.parse(options);
|
||||
// round input
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
if (x === 0 && w === image.bitmap.width) {
|
||||
// shortcut
|
||||
const start = (w * y + x) << 2;
|
||||
const end = start + ((h * w) << 2);
|
||||
image.bitmap.data = image.bitmap.data.slice(start, end);
|
||||
}
|
||||
else {
|
||||
const bitmap = Buffer.allocUnsafe(w * h * 4);
|
||||
let offset = 0;
|
||||
(0, utils_1.scan)(image, x, y, w, h, function (_, __, idx) {
|
||||
const data = image.bitmap.data.readUInt32BE(idx);
|
||||
bitmap.writeUInt32BE(data, offset);
|
||||
offset += 4;
|
||||
});
|
||||
image.bitmap.data = bitmap;
|
||||
}
|
||||
image.bitmap.width = w;
|
||||
image.bitmap.height = h;
|
||||
return image;
|
||||
},
|
||||
/**
|
||||
* Autocrop same color borders from this image.
|
||||
* This function will attempt to crop out transparent pixels from the image.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.autocrop();
|
||||
* ```
|
||||
*/
|
||||
autocrop(image, options = {}) {
|
||||
const { tolerance = 0.0002, cropOnlyFrames = true, cropSymmetric = false, leaveBorder = 0, ignoreSides: ignoreSidesArg, } = typeof options === "number"
|
||||
? { tolerance: options }
|
||||
: AutocropComplexOptionsSchema.parse(options);
|
||||
const w = image.bitmap.width;
|
||||
const h = image.bitmap.height;
|
||||
const minPixelsPerSide = 1; // to avoid cropping completely the image, resulting in an invalid 0 sized image
|
||||
// i.e. north and south / east and west are cropped by the same value
|
||||
const ignoreSides = {
|
||||
north: false,
|
||||
south: false,
|
||||
east: false,
|
||||
west: false,
|
||||
...ignoreSidesArg,
|
||||
};
|
||||
/**
|
||||
* 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 = image.getPixelColor(0, 0); // top left pixel color is the target color
|
||||
const rgba1 = (0, utils_1.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 = image.getPixelColor(0, 0);
|
||||
if (!ignoreSides.north) {
|
||||
north: for (let y = 0; y < h - minPixelsPerSide; y++) {
|
||||
for (let x = 0; x < w; x++) {
|
||||
const colorXY = image.getPixelColor(x, y);
|
||||
const rgba2 = (0, utils_1.intToRGBA)(colorXY);
|
||||
if ((0, utils_1.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 = image.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 = image.getPixelColor(x, y);
|
||||
const rgba2 = (0, utils_1.intToRGBA)(colorXY);
|
||||
if ((0, utils_1.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 = image.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 = image.getPixelColor(x, y);
|
||||
const rgba2 = (0, utils_1.intToRGBA)(colorXY);
|
||||
if ((0, utils_1.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 = image.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 = image.getPixelColor(x, y);
|
||||
const rgba2 = (0, utils_1.intToRGBA)(colorXY);
|
||||
if ((0, utils_1.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(image, {
|
||||
x: westPixelsToCrop,
|
||||
y: northPixelsToCrop,
|
||||
w: widthOfRemainingPixels,
|
||||
h: heightOfRemainingPixels,
|
||||
});
|
||||
}
|
||||
return image;
|
||||
},
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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 truncated
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import { JimpClass } from "@jimp/types";
|
||||
import { z } from "zod";
|
||||
export declare const CropOptionsSchema: z.ZodObject<{
|
||||
/** the x position to crop form */
|
||||
x: z.ZodNumber;
|
||||
/** the y position to crop form */
|
||||
y: z.ZodNumber;
|
||||
/** the width to crop form */
|
||||
w: z.ZodNumber;
|
||||
/** the height to crop form */
|
||||
h: z.ZodNumber;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
}, {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
}>;
|
||||
export type CropOptions = z.infer<typeof CropOptionsSchema>;
|
||||
declare const AutocropComplexOptionsSchema: z.ZodObject<{
|
||||
/** percent of color difference tolerance (default value) */
|
||||
tolerance: z.ZodOptional<z.ZodNumber>;
|
||||
/** flag to force cropping only if the image has a real "frame" i.e. all 4 sides have some border (default value) */
|
||||
cropOnlyFrames: z.ZodOptional<z.ZodBoolean>;
|
||||
/** force cropping top be symmetric */
|
||||
cropSymmetric: z.ZodOptional<z.ZodBoolean>;
|
||||
/** Amount of pixels in border to leave */
|
||||
leaveBorder: z.ZodOptional<z.ZodNumber>;
|
||||
ignoreSides: z.ZodOptional<z.ZodObject<{
|
||||
north: z.ZodOptional<z.ZodBoolean>;
|
||||
south: z.ZodOptional<z.ZodBoolean>;
|
||||
east: z.ZodOptional<z.ZodBoolean>;
|
||||
west: z.ZodOptional<z.ZodBoolean>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
north?: boolean | undefined;
|
||||
south?: boolean | undefined;
|
||||
east?: boolean | undefined;
|
||||
west?: boolean | undefined;
|
||||
}, {
|
||||
north?: boolean | undefined;
|
||||
south?: boolean | undefined;
|
||||
east?: boolean | undefined;
|
||||
west?: boolean | undefined;
|
||||
}>>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
tolerance?: number | undefined;
|
||||
cropOnlyFrames?: boolean | undefined;
|
||||
cropSymmetric?: boolean | undefined;
|
||||
leaveBorder?: number | undefined;
|
||||
ignoreSides?: {
|
||||
north?: boolean | undefined;
|
||||
south?: boolean | undefined;
|
||||
east?: boolean | undefined;
|
||||
west?: boolean | undefined;
|
||||
} | undefined;
|
||||
}, {
|
||||
tolerance?: number | undefined;
|
||||
cropOnlyFrames?: boolean | undefined;
|
||||
cropSymmetric?: boolean | undefined;
|
||||
leaveBorder?: number | undefined;
|
||||
ignoreSides?: {
|
||||
north?: boolean | undefined;
|
||||
south?: boolean | undefined;
|
||||
east?: boolean | undefined;
|
||||
west?: boolean | undefined;
|
||||
} | undefined;
|
||||
}>;
|
||||
export type AutocropComplexOptions = z.infer<typeof AutocropComplexOptionsSchema>;
|
||||
export type AutocropOptions = number | AutocropComplexOptions;
|
||||
export declare const methods: {
|
||||
/**
|
||||
* Crops the image at a given point to a give size.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.crop(150, 100);
|
||||
* ```
|
||||
*/
|
||||
crop<I extends JimpClass>(image: I, options: CropOptions): I;
|
||||
/**
|
||||
* Autocrop same color borders from this image.
|
||||
* This function will attempt to crop out transparent pixels from the image.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.autocrop();
|
||||
* ```
|
||||
*/
|
||||
autocrop<I extends JimpClass>(image: I, options?: AutocropOptions): I;
|
||||
};
|
||||
export {};
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB;IAC5B,kCAAkC;;IAElC,kCAAkC;;IAElC,6BAA6B;;IAE7B,8BAA8B;;;;;;;;;;;;EAE9B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,QAAA,MAAM,4BAA4B;IAChC,4DAA4D;;IAE5D,oHAAoH;;IAEpH,sCAAsC;;IAEtC,0CAA0C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAU1C,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAC1C,OAAO,4BAA4B,CACpC,CAAC;AACF,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,sBAAsB,CAAC;AAE9D,eAAO,MAAM,OAAO;IAClB;;;;;;;;;;OAUG;SACE,CAAC,SAAS,SAAS,SAAS,CAAC,WAAW,WAAW;IAiCxD;;;;;;;;;;;OAWG;aACM,CAAC,SAAS,SAAS,SAAS,CAAC,YAAW,eAAe;CAqLjE,CAAC"}
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
import { colorDiff, intToRGBA, scan } from "@jimp/utils";
|
||||
import { z } from "zod";
|
||||
export const CropOptionsSchema = z.object({
|
||||
/** the x position to crop form */
|
||||
x: z.number(),
|
||||
/** the y position to crop form */
|
||||
y: z.number(),
|
||||
/** the width to crop form */
|
||||
w: z.number(),
|
||||
/** the height to crop form */
|
||||
h: z.number(),
|
||||
});
|
||||
const AutocropComplexOptionsSchema = z.object({
|
||||
/** percent of color difference tolerance (default value) */
|
||||
tolerance: z.number().min(0).max(1).optional(),
|
||||
/** flag to force cropping only if the image has a real "frame" i.e. all 4 sides have some border (default value) */
|
||||
cropOnlyFrames: z.boolean().optional(),
|
||||
/** force cropping top be symmetric */
|
||||
cropSymmetric: z.boolean().optional(),
|
||||
/** Amount of pixels in border to leave */
|
||||
leaveBorder: z.number().optional(),
|
||||
ignoreSides: z
|
||||
.object({
|
||||
north: z.boolean().optional(),
|
||||
south: z.boolean().optional(),
|
||||
east: z.boolean().optional(),
|
||||
west: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
export const methods = {
|
||||
/**
|
||||
* Crops the image at a given point to a give size.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.crop(150, 100);
|
||||
* ```
|
||||
*/
|
||||
crop(image, options) {
|
||||
let { x, y, w, h } = CropOptionsSchema.parse(options);
|
||||
// round input
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
if (x === 0 && w === image.bitmap.width) {
|
||||
// shortcut
|
||||
const start = (w * y + x) << 2;
|
||||
const end = start + ((h * w) << 2);
|
||||
image.bitmap.data = image.bitmap.data.slice(start, end);
|
||||
}
|
||||
else {
|
||||
const bitmap = Buffer.allocUnsafe(w * h * 4);
|
||||
let offset = 0;
|
||||
scan(image, x, y, w, h, function (_, __, idx) {
|
||||
const data = image.bitmap.data.readUInt32BE(idx);
|
||||
bitmap.writeUInt32BE(data, offset);
|
||||
offset += 4;
|
||||
});
|
||||
image.bitmap.data = bitmap;
|
||||
}
|
||||
image.bitmap.width = w;
|
||||
image.bitmap.height = h;
|
||||
return image;
|
||||
},
|
||||
/**
|
||||
* Autocrop same color borders from this image.
|
||||
* This function will attempt to crop out transparent pixels from the image.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.autocrop();
|
||||
* ```
|
||||
*/
|
||||
autocrop(image, options = {}) {
|
||||
const { tolerance = 0.0002, cropOnlyFrames = true, cropSymmetric = false, leaveBorder = 0, ignoreSides: ignoreSidesArg, } = typeof options === "number"
|
||||
? { tolerance: options }
|
||||
: AutocropComplexOptionsSchema.parse(options);
|
||||
const w = image.bitmap.width;
|
||||
const h = image.bitmap.height;
|
||||
const minPixelsPerSide = 1; // to avoid cropping completely the image, resulting in an invalid 0 sized image
|
||||
// i.e. north and south / east and west are cropped by the same value
|
||||
const ignoreSides = {
|
||||
north: false,
|
||||
south: false,
|
||||
east: false,
|
||||
west: false,
|
||||
...ignoreSidesArg,
|
||||
};
|
||||
/**
|
||||
* 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 = image.getPixelColor(0, 0); // top left pixel color is the target color
|
||||
const rgba1 = 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 = image.getPixelColor(0, 0);
|
||||
if (!ignoreSides.north) {
|
||||
north: for (let y = 0; y < h - minPixelsPerSide; y++) {
|
||||
for (let x = 0; x < w; x++) {
|
||||
const colorXY = image.getPixelColor(x, y);
|
||||
const rgba2 = intToRGBA(colorXY);
|
||||
if (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 = image.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 = image.getPixelColor(x, y);
|
||||
const rgba2 = intToRGBA(colorXY);
|
||||
if (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 = image.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 = image.getPixelColor(x, y);
|
||||
const rgba2 = intToRGBA(colorXY);
|
||||
if (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 = image.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 = image.getPixelColor(x, y);
|
||||
const rgba2 = intToRGBA(colorXY);
|
||||
if (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(image, {
|
||||
x: westPixelsToCrop,
|
||||
y: northPixelsToCrop,
|
||||
w: widthOfRemainingPixels,
|
||||
h: heightOfRemainingPixels,
|
||||
});
|
||||
}
|
||||
return image;
|
||||
},
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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 truncated
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
import shared from "@jimp/config-eslint/base.js";
|
||||
export default [...shared];
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "@jimp/plugin-crop",
|
||||
"version": "1.6.0",
|
||||
"repository": "jimp-dev/jimp",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "vitest",
|
||||
"test:browser": "vitest --config vitest.config.browser.mjs",
|
||||
"build": "tshy",
|
||||
"dev": "tshy --watch",
|
||||
"clean": "rm -rf node_modules .tshy .tshy-build dist .turbo"
|
||||
},
|
||||
"author": "Andrew Lisowski <lisowski54@gmail.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/core": "1.6.0",
|
||||
"@jimp/types": "1.6.0",
|
||||
"@jimp/utils": "1.6.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jimp/config-eslint": "1.6.0",
|
||||
"@jimp/config-typescript": "1.6.0",
|
||||
"@jimp/config-vitest": "1.6.0",
|
||||
"@jimp/test-utils": "1.6.0",
|
||||
"@types/node": "^18.19.48",
|
||||
"@vitest/browser": "^2.0.5",
|
||||
"eslint": "^9.9.1",
|
||||
"tshy": "^3.0.2",
|
||||
"typescript": "^5.5.4",
|
||||
"vite-plugin-node-polyfills": "^0.22.0",
|
||||
"vitest": "^2.0.5"
|
||||
},
|
||||
"tshy": {
|
||||
"exclude": [
|
||||
"**/*.test.ts"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"default": "./dist/esm/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/commonjs/index.d.ts",
|
||||
"default": "./dist/commonjs/index.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main": "./dist/commonjs/index.js",
|
||||
"types": "./dist/commonjs/index.d.ts",
|
||||
"type": "module",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"module": "./dist/esm/index.js",
|
||||
"gitHead": "c88abe6046dccbdb6e4f5f00c3dd403c81d83515"
|
||||
}
|
||||
+353
@@ -0,0 +1,353 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Autocrop > ignore sides east 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`Autocrop > ignore sides north 1`] = `
|
||||
Visualization:
|
||||
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`Autocrop > ignore sides south and west 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image border with small variation 1`] = `
|
||||
Visualization:
|
||||
|
||||
323232323232
|
||||
232323232323
|
||||
32 ◆◆ 32
|
||||
23 ◆▦▦◆ 23
|
||||
32 ◆▦▦▦▦◆ 32
|
||||
23 ◆▦▦◆ 23
|
||||
32 ◆◆ 32
|
||||
232323232323
|
||||
323232323232
|
||||
|
||||
Data:
|
||||
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image border with small variation 2`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image border with small variation configured by options 1`] = `
|
||||
Visualization:
|
||||
|
||||
323232323232
|
||||
232323232323
|
||||
32 ◆◆ 32
|
||||
23 ◆▦▦◆ 23
|
||||
32 ◆▦▦▦▦◆ 32
|
||||
23 ◆▦▦◆ 23
|
||||
32 ◆◆ 32
|
||||
232323232323
|
||||
323232323232
|
||||
|
||||
Data:
|
||||
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image border with small variation configured by options 2`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image not cropped given an out of bounds "leaveBorder" value 1`] = `
|
||||
Visualization:
|
||||
|
||||
323232323232
|
||||
232323232323
|
||||
32 ◆◆ 32
|
||||
23 ◆▦▦◆ 23
|
||||
32 ◆▦▦▦▦◆ 32
|
||||
23 ◆▦▦◆ 23
|
||||
32 ◆◆ 32
|
||||
232323232323
|
||||
323232323232
|
||||
|
||||
Data:
|
||||
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image with one color border 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image with opaque surround color 1`] = `
|
||||
Visualization:
|
||||
|
||||
▥▥◆◆▥▥
|
||||
▥◆▦▦◆▥
|
||||
◆▦▦▦▦◆
|
||||
▥◆▦▦◆▥
|
||||
▥▥◆◆▥▥
|
||||
|
||||
Data:
|
||||
|
||||
BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
BF-BF-BFᶠᶠ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ BF-BF-BFᶠᶠ
|
||||
FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ
|
||||
BF-BF-BFᶠᶠ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ BF-BF-BFᶠᶠ
|
||||
BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image with symmetric border configured by options 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆ ▥▥
|
||||
◆▦▦◆ ▥▥
|
||||
◆▦▦▦▦◆ ▥▥
|
||||
◆▦▦◆ ▥▥
|
||||
◆◆ ▥▥
|
||||
▥▥▥▥▥▥▥▥▥▥
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image with top and bottom frame and leaveBorder 1`] = `
|
||||
Visualization:
|
||||
|
||||
▥▥▥▥▥▥▥▥
|
||||
▥▥▥▥▥▥▥▥
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
▥▥▥▥▥▥▥▥
|
||||
▥▥▥▥▥▥▥▥
|
||||
|
||||
Data:
|
||||
|
||||
BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image with transparent surround color 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image without frame 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image without frame and with some border left 1`] = `
|
||||
Visualization:
|
||||
|
||||
3232323232
|
||||
2 ◆◆ 3
|
||||
3 ◆▦▦◆ 2
|
||||
2 ◆▦▦▦▦◆ 3
|
||||
3 ◆▦▦◆ 2
|
||||
2 ◆◆ 3
|
||||
3232323232
|
||||
|
||||
Data:
|
||||
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 22-22-22ᶠᶠ
|
||||
22-22-22ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ 33-33-33ᶠᶠ
|
||||
33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ 33-33-33ᶠᶠ 22-22-22ᶠᶠ
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image without frame and with symmetric border configured by options 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆ ▥▥
|
||||
◆▦▦◆ ▥▥
|
||||
◆▦▦▦▦◆ ▥▥
|
||||
◆▦▦◆ ▥▥
|
||||
◆◆ ▥▥
|
||||
▥▥▥▥▥▥▥▥▥▥
|
||||
▥▥▥▥▥▥▥▥▥▥
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ BF-BF-BFᶠᶠ
|
||||
`;
|
||||
|
||||
exports[`Autocrop > image without frame configured by options 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`crop > full height from left 1`] = `
|
||||
Visualization:
|
||||
|
||||
|
||||
◆
|
||||
◆▦
|
||||
◆
|
||||
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ
|
||||
FF-FF-00ᶠᶠ 80-80-80ᶠᶠ
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`crop > full height from middle 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
▦▦
|
||||
▦▦
|
||||
▦▦
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ
|
||||
80-80-80ᶠᶠ 80-80-80ᶠᶠ
|
||||
80-80-80ᶠᶠ 80-80-80ᶠᶠ
|
||||
80-80-80ᶠᶠ 80-80-80ᶠᶠ
|
||||
FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ
|
||||
`;
|
||||
|
||||
exports[`crop > full height from right 1`] = `
|
||||
Visualization:
|
||||
|
||||
|
||||
◆
|
||||
▦◆
|
||||
◆
|
||||
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
80-80-80ᶠᶠ FF-FF-00ᶠᶠ
|
||||
FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`crop > full width from bottom 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆▦▦◆
|
||||
◆◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`crop > full width from middle 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆▦▦▦▦◆
|
||||
◆▦▦◆
|
||||
|
||||
Data:
|
||||
|
||||
FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
`;
|
||||
|
||||
exports[`crop > full width from top 1`] = `
|
||||
Visualization:
|
||||
|
||||
◆◆
|
||||
◆▦▦◆
|
||||
|
||||
Data:
|
||||
|
||||
00-00-00⁰⁰ 00-00-00⁰⁰ FF-FF-00ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰ 00-00-00⁰⁰
|
||||
00-00-00⁰⁰ FF-FF-00ᶠᶠ 80-80-80ᶠᶠ 80-80-80ᶠᶠ FF-FF-00ᶠᶠ 00-00-00⁰⁰
|
||||
`;
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
import { expect, test, describe } from "vitest";
|
||||
|
||||
import { methods } from "./index.js";
|
||||
import { createJimp } from "@jimp/core";
|
||||
import { makeTestImage } from "@jimp/test-utils";
|
||||
|
||||
const jimp = createJimp({ plugins: [methods] });
|
||||
|
||||
describe("Autocrop", () => {
|
||||
test("image with transparent surround color", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
" ",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
" ",
|
||||
),
|
||||
);
|
||||
|
||||
expect(imgSrc.autocrop()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image with opaque surround color", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥◆◆▥▥▥▥",
|
||||
"▥▥▥◆▦▦◆▥▥▥",
|
||||
"▥▥◆▦▦▦▦◆▥▥",
|
||||
"▥▥▥◆▦▦◆▥▥▥",
|
||||
"▥▥▥▥◆◆▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
),
|
||||
);
|
||||
|
||||
expect(imgSrc.autocrop()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image with one color border", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥ ◆◆ ▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥",
|
||||
"▥▥ ◆▦▦▦▦◆ ▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥",
|
||||
"▥▥ ◆◆ ▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
),
|
||||
);
|
||||
|
||||
expect(imgSrc.autocrop()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image border with small variation", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232",
|
||||
),
|
||||
);
|
||||
expect(imgSrc.clone().autocrop()).toMatchSnapshot();
|
||||
expect(imgSrc.clone().autocrop(0.005)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image border with small variation configured by options", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232",
|
||||
),
|
||||
);
|
||||
expect(imgSrc.clone().autocrop()).toMatchSnapshot();
|
||||
expect(imgSrc.clone().autocrop({ tolerance: 0.005 })).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image without frame", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"▥▥ ◆◆ ",
|
||||
"▥▥ ◆▦▦◆ ",
|
||||
"▥▥ ◆▦▦▦▦◆ ",
|
||||
"▥▥ ◆▦▦◆ ",
|
||||
"▥▥ ◆◆ ",
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
),
|
||||
);
|
||||
|
||||
expect(imgSrc.autocrop({ cropOnlyFrames: false })).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image without frame configured by options", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"▥▥ ◆◆ ",
|
||||
"▥▥ ◆▦▦◆ ",
|
||||
"▥▥ ◆▦▦▦▦◆ ",
|
||||
"▥▥ ◆▦▦◆ ",
|
||||
"▥▥ ◆◆ ",
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥",
|
||||
),
|
||||
);
|
||||
|
||||
expect(imgSrc.autocrop({ cropOnlyFrames: false })).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image with symmetric border configured by options", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥ ◆◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆◆ ▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
),
|
||||
);
|
||||
|
||||
expect(imgSrc.autocrop({ cropSymmetric: true })).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image without frame and with symmetric border configured by options", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"▥▥ ◆◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆▦▦◆ ▥▥▥▥",
|
||||
"▥▥ ◆◆ ▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥▥▥▥▥▥▥",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
imgSrc.autocrop({ cropSymmetric: true, cropOnlyFrames: false }),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image without frame and with some border left", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232",
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
imgSrc.autocrop({
|
||||
tolerance: 0.005,
|
||||
leaveBorder: 1,
|
||||
}),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('image not cropped given an out of bounds "leaveBorder" value ', async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"323232323232",
|
||||
"232323232323",
|
||||
"32 ◆◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆▦▦▦▦◆ 32",
|
||||
"23 ◆▦▦◆ 23",
|
||||
"32 ◆◆ 32",
|
||||
"232323232323",
|
||||
"323232323232",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
imgSrc.autocrop({
|
||||
tolerance: 0.005,
|
||||
leaveBorder: 100,
|
||||
}),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("image with top and bottom frame and leaveBorder", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
"▥▥▥▥▥▥▥▥",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
imgSrc.autocrop({
|
||||
cropSymmetric: true,
|
||||
cropOnlyFrames: false,
|
||||
leaveBorder: 2,
|
||||
}),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("ignore sides north", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
" ",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
" ",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
imgSrc.autocrop({ cropOnlyFrames: false, ignoreSides: { north: true } }),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("ignore sides south and west", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
" ",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
" ",
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
imgSrc.autocrop({
|
||||
cropOnlyFrames: false,
|
||||
ignoreSides: { west: true, south: true },
|
||||
}),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("ignore sides east", async () => {
|
||||
const imgSrc = jimp.fromBitmap(
|
||||
makeTestImage(
|
||||
" ",
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆▦▦▦▦◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
" ",
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
imgSrc.autocrop({ cropOnlyFrames: false, ignoreSides: { east: true } }),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import { expect, test, describe } from "vitest";
|
||||
import { createJimp } from "@jimp/core";
|
||||
import { makeTestImage } from "@jimp/test-utils";
|
||||
|
||||
import { methods as crop } from "./index.js";
|
||||
|
||||
const jimp = createJimp({ plugins: [crop], formats: [] });
|
||||
|
||||
describe("crop", () => {
|
||||
// 6x5 size
|
||||
const testImage = makeTestImage(
|
||||
" ◆◆ ",
|
||||
" ◆▦▦◆ ",
|
||||
"◆▦▦▦▦◆",
|
||||
" ◆▦▦◆ ",
|
||||
" ◆◆ ",
|
||||
);
|
||||
|
||||
test("full width from top", () => {
|
||||
expect(
|
||||
jimp.fromBitmap(testImage).crop({ x: 0, y: 0, w: 6, h: 2 }),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("full width from bottom", () => {
|
||||
expect(
|
||||
jimp.fromBitmap(testImage).crop({ x: 0, y: 3, w: 6, h: 2 }),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("full width from middle", () => {
|
||||
expect(
|
||||
jimp.fromBitmap(testImage).crop({ x: 0, y: 2, w: 6, h: 2 }),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("full height from left", () => {
|
||||
expect(
|
||||
jimp.fromBitmap(testImage).crop({ x: 0, y: 0, w: 2, h: 5 }),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("full height from right", () => {
|
||||
expect(
|
||||
jimp.fromBitmap(testImage).crop({ x: 4, y: 0, w: 2, h: 5 }),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("full height from middle", () => {
|
||||
expect(
|
||||
jimp.fromBitmap(testImage).crop({ x: 2, y: 0, w: 2, h: 5 }),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
import { JimpClass } from "@jimp/types";
|
||||
import { colorDiff, intToRGBA, scan } from "@jimp/utils";
|
||||
import { z } from "zod";
|
||||
|
||||
export const CropOptionsSchema = z.object({
|
||||
/** the x position to crop form */
|
||||
x: z.number(),
|
||||
/** the y position to crop form */
|
||||
y: z.number(),
|
||||
/** the width to crop form */
|
||||
w: z.number(),
|
||||
/** the height to crop form */
|
||||
h: z.number(),
|
||||
});
|
||||
|
||||
export type CropOptions = z.infer<typeof CropOptionsSchema>;
|
||||
|
||||
const AutocropComplexOptionsSchema = z.object({
|
||||
/** percent of color difference tolerance (default value) */
|
||||
tolerance: z.number().min(0).max(1).optional(),
|
||||
/** flag to force cropping only if the image has a real "frame" i.e. all 4 sides have some border (default value) */
|
||||
cropOnlyFrames: z.boolean().optional(),
|
||||
/** force cropping top be symmetric */
|
||||
cropSymmetric: z.boolean().optional(),
|
||||
/** Amount of pixels in border to leave */
|
||||
leaveBorder: z.number().optional(),
|
||||
ignoreSides: z
|
||||
.object({
|
||||
north: z.boolean().optional(),
|
||||
south: z.boolean().optional(),
|
||||
east: z.boolean().optional(),
|
||||
west: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type AutocropComplexOptions = z.infer<
|
||||
typeof AutocropComplexOptionsSchema
|
||||
>;
|
||||
export type AutocropOptions = number | AutocropComplexOptions;
|
||||
|
||||
export const methods = {
|
||||
/**
|
||||
* Crops the image at a given point to a give size.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.crop(150, 100);
|
||||
* ```
|
||||
*/
|
||||
crop<I extends JimpClass>(image: I, options: CropOptions) {
|
||||
let { x, y, w, h } = CropOptionsSchema.parse(options);
|
||||
// round input
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
|
||||
if (x === 0 && w === image.bitmap.width) {
|
||||
// shortcut
|
||||
const start = (w * y + x) << 2;
|
||||
const end = start + ((h * w) << 2);
|
||||
|
||||
image.bitmap.data = image.bitmap.data.slice(start, end);
|
||||
} else {
|
||||
const bitmap = Buffer.allocUnsafe(w * h * 4);
|
||||
let offset = 0;
|
||||
|
||||
scan(image, x, y, w, h, function (_, __, idx) {
|
||||
const data = image.bitmap.data.readUInt32BE(idx);
|
||||
bitmap.writeUInt32BE(data, offset);
|
||||
offset += 4;
|
||||
});
|
||||
|
||||
image.bitmap.data = bitmap;
|
||||
}
|
||||
|
||||
image.bitmap.width = w;
|
||||
image.bitmap.height = h;
|
||||
|
||||
return image;
|
||||
},
|
||||
|
||||
/**
|
||||
* Autocrop same color borders from this image.
|
||||
* This function will attempt to crop out transparent pixels from the image.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { Jimp } from "jimp";
|
||||
*
|
||||
* const image = await Jimp.read("test/image.png");
|
||||
* const cropped = image.autocrop();
|
||||
* ```
|
||||
*/
|
||||
autocrop<I extends JimpClass>(image: I, options: AutocropOptions = {}) {
|
||||
const {
|
||||
tolerance = 0.0002,
|
||||
cropOnlyFrames = true,
|
||||
cropSymmetric = false,
|
||||
leaveBorder = 0,
|
||||
ignoreSides: ignoreSidesArg,
|
||||
} = typeof options === "number"
|
||||
? ({ tolerance: options } as AutocropComplexOptions)
|
||||
: AutocropComplexOptionsSchema.parse(options);
|
||||
const w = image.bitmap.width;
|
||||
const h = image.bitmap.height;
|
||||
const minPixelsPerSide = 1; // to avoid cropping completely the image, resulting in an invalid 0 sized image
|
||||
|
||||
// i.e. north and south / east and west are cropped by the same value
|
||||
const ignoreSides = {
|
||||
north: false,
|
||||
south: false,
|
||||
east: false,
|
||||
west: false,
|
||||
...ignoreSidesArg,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = image.getPixelColor(0, 0); // top left pixel color is the target color
|
||||
const rgba1 = 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 = image.getPixelColor(0, 0);
|
||||
if (!ignoreSides.north) {
|
||||
north: for (let y = 0; y < h - minPixelsPerSide; y++) {
|
||||
for (let x = 0; x < w; x++) {
|
||||
const colorXY = image.getPixelColor(x, y);
|
||||
const rgba2 = intToRGBA(colorXY);
|
||||
|
||||
if (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 = image.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 = image.getPixelColor(x, y);
|
||||
const rgba2 = intToRGBA(colorXY);
|
||||
|
||||
if (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 = image.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 = image.getPixelColor(x, y);
|
||||
const rgba2 = intToRGBA(colorXY);
|
||||
|
||||
if (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 = image.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 = image.getPixelColor(x, y);
|
||||
const rgba2 = intToRGBA(colorXY);
|
||||
|
||||
if (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(image, {
|
||||
x: westPixelsToCrop,
|
||||
y: northPixelsToCrop,
|
||||
w: widthOfRemainingPixels,
|
||||
h: heightOfRemainingPixels,
|
||||
});
|
||||
}
|
||||
|
||||
return image;
|
||||
},
|
||||
};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "@jimp/config-typescript/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import config from "@jimp/config-vitest/browser";
|
||||
|
||||
export default config;
|
||||
Reference in new issue
Block a user