chore: add jimp dependency, icon padding script, and update configuration
This commit is contained in:
+209
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+79
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+29
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+162
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+16
@@ -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
@@ -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
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+25
@@ -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
@@ -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
@@ -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
@@ -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"}
|
||||
Reference in New Issue
Block a user