Merge pull request #419 from guzba/master

small jpeg things
This commit is contained in:
treeform 2022-05-12 18:27:13 -07:00 committed by GitHub
commit 33fa4e6390
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 27 additions and 37 deletions

View file

@ -65,7 +65,7 @@ proc encodeImage*(image: Image, fileFormat: FileFormat): string {.raises: [Pixie
of PngFormat: of PngFormat:
image.encodePng() image.encodePng()
of JpgFormat: of JpgFormat:
image.encodeJpg() raise newException(PixieError, "Unsupported file format")
of BmpFormat: of BmpFormat:
image.encodeBmp() image.encodeBmp()
of QoiFormat: of QoiFormat:

View file

@ -63,7 +63,7 @@ type
channel: Mask channel: Mask
DecoderState = object DecoderState = object
buffer: seq[uint8] buffer: string
pos, bitCount: int pos, bitCount: int
bits: uint32 bits: uint32
hitEnd: bool hitEnd: bool
@ -97,7 +97,7 @@ proc readUint8(state: var DecoderState): uint8 =
## Reads a byte from the input stream. ## Reads a byte from the input stream.
if state.pos >= state.buffer.len: if state.pos >= state.buffer.len:
failInvalid() failInvalid()
result = state.buffer[state.pos] result = cast[uint8](state.buffer[state.pos])
inc state.pos inc state.pos
proc readUint16be(state: var DecoderState): uint16 = proc readUint16be(state: var DecoderState): uint16 =
@ -472,8 +472,6 @@ proc getBitsAsUnsignedInt(state: var DecoderState, n: int): int =
state.bitCount -= n state.bitCount -= n
return k.int return k.int
{.push overflowChecks: off, rangeChecks: off.}
proc decodeRegularBlock( proc decodeRegularBlock(
state: var DecoderState, component: int, data: var array[64, int16] state: var DecoderState, component: int, data: var array[64, int16]
) = ) =
@ -489,7 +487,7 @@ proc decodeRegularBlock(
state.getBitsAsSignedInt(t) state.getBitsAsSignedInt(t)
dc = state.components[component].dcPred + diff dc = state.components[component].dcPred + diff
state.components[component].dcPred = dc state.components[component].dcPred = dc
data[0] = dc.int16 data[0] = cast[int16](dc)
var i = 1 var i = 1
while true: while true:
@ -506,7 +504,7 @@ proc decodeRegularBlock(
if i notin 0 ..< 64: if i notin 0 ..< 64:
failInvalid() failInvalid()
let zig = deZigZag[i] let zig = deZigZag[i]
data[zig] = state.getBitsAsSignedInt(s.int).int16 data[zig] = cast[int16](state.getBitsAsSignedInt(s.int))
inc i inc i
if not(i < 64): if not(i < 64):
@ -531,11 +529,11 @@ proc decodeProgressiveBlock(
let let
dc = state.components[component].dcPred + diff dc = state.components[component].dcPred + diff
state.components[component].dcPred = dc state.components[component].dcPred = dc
data[0] = (dc * (1 shl state.successiveApproxLow)).int16 data[0] = cast[int16](dc * (1 shl state.successiveApproxLow))
else: else:
if getBit(state) != 0: if getBit(state) != 0:
data[0] += (1 shl state.successiveApproxLow).int16 data[0] = cast[int16](data[0] + (1 shl state.successiveApproxLow))
proc decodeProgressiveContinuationBlock( proc decodeProgressiveContinuationBlock(
state: var DecoderState, component: int, data: var array[64, int16] state: var DecoderState, component: int, data: var array[64, int16]
@ -576,7 +574,7 @@ proc decodeProgressiveContinuationBlock(
inc k inc k
if s >= 15: if s >= 15:
failInvalid() failInvalid()
data[zig] = (state.getBitsAsSignedInt(s.int) * (1 shl shift)).int16 data[zig] = cast[int16](state.getBitsAsSignedInt(s.int) * (1 shl shift))
if not(k <= state.spectralEnd): if not(k <= state.spectralEnd):
break break
@ -592,9 +590,9 @@ proc decodeProgressiveContinuationBlock(
if state.getBit() != 0: if state.getBit() != 0:
if (data[zig] and bit) == 0: if (data[zig] and bit) == 0:
if data[zig] > 0: if data[zig] > 0:
data[zig] += bit.int16 data[zig] = cast[int16](data[zig] + bit)
else: else:
data[zig] -= bit.int16 data[zig] = cast[int16](data[zig] - bit)
else: else:
var k = state.spectralStart var k = state.spectralStart
while true: while true:
@ -628,12 +626,12 @@ proc decodeProgressiveContinuationBlock(
if getBit(state) != 0: if getBit(state) != 0:
if (data[zig] and bit) == 0: if (data[zig] and bit) == 0:
if data[zig] > 0: if data[zig] > 0:
data[zig] += bit.int16 data[zig] = cast[int16](data[zig] + bit)
else: else:
data[zig] -= bit.int16 data[zig] = cast[int16](data[zig] - bit)
else: else:
if r == 0: if r == 0:
data[zig] = s.int16 data[zig] = cast[int16](s)
break break
dec r dec r
@ -679,6 +677,8 @@ template idct1D(s0, s1, s2, s3, s4, s5, s6, s7: int32) =
t1 += p2 + p4 t1 += p2 + p4
t0 += p1 + p3 t0 += p1 + p3
{.push overflowChecks: off, rangeChecks: off.}
proc idctBlock(component: var Component, offset: int, data: array[64, int16]) = proc idctBlock(component: var Component, offset: int, data: array[64, int16]) =
## Inverse discrete cosine transform whole block. ## Inverse discrete cosine transform whole block.
var values: array[64, int32] var values: array[64, int32]
@ -755,6 +755,8 @@ proc idctBlock(component: var Component, offset: int, data: array[64, int16]) =
component.channel.data[outPos + 3] = clampByte((x3 + t0) shr 17) component.channel.data[outPos + 3] = clampByte((x3 + t0) shr 17)
component.channel.data[outPos + 4] = clampByte((x3 - t0) shr 17) component.channel.data[outPos + 4] = clampByte((x3 - t0) shr 17)
{.pop.}
proc decodeBlock(state: var DecoderState, comp, row, column: int) = proc decodeBlock(state: var DecoderState, comp, row, column: int) =
## Decodes a block. ## Decodes a block.
var data {.byaddr.} = state.components[comp].blocks[row][column] var data {.byaddr.} = state.components[comp].blocks[row][column]
@ -773,8 +775,8 @@ template checkReset(state: var DecoderState) =
if state.bitCount < 24: if state.bitCount < 24:
state.fillBits() state.fillBits()
if state.buffer[state.pos] == 0xFF: if state.buffer[state.pos] == 0xFF.char:
if state.buffer[state.pos+1] in {0xD0 .. 0xD7}: if state.buffer[state.pos+1] in {0xD0.char .. 0xD7.char}:
state.pos += 2 state.pos += 2
else: else:
failInvalid("did not get expected reset marker") failInvalid("did not get expected reset marker")
@ -821,7 +823,7 @@ proc quantizationAndIDCTPass(state: var DecoderState) =
let qTableId = state.components[comp].quantizationTableId let qTableId = state.components[comp].quantizationTableId
if qTableId.int notin 0 ..< state.quantizationTables.len: if qTableId.int notin 0 ..< state.quantizationTables.len:
failInvalid() failInvalid()
data[i] = data[i] * state.quantizationTables[qTableId][i].int16 data[i] = cast[int16](data[i] * state.quantizationTables[qTableId][i].int32)
state.components[comp].idctBlock( state.components[comp].idctBlock(
state.components[comp].widthStride * column * 8 + row * 8, state.components[comp].widthStride * column * 8 + row * 8,
@ -888,8 +890,6 @@ proc grayScaleToRgbx(gray: uint8): ColorRGBX =
result.b = g result.b = g
result.a = 255 result.a = 255
{.pop.}
proc buildImage(state: var DecoderState): Image = proc buildImage(state: var DecoderState): Image =
## Takes a jpeg image object and builds a pixie Image from it. ## Takes a jpeg image object and builds a pixie Image from it.
@ -929,7 +929,7 @@ proc buildImage(state: var DecoderState): Image =
else: else:
failInvalid() failInvalid()
proc decodeJpeg*(data: seq[uint8]): Image {.raises: [PixieError].} = proc decodeJpeg*(data: string): Image {.raises: [PixieError].} =
## Decodes the JPEG into an Image. ## Decodes the JPEG into an Image.
var state = DecoderState() var state = DecoderState()
@ -994,11 +994,5 @@ proc decodeJpeg*(data: seq[uint8]): Image {.raises: [PixieError].} =
state.buildImage() state.buildImage()
proc decodeJpeg*(data: string): Image {.inline, raises: [PixieError].} =
decodeJpeg(cast[seq[uint8]](data))
proc encodeJpeg*(image: Image): string =
raise newException(PixieError, "Encoding JPG not supported yet")
when defined(release): when defined(release):
{.pop.} {.pop.}

View file

@ -20,7 +20,3 @@ proc decodeJpg*(data: string): Image {.inline, raises: [PixieError].} =
result = newImage(width, height) result = newImage(width, height)
copyMem(result.data[0].addr, pixels[0].unsafeAddr, pixels.len) copyMem(result.data[0].addr, pixels[0].unsafeAddr, pixels.len)
proc encodeJpg*(image: Image): string {.raises: [PixieError].} =
## Encodes Image into a JPEG data string.
raise newException(PixieError, "Encoding JPG not supported yet")

View file

@ -1,6 +1,6 @@
import benchy, jpegsuite, pixie/fileformats/jpg import benchy, jpegsuite, pixie/fileformats/jpg, strformat
for file in jpegSuiteFiles: for file in jpegSuiteFiles:
let data = readFile(file) let data = readFile(file)
timeIt "jpeg " & $(len(data) div 1024) & "k decode": timeIt &"jpeg {(data.len div 1024)}k decode", 10000:
discard decodeJpg(data) discard decodeJpg(data)

View file

@ -1,4 +1,4 @@
import jpegsuite, pixie/common, pixie/fileformats/jpg, random, strformat import jpegsuite, pixie/common, pixie/fileformats/jpeg, random, strformat
randomize() randomize()
@ -12,13 +12,13 @@ for i in 0 ..< 10_000:
echo &"{i} {file} {pos} {value}" echo &"{i} {file} {pos} {value}"
try: try:
let img = decodeJpg(data) let img = decodeJpeg(data)
doAssert img.height > 0 and img.width > 0 doAssert img.height > 0 and img.width > 0
except PixieError: except PixieError:
discard discard
data = data[0 ..< pos] data = data[0 ..< pos]
try: try:
let img = decodeJpg(data) let img = decodeJpeg(data)
doAssert img.height > 0 and img.width > 0 doAssert img.height > 0 and img.width > 0
except PixieError: except PixieError:
discard discard

View file

@ -9,7 +9,7 @@ for file in jpegSuiteFiles:
let genFile = file.replace("masters", "generated").replace(".jpg", ".png") let genFile = file.replace("masters", "generated").replace(".jpg", ".png")
img.writeFile(genFile) img.writeFile(genFile)
if execShellCmd(&"convert {file} {genFile}") != 0: if execShellCmd(&"magick {file} {genFile}") != 0:
echo "fail" echo "fail"
var img2 = readImage(genFile) var img2 = readImage(genFile)