var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __reExport = (target, module2, copyDefault, desc) => { if (module2 && typeof module2 === "object" || typeof module2 === "function") { for (let key of __getOwnPropNames(module2)) if (!__hasOwnProp.call(target, key) && (copyDefault || key !== "default")) __defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); } return target; }; var __toCommonJS = /* @__PURE__ */ ((cache) => { return (module2, temp) => { return cache && cache.get(module2) || (temp = __reExport(__markAsModule({}), module2, 1), cache && cache.set(module2, temp), temp); }; })(typeof WeakMap !== "undefined" ? /* @__PURE__ */ new WeakMap() : 0); var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // src/index.ts var src_exports = {}; __export(src_exports, { applyPalette: () => applyPalette, applyPaletteSync: () => applyPaletteSync, buildPalette: () => buildPalette, buildPaletteSync: () => buildPaletteSync, constants: () => constants_exports, conversion: () => conversion_exports, distance: () => distance_exports, image: () => image_exports, palette: () => palette_exports, quality: () => quality_exports, utils: () => utils_exports }); // src/constants/index.ts var constants_exports = {}; __export(constants_exports, { bt709: () => bt709_exports }); // src/constants/bt709.ts var bt709_exports = {}; __export(bt709_exports, { Y: () => Y, x: () => x, y: () => y }); var Y = /* @__PURE__ */ ((Y2) => { Y2[Y2["RED"] = 0.2126] = "RED"; Y2[Y2["GREEN"] = 0.7152] = "GREEN"; Y2[Y2["BLUE"] = 0.0722] = "BLUE"; Y2[Y2["WHITE"] = 1] = "WHITE"; return Y2; })(Y || {}); var x = /* @__PURE__ */ ((x2) => { x2[x2["RED"] = 0.64] = "RED"; x2[x2["GREEN"] = 0.3] = "GREEN"; x2[x2["BLUE"] = 0.15] = "BLUE"; x2[x2["WHITE"] = 0.3127] = "WHITE"; return x2; })(x || {}); var y = /* @__PURE__ */ ((y2) => { y2[y2["RED"] = 0.33] = "RED"; y2[y2["GREEN"] = 0.6] = "GREEN"; y2[y2["BLUE"] = 0.06] = "BLUE"; y2[y2["WHITE"] = 0.329] = "WHITE"; return y2; })(y || {}); // src/conversion/index.ts var conversion_exports = {}; __export(conversion_exports, { lab2rgb: () => lab2rgb, lab2xyz: () => lab2xyz, rgb2hsl: () => rgb2hsl, rgb2lab: () => rgb2lab, rgb2xyz: () => rgb2xyz, xyz2lab: () => xyz2lab, xyz2rgb: () => xyz2rgb }); // src/conversion/rgb2xyz.ts function correctGamma(n) { return n > 0.04045 ? ((n + 0.055) / 1.055) ** 2.4 : n / 12.92; } function rgb2xyz(r, g, b) { r = correctGamma(r / 255); g = correctGamma(g / 255); b = correctGamma(b / 255); return { x: r * 0.4124 + g * 0.3576 + b * 0.1805, y: r * 0.2126 + g * 0.7152 + b * 0.0722, z: r * 0.0193 + g * 0.1192 + b * 0.9505 }; } // src/utils/arithmetic.ts var arithmetic_exports = {}; __export(arithmetic_exports, { degrees2radians: () => degrees2radians, inRange0to255: () => inRange0to255, inRange0to255Rounded: () => inRange0to255Rounded, intInRange: () => intInRange, max3: () => max3, min3: () => min3, stableSort: () => stableSort }); function degrees2radians(n) { return n * (Math.PI / 180); } function max3(a, b, c) { let m = a; if (m < b) m = b; if (m < c) m = c; return m; } function min3(a, b, c) { let m = a; if (m > b) m = b; if (m > c) m = c; return m; } function intInRange(value, low, high) { if (value > high) value = high; if (value < low) value = low; return value | 0; } function inRange0to255Rounded(n) { n = Math.round(n); if (n > 255) n = 255; else if (n < 0) n = 0; return n; } function inRange0to255(n) { if (n > 255) n = 255; else if (n < 0) n = 0; return n; } function stableSort(arrayToSort, callback) { const type = typeof arrayToSort[0]; let sorted; if (type === "number" || type === "string") { const ord = /* @__PURE__ */ Object.create(null); for (let i = 0, l = arrayToSort.length; i < l; i++) { const val = arrayToSort[i]; if (ord[val] || ord[val] === 0) continue; ord[val] = i; } sorted = arrayToSort.sort((a, b) => callback(a, b) || ord[a] - ord[b]); } else { const ord2 = arrayToSort.slice(0); sorted = arrayToSort.sort((a, b) => callback(a, b) || ord2.indexOf(a) - ord2.indexOf(b)); } return sorted; } // src/conversion/rgb2hsl.ts function rgb2hsl(r, g, b) { const min = min3(r, g, b); const max = max3(r, g, b); const delta = max - min; const l = (min + max) / 510; let s = 0; if (l > 0 && l < 1) s = delta / (l < 0.5 ? max + min : 510 - max - min); let h = 0; if (delta > 0) { if (max === r) { h = (g - b) / delta; } else if (max === g) { h = 2 + (b - r) / delta; } else { h = 4 + (r - g) / delta; } h *= 60; if (h < 0) h += 360; } return { h, s, l }; } // src/conversion/xyz2lab.ts var refX = 0.95047; var refY = 1; var refZ = 1.08883; function pivot(n) { return n > 8856e-6 ? n ** (1 / 3) : 7.787 * n + 16 / 116; } function xyz2lab(x2, y2, z) { x2 = pivot(x2 / refX); y2 = pivot(y2 / refY); z = pivot(z / refZ); if (116 * y2 - 16 < 0) throw new Error("xxx"); return { L: Math.max(0, 116 * y2 - 16), a: 500 * (x2 - y2), b: 200 * (y2 - z) }; } // src/conversion/rgb2lab.ts function rgb2lab(r, g, b) { const xyz = rgb2xyz(r, g, b); return xyz2lab(xyz.x, xyz.y, xyz.z); } // src/conversion/lab2xyz.ts var refX2 = 0.95047; var refY2 = 1; var refZ2 = 1.08883; function pivot2(n) { return n > 0.206893034 ? n ** 3 : (n - 16 / 116) / 7.787; } function lab2xyz(L, a, b) { const y2 = (L + 16) / 116; const x2 = a / 500 + y2; const z = y2 - b / 200; return { x: refX2 * pivot2(x2), y: refY2 * pivot2(y2), z: refZ2 * pivot2(z) }; } // src/conversion/xyz2rgb.ts function correctGamma2(n) { return n > 31308e-7 ? 1.055 * n ** (1 / 2.4) - 0.055 : 12.92 * n; } function xyz2rgb(x2, y2, z) { const r = correctGamma2(x2 * 3.2406 + y2 * -1.5372 + z * -0.4986); const g = correctGamma2(x2 * -0.9689 + y2 * 1.8758 + z * 0.0415); const b = correctGamma2(x2 * 0.0557 + y2 * -0.204 + z * 1.057); return { r: inRange0to255Rounded(r * 255), g: inRange0to255Rounded(g * 255), b: inRange0to255Rounded(b * 255) }; } // src/conversion/lab2rgb.ts function lab2rgb(L, a, b) { const xyz = lab2xyz(L, a, b); return xyz2rgb(xyz.x, xyz.y, xyz.z); } // src/distance/index.ts var distance_exports = {}; __export(distance_exports, { AbstractDistanceCalculator: () => AbstractDistanceCalculator, AbstractEuclidean: () => AbstractEuclidean, AbstractManhattan: () => AbstractManhattan, CIE94GraphicArts: () => CIE94GraphicArts, CIE94Textiles: () => CIE94Textiles, CIEDE2000: () => CIEDE2000, CMetric: () => CMetric, Euclidean: () => Euclidean, EuclideanBT709: () => EuclideanBT709, EuclideanBT709NoAlpha: () => EuclideanBT709NoAlpha, Manhattan: () => Manhattan, ManhattanBT709: () => ManhattanBT709, ManhattanNommyde: () => ManhattanNommyde, PNGQuant: () => PNGQuant }); // src/distance/distanceCalculator.ts var AbstractDistanceCalculator = class { constructor() { __publicField(this, "_maxDistance"); __publicField(this, "_whitePoint"); this._setDefaults(); this.setWhitePoint(255, 255, 255, 255); } setWhitePoint(r, g, b, a) { this._whitePoint = { r: r > 0 ? 255 / r : 0, g: g > 0 ? 255 / g : 0, b: b > 0 ? 255 / b : 0, a: a > 0 ? 255 / a : 0 }; this._maxDistance = this.calculateRaw(r, g, b, a, 0, 0, 0, 0); } calculateNormalized(colorA, colorB) { return this.calculateRaw(colorA.r, colorA.g, colorA.b, colorA.a, colorB.r, colorB.g, colorB.b, colorB.a) / this._maxDistance; } }; // src/distance/cie94.ts var AbstractCIE94 = class extends AbstractDistanceCalculator { calculateRaw(r1, g1, b1, a1, r2, g2, b2, a2) { const lab1 = rgb2lab(inRange0to255(r1 * this._whitePoint.r), inRange0to255(g1 * this._whitePoint.g), inRange0to255(b1 * this._whitePoint.b)); const lab2 = rgb2lab(inRange0to255(r2 * this._whitePoint.r), inRange0to255(g2 * this._whitePoint.g), inRange0to255(b2 * this._whitePoint.b)); const dL = lab1.L - lab2.L; const dA = lab1.a - lab2.a; const dB = lab1.b - lab2.b; const c1 = Math.sqrt(lab1.a * lab1.a + lab1.b * lab1.b); const c2 = Math.sqrt(lab2.a * lab2.a + lab2.b * lab2.b); const dC = c1 - c2; let deltaH = dA * dA + dB * dB - dC * dC; deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH); const dAlpha = (a2 - a1) * this._whitePoint.a * this._kA; return Math.sqrt((dL / this._Kl) ** 2 + (dC / (1 + this._K1 * c1)) ** 2 + (deltaH / (1 + this._K2 * c1)) ** 2 + dAlpha ** 2); } }; var CIE94Textiles = class extends AbstractCIE94 { _setDefaults() { this._Kl = 2; this._K1 = 0.048; this._K2 = 0.014; this._kA = 0.25 * 50 / 255; } }; var CIE94GraphicArts = class extends AbstractCIE94 { _setDefaults() { this._Kl = 1; this._K1 = 0.045; this._K2 = 0.015; this._kA = 0.25 * 100 / 255; } }; // src/distance/ciede2000.ts var _CIEDE2000 = class extends AbstractDistanceCalculator { _setDefaults() { } static _calculatehp(b, ap) { const hp = Math.atan2(b, ap); if (hp >= 0) return hp; return hp + _CIEDE2000._deg360InRad; } static _calculateRT(ahp, aCp) { const aCp_to_7 = aCp ** 7; const R_C = 2 * Math.sqrt(aCp_to_7 / (aCp_to_7 + _CIEDE2000._pow25to7)); const delta_theta = _CIEDE2000._deg30InRad * Math.exp(-(((ahp - _CIEDE2000._deg275InRad) / _CIEDE2000._deg25InRad) ** 2)); return -Math.sin(2 * delta_theta) * R_C; } static _calculateT(ahp) { return 1 - 0.17 * Math.cos(ahp - _CIEDE2000._deg30InRad) + 0.24 * Math.cos(ahp * 2) + 0.32 * Math.cos(ahp * 3 + _CIEDE2000._deg6InRad) - 0.2 * Math.cos(ahp * 4 - _CIEDE2000._deg63InRad); } static _calculate_ahp(C1pC2p, h_bar, h1p, h2p) { const hpSum = h1p + h2p; if (C1pC2p === 0) return hpSum; if (h_bar <= _CIEDE2000._deg180InRad) return hpSum / 2; if (hpSum < _CIEDE2000._deg360InRad) { return (hpSum + _CIEDE2000._deg360InRad) / 2; } return (hpSum - _CIEDE2000._deg360InRad) / 2; } static _calculate_dHp(C1pC2p, h_bar, h2p, h1p) { let dhp; if (C1pC2p === 0) { dhp = 0; } else if (h_bar <= _CIEDE2000._deg180InRad) { dhp = h2p - h1p; } else if (h2p <= h1p) { dhp = h2p - h1p + _CIEDE2000._deg360InRad; } else { dhp = h2p - h1p - _CIEDE2000._deg360InRad; } return 2 * Math.sqrt(C1pC2p) * Math.sin(dhp / 2); } calculateRaw(r1, g1, b1, a1, r2, g2, b2, a2) { const lab1 = rgb2lab(inRange0to255(r1 * this._whitePoint.r), inRange0to255(g1 * this._whitePoint.g), inRange0to255(b1 * this._whitePoint.b)); const lab2 = rgb2lab(inRange0to255(r2 * this._whitePoint.r), inRange0to255(g2 * this._whitePoint.g), inRange0to255(b2 * this._whitePoint.b)); const dA = (a2 - a1) * this._whitePoint.a * _CIEDE2000._kA; const dE2 = this.calculateRawInLab(lab1, lab2); return Math.sqrt(dE2 + dA * dA); } calculateRawInLab(Lab1, Lab2) { const L1 = Lab1.L; const a1 = Lab1.a; const b1 = Lab1.b; const L2 = Lab2.L; const a2 = Lab2.a; const b2 = Lab2.b; const C1 = Math.sqrt(a1 * a1 + b1 * b1); const C2 = Math.sqrt(a2 * a2 + b2 * b2); const pow_a_C1_C2_to_7 = ((C1 + C2) / 2) ** 7; const G = 0.5 * (1 - Math.sqrt(pow_a_C1_C2_to_7 / (pow_a_C1_C2_to_7 + _CIEDE2000._pow25to7))); const a1p = (1 + G) * a1; const a2p = (1 + G) * a2; const C1p = Math.sqrt(a1p * a1p + b1 * b1); const C2p = Math.sqrt(a2p * a2p + b2 * b2); const C1pC2p = C1p * C2p; const h1p = _CIEDE2000._calculatehp(b1, a1p); const h2p = _CIEDE2000._calculatehp(b2, a2p); const h_bar = Math.abs(h1p - h2p); const dLp = L2 - L1; const dCp = C2p - C1p; const dHp = _CIEDE2000._calculate_dHp(C1pC2p, h_bar, h2p, h1p); const ahp = _CIEDE2000._calculate_ahp(C1pC2p, h_bar, h1p, h2p); const T = _CIEDE2000._calculateT(ahp); const aCp = (C1p + C2p) / 2; const aLp_minus_50_square = ((L1 + L2) / 2 - 50) ** 2; const S_L = 1 + 0.015 * aLp_minus_50_square / Math.sqrt(20 + aLp_minus_50_square); const S_C = 1 + 0.045 * aCp; const S_H = 1 + 0.015 * T * aCp; const R_T = _CIEDE2000._calculateRT(ahp, aCp); const dLpSL = dLp / S_L; const dCpSC = dCp / S_C; const dHpSH = dHp / S_H; return dLpSL ** 2 + dCpSC ** 2 + dHpSH ** 2 + R_T * dCpSC * dHpSH; } }; var CIEDE2000 = _CIEDE2000; __publicField(CIEDE2000, "_kA", 0.25 * 100 / 255); __publicField(CIEDE2000, "_pow25to7", 25 ** 7); __publicField(CIEDE2000, "_deg360InRad", degrees2radians(360)); __publicField(CIEDE2000, "_deg180InRad", degrees2radians(180)); __publicField(CIEDE2000, "_deg30InRad", degrees2radians(30)); __publicField(CIEDE2000, "_deg6InRad", degrees2radians(6)); __publicField(CIEDE2000, "_deg63InRad", degrees2radians(63)); __publicField(CIEDE2000, "_deg275InRad", degrees2radians(275)); __publicField(CIEDE2000, "_deg25InRad", degrees2radians(25)); // src/distance/cmetric.ts var CMetric = class extends AbstractDistanceCalculator { calculateRaw(r1, g1, b1, a1, r2, g2, b2, a2) { const rmean = (r1 + r2) / 2 * this._whitePoint.r; const r = (r1 - r2) * this._whitePoint.r; const g = (g1 - g2) * this._whitePoint.g; const b = (b1 - b2) * this._whitePoint.b; const dE = ((512 + rmean) * r * r >> 8) + 4 * g * g + ((767 - rmean) * b * b >> 8); const dA = (a2 - a1) * this._whitePoint.a; return Math.sqrt(dE + dA * dA); } _setDefaults() { } }; // src/distance/euclidean.ts var AbstractEuclidean = class extends AbstractDistanceCalculator { calculateRaw(r1, g1, b1, a1, r2, g2, b2, a2) { const dR = r2 - r1; const dG = g2 - g1; const dB = b2 - b1; const dA = a2 - a1; return Math.sqrt(this._kR * dR * dR + this._kG * dG * dG + this._kB * dB * dB + this._kA * dA * dA); } }; var Euclidean = class extends AbstractEuclidean { _setDefaults() { this._kR = 1; this._kG = 1; this._kB = 1; this._kA = 1; } }; var EuclideanBT709 = class extends AbstractEuclidean { _setDefaults() { this._kR = 0.2126 /* RED */; this._kG = 0.7152 /* GREEN */; this._kB = 0.0722 /* BLUE */; this._kA = 1; } }; var EuclideanBT709NoAlpha = class extends AbstractEuclidean { _setDefaults() { this._kR = 0.2126 /* RED */; this._kG = 0.7152 /* GREEN */; this._kB = 0.0722 /* BLUE */; this._kA = 0; } }; // src/distance/manhattan.ts var AbstractManhattan = class extends AbstractDistanceCalculator { calculateRaw(r1, g1, b1, a1, r2, g2, b2, a2) { let dR = r2 - r1; let dG = g2 - g1; let dB = b2 - b1; let dA = a2 - a1; if (dR < 0) dR = 0 - dR; if (dG < 0) dG = 0 - dG; if (dB < 0) dB = 0 - dB; if (dA < 0) dA = 0 - dA; return this._kR * dR + this._kG * dG + this._kB * dB + this._kA * dA; } }; var Manhattan = class extends AbstractManhattan { _setDefaults() { this._kR = 1; this._kG = 1; this._kB = 1; this._kA = 1; } }; var ManhattanNommyde = class extends AbstractManhattan { _setDefaults() { this._kR = 0.4984; this._kG = 0.8625; this._kB = 0.2979; this._kA = 1; } }; var ManhattanBT709 = class extends AbstractManhattan { _setDefaults() { this._kR = 0.2126 /* RED */; this._kG = 0.7152 /* GREEN */; this._kB = 0.0722 /* BLUE */; this._kA = 1; } }; // src/distance/pngQuant.ts var PNGQuant = class extends AbstractDistanceCalculator { calculateRaw(r1, g1, b1, a1, r2, g2, b2, a2) { const alphas = (a2 - a1) * this._whitePoint.a; return this._colordifferenceCh(r1 * this._whitePoint.r, r2 * this._whitePoint.r, alphas) + this._colordifferenceCh(g1 * this._whitePoint.g, g2 * this._whitePoint.g, alphas) + this._colordifferenceCh(b1 * this._whitePoint.b, b2 * this._whitePoint.b, alphas); } _colordifferenceCh(x2, y2, alphas) { const black = x2 - y2; const white = black + alphas; return black * black + white * white; } _setDefaults() { } }; // src/palette/index.ts var palette_exports = {}; __export(palette_exports, { AbstractPaletteQuantizer: () => AbstractPaletteQuantizer, ColorHistogram: () => ColorHistogram, NeuQuant: () => NeuQuant, NeuQuantFloat: () => NeuQuantFloat, RGBQuant: () => RGBQuant, WuColorCube: () => WuColorCube, WuQuant: () => WuQuant }); // src/palette/paletteQuantizer.ts var AbstractPaletteQuantizer = class { quantizeSync() { for (const value of this.quantize()) { if (value.palette) { return value.palette; } } throw new Error("unreachable"); } }; // src/utils/point.ts var Point = class { constructor() { __publicField(this, "r"); __publicField(this, "g"); __publicField(this, "b"); __publicField(this, "a"); __publicField(this, "uint32"); __publicField(this, "rgba"); this.uint32 = -1 >>> 0; this.r = this.g = this.b = this.a = 0; this.rgba = new Array(4); this.rgba[0] = 0; this.rgba[1] = 0; this.rgba[2] = 0; this.rgba[3] = 0; } static createByQuadruplet(quadruplet) { const point = new Point(); point.r = quadruplet[0] | 0; point.g = quadruplet[1] | 0; point.b = quadruplet[2] | 0; point.a = quadruplet[3] | 0; point._loadUINT32(); point._loadQuadruplet(); return point; } static createByRGBA(red, green, blue, alpha) { const point = new Point(); point.r = red | 0; point.g = green | 0; point.b = blue | 0; point.a = alpha | 0; point._loadUINT32(); point._loadQuadruplet(); return point; } static createByUint32(uint32) { const point = new Point(); point.uint32 = uint32 >>> 0; point._loadRGBA(); point._loadQuadruplet(); return point; } from(point) { this.r = point.r; this.g = point.g; this.b = point.b; this.a = point.a; this.uint32 = point.uint32; this.rgba[0] = point.r; this.rgba[1] = point.g; this.rgba[2] = point.b; this.rgba[3] = point.a; } getLuminosity(useAlphaChannel) { let r = this.r; let g = this.g; let b = this.b; if (useAlphaChannel) { r = Math.min(255, 255 - this.a + this.a * r / 255); g = Math.min(255, 255 - this.a + this.a * g / 255); b = Math.min(255, 255 - this.a + this.a * b / 255); } return r * 0.2126 /* RED */ + g * 0.7152 /* GREEN */ + b * 0.0722 /* BLUE */; } _loadUINT32() { this.uint32 = (this.a << 24 | this.b << 16 | this.g << 8 | this.r) >>> 0; } _loadRGBA() { this.r = this.uint32 & 255; this.g = this.uint32 >>> 8 & 255; this.b = this.uint32 >>> 16 & 255; this.a = this.uint32 >>> 24 & 255; } _loadQuadruplet() { this.rgba[0] = this.r; this.rgba[1] = this.g; this.rgba[2] = this.b; this.rgba[3] = this.a; } }; // src/utils/pointContainer.ts var PointContainer = class { constructor() { __publicField(this, "_pointArray"); __publicField(this, "_width"); __publicField(this, "_height"); this._width = 0; this._height = 0; this._pointArray = []; } getWidth() { return this._width; } getHeight() { return this._height; } setWidth(width) { this._width = width; } setHeight(height) { this._height = height; } getPointArray() { return this._pointArray; } clone() { const clone = new PointContainer(); clone._width = this._width; clone._height = this._height; for (let i = 0, l = this._pointArray.length; i < l; i++) { clone._pointArray[i] = Point.createByUint32(this._pointArray[i].uint32 | 0); } return clone; } toUint32Array() { const l = this._pointArray.length; const uint32Array = new Uint32Array(l); for (let i = 0; i < l; i++) { uint32Array[i] = this._pointArray[i].uint32; } return uint32Array; } toUint8Array() { return new Uint8Array(this.toUint32Array().buffer); } static fromHTMLImageElement(img) { const width = img.naturalWidth; const height = img.naturalHeight; const canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; const ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, width, height, 0, 0, width, height); return PointContainer.fromHTMLCanvasElement(canvas); } static fromHTMLCanvasElement(canvas) { const width = canvas.width; const height = canvas.height; const ctx = canvas.getContext("2d"); const imgData = ctx.getImageData(0, 0, width, height); return PointContainer.fromImageData(imgData); } static fromImageData(imageData) { const width = imageData.width; const height = imageData.height; return PointContainer.fromUint8Array(imageData.data, width, height); } static fromUint8Array(uint8Array, width, height) { switch (Object.prototype.toString.call(uint8Array)) { case "[object Uint8ClampedArray]": case "[object Uint8Array]": break; default: uint8Array = new Uint8Array(uint8Array); } const uint32Array = new Uint32Array(uint8Array.buffer); return PointContainer.fromUint32Array(uint32Array, width, height); } static fromUint32Array(uint32Array, width, height) { const container = new PointContainer(); container._width = width; container._height = height; for (let i = 0, l = uint32Array.length; i < l; i++) { container._pointArray[i] = Point.createByUint32(uint32Array[i] | 0); } return container; } static fromBuffer(buffer, width, height) { const uint32Array = new Uint32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / Uint32Array.BYTES_PER_ELEMENT); return PointContainer.fromUint32Array(uint32Array, width, height); } }; // src/utils/palette.ts var hueGroups = 10; function hueGroup(hue, segmentsNumber) { const maxHue = 360; const seg = maxHue / segmentsNumber; const half = seg / 2; for (let i = 1, mid = seg - half; i < segmentsNumber; i++, mid += seg) { if (hue >= mid && hue < mid + seg) return i; } return 0; } var Palette = class { constructor() { __publicField(this, "_pointContainer"); __publicField(this, "_pointArray", []); __publicField(this, "_i32idx", {}); this._pointContainer = new PointContainer(); this._pointContainer.setHeight(1); this._pointArray = this._pointContainer.getPointArray(); } add(color) { this._pointArray.push(color); this._pointContainer.setWidth(this._pointArray.length); } has(color) { for (let i = this._pointArray.length - 1; i >= 0; i--) { if (color.uint32 === this._pointArray[i].uint32) return true; } return false; } getNearestColor(colorDistanceCalculator, color) { return this._pointArray[this._getNearestIndex(colorDistanceCalculator, color) | 0]; } getPointContainer() { return this._pointContainer; } _nearestPointFromCache(key) { return typeof this._i32idx[key] === "number" ? this._i32idx[key] : -1; } _getNearestIndex(colorDistanceCalculator, point) { let idx = this._nearestPointFromCache("" + point.uint32); if (idx >= 0) return idx; let minimalDistance = Number.MAX_VALUE; idx = 0; for (let i = 0, l = this._pointArray.length; i < l; i++) { const p = this._pointArray[i]; const distance = colorDistanceCalculator.calculateRaw(point.r, point.g, point.b, point.a, p.r, p.g, p.b, p.a); if (distance < minimalDistance) { minimalDistance = distance; idx = i; } } this._i32idx[point.uint32] = idx; return idx; } sort() { this._i32idx = {}; this._pointArray.sort((a, b) => { const hslA = rgb2hsl(a.r, a.g, a.b); const hslB = rgb2hsl(b.r, b.g, b.b); const hueA = a.r === a.g && a.g === a.b ? 0 : 1 + hueGroup(hslA.h, hueGroups); const hueB = b.r === b.g && b.g === b.b ? 0 : 1 + hueGroup(hslB.h, hueGroups); const hueDiff = hueB - hueA; if (hueDiff) return -hueDiff; const lA = a.getLuminosity(true); const lB = b.getLuminosity(true); if (lB - lA !== 0) return lB - lA; const satDiff = (hslB.s * 100 | 0) - (hslA.s * 100 | 0); if (satDiff) return -satDiff; return 0; }); } }; // src/utils/index.ts var utils_exports = {}; __export(utils_exports, { HueStatistics: () => HueStatistics, Palette: () => Palette, Point: () => Point, PointContainer: () => PointContainer, ProgressTracker: () => ProgressTracker, arithmetic: () => arithmetic_exports }); // src/utils/hueStatistics.ts var HueGroup = class { constructor() { __publicField(this, "num", 0); __publicField(this, "cols", []); } }; var HueStatistics = class { constructor(numGroups, minCols) { __publicField(this, "_numGroups"); __publicField(this, "_minCols"); __publicField(this, "_stats"); __publicField(this, "_groupsFull"); this._numGroups = numGroups; this._minCols = minCols; this._stats = []; for (let i = 0; i <= numGroups; i++) { this._stats[i] = new HueGroup(); } this._groupsFull = 0; } check(i32) { if (this._groupsFull === this._numGroups + 1) { this.check = () => { }; } const r = i32 & 255; const g = i32 >>> 8 & 255; const b = i32 >>> 16 & 255; const hg = r === g && g === b ? 0 : 1 + hueGroup(rgb2hsl(r, g, b).h, this._numGroups); const gr = this._stats[hg]; const min = this._minCols; gr.num++; if (gr.num > min) { return; } if (gr.num === min) { this._groupsFull++; } if (gr.num <= min) { this._stats[hg].cols.push(i32); } } injectIntoDictionary(histG) { for (let i = 0; i <= this._numGroups; i++) { if (this._stats[i].num <= this._minCols) { this._stats[i].cols.forEach((col) => { if (!histG[col]) { histG[col] = 1; } else { histG[col]++; } }); } } } injectIntoArray(histG) { for (let i = 0; i <= this._numGroups; i++) { if (this._stats[i].num <= this._minCols) { this._stats[i].cols.forEach((col) => { if (histG.indexOf(col) === -1) { histG.push(col); } }); } } } }; // src/utils/progressTracker.ts var _ProgressTracker = class { constructor(valueRange, progressRange) { __publicField(this, "progress"); __publicField(this, "_step"); __publicField(this, "_range"); __publicField(this, "_last"); __publicField(this, "_progressRange"); this._range = valueRange; this._progressRange = progressRange; this._step = Math.max(1, this._range / (_ProgressTracker.steps + 1) | 0); this._last = -this._step; this.progress = 0; } shouldNotify(current) { if (current - this._last >= this._step) { this._last = current; this.progress = Math.min(this._progressRange * this._last / this._range, this._progressRange); return true; } return false; } }; var ProgressTracker = _ProgressTracker; __publicField(ProgressTracker, "steps", 100); // src/palette/neuquant/neuquant.ts var networkBiasShift = 3; var Neuron = class { constructor(defaultValue) { __publicField(this, "r"); __publicField(this, "g"); __publicField(this, "b"); __publicField(this, "a"); this.r = this.g = this.b = this.a = defaultValue; } toPoint() { return Point.createByRGBA(this.r >> networkBiasShift, this.g >> networkBiasShift, this.b >> networkBiasShift, this.a >> networkBiasShift); } subtract(r, g, b, a) { this.r -= r | 0; this.g -= g | 0; this.b -= b | 0; this.a -= a | 0; } }; var _NeuQuant = class extends AbstractPaletteQuantizer { constructor(colorDistanceCalculator, colors = 256) { super(); __publicField(this, "_pointArray"); __publicField(this, "_networkSize"); __publicField(this, "_network"); __publicField(this, "_sampleFactor"); __publicField(this, "_radPower"); __publicField(this, "_freq"); __publicField(this, "_bias"); __publicField(this, "_distance"); this._distance = colorDistanceCalculator; this._pointArray = []; this._sampleFactor = 1; this._networkSize = colors; this._distance.setWhitePoint(255 << networkBiasShift, 255 << networkBiasShift, 255 << networkBiasShift, 255 << networkBiasShift); } sample(pointContainer) { this._pointArray = this._pointArray.concat(pointContainer.getPointArray()); } *quantize() { this._init(); yield* this._learn(); yield { palette: this._buildPalette(), progress: 100 }; } _init() { this._freq = []; this._bias = []; this._radPower = []; this._network = []; for (let i = 0; i < this._networkSize; i++) { this._network[i] = new Neuron((i << networkBiasShift + 8) / this._networkSize | 0); this._freq[i] = _NeuQuant._initialBias / this._networkSize | 0; this._bias[i] = 0; } } *_learn() { let sampleFactor = this._sampleFactor; const pointsNumber = this._pointArray.length; if (pointsNumber < _NeuQuant._minpicturebytes) sampleFactor = 1; const alphadec = 30 + (sampleFactor - 1) / 3 | 0; const pointsToSample = pointsNumber / sampleFactor | 0; let delta = pointsToSample / _NeuQuant._nCycles | 0; let alpha = _NeuQuant._initAlpha; let radius = (this._networkSize >> 3) * _NeuQuant._radiusBias; let rad = radius >> _NeuQuant._radiusBiasShift; if (rad <= 1) rad = 0; for (let i = 0; i < rad; i++) { this._radPower[i] = alpha * ((rad * rad - i * i) * _NeuQuant._radBias / (rad * rad)) >>> 0; } let step; if (pointsNumber < _NeuQuant._minpicturebytes) { step = 1; } else if (pointsNumber % _NeuQuant._prime1 !== 0) { step = _NeuQuant._prime1; } else if (pointsNumber % _NeuQuant._prime2 !== 0) { step = _NeuQuant._prime2; } else if (pointsNumber % _NeuQuant._prime3 !== 0) { step = _NeuQuant._prime3; } else { step = _NeuQuant._prime4; } const tracker = new ProgressTracker(pointsToSample, 99); for (let i = 0, pointIndex = 0; i < pointsToSample; ) { if (tracker.shouldNotify(i)) { yield { progress: tracker.progress }; } const point = this._pointArray[pointIndex]; const b = point.b << networkBiasShift; const g = point.g << networkBiasShift; const r = point.r << networkBiasShift; const a = point.a << networkBiasShift; const neuronIndex = this._contest(b, g, r, a); this._alterSingle(alpha, neuronIndex, b, g, r, a); if (rad !== 0) this._alterNeighbour(rad, neuronIndex, b, g, r, a); pointIndex += step; if (pointIndex >= pointsNumber) pointIndex -= pointsNumber; i++; if (delta === 0) delta = 1; if (i % delta === 0) { alpha -= alpha / alphadec | 0; radius -= radius / _NeuQuant._radiusDecrease | 0; rad = radius >> _NeuQuant._radiusBiasShift; if (rad <= 1) rad = 0; for (let j = 0; j < rad; j++) { this._radPower[j] = alpha * ((rad * rad - j * j) * _NeuQuant._radBias / (rad * rad)) >>> 0; } } } } _buildPalette() { const palette = new Palette(); this._network.forEach((neuron) => { palette.add(neuron.toPoint()); }); palette.sort(); return palette; } _alterNeighbour(rad, i, b, g, r, al) { let lo = i - rad; if (lo < -1) lo = -1; let hi = i + rad; if (hi > this._networkSize) hi = this._networkSize; let j = i + 1; let k = i - 1; let m = 1; while (j < hi || k > lo) { const a = this._radPower[m++] / _NeuQuant._alphaRadBias; if (j < hi) { const p = this._network[j++]; p.subtract(a * (p.r - r), a * (p.g - g), a * (p.b - b), a * (p.a - al)); } if (k > lo) { const p = this._network[k--]; p.subtract(a * (p.r - r), a * (p.g - g), a * (p.b - b), a * (p.a - al)); } } } _alterSingle(alpha, i, b, g, r, a) { alpha /= _NeuQuant._initAlpha; const n = this._network[i]; n.subtract(alpha * (n.r - r), alpha * (n.g - g), alpha * (n.b - b), alpha * (n.a - a)); } _contest(b, g, r, a) { const multiplier = 255 * 4 << networkBiasShift; let bestd = ~(1 << 31); let bestbiasd = bestd; let bestpos = -1; let bestbiaspos = bestpos; for (let i = 0; i < this._networkSize; i++) { const n = this._network[i]; const dist = this._distance.calculateNormalized(n, { r, g, b, a }) * multiplier | 0; if (dist < bestd) { bestd = dist; bestpos = i; } const biasdist = dist - (this._bias[i] >> _NeuQuant._initialBiasShift - networkBiasShift); if (biasdist < bestbiasd) { bestbiasd = biasdist; bestbiaspos = i; } const betafreq = this._freq[i] >> _NeuQuant._betaShift; this._freq[i] -= betafreq; this._bias[i] += betafreq << _NeuQuant._gammaShift; } this._freq[bestpos] += _NeuQuant._beta; this._bias[bestpos] -= _NeuQuant._betaGamma; return bestbiaspos; } }; var NeuQuant = _NeuQuant; __publicField(NeuQuant, "_prime1", 499); __publicField(NeuQuant, "_prime2", 491); __publicField(NeuQuant, "_prime3", 487); __publicField(NeuQuant, "_prime4", 503); __publicField(NeuQuant, "_minpicturebytes", _NeuQuant._prime4); __publicField(NeuQuant, "_nCycles", 100); __publicField(NeuQuant, "_initialBiasShift", 16); __publicField(NeuQuant, "_initialBias", 1 << _NeuQuant._initialBiasShift); __publicField(NeuQuant, "_gammaShift", 10); __publicField(NeuQuant, "_betaShift", 10); __publicField(NeuQuant, "_beta", _NeuQuant._initialBias >> _NeuQuant._betaShift); __publicField(NeuQuant, "_betaGamma", _NeuQuant._initialBias << _NeuQuant._gammaShift - _NeuQuant._betaShift); __publicField(NeuQuant, "_radiusBiasShift", 6); __publicField(NeuQuant, "_radiusBias", 1 << _NeuQuant._radiusBiasShift); __publicField(NeuQuant, "_radiusDecrease", 30); __publicField(NeuQuant, "_alphaBiasShift", 10); __publicField(NeuQuant, "_initAlpha", 1 << _NeuQuant._alphaBiasShift); __publicField(NeuQuant, "_radBiasShift", 8); __publicField(NeuQuant, "_radBias", 1 << _NeuQuant._radBiasShift); __publicField(NeuQuant, "_alphaRadBiasShift", _NeuQuant._alphaBiasShift + _NeuQuant._radBiasShift); __publicField(NeuQuant, "_alphaRadBias", 1 << _NeuQuant._alphaRadBiasShift); // src/palette/neuquant/neuquantFloat.ts var networkBiasShift2 = 3; var NeuronFloat = class { constructor(defaultValue) { __publicField(this, "r"); __publicField(this, "g"); __publicField(this, "b"); __publicField(this, "a"); this.r = this.g = this.b = this.a = defaultValue; } toPoint() { return Point.createByRGBA(this.r >> networkBiasShift2, this.g >> networkBiasShift2, this.b >> networkBiasShift2, this.a >> networkBiasShift2); } subtract(r, g, b, a) { this.r -= r; this.g -= g; this.b -= b; this.a -= a; } }; var _NeuQuantFloat = class extends AbstractPaletteQuantizer { constructor(colorDistanceCalculator, colors = 256) { super(); __publicField(this, "_pointArray"); __publicField(this, "_networkSize"); __publicField(this, "_network"); __publicField(this, "_sampleFactor"); __publicField(this, "_radPower"); __publicField(this, "_freq"); __publicField(this, "_bias"); __publicField(this, "_distance"); this._distance = colorDistanceCalculator; this._pointArray = []; this._sampleFactor = 1; this._networkSize = colors; this._distance.setWhitePoint(255 << networkBiasShift2, 255 << networkBiasShift2, 255 << networkBiasShift2, 255 << networkBiasShift2); } sample(pointContainer) { this._pointArray = this._pointArray.concat(pointContainer.getPointArray()); } *quantize() { this._init(); yield* this._learn(); yield { palette: this._buildPalette(), progress: 100 }; } _init() { this._freq = []; this._bias = []; this._radPower = []; this._network = []; for (let i = 0; i < this._networkSize; i++) { this._network[i] = new NeuronFloat((i << networkBiasShift2 + 8) / this._networkSize); this._freq[i] = _NeuQuantFloat._initialBias / this._networkSize; this._bias[i] = 0; } } *_learn() { let sampleFactor = this._sampleFactor; const pointsNumber = this._pointArray.length; if (pointsNumber < _NeuQuantFloat._minpicturebytes) sampleFactor = 1; const alphadec = 30 + (sampleFactor - 1) / 3; const pointsToSample = pointsNumber / sampleFactor; let delta = pointsToSample / _NeuQuantFloat._nCycles | 0; let alpha = _NeuQuantFloat._initAlpha; let radius = (this._networkSize >> 3) * _NeuQuantFloat._radiusBias; let rad = radius >> _NeuQuantFloat._radiusBiasShift; if (rad <= 1) rad = 0; for (let i = 0; i < rad; i++) { this._radPower[i] = alpha * ((rad * rad - i * i) * _NeuQuantFloat._radBias / (rad * rad)); } let step; if (pointsNumber < _NeuQuantFloat._minpicturebytes) { step = 1; } else if (pointsNumber % _NeuQuantFloat._prime1 !== 0) { step = _NeuQuantFloat._prime1; } else if (pointsNumber % _NeuQuantFloat._prime2 !== 0) { step = _NeuQuantFloat._prime2; } else if (pointsNumber % _NeuQuantFloat._prime3 !== 0) { step = _NeuQuantFloat._prime3; } else { step = _NeuQuantFloat._prime4; } const tracker = new ProgressTracker(pointsToSample, 99); for (let i = 0, pointIndex = 0; i < pointsToSample; ) { if (tracker.shouldNotify(i)) { yield { progress: tracker.progress }; } const point = this._pointArray[pointIndex]; const b = point.b << networkBiasShift2; const g = point.g << networkBiasShift2; const r = point.r << networkBiasShift2; const a = point.a << networkBiasShift2; const neuronIndex = this._contest(b, g, r, a); this._alterSingle(alpha, neuronIndex, b, g, r, a); if (rad !== 0) this._alterNeighbour(rad, neuronIndex, b, g, r, a); pointIndex += step; if (pointIndex >= pointsNumber) pointIndex -= pointsNumber; i++; if (delta === 0) delta = 1; if (i % delta === 0) { alpha -= alpha / alphadec; radius -= radius / _NeuQuantFloat._radiusDecrease; rad = radius >> _NeuQuantFloat._radiusBiasShift; if (rad <= 1) rad = 0; for (let j = 0; j < rad; j++) { this._radPower[j] = alpha * ((rad * rad - j * j) * _NeuQuantFloat._radBias / (rad * rad)); } } } } _buildPalette() { const palette = new Palette(); this._network.forEach((neuron) => { palette.add(neuron.toPoint()); }); palette.sort(); return palette; } _alterNeighbour(rad, i, b, g, r, al) { let lo = i - rad; if (lo < -1) lo = -1; let hi = i + rad; if (hi > this._networkSize) hi = this._networkSize; let j = i + 1; let k = i - 1; let m = 1; while (j < hi || k > lo) { const a = this._radPower[m++] / _NeuQuantFloat._alphaRadBias; if (j < hi) { const p = this._network[j++]; p.subtract(a * (p.r - r), a * (p.g - g), a * (p.b - b), a * (p.a - al)); } if (k > lo) { const p = this._network[k--]; p.subtract(a * (p.r - r), a * (p.g - g), a * (p.b - b), a * (p.a - al)); } } } _alterSingle(alpha, i, b, g, r, a) { alpha /= _NeuQuantFloat._initAlpha; const n = this._network[i]; n.subtract(alpha * (n.r - r), alpha * (n.g - g), alpha * (n.b - b), alpha * (n.a - a)); } _contest(b, g, r, al) { const multiplier = 255 * 4 << networkBiasShift2; let bestd = ~(1 << 31); let bestbiasd = bestd; let bestpos = -1; let bestbiaspos = bestpos; for (let i = 0; i < this._networkSize; i++) { const n = this._network[i]; const dist = this._distance.calculateNormalized(n, { r, g, b, a: al }) * multiplier; if (dist < bestd) { bestd = dist; bestpos = i; } const biasdist = dist - (this._bias[i] >> _NeuQuantFloat._initialBiasShift - networkBiasShift2); if (biasdist < bestbiasd) { bestbiasd = biasdist; bestbiaspos = i; } const betafreq = this._freq[i] >> _NeuQuantFloat._betaShift; this._freq[i] -= betafreq; this._bias[i] += betafreq << _NeuQuantFloat._gammaShift; } this._freq[bestpos] += _NeuQuantFloat._beta; this._bias[bestpos] -= _NeuQuantFloat._betaGamma; return bestbiaspos; } }; var NeuQuantFloat = _NeuQuantFloat; __publicField(NeuQuantFloat, "_prime1", 499); __publicField(NeuQuantFloat, "_prime2", 491); __publicField(NeuQuantFloat, "_prime3", 487); __publicField(NeuQuantFloat, "_prime4", 503); __publicField(NeuQuantFloat, "_minpicturebytes", _NeuQuantFloat._prime4); __publicField(NeuQuantFloat, "_nCycles", 100); __publicField(NeuQuantFloat, "_initialBiasShift", 16); __publicField(NeuQuantFloat, "_initialBias", 1 << _NeuQuantFloat._initialBiasShift); __publicField(NeuQuantFloat, "_gammaShift", 10); __publicField(NeuQuantFloat, "_betaShift", 10); __publicField(NeuQuantFloat, "_beta", _NeuQuantFloat._initialBias >> _NeuQuantFloat._betaShift); __publicField(NeuQuantFloat, "_betaGamma", _NeuQuantFloat._initialBias << _NeuQuantFloat._gammaShift - _NeuQuantFloat._betaShift); __publicField(NeuQuantFloat, "_radiusBiasShift", 6); __publicField(NeuQuantFloat, "_radiusBias", 1 << _NeuQuantFloat._radiusBiasShift); __publicField(NeuQuantFloat, "_radiusDecrease", 30); __publicField(NeuQuantFloat, "_alphaBiasShift", 10); __publicField(NeuQuantFloat, "_initAlpha", 1 << _NeuQuantFloat._alphaBiasShift); __publicField(NeuQuantFloat, "_radBiasShift", 8); __publicField(NeuQuantFloat, "_radBias", 1 << _NeuQuantFloat._radBiasShift); __publicField(NeuQuantFloat, "_alphaRadBiasShift", _NeuQuantFloat._alphaBiasShift + _NeuQuantFloat._radBiasShift); __publicField(NeuQuantFloat, "_alphaRadBias", 1 << _NeuQuantFloat._alphaRadBiasShift); // src/palette/rgbquant/colorHistogram.ts var _ColorHistogram = class { constructor(method, colors) { __publicField(this, "_method"); __publicField(this, "_hueStats"); __publicField(this, "_histogram"); __publicField(this, "_initColors"); __publicField(this, "_minHueCols"); this._method = method; this._minHueCols = colors << 2; this._initColors = colors << 2; this._hueStats = new HueStatistics(_ColorHistogram._hueGroups, this._minHueCols); this._histogram = /* @__PURE__ */ Object.create(null); } sample(pointContainer) { switch (this._method) { case 1: this._colorStats1D(pointContainer); break; case 2: this._colorStats2D(pointContainer); break; } } getImportanceSortedColorsIDXI32() { const sorted = stableSort(Object.keys(this._histogram), (a, b) => this._histogram[b] - this._histogram[a]); if (sorted.length === 0) { return []; } let idxi32; switch (this._method) { case 1: const initialColorsLimit = Math.min(sorted.length, this._initColors); const last = sorted[initialColorsLimit - 1]; const freq = this._histogram[last]; idxi32 = sorted.slice(0, initialColorsLimit); let pos = initialColorsLimit; const len = sorted.length; while (pos < len && this._histogram[sorted[pos]] === freq) { idxi32.push(sorted[pos++]); } this._hueStats.injectIntoArray(idxi32); break; case 2: idxi32 = sorted; break; default: throw new Error("Incorrect method"); } return idxi32.map((v) => +v); } _colorStats1D(pointContainer) { const histG = this._histogram; const pointArray = pointContainer.getPointArray(); const len = pointArray.length; for (let i = 0; i < len; i++) { const col = pointArray[i].uint32; this._hueStats.check(col); if (col in histG) { histG[col]++; } else { histG[col] = 1; } } } _colorStats2D(pointContainer) { const width = pointContainer.getWidth(); const height = pointContainer.getHeight(); const pointArray = pointContainer.getPointArray(); const boxW = _ColorHistogram._boxSize[0]; const boxH = _ColorHistogram._boxSize[1]; const area = boxW * boxH; const boxes = this._makeBoxes(width, height, boxW, boxH); const histG = this._histogram; boxes.forEach((box) => { let effc = Math.round(box.w * box.h / area) * _ColorHistogram._boxPixels; if (effc < 2) effc = 2; const histL = {}; this._iterateBox(box, width, (i) => { const col = pointArray[i].uint32; this._hueStats.check(col); if (col in histG) { histG[col]++; } else if (col in histL) { if (++histL[col] >= effc) { histG[col] = histL[col]; } } else { histL[col] = 1; } }); }); this._hueStats.injectIntoDictionary(histG); } _iterateBox(bbox, wid, fn) { const b = bbox; const i0 = b.y * wid + b.x; const i1 = (b.y + b.h - 1) * wid + (b.x + b.w - 1); const incr = wid - b.w + 1; let cnt = 0; let i = i0; do { fn.call(this, i); i += ++cnt % b.w === 0 ? incr : 1; } while (i <= i1); } _makeBoxes(width, height, stepX, stepY) { const wrem = width % stepX; const hrem = height % stepY; const xend = width - wrem; const yend = height - hrem; const boxesArray = []; for (let y2 = 0; y2 < height; y2 += stepY) { for (let x2 = 0; x2 < width; x2 += stepX) { boxesArray.push({ x: x2, y: y2, w: x2 === xend ? wrem : stepX, h: y2 === yend ? hrem : stepY }); } } return boxesArray; } }; var ColorHistogram = _ColorHistogram; __publicField(ColorHistogram, "_boxSize", [64, 64]); __publicField(ColorHistogram, "_boxPixels", 2); __publicField(ColorHistogram, "_hueGroups", 10); // src/palette/rgbquant/rgbquant.ts var RemovedColor = class { constructor(index, color, distance) { __publicField(this, "index"); __publicField(this, "color"); __publicField(this, "distance"); this.index = index; this.color = color; this.distance = distance; } }; var RGBQuant = class extends AbstractPaletteQuantizer { constructor(colorDistanceCalculator, colors = 256, method = 2) { super(); __publicField(this, "_colors"); __publicField(this, "_initialDistance"); __publicField(this, "_distanceIncrement"); __publicField(this, "_histogram"); __publicField(this, "_distance"); this._distance = colorDistanceCalculator; this._colors = colors; this._histogram = new ColorHistogram(method, colors); this._initialDistance = 0.01; this._distanceIncrement = 5e-3; } sample(image) { this._histogram.sample(image); } *quantize() { const idxi32 = this._histogram.getImportanceSortedColorsIDXI32(); if (idxi32.length === 0) { throw new Error("No colors in image"); } yield* this._buildPalette(idxi32); } *_buildPalette(idxi32) { const palette = new Palette(); const colorArray = palette.getPointContainer().getPointArray(); const usageArray = new Array(idxi32.length); for (let i = 0; i < idxi32.length; i++) { colorArray.push(Point.createByUint32(idxi32[i])); usageArray[i] = 1; } const len = colorArray.length; const memDist = []; let palLen = len; let thold = this._initialDistance; const tracker = new ProgressTracker(palLen - this._colors, 99); while (palLen > this._colors) { memDist.length = 0; for (let i = 0; i < len; i++) { if (tracker.shouldNotify(len - palLen)) { yield { progress: tracker.progress }; } if (usageArray[i] === 0) continue; const pxi = colorArray[i]; for (let j = i + 1; j < len; j++) { if (usageArray[j] === 0) continue; const pxj = colorArray[j]; const dist = this._distance.calculateNormalized(pxi, pxj); if (dist < thold) { memDist.push(new RemovedColor(j, pxj, dist)); usageArray[j] = 0; palLen--; } } } thold += palLen > this._colors * 3 ? this._initialDistance : this._distanceIncrement; } if (palLen < this._colors) { stableSort(memDist, (a, b) => b.distance - a.distance); let k = 0; while (palLen < this._colors && k < memDist.length) { const removedColor = memDist[k]; usageArray[removedColor.index] = 1; palLen++; k++; } } let colors = colorArray.length; for (let colorIndex = colors - 1; colorIndex >= 0; colorIndex--) { if (usageArray[colorIndex] === 0) { if (colorIndex !== colors - 1) { colorArray[colorIndex] = colorArray[colors - 1]; } --colors; } } colorArray.length = colors; palette.sort(); yield { palette, progress: 100 }; } }; // src/palette/wu/wuQuant.ts function createArray1D(dimension1) { const a = []; for (let k = 0; k < dimension1; k++) { a[k] = 0; } return a; } function createArray4D(dimension1, dimension2, dimension3, dimension4) { const a = new Array(dimension1); for (let i = 0; i < dimension1; i++) { a[i] = new Array(dimension2); for (let j = 0; j < dimension2; j++) { a[i][j] = new Array(dimension3); for (let k = 0; k < dimension3; k++) { a[i][j][k] = new Array(dimension4); for (let l = 0; l < dimension4; l++) { a[i][j][k][l] = 0; } } } } return a; } function createArray3D(dimension1, dimension2, dimension3) { const a = new Array(dimension1); for (let i = 0; i < dimension1; i++) { a[i] = new Array(dimension2); for (let j = 0; j < dimension2; j++) { a[i][j] = new Array(dimension3); for (let k = 0; k < dimension3; k++) { a[i][j][k] = 0; } } } return a; } function fillArray3D(a, dimension1, dimension2, dimension3, value) { for (let i = 0; i < dimension1; i++) { a[i] = []; for (let j = 0; j < dimension2; j++) { a[i][j] = []; for (let k = 0; k < dimension3; k++) { a[i][j][k] = value; } } } } function fillArray1D(a, dimension1, value) { for (let i = 0; i < dimension1; i++) { a[i] = value; } } var WuColorCube = class { constructor() { __publicField(this, "redMinimum"); __publicField(this, "redMaximum"); __publicField(this, "greenMinimum"); __publicField(this, "greenMaximum"); __publicField(this, "blueMinimum"); __publicField(this, "blueMaximum"); __publicField(this, "volume"); __publicField(this, "alphaMinimum"); __publicField(this, "alphaMaximum"); } }; var _WuQuant = class extends AbstractPaletteQuantizer { constructor(colorDistanceCalculator, colors = 256, significantBitsPerChannel = 5) { super(); __publicField(this, "_reds"); __publicField(this, "_greens"); __publicField(this, "_blues"); __publicField(this, "_alphas"); __publicField(this, "_sums"); __publicField(this, "_weights"); __publicField(this, "_momentsRed"); __publicField(this, "_momentsGreen"); __publicField(this, "_momentsBlue"); __publicField(this, "_momentsAlpha"); __publicField(this, "_moments"); __publicField(this, "_table"); __publicField(this, "_pixels"); __publicField(this, "_cubes"); __publicField(this, "_colors"); __publicField(this, "_significantBitsPerChannel"); __publicField(this, "_maxSideIndex"); __publicField(this, "_alphaMaxSideIndex"); __publicField(this, "_sideSize"); __publicField(this, "_alphaSideSize"); __publicField(this, "_distance"); this._distance = colorDistanceCalculator; this._setQuality(significantBitsPerChannel); this._initialize(colors); } sample(image) { const pointArray = image.getPointArray(); for (let i = 0, l = pointArray.length; i < l; i++) { this._addColor(pointArray[i]); } this._pixels = this._pixels.concat(pointArray); } *quantize() { yield* this._preparePalette(); const palette = new Palette(); for (let paletteIndex = 0; paletteIndex < this._colors; paletteIndex++) { if (this._sums[paletteIndex] > 0) { const sum = this._sums[paletteIndex]; const r = this._reds[paletteIndex] / sum; const g = this._greens[paletteIndex] / sum; const b = this._blues[paletteIndex] / sum; const a = this._alphas[paletteIndex] / sum; const color = Point.createByRGBA(r | 0, g | 0, b | 0, a | 0); palette.add(color); } } palette.sort(); yield { palette, progress: 100 }; } *_preparePalette() { yield* this._calculateMoments(); let next = 0; const volumeVariance = createArray1D(this._colors); for (let cubeIndex = 1; cubeIndex < this._colors; ++cubeIndex) { if (this._cut(this._cubes[next], this._cubes[cubeIndex])) { volumeVariance[next] = this._cubes[next].volume > 1 ? this._calculateVariance(this._cubes[next]) : 0; volumeVariance[cubeIndex] = this._cubes[cubeIndex].volume > 1 ? this._calculateVariance(this._cubes[cubeIndex]) : 0; } else { volumeVariance[next] = 0; cubeIndex--; } next = 0; let temp = volumeVariance[0]; for (let index = 1; index <= cubeIndex; ++index) { if (volumeVariance[index] > temp) { temp = volumeVariance[index]; next = index; } } if (temp <= 0) { this._colors = cubeIndex + 1; break; } } const lookupRed = []; const lookupGreen = []; const lookupBlue = []; const lookupAlpha = []; for (let k = 0; k < this._colors; ++k) { const weight = _WuQuant._volume(this._cubes[k], this._weights); if (weight > 0) { lookupRed[k] = _WuQuant._volume(this._cubes[k], this._momentsRed) / weight | 0; lookupGreen[k] = _WuQuant._volume(this._cubes[k], this._momentsGreen) / weight | 0; lookupBlue[k] = _WuQuant._volume(this._cubes[k], this._momentsBlue) / weight | 0; lookupAlpha[k] = _WuQuant._volume(this._cubes[k], this._momentsAlpha) / weight | 0; } else { lookupRed[k] = 0; lookupGreen[k] = 0; lookupBlue[k] = 0; lookupAlpha[k] = 0; } } this._reds = createArray1D(this._colors + 1); this._greens = createArray1D(this._colors + 1); this._blues = createArray1D(this._colors + 1); this._alphas = createArray1D(this._colors + 1); this._sums = createArray1D(this._colors + 1); for (let index = 0, l = this._pixels.length; index < l; index++) { const color = this._pixels[index]; const match = -1; let bestMatch = match; let bestDistance = Number.MAX_VALUE; for (let lookup = 0; lookup < this._colors; lookup++) { const foundRed = lookupRed[lookup]; const foundGreen = lookupGreen[lookup]; const foundBlue = lookupBlue[lookup]; const foundAlpha = lookupAlpha[lookup]; const distance = this._distance.calculateRaw(foundRed, foundGreen, foundBlue, foundAlpha, color.r, color.g, color.b, color.a); if (distance < bestDistance) { bestDistance = distance; bestMatch = lookup; } } this._reds[bestMatch] += color.r; this._greens[bestMatch] += color.g; this._blues[bestMatch] += color.b; this._alphas[bestMatch] += color.a; this._sums[bestMatch]++; } } _addColor(color) { const bitsToRemove = 8 - this._significantBitsPerChannel; const indexRed = (color.r >> bitsToRemove) + 1; const indexGreen = (color.g >> bitsToRemove) + 1; const indexBlue = (color.b >> bitsToRemove) + 1; const indexAlpha = (color.a >> bitsToRemove) + 1; this._weights[indexAlpha][indexRed][indexGreen][indexBlue]++; this._momentsRed[indexAlpha][indexRed][indexGreen][indexBlue] += color.r; this._momentsGreen[indexAlpha][indexRed][indexGreen][indexBlue] += color.g; this._momentsBlue[indexAlpha][indexRed][indexGreen][indexBlue] += color.b; this._momentsAlpha[indexAlpha][indexRed][indexGreen][indexBlue] += color.a; this._moments[indexAlpha][indexRed][indexGreen][indexBlue] += this._table[color.r] + this._table[color.g] + this._table[color.b] + this._table[color.a]; } *_calculateMoments() { const area = []; const areaRed = []; const areaGreen = []; const areaBlue = []; const areaAlpha = []; const area2 = []; const xarea = createArray3D(this._sideSize, this._sideSize, this._sideSize); const xareaRed = createArray3D(this._sideSize, this._sideSize, this._sideSize); const xareaGreen = createArray3D(this._sideSize, this._sideSize, this._sideSize); const xareaBlue = createArray3D(this._sideSize, this._sideSize, this._sideSize); const xareaAlpha = createArray3D(this._sideSize, this._sideSize, this._sideSize); const xarea2 = createArray3D(this._sideSize, this._sideSize, this._sideSize); let trackerProgress = 0; const tracker = new ProgressTracker(this._alphaMaxSideIndex * this._maxSideIndex, 99); for (let alphaIndex = 1; alphaIndex <= this._alphaMaxSideIndex; ++alphaIndex) { fillArray3D(xarea, this._sideSize, this._sideSize, this._sideSize, 0); fillArray3D(xareaRed, this._sideSize, this._sideSize, this._sideSize, 0); fillArray3D(xareaGreen, this._sideSize, this._sideSize, this._sideSize, 0); fillArray3D(xareaBlue, this._sideSize, this._sideSize, this._sideSize, 0); fillArray3D(xareaAlpha, this._sideSize, this._sideSize, this._sideSize, 0); fillArray3D(xarea2, this._sideSize, this._sideSize, this._sideSize, 0); for (let redIndex = 1; redIndex <= this._maxSideIndex; ++redIndex, ++trackerProgress) { if (tracker.shouldNotify(trackerProgress)) { yield { progress: tracker.progress }; } fillArray1D(area, this._sideSize, 0); fillArray1D(areaRed, this._sideSize, 0); fillArray1D(areaGreen, this._sideSize, 0); fillArray1D(areaBlue, this._sideSize, 0); fillArray1D(areaAlpha, this._sideSize, 0); fillArray1D(area2, this._sideSize, 0); for (let greenIndex = 1; greenIndex <= this._maxSideIndex; ++greenIndex) { let line = 0; let lineRed = 0; let lineGreen = 0; let lineBlue = 0; let lineAlpha = 0; let line2 = 0; for (let blueIndex = 1; blueIndex <= this._maxSideIndex; ++blueIndex) { line += this._weights[alphaIndex][redIndex][greenIndex][blueIndex]; lineRed += this._momentsRed[alphaIndex][redIndex][greenIndex][blueIndex]; lineGreen += this._momentsGreen[alphaIndex][redIndex][greenIndex][blueIndex]; lineBlue += this._momentsBlue[alphaIndex][redIndex][greenIndex][blueIndex]; lineAlpha += this._momentsAlpha[alphaIndex][redIndex][greenIndex][blueIndex]; line2 += this._moments[alphaIndex][redIndex][greenIndex][blueIndex]; area[blueIndex] += line; areaRed[blueIndex] += lineRed; areaGreen[blueIndex] += lineGreen; areaBlue[blueIndex] += lineBlue; areaAlpha[blueIndex] += lineAlpha; area2[blueIndex] += line2; xarea[redIndex][greenIndex][blueIndex] = xarea[redIndex - 1][greenIndex][blueIndex] + area[blueIndex]; xareaRed[redIndex][greenIndex][blueIndex] = xareaRed[redIndex - 1][greenIndex][blueIndex] + areaRed[blueIndex]; xareaGreen[redIndex][greenIndex][blueIndex] = xareaGreen[redIndex - 1][greenIndex][blueIndex] + areaGreen[blueIndex]; xareaBlue[redIndex][greenIndex][blueIndex] = xareaBlue[redIndex - 1][greenIndex][blueIndex] + areaBlue[blueIndex]; xareaAlpha[redIndex][greenIndex][blueIndex] = xareaAlpha[redIndex - 1][greenIndex][blueIndex] + areaAlpha[blueIndex]; xarea2[redIndex][greenIndex][blueIndex] = xarea2[redIndex - 1][greenIndex][blueIndex] + area2[blueIndex]; this._weights[alphaIndex][redIndex][greenIndex][blueIndex] = this._weights[alphaIndex - 1][redIndex][greenIndex][blueIndex] + xarea[redIndex][greenIndex][blueIndex]; this._momentsRed[alphaIndex][redIndex][greenIndex][blueIndex] = this._momentsRed[alphaIndex - 1][redIndex][greenIndex][blueIndex] + xareaRed[redIndex][greenIndex][blueIndex]; this._momentsGreen[alphaIndex][redIndex][greenIndex][blueIndex] = this._momentsGreen[alphaIndex - 1][redIndex][greenIndex][blueIndex] + xareaGreen[redIndex][greenIndex][blueIndex]; this._momentsBlue[alphaIndex][redIndex][greenIndex][blueIndex] = this._momentsBlue[alphaIndex - 1][redIndex][greenIndex][blueIndex] + xareaBlue[redIndex][greenIndex][blueIndex]; this._momentsAlpha[alphaIndex][redIndex][greenIndex][blueIndex] = this._momentsAlpha[alphaIndex - 1][redIndex][greenIndex][blueIndex] + xareaAlpha[redIndex][greenIndex][blueIndex]; this._moments[alphaIndex][redIndex][greenIndex][blueIndex] = this._moments[alphaIndex - 1][redIndex][greenIndex][blueIndex] + xarea2[redIndex][greenIndex][blueIndex]; } } } } } static _volumeFloat(cube, moment) { return moment[cube.alphaMaximum][cube.redMaximum][cube.greenMaximum][cube.blueMaximum] - moment[cube.alphaMaximum][cube.redMaximum][cube.greenMinimum][cube.blueMaximum] - moment[cube.alphaMaximum][cube.redMinimum][cube.greenMaximum][cube.blueMaximum] + moment[cube.alphaMaximum][cube.redMinimum][cube.greenMinimum][cube.blueMaximum] - moment[cube.alphaMinimum][cube.redMaximum][cube.greenMaximum][cube.blueMaximum] + moment[cube.alphaMinimum][cube.redMaximum][cube.greenMinimum][cube.blueMaximum] + moment[cube.alphaMinimum][cube.redMinimum][cube.greenMaximum][cube.blueMaximum] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][cube.blueMaximum] - (moment[cube.alphaMaximum][cube.redMaximum][cube.greenMaximum][cube.blueMinimum] - moment[cube.alphaMinimum][cube.redMaximum][cube.greenMaximum][cube.blueMinimum] - moment[cube.alphaMaximum][cube.redMaximum][cube.greenMinimum][cube.blueMinimum] + moment[cube.alphaMinimum][cube.redMaximum][cube.greenMinimum][cube.blueMinimum] - moment[cube.alphaMaximum][cube.redMinimum][cube.greenMaximum][cube.blueMinimum] + moment[cube.alphaMinimum][cube.redMinimum][cube.greenMaximum][cube.blueMinimum] + moment[cube.alphaMaximum][cube.redMinimum][cube.greenMinimum][cube.blueMinimum] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][cube.blueMinimum]); } static _volume(cube, moment) { return _WuQuant._volumeFloat(cube, moment) | 0; } static _top(cube, direction, position, moment) { let result; switch (direction) { case _WuQuant._alpha: result = moment[position][cube.redMaximum][cube.greenMaximum][cube.blueMaximum] - moment[position][cube.redMaximum][cube.greenMinimum][cube.blueMaximum] - moment[position][cube.redMinimum][cube.greenMaximum][cube.blueMaximum] + moment[position][cube.redMinimum][cube.greenMinimum][cube.blueMaximum] - (moment[position][cube.redMaximum][cube.greenMaximum][cube.blueMinimum] - moment[position][cube.redMaximum][cube.greenMinimum][cube.blueMinimum] - moment[position][cube.redMinimum][cube.greenMaximum][cube.blueMinimum] + moment[position][cube.redMinimum][cube.greenMinimum][cube.blueMinimum]); break; case _WuQuant._red: result = moment[cube.alphaMaximum][position][cube.greenMaximum][cube.blueMaximum] - moment[cube.alphaMaximum][position][cube.greenMinimum][cube.blueMaximum] - moment[cube.alphaMinimum][position][cube.greenMaximum][cube.blueMaximum] + moment[cube.alphaMinimum][position][cube.greenMinimum][cube.blueMaximum] - (moment[cube.alphaMaximum][position][cube.greenMaximum][cube.blueMinimum] - moment[cube.alphaMaximum][position][cube.greenMinimum][cube.blueMinimum] - moment[cube.alphaMinimum][position][cube.greenMaximum][cube.blueMinimum] + moment[cube.alphaMinimum][position][cube.greenMinimum][cube.blueMinimum]); break; case _WuQuant._green: result = moment[cube.alphaMaximum][cube.redMaximum][position][cube.blueMaximum] - moment[cube.alphaMaximum][cube.redMinimum][position][cube.blueMaximum] - moment[cube.alphaMinimum][cube.redMaximum][position][cube.blueMaximum] + moment[cube.alphaMinimum][cube.redMinimum][position][cube.blueMaximum] - (moment[cube.alphaMaximum][cube.redMaximum][position][cube.blueMinimum] - moment[cube.alphaMaximum][cube.redMinimum][position][cube.blueMinimum] - moment[cube.alphaMinimum][cube.redMaximum][position][cube.blueMinimum] + moment[cube.alphaMinimum][cube.redMinimum][position][cube.blueMinimum]); break; case _WuQuant._blue: result = moment[cube.alphaMaximum][cube.redMaximum][cube.greenMaximum][position] - moment[cube.alphaMaximum][cube.redMaximum][cube.greenMinimum][position] - moment[cube.alphaMaximum][cube.redMinimum][cube.greenMaximum][position] + moment[cube.alphaMaximum][cube.redMinimum][cube.greenMinimum][position] - (moment[cube.alphaMinimum][cube.redMaximum][cube.greenMaximum][position] - moment[cube.alphaMinimum][cube.redMaximum][cube.greenMinimum][position] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMaximum][position] + moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][position]); break; default: throw new Error("impossible"); } return result | 0; } static _bottom(cube, direction, moment) { switch (direction) { case _WuQuant._alpha: return -moment[cube.alphaMinimum][cube.redMaximum][cube.greenMaximum][cube.blueMaximum] + moment[cube.alphaMinimum][cube.redMaximum][cube.greenMinimum][cube.blueMaximum] + moment[cube.alphaMinimum][cube.redMinimum][cube.greenMaximum][cube.blueMaximum] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][cube.blueMaximum] - (-moment[cube.alphaMinimum][cube.redMaximum][cube.greenMaximum][cube.blueMinimum] + moment[cube.alphaMinimum][cube.redMaximum][cube.greenMinimum][cube.blueMinimum] + moment[cube.alphaMinimum][cube.redMinimum][cube.greenMaximum][cube.blueMinimum] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][cube.blueMinimum]); case _WuQuant._red: return -moment[cube.alphaMaximum][cube.redMinimum][cube.greenMaximum][cube.blueMaximum] + moment[cube.alphaMaximum][cube.redMinimum][cube.greenMinimum][cube.blueMaximum] + moment[cube.alphaMinimum][cube.redMinimum][cube.greenMaximum][cube.blueMaximum] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][cube.blueMaximum] - (-moment[cube.alphaMaximum][cube.redMinimum][cube.greenMaximum][cube.blueMinimum] + moment[cube.alphaMaximum][cube.redMinimum][cube.greenMinimum][cube.blueMinimum] + moment[cube.alphaMinimum][cube.redMinimum][cube.greenMaximum][cube.blueMinimum] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][cube.blueMinimum]); case _WuQuant._green: return -moment[cube.alphaMaximum][cube.redMaximum][cube.greenMinimum][cube.blueMaximum] + moment[cube.alphaMaximum][cube.redMinimum][cube.greenMinimum][cube.blueMaximum] + moment[cube.alphaMinimum][cube.redMaximum][cube.greenMinimum][cube.blueMaximum] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][cube.blueMaximum] - (-moment[cube.alphaMaximum][cube.redMaximum][cube.greenMinimum][cube.blueMinimum] + moment[cube.alphaMaximum][cube.redMinimum][cube.greenMinimum][cube.blueMinimum] + moment[cube.alphaMinimum][cube.redMaximum][cube.greenMinimum][cube.blueMinimum] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][cube.blueMinimum]); case _WuQuant._blue: return -moment[cube.alphaMaximum][cube.redMaximum][cube.greenMaximum][cube.blueMinimum] + moment[cube.alphaMaximum][cube.redMaximum][cube.greenMinimum][cube.blueMinimum] + moment[cube.alphaMaximum][cube.redMinimum][cube.greenMaximum][cube.blueMinimum] - moment[cube.alphaMaximum][cube.redMinimum][cube.greenMinimum][cube.blueMinimum] - (-moment[cube.alphaMinimum][cube.redMaximum][cube.greenMaximum][cube.blueMinimum] + moment[cube.alphaMinimum][cube.redMaximum][cube.greenMinimum][cube.blueMinimum] + moment[cube.alphaMinimum][cube.redMinimum][cube.greenMaximum][cube.blueMinimum] - moment[cube.alphaMinimum][cube.redMinimum][cube.greenMinimum][cube.blueMinimum]); default: return 0; } } _calculateVariance(cube) { const volumeRed = _WuQuant._volume(cube, this._momentsRed); const volumeGreen = _WuQuant._volume(cube, this._momentsGreen); const volumeBlue = _WuQuant._volume(cube, this._momentsBlue); const volumeAlpha = _WuQuant._volume(cube, this._momentsAlpha); const volumeMoment = _WuQuant._volumeFloat(cube, this._moments); const volumeWeight = _WuQuant._volume(cube, this._weights); const distance = volumeRed * volumeRed + volumeGreen * volumeGreen + volumeBlue * volumeBlue + volumeAlpha * volumeAlpha; return volumeMoment - distance / volumeWeight; } _maximize(cube, direction, first, last, wholeRed, wholeGreen, wholeBlue, wholeAlpha, wholeWeight) { const bottomRed = _WuQuant._bottom(cube, direction, this._momentsRed) | 0; const bottomGreen = _WuQuant._bottom(cube, direction, this._momentsGreen) | 0; const bottomBlue = _WuQuant._bottom(cube, direction, this._momentsBlue) | 0; const bottomAlpha = _WuQuant._bottom(cube, direction, this._momentsAlpha) | 0; const bottomWeight = _WuQuant._bottom(cube, direction, this._weights) | 0; let result = 0; let cutPosition = -1; for (let position = first; position < last; ++position) { let halfRed = bottomRed + _WuQuant._top(cube, direction, position, this._momentsRed); let halfGreen = bottomGreen + _WuQuant._top(cube, direction, position, this._momentsGreen); let halfBlue = bottomBlue + _WuQuant._top(cube, direction, position, this._momentsBlue); let halfAlpha = bottomAlpha + _WuQuant._top(cube, direction, position, this._momentsAlpha); let halfWeight = bottomWeight + _WuQuant._top(cube, direction, position, this._weights); if (halfWeight !== 0) { let halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue + halfAlpha * halfAlpha; let temp = halfDistance / halfWeight; halfRed = wholeRed - halfRed; halfGreen = wholeGreen - halfGreen; halfBlue = wholeBlue - halfBlue; halfAlpha = wholeAlpha - halfAlpha; halfWeight = wholeWeight - halfWeight; if (halfWeight !== 0) { halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue + halfAlpha * halfAlpha; temp += halfDistance / halfWeight; if (temp > result) { result = temp; cutPosition = position; } } } } return { max: result, position: cutPosition }; } _cut(first, second) { let direction; const wholeRed = _WuQuant._volume(first, this._momentsRed); const wholeGreen = _WuQuant._volume(first, this._momentsGreen); const wholeBlue = _WuQuant._volume(first, this._momentsBlue); const wholeAlpha = _WuQuant._volume(first, this._momentsAlpha); const wholeWeight = _WuQuant._volume(first, this._weights); const red = this._maximize(first, _WuQuant._red, first.redMinimum + 1, first.redMaximum, wholeRed, wholeGreen, wholeBlue, wholeAlpha, wholeWeight); const green = this._maximize(first, _WuQuant._green, first.greenMinimum + 1, first.greenMaximum, wholeRed, wholeGreen, wholeBlue, wholeAlpha, wholeWeight); const blue = this._maximize(first, _WuQuant._blue, first.blueMinimum + 1, first.blueMaximum, wholeRed, wholeGreen, wholeBlue, wholeAlpha, wholeWeight); const alpha = this._maximize(first, _WuQuant._alpha, first.alphaMinimum + 1, first.alphaMaximum, wholeRed, wholeGreen, wholeBlue, wholeAlpha, wholeWeight); if (alpha.max >= red.max && alpha.max >= green.max && alpha.max >= blue.max) { direction = _WuQuant._alpha; if (alpha.position < 0) return false; } else if (red.max >= alpha.max && red.max >= green.max && red.max >= blue.max) { direction = _WuQuant._red; } else if (green.max >= alpha.max && green.max >= red.max && green.max >= blue.max) { direction = _WuQuant._green; } else { direction = _WuQuant._blue; } second.redMaximum = first.redMaximum; second.greenMaximum = first.greenMaximum; second.blueMaximum = first.blueMaximum; second.alphaMaximum = first.alphaMaximum; switch (direction) { case _WuQuant._red: second.redMinimum = first.redMaximum = red.position; second.greenMinimum = first.greenMinimum; second.blueMinimum = first.blueMinimum; second.alphaMinimum = first.alphaMinimum; break; case _WuQuant._green: second.greenMinimum = first.greenMaximum = green.position; second.redMinimum = first.redMinimum; second.blueMinimum = first.blueMinimum; second.alphaMinimum = first.alphaMinimum; break; case _WuQuant._blue: second.blueMinimum = first.blueMaximum = blue.position; second.redMinimum = first.redMinimum; second.greenMinimum = first.greenMinimum; second.alphaMinimum = first.alphaMinimum; break; case _WuQuant._alpha: second.alphaMinimum = first.alphaMaximum = alpha.position; second.blueMinimum = first.blueMinimum; second.redMinimum = first.redMinimum; second.greenMinimum = first.greenMinimum; break; } first.volume = (first.redMaximum - first.redMinimum) * (first.greenMaximum - first.greenMinimum) * (first.blueMaximum - first.blueMinimum) * (first.alphaMaximum - first.alphaMinimum); second.volume = (second.redMaximum - second.redMinimum) * (second.greenMaximum - second.greenMinimum) * (second.blueMaximum - second.blueMinimum) * (second.alphaMaximum - second.alphaMinimum); return true; } _initialize(colors) { this._colors = colors; this._cubes = []; for (let cubeIndex = 0; cubeIndex < colors; cubeIndex++) { this._cubes[cubeIndex] = new WuColorCube(); } this._cubes[0].redMinimum = 0; this._cubes[0].greenMinimum = 0; this._cubes[0].blueMinimum = 0; this._cubes[0].alphaMinimum = 0; this._cubes[0].redMaximum = this._maxSideIndex; this._cubes[0].greenMaximum = this._maxSideIndex; this._cubes[0].blueMaximum = this._maxSideIndex; this._cubes[0].alphaMaximum = this._alphaMaxSideIndex; this._weights = createArray4D(this._alphaSideSize, this._sideSize, this._sideSize, this._sideSize); this._momentsRed = createArray4D(this._alphaSideSize, this._sideSize, this._sideSize, this._sideSize); this._momentsGreen = createArray4D(this._alphaSideSize, this._sideSize, this._sideSize, this._sideSize); this._momentsBlue = createArray4D(this._alphaSideSize, this._sideSize, this._sideSize, this._sideSize); this._momentsAlpha = createArray4D(this._alphaSideSize, this._sideSize, this._sideSize, this._sideSize); this._moments = createArray4D(this._alphaSideSize, this._sideSize, this._sideSize, this._sideSize); this._table = []; for (let tableIndex = 0; tableIndex < 256; ++tableIndex) { this._table[tableIndex] = tableIndex * tableIndex; } this._pixels = []; } _setQuality(significantBitsPerChannel = 5) { this._significantBitsPerChannel = significantBitsPerChannel; this._maxSideIndex = 1 << this._significantBitsPerChannel; this._alphaMaxSideIndex = this._maxSideIndex; this._sideSize = this._maxSideIndex + 1; this._alphaSideSize = this._alphaMaxSideIndex + 1; } }; var WuQuant = _WuQuant; __publicField(WuQuant, "_alpha", 3); __publicField(WuQuant, "_red", 2); __publicField(WuQuant, "_green", 1); __publicField(WuQuant, "_blue", 0); // src/image/index.ts var image_exports = {}; __export(image_exports, { AbstractImageQuantizer: () => AbstractImageQuantizer, ErrorDiffusionArray: () => ErrorDiffusionArray, ErrorDiffusionArrayKernel: () => ErrorDiffusionArrayKernel, ErrorDiffusionRiemersma: () => ErrorDiffusionRiemersma, NearestColor: () => NearestColor }); // src/image/imageQuantizer.ts var AbstractImageQuantizer = class { quantizeSync(pointContainer, palette) { for (const value of this.quantize(pointContainer, palette)) { if (value.pointContainer) { return value.pointContainer; } } throw new Error("unreachable"); } }; // src/image/nearestColor.ts var NearestColor = class extends AbstractImageQuantizer { constructor(colorDistanceCalculator) { super(); __publicField(this, "_distance"); this._distance = colorDistanceCalculator; } *quantize(pointContainer, palette) { const pointArray = pointContainer.getPointArray(); const width = pointContainer.getWidth(); const height = pointContainer.getHeight(); const tracker = new ProgressTracker(height, 99); for (let y2 = 0; y2 < height; y2++) { if (tracker.shouldNotify(y2)) { yield { progress: tracker.progress }; } for (let x2 = 0, idx = y2 * width; x2 < width; x2++, idx++) { const point = pointArray[idx]; point.from(palette.getNearestColor(this._distance, point)); } } yield { pointContainer, progress: 100 }; } }; // src/image/array.ts var ErrorDiffusionArrayKernel = /* @__PURE__ */ ((ErrorDiffusionArrayKernel2) => { ErrorDiffusionArrayKernel2[ErrorDiffusionArrayKernel2["FloydSteinberg"] = 0] = "FloydSteinberg"; ErrorDiffusionArrayKernel2[ErrorDiffusionArrayKernel2["FalseFloydSteinberg"] = 1] = "FalseFloydSteinberg"; ErrorDiffusionArrayKernel2[ErrorDiffusionArrayKernel2["Stucki"] = 2] = "Stucki"; ErrorDiffusionArrayKernel2[ErrorDiffusionArrayKernel2["Atkinson"] = 3] = "Atkinson"; ErrorDiffusionArrayKernel2[ErrorDiffusionArrayKernel2["Jarvis"] = 4] = "Jarvis"; ErrorDiffusionArrayKernel2[ErrorDiffusionArrayKernel2["Burkes"] = 5] = "Burkes"; ErrorDiffusionArrayKernel2[ErrorDiffusionArrayKernel2["Sierra"] = 6] = "Sierra"; ErrorDiffusionArrayKernel2[ErrorDiffusionArrayKernel2["TwoSierra"] = 7] = "TwoSierra"; ErrorDiffusionArrayKernel2[ErrorDiffusionArrayKernel2["SierraLite"] = 8] = "SierraLite"; return ErrorDiffusionArrayKernel2; })(ErrorDiffusionArrayKernel || {}); var ErrorDiffusionArray = class extends AbstractImageQuantizer { constructor(colorDistanceCalculator, kernel, serpentine = true, minimumColorDistanceToDither = 0, calculateErrorLikeGIMP = false) { super(); __publicField(this, "_minColorDistance"); __publicField(this, "_serpentine"); __publicField(this, "_kernel"); __publicField(this, "_calculateErrorLikeGIMP"); __publicField(this, "_distance"); this._setKernel(kernel); this._distance = colorDistanceCalculator; this._minColorDistance = minimumColorDistanceToDither; this._serpentine = serpentine; this._calculateErrorLikeGIMP = calculateErrorLikeGIMP; } *quantize(pointContainer, palette) { const pointArray = pointContainer.getPointArray(); const originalPoint = new Point(); const width = pointContainer.getWidth(); const height = pointContainer.getHeight(); const errorLines = []; let dir = 1; let maxErrorLines = 1; for (const kernel of this._kernel) { const kernelErrorLines = kernel[2] + 1; if (maxErrorLines < kernelErrorLines) maxErrorLines = kernelErrorLines; } for (let i = 0; i < maxErrorLines; i++) { this._fillErrorLine(errorLines[i] = [], width); } const tracker = new ProgressTracker(height, 99); for (let y2 = 0; y2 < height; y2++) { if (tracker.shouldNotify(y2)) { yield { progress: tracker.progress }; } if (this._serpentine) dir *= -1; const lni = y2 * width; const xStart = dir === 1 ? 0 : width - 1; const xEnd = dir === 1 ? width : -1; this._fillErrorLine(errorLines[0], width); errorLines.push(errorLines.shift()); const errorLine = errorLines[0]; for (let x2 = xStart, idx = lni + xStart; x2 !== xEnd; x2 += dir, idx += dir) { const point = pointArray[idx]; const error = errorLine[x2]; originalPoint.from(point); const correctedPoint = Point.createByRGBA(inRange0to255Rounded(point.r + error[0]), inRange0to255Rounded(point.g + error[1]), inRange0to255Rounded(point.b + error[2]), inRange0to255Rounded(point.a + error[3])); const palettePoint = palette.getNearestColor(this._distance, correctedPoint); point.from(palettePoint); if (this._minColorDistance) { const dist = this._distance.calculateNormalized(originalPoint, palettePoint); if (dist < this._minColorDistance) continue; } let er; let eg; let eb; let ea; if (this._calculateErrorLikeGIMP) { er = correctedPoint.r - palettePoint.r; eg = correctedPoint.g - palettePoint.g; eb = correctedPoint.b - palettePoint.b; ea = correctedPoint.a - palettePoint.a; } else { er = originalPoint.r - palettePoint.r; eg = originalPoint.g - palettePoint.g; eb = originalPoint.b - palettePoint.b; ea = originalPoint.a - palettePoint.a; } const dStart = dir === 1 ? 0 : this._kernel.length - 1; const dEnd = dir === 1 ? this._kernel.length : -1; for (let i = dStart; i !== dEnd; i += dir) { const x1 = this._kernel[i][1] * dir; const y1 = this._kernel[i][2]; if (x1 + x2 >= 0 && x1 + x2 < width && y1 + y2 >= 0 && y1 + y2 < height) { const d = this._kernel[i][0]; const e = errorLines[y1][x1 + x2]; e[0] += er * d; e[1] += eg * d; e[2] += eb * d; e[3] += ea * d; } } } } yield { pointContainer, progress: 100 }; } _fillErrorLine(errorLine, width) { if (errorLine.length > width) { errorLine.length = width; } const l = errorLine.length; for (let i = 0; i < l; i++) { const error = errorLine[i]; error[0] = error[1] = error[2] = error[3] = 0; } for (let i = l; i < width; i++) { errorLine[i] = [0, 0, 0, 0]; } } _setKernel(kernel) { switch (kernel) { case 0 /* FloydSteinberg */: this._kernel = [ [7 / 16, 1, 0], [3 / 16, -1, 1], [5 / 16, 0, 1], [1 / 16, 1, 1] ]; break; case 1 /* FalseFloydSteinberg */: this._kernel = [ [3 / 8, 1, 0], [3 / 8, 0, 1], [2 / 8, 1, 1] ]; break; case 2 /* Stucki */: this._kernel = [ [8 / 42, 1, 0], [4 / 42, 2, 0], [2 / 42, -2, 1], [4 / 42, -1, 1], [8 / 42, 0, 1], [4 / 42, 1, 1], [2 / 42, 2, 1], [1 / 42, -2, 2], [2 / 42, -1, 2], [4 / 42, 0, 2], [2 / 42, 1, 2], [1 / 42, 2, 2] ]; break; case 3 /* Atkinson */: this._kernel = [ [1 / 8, 1, 0], [1 / 8, 2, 0], [1 / 8, -1, 1], [1 / 8, 0, 1], [1 / 8, 1, 1], [1 / 8, 0, 2] ]; break; case 4 /* Jarvis */: this._kernel = [ [7 / 48, 1, 0], [5 / 48, 2, 0], [3 / 48, -2, 1], [5 / 48, -1, 1], [7 / 48, 0, 1], [5 / 48, 1, 1], [3 / 48, 2, 1], [1 / 48, -2, 2], [3 / 48, -1, 2], [5 / 48, 0, 2], [3 / 48, 1, 2], [1 / 48, 2, 2] ]; break; case 5 /* Burkes */: this._kernel = [ [8 / 32, 1, 0], [4 / 32, 2, 0], [2 / 32, -2, 1], [4 / 32, -1, 1], [8 / 32, 0, 1], [4 / 32, 1, 1], [2 / 32, 2, 1] ]; break; case 6 /* Sierra */: this._kernel = [ [5 / 32, 1, 0], [3 / 32, 2, 0], [2 / 32, -2, 1], [4 / 32, -1, 1], [5 / 32, 0, 1], [4 / 32, 1, 1], [2 / 32, 2, 1], [2 / 32, -1, 2], [3 / 32, 0, 2], [2 / 32, 1, 2] ]; break; case 7 /* TwoSierra */: this._kernel = [ [4 / 16, 1, 0], [3 / 16, 2, 0], [1 / 16, -2, 1], [2 / 16, -1, 1], [3 / 16, 0, 1], [2 / 16, 1, 1], [1 / 16, 2, 1] ]; break; case 8 /* SierraLite */: this._kernel = [ [2 / 4, 1, 0], [1 / 4, -1, 1], [1 / 4, 0, 1] ]; break; default: throw new Error(`ErrorDiffusionArray: unknown kernel = ${kernel}`); } } }; // src/image/spaceFillingCurves/hilbertCurve.ts function* hilbertCurve(width, height, callback) { const maxBound = Math.max(width, height); const level = Math.floor(Math.log(maxBound) / Math.log(2) + 1); const tracker = new ProgressTracker(width * height, 99); const data = { width, height, level, callback, tracker, index: 0, x: 0, y: 0 }; yield* walkHilbert(data, 1 /* UP */); visit(data, 0 /* NONE */); } function* walkHilbert(data, direction) { if (data.level < 1) return; if (data.tracker.shouldNotify(data.index)) { yield { progress: data.tracker.progress }; } data.level--; switch (direction) { case 2 /* LEFT */: yield* walkHilbert(data, 1 /* UP */); visit(data, 3 /* RIGHT */); yield* walkHilbert(data, 2 /* LEFT */); visit(data, 4 /* DOWN */); yield* walkHilbert(data, 2 /* LEFT */); visit(data, 2 /* LEFT */); yield* walkHilbert(data, 4 /* DOWN */); break; case 3 /* RIGHT */: yield* walkHilbert(data, 4 /* DOWN */); visit(data, 2 /* LEFT */); yield* walkHilbert(data, 3 /* RIGHT */); visit(data, 1 /* UP */); yield* walkHilbert(data, 3 /* RIGHT */); visit(data, 3 /* RIGHT */); yield* walkHilbert(data, 1 /* UP */); break; case 1 /* UP */: yield* walkHilbert(data, 2 /* LEFT */); visit(data, 4 /* DOWN */); yield* walkHilbert(data, 1 /* UP */); visit(data, 3 /* RIGHT */); yield* walkHilbert(data, 1 /* UP */); visit(data, 1 /* UP */); yield* walkHilbert(data, 3 /* RIGHT */); break; case 4 /* DOWN */: yield* walkHilbert(data, 3 /* RIGHT */); visit(data, 1 /* UP */); yield* walkHilbert(data, 4 /* DOWN */); visit(data, 2 /* LEFT */); yield* walkHilbert(data, 4 /* DOWN */); visit(data, 4 /* DOWN */); yield* walkHilbert(data, 2 /* LEFT */); break; default: break; } data.level++; } function visit(data, direction) { if (data.x >= 0 && data.x < data.width && data.y >= 0 && data.y < data.height) { data.callback(data.x, data.y); data.index++; } switch (direction) { case 2 /* LEFT */: data.x--; break; case 3 /* RIGHT */: data.x++; break; case 1 /* UP */: data.y--; break; case 4 /* DOWN */: data.y++; break; } } // src/image/riemersma.ts var ErrorDiffusionRiemersma = class extends AbstractImageQuantizer { constructor(colorDistanceCalculator, errorQueueSize = 16, errorPropagation = 1) { super(); __publicField(this, "_distance"); __publicField(this, "_weights"); __publicField(this, "_errorQueueSize"); this._distance = colorDistanceCalculator; this._errorQueueSize = errorQueueSize; this._weights = ErrorDiffusionRiemersma._createWeights(errorPropagation, errorQueueSize); } *quantize(pointContainer, palette) { const pointArray = pointContainer.getPointArray(); const width = pointContainer.getWidth(); const height = pointContainer.getHeight(); const errorQueue = []; let head = 0; for (let i = 0; i < this._errorQueueSize; i++) { errorQueue[i] = { r: 0, g: 0, b: 0, a: 0 }; } yield* hilbertCurve(width, height, (x2, y2) => { const p = pointArray[x2 + y2 * width]; let { r, g, b, a } = p; for (let i = 0; i < this._errorQueueSize; i++) { const weight = this._weights[i]; const e = errorQueue[(i + head) % this._errorQueueSize]; r += e.r * weight; g += e.g * weight; b += e.b * weight; a += e.a * weight; } const correctedPoint = Point.createByRGBA(inRange0to255Rounded(r), inRange0to255Rounded(g), inRange0to255Rounded(b), inRange0to255Rounded(a)); const quantizedPoint = palette.getNearestColor(this._distance, correctedPoint); head = (head + 1) % this._errorQueueSize; const tail = (head + this._errorQueueSize - 1) % this._errorQueueSize; errorQueue[tail].r = p.r - quantizedPoint.r; errorQueue[tail].g = p.g - quantizedPoint.g; errorQueue[tail].b = p.b - quantizedPoint.b; errorQueue[tail].a = p.a - quantizedPoint.a; p.from(quantizedPoint); }); yield { pointContainer, progress: 100 }; } static _createWeights(errorPropagation, errorQueueSize) { const weights = []; const multiplier = Math.exp(Math.log(errorQueueSize) / (errorQueueSize - 1)); for (let i = 0, next = 1; i < errorQueueSize; i++) { weights[i] = (next + 0.5 | 0) / errorQueueSize * errorPropagation; next *= multiplier; } return weights; } }; // src/quality/index.ts var quality_exports = {}; __export(quality_exports, { ssim: () => ssim }); // src/quality/ssim.ts var K1 = 0.01; var K2 = 0.03; function ssim(image1, image2) { if (image1.getHeight() !== image2.getHeight() || image1.getWidth() !== image2.getWidth()) { throw new Error("Images have different sizes!"); } const bitsPerComponent = 8; const L = (1 << bitsPerComponent) - 1; const c1 = (K1 * L) ** 2; const c2 = (K2 * L) ** 2; let numWindows = 0; let mssim = 0; iterate(image1, image2, (lumaValues1, lumaValues2, averageLumaValue1, averageLumaValue2) => { let sigxy = 0; let sigsqx = 0; let sigsqy = 0; for (let i = 0; i < lumaValues1.length; i++) { sigsqx += (lumaValues1[i] - averageLumaValue1) ** 2; sigsqy += (lumaValues2[i] - averageLumaValue2) ** 2; sigxy += (lumaValues1[i] - averageLumaValue1) * (lumaValues2[i] - averageLumaValue2); } const numPixelsInWin = lumaValues1.length - 1; sigsqx /= numPixelsInWin; sigsqy /= numPixelsInWin; sigxy /= numPixelsInWin; const numerator = (2 * averageLumaValue1 * averageLumaValue2 + c1) * (2 * sigxy + c2); const denominator = (averageLumaValue1 ** 2 + averageLumaValue2 ** 2 + c1) * (sigsqx + sigsqy + c2); const ssim2 = numerator / denominator; mssim += ssim2; numWindows++; }); return mssim / numWindows; } function iterate(image1, image2, callback) { const windowSize = 8; const width = image1.getWidth(); const height = image1.getHeight(); for (let y2 = 0; y2 < height; y2 += windowSize) { for (let x2 = 0; x2 < width; x2 += windowSize) { const windowWidth = Math.min(windowSize, width - x2); const windowHeight = Math.min(windowSize, height - y2); const lumaValues1 = calculateLumaValuesForWindow(image1, x2, y2, windowWidth, windowHeight); const lumaValues2 = calculateLumaValuesForWindow(image2, x2, y2, windowWidth, windowHeight); const averageLuma1 = calculateAverageLuma(lumaValues1); const averageLuma2 = calculateAverageLuma(lumaValues2); callback(lumaValues1, lumaValues2, averageLuma1, averageLuma2); } } } function calculateLumaValuesForWindow(image, x2, y2, width, height) { const pointArray = image.getPointArray(); const lumaValues = []; let counter = 0; for (let j = y2; j < y2 + height; j++) { const offset = j * image.getWidth(); for (let i = x2; i < x2 + width; i++) { const point = pointArray[offset + i]; lumaValues[counter] = point.r * 0.2126 /* RED */ + point.g * 0.7152 /* GREEN */ + point.b * 0.0722 /* BLUE */; counter++; } } return lumaValues; } function calculateAverageLuma(lumaValues) { let sumLuma = 0; for (const luma of lumaValues) { sumLuma += luma; } return sumLuma / lumaValues.length; } // src/basicAPI.ts var setImmediateImpl = typeof setImmediate === "function" ? setImmediate : typeof process !== "undefined" && typeof (process == null ? void 0 : process.nextTick) === "function" ? (callback) => process.nextTick(callback) : (callback) => setTimeout(callback, 0); function buildPaletteSync(images, { colorDistanceFormula, paletteQuantization, colors } = {}) { const distanceCalculator = colorDistanceFormulaToColorDistance(colorDistanceFormula); const paletteQuantizer = paletteQuantizationToPaletteQuantizer(distanceCalculator, paletteQuantization, colors); images.forEach((image) => paletteQuantizer.sample(image)); return paletteQuantizer.quantizeSync(); } async function buildPalette(images, { colorDistanceFormula, paletteQuantization, colors, onProgress } = {}) { return new Promise((resolve, reject) => { const distanceCalculator = colorDistanceFormulaToColorDistance(colorDistanceFormula); const paletteQuantizer = paletteQuantizationToPaletteQuantizer(distanceCalculator, paletteQuantization, colors); images.forEach((image) => paletteQuantizer.sample(image)); let palette; const iterator = paletteQuantizer.quantize(); const next = () => { try { const result = iterator.next(); if (result.done) { resolve(palette); } else { if (result.value.palette) palette = result.value.palette; if (onProgress) onProgress(result.value.progress); setImmediateImpl(next); } } catch (error) { reject(error); } }; setImmediateImpl(next); }); } function applyPaletteSync(image, palette, { colorDistanceFormula, imageQuantization } = {}) { const distanceCalculator = colorDistanceFormulaToColorDistance(colorDistanceFormula); const imageQuantizer = imageQuantizationToImageQuantizer(distanceCalculator, imageQuantization); return imageQuantizer.quantizeSync(image, palette); } async function applyPalette(image, palette, { colorDistanceFormula, imageQuantization, onProgress } = {}) { return new Promise((resolve, reject) => { const distanceCalculator = colorDistanceFormulaToColorDistance(colorDistanceFormula); const imageQuantizer = imageQuantizationToImageQuantizer(distanceCalculator, imageQuantization); let outPointContainer; const iterator = imageQuantizer.quantize(image, palette); const next = () => { try { const result = iterator.next(); if (result.done) { resolve(outPointContainer); } else { if (result.value.pointContainer) { outPointContainer = result.value.pointContainer; } if (onProgress) onProgress(result.value.progress); setImmediateImpl(next); } } catch (error) { reject(error); } }; setImmediateImpl(next); }); } function colorDistanceFormulaToColorDistance(colorDistanceFormula = "euclidean-bt709") { switch (colorDistanceFormula) { case "cie94-graphic-arts": return new CIE94GraphicArts(); case "cie94-textiles": return new CIE94Textiles(); case "ciede2000": return new CIEDE2000(); case "color-metric": return new CMetric(); case "euclidean": return new Euclidean(); case "euclidean-bt709": return new EuclideanBT709(); case "euclidean-bt709-noalpha": return new EuclideanBT709NoAlpha(); case "manhattan": return new Manhattan(); case "manhattan-bt709": return new ManhattanBT709(); case "manhattan-nommyde": return new ManhattanNommyde(); case "pngquant": return new PNGQuant(); default: throw new Error(`Unknown colorDistanceFormula ${colorDistanceFormula}`); } } function imageQuantizationToImageQuantizer(distanceCalculator, imageQuantization = "floyd-steinberg") { switch (imageQuantization) { case "nearest": return new NearestColor(distanceCalculator); case "riemersma": return new ErrorDiffusionRiemersma(distanceCalculator); case "floyd-steinberg": return new ErrorDiffusionArray(distanceCalculator, 0 /* FloydSteinberg */); case "false-floyd-steinberg": return new ErrorDiffusionArray(distanceCalculator, 1 /* FalseFloydSteinberg */); case "stucki": return new ErrorDiffusionArray(distanceCalculator, 2 /* Stucki */); case "atkinson": return new ErrorDiffusionArray(distanceCalculator, 3 /* Atkinson */); case "jarvis": return new ErrorDiffusionArray(distanceCalculator, 4 /* Jarvis */); case "burkes": return new ErrorDiffusionArray(distanceCalculator, 5 /* Burkes */); case "sierra": return new ErrorDiffusionArray(distanceCalculator, 6 /* Sierra */); case "two-sierra": return new ErrorDiffusionArray(distanceCalculator, 7 /* TwoSierra */); case "sierra-lite": return new ErrorDiffusionArray(distanceCalculator, 8 /* SierraLite */); default: throw new Error(`Unknown imageQuantization ${imageQuantization}`); } } function paletteQuantizationToPaletteQuantizer(distanceCalculator, paletteQuantization = "wuquant", colors = 256) { switch (paletteQuantization) { case "neuquant": return new NeuQuant(distanceCalculator, colors); case "rgbquant": return new RGBQuant(distanceCalculator, colors); case "wuquant": return new WuQuant(distanceCalculator, colors); case "neuquant-float": return new NeuQuantFloat(distanceCalculator, colors); default: throw new Error(`Unknown paletteQuantization ${paletteQuantization}`); } } module.exports = __toCommonJS(src_exports); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { applyPalette, applyPaletteSync, buildPalette, buildPaletteSync, constants, conversion, distance, image, palette, quality, utils }); /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * cie94.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * ciede2000.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * cmetric.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * common.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * constants.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * ditherErrorDiffusionArray.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * euclidean.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * helper.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * hueStatistics.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * iq.ts - Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * lab2rgb.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * lab2xyz.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * manhattanNeuQuant.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * nearestColor.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * palette.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * pngQuant.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * point.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * pointContainer.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * rgb2hsl.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * rgb2lab.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * rgb2xyz.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * ssim.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * wuQuant.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * xyz2lab.ts - part of Image Quantization Library */ /** * @preserve * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * xyz2rgb.ts - part of Image Quantization Library */ /** * @preserve * MIT License * * Copyright 2015-2018 Igor Bezkrovnyi * * 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. * * riemersma.ts - part of Image Quantization Library */ /** * @preserve TypeScript port: * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * colorHistogram.ts - part of Image Quantization Library */ /** * @preserve TypeScript port: * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * neuquant.ts - part of Image Quantization Library */ /** * @preserve TypeScript port: * Copyright 2015-2018 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * rgbquant.ts - part of Image Quantization Library */ //# sourceMappingURL=image-q.cjs.map