cross-image 0.2.1 → 0.2.3

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 (44) hide show
  1. package/README.md +160 -32
  2. package/esm/mod.d.ts +2 -1
  3. package/esm/mod.js +2 -1
  4. package/esm/src/formats/jpeg.d.ts +12 -1
  5. package/esm/src/formats/jpeg.js +633 -4
  6. package/esm/src/formats/png_base.d.ts +8 -0
  7. package/esm/src/formats/png_base.js +176 -3
  8. package/esm/src/formats/ppm.d.ts +50 -0
  9. package/esm/src/formats/ppm.js +242 -0
  10. package/esm/src/formats/tiff.d.ts +10 -1
  11. package/esm/src/formats/tiff.js +194 -44
  12. package/esm/src/formats/webp.d.ts +9 -2
  13. package/esm/src/formats/webp.js +211 -62
  14. package/esm/src/image.d.ts +81 -0
  15. package/esm/src/image.js +282 -5
  16. package/esm/src/types.d.ts +41 -1
  17. package/esm/src/utils/image_processing.d.ts +98 -0
  18. package/esm/src/utils/image_processing.js +440 -0
  19. package/esm/src/utils/metadata/xmp.d.ts +52 -0
  20. package/esm/src/utils/metadata/xmp.js +325 -0
  21. package/esm/src/utils/resize.d.ts +4 -0
  22. package/esm/src/utils/resize.js +74 -0
  23. package/package.json +1 -1
  24. package/script/mod.d.ts +2 -1
  25. package/script/mod.js +4 -2
  26. package/script/src/formats/jpeg.d.ts +12 -1
  27. package/script/src/formats/jpeg.js +633 -4
  28. package/script/src/formats/png_base.d.ts +8 -0
  29. package/script/src/formats/png_base.js +176 -3
  30. package/script/src/formats/ppm.d.ts +50 -0
  31. package/script/src/formats/ppm.js +246 -0
  32. package/script/src/formats/tiff.d.ts +10 -1
  33. package/script/src/formats/tiff.js +194 -44
  34. package/script/src/formats/webp.d.ts +9 -2
  35. package/script/src/formats/webp.js +211 -62
  36. package/script/src/image.d.ts +81 -0
  37. package/script/src/image.js +280 -3
  38. package/script/src/types.d.ts +41 -1
  39. package/script/src/utils/image_processing.d.ts +98 -0
  40. package/script/src/utils/image_processing.js +451 -0
  41. package/script/src/utils/metadata/xmp.d.ts +52 -0
  42. package/script/src/utils/metadata/xmp.js +333 -0
  43. package/script/src/utils/resize.d.ts +4 -0
  44. package/script/src/utils/resize.js +75 -0
package/esm/src/image.js CHANGED
@@ -1,5 +1,5 @@
1
- import { resizeBilinear, resizeNearest } from "./utils/resize.js";
2
- import { adjustBrightness, adjustContrast, adjustExposure, adjustSaturation, composite, crop, fillRect, grayscale, invert, } 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";
@@ -11,6 +11,7 @@ import { ICOFormat } from "./formats/ico.js";
11
11
  import { DNGFormat } from "./formats/dng.js";
12
12
  import { PAMFormat } from "./formats/pam.js";
13
13
  import { PCXFormat } from "./formats/pcx.js";
14
+ import { PPMFormat } from "./formats/ppm.js";
14
15
  import { ASCIIFormat } from "./formats/ascii.js";
15
16
  import { validateImageDimensions } from "./utils/security.js";
