commit
33fa4e6390
6 changed files with 27 additions and 37 deletions
|
@ -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:
|
||||||
|
|
|
@ -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.}
|
||||||
|
|
|
@ -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")
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue