import { describe, it, expect } from 'vitest'
import { decode, naturalSize } from './thumbhash'
const fnv = (bytes) => {
let h = 2166136261 >>> 0
for (const b of bytes) {
h ^= b
h = Math.imul(h, 16777619) >>> 0
}
return h.toString(16)
}
const REFERENCE = [
['alpha', 'JOiFBQ4nkIexh3p4iA8uB+lYhIeAh3d4dw==', 32, 32, '500141bc'],
['landscape', '3wcOFJpwh4eBh3d4iIePgAj3hw==', 32, 18, '18465aed'],
['portrait', '3/cNFBqBB4iId4d3d4iAjwj4hw==', 18, 32, '56500cc1'],
['solid', 'HoUBBwB4eHeHd3hweId3h3h4B2+Ih4gA', 32, 32, '39cc5c5'],
['square', 'H/gNBxpwh4dwd3eIiHd3iHeHeJ+dcH8I', 32, 32, '740d8afc'],
['tiny', 'HoU9tx4I9wiIh4hwj3CI+AiIcH/494cP', 32, 32, 'f2555987'],
]
describe('thumbhash decode', () => {
it.each(REFERENCE)(
'reproduces the reference pixels for %s',
(_name, hash, w, h, digest) => {
expect(fnv(decode(hash, w, h))).toEqual(digest)
},
)
it.each(REFERENCE)(
'reports the reference natural size for %s',
(_name, hash, w, h) => {
expect(naturalSize(hash)).toEqual({ width: w, height: h })
},
)
it('decodes to any requested grid, not just the natural one', () => {
const [, hash] = REFERENCE[4]
const pixels = decode(hash, 8, 5)
expect(pixels).toHaveLength(8 * 5 * 4)
for (let i = 3; i < pixels.length; i += 4) {
expect(pixels[i]).toBe(255)
}
})
it('carries alpha through for a hash that has it', () => {
const [, hash, w, h] = REFERENCE[0]
const pixels = decode(hash, w, h)
const alphas = new Set()
for (let i = 3; i < pixels.length; i += 4) {
alphas.add(pixels[i])
}
expect(alphas.size).toBeGreaterThan(1)
})
it.each([
['empty', ''],
['too short to hold a header', 'AAAA'],
['not base64', '!!!not-a-thumbhash!!!'],
])('throws on a hash that is %s', (_name, hash) => {
expect(() => decode(hash, 32, 32)).toThrow()
})
})