cross-image 0.4.0 → 0.4.1

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.
Files changed (50) hide show
  1. package/README.md +109 -1
  2. package/esm/mod.d.ts +3 -1
  3. package/esm/mod.js +2 -0
  4. package/esm/src/formats/apng.d.ts +5 -3
  5. package/esm/src/formats/apng.js +11 -4
  6. package/esm/src/formats/avif.d.ts +2 -2
  7. package/esm/src/formats/avif.js +11 -1
  8. package/esm/src/formats/gif.d.ts +3 -3
  9. package/esm/src/formats/gif.js +4 -4
  10. package/esm/src/formats/heic.d.ts +2 -2
  11. package/esm/src/formats/heic.js +11 -1
  12. package/esm/src/formats/png.d.ts +3 -2
  13. package/esm/src/formats/png.js +8 -2
  14. package/esm/src/formats/png_base.d.ts +42 -1
  15. package/esm/src/formats/png_base.js +198 -5
  16. package/esm/src/formats/tiff.js +76 -6
  17. package/esm/src/image.d.ts +15 -0
  18. package/esm/src/image.js +29 -1
  19. package/esm/src/types.d.ts +66 -0
  20. package/esm/src/utils/base64.d.ts +32 -0
  21. package/esm/src/utils/base64.js +173 -0
  22. package/esm/src/utils/gif_encoder.d.ts +3 -1
  23. package/esm/src/utils/gif_encoder.js +4 -2
  24. package/esm/src/utils/image_processing.d.ts +31 -0
  25. package/esm/src/utils/image_processing.js +88 -0
  26. package/package.json +1 -1
  27. package/script/mod.d.ts +3 -1
  28. package/script/mod.js +11 -1
  29. package/script/src/formats/apng.d.ts +5 -3
  30. package/script/src/formats/apng.js +11 -4
  31. package/script/src/formats/avif.d.ts +2 -2
  32. package/script/src/formats/avif.js +11 -1
  33. package/script/src/formats/gif.d.ts +3 -3
  34. package/script/src/formats/gif.js +4 -4
  35. package/script/src/formats/heic.d.ts +2 -2
  36. package/script/src/formats/heic.js +11 -1
  37. package/script/src/formats/png.d.ts +3 -2
  38. package/script/src/formats/png.js +8 -2
  39. package/script/src/formats/png_base.d.ts +42 -1
  40. package/script/src/formats/png_base.js +198 -5
  41. package/script/src/formats/tiff.js +76 -6
  42. package/script/src/image.d.ts +15 -0
  43. package/script/src/image.js +28 -0
  44. package/script/src/types.d.ts +66 -0
  45. package/script/src/utils/base64.d.ts +32 -0
  46. package/script/src/utils/base64.js +179 -0
  47. package/script/src/utils/gif_encoder.d.ts +3 -1
  48. package/script/src/utils/gif_encoder.js +4 -2
  49. package/script/src/utils/image_processing.d.ts +31 -0
  50. package/script/src/utils/image_processing.js +92 -0
@@ -43,7 +43,48 @@ export declare abstract class PNGBase {
43
43
  /**
44
44
  * Filter PNG data for encoding (using filter type 0 - None)
45
45
  */
46
- protected filterData(data: Uint8Array, width: number, height: number): Uint8Array;
46
+ /**
47
+ * Apply PNG filter to image data based on compression level
48
+ * @param data Raw RGBA pixel data
49
+ * @param width Image width
50
+ * @param height Image height
51
+ * @param compressionLevel Compression level (0-9, default 6)
52
+ * @returns Filtered data with filter type byte per scanline
53
+ */
54
+ protected filterData(data: Uint8Array, width: number, height: number, compressionLevel?: number): Uint8Array;
55
+ /**
56
+ * Apply filter type 0 (None) - no filtering
57
+ */
58
+ private applyNoFilter;
59
+ /**
60
+ * Apply filter type 1 (Sub) - subtract left pixel
61
+ */
62
+ private applySubFilter;
63
+ /**
64
+ * Apply filter type 2 (Up) - subtract above pixel
65
+ */
66
+ private applyUpFilter;
67
+ /**
68
+ * Apply filter type 3 (Average) - subtract average of left and above
69
+ */
70
+ private applyAverageFilter;
71
+ /**
72
+ * Apply filter type 4 (Paeth) - Paeth predictor
73
+ */
74
+ private applyPaethFilter;
75
+ /**
76
+ * Calculate sum of absolute differences for a filtered scanline
77
+ * Lower values indicate better compression potential
78
+ */
79
+ private calculateFilterScore;
80
+ /**
81
+ * Apply adaptive filtering - choose best filter per scanline
82
+ */
83
+ private applyAdaptiveFilter;
84
+ /**
85
+ * Filter a single scanline with specified filter type
86
+ */
87
+ private filterScanline;
47
88
  /**
48
89
  * Get bytes per pixel for a given color type and bit depth
49
90
  */
@@ -144,18 +144,211 @@ class PNGBase {
144
144
  /**
145
145
  * Filter PNG data for encoding (using filter type 0 - None)
146
146
  */
147
- filterData(data, width, height) {
148
- // Use filter type 0 (None) for simplicity
149
- const filtered = new Uint8Array(height * (1 + width * 4));
147
+ /**
148
+ * Apply PNG filter to image data based on compression level
149
+ * @param data Raw RGBA pixel data
150
+ * @param width Image width
151
+ * @param height Image height
152
+ * @param compressionLevel Compression level (0-9, default 6)
153
+ * @returns Filtered data with filter type byte per scanline
154
+ */
155
+ filterData(data, width, height, compressionLevel = 6) {
156
+ // Choose filtering strategy based on compression level
157
+ if (compressionLevel <= 2) {
158
+ // Fast: No filtering
159
+ return this.applyNoFilter(data, width, height);
160
+ }
161
+ else if (compressionLevel <= 6) {
162
+ // Balanced: Sub filter
163
+ return this.applySubFilter(data, width, height);
164
+ }
165
+ else {
166
+ // Best: Adaptive filtering (choose best filter per scanline)
167
+ return this.applyAdaptiveFilter(data, width, height);
168
+ }
169
+ }
170
+ /**
171
+ * Apply filter type 0 (None) - no filtering
172
+ */
173
+ applyNoFilter(data, width, height) {
174
+ const bytesPerScanline = width * 4;
175
+ const filtered = new Uint8Array(height * (1 + bytesPerScanline));
150
176
  let pos = 0;
151
177
  for (let y = 0; y < height; y++) {
152
178
  filtered[pos++] = 0; // Filter type: None
153
- for (let x = 0; x < width * 4; x++) {
154
- filtered[pos++] = data[y * width * 4 + x];
179
+ const scanlineStart = y * bytesPerScanline;
180
+ for (let x = 0; x < bytesPerScanline; x++) {
181
+ filtered[pos++] = data[scanlineStart + x];
182
+ }
183
+ }
184
+ return filtered;
185
+ }
186
+ /**
187
+ * Apply filter type 1 (Sub) - subtract left pixel
188
+ */
189
+ applySubFilter(data, width, height) {
190
+ const bytesPerScanline = width * 4;
191
+ const filtered = new Uint8Array(height * (1 + bytesPerScanline));
192
+ let pos = 0;
193
+ for (let y = 0; y < height; y++) {
194
+ filtered[pos++] = 1; // Filter type: Sub
195
+ const scanlineStart = y * bytesPerScanline;
196
+ for (let x = 0; x < bytesPerScanline; x++) {
197
+ const current = data[scanlineStart + x];
198
+ const left = x >= 4 ? data[scanlineStart + x - 4] : 0;
199
+ filtered[pos++] = (current - left) & 0xff;
200
+ }
201
+ }
202
+ return filtered;
203
+ }
204
+ /**
205
+ * Apply filter type 2 (Up) - subtract above pixel
206
+ */
207
+ applyUpFilter(data, width, height) {
208
+ const bytesPerScanline = width * 4;
209
+ const filtered = new Uint8Array(height * (1 + bytesPerScanline));
210
+ let pos = 0;
211
+ for (let y = 0; y < height; y++) {
212
+ filtered[pos++] = 2; // Filter type: Up
213
+ const scanlineStart = y * bytesPerScanline;
214
+ const prevScanlineStart = (y - 1) * bytesPerScanline;
215
+ for (let x = 0; x < bytesPerScanline; x++) {
216
+ const current = data[scanlineStart + x];
217
+ const up = y > 0 ? data[prevScanlineStart + x] : 0;
218
+ filtered[pos++] = (current - up) & 0xff;
219
+ }
220
+ }
221
+ return filtered;
222
+ }
223
+ /**
224
+ * Apply filter type 3 (Average) - subtract average of left and above
225
+ */
226
+ applyAverageFilter(data, width, height) {
227
+ const bytesPerScanline = width * 4;
228
+ const filtered = new Uint8Array(height * (1 + bytesPerScanline));
229
+ let pos = 0;
230
+ for (let y = 0; y < height; y++) {
231
+ filtered[pos++] = 3; // Filter type: Average
232
+ const scanlineStart = y * bytesPerScanline;
233
+ const prevScanlineStart = (y - 1) * bytesPerScanline;
234
+ for (let x = 0; x < bytesPerScanline; x++) {
235
+ const current = data[scanlineStart + x];
236
+ const left = x >= 4 ? data[scanlineStart + x - 4] : 0;
237
+ const up = y > 0 ? data[prevScanlineStart + x] : 0;
238
+ const avg = Math.floor((left + up) / 2);
239
+ filtered[pos++] = (current - avg) & 0xff;
155
240
  }
156
241
  }
157
242
  return filtered;
158
243
  }
244
+ /**
245
+ * Apply filter type 4 (Paeth) - Paeth predictor
246
+ */
247
+ applyPaethFilter(data, width, height) {
248
+ const bytesPerScanline = width * 4;
249
+ const filtered = new Uint8Array(height * (1 + bytesPerScanline));
250
+ let pos = 0;
251
+ for (let y = 0; y < height; y++) {
252
+ filtered[pos++] = 4; // Filter type: Paeth
253
+ const scanlineStart = y * bytesPerScanline;
254
+ const prevScanlineStart = (y - 1) * bytesPerScanline;
255
+ for (let x = 0; x < bytesPerScanline; x++) {
256
+ const current = data[scanlineStart + x];
257
+ const left = x >= 4 ? data[scanlineStart + x - 4] : 0;
258
+ const up = y > 0 ? data[prevScanlineStart + x] : 0;
259
+ const upLeft = (y > 0 && x >= 4) ? data[prevScanlineStart + x - 4] : 0;
260
+ const paeth = this.paethPredictor(left, up, upLeft);
261
+ filtered[pos++] = (current - paeth) & 0xff;
262
+ }
263
+ }
264
+ return filtered;
265
+ }
266
+ /**
267
+ * Calculate sum of absolute differences for a filtered scanline
268
+ * Lower values indicate better compression potential
269
+ */
270
+ calculateFilterScore(filtered) {
271
+ let sum = 0;
272
+ for (let i = 1; i < filtered.length; i++) {
273
+ const byte = filtered[i];
274
+ // Penalize larger absolute values
275
+ sum += byte < 128 ? byte : (256 - byte);
276
+ }
277
+ return sum;
278
+ }
279
+ /**
280
+ * Apply adaptive filtering - choose best filter per scanline
281
+ */
282
+ applyAdaptiveFilter(data, width, height) {
283
+ const bytesPerScanline = width * 4;
284
+ const filtered = new Uint8Array(height * (1 + bytesPerScanline));
285
+ let outPos = 0;
286
+ // Try each filter type and choose the best for each scanline
287
+ const filters = [
288
+ (y) => this.filterScanline(data, y, width, 0), // None
289
+ (y) => this.filterScanline(data, y, width, 1), // Sub
290
+ (y) => this.filterScanline(data, y, width, 2), // Up
291
+ (y) => this.filterScanline(data, y, width, 3), // Average
292
+ (y) => this.filterScanline(data, y, width, 4), // Paeth
293
+ ];
294
+ for (let y = 0; y < height; y++) {
295
+ let bestFilter = null;
296
+ let bestScore = Infinity;
297
+ // Try each filter type
298
+ for (const filterFn of filters) {
299
+ const result = filterFn(y);
300
+ const score = this.calculateFilterScore(result);
301
+ if (score < bestScore) {
302
+ bestScore = score;
303
+ bestFilter = result;
304
+ }
305
+ }
306
+ // Copy best filter result
307
+ if (bestFilter) {
308
+ filtered.set(bestFilter, outPos);
309
+ outPos += bestFilter.length;
310
+ }
311
+ }
312
+ return filtered;
313
+ }
314
+ /**
315
+ * Filter a single scanline with specified filter type
316
+ */
317
+ filterScanline(data, y, width, filterType) {
318
+ const bytesPerScanline = width * 4;
319
+ const result = new Uint8Array(1 + bytesPerScanline);
320
+ result[0] = filterType;
321
+ const scanlineStart = y * bytesPerScanline;
322
+ const prevScanlineStart = (y - 1) * bytesPerScanline;
323
+ for (let x = 0; x < bytesPerScanline; x++) {
324
+ const current = data[scanlineStart + x];
325
+ const left = x >= 4 ? data[scanlineStart + x - 4] : 0;
326
+ const up = y > 0 ? data[prevScanlineStart + x] : 0;
327
+ const upLeft = (y > 0 && x >= 4) ? data[prevScanlineStart + x - 4] : 0;
328
+ let filtered;
329
+ switch (filterType) {
330
+ case 0: // None
331
+ filtered = current;
332
+ break;
333
+ case 1: // Sub
334
+ filtered = (current - left) & 0xff;
335
+ break;
336
+ case 2: // Up
337
+ filtered = (current - up) & 0xff;
338
+ break;
339
+ case 3: // Average
340
+ filtered = (current - Math.floor((left + up) / 2)) & 0xff;
341
+ break;
342
+ case 4: // Paeth
343
+ filtered = (current - this.paethPredictor(left, up, upLeft)) & 0xff;
344
+ break;
345
+ default:
346
+ filtered = current;
347
+ }
348
+ result[x + 1] = filtered;
349
+ }
350
+ return result;
351
+ }
159
352
  /**
160
353
  * Get bytes per pixel for a given color type and bit depth
161
354
  */
@@ -5,6 +5,7 @@ const tiff_lzw_js_1 = require("../utils/tiff_lzw.js");
5
5
  const tiff_packbits_js_1 = require("../utils/tiff_packbits.js");
6
6
  const tiff_deflate_js_1 = require("../utils/tiff_deflate.js");
7
7
  const security_js_1 = require("../utils/security.js");
8
+ const image_processing_js_1 = require("../utils/image_processing.js");
8
9
  // Constants for unit conversions
9
10
  const DEFAULT_DPI = 72;
10
11
  /**
@@ -139,6 +140,7 @@ class TIFFFormat {
139
140
  const compression = opts?.compression ?? "none";
140
141
  const grayscale = opts?.grayscale ?? false;
141
142
  const rgb = opts?.rgb ?? false;
143
+ const cmyk = opts?.cmyk ?? false;
142
144
  // Convert RGBA to grayscale if requested
143
145
  let sourceData;
144
146
  let samplesPerPixel;
@@ -153,6 +155,16 @@ class TIFFFormat {
153
155
  sourceData[i] = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
154
156
  }
155
157
  }
158
+ else if (cmyk) {
159
+ // Convert RGBA to CMYK
160
+ const cmykData = (0, image_processing_js_1.rgbaToCmyk)(data);
161
+ sourceData = new Uint8Array(width * height * 4);
162
+ samplesPerPixel = 4;
163
+ // Convert Float32Array CMYK (0-1) to Uint8Array (0-255)
164
+ for (let i = 0; i < cmykData.length; i++) {
165
+ sourceData[i] = Math.round(cmykData[i] * 255);
166
+ }
167
+ }
156
168
  else if (rgb) {
157
169
  // Convert RGBA to RGB (strip alpha channel)
158
170
  sourceData = new Uint8Array(width * height * 3);
@@ -244,8 +256,8 @@ class TIFFFormat {
244
256
  }
245
257
  // Compression (0x0103) - 1 = uncompressed, 5 = LZW
246
258
  this.writeIFDEntry(result, 0x0103, 3, 1, compressionCode);
247
- // PhotometricInterpretation (0x0106) - 1 = BlackIsZero (grayscale), 2 = RGB
248
- this.writeIFDEntry(result, 0x0106, 3, 1, grayscale ? 1 : 2);
259
+ // PhotometricInterpretation (0x0106) - 1 = BlackIsZero (grayscale), 2 = RGB, 5 = CMYK
260
+ this.writeIFDEntry(result, 0x0106, 3, 1, grayscale ? 1 : (cmyk ? 5 : 2));
249
261
  // StripOffsets (0x0111)
250
262
  this.writeIFDEntry(result, 0x0111, 4, 1, 8);
251
263
  // SamplesPerPixel (0x0115) - 1 for grayscale, 3 for RGB, 4 for RGBA
@@ -795,14 +807,16 @@ class TIFFFormat {
795
807
  }
796
808
  // Check photometric interpretation
797
809
  const photometric = this.getIFDValue(data, ifdOffset, 0x0106, isLittleEndian);
798
- if (photometric !== 0 && photometric !== 1 && photometric !== 2) {
799
- // Support: 0 = WhiteIsZero, 1 = BlackIsZero, 2 = RGB
810
+ if (photometric !== 0 && photometric !== 1 && photometric !== 2 &&
811
+ photometric !== 5) {
812
+ // Support: 0 = WhiteIsZero, 1 = BlackIsZero, 2 = RGB, 5 = CMYK
800
813
  return null;
801
814
  }
802
815
  // Get samples per pixel
803
816
  const samplesPerPixel = this.getIFDValue(data, ifdOffset, 0x0115, isLittleEndian);
804
817
  // For grayscale (photometric 0 or 1), expect 1 sample per pixel
805
818
  // For RGB, expect 3 or 4 samples per pixel
819
+ // For CMYK, expect 4 samples per pixel
806
820
  if (!samplesPerPixel) {
807
821
  return null;
808
822
  }
@@ -814,6 +828,10 @@ class TIFFFormat {
814
828
  // RGB requires 3 or 4 samples per pixel
815
829
  return null;
816
830
  }
831
+ if (photometric === 5 && samplesPerPixel !== 4) {
832
+ // CMYK requires 4 samples per pixel
833
+ return null;
834
+ }
817
835
  // Get strip offset
818
836
  const stripOffset = this.getIFDValue(data, ifdOffset, 0x0111, isLittleEndian);
819
837
  if (!stripOffset || stripOffset >= data.length) {
@@ -871,6 +889,29 @@ class TIFFFormat {
871
889
  }
872
890
  }
873
891
  }
892
+ else if (photometric === 5) {
893
+ // CMYK image - convert to RGB
894
+ for (let y = 0; y < height; y++) {
895
+ for (let x = 0; x < width; x++) {
896
+ const dstIdx = (y * width + x) * 4;
897
+ if (srcPos + 4 > pixelData.length) {
898
+ return null; // Not enough data
899
+ }
900
+ // TIFF stores CMYK in order, values are 0-255
901
+ // Convert to 0-1 range for conversion
902
+ const c = pixelData[srcPos++] / 255;
903
+ const m = pixelData[srcPos++] / 255;
904
+ const yVal = pixelData[srcPos++] / 255;
905
+ const k = pixelData[srcPos++] / 255;
906
+ // Convert CMYK to RGB
907
+ const [r, g, b] = (0, image_processing_js_1.cmykToRgb)(c, m, yVal, k);
908
+ rgba[dstIdx] = r; // R
909
+ rgba[dstIdx + 1] = g; // G
910
+ rgba[dstIdx + 2] = b; // B
911
+ rgba[dstIdx + 3] = 255; // A (opaque)
912
+ }
913
+ }
914
+ }
874
915
  else {
875
916
  // RGB/RGBA image
876
917
  for (let y = 0; y < height; y++) {
@@ -903,14 +944,16 @@ class TIFFFormat {
903
944
  }
904
945
  // Check photometric interpretation
905
946
  const photometric = this.getIFDValue(data, ifdOffset, 0x0106, isLittleEndian);
906
- if (photometric !== 0 && photometric !== 1 && photometric !== 2) {
907
- // Support: 0 = WhiteIsZero, 1 = BlackIsZero, 2 = RGB
947
+ if (photometric !== 0 && photometric !== 1 && photometric !== 2 &&
948
+ photometric !== 5) {
949
+ // Support: 0 = WhiteIsZero, 1 = BlackIsZero, 2 = RGB, 5 = CMYK
908
950
  return null;
909
951
  }
910
952
  // Get samples per pixel
911
953
  const samplesPerPixel = this.getIFDValue(data, ifdOffset, 0x0115, isLittleEndian);
912
954
  // For grayscale (photometric 0 or 1), expect 1 sample per pixel
913
955
  // For RGB, expect 3 or 4 samples per pixel
956
+ // For CMYK, expect 4 samples per pixel
914
957
  if (!samplesPerPixel) {
915
958
  return null;
916
959
  }
@@ -922,6 +965,10 @@ class TIFFFormat {
922
965
  // RGB requires 3 or 4 samples per pixel
923
966
  return null;
924
967
  }
968
+ if (photometric === 5 && samplesPerPixel !== 4) {
969
+ // CMYK requires 4 samples per pixel
970
+ return null;
971
+ }
925
972
  // Get strip offset
926
973
  const stripOffset = this.getIFDValue(data, ifdOffset, 0x0111, isLittleEndian);
927
974
  if (!stripOffset || stripOffset >= data.length) {
@@ -979,6 +1026,29 @@ class TIFFFormat {
979
1026
  }
980
1027
  }
981
1028
  }
1029
+ else if (photometric === 5) {
1030
+ // CMYK image - convert to RGB
1031
+ for (let y = 0; y < height; y++) {
1032
+ for (let x = 0; x < width; x++) {
1033
+ const dstIdx = (y * width + x) * 4;
1034
+ if (srcPos + 4 > pixelData.length) {
1035
+ return null; // Not enough data
1036
+ }
1037
+ // TIFF stores CMYK in order, values are 0-255
1038
+ // Convert to 0-1 range for conversion
1039
+ const c = pixelData[srcPos++] / 255;
1040
+ const m = pixelData[srcPos++] / 255;
1041
+ const yVal = pixelData[srcPos++] / 255;
1042
+ const k = pixelData[srcPos++] / 255;
1043
+ // Convert CMYK to RGB
1044
+ const [r, g, b] = (0, image_processing_js_1.cmykToRgb)(c, m, yVal, k);
1045
+ rgba[dstIdx] = r; // R
1046
+ rgba[dstIdx + 1] = g; // G
1047
+ rgba[dstIdx + 2] = b; // B
1048
+ rgba[dstIdx + 3] = 255; // A (opaque)
1049
+ }
1050
+ }
1051
+ }
982
1052
  else {
983
1053
  // RGB/RGBA image
984
1054
  for (let y = 0; y < height; y++) {
@@ -350,5 +350,20 @@ export declare class Image {
350
350
  * @returns This image instance for chaining
351
351
  */
352
352
  flipVertical(): this;
353
+ /**
354
+ * Convert the image to CMYK color space
355
+ * Returns a Float32Array with 4 values per pixel (C, M, Y, K) in 0-1 range
356
+ * @returns CMYK image data as Float32Array
357
+ */
358
+ toCMYK(): Float32Array;
359
+ /**
360
+ * Create an Image from CMYK data
361
+ * @param cmykData CMYK image data (4 values per pixel in 0-1 range)
362
+ * @param width Image width
363
+ * @param height Image height
364
+ * @param alpha Optional alpha value for all pixels (0-255, default: 255)
365
+ * @returns New Image instance
366
+ */
367
+ static fromCMYK(cmykData: Float32Array, width: number, height: number, alpha?: number): Image;
353
368
  }
354
369
  //# sourceMappingURL=image.d.ts.map
@@ -867,6 +867,34 @@ class Image {
867
867
  this.imageData.data = (0, image_processing_js_1.flipVertical)(this.imageData.data, this.imageData.width, this.imageData.height);
868
868
  return this;
869
869
  }
870
+ /**
871
+ * Convert the image to CMYK color space
872
+ * Returns a Float32Array with 4 values per pixel (C, M, Y, K) in 0-1 range
873
+ * @returns CMYK image data as Float32Array
874
+ */
875
+ toCMYK() {
876
+ if (!this.imageData)
877
+ throw new Error("No image loaded");
878
+ return (0, image_processing_js_1.rgbaToCmyk)(this.imageData.data);
879
+ }
880
+ /**
881
+ * Create an Image from CMYK data
882
+ * @param cmykData CMYK image data (4 values per pixel in 0-1 range)
883
+ * @param width Image width
884
+ * @param height Image height
885
+ * @param alpha Optional alpha value for all pixels (0-255, default: 255)
886
+ * @returns New Image instance
887
+ */
888
+ static fromCMYK(cmykData, width, height, alpha = 255) {
889
+ const rgbaData = (0, image_processing_js_1.cmykToRgba)(cmykData, alpha);
890
+ const image = new Image();
891
+ image.imageData = {
892
+ width,
893
+ height,
894
+ data: rgbaData,
895
+ };
896
+ return image;
897
+ }
870
898
  }
871
899
  exports.Image = Image;
872
900
  Object.defineProperty(Image, "formats", {
@@ -135,6 +135,42 @@ export interface ResizeOptions {
135
135
  */
136
136
  fit?: "stretch" | "fit" | "fill" | "cover" | "contain";
137
137
  }
138
+ /**
139
+ * Options for PNG encoding.
140
+ */
141
+ export interface PNGEncoderOptions {
142
+ /**
143
+ * Compression level (0-9)
144
+ * - 0-2: No filtering (fastest)
145
+ * - 3-6: Sub filter (balanced, default is 6)
146
+ * - 7-9: Adaptive filtering per scanline (best compression)
147
+ *
148
+ * Default: 6 (balanced)
149
+ *
150
+ * Note: Affects PNG filter selection. The native deflate compression
151
+ * is used regardless of level.
152
+ */
153
+ compressionLevel?: number;
154
+ }
155
+ /**
156
+ * Options for APNG (Animated PNG) encoding.
157
+ *
158
+ * APNG uses PNG encoding for each frame.
159
+ */
160
+ export interface APNGEncoderOptions extends PNGEncoderOptions {
161
+ }
162
+ /**
163
+ * Options for GIF encoding.
164
+ */
165
+ export interface GIFEncoderOptions {
166
+ /**
167
+ * Loop count for animated GIFs.
168
+ * - 0 (default): Loop infinitely
169
+ * - 1+: Loop a specific number of times
170
+ * - undefined or not set: Loop infinitely (same as 0)
171
+ */
172
+ loop?: number;
173
+ }
138
174
  /**
139
175
  * Options for ASCII art encoding
140
176
  */
@@ -158,6 +194,8 @@ export interface TIFFEncoderOptions {
158
194
  grayscale?: boolean;
159
195
  /** Encode as RGB without alpha channel (default: false, ignored if grayscale is true) */
160
196
  rgb?: boolean;
197
+ /** Encode as CMYK color space (default: false, ignored if grayscale is true) */
198
+ cmyk?: boolean;
161
199
  }
162
200
  /**
163
201
  * Options for WebP encoding
@@ -189,6 +227,34 @@ export interface JPEGEncoderOptions {
189
227
  */
190
228
  progressive?: boolean;
191
229
  }
230
+ /**
231
+ * Options for AVIF encoding.
232
+ *
233
+ * Note: AVIF encoding is currently delegated to runtime APIs (OffscreenCanvas).
234
+ * Many runtimes ignore `quality` for AVIF, or may not support AVIF encoding at all.
235
+ */
236
+ export interface AVIFEncoderOptions {
237
+ /**
238
+ * Best-effort encoding quality.
239
+ *
240
+ * Accepts either 0-1 (canvas-style) or 1-100 (library-style).
241
+ */
242
+ quality?: number;
243
+ }
244
+ /**
245
+ * Options for HEIC encoding.
246
+ *
247
+ * Note: HEIC encoding is currently delegated to runtime APIs (OffscreenCanvas).
248
+ * Many runtimes do not support HEIC encoding.
249
+ */
250
+ export interface HEICEncoderOptions {
251
+ /**
252
+ * Best-effort encoding quality.
253
+ *
254
+ * Accepts either 0-1 (canvas-style) or 1-100 (library-style).
255
+ */
256
+ quality?: number;
257
+ }
192
258
  /**
193
259
  * Common options for decode APIs.
194
260
  *
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Base64 utilities.
3
+ *
4
+ * Designed to work across Deno, Node.js, and Bun without external dependencies.
5
+ */
6
+ /**
7
+ * Encode bytes into a standard Base64 string.
8
+ */
9
+ export declare function encodeBase64(bytes: Uint8Array): string;
10
+ /**
11
+ * Decode a Base64 (or Base64URL) string into bytes.
12
+ *
13
+ * - Whitespace is ignored.
14
+ * - Missing padding is tolerated.
15
+ * - `-`/`_` are accepted as URL-safe variants.
16
+ */
17
+ export declare function decodeBase64(base64: string): Uint8Array;
18
+ export interface ParsedDataUrl {
19
+ mime: string;
20
+ bytes: Uint8Array;
21
+ }
22
+ /**
23
+ * Create a base64 data URL.
24
+ */
25
+ export declare function toDataUrl(mime: string, bytes: Uint8Array): string;
26
+ /**
27
+ * Parse a base64 data URL.
28
+ *
29
+ * Supports `data:<mime>;base64,<payload>`.
30
+ */
31
+ export declare function parseDataUrl(url: string): ParsedDataUrl;
32
+ //# sourceMappingURL=base64.d.ts.map