From c280cd78641c75d7acb2390aba5a850841b01e8f Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 12 May 2022 20:14:16 -0500 Subject: [PATCH 1/9] proc --- src/pixie/fileformats/jpeg.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index f3f68c2..2a753d6 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -768,7 +768,7 @@ proc decodeBlock(state: var DecoderState, comp, row, column: int) = else: state.decodeRegularBlock(comp, data) -template checkReset(state: var DecoderState) = +proc checkReset(state: var DecoderState) = ## Check if we might have run into a reset marker, then deal with it. dec state.todo if state.todo <= 0: From f03b2e2ef3d7834f9909c62bbdafc9435ddccd16 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 12 May 2022 22:46:57 -0500 Subject: [PATCH 2/9] huffman stuff --- src/pixie/fileformats/jpeg.nim | 39 ++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index 2a753d6..5927d88 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -143,6 +143,7 @@ proc decodeDQT(state: var DecoderState) = proc buildHuffman(huffman: var Huffman, counts: array[16, uint8]) = ## Builds the huffman data structure. block: + # JPEG spec page 51 var k: int for i in 0.uint8 ..< 16: for j in 0.uint8 ..< counts[i]: @@ -152,14 +153,15 @@ proc buildHuffman(huffman: var Huffman, counts: array[16, uint8]) = inc k huffman.sizes[k] = 0 - var code, j: int + # JPEG spec page 52 + var code, k: int for i in 1.uint8 .. 16: - huffman.deltas[i] = j - code - if huffman.sizes[j] == i: - while huffman.sizes[j] == i: - huffman.codes[j] = code.uint16 + huffman.deltas[i] = k - code + if huffman.sizes[k] == i: + while huffman.sizes[k] == i: + huffman.codes[k] = code.uint16 inc code - inc j + inc k if code - 1 >= 1 shl i: failInvalid() huffman.maxCodes[i] = code shl (16 - i) @@ -169,12 +171,12 @@ proc buildHuffman(huffman: var Huffman, counts: array[16, uint8]) = for i in 0 ..< huffman.fast.len: huffman.fast[i] = 255 - for i in 0 ..< j: + for i in 0 ..< k: let size = huffman.sizes[i] if size <= fastBits: let fast = huffman.codes[i].int shl (fastBits - size) - for k in 0 ..< 1 shl (fastBits - size): - huffman.fast[fast + k] = i.uint8 + for j in 0 ..< 1 shl (fastBits - size): + huffman.fast[fast + j] = i.uint8 proc decodeDHT(state: var DecoderState) = ## Decode Define Huffman Table @@ -405,34 +407,35 @@ proc huffmanDecode(state: var DecoderState, tableCurrent, table: int): uint8 = if state.bitCount < 16: state.fillBits() + var huffman {.byaddr.} = state.huffmanTables[tableCurrent][table] + let fastId = (state.bits shr (32 - fastBits)) and ((1 shl fastBits) - 1) - fast = state.huffmanTables[tableCurrent][table].fast[fastId] + fast = huffman.fast[fastId] + if fast < 255: - let size = state.huffmanTables[tableCurrent][table].sizes[fast].int + let size = huffman.sizes[fast].int if size > state.bitCount: failInvalid() state.bits = state.bits shl size state.bitCount -= size - return state.huffmanTables[tableCurrent][table].symbols[fast] + return huffman.symbols[fast] var tmp = (state.bits shr 16).int i = fastBits + 1 - while i < state.huffmanTables[tableCurrent][table].maxCodes.len: - if tmp < state.huffmanTables[tableCurrent][table].maxCodes[i]: + while i < huffman.maxCodes.len: + if tmp < huffman.maxCodes[i]: break inc i if i == 17 or i > state.bitCount: failInvalid() - let symbolId = (state.bits shr (32 - i)).int + - state.huffmanTables[tableCurrent][table].deltas[i] - + let symbolId = (state.bits shr (32 - i)).int + huffman.deltas[i] state.bits = state.bits shl i state.bitCount -= i - return state.huffmanTables[tableCurrent][table].symbols[symbolId] + huffman.symbols[symbolId] template lrot(value: uint32, shift: int): uint32 = ## Left rotate - used for huffman decoding. From 6cfb98553cf5e1bc1bbaab7250d367a3498351b1 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 12 May 2022 22:51:33 -0500 Subject: [PATCH 3/9] rename --- src/pixie/fileformats/jpeg.nim | 74 +++++++++++++++++----------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index 5927d88..ec601ec 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -64,8 +64,8 @@ type DecoderState = object buffer: string - pos, bitCount: int - bits: uint32 + pos, bitsBuffered: int + bitBuffer: uint32 hitEnd: bool imageHeight, imageWidth: int quantizationTables: array[4, array[64, uint8]] @@ -306,8 +306,8 @@ proc decodeSOF2(state: var DecoderState) = proc reset(state: var DecoderState) = ## Rests the decoder state need for reset markers. - state.bits = 0 - state.bitCount = 0 + state.bitBuffer = 0 + state.bitsBuffered = 0 for component in 0 ..< state.components.len: state.components[component].dcPred = 0 state.hitEnd = false @@ -381,7 +381,7 @@ proc decodeSOS(state: var DecoderState) = if len != 0: failInvalid() -proc fillBits(state: var DecoderState) = +proc fillBitBuffer(state: var DecoderState) = ## When we are low on bits, we need to call this to populate some more. while true: let b = if state.hitEnd: @@ -396,45 +396,45 @@ proc fillBits(state: var DecoderState) = dec state.pos state.hitEnd = true return - state.bits = state.bits or (b shl (24 - state.bitCount)) - state.bitCount += 8 + state.bitBuffer = state.bitBuffer or (b shl (24 - state.bitsBuffered)) + state.bitsBuffered += 8 - if not(state.bitCount <= 24): + if not(state.bitsBuffered <= 24): break proc huffmanDecode(state: var DecoderState, tableCurrent, table: int): uint8 = ## Decode a uint8 from the huffman table. - if state.bitCount < 16: - state.fillBits() + if state.bitsBuffered < 16: + state.fillBitBuffer() var huffman {.byaddr.} = state.huffmanTables[tableCurrent][table] let - fastId = (state.bits shr (32 - fastBits)) and ((1 shl fastBits) - 1) + fastId = (state.bitBuffer shr (32 - fastBits)) and ((1 shl fastBits) - 1) fast = huffman.fast[fastId] if fast < 255: let size = huffman.sizes[fast].int - if size > state.bitCount: + if size > state.bitsBuffered: failInvalid() - state.bits = state.bits shl size - state.bitCount -= size + state.bitBuffer = state.bitBuffer shl size + state.bitsBuffered -= size return huffman.symbols[fast] var - tmp = (state.bits shr 16).int + tmp = (state.bitBuffer shr 16).int i = fastBits + 1 while i < huffman.maxCodes.len: if tmp < huffman.maxCodes[i]: break inc i - if i == 17 or i > state.bitCount: + if i == 17 or i > state.bitsBuffered: failInvalid() - let symbolId = (state.bits shr (32 - i)).int + huffman.deltas[i] - state.bits = state.bits shl i - state.bitCount -= i + let symbolId = (state.bitBuffer shr (32 - i)).int + huffman.deltas[i] + state.bitBuffer = state.bitBuffer shl i + state.bitsBuffered -= i huffman.symbols[symbolId] template lrot(value: uint32, shift: int): uint32 = @@ -443,36 +443,36 @@ template lrot(value: uint32, shift: int): uint32 = proc getBit(state: var DecoderState): int = ## Get a single bit. - if state.bitCount < 1: - state.fillBits() - let k = state.bits - state.bits = state.bits shl 1 - dec state.bitCount + if state.bitsBuffered < 1: + state.fillBitBuffer() + let k = state.bitBuffer + state.bitBuffer = state.bitBuffer shl 1 + dec state.bitsBuffered return (k.int and 0x80000000.int) proc getBitsAsSignedInt(state: var DecoderState, n: int): int = ## Get n number of bits as a signed integer. if n notin 0 .. 16: failInvalid() - if state.bitCount < n: - state.fillBits() - let sign = cast[int32](state.bits) shr 31 - var k = lrot(state.bits, n) - state.bits = k and (not bitMasks[n]) + if state.bitsBuffered < n: + state.fillBitBuffer() + let sign = cast[int32](state.bitBuffer) shr 31 + var k = lrot(state.bitBuffer, n) + state.bitBuffer = k and (not bitMasks[n]) k = k and bitMasks[n] - state.bitCount -= n + state.bitsBuffered -= n result = k.int + (biases[n] and (not sign)) proc getBitsAsUnsignedInt(state: var DecoderState, n: int): int = ## Get n number of bits as a unsigned integer. if n notin 0 .. 16: failInvalid() - if state.bitCount < n: - state.fillBits() - var k = lrot(state.bits, n) - state.bits = k and (not bitMasks[n]) + if state.bitsBuffered < n: + state.fillBitBuffer() + var k = lrot(state.bitBuffer, n) + state.bitBuffer = k and (not bitMasks[n]) k = k and bitMasks[n] - state.bitCount -= n + state.bitsBuffered -= n return k.int proc decodeRegularBlock( @@ -775,8 +775,8 @@ proc checkReset(state: var DecoderState) = ## Check if we might have run into a reset marker, then deal with it. dec state.todo if state.todo <= 0: - if state.bitCount < 24: - state.fillBits() + if state.bitsBuffered < 24: + state.fillBitBuffer() if state.buffer[state.pos] == 0xFF.char: if state.buffer[state.pos+1] in {0xD0.char .. 0xD7.char}: From 2991c16c9d3b0efb840d07f4b9f023cdb54c7506 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 12 May 2022 22:55:35 -0500 Subject: [PATCH 4/9] f --- src/pixie/fileformats/jpeg.nim | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index ec601ec..92b72a7 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -383,25 +383,23 @@ proc decodeSOS(state: var DecoderState) = proc fillBitBuffer(state: var DecoderState) = ## When we are low on bits, we need to call this to populate some more. - while true: - let b = if state.hitEnd: + while state.bitsBuffered < 24: + let b = + if state.hitEnd: 0.uint32 else: state.readUint8().uint32 if b == 0xFF: var c = state.readUint8() - while c == 0xFF: c = state.readUint8() + while c == 0xFF: + c = state.readUint8() if c != 0: - dec state.pos - dec state.pos + state.pos -= 2 state.hitEnd = true return state.bitBuffer = state.bitBuffer or (b shl (24 - state.bitsBuffered)) state.bitsBuffered += 8 - if not(state.bitsBuffered <= 24): - break - proc huffmanDecode(state: var DecoderState, tableCurrent, table: int): uint8 = ## Decode a uint8 from the huffman table. if state.bitsBuffered < 16: From 86f9f25011bff2c289ae8eccb05332dafd9fcd23 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 12 May 2022 23:02:20 -0500 Subject: [PATCH 5/9] f --- src/pixie/fileformats/jpeg.nim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index 92b72a7..de625b0 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -839,13 +839,13 @@ proc magnifyXBy2(mask: Mask): Mask = let n = 3 * mask.unsafe[x, y].uint16 if x == 0: result.unsafe[x * 2 + 0, y] = mask.unsafe[x, y] - result.unsafe[x * 2 + 1, y] = ((n + mask.unsafe[x+1, y].uint16 + 2) div 4).uint8 + result.unsafe[x * 2 + 1, y] = ((n + mask.unsafe[x + 1, y].uint16 + 2) div 4).uint8 elif x == mask.width - 1: - result.unsafe[x * 2 + 0, y] = ((n + mask.unsafe[x-1, y].uint16 + 2) div 4).uint8 + result.unsafe[x * 2 + 0, y] = ((n + mask.unsafe[x - 1, y].uint16 + 2) div 4).uint8 result.unsafe[x * 2 + 1, y] = mask.unsafe[x, y] else: - result.unsafe[x * 2 + 0, y] = ((n + mask.unsafe[x-1, y].uint16) div 4).uint8 - result.unsafe[x * 2 + 1, y] = ((n + mask.unsafe[x+1, y].uint16) div 4).uint8 + result.unsafe[x * 2 + 0, y] = ((n + mask.unsafe[x - 1, y].uint16) div 4).uint8 + result.unsafe[x * 2 + 1, y] = ((n + mask.unsafe[x + 1, y].uint16) div 4).uint8 proc magnifyYBy2(mask: Mask): Mask = ## Smooth magnify by power of 2 only in the Y direction. @@ -855,13 +855,13 @@ proc magnifyYBy2(mask: Mask): Mask = let n = 3 * mask.unsafe[x, y].uint16 if y == 0: result.unsafe[x, y * 2 + 0] = mask.unsafe[x, y] - result.unsafe[x, y * 2 + 1] = ((n + mask.unsafe[x, y+1].uint16 + 2) div 4).uint8 + result.unsafe[x, y * 2 + 1] = ((n + mask.unsafe[x, y + 1].uint16 + 2) div 4).uint8 elif y == mask.height - 1: - result.unsafe[x, y * 2 + 0] = ((n + mask.unsafe[x, y-1].uint16 + 2) div 4).uint8 + result.unsafe[x, y * 2 + 0] = ((n + mask.unsafe[x, y - 1].uint16 + 2) div 4).uint8 result.unsafe[x, y * 2 + 1] = mask.unsafe[x, y] else: - result.unsafe[x, y * 2 + 0] = ((n + mask.unsafe[x, y-1].uint16) div 4).uint8 - result.unsafe[x, y * 2 + 1] = ((n + mask.unsafe[x, y+1].uint16) div 4).uint8 + result.unsafe[x, y * 2 + 0] = ((n + mask.unsafe[x, y - 1].uint16) div 4).uint8 + result.unsafe[x, y * 2 + 1] = ((n + mask.unsafe[x, y + 1].uint16) div 4).uint8 proc yCbCrToRgbx(py, pcb, pcr: uint8): ColorRGBX = ## Takes a 3 component yCbCr outputs and populates image. From 8d57ba910347953f56b88a93389c1656e5b6f19e Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 12 May 2022 23:09:37 -0500 Subject: [PATCH 6/9] f --- src/pixie/fileformats/jpeg.nim | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index de625b0..c13d9b3 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -883,13 +883,9 @@ proc yCbCrToRgbx(py, pcb, pcr: uint8): ColorRGBX = result.b = clampByte(b shr 20) result.a = 255 -proc grayScaleToRgbx(gray: uint8): ColorRGBX = +proc grayScaleToRgbx(gray: uint8): ColorRGBX {.inline.} = ## Takes a single gray scale component output and populates image. - let g = gray - result.r = g - result.g = g - result.b = g - result.a = 255 + rgbx(gray, gray, gray, 255) proc buildImage(state: var DecoderState): Image = ## Takes a jpeg image object and builds a pixie Image from it. From 6f6f73382b3d923452020f9c92cba679e0dc42b7 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 12 May 2022 23:19:15 -0500 Subject: [PATCH 7/9] f --- src/pixie/fileformats/jpeg.nim | 12 ++++++------ tests/benchmark_jpeg.nim | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index c13d9b3..e181b06 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -89,7 +89,7 @@ template failInvalid(reason = "unable to load") = ## Throw exception with a reason. raise newException(PixieError, "Invalid JPEG, " & reason) -template clampByte(x): uint8 = +template clampByte(x: int32): uint8 = ## Clamp integer into byte range. clamp(x, 0, 0xFF).uint8 @@ -865,13 +865,13 @@ proc magnifyYBy2(mask: Mask): Mask = proc yCbCrToRgbx(py, pcb, pcr: uint8): ColorRGBX = ## Takes a 3 component yCbCr outputs and populates image. - template float2Fixed(x: float32): int = - (x * 4096 + 0.5).int shl 8 + template float2Fixed(x: float32): int32 = + (x * 4096 + 0.5).int32 shl 8 let - yFixed = (py.int shl 20) + (1 shl 19) - cb = pcb.int - 128 - cr = pcr.int - 128 + yFixed = (py.int32 shl 20) + (1 shl 19) + cb = pcb.int32 - 128 + cr = pcr.int32 - 128 var r = yFixed + cr * float2Fixed(1.40200) g = yFixed + diff --git a/tests/benchmark_jpeg.nim b/tests/benchmark_jpeg.nim index 40b828a..06d74f5 100644 --- a/tests/benchmark_jpeg.nim +++ b/tests/benchmark_jpeg.nim @@ -2,5 +2,5 @@ import benchy, jpegsuite, pixie/fileformats/jpg, strformat for file in jpegSuiteFiles: let data = readFile(file) - timeIt &"jpeg {(data.len div 1024)}k decode", 10000: + timeIt &"jpeg {(data.len div 1024)}k decode": discard decodeJpg(data) From 68b9d63ecdd5780714340170c50665c26fb8cd2d Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 12 May 2022 23:27:28 -0500 Subject: [PATCH 8/9] f --- src/pixie/fileformats/jpeg.nim | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index e181b06..c868275 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -283,10 +283,7 @@ proc decodeSOF0(state: var DecoderState) = component.widthStride = state.numMcuWide * component.yScale * 8 component.heightStride = state.numMcuHigh * component.xScale * 8 - - component.channel = newMask( - component.widthStride, component.heightStride - ) + component.channel = newMask(component.widthStride, component.heightStride) if state.progressive: component.widthCoeff = component.widthStride div 8 @@ -482,10 +479,11 @@ proc decodeRegularBlock( failInvalid() let - diff = if t == 0: - 0 - else: - state.getBitsAsSignedInt(t) + diff = + if t == 0: + 0 + else: + state.getBitsAsSignedInt(t) dc = state.components[component].dcPred + diff state.components[component].dcPred = dc data[0] = cast[int16](dc) @@ -787,7 +785,6 @@ proc decodeBlocks(state: var DecoderState) = ## Decodes scan data blocks that follow a SOS block. if state.scanComponents == 1: # Single component pass. - let comp = state.componentOrder[0] w = (state.components[comp].width + 7) shr 3 @@ -815,7 +812,6 @@ proc quantizationAndIDCTPass(state: var DecoderState) = let w = (state.components[comp].width + 7) shr 3 h = (state.components[comp].height + 7) shr 3 - for column in 0 ..< h: for row in 0 ..< w: var data = state.components[comp].blocks[row][column] From 01fe7326089e914edbecbfb68fa1dd8bce2f6965 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Fri, 13 May 2022 00:08:03 -0500 Subject: [PATCH 9/9] f --- src/pixie/fileformats/jpeg.nim | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index c868275..2fb59e2 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -399,11 +399,10 @@ proc fillBitBuffer(state: var DecoderState) = proc huffmanDecode(state: var DecoderState, tableCurrent, table: int): uint8 = ## Decode a uint8 from the huffman table. - if state.bitsBuffered < 16: - state.fillBitBuffer() - var huffman {.byaddr.} = state.huffmanTables[tableCurrent][table] + state.fillBitBuffer() + let fastId = (state.bitBuffer shr (32 - fastBits)) and ((1 shl fastBits) - 1) fast = huffman.fast[fastId] @@ -430,7 +429,7 @@ proc huffmanDecode(state: var DecoderState, tableCurrent, table: int): uint8 = let symbolId = (state.bitBuffer shr (32 - i)).int + huffman.deltas[i] state.bitBuffer = state.bitBuffer shl i state.bitsBuffered -= i - huffman.symbols[symbolId] + return huffman.symbols[symbolId] template lrot(value: uint32, shift: int): uint32 = ## Left rotate - used for huffman decoding.