diff --git a/src/pixie/fileformats/jpg.nim b/src/pixie/fileformats/jpg.nim index 5065d6f..acb754c 100644 --- a/src/pixie/fileformats/jpg.nim +++ b/src/pixie/fileformats/jpg.nim @@ -4,6 +4,7 @@ import pixie/common, pixie/images, strutils # See http://www.vip.sugovica.hu/Sardi/kepnezo/JPEG%20File%20Layout%20and%20Format.htm const + fastBits = 9 jpgStartOfImage* = [0xFF.uint8, 0xD8] deZigZag = [ 0.uint8, 1, 8, 16, 9, 2, 3, 10, @@ -26,9 +27,12 @@ const type Huffman = object + codes: array[256, uint16] symbols: array[256, uint8] + sizes: array[257, uint8] deltas: array[17, int] maxCodes: array[18, int] + fast: array[1 shl fastBits, uint8] ResampleProc = proc(dst, line0, line1: ptr UncheckedArray[uint8], widthPreExpansion, horizontalExpansionFactor: int @@ -113,20 +117,20 @@ proc decodeDQT(state: var DecoderState) = proc decodeDHT(state: var DecoderState) = proc buildHuffman(huffman: var Huffman, counts: array[16, uint8]) = - var sizes: array[257, uint8] block: var k: int for i in 0.uint8 ..< 16: for j in 0.uint8 ..< counts[i]: - sizes[k] = i + 1 + huffman.sizes[k] = i + 1 inc k - sizes[k] = 0 + huffman.sizes[k] = 0 var code, j: int for i in 1.uint8 .. 16: huffman.deltas[i] = j - code - if sizes[j] == i: - while sizes[j] == i: + if huffman.sizes[j] == i: + while huffman.sizes[j] == i: + huffman.codes[j] = code.uint16 inc code inc j if code - 1 >= 1 shl i: @@ -135,6 +139,16 @@ proc decodeDHT(state: var DecoderState) = code = code shl 1 huffman.maxCodes[17] = int.high + for i in 0 ..< huffman.fast.len: + huffman.fast[i] = 255 + + for i in 0 ..< j: + 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 + var len = state.readUint16be() - 2 while len > 0: let @@ -313,28 +327,39 @@ proc huffmanDecode(state: var DecoderState, tableCurrent, table: int): uint8 = if state.bitCount < 16: state.fillBits() - var - tmp = (state.bits shr 16).int - i = 1 - while i < state.huffmanTables[tableCurrent][table].maxCodes.len: - if tmp < state.huffmanTables[tableCurrent][table].maxCodes[i]: - break - inc i + let + fastId = (state.bits shr (32 - fastBits)) and ((1 shl fastBits) - 1) + fast = state.huffmanTables[tableCurrent][table].fast[fastId] + if fast < 255: + let size = state.huffmanTables[tableCurrent][table].sizes[fast].int + if size > state.bitCount: + failInvalid() - if i == 17 or i > state.bitCount: - failInvalid() + result = state.huffmanTables[tableCurrent][table].symbols[fast] + state.bits = state.bits shl size + state.bitCount -= size + else: + 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]: + break + inc i - let symbolId = (state.bits shr (32 - i)).int + - state.huffmanTables[tableCurrent][table].deltas[i] - result = state.huffmanTables[tableCurrent][table].symbols[symbolId] + if i == 17 or i > state.bitCount: + failInvalid() - state.bits = state.bits shl i - state.bitCount -= i + let symbolId = (state.bits shr (32 - i)).int + + state.huffmanTables[tableCurrent][table].deltas[i] + result = state.huffmanTables[tableCurrent][table].symbols[symbolId] + state.bits = state.bits shl i + state.bitCount -= i template lrot(value: uint32, shift: int): uint32 = (value shl shift) or (value shr (32 - shift)) -proc extendReceive(state: var DecoderState, t: int): int = +proc extendReceive(state: var DecoderState, t: int): int {.inline.} = if state.bitCount < t: state.fillBits() @@ -345,7 +370,9 @@ proc extendReceive(state: var DecoderState, t: int): int = state.bitCount -= t result = k.int + (biases[t] and (not sign)) -proc decodeImageBlock(state: var DecoderState, component: int): array[64, int16] = +proc decodeImageBlock( + state: var DecoderState, component: int +): array[64, int16] = let t = state.huffmanDecode(0, state.components[component].huffmanDC).int if t < 0: failInvalid() diff --git a/src/pixie/fileformats/png.nim b/src/pixie/fileformats/png.nim index 4fa73b1..eb43363 100644 --- a/src/pixie/fileformats/png.nim +++ b/src/pixie/fileformats/png.nim @@ -436,6 +436,8 @@ proc encodePng*( else: raise newException(PixieError, "Invalid PNG number of channels") + let data = cast[ptr UncheckedArray[uint8]](data) + # Add the PNG file signature result.add([137.uint8, 80, 78, 71, 13, 10, 26, 10]) @@ -456,7 +458,6 @@ proc encodePng*( for x in 0 ..< width * channels: # Move through the image data byte-by-byte let - data = cast[ptr UncheckedArray[uint8]](data) dataPos = y * width * channels + x filteredPos = y * width * channels + y + 1 + x var left, up: int diff --git a/tests/benchmark_jpg.nim b/tests/benchmark_jpg.nim new file mode 100644 index 0000000..df40317 --- /dev/null +++ b/tests/benchmark_jpg.nim @@ -0,0 +1,14 @@ +import pixie/fileformats/jpg, pixie/fileformats/stb_image/stb_image, fidget/opengl/perf + +let data = readFile("tests/images/jpg/jpeg420exif.jpg") + +timeIt "pixie decode": + for i in 0 ..< 20: + discard decodeJpg(cast[seq[uint8]](data)) + +timeIt "stb_image decode": + for i in 0 ..< 20: + var + width: int + height: int + discard loadFromMemory(cast[seq[uint8]](data), width, height) diff --git a/tests/benchmark_png.nim b/tests/benchmark_png.nim index e03ec3e..4e91c68 100644 --- a/tests/benchmark_png.nim +++ b/tests/benchmark_png.nim @@ -1,7 +1,7 @@ import pixie/fileformats/png, stb_image/read as stbi, stb_image/write as stbr, fidget/opengl/perf, nimPNG -let data = readFile("tests/images/lenna.png") +let data = readFile("tests/images/png/lenna.png") timeIt "pixie decode": for i in 0 ..< 100: diff --git a/tests/test_jpg.nim b/tests/test_jpg.nim index 13798e2..7ba1a26 100644 --- a/tests/test_jpg.nim +++ b/tests/test_jpg.nim @@ -12,8 +12,8 @@ proc stbDecode*(data: string): Image = let original = readFile("tests/images/jpg/jpeg420exif.jpg") - pixieDecoded = decodeJpg(original) stbDecoded = stbDecode(original) + pixieDecoded = decodeJpg(original) doAssert pixieDecoded.width == stbDecoded.width doAssert pixieDecoded.height == stbDecoded.height