diff --git a/nimPNG.nim b/nimPNG.nim index 455b72b..8ff9afb 100644 --- a/nimPNG.nim +++ b/nimPNG.nim @@ -760,8 +760,8 @@ proc parsePNG(s: Stream, settings: PNGDecoder): PNG = let data = s.readStr(length) let crc = cast[uint32](s.readInt32BE()) let calculatedCRC = crc32(crc32(0, $chunkType), data) - #if calculatedCRC != crc and not PNGDecoder(png.settings).ignoreCRC: - #raise PNGError("wrong crc for: " & $chunkType) + if calculatedCRC != crc and not PNGDecoder(png.settings).ignoreCRC: + raise PNGError("wrong crc for: " & $chunkType) var chunk = png.createChunk(chunkType, data, crc) if chunkType != IDAT and chunk != nil: @@ -1224,11 +1224,12 @@ proc RGBAFromGrey124(output: var cstring, input: cstring, numPixels: int, mode: var highest = ((1 shl mode.bitDepth) - 1) #highest possible value for this bit depth var obp = 0 for i in 0..numPixels-1: - let val = chr((readBitsFromReversedStream(obp, input, mode.bitDepth) * 255) div highest) + let val = readBitsFromReversedStream(obp, input, mode.bitDepth) + let value = chr((val * 255) div highest) let x = i * 4 - output[x] = val - output[x+1] = val - output[x+2] = val + output[x] = value + output[x+1] = value + output[x+2] = value if mode.keyDefined and (ord(val) == mode.keyR): output[x+3] = chr(0) else: output[x+3] = chr(255) @@ -1361,10 +1362,11 @@ proc RGBA8FromGrey16(p: var RGBA8, input: cstring, px: int, mode: PNGColorMode) proc RGBA8FromGrey124(p: var RGBA8, input: cstring, px: int, mode: PNGColorMode) = let highest = ((1 shl mode.bitDepth) - 1) #highest possible value for this bit depth var obp = px * mode.bitDepth - let val = chr((readBitsFromReversedStream(obp, input, mode.bitDepth) * 255) div highest) - p.r = val - p.g = val - p.b = val + let val = readBitsFromReversedStream(obp, input, mode.bitDepth) + let value = chr((val * 255) div highest) + p.r = value + p.g = value + p.b = value if mode.keyDefined and (ord(val) == mode.keyR): p.a = chr(0) else: p.a = chr(255) @@ -2829,10 +2831,12 @@ proc PNGEncode*(input: string, w, h: int, settings = PNGEncoder(nil)): PNG = (modeOut.paletteSize == 0 or modeOut.paletteSize > 256): raise PNGError("invalid palette size, it is only allowed to be 1-256") + let inputSize = getRawSize(w, h, modeIn) + if input.len < inputSize: + raise PNGError("not enough input to encode") + if state.autoConvert: autoChooseColor(modeOut, input, w, h, modeIn) - #modeOut.show - #modeIn.show if state.interlaceMethod notin {IM_NONE, IM_INTERLACED}: raise PNGError("unexisting interlace mode") @@ -2910,6 +2914,12 @@ proc PNGEncode*(input: string, colorType: PNGcolorType, bitDepth, w, h: int, set state.modeIn.bitDepth = bitDepth result = PNGEncode(input, w, h, state) +proc PNGEncode32*(input: string, w, h: int): PNG = + result = PNGEncode(input, LCT_RGBA, 8, w, h) + +proc PNGEncode24*(input: string, w, h: int): PNG = + result = PNGEncode(input, LCT_RGB, 8, w, h) + proc writeChunks*(png: PNG, s: Stream) = s.write PNGSignature @@ -2924,6 +2934,16 @@ proc writeChunks*(png: PNG, s: Stream) = s.write chunk.data s.writeInt32BE cast[int](chunk.crc) +proc savePNG32*(fileName, input: string, w, h: int) = + var png = PNGEncode(input, LCT_RGBA, 8, w, h) + var s = newFileStream(fileName, fmWrite) + png.writeChunks s + +proc savePNG24*(fileName, input: string, w, h: int) = + var png = PNGEncode(input, LCT_RGB, 8, w, h) + var s = newFileStream(fileName, fmWrite) + png.writeChunks s + proc getFilterTypesInterlaced(png: PNG): seq[string] = var header = PNGHeader(png.getChunk(IHDR)) var idat = PNGData(png.getChunk(IDAT)) diff --git a/readme.md b/readme.md index 59bea9a..b2ffb9f 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ #nimPNG Portable Network Graphics Encoder and Decoder written in Nim -store lossless image with godd compression +store lossless image with good compression all PNG standard color mode are supported: @@ -41,4 +41,7 @@ Supported color conversions: - any grey or grey+alpha, to grey or grey+alpha - anything to a palette, as long as the palette has the requested colors in it - removing alpha channel -- higher to smaller bitdepth, and vice versa \ No newline at end of file +- higher to smaller bitdepth, and vice versa + +###Feature planned: +- streaming for progressive loading diff --git a/testCodec.nim b/testCodec.nim index a481472..f4942b8 100644 --- a/testCodec.nim +++ b/testCodec.nim @@ -1,4 +1,4 @@ -import nimPNG, streams, math, unsigned, strutils, tables +import nimPNG, streams, math, unsigned, strutils, tables, base64 type Image = ref object @@ -7,25 +7,8 @@ type colorType: PNGcolorType bitDepth: int -proc fromBase64(v: int): int = - if(v >= ord('A')) and (v <= ord('Z')): return (v - ord('A')) - if(v >= ord('a')) and (v <= ord('z')): return (v - ord('a') + 26) - if(v >= ord('0')) and (v <= ord('9')): return (v - ord('0') + 52) - if v == ord('+'): return 62 - if v == ord('/'): return 63 - result = 0 #v == '=' - proc fromBase64(input: string): string = - var i = 0 - result = "" - while (i + 3) < input.len: - let v = 262144 * fromBase64(input[i].int) + - 4096 * fromBase64(input[i + 1].int) + - 64 * fromBase64(input[i + 2].int) + fromBase64(input[i + 3].int) - result.add chr((v shr 16) and 0xff) - if input[i + 3] != '=': result.add chr((v shr 8) and 0xff) - if input[i + 2] != '=': result.add chr((v shr 0) and 0xff) - inc(i, 4) + result = base64.decode(input) proc assertEquals[T, U](expected: T, actual: U, message = "") = if expected != actual.T: @@ -33,11 +16,6 @@ proc assertEquals[T, U](expected: T, actual: U, message = "") = "Message: ", message quit() -proc assertTrue(value: bool, message = "") = - if not value: - echo "Error: expected true. Message: ", message - quit() - proc getNumColorChannels(colorType: PNGcolorType): int = case colorType of LCT_GREY: result = 1 @@ -223,10 +201,10 @@ proc testColor(r, g, b, a: int) = doCodecTest(image3) #a 256th color - image.data[255 * 4 + 0] = 255.chr - image.data[255 * 4 + 1] = 255.chr - image.data[255 * 4 + 2] = 255.chr - image.data[255 * 4 + 3] = 255.chr + 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 doCodecTest(image3) @@ -334,6 +312,7 @@ proc doPngSuiteEqualTest(b64a, b64b: string) = proc testPngSuiteTiny() = echo "testPngSuiteTiny" + doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAFS3GZcAAAABGdBTUEAAYagMeiWXwAAAANzQklU" & "BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=", 1, 1, 0, 0, 255, 255) #s01n3p01.png @@ -883,7 +862,7 @@ proc testAutoColorModel(colors: string, inbitDepth: int, colorType: PNGcolorType s.setPosition 0 var raw = s.PNGDecode() var info = raw.getInfo() - var decoded = png.convert(colorType, inbitDepth) + var decoded = raw.convert(LCT_RGBA, inbitdepth) assert num == info.width assert 1 == info.height @@ -891,15 +870,20 @@ proc testAutoColorModel(colors: string, inbitDepth: int, colorType: PNGcolorType assert bitDepth == info.mode.bitDepth assert key == info.mode.keyDefined - if inbitDepth == 8: - colors.toHex - colors2.toHex - for i in 0..colors.high: - assert colors[i] == decoded.data[i] - else : - let len = colors.len div 2 - for i in 0.. no more key testAutoColorModel(rgb_key2, 8, LCT_RGBA, 8, false) - + 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) - + 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 @@ -1008,19 +992,18 @@ proc testAutoColorModels() = testAutoColorModel(alpha16, 16, LCT_RGBA, 16, false) proc doMain() = - #testPNGCodec() - #testPngSuiteTiny() - #testPaletteFilterTypesZero() - #testComplexPNG() - #testPredefinedFilters() - #testColorKeyConvert() - #testColorConvert() - #testColorConvert2() - #testPaletteToPaletteConvert() - #testRGBToPaletteConvert() - #test16bitColorEndianness(); - #testNoAutoConvert(); + testPNGCodec() + testPngSuiteTiny() + testPaletteFilterTypesZero() + testComplexPNG() + testPredefinedFilters() + testColorKeyConvert() + testColorConvert() + testColorConvert2() + testPaletteToPaletteConvert() + testRGBToPaletteConvert() + test16bitColorEndianness(); + testNoAutoConvert(); testAutoColorModels(); - - echo getTotalMem() -doMain() \ No newline at end of file + +doMain() diff --git a/testSuite.nim b/testSuite.nim index de598f6..34bc981 100644 --- a/testSuite.nim +++ b/testSuite.nim @@ -6,11 +6,12 @@ type data: string proc writeWORD(s: Stream, val: int) = - let word = int16(val) + let word = val.int16 s.write(word) proc writeDWORD(s: Stream, val: int) = - s.write(val) + let dword = val.int32 + s.write(dword) proc newBMP(w, h: int): BMP = new(result) @@ -49,7 +50,7 @@ proc write(s: Stream, bmp: BMP) = if paddingLen > 0: s.write(padding) proc loadPNG(fileName: string): BMP = - var settings = makeDefaultPNGDecoderSettings() + var settings = makePNGDecoder() settings.readTextChunks = true var png = loadPNG24(fileName, settings) if png == nil: return nil