UNPKG

sharp-vibrant

Version:

Extract prominent colors from an image in a node environment using sharp.

255 lines 8.7 kB
"use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to set private field on non-instance"); } privateMap.set(receiver, value); return value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); }; var _volume, _avg, _count; Object.defineProperty(exports, "__esModule", { value: true }); const util_1 = require("../util"); class VBox { constructor(r1, r2, g1, g2, b1, b2, hist) { _volume.set(this, -1); _avg.set(this, null); _count.set(this, -1); this.dimension = { r1, r2, g1, g2, b1, b2, }; this.hist = hist; } static build(pixels) { const hn = 1 << (3 * util_1.SIGBITS); const hist = new Uint32Array(hn); let rmax = 0; let rmin = Number.MAX_VALUE; let gmax = 0; let gmin = Number.MAX_VALUE; let bmax = 0; let bmin = Number.MAX_VALUE; let r; let g; let b; let a; const n = pixels.length / 4; let i = 0; while (i < n) { const offset = i * 4; i += 1; r = pixels[offset + 0]; g = pixels[offset + 1]; b = pixels[offset + 2]; a = pixels[offset + 3]; // Ignored pixels' alpha is marked as 0 in filtering stage if (a === 0) continue; r >>= util_1.RSHIFT; g >>= util_1.RSHIFT; b >>= util_1.RSHIFT; const index = util_1.getColorIndex(r, g, b); hist[index] += 1; if (r > rmax) rmax = r; if (r < rmin) rmin = r; if (g > gmax) gmax = g; if (g < gmin) gmin = g; if (b > bmax) bmax = b; if (b < bmin) bmin = b; } return new VBox(rmin, rmax, gmin, gmax, bmin, bmax, hist); } invalidate() { __classPrivateFieldSet(this, _volume, -1); __classPrivateFieldSet(this, _count, -1); __classPrivateFieldSet(this, _avg, null); } get volume() { if (__classPrivateFieldGet(this, _volume) < 0) { const { r1, r2, g1, g2, b1, b2, } = this.dimension; __classPrivateFieldSet(this, _volume, (r2 - r1 + 1) * (g2 - g1 + 1) * (b2 - b1 + 1)); } return __classPrivateFieldGet(this, _volume); } get count() { if (__classPrivateFieldGet(this, _count) < 0) { const { hist } = this; const { r1, r2, g1, g2, b1, b2, } = this.dimension; let c = 0; for (let r = r1; r <= r2; r += 1) { for (let g = g1; g <= g2; g += 1) { for (let b = b1; b <= b2; b += 1) { const index = util_1.getColorIndex(r, g, b); c += hist[index]; } } } __classPrivateFieldSet(this, _count, c); } return __classPrivateFieldGet(this, _count); } clone() { const { hist } = this; const { r1, r2, g1, g2, b1, b2, } = this.dimension; return new VBox(r1, r2, g1, g2, b1, b2, hist); } get avg() { if (!__classPrivateFieldGet(this, _avg)) { const { hist } = this; const { r1, r2, g1, g2, b1, b2, } = this.dimension; let ntot = 0; const mult = 1 << (8 - util_1.SIGBITS); let rsum = 0; let gsum = 0; let bsum = 0; for (let r = r1; r <= r2; r += 1) { for (let g = g1; g <= g2; g += 1) { for (let b = b1; b <= b2; b += 1) { const index = util_1.getColorIndex(r, g, b); const h = hist[index]; ntot += h; rsum += (h * (r + 0.5) * mult); gsum += (h * (g + 0.5) * mult); bsum += (h * (b + 0.5) * mult); } } } if (ntot) { __classPrivateFieldSet(this, _avg, [ ~~(rsum / ntot), ~~(gsum / ntot), ~~(bsum / ntot), ]); } else { __classPrivateFieldSet(this, _avg, [ ~~((mult * (r1 + r2 + 1)) / 2), ~~((mult * (g1 + g2 + 1)) / 2), ~~((mult * (b1 + b2 + 1)) / 2), ]); } } return __classPrivateFieldGet(this, _avg); } contains(rgb) { let [r, g, b] = rgb; const { r1, r2, g1, g2, b1, b2, } = this.dimension; r >>= util_1.RSHIFT; g >>= util_1.RSHIFT; b >>= util_1.RSHIFT; return r >= r1 && r <= r2 && g >= g1 && g <= g2 && b >= b1 && b <= b2; } split() { const { hist } = this; const { r1, r2, g1, g2, b1, b2, } = this.dimension; const { count } = this; if (!count) return []; if (count === 1) return [this.clone()]; const rw = r2 - r1 + 1; const gw = g2 - g1 + 1; const bw = b2 - b1 + 1; const maxw = Math.max(rw, gw, bw); let accSum = null; let sum = 0; let total = 0; let maxd = null; if (maxw === rw) { maxd = 'r'; accSum = new Uint32Array(r2 + 1); for (let r = r1; r <= r2; r += 1) { sum = 0; for (let g = g1; g <= g2; g += 1) { for (let b = b1; b <= b2; b += 1) { const index = util_1.getColorIndex(r, g, b); sum += hist[index]; } } total += sum; accSum[r] = total; } } else if (maxw === gw) { maxd = 'g'; accSum = new Uint32Array(g2 + 1); for (let g = g1; g <= g2; g += 1) { sum = 0; for (let r = r1; r <= r2; r += 1) { for (let b = b1; b <= b2; b += 1) { const index = util_1.getColorIndex(r, g, b); sum += hist[index]; } } total += sum; accSum[g] = total; } } else { maxd = 'b'; accSum = new Uint32Array(b2 + 1); for (let b = b1; b <= b2; b += 1) { sum = 0; for (let r = r1; r <= r2; r += 1) { for (let g = g1; g <= g2; g += 1) { const index = util_1.getColorIndex(r, g, b); sum += hist[index]; } } total += sum; accSum[b] = total; } } let splitPoint = -1; const reverseSum = new Uint32Array(accSum.length); for (let i = 0; i < accSum.length; i += 1) { const d = accSum[i]; if (splitPoint < 0 && d > total / 2) splitPoint = i; reverseSum[i] = total - d; } return this.doCut(maxd, splitPoint, accSum, reverseSum); } doCut(d, splitPoint, accSum, reverseSum) { const dim1 = `${d}1`; const dim2 = `${d}2`; const d1 = this.dimension[dim1]; let d2 = this.dimension[dim2]; const vbox1 = this.clone(); const vbox2 = this.clone(); const left = splitPoint - d1; const right = d2 - splitPoint; if (left <= right) { d2 = Math.min(d2 - 1, ~~(splitPoint + right / 2)); d2 = Math.max(0, d2); } else { d2 = Math.max(d1, ~~(splitPoint - 1 - left / 2)); d2 = Math.min(this.dimension[dim2], d2); } while (!accSum[d2]) d2 += 1; let c2 = reverseSum[d2]; while (!c2 && accSum[d2 - 1]) c2 = reverseSum[d2 -= 1]; vbox1.dimension[dim2] = d2; vbox2.dimension[dim1] = d2 + 1; return [vbox1, vbox2]; } } exports.default = VBox; _volume = new WeakMap(), _avg = new WeakMap(), _count = new WeakMap(); //# sourceMappingURL=vbox.js.map