diff --git a/nimPNG/filters.nim b/nimPNG/filters.nim index 81107ed..ade7c2f 100644 --- a/nimPNG/filters.nim +++ b/nimPNG/filters.nim @@ -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..= 8: + for i in 0..6: + let byteWidth = bpp div 8 + for y in 0..