commit
91ee3443f3
|
@ -59,7 +59,7 @@ type
|
|||
widthCoeff, heightCoeff: int
|
||||
coeff, lineBuf: seq[uint8]
|
||||
blocks: seq[seq[array[64, int16]]]
|
||||
channel: seq[uint8]
|
||||
channel: Mask
|
||||
|
||||
DecoderState = object
|
||||
buffer: ptr UncheckedArray[uint8]
|
||||
|
@ -84,9 +84,46 @@ type
|
|||
eobRun: int
|
||||
hitEnd: bool
|
||||
|
||||
Mask = ref object
|
||||
## Mask object that holds mask opacity data.
|
||||
width*, height*: int
|
||||
data*: seq[uint8]
|
||||
|
||||
UnsafeMask = distinct Mask
|
||||
|
||||
when defined(release):
|
||||
{.push checks: off.}
|
||||
|
||||
proc newMask(width, height: int): Mask {.raises: [PixieError].} =
|
||||
## Creates a new mask with the parameter dimensions.
|
||||
if width <= 0 or height <= 0:
|
||||
raise newException(PixieError, "Mask width and height must be > 0")
|
||||
|
||||
result = Mask()
|
||||
result.width = width
|
||||
result.height = height
|
||||
result.data = newSeq[uint8](width * height)
|
||||
|
||||
template dataIndex(mask: Mask, x, y: int): int =
|
||||
mask.width * y + x
|
||||
|
||||
template unsafe(src: Mask): UnsafeMask =
|
||||
cast[UnsafeMask](src)
|
||||
|
||||
template `[]`(view: UnsafeMask, x, y: int): uint8 =
|
||||
## Gets a value from (x, y) coordinates.
|
||||
## * No bounds checking *
|
||||
## Make sure that x, y are in bounds.
|
||||
## Failure in the assumptions will case unsafe memory reads.
|
||||
cast[Mask](view).data[cast[Mask](view).dataIndex(x, y)]
|
||||
|
||||
template `[]=`(view: UnsafeMask, x, y: int, color: uint8) =
|
||||
## Sets a value from (x, y) coordinates.
|
||||
## * No bounds checking *
|
||||
## Make sure that x, y are in bounds.
|
||||
## Failure in the assumptions will case unsafe memory writes.
|
||||
cast[Mask](view).data[cast[Mask](view).dataIndex(x, y)] = color
|
||||
|
||||
template failInvalid(reason = "unable to load") =
|
||||
## Throw exception with a reason.
|
||||
raise newException(PixieError, "Invalid JPEG, " & reason)
|
||||
|
@ -321,8 +358,7 @@ proc decodeSOF0(state: var DecoderState) =
|
|||
|
||||
component.widthStride = state.numMcuWide * component.yScale * 8
|
||||
component.heightStride = state.numMcuHigh * component.xScale * 8
|
||||
component.channel =
|
||||
newSeq[uint8](component.widthStride * component.heightStride)
|
||||
component.channel = newMask(component.widthStride, component.heightStride)
|
||||
|
||||
if state.progressive:
|
||||
component.widthCoeff = component.widthStride div 8
|
||||
|
@ -813,14 +849,14 @@ proc idctBlock(component: var Component, offset: int, data: array[64, int16]) =
|
|||
x2 += 65536 + (128 shl 17)
|
||||
x3 += 65536 + (128 shl 17)
|
||||
|
||||
component.channel[outPos + 0] = clampByte((x0 + t3) shr 17)
|
||||
component.channel[outPos + 7] = clampByte((x0 - t3) shr 17)
|
||||
component.channel[outPos + 1] = clampByte((x1 + t2) shr 17)
|
||||
component.channel[outPos + 6] = clampByte((x1 - t2) shr 17)
|
||||
component.channel[outPos + 2] = clampByte((x2 + t1) shr 17)
|
||||
component.channel[outPos + 5] = clampByte((x2 - t1) shr 17)
|
||||
component.channel[outPos + 3] = clampByte((x3 + t0) shr 17)
|
||||
component.channel[outPos + 4] = clampByte((x3 - t0) shr 17)
|
||||
component.channel.data[outPos + 0] = clampByte((x0 + t3) shr 17)
|
||||
component.channel.data[outPos + 7] = clampByte((x0 - t3) shr 17)
|
||||
component.channel.data[outPos + 1] = clampByte((x1 + t2) shr 17)
|
||||
component.channel.data[outPos + 6] = clampByte((x1 - t2) shr 17)
|
||||
component.channel.data[outPos + 2] = clampByte((x2 + t1) shr 17)
|
||||
component.channel.data[outPos + 5] = clampByte((x2 - t1) shr 17)
|
||||
component.channel.data[outPos + 3] = clampByte((x3 + t0) shr 17)
|
||||
component.channel.data[outPos + 4] = clampByte((x3 - t0) shr 17)
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
@ -902,59 +938,45 @@ proc quantizationAndIDCTPass(state: var DecoderState) =
|
|||
data
|
||||
)
|
||||
|
||||
template dataIndex(component: var Component, x, y: int): int =
|
||||
component.widthStride * y + x
|
||||
|
||||
template `[]`*(component: Component, x, y: int): uint8 =
|
||||
component.channel[component.dataIndex(x, y)]
|
||||
|
||||
proc magnifyXBy2(component: var Component) =
|
||||
proc magnifyXBy2(mask: Mask): Mask =
|
||||
## Smooth magnify by power of 2 only in the X direction.
|
||||
var magnified =
|
||||
newSeq[uint8](component.widthStride * 2 * component.heightStride)
|
||||
for y in 0 ..< component.heightStride:
|
||||
for x in 0 ..< component.widthStride:
|
||||
let n = 3 * component[x, y].uint16
|
||||
result = newMask(mask.width * 2, mask.height)
|
||||
for y in 0 ..< mask.height:
|
||||
for x in 0 ..< mask.width:
|
||||
let n = 3 * mask.unsafe[x, y].uint16
|
||||
if x == 0:
|
||||
magnified[component.dataIndex(x * 2 + 0, y)] = component[x, y]
|
||||
magnified[component.dataIndex(x * 2 + 1, y)] =
|
||||
((n + component[x + 1, y].uint16 + 2) div 4).uint8
|
||||
elif x == component.widthStride - 1:
|
||||
magnified[component.dataIndex(x * 2 + 0, y)] =
|
||||
((n + component[x - 1, y].uint16 + 2) div 4).uint8
|
||||
magnified[component.dataIndex(x * 2 + 1, y)] = component[x, y]
|
||||
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
|
||||
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 + 1, y] = mask.unsafe[x, y]
|
||||
else:
|
||||
magnified[component.dataIndex(x * 2 + 0, y)] =
|
||||
((n + component[x - 1, y].uint16) div 4).uint8
|
||||
magnified[component.dataIndex(x * 2 + 1, y)] =
|
||||
((n + component[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
|
||||
|
||||
component.channel = move magnified
|
||||
component.widthStride *= 2
|
||||
|
||||
proc magnifyYBy2(component: var Component) =
|
||||
proc magnifyYBy2(mask: Mask): Mask =
|
||||
## Smooth magnify by power of 2 only in the Y direction.
|
||||
var magnified =
|
||||
newSeq[uint8](component.widthStride * component.heightStride * 2)
|
||||
for y in 0 ..< component.heightStride:
|
||||
for x in 0 ..< component.widthStride:
|
||||
let n = 3 * component[x, y].uint16
|
||||
result = newMask(mask.width, mask.height * 2)
|
||||
for y in 0 ..< mask.height:
|
||||
for x in 0 ..< mask.width:
|
||||
let n = 3 * mask.unsafe[x, y].uint16
|
||||
if y == 0:
|
||||
magnified[component.dataIndex(x, y * 2 + 0)] = component[x, y]
|
||||
magnified[component.dataIndex(x, y * 2 + 1)] =
|
||||
((n + component[x, y + 1].uint16 + 2) div 4).uint8
|
||||
elif y == component.heightStride - 1:
|
||||
magnified[component.dataIndex(x, y * 2 + 0)] =
|
||||
((n + component[x, y - 1].uint16 + 2) div 4).uint8
|
||||
magnified[component.dataIndex(x, y * 2 + 1)] = component[x, y]
|
||||
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
|
||||
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 + 1] = mask.unsafe[x, y]
|
||||
else:
|
||||
magnified[component.dataIndex(x, y * 2 + 0)] =
|
||||
((n + component[x, y - 1].uint16) div 4).uint8
|
||||
magnified[component.dataIndex(x, y * 2 + 1)] =
|
||||
((n + component[x, y + 1].uint16) div 4).uint8
|
||||
|
||||
component.channel = move magnified
|
||||
component.heightStride *= 2
|
||||
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.
|
||||
|
@ -989,29 +1011,33 @@ proc buildImage(state: var DecoderState): Image =
|
|||
of 3:
|
||||
for component in state.components.mitems:
|
||||
while component.yScale < state.maxYScale:
|
||||
component.magnifyXBy2()
|
||||
component.channel = component.channel.magnifyXBy2()
|
||||
component.yScale *= 2
|
||||
|
||||
while component.xScale < state.maxXScale:
|
||||
component.magnifyYBy2()
|
||||
component.channel = component.channel.magnifyYBy2()
|
||||
component.xScale *= 2
|
||||
|
||||
let
|
||||
cy = state.components[0].channel
|
||||
cb = state.components[1].channel
|
||||
cr = state.components[2].channel
|
||||
for y in 0 ..< state.imageHeight:
|
||||
var channelIndex = state.components[0].dataIndex(0, y)
|
||||
var channelIndex = cy.dataIndex(0, y)
|
||||
for x in 0 ..< state.imageWidth:
|
||||
result.unsafe[x, y] = yCbCrToRgbx(
|
||||
state.components[0].channel[channelIndex], # cy
|
||||
state.components[1].channel[channelIndex], # cb
|
||||
state.components[2].channel[channelIndex], # cr
|
||||
cy.data[channelIndex],
|
||||
cb.data[channelIndex],
|
||||
cr.data[channelIndex],
|
||||
)
|
||||
inc channelIndex
|
||||
|
||||
of 1:
|
||||
let cy = state.components[0].channel
|
||||
for y in 0 ..< state.imageHeight:
|
||||
var channelIndex = state.components[0].dataIndex(0, y)
|
||||
var channelIndex = cy.dataIndex(0, y)
|
||||
for x in 0 ..< state.imageWidth:
|
||||
result.unsafe[x, y] =
|
||||
grayScaleToRgbx(state.components[0].channel[channelIndex]) # cy
|
||||
result.unsafe[x, y] = grayScaleToRgbx(cy.data[channelIndex])
|
||||
inc channelIndex
|
||||
|
||||
else:
|
||||
|
|
Loading…
Reference in a new issue