| |
|
|
| import { throwError, isNodePattern } from "@jimp/utils"; |
| export default function pluginCrop(event) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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); |
|
|
| |
| x = Math.round(x); |
| y = Math.round(y); |
| w = Math.round(w); |
| h = Math.round(h); |
| if (x === 0 && w === this.bitmap.width) { |
| |
| 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() { |
| const w = this.bitmap.width; |
| const h = this.bitmap.height; |
| const minPixelsPerSide = 1; |
|
|
| let cb; |
| let leaveBorder = 0; |
| let tolerance = 0.0002; |
| let cropOnlyFrames = true; |
| |
| let cropSymmetric = false; |
| |
| let ignoreSides = { |
| north: false, |
| south: false, |
| east: false, |
| west: false |
| }; |
|
|
| |
| 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 = args[a]; |
| } |
| if (typeof args[a] === "boolean") { |
| |
| cropOnlyFrames = args[a]; |
| } |
| if (typeof args[a] === "function") { |
| |
| cb = args[a]; |
| } |
| if (typeof args[a] === "object") { |
| |
| 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); |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| let colorTarget = this.getPixelColor(0, 0); |
| const rgba1 = this.constructor.intToRGBA(colorTarget); |
|
|
| |
| let northPixelsToCrop = 0; |
| let eastPixelsToCrop = 0; |
| let southPixelsToCrop = 0; |
| let westPixelsToCrop = 0; |
|
|
| |
| 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) { |
| |
| break north; |
| } |
| } |
|
|
| |
| northPixelsToCrop++; |
| } |
| } |
|
|
| |
| 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) { |
| |
| break west; |
| } |
| } |
|
|
| |
| westPixelsToCrop++; |
| } |
| } |
|
|
| |
| 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) { |
| |
| break south; |
| } |
| } |
|
|
| |
| southPixelsToCrop++; |
| } |
| } |
|
|
| |
| 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) { |
| |
| break east; |
| } |
| } |
|
|
| |
| eastPixelsToCrop++; |
| } |
| } |
|
|
| |
| let doCrop = false; |
|
|
| |
| 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; |
| } |
|
|
| |
| westPixelsToCrop = westPixelsToCrop >= 0 ? westPixelsToCrop : 0; |
| eastPixelsToCrop = eastPixelsToCrop >= 0 ? eastPixelsToCrop : 0; |
| northPixelsToCrop = northPixelsToCrop >= 0 ? northPixelsToCrop : 0; |
| southPixelsToCrop = southPixelsToCrop >= 0 ? southPixelsToCrop : 0; |
|
|
| |
| const widthOfRemainingPixels = w - (westPixelsToCrop + eastPixelsToCrop); |
| const heightOfRemainingPixels = h - (southPixelsToCrop + northPixelsToCrop); |
| if (cropOnlyFrames) { |
| |
| doCrop = eastPixelsToCrop !== 0 && northPixelsToCrop !== 0 && westPixelsToCrop !== 0 && southPixelsToCrop !== 0; |
| } else { |
| |
| doCrop = eastPixelsToCrop !== 0 || northPixelsToCrop !== 0 || westPixelsToCrop !== 0 || southPixelsToCrop !== 0; |
| } |
| if (doCrop) { |
| |
| this.crop(westPixelsToCrop, northPixelsToCrop, widthOfRemainingPixels, heightOfRemainingPixels); |
| } |
| if (isNodePattern(cb)) { |
| cb.call(this, null, this); |
| } |
| return this; |
| } |
| } |
| }; |
| } |
| |