mirror of https://github.com/status-im/nimPNG.git
fix predefined filters
This commit is contained in:
parent
7c55ff3f9d
commit
5f0dd589be
38
nimPNG.nim
38
nimPNG.nim
|
@ -41,7 +41,7 @@ type
|
||||||
LCT_GREY_ALPHA = 4, # greyscale with alpha: 8,16 bit
|
LCT_GREY_ALPHA = 4, # greyscale with alpha: 8,16 bit
|
||||||
LCT_RGBA = 6 # RGB with alpha: 8,16 bit
|
LCT_RGBA = 6 # RGB with alpha: 8,16 bit
|
||||||
|
|
||||||
PNGFilter0 = enum
|
PNGFilter* = enum
|
||||||
FLT_NONE,
|
FLT_NONE,
|
||||||
FLT_SUB,
|
FLT_SUB,
|
||||||
FLT_UP,
|
FLT_UP,
|
||||||
|
@ -976,7 +976,7 @@ proc addColorBits(output: var DataBuf, index, bits, input: int) =
|
||||||
if p == 0: output[idx] = chr(val)
|
if p == 0: output[idx] = chr(val)
|
||||||
else: output[idx] = chr(ord(output[idx]) or val)
|
else: output[idx] = chr(ord(output[idx]) or val)
|
||||||
|
|
||||||
proc unfilterScanLine(recon: var DataBuf, scanLine, precon: DataBuf, byteWidth, len: int, filterType: PNGFilter0) =
|
proc unfilterScanLine(recon: var DataBuf, scanLine, precon: DataBuf, byteWidth, len: int, filterType: PNGFilter) =
|
||||||
# For PNG filter method 0
|
# For PNG filter method 0
|
||||||
# unfilter a PNG image scanLine by scanLine. when the pixels are smaller than 1 byte,
|
# unfilter a PNG image scanLine by scanLine. when the pixels are smaller than 1 byte,
|
||||||
# the filter works byte per byte (byteWidth = 1)
|
# the filter works byte per byte (byteWidth = 1)
|
||||||
|
@ -1033,7 +1033,7 @@ proc unfilter(output: var DataBuf, input: DataBuf, w, h, bpp: int) =
|
||||||
for y in 0..h-1:
|
for y in 0..h-1:
|
||||||
let outIndex = lineBytes * y
|
let outIndex = lineBytes * y
|
||||||
let inIndex = (1 + lineBytes) * y # the extra filterbyte added to each row
|
let inIndex = (1 + lineBytes) * y # the extra filterbyte added to each row
|
||||||
let filterType = PNGFilter0(input[inindex])
|
let filterType = PNGFilter(input[inindex])
|
||||||
let scanLine = input.subbuffer(inIndex + 1)
|
let scanLine = input.subbuffer(inIndex + 1)
|
||||||
var outp = output.subbuffer(outIndex)
|
var outp = output.subbuffer(outIndex)
|
||||||
unfilterScanLine(outp, scanLine, prevLine, byteWidth, lineBytes, filterType)
|
unfilterScanLine(outp, scanLine, prevLine, byteWidth, lineBytes, filterType)
|
||||||
|
@ -2101,7 +2101,7 @@ type
|
||||||
#used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with
|
#used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with
|
||||||
#the same length as the amount of scanLines in the image, and each value must <= 5.
|
#the same length as the amount of scanLines in the image, and each value must <= 5.
|
||||||
#Don't forget that filter_palette_zero must be set to false to ensure this is also used on palette or low bitdepth images.
|
#Don't forget that filter_palette_zero must be set to false to ensure this is also used on palette or low bitdepth images.
|
||||||
predefinedFilters*: string
|
predefinedFilters*: seq[PNGFilter]
|
||||||
|
|
||||||
#force creating a PLTE chunk if colorType is 2 or 6 (= a suggested palette).
|
#force creating a PLTE chunk if colorType is 2 or 6 (= a suggested palette).
|
||||||
#If colorType is 3, PLTE is _always_ created.
|
#If colorType is 3, PLTE is _always_ created.
|
||||||
|
@ -2153,7 +2153,7 @@ proc makePNGEncoder*(): PNGEncoder =
|
||||||
s.modeIn = newColorMode()
|
s.modeIn = newColorMode()
|
||||||
s.modeOut = newColorMode()
|
s.modeOut = newColorMode()
|
||||||
s.forcePalette = false
|
s.forcePalette = false
|
||||||
s.predefinedFilters = ""
|
s.predefinedFilters = @[]
|
||||||
s.addID = false
|
s.addID = false
|
||||||
s.textCompression = true
|
s.textCompression = true
|
||||||
s.interlaceMethod = IM_NONE
|
s.interlaceMethod = IM_NONE
|
||||||
|
@ -2675,7 +2675,7 @@ proc addPaddingBits(output: var DataBuf, input: DataBuf, olinebits, ilinebits, h
|
||||||
setBitOfReversedStream(obp, output, bit)
|
setBitOfReversedStream(obp, output, bit)
|
||||||
for x in 0..diff-1: setBitOfReversedStream(obp, output, 0)
|
for x in 0..diff-1: setBitOfReversedStream(obp, output, 0)
|
||||||
|
|
||||||
proc filterScanLine(output: var DataBuf, scanLine, prevLine: DataBuf, len, byteWidth: int, filterType: PNGFilter0) =
|
proc filterScanLine(output: var DataBuf, scanLine, prevLine: DataBuf, len, byteWidth: int, filterType: PNGFilter) =
|
||||||
case filterType
|
case filterType
|
||||||
of FLT_NONE:
|
of FLT_NONE:
|
||||||
for i in 0..len-1: output[i] = scanLine[i]
|
for i in 0..len-1: output[i] = scanLine[i]
|
||||||
|
@ -2748,7 +2748,7 @@ proc filterMinsum(output: var DataBuf, input: DataBuf, w, h, bpp: int) =
|
||||||
#try the 5 filter types
|
#try the 5 filter types
|
||||||
for fType in 0..4:
|
for fType in 0..4:
|
||||||
var outp = initBuffer(attempt[fType])
|
var outp = initBuffer(attempt[fType])
|
||||||
filterScanLine(outp, input.subbuffer(y * lineBytes), prevLine, lineBytes, byteWidth, PNGFilter0(fType))
|
filterScanLine(outp, input.subbuffer(y * lineBytes), prevLine, lineBytes, byteWidth, PNGFilter(fType))
|
||||||
#calculate the sum of the result
|
#calculate the sum of the result
|
||||||
sum[fType] = 0
|
sum[fType] = 0
|
||||||
if fType == 0:
|
if fType == 0:
|
||||||
|
@ -2793,7 +2793,7 @@ proc filterEntropy(output: var DataBuf, input: DataBuf, w, h, bpp: int) =
|
||||||
#try the 5 filter types
|
#try the 5 filter types
|
||||||
for fType in 0..4:
|
for fType in 0..4:
|
||||||
var outp = initBuffer(attempt[fType])
|
var outp = initBuffer(attempt[fType])
|
||||||
filterScanLine(outp, input.subbuffer(y * lineBytes), prevLine, lineBytes, byteWidth, PNGFilter0(fType))
|
filterScanLine(outp, input.subbuffer(y * lineBytes), prevLine, lineBytes, byteWidth, PNGFilter(fType))
|
||||||
for x in 0..255: count[x] = 0
|
for x in 0..255: count[x] = 0
|
||||||
for x in 0..lineBytes-1:
|
for x in 0..lineBytes-1:
|
||||||
inc count[ord(attempt[fType][x])]
|
inc count[ord(attempt[fType][x])]
|
||||||
|
@ -2826,7 +2826,7 @@ proc filterPredefined(output: var DataBuf, input: DataBuf, w, h, bpp: int, state
|
||||||
let fType = ord(state.predefinedFilters[y])
|
let fType = ord(state.predefinedFilters[y])
|
||||||
output[outindex] = chr(fType) #filter type byte
|
output[outindex] = chr(fType) #filter type byte
|
||||||
var outp = output.subbuffer(outindex + 1)
|
var outp = output.subbuffer(outindex + 1)
|
||||||
filterScanLine(outp, input.subbuffer(inindex), prevLine, lineBytes, byteWidth, PNGFilter0(fType))
|
filterScanLine(outp, input.subbuffer(inindex), prevLine, lineBytes, byteWidth, PNGFilter(fType))
|
||||||
prevLine = input.subbuffer(inindex)
|
prevLine = input.subbuffer(inindex)
|
||||||
|
|
||||||
proc filterBruteForce(output: var DataBuf, input: DataBuf, w, h, bpp: int) =
|
proc filterBruteForce(output: var DataBuf, input: DataBuf, w, h, bpp: int) =
|
||||||
|
@ -2856,7 +2856,7 @@ proc filterBruteForce(output: var DataBuf, input: DataBuf, w, h, bpp: int) =
|
||||||
for fType in 0..4:
|
for fType in 0..4:
|
||||||
#let testSize = attempt[fType].len
|
#let testSize = attempt[fType].len
|
||||||
var outp = initBuffer(attempt[fType])
|
var outp = initBuffer(attempt[fType])
|
||||||
filterScanline(outp, input.subbuffer(y * lineBytes), prevLine, lineBytes, byteWidth, PNGFilter0(fType))
|
filterScanline(outp, input.subbuffer(y * lineBytes), prevLine, lineBytes, byteWidth, PNGFilter(fType))
|
||||||
size[fType] = 0
|
size[fType] = 0
|
||||||
|
|
||||||
var nz = nzDeflateInit(attempt[fType])
|
var nz = nzDeflateInit(attempt[fType])
|
||||||
|
@ -3385,24 +3385,24 @@ when not defined(js):
|
||||||
debugEcho getCurrentExceptionMsg()
|
debugEcho getCurrentExceptionMsg()
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc getFilterTypesInterlaced(png: PNG): seq[string] =
|
proc getFilterTypesInterlaced(png: PNG): seq[seq[PNGFilter]] =
|
||||||
var header = PNGHeader(png.getChunk(IHDR))
|
var header = PNGHeader(png.getChunk(IHDR))
|
||||||
var idat = PNGData(png.getChunk(IDAT))
|
var idat = PNGData(png.getChunk(IDAT))
|
||||||
|
|
||||||
if header.interlaceMethod == IM_NONE:
|
if header.interlaceMethod == IM_NONE:
|
||||||
result = newSeq[string](1)
|
result = newSeq[seq[PNGFilter]](1)
|
||||||
result[0] = ""
|
result[0] = @[]
|
||||||
|
|
||||||
#A line is 1 filter byte + all pixels
|
#A line is 1 filter byte + all pixels
|
||||||
let lineBytes = 1 + idatRawSize(header.width, 1, header)
|
let lineBytes = 1 + idatRawSize(header.width, 1, header)
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < idat.idat.len:
|
while i < idat.idat.len:
|
||||||
result[0].add idat.idat[i]
|
result[0].add PNGFilter(idat.idat[i].int)
|
||||||
inc(i, lineBytes)
|
inc(i, lineBytes)
|
||||||
else:
|
else:
|
||||||
result = newSeq[string](7)
|
result = newSeq[seq[PNGFilter]](7)
|
||||||
for j in 0..6:
|
for j in 0..6:
|
||||||
result[j] = ""
|
result[j] = @[]
|
||||||
var w2 = (header.width - ADAM7_IX[j] + ADAM7_DX[j] - 1) div ADAM7_DX[j]
|
var w2 = (header.width - ADAM7_IX[j] + ADAM7_DX[j] - 1) div ADAM7_DX[j]
|
||||||
var h2 = (header.height - ADAM7_IY[j] + ADAM7_DY[j] - 1) div ADAM7_DY[j]
|
var h2 = (header.height - ADAM7_IY[j] + ADAM7_DY[j] - 1) div ADAM7_DY[j]
|
||||||
if(ADAM7_IX[j] >= header.width) or (ADAM7_IY[j] >= header.height):
|
if(ADAM7_IX[j] >= header.width) or (ADAM7_IY[j] >= header.height):
|
||||||
|
@ -3412,10 +3412,10 @@ proc getFilterTypesInterlaced(png: PNG): seq[string] =
|
||||||
let lineBytes = 1 + idatRawSize(w2, 1, header)
|
let lineBytes = 1 + idatRawSize(w2, 1, header)
|
||||||
var pos = 0
|
var pos = 0
|
||||||
for i in 0..h2-1:
|
for i in 0..h2-1:
|
||||||
result[j].add idat.idat[pos]
|
result[j].add PNGFilter(idat.idat[pos].int)
|
||||||
inc(pos, linebytes)
|
inc(pos, linebytes)
|
||||||
|
|
||||||
proc getFilterTypes*(png: PNG): string =
|
proc getFilterTypes*(png: PNG): seq[PNGFilter] =
|
||||||
var passes = getFilterTypesInterlaced(png)
|
var passes = getFilterTypesInterlaced(png)
|
||||||
|
|
||||||
if passes.len == 1:
|
if passes.len == 1:
|
||||||
|
@ -3425,7 +3425,7 @@ proc getFilterTypes*(png: PNG): string =
|
||||||
#Interlaced. Simplify it: put pass 6 and 7 alternating in the one vector so
|
#Interlaced. Simplify it: put pass 6 and 7 alternating in the one vector so
|
||||||
#that one filter per scanline of the uninterlaced image is given, with that
|
#that one filter per scanline of the uninterlaced image is given, with that
|
||||||
#filter corresponding the closest to what it would be for non-interlaced image.
|
#filter corresponding the closest to what it would be for non-interlaced image.
|
||||||
result = ""
|
result = @[]
|
||||||
for i in 0..header.height-1:
|
for i in 0..header.height-1:
|
||||||
if (i mod 2) == 0: result.add passes[5][i div 2]
|
if (i mod 2) == 0: result.add passes[5][i div 2]
|
||||||
else: result.add passes[6][i div 2]
|
else: result.add passes[6][i div 2]
|
||||||
|
|
|
@ -438,7 +438,7 @@ proc testPaletteFilterTypesZero() =
|
||||||
|
|
||||||
assertEquals(17, filterTypes.len)
|
assertEquals(17, filterTypes.len)
|
||||||
for i in 0..16:
|
for i in 0..16:
|
||||||
assertEquals(0.chr, filterTypes[i])
|
assertEquals(FLT_NONE, filterTypes[i])
|
||||||
|
|
||||||
proc testComplexPNG() =
|
proc testComplexPNG() =
|
||||||
echo "testComplexPNG"
|
echo "testComplexPNG"
|
||||||
|
@ -485,13 +485,18 @@ proc testPredefinedFilters() =
|
||||||
var state = makePNGEncoder()
|
var state = makePNGEncoder()
|
||||||
state.filterStrategy = LFS_PREDEFINED
|
state.filterStrategy = LFS_PREDEFINED
|
||||||
state.filterPaletteZero = false
|
state.filterPaletteZero = false
|
||||||
state.predefinedFilters = repeat(chr(3), h) #everything to filter type '3'
|
|
||||||
|
# everything to filter type 'FLT_AVERAGE'
|
||||||
|
state.predefinedFilters = newSeq[PNGFilter](h)
|
||||||
|
for i in 0..<h:
|
||||||
|
state.predefinedFilters[i] = FLT_AVERAGE
|
||||||
|
|
||||||
var png = encodePNG(image.data, w, h, state)
|
var png = encodePNG(image.data, w, h, state)
|
||||||
var outFilters = png.getFilterTypes()
|
var outFilters = png.getFilterTypes()
|
||||||
|
|
||||||
assertEquals(h, outFilters.len)
|
assertEquals(h, outFilters.len)
|
||||||
for i in 0..<h:
|
for i in 0..<h:
|
||||||
assertEquals(chr(3), outFilters[i])
|
assertEquals(FLT_AVERAGE, outFilters[i])
|
||||||
|
|
||||||
# Tests combinations of various colors in different orders
|
# Tests combinations of various colors in different orders
|
||||||
proc testFewColors() =
|
proc testFewColors() =
|
||||||
|
|
Loading…
Reference in New Issue