Simpler upscaling code.
This commit is contained in:
parent
fdd6f51dde
commit
f4cd87b505
1 changed files with 96 additions and 193 deletions
|
@ -1,4 +1,4 @@
|
||||||
import pixie/common, pixie/images, sequtils, strutils
|
import pixie/common, pixie/images, pixie/masks, sequtils, strutils, chroma
|
||||||
|
|
||||||
# This JPEG decoder is loosely based on stb_image which is public domain.
|
# This JPEG decoder is loosely based on stb_image which is public domain.
|
||||||
|
|
||||||
|
@ -68,8 +68,9 @@ type
|
||||||
huffmanDC, huffmanAC: int
|
huffmanDC, huffmanAC: int
|
||||||
dcPred: int
|
dcPred: int
|
||||||
widthCoeff, heightCoeff: int
|
widthCoeff, heightCoeff: int
|
||||||
data, coeff, lineBuf: seq[uint8]
|
coeff, lineBuf: seq[uint8]
|
||||||
blocks: seq[seq[array[64, int16]]]
|
blocks: seq[seq[array[64, int16]]]
|
||||||
|
channel: Mask
|
||||||
|
|
||||||
DecoderState = object
|
DecoderState = object
|
||||||
buffer: seq[uint8]
|
buffer: seq[uint8]
|
||||||
|
@ -91,6 +92,9 @@ type
|
||||||
todo: int
|
todo: int
|
||||||
eobRun: int
|
eobRun: int
|
||||||
|
|
||||||
|
when defined(release):
|
||||||
|
{.push checks: off.}
|
||||||
|
|
||||||
template failInvalid(reason = "unable to load") =
|
template failInvalid(reason = "unable to load") =
|
||||||
## Throw exception with a reason.
|
## Throw exception with a reason.
|
||||||
raise newException(PixieError, "Invalid JPEG, " & reason)
|
raise newException(PixieError, "Invalid JPEG, " & reason)
|
||||||
|
@ -299,8 +303,8 @@ proc decodeSOF0(state: var DecoderState) =
|
||||||
state.components[i].heightStride =
|
state.components[i].heightStride =
|
||||||
state.numMcuHigh * state.components[i].xScale * 8
|
state.numMcuHigh * state.components[i].xScale * 8
|
||||||
|
|
||||||
state.components[i].data.setLen(
|
state.components[i].channel = newMask(
|
||||||
state.components[i].widthStride * state.components[i].heightStride
|
state.components[i].widthStride, state.components[i].heightStride
|
||||||
)
|
)
|
||||||
|
|
||||||
if state.progressive:
|
if state.progressive:
|
||||||
|
@ -760,14 +764,14 @@ proc idctBlock(component: var Component, offset: int, data: array[64, int16]) =
|
||||||
x2 += 65536 + (128 shl 17)
|
x2 += 65536 + (128 shl 17)
|
||||||
x3 += 65536 + (128 shl 17)
|
x3 += 65536 + (128 shl 17)
|
||||||
|
|
||||||
component.data[outPos + 0] = clampByte((x0 + t3) shr 17)
|
component.channel.data[outPos + 0] = clampByte((x0 + t3) shr 17)
|
||||||
component.data[outPos + 7] = clampByte((x0 - t3) shr 17)
|
component.channel.data[outPos + 7] = clampByte((x0 - t3) shr 17)
|
||||||
component.data[outPos + 1] = clampByte((x1 + t2) shr 17)
|
component.channel.data[outPos + 1] = clampByte((x1 + t2) shr 17)
|
||||||
component.data[outPos + 6] = clampByte((x1 - t2) shr 17)
|
component.channel.data[outPos + 6] = clampByte((x1 - t2) shr 17)
|
||||||
component.data[outPos + 2] = clampByte((x2 + t1) shr 17)
|
component.channel.data[outPos + 2] = clampByte((x2 + t1) shr 17)
|
||||||
component.data[outPos + 5] = clampByte((x2 - t1) shr 17)
|
component.channel.data[outPos + 5] = clampByte((x2 - t1) shr 17)
|
||||||
component.data[outPos + 3] = clampByte((x3 + t0) shr 17)
|
component.channel.data[outPos + 3] = clampByte((x3 + t0) shr 17)
|
||||||
component.data[outPos + 4] = clampByte((x3 - t0) shr 17)
|
component.channel.data[outPos + 4] = clampByte((x3 - t0) shr 17)
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
@ -857,196 +861,92 @@ proc quantizationAndIDCTPass(state: var DecoderState) =
|
||||||
data
|
data
|
||||||
)
|
)
|
||||||
|
|
||||||
proc resampleRowH1V1(
|
proc yCbCrToRgbx(py, pcb, pcr: uint8): ColorRGBX =
|
||||||
dst, a, b: ptr UncheckedArray[uint8],
|
|
||||||
widthPreExpansion, horizontalExpansionFactor: int
|
|
||||||
): ptr UncheckedArray[uint8] =
|
|
||||||
a
|
|
||||||
|
|
||||||
proc resampleRowH1V2(
|
|
||||||
dst, a, b: ptr UncheckedArray[uint8],
|
|
||||||
widthPreExpansion, horizontalExpansionFactor: int
|
|
||||||
): ptr UncheckedArray[uint8] =
|
|
||||||
for i in 0 ..< widthPreExpansion:
|
|
||||||
dst[i] = ((3 * a[i].int + b[i].int + 2) shr 2).uint8
|
|
||||||
dst
|
|
||||||
|
|
||||||
proc resampleRowH2V1(
|
|
||||||
dst, a, b: ptr UncheckedArray[uint8],
|
|
||||||
widthPreExpansion, horizontalExpansionFactor: int
|
|
||||||
): ptr UncheckedArray[uint8] =
|
|
||||||
if widthPreExpansion == 1:
|
|
||||||
dst[0] = a[0]
|
|
||||||
dst[1] = dst[0]
|
|
||||||
else:
|
|
||||||
dst[0] = a[0]
|
|
||||||
dst[1] = ((a[0].int * 3 + a[1].int + 2) shr 2).uint8
|
|
||||||
for i in 1 ..< widthPreExpansion - 1:
|
|
||||||
let n = 3 * a[i].int + 2
|
|
||||||
dst[i * 2 + 0] = ((n + a[i - 1].int) shr 2).uint8
|
|
||||||
dst[i * 2 + 1] = ((n + a[i + 1].int) shr 2).uint8
|
|
||||||
|
|
||||||
dst[widthPreExpansion * 2 + 0] = ((
|
|
||||||
a[widthPreExpansion - 2].int * 3 + a[widthPreExpansion - 1].int + 2
|
|
||||||
) shr 2).uint8
|
|
||||||
dst[widthPreExpansion * 2 + 1] = (a[widthPreExpansion - 1]) shr 2
|
|
||||||
dst
|
|
||||||
|
|
||||||
proc resampleRowH4V1(
|
|
||||||
dst, a, b: ptr UncheckedArray[uint8],
|
|
||||||
widthPreExpansion, horizontalExpansionFactor: int
|
|
||||||
): ptr UncheckedArray[uint8] =
|
|
||||||
for i in 0 ..< widthPreExpansion * 4:
|
|
||||||
dst[i] = a[i div 4]
|
|
||||||
dst
|
|
||||||
|
|
||||||
proc resampleRowH2V2(
|
|
||||||
dst, a, b: ptr UncheckedArray[uint8],
|
|
||||||
widthPreExpansion, horizontalExpansionFactor: int
|
|
||||||
): ptr UncheckedArray[uint8] =
|
|
||||||
if widthPreExpansion == 1:
|
|
||||||
dst[0] = ((3 * a[0].int + b[0].int + 2) shr 2).uint8
|
|
||||||
dst[1] = dst[0]
|
|
||||||
else:
|
|
||||||
var
|
|
||||||
t0: int
|
|
||||||
t1 = 3 * a[0].int + b[0].int
|
|
||||||
dst[0] = ((t1 + 2) shr 2).uint8
|
|
||||||
for i in 1 ..< widthPreExpansion:
|
|
||||||
t0 = t1
|
|
||||||
t1 = 3 * a[i].int + b[i].int
|
|
||||||
dst[i * 2 - 1] = ((3 * t0 + t1 + 8) shr 4).uint8
|
|
||||||
dst[i * 2 + 0] = ((3 * t1 + t0 + 8) shr 4).uint8
|
|
||||||
dst[widthPreExpansion * 2 - 1] = ((t1 + 2) shr 2).uint8
|
|
||||||
dst
|
|
||||||
|
|
||||||
proc yCbCrToRgbx(dst, py, pcb, pcr: ptr UncheckedArray[uint8], width: int) =
|
|
||||||
## Takes a 3 component yCbCr outputs and populates image.
|
## Takes a 3 component yCbCr outputs and populates image.
|
||||||
template float2Fixed(x: float32): int =
|
template float2Fixed(x: float32): int =
|
||||||
(x * 4096 + 0.5).int shl 8
|
(x * 4096 + 0.5).int shl 8
|
||||||
|
|
||||||
var pos: int
|
|
||||||
for i in 0 ..< width:
|
|
||||||
let
|
let
|
||||||
yFixed = (py[][i].int shl 20) + (1 shl 19)
|
yFixed = (py.int shl 20) + (1 shl 19)
|
||||||
cb = pcb[][i].int - 128
|
cb = pcb.int - 128
|
||||||
cr = pcr[][i].int - 128
|
cr = pcr.int - 128
|
||||||
var
|
var
|
||||||
r = yFixed + cr * float2Fixed(1.40200)
|
r = yFixed + cr * float2Fixed(1.40200)
|
||||||
g = yFixed +
|
g = yFixed +
|
||||||
(cr * -float2Fixed(0.71414)) +
|
(cr * -float2Fixed(0.71414)) +
|
||||||
((cb * -float2Fixed(0.34414)) and -65536)
|
((cb * -float2Fixed(0.34414)) and -65536)
|
||||||
b = yFixed + cb * float2Fixed(1.77200)
|
b = yFixed + cb * float2Fixed(1.77200)
|
||||||
dst[pos + 0] = clampByte(r shr 20)
|
result.r = clampByte(r shr 20)
|
||||||
dst[pos + 1] = clampByte(g shr 20)
|
result.g = clampByte(g shr 20)
|
||||||
dst[pos + 2] = clampByte(b shr 20)
|
result.b = clampByte(b shr 20)
|
||||||
dst[pos + 3] = 255
|
result.a = 255
|
||||||
pos += 4
|
|
||||||
|
|
||||||
proc grayScaleToRgbx(dst, gray: ptr UncheckedArray[uint8], width: int) =
|
proc grayScaleToRgbx(gray: uint8): ColorRGBX =
|
||||||
## Takes a single gray scale component output and populates image.
|
## Takes a single gray scale component output and populates image.
|
||||||
var pos: int
|
let g = gray
|
||||||
for i in 0 ..< width:
|
result.r = g
|
||||||
let g = gray[i]
|
result.g = g
|
||||||
dst[pos + 0] = g
|
result.b = g
|
||||||
dst[pos + 1] = g
|
result.a = 255
|
||||||
dst[pos + 2] = g
|
|
||||||
dst[pos + 3] = 255
|
proc magnifyXBy2(mask: Mask): Mask =
|
||||||
pos += 4
|
result = newMask(mask.width * 2, mask.height)
|
||||||
|
for y in 0 ..< mask.height:
|
||||||
|
for x in 0 ..< mask.width:
|
||||||
|
if x == 0 or x == mask.width - 1:
|
||||||
|
result[x * 2 + 0, y] = mask[x, y]
|
||||||
|
result[x * 2 + 1, y] = mask[x, y]
|
||||||
|
else:
|
||||||
|
result[x * 2 + 0, y] = mask[x, y] div 2 + mask[x-1, y] div 2
|
||||||
|
result[x * 2 + 1, y] = mask[x, y] div 2 + mask[x+1, y] div 2
|
||||||
|
|
||||||
|
proc magnifyYBy2(mask: Mask): Mask =
|
||||||
|
result = newMask(mask.width, mask.height * 2)
|
||||||
|
for y in 0 ..< mask.height:
|
||||||
|
for x in 0 ..< mask.width:
|
||||||
|
if y == 0 or y == mask.width - 1:
|
||||||
|
result[x, y * 2 + 0] = mask[x, y]
|
||||||
|
result[x, y * 2 + 1] = mask[x, y]
|
||||||
|
else:
|
||||||
|
result[x, y * 2 + 0] = mask[x, y] div 2 + mask[x, y-1] div 2
|
||||||
|
result[x, y * 2 + 1] = mask[x, y] div 2 + mask[x, y+1] div 2
|
||||||
|
|
||||||
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.
|
||||||
result = newImage(state.imageWidth, state.imageHeight)
|
|
||||||
|
|
||||||
var resamples: array[3, Resample]
|
|
||||||
for i in 0 ..< state.components.len:
|
|
||||||
resamples[i].horizontalExpansionFactor =
|
|
||||||
state.maxYScale div
|
|
||||||
state.components[i].yScale
|
|
||||||
resamples[i].verticalExpansionFactor =
|
|
||||||
state.maxXScale div
|
|
||||||
state.components[i].xScale
|
|
||||||
resamples[i].yStep = resamples[i].verticalExpansionFactor shr 1
|
|
||||||
resamples[i].widthPreExpansion = (
|
|
||||||
state.imageWidth + resamples[i].horizontalExpansionFactor - 1
|
|
||||||
) div resamples[i].horizontalExpansionFactor
|
|
||||||
|
|
||||||
resamples[i].line0 = cast[ptr UncheckedArray[uint8]](
|
|
||||||
state.components[i].data[0].addr
|
|
||||||
)
|
|
||||||
resamples[i].line1 = cast[ptr UncheckedArray[uint8]](
|
|
||||||
state.components[i].data[0].addr
|
|
||||||
)
|
|
||||||
state.components[i].lineBuf.setLen(state.imageWidth + 3)
|
|
||||||
|
|
||||||
if resamples[i].horizontalExpansionFactor == 1 and
|
|
||||||
resamples[i].verticalExpansionFactor == 1:
|
|
||||||
resamples[i].resample = resampleRowH1V1
|
|
||||||
elif resamples[i].horizontalExpansionFactor == 1 and
|
|
||||||
resamples[i].verticalExpansionFactor == 2:
|
|
||||||
resamples[i].resample = resampleRowH1V2
|
|
||||||
elif resamples[i].horizontalExpansionFactor == 2 and
|
|
||||||
resamples[i].verticalExpansionFactor == 1:
|
|
||||||
resamples[i].resample = resampleRowH2V1
|
|
||||||
elif resamples[i].horizontalExpansionFactor == 2 and
|
|
||||||
resamples[i].verticalExpansionFactor == 2:
|
|
||||||
resamples[i].resample = resampleRowH2V2
|
|
||||||
elif resamples[i].horizontalExpansionFactor == 4 and
|
|
||||||
resamples[i].verticalExpansionFactor == 1:
|
|
||||||
resamples[i].resample = resampleRowH4V1
|
|
||||||
else:
|
|
||||||
failInvalid()
|
|
||||||
|
|
||||||
for y in 0 ..< state.imageHeight:
|
|
||||||
var componentOutputs: seq[ptr UncheckedArray[uint8]]
|
|
||||||
for i in 0 ..< state.components.len:
|
|
||||||
let yBottom =
|
|
||||||
resamples[i].yStep >= (resamples[i].verticalExpansionFactor shr 1)
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
# for x in sample ^ 2
|
|
||||||
# resample x dir
|
|
||||||
|
|
||||||
# for y in sample ^ 2
|
|
||||||
# resample y dir
|
|
||||||
|
|
||||||
componentOutputs.add resamples[i].resample(
|
|
||||||
cast[ptr UncheckedArray[uint8]](state.components[i].lineBuf[0].addr),
|
|
||||||
if yBottom: resamples[i].line1 else: resamples[i].line0,
|
|
||||||
if yBottom: resamples[i].line0 else: resamples[i].line1,
|
|
||||||
resamples[i].widthPreExpansion,
|
|
||||||
resamples[i].horizontalExpansionFactor
|
|
||||||
)
|
|
||||||
|
|
||||||
inc resamples[i].yStep
|
|
||||||
if resamples[i].yStep >= resamples[i].verticalExpansionFactor:
|
|
||||||
resamples[i].yStep = 0
|
|
||||||
resamples[i].line0 = resamples[i].line1
|
|
||||||
inc resamples[i].yPos
|
|
||||||
if resamples[i].yPos < state.components[i].height:
|
|
||||||
resamples[i].line1 = cast[ptr UncheckedArray[uint8]](
|
|
||||||
state.components[i].data[
|
|
||||||
resamples[i].yPos * state.components[i].widthStride
|
|
||||||
].addr
|
|
||||||
)
|
|
||||||
|
|
||||||
let dst = cast[ptr UncheckedArray[uint8]](
|
|
||||||
result.data[state.imageWidth * y].addr
|
|
||||||
)
|
|
||||||
|
|
||||||
if state.components.len == 3:
|
if state.components.len == 3:
|
||||||
yCbCrToRgbx(
|
for componentIdx, component in state.components.mpairs:
|
||||||
dst,
|
|
||||||
componentOutputs[0],
|
while component.xScale < state.maxXScale:
|
||||||
componentOutputs[1],
|
component.channel = component.channel.magnifyYBy2()
|
||||||
componentOutputs[2],
|
component.xScale *= 2
|
||||||
state.imageWidth
|
|
||||||
|
while component.yScale < state.maxYScale:
|
||||||
|
component.channel = component.channel.magnifyXBy2()
|
||||||
|
component.yScale *= 2
|
||||||
|
|
||||||
|
result = newImage(state.imageWidth, state.imageHeight)
|
||||||
|
|
||||||
|
if state.components.len == 3:
|
||||||
|
let
|
||||||
|
cy = state.components[0].channel
|
||||||
|
cb = state.components[1].channel
|
||||||
|
cr = state.components[2].channel
|
||||||
|
for y in 0 ..< state.imageHeight:
|
||||||
|
for x in 0 ..< state.imageWidth:
|
||||||
|
result[x, y] = yCbCrToRgbx(
|
||||||
|
cy[x, y],
|
||||||
|
cb[x, y],
|
||||||
|
cr[x, y],
|
||||||
)
|
)
|
||||||
|
|
||||||
elif state.components.len == 1:
|
elif state.components.len == 1:
|
||||||
grayScaleToRgbx(
|
let
|
||||||
dst,
|
cy = state.components[0].channel
|
||||||
componentOutputs[0],
|
for y in 0 ..< state.imageHeight:
|
||||||
state.imageWidth,
|
for x in 0 ..< state.imageWidth:
|
||||||
|
result[x, y] = grayScaleToRgbx(
|
||||||
|
cy[x, y],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
@ -1121,3 +1021,6 @@ proc decodeJpeg*(data: string): Image {.inline, raises: [PixieError].} =
|
||||||
|
|
||||||
proc encodeJpeg*(image: Image): string =
|
proc encodeJpeg*(image: Image): string =
|
||||||
raise newException(PixieError, "Encoding JPG not supported yet")
|
raise newException(PixieError, "Encoding JPG not supported yet")
|
||||||
|
|
||||||
|
when defined(release):
|
||||||
|
{.pop.}
|
||||||
|
|
Loading…
Reference in a new issue