You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
9.5 KiB
267 lines
9.5 KiB
"use strict"; |
|
|
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); |
|
|
|
Object.defineProperty(exports, "__esModule", { |
|
value: true |
|
}); |
|
exports["default"] = pluginCrop; |
|
|
|
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); |
|
|
|
var _utils = require("@jimp/utils"); |
|
|
|
/* eslint-disable no-labels */ |
|
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 _utils.throwError.call(this, 'x and y must be numbers', cb); |
|
if (typeof w !== 'number' || typeof h !== 'number') return _utils.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 |
|
var start = w * y + x << 2; |
|
var end = start + h * w << 2; |
|
this.bitmap.data = this.bitmap.data.slice(start, end); |
|
} else { |
|
var bitmap = Buffer.allocUnsafe(w * h * 4); |
|
var offset = 0; |
|
this.scanQuiet(x, y, w, h, function (x, y, idx) { |
|
var 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 ((0, _utils.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: function autocrop() { |
|
var w = this.bitmap.width; |
|
var h = this.bitmap.height; |
|
var minPixelsPerSide = 1; // to avoid cropping completely the image, resulting in an invalid 0 sized image |
|
|
|
var cb; // callback |
|
|
|
var leaveBorder = 0; // Amount of pixels in border to leave |
|
|
|
var tolerance = 0.0002; // percent of color difference tolerance (default value) |
|
|
|
var cropOnlyFrames = true; // flag to force cropping only if the image has a real "frame" |
|
// i.e. all 4 sides have some border (default value) |
|
|
|
var cropSymmetric = false; // flag to force cropping top be symmetric. |
|
// i.e. north and south / east and west are cropped by the same value |
|
// parse arguments |
|
|
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { |
|
args[_key] = arguments[_key]; |
|
} |
|
|
|
for (var 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 ((0, _typeof2["default"])(args[a]) === 'object') { |
|
// config object passed |
|
var config = args[a]; |
|
|
|
if (typeof config.tolerance !== 'undefined') { |
|
tolerance = config.tolerance; |
|
} |
|
|
|
if (typeof config.cropOnlyFrames !== 'undefined') { |
|
cropOnlyFrames = config.cropOnlyFrames; |
|
} |
|
|
|
if (typeof config.cropSymmetric !== 'undefined') { |
|
cropSymmetric = config.cropSymmetric; |
|
} |
|
|
|
if (typeof config.leaveBorder !== 'undefined') { |
|
leaveBorder = config.leaveBorder; |
|
} |
|
} |
|
} |
|
/** |
|
* 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 |
|
|
|
|
|
var colorTarget = this.getPixelColor(0, 0); // top left pixel color is the target color |
|
|
|
var rgba1 = this.constructor.intToRGBA(colorTarget); // for north and east sides |
|
|
|
var northPixelsToCrop = 0; |
|
var eastPixelsToCrop = 0; |
|
var southPixelsToCrop = 0; |
|
var westPixelsToCrop = 0; // north side (scan rows from north to south) |
|
|
|
colorTarget = this.getPixelColor(0, 0); |
|
|
|
north: for (var y = 0; y < h - minPixelsPerSide; y++) { |
|
for (var x = 0; x < w; x++) { |
|
var colorXY = this.getPixelColor(x, y); |
|
var 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++; |
|
} // east side (scan columns from east to west) |
|
|
|
|
|
colorTarget = this.getPixelColor(w, 0); |
|
|
|
east: for (var _x = 0; _x < w - minPixelsPerSide; _x++) { |
|
for (var _y = 0 + northPixelsToCrop; _y < h; _y++) { |
|
var _colorXY = this.getPixelColor(_x, _y); |
|
|
|
var _rgba = this.constructor.intToRGBA(_colorXY); |
|
|
|
if (this.constructor.colorDiff(rgba1, _rgba) > 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++; |
|
} // south side (scan rows from south to north) |
|
|
|
|
|
colorTarget = this.getPixelColor(0, h); |
|
|
|
south: for (var _y2 = h - 1; _y2 >= northPixelsToCrop + minPixelsPerSide; _y2--) { |
|
for (var _x2 = w - eastPixelsToCrop - 1; _x2 >= 0; _x2--) { |
|
var _colorXY2 = this.getPixelColor(_x2, _y2); |
|
|
|
var _rgba2 = this.constructor.intToRGBA(_colorXY2); |
|
|
|
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++; |
|
} // west side (scan columns from west to east) |
|
|
|
|
|
colorTarget = this.getPixelColor(w, h); |
|
|
|
west: for (var _x3 = w - 1; _x3 >= 0 + eastPixelsToCrop + minPixelsPerSide; _x3--) { |
|
for (var _y3 = h - 1; _y3 >= 0 + northPixelsToCrop; _y3--) { |
|
var _colorXY3 = this.getPixelColor(_x3, _y3); |
|
|
|
var _rgba3 = this.constructor.intToRGBA(_colorXY3); |
|
|
|
if (this.constructor.colorDiff(rgba1, _rgba3) > 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++; |
|
} // decide if a crop is needed |
|
|
|
|
|
var doCrop = false; // apply leaveBorder |
|
|
|
westPixelsToCrop -= leaveBorder; |
|
eastPixelsToCrop -= leaveBorder; |
|
northPixelsToCrop -= leaveBorder; |
|
southPixelsToCrop -= leaveBorder; |
|
|
|
if (cropSymmetric) { |
|
var horizontal = Math.min(eastPixelsToCrop, westPixelsToCrop); |
|
var 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 |
|
|
|
var widthOfRemainingPixels = w - (westPixelsToCrop + eastPixelsToCrop); |
|
var 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(eastPixelsToCrop, northPixelsToCrop, widthOfRemainingPixels, heightOfRemainingPixels); |
|
} |
|
|
|
if ((0, _utils.isNodePattern)(cb)) { |
|
cb.call(this, null, this); |
|
} |
|
|
|
return this; |
|
} |
|
} |
|
}; |
|
} |
|
//# sourceMappingURL=index.js.map
|