cross-image 0.2.2 → 0.2.4

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 (88) hide show
  1. package/README.md +333 -168
  2. package/esm/mod.d.ts +2 -0
  3. package/esm/mod.js +2 -0
  4. package/esm/src/formats/apng.d.ts +13 -1
  5. package/esm/src/formats/apng.js +97 -0
  6. package/esm/src/formats/ascii.d.ts +11 -1
  7. package/esm/src/formats/ascii.js +24 -0
  8. package/esm/src/formats/avif.d.ts +96 -0
  9. package/esm/src/formats/avif.js +607 -0
  10. package/esm/src/formats/bmp.d.ts +11 -1
  11. package/esm/src/formats/bmp.js +73 -0
  12. package/esm/src/formats/dng.d.ts +13 -1
  13. package/esm/src/formats/dng.js +26 -4
  14. package/esm/src/formats/gif.d.ts +15 -2
  15. package/esm/src/formats/gif.js +146 -4
  16. package/esm/src/formats/heic.d.ts +96 -0
  17. package/esm/src/formats/heic.js +608 -0
  18. package/esm/src/formats/ico.d.ts +11 -1
  19. package/esm/src/formats/ico.js +28 -0
  20. package/esm/src/formats/jpeg.d.ts +19 -1
  21. package/esm/src/formats/jpeg.js +709 -4
  22. package/esm/src/formats/pam.d.ts +11 -1
  23. package/esm/src/formats/pam.js +66 -0
  24. package/esm/src/formats/pcx.d.ts +11 -1
  25. package/esm/src/formats/pcx.js +45 -0
  26. package/esm/src/formats/png.d.ts +13 -1
  27. package/esm/src/formats/png.js +87 -0
  28. package/esm/src/formats/png_base.d.ts +8 -0
  29. package/esm/src/formats/png_base.js +176 -3
  30. package/esm/src/formats/ppm.d.ts +11 -1
  31. package/esm/src/formats/ppm.js +34 -0
  32. package/esm/src/formats/tiff.d.ts +13 -1
  33. package/esm/src/formats/tiff.js +165 -0
  34. package/esm/src/formats/webp.d.ts +16 -2
  35. package/esm/src/formats/webp.js +303 -62
  36. package/esm/src/image.d.ts +60 -0
  37. package/esm/src/image.js +253 -5
  38. package/esm/src/types.d.ts +59 -1
  39. package/esm/src/utils/image_processing.d.ts +55 -0
  40. package/esm/src/utils/image_processing.js +210 -0
  41. package/esm/src/utils/metadata/xmp.d.ts +52 -0
  42. package/esm/src/utils/metadata/xmp.js +325 -0
  43. package/esm/src/utils/resize.d.ts +4 -0
  44. package/esm/src/utils/resize.js +74 -0
  45. package/package.json +18 -1
  46. package/script/mod.d.ts +2 -0
  47. package/script/mod.js +5 -1
  48. package/script/src/formats/apng.d.ts +13 -1
  49. package/script/src/formats/apng.js +97 -0
  50. package/script/src/formats/ascii.d.ts +11 -1
  51. package/script/src/formats/ascii.js +24 -0
  52. package/script/src/formats/avif.d.ts +96 -0
  53. package/script/src/formats/avif.js +611 -0
  54. package/script/src/formats/bmp.d.ts +11 -1
  55. package/script/src/formats/bmp.js +73 -0
  56. package/script/src/formats/dng.d.ts +13 -1
  57. package/script/src/formats/dng.js +26 -4
  58. package/script/src/formats/gif.d.ts +15 -2
  59. package/script/src/formats/gif.js +146 -4
  60. package/script/src/formats/heic.d.ts +96 -0
  61. package/script/src/formats/heic.js +612 -0
  62. package/script/src/formats/ico.d.ts +11 -1
  63. package/script/src/formats/ico.js +28 -0
  64. package/script/src/formats/jpeg.d.ts +19 -1
  65. package/script/src/formats/jpeg.js +709 -4
  66. package/script/src/formats/pam.d.ts +11 -1
  67. package/script/src/formats/pam.js +66 -0
  68. package/script/src/formats/pcx.d.ts +11 -1
  69. package/script/src/formats/pcx.js +45 -0
  70. package/script/src/formats/png.d.ts +13 -1
  71. package/script/src/formats/png.js +87 -0
  72. package/script/src/formats/png_base.d.ts +8 -0
  73. package/script/src/formats/png_base.js +176 -3
  74. package/script/src/formats/ppm.d.ts +11 -1
  75. package/script/src/formats/ppm.js +34 -0
  76. package/script/src/formats/tiff.d.ts +13 -1
  77. package/script/src/formats/tiff.js +165 -0
  78. package/script/src/formats/webp.d.ts +16 -2
  79. package/script/src/formats/webp.js +303 -62
  80. package/script/src/image.d.ts +60 -0
  81. package/script/src/image.js +251 -3
  82. package/script/src/types.d.ts +59 -1
  83. package/script/src/utils/image_processing.d.ts +55 -0
  84. package/script/src/utils/image_processing.js +216 -0
  85. package/script/src/utils/metadata/xmp.d.ts +52 -0
  86. package/script/src/utils/metadata/xmp.js +333 -0
  87. package/script/src/utils/resize.d.ts +4 -0
  88. package/script/src/utils/resize.js +75 -0
@@ -79,6 +79,21 @@ export declare class Image {
79
79
  * @returns Image instance
80
80
  */
81
81
  static decode(data: Uint8Array, format?: string): Promise<Image>;
82
+ /**
83
+ * Get supported metadata fields for a specific format
84
+ * @param format Format name (e.g., "jpeg", "png", "webp")
85
+ * @returns Array of supported metadata field names, or undefined if format doesn't support metadata
86
+ */
87
+ static getSupportedMetadata(format: string): Array<keyof ImageMetadata> | undefined;
88
+ /**
89
+ * Extract metadata from image data without fully decoding the pixel data
90
+ * This is useful for quickly reading EXIF, XMP, or other metadata from images
91
+ * that may have unsupported features or compression methods
92
+ * @param data Raw image data
93
+ * @param format Optional format hint (e.g., "png", "jpeg", "webp")
94
+ * @returns Metadata extracted from the image, or undefined if extraction fails or format is unsupported
95
+ */
96
+ static extractMetadata(data: Uint8Array, format?: string): Promise<ImageMetadata | undefined>;
82
97
  /**
83
98
  * Read an image from bytes
84
99
  * @deprecated Use `decode()` instead. This method will be removed in a future version.
@@ -197,6 +212,12 @@ export declare class Image {
197
212
  * @returns This image instance for chaining
198
213
  */
199
214
  saturation(amount: number): this;
215
+ /**
216
+ * Adjust hue of the image by rotating the color wheel
217
+ * @param degrees Hue rotation in degrees (any value accepted, wraps at 360)
218
+ * @returns This image instance for chaining
219
+ */
220
+ hue(degrees: number): this;
200
221
  /**
201
222
  * Invert colors of the image
202
223
  * @returns This image instance for chaining
@@ -282,5 +303,44 @@ export declare class Image {
282
303
  * @returns This image instance for chaining
283
304
  */
284
305
  setPixel(x: number, y: number, r: number, g: number, b: number, a?: number): this;
306
+ /**
307
+ * Rotate the image 90 degrees clockwise
308
+ * @returns This image instance for chaining
309
+ */
310
+ rotate90(): this;
311
+ /**
312
+ * Rotate the image 180 degrees
313
+ * @returns This image instance for chaining
314
+ */
315
+ rotate180(): this;
316
+ /**
317
+ * Rotate the image 270 degrees clockwise (or 90 degrees counter-clockwise)
318
+ * @returns This image instance for chaining
319
+ */
320
+ rotate270(): this;
321
+ /**
322
+ * Rotate the image by the specified angle in degrees
323
+ * @param degrees Rotation angle in degrees (positive = clockwise, negative = counter-clockwise)
324
+ * @returns This image instance for chaining
325
+ *
326
+ * @example
327
+ * ```ts
328
+ * image.rotate(90); // Rotate 90° clockwise
329
+ * image.rotate(-90); // Rotate 90° counter-clockwise
330
+ * image.rotate(180); // Rotate 180°
331
+ * image.rotate(45); // Rotate 45° clockwise (rounded to nearest 90°)
332
+ * ```
333
+ */
334
+ rotate(degrees: number): this;
335
+ /**
336
+ * Flip the image horizontally (mirror)
337
+ * @returns This image instance for chaining
338
+ */
339
+ flipHorizontal(): this;
340
+ /**
341
+ * Flip the image vertically
342
+ * @returns This image instance for chaining
343
+ */
344
+ flipVertical(): this;
285
345
  }
