mirror of https://github.com/status-im/nimPNG.git
gc:arc refactor adam7interlace
This commit is contained in:
parent
7304e5e28c
commit
0d8a34e076
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue