cross-image 0.1.4 → 0.2.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.
@@ -51,6 +51,22 @@ class HuffmanTable {
51
51
  this.singleSymbol = symbol;
52
52
  return;
53
53
  }
54
+ // Build Huffman tree
55
+ // Note: WebP uses LSB-first bit packing for the bitstream, but Huffman codes
56
+ // are typically described MSB-first. However, the spec says:
57
+ // "The bits of the code are read from the stream LSB first."
58
+ // This means if code is 01 (binary), we read 1 then 0?
59
+ // Actually, standard canonical Huffman codes are read MSB to LSB from the stream.
60
+ // But WebP bit reader reads LSB to MSB from bytes.
61
+ // Let's check the spec carefully.
62
+ // "The bits of the code are read from the stream LSB first."
63
+ // This usually means the bit reader returns bits in order.
64
+ // If we have code 0x2 (binary 10) with length 2.
65
+ // If we write it MSB first: 1, then 0.
66
+ // If we read it: readBits(1) -> 1, readBits(1) -> 0.
67
+ // This matches how we build the tree (left=0, right=1).
68
+ // Wait, addCode uses (code >> i) & 1. This is MSB first.
69
+ // So we expect the first bit read to be the MSB of the code.
54
70
  let node = this.root;
55
71
  for (let i = codeLength - 1; i >= 0; i--) {
56
72
  const bit = (code >> i) & 1;
@@ -77,6 +93,7 @@ class HuffmanTable {
77
93
  const bit = reader.readBits(1);
78
94
  node = bit === 0 ? node.left : node.right;
79
95
  if (!node) {
96
+ // console.log("Invalid Huffman code - walked off tree");
80
97
  throw new Error("Invalid Huffman code");
81
98
  }
82
99
  }
@@ -111,21 +128,21 @@ class BitReader {
111
128
  });
112
129
  this.data = data;
113
130
  this.pos = offset;
114
- this.bitPos = 0;
131
+ this.bitPos = 8; // Start at 8 to trigger first byte read
115
132
  this.value = 0;
116
133
  }
