From b71ee86fb4147d0cd7656bc64a6de020a250e125 Mon Sep 17 00:00:00 2001 From: Deluan Date: Sat, 25 Jul 2026 15:11:28 -0400 Subject: [PATCH] test(thumbhash): vendor the reference and generate golden vectors --- core/artwork/thumbhash/testdata/alpha.png | Bin 0 -> 433 bytes .../thumbhash/testdata/gen_fixtures.mjs | 75 +++++ .../artwork/thumbhash/testdata/gen_golden.mjs | 37 +++ core/artwork/thumbhash/testdata/golden.json | 8 + core/artwork/thumbhash/testdata/landscape.png | Bin 0 -> 18982 bytes core/artwork/thumbhash/testdata/portrait.png | Bin 0 -> 18750 bytes core/artwork/thumbhash/testdata/solid.png | Bin 0 -> 209 bytes core/artwork/thumbhash/testdata/square.png | Bin 0 -> 30632 bytes core/artwork/thumbhash/testdata/thumbhash.js | 288 ++++++++++++++++++ core/artwork/thumbhash/testdata/tiny.png | Bin 0 -> 70 bytes .../artwork/thumbhash/thumbhash_suite_test.go | 17 ++ 11 files changed, 425 insertions(+) create mode 100644 core/artwork/thumbhash/testdata/alpha.png create mode 100644 core/artwork/thumbhash/testdata/gen_fixtures.mjs create mode 100644 core/artwork/thumbhash/testdata/gen_golden.mjs create mode 100644 core/artwork/thumbhash/testdata/golden.json create mode 100644 core/artwork/thumbhash/testdata/landscape.png create mode 100644 core/artwork/thumbhash/testdata/portrait.png create mode 100644 core/artwork/thumbhash/testdata/solid.png create mode 100644 core/artwork/thumbhash/testdata/square.png create mode 100644 core/artwork/thumbhash/testdata/thumbhash.js create mode 100644 core/artwork/thumbhash/testdata/tiny.png create mode 100644 core/artwork/thumbhash/thumbhash_suite_test.go diff --git a/core/artwork/thumbhash/testdata/alpha.png b/core/artwork/thumbhash/testdata/alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..5770c696a4b702dac15b24084cc2556e90e37f40 GIT binary patch literal 433 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdoh8V65 { + let c = n + for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1 + return c >>> 0 +}) +const crc32 = (buf) => { + let c = 0xffffffff + for (const b of buf) c = crcTable[(c ^ b) & 0xff] ^ (c >>> 8) + return (c ^ 0xffffffff) >>> 0 +} +const chunk = (type, data) => { + const len = Buffer.alloc(4) + len.writeUInt32BE(data.length) + const body = Buffer.concat([Buffer.from(type, 'ascii'), data]) + const crc = Buffer.alloc(4) + crc.writeUInt32BE(crc32(body)) + return Buffer.concat([len, body, crc]) +} +const png = (w, h, rgba) => { + const ihdr = Buffer.alloc(13) + ihdr.writeUInt32BE(w, 0) + ihdr.writeUInt32BE(h, 4) + ihdr[8] = 8 // bit depth + ihdr[9] = 6 // truecolor + alpha + const raw = Buffer.alloc(h * (w * 4 + 1)) + for (let y = 0; y < h; y++) { + raw[y * (w * 4 + 1)] = 0 // filter: none + for (let x = 0; x < w * 4; x++) raw[y * (w * 4 + 1) + 1 + x] = rgba[y * w * 4 + x] + } + return Buffer.concat([ + Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), + chunk('IHDR', ihdr), + chunk('IDAT', deflateSync(raw)), + chunk('IEND', Buffer.alloc(0)), + ]) +} + +const make = (w, h, fn) => { + const rgba = new Uint8Array(w * h * 4) + for (let y = 0; y < h; y++) + for (let x = 0; x < w; x++) fn(rgba, (y * w + x) * 4, x, y, w, h) + return png(w, h, rgba) +} + +const gradient = (rgba, i, x, y, w, h) => { + rgba[i] = Math.floor((255 * x) / w) + rgba[i + 1] = Math.floor((255 * y) / h) + rgba[i + 2] = Math.floor((255 * (x + y)) / (w + h)) + rgba[i + 3] = 255 +} +const solid = (rgba, i) => { + rgba[i] = 60 + rgba[i + 1] = 120 + rgba[i + 2] = 180 + rgba[i + 3] = 255 +} +const alphaRamp = (rgba, i, x, y, w) => { + rgba[i] = 200 + rgba[i + 1] = 50 + rgba[i + 2] = 90 + rgba[i + 3] = Math.floor((255 * x) / w) +} + +const out = new URL('.', import.meta.url).pathname +writeFileSync(out + 'square.png', make(100, 100, gradient)) +writeFileSync(out + 'landscape.png', make(100, 60, gradient)) +writeFileSync(out + 'portrait.png', make(60, 100, gradient)) +writeFileSync(out + 'alpha.png', make(80, 80, alphaRamp)) +writeFileSync(out + 'solid.png', make(64, 64, solid)) +writeFileSync(out + 'tiny.png', make(1, 1, solid)) +console.log('fixtures written') diff --git a/core/artwork/thumbhash/testdata/gen_golden.mjs b/core/artwork/thumbhash/testdata/gen_golden.mjs new file mode 100644 index 000000000..2825e0709 --- /dev/null +++ b/core/artwork/thumbhash/testdata/gen_golden.mjs @@ -0,0 +1,37 @@ +// Produces golden.json from the vendored reference. Run: node gen_golden.mjs +import { readFileSync, writeFileSync, readdirSync } from 'fs' +import { inflateSync } from 'zlib' +import { rgbaToThumbHash } from './thumbhash.js' + +// Minimal reader for the exact PNGs gen_fixtures.mjs writes: 8-bit RGBA, filter 0, single IDAT. +const readPNG = (buf) => { + let w = 0, h = 0 + const idat = [] + for (let off = 8; off < buf.length; ) { + const len = buf.readUInt32BE(off) + const type = buf.toString('ascii', off + 4, off + 8) + const data = buf.subarray(off + 8, off + 8 + len) + if (type === 'IHDR') { + w = data.readUInt32BE(0) + h = data.readUInt32BE(4) + if (data[8] !== 8 || data[9] !== 6) throw new Error('expected 8-bit RGBA') + } else if (type === 'IDAT') idat.push(data) + off += 12 + len + } + const raw = inflateSync(Buffer.concat(idat)) + const rgba = new Uint8Array(w * h * 4) + for (let y = 0; y < h; y++) { + if (raw[y * (w * 4 + 1)] !== 0) throw new Error('expected filter 0') + for (let x = 0; x < w * 4; x++) rgba[y * w * 4 + x] = raw[y * (w * 4 + 1) + 1 + x] + } + return { w, h, rgba } +} + +const dir = new URL('.', import.meta.url).pathname +const golden = {} +for (const name of readdirSync(dir).filter((f) => f.endsWith('.png')).sort()) { + const { w, h, rgba } = readPNG(readFileSync(dir + name)) + golden[name] = Buffer.from(rgbaToThumbHash(w, h, rgba)).toString('base64') +} +writeFileSync(dir + 'golden.json', JSON.stringify(golden, null, 2) + '\n') +console.log(golden) diff --git a/core/artwork/thumbhash/testdata/golden.json b/core/artwork/thumbhash/testdata/golden.json new file mode 100644 index 000000000..83fc2fdde --- /dev/null +++ b/core/artwork/thumbhash/testdata/golden.json @@ -0,0 +1,8 @@ +{ + "alpha.png": "HCmDBQA3j3mHeHeXiGCI94h3gHd4iIh4eA==", + "landscape.png": "3wcOFJpwd3dxd3eHh3ePgAj4hw==", + "portrait.png": "3/cNFBpxB4d3d3d4d3eAjwj3eA==", + "solid.png": "HoUBBwB4eHeHd3hweId3h3h4B2+Ih4gA", + "square.png": "HwgOBxpwd4dwd3h3h3eHd3d3+PiIgI8H", + "tiny.png": "HoU9tx4I9wiIh4hwj3CI+AiIcH/494cP" +} diff --git a/core/artwork/thumbhash/testdata/landscape.png b/core/artwork/thumbhash/testdata/landscape.png new file mode 100644 index 0000000000000000000000000000000000000000..1ae8955ecda01b1103aff518f1f11b736a077027 GIT binary patch literal 18982 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 T1snL=00000NkvXXu0mjfNJSp^ literal 0 HcmV?d00001 diff --git a/core/artwork/thumbhash/testdata/thumbhash.js b/core/artwork/thumbhash/testdata/thumbhash.js new file mode 100644 index 000000000..369b118ec --- /dev/null +++ b/core/artwork/thumbhash/testdata/thumbhash.js @@ -0,0 +1,288 @@ +/** + * Encodes an RGBA image to a ThumbHash. RGB should not be premultiplied by A. + * + * @param w The width of the input image. Must be ≤100px. + * @param h The height of the input image. Must be ≤100px. + * @param rgba The pixels in the input image, row-by-row. Must have w*h*4 elements. + * @returns The ThumbHash as a Uint8Array. + */ +export function rgbaToThumbHash(w, h, rgba) { + // Encoding an image larger than 100x100 is slow with no benefit + if (w > 100 || h > 100) throw new Error(`${w}x${h} doesn't fit in 100x100`) + let { PI, round, max, cos, abs } = Math + + // Determine the average color + let avg_r = 0, avg_g = 0, avg_b = 0, avg_a = 0 + for (let i = 0, j = 0; i < w * h; i++, j += 4) { + let alpha = rgba[j + 3] / 255 + avg_r += alpha / 255 * rgba[j] + avg_g += alpha / 255 * rgba[j + 1] + avg_b += alpha / 255 * rgba[j + 2] + avg_a += alpha + } + if (avg_a) { + avg_r /= avg_a + avg_g /= avg_a + avg_b /= avg_a + } + + let hasAlpha = avg_a < w * h + let l_limit = hasAlpha ? 5 : 7 // Use fewer luminance bits if there's alpha + let lx = max(1, round(l_limit * w / max(w, h))) + let ly = max(1, round(l_limit * h / max(w, h))) + let l = [] // luminance + let p = [] // yellow - blue + let q = [] // red - green + let a = [] // alpha + + // Convert the image from RGBA to LPQA (composite atop the average color) + for (let i = 0, j = 0; i < w * h; i++, j += 4) { + let alpha = rgba[j + 3] / 255 + let r = avg_r * (1 - alpha) + alpha / 255 * rgba[j] + let g = avg_g * (1 - alpha) + alpha / 255 * rgba[j + 1] + let b = avg_b * (1 - alpha) + alpha / 255 * rgba[j + 2] + l[i] = (r + g + b) / 3 + p[i] = (r + g) / 2 - b + q[i] = r - g + a[i] = alpha + } + + // Encode using the DCT into DC (constant) and normalized AC (varying) terms + let encodeChannel = (channel, nx, ny) => { + let dc = 0, ac = [], scale = 0, fx = [] + for (let cy = 0; cy < ny; cy++) { + for (let cx = 0; cx * ny < nx * (ny - cy); cx++) { + let f = 0 + for (let x = 0; x < w; x++) + fx[x] = cos(PI / w * cx * (x + 0.5)) + for (let y = 0; y < h; y++) + for (let x = 0, fy = cos(PI / h * cy * (y + 0.5)); x < w; x++) + f += channel[x + y * w] * fx[x] * fy + f /= w * h + if (cx || cy) { + ac.push(f) + scale = max(scale, abs(f)) + } else { + dc = f + } + } + } + if (scale) + for (let i = 0; i < ac.length; i++) + ac[i] = 0.5 + 0.5 / scale * ac[i] + return [dc, ac, scale] + } + let [l_dc, l_ac, l_scale] = encodeChannel(l, max(3, lx), max(3, ly)) + let [p_dc, p_ac, p_scale] = encodeChannel(p, 3, 3) + let [q_dc, q_ac, q_scale] = encodeChannel(q, 3, 3) + let [a_dc, a_ac, a_scale] = hasAlpha ? encodeChannel(a, 5, 5) : [] + + // Write the constants + let isLandscape = w > h + let header24 = round(63 * l_dc) | (round(31.5 + 31.5 * p_dc) << 6) | (round(31.5 + 31.5 * q_dc) << 12) | (round(31 * l_scale) << 18) | (hasAlpha << 23) + let header16 = (isLandscape ? ly : lx) | (round(63 * p_scale) << 3) | (round(63 * q_scale) << 9) | (isLandscape << 15) + let hash = [header24 & 255, (header24 >> 8) & 255, header24 >> 16, header16 & 255, header16 >> 8] + let ac_start = hasAlpha ? 6 : 5 + let ac_index = 0 + if (hasAlpha) hash.push(round(15 * a_dc) | (round(15 * a_scale) << 4)) + + // Write the varying factors + for (let ac of hasAlpha ? [l_ac, p_ac, q_ac, a_ac] : [l_ac, p_ac, q_ac]) + for (let f of ac) + hash[ac_start + (ac_index >> 1)] |= round(15 * f) << ((ac_index++ & 1) << 2) + return new Uint8Array(hash) +} + +/** + * Decodes a ThumbHash to an RGBA image. RGB is not be premultiplied by A. + * + * @param hash The bytes of the ThumbHash. + * @returns The width, height, and pixels of the rendered placeholder image. + */ +export function thumbHashToRGBA(hash) { + let { PI, min, max, cos, round } = Math + + // Read the constants + let header24 = hash[0] | (hash[1] << 8) | (hash[2] << 16) + let header16 = hash[3] | (hash[4] << 8) + let l_dc = (header24 & 63) / 63 + let p_dc = ((header24 >> 6) & 63) / 31.5 - 1 + let q_dc = ((header24 >> 12) & 63) / 31.5 - 1 + let l_scale = ((header24 >> 18) & 31) / 31 + let hasAlpha = header24 >> 23 + let p_scale = ((header16 >> 3) & 63) / 63 + let q_scale = ((header16 >> 9) & 63) / 63 + let isLandscape = header16 >> 15 + let lx = max(3, isLandscape ? hasAlpha ? 5 : 7 : header16 & 7) + let ly = max(3, isLandscape ? header16 & 7 : hasAlpha ? 5 : 7) + let a_dc = hasAlpha ? (hash[5] & 15) / 15 : 1 + let a_scale = (hash[5] >> 4) / 15 + + // Read the varying factors (boost saturation by 1.25x to compensate for quantization) + let ac_start = hasAlpha ? 6 : 5 + let ac_index = 0 + let decodeChannel = (nx, ny, scale) => { + let ac = [] + for (let cy = 0; cy < ny; cy++) + for (let cx = cy ? 0 : 1; cx * ny < nx * (ny - cy); cx++) + ac.push((((hash[ac_start + (ac_index >> 1)] >> ((ac_index++ & 1) << 2)) & 15) / 7.5 - 1) * scale) + return ac + } + let l_ac = decodeChannel(lx, ly, l_scale) + let p_ac = decodeChannel(3, 3, p_scale * 1.25) + let q_ac = decodeChannel(3, 3, q_scale * 1.25) + let a_ac = hasAlpha && decodeChannel(5, 5, a_scale) + + // Decode using the DCT into RGB + let ratio = thumbHashToApproximateAspectRatio(hash) + let w = round(ratio > 1 ? 32 : 32 * ratio) + let h = round(ratio > 1 ? 32 / ratio : 32) + let rgba = new Uint8Array(w * h * 4), fx = [], fy = [] + for (let y = 0, i = 0; y < h; y++) { + for (let x = 0; x < w; x++, i += 4) { + let l = l_dc, p = p_dc, q = q_dc, a = a_dc + + // Precompute the coefficients + for (let cx = 0, n = max(lx, hasAlpha ? 5 : 3); cx < n; cx++) + fx[cx] = cos(PI / w * (x + 0.5) * cx) + for (let cy = 0, n = max(ly, hasAlpha ? 5 : 3); cy < n; cy++) + fy[cy] = cos(PI / h * (y + 0.5) * cy) + + // Decode L + for (let cy = 0, j = 0; cy < ly; cy++) + for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx * ly < lx * (ly - cy); cx++, j++) + l += l_ac[j] * fx[cx] * fy2 + + // Decode P and Q + for (let cy = 0, j = 0; cy < 3; cy++) { + for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 3 - cy; cx++, j++) { + let f = fx[cx] * fy2 + p += p_ac[j] * f + q += q_ac[j] * f + } + } + + // Decode A + if (hasAlpha) + for (let cy = 0, j = 0; cy < 5; cy++) + for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 5 - cy; cx++, j++) + a += a_ac[j] * fx[cx] * fy2 + + // Convert to RGB + let b = l - 2 / 3 * p + let r = (3 * l - b + q) / 2 + let g = r - q + rgba[i] = max(0, 255 * min(1, r)) + rgba[i + 1] = max(0, 255 * min(1, g)) + rgba[i + 2] = max(0, 255 * min(1, b)) + rgba[i + 3] = max(0, 255 * min(1, a)) + } + } + return { w, h, rgba } +} + +/** + * Extracts the average color from a ThumbHash. RGB is not be premultiplied by A. + * + * @param hash The bytes of the ThumbHash. + * @returns The RGBA values for the average color. Each value ranges from 0 to 1. + */ +export function thumbHashToAverageRGBA(hash) { + let { min, max } = Math + let header = hash[0] | (hash[1] << 8) | (hash[2] << 16) + let l = (header & 63) / 63 + let p = ((header >> 6) & 63) / 31.5 - 1 + let q = ((header >> 12) & 63) / 31.5 - 1 + let hasAlpha = header >> 23 + let a = hasAlpha ? (hash[5] & 15) / 15 : 1 + let b = l - 2 / 3 * p + let r = (3 * l - b + q) / 2 + let g = r - q + return { + r: max(0, min(1, r)), + g: max(0, min(1, g)), + b: max(0, min(1, b)), + a + } +} + +/** + * Extracts the approximate aspect ratio of the original image. + * + * @param hash The bytes of the ThumbHash. + * @returns The approximate aspect ratio (i.e. width / height). + */ +export function thumbHashToApproximateAspectRatio(hash) { + let header = hash[3] + let hasAlpha = hash[2] & 0x80 + let isLandscape = hash[4] & 0x80 + let lx = isLandscape ? hasAlpha ? 5 : 7 : header & 7 + let ly = isLandscape ? header & 7 : hasAlpha ? 5 : 7 + return lx / ly +} + +/** + * Encodes an RGBA image to a PNG data URL. RGB should not be premultiplied by + * A. This is optimized for speed and simplicity and does not optimize for size + * at all. This doesn't do any compression (all values are stored uncompressed). + * + * @param w The width of the input image. Must be ≤100px. + * @param h The height of the input image. Must be ≤100px. + * @param rgba The pixels in the input image, row-by-row. Must have w*h*4 elements. + * @returns A data URL containing a PNG for the input image. + */ +export function rgbaToDataURL(w, h, rgba) { + let row = w * 4 + 1 + let idat = 6 + h * (5 + row) + let bytes = [ + 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, + w >> 8, w & 255, 0, 0, h >> 8, h & 255, 8, 6, 0, 0, 0, 0, 0, 0, 0, + idat >>> 24, (idat >> 16) & 255, (idat >> 8) & 255, idat & 255, + 73, 68, 65, 84, 120, 1 + ] + let table = [ + 0, 498536548, 997073096, 651767980, 1994146192, 1802195444, 1303535960, + 1342533948, -306674912, -267414716, -690576408, -882789492, -1687895376, + -2032938284, -1609899400, -1111625188 + ] + let a = 1, b = 0 + for (let y = 0, i = 0, end = row - 1; y < h; y++, end += row - 1) { + bytes.push(y + 1 < h ? 0 : 1, row & 255, row >> 8, ~row & 255, (row >> 8) ^ 255, 0) + for (b = (b + a) % 65521; i < end; i++) { + let u = rgba[i] & 255 + bytes.push(u) + a = (a + u) % 65521 + b = (b + a) % 65521 + } + } + bytes.push( + b >> 8, b & 255, a >> 8, a & 255, 0, 0, 0, 0, + 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130 + ) + for (let [start, end] of [[12, 29], [37, 41 + idat]]) { + let c = ~0 + for (let i = start; i < end; i++) { + c ^= bytes[i] + c = (c >>> 4) ^ table[c & 15] + c = (c >>> 4) ^ table[c & 15] + } + c = ~c + bytes[end++] = c >>> 24 + bytes[end++] = (c >> 16) & 255 + bytes[end++] = (c >> 8) & 255 + bytes[end++] = c & 255 + } + return 'data:image/png;base64,' + btoa(String.fromCharCode(...bytes)) +} + +/** + * Decodes a ThumbHash to a PNG data URL. This is a convenience function that + * just calls "thumbHashToRGBA" followed by "rgbaToDataURL". + * + * @param hash The bytes of the ThumbHash. + * @returns A data URL containing a PNG for the rendered ThumbHash. + */ +export function thumbHashToDataURL(hash) { + let image = thumbHashToRGBA(hash) + return rgbaToDataURL(image.w, image.h, image.rgba) +} diff --git a/core/artwork/thumbhash/testdata/tiny.png b/core/artwork/thumbhash/testdata/tiny.png new file mode 100644 index 0000000000000000000000000000000000000000..ab635e11a904d71fce72f9361aa4fb420e9f5d18 GIT binary patch literal 70 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k92}1TpU9x<|J=edE=8j1IrPnj0%CR Qyg&&CPgg&ebxsLQ0L54j#Q*>R literal 0 HcmV?d00001 diff --git a/core/artwork/thumbhash/thumbhash_suite_test.go b/core/artwork/thumbhash/thumbhash_suite_test.go new file mode 100644 index 000000000..11a6f0e25 --- /dev/null +++ b/core/artwork/thumbhash/thumbhash_suite_test.go @@ -0,0 +1,17 @@ +package thumbhash_test + +import ( + "testing" + + "github.com/navidrome/navidrome/log" + "github.com/navidrome/navidrome/tests" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestThumbHash(t *testing.T) { + tests.Init(t, false) + log.SetLevel(log.LevelFatal) + RegisterFailHandler(Fail) + RunSpecs(t, "ThumbHash Suite") +}