nim jpg stuff
This commit is contained in:
parent
c07cf13ae4
commit
89fd898764
2 changed files with 86 additions and 40 deletions
|
@ -1,7 +1,6 @@
|
||||||
import pixie/common, pixie/images, strutils
|
import pixie/common, pixie/images, strutils
|
||||||
|
|
||||||
# See https://github.com/nothings/stb/blob/master/stb_image.h
|
# See https://github.com/nothings/stb/blob/master/stb_image.h
|
||||||
# See http://www.vip.sugovica.hu/Sardi/kepnezo/JPEG%20File%20Layout%20and%20Format.htm
|
|
||||||
|
|
||||||
const
|
const
|
||||||
fastBits = 9
|
fastBits = 9
|
||||||
|
@ -51,7 +50,8 @@ type
|
||||||
widthStride, heightStride: int
|
widthStride, heightStride: int
|
||||||
huffmanDC, huffmanAC: int
|
huffmanDC, huffmanAC: int
|
||||||
dcPred: int
|
dcPred: int
|
||||||
data, lineBuf: seq[uint8]
|
widthCoeff, heightCoeff: int
|
||||||
|
data, coeff, lineBuf: seq[uint8]
|
||||||
|
|
||||||
DecoderState = object
|
DecoderState = object
|
||||||
buffer: seq[uint8]
|
buffer: seq[uint8]
|
||||||
|
@ -61,10 +61,13 @@ type
|
||||||
quantizationTables: array[4, array[64, uint8]]
|
quantizationTables: array[4, array[64, uint8]]
|
||||||
huffmanTables: array[2, array[4, Huffman]] # 0 = DC, 1 = AC
|
huffmanTables: array[2, array[4, Huffman]] # 0 = DC, 1 = AC
|
||||||
components: array[3, Component]
|
components: array[3, Component]
|
||||||
|
scanComponents: int
|
||||||
|
spectralStart, spectralEnd: int
|
||||||
|
successiveApproxLow, successiveApproxHigh: int
|
||||||
maxHorizontalSamplingFactor, maxVerticalSamplingFactor: int
|
maxHorizontalSamplingFactor, maxVerticalSamplingFactor: int
|
||||||
mcuWidth, mcuHeight, numMcuWide, numMcuHigh: int
|
mcuWidth, mcuHeight, numMcuWide, numMcuHigh: int
|
||||||
componentOrder: array[3, int]
|
componentOrder: array[3, int]
|
||||||
hitEOI: bool
|
progressive, hitEOI: bool
|
||||||
|
|
||||||
template failInvalid() =
|
template failInvalid() =
|
||||||
raise newException(PixieError, "Invalid JPG buffer, unable to load")
|
raise newException(PixieError, "Invalid JPG buffer, unable to load")
|
||||||
|
@ -271,14 +274,25 @@ proc decodeSOF(state: var DecoderState) =
|
||||||
state.components[i].widthStride * state.components[i].heightStride
|
state.components[i].widthStride * state.components[i].heightStride
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if state.progressive:
|
||||||
|
state.components[i].widthCoeff = state.components[i].widthStride div 8
|
||||||
|
state.components[i].heightCoeff = state.components[i].heightStride div 8
|
||||||
|
state.components[i].coeff.setLen(
|
||||||
|
state.components[i].widthStride * state.components[i].heightStride
|
||||||
|
)
|
||||||
|
|
||||||
proc decodeSOS(state: var DecoderState) =
|
proc decodeSOS(state: var DecoderState) =
|
||||||
var len = state.readUint16be() - 2
|
var len = state.readUint16be() - 2
|
||||||
|
|
||||||
let components = state.readUint8()
|
state.scanComponents = state.readUint8().int
|
||||||
if components != 3:
|
|
||||||
raise newException(PixieError, "Unsupported JPG component count, must be 3")
|
|
||||||
|
|
||||||
for i in 0 ..< 3:
|
if state.scanComponents notin [1, 3]:
|
||||||
|
raise newException(PixieError, "Unsupported JPG scan component count")
|
||||||
|
|
||||||
|
if not state.progressive and state.scanComponents != 3:
|
||||||
|
raise newException(PixieError, "Unsupported JPG scan component count")
|
||||||
|
|
||||||
|
for i in 0 ..< state.scanComponents:
|
||||||
let
|
let
|
||||||
id = state.readUint8()
|
id = state.readUint8()
|
||||||
info = state.readUint8()
|
info = state.readUint8()
|
||||||
|
@ -300,11 +314,28 @@ proc decodeSOS(state: var DecoderState) =
|
||||||
state.components[component].huffmanDC = huffmanDC.int
|
state.components[component].huffmanDC = huffmanDC.int
|
||||||
state.componentOrder[i] = component
|
state.componentOrder[i] = component
|
||||||
|
|
||||||
# Skip 3 bytes
|
state.spectralStart = state.readUint8().int
|
||||||
for i in 0 ..< 3:
|
state.spectralEnd = state.readUint8().int
|
||||||
discard state.readUint8()
|
|
||||||
|
|
||||||
len -= 10
|
let aa = state.readUint8().int
|
||||||
|
state.successiveApproxLow = aa and 15
|
||||||
|
state.successiveApproxHigh = aa shr 4
|
||||||
|
|
||||||
|
if state.progressive:
|
||||||
|
if state.spectralStart > 63 or state.spectralEnd > 63:
|
||||||
|
failInvalid()
|
||||||
|
if state.spectralEnd > state.spectralEnd:
|
||||||
|
failInvalid()
|
||||||
|
if state.successiveApproxHigh > 13 or state.successiveApproxLow > 13:
|
||||||
|
failInvalid()
|
||||||
|
else:
|
||||||
|
if state.spectralStart != 0:
|
||||||
|
failInvalid()
|
||||||
|
if state.successiveApproxHigh != 0 or state.successiveApproxLow != 0:
|
||||||
|
failInvalid()
|
||||||
|
state.spectralEnd = 63
|
||||||
|
|
||||||
|
len -= 4 + 2 * state.scanComponents.uint16
|
||||||
|
|
||||||
if len != 0:
|
if len != 0:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
@ -370,7 +401,7 @@ proc extendReceive(state: var DecoderState, t: int): int {.inline.} =
|
||||||
state.bitCount -= t
|
state.bitCount -= t
|
||||||
result = k.int + (biases[t] and (not sign))
|
result = k.int + (biases[t] and (not sign))
|
||||||
|
|
||||||
proc decodeImageBlock(
|
proc decodeBlock(
|
||||||
state: var DecoderState, component: int
|
state: var DecoderState, component: int
|
||||||
): array[64, int16] =
|
): array[64, int16] =
|
||||||
let t = state.huffmanDecode(0, state.components[component].huffmanDC).int
|
let t = state.huffmanDecode(0, state.components[component].huffmanDC).int
|
||||||
|
@ -526,24 +557,36 @@ proc idctBlock(component: var Component, offset: int, data: array[64, int16]) =
|
||||||
component.data[outPos + 3] = clamp((x3 + t0) shr 17)
|
component.data[outPos + 3] = clamp((x3 + t0) shr 17)
|
||||||
component.data[outPos + 4] = clamp((x3 - t0) shr 17)
|
component.data[outPos + 4] = clamp((x3 - t0) shr 17)
|
||||||
|
|
||||||
proc decodeImageData(state: var DecoderState) =
|
proc idctBlockDC(component: var Component, offset: int) =
|
||||||
for y in 0 ..< state.numMcuHigh:
|
discard
|
||||||
for x in 0 ..< state.numMcuWide:
|
|
||||||
for component in state.componentOrder:
|
proc decodeScanData(state: var DecoderState) =
|
||||||
for j in 0 ..< state.components[component].verticalSamplingFactor:
|
if state.progressive:
|
||||||
for i in 0 ..< state.components[component].horizontalSamplingFactor:
|
if state.scanComponents == 1:
|
||||||
let
|
discard
|
||||||
data = state.decodeImageBlock(component)
|
else:
|
||||||
rowPos = (
|
discard
|
||||||
x * state.components[component].horizontalSamplingFactor + i
|
else:
|
||||||
) * 8
|
for y in 0 ..< state.numMcuHigh:
|
||||||
column = (
|
for x in 0 ..< state.numMcuWide:
|
||||||
y * state.components[component].verticalSamplingFactor + j
|
for comp in state.componentOrder:
|
||||||
) * 8
|
for j in 0 ..< state.components[comp].verticalSamplingFactor:
|
||||||
state.components[component].idctBlock(
|
for i in 0 ..< state.components[comp].horizontalSamplingFactor:
|
||||||
state.components[component].widthStride * column + rowPos,
|
let
|
||||||
data
|
data = state.decodeBlock(comp)
|
||||||
)
|
rowPos = (
|
||||||
|
x * state.components[comp].horizontalSamplingFactor + i
|
||||||
|
) * 8
|
||||||
|
column = (
|
||||||
|
y * state.components[comp].verticalSamplingFactor + j
|
||||||
|
) * 8
|
||||||
|
state.components[comp].idctBlock(
|
||||||
|
state.components[comp].widthStride * column + rowPos,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
|
proc finishProgressive(state: var DecoderState) =
|
||||||
|
discard
|
||||||
|
|
||||||
proc resampleRowH1V1(
|
proc resampleRowH1V1(
|
||||||
dst, a, b: ptr UncheckedArray[uint8],
|
dst, a, b: ptr UncheckedArray[uint8],
|
||||||
|
@ -632,23 +675,26 @@ proc decodeJpg*(data: seq[uint8]): Image =
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
var marker = state.seekToMarker()
|
var marker = state.seekToMarker()
|
||||||
while marker != 0xC0: # SOF
|
while marker != 0xC0 and marker != 0xC1 and marker != 0xC2:
|
||||||
|
# Baseline DCT or Extended DCT or Progressive DCT
|
||||||
state.decodeSegment(marker)
|
state.decodeSegment(marker)
|
||||||
marker = state.seekToMarker()
|
marker = state.seekToMarker()
|
||||||
|
|
||||||
|
state.progressive = marker == 0xC2
|
||||||
state.decodeSOF()
|
state.decodeSOF()
|
||||||
|
|
||||||
marker = state.seekToMarker()
|
while true:
|
||||||
while marker != 0xDA: # Start of Scan
|
|
||||||
state.decodeSegment(marker)
|
|
||||||
marker = state.seekToMarker()
|
marker = state.seekToMarker()
|
||||||
|
if marker == 0xDA: # Start of Scan
|
||||||
|
state.decodeSOS()
|
||||||
|
state.decodeScanData()
|
||||||
|
else:
|
||||||
|
state.decodeSegment(marker)
|
||||||
|
if state.hitEOI:
|
||||||
|
break
|
||||||
|
|
||||||
state.decodeSOS()
|
if state.progressive:
|
||||||
|
state.finishProgressive()
|
||||||
state.decodeImageData()
|
|
||||||
|
|
||||||
if not state.hitEOI:
|
|
||||||
failInvalid()
|
|
||||||
|
|
||||||
result = newImage(state.imageWidth, state.imageHeight)
|
result = newImage(state.imageWidth, state.imageHeight)
|
||||||
|
|
||||||
|
|
BIN
tests/images/jpg/itu-t81.pdf
Normal file
BIN
tests/images/jpg/itu-t81.pdf
Normal file
Binary file not shown.
Loading…
Reference in a new issue