const _typeof = typeof Symbol === 'function' && typeof Symbol . iterator === 'symbol' ? function ( obj ) { return typeof obj ; } : function ( obj ) { return obj && typeof Symbol === 'function' && obj . constructor === Symbol && obj !== Symbol . prototype ? 'symbol' : typeof obj ; } ; const JpegError = function JpegErrorClosure ( ) { function JpegError ( msg ) { this . message = 'JPEG error: ' + msg ; } JpegError . prototype = new Error ( ) ; JpegError . prototype . name = 'JpegError' ; JpegError . constructor = JpegError ; return JpegError ; } ( ) ; const dctZigZag = new Uint8Array ( [ 0 , 1 , 8 , 16 , 9 , 2 , 3 , 10 , 17 , 24 , 32 , 25 , 18 , 11 , 4 , 5 , 12 , 19 , 26 , 33 , 40 , 48 , 41 , 34 , 27 , 20 , 13 , 6 , 7 , 14 , 21 , 28 , 35 , 42 , 49 , 56 , 57 , 50 , 43 , 36 , 29 , 22 , 15 , 23 , 30 , 37 , 44 , 51 , 58 , 59 , 52 , 45 , 38 , 31 , 39 , 46 , 53 , 60 , 61 , 54 , 47 , 55 , 62 , 63 ] ) ; const dctCos1 = 4017 ; const dctSin1 = 799 ; const dctCos3 = 3406 ; const dctSin3 = 2276 ; const dctCos6 = 1567 ; const dctSin6 = 3784 ; const dctSqrt2 = 5793 ; const dctSqrt1d2 = 2896 ; function buildHuffmanTable ( codeLengths , values ) { let k = 0 , code = [ ] , i , j , length = 16 ; while ( length > 0 && ! codeLengths [ length - 1 ] ) { length -- ; } code . push ( { children : [ ] , index : 0 , } ) ; let p = code [ 0 ] , q ; for ( i = 0 ; i < length ; i ++ ) { for ( j = 0 ; j < codeLengths [ i ] ; j ++ ) { p = code . pop ( ) ; p . children [ p . index ] = values [ k ] ; while ( p . index > 0 ) { p = code . pop ( ) ; } p . index ++ ; code . push ( p ) ; while ( code . length <= i ) { code . push ( q = { children : [ ] , index : 0 , } ) ; p . children [ p . index ] = q . children ; p = q ; } k ++ ; } if ( i + 1 < length ) { code . push ( q = { children : [ ] , index : 0 , } ) ; p . children [ p . index ] = q . children ; p = q ; } } return code [ 0 ] . children ; } function getBlockBufferOffset ( component , row , col ) { return 64 * ( ( component . blocksPerLine + 1 ) * row + col ) ; } function decodeScan ( data , offset , frame , components , resetInterval , spectralStart , spectralEnd , successivePrev , successive ) { const mcusPerLine = frame . mcusPerLine ; const progressive = frame . progressive ; let startOffset = offset , bitsData = 0 , bitsCount = 0 ; function readBit ( ) { if ( bitsCount > 0 ) { bitsCount -- ; return bitsData >> bitsCount & 1 ; } bitsData = data [ offset ++ ] ; if ( bitsData === 0xFF ) { const nextByte = data [ offset ++ ] ; if ( nextByte ) { throw new JpegError ( 'unexpected marker ' + ( bitsData << 8 | nextByte ) . toString ( 16 ) ) ; } } bitsCount = 7 ; return bitsData >>> 7 ; } function decodeHuffman ( tree ) { let node = tree ; while ( true ) { node = node [ readBit ( ) ] ; if ( typeof node === 'number' ) { return node ; } if ( ( typeof node === 'undefined' ? 'undefined' : _typeof ( node ) ) !== 'object' ) { throw new JpegError ( 'invalid huffman sequence' ) ; } } } function receive ( length ) { let n = 0 ; while ( length > 0 ) { n = n << 1 | readBit ( ) ; length -- ; } return n ; } function receiveAndExtend ( length ) { if ( length === 1 ) { return readBit ( ) === 1 ? 1 : - 1 ; } const n = receive ( length ) ; if ( n >= 1 << length - 1 ) { return n ; } return n + ( - 1 << length ) + 1 ; } function decodeBaseline ( component , offset ) { const t = decodeHuffman ( component . huffmanTableDC ) ; const diff = t === 0 ? 0 : receiveAndExtend ( t ) ; component . blockData [ offset ] = component . pred += diff ; let k = 1 ; while ( k < 64 ) { const rs = decodeHuffman ( component . huffmanTableAC ) ; const s = rs & 15 , r = rs >> 4 ; if ( s === 0 ) { if ( r < 15 ) { break ; } k += 16 ; continue ; } k += r ; const z = dctZigZag [ k ] ; component . blockData [ offset + z ] = receiveAndExtend ( s ) ; k ++ ; } } function decodeDCFirst ( component , offset ) { const t = decodeHuffman ( component . huffmanTableDC ) ; const diff = t === 0 ? 0 : receiveAndExtend ( t ) << successive ; component . blockData [ offset ] = component . pred += diff ; } function decodeDCSuccessive ( component , offset ) { component . blockData [ offset ] |= readBit ( ) << successive ; } let eobrun = 0 ; function decodeACFirst ( component , offset ) { if ( eobrun > 0 ) { eobrun -- ; return ; } let k = spectralStart , e = spectralEnd ; while ( k <= e ) { const rs = decodeHuffman ( component . huffmanTableAC ) ; const s = rs & 15 , r = rs >> 4 ; if ( s === 0 ) { if ( r < 15 ) { eobrun = receive ( r ) + ( 1 << r ) - 1 ; break ; } k += 16 ; continue ; } k += r ; const z = dctZigZag [ k ] ; component . blockData [ offset + z ] = receiveAndExtend ( s ) * ( 1 << successive ) ; k ++ ; } } let successiveACState = 0 , successiveACNextValue ; function decodeACSuccessive ( component , offset ) { let k = spectralStart ; const e = spectralEnd ; let r = 0 ; let s ; let rs ; while ( k <= e ) { const z = dctZigZag [ k ] ; switch ( successiveACState ) { case 0 : rs = decodeHuffman ( component . huffmanTableAC ) ; s = rs & 15 ; r = rs >> 4 ; if ( s === 0 ) { if ( r < 15 ) { eobrun = receive ( r ) + ( 1 << r ) ; successiveACState = 4 ; } else { r = 16 ; successiveACState = 1 ; } } else { if ( s !== 1 ) { throw new JpegError ( 'invalid ACn encoding' ) ; } successiveACNextValue = receiveAndExtend ( s ) ; successiveACState = r ? 2 : 3 ; } continue ; case 1 : case 2 : if ( component . blockData [ offset + z ] ) { component . blockData [ offset + z ] += readBit ( ) << successive ; } else { r -- ; if ( r === 0 ) { successiveACState = successiveACState === 2 ? 3 : 0 ; } } break ; case 3 : if ( component . blockData [ offset + z ] ) { component . blockData [ offset + z ] += readBit ( ) << successive ; } else { component . blockData [ offset + z ] = successiveACNextValue << successive ; successiveACState = 0 ; } break ; case 4 : if ( component . blockData [ offset + z ] ) { component . blockData [ offset + z ] += readBit ( ) << successive ; } break ; } k ++ ; } if ( successiveACState === 4 ) { eobrun -- ; if ( eobrun === 0 ) { successiveACState = 0 ; } } } function decodeMcu ( component , decode , mcu , row , col ) { const mcuRow = mcu / mcusPerLine | 0 ; const mcuCol = mcu % mcusPerLine ; const blockRow = mcuRow * component . v + row ; const blockCol = mcuCol * component . h + col ; const offset = getBlockBufferOffset ( component , blockRow , blockCol ) ; decode ( component , offset ) ; } function decodeBlock ( component , decode , mcu ) { const blockRow = mcu / component . blocksPerLine | 0 ; const blockCol = mcu % component . blocksPerLine ; const offset = getBlockBufferOffset ( component , blockRow , blockCol ) ; decode ( component , offset ) ; } const componentsLength = components . length ; let component , i , j , k , n ; let decodeFn ; if ( progressive ) { if ( spectralStart === 0 ) { decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive ; } else { decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive ; } } else { decodeFn = decodeBaseline ; } let mcu = 0 , fileMarker ; let mcuExpected ; if ( componentsLength === 1 ) { mcuExpected = components [ 0 ] . blocksPerLine * components [ 0 ] . blocksPerColumn ; } else { mcuExpected = mcusPerLine * frame . mcusPerColumn ; } let h , v ; while ( mcu < mcuExpected ) { const mcuToRead = resetInterval ? Math . min ( mcuExpected - mcu , resetInterval ) : mcuExpected ; for ( i = 0 ; i < componentsLength ; i ++ ) { components [ i ] . pred = 0 ; } eobrun = 0 ; if ( componentsLength === 1 ) { component = components [ 0 ] ; for ( n = 0 ; n < mcuToRead ; n ++ ) { decodeBlock ( component , decodeFn , mcu ) ; mcu ++ ; } } else { for ( n = 0 ; n < mcuToRead ; n ++ ) { for ( i = 0 ; i < componentsLength ; i ++ ) { component = components [ i ] ; h = component . h ; v = component . v ; for ( j = 0 ; j < v ; j ++ ) { for ( k = 0 ; k < h ; k ++ ) { decodeMcu ( component , decodeFn , mcu , j , k ) ; } } } mcu ++ ; } } bitsCount = 0 ; fileMarker = findNextFileMarker ( data , offset ) ; if ( fileMarker && fileMarker . invalid ) { offset = fileMarker . offset ; } const marker = fileMarker && fileMarker . marker ; if ( ! marker || marker <= 0xFF00 ) { throw new JpegError ( 'marker was not found' ) ; } if ( marker >= 0xFFD0 && marker <= 0xFFD7 ) { offset += 2 ; } else { break ; } } fileMarker = findNextFileMarker ( data , offset ) ; if ( fileMarker && fileMarker . invalid ) { offset = fileMarker . offset ; } return offset - startOffset ; } function quantizeAndInverse ( component , blockBufferOffset , p ) { const qt = component . quantizationTable , blockData = component . blockData ; let v0 , v1 , v2 , v3 , v4 , v5 , v6 , v7 ; let p0 , p1 , p2 , p3 , p4 , p5 , p6 , p7 ; let t ; if ( ! qt ) { throw new JpegError ( 'missing required Quantization Table.' ) ; } for ( let row = 0 ; row < 64 ; row += 8 ) { p0 = blockData [ blockBufferOffset + row ] ; p1 = blockData [ blockBufferOffset + row + 1 ] ; p2 = blockData [ blockBufferOffset + row + 2 ] ; p3 = blockData [ blockBufferOffset + row + 3 ] ; p4 = blockData [ blockBufferOffset + row + 4 ] ; p5 = blockData [ blockBufferOffset + row + 5 ] ; p6 = blockData [ blockBufferOffset + row + 6 ] ; p7 = blockData [ blockBufferOffset + row + 7 ] ; p0 *= qt [ row ] ; if ( ( p1 | p2 | p3 | p4 | p5 | p6 | p7 ) === 0 ) { t = dctSqrt2 * p0 + 512 >> 10 ; p [ row ] = t ; p [ row + 1 ] = t ; p [ row + 2 ] = t ; p [ row + 3 ] = t ; p [ row + 4 ] = t ; p [ row + 5 ] = t ; p [ row + 6 ] = t ; p [ row + 7 ] = t ; continue ; } p1 *= qt [ row + 1 ] ; p2 *= qt [ row + 2 ] ; p3 *= qt [ row + 3 ] ; p4 *= qt [ row + 4 ] ; p5 *= qt [ row + 5 ] ; p6 *= qt [ row + 6 ] ; p7 *= qt [ row + 7 ] ; v0 = dctSqrt2 * p0 + 128 >> 8 ; v1 = dctSqrt2 * p4 + 128 >> 8 ; v2 = p2 ; v3 = p6 ; v4 = dctSqrt1d2 * ( p1 - p7 ) + 128 >> 8 ; v7 = dctSqrt1d2 * ( p1 + p7 ) + 128 >> 8 ; v5 = p3 << 4 ; v6 = p5 << 4 ; v0 = v0 + v1 + 1 >> 1 ; v1 = v0 - v1 ; t = v2 * dctSin6 + v3 * dctCos6 + 128 >> 8 ; v2 = v2 * dctCos6 - v3 * dctSin6 + 128 >> 8 ; v3 = t ; v4 = v4 + v6 + 1 >> 1 ; v6 = v4 - v6 ; v7 = v7 + v5 + 1 >> 1 ; v5 = v7 - v5 ; v0 = v0 + v3 + 1 >> 1 ; v3 = v0 - v3 ; v1 = v1 + v2 + 1 >> 1 ; v2 = v1 - v2 ; t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12 ; v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12 ; v7 = t ; t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12 ; v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12 ; v6 = t ; p [ row ] = v0 + v7 ; p [ row + 7 ] = v0 - v7 ; p [ row + 1 ] = v1 + v6 ; p [ row + 6 ] = v1 - v6 ; p [ row + 2 ] = v2 + v5 ; p [ row + 5 ] = v2 - v5 ; p [ row + 3 ] = v3 + v4 ; p [ row + 4 ] = v3 - v4 ; } for ( let col = 0 ; col < 8 ; ++ col ) { p0 = p [ col ] ; p1 = p [ col + 8 ] ; p2 = p [ col + 16 ] ; p3 = p [ col + 24 ] ; p4 = p [ col + 32 ] ; p5 = p [ col + 40 ] ; p6 = p [ col + 48 ] ; p7 = p [ col + 56 ] ; if ( ( p1 | p2 | p3 | p4 | p5 | p6 | p7 ) === 0 ) { t = dctSqrt2 * p0 + 8192 >> 14 ; t = t < - 2040 ? 0 : t >= 2024 ? 255 : t + 2056 >> 4 ; blockData [ blockBufferOffset + col ] = t ; blockData [ blockBufferOffset + col + 8 ] = t ; blockData [ blockBufferOffset + col + 16 ] = t ; blockData [ blockBufferOffset + col + 24 ] = t ; blockData [ blockBufferOffset + col + 32 ] = t ; blockData [ blockBufferOffset + col + 40 ] = t ; blockData [ blockBufferOffset + col + 48 ] = t ; blockData [ blockBufferOffset + col + 56 ] = t ; continue ; } v0 = dctSqrt2 * p0 + 2048 >> 12 ; v1 = dctSqrt2 * p4 + 2048 >> 12 ; v2 = p2 ; v3 = p6 ; v4 = dctSqrt1d2 * ( p1 - p7 ) + 2048 >> 12 ; v7 = dctSqrt1d2 * ( p1 + p7 ) + 2048 >> 12 ; v5 = p3 ; v6 = p5 ; v0 = ( v0 + v1 + 1 >> 1 ) + 4112 ; v1 = v0 - v1 ; t = v2 * dctSin6 + v3 * dctCos6 + 2048 >> 12 ; v2 = v2 * dctCos6 - v3 * dctSin6 + 2048 >> 12 ; v3 = t ; v4 = v4 + v6 + 1 >> 1 ; v6 = v4 - v6 ; v7 = v7 + v5 + 1 >> 1 ; v5 = v7 - v5 ; v0 = v0 + v3 + 1 >> 1 ; v3 = v0 - v3 ; v1 = v1 + v2 + 1 >> 1 ; v2 = v1 - v2 ; t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12 ; v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12 ; v7 = t ; t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12 ; v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12 ; v6 = t ; p0 = v0 + v7 ; p7 = v0 - v7 ; p1 = v1 + v6 ; p6 = v1 - v6 ; p2 = v2 + v5 ; p5 = v2 - v5 ; p3 = v3 + v4 ; p4 = v3 - v4 ; p0 = p0 < 16 ? 0 : p0 >= 4080 ? 255 : p0 >> 4 ; p1 = p1 < 16 ? 0 : p1 >= 4080 ? 255 : p1 >> 4 ; p2 = p2 < 16 ? 0 : p2 >= 4080 ? 255 : p2 >> 4 ; p3 = p3 < 16 ? 0 : p3 >= 4080 ? 255 : p3 >> 4 ; p4 = p4 < 16 ? 0 : p4 >= 4080 ? 255 : p4 >> 4 ; p5 = p5 < 16 ? 0 : p5 >= 4080 ? 255 : p5 >> 4 ; p6 = p6 < 16 ? 0 : p6 >= 4080 ? 255 : p6 >> 4 ; p7 = p7 < 16 ? 0 : p7 >= 4080 ? 255 : p7 >> 4 ; blockData [ blockBufferOffset + col ] = p0 ; blockData [ blockBufferOffset + col + 8 ] = p1 ; blockData [ blockBufferOffset + col + 16 ] = p2 ; blockData [ blockBufferOffset + col + 24 ] = p3 ; blockData [ blockBufferOffset + col + 32 ] = p4 ; blockData [ blockBufferOffset + col + 40 ] = p5 ; blockData [ blockBufferOffset + col + 48 ] = p6 ; blockData [ blockBufferOffset + col + 56 ] = p7 ; } } function buildComponentData ( frame , component ) { const blocksPerLine = component . blocksPerLine ; const blocksPerColumn = component . blocksPerColumn ; const computationBuffer = new Int16Array ( 64 ) ; for ( let blockRow = 0 ; blockRow < blocksPerColumn ; blockRow ++ ) { for ( let blockCol = 0 ; blockCol < blocksPerLine ; blockCol ++ ) { const offset = getBlockBufferOffset ( component , blockRow , blockCol ) ; quantizeAndInverse ( component , offset , computationBuffer ) ; } } return component . blockData ; } function clamp0to255 ( a ) { return a <= 0 ? 0 : a >= 255 ? 255 : a ; } function findNextFileMarker ( data , currentPos , startPos ) { function peekUint16 ( pos ) { return data [ pos ] << 8 | data [ pos + 1 ] ; } const maxPos = data . length - 1 ; let newPos = startPos < currentPos ? startPos : currentPos ; if ( currentPos >= maxPos ) { return null ; } const currentMarker = peekUint16 ( currentPos ) ; if ( currentMarker >= 0xFFC0 && currentMarker <= 0xFFFE ) { return { invalid : null , marker : currentMarker , offset : currentPos , } ; } let newMarker = peekUint16 ( newPos ) ; while ( ! ( newMarker >= 0xFFC0 && newMarker <= 0xFFFE ) ) { if ( ++ newPos >= maxPos ) { return null ; } newMarker = peekUint16 ( newPos ) ; } return { invalid : currentMarker . toString ( 16 ) , marker : newMarker , offset : newPos , } ; } export class JpegImage { constructor ( ) { this . decodeTransform = null ; this . colorTransform = - 1 ; this . width = 0 ; this . height = 0 ; } parse ( data ) { function readUint16 ( ) { const value = data [ offset ] << 8 | data [ offset + 1 ] ; offset += 2 ; return value ; } function readDataBlock ( ) { const length = readUint16 ( ) ; let endOffset = offset + length - 2 ; const fileMarker = findNextFileMarker ( data , endOffset , offset ) ; if ( fileMarker && fileMarker . invalid ) { endOffset = fileMarker . offset ; } const array = data . subarray ( offset , endOffset ) ; offset += array . length ; return array ; } function prepareComponents ( frame ) { const mcusPerLine = Math . ceil ( frame . samplesPerLine / 8 / frame . maxH ) ; const mcusPerColumn = Math . ceil ( frame . scanLines / 8 / frame . maxV ) ; for ( let i = 0 ; i < frame . components . length ; i ++ ) { component = frame . components [ i ] ; const blocksPerLine = Math . ceil ( Math . ceil ( frame . samplesPerLine / 8 ) * component . h / frame . maxH ) ; const blocksPerColumn = Math . ceil ( Math . ceil ( frame . scanLines / 8 ) * component . v / frame . maxV ) ; const blocksPerLineForMcu = mcusPerLine * component . h ; const blocksPerColumnForMcu = mcusPerColumn * component . v ; const blocksBufferSize = 64 * blocksPerColumnForMcu * ( blocksPerLineForMcu + 1 ) ; component . blockData = new Int16Array ( blocksBufferSize ) ; component . blocksPerLine = blocksPerLine ; component . blocksPerColumn = blocksPerColumn ; } frame . mcusPerLine = mcusPerLine ; frame . mcusPerColumn = mcusPerColumn ; } var offset = 0 ; let jfif = null ; let adobe = null ; let frame , resetInterval ; const quantizationTables = [ ] ; const huffmanTablesAC = [ ] , huffmanTablesDC = [ ] ; let fileMarker = readUint16 ( ) ; if ( fileMarker !== 0xFFD8 ) { throw new JpegError ( 'SOI not found' ) ; } fileMarker = readUint16 ( ) ; while ( fileMarker !== 0xFFD9 ) { var i , j , l ; switch ( fileMarker ) { case 0xFFE0 : case 0xFFE1 : case 0xFFE2 : case 0xFFE3 : case 0xFFE4 : case 0xFFE5 : case 0xFFE6 : case 0xFFE7 : case 0xFFE8 : case 0xFFE9 : case 0xFFEA : case 0xFFEB : case 0xFFEC : case 0xFFED : case 0xFFEE : case 0xFFEF : case 0xFFFE : var appData = readDataBlock ( ) ; if ( fileMarker === 0xFFE0 ) { if ( appData [ 0 ] === 0x4A && appData [ 1 ] === 0x46 && appData [ 2 ] === 0x49 && appData [ 3 ] === 0x46 && appData [ 4 ] === 0 ) { jfif = { version : { major : appData [ 5 ] , minor : appData [ 6 ] , } , densityUnits : appData [ 7 ] , xDensity : appData [ 8 ] << 8 | appData [ 9 ] , yDensity : appData [ 10 ] << 8 | appData [ 11 ] , thumbWidth : appData [ 12 ] , thumbHeight : appData [ 13 ] , thumbData : appData . subarray ( 14 , 14 + 3 * appData [ 12 ] * appData [ 13 ] ) , } ; } } if ( fileMarker === 0xFFEE ) { if ( appData [ 0 ] === 0x41 && appData [ 1 ] === 0x64 && appData [ 2 ] === 0x6F && appData [ 3 ] === 0x62 && appData [ 4 ] === 0x65 ) { adobe = { version : appData [ 5 ] << 8 | appData [ 6 ] , flags0 : appData [ 7 ] << 8 | appData [ 8 ] , flags1 : appData [ 9 ] << 8 | appData [ 10 ] , transformCode : appData [ 11 ] , } ; } } break ; case 0xFFDB : var quantizationTablesLength = readUint16 ( ) ; var quantizationTablesEnd = quantizationTablesLength + offset - 2 ; var z ; while ( offset < quantizationTablesEnd ) { const quantizationTableSpec = data [ offset ++ ] ; const tableData = new Uint16Array ( 64 ) ; if ( quantizationTableSpec >> 4 === 0 ) { for ( j = 0 ; j < 64 ; j ++ ) { z = dctZigZag [ j ] ; tableData [ z ] = data [ offset ++ ] ; } } else if ( quantizationTableSpec >> 4 === 1 ) { for ( j = 0 ; j < 64 ; j ++ ) { z = dctZigZag [ j ] ; tableData [ z ] = readUint16 ( ) ; } } else { throw new JpegError ( 'DQT - invalid table spec' ) ; } quantizationTables [ quantizationTableSpec & 15 ] = tableData ; } break ; case 0xFFC0 : case 0xFFC1 : case 0xFFC2 : if ( frame ) { throw new JpegError ( 'Only single frame JPEGs supported' ) ; } readUint16 ( ) ; frame = { } ; frame . extended = fileMarker === 0xFFC1 ; frame . progressive = fileMarker === 0xFFC2 ; frame . precision = data [ offset ++ ] ; frame . scanLines = readUint16 ( ) ; frame . samplesPerLine = readUint16 ( ) ; frame . components = [ ] ; frame . componentIds = { } ; var componentsCount = data [ offset ++ ] , componentId ; var maxH = 0 , maxV = 0 ; for ( i = 0 ; i < componentsCount ; i ++ ) { componentId = data [ offset ] ; const h = data [ offset + 1 ] >> 4 ; const v = data [ offset + 1 ] & 15 ; if ( maxH < h ) { maxH = h ; } if ( maxV < v ) { maxV = v ; } const qId = data [ offset + 2 ] ; l = frame . components . push ( { h : h , v : v , quantizationId : qId , quantizationTable : null , } ) ; frame . componentIds [ componentId ] = l - 1 ; offset += 3 ; } frame . maxH = maxH ; frame . maxV = maxV ; prepareComponents ( frame ) ; break ; case 0xFFC4 : var huffmanLength = readUint16 ( ) ; for ( i = 2 ; i < huffmanLength ; ) { const huffmanTableSpec = data [ offset ++ ] ; const codeLengths = new Uint8Array ( 16 ) ; let codeLengthSum = 0 ; for ( j = 0 ; j < 16 ; j ++ , offset ++ ) { codeLengthSum += codeLengths [ j ] = data [ offset ] ; } const huffmanValues = new Uint8Array ( codeLengthSum ) ; for ( j = 0 ; j < codeLengthSum ; j ++ , offset ++ ) { huffmanValues [ j ] = data [ offset ] ; } i += 17 + codeLengthSum ; ( huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC ) [ huffmanTableSpec & 15 ] = buildHuffmanTable ( codeLengths , huffmanValues ) ; } break ; case 0xFFDD : readUint16 ( ) ; resetInterval = readUint16 ( ) ; break ; case 0xFFDA : readUint16 ( ) ; var selectorsCount = data [ offset ++ ] ; var components = [ ] , component ; for ( i = 0 ; i < selectorsCount ; i ++ ) { const componentIndex = frame . componentIds [ data [ offset ++ ] ] ; component = frame . components [ componentIndex ] ; const tableSpec = data [ offset ++ ] ; component . huffmanTableDC = huffmanTablesDC [ tableSpec >> 4 ] ; component . huffmanTableAC = huffmanTablesAC [ tableSpec & 15 ] ; components . push ( component ) ; } var spectralStart = data [ offset ++ ] ; var spectralEnd = data [ offset ++ ] ; var successiveApproximation = data [ offset ++ ] ; var processed = decodeScan ( data , offset , frame , components , resetInterval , spectralStart , spectralEnd , successiveApproximation >> 4 , successiveApproximation & 15 ) ; offset += processed ; break ; case 0xFFFF : if ( data [ offset ] !== 0xFF ) { offset -- ; } break ; default : if ( data [ offset - 3 ] === 0xFF && data [ offset - 2 ] >= 0xC0 && data [ offset - 2 ] <= 0xFE ) { offset -= 3 ; break ; } throw new JpegError ( 'unknown marker ' + fileMarker . toString ( 16 ) ) ; } fileMarker = readUint16 ( ) ; } this . width = frame . samplesPerLine ; this . height = frame . scanLines ; this . jfif = jfif ; this . adobe = adobe ; this . components = [ ] ; for ( i = 0 ; i < frame . components . length ; i ++ ) { component = frame . components [ i ] ; const quantizationTable = quantizationTables [ component . quantizationId ] ; if ( quantizationTable ) { component . quantizationTable = quantizationTable ; } this . components . push ( { output : buildComponentData ( frame , component ) , scaleX : component . h / frame . maxH , scaleY : component . v / frame . maxV , blocksPerLine : component . blocksPerLine , blocksPerColumn : component . blocksPerColumn , } ) ; } this . numComponents = this . components . length ; } getData ( imageData ) { const data = imageData . data ; const components = this . components ; const lineData = new Uint8Array ( ( components [ 0 ] . blocksPerLine << 3 ) * components [ 0 ] . blocksPerColumn * 8 ) ; [ components [ 0 ] , components [ 2 ] ] = [ components [ 2 ] , components [ 0 ] ] ; for ( let i = 0 , numComponents = components . length ; i < numComponents ; i ++ ) { const component = components [ i ] ; const blocksPerLine = component . blocksPerLine ; const blocksPerColumn = component . blocksPerColumn ; const samplesPerLine = blocksPerLine << 3 ; var j , k , ll = 0 ; var lineOffset = 0 ; for ( let blockRow = 0 ; blockRow < blocksPerColumn ; blockRow ++ ) { const scanLine = blockRow << 3 ; for ( let blockCol = 0 ; blockCol < blocksPerLine ; blockCol ++ ) { const bufferOffset = getBlockBufferOffset ( component , blockRow , blockCol ) ; let offset2 = 0 , sample = blockCol << 3 ; for ( j = 0 ; j < 8 ; j ++ ) { var lineOffset = ( scanLine + j ) * samplesPerLine ; for ( k = 0 ; k < 8 ; k ++ ) { lineData [ lineOffset + sample + k ] = component . output [ bufferOffset + offset2 ++ ] ; } } } } let offset = i ; for ( let y = 0 ; y < this . height ; y ++ ) { for ( let x = 0 ; x < this . width ; x ++ ) { data [ offset ] = lineData [ y * samplesPerLine + x ] ; offset += numComponents ; } } } return data ; } }