chore: add jimp dependency, icon padding script, and update configuration

This commit is contained in:
2026-05-31 14:04:37 +02:00
parent 30f81045ac
commit 7cd17307ba
1126 changed files with 166400 additions and 2 deletions
+209
View File
@@ -0,0 +1,209 @@
export function srcOver(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const r = (src.r * src.a + dst.r * dst.a * (1 - src.a)) / a;
const g = (src.g * src.a + dst.g * dst.a * (1 - src.a)) / a;
const b = (src.b * src.a + dst.b * dst.a * (1 - src.a)) / a;
return {
r,
g,
b,
a
};
}
export function dstOver(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const r = (dst.r * dst.a + src.r * src.a * (1 - dst.a)) / a;
const g = (dst.g * dst.a + src.g * src.a * (1 - dst.a)) / a;
const b = (dst.b * dst.a + src.b * src.a * (1 - dst.a)) / a;
return {
r,
g,
b,
a
};
}
export function multiply(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const sra = src.r * src.a;
const sga = src.g * src.a;
const sba = src.b * src.a;
const dra = dst.r * dst.a;
const dga = dst.g * dst.a;
const dba = dst.b * dst.a;
const r = (sra * dra + sra * (1 - dst.a) + dra * (1 - src.a)) / a;
const g = (sga * dga + sga * (1 - dst.a) + dga * (1 - src.a)) / a;
const b = (sba * dba + sba * (1 - dst.a) + dba * (1 - src.a)) / a;
return {
r,
g,
b,
a
};
}
export function add(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const sra = src.r * src.a;
const sga = src.g * src.a;
const sba = src.b * src.a;
const dra = dst.r * dst.a;
const dga = dst.g * dst.a;
const dba = dst.b * dst.a;
const r = (sra + dra) / a;
const g = (sga + dga) / a;
const b = (sba + dba) / a;
return {
r,
g,
b,
a
};
}
export function screen(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const sra = src.r * src.a;
const sga = src.g * src.a;
const sba = src.b * src.a;
const dra = dst.r * dst.a;
const dga = dst.g * dst.a;
const dba = dst.b * dst.a;
const r = (sra * dst.a + dra * src.a - sra * dra + sra * (1 - dst.a) + dra * (1 - src.a)) / a;
const g = (sga * dst.a + dga * src.a - sga * dga + sga * (1 - dst.a) + dga * (1 - src.a)) / a;
const b = (sba * dst.a + dba * src.a - sba * dba + sba * (1 - dst.a) + dba * (1 - src.a)) / a;
return {
r,
g,
b,
a
};
}
export function overlay(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const sra = src.r * src.a;
const sga = src.g * src.a;
const sba = src.b * src.a;
const dra = dst.r * dst.a;
const dga = dst.g * dst.a;
const dba = dst.b * dst.a;
const r = (2 * dra <= dst.a ? 2 * sra * dra + sra * (1 - dst.a) + dra * (1 - src.a) : sra * (1 + dst.a) + dra * (1 + src.a) - 2 * dra * sra - dst.a * src.a) / a;
const g = (2 * dga <= dst.a ? 2 * sga * dga + sga * (1 - dst.a) + dga * (1 - src.a) : sga * (1 + dst.a) + dga * (1 + src.a) - 2 * dga * sga - dst.a * src.a) / a;
const b = (2 * dba <= dst.a ? 2 * sba * dba + sba * (1 - dst.a) + dba * (1 - src.a) : sba * (1 + dst.a) + dba * (1 + src.a) - 2 * dba * sba - dst.a * src.a) / a;
return {
r,
g,
b,
a
};
}
export function darken(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const sra = src.r * src.a;
const sga = src.g * src.a;
const sba = src.b * src.a;
const dra = dst.r * dst.a;
const dga = dst.g * dst.a;
const dba = dst.b * dst.a;
const r = (Math.min(sra * dst.a, dra * src.a) + sra * (1 - dst.a) + dra * (1 - src.a)) / a;
const g = (Math.min(sga * dst.a, dga * src.a) + sga * (1 - dst.a) + dga * (1 - src.a)) / a;
const b = (Math.min(sba * dst.a, dba * src.a) + sba * (1 - dst.a) + dba * (1 - src.a)) / a;
return {
r,
g,
b,
a
};
}
export function lighten(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const sra = src.r * src.a;
const sga = src.g * src.a;
const sba = src.b * src.a;
const dra = dst.r * dst.a;
const dga = dst.g * dst.a;
const dba = dst.b * dst.a;
const r = (Math.max(sra * dst.a, dra * src.a) + sra * (1 - dst.a) + dra * (1 - src.a)) / a;
const g = (Math.max(sga * dst.a, dga * src.a) + sga * (1 - dst.a) + dga * (1 - src.a)) / a;
const b = (Math.max(sba * dst.a, dba * src.a) + sba * (1 - dst.a) + dba * (1 - src.a)) / a;
return {
r,
g,
b,
a
};
}
export function hardLight(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const sra = src.r * src.a;
const sga = src.g * src.a;
const sba = src.b * src.a;
const dra = dst.r * dst.a;
const dga = dst.g * dst.a;
const dba = dst.b * dst.a;
const r = (2 * sra <= src.a ? 2 * sra * dra + sra * (1 - dst.a) + dra * (1 - src.a) : sra * (1 + dst.a) + dra * (1 + src.a) - 2 * dra * sra - dst.a * src.a) / a;
const g = (2 * sga <= src.a ? 2 * sga * dga + sga * (1 - dst.a) + dga * (1 - src.a) : sga * (1 + dst.a) + dga * (1 + src.a) - 2 * dga * sga - dst.a * src.a) / a;
const b = (2 * sba <= src.a ? 2 * sba * dba + sba * (1 - dst.a) + dba * (1 - src.a) : sba * (1 + dst.a) + dba * (1 + src.a) - 2 * dba * sba - dst.a * src.a) / a;
return {
r,
g,
b,
a
};
}
export function difference(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const sra = src.r * src.a;
const sga = src.g * src.a;
const sba = src.b * src.a;
const dra = dst.r * dst.a;
const dga = dst.g * dst.a;
const dba = dst.b * dst.a;
const r = (sra + dra - 2 * Math.min(sra * dst.a, dra * src.a)) / a;
const g = (sga + dga - 2 * Math.min(sga * dst.a, dga * src.a)) / a;
const b = (sba + dba - 2 * Math.min(sba * dst.a, dba * src.a)) / a;
return {
r,
g,
b,
a
};
}
export function exclusion(src, dst) {
let ops = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
src.a *= ops;
const a = dst.a + src.a - dst.a * src.a;
const sra = src.r * src.a;
const sga = src.g * src.a;
const sba = src.b * src.a;
const dra = dst.r * dst.a;
const dga = dst.g * dst.a;
const dba = dst.b * dst.a;
const r = (sra * dst.a + dra * src.a - 2 * sra * dra + sra * (1 - dst.a) + dra * (1 - src.a)) / a;
const g = (sga * dst.a + dga * src.a - 2 * sga * dga + sga * (1 - dst.a) + dga * (1 - src.a)) / a;
const b = (sba * dst.a + dba * src.a - 2 * sba * dba + sba * (1 - dst.a) + dba * (1 - src.a)) / a;
return {
r,
g,
b,
a
};
}
//# sourceMappingURL=composite-modes.js.map
File diff suppressed because one or more lines are too long
+79
View File
@@ -0,0 +1,79 @@
import { isNodePattern, throwError } from "@jimp/utils";
import * as constants from "../constants";
import * as compositeModes from "./composite-modes";
/**
* Composites a source image over to this image respecting alpha channels
* @param {Jimp} src the source Jimp instance
* @param {number} x the x position to blit the image
* @param {number} y the y position to blit the image
* @param {object} options determine what mode to use
* @param {function(Error, Jimp)} cb (optional) a callback for when complete
* @returns {Jimp} this for chaining of methods
*/
export default function composite(src, x, y) {
let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
let cb = arguments.length > 4 ? arguments[4] : undefined;
if (typeof options === "function") {
cb = options;
options = {};
}
if (!(src instanceof this.constructor)) {
return throwError.call(this, "The source must be a Jimp image", cb);
}
if (typeof x !== "number" || typeof y !== "number") {
return throwError.call(this, "x and y must be numbers", cb);
}
let {
mode,
opacitySource,
opacityDest
} = options;
if (!mode) {
mode = constants.BLEND_SOURCE_OVER;
}
if (typeof opacitySource !== "number" || opacitySource < 0 || opacitySource > 1) {
opacitySource = 1.0;
}
if (typeof opacityDest !== "number" || opacityDest < 0 || opacityDest > 1) {
opacityDest = 1.0;
}
// eslint-disable-next-line import/namespace
const blendmode = compositeModes[mode];
// round input
x = Math.round(x);
y = Math.round(y);
const baseImage = this;
if (opacityDest !== 1.0) {
baseImage.opacity(opacityDest);
}
src.scanQuiet(0, 0, src.bitmap.width, src.bitmap.height, function (sx, sy, idx) {
const dstIdx = baseImage.getPixelIndex(x + sx, y + sy, constants.EDGE_CROP);
if (dstIdx === -1) {
// Skip target pixels outside of dst
return;
}
const blended = blendmode({
r: this.bitmap.data[idx + 0] / 255,
g: this.bitmap.data[idx + 1] / 255,
b: this.bitmap.data[idx + 2] / 255,
a: this.bitmap.data[idx + 3] / 255
}, {
r: baseImage.bitmap.data[dstIdx + 0] / 255,
g: baseImage.bitmap.data[dstIdx + 1] / 255,
b: baseImage.bitmap.data[dstIdx + 2] / 255,
a: baseImage.bitmap.data[dstIdx + 3] / 255
}, opacitySource);
baseImage.bitmap.data[dstIdx + 0] = this.constructor.limit255(blended.r * 255);
baseImage.bitmap.data[dstIdx + 1] = this.constructor.limit255(blended.g * 255);
baseImage.bitmap.data[dstIdx + 2] = this.constructor.limit255(blended.b * 255);
baseImage.bitmap.data[dstIdx + 3] = this.constructor.limit255(blended.a * 255);
});
if (isNodePattern(cb)) {
cb.call(this, null, this);
}
return this;
}
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
+29
View File
@@ -0,0 +1,29 @@
// used to auto resizing etc.
export const AUTO = -1;
// Align modes for cover, contain, bit masks
export const HORIZONTAL_ALIGN_LEFT = 1;
export const HORIZONTAL_ALIGN_CENTER = 2;
export const HORIZONTAL_ALIGN_RIGHT = 4;
export const VERTICAL_ALIGN_TOP = 8;
export const VERTICAL_ALIGN_MIDDLE = 16;
export const VERTICAL_ALIGN_BOTTOM = 32;
// blend modes
export const BLEND_SOURCE_OVER = "srcOver";
export const BLEND_DESTINATION_OVER = "dstOver";
export const BLEND_MULTIPLY = "multiply";
export const BLEND_ADD = "add";
export const BLEND_SCREEN = "screen";
export const BLEND_OVERLAY = "overlay";
export const BLEND_DARKEN = "darken";
export const BLEND_LIGHTEN = "lighten";
export const BLEND_HARDLIGHT = "hardLight";
export const BLEND_DIFFERENCE = "difference";
export const BLEND_EXCLUSION = "exclusion";
// Edge Handling
export const EDGE_EXTEND = 1;
export const EDGE_WRAP = 2;
export const EDGE_CROP = 3;
//# sourceMappingURL=constants.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"constants.js","names":["AUTO","HORIZONTAL_ALIGN_LEFT","HORIZONTAL_ALIGN_CENTER","HORIZONTAL_ALIGN_RIGHT","VERTICAL_ALIGN_TOP","VERTICAL_ALIGN_MIDDLE","VERTICAL_ALIGN_BOTTOM","BLEND_SOURCE_OVER","BLEND_DESTINATION_OVER","BLEND_MULTIPLY","BLEND_ADD","BLEND_SCREEN","BLEND_OVERLAY","BLEND_DARKEN","BLEND_LIGHTEN","BLEND_HARDLIGHT","BLEND_DIFFERENCE","BLEND_EXCLUSION","EDGE_EXTEND","EDGE_WRAP","EDGE_CROP"],"sources":["../src/constants.js"],"sourcesContent":["// used to auto resizing etc.\nexport const AUTO = -1;\n\n// Align modes for cover, contain, bit masks\nexport const HORIZONTAL_ALIGN_LEFT = 1;\nexport const HORIZONTAL_ALIGN_CENTER = 2;\nexport const HORIZONTAL_ALIGN_RIGHT = 4;\n\nexport const VERTICAL_ALIGN_TOP = 8;\nexport const VERTICAL_ALIGN_MIDDLE = 16;\nexport const VERTICAL_ALIGN_BOTTOM = 32;\n\n// blend modes\nexport const BLEND_SOURCE_OVER = \"srcOver\";\nexport const BLEND_DESTINATION_OVER = \"dstOver\";\nexport const BLEND_MULTIPLY = \"multiply\";\nexport const BLEND_ADD = \"add\";\nexport const BLEND_SCREEN = \"screen\";\nexport const BLEND_OVERLAY = \"overlay\";\nexport const BLEND_DARKEN = \"darken\";\nexport const BLEND_LIGHTEN = \"lighten\";\nexport const BLEND_HARDLIGHT = \"hardLight\";\nexport const BLEND_DIFFERENCE = \"difference\";\nexport const BLEND_EXCLUSION = \"exclusion\";\n\n// Edge Handling\nexport const EDGE_EXTEND = 1;\nexport const EDGE_WRAP = 2;\nexport const EDGE_CROP = 3;\n"],"mappings":"AAAA;AACA,OAAO,MAAMA,IAAI,GAAG,CAAC,CAAC;;AAEtB;AACA,OAAO,MAAMC,qBAAqB,GAAG,CAAC;AACtC,OAAO,MAAMC,uBAAuB,GAAG,CAAC;AACxC,OAAO,MAAMC,sBAAsB,GAAG,CAAC;AAEvC,OAAO,MAAMC,kBAAkB,GAAG,CAAC;AACnC,OAAO,MAAMC,qBAAqB,GAAG,EAAE;AACvC,OAAO,MAAMC,qBAAqB,GAAG,EAAE;;AAEvC;AACA,OAAO,MAAMC,iBAAiB,GAAG,SAAS;AAC1C,OAAO,MAAMC,sBAAsB,GAAG,SAAS;AAC/C,OAAO,MAAMC,cAAc,GAAG,UAAU;AACxC,OAAO,MAAMC,SAAS,GAAG,KAAK;AAC9B,OAAO,MAAMC,YAAY,GAAG,QAAQ;AACpC,OAAO,MAAMC,aAAa,GAAG,SAAS;AACtC,OAAO,MAAMC,YAAY,GAAG,QAAQ;AACpC,OAAO,MAAMC,aAAa,GAAG,SAAS;AACtC,OAAO,MAAMC,eAAe,GAAG,WAAW;AAC1C,OAAO,MAAMC,gBAAgB,GAAG,YAAY;AAC5C,OAAO,MAAMC,eAAe,GAAG,WAAW;;AAE1C;AACA,OAAO,MAAMC,WAAW,GAAG,CAAC;AAC5B,OAAO,MAAMC,SAAS,GAAG,CAAC;AAC1B,OAAO,MAAMC,SAAS,GAAG,CAAC"}
+1027
View File
File diff suppressed because it is too large Load Diff
+1
View File
File diff suppressed because one or more lines are too long
+162
View File
@@ -0,0 +1,162 @@
/*
Copyright (c) 2011 Elliot Shepherd
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.
*/
// https://code.google.com/p/ironchef-team21/source/browse/ironchef_team21/src/ImagePHash.java
/*
* pHash-like image hash.
* Author: Elliot Shepherd (elliot@jarofworms.com
* Based On: http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
*/
function ImagePHash(size, smallerSize) {
this.size = this.size || size;
this.smallerSize = this.smallerSize || smallerSize;
initCoefficients(this.size);
}
ImagePHash.prototype.size = 32;
ImagePHash.prototype.smallerSize = 8;
ImagePHash.prototype.distance = function (s1, s2) {
let counter = 0;
for (let k = 0; k < s1.length; k++) {
if (s1[k] !== s2[k]) {
counter++;
}
}
return counter / s1.length;
};
// Returns a 'binary string' (like. 001010111011100010) which is easy to do a hamming distance on.
ImagePHash.prototype.getHash = function (img) {
/* 1. Reduce size.
* Like Average Hash, pHash starts with a small image.
* However, the image is larger than 8x8; 32x32 is a good size.
* This is really done to simplify the DCT computation and not
* because it is needed to reduce the high frequencies.
*/
img = img.clone().resize(this.size, this.size);
/* 2. Reduce color.
* The image is reduced to a grayscale just to further simplify
* the number of computations.
*/
img.grayscale();
const vals = [];
for (let x = 0; x < img.bitmap.width; x++) {
vals[x] = [];
for (let y = 0; y < img.bitmap.height; y++) {
vals[x][y] = intToRGBA(img.getPixelColor(x, y)).b;
}
}
/* 3. Compute the DCT.
* The DCT separates the image into a collection of frequencies
* and scalars. While JPEG uses an 8x8 DCT, this algorithm uses
* a 32x32 DCT.
*/
const dctVals = applyDCT(vals, this.size);
/* 4. Reduce the DCT.
* This is the magic step. While the DCT is 32x32, just keep the
* top-left 8x8. Those represent the lowest frequencies in the
* picture.
*/
/* 5. Compute the average value.
* Like the Average Hash, compute the mean DCT value (using only
* the 8x8 DCT low-frequency values and excluding the first term
* since the DC coefficient can be significantly different from
* the other values and will throw off the average).
*/
let total = 0;
for (let x = 0; x < this.smallerSize; x++) {
for (let y = 0; y < this.smallerSize; y++) {
total += dctVals[x][y];
}
}
const avg = total / (this.smallerSize * this.smallerSize);
/* 6. Further reduce the DCT.
* This is the magic step. Set the 64 hash bits to 0 or 1
* depending on whether each of the 64 DCT values is above or
* below the average value. The result doesn't tell us the
* actual low frequencies; it just tells us the very-rough
* relative scale of the frequencies to the mean. The result
* will not vary as long as the overall structure of the image
* remains the same; this can survive gamma and color histogram
* adjustments without a problem.
*/
let hash = "";
for (let x = 0; x < this.smallerSize; x++) {
for (let y = 0; y < this.smallerSize; y++) {
hash += dctVals[x][y] > avg ? "1" : "0";
}
}
return hash;
};
// DCT function stolen from http://stackoverflow.com/questions/4240490/problems-with-dct-and-idct-algorithm-in-java
/**
Convert a 32-bit integer color value to an RGBA object.
*/
function intToRGBA(i) {
const a = i & 0xff;
i >>>= 8;
const b = i & 0xff;
i >>>= 8;
const g = i & 0xff;
i >>>= 8;
const r = i & 0xff;
return {
r,
g,
b,
a
};
}
const c = [];
function initCoefficients(size) {
for (let i = 1; i < size; i++) {
c[i] = 1;
}
c[0] = 1 / Math.sqrt(2.0);
}
function applyDCT(f, size) {
const N = size;
const F = [];
for (let u = 0; u < N; u++) {
F[u] = [];
for (let v = 0; v < N; v++) {
let sum = 0;
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
sum += Math.cos((2 * i + 1) / (2.0 * N) * u * Math.PI) * Math.cos((2 * j + 1) / (2.0 * N) * v * Math.PI) * f[i][j];
}
}
sum *= c[u] * c[v] / 4;
F[u][v] = sum;
}
}
return F;
}
export default ImagePHash;
//# sourceMappingURL=phash.js.map
File diff suppressed because one or more lines are too long
+16
View File
@@ -0,0 +1,16 @@
import "isomorphic-fetch";
export default ((_ref, cb) => {
let {
url,
...options
} = _ref;
fetch(url, options).then(response => {
if (response.ok) {
return response.arrayBuffer().catch(error => {
throw new Error(`Response is not a buffer for url ${url}. Error: ${error.message}`);
});
}
throw new Error(`HTTP Status ${response.status} for url ${url}`);
}).then(data => cb(null, data)).catch(error => cb(error));
});
//# sourceMappingURL=request.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"request.js","names":["cb","url","options","fetch","then","response","ok","arrayBuffer","catch","error","Error","message","status","data"],"sources":["../src/request.js"],"sourcesContent":["import \"isomorphic-fetch\";\n\nexport default ({ url, ...options }, cb) => {\n fetch(url, options)\n .then((response) => {\n if (response.ok) {\n return response.arrayBuffer().catch((error) => {\n throw new Error(\n `Response is not a buffer for url ${url}. Error: ${error.message}`\n );\n });\n }\n\n throw new Error(`HTTP Status ${response.status} for url ${url}`);\n })\n .then((data) => cb(null, data))\n .catch((error) => cb(error));\n};\n"],"mappings":"AAAA,OAAO,kBAAkB;AAEzB,gBAAe,OAAsBA,EAAE,KAAK;EAAA,IAA5B;IAAEC,GAAG;IAAE,GAAGC;EAAQ,CAAC;EACjCC,KAAK,CAACF,GAAG,EAAEC,OAAO,CAAC,CAChBE,IAAI,CAAEC,QAAQ,IAAK;IAClB,IAAIA,QAAQ,CAACC,EAAE,EAAE;MACf,OAAOD,QAAQ,CAACE,WAAW,EAAE,CAACC,KAAK,CAAEC,KAAK,IAAK;QAC7C,MAAM,IAAIC,KAAK,CACZ,oCAAmCT,GAAI,YAAWQ,KAAK,CAACE,OAAQ,EAAC,CACnE;MACH,CAAC,CAAC;IACJ;IAEA,MAAM,IAAID,KAAK,CAAE,eAAcL,QAAQ,CAACO,MAAO,YAAWX,GAAI,EAAC,CAAC;EAClE,CAAC,CAAC,CACDG,IAAI,CAAES,IAAI,IAAKb,EAAE,CAAC,IAAI,EAAEa,IAAI,CAAC,CAAC,CAC9BL,KAAK,CAAEC,KAAK,IAAKT,EAAE,CAACS,KAAK,CAAC,CAAC;AAChC,CAAC"}
+210
View File
@@ -0,0 +1,210 @@
import FileType from "file-type";
import EXIFParser from "exif-parser";
import { throwError } from "@jimp/utils";
import * as constants from "../constants";
import * as MIME from "./mime";
import promisify from "./promisify";
async function getMIMEFromBuffer(buffer, path) {
const fileTypeFromBuffer = await FileType.fromBuffer(buffer);
if (fileTypeFromBuffer) {
// If fileType returns something for buffer, then return the mime given
return fileTypeFromBuffer.mime;
}
if (path) {
// If a path is supplied, and fileType yields no results, then retry with MIME
// Path can be either a file path or a url
return MIME.getType(path);
}
return null;
}
/*
* Obtains image orientation from EXIF metadata.
*
* @param img {Jimp} a Jimp image object
* @returns {number} a number 1-8 representing EXIF orientation,
* in particular 1 if orientation tag is missing
*/
function getExifOrientation(img) {
return img._exif && img._exif.tags && img._exif.tags.Orientation || 1;
}
/**
* Returns a function which translates EXIF-rotated coordinates into
* non-rotated ones.
*
* Transformation reference: http://sylvana.net/jpegcrop/exif_orientation.html.
*
* @param img {Jimp} a Jimp image object
* @returns {function} transformation function for transformBitmap().
*/
function getExifOrientationTransformation(img) {
const w = img.getWidth();
const h = img.getHeight();
switch (getExifOrientation(img)) {
case 1:
// Horizontal (normal)
// does not need to be supported here
return null;
case 2:
// Mirror horizontal
return function (x, y) {
return [w - x - 1, y];
};
case 3:
// Rotate 180
return function (x, y) {
return [w - x - 1, h - y - 1];
};
case 4:
// Mirror vertical
return function (x, y) {
return [x, h - y - 1];
};
case 5:
// Mirror horizontal and rotate 270 CW
return function (x, y) {
return [y, x];
};
case 6:
// Rotate 90 CW
return function (x, y) {
return [y, h - x - 1];
};
case 7:
// Mirror horizontal and rotate 90 CW
return function (x, y) {
return [w - y - 1, h - x - 1];
};
case 8:
// Rotate 270 CW
return function (x, y) {
return [w - y - 1, x];
};
default:
return null;
}
}
/*
* Transforms bitmap in place (moves pixels around) according to given
* transformation function.
*
* @param img {Jimp} a Jimp image object, which bitmap is supposed to
* be transformed
* @param width {number} bitmap width after the transformation
* @param height {number} bitmap height after the transformation
* @param transformation {function} transformation function which defines pixel
* mapping between new and source bitmap. It takes a pair of coordinates
* in the target, and returns a respective pair of coordinates in
* the source bitmap, i.e. has following form:
* `function(new_x, new_y) { return [src_x, src_y] }`.
*/
function transformBitmap(img, width, height, transformation) {
// Underscore-prefixed values are related to the source bitmap
// Their counterparts with no prefix are related to the target bitmap
const _data = img.bitmap.data;
const _width = img.bitmap.width;
const data = Buffer.alloc(_data.length);
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
const [_x, _y] = transformation(x, y);
const idx = width * y + x << 2;
const _idx = _width * _y + _x << 2;
const pixel = _data.readUInt32BE(_idx);
data.writeUInt32BE(pixel, idx);
}
}
img.bitmap.data = data;
img.bitmap.width = width;
img.bitmap.height = height;
}
/*
* Automagically rotates an image based on its EXIF data (if present).
* @param img {Jimp} a Jimp image object
*/
function exifRotate(img) {
if (getExifOrientation(img) < 2) return;
const transformation = getExifOrientationTransformation(img);
const swapDimensions = getExifOrientation(img) > 4;
const newWidth = swapDimensions ? img.bitmap.height : img.bitmap.width;
const newHeight = swapDimensions ? img.bitmap.width : img.bitmap.height;
transformBitmap(img, newWidth, newHeight, transformation);
}
// parses a bitmap from the constructor to the JIMP bitmap property
export async function parseBitmap(data, path, cb) {
const mime = await getMIMEFromBuffer(data, path);
if (typeof mime !== "string") {
return cb(new Error("Could not find MIME for Buffer <" + path + ">"));
}
this._originalMime = mime.toLowerCase();
try {
const mime = this.getMIME();
if (this.constructor.decoders[mime]) {
this.bitmap = this.constructor.decoders[mime](data);
} else {
return throwError.call(this, "Unsupported MIME type: " + mime, cb);
}
} catch (error) {
return cb.call(this, error, this);
}
try {
this._exif = EXIFParser.create(data).parse();
exifRotate(this); // EXIF data
} catch (error) {
/* meh */
}
cb.call(this, null, this);
return this;
}
function compositeBitmapOverBackground(Jimp, image) {
return new Jimp(image.bitmap.width, image.bitmap.height, image._background).composite(image, 0, 0).bitmap;
}
/**
* Converts the image to a buffer
* @param {(string|number)} mime the mime type of the image buffer to be created
* @param {function(Error, Jimp)} cb a Node-style function to call with the buffer as the second argument
* @returns {Jimp} this for chaining of methods
*/
export function getBuffer(mime, cb) {
if (mime === constants.AUTO) {
// allow auto MIME detection
mime = this.getMIME();
}
if (typeof mime !== "string") {
return throwError.call(this, "mime must be a string", cb);
}
if (typeof cb !== "function") {
return throwError.call(this, "cb must be a function", cb);
}
mime = mime.toLowerCase();
if (this._rgba && this.constructor.hasAlpha[mime]) {
this.bitmap.data = Buffer.from(this.bitmap.data);
} else {
// when format doesn't support alpha
// composite onto a new image so that the background shows through alpha channels
this.bitmap.data = compositeBitmapOverBackground(this.constructor, this).data;
}
if (this.constructor.encoders[mime]) {
const buffer = this.constructor.encoders[mime](this);
// Typically, buffers return a string or map. However, the gif library "gifwrap" seemingly returns promises.
if (buffer instanceof Promise) {
// trigger the callback when the promise has been resolved
buffer.then(buff => {
cb.call(this, null, buff);
});
} else {
cb.call(this, null, buffer);
}
} else {
return throwError.call(this, "Unsupported MIME type: " + mime, cb);
}
return this;
}
export function getBufferAsync(mime) {
return promisify(getBuffer, this, mime);
}
//# sourceMappingURL=image-bitmap.js.map
File diff suppressed because one or more lines are too long
+25
View File
@@ -0,0 +1,25 @@
const mimeTypes = {};
const findType = extension => Object.entries(mimeTypes).find(type => type[1].includes(extension)) || [];
export const addType = (mime, extensions) => {
mimeTypes[mime] = extensions;
};
/**
* Lookup a mime type based on extension
* @param {string} path path to find extension for
* @returns {string} mime found mime type
*/
export const getType = path => {
const pathParts = path.split("/").slice(-1);
const extension = pathParts[pathParts.length - 1].split(".").pop();
const type = findType(extension);
return type[0];
};
/**
* Return file extension associated with a mime type
* @param {string} type mime type to look up
* @returns {string} extension file extension
*/
export const getExtension = type => (mimeTypes[type.toLowerCase()] || [])[0];
//# sourceMappingURL=mime.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"mime.js","names":["mimeTypes","findType","extension","Object","entries","find","type","includes","addType","mime","extensions","getType","path","pathParts","split","slice","length","pop","getExtension","toLowerCase"],"sources":["../../src/utils/mime.js"],"sourcesContent":["const mimeTypes = {};\n\nconst findType = (extension) =>\n Object.entries(mimeTypes).find((type) => type[1].includes(extension)) || [];\n\nexport const addType = (mime, extensions) => {\n mimeTypes[mime] = extensions;\n};\n\n/**\n * Lookup a mime type based on extension\n * @param {string} path path to find extension for\n * @returns {string} mime found mime type\n */\nexport const getType = (path) => {\n const pathParts = path.split(\"/\").slice(-1);\n const extension = pathParts[pathParts.length - 1].split(\".\").pop();\n const type = findType(extension);\n\n return type[0];\n};\n\n/**\n * Return file extension associated with a mime type\n * @param {string} type mime type to look up\n * @returns {string} extension file extension\n */\nexport const getExtension = (type) => (mimeTypes[type.toLowerCase()] || [])[0];\n"],"mappings":"AAAA,MAAMA,SAAS,GAAG,CAAC,CAAC;AAEpB,MAAMC,QAAQ,GAAIC,SAAS,IACzBC,MAAM,CAACC,OAAO,CAACJ,SAAS,CAAC,CAACK,IAAI,CAAEC,IAAI,IAAKA,IAAI,CAAC,CAAC,CAAC,CAACC,QAAQ,CAACL,SAAS,CAAC,CAAC,IAAI,EAAE;AAE7E,OAAO,MAAMM,OAAO,GAAG,CAACC,IAAI,EAAEC,UAAU,KAAK;EAC3CV,SAAS,CAACS,IAAI,CAAC,GAAGC,UAAU;AAC9B,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,OAAO,GAAIC,IAAI,IAAK;EAC/B,MAAMC,SAAS,GAAGD,IAAI,CAACE,KAAK,CAAC,GAAG,CAAC,CAACC,KAAK,CAAC,CAAC,CAAC,CAAC;EAC3C,MAAMb,SAAS,GAAGW,SAAS,CAACA,SAAS,CAACG,MAAM,GAAG,CAAC,CAAC,CAACF,KAAK,CAAC,GAAG,CAAC,CAACG,GAAG,EAAE;EAClE,MAAMX,IAAI,GAAGL,QAAQ,CAACC,SAAS,CAAC;EAEhC,OAAOI,IAAI,CAAC,CAAC,CAAC;AAChB,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMY,YAAY,GAAIZ,IAAI,IAAK,CAACN,SAAS,CAACM,IAAI,CAACa,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC"}
+16
View File
@@ -0,0 +1,16 @@
const promisify = function (fun, ctx) {
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
return new Promise((resolve, reject) => {
args.push((err, data) => {
if (err) {
reject(err);
}
resolve(data);
});
fun.bind(ctx)(...args);
});
};
export default promisify;
//# sourceMappingURL=promisify.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"promisify.js","names":["promisify","fun","ctx","args","Promise","resolve","reject","push","err","data","bind"],"sources":["../../src/utils/promisify.js"],"sourcesContent":["const promisify = (fun, ctx, ...args) =>\n new Promise((resolve, reject) => {\n args.push((err, data) => {\n if (err) {\n reject(err);\n }\n\n resolve(data);\n });\n fun.bind(ctx)(...args);\n });\n\nexport default promisify;\n"],"mappings":"AAAA,MAAMA,SAAS,GAAG,UAACC,GAAG,EAAEC,GAAG;EAAA,kCAAKC,IAAI;IAAJA,IAAI;EAAA;EAAA,OAClC,IAAIC,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IAC/BH,IAAI,CAACI,IAAI,CAAC,CAACC,GAAG,EAAEC,IAAI,KAAK;MACvB,IAAID,GAAG,EAAE;QACPF,MAAM,CAACE,GAAG,CAAC;MACb;MAEAH,OAAO,CAACI,IAAI,CAAC;IACf,CAAC,CAAC;IACFR,GAAG,CAACS,IAAI,CAACR,GAAG,CAAC,CAAC,GAAGC,IAAI,CAAC;EACxB,CAAC,CAAC;AAAA;AAEJ,eAAeH,SAAS"}