@@ -137,6 +137,20 @@ interface JPEGDecoderOptions extends ImageDecoderOptions {
137137 extractCoefficients ?: boolean ;
138138}
139139
140+ /**
141+ * Precomputed IDCT cosine table: IDCT_COS[k][n] = cos((2n+1)*k*PI/16)
142+ * Eliminates all Math.cos calls from the IDCT hot path.
143+ */
144+ const IDCT_COS : Float64Array [ ] = Array . from (
145+ { length : 8 } ,
146+ ( _ , k ) =>
147+ Float64Array . from (
148+ { length : 8 } ,
149+ ( _ , n ) => Math . cos ( ( 2 * n + 1 ) * k * Math . PI / 16 ) ,
150+ ) ,
151+ ) ;
152+ const IDCT_SCALE = 1 / Math . sqrt ( 2 ) ;
153+
140154export class JPEGDecoder {
141155 private data : Uint8Array ;
142156 private pos : number = 0 ;
@@ -959,8 +973,7 @@ export class JPEGDecoder {
959973 }
960974
961975 private idct ( block : number [ ] | Int32Array ) : void {
962- // Simplified 2D IDCT
963- // This is a basic implementation - not optimized
976+ // 2D IDCT using precomputed cosine table — no Math.cos in the hot path
964977 const temp = new Float32Array ( 64 ) ;
965978
966979 // 1D IDCT on rows
@@ -970,9 +983,8 @@ export class JPEGDecoder {
970983 for ( let j = 0 ; j < 8 ; j ++ ) {
971984 let sum = 0 ;
972985 for ( let k = 0 ; k < 8 ; k ++ ) {
973- const c = k === 0 ? 1 / Math . sqrt ( 2 ) : 1 ;
974- sum += c * block [ offset + k ] *
975- Math . cos ( ( 2 * j + 1 ) * k * Math . PI / 16 ) ;
986+ const c = k === 0 ? IDCT_SCALE : 1 ;
987+ sum += c * block [ offset + k ] * IDCT_COS [ k ] [ j ] ;
976988 }
977989 temp [ offset + j ] = sum / 2 ;
978990 }
@@ -983,8 +995,8 @@ export class JPEGDecoder {
983995 for ( let i = 0 ; i < 8 ; i ++ ) {
984996 let sum = 0 ;
985997 for ( let k = 0 ; k < 8 ; k ++ ) {
986- const c = k === 0 ? 1 / Math . sqrt ( 2 ) : 1 ;
987- sum += c * temp [ k * 8 + j ] * Math . cos ( ( 2 * i + 1 ) * k * Math . PI / 16 ) ;
998+ const c = k === 0 ? IDCT_SCALE : 1 ;
999+ sum += c * temp [ k * 8 + j ] * IDCT_COS [ k ] [ i ] ;
9881000 }
9891001 // Level shift and clamp
9901002 block [ i * 8 + j ] = Math . max (
0 commit comments