286
346
  //# sourceMappingURL=image.d.ts.map
package/esm/src/image.js CHANGED
@@ -1,5 +1,5 @@
1
- import { resizeBilinear, resizeNearest } from "./utils/resize.js";
2
- import { adjustBrightness, adjustContrast, adjustExposure, adjustSaturation, boxBlur, composite, crop, fillRect, gaussianBlur, grayscale, invert, medianFilter, sepia, sharpen, } from "./utils/image_processing.js";
1
+ import { resizeBicubic, resizeBilinear, resizeNearest, } from "./utils/resize.js";
2
+ import { adjustBrightness, adjustContrast, adjustExposure, adjustHue, adjustSaturation, boxBlur, composite, crop, fillRect, flipHorizontal, flipVertical, gaussianBlur, grayscale, invert, medianFilter, rotate180, rotate270, rotate90, sepia, sharpen, } from "./utils/image_processing.js";
3
3
  import { PNGFormat } from "./formats/png.js";
4
4
  import { APNGFormat } from "./formats/apng.js";
5
5
  import { JPEGFormat } from "./formats/jpeg.js";
@@ -13,6 +13,8 @@ import { PAMFormat } from "./formats/pam.js";
13
13
  import { PCXFormat } from "./formats/pcx.js";
14
14
  import { PPMFormat } from "./formats/ppm.js";
15
15
  import { ASCIIFormat } from "./formats/ascii.js";
16
+ import { HEICFormat } from "./formats/heic.js";
17
+ import { AVIFFormat } from "./formats/avif.js";
16
18
  import { validateImageDimensions } from "./utils/security.js";