16
17
  /**
@@ -180,6 +181,18 @@ export class Image {
180
181
  }
181
182
  throw new Error("Unsupported or unrecognized image format");
182
183
  }
184
+ /**
185
+ * Get supported metadata fields for a specific format
186
+ * @param format Format name (e.g., "jpeg", "png", "webp")
187
+ * @returns Array of supported metadata field names, or undefined if format doesn't support metadata
188
+ */
189
+ static getSupportedMetadata(format) {
190
+ const formatHandler = Image.formats.find((f) => f.name === format.toLowerCase());
191
+ if (!formatHandler) {
192
+ throw new Error(`Unknown image format: ${format}`);
193
+ }
194
+ return formatHandler.getSupportedMetadata?.();
195
+ }
183
196
  /**
184
197
  * Read an image from bytes
185
198
  * @deprecated Use `decode()` instead. This method will be removed in a future version.
@@ -300,19 +313,98 @@ export class Image {
300
313
  resize(options) {
301
314
  if (!this.imageData)
302
315
  throw new Error("No image loaded");
303
- const { width, height, method = "bilinear" } = options;
316
+ const { width, height, method = "bilinear", fit = "stretch" } = options;
304
317
  // Validate new dimensions for security (prevent integer overflow and heap exhaustion)
305
318
  validateImageDimensions(width, height);
306
319
  const { data: srcData, width: srcWidth, height: srcHeight } = this.imageData;
320
+ // Handle fitting modes
321
+ let targetWidth = width;
322
+ let targetHeight = height;
323
+ let shouldCenter = false;
324
+ const fitMode = fit === "contain" ? "fit" : fit === "cover" ? "fill" : fit;
325
+ if (fitMode === "fit" || fitMode === "fill") {
326
+ const srcAspect = srcWidth / srcHeight;
327
+ const targetAspect = width / height;
328
+ if (fitMode === "fit") {
329
+ // Fit within dimensions (letterbox)
330
+ if (srcAspect > targetAspect) {
331
+ // Source is wider - fit to width
332
+ targetWidth = width;
333
+ targetHeight = Math.round(width / srcAspect);
334
+ }
335
+ else {
336
+ // Source is taller - fit to height
337
+ targetWidth = Math.round(height * srcAspect);
338
+ targetHeight = height;
339
+ }
340
+ shouldCenter = true;
341
+ }
342
+ else {
343
+ // Fill dimensions (crop)
344
+ if (srcAspect > targetAspect) {
345
+ // Source is wider - fit to height and crop width
346
+ targetWidth = Math.round(height * srcAspect);
347
+ targetHeight = height;
348
+ }
349
+ else {
350
+ // Source is taller - fit to width and crop height
351
+ targetWidth = width;
352
+ targetHeight = Math.round(width / srcAspect);
353
+ }
354
+ shouldCenter = true;
355
+ }
356
+ }
357
+ // Perform the resize
307
358
  let resizedData;
308
359
  if (method === "nearest") {
309
- resizedData = resizeNearest(srcData, srcWidth, srcHeight, width, height);
360
+ resizedData = resizeNearest(srcData, srcWidth, srcHeight, targetWidth, targetHeight);
361
+ }
362
+ else if (method === "bicubic") {
363
+ resizedData = resizeBicubic(srcData, srcWidth, srcHeight, targetWidth, targetHeight);
310
364
  }
311
365
  else {
312
- resizedData = resizeBilinear(srcData, srcWidth, srcHeight, width, height);
366
+ resizedData = resizeBilinear(srcData, srcWidth, srcHeight, targetWidth, targetHeight);
313
367
  }
314
368
  // Preserve metadata when resizing
315
369
  const metadata = this.imageData.metadata;
370
+ // If we need to center (fit mode) or crop (fill mode), create a canvas
371
+ if (shouldCenter && (targetWidth !== width || targetHeight !== height)) {
372
+ const canvas = new Uint8Array(width * height * 4);
373
+ // Fill with transparent black by default
374
+ canvas.fill(0);
375
+ if (fitMode === "fit") {
376
+ // Center the resized image (letterbox)
377
+ const offsetX = Math.floor((width - targetWidth) / 2);
378
+ const offsetY = Math.floor((height - targetHeight) / 2);
379
+ for (let y = 0; y < targetHeight; y++) {
380
+ for (let x = 0; x < targetWidth; x++) {
381
+ const srcIdx = (y * targetWidth + x) * 4;
382
+ const dstIdx = ((y + offsetY) * width + (x + offsetX)) * 4;
383
+ canvas[dstIdx] = resizedData[srcIdx];
384
+ canvas[dstIdx + 1] = resizedData[srcIdx + 1];
385
+ canvas[dstIdx + 2] = resizedData[srcIdx + 2];
386
+ canvas[dstIdx + 3] = resizedData[srcIdx + 3];
387
+ }
388
+ }
389
+ resizedData = canvas;
390
+ }
391
+ else {
392
+ // Crop to fill (center crop)
393
+ const offsetX = Math.floor((targetWidth - width) / 2);
394
+ const offsetY = Math.floor((targetHeight - height) / 2);
395
+ for (let y = 0; y < height; y++) {
396
+ for (let x = 0; x < width; x++) {
397
+ const srcIdx = ((y + offsetY) * targetWidth + (x + offsetX)) * 4;
398
+ const dstIdx = (y * width + x) * 4;
399
+ canvas[dstIdx] = resizedData[srcIdx];
400
+ canvas[dstIdx + 1] = resizedData[srcIdx + 1];
401
+ canvas[dstIdx + 2] = resizedData[srcIdx + 2];
402
+ canvas[dstIdx + 3] = resizedData[srcIdx + 3];
403
+ }
404
+ }
405
+ resizedData = canvas;
406
+ }
407
+ }
316
408
  this.imageData = {
317
409
  width,
318
410
  height,
@@ -438,6 +530,17 @@ export class Image {
438
530
  this.imageData.data = adjustSaturation(this.imageData.data, amount);
439
531
  return this;
440
532
  }
533
+ /**
534
+ * Adjust hue of the image by rotating the color wheel
535
+ * @param degrees Hue rotation in degrees (any value accepted, wraps at 360)
536
+ * @returns This image instance for chaining
537
+ */
538
+ hue(degrees) {
539
+ if (!this.imageData)
540
+ throw new Error("No image loaded");
541
+ this.imageData.data = adjustHue(this.imageData.data, degrees);
542
+ return this;
543
+ }
441
544
  /**
442
545
  * Invert colors of the image
443
546
  * @returns This image instance for chaining
@@ -458,6 +561,61 @@ export class Image {
458
561
  this.imageData.data = grayscale(this.imageData.data);
459
562
  return this;
460
563
  }
564
+ /**
565
+ * Apply sepia tone effect to the image
566
+ * @returns This image instance for chaining
567
+ */
568
+ sepia() {
569
+ if (!this.imageData)
570
+ throw new Error("No image loaded");
571
+ this.imageData.data = sepia(this.imageData.data);
572
+ return this;
573
+ }
574
+ /**
575
+ * Apply box blur filter to the image
576
+ * @param radius Blur radius (default: 1)
577
+ * @returns This image instance for chaining
578
+ */
579
+ blur(radius = 1) {
580
+ if (!this.imageData)
581
+ throw new Error("No image loaded");
582
+ this.imageData.data = boxBlur(this.imageData.data, this.imageData.width, this.imageData.height, radius);
583
+ return this;
584
+ }
585
+ /**
586
+ * Apply Gaussian blur filter to the image
587
+ * @param radius Blur radius (default: 1)
588
+ * @param sigma Optional standard deviation (if not provided, calculated from radius)
589
+ * @returns This image instance for chaining
590
+ */
591
+ gaussianBlur(radius = 1, sigma) {
592
+ if (!this.imageData)
593
+ throw new Error("No image loaded");
594
+ this.imageData.data = gaussianBlur(this.imageData.data, this.imageData.width, this.imageData.height, radius, sigma);
595
+ return this;
596
+ }
597
+ /**
598
+ * Apply sharpen filter to the image
599
+ * @param amount Sharpening amount (0-1, default: 0.5)
600
+ * @returns This image instance for chaining
601
+ */
602
+ sharpen(amount = 0.5) {
603
+ if (!this.imageData)
604
+ throw new Error("No image loaded");
605
+ this.imageData.data = sharpen(this.imageData.data, this.imageData.width, this.imageData.height, amount);
606
+ return this;
607
+ }
608
+ /**
609
+ * Apply median filter to reduce noise
610
+ * @param radius Filter radius (default: 1)
611
+ * @returns This image instance for chaining
612
+ */
613
+ medianFilter(radius = 1) {
614
+ if (!this.imageData)
615
+ throw new Error("No image loaded");
616
+ this.imageData.data = medianFilter(this.imageData.data, this.imageData.width, this.imageData.height, radius);
617
+ return this;
618
+ }
461
619
  /**
462
620
  * Fill a rectangular region with a color
463
621
  * @param x Starting X position
@@ -546,6 +704,124 @@ export class Image {
546
704
  this.imageData.data[idx + 3] = a;
547
705
  return this;
548
706
  }
707
+ /**
708
+ * Rotate the image 90 degrees clockwise
709
+ * @returns This image instance for chaining
710
+ */
711
+ rotate90() {
712
+ if (!this.imageData)
713
+ throw new Error("No image loaded");
714
+ const result = rotate90(this.imageData.data, this.imageData.width, this.imageData.height);
715
+ this.imageData.width = result.width;
716
+ this.imageData.height = result.height;
717
+ this.imageData.data = result.data;
718
+ // Update physical dimensions if DPI is set
719
+ if (this.imageData.metadata) {
720
+ const metadata = this.imageData.metadata;
721
+ if (metadata.dpiX && metadata.dpiY) {
722
+ // Swap physical dimensions
723
+ const tempPhysical = metadata.physicalWidth;
724
+ this.imageData.metadata.physicalWidth = metadata.physicalHeight;
725
+ this.imageData.metadata.physicalHeight = tempPhysical;
726
+ // Swap DPI
727
+ const tempDpi = metadata.dpiX;
728
+ this.imageData.metadata.dpiX = metadata.dpiY;
729
+ this.imageData.metadata.dpiY = tempDpi;
730
+ }
731
+ }
732
+ return this;
733
+ }
734
+ /**
735
+ * Rotate the image 180 degrees
736
+ * @returns This image instance for chaining
737
+ */
738
+ rotate180() {
739
+ if (!this.imageData)
740
+ throw new Error("No image loaded");
741
+ this.imageData.data = rotate180(this.imageData.data, this.imageData.width, this.imageData.height);
742
+ return this;
743
+ }
744
+ /**
745
+ * Rotate the image 270 degrees clockwise (or 90 degrees counter-clockwise)
746
+ * @returns This image instance for chaining
747
+ */
748
+ rotate270() {
749
+ if (!this.imageData)
750
+ throw new Error("No image loaded");
751
+ const result = rotate270(this.imageData.data, this.imageData.width, this.imageData.height);
752
+ this.imageData.width = result.width;
753
+ this.imageData.height = result.height;
754
+ this.imageData.data = result.data;
755
+ // Update physical dimensions if DPI is set
756
+ if (this.imageData.metadata) {
757
+ const metadata = this.imageData.metadata;
758
+ if (metadata.dpiX && metadata.dpiY) {
759
+ // Swap physical dimensions
760
+ const tempPhysical = metadata.physicalWidth;
761
+ this.imageData.metadata.physicalWidth = metadata.physicalHeight;
762
+ this.imageData.metadata.physicalHeight = tempPhysical;
763
+ // Swap DPI
764
+ const tempDpi = metadata.dpiX;
765
+ this.imageData.metadata.dpiX = metadata.dpiY;
766
+ this.imageData.metadata.dpiY = tempDpi;
767
+ }
768
+ }
769
+ return this;
770
+ }
771
+ /**
772
+ * Rotate the image by the specified angle in degrees
773
+ * @param degrees Rotation angle in degrees (positive = clockwise, negative = counter-clockwise)
774
+ * @returns This image instance for chaining
775
+ *
776
+ * @example
777
+ * ```ts
778
+ * image.rotate(90); // Rotate 90° clockwise
779
+ * image.rotate(-90); // Rotate 90° counter-clockwise
780
+ * image.rotate(180); // Rotate 180°
781
+ * image.rotate(45); // Rotate 45° clockwise (rounded to nearest 90°)
782
+ * ```
783
+ */
784
+ rotate(degrees) {
785
+ // Normalize to 0-360 range
786
+ let normalizedDegrees = degrees % 360;
787
+ if (normalizedDegrees < 0) {
788
+ normalizedDegrees += 360;
789
+ }
790
+ // Round to nearest 90 degrees
791
+ const rounded = Math.round(normalizedDegrees / 90) * 90;
792
+ // Apply rotation based on rounded value
793
+ switch (rounded % 360) {
794
+ case 90:
795
+ return this.rotate90();
796
+ case 180:
797
+ return this.rotate180();
798
+ case 270:
799
+ return this.rotate270();
800
+ default:
801
+ // 0 or 360 degrees - no rotation needed
802
+ return this;
803
+ }
804
+ }
805
+ /**
806
+ * Flip the image horizontally (mirror)
807
+ * @returns This image instance for chaining
808
+ */
809
+ flipHorizontal() {
810
+ if (!this.imageData)
811
+ throw new Error("No image loaded");
812
+ this.imageData.data = flipHorizontal(this.imageData.data, this.imageData.width, this.imageData.height);
813
+ return this;
814
+ }
815
+ /**
816
+ * Flip the image vertically
817
+ * @returns This image instance for chaining
818
+ */
819
+ flipVertical() {
820
+ if (!this.imageData)
821
+ throw new Error("No image loaded");
822
+ this.imageData.data = flipVertical(this.imageData.data, this.imageData.width, this.imageData.height);
823
+ return this;
824
+ }
549
825
  }