117
134
  readBits(numBits) {
118
135
  let result = 0;
119
136
  for (let i = 0; i < numBits; i++) {
120
- if (this.bitPos === 0) {
137
+ if (this.bitPos === 8) {
121
138
  if (this.pos >= this.data.length) {
122
139
  throw new Error("Unexpected end of data");
123
140
  }
124
141
  this.value = this.data[this.pos++];
125
- this.bitPos = 8;
142
+ this.bitPos = 0;
126
143
  }
127
- result |= ((this.value >> (this.bitPos - 1)) & 1) << i;
128
- this.bitPos--;
144
+ result |= ((this.value >> this.bitPos) & 1) << i;
145
+ this.bitPos++;
129
146
  }
130
147
  return result;
131
148
  }
@@ -135,8 +152,8 @@ class BitReader {
135
152
  // Read bytes aligned to byte boundary
136
153
  readBytes(count) {
137
154
  // Align to byte boundary
138
- if (this.bitPos !== 0 && this.bitPos !== 8) {
139
- this.bitPos = 0;
155
+ if (this.bitPos !== 8) {
156
+ this.bitPos = 8; // Skip remaining bits in current byte
140
157
  }
141
158
  if (this.pos + count > this.data.length) {
142
159
  throw new Error("Unexpected end of data");
@@ -334,8 +351,6 @@ export class WebPDecoder {
334
351
  }
335
352
  // Read the main Huffman codes
336
353
  // There are 5 Huffman code groups: green, red, blue, alpha, distance
337
- // But we read 4 + optional distance code
338
- const numCodeGroups = reader.readBits(4) + 4;
339
354
  const tables = {
340
355
  green: new HuffmanTable(),
341
356
  red: new HuffmanTable(),
@@ -350,7 +365,7 @@ export class WebPDecoder {
350
365
  tables.alpha,
351
366
  tables.distance,
352
367
  ];
353
- for (let i = 0; i < numCodeGroups && i < 5; i++) {
368
+ for (let i = 0; i < 5; i++) {
354
369
  this.readHuffmanCode(reader, tableArray[i], useColorCache, colorCacheBits, i === 0);
355
370
  }
356
371
  return tables;
@@ -364,8 +379,8 @@ export class WebPDecoder {
364
379
  const symbols = [];
365
380
  for (let i = 0; i < numSymbols; i++) {
366
381
  const symbolBits = isFirstEightBits
367
- ? 1 + reader.readBits(7)
368
- : reader.readBits(8);
382
+ ? reader.readBits(8)
383
+ : reader.readBits(1);
369
384
  symbols.push(symbolBits);
370
385
  }
371
386
  // Build simple Huffman table
@@ -417,6 +432,12 @@ export class WebPDecoder {
417
432
  for (let i = 0; i < numCodeLengthCodes; i++) {
418
433
  codeLengthCodeLengths[codeLengthCodeOrder[i]] = reader.readBits(3);
419
434
  }
435
+ // Read max_symbol (trimmed length indicator)
436
+ // If 1, we read n_bit and then n_bit bits for max_symbol?
437
+ // Subagent said "write_trimmed_length".
438
+ // If 0, we don't trim.
439
+ // We just read 1 bit and ignore it for now (assuming 0).
440
+ const _trimmed = reader.readBits(1);
420
441
  // Build code length Huffman table
421
442
  const codeLengthTable = new HuffmanTable();
422
443
  this.buildHuffmanTable(codeLengthTable, codeLengthCodeLengths);
@@ -451,6 +472,20 @@ export class WebPDecoder {
451
472
  return codeLengths;
452
473
  }
453
474
  buildHuffmanTable(table, codeLengths) {
475
+ // Check for single symbol optimization (VP8L specific)
476
+ let nonZeroCount = 0;
477
+ let singleSymbol = -1;
478
+ for (let i = 0; i < codeLengths.length; i++) {
479
+ if (codeLengths[i] > 0) {
480
+ nonZeroCount++;
481
+ singleSymbol = i;
482
+ }
483
+ }
484
+ if (nonZeroCount === 1) {
485
+ // If only one symbol, it has 0 length in the bitstream
486
+ table.addCode(singleSymbol, 0, 0);
487
+ return;
488
+ }
454
489
  // Build canonical Huffman codes
455
490
  const maxCodeLength = Math.max(...codeLengths);
456
491
  const lengthCounts = new Array(maxCodeLength + 1).fill(0);
@@ -55,30 +55,30 @@ class BitWriter {
55
55
  });
56
56
  }
57
57
  writeBits(value, numBits) {
58
- // Pack bits to match how the decoder reads them
59
- // The decoder reads from MSB to LSB of each byte
60
- // So we write from MSB down as well
58
+ // Pack bits LSB first (standard WebP/VP8L order)
61
59
  for (let i = 0; i < numBits; i++) {
62
60
  const bit = (value >> i) & 1;
63
- // Write bit at the current position (counting from MSB)
64
- // bitCount represents how many bits we've written
65
- // Position in current byte = 7 - (bitCount % 8)
66
- if (this.bitCount % 8 === 0) {
67
- this.bits = 0; // Start new byte
61
+ // If we've filled the current byte, push it and start a new one
62
+ if (this.bitCount > 0 && this.bitCount % 8 === 0) {
63
+ this.bytes.push(this.bits);
64
+ this.bits = 0;
68
65
  }
69
- const bitPos = 7 - (this.bitCount % 8);
66
+ const bitPos = this.bitCount % 8;
70
67
  this.bits |= bit << bitPos;
71
68
  this.bitCount++;
72
- if (this.bitCount % 8 === 0) {
73
- this.bytes.push(this.bits);
74
- }
75
69
  }
76
70
  }
77
71
  flush() {
78
72
  if (this.bitCount % 8 !== 0) {
79
73
  this.bytes.push(this.bits);
80
74
  this.bits = 0;
81
- this.bitCount = 0;
75
+ // Do not reset bitCount here as it tracks total bits written
76
+ }
77
+ else if (this.bitCount > 0 && this.bytes.length * 8 < this.bitCount) {
78
+ // Edge case: if we just finished a byte but haven't pushed it yet
79
+ // (The loop pushes at the START of the next bit, so we might have a full byte pending)
80
+ this.bytes.push(this.bits);
81
+ this.bits = 0;
82
82
  }
83
83
  }
84
84
  getBytes() {
@@ -241,10 +241,6 @@ export class WebPEncoder {
241
241
  writer.writeBits(0, 1);
242
242
  // No meta Huffman codes
243
243
  writer.writeBits(0, 1);
244
- // Number of code groups: Always 5 (green, red, blue, alpha, distance)
245
- // Even without LZ77, we must provide all 5 Huffman codes
246
- const numCodeGroups = 5;
247
- writer.writeBits(numCodeGroups - 4, 4); // 1 means 5 groups
248
244
  // Apply quantization if quality < 100
249
245
  const encodingData = this.quantizeImageData();
250
246
  // Collect symbol frequencies for each channel
@@ -353,13 +349,13 @@ export class WebPEncoder {
353
349
  if (symbols.length === 1) {
354
350
  // Single symbol
355
351
  writer.writeBits(0, 1); // num_symbols = 1 (0 + 1)
356
- writer.writeBits(0, 1); // is_first_8bits = 0
352
+ writer.writeBits(1, 1); // is_first_8bits = 1 (use 8 bits)
357
353
  writer.writeBits(symbols[0], 8); // symbol
358
354
  }
359
355
  else if (symbols.length === 2) {
360
356
  // Two symbols
361
357
  writer.writeBits(1, 1); // num_symbols = 2 (1 + 1)
362
- writer.writeBits(0, 1); // is_first_8bits = 0
358
+ writer.writeBits(1, 1); // is_first_8bits = 1 (use 8 bits)
363
359
  writer.writeBits(symbols[0], 8); // first symbol
364
360
  writer.writeBits(symbols[1], 8); // second symbol
365
361
  }
@@ -378,9 +374,11 @@ export class WebPEncoder {
378
374
  const symbols = Array.from(frequencies.keys()).sort((a, b) => a - b);
379
375
  if (symbols.length === 0)
380
376
  return codeLengths;
381
- // For a single symbol, use code length 0
377
+ // For a single symbol, use code length 1
378
+ // (Canonical Huffman codes require length >= 1)
382
379
  if (symbols.length === 1) {
383
- codeLengths[symbols[0]] = 0;
380
+ // console.log(`Single symbol ${symbols[0]}, forcing length 1`);
381
+ codeLengths[symbols[0]] = 1;
384
382
  return codeLengths;
385
383
  }
386
384
  // For two symbols, use code length 1 for both
@@ -389,27 +387,64 @@ export class WebPEncoder {
389
387
  codeLengths[symbols[1]] = 1;
390
388
  return codeLengths;
391
389
  }
392
- const nodes = symbols.map((symbol) => ({
390
+ let nodes = symbols.map((symbol) => ({
393
391
  freq: frequencies.get(symbol),
394
392
  symbol,
395
393
  }));
396
- // Build tree by repeatedly combining two smallest nodes
397
- while (nodes.length > 1) {
398
- // Sort by frequency (smallest first)
399
- nodes.sort((a, b) => a.freq - b.freq);
400
- // Take two smallest nodes
401
- const left = nodes.shift();
402
- const right = nodes.shift();
403
- // Create parent node
404
- const parent = {
405
- freq: left.freq + right.freq,
406
- left,
407
- right,
408
- };
409
- nodes.push(parent);
394
+ // Helper to build tree
395
+ const buildTree = (leafs) => {
396
+ const queue = [...leafs];
397
+ while (queue.length > 1) {
398
+ queue.sort((a, b) => a.freq - b.freq);
399
+ const left = queue.shift();
400
+ const right = queue.shift();
401
+ queue.push({
402
+ freq: left.freq + right.freq,
403
+ left,
404
+ right,
405
+ });
406
+ }
407
+ return queue[0];
408
+ };
409
+ let root = buildTree(nodes);
410
+ // Check max depth
411
+ let maxDepth = 0;
412
+ const checkDepth = (node, depth) => {
413
+ if (node.symbol !== undefined) {
414
+ maxDepth = Math.max(maxDepth, depth);
415
+ }
416
+ else {
417
+ if (node.left)
418
+ checkDepth(node.left, depth + 1);
419
+ if (node.right)
420
+ checkDepth(node.right, depth + 1);
421
+ }
422
+ };
423
+ checkDepth(root, 0);
424
+ // If tree is too deep, flatten frequencies and rebuild
425
+ let attempts = 0;
426
+ while (maxDepth > maxCodeLength && attempts < 5) {
427
+ // console.log(`Tree too deep (${maxDepth} > ${maxCodeLength}), flattening...`);
428
+ attempts++;
429
+ // Add bias to frequencies to flatten the tree
430
+ // Increase bias with each attempt
431
+ const bias = (Math.ceil(root.freq / (symbols.length * 2)) || 1) *
432
+ attempts;
433
+ nodes = symbols.map((symbol) => ({
434
+ freq: frequencies.get(symbol) + bias,
435
+ symbol,
436
+ }));
437
+ root = buildTree(nodes);
438
+ // Re-check depth
439
+ maxDepth = 0;
440
+ checkDepth(root, 0);
441
+ }
442
+ if (maxDepth > maxCodeLength) {
443
+ console.warn(`Failed to reduce Huffman tree depth to ${maxCodeLength} (current: ${maxDepth})`);
444
+ // Force hard limit by sorting and assigning lengths?
445
+ // For now, let's just see if this is happening.
410
446
  }
411
447
  // Calculate code lengths by traversing tree (iterative to avoid deep recursion)
412
- const root = nodes[0];
413
448
  const stack = [{
414
449
  node: root,
415
450
  depth: 0,
@@ -417,6 +452,7 @@ export class WebPEncoder {
417
452
  while (stack.length > 0) {
418
453
  const { node, depth } = stack.pop();
419
454
  if (node.symbol !== undefined) {
455
+ // Clamp depth to maxCodeLength (should be safe now with flattening heuristic)
420
456
  codeLengths[node.symbol] = Math.min(depth, maxCodeLength);
421
457
  }
422
458
  else {
@@ -589,13 +625,34 @@ export class WebPEncoder {
589
625
  }
590
626
  numCodeLengthCodes = Math.max(4, numCodeLengthCodes);
591
627
  // Write number of code length codes
628
+ // console.log(`Complex Huffman: numCodeLengthCodes=${numCodeLengthCodes}, rleEncoded.length=${rleEncoded.length}`);
592
629
  writer.writeBits(numCodeLengthCodes - 4, 4);
593
630
  // Write code length code lengths
594
631
  for (let i = 0; i < numCodeLengthCodes; i++) {
595
632
  writer.writeBits(codeLengthCodeLengths[codeLengthCodeOrder[i]], 3);
596
633
  }
634
+ // Write max_symbol is encoded? No, it's write_trimmed_length
635
+ // VP8L spec says: "int max_symbol is read."
636
+ // Wait, subagent said "write_trimmed_length".
637
+ // Let's check the spec or libwebp source if possible.
638
+ // But assuming subagent is correct:
639
+ writer.writeBits(0, 1); // write_trimmed_length = 0 (no trimming)
597
640
  // Build canonical codes for code lengths
598
641
  const codeLengthCodes = this.buildCanonicalCodes(codeLengthCodeLengths);
642
+ // Check for single symbol optimization (VP8L specific)
643
+ let nonZeroCount = 0;
644
+ for (const len of codeLengthCodeLengths) {
645
+ if (len > 0)
646
+ nonZeroCount++;
647
+ }
648
+ if (nonZeroCount === 1) {
649
+ // If only one symbol is used in the code length alphabet,
650
+ // we don't write any bits for the code itself in the RLE stream.
651
+ // The symbol is implicit because it's the only one with non-zero length in the header.
652
+ for (const [_symbol, info] of codeLengthCodes) {
653
+ info.length = 0;
654
+ }
655
+ }
599
656
  // Write RLE-encoded code lengths using code length codes
600
657
  for (let i = 0; i < rleEncoded.length; i++) {
601
658
  const code = rleEncoded[i];
@@ -605,8 +662,9 @@ export class WebPEncoder {
605
662
  }
606
663
  // Write the Huffman code bits from MSB to LSB
607
664
  // This matches how the decoder's addCode builds the tree
608
- for (let bit = huffCode.length - 1; bit >= 0; bit--) {
609
- writer.writeBits((huffCode.code >> bit) & 1, 1);
665
+ // (First bit written is MSB, which corresponds to top of tree)
666
+ for (let i = huffCode.length - 1; i >= 0; i--) {
667
+ writer.writeBits((huffCode.code >> i) & 1, 1);
610
668
  }
611
669
  // Write extra bits for special codes
612
670
  if (code === 16) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cross-image",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "A pure JavaScript, dependency-free, cross-runtime image processing library for Deno, Node.js, and Bun.",
5
5
  "keywords": [
6
6
  "image",
@@ -11,6 +11,9 @@
11
11
  "gif",
12
12
  "tiff",
13
13
  "bmp",
14
+ "dng",
15
+ "pam",
16
+ "pcx",
14
17
  "cross-runtime",
15
18
  "deno",
16
19
  "node",
package/script/mod.d.ts CHANGED
@@ -2,23 +2,45 @@
2
2
  * @module @cross/image
3
3
  *
4
4
  * A pure JavaScript, dependency-free, cross-runtime image processing library.
5
- * Supports reading, resizing, and saving common image formats (PNG, JPEG, WebP, GIF, TIFF, BMP, RAW).
5
+ * Supports decoding, resizing, and encoding common image formats (PNG, JPEG, WebP, GIF, TIFF, BMP, DNG, PAM, PCX).
6
+ * Includes image processing capabilities like compositing, level adjustments, and pixel manipulation.
6
7
  *
7
8
  * @example
8
9
  * ```ts
9
10
  * import { Image } from "@cross/image";
10
11
  *
11
- * // Read an image
12
+ * // Decode an image
12
13
  * const data = await Deno.readFile("input.png");
13
- * const image = await Image.read(data);
14
+ * const image = await Image.decode(data);
14
15
  *
15
- * // Resize it
16
- * image.resize({ width: 200, height: 200 });
16
+ * // Apply image processing
17
+ * image
18
+ * .resize({ width: 200, height: 200 })
19
+ * .brightness(0.1)
20
+ * .contrast(0.2);
17
21
  *
18
- * // Save as different format
19
- * const output = await image.save("jpeg");
22
+ * // Encode as different format
23
+ * const output = await image.encode("jpeg");
20
24
  * await Deno.writeFile("output.jpg", output);
21
25
  * ```
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { Image } from "@cross/image";
30
+ *
31
+ * // Create a blank canvas
32
+ * const canvas = Image.create(400, 300, 255, 255, 255);
33
+ *
34
+ * // Draw on it
35
+ * canvas.fillRect(50, 50, 100, 100, 255, 0, 0, 255);
36
+ *
37
+ * // Load and composite another image
38
+ * const overlay = await Image.decode(await Deno.readFile("logo.png"));
39
+ * canvas.composite(overlay, 10, 10, 0.8);
40
+ *
41
+ * // Save the result
42
+ * await Deno.writeFile("result.png", await canvas.encode("png"));
43
+ * ```
22
44
  */
23
45
  export { Image } from "./src/image.js";
24
46
  export type { ASCIIOptions, FrameMetadata, ImageData, ImageFormat, ImageFrame, ImageMetadata, MultiFrameImageData, ResizeOptions, WebPEncodeOptions, } from "./src/types.js";
@@ -28,6 +50,8 @@ export { WebPFormat } from "./src/formats/webp.js";
28
50
  export { GIFFormat } from "./src/formats/gif.js";
29
51
  export { type TIFFEncodeOptions, TIFFFormat } from "./src/formats/tiff.js";
30
52
  export { BMPFormat } from "./src/formats/bmp.js";
31
- export { RAWFormat } from "./src/formats/raw.js";
53
+ export { DNGFormat } from "./src/formats/dng.js";
54
+ export { PAMFormat } from "./src/formats/pam.js";
55
+ export { PCXFormat } from "./src/formats/pcx.js";
32
56
  export { ASCIIFormat } from "./src/formats/ascii.js";
33
57
  //# sourceMappingURL=mod.d.ts.map
package/script/mod.js CHANGED
@@ -3,26 +3,48 @@
3
3
  * @module @cross/image
4
4
  *
5
5
  * A pure JavaScript, dependency-free, cross-runtime image processing library.
6
- * Supports reading, resizing, and saving common image formats (PNG, JPEG, WebP, GIF, TIFF, BMP, RAW).
6
+ * Supports decoding, resizing, and encoding common image formats (PNG, JPEG, WebP, GIF, TIFF, BMP, DNG, PAM, PCX).
7
+ * Includes image processing capabilities like compositing, level adjustments, and pixel manipulation.
7
8
  *
8
9
  * @example
9
10
  * ```ts
10
11
  * import { Image } from "@cross/image";
11
12
  *
12
- * // Read an image
13
+ * // Decode an image
13
14
  * const data = await Deno.readFile("input.png");
14
- * const image = await Image.read(data);
15
+ * const image = await Image.decode(data);
15
16
  *
16
- * // Resize it
17
- * image.resize({ width: 200, height: 200 });
17
+ * // Apply image processing
18
+ * image
19
+ * .resize({ width: 200, height: 200 })
20
+ * .brightness(0.1)
21
+ * .contrast(0.2);
18
22
  *
19
- * // Save as different format
20
- * const output = await image.save("jpeg");
23
+ * // Encode as different format
24
+ * const output = await image.encode("jpeg");
21
25
  * await Deno.writeFile("output.jpg", output);
22
26
  * ```
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { Image } from "@cross/image";
31
+ *
32
+ * // Create a blank canvas
33
+ * const canvas = Image.create(400, 300, 255, 255, 255);
34
+ *
35
+ * // Draw on it
36
+ * canvas.fillRect(50, 50, 100, 100, 255, 0, 0, 255);
37
+ *
38
+ * // Load and composite another image
39
+ * const overlay = await Image.decode(await Deno.readFile("logo.png"));
40
+ * canvas.composite(overlay, 10, 10, 0.8);
41
+ *
42
+ * // Save the result
43
+ * await Deno.writeFile("result.png", await canvas.encode("png"));
44
+ * ```
23
45
  */
24
46
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.ASCIIFormat = exports.RAWFormat = exports.BMPFormat = exports.TIFFFormat = exports.GIFFormat = exports.WebPFormat = exports.JPEGFormat = exports.PNGFormat = exports.Image = void 0;
47
+ exports.ASCIIFormat = exports.PCXFormat = exports.PAMFormat = exports.DNGFormat = exports.BMPFormat = exports.TIFFFormat = exports.GIFFormat = exports.WebPFormat = exports.JPEGFormat = exports.PNGFormat = exports.Image = void 0;
26
48
  var image_js_1 = require("./src/image.js");
27
49
  Object.defineProperty(exports, "Image", { enumerable: true, get: function () { return image_js_1.Image; } });
28
50
  var png_js_1 = require("./src/formats/png.js");
@@ -37,7 +59,11 @@ var tiff_js_1 = require("./src/formats/tiff.js");
37
59
  Object.defineProperty(exports, "TIFFFormat", { enumerable: true, get: function () { return tiff_js_1.TIFFFormat; } });
38
60
  var bmp_js_1 = require("./src/formats/bmp.js");
39
61
  Object.defineProperty(exports, "BMPFormat", { enumerable: true, get: function () { return bmp_js_1.BMPFormat; } });
40
- var raw_js_1 = require("./src/formats/raw.js");
41
- Object.defineProperty(exports, "RAWFormat", { enumerable: true, get: function () { return raw_js_1.RAWFormat; } });
62
+ var dng_js_1 = require("./src/formats/dng.js");
63
+ Object.defineProperty(exports, "DNGFormat", { enumerable: true, get: function () { return dng_js_1.DNGFormat; } });
64
+ var pam_js_1 = require("./src/formats/pam.js");
65
+ Object.defineProperty(exports, "PAMFormat", { enumerable: true, get: function () { return pam_js_1.PAMFormat; } });
66
+ var pcx_js_1 = require("./src/formats/pcx.js");
67
+ Object.defineProperty(exports, "PCXFormat", { enumerable: true, get: function () { return pcx_js_1.PCXFormat; } });
42
68
  var ascii_js_1 = require("./src/formats/ascii.js");
43
69
  Object.defineProperty(exports, "ASCIIFormat", { enumerable: true, get: function () { return ascii_js_1.ASCIIFormat; } });
@@ -0,0 +1,27 @@
1
+ import type { ImageData } from "../types.js";
2
+ import { TIFFFormat } from "./tiff.js";
3
+ /**
4
+ * DNG format handler
5
+ * Implements a basic Linear DNG (Digital Negative) writer.
6
+ * DNG is based on TIFF/EP. This implementation creates a valid DNG
7
+ * containing uncompressed linear RGB data (demosaiced).
8
+ */
9
+ export declare class DNGFormat extends TIFFFormat {
10
+ /** Format name identifier */
11
+ readonly name = "dng";
12
+ /** MIME type for DNG images */
13
+ readonly mimeType = "image/x-adobe-dng";
14
+ /**
15
+ * Check if the given data is a DNG image
16
+ * @param data Raw image data to check
17
+ * @returns true if data has DNG signature (TIFF signature + DNGVersion tag)
18
+ */
19
+ canDecode(data: Uint8Array): boolean;
20
+ /**
21
+ * Encode RGBA image data to DNG format (Linear DNG)
22
+ * @param imageData Image data to encode
23
+ * @returns Encoded DNG image bytes
24
+ */
25
+ encode(imageData: ImageData): Promise<Uint8Array>;
26
+ }
27
+ //# sourceMappingURL=dng.d.ts.map