extract-colors
Version:
Extract color palettes from images
155 lines (154 loc) • 9.98 kB
JavaScript
const encodedJs = "IWZ1bmN0aW9uKCl7InVzZSBzdHJpY3QiO2NsYXNzIHR7Y29uc3RydWN0b3IodCxzLGksaD10PDwxNnxzPDw4fGkpe3RoaXMudD0xLHRoaXMuaT0tMSx0aGlzLmg9LTEsdGhpcy5vPS0xLHRoaXMudT0tMSx0aGlzLmw9dCx0aGlzLk09cyx0aGlzLkM9aSx0aGlzLnA9aH1zdGF0aWMgZGlzdGFuY2UodCxzKXtyZXR1cm4oTWF0aC5hYnMocy5sLXQubCkrTWF0aC5hYnMocy5NLXQuTSkrTWF0aC5hYnMocy5DLXQuQykpLzc2NX1jbG9uZSgpe2NvbnN0IHM9bmV3IHQodGhpcy5sLHRoaXMuTSx0aGlzLkMsdGhpcy5wKTtyZXR1cm4gcy50PXRoaXMudCxzfW0oKXtjb25zdCB0PXRoaXMubC8yNTUscz10aGlzLk0vMjU1LGk9dGhpcy5DLzI1NSxoPU1hdGgubWF4KHQscyxpKSxlPU1hdGgubWluKHQscyxpKTtpZih0aGlzLm89KGgrZSkvMixoPT09ZSl0aGlzLmg9MCx0aGlzLmk9MCx0aGlzLnU9MDtlbHNle2NvbnN0IG49aC1lO3N3aXRjaCh0aGlzLmk9dGhpcy5vPi41P24vKDItaC1lKTpuLyhoK2UpLHRoaXMudT10aGlzLmkqKDIqKC41LU1hdGguYWJzKC41LXRoaXMubykpKSxoKXtjYXNlIHQ6dGhpcy5oPSgocy1pKS9uKyhzPGk/NjowKSkvNjticmVhaztjYXNlIHM6dGhpcy5oPSgoaS10KS9uKzIpLzY7YnJlYWs7Y2FzZSBpOnRoaXMuaD0oKHQtcykvbis0KS82fX19Z2V0IHYoKXtyZXR1cm4tMT09PXRoaXMuaCYmdGhpcy5tKCksdGhpcy5ofWdldCBMKCl7cmV0dXJuLTE9PT10aGlzLmkmJnRoaXMubSgpLHRoaXMuaX1nZXQgTygpe3JldHVybi0xPT09dGhpcy5vJiZ0aGlzLm0oKSx0aGlzLm99Z2V0IF8oKXtyZXR1cm4tMT09PXRoaXMudSYmdGhpcy5tKCksdGhpcy51fX1jb25zdCBzPSh0LHMpPT5NYXRoLmFicyh0LXMpO2NsYXNzIGl7Y29uc3RydWN0b3IoKXt0aGlzLmo9W10sdGhpcy4kPW51bGx9ayh0KXt0aGlzLmoucHVzaCh0KSx0aGlzLiQ9bnVsbH1HKHQsaSxoLGUpe2Zvcihjb25zdCBhIG9mIHRoaXMuail7aWYoIShuPWEudixyPXQudixNYXRoLm1pbihzKG4scikscygobisuNSklMSwocisuNSklMSkpPGkmJnMoYS5MLHQuTCk8aCYmcyhhLk8sdC5PKTxlKSlyZXR1cm4hMX12YXIgbixyO3JldHVybiEwfWdldCBJKCl7aWYoIXRoaXMuJCl7Y29uc3R7cjpzLGc6aSxiOmh9PXRoaXMuai5yZWR1Y2UoKCh0LHMpPT4odC5yKz1zLmwsdC5nKz1zLk0sdC5iKz1zLkMsdCkpLHtyOjAsZzowLGI6MH0pLGU9dGhpcy5qLnJlZHVjZSgoKHQscyk9PnQrcy50KSwwKTt0aGlzLiQ9bmV3IHQoTWF0aC5yb3VuZChzL3RoaXMuai5sZW5ndGgpLE1hdGgucm91bmQoaS90aGlzLmoubGVuZ3RoKSxNYXRoLnJvdW5kKGgvdGhpcy5qLmxlbmd0aCkpLHRoaXMuJC50PWV9cmV0dXJuIHRoaXMuJH19Y2xhc3MgaHtjb25zdHJ1Y3Rvcih0LHMsaSl7dGhpcy5TPVtdLHRoaXMudj10LHRoaXMuTD1zLHRoaXMuTz1pfWsodCl7Y29uc3Qgcz10aGlzLlMuZmluZCgocz0+cy5HKHQsdGhpcy52LHRoaXMuTCx0aGlzLk8pKSk7aWYocylzLmsodCk7ZWxzZXtjb25zdCBzPW5ldyBpO3Muayh0KSx0aGlzLlMucHVzaChzKX19Qigpe3JldHVybiB0aGlzLlMubWFwKCh0PT50LkkpKX19Y2xhc3MgZXtjb25zdHJ1Y3Rvcigpe3RoaXMudD0wLHRoaXMuRD17fX1rKHMsaSxoLGUpe3JldHVybiB0aGlzLnQrKyx0aGlzLkRbc10/dGhpcy5EW3NdLnQrKzp0aGlzLkRbc109bmV3IHQoaSxoLGUscyksdGhpcy5EW3NdfUYoKXtyZXR1cm4gT2JqZWN0LmtleXModGhpcy5EKS5tYXAoKHQ9PnRoaXMuRFt0XSkpfUgoKXtjb25zdCB0PXRoaXMuRigpLnJlZHVjZSgoKHQscyk9PnQudD49cy50P3Q6cykpLmNsb25lKCk7cmV0dXJuIHQudD10aGlzLnQsdH19Y2xhc3Mgbntjb25zdHJ1Y3Rvcigpe3RoaXMudD0wLHRoaXMuRD17fX1GKCl7cmV0dXJuIE9iamVjdC5rZXlzKHRoaXMuRCkubWFwKCh0PT50aGlzLkRbdF0pKX1rKHQscyxpKXtjb25zdCBoPXQ8PDE2fHM8PDh8aSxlPSh0Pj40JjE1KTw8OHwocz4+NCYxNSk8PDR8aT4+NCYxNTtyZXR1cm4gdGhpcy50KyssdGhpcy5QKGUpLmsoaCx0LHMsaSl9UCh0KXtyZXR1cm4gdGhpcy5EW3RdfHwodGhpcy5EW3RdPW5ldyBlKSx0aGlzLkRbdF19cShzKXtjb25zdCBpPXRoaXMuRigpLm1hcCgodD0+dC5IKCkpKTtpLnNvcnQoKCh0LHMpPT5zLnQtdC50KSk7Y29uc3QgaD1bXTtmb3IoO2kubGVuZ3RoOyl7Y29uc3QgZT1pLnNoaWZ0KCk7aS5maWx0ZXIoKGk9PnQuZGlzdGFuY2UoZSxpKTxzKSkuZm9yRWFjaCgodD0+e2UudCs9dC50O2NvbnN0IHM9aS5maW5kSW5kZXgoKHM9PnM9PT10KSk7aS5zcGxpY2UocywxKX0pKSxoLnB1c2goZSl9cmV0dXJuIGh9fXZhciByPSh7ZGF0YTp0LHdpZHRoOnMsaGVpZ2h0Oml9LGgsZSxyKT0+e2NvbnN0IGE9bmV3IG4sbz1zJiZpJiZNYXRoLmZsb29yKHMqaS9oKXx8MTtsZXQgYz0wO2ZvcihsZXQgbj0wO248dC5sZW5ndGg7bis9NCpvKXtjb25zdCBzPXRbbl0saT10W24rMV0saD10W24rMl07cihzLGksaCx0W24rM10pP2EuayhzLGksaCk6YysrfXJldHVybntqOmEucShlKSxjb3VudDphLnQrY319O2NvbnN0IGE9KHQscyxpLGUsbik9Pntjb25zdCByPSgodCxzLGksZSxuKT0+e2NvbnN0IHI9bmV3IGgoaSxlLG4pO3QuZm9yRWFjaCgodD0+ci5rKHQpKSk7Y29uc3QgYT1yLkIoKTtyZXR1cm4gYS5zb3J0KCgodCxpKT0+KGkuXysuMSkqKC45LWkudC9zKS0odC5fKy4xKSooLjktdC50L3MpKSksYX0pKHQscyxpLGUsbik7cmV0dXJuIHIubWFwKCh0PT4oKHQscyk9Pih7aGV4OmAjJHsiMCIucmVwZWF0KDYtdC5wLnRvU3RyaW5nKDE2KS5sZW5ndGgpfSR7dC5wLnRvU3RyaW5nKDE2KX1gLHJlZDp0LmwsZ3JlZW46dC5NLGJsdWU6dC5DLGFyZWE6dC50L3MsaHVlOnQudixzYXR1cmF0aW9uOnQuTCxsaWdodG5lc3M6dC5PLGludGVuc2l0eTp0Ll99KSkodCxzKSkpfSxvPWFzeW5jKHQscyk9Pntjb25zdFtpLGgsZSxuLG8sY109cyx1PSgodCxzKT0+e2NvbnN0IGk9dC53aWR0aCp0LmhlaWdodCxoPWk8cz90LndpZHRoOk1hdGgucm91bmQodC53aWR0aCpNYXRoLnNxcnQocy9pKSksZT1pPHM/dC5oZWlnaHQ6TWF0aC5yb3VuZCh0LmhlaWdodCpNYXRoLnNxcnQocy9pKSksbj1uZXcgT2Zmc2NyZWVuQ2FudmFzKGgsZSkuZ2V0Q29udGV4dCgiMmQiKTtyZXR1cm4gbi5kcmF3SW1hZ2UodCwwLDAsdC53aWR0aCx0LmhlaWdodCwwLDAsaCxlKSxuLmdldEltYWdlRGF0YSgwLDAsaCxlKX0pKHQsaSkse2o6bCxjb3VudDpnfT1yKHUsaSxoLGUpO3JldHVybiBhKGwsZyxuLG8sYyl9LGM9YXN5bmModCxzLGkpPT57aWYodCBpbnN0YW5jZW9mIEltYWdlRGF0YXx8dCBpbnN0YW5jZW9mIE9iamVjdCYmdC5kYXRhKXJldHVybiBpKCgodCxzKT0+e2NvbnN0W2ksaCxlLG4sbyxjXT1zLHtqOnUsY291bnQ6bH09cih0LGksaCxlKTtyZXR1cm4gYSh1LGwsbixvLGMpfSkodCxzKSk7aWYoInN0cmluZyI9PXR5cGVvZiB0KXJldHVybiBpKGF3YWl0KGFzeW5jKHQscyk9Pntjb25zdCBpPWF3YWl0IGZldGNoKHQse21vZGU6c1s3XX0pLGg9YXdhaXQgaS5ibG9iKCksZT1hd2FpdCBjcmVhdGVJbWFnZUJpdG1hcChoKSxuPWF3YWl0IG8oZSxzKTtyZXR1cm4gZS5jbG9zZSgpLG59KSh0LHMpKTt0aHJvdyBuZXcgRXJyb3IoIkNhbiBub3QgYW5hbHlzZSBwaWN0dXJlIil9O29ubWVzc2FnZT10PT57Y29uc3RbcyxbaSxoLGUsLi4ubl1dPXQuZGF0YTtjKHMsW2ksaCxGdW5jdGlvbihgcmV0dXJuICR7ZX1gKSgpLC4uLm5dLHBvc3RNZXNzYWdlKX19KCk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPXdvcmtlci1CcFdWVnE4bi5qcy5tYXAK";
const decodeBase64 = (base64) => Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
const blob = typeof self !== "undefined" && self.Blob && new Blob([decodeBase64(encodedJs)], { type: "text/javascript;charset=utf-8" });
function WorkerWrapper(options) {
let objURL;
try {
objURL = blob && (self.URL || self.webkitURL).createObjectURL(blob);
if (!objURL) throw "";
const worker = new Worker(objURL, {
name: options == null ? void 0 : options.name
});
worker.addEventListener("error", () => {
(self.URL || self.webkitURL).revokeObjectURL(objURL);
});
return worker;
} catch (e) {
return new Worker(
"data:text/javascript;base64," + encodedJs,
{
name: options == null ? void 0 : options.name
}
);
} finally {
objURL && (self.URL || self.webkitURL).revokeObjectURL(objURL);
}
}
const EXTRACTOR_PIXELS_DEFAULT = 64e3;
const EXTRACTOR_DISTANCE_DEFAULT = 0.22;
const AVERAGE_HUE_DEFAULT = 1 / 12;
const AVERAGE_SATURATION_DEFAULT = 1 / 5;
const AVERAGE_LIGHTNESS_DEFAULT = 1 / 5;
function testInputs({
pixels = EXTRACTOR_PIXELS_DEFAULT,
distance = EXTRACTOR_DISTANCE_DEFAULT,
colorValidator = (_red, _green, _blue, _alpha) => (_alpha ?? 255) > 250,
hueDistance = AVERAGE_HUE_DEFAULT,
saturationDistance = AVERAGE_LIGHTNESS_DEFAULT,
lightnessDistance = AVERAGE_SATURATION_DEFAULT,
crossOrigin = "",
requestMode = "cors"
} = {}) {
const testUint = (label, val, min = 0, max = Number.MAX_SAFE_INTEGER) => {
if (!Number.isInteger(val)) {
throw new Error(`${label} is not a valid number (${val})`);
}
if (val < min) {
console.warn(`${label} can not be less than ${min} (it's ${val})`);
}
if (val > max) {
console.warn(`${label} can not be more than ${max} (it's ${val})`);
}
return Math.min(Math.max(val, min), max);
};
const testNumber = (label, val, min = 0, max = Number.MAX_VALUE) => {
if (Number(val) !== val) {
throw new Error(`${label} is not a valid number (${val})`);
}
if (val < min) {
console.warn(`${label} can not be less than ${min} (it's ${val})`);
}
if (val > max) {
console.warn(`${label} can not be more than ${max} (it's ${val})`);
}
return Math.min(Math.max(val, min), max);
};
const testFunction = (label, val) => {
if (!val || {}.toString.call(val) !== "[object Function]") {
throw new Error(`${label} is not a function (${val})`);
}
return val;
};
const testValueInList = (label, val, list) => {
if (list.indexOf(val) < 0) {
console.warn(
`${label} can be one of this values ${list.map((v) => `"${v}"`).join(", ")} (it's "${val}")`
);
}
};
testUint("pixels", pixels || 0, 1);
testNumber("distance", distance, 0, 1);
testFunction("colorValidator", colorValidator);
testNumber("hueDistance", hueDistance, 0, 1);
testNumber("saturationDistance", saturationDistance, 0, 1);
testNumber("lightnessDistance", lightnessDistance, 0, 1);
testValueInList("crossOrigin", crossOrigin, [
"",
"anonymous",
"use-credentials"
]);
testValueInList("requestMode", requestMode, [
"cors",
"navigate",
"no-cors",
"same-origin"
]);
}
const cleanInputs = ({
pixels = EXTRACTOR_PIXELS_DEFAULT,
distance = EXTRACTOR_DISTANCE_DEFAULT,
colorValidator = (_red, _green, _blue, _alpha) => (_alpha ?? 255) > 250,
hueDistance = AVERAGE_HUE_DEFAULT,
saturationDistance = AVERAGE_LIGHTNESS_DEFAULT,
lightnessDistance = AVERAGE_SATURATION_DEFAULT,
crossOrigin = "",
requestMode = "cors"
} = {}) => {
return [
Math.max(pixels, 1),
Math.min(Math.max(distance, 0), 1),
colorValidator,
Math.min(Math.max(hueDistance, 0), 1),
Math.min(Math.max(saturationDistance, 0), 1),
Math.min(Math.max(lightnessDistance, 0), 1),
crossOrigin,
requestMode
];
};
const extractColors = (picture, options) => {
if (process.env.NODE_ENV !== "production") {
testInputs(options);
}
if (picture instanceof HTMLImageElement) {
if (process.env.NODE_ENV !== "production") {
console.warn(
"HTMLImageElement not enable on worker, please send 'src' or image data instead HTMLImageElement"
);
}
picture = picture.src;
}
const [_pixels, _distance, _colorValidator, ..._cleanInputsRest] = cleanInputs(options);
return new Promise((resolve, reject) => {
try {
const worker = new WorkerWrapper();
worker.postMessage([
picture,
[_pixels, _distance, _colorValidator.toString(), ..._cleanInputsRest]
]);
worker.addEventListener("message", (message) => {
resolve(message.data);
worker.terminate();
});
worker.addEventListener("error", (error) => {
reject(error);
worker.terminate();
});
} catch (error) {
reject(error);
}
});
};
export {
extractColors
};
//# sourceMappingURL=worker-wrapper.mjs.map