550
826
  Object.defineProperty(Image, "formats", {
551
827
  enumerable: true,
@@ -563,6 +839,7 @@ Object.defineProperty(Image, "formats", {
563
839
  new DNGFormat(),
564
840
  new PAMFormat(),
565
841
  new PCXFormat(),
842
+ new PPMFormat(),
566
843
  new ASCIIFormat(),
567
844
  ]
568
845
  });
@@ -24,6 +24,32 @@ 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;
27
53
  /** Custom metadata fields */
28
54
  custom?: Record<string, string | number | boolean>;
29
55
  }
@@ -88,7 +114,16 @@ export interface ResizeOptions {
88
114
  /** Target height in pixels */
89
115
  height: number;
90
116
  /** Resize method (default: "bilinear") */
91
- method?: "nearest" | "bilinear";
117
+ method?: "nearest" | "bilinear" | "bicubic";
118
+ /**
119
+ * Fitting mode (default: "stretch")
120
+ * - "stretch": Stretch image to fill dimensions (may distort)
121
+ * - "fit": Fit image within dimensions maintaining aspect ratio (may have letterboxing)
122
+ * - "fill": Fill dimensions maintaining aspect ratio (may crop)
123
+ * - "cover": Alias for "fill"
124
+ * - "contain": Alias for "fit"
125
+ */
126
+ fit?: "stretch" | "fit" | "fill" | "cover" | "contain";
92
127
  }
