import { Lut, RGBTuple } from '../typings' const defaultDomainMin: RGBTuple = [0, 0, 0] const defaultDomainMax: RGBTuple = [1, 1, 1] const parseTuple = (...rest: string[]) => { const rgbTuple = rest.map(Number) if (rgbTuple.length !== 3) throw new Error(`RGB tuple has ${rgbTuple.length}`) return rgbTuple as RGBTuple } export const parseCubeLut = (data: Uint8Array): Lut => { const lines = new TextDecoder().decode(data).split('\n') const lut: Partial & { domain: Lut['domain']; data: Lut['data'] } = { domain: { min: defaultDomainMin, max: defaultDomainMax }, data: [] } for (let i = 0; i < lines.length; i++) { try { const line = lines[i] // Skip comments and empty lines if (line[0] === '#' || line === '') continue const [identifier, ...rest] = line.split(' ') switch (identifier) { case 'TITLE': lut.title = line.slice(7, -1) break case 'LUT_3D_SIZE' || 'LUT_1D_SIZE': lut.type = identifier === 'LUT_3D_SIZE' ? '3d' : '1d' if (!rest[0]) throw new Error('Lut file does not specify size') lut.size = Number(rest[0]) break case 'DOMAIN_MIN': lut.domain.min = parseTuple(...rest) break case 'DOMAIN_MAX': lut.domain.max = parseTuple(...rest) break default: // We have data, parse it lut.data.push(parseTuple(identifier, ...rest)) break } } catch (err) { throw new Error(`failed parsing cube lut at line ${i}: ${err}`) } } if (!lut.type) throw new Error('Lut file missing dimension') if (lut.data.length === 0) throw new Error('Lut has no data') return lut as Lut }