@realsee/hdr-toolbox 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/exr.js ADDED
@@ -0,0 +1,291 @@
1
+ import { inflateSync } from "node:zlib";
2
+ const MAGIC = 20000630;
3
+ const PIXEL_TYPE_HALF = 1;
4
+ const PIXEL_TYPE_FLOAT = 2;
5
+ const COMPRESSION_NONE = 0;
6
+ const COMPRESSION_RLE = 1;
7
+ const COMPRESSION_ZIPS = 2;
8
+ const COMPRESSION_ZIP = 3;
9
+ /** ZIP/ZIPS pack 16 scanlines per chunk; ZIPS packs a single scanline. */
10
+ const ZIP_ROWS_PER_CHUNK = 16;
11
+ function halfToFloat(half) {
12
+ const sign = (half & 0x8000) >> 15;
13
+ const exponent = (half & 0x7c00) >> 10;
14
+ const fraction = half & 0x03ff;
15
+ if (exponent === 0) {
16
+ return (sign ? -1 : 1) * 2 ** -14 * (fraction / 1024);
17
+ }
18
+ if (exponent === 0x1f) {
19
+ return fraction ? Number.NaN : (sign ? -1 : 1) * Number.POSITIVE_INFINITY;
20
+ }
21
+ return (sign ? -1 : 1) * 2 ** (exponent - 15) * (1 + fraction / 1024);
22
+ }
23
+ class ByteReader {
24
+ view;
25
+ offset = 0;
26
+ constructor(view) {
27
+ this.view = view;
28
+ }
29
+ uint8() {
30
+ return this.view.getUint8(this.offset++);
31
+ }
32
+ int32() {
33
+ const value = this.view.getInt32(this.offset, true);
34
+ this.offset += 4;
35
+ return value;
36
+ }
37
+ uint32() {
38
+ const value = this.view.getUint32(this.offset, true);
39
+ this.offset += 4;
40
+ return value;
41
+ }
42
+ uint64() {
43
+ const low = this.view.getUint32(this.offset, true);
44
+ const high = this.view.getUint32(this.offset + 4, true);
45
+ this.offset += 8;
46
+ return high * 0x1_0000_0000 + low;
47
+ }
48
+ nullString() {
49
+ let result = "";
50
+ for (let byte = this.uint8(); byte !== 0; byte = this.uint8()) {
51
+ result += String.fromCharCode(byte);
52
+ }
53
+ return result;
54
+ }
55
+ skip(count) {
56
+ this.offset += count;
57
+ }
58
+ get position() {
59
+ return this.offset;
60
+ }
61
+ set position(value) {
62
+ this.offset = value;
63
+ }
64
+ }
65
+ function parseChannels(reader) {
66
+ const channels = [];
67
+ let byteOffset = 0;
68
+ // The channel list ends with an empty channel name (a lone null byte).
69
+ while (true) {
70
+ const name = reader.nullString();
71
+ if (name === "")
72
+ break;
73
+ const pixelType = reader.int32();
74
+ reader.skip(4); // pLinear + reserved
75
+ reader.skip(8); // xSampling + ySampling
76
+ const byteSize = pixelType === PIXEL_TYPE_FLOAT ? 4 : pixelType === PIXEL_TYPE_HALF ? 2 : 0;
77
+ if (byteSize === 0)
78
+ throw new Error(`Unsupported EXR channel pixel type for "${name}": ${pixelType}.`);
79
+ channels.push({ name, pixelType, byteOffset, byteSize });
80
+ byteOffset += byteSize;
81
+ }
82
+ // EXR stores channels alphabetically; byte offsets within a pixel follow that
83
+ // same order, which is exactly how we accumulated them above.
84
+ return channels;
85
+ }
86
+ function decompressChunk(compression, payload, expectedSize) {
87
+ if (compression === COMPRESSION_NONE)
88
+ return payload;
89
+ if (compression === COMPRESSION_ZIP || compression === COMPRESSION_ZIPS) {
90
+ const inflated = inflateSync(payload);
91
+ return reconstructZip(inflated, expectedSize);
92
+ }
93
+ throw new Error(`Unsupported EXR compression type: ${compression}.`);
94
+ }
95
+ /**
96
+ * Undoes the two reversible transforms the EXR ZIP codec applies before
97
+ * deflate: a per-byte delta (predictor) and an interleave of the two byte
98
+ * halves. Mirrors the reference OpenEXR implementation.
99
+ */
100
+ function reconstructZip(source, expectedSize) {
101
+ const buffer = Uint8Array.prototype.slice.call(source, 0, expectedSize);
102
+ for (let index = 1; index < buffer.length; index++) {
103
+ const delta = buffer[index - 1] + buffer[index] - 128;
104
+ buffer[index] = delta & 0xff;
105
+ }
106
+ const result = new Uint8Array(buffer.length);
107
+ const half = Math.floor((buffer.length + 1) / 2);
108
+ let low = 0;
109
+ let high = half;
110
+ let out = 0;
111
+ while (out < result.length) {
112
+ result[out++] = buffer[low++];
113
+ if (out < result.length)
114
+ result[out++] = buffer[high++];
115
+ }
116
+ return result;
117
+ }
118
+ /** Reads a linear RGB EXR into interleaved float RGB, dropping any alpha. */
119
+ export function readExr(buffer) {
120
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
121
+ const reader = new ByteReader(view);
122
+ if (reader.uint32() !== MAGIC)
123
+ throw new Error("Not an OpenEXR file (bad magic number).");
124
+ reader.skip(4); // version + flags; single-part scanline only
125
+ let channels;
126
+ let compression = COMPRESSION_NONE;
127
+ let dataWindow;
128
+ // Header: a sequence of (name,type,size,value) attributes ending at a null byte.
129
+ while (true) {
130
+ const attributeName = reader.nullString();
131
+ if (attributeName === "")
132
+ break;
133
+ reader.nullString(); // attribute type
134
+ const size = reader.int32();
135
+ const next = reader.position + size;
136
+ if (attributeName === "channels") {
137
+ channels = parseChannels(reader);
138
+ }
139
+ else if (attributeName === "compression") {
140
+ compression = reader.uint8();
141
+ }
142
+ else if (attributeName === "dataWindow") {
143
+ dataWindow = {
144
+ xMin: reader.int32(),
145
+ yMin: reader.int32(),
146
+ xMax: reader.int32(),
147
+ yMax: reader.int32(),
148
+ };
149
+ }
150
+ reader.position = next;
151
+ }
152
+ if (!channels || !dataWindow)
153
+ throw new Error("EXR header is missing channels or dataWindow.");
154
+ if (compression === COMPRESSION_RLE)
155
+ throw new Error("RLE-compressed EXR files are not supported.");
156
+ const width = dataWindow.xMax - dataWindow.xMin + 1;
157
+ const height = dataWindow.yMax - dataWindow.yMin + 1;
158
+ if (width <= 0 || height <= 0)
159
+ throw new Error("EXR data window is empty.");
160
+ const channelByName = new Map(channels.map((channel) => [channel.name, channel]));
161
+ const red = channelByName.get("R");
162
+ const green = channelByName.get("G");
163
+ const blue = channelByName.get("B");
164
+ if (!red || !green || !blue)
165
+ throw new Error("EXR file must contain R, G and B channels for HDR conversion.");
166
+ const bytesPerPixel = channels.reduce((sum, channel) => sum + channel.byteSize, 0);
167
+ const rowsPerChunk = compression === COMPRESSION_ZIP ? ZIP_ROWS_PER_CHUNK : 1;
168
+ const chunkCount = Math.ceil(height / rowsPerChunk);
169
+ // Offset table: one uint64 file offset per chunk, in scanline order.
170
+ const chunkOffsets = [];
171
+ for (let index = 0; index < chunkCount; index++)
172
+ chunkOffsets.push(reader.uint64());
173
+ const output = new Float32Array(width * height * 3);
174
+ const readSample = (block, offset, pixelType) => pixelType === PIXEL_TYPE_FLOAT
175
+ ? block.getFloat32(offset, true)
176
+ : halfToFloat(block.getUint16(offset, true));
177
+ for (let chunk = 0; chunk < chunkCount; chunk++) {
178
+ reader.position = chunkOffsets[chunk];
179
+ const firstRow = reader.int32();
180
+ const dataSize = reader.int32();
181
+ const start = reader.position;
182
+ const rowsInChunk = Math.min(rowsPerChunk, height - (firstRow - dataWindow.yMin));
183
+ const expectedSize = rowsInChunk * width * bytesPerPixel;
184
+ const payload = new Uint8Array(buffer.buffer, buffer.byteOffset + start, dataSize);
185
+ const pixels = decompressChunk(compression, payload, expectedSize);
186
+ const block = new DataView(pixels.buffer, pixels.byteOffset, pixels.byteLength);
187
+ for (let row = 0; row < rowsInChunk; row++) {
188
+ const imageRow = firstRow - dataWindow.yMin + row;
189
+ const rowStart = row * width * bytesPerPixel;
190
+ for (let channel of channels) {
191
+ if (channel.name !== "R" && channel.name !== "G" && channel.name !== "B")
192
+ continue;
193
+ const target = channel.name === "R" ? 0 : channel.name === "G" ? 1 : 2;
194
+ let sampleOffset = rowStart + channel.byteOffset * width;
195
+ let pixelIndex = imageRow * width * 3 + target;
196
+ for (let x = 0; x < width; x++) {
197
+ output[pixelIndex] = readSample(block, sampleOffset, channel.pixelType);
198
+ sampleOffset += channel.byteSize;
199
+ pixelIndex += 3;
200
+ }
201
+ }
202
+ }
203
+ }
204
+ return { width, height, data: output };
205
+ }
206
+ function writeAttribute(parts, name, type, value) {
207
+ parts.push(Buffer.from(`${name}\0${type}\0`, "latin1"));
208
+ const size = Buffer.alloc(4);
209
+ size.writeInt32LE(value.length, 0);
210
+ parts.push(size, value);
211
+ }
212
+ /**
213
+ * Writes interleaved linear float RGB as an uncompressed single-part scanline
214
+ * EXR. Channels are emitted alphabetically (B, G, R) as the format requires.
215
+ */
216
+ export function writeExr(image) {
217
+ const { width, height, data } = image;
218
+ if (data.length !== width * height * 3)
219
+ throw new Error("EXR pixel data length does not match width * height * 3.");
220
+ const header = [];
221
+ const magic = Buffer.alloc(8);
222
+ magic.writeUInt32LE(MAGIC, 0);
223
+ magic.writeUInt32LE(2, 4); // version 2, scanline, single-part
224
+ header.push(magic);
225
+ // channels: alphabetical B, G, R, each FLOAT with xSampling=ySampling=1.
226
+ const channelParts = [];
227
+ for (const name of ["B", "G", "R"]) {
228
+ const entry = Buffer.alloc(name.length + 1 + 16);
229
+ entry.write(`${name}\0`, 0, "latin1");
230
+ let p = name.length + 1;
231
+ entry.writeInt32LE(PIXEL_TYPE_FLOAT, p); // pixelType
232
+ p += 4;
233
+ entry.writeInt32LE(0, p); // pLinear + reserved
234
+ p += 4;
235
+ entry.writeInt32LE(1, p); // xSampling
236
+ p += 4;
237
+ entry.writeInt32LE(1, p); // ySampling
238
+ channelParts.push(entry);
239
+ }
240
+ channelParts.push(Buffer.from([0]));
241
+ writeAttribute(header, "channels", "chlist", Buffer.concat(channelParts));
242
+ writeAttribute(header, "compression", "compression", Buffer.from([COMPRESSION_NONE]));
243
+ const window = Buffer.alloc(16);
244
+ window.writeInt32LE(0, 0);
245
+ window.writeInt32LE(0, 4);
246
+ window.writeInt32LE(width - 1, 8);
247
+ window.writeInt32LE(height - 1, 12);
248
+ writeAttribute(header, "dataWindow", "box2i", window);
249
+ writeAttribute(header, "displayWindow", "box2i", window);
250
+ const lineOrder = Buffer.from([0]); // INCREASING_Y
251
+ writeAttribute(header, "lineOrder", "lineOrder", lineOrder);
252
+ const screenWindowCenter = Buffer.alloc(8); // 0,0 floats
253
+ writeAttribute(header, "screenWindowCenter", "v2f", screenWindowCenter);
254
+ const screenWindowWidth = Buffer.alloc(4);
255
+ screenWindowWidth.writeFloatLE(1, 0);
256
+ writeAttribute(header, "screenWindowWidth", "float", screenWindowWidth);
257
+ const pixelAspectRatio = Buffer.alloc(4);
258
+ pixelAspectRatio.writeFloatLE(1, 0);
259
+ writeAttribute(header, "pixelAspectRatio", "float", pixelAspectRatio);
260
+ header.push(Buffer.from([0])); // end of header
261
+ const bytesPerChannelRow = width * 4;
262
+ const scanlineDataSize = bytesPerChannelRow * 3;
263
+ const scanlineRecord = 8 + scanlineDataSize; // y (int) + size (int) + data
264
+ const headerBuffer = Buffer.concat(header);
265
+ const offsetTableSize = height * 8;
266
+ const pixelDataStart = headerBuffer.length + offsetTableSize;
267
+ const offsetTable = Buffer.alloc(offsetTableSize);
268
+ for (let y = 0; y < height; y++) {
269
+ offsetTable.writeBigUInt64LE(BigInt(pixelDataStart + y * scanlineRecord), y * 8);
270
+ }
271
+ const pixelData = Buffer.alloc(height * scanlineRecord);
272
+ for (let y = 0; y < height; y++) {
273
+ const base = y * scanlineRecord;
274
+ pixelData.writeInt32LE(y, base);
275
+ pixelData.writeInt32LE(scanlineDataSize, base + 4);
276
+ const rowStart = base + 8;
277
+ // Channel order within a scanline matches the (alphabetical) channel list.
278
+ for (let channel = 0; channel < 3; channel++) {
279
+ const source = channel === 0 ? 2 : channel === 1 ? 1 : 0; // B,G,R -> data index
280
+ let out = rowStart + channel * bytesPerChannelRow;
281
+ let pixel = y * width * 3 + source;
282
+ for (let x = 0; x < width; x++) {
283
+ pixelData.writeFloatLE(data[pixel], out);
284
+ out += 4;
285
+ pixel += 3;
286
+ }
287
+ }
288
+ }
289
+ return Buffer.concat([headerBuffer, offsetTable, pixelData]);
290
+ }
291
+ //# sourceMappingURL=exr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exr.js","sourceRoot":"","sources":["../src/exr.ts"],"names":[],"mappings":"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"}
package/dist/hdr.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { LinearHdrImage } from "./exr.js";
2
+ /** Reads a Radiance HDR file into interleaved linear float RGB. */
3
+ export declare function readHdr(buffer: Buffer): LinearHdrImage;
4
+ /** Writes interleaved linear float RGB as a new-style RLE Radiance HDR file. */
5
+ export declare function writeHdr(image: LinearHdrImage): Buffer;
6
+ //# sourceMappingURL=hdr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hdr.d.ts","sourceRoot":"","sources":["../src/hdr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAsH/C,mEAAmE;AACnE,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAwBtD;AA6ED,gFAAgF;AAChF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CA6BtD"}
package/dist/hdr.js ADDED
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Minimal Radiance HDR (RGBE, ".hdr"/".pic") reader and writer for linear
3
+ * scanline RGB images.
4
+ *
5
+ * RGBE stores three 8-bit mantissas plus one shared 8-bit exponent per pixel,
6
+ * giving a wide dynamic range in 32 bits. This module reads the flat and
7
+ * new-style (adaptive) RLE encodings and always writes the new-style RLE form,
8
+ * which every Radiance reader understands. Pixels are exchanged as interleaved
9
+ * linear float RGB, matching the EXR codec so both share one conversion path.
10
+ */
11
+ const EXPONENT_BIAS = 128;
12
+ /**
13
+ * Converts one RGBE quadruple to linear float RGB, writing into `out`.
14
+ *
15
+ * With the shared exponent byte `e`, each mantissa byte decodes as
16
+ * `byte * 2^(e - 128 - 8)`, where the -8 undoes the 0-255 mantissa scale. This
17
+ * matches the common three.js/parse-hdr convention (no half-bucket bias), which
18
+ * keeps near-zero channels at zero.
19
+ */
20
+ function rgbeToFloat(r, g, b, e, out, offset) {
21
+ if (e === 0) {
22
+ out[offset] = 0;
23
+ out[offset + 1] = 0;
24
+ out[offset + 2] = 0;
25
+ return;
26
+ }
27
+ const scale = 2 ** (e - (EXPONENT_BIAS + 8));
28
+ out[offset] = r * scale;
29
+ out[offset + 1] = g * scale;
30
+ out[offset + 2] = b * scale;
31
+ }
32
+ /**
33
+ * Converts one linear float RGB pixel to an RGBE quadruple.
34
+ *
35
+ * Mirrors the reference Radiance encoder: decompose the channel maximum as
36
+ * `max = mantissa * 2^exponent` with `mantissa` in [0.5, 1) (i.e. frexp), then
37
+ * scale each channel by `mantissa / max * 256` and store `exponent + 128`.
38
+ */
39
+ function floatToRgbe(r, g, b) {
40
+ const max = Math.max(r, g, b);
41
+ if (max <= 1e-32)
42
+ return [0, 0, 0, 0];
43
+ // frexp exponent: the e with max / 2^e in [0.5, 1).
44
+ const exponent = Math.floor(Math.log2(max)) + 1;
45
+ const factor = 2 ** -exponent * 256;
46
+ // Round (not floor) so the quantisation error is centred, since the decoder
47
+ // does not add a half-bucket bias.
48
+ const clampByte = (value) => {
49
+ const byte = Math.round(value * factor);
50
+ return byte < 0 ? 0 : byte > 255 ? 255 : byte;
51
+ };
52
+ return [clampByte(r), clampByte(g), clampByte(b), exponent + EXPONENT_BIAS];
53
+ }
54
+ function readHeader(buffer) {
55
+ let offset = 0;
56
+ const readLine = () => {
57
+ let line = "";
58
+ while (offset < buffer.length) {
59
+ const byte = buffer[offset++];
60
+ if (byte === 0x0a)
61
+ break;
62
+ line += String.fromCharCode(byte);
63
+ }
64
+ return line;
65
+ };
66
+ const magic = readLine();
67
+ if (!magic.startsWith("#?"))
68
+ throw new Error("Not a Radiance HDR file (missing #? signature).");
69
+ let format;
70
+ // Header lines continue until a blank line separates them from the resolution.
71
+ while (offset < buffer.length) {
72
+ const line = readLine();
73
+ if (line === "")
74
+ break;
75
+ const match = /^FORMAT=(.+)$/.exec(line);
76
+ if (match)
77
+ format = match[1].trim();
78
+ }
79
+ if (format && format !== "32-bit_rle_rgbe")
80
+ throw new Error(`Unsupported Radiance HDR format "${format}" (only 32-bit_rle_rgbe).`);
81
+ const resolution = readLine();
82
+ // Standard orientation is "-Y height +X width"; accept the common variants.
83
+ const match = /([+-][XY])\s+(\d+)\s+([+-][XY])\s+(\d+)/.exec(resolution);
84
+ if (!match)
85
+ throw new Error(`Unsupported Radiance HDR resolution line: "${resolution}".`);
86
+ const [, axis1, size1, axis2, size2] = match;
87
+ let width;
88
+ let height;
89
+ let flipY = false;
90
+ if (axis1.endsWith("Y")) {
91
+ height = Number(size1);
92
+ width = Number(size2);
93
+ flipY = axis1.startsWith("+");
94
+ }
95
+ else {
96
+ width = Number(size1);
97
+ height = Number(size2);
98
+ flipY = axis2.startsWith("+");
99
+ }
100
+ if (axis1.endsWith(axis2.charAt(1)))
101
+ throw new Error("Radiance HDR resolution line must specify one X and one Y axis.");
102
+ return { width, height, flipY, dataStart: offset };
103
+ }
104
+ /** Reads a Radiance HDR file into interleaved linear float RGB. */
105
+ export function readHdr(buffer) {
106
+ const { width, height, flipY, dataStart } = readHeader(buffer);
107
+ const data = new Float32Array(width * height * 3);
108
+ let offset = dataStart;
109
+ const scanline = new Uint8Array(width * 4);
110
+ for (let row = 0; row < height; row++) {
111
+ offset = readScanline(buffer, offset, width, scanline);
112
+ const targetRow = flipY ? height - 1 - row : row;
113
+ let out = targetRow * width * 3;
114
+ for (let x = 0; x < width; x++) {
115
+ rgbeToFloat(scanline[x * 4], scanline[x * 4 + 1], scanline[x * 4 + 2], scanline[x * 4 + 3], data, out);
116
+ out += 3;
117
+ }
118
+ }
119
+ return { width, height, data };
120
+ }
121
+ /**
122
+ * Decodes one scanline into `scanline` as width*4 RGBE bytes and returns the new
123
+ * read offset. Handles new-style adaptive RLE, old-style RLE, and flat pixels.
124
+ */
125
+ function readScanline(buffer, start, width, scanline) {
126
+ let offset = start;
127
+ // New-style RLE scanlines begin with the marker 0x02 0x02 and a 16-bit width.
128
+ const isNewStyle = width >= 8 &&
129
+ width <= 0x7fff &&
130
+ buffer[offset] === 2 &&
131
+ buffer[offset + 1] === 2 &&
132
+ ((buffer[offset + 2] << 8) | buffer[offset + 3]) === width;
133
+ if (!isNewStyle)
134
+ return readFlatOrOldScanline(buffer, offset, width, scanline);
135
+ offset += 4;
136
+ // Each of the four RGBE components is run-length encoded separately.
137
+ for (let channel = 0; channel < 4; channel++) {
138
+ let x = 0;
139
+ while (x < width) {
140
+ let count = buffer[offset++];
141
+ if (count > 128) {
142
+ // A run: the next byte repeats (count - 128) times.
143
+ const value = buffer[offset++];
144
+ count -= 128;
145
+ while (count-- > 0)
146
+ scanline[(x++ << 2) + channel] = value;
147
+ }
148
+ else {
149
+ // A dump: `count` literal bytes follow.
150
+ while (count-- > 0)
151
+ scanline[(x++ << 2) + channel] = buffer[offset++];
152
+ }
153
+ }
154
+ }
155
+ return offset;
156
+ }
157
+ /** Reads a flat (uncompressed) or old-style RLE scanline. */
158
+ function readFlatOrOldScanline(buffer, start, width, scanline) {
159
+ let offset = start;
160
+ let x = 0;
161
+ let repeatShift = 0;
162
+ while (x < width) {
163
+ const r = buffer[offset++];
164
+ const g = buffer[offset++];
165
+ const b = buffer[offset++];
166
+ const e = buffer[offset++];
167
+ // Old-style RLE: an (1,1,1,count) pixel repeats the previous one count<<shift times.
168
+ if (r === 1 && g === 1 && b === 1 && x > 0) {
169
+ let count = e << repeatShift;
170
+ const base = (x - 1) * 4;
171
+ while (count-- > 0 && x < width) {
172
+ scanline[x * 4] = scanline[base];
173
+ scanline[x * 4 + 1] = scanline[base + 1];
174
+ scanline[x * 4 + 2] = scanline[base + 2];
175
+ scanline[x * 4 + 3] = scanline[base + 3];
176
+ x++;
177
+ }
178
+ repeatShift += 8;
179
+ }
180
+ else {
181
+ scanline[x * 4] = r;
182
+ scanline[x * 4 + 1] = g;
183
+ scanline[x * 4 + 2] = b;
184
+ scanline[x * 4 + 3] = e;
185
+ x++;
186
+ repeatShift = 0;
187
+ }
188
+ }
189
+ return offset;
190
+ }
191
+ /** Writes interleaved linear float RGB as a new-style RLE Radiance HDR file. */
192
+ export function writeHdr(image) {
193
+ const { width, height, data } = image;
194
+ if (data.length !== width * height * 3)
195
+ throw new Error("HDR pixel data length does not match width * height * 3.");
196
+ const header = Buffer.from(`#?RADIANCE\nFORMAT=32-bit_rle_rgbe\n\n-Y ${height} +X ${width}\n`, "latin1");
197
+ const chunks = [header];
198
+ const rgbe = new Uint8Array(width * 4);
199
+ // New-style RLE requires widths in [8, 32767]; otherwise write flat pixels.
200
+ const useRle = width >= 8 && width <= 0x7fff;
201
+ for (let row = 0; row < height; row++) {
202
+ let source = row * width * 3;
203
+ for (let x = 0; x < width; x++) {
204
+ const [r, g, b, e] = floatToRgbe(data[source], data[source + 1], data[source + 2]);
205
+ rgbe[x * 4] = r;
206
+ rgbe[x * 4 + 1] = g;
207
+ rgbe[x * 4 + 2] = b;
208
+ rgbe[x * 4 + 3] = e;
209
+ source += 3;
210
+ }
211
+ chunks.push(useRle ? encodeRleScanline(rgbe, width) : Buffer.from(rgbe.slice()));
212
+ }
213
+ return Buffer.concat(chunks);
214
+ }
215
+ /** Length of the run starting at `index` for one interleaved RGBE channel. */
216
+ function runLengthAt(rgbe, channel, index, width) {
217
+ const value = rgbe[index * 4 + channel];
218
+ let length = 1;
219
+ while (index + length < width && rgbe[(index + length) * 4 + channel] === value)
220
+ length++;
221
+ return length;
222
+ }
223
+ /** Encodes one width*4 RGBE scanline using new-style adaptive RLE. */
224
+ function encodeRleScanline(rgbe, width) {
225
+ const out = [2, 2, (width >> 8) & 0xff, width & 0xff];
226
+ // Runs of >= 4 bytes are worth encoding; shorter spans go in literal dumps.
227
+ const minRun = 4;
228
+ for (let channel = 0; channel < 4; channel++) {
229
+ let x = 0;
230
+ while (x < width) {
231
+ // Scan forward over bytes that do not start a worthwhile run; those bytes
232
+ // become a literal dump. `runStart` always advances, so this terminates.
233
+ let runStart = x;
234
+ let run = 0;
235
+ while (runStart < width) {
236
+ run = runLengthAt(rgbe, channel, runStart, width);
237
+ if (run >= minRun)
238
+ break;
239
+ runStart += run;
240
+ }
241
+ // Emit the literal span [x, runStart) in chunks of at most 128 bytes.
242
+ while (x < runStart) {
243
+ const dumpLength = Math.min(128, runStart - x);
244
+ out.push(dumpLength);
245
+ for (let i = 0; i < dumpLength; i++)
246
+ out.push(rgbe[(x + i) * 4 + channel]);
247
+ x += dumpLength;
248
+ }
249
+ // Emit the run [runStart, runStart + run) in chunks of at most 127 repeats.
250
+ if (runStart < width) {
251
+ const value = rgbe[runStart * 4 + channel];
252
+ while (run > 0) {
253
+ const chunk = Math.min(127, run);
254
+ out.push(128 + chunk, value);
255
+ run -= chunk;
256
+ x += chunk;
257
+ }
258
+ }
259
+ }
260
+ }
261
+ return Buffer.from(out);
262
+ }
263
+ //# sourceMappingURL=hdr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hdr.js","sourceRoot":"","sources":["../src/hdr.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /** Bitmap formats accepted for SDR/gain-map input and produced on extract output. */
2
+ export declare const supportedImageFormats: readonly ["jpeg", "jpg", "png", "avif"];
3
+ export type ImageFormat = (typeof supportedImageFormats)[number];
4
+ /** Canonical form used internally; "jpg" is an alias of "jpeg". */
5
+ export type CanonicalImageFormat = "jpeg" | "png" | "avif";
6
+ export declare function canonicalizeImageFormat(format: ImageFormat): CanonicalImageFormat;
7
+ export declare function isSupportedImageFormat(value: string): value is ImageFormat;
8
+ /**
9
+ * Resolves the desired output format for extraction, preferring an explicit
10
+ * `--format` flag, then the output path extension, then defaulting to JPEG.
11
+ */
12
+ export declare function resolveOutputFormat(explicit: string | undefined, outputPath: string): CanonicalImageFormat;
13
+ /**
14
+ * Detects the bitmap format of a buffer via sharp. Returns the canonical format
15
+ * when it is one we support, otherwise throws so callers reject unknown inputs.
16
+ * AVIF is reported by sharp as HEIF with "av1" compression.
17
+ */
18
+ export declare function detectImageFormat(buffer: Buffer): Promise<CanonicalImageFormat>;
19
+ /**
20
+ * Ensures a buffer is JPEG, transcoding from PNG/AVIF when needed. JPEG inputs
21
+ * are returned untouched so Ultra HDR composition stays byte-for-byte lossless
22
+ * for the common JPEG-in path. The ICC profile, if present, is preserved.
23
+ */
24
+ export declare function ensureJpegBuffer(buffer: Buffer): Promise<Buffer>;
25
+ /**
26
+ * Transcodes an extracted JPEG subimage to the requested output format. JPEG
27
+ * output is a passthrough of the original bytes to preserve the exact subimage.
28
+ */
29
+ export declare function encodeImage(jpeg: Buffer, format: CanonicalImageFormat): Promise<Buffer>;
30
+ //# sourceMappingURL=image-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-format.d.ts","sourceRoot":"","sources":["../src/image-format.ts"],"names":[],"mappings":"AAGA,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,yCAA0C,CAAC;AAE7E,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,mEAAmE;AACnE,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAO3D,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,WAAW,GAAG,oBAAoB,CAEjF;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,WAAW,CAE1E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,UAAU,EAAE,MAAM,GACjB,oBAAoB,CAWtB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAQrF;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGtE;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAK7F"}