cross-image 0.2.4 → 0.4.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.
Files changed (105) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +507 -333
  3. package/esm/mod.d.ts +4 -4
  4. package/esm/mod.js +2 -2
  5. package/esm/src/formats/apng.d.ts +5 -5
  6. package/esm/src/formats/apng.js +7 -9
  7. package/esm/src/formats/ascii.d.ts +3 -3
  8. package/esm/src/formats/ascii.js +1 -1
  9. package/esm/src/formats/avif.d.ts +3 -3
  10. package/esm/src/formats/avif.js +7 -7
  11. package/esm/src/formats/bmp.d.ts +3 -3
  12. package/esm/src/formats/bmp.js +2 -2
  13. package/esm/src/formats/dng.d.ts +1 -1
  14. package/esm/src/formats/dng.js +1 -1
  15. package/esm/src/formats/gif.d.ts +4 -4
  16. package/esm/src/formats/gif.js +14 -10
  17. package/esm/src/formats/heic.d.ts +3 -3
  18. package/esm/src/formats/heic.js +7 -7
  19. package/esm/src/formats/ico.d.ts +3 -3
  20. package/esm/src/formats/ico.js +4 -4
  21. package/esm/src/formats/jpeg.d.ts +3 -3
  22. package/esm/src/formats/jpeg.js +23 -11
  23. package/esm/src/formats/pam.d.ts +3 -3
  24. package/esm/src/formats/pam.js +2 -2
  25. package/esm/src/formats/pcx.d.ts +3 -3
  26. package/esm/src/formats/pcx.js +2 -2
  27. package/esm/src/formats/png.d.ts +3 -3
  28. package/esm/src/formats/png.js +2 -2
  29. package/esm/src/formats/png_base.js +2 -5
  30. package/esm/src/formats/ppm.d.ts +3 -3
  31. package/esm/src/formats/ppm.js +2 -2
  32. package/esm/src/formats/tiff.d.ts +7 -18
  33. package/esm/src/formats/tiff.js +86 -21
  34. package/esm/src/formats/webp.d.ts +3 -3
  35. package/esm/src/formats/webp.js +11 -8
  36. package/esm/src/image.d.ts +11 -3
  37. package/esm/src/image.js +37 -21
  38. package/esm/src/types.d.ts +56 -4
  39. package/esm/src/utils/gif_decoder.d.ts +4 -1
  40. package/esm/src/utils/gif_decoder.js +91 -65
  41. package/esm/src/utils/image_processing.js +144 -70
  42. package/esm/src/utils/jpeg_decoder.d.ts +17 -4
  43. package/esm/src/utils/jpeg_decoder.js +448 -83
  44. package/esm/src/utils/jpeg_encoder.d.ts +15 -1
  45. package/esm/src/utils/jpeg_encoder.js +263 -24
  46. package/esm/src/utils/resize.js +51 -20
  47. package/esm/src/utils/tiff_deflate.d.ts +18 -0
  48. package/esm/src/utils/tiff_deflate.js +27 -0
  49. package/esm/src/utils/tiff_packbits.d.ts +24 -0
  50. package/esm/src/utils/tiff_packbits.js +90 -0
  51. package/esm/src/utils/webp_decoder.d.ts +3 -1
  52. package/esm/src/utils/webp_decoder.js +144 -63
  53. package/esm/src/utils/webp_encoder.js +5 -11
  54. package/package.json +1 -1
  55. package/script/mod.d.ts +4 -4
  56. package/script/mod.js +2 -2
  57. package/script/src/formats/apng.d.ts +5 -5
  58. package/script/src/formats/apng.js +7 -9
  59. package/script/src/formats/ascii.d.ts +3 -3
  60. package/script/src/formats/ascii.js +1 -1
  61. package/script/src/formats/avif.d.ts +3 -3
  62. package/script/src/formats/avif.js +7 -7
  63. package/script/src/formats/bmp.d.ts +3 -3
  64. package/script/src/formats/bmp.js +2 -2
  65. package/script/src/formats/dng.d.ts +1 -1
  66. package/script/src/formats/dng.js +1 -1
  67. package/script/src/formats/gif.d.ts +4 -4
  68. package/script/src/formats/gif.js +14 -10
  69. package/script/src/formats/heic.d.ts +3 -3
  70. package/script/src/formats/heic.js +7 -7
  71. package/script/src/formats/ico.d.ts +3 -3
  72. package/script/src/formats/ico.js +4 -4
  73. package/script/src/formats/jpeg.d.ts +3 -3
  74. package/script/src/formats/jpeg.js +23 -11
  75. package/script/src/formats/pam.d.ts +3 -3
  76. package/script/src/formats/pam.js +2 -2
  77. package/script/src/formats/pcx.d.ts +3 -3
  78. package/script/src/formats/pcx.js +2 -2
  79. package/script/src/formats/png.d.ts +3 -3
  80. package/script/src/formats/png.js +2 -2
  81. package/script/src/formats/png_base.js +2 -5
  82. package/script/src/formats/ppm.d.ts +3 -3
  83. package/script/src/formats/ppm.js +2 -2
  84. package/script/src/formats/tiff.d.ts +7 -18
  85. package/script/src/formats/tiff.js +86 -21
  86. package/script/src/formats/webp.d.ts +3 -3
  87. package/script/src/formats/webp.js +11 -8
  88. package/script/src/image.d.ts +11 -3
  89. package/script/src/image.js +36 -20
  90. package/script/src/types.d.ts +56 -4
  91. package/script/src/utils/gif_decoder.d.ts +4 -1
  92. package/script/src/utils/gif_decoder.js +91 -65
  93. package/script/src/utils/image_processing.js +144 -70
  94. package/script/src/utils/jpeg_decoder.d.ts +17 -4
  95. package/script/src/utils/jpeg_decoder.js +448 -83
  96. package/script/src/utils/jpeg_encoder.d.ts +15 -1
  97. package/script/src/utils/jpeg_encoder.js +263 -24
  98. package/script/src/utils/resize.js +51 -20
  99. package/script/src/utils/tiff_deflate.d.ts +18 -0
  100. package/script/src/utils/tiff_deflate.js +31 -0
  101. package/script/src/utils/tiff_packbits.d.ts +24 -0
  102. package/script/src/utils/tiff_packbits.js +94 -0
  103. package/script/src/utils/webp_decoder.d.ts +3 -1
  104. package/script/src/utils/webp_decoder.js +144 -63
  105. package/script/src/utils/webp_encoder.js +5 -11
@@ -5,15 +5,20 @@
5
5
  * This is a simplified implementation focusing on correctness over performance.
6
6
  * For production use with better quality/size, the OffscreenCanvas API is preferred.
7
7
  */
8
+ export interface JPEGEncoderOptions {
9
+ quality?: number;
10
+ progressive?: boolean;
11
+ }
8
12
  export declare class JPEGEncoder {
9
13
  private quality;
14
+ private progressive;
10
15
  private luminanceQuantTable;
11
16
  private chrominanceQuantTable;
12
17
  private dcLuminanceHuffman;
13
18
  private acLuminanceHuffman;
14
19
  private dcChrominanceHuffman;
15
20
  private acChrominanceHuffman;
16
- constructor(quality?: number);
21
+ constructor(options?: JPEGEncoderOptions);
17
22
  private initQuantizationTables;
18
23
  private initHuffmanTables;
19
24
  private buildHuffmanTable;
@@ -21,9 +26,18 @@ export declare class JPEGEncoder {
21
26
  private writeAPP0;
22
27
  private writeDQT;
23
28
  private writeSOF0;
29
+ private writeSOF2;
24
30
  private writeDHT;
25
31
  private writeHuffmanTable;
26
32
  private writeSOS;
33
+ private writeProgressiveSOS;
34
+ private encodeProgressive;
35
+ private dctAndQuantize;
36
+ private performDCT2D;
37
+ private encodeProgressiveDCScan;
38
+ private encodeProgressiveACScanSingle;
39
+ private encodeOnlyDC;
40
+ private encodeOnlyAC;
27
41
  private encodeScan;
28
42
  private encodeBlock;
29
43
  private forwardDCT;
@@ -694,13 +694,19 @@ class BitWriter {
694
694
  }
695
695
  }
696
696
  class JPEGEncoder {
697
- constructor(quality = 85) {
697
+ constructor(options = {}) {
698
698
  Object.defineProperty(this, "quality", {
699
699
  enumerable: true,
700
700
  configurable: true,
701
701
  writable: true,
702
702
  value: void 0
703
703
  });
704
+ Object.defineProperty(this, "progressive", {
705
+ enumerable: true,
706
+ configurable: true,
707
+ writable: true,
708
+ value: void 0
709
+ });
704
710
  Object.defineProperty(this, "luminanceQuantTable", {
705
711
  enumerable: true,
706
712
  configurable: true,
@@ -737,14 +743,13 @@ class JPEGEncoder {
737
743
  writable: true,
738
744
  value: void 0
739
745
  });
740
- this.quality = Math.max(1, Math.min(100, quality));
746
+ this.quality = Math.max(1, Math.min(100, options.quality ?? 85));
747
+ this.progressive = options.progressive ?? false;
741
748
  this.initQuantizationTables();
742
749
  this.initHuffmanTables();
743
750
  }
744
751
  initQuantizationTables() {
745
- const scaleFactor = this.quality < 50
746
- ? 5000 / this.quality
747
- : 200 - this.quality * 2;
752
+ const scaleFactor = this.quality < 50 ? 5000 / this.quality : 200 - this.quality * 2;
748
753
  for (let i = 0; i < 64; i++) {
749
754
  let lumVal = Math.floor((STANDARD_LUMINANCE_QUANT_TABLE[i] * scaleFactor + 50) / 100);
750
755
  let chromVal = Math.floor((STANDARD_CHROMINANCE_QUANT_TABLE[i] * scaleFactor + 50) / 100);
@@ -761,8 +766,8 @@ class JPEGEncoder {
761
766
  this.acChrominanceHuffman = this.buildHuffmanTable(STD_AC_CHROMINANCE_NRCODES, STD_AC_CHROMINANCE_VALUES);
762
767
  }
763
768
  buildHuffmanTable(nrcodes, values) {
764
- const codes = new Array(256).fill(0);
765
- const sizes = new Array(256).fill(0);
769
+ const codes = new Uint32Array(256);
770
+ const sizes = new Uint8Array(256);
766
771
  let code = 0;
767
772
  let valueIndex = 0;
768
773
  for (let length = 1; length <= 16; length++) {
@@ -785,16 +790,28 @@ class JPEGEncoder {
785
790
  this.writeAPP0(output, dpiX, dpiY);
786
791
  // DQT (Define Quantization Tables)
787
792
  this.writeDQT(output);
788
- // SOF0 (Start of Frame - Baseline DCT)
789
- this.writeSOF0(output, width, height);
793
+ // SOF (Start of Frame - either baseline or progressive)
794
+ if (this.progressive) {
795
+ this.writeSOF2(output, width, height);
796
+ }
797
+ else {
798
+ this.writeSOF0(output, width, height);
799
+ }
790
800
  // DHT (Define Huffman Tables)
791
801
  this.writeDHT(output);
792
- // SOS (Start of Scan)
793
- this.writeSOS(output);
794
- // Encode scan data
795
- const scanData = this.encodeScan(width, height, rgba);
796
- for (let i = 0; i < scanData.length; i++) {
797
- output.push(scanData[i]);
802
+ if (this.progressive) {
803
+ // Progressive encoding: multiple scans
804
+ this.encodeProgressive(output, width, height, rgba);
805
+ }
806
+ else {
807
+ // Baseline encoding: single scan
808
+ // SOS (Start of Scan)
809
+ this.writeSOS(output);
810
+ // Encode scan data
811
+ const scanData = this.encodeScan(width, height, rgba);
812
+ for (let i = 0; i < scanData.length; i++) {
813
+ output.push(scanData[i]);
814
+ }
798
815
  }
799
816
  // EOI (End of Image)
800
817
  output.push(0xff, 0xd9);
@@ -846,6 +863,26 @@ class JPEGEncoder {
846
863
  output.push(0x11); // Sampling factors (1x1)
847
864
  output.push(0x01); // Quantization table 1
848
865
  }
866
+ writeSOF2(output, width, height) {
867
+ output.push(0xff, 0xc2); // SOF2 marker (Progressive DCT)
868
+ output.push(0x00, 0x11); // Length (17 bytes)
869
+ output.push(0x08); // Precision (8 bits)
870
+ output.push((height >> 8) & 0xff, height & 0xff); // Height
871
+ output.push((width >> 8) & 0xff, width & 0xff); // Width
872
+ output.push(0x03); // Number of components (3 = YCbCr)
873
+ // Y component
874
+ output.push(0x01); // Component ID
875
+ output.push(0x11); // Sampling factors (1x1)
876
+ output.push(0x00); // Quantization table 0
877
+ // Cb component
878
+ output.push(0x02); // Component ID
879
+ output.push(0x11); // Sampling factors (1x1)
880
+ output.push(0x01); // Quantization table 1
881
+ // Cr component
882
+ output.push(0x03); // Component ID
883
+ output.push(0x11); // Sampling factors (1x1)
884
+ output.push(0x01); // Quantization table 1
885
+ }
849
886
  writeDHT(output) {
850
887
  // DC Luminance
851
888
  this.writeHuffmanTable(output, 0x00, STD_DC_LUMINANCE_NRCODES, STD_DC_LUMINANCE_VALUES);
@@ -893,6 +930,200 @@ class JPEGEncoder {
893
930
  output.push(0x3f); // End of spectral selection
894
931
  output.push(0x00); // Successive approximation
895
932
  }
933
+ writeProgressiveSOS(output, componentIds, spectralStart, spectralEnd, successiveHigh, successiveLow) {
934
+ output.push(0xff, 0xda); // SOS marker
935
+ // Length depends on number of components
936
+ const length = 6 + componentIds.length * 2;
937
+ output.push((length >> 8) & 0xff, length & 0xff);
938
+ output.push(componentIds.length); // Number of components
939
+ for (const id of componentIds) {
940
+ output.push(id); // Component ID
941
+ if (id === 1) {
942
+ // Y component uses table 0
943
+ output.push(0x00); // DC table 0, AC table 0
944
+ }
945
+ else {
946
+ // Cb/Cr components use table 1
947
+ output.push(0x11); // DC table 1, AC table 1
948
+ }
949
+ }
950
+ output.push(spectralStart); // Start of spectral selection (Ss)
951
+ output.push(spectralEnd); // End of spectral selection (Se)
952
+ output.push((successiveHigh << 4) | successiveLow); // Successive approximation (Ah, Al)
953
+ }
954
+ encodeProgressive(output, width, height, rgba) {
955
+ // Simplified progressive encoding:
956
+ // Scan 1: Interleaved DC coefficients for all components (Ss=0, Se=0)
957
+ // Scans 2-4: Non-interleaved AC coefficients (Ss=1, Se=63) per component.
958
+ //
959
+ // Note: Some JPEG readers are picky about interleaved AC scans in
960
+ // progressive images, so we emit AC scans as single-component for broader
961
+ // compatibility.
962
+ // For a more sophisticated progressive JPEG, we would use multiple scans
963
+ // with different spectral selections and successive approximation values.
964
+ // This basic implementation provides progressive structure while keeping
965
+ // complexity manageable.
966
+ // Pre-process all blocks (DCT + quantization)
967
+ const mcuWidth = Math.ceil(width / 8);
968
+ const mcuHeight = Math.ceil(height / 8);
969
+ const yBlocks = [];
970
+ const cbBlocks = [];
971
+ const crBlocks = [];
972
+ for (let mcuY = 0; mcuY < mcuHeight; mcuY++) {
973
+ for (let mcuX = 0; mcuX < mcuWidth; mcuX++) {
974
+ const yBlock = new Float32Array(64);
975
+ const cbBlock = new Float32Array(64);
976
+ const crBlock = new Float32Array(64);
977
+ // Extract 8x8 block and convert RGB to YCbCr
978
+ for (let y = 0; y < 8; y++) {
979
+ for (let x = 0; x < 8; x++) {
980
+ const px = mcuX * 8 + x;
981
+ const py = mcuY * 8 + y;
982
+ if (px < width && py < height) {
983
+ const offset = (py * width + px) * 4;
984
+ const r = rgba[offset];
985
+ const g = rgba[offset + 1];
986
+ const b = rgba[offset + 2];
987
+ // RGB to YCbCr conversion
988
+ const yVal = 0.299 * r + 0.587 * g + 0.114 * b;
989
+ const cbVal = -0.168736 * r - 0.331264 * g + 0.5 * b + 128;
990
+ const crVal = 0.5 * r - 0.418688 * g - 0.081312 * b + 128;
991
+ yBlock[y * 8 + x] = yVal - 128; // Level shift
992
+ cbBlock[y * 8 + x] = cbVal - 128;
993
+ crBlock[y * 8 + x] = crVal - 128;
994
+ }
995
+ }
996
+ }
997
+ // DCT and quantize
998
+ yBlocks.push(this.dctAndQuantize(yBlock, this.luminanceQuantTable));
999
+ cbBlocks.push(this.dctAndQuantize(cbBlock, this.chrominanceQuantTable));
1000
+ crBlocks.push(this.dctAndQuantize(crBlock, this.chrominanceQuantTable));
1001
+ }
1002
+ }
1003
+ // Scan 1: DC-only (interleaved across all components)
1004
+ this.writeProgressiveSOS(output, [1, 2, 3], 0, 0, 0, 0);
1005
+ const dcScanData = this.encodeProgressiveDCScan(yBlocks, cbBlocks, crBlocks);
1006
+ for (let i = 0; i < dcScanData.length; i++) {
1007
+ output.push(dcScanData[i]);
1008
+ }
1009
+ // Scan 2: AC coefficients (Y only)
1010
+ this.writeProgressiveSOS(output, [1], 1, 63, 0, 0);
1011
+ const yAcScanData = this.encodeProgressiveACScanSingle(yBlocks, this.acLuminanceHuffman);
1012
+ for (let i = 0; i < yAcScanData.length; i++) {
1013
+ output.push(yAcScanData[i]);
1014
+ }
1015
+ // Scan 3: AC coefficients (Cb only)
1016
+ this.writeProgressiveSOS(output, [2], 1, 63, 0, 0);
1017
+ const cbAcScanData = this.encodeProgressiveACScanSingle(cbBlocks, this.acChrominanceHuffman);
1018
+ for (let i = 0; i < cbAcScanData.length; i++) {
1019
+ output.push(cbAcScanData[i]);
1020
+ }
1021
+ // Scan 4: AC coefficients (Cr only)
1022
+ this.writeProgressiveSOS(output, [3], 1, 63, 0, 0);
1023
+ const crAcScanData = this.encodeProgressiveACScanSingle(crBlocks, this.acChrominanceHuffman);
1024
+ for (let i = 0; i < crAcScanData.length; i++) {
1025
+ output.push(crAcScanData[i]);
1026
+ }
1027
+ }
1028
+ dctAndQuantize(block, quantTable) {
1029
+ // Perform DCT
1030
+ const dct = this.performDCT2D(block);
1031
+ // Quantize
1032
+ const quantized = new Int32Array(64);
1033
+ for (let i = 0; i < 64; i++) {
1034
+ quantized[i] = Math.round(dct[i] / quantTable[i]);
1035
+ }
1036
+ return quantized;
1037
+ }
1038
+ performDCT2D(block) {
1039
+ const output = new Float32Array(64);
1040
+ for (let v = 0; v < 8; v++) {
1041
+ for (let u = 0; u < 8; u++) {
1042
+ let sum = 0;
1043
+ for (let y = 0; y < 8; y++) {
1044
+ for (let x = 0; x < 8; x++) {
1045
+ const cu = u === 0 ? 1 / Math.sqrt(2) : 1;
1046
+ const cv = v === 0 ? 1 / Math.sqrt(2) : 1;
1047
+ sum += block[y * 8 + x] *
1048
+ Math.cos((2 * x + 1) * u * Math.PI / 16) *
1049
+ Math.cos((2 * y + 1) * v * Math.PI / 16) *
1050
+ cu * cv;
1051
+ }
1052
+ }
1053
+ output[v * 8 + u] = sum / 4;
1054
+ }
1055
+ }
1056
+ return output;
1057
+ }
1058
+ encodeProgressiveDCScan(yBlocks, cbBlocks, crBlocks) {
1059
+ // Encode only DC coefficients for progressive JPEG
1060
+ const bitWriter = new BitWriter();
1061
+ let dcY = 0, dcCb = 0, dcCr = 0;
1062
+ for (let i = 0; i < yBlocks.length; i++) {
1063
+ // Encode only DC coefficients
1064
+ dcY = this.encodeOnlyDC(yBlocks[i][0], dcY, this.dcLuminanceHuffman, bitWriter);
1065
+ dcCb = this.encodeOnlyDC(cbBlocks[i][0], dcCb, this.dcChrominanceHuffman, bitWriter);
1066
+ dcCr = this.encodeOnlyDC(crBlocks[i][0], dcCr, this.dcChrominanceHuffman, bitWriter);
1067
+ }
1068
+ bitWriter.flush();
1069
+ return Array.from(bitWriter.getBytes());
1070
+ }
1071
+ encodeProgressiveACScanSingle(blocks, acTable) {
1072
+ // Encode only AC coefficients for progressive JPEG (single component)
1073
+ const bitWriter = new BitWriter();
1074
+ for (let i = 0; i < blocks.length; i++) {
1075
+ this.encodeOnlyAC(blocks[i], acTable, bitWriter);
1076
+ }
1077
+ bitWriter.flush();
1078
+ return Array.from(bitWriter.getBytes());
1079
+ }
1080
+ encodeOnlyDC(dc, prevDC, dcTable, bitWriter) {
1081
+ const dcDiff = dc - prevDC;
1082
+ const clampedDiff = Math.max(-2047, Math.min(2047, dcDiff));
1083
+ const absDiff = Math.abs(clampedDiff);
1084
+ let size = 0;
1085
+ if (absDiff > 0) {
1086
+ size = Math.floor(Math.log2(absDiff)) + 1;
1087
+ }
1088
+ bitWriter.writeBits(dcTable.codes[size], dcTable.sizes[size]);
1089
+ if (size > 0) {
1090
+ const magnitude = clampedDiff < 0 ? clampedDiff + (1 << size) - 1 : clampedDiff;
1091
+ bitWriter.writeBits(magnitude, size);
1092
+ }
1093
+ return dc;
1094
+ }
1095
+ encodeOnlyAC(quantized, acTable, bitWriter) {
1096
+ let zeroCount = 0;
1097
+ // Start from index 1 (skip DC coefficient)
1098
+ for (let i = 1; i < 64; i++) {
1099
+ const coef = quantized[ZIGZAG[i]];
1100
+ const clampedCoef = Math.max(-1023, Math.min(1023, coef));
1101
+ if (clampedCoef === 0) {
1102
+ zeroCount++;
1103
+ if (zeroCount === 16) {
1104
+ bitWriter.writeBits(acTable.codes[0xf0], acTable.sizes[0xf0]);
1105
+ zeroCount = 0;
1106
+ }
1107
+ }
1108
+ else {
1109
+ while (zeroCount >= 16) {
1110
+ bitWriter.writeBits(acTable.codes[0xf0], acTable.sizes[0xf0]);
1111
+ zeroCount -= 16;
1112
+ }
1113
+ const absCoef = Math.abs(clampedCoef);
1114
+ const size = Math.floor(Math.log2(absCoef)) + 1;
1115
+ const symbol = (zeroCount << 4) | size;
1116
+ bitWriter.writeBits(acTable.codes[symbol], acTable.sizes[symbol]);
1117
+ const magnitude = clampedCoef < 0 ? clampedCoef + (1 << size) - 1 : clampedCoef;
1118
+ bitWriter.writeBits(magnitude, size);
1119
+ zeroCount = 0;
1120
+ }
1121
+ }
1122
+ // Write EOB if there are trailing zeros
1123
+ if (zeroCount > 0) {
1124
+ bitWriter.writeBits(acTable.codes[0x00], acTable.sizes[0x00]);
1125
+ }
1126
+ }
896
1127
  encodeScan(width, height, rgba) {
897
1128
  const bitWriter = new BitWriter();
898
1129
  // Convert RGBA to YCbCr and encode MCUs
@@ -901,9 +1132,9 @@ class JPEGEncoder {
901
1132
  const mcuHeight = Math.ceil(height / 8);
902
1133
  for (let mcuY = 0; mcuY < mcuHeight; mcuY++) {
903
1134
  for (let mcuX = 0; mcuX < mcuWidth; mcuX++) {
904
- const yBlock = new Array(64).fill(0);
905
- const cbBlock = new Array(64).fill(0);
906
- const crBlock = new Array(64).fill(0);
1135
+ const yBlock = new Float32Array(64);
1136
+ const cbBlock = new Float32Array(64);
1137
+ const crBlock = new Float32Array(64);
907
1138
  // Extract 8x8 block and convert RGB to YCbCr
908
1139
  for (let y = 0; y < 8; y++) {
909
1140
  for (let x = 0; x < 8; x++) {
@@ -937,7 +1168,7 @@ class JPEGEncoder {
937
1168
  // Apply DCT
938
1169
  this.forwardDCT(block);
939
1170
  // Quantize and reorder to zigzag
940
- const quantized = new Array(64);
1171
+ const quantized = new Int32Array(64);
941
1172
  for (let i = 0; i < 64; i++) {
942
1173
  const zigzagIndex = ZIGZAG[i];
943
1174
  quantized[i] = Math.round(block[zigzagIndex] / quantTable[zigzagIndex]);
@@ -951,7 +1182,7 @@ class JPEGEncoder {
951
1182
  }
952
1183
  forwardDCT(block) {
953
1184
  // Simplified 2D DCT
954
- const temp = new Array(64);
1185
+ const temp = new Float32Array(64);
955
1186
  // 1D DCT on rows
956
1187
  for (let i = 0; i < 8; i++) {
957
1188
  const offset = i * 8;
@@ -977,7 +1208,11 @@ class JPEGEncoder {
977
1208
  }
978
1209
  }
979
1210
  encodeDC(value, huffTable, bitWriter) {
980
- const absValue = Math.abs(value);
1211
+ // Clamp DC coefficient to the range supported by standard Huffman tables
1212
+ // Standard DC tables support sizes 0-11, which means values in range [-2047, 2047]
1213
+ const maxDC = 2047;
1214
+ const clampedValue = Math.max(-maxDC, Math.min(maxDC, value));
1215
+ const absValue = Math.abs(clampedValue);
981
1216
  let size = 0;
982
1217
  if (absValue > 0) {
983
1218
  size = Math.floor(Math.log2(absValue)) + 1;
@@ -986,7 +1221,7 @@ class JPEGEncoder {
986
1221
  bitWriter.writeBits(huffTable.codes[size], huffTable.sizes[size]);
987
1222
  // Write magnitude
988
1223
  if (size > 0) {
989
- const magnitude = value < 0 ? value + (1 << size) - 1 : value;
1224
+ const magnitude = clampedValue < 0 ? clampedValue + (1 << size) - 1 : clampedValue;
990
1225
  bitWriter.writeBits(magnitude, size);
991
1226
  }
992
1227
  }
@@ -1003,11 +1238,15 @@ class JPEGEncoder {
1003
1238
  bitWriter.writeBits(huffTable.codes[0xf0], huffTable.sizes[0xf0]);
1004
1239
  zeroCount -= 16;
1005
1240
  }
1006
- const absCoef = Math.abs(coef);
1241
+ // Clamp AC coefficient to the range supported by standard Huffman tables
1242
+ // Standard AC tables support sizes 1-10, which means values in range [-1023, 1023]
1243
+ const maxAC = 1023;
1244
+ const clampedCoef = Math.max(-maxAC, Math.min(maxAC, coef));
1245
+ const absCoef = Math.abs(clampedCoef);
1007
1246
  const size = Math.floor(Math.log2(absCoef)) + 1;
1008
1247
  const symbol = (zeroCount << 4) | size;
1009
1248
  bitWriter.writeBits(huffTable.codes[symbol], huffTable.sizes[symbol]);
1010
- const magnitude = coef < 0 ? coef + (1 << size) - 1 : coef;
1249
+ const magnitude = clampedCoef < 0 ? clampedCoef + (1 << size) - 1 : clampedCoef;
1011
1250
  bitWriter.writeBits(magnitude, size);
1012
1251
  zeroCount = 0;
1013
1252
  }
@@ -10,26 +10,55 @@ function resizeBilinear(src, srcWidth, srcHeight, dstWidth, dstHeight) {
10
10
  const dst = new Uint8Array(dstWidth * dstHeight * 4);
11
11
  const xRatio = srcWidth / dstWidth;
12
12
  const yRatio = srcHeight / dstHeight;
13
+ const srcWidthMinus1 = srcWidth - 1;
14
+ const srcHeightMinus1 = srcHeight - 1;
13
15
  for (let y = 0; y < dstHeight; y++) {
16
+ const srcY = y * yRatio;
17
+ const y1 = srcY | 0; // Fast floor
18
+ const y2 = y1 < srcHeightMinus1 ? y1 + 1 : srcHeightMinus1;
19
+ const dy = srcY - y1;
20
+ const dyInv = 1 - dy;
21
+ const dstRowOffset = y * dstWidth * 4;
14
22
  for (let x = 0; x < dstWidth; x++) {
15
23
  const srcX = x * xRatio;
16
- const srcY = y * yRatio;
17
- const x1 = Math.floor(srcX);
18
- const y1 = Math.floor(srcY);
19
- const x2 = Math.min(x1 + 1, srcWidth - 1);
20
- const y2 = Math.min(y1 + 1, srcHeight - 1);
24
+ const x1 = srcX | 0; // Fast floor
25
+ const x2 = x1 < srcWidthMinus1 ? x1 + 1 : srcWidthMinus1;
21
26
  const dx = srcX - x1;
22
- const dy = srcY - y1;
23
- for (let c = 0; c < 4; c++) {
24
- const p1 = src[(y1 * srcWidth + x1) * 4 + c];
25
- const p2 = src[(y1 * srcWidth + x2) * 4 + c];
26
- const p3 = src[(y2 * srcWidth + x1) * 4 + c];
27
- const p4 = src[(y2 * srcWidth + x2) * 4 + c];
28
- const v1 = p1 * (1 - dx) + p2 * dx;
29
- const v2 = p3 * (1 - dx) + p4 * dx;
30
- const v = v1 * (1 - dy) + v2 * dy;
31
- dst[(y * dstWidth + x) * 4 + c] = Math.round(v);
32
- }
27
+ const dxInv = 1 - dx;
28
+ const idx1 = (y1 * srcWidth + x1) * 4;
29
+ const idx2 = (y1 * srcWidth + x2) * 4;
30
+ const idx3 = (y2 * srcWidth + x1) * 4;
31
+ const idx4 = (y2 * srcWidth + x2) * 4;
32
+ const dstIdx = dstRowOffset + x * 4;
33
+ // Unroll channel loop for better performance
34
+ const p1_r = src[idx1];
35
+ const p2_r = src[idx2];
36
+ const p3_r = src[idx3];
37
+ const p4_r = src[idx4];
38
+ const v1_r = p1_r * dxInv + p2_r * dx;
39
+ const v2_r = p3_r * dxInv + p4_r * dx;
40
+ dst[dstIdx] = (v1_r * dyInv + v2_r * dy + 0.5) | 0;
41
+ const p1_g = src[idx1 + 1];
42
+ const p2_g = src[idx2 + 1];
43
+ const p3_g = src[idx3 + 1];
44
+ const p4_g = src[idx4 + 1];
45
+ const v1_g = p1_g * dxInv + p2_g * dx;
46
+ const v2_g = p3_g * dxInv + p4_g * dx;
47
+ dst[dstIdx + 1] = (v1_g * dyInv + v2_g * dy + 0.5) | 0;
48
+ const p1_b = src[idx1 + 2];
49
+ const p2_b = src[idx2 + 2];
50
+ const p3_b = src[idx3 + 2];
51
+ const p4_b = src[idx4 + 2];
52
+ const v1_b = p1_b * dxInv + p2_b * dx;
53
+ const v2_b = p3_b * dxInv + p4_b * dx;
54
+ dst[dstIdx + 2] = (v1_b * dyInv + v2_b * dy + 0.5) | 0;
55
+ const p1_a = src[idx1 + 3];
56
+ const p2_a = src[idx2 + 3];
57
+ const p3_a = src[idx3 + 3];
58
+ const p4_a = src[idx4 + 3];
59
+ const v1_a = p1_a * dxInv + p2_a * dx;
60
+ const v2_a = p3_a * dxInv + p4_a * dx;
61
+ dst[dstIdx + 3] = (v1_a * dyInv + v2_a * dy + 0.5) | 0;
33
62
  }
34
63
  }
35
64
  return dst;
@@ -42,11 +71,13 @@ function resizeNearest(src, srcWidth, srcHeight, dstWidth, dstHeight) {
42
71
  const xRatio = srcWidth / dstWidth;
43
72
  const yRatio = srcHeight / dstHeight;
44
73
  for (let y = 0; y < dstHeight; y++) {
74
+ const srcY = (y * yRatio) | 0; // Use bitwise OR for fast floor
75
+ const srcRowOffset = srcY * srcWidth;
76
+ const dstRowOffset = y * dstWidth;
45
77
  for (let x = 0; x < dstWidth; x++) {
46
- const srcX = Math.floor(x * xRatio);
47
- const srcY = Math.floor(y * yRatio);
48
- const srcIdx = (srcY * srcWidth + srcX) * 4;
49
- const dstIdx = (y * dstWidth + x) * 4;
78
+ const srcX = (x * xRatio) | 0; // Use bitwise OR for fast floor
79
+ const srcIdx = (srcRowOffset + srcX) * 4;
80
+ const dstIdx = (dstRowOffset + x) * 4;
50
81
  dst[dstIdx] = src[srcIdx];
51
82
  dst[dstIdx + 1] = src[srcIdx + 1];
52
83
  dst[dstIdx + 2] = src[srcIdx + 2];
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Deflate compression/decompression for TIFF
3
+ * Uses native JavaScript CompressionStream/DecompressionStream APIs
4
+ * Compression code: 8 (Adobe-style Deflate)
5
+ */
6
+ /**
7
+ * Compress data using Deflate
8
+ * @param data Uncompressed data
9
+ * @returns Compressed data
10
+ */
11
+ export declare function deflateCompress(data: Uint8Array): Promise<Uint8Array>;
12
+ /**
13
+ * Decompress Deflate data
14
+ * @param data Compressed data
15
+ * @returns Decompressed data
16
+ */
17
+ export declare function deflateDecompress(data: Uint8Array): Promise<Uint8Array>;
18
+ //# sourceMappingURL=tiff_deflate.d.ts.map
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * Deflate compression/decompression for TIFF
4
+ * Uses native JavaScript CompressionStream/DecompressionStream APIs
5
+ * Compression code: 8 (Adobe-style Deflate)
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.deflateCompress = deflateCompress;
9
+ exports.deflateDecompress = deflateDecompress;
10
+ /**
11
+ * Compress data using Deflate
12
+ * @param data Uncompressed data
13
+ * @returns Compressed data
14
+ */
15
+ async function deflateCompress(data) {
16
+ const stream = new Response(data).body
17
+ .pipeThrough(new CompressionStream("deflate"));
18
+ const compressed = await new Response(stream).arrayBuffer();
19
+ return new Uint8Array(compressed);
20
+ }
21
+ /**
22
+ * Decompress Deflate data
23
+ * @param data Compressed data
24
+ * @returns Decompressed data
25
+ */
26
+ async function deflateDecompress(data) {
27
+ const stream = new Response(data).body
28
+ .pipeThrough(new DecompressionStream("deflate"));
29
+ const decompressed = await new Response(stream).arrayBuffer();
30
+ return new Uint8Array(decompressed);
31
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * PackBits compression/decompression for TIFF
3
+ * PackBits is a simple run-length encoding (RLE) scheme used in TIFF images
4
+ * Compression code: 32773
5
+ *
6
+ * Format:
7
+ * - Header byte n:
8
+ * - If n >= 0 and n <= 127: Copy the next n+1 bytes literally
9
+ * - If n >= -127 and n <= -1: Repeat the next byte -n+1 times
10
+ * - If n = -128: No operation (skip)
11
+ */
12
+ /**
13
+ * Compress data using PackBits RLE
14
+ * @param data Uncompressed data
15
+ * @returns Compressed data
16
+ */
17
+ export declare function packBitsCompress(data: Uint8Array): Uint8Array;
18
+ /**
19
+ * Decompress PackBits RLE data
20
+ * @param data Compressed data
21
+ * @returns Decompressed data
22
+ */
23
+ export declare function packBitsDecompress(data: Uint8Array): Uint8Array;
24
+ //# sourceMappingURL=tiff_packbits.d.ts.map