17
19
  /**
18
20
  * Main Image class for reading, manipulating, and saving images
@@ -181,6 +183,42 @@ export class Image {
181
183
  }
182
184
  throw new Error("Unsupported or unrecognized image format");
183
185
  }
186
+ /**
187
+ * Get supported metadata fields for a specific format
188
+ * @param format Format name (e.g., "jpeg", "png", "webp")
189
+ * @returns Array of supported metadata field names, or undefined if format doesn't support metadata
190
+ */
191
+ static getSupportedMetadata(format) {
192
+ const formatHandler = Image.formats.find((f) => f.name === format.toLowerCase());
193
+ if (!formatHandler) {
194
+ throw new Error(`Unknown image format: ${format}`);
195
+ }
196
+ return formatHandler.getSupportedMetadata?.();
197
+ }
198
+ /**
199
+ * Extract metadata from image data without fully decoding the pixel data
200
+ * This is useful for quickly reading EXIF, XMP, or other metadata from images
201
+ * that may have unsupported features or compression methods
202
+ * @param data Raw image data
203
+ * @param format Optional format hint (e.g., "png", "jpeg", "webp")
204
+ * @returns Metadata extracted from the image, or undefined if extraction fails or format is unsupported
205
+ */
206
+ static async extractMetadata(data, format) {
207
+ // Try specified format first
208
+ if (format) {
209
+ const handler = Image.formats.find((f) => f.name === format);
210
+ if (handler && handler.canDecode(data) && handler.extractMetadata) {
211
+ return await handler.extractMetadata(data);
212
+ }
213
+ }
214
+ // Auto-detect format
215
+ for (const handler of Image.formats) {
216
+ if (handler.canDecode(data) && handler.extractMetadata) {
217
+ return await handler.extractMetadata(data);
218
+ }
219
+ }
220
+ return undefined;
221
+ }
184
222
  /**
185
223
  * Read an image from bytes
186
224
  * @deprecated Use `decode()` instead. This method will be removed in a future version.
@@ -301,19 +339,98 @@ export class Image {
301
339
  resize(options) {
302
340
  if (!this.imageData)
303
341
  throw new Error("No image loaded");
304
- const { width, height, method = "bilinear" } = options;
342
+ const { width, height, method = "bilinear", fit = "stretch" } = options;
305
343
  // Validate new dimensions for security (prevent integer overflow and heap exhaustion)
306
344
  validateImageDimensions(width, height);
307
345
  const { data: srcData, width: srcWidth, height: srcHeight } = this.imageData;
346
+ // Handle fitting modes
347
+ let targetWidth = width;
348
+ let targetHeight = height;
349
+ let shouldCenter = false;
350
+ const fitMode = fit === "contain" ? "fit" : fit === "cover" ? "fill" : fit;
351
+ if (fitMode === "fit" || fitMode === "fill") {
352
+ const srcAspect = srcWidth / srcHeight;
353
+ const targetAspect = width / height;
354
+ if (fitMode === "fit") {
355
+ // Fit within dimensions (letterbox)
356
+ if (srcAspect > targetAspect) {
357
+ // Source is wider - fit to width
358
+ targetWidth = width;
359
+ targetHeight = Math.round(width / srcAspect);
360
+ }
361
+ else {
362
+ // Source is taller - fit to height
363
+ targetWidth = Math.round(height * srcAspect);
364
+ targetHeight = height;
365
+ }
366
+ shouldCenter = true;
367
+ }
368
+ else {
369
+ // Fill dimensions (crop)
370
+ if (srcAspect > targetAspect) {
371
+ // Source is wider - fit to height and crop width
372
+ targetWidth = Math.round(height * srcAspect);
373
+ targetHeight = height;
374
+ }
375
+ else {
376
+ // Source is taller - fit to width and crop height
377
+ targetWidth = width;
378
+ targetHeight = Math.round(width / srcAspect);
379
+ }
380
+ shouldCenter = true;
381
+ }
382
+ }
383
+ // Perform the resize
308
384
  let resizedData;
309
385
  if (method === "nearest") {
310
- resizedData = resizeNearest(srcData, srcWidth, srcHeight, width, height);
386
+ resizedData = resizeNearest(srcData, srcWidth, srcHeight, targetWidth, targetHeight);
387
+ }
388
+ else if (method === "bicubic") {
389
+ resizedData = resizeBicubic(srcData, srcWidth, srcHeight, targetWidth, targetHeight);
311
390
  }
312
391
  else {
313
- resizedData = resizeBilinear(srcData, srcWidth, srcHeight, width, height);
392
+ resizedData = resizeBilinear(srcData, srcWidth, srcHeight, targetWidth, targetHeight);
314
393
  }
315
394
  // Preserve metadata when resizing
316
395
  const metadata = this.imageData.metadata;
396
+ // If we need to center (fit mode) or crop (fill mode), create a canvas
397
+ if (shouldCenter && (targetWidth !== width || targetHeight !== height)) {
398
+ const canvas = new Uint8Array(width * height * 4);
399
+ // Fill with transparent black by default
400
+ canvas.fill(0);
401
+ if (fitMode === "fit") {
402
+ // Center the resized image (letterbox)
403
+ const offsetX = Math.floor((width - targetWidth) / 2);
404
+ const offsetY = Math.floor((height - targetHeight) / 2);
405
+ for (let y = 0; y < targetHeight; y++) {
406
+ for (let x = 0; x < targetWidth; x++) {
407
+ const srcIdx = (y * targetWidth + x) * 4;
408
+ const dstIdx = ((y + offsetY) * width + (x + offsetX)) * 4;
409
+ canvas[dstIdx] = resizedData[srcIdx];
410
+ canvas[dstIdx + 1] = resizedData[srcIdx + 1];
411
+ canvas[dstIdx + 2] = resizedData[srcIdx + 2];
412
+ canvas[dstIdx + 3] = resizedData[srcIdx + 3];
413
+ }
414
+ }
415
+ resizedData = canvas;
416
+ }
417
+ else {
418
+ // Crop to fill (center crop)
419
+ const offsetX = Math.floor((targetWidth - width) / 2);
420
+ const offsetY = Math.floor((targetHeight - height) / 2);
421
+ for (let y = 0; y < height; y++) {
422
+ for (let x = 0; x < width; x++) {
423
+ const srcIdx = ((y + offsetY) * targetWidth + (x + offsetX)) * 4;
424
+ const dstIdx = (y * width + x) * 4;
425
+ canvas[dstIdx] = resizedData[srcIdx];
426
+ canvas[dstIdx + 1] = resizedData[srcIdx + 1];
427
+ canvas[dstIdx + 2] = resizedData[srcIdx + 2];
428
+ canvas[dstIdx + 3] = resizedData[srcIdx + 3];
429
+ }
430
+ }
431
+ resizedData = canvas;
432
+ }
433
+ }
317
434
  this.imageData = {
318
435
  width,
319
436
  height,
@@ -439,6 +556,17 @@ export class Image {
439
556
  this.imageData.data = adjustSaturation(this.imageData.data, amount);
440
557
  return this;
441
558
  }
559
+ /**
560
+ * Adjust hue of the image by rotating the color wheel
561
+ * @param degrees Hue rotation in degrees (any value accepted, wraps at 360)
562
+ * @returns This image instance for chaining
563
+ */
564
+ hue(degrees) {
565
+ if (!this.imageData)
566
+ throw new Error("No image loaded");
567
+ this.imageData.data = adjustHue(this.imageData.data, degrees);
568
+ return this;
569
+ }
442
570
  /**
443
571
  * Invert colors of the image
444
572
  * @returns This image instance for chaining
@@ -602,6 +730,124 @@ export class Image {
602
730
  this.imageData.data[idx + 3] = a;
603
731
  return this;
604
732
  }
733
+ /**
734
+ * Rotate the image 90 degrees clockwise
735
+ * @returns This image instance for chaining
736
+ */
737
+ rotate90() {
738
+ if (!this.imageData)
739
+ throw new Error("No image loaded");
740
+ const result = rotate90(this.imageData.data, this.imageData.width, this.imageData.height);
741
+ this.imageData.width = result.width;
742
+ this.imageData.height = result.height;
743
+ this.imageData.data = result.data;
744
+ // Update physical dimensions if DPI is set
745
+ if (this.imageData.metadata) {
746
+ const metadata = this.imageData.metadata;
747
+ if (metadata.dpiX && metadata.dpiY) {
748
+ // Swap physical dimensions
749
+ const tempPhysical = metadata.physicalWidth;
750
+ this.imageData.metadata.physicalWidth = metadata.physicalHeight;
751
+ this.imageData.metadata.physicalHeight = tempPhysical;
752
+ // Swap DPI
753
+ const tempDpi = metadata.dpiX;
754
+ this.imageData.metadata.dpiX = metadata.dpiY;
755
+ this.imageData.metadata.dpiY = tempDpi;
756
+ }
757
+ }
758
+ return this;
759
+ }
760
+ /**
761
+ * Rotate the image 180 degrees
762
+ * @returns This image instance for chaining
763
+ */
764
+ rotate180() {
765
+ if (!this.imageData)
766
+ throw new Error("No image loaded");
767
+ this.imageData.data = rotate180(this.imageData.data, this.imageData.width, this.imageData.height);
768
+ return this;
769
+ }
770
+ /**
771
+ * Rotate the image 270 degrees clockwise (or 90 degrees counter-clockwise)
772
+ * @returns This image instance for chaining
773
+ */
774
+ rotate270() {
775
+ if (!this.imageData)
776
+ throw new Error("No image loaded");
777
+ const result = rotate270(this.imageData.data, this.imageData.width, this.imageData.height);
778
+ this.imageData.width = result.width;
779
+ this.imageData.height = result.height;
780
+ this.imageData.data = result.data;
781
+ // Update physical dimensions if DPI is set
782
+ if (this.imageData.metadata) {
783
+ const metadata = this.imageData.metadata;
784
+ if (metadata.dpiX && metadata.dpiY) {
785
+ // Swap physical dimensions
786
+ const tempPhysical = metadata.physicalWidth;
787
+ this.imageData.metadata.physicalWidth = metadata.physicalHeight;
788
+ this.imageData.metadata.physicalHeight = tempPhysical;
789
+ // Swap DPI
790
+ const tempDpi = metadata.dpiX;
791
+ this.imageData.metadata.dpiX = metadata.dpiY;
792
+ this.imageData.metadata.dpiY = tempDpi;
793
+ }
794
+ }
795
+ return this;
796
+ }
797
+ /**
798
+ * Rotate the image by the specified angle in degrees
799
+ * @param degrees Rotation angle in degrees (positive = clockwise, negative = counter-clockwise)
800
+ * @returns This image instance for chaining
801
+ *
802
+ * @example
803
+ * ```ts
804
+ * image.rotate(90); // Rotate 90° clockwise
805
+ * image.rotate(-90); // Rotate 90° counter-clockwise
806
+ * image.rotate(180); // Rotate 180°
807
+ * image.rotate(45); // Rotate 45° clockwise (rounded to nearest 90°)
808
+ * ```
809
+ */
810
+ rotate(degrees) {
811
+ // Normalize to 0-360 range
812
+ let normalizedDegrees = degrees % 360;
813
+ if (normalizedDegrees < 0) {
814
+ normalizedDegrees += 360;
815
+ }
816
+ // Round to nearest 90 degrees
817
+ const rounded = Math.round(normalizedDegrees / 90) * 90;
818
+ // Apply rotation based on rounded value
819
+ switch (rounded % 360) {
820
+ case 90:
821
+ return this.rotate90();
822
+ case 180:
823
+ return this.rotate180();
824
+ case 270:
825
+ return this.rotate270();
826
+ default:
827
+ // 0 or 360 degrees - no rotation needed
828
+ return this;
829
+ }
830
+ }
831
+ /**
832
+ * Flip the image horizontally (mirror)
833
+ * @returns This image instance for chaining
834
+ */
835
+ flipHorizontal() {
836
+ if (!this.imageData)
837
+ throw new Error("No image loaded");
838
+ this.imageData.data = flipHorizontal(this.imageData.data, this.imageData.width, this.imageData.height);
839
+ return this;
840
+ }
841
+ /**
842
+ * Flip the image vertically
843
+ * @returns This image instance for chaining
844
+ */
845
+ flipVertical() {
846
+ if (!this.imageData)
847
+ throw new Error("No image loaded");
848
+ this.imageData.data = flipVertical(this.imageData.data, this.imageData.width, this.imageData.height);
849
+ return this;
850
+ }
605
851
  }
606
852
  Object.defineProperty(Image, "formats", {
607
853
  enumerable: true,
@@ -621,5 +867,7 @@ Object.defineProperty(Image, "formats", {
621
867
  new PCXFormat(),
622
868
  new PPMFormat(),
623
869
  new ASCIIFormat(),
870
+ new HEICFormat(),
871
+ new AVIFFormat(),
624
872
  ]
625
873
  });
@@ -24,6 +24,42 @@ export interface ImageMetadata {
24
24
  copyright?: string;
25
25
  /** Creation date */
26
26
  creationDate?: Date;
27
+ /** Camera make/manufacturer (e.g., "Canon", "Nikon") */
28
+ cameraMake?: string;
29
+ /** Camera model (e.g., "Canon EOS 5D Mark IV") */
30
+ cameraModel?: string;
31
+ /** Lens make/manufacturer */
32
+ lensMake?: string;
33
+ /** Lens model */
34
+ lensModel?: string;
35
+ /** ISO speed rating (e.g., 100, 400, 3200) */
36
+ iso?: number;
37
+ /** Exposure time / Shutter speed in seconds (e.g., 0.0125 = 1/80s) */
38
+ exposureTime?: number;
39
+ /** F-number / Aperture (e.g., 2.8, 5.6, 16) */
40
+ fNumber?: number;
41
+ /** Focal length in millimeters (e.g., 50, 85, 200) */
42
+ focalLength?: number;
43
+ /** Flash mode (0 = no flash, 1 = flash fired) */
44
+ flash?: number;
45
+ /** White balance mode (0 = auto, 1 = manual) */
46
+ whiteBalance?: number;
47
+ /** Orientation (1 = normal, 3 = 180°, 6 = 90° CW, 8 = 90° CCW) */
48
+ orientation?: number;
49
+ /** Software used to create/edit the image */
50
+ software?: string;
51
+ /** User comment / notes */
52
+ userComment?: string;
53
+ /** Image file format (e.g., "png", "jpeg", "gif", "webp", "tiff") */
54
+ format?: string;
55
+ /** Compression algorithm used (e.g., "deflate", "lzw", "dct", "vp8", "vp8l", "none") */
56
+ compression?: string;
57
+ /** Number of frames in multi-frame images (e.g., animated GIFs, APNGs, multi-page TIFFs) */
58
+ frameCount?: number;
59
+ /** Bit depth per channel (e.g., 8, 16) */
60
+ bitDepth?: number;
61
+ /** Color type (e.g., "grayscale", "rgb", "rgba", "indexed", "grayscale-alpha") */
62
+ colorType?: string;
27
63
  /** Custom metadata fields */
28
64
  custom?: Record<string, string | number | boolean>;
29
65
  }
@@ -88,7 +124,16 @@ export interface ResizeOptions {
88
124
  /** Target height in pixels */
89
125
  height: number;
90
126
  /** Resize method (default: "bilinear") */
91
- method?: "nearest" | "bilinear";
127
+ method?: "nearest" | "bilinear" | "bicubic";
128
+ /**
129
+ * Fitting mode (default: "stretch")
130
+ * - "stretch": Stretch image to fill dimensions (may distort)
131
+ * - "fit": Fit image within dimensions maintaining aspect ratio (may have letterboxing)
132
+ * - "fill": Fill dimensions maintaining aspect ratio (may crop)
133
+ * - "cover": Alias for "fill"
134
+ * - "contain": Alias for "fit"
135
+ */
136
+ fit?: "stretch" | "fit" | "fill" | "cover" | "contain";
92
137
  }
93
138
  /**
94
139
  * Options for ASCII art encoding
@@ -163,5 +208,18 @@ export interface ImageFormat {
163
208
  * Check if the format supports multiple frames
164
209
  */
165
210
  supportsMultipleFrames?(): boolean;
211
+ /**
212
+ * Get the list of metadata fields supported by this format
213
+ * @returns Array of metadata field names that can be persisted
214
+ */
215
+ getSupportedMetadata?(): Array<keyof ImageMetadata>;
216
+ /**
217
+ * Extract metadata from image data without fully decoding the pixel data
218
+ * This is useful for quickly reading EXIF, XMP, or other metadata from images
219
+ * that may have unsupported features or compression methods
220
+ * @param data Raw image data
221
+ * @returns Metadata extracted from the image, or undefined if extraction fails
222
+ */
223
+ extractMetadata?(data: Uint8Array): Promise<ImageMetadata | undefined>;
166
224
  }
