Skip to content

Commit

Permalink
added ability to ignore sides on autocrop (#924)
Browse files Browse the repository at this point in the history
* added ability to ignore sides on autocrop

* tests for autocrop ignoreSides
  • Loading branch information
cbanfiel committed Aug 8, 2020
1 parent 5e34fdf commit e67d683
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 52 deletions.
6 changes: 6 additions & 0 deletions packages/plugin-crop/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ interface CropClass {
cropOnlyFrames?: boolean;
cropSymmetric?: boolean;
leaveBorder?: number;
ignoreSides?: {
north: boolean;
south: boolean;
east: boolean;
west: boolean;
}
},
cb?: ImageCallback<this>
): this;
Expand Down
123 changes: 71 additions & 52 deletions packages/plugin-crop/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ export default function pluginCrop(event) {
// 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++) {
Expand Down Expand Up @@ -111,6 +117,10 @@ export default function pluginCrop(event) {
if (typeof config.leaveBorder !== 'undefined') {
({ leaveBorder } = config);
}

if (typeof config.ignoreSides !== 'undefined') {
({ ignoreSides } = config);
}
}
}

Expand All @@ -133,78 +143,87 @@ export default function pluginCrop(event) {

// north side (scan rows from north to south)
colorTarget = this.getPixelColor(0, 0);
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;
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++;
// this row contains all pixels with the same color: increment this side pixels to crop
northPixelsToCrop++;
}
}

// east side (scan columns from east to west)
colorTarget = this.getPixelColor(w, 0);
east: 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 east;
if (!ignoreSides.east) {
east: 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 east;
}
}
}

// this column contains all pixels with the same color: increment this side pixels to crop
eastPixelsToCrop++;
// this column contains all pixels with the same color: increment this side pixels to crop
eastPixelsToCrop++;
}
}

// south side (scan rows from south to north)
colorTarget = this.getPixelColor(0, h);
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;

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++;
// this row contains all pixels with the same color: increment this side pixels to crop
southPixelsToCrop++;
}
}

// west side (scan columns from west to east)
colorTarget = this.getPixelColor(w, h);
west: for (
let x = w - 1;
x >= 0 + eastPixelsToCrop + 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 west;
if (!ignoreSides.west) {
west: for (
let x = w - 1;
x >= 0 + eastPixelsToCrop + 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 west;
}
}
}

// this column contains all pixels with the same color: increment this side pixels to crop
westPixelsToCrop++;
// this column contains all pixels with the same color: increment this side pixels to crop
westPixelsToCrop++;
}
}

// decide if a crop is needed
Expand Down
73 changes: 73 additions & 0 deletions packages/plugin-crop/test/autocrop.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -353,4 +353,77 @@ describe('Autocrop', () => {
)
);
});

it('ignore sides north', async () => {
const imgSrc = await jimp.read(
mkJGD(
' ',
' ◆◆ ',
' ◆▦▦◆ ',
' ◆▦▦▦▦◆ ',
' ◆▦▦◆ ',
' ◆◆ ',
' '
)
);

imgSrc
.autocrop({ cropOnlyFrames: false, ignoreSides: { north: true } })
.getJGDSync()
.should.be.sameJGD(
mkJGD(' ', ' ◆◆ ', ' ◆▦▦◆ ', '◆▦▦▦▦◆', ' ◆▦▦◆ ', ' ◆◆ ')
);
});

it('ignore sides south and west', async () => {
const imgSrc = await jimp.read(
mkJGD(
' ',
' ◆◆ ',
' ◆▦▦◆ ',
' ◆▦▦▦▦◆ ',
' ◆▦▦◆ ',
' ◆◆ ',
' '
)
);

imgSrc
.autocrop({
cropOnlyFrames: false,
ignoreSides: { west: true, south: true }
})
.getJGDSync()
.should.be.sameJGD(
mkJGD(
' ◆◆ ',
' ◆▦▦◆ ',
'◆▦▦▦▦◆ ',
' ◆▦▦◆ ',
' ◆◆ ',
' '
)
);
});

it('ignore sides east', async () => {
const imgSrc = await jimp.read(
mkJGD(
' ',
' ◆◆ ',
' ◆▦▦◆ ',
' ◆▦▦▦▦◆ ',
' ◆▦▦◆ ',
' ◆◆ ',
' '
)
);

imgSrc
.autocrop({ cropOnlyFrames: false, ignoreSides: { east: true } })
.getJGDSync()
.should.be.sameJGD(
mkJGD(' ◆◆ ', ' ◆▦▦◆ ', ' ◆▦▦▦▦◆', ' ◆▦▦◆ ', ' ◆◆ ')
);
});
});

0 comments on commit e67d683

Please sign in to comment.