UNPKG

extract-colors

Version:

Extract color palettes from images

155 lines (154 loc) 9.98 kB
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