diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index 955f300..bc870ae 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -1,4 +1,4 @@ -import pixie/common, pixie/images, strutils, tables +import pixie/common, pixie/images, sequtils, strutils # This JPEG decoder is loosely based on stb_image which is public domain. @@ -69,6 +69,7 @@ type dcPred: int widthCoeff, heightCoeff: int data, coeff, lineBuf: seq[uint8] + blocks: seq[seq[array[64, int16]]] DecoderState = object buffer: seq[uint8] @@ -86,8 +87,6 @@ type mcuWidth, mcuHeight, numMcuWide, numMcuHigh: int componentOrder: seq[int] progressive: bool - decodedBlocks: Table[(int, int, int), array[64, int16]] - restartInterval: int todo: int eobRun: int @@ -287,6 +286,14 @@ proc decodeSOF0(state: var DecoderState) = state.maxXScale - 1 ) div state.maxXScale + # Allocate block data structures. + state.components[i].blocks = newSeqWith( + state.components[i].width, + newSeq[array[64, int16]]( + state.components[i].height + ) + ) + state.components[i].widthStride = state.numMcuWide * state.components[i].yScale * 8 state.components[i].heightStride = @@ -766,12 +773,7 @@ proc idctBlock(component: var Component, offset: int, data: array[64, int16]) = proc decodeBlock(state: var DecoderState, comp, row, column: int) = ## Decodes a block. - var data: array[64, int16] - if (comp, row, column) in state.decodedBlocks: - try: - data = state.decodedBlocks[(comp, row, column)] - except: - failInvalid() + var data = state.components[comp].blocks[row][column] if state.progressive: if state.spectralStart == 0: state.decodeProgressiveBlock(comp, data) @@ -779,10 +781,7 @@ proc decodeBlock(state: var DecoderState, comp, row, column: int) = state.decodeProgressiveContinuationBlock(comp, data) else: state.decodeRegularBlock(comp, data) - try: - state.decodedBlocks[(comp, row, column)] = data - except: - failInvalid() + state.components[comp].blocks[row][column] = data template checkReset(state: var DecoderState) = dec state.todo @@ -807,11 +806,8 @@ proc decodeBlocks(state: var DecoderState) = comp = state.componentOrder[0] w = (state.components[comp].width + 7) shr 3 h = (state.components[comp].height + 7) shr 3 - for j in 0 ..< h: - for i in 0 ..< w: - let - row = i * 8 - column = j * 8 + for column in 0 ..< h: + for row in 0 ..< w: state.decodeBlock(comp, row, column) state.checkReset() else: @@ -822,8 +818,8 @@ proc decodeBlocks(state: var DecoderState) = for j in 0 ..< state.components[comp].yScale: for i in 0 ..< state.components[comp].xScale: let - row = (x * state.components[comp].xScale + i) * 8 - column = (y * state.components[comp].yScale + j) * 8 + row = (x * state.components[comp].xScale + i) + column = (y * state.components[comp].yScale + j) state.decodeBlock(comp, row, column) state.checkReset() else: @@ -834,8 +830,8 @@ proc decodeBlocks(state: var DecoderState) = for j in 0 ..< state.components[comp].xScale: for i in 0 ..< state.components[comp].yScale: let - row = (x * state.components[comp].yScale + i) * 8 - column = (y * state.components[comp].xScale + j) * 8 + row = (x * state.components[comp].yScale + i) + column = (y * state.components[comp].xScale + j) state.decodeBlock(comp, row, column) state.checkReset() @@ -846,19 +842,9 @@ proc quantizationAndIDCTPass(state: var DecoderState) = w = (state.components[comp].width + 7) shr 3 h = (state.components[comp].height + 7) shr 3 - for j in 0 ..< h: - for i in 0 ..< w: - let - row = i * 8 - column = j * 8 - - var data: array[64, int16] - - if (comp, row, column) in state.decodedBlocks: - try: - data = state.decodedBlocks[(comp, row, column)] - except: - failInvalid() + for column in 0 ..< h: + for row in 0 ..< w: + var data = state.components[comp].blocks[row][column] for i in 0 ..< 64: let qTableId = state.components[comp].quantizationTableId @@ -867,7 +853,7 @@ proc quantizationAndIDCTPass(state: var DecoderState) = data[i] = clampInt16(data[i] * state.quantizationTables[qTableId][i].int) state.components[comp].idctBlock( - state.components[comp].widthStride * column + row, + state.components[comp].widthStride * column * 8 + row * 8, data ) diff --git a/tests/benchmark_jpeg.nim b/tests/benchmark_jpeg.nim index 3bbe13e..0558438 100644 --- a/tests/benchmark_jpeg.nim +++ b/tests/benchmark_jpeg.nim @@ -1,6 +1,45 @@ import benchy, pixie/fileformats/jpg -let data = readFile("tests/fileformats/jpeg/jpeg420exif.jpg") +var files = @[ + "tests/fileformats/jpeg/master/red.jpg", + "tests/fileformats/jpeg/master/green.jpg", + "tests/fileformats/jpeg/master/blue.jpg", + "tests/fileformats/jpeg/master/white.jpg", + "tests/fileformats/jpeg/master/black.jpg", -timeIt "pixie decode": - discard decodeJpg(data) + "tests/fileformats/jpeg/master/8x8.jpg", + "tests/fileformats/jpeg/master/8x8_progressive.jpg", + + "tests/fileformats/jpeg/master/16x16.jpg", + "tests/fileformats/jpeg/master/16x16_progressive.jpg", + + "tests/fileformats/jpeg/master/quality_01.jpg", + "tests/fileformats/jpeg/master/quality_10.jpg", + "tests/fileformats/jpeg/master/quality_25.jpg", + "tests/fileformats/jpeg/master/quality_50.jpg", + "tests/fileformats/jpeg/master/quality_100.jpg", + + "tests/fileformats/jpeg/master/cat_4_4_4.jpg", + "tests/fileformats/jpeg/master/cat_4_4_4.jpg", + "tests/fileformats/jpeg/master/cat_4_2_2.jpg", + "tests/fileformats/jpeg/master/cat_4_2_0.jpg", + "tests/fileformats/jpeg/master/cat_4_1_1.jpg", + "tests/fileformats/jpeg/master/cat_4_4_4_progressive.jpg", + "tests/fileformats/jpeg/master/cat_restart_markers_5.jpg", + "tests/fileformats/jpeg/master/cat_restart_markers_5_progressive.jpg", + + "tests/fileformats/jpeg/master/mandrill.jpg", + "tests/fileformats/jpeg/master/exif_overrun.jpg", + "tests/fileformats/jpeg/master/grayscale_test.jpg", + "tests/fileformats/jpeg/master/progressive.jpg", + + "tests/fileformats/jpeg/master/testimg.jpg", + "tests/fileformats/jpeg/master/testimgp.jpg", + "tests/fileformats/jpeg/master/testorig.jpg", + "tests/fileformats/jpeg/master/testprog.jpg", +] + +for file in files: + let data = readFile(file) + timeIt "jpeg " & $(len(data) div 1024) & "k decode": + discard decodeJpg(data)