nimPNG/tests/test_codec.nim

1212 lines
42 KiB
Nim
Raw Normal View History

2020-04-10 05:54:40 +00:00
import ../nimPNG, streams, math, strutils, tables, base64, os
2020-04-14 04:49:37 +00:00
import ../nimPNG/[buffer, filters]
2015-09-02 13:15:44 +00:00
type
Image = ref object
data: string
width, height: int
colorType: PNGcolorType
bitDepth: int
proc fromBase64(input: string): string =
2015-09-03 04:56:22 +00:00
result = base64.decode(input)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
proc assertEquals[T, U](expected: T, actual: U, message = "") =
2015-09-08 08:01:31 +00:00
if expected != actual:
echo "Error: Not equal! Expected ", expected, " got ", actual, ". ",
2015-09-02 13:15:44 +00:00
"Message: ", message
quit()
proc getNumColorChannels(colorType: PNGcolorType): int =
case colorType
of LCT_GREY: result = 1
of LCT_RGB: result = 3
of LCT_PALETTE: result = 1
of LCT_GREY_ALPHA: result = 2
of LCT_RGBA: result = 4
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
proc generateTestImage(width, height: int, colorType = LCT_RGBA, bitDepth = 8): Image =
new(result)
result.width = width
result.height = height
result.colorType = colorType
result.bitDepth = bitDepth
let bits = bitDepth * getNumColorChannels(colorType)
let size = (width * height * bits + 7) div 8
result.data = newString(size)
var value = 128
2017-11-04 09:42:47 +00:00
for i in 0..<size:
2015-09-02 13:15:44 +00:00
result.data[i] = chr(value mod 255)
value.inc
proc assertPixels(image: Image, decoded: string, message: string) =
for i in 0..image.data.high:
var byte_expected = ord(image.data[i])
var byte_actual = ord(decoded[i])
#last byte is special due to possible random padding bits which need not to be equal
if i == image.data.high:
let numbits = getNumColorChannels(image.colorType) * image.bitDepth * image.width * image.height
let padding = 8 - (numbits - 8 * (numbits div 8))
if padding != 8:
#set all padding bits of both to 0
2017-11-04 09:42:47 +00:00
for j in 0..<padding:
2015-09-02 13:15:44 +00:00
byte_expected = (byte_expected and (not (1 shl j))) mod 256
byte_actual = (byte_actual and (not (1 shl j))) mod 256
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
assertEquals(byte_expected, byte_actual, message & " " & $i)
proc doCodecTest(image: Image, state: PNGEncoder) =
2015-09-08 08:01:31 +00:00
var png = encodePNG(image.data, image.colorType, image.bitDepth, image.width, image.height, state)
2015-09-02 13:15:44 +00:00
var s = newStringStream()
png.writeChunks s
#if the image is large enough, compressing it should result in smaller size
#if image.data.len > 512:
#assertTrue(s.data.len < image.data.len, "compressed size")
2016-01-18 02:24:27 +00:00
2020-04-14 06:58:50 +00:00
#debugEcho "PNG LEN: ", s.getPosition()
#debugEcho "PNG DATA: ", s.data.toHex
2015-09-02 13:15:44 +00:00
s.setPosition 0
2015-09-08 08:01:31 +00:00
var decoded = s.decodePNG(image.colorType, image.bitDepth)
2015-09-02 13:15:44 +00:00
2020-04-14 06:58:50 +00:00
#debugEcho "DECODED LEN: ", decoded.data.len
#debugEcho "DECODED DATA: ", decoded.data.toHex
2015-09-08 08:01:31 +00:00
assertEquals(image.width, decoded.width)
assertEquals(image.height, decoded.height)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
if state == nil:
assertPixels(image, decoded.data, "Pixels")
else:
assertPixels(image, decoded.data, "Pixels Interlaced")
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
#Test PNG encoding and decoding the encoded result
proc doCodecTest(image: Image) =
doCodecTest(image, nil)
var state = makePNGEncoder()
state.interlaceMethod = IM_INTERLACED
doCodecTest(image, state)
#Test PNG encoding and decoding using some image generated with the given parameters
proc codecTest(width, height: int, colorType = LCT_RGBA, bitDepth = 8) =
echo "codec test ", width, " ", height
var image = generateTestImage(width, height, colorType, bitDepth)
image.doCodecTest()
proc testOtherPattern1() =
echo "codec other pattern 1"
var image: Image
new(image)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
let w = 192
let h = 192
image.width = w
image.height = h
image.colorType = LCT_RGBA
image.bitDepth = 8
image.data = newString(w * h * 4)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
for y in 0..h-1:
for x in 0..w-1:
2018-05-16 05:52:37 +00:00
image.data[4 * w * y + 4 * x + 0] = chr(int(127 * (1 + math.sin(float(x * x + y * y) / (float(w * h) / 8.0)))))
image.data[4 * w * y + 4 * x + 1] = chr(int(127 * (1 + math.sin(float((w - x - 1) * (w - x - 1) + y * y) / (float(w * h) / 8.0)))))
image.data[4 * w * y + 4 * x + 2] = chr(int(127 * (1 + math.sin(float(x * x + (h - y - 1) * (h - y - 1)) / (float(w * h) / 8.0)))))
2015-09-02 13:15:44 +00:00
image.data[4 * w * y + 4 * x + 3] = chr(int(127 * (1 + math.sin(float((w - x - 1) * (w - x - 1) + (h - y - 1) * (h - y - 1)) / (float(w * h) / 8.0)))))
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
doCodecTest(image)
proc testOtherPattern2() =
echo "codec other pattern 2"
var image: Image
new(image)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
let w = 192
let h = 192
image.width = w
image.height = h
image.colorType = LCT_RGBA
image.bitDepth = 8
image.data = newString(w * h * 4)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
for y in 0..h-1:
for x in 0..w-1:
image.data[4 * w * y + 4 * x + 0] = chr(255 * not (x and y) and 0xFF)
image.data[4 * w * y + 4 * x + 1] = chr((x xor y) and 0xFF)
image.data[4 * w * y + 4 * x + 2] = chr((x or y) and 0xFF)
image.data[4 * w * y + 4 * x + 3] = chr(255)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
doCodecTest(image)
proc testSinglePixel(r, g, b, a: int) =
echo "codec single pixel " , r , " " , g , " " , b , " " , a
var pixel: Image
new(pixel)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
pixel.width = 1
pixel.height = 1
pixel.colorType = LCT_RGBA
pixel.bitDepth = 8
pixel.data = newString(4)
pixel.data[0] = r.chr
pixel.data[1] = g.chr
pixel.data[2] = b.chr
pixel.data[3] = a.chr
doCodecTest(pixel)
proc testColor(r, g, b, a: int) =
echo "codec test color ", r , " " , g , " " , b , " " , a
var image: Image
new(image)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
let w = 20
let h = 20
image.width = w
image.height = h
image.colorType = LCT_RGBA
image.bitDepth = 8
image.data = newString(w * h * 4)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
for y in 0..h-1:
for x in 0..w-1:
image.data[20 * 4 * y + 4 * x + 0] = r.chr
image.data[20 * 4 * y + 4 * x + 0] = g.chr
image.data[20 * 4 * y + 4 * x + 0] = b.chr
image.data[20 * 4 * y + 4 * x + 0] = a.chr
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
doCodecTest(image)
image.data[3] = 0.chr #one fully transparent pixel
doCodecTest(image)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
image.data[3] = 128.chr #one semi transparent pixel
doCodecTest(image)
var image3: Image
new(image3)
image3.width = image.width
image3.height = image.height
image3.colorType = image.colorType
image3.bitDepth = image.bitDepth
image3.data = image.data
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
#add 255 different colors
for i in 0..254:
image.data[i * 4 + 0] = i.chr
image.data[i * 4 + 1] = i.chr
image.data[i * 4 + 2] = i.chr
image.data[i * 4 + 3] = 255.chr
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
doCodecTest(image3)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
#a 256th color
2015-09-03 04:56:22 +00:00
image3.data[255 * 4 + 0] = 255.chr
image3.data[255 * 4 + 1] = 255.chr
image3.data[255 * 4 + 2] = 255.chr
image3.data[255 * 4 + 3] = 255.chr
2015-09-02 13:15:44 +00:00
doCodecTest(image3)
testSinglePixel(r, g, b, a)
proc testSize(w, h: int) =
echo "codec test size ", w, " ", h
var image: Image
new(image)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
image.width = w
image.height = h
image.colorType = LCT_RGBA
image.bitDepth = 8
image.data = newString(w * h * 4)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
for y in 0..h-1:
for x in 0..w-1:
image.data[w * 4 * y + 4 * x + 0] = (x mod 256).chr
image.data[w * 4 * y + 4 * x + 0] = (y mod 256).chr
image.data[w * 4 * y + 4 * x + 0] = 255.chr
image.data[w * 4 * y + 4 * x + 0] = 255.chr
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
doCodecTest(image)
proc testPNGCodec() =
codecTest(1, 1)
codecTest(2, 2)
codecTest(1, 1, LCT_GREY, 1)
codecTest(7, 7, LCT_GREY, 1)
codecTest(127, 127)
codecTest(127, 127, LCT_GREY, 1)
testOtherPattern1()
testOtherPattern2()
testColor(255, 255, 255, 255)
testColor(0, 0, 0, 255)
testColor(1, 2, 3, 255)
testColor(255, 0, 0, 255)
testColor(0, 255, 0, 255)
testColor(0, 0, 255, 255)
testColor(0, 0, 0, 255)
testColor(1, 1, 1, 255)
testColor(1, 1, 1, 1)
testColor(0, 0, 0, 128)
testColor(255, 0, 0, 128)
testColor(127, 127, 127, 255)
testColor(128, 128, 128, 255)
testColor(127, 127, 127, 128)
testColor(128, 128, 128, 128)
#transparent single pixels
testColor(0, 0, 0, 0)
testColor(255, 0, 0, 0)
testColor(1, 2, 3, 0)
testColor(255, 255, 255, 0)
testColor(254, 254, 254, 0)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
#This is mainly to test the Adam7 interlacing
for h in 1..11:
for w in 1..12:
testSize(w, h)
proc doPngSuiteTinyTest(b64: string, w, h, r, g, b, a: int) =
var input = fromBase64(b64)
var s = newStringStream(input)
2015-09-08 08:01:31 +00:00
var decoded = s.decodePNG(LCT_RGBA, 8)
2015-09-02 13:15:44 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(w, decoded.width)
assertEquals(h, decoded.height)
assertEquals(r, decoded.data[0].int)
assertEquals(g, decoded.data[1].int)
assertEquals(b, decoded.data[2].int)
assertEquals(a, decoded.data[3].int)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var state = makePNGEncoder()
state.autoConvert = false
2015-09-08 08:01:31 +00:00
var png = encodePNG(decoded.data, LCT_RGBA, 8, w, h, state)
2015-09-02 13:15:44 +00:00
s = newStringStream()
png.writeChunks s
s.setPosition 0
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
var decoded2 = s.decodePNG(LCT_RGBA, 8)
2015-09-02 13:15:44 +00:00
for i in 0..decoded.data.high:
2015-09-08 08:01:31 +00:00
assertEquals(decoded.data[i], decoded2.data[i])
2015-09-02 13:15:44 +00:00
#checks that both png suite images have the exact same pixel content, e.g. to check that
#it decodes an interlaced and non-interlaced corresponding png suite image equally
proc doPngSuiteEqualTest(b64a, b64b: string) =
var input1 = fromBase64(b64a)
var s1 = newStringStream(input1)
2015-09-08 08:01:31 +00:00
var decoded1 = s1.decodePNG(LCT_RGBA, 8)
2015-09-02 13:15:44 +00:00
var input2 = fromBase64(b64b)
var s2 = newStringStream(input2)
2015-09-08 08:01:31 +00:00
var decoded2 = s2.decodePNG(LCT_RGBA, 8)
2015-09-02 13:15:44 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(decoded1.height, decoded2.height)
assertEquals(decoded1.width, decoded2.width)
2015-09-02 13:15:44 +00:00
let size = decoded1.height * decoded1.width * 4
2017-11-04 09:42:47 +00:00
for i in 0..<size:
2015-09-02 13:15:44 +00:00
if decoded1.data[i] != decoded2.data[i]:
echo "x: ", ((i div 4) mod decoded1.width), " y: ", ((i div 4) mod decoded1.width), " c: ", i mod 4
2015-09-08 08:01:31 +00:00
assertEquals(decoded1.data[i], decoded2.data[i])
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
proc testPngSuiteTiny() =
echo "testPngSuiteTiny"
2015-09-03 04:56:22 +00:00
2015-09-02 13:15:44 +00:00
doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAFS3GZcAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
1, 1, 0, 0, 255, 255) #s01n3p01.png
doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
1, 1, 0, 0, 255, 255) #s01i3p01.png
doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAC5PL9AAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAABpJREFUeJxj+P+H4WoMw605DDfmgEgg" &
"+/8fAHF5CrkeXW0HAAAAAElFTkSuQmCC",
7, 7, 0, 0, 255, 255) #s07n3p02.png
doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAHOO4/WAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAACVJREFUeJxjOMBwgOEBwweGDQyvGf4z" &
"/GFIAcI/DFdjGG7MAZIAweMMgVWC+YkAAAAASUVORK5CYII=",
7, 7, 0, 0, 255, 255) #s07i3p02.png
doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"AQEBfC53ggAAAAxQTFRFAP8A/wAA//8AAAD/ZT8rugAAACJJREFUeJxj+B+6igGEGfAw8MnBGKug" &
"LHwMqNL/+BiDzD0AvUl/geqJjhsAAAAASUVORK5CYII=",
32, 32, 0, 0, 255, 255) #basn3p02.png
doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABGdBTUEAAYagMeiWXwAAAAZQTFRF" &
"7v8iImb/bBrSJgAAABVJREFUeJxj4AcCBjTiAxCgEwOkDgC7Hz/Bk4JmWQAAAABJRU5ErkJggg==",
32, 32, 238, 255, 34, 255) #basn3p01.png
doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAAGgflrAAAABGdBTUEAAYagMeiWXwAAAF5JREFU" &
"eJzV0jEKwDAMQ1E5W+9/xtygk8AoezLVKgSj2Y8/OICnuFcTE2OgOoJgHQiZAN2C9kDKBOgW3AZC" &
"JkC3oD2QMgG6BbeBkAnQLWgPpExgP28H7E/0GTjPfwAW2EvYX64rn9cAAAAASUVORK5CYII=",
32, 32, 0, 0, 0, 255) #basn0g16.png
doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAFxhsn9AAAABGdBTUEAAYagMeiWXwAAAOJJREFU" &
"eJy1kTsOwjAQRMdJCqj4XYHD5DAcj1Okyg2okCyBRLOSC0BDERKCI7xJVmgaa/X8PFo7oESJEtka" &
"TeLDjdjjgCMe7eTE96FGd3AL7HvZsdNEaJMVo0GNGm775bgwW6Afj/SAjAY+JsYNXIHtz2xYxTXi" &
"UoOek4AbFcCnDYEK4NMGsgXcMrGHJytkBX5HIP8FAhVANIMVIBVANMPfgUAFEM3wAVyG5cxcecY5" &
"/dup3LVFa1HXmA61LY59f6Ygp1Eg1gZGQaBRILYGdxoFYmtAGgXx9YmCfPD+RMHwuuAFVpjuiRT/" &
"//4AAAAASUVORK5CYII=",
32, 32, 0, 0, 0, 255) #basi0g16.png
#s01n3p01.png s01i3p01.png
doPngSuiteEqualTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAFS3GZcAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=")
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
#s07n3p02.png and s07i3p02.png
doPngSuiteEqualTest("iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAC5PL9AAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAABpJREFUeJxj+P+H4WoMw605DDfmgEgg" &
"+/8fAHF5CrkeXW0HAAAAAElFTkSuQmCC",
"iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAHOO4/WAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAACVJREFUeJxjOMBwgOEBwweGDQyvGf4z" &
"/GFIAcI/DFdjGG7MAZIAweMMgVWC+YkAAAAASUVORK5CYII=")
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
#basn0g16.png and basi0g16.png
doPngSuiteEqualTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAAGgflrAAAABGdBTUEAAYagMeiWXwAAAF5JREFU" &
"eJzV0jEKwDAMQ1E5W+9/xtygk8AoezLVKgSj2Y8/OICnuFcTE2OgOoJgHQiZAN2C9kDKBOgW3AZC" &
"JkC3oD2QMgG6BbeBkAnQLWgPpExgP28H7E/0GTjPfwAW2EvYX64rn9cAAAAASUVORK5CYII=",
"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAFxhsn9AAAABGdBTUEAAYagMeiWXwAAAOJJREFU" &
"eJy1kTsOwjAQRMdJCqj4XYHD5DAcj1Okyg2okCyBRLOSC0BDERKCI7xJVmgaa/X8PFo7oESJEtka" &
"TeLDjdjjgCMe7eTE96FGd3AL7HvZsdNEaJMVo0GNGm775bgwW6Afj/SAjAY+JsYNXIHtz2xYxTXi" &
"UoOek4AbFcCnDYEK4NMGsgXcMrGHJytkBX5HIP8FAhVANIMVIBVANMPfgUAFEM3wAVyG5cxcecY5" &
"/dup3LVFa1HXmA61LY59f6Ygp1Eg1gZGQaBRILYGdxoFYmtAGgXx9YmCfPD+RMHwuuAFVpjuiRT/" &
"//4AAAAASUVORK5CYII=")
#Create a PNG image with all known chunks (except only one of tEXt or zTXt) plus
#unknown chunks, and a palette.
proc createComplexPNG(): string =
2016-01-18 02:24:27 +00:00
let
2015-09-02 13:15:44 +00:00
w = 16
h = 17
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var image = newString(w * h)
for i in 0..image.high:
image[i] = chr(i mod 256)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var state = makePNGEncoder()
state.modeIn.colorType = LCT_PALETTE
state.modeIn.bitDepth = 8
state.modeOut.colorType = LCT_PALETTE
state.modeOut.bitDepth = 8
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
state.autoConvert = false
state.textCompression = true
state.addID = true
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
for i in 0..255:
state.modeIn.addPalette(i, i, i ,i)
state.modeOut.addPalette(i, i, i ,i)
state.backgroundDefined = true
state.backgroundR = 127
state.addText("key0", "string0")
state.addText("key1", "string1")
state.addIText("ikey0", "ilangtag0", "itranskey0", "istring0")
state.addIText("ikey1", "ilangtag1", "itranskey1", "istring1")
state.timeDefined = true
state.year = 2012
state.month = 1
state.day = 2
state.hour = 3
state.minute = 4
state.second = 5
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
state.physDefined = true
state.physX = 1
state.physY = 2
state.physUnit = 1
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
state.addUnknownChunk("uNKa", "a01")
state.addUnknownChunk("uNKb", "b00")
state.addUnknownChunk("uNKc", "c00")
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
var png = encodePNG(image, w, h, state)
2015-09-02 13:15:44 +00:00
var s = newStringStream()
png.writeChunks s
result = s.data
#test that, by default, it chooses filter type zero for all scanlines if the image has a palette
proc testPaletteFilterTypesZero() =
echo "testPaletteFilterTypesZero"
var raw = createComplexPNG()
var s = newStringStream(raw)
2015-09-08 08:01:31 +00:00
var png = s.decodePNG()
2015-09-02 13:15:44 +00:00
var filterTypes = png.getFilterTypes()
2015-09-08 08:01:31 +00:00
assertEquals(17, filterTypes.len)
2016-01-18 02:24:27 +00:00
for i in 0..16:
2020-04-13 07:44:49 +00:00
assertEquals(FLT_NONE, filterTypes[i])
2015-09-02 13:15:44 +00:00
proc testComplexPNG() =
echo "testComplexPNG"
var raw = createComplexPNG()
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var s = newStringStream(raw)
var state = makePNGDecoder()
state.readTextChunks = true
state.rememberUnknownChunks = true
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
var png = s.decodePNG(state)
2015-09-02 13:15:44 +00:00
var info = png.getInfo()
2016-01-18 02:24:27 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(16, info.width)
assertEquals(17, info.height)
assertEquals(true, info.backgroundDefined)
assertEquals(127 , info.backgroundR)
assertEquals(true , info.timeDefined)
assertEquals(2012 , info.year)
assertEquals(1 , info.month)
assertEquals(2 , info.day)
assertEquals(3 , info.hour)
assertEquals(4 , info.minute)
assertEquals(5 , info.second)
assertEquals(true , info.physDefined)
assertEquals(1 , info.physX)
assertEquals(2 , info.physY)
assertEquals(1 , info.physUnit)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
let chunkNames = png.getChunkNames()
let expectedNames = "IHDR uNKa PLTE tRNS bKGD pHYs uNKb IDAT tIME zTXt zTXt tEXt iTXt iTXt uNKc IEND"
2016-08-13 08:39:26 +00:00
assertEquals(expectedNames, chunkNames)
2015-09-02 13:15:44 +00:00
#TODO: test strings and unknown chunks too
proc testPredefinedFilters() =
let
w = 32
h = 32
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
echo "testPredefinedFilters"
var image = generateTestImage(w, h, LCT_RGBA, 8)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var state = makePNGEncoder()
state.filterStrategy = LFS_PREDEFINED
state.filterPaletteZero = false
2020-04-14 04:49:37 +00:00
2020-04-13 07:44:49 +00:00
# everything to filter type 'FLT_AVERAGE'
state.predefinedFilters = newSeq[PNGFilter](h)
for i in 0..<h:
state.predefinedFilters[i] = FLT_AVERAGE
2020-04-14 04:49:37 +00:00
2015-09-08 08:01:31 +00:00
var png = encodePNG(image.data, w, h, state)
2015-09-02 13:15:44 +00:00
var outFilters = png.getFilterTypes()
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
assertEquals(h, outFilters.len)
2017-11-04 09:42:47 +00:00
for i in 0..<h:
2020-04-13 07:44:49 +00:00
assertEquals(FLT_AVERAGE, outFilters[i])
2015-09-02 13:15:44 +00:00
2017-05-18 13:22:07 +00:00
# Tests combinations of various colors in different orders
proc testFewColors() =
echo "codec test few colors"
var image = new(Image)
image.width = 20
image.height = 20
image.colorType = LCT_RGBA
image.bitDepth = 8
image.data = newString(image.width * image.height * 4)
var colors = newSeq[char]()
colors.add(0.chr); colors.add(0.chr); colors.add(0.chr); colors.add(255.chr) # black
colors.add(255.chr); colors.add(255.chr); colors.add(255.chr); colors.add(255.chr) # white
colors.add(128.chr); colors.add(128.chr); colors.add(128.chr); colors.add(255.chr) # grey
colors.add(0.chr); colors.add(0.chr); colors.add(255.chr); colors.add(255.chr) # blue
colors.add(255.chr); colors.add(255.chr); colors.add(255.chr); colors.add(1.chr) # transparent white
colors.add(255.chr); colors.add(255.chr); colors.add(255.chr); colors.add(1.chr) # translucent white
let len = colors.len
for u in countup(0, len-1, 4):
for v in countup(0, len-1, 4):
for w in countup(0, len-1, 4):
for z in countup(0, len-1, 4):
2017-11-04 09:42:47 +00:00
for c in 0..<4:
for y in 0..<image.height:
for x in 0..<image.width:
2017-05-18 13:22:07 +00:00
image.data[y * image.width * 4 + x * 4 + c] = if (x xor y) != 0: colors[u + c] else: colors[v + c]
image.data[c] = colors[w + c]
image.data[image.data.len - 4 + c] = colors[z + c]
doCodecTest(image)
2015-09-02 13:15:44 +00:00
proc testColorKeyConvert() =
echo "testColorKeyConvert"
2016-01-18 02:24:27 +00:00
let
2015-09-02 13:15:44 +00:00
w = 32
h = 32
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var image = newString(w * h * 4)
let len = w*h
for i in 0..len-1:
image[i * 4 + 0] = chr(i mod 256)
image[i * 4 + 1] = chr(i div 256)
image[i * 4 + 2] = 0.chr
image[i * 4 + 3] = if i == 23: 0.chr else: 255.chr
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
var raw = encodePNG(image, w, h)
2015-09-02 13:15:44 +00:00
var s = newStringStream()
raw.writeChunks s
s.setPosition 0
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
var png = s.decodePNG()
2015-09-02 13:15:44 +00:00
var info = png.getInfo()
var image2 = png.convert(LCT_RGBA, 8)
2016-01-18 02:24:27 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(32 , info.width)
assertEquals(32 , info.height)
assertEquals(true , info.mode.keyDefined)
assertEquals(23 , info.mode.keyR)
assertEquals(0 , info.mode.keyG)
assertEquals(0 , info.mode.keyB)
assertEquals(image.len , image2.data.len)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
for i in 0..image.high:
2015-09-08 08:01:31 +00:00
assertEquals(image[i], image2.data[i])
2015-09-02 13:15:44 +00:00
proc removeSpaces(input: string): string =
result = ""
2016-01-18 02:24:27 +00:00
for c in input:
2015-09-02 13:15:44 +00:00
if c != ' ': result.add c
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
proc bitStringToBytes(input: string): string =
let bits = removeSpaces(input)
result = newString((bits.len + 7) div 8)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
for i in 0..bits.high:
let c = bits[i]
let j = i div 8
let k = i mod 8
if k == 0: result[j] = chr(0)
if c == '1': result[j] = chr(result[j].ord or (1 shl (7 - k)))
#test color convert on a single pixel. Testing palette and testing color keys is
#not supported by this function. Pixel values given using bits in an std::string
#of 0's and 1's.
proc colorConvertTest(bits_in: string, colorType_in: PNGcolorType, bitDepth_in: int,
bits_out: string, colorType_out: PNGcolorType, bitDepth_out: int) =
echo "color convert test ", bits_in, " - ", bits_out
let expected = bitStringToBytes(bits_out)
2020-04-14 04:49:37 +00:00
let image = bitStringToBytes(bits_in)
2015-09-02 13:15:44 +00:00
let modeIn = newColorMode(colorType_in, bitDepth_in)
let modeOut = newColorMode(colorType_out, bitDepth_out)
var actual = newString(expected.len)
2020-04-14 04:49:37 +00:00
convert(
actual.toOpenArray(0, actual.len-1),
image.toOpenArray(0, image.len-1),
modeOut, modeIn, 1)
2015-09-02 13:15:44 +00:00
for i in 0..expected.high:
assertEquals(expected[i].int, actual[i].int, "byte " & $i)
#Tests some specific color conversions with specific color bit combinations
proc testColorConvert() =
#test color conversions to RGBA8
colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111 11111111", LCT_RGBA, 8)
colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010 11111111", LCT_RGBA, 8)
colorConvertTest("1001", LCT_GREY, 4, "10011001 10011001 10011001 11111111", LCT_RGBA, 8)
colorConvertTest("10010101", LCT_GREY, 8, "10010101 10010101 10010101 11111111", LCT_RGBA, 8)
colorConvertTest("10010101 11111110", LCT_GREY_ALPHA, 8, "10010101 10010101 10010101 11111110", LCT_RGBA, 8)
colorConvertTest("10010101 00000001 11111110 00000001", LCT_GREY_ALPHA, 16, "10010101 10010101 10010101 11111110", LCT_RGBA, 8)
colorConvertTest("01010101 00000000 00110011", LCT_RGB, 8, "01010101 00000000 00110011 11111111", LCT_RGBA, 8)
colorConvertTest("01010101 00000000 00110011 10101010", LCT_RGBA, 8, "01010101 00000000 00110011 10101010", LCT_RGBA, 8)
colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011", LCT_RGB, 16, "10101010 11111111 11001100 11111111", LCT_RGBA, 8)
colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011 11100111 00011000", LCT_RGBA, 16, "10101010 11111111 11001100 11100111", LCT_RGBA, 8)
#test color conversions to RGB8
colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111", LCT_RGB, 8)
colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010", LCT_RGB, 8)
colorConvertTest("1001", LCT_GREY, 4, "10011001 10011001 10011001", LCT_RGB, 8)
colorConvertTest("10010101", LCT_GREY, 8, "10010101 10010101 10010101", LCT_RGB, 8)
colorConvertTest("10010101 11111110", LCT_GREY_ALPHA, 8, "10010101 10010101 10010101", LCT_RGB, 8)
colorConvertTest("10010101 00000001 11111110 00000001", LCT_GREY_ALPHA, 16, "10010101 10010101 10010101", LCT_RGB, 8)
colorConvertTest("01010101 00000000 00110011", LCT_RGB, 8, "01010101 00000000 00110011", LCT_RGB, 8)
colorConvertTest("01010101 00000000 00110011 10101010", LCT_RGBA, 8, "01010101 00000000 00110011", LCT_RGB, 8)
colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011", LCT_RGB, 16, "10101010 11111111 11001100", LCT_RGB, 8)
colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011 11100111 00011000", LCT_RGBA, 16, "10101010 11111111 11001100", LCT_RGB, 8)
#test color conversions to RGBA16
colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111", LCT_RGBA, 16)
colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010 10101010 10101010 10101010 11111111 11111111", LCT_RGBA, 16)
#test greyscale color conversions
colorConvertTest("1", LCT_GREY, 1, "11111111", LCT_GREY, 8)
colorConvertTest("1", LCT_GREY, 1, "1111111111111111", LCT_GREY, 16)
colorConvertTest("0", LCT_GREY, 1, "00000000", LCT_GREY, 8)
colorConvertTest("0", LCT_GREY, 1, "0000000000000000", LCT_GREY, 16)
colorConvertTest("11", LCT_GREY, 2, "11111111", LCT_GREY, 8)
colorConvertTest("11", LCT_GREY, 2, "1111111111111111", LCT_GREY, 16)
colorConvertTest("10", LCT_GREY, 2, "10101010", LCT_GREY, 8)
colorConvertTest("10", LCT_GREY, 2, "1010101010101010", LCT_GREY, 16)
colorConvertTest("1000", LCT_GREY, 4, "10001000", LCT_GREY, 8)
colorConvertTest("1000", LCT_GREY, 4, "1000100010001000", LCT_GREY, 16)
colorConvertTest("10110101", LCT_GREY, 8, "1011010110110101", LCT_GREY, 16)
colorConvertTest("1011010110110101", LCT_GREY, 16, "10110101", LCT_GREY, 8)
#others
colorConvertTest("11111111 11111111 11111111 00000000 00000000 00000000", LCT_RGB, 1, "10", LCT_GREY, 1)
#This tests color conversions from any color model to any color model, with any bit depth
#But it tests only with colors black and white, because that are the only colors every single model supports
proc testColorConvert2() =
echo "testColorConvert2"
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
proc toString(input: openArray[int]): string =
result = newString(input.len)
for i in 0..input.high:
result[i] = chr(input[i])
2016-01-18 02:24:27 +00:00
const
2015-09-02 13:15:44 +00:00
combos = [(colorType: LCT_GREY, bitDepth: 1),
(colorType: LCT_GREY, bitDepth: 2),
(colorType: LCT_GREY, bitDepth: 4),
(colorType: LCT_GREY, bitDepth: 8),
(colorType: LCT_GREY, bitDepth: 16),
(colorType: LCT_RGB, bitDepth: 8),
(colorType: LCT_RGB, bitDepth: 16),
(colorType: LCT_PALETTE, bitDepth: 1),
(colorType: LCT_PALETTE, bitDepth: 2),
(colorType: LCT_PALETTE, bitDepth: 4),
(colorType: LCT_PALETTE, bitDepth: 8),
(colorType: LCT_GREY_ALPHA, bitDepth: 8),
(colorType: LCT_GREY_ALPHA, bitDepth: 16),
(colorType: LCT_RGBA, bitDepth: 8),
(colorType: LCT_RGBA, bitDepth: 16)]
2016-01-18 02:24:27 +00:00
2020-04-14 04:49:37 +00:00
eight = [0,0,0,255, 255,255,255,255,
2015-09-02 13:15:44 +00:00
0,0,0,255, 255,255,255,255,
255,255,255,255, 0,0,0,255,
255,255,255,255, 255,255,255,255,
2020-04-14 04:49:37 +00:00
0,0,0,255].toString() #input in RGBA8
2016-01-18 02:24:27 +00:00
var
2015-09-02 13:15:44 +00:00
modeIn = newColorMode()
modeOut = newColorMode()
mode_8 = newColorMode()
2020-04-14 04:49:37 +00:00
input = newString(72)
output = newString(72)
eight2 = newString(36)
2015-09-02 13:15:44 +00:00
for i in 0..255:
let j = if i == 1: 255 else: i
modeIn.addPalette(j, j, j, 255)
modeOut.addPalette(j, j, j, 255)
for cma in combos:
modeIn.colorType = cma.colorType
modeIn.bitDepth = cma.bitDepth
for cmb in combos:
modeOut.colorType = cmb.colorType
modeOut.bitDepth = cmb.bitDepth
2020-04-14 04:49:37 +00:00
convert(input.toOpenArray(0, input.len-1), eight.toOpenArray(0, eight.len-1), modeIn, mode_8, 3 * 3)
convert(output.toOpenArray(0, output.len-1), input.toOpenArray(0, input.len-1), modeOut, modeIn, 3 * 3) #Test input to output type
convert(eight2.toOpenArray(0, eight2.len-1), output.toOpenArray(0, output.len-1), mode_8, modeOut, 3 * 3)
assertEquals(eight, eight2)
2015-09-02 13:15:44 +00:00
#tests that there are no crashes with auto color chooser in case of palettes with translucency etc...
proc testPaletteToPaletteConvert() =
echo "testPaletteToPaletteConvert"
2016-01-18 02:24:27 +00:00
let
2015-09-02 13:15:44 +00:00
w = 16
h = 16
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var image = newString(w * h)
for i in 0..image.high: image[i] = chr(i mod 256)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var state = makePNGEncoder()
state.modeOut.colorType = LCT_PALETTE
state.modeIn.colorType = LCT_PALETTE
state.modeOut.bitDepth = 8
state.modeIn.bitDepth = 8
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
assertEquals(true, state.autoConvert)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
for i in 0..255:
state.modeIn.addPalette(i, i, i, i)
state.modeOut.addPalette(i, i, i, i)
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
discard encodePNG(image, w, h, state)
2015-09-02 13:15:44 +00:00
#for this test, you have to choose palette colors that cause PNG to actually use a palette,
#so don't use all greyscale colors for example
proc doRGBAToPaletteTest(palette: openArray[int], expectedType = LCT_PALETTE) =
echo "testRGBToPaletteConvert ", palette.len
2016-01-18 02:24:27 +00:00
let
2015-09-02 13:15:44 +00:00
w = palette.len div 4
h = 257 #PNG encodes no palette if image is too small
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var image = newString(w * h * 4)
2016-01-18 02:24:27 +00:00
for i in 0..image.high:
2015-09-02 13:15:44 +00:00
image[i] = palette[i mod palette.len].chr
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
var raw = encodePNG(image, w, h)
2015-09-02 13:15:44 +00:00
var s = newStringStream()
raw.writeChunks s
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
s.setPosition 0
2015-09-08 08:01:31 +00:00
var png2 = s.decodePNG()
2015-09-02 13:15:44 +00:00
var info = png2.getInfo()
var image2 = png2.convert(LCT_RGBA, 8)
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
assertEquals(image2.data, image)
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
assertEquals(expectedType, info.mode.colorType)
2015-09-02 13:15:44 +00:00
if expectedType == LCT_PALETTE:
2016-08-13 08:39:26 +00:00
assertEquals((palette.len div 4), info.mode.paletteSize)
2015-09-02 13:15:44 +00:00
for i in 0..info.mode.palette.high:
2016-08-13 08:39:26 +00:00
assertEquals(info.mode.palette[i].r, image[i * 4 + 0])
assertEquals(info.mode.palette[i].g, image[i * 4 + 1])
assertEquals(info.mode.palette[i].b, image[i * 4 + 2])
assertEquals(info.mode.palette[i].a, image[i * 4 + 3])
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
proc testRGBToPaletteConvert() =
const
palette1 = [1,2,3,4]
palette2 = [1,2,3,4, 5,6,7,8]
palette3 = [1,1,1,255, 20,20,20,255, 20,20,21,255]
2016-01-18 02:24:27 +00:00
doRGBAToPaletteTest(palette1)
2015-09-02 13:15:44 +00:00
doRGBAToPaletteTest(palette2)
doRGBAToPaletteTest(palette3)
var palette: seq[int] = @[]
for i in 0..255:
palette.add(i)
palette.add(5)
palette.add(6)
palette.add(128)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
doRGBAToPaletteTest(palette)
palette.add(5)
palette.add(6)
palette.add(7)
palette.add(8)
doRGBAToPaletteTest(palette, LCT_RGBA)
#Test that when decoding to 16-bit per channel, it always uses big endian consistently.
#It should always output big endian, the convention used inside of PNG, even though x86 CPU's are little endian.
proc test16bitColorEndianness() =
echo "test16bitColorEndianness"
#basn0g16.png from the PNG test suite
2016-01-18 02:24:27 +00:00
var base64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAAGgflrAAAABGdBTUEAAYagMeiWXwAAAF5JREFU" &
2015-09-02 13:15:44 +00:00
"eJzV0jEKwDAMQ1E5W+9/xtygk8AoezLVKgSj2Y8/OICnuFcTE2OgOoJgHQiZAN2C9kDKBOgW3AZC" &
"JkC3oD2QMgG6BbeBkAnQLWgPpExgP28H7E/0GTjPfwAW2EvYX64rn9cAAAAASUVORK5CYII="
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var png = fromBase64(base64)
var s = newStringStream(png)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
#Decode from 16-bit grey image to 16-bit per channel RGBA
2015-09-08 08:01:31 +00:00
var decoded = s.decodePNG(LCT_RGBA, 16)
assertEquals(0x09, decoded.data[8].ord)
assertEquals(0x00, decoded.data[9].ord)
2015-09-02 13:15:44 +00:00
#Decode from 16-bit grey image to 16-bit grey raw image (no conversion)
var state = makePNGDecoder()
state.colorConvert = false
s.setPosition 0
2015-09-08 08:01:31 +00:00
var raw = s.decodePNG(state)
assertEquals(0x09, raw.pixels[2].ord)
assertEquals(0x00, raw.pixels[3].ord)
2015-09-02 13:15:44 +00:00
#Decode from 16-bit per channel RGB image to 16-bit per channel RGBA
base64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAIAAACsiDHgAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"DQ0N0DeNwQAAAH5JREFUeJztl8ENxEAIAwcJ6cpI+q8qKeNepAgelq2dCjz4AdQM1jRcf3WIDQ13" &
"qUNsiBBQZ1gR0cARUFIz3pug3586wo5+rOcfIaBOsCSggSOgpcB8D4D3R9DgfUyECIhDbAhp4Ajo" &
"KPD+CBq8P4IG72MiQkCdYUVEA0dAyQcwUyZpXH92ZwAAAABJRU5ErkJggg==" #cs3n2c16.png
png = fromBase64(base64)
s = newStringStream(png)
2015-09-08 08:01:31 +00:00
decoded = s.decodePNG(LCT_RGBA, 16)
2016-01-18 02:24:27 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(0x1f, decoded.data[258].ord)
assertEquals(0xf9, decoded.data[259].ord)
2015-09-02 13:15:44 +00:00
#Decode from 16-bit per channel RGB image to 16-bit per channel RGBA raw image (no conversion)
s.setPosition 0
2015-09-08 08:01:31 +00:00
raw = s.decodePNG(state)
2016-01-18 02:24:27 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(0x1f, raw.pixels[194].ord)
assertEquals(0xf9, raw.pixels[195].ord)
2015-09-02 13:15:44 +00:00
#Decode from palette image to 16-bit per channel RGBA
base64 = "iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAC5PL9AAAAABGdBTUEAAYagMeiWXwAAAANzQklU" &
"BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAABpJREFUeJxj+P+H4WoMw605DDfmgEgg" &
"+/8fAHF5CrkeXW0HAAAAAElFTkSuQmCC" #s07n3p02.png
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
png = fromBase64(base64)
s = newStringStream(png)
2015-09-08 08:01:31 +00:00
decoded = s.decodePNG(LCT_RGBA, 16)
2016-01-18 02:24:27 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(0x77, decoded.data[84].ord)
assertEquals(0x77, decoded.data[85].ord)
2015-09-02 13:15:44 +00:00
proc testNoAutoConvert() =
echo "testNoAutoConvert"
2016-01-18 02:24:27 +00:00
let
2015-09-02 13:15:44 +00:00
w = 32
h = 32
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var image = newString(w * h * 4)
let len = w * h
for i in 0..len-1:
let c = if (i mod 2) != 0: 255.chr else: 0.chr
image[i * 4 + 0] = c
image[i * 4 + 1] = c
image[i * 4 + 2] = c
image[i * 4 + 3] = 0.chr
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var state = makePNGEncoder()
state.modeOut.colorType = LCT_RGBA
state.modeOut.bitDepth = 8
state.autoConvert = false
2015-09-08 08:01:31 +00:00
var png = encodePNG(image, w, h, state)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var s = newStringStream()
png.writeChunks s
s.setPosition 0
2016-01-18 02:24:27 +00:00
2015-09-08 08:01:31 +00:00
var raw = s.decodePNG()
2015-09-02 13:15:44 +00:00
var info = raw.getInfo()
2016-01-18 02:24:27 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(32 , info.width)
assertEquals(32 , info.height)
2016-01-18 02:24:27 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(LCT_RGBA , info.mode.colorType)
assertEquals(8 , info.mode.bitDepth)
assertEquals(image , raw.pixels)
2015-09-02 13:15:44 +00:00
#colors is in RGBA, inbitDepth must be 8 or 16, the amount of bits per channel.
#colorType and bitDepth are the expected values. insize is amount of pixels. So the amount of bytes is insize * 4 * (inbitDepth / 8)
proc testAutoColorModel(colors: string, inbitDepth: int, colorType: PNGcolorType, bitDepth: int, key: bool) =
echo "testAutoColorModel ", inbitDepth, " ", colorType, " ", bitDepth, " ", key
let innum = colors.len div 4 * inbitDepth div 8
let num = max(innum, 65536) #Make image bigger so the convert doesn't avoid palette due to small image.
var colors2 = newString(num * 4 * (inbitDepth div 8))
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
for i in 0..colors2.high:
colors2[i] = colors[i mod colors.len]
2015-09-08 08:01:31 +00:00
var png = encodePNG(colors2, LCT_RGBA, inbitDepth, num, 1)
2015-09-02 13:15:44 +00:00
var s = newStringStream()
png.writeChunks s
#now extract the color type it chose
s.setPosition 0
2015-09-08 08:01:31 +00:00
var raw = s.decodePNG()
2015-09-02 13:15:44 +00:00
var info = raw.getInfo()
2015-09-03 04:56:22 +00:00
var decoded = raw.convert(LCT_RGBA, inbitdepth)
2016-01-18 02:24:27 +00:00
2016-08-13 08:39:26 +00:00
assertEquals(num , info.width)
assertEquals(1 , info.height)
assertEquals(colorType , info.mode.colorType)
assertEquals(bitDepth , info.mode.bitDepth)
assertEquals(key , info.mode.keyDefined)
2016-01-18 02:24:27 +00:00
2015-09-03 04:56:22 +00:00
for i in 0..colors.high:
2016-08-13 08:39:26 +00:00
assertEquals(colors[i], decoded.data[i])
2015-09-02 13:15:44 +00:00
proc addColor(colors: var string, r, g, b, a: int) =
colors.add r.chr
colors.add g.chr
colors.add b.chr
colors.add a.chr
proc addColor16(colors: var string, r, g, b, a: int) =
colors.add chr(r and 255)
colors.add chr((r shr 8) and 255)
colors.add chr(g and 255)
colors.add chr((g shr 8) and 255)
colors.add chr(b and 255)
colors.add chr((b shr 8) and 255)
colors.add chr(a and 255)
colors.add chr((a shr 8) and 255)
proc testAutoColorModels() =
var grey1 = ""
for i in 0..1: addColor(grey1, i * 255, i * 255, i * 255, 255)
testAutoColorModel(grey1, 8, LCT_GREY, 1, false)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var grey2 = ""
for i in 0..3: addColor(grey2, i * 85, i * 85, i * 85, 255)
testAutoColorModel(grey2, 8, LCT_GREY, 2, false)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var grey4 = ""
for i in 0..15: addColor(grey4, i * 17, i * 17, i * 17, 255)
testAutoColorModel(grey4, 8, LCT_GREY, 4, false)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var grey8 = ""
for i in 0..255: addColor(grey8, i, i, i, 255)
testAutoColorModel(grey8, 8, LCT_GREY, 8, false)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var grey16 = ""
for i in 0..256: addColor16(grey16, i, i, i, 65535)
testAutoColorModel(grey16, 16, LCT_GREY, 16, false)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var palette = ""
addColor(palette, 0, 0, 1, 255)
testAutoColorModel(palette, 8, LCT_PALETTE, 1, false)
addColor(palette, 0, 0, 2, 255)
testAutoColorModel(palette, 8, LCT_PALETTE, 1, false)
for i in 3..4: addColor(palette, 0, 0, i, 255)
testAutoColorModel(palette, 8, LCT_PALETTE, 2, false)
for i in 5..7: addColor(palette, 0, 0, i, 255)
testAutoColorModel(palette, 8, LCT_PALETTE, 4, false)
for i in 8..17: addColor(palette, 0, 0, i, 255)
testAutoColorModel(palette, 8, LCT_PALETTE, 8, false)
addColor(palette, 0, 0, 18, 0) #transparent
testAutoColorModel(palette, 8, LCT_PALETTE, 8, false)
addColor(palette, 0, 0, 18, 1) #translucent
testAutoColorModel(palette, 8, LCT_PALETTE, 8, false)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var rgb = grey8
addColor(rgb, 255, 0, 0, 255)
testAutoColorModel(rgb, 8, LCT_RGB, 8, false)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var rgb_key = rgb
addColor(rgb_key, 128, 0, 0, 0)
testAutoColorModel(rgb_key, 8, LCT_RGB, 8, true)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var rgb_key2 = rgb_key
addColor(rgb_key2, 128, 0, 0, 255) #same color but opaque ==> no more key
testAutoColorModel(rgb_key2, 8, LCT_RGBA, 8, false)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var rgb_key3 = rgb_key
addColor(rgb_key3, 128, 0, 0, 255) #semi-translucent ==> no more key
testAutoColorModel(rgb_key3, 8, LCT_RGBA, 8, false)
2016-01-18 02:24:27 +00:00
2015-09-02 13:15:44 +00:00
var rgb_key4 = rgb_key
addColor(rgb_key4, 128, 0, 0, 255)
addColor(rgb_key4, 129, 0, 0, 255) #two different transparent colors ==> no more key
testAutoColorModel(rgb_key4, 8, LCT_RGBA, 8, false)
var grey1_key = grey1
grey1_key[7] = 0.chr
testAutoColorModel(grey1_key, 8, LCT_GREY, 1, true)
var grey2_key = grey2
grey2_key[7] = 0.chr
testAutoColorModel(grey2_key, 8, LCT_GREY, 2, true)
var grey4_key = grey4
grey4_key[7] = 0.chr
testAutoColorModel(grey4_key, 8, LCT_GREY, 4, true)
var grey8_key = grey8
grey8_key[7] = 0.chr
testAutoColorModel(grey8_key, 8, LCT_GREY, 8, true)
var small16 = ""
addColor16(small16, 1, 0, 0, 65535)
testAutoColorModel(small16, 16, LCT_RGB, 16, false)
var small16a = ""
addColor16(small16a, 1, 0, 0, 1)
testAutoColorModel(small16a, 16, LCT_RGBA, 16, false)
var not16 = ""
addColor16(not16, 257, 257, 257, 0)
testAutoColorModel(not16, 16, LCT_PALETTE, 1, false)
var alpha16 = ""
addColor16(alpha16, 257, 0, 0, 10000)
testAutoColorModel(alpha16, 16, LCT_RGBA, 16, false)
2016-01-18 02:24:27 +00:00
proc testFilter() =
echo "test Filter"
2017-05-19 15:07:32 +00:00
let input = "tfilter.png"
let temp = "temp.png"
2020-04-10 05:54:40 +00:00
let png = loadPNG32("tests" / "misc" / input)
discard savePNG32("tests" / "misc" / temp, png.data, png.width, png.height)
let png2 = loadPNG32("tests" / "misc" / temp)
2016-01-18 02:24:27 +00:00
if png.data != png2.data:
echo "testFilter failed"
quit()
2017-05-18 13:22:07 +00:00
proc testPaletteToPaletteDecode() =
echo "testPaletteToPaletteDecode"
# It's a bit big for a 2x2 image... but this tests needs one with 256 palette entries in it.
let base64 = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAMAAABFaP0WAAAAA3NCSVQICAjb4U/gAAADAFBMVEUA" &
"AAAAADMAAGYAAJkAAMwAAP8AMwAAMzMAM2YAM5kAM8wAM/8AZgAAZjMAZmYAZpkAZswAZv8AmQAA" &
"mTMAmWYAmZkAmcwAmf8AzAAAzDMAzGYAzJkAzMwAzP8A/wAA/zMA/2YA/5kA/8wA//8zAAAzADMz" &
"AGYzAJkzAMwzAP8zMwAzMzMzM2YzM5kzM8wzM/8zZgAzZjMzZmYzZpkzZswzZv8zmQAzmTMzmWYz" &
"mZkzmcwzmf8zzAAzzDMzzGYzzJkzzMwzzP8z/wAz/zMz/2Yz/5kz/8wz//9mAABmADNmAGZmAJlm" &
"AMxmAP9mMwBmMzNmM2ZmM5lmM8xmM/9mZgBmZjNmZmZmZplmZsxmZv9mmQBmmTNmmWZmmZlmmcxm" &
"mf9mzABmzDNmzGZmzJlmzMxmzP9m/wBm/zNm/2Zm/5lm/8xm//+ZAACZADOZAGaZAJmZAMyZAP+Z" &
"MwCZMzOZM2aZM5mZM8yZM/+ZZgCZZjOZZmaZZpmZZsyZZv+ZmQCZmTOZmWaZmZmZmcyZmf+ZzACZ" &
"zDOZzGaZzJmZzMyZzP+Z/wCZ/zOZ/2aZ/5mZ/8yZ///MAADMADPMAGbMAJnMAMzMAP/MMwDMMzPM" &
"M2bMM5nMM8zMM//MZgDMZjPMZmbMZpnMZszMZv/MmQDMmTPMmWbMmZnMmczMmf/MzADMzDPMzGbM" &
"zJnMzMzMzP/M/wDM/zPM/2bM/5nM/8zM////AAD/ADP/AGb/AJn/AMz/AP//MwD/MzP/M2b/M5n/" &
"M8z/M///ZgD/ZjP/Zmb/Zpn/Zsz/Zv//mQD/mTP/mWb/mZn/mcz/mf//zAD/zDP/zGb/zJn/zMz/" &
"zP///wD//zP//2b//5n//8z///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" &
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" &
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlenwdAAABAHRSTlP/////////////////////////" &
"////////////////////////////////////////////////////////////////////////////" &
"////////////////////////////////////////////////////////////////////////////" &
"////////////////////////////////////////////////////////////////////////////" &
"//////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" &
"AAAAAAAAAAAAG8mZagAAAAlwSFlzAAAOTQAADpwB3vacVwAAAA5JREFUCJlj2CLHwHodAATjAa+k" &
"lTE5AAAAAElFTkSuQmCC"
let png = fromBase64(base64)
var s = newStringStream(png)
let decoded = s.decodePNG(LCT_PALETTE, 8)
assertEquals(2, decoded.width)
assertEquals(2, decoded.height)
assertEquals(180, decoded.data[0].int)
assertEquals(30, decoded.data[1].int)
assertEquals(5, decoded.data[2].int)
assertEquals(215, decoded.data[3].int)
# 2-bit palette
proc testPaletteToPaletteDecode2() =
echo "testPaletteToPaletteDecode2"
let base64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAADFBMVEX/AAAA/wAAAP/////7AGD2AAAAE0lEQVR4AWMQhAKG3VCALDIqAgDl2WYBCQHY9gAAAABJRU5ErkJggg=="
let png = fromBase64(base64)
var s = newStringStream(png)
let decoded = s.decodePNG(LCT_PALETTE, 8)
assertEquals(32, decoded.width)
assertEquals(32, decoded.height)
assertEquals(0, decoded.data[0].int)
assertEquals(1, decoded.data[1].int)
# Now add a user-specified output palette, that differs from the input palette. That should give error 82.
#LodePNGState state;
#lodepng_state_init(&state);
#state.info_raw.colortype = LCT_PALETTE;
#state.info_raw.bitdepth = 8;
#lodepng_palette_add(&state.info_raw, 0, 0, 0, 255);
#lodepng_palette_add(&state.info_raw, 1, 1, 1, 255);
#lodepng_palette_add(&state.info_raw, 2, 2, 2, 255);
#lodepng_palette_add(&state.info_raw, 3, 3, 3, 255);
#unsigned char* image2 = 0;
#unsigned error2 = lodepng_decode(&image2, &width, &height, &state, &png[0], png.size());
#ASSERT_EQUALS(82, error2);
#lodepng_state_cleanup(&state);
#free(image2);
proc flipBit(c: uint8, bitpos: int): uint8 =
result = c xor uint8(1 shl bitpos)
# Test various broken inputs. Returned errors are not checked, what is tested is
# that is doesn't crash, and, when run with valgrind, no memory warnings are
# given.
proc testFuzzing() =
echo "testFuzzing"
var
png = createComplexPNG()
broken = newString(png.len)
errors = initTable[string, int]()
copyMem(broken.cstring, png.cstring, png.len)
var settings = makePNGDecoder()
settings.ignoreCRC = true
settings.ignoreAdler32 = true
2017-11-04 09:42:47 +00:00
for i in 0..<png.len:
2017-05-18 13:22:07 +00:00
broken[i] = cast[char](not png[i].int)
try:
var s = newStringStream(broken)
2017-11-04 09:42:47 +00:00
discard s.decodePNG(settings)
2017-05-18 13:22:07 +00:00
except Exception as ex:
if errors.hasKey(ex.msg):
inc errors[ex.msg]
else:
errors[ex.msg] = 0
broken[i] = chr(0)
try:
var s = newStringStream(broken)
2017-11-04 09:42:47 +00:00
discard s.decodePNG(settings)
2017-05-18 13:22:07 +00:00
except Exception as ex:
if errors.hasKey(ex.msg):
inc errors[ex.msg]
else:
errors[ex.msg] = 0
2017-11-04 09:42:47 +00:00
for j in 0..<8:
2017-05-18 13:22:07 +00:00
broken[i] = chr(flipBit(png[i].uint8, j))
try:
var s = newStringStream(broken)
2017-11-04 09:42:47 +00:00
discard s.decodePNG(settings)
2017-05-18 13:22:07 +00:00
except Exception as ex:
if errors.hasKey(ex.msg):
inc errors[ex.msg]
else:
errors[ex.msg] = 0
broken[i] = chr(255)
try:
var s = newStringStream(broken)
2017-11-04 09:42:47 +00:00
discard s.decodePNG(settings)
2017-05-18 13:22:07 +00:00
except Exception as ex:
if errors.hasKey(ex.msg):
inc errors[ex.msg]
else:
errors[ex.msg] = 0
GC_fullcollect()
echo GC_getStatistics()
broken[i] = png[i] #fix it again for the next test
echo "testFuzzing shrinking"
copyMem(broken.cstring, png.cstring, png.len)
while broken.len > 0:
broken.setLen(broken.len - 1)
try:
var s = newStringStream(broken)
2017-11-04 09:42:47 +00:00
discard s.decodePNG(settings)
2017-05-18 13:22:07 +00:00
except Exception as ex:
if errors.hasKey(ex.msg):
inc errors[ex.msg]
else:
errors[ex.msg] = 0
echo GC_getStatistics()
#For fun, print the number of each error
echo "Fuzzing error code counts: "
for key, val in pairs(errors):
echo key, " : ", val
2015-09-02 13:15:44 +00:00
proc doMain() =
2017-05-18 13:22:07 +00:00
# PNG
2015-09-03 04:56:22 +00:00
testPNGCodec()
testPngSuiteTiny()
testPaletteFilterTypesZero()
testComplexPNG()
testPredefinedFilters()
2017-05-18 13:22:07 +00:00
testPaletteToPaletteDecode()
testPaletteToPaletteDecode2()
#testFuzzing() OOM
# COLOR
testFewColors()
2015-09-03 04:56:22 +00:00
testColorKeyConvert()
testColorConvert()
testColorConvert2()
testPaletteToPaletteConvert()
testRGBToPaletteConvert()
2016-01-18 02:24:27 +00:00
test16bitColorEndianness()
testNoAutoConvert()
testAutoColorModels()
testFilter()
2020-04-14 04:49:37 +00:00
2015-09-03 04:56:22 +00:00
doMain()