mirror of
https://github.com/status-im/nimPNG.git
synced 2025-02-18 07:26:32 +00:00
gc:arc refactor adam7interlace
This commit is contained in:
parent
7304e5e28c
commit
0d8a34e076
@ -8,6 +8,17 @@ type
|
|||||||
FLT_AVERAGE,
|
FLT_AVERAGE,
|
||||||
FLT_PAETH
|
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
|
# Paeth predicter, used by PNG filter type 4
|
||||||
proc paethPredictor(a, b, c: int): uint =
|
proc paethPredictor(a, b, c: int): uint =
|
||||||
let pa = abs(b - c)
|
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)
|
byteWidth, lineBytes, FLT_NONE)
|
||||||
prevIndex = inIndex
|
prevIndex = inIndex
|
||||||
|
|
||||||
|
|
||||||
proc filterMinsum*(output: var openArray[byte], input: openArray[byte], w, h, bpp: int) =
|
proc filterMinsum*(output: var openArray[byte], input: openArray[byte], w, h, bpp: int) =
|
||||||
let lineBytes = (w * bpp + 7) div 8
|
let lineBytes = (w * bpp + 7) div 8
|
||||||
let byteWidth = (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
|
output.toOpenArray(prevIndex, output.len-1), # prevLine
|
||||||
byteWidth, lineBytes, filterType)
|
byteWidth, lineBytes, filterType)
|
||||||
prevIndex = outIndex
|
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…
x
Reference in New Issue
Block a user