93
128
  /**
94
129
  * Options for ASCII art encoding
@@ -163,5 +198,10 @@ export interface ImageFormat {
163
198
  * Check if the format supports multiple frames
164
199
  */
165
200
  supportsMultipleFrames?(): boolean;
201
+ /**
202
+ * Get the list of metadata fields supported by this format
203
+ * @returns Array of metadata field names that can be persisted
204
+ */
205
+ getSupportedMetadata?(): Array<keyof ImageMetadata>;
166
206
  }
167
207
  //# 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)
@@ -88,4 +95,95 @@ export declare function crop(data: Uint8Array, width: number, height: number, x:
88
95
  width: number;
89
96
  height: number;
90
97
  };
98
+ /**
99
+ * Apply a box blur filter to an image
100
+ * @param data Image data (RGBA)
101
+ * @param width Image width
102
+ * @param height Image height
103
+ * @param radius Blur radius (default: 1)
104
+ * @returns New image data with box blur applied
105
+ */
106
+ export declare function boxBlur(data: Uint8Array, width: number, height: number, radius?: number): Uint8Array;
107
+ /**
108
+ * Apply Gaussian blur to an image
109
+ * @param data Image data (RGBA)
110
+ * @param width Image width
111
+ * @param height Image height
112
+ * @param radius Blur radius (default: 1)
113
+ * @param sigma Optional standard deviation (if not provided, calculated from radius)
114
+ * @returns New image data with Gaussian blur applied
115
+ */
116
+ export declare function gaussianBlur(data: Uint8Array, width: number, height: number, radius?: number, sigma?: number): Uint8Array;
117
+ /**
118
+ * Apply sharpen filter to an image
119
+ * @param data Image data (RGBA)
120
+ * @param width Image width
121
+ * @param height Image height
122
+ * @param amount Sharpening amount (0-1, default: 0.5)
123
+ * @returns New image data with sharpening applied
124
+ */
125
+ export declare function sharpen(data: Uint8Array, width: number, height: number, amount?: number): Uint8Array;
126
+ /**
127
+ * Apply sepia tone effect to an image
128
+ * @param data Image data (RGBA)
129
+ * @returns New image data with sepia tone applied
130
+ */
131
+ export declare function sepia(data: Uint8Array): Uint8Array;
132
+ /**
133
+ * Apply median filter to reduce noise
134
+ * @param data Image data (RGBA)
135
+ * @param width Image width
136
+ * @param height Image height
137
+ * @param radius Filter radius (default: 1)
138
+ * @returns New image data with median filter applied
139
+ */
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;
91
189
  //# sourceMappingURL=image_processing.d.ts.map