gc:arc refactor adam7interlace

This commit is contained in:
andri lim 2020-04-14 09:57:50 +07:00
parent 7304e5e28c
commit 0d8a34e076
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
1 changed files with 166 additions and 1 deletions

View File

@ -8,6 +8,17 @@ type
FLT_AVERAGE,
FLT_PAETH
PNGPass* = object
w*, h*: array[0..6, int]
filterStart*, paddedStart*, start*: array[0..7, int]
const
# shared values used by multiple Adam7 related functions
ADAM7_IX = [ 0, 4, 0, 2, 0, 1, 0 ] # x start values
ADAM7_IY = [ 0, 0, 4, 0, 2, 0, 1 ] # y start values
ADAM7_DX = [ 8, 8, 4, 4, 2, 2, 1 ] # x delta values
ADAM7_DY = [ 8, 8, 8, 4, 4, 2, 2 ] # y delta values
# Paeth predicter, used by PNG filter type 4
proc paethPredictor(a, b, c: int): uint =
let pa = abs(b - c)
@ -102,7 +113,6 @@ proc filterZero*(output: var openArray[byte], input: openArray[byte], w, h, bpp:
byteWidth, lineBytes, FLT_NONE)
prevIndex = inIndex
proc filterMinsum*(output: var openArray[byte], input: openArray[byte], w, h, bpp: int) =
let lineBytes = (w * bpp + 7) div 8
let byteWidth = (bpp + 7) div 8
@ -389,3 +399,158 @@ proc unfilter*(output: var openArray[byte], input: openArray[byte], w, h, bpp: i
output.toOpenArray(prevIndex, output.len-1), # prevLine
byteWidth, lineBytes, filterType)
prevIndex = outIndex
proc readBitFromReversedStream(bitptr: var int, bitstream: openArray[byte]): int =
result = ((int(bitstream[bitptr shr 3]) shr (7 - (bitptr and 0x7))) and 1)
inc bitptr
proc readBitsFromReversedStream(bitptr: var int, bitstream: openArray[byte], nbits: int): int =
result = 0
var i = nbits - 1
while i > -1:
result += readBitFromReversedStream(bitptr, bitstream) shl i
dec i
proc `&=`(a: var byte, b: byte) =
a = byte(int(a) and int(b))
proc `|=`(a: var byte, b: byte) =
a = byte(int(a) or int(b))
proc setBitOfReversedStream0(bitptr: var int, bitstream: var openArray[byte], bit: int) =
# the current bit in bitstream must be 0 for this to work
if bit != 0:
# earlier bit of huffman code is in a lesser significant bit of an earlier byte
bitstream[bitptr shr 3] |= byte(bit shl (7 - (bitptr and 0x7)))
inc bitptr
proc setBitOfReversedStream(bitptr: var int, bitstream: var openArray[byte], bit: int) =
# the current bit in bitstream may be 0 or 1 for this to work
if bit == 0: bitstream[bitptr shr 3] &= byte(not (1 shl (7 - (bitptr and 0x7))))
else: bitstream[bitptr shr 3] |= byte(1 shl (7 - (bitptr and 0x7)))
inc bitptr
proc removePaddingBits(output: var openArray[byte], input: openArray[byte], olinebits, ilinebits, h: int) =
# After filtering there are still padding bits if scanLines have non multiple of 8 bit amounts. They need
# to be removed (except at last scanLine of (Adam7-reduced) image) before working with pure image buffers
# for the Adam7 code, the color convert code and the output to the user.
# in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must
# have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits
# also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
# only useful if (ilinebits - olinebits) is a value in the range 1..7
let diff = ilinebits - olinebits
var
ibp = 0
obp = 0 # input and output bit pointers
for y in 0..h-1:
for x in 0..olinebits-1:
var bit = readBitFromReversedStream(ibp, input)
setBitOfReversedStream(obp, output, bit)
inc(ibp, diff)
# Outputs various dimensions and positions in the image related to the Adam7 reduced images.
# passw: output containing the width of the 7 passes
# passh: output containing the height of the 7 passes
# filter_passstart: output containing the index of the start and end of each
# reduced image with filter bytes
# padded_passstart output containing the index of the start and end of each
# reduced image when without filter bytes but with padded scanLines
# passstart: output containing the index of the start and end of each reduced
# image without padding between scanLines, but still padding between the images
# w, h: width and height of non-interlaced image
# bpp: bits per pixel
# "padded" is only relevant if bpp is less than 8 and a scanLine or image does not
# end at a full byte
proc adam7PassValues*(pass: var PNGPass, w, h, bpp: int) =
# the passstart values have 8 values:
# the 8th one indicates the byte after the end of the 7th (= last) pass
# calculate width and height in pixels of each pass
for i in 0..6:
pass.w[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) div ADAM7_DX[i]
pass.h[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) div ADAM7_DY[i]
if pass.w[i] == 0: pass.h[i] = 0
if pass.h[i] == 0: pass.w[i] = 0
pass.filterStart[0] = 0
pass.paddedStart[0] = 0
pass.start[0] = 0
for i in 0..6:
# if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)
pass.filterStart[i + 1] = pass.filterStart[i]
if (pass.w[i] != 0) and (pass.h[i] != 0):
pass.filterStart[i + 1] += pass.h[i] * (1 + (pass.w[i] * bpp + 7) div 8)
# bits padded if needed to fill full byte at end of each scanLine
pass.paddedStart[i + 1] = pass.paddedStart[i] + pass.h[i] * ((pass.w[i] * bpp + 7) div 8)
# only padded at end of reduced image
pass.start[i + 1] = pass.start[i] + (pass.h[i] * pass.w[i] * bpp + 7) div 8
# input: Adam7 interlaced image, with no padding bits between scanLines, but between
# reduced images so that each reduced image starts at a byte.
# output: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h
# bpp: bits per pixel
# output has the following size in bits: w * h * bpp.
# input is possibly bigger due to padding bits between reduced images.
# output must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation
# (because that's likely a little bit faster)
# NOTE: comments about padding bits are only relevant if bpp < 8
proc adam7Deinterlace*(output: var openArray[byte], input: openArray[byte], w, h, bpp: int) =
var pass: PNGPass
adam7PassValues(pass, w, h, bpp)
if bpp >= 8:
for i in 0..6:
let byteWidth = bpp div 8
for y in 0..<pass.h[i]:
for x in 0..<pass.w[i]:
let inStart = pass.start[i] + (y * pass.w[i] + x) * byteWidth
let outStart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * byteWidth
for b in 0..<byteWidth:
output[outStart + b] = input[inStart + b]
else: # bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers
for i in 0..6:
let ilinebits = bpp * pass.w[i]
let olinebits = bpp * w
for y in 0..<pass.h[i]:
for x in 0..<pass.w[i]:
var ibp = (8 * pass.start[i]) + (y * ilinebits + x * bpp)
var obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp
for b in 0..<bpp:
let bit = readBitFromReversedStream(ibp, input)
# note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise
setBitOfReversedStream0(obp, output, bit)
#input: non-interlaced image with size w*h
#output: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with
# no padding bits between scanlines, but between reduced images so that each
# reduced image starts at a byte.
#bpp: bits per pixel
#there are no padding bits, not between scanlines, not between reduced images
#in has the following size in bits: w * h * bpp.
#output is possibly bigger due to padding bits between reduced images
#NOTE: comments about padding bits are only relevant if bpp < 8
proc adam7Interlace*(output: var openArray[byte], input: openArray[byte], w, h, bpp: int) =
var pass: PNGPass
adam7PassValues(pass, w, h, bpp)
if bpp >= 8:
for i in 0..6:
let byteWidth = bpp div 8
for y in 0..<pass.h[i]:
for x in 0..<pass.w[i]:
let inStart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * byteWidth
let outStart = pass.start[i] + (y * pass.w[i] + x) * byteWidth
for b in 0..<byteWidth:
output[outStart + b] = input[inStart + b]
else: # bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers
for i in 0..6:
let ilinebits = bpp * pass.w[i]
let olinebits = bpp * w
for y in 0..<pass.h[i]:
for x in 0..<pass.w[i]:
var ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp
var obp = (8 * pass.start[i]) + (y * ilinebits + x * bpp)
for b in 0..<bpp:
let bit = readBitFromReversedStream(ibp, input)
setBitOfReversedStream(obp, output, bit)