167
225
  //# sourceMappingURL=types.d.ts.map
@@ -44,6 +44,13 @@ export declare function adjustExposure(data: Uint8Array, amount: number): Uint8A
44
44
  * @returns New image data with adjusted saturation
45
45
  */
46
46
  export declare function adjustSaturation(data: Uint8Array, amount: number): Uint8Array;
47
+ /**
48
+ * Adjust hue of an image by rotating the hue wheel
49
+ * @param data Image data (RGBA)
50
+ * @param degrees Hue rotation in degrees (any value accepted, wraps at 360)
51
+ * @returns New image data with adjusted hue
52
+ */
53
+ export declare function adjustHue(data: Uint8Array, degrees: number): Uint8Array;
47
54
  /**
48
55
  * Invert colors of an image
49
56
  * @param data Image data (RGBA)
@@ -131,4 +138,52 @@ export declare function sepia(data: Uint8Array): Uint8Array;
131
138
  * @returns New image data with median filter applied
132
139
  */
133
140
  export declare function medianFilter(data: Uint8Array, width: number, height: number, radius?: number): Uint8Array;
141
+ /**
142
+ * Rotate image 90 degrees clockwise
143
+ * @param data Image data (RGBA)
144
+ * @param width Image width
145
+ * @param height Image height
146
+ * @returns Rotated image data with swapped dimensions
147
+ */
148
+ export declare function rotate90(data: Uint8Array, width: number, height: number): {
149
+ data: Uint8Array;
150
+ width: number;
151
+ height: number;
152
+ };
153
+ /**
154
+ * Rotate image 180 degrees
155
+ * @param data Image data (RGBA)
156
+ * @param width Image width
157
+ * @param height Image height
158
+ * @returns Rotated image data with same dimensions
159
+ */
160
+ export declare function rotate180(data: Uint8Array, width: number, height: number): Uint8Array;
161
+ /**
162
+ * Rotate image 270 degrees clockwise (or 90 degrees counter-clockwise)
163
+ * @param data Image data (RGBA)
164
+ * @param width Image width
165
+ * @param height Image height
166
+ * @returns Rotated image data with swapped dimensions
167
+ */
168
+ export declare function rotate270(data: Uint8Array, width: number, height: number): {
169
+ data: Uint8Array;
170
+ width: number;
171
+ height: number;
172
+ };
173
+ /**
174
+ * Flip image horizontally (mirror)
175
+ * @param data Image data (RGBA)
176
+ * @param width Image width
177
+ * @param height Image height
178
+ * @returns Flipped image data
179
+ */
180
+ export declare function flipHorizontal(data: Uint8Array, width: number, height: number): Uint8Array;
181
+ /**
182
+ * Flip image vertically
183
+ * @param data Image data (RGBA)
184
+ * @param width Image width
185
+ * @param height Image height
186
+ * @returns Flipped image data
187
+ */
188
+ export declare function flipVertical(data: Uint8Array, width: number, height: number): Uint8Array;
134
189
  //# sourceMappingURL=image_processing.d.ts.map