Coder Social home page Coder Social logo

nimpng's Introduction

nimPNG (PNG + APNG)

Portable Network Graphics Encoder and Decoder written in Nim store lossless image with good compression.

Notable releases:

  • 0.2.0 support Animated PNG!
  • 0.2.6 compile with --gc:arc.
  • 0.3.0 new set of API using seq[uint8] and new method to handle error.
  • 0.3.1 fix new API bug and add openArray API

nimble license Github action

all PNG standard color mode are supported:

  • LCT_GREY = 0, # greyscale: 1,2,4,8,16 bit
  • LCT_RGB = 2, # RGB: 8,16 bit
  • LCT_PALETTE = 3, # palette: 1,2,4,8 bit
  • LCT_GREY_ALPHA = 4, # greyscale with alpha: 8,16 bit
  • LCT_RGBA = 6 # RGB with alpha: 8,16 bit

both interlaced and non-interlaced mode supported

recognize all PNG standard chunks: IHDR, IEND, PLTE, IDAT, tRNS, bKGD, pHYs, tIME, iTXt, zTXt tEXt, gAMA, cHRM, sRGB, iCCP, sBIT, sPLT, hIST

unknown chunks will be handled properly

the following chunks are supported (generated/interpreted) by both encoder and decoder:

  • IHDR: header information
  • PLTE: color palette
  • IDAT: pixel data
  • IEND: the final chunk
  • tRNS: transparency for palettized images
  • tEXt: textual information
  • zTXt: compressed textual information
  • iTXt: international textual information
  • bKGD: suggested background color
  • pHYs: physical dimensions
  • tIME: modification time

the following chunks are parsed correctly, but not used by decoder: cHRM, gAMA, iCCP, sRGB, sBIT, hIST, sPLT

Supported color conversions:

  • anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA
  • 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

Planned Feature(s):

  • streaming for progressive loading

Basic Usage

import nimPNG

let png = loadPNG32("image.png")
# is equivalent to:
# let png = loadPNG("image.png", LCT_RGBA, 8)
# will produce rgba pixels:
# png.width -> width of the image
# png.height -> height of the image
# png.data -> pixels data in RGBA format

if you already have the whole file in memory:

let png = decodePNG32(raw_bytes)
# will do the same as above

other variants:

  • loadPNG24 -> will produce pixels in RGB format 8 bpp
  • decodePNG24 -> load png from memory instead of file

to create PNG:

  • savePNG32("output.png", rgba_pixels, width, height) or savePNG24
  • encodePNG32(rgba_pixels, width, height) or encodePNG24

special notes:

  • Use loadPNG or savePNG if you need specific input/output format by supplying supported colorType and bitDepth information.
  • Use encodePNG or decodePNG to do in-memory encoding/decoding by supplying desired colorType and bitDepth information.

pixels are stored as raw bytes using Nim's string/seq[T] as container:

Byte Order Format
r1,g1,b1,a1,...,rn,gn,bn,an RGBA 8 bit
r1,g1,b1,r2,g2,b2,...,rn,gn,bn RGB 8 bit
grey1,grey2,grey3, ..., greyn GREY 8 bit
grey1,a1,grey2,a2,...,greyn,an GREY ALPHA 8 bit

Animated PNG (APNG)

Since version 0.2.0, nimPNG provides support for Animated PNG.

Both decoder and encoder recognize/generate APNG chunks correctly: acTL, fcTL, fdAT.

Decoded frames is provided as is, the dimension and coordinate offset might be different with default frame. No alpha blending or other blending method performed. It is up to the application to do proper in-memory rendering before displaying the animation. Don't ask how to do it, any decent graphics rendering library have their own set of API to do alpha blending and offset rendering. In the future nimPNG might be shipped with simple frame rendering utility for common cases. Right now nimPNG is just a PNG encoder/decoder.

Decoding

#let png = loadPNG32("image.png")
# or
#let png = loadPNG("image.png", LCT_RGBA, 8)
# or
#let png = decodePNG32(raw_bytes)

The usual loadPNG and decodePNG can decode both unanimated and animated PNG. png.width, png.height, png.data works as usual. If the decoded PNG is an APNG, png.data will contains default frame. Animation frames can be accessible via png.frames. If it is not an APNG, png.frames will be have zero length.

Encoding

var png = prepareAPNG24(numPlays)
  • First step is to call prepareAPNG, prepareAPNG24, or prepareAPNG32. You also can specify how many times the animation will be played
  png.addDefaultImage(framePixels, w, h, ctl)
  • Second step is also mandatory, you should call addDefaultImage. ctl is optional, if you provide a ctl(Frame Control), the default image will be part of the animation. If ctl is nil, default image will not be part of animation.
  png.addFrame(frames[i].data, ctl)
  • Third step is calling addFrame one or more times. Here ctl is mandatory.
  png.saveAPNG("rainbow.png")
  # or
  var str = png.encodeAPNG()
  • Final step is to call saveAPNG if you want save it to a file or call encodeAPNG if you want to get the result in memory.

You can read the details of frame control from spec. You can also see an example in tester/test.nim -> generateAPNG

Installation via nimble

nimble install nimPNG

nimpng's People

Contributors

andreaferretti avatar bung87 avatar jangko avatar metagn avatar narimiran avatar yglukhov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nimpng's Issues

New API – can't savePNG32 with a seq[uint8]

due to a type mismatch.

Here's the error message:

/home/daknus/Coding/Nim/aglet/tests/tcloud.nim(145, 29) template/generic instantiation of `savePNG32` from here
/home/daknus/.nimble/pkgs/nimPNG-0.3.0/nimPNG.nim(3104, 20) template/generic instantiation of `savePNG32Impl` from here
/home/daknus/.nimble/pkgs/nimPNG-0.3.0/nimPNG.nim(2980, 16) template/generic instantiation of `savePNGImpl` from here
/home/daknus/.nimble/pkgs/nimPNG-0.3.0/nimPNG.nim(2971, 26) template/generic instantiation of `encodePNG` from here
/home/daknus/.nimble/pkgs/nimPNG-0.3.0/nimPNG.nim(2942, 21) template/generic instantiation of `encodePNG` from here
/home/daknus/.nimble/pkgs/nimPNG-0.3.0/nimPNG.nim(2929, 17) template/generic instantiation of `encoderCore` from here
/home/daknus/.nimble/pkgs/nimPNG-0.3.0/nimPNG.nim(2838, 8) template/generic instantiation of `autoChooseColor` from here
/home/daknus/.nimble/pkgs/nimPNG-0.3.0/nimPNG.nim(2518, 17) template/generic instantiation of `getColorProfile` from here
/home/daknus/.nimble/pkgs/nimPNG-0.3.0/nimPNG.nim(2503, 24) Error: type mismatch: got <seq[uint8], int, int, PNGColorMode, PNGColorProfile, Table[nimPNG.RGBA8, system.int]>
but expected one of:
proc calculateColorProfile(input: string; w, h: int; mode: PNGColorMode;
                          prof: PNGColorProfile; tree: var Table[RGBA8, int])
  first type mismatch at position: 1
  required type for input: string
  but expression 'png.pixels' is of type: seq[uint8]

expression: calculateColorProfile(png.pixels, png.width, png.height, mode, prof, tree)

How to remove all unnecessary data

Hi, I want to remove all unnecessary data from a png image, I usualy open the image in gimp and export with all options disabled.
But now I need to do this for ~200 images, so I wanted to automate this, and I thought about this package.

How can I get the equivalent of gimp's exported png ?
image
Thank you πŸ™‚

compiletime decoder

Hello janko

first:
I like your repository and the simplicity of the api very much.

Questions:
Is it possible to implement a compiletime decoder as well? Are there any known hurdles or problems? Have you ever tried this?

possible new API could be:
const png = loadStaticPNG32("image.png")

wrong readme. how to use loadPNG?

import nimPNG
let png = loadPNG("0.png", LCT_GREY, 8)
img_test.nim(3, 18) Error: type mismatch: got <string, PNGColorType, int literal(8)>
but expected one of: 
proc loadPNG(fileName: string; colorType: PNGColorType; bitDepth: int;
            settings: PNGDecoder): PNGResult
  first type mismatch at position: 4
  required type: PNGDecoder

expression: loadPNG("0.png", LCT_GREY, 8)

nimPNG relies on bugs of `newString` to work

As claried by nim-lang/Nim#22555

newString returns a new string of length len but with uninitialized

A potential solution is to define a newStringWithDefault function:

proc newStringWithDefault(x: int): string =
  result = newString(0)
  setLen(result, x)

Or find all the unexpected cases.

This hash look up in calculateColorProfile is the slowest part of saving an image.

I have profiled nimPNG code saving with VTune and 45% of the time is spent on this line:

nimPNG/nimPNG.nim

Line 2358 in 676bca4

if not tree.hasKey(p):

This line is in calculateColorProfile ... should it just be computed once? It seems like a very slow operation with the hashing taking up most of the time. Is there a way to compute calculateColorProfile only once?

image

image

I used vTune with nim c -d:release --debugger:native to produce this.

Does not work with Nim 0.19.0 when installed from nimble

Installing this library using nimble and simply trying to import it from any file results in the following error message:

../.nimble/pkgs/nimPNG-0.2.3/nimPNG.nim(29, 15) Error: cannot open file: private/buffer

Tested with Nim 0.19.0 Windows and Linux, the former installed from the official installer and the latter installed from choosenim.

Allow parsing into different data types

Essentially it would be nice if nimPNG could parse a 16-bit greyscale into a seq[uint16] for example, and similarly write this data. This shouldn't be too hard to implement, although might add a bit of complexity when handling byte ordering, but it would make it much easier to use nimPNG.

Resize image?

Would a PR to resize images be accepted here? Or is that beyond the scope of this module?

Edit: I should add that I have no idea how to do it. But I may be motivated enough to figure it out. :)

savePNG doesn't work for arrays

savePNG has some cases for openarray, but it only works for seq. This is what I was trying to do:

import nimPNG

var data: array[100, array[100, uint32]]

for x in 0..<50:
  for y in 0..<50:
    data[y][x] = 0xFF0000FF'u32

for x in 50..<100:
  for y in 0..<50:
    data[y][x] = 0x00FF00FF'u32

for x in 0..<50:
  for y in 50..<100:
    data[y][x] = 0x0000FFFF'u32

for x in 50..<100:
  for y in 50..<100:
    data[y][x] = 0x000000FF'u32

echo savePng("image.png", cast[array[data.len*data[0].len*4, uint8]](data), LCT_RGBA, 8, data.len, data[0].len)

But I get this error:

/tmp/png.nim(22, 13) template/generic instantiation of `savePNG` from here
/home/peter/.nimble/pkgs/nimPNG-#head/nimPNG.nim(3131, 18) template/generic instantiation of `savePNGImpl` from here
/home/peter/.nimble/pkgs/nimPNG-#head/nimPNG.nim(3002, 26) template/generic instantiation of `encodePNG` from here
/home/peter/.nimble/pkgs/nimPNG-#head/nimPNG.nim(2967, 21) template/generic instantiation of `encodePNG` from here
/home/peter/.nimble/pkgs/nimPNG-#head/nimPNG.nim(2954, 17) template/generic instantiation of `encoderCore` from here
/home/peter/.nimble/pkgs/nimPNG-#head/nimPNG.nim(2870, 54) template/generic instantiation of `newStorage` from here
/home/peter/.nimble/pkgs/nimPNG-#head/nimPNG.nim(282, 32) Error: type mismatch: got <array[0..39999, uint8]>
but expected one of: 
template getUnderlyingType[T](_: seq[T]): untyped
  first type mismatch at position: 1
  required type for _: seq[T]
  but expression 'T' is of type: array[0..39999, uint8]

expression: getUnderlyingType(T)

On the head branch, and this on the latest tagged version:

/tmp/png.nim(22, 13) template/generic instantiation of `savePNG` from here
/home/peter/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(3127, 18) template/generic instantiation of `savePNGImpl` from here
/home/peter/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(2998, 26) template/generic instantiation of `encodePNG` from here
/home/peter/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(2963, 21) template/generic instantiation of `encodePNG` from here
/home/peter/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(2950, 17) template/generic instantiation of `encoderCore` from here
/home/peter/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(2866, 54) template/generic instantiation of `newStorage` from here
/home/peter/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(280, 18) Error: type mismatch: got <seq[uint8]> but expected 'array[0..39999, uint8]'

Throw exceptions on errors instead of writing to stdout

Providing a good error message for the end user is important, especially in GUI applications. For a typical Windows app user, they're not going to look in the console, they'll expect a nice error message stating that the image could not be loaded.

The current way nimPNG handles exceptions is func-friendly, but fundamentally wrong, because Nim's error handling mechanism inherently has side effects. Unless a Result[T, E] is introduced to the stdlib, debugEcho is only a way of mitigating the problem and does not solve anything except frustrate the library users because their stdout is trashed and they don't have an easy way of providing an error message to the end user.

Proposal: Raise IoErrors instead of echoing errors to stdout.

Cannot compile on Nim devel

Due the changes in nim handling, I get the following error:

/Users/andrea/.nimble/pkgs/nimPNG-#head/nimPNG.nim(333, 18) template/generic instantiation from here
lib/system.nim(408, 10) Error: type mismatch: got <seq[RGBA8], nil>
but expected one of: 
proc `==`[Enum: enum](x, y: Enum): bool
  first type mismatch at position: 1
  required type: Enum: enum
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: pointer): bool
  first type mismatch at position: 1
  required type: pointer
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: string): bool
  first type mismatch at position: 1
  required type: string
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: char): bool
  first type mismatch at position: 1
  required type: char
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: bool): bool
  first type mismatch at position: 1
  required type: bool
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[T](x, y: set[T]): bool
  first type mismatch at position: 1
  required type: set[T]
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[T](x, y: ref T): bool
  first type mismatch at position: 1
  required type: ref T
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[T](x, y: ptr T): bool
  first type mismatch at position: 1
  required type: ptr T
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[T: proc](x, y: T): bool
  first type mismatch at position: 1
  required type: T: proc
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(a, b: PNGChunkType): bool
  first type mismatch at position: 1
  required type: PNGChunkType
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x: type(nil); y: string): bool
  first type mismatch at position: 1
  required type: nil
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x: string; y: type(nil)): bool
  first type mismatch at position: 1
  required type: string
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: int8): bool
  first type mismatch at position: 1
  required type: int8
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: int): bool
  first type mismatch at position: 1
  required type: int
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: int16): bool
  first type mismatch at position: 1
  required type: int16
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: int32): bool
  first type mismatch at position: 1
  required type: int32
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[I, T](x, y: array[I, T]): bool
  first type mismatch at position: 1
  required type: array[I, T]
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[T](x, y: seq[T]): bool
  first type mismatch at position: 2
  required type: seq[T]
  but expression 'nil' is of type: nil
proc `==`(x, y: cstring): bool
  first type mismatch at position: 1
  required type: cstring
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: float): bool
  first type mismatch at position: 1
  required type: float
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: int64): bool
  first type mismatch at position: 1
  required type: int64
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[T: SomeUnsignedInt](x, y: T): bool
  first type mismatch at position: 1
  required type: T: SomeUnsignedInt
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[T: tuple |
    object](x, y: T): bool
  first type mismatch at position: 1
  required type: T: tuple or object
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`(x, y: float32): bool
  first type mismatch at position: 1
  required type: float32
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[A, B](s, t: OrderedTableRef[A, B]): bool
  first type mismatch at position: 1
  required type: OrderedTableRef[==.A, ==.B]
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[A, B](s, t: Table[A, B]): bool
  first type mismatch at position: 1
  required type: Table[==.A, ==.B]
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[A, B](s, t: OrderedTable[A, B]): bool
  first type mismatch at position: 1
  required type: OrderedTable[==.A, ==.B]
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[A, B](s, t: TableRef[A, B]): bool
  first type mismatch at position: 1
  required type: TableRef[==.A, ==.B]
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[A](s, t: CountTableRef[A]): bool
  first type mismatch at position: 1
  required type: CountTableRef[==.A]
  but expression 'src.palette' is of type: seq[RGBA8]
proc `==`[A](s, t: CountTable[A]): bool
  first type mismatch at position: 1
  required type: CountTable[==.A]
  but expression 'src.palette' is of type: seq[RGBA8]

expression: src.palette == nil

nim 1.6.0 savePNG results in too nested template instantiation

Nim Compiler Version 1.6.0 [Windows: amd64]

  savePNG(filename, data, LCT_RGBA, 32, swCanvas32.w, swCanvas32.h)

results in:

C:\Users\user\git\nico\nico\backends\sdl2.nim(807, 10) template/generic instantiation of `savePNG` from here
C:\Users\user\.nimble\pkgs\nimPNG-#head\nimPNG.nim(3131, 18) template/generic instantiation of `savePNGImpl` from here
C:\Users\user\.nimble\pkgs\nimPNG-#head\nimPNG.nim(3002, 26) template/generic instantiation of `encodePNG` from here
C:\Users\user\.nimble\pkgs\nimPNG-#head\nimPNG.nim(2967, 21) template/generic instantiation of `encodePNG` from here
C:\Users\user\.nimble\pkgs\nimPNG-#head\nimPNG.nim(2954, 17) template/generic instantiation of `encoderCore` from here
C:\Users\user\.nimble\pkgs\nimPNG-#head\nimPNG.nim(2873, 18) template/generic instantiation of `frameConvert` from here
C:\Users\user\.nimble\pkgs\nimPNG-#head\nimPNG.nim(2833, 24) template/generic instantiation of `preProcessScanLines` from here
C:\Users\user\.nimble\pkgs\nimPNG-#head\nimPNG.nim(2642, 13) template/generic instantiation of `filter` from here
C:\Users\user\.nimble\pkgs\nimPNG-#head\nimPNG.nim(2620, 30) template/generic instantiation of `filterMinsum` from here
C:\Users\user\.nimble\pkgs\nimPNG-#head\nimPNG\filters.nim(138, 31) Error: template instantiation too nested

"value out of range" error when writing PNG

I'm new to Nim and trying out nimPNG. I'm using the following code:

import nimPNG

let width = 5
let height = 10
var grey = newString(width * height)
var i = 0
grey[i] = 120.char; i += 1
grey[i] = 95.char; i += 1
grey[i] = 85.char; i += 1
grey[i] = 95.char; i += 1
grey[i] = 120.char; i += 1
grey[i] = 95.char; i += 1
grey[i] = 60.char; i += 1
grey[i] = 42.char; i += 1
grey[i] = 60.char; i += 1
grey[i] = 95.char; i += 1
grey[i] = 85.char; i += 1
grey[i] = 42.char; i += 1
grey[i] = 0.char; i += 1
grey[i] = 38.char; i += 1
grey[i] = 76.char; i += 1
grey[i] = 95.char; i += 1
grey[i] = 57.char; i += 1
grey[i] = 19.char; i += 1
grey[i] = 19.char; i += 1
grey[i] = 57.char; i += 1
grey[i] = 114.char; i += 1
grey[i] = 76.char; i += 1
grey[i] = 38.char; i += 1
grey[i] = 0.char; i += 1
grey[i] = 38.char; i += 1
grey[i] = 133.char; i += 1
grey[i] = 95.char; i += 1
grey[i] = 57.char; i += 1
grey[i] = 19.char; i += 1
grey[i] = 19.char; i += 1
grey[i] = 152.char; i += 1
grey[i] = 114.char; i += 1
grey[i] = 76.char; i += 1
grey[i] = 38.char; i += 1
grey[i] = 0.char; i += 1
grey[i] = 171.char; i += 1
grey[i] = 133.char; i += 1
grey[i] = 95.char; i += 1
grey[i] = 60.char; i += 1
grey[i] = 42.char; i += 1
grey[i] = 190.char; i += 1
grey[i] = 153.char; i += 1
grey[i] = 120.char; i += 1
grey[i] = 95.char; i += 1
grey[i] = 85.char; i += 1
# These lines cause a "value out of range: -25" error
grey[i] = 212.char; i += 1
grey[i] = 180.char; i += 1
grey[i] = 153.char; i += 1
grey[i] = 134.char; i += 1
grey[i] = 127.char; i += 1
# These lines will work fine.  (Values up to 10, in fact)
#grey[i] = 0.char; i += 1
#grey[i] = 0.char; i += 1
#grey[i] = 0.char; i += 1
#grey[i] = 0.char; i += 1
#grey[i] = 0.char; i += 1

if not savePNG("/tmp/out.png", grey, LCT_GREY, 8, width, height):
    echo "Could not write to /tmp/out.png"

This code gives a "value out of range: -25" message printed out, and savePNG() returns false. The strange thing is that any values greater than 10, I think, for the bottom row will give some form of this error (the actual number complained about varies). I also tried LCT_RGB with the string 3 times as large, writing the value 3 times each time, and got exactly the same error. I also couldn't find the error string anywhere in the nimPNG code, so I'm not sure if this is a bug in nimPNG or Nim 1.0 or my code. I'd assume my code, but I can't figure out what would be wrong. Especially since the following code works:

for i in 0 ..< width * height:
    grey[i] = 255.char
if not savePNG("/tmp/out.png", grey, LCT_GREY, 8, width, height):
    echo "Could not write to /tmp/out.png"

nimPNG always fails unless in release mode

For instance, if I try to run the code from #9 on the image posted there, I get an assertion failure.

import nimPNG
let data = loadPNG32("sample.png")
assert(not data.isNil)
djerzinski:nim andrea$ nim c -r example
...
example.nim(7)           example
system.nim(3788)         failedAssertImpl
system.nim(3781)         raiseAssert
system.nim(2843)         sysFatal
Error: unhandled exception: not isNil(data)  [AssertionError]

But if I run the same code with -d:release, everything seems to work fine :-?

Compatibility with Nim 1.0.0

I am trying to use nimPNG with Nim 1.0.0 but I am having lots of issues while compiling (on MacOS).

Normally, I'd try to debug this myself, but I am at a loss here. Here is the output of tests on my Mac:

# nim c -r tester/test.nim
/Users/andrea/esperimenti/nimPNG/tester/test.nim(103) test
/Users/andrea/esperimenti/nimPNG/tester/test.nim(99) main
/Users/andrea/.mynim/devel/lib/system/assertions.nim(27) failedAssertImpl
/Users/andrea/.mynim/devel/lib/system/assertions.nim(20) raiseAssert
/Users/andrea/.mynim/devel/lib/system/fatal.nim(39) sysFatal
Error: unhandled exception: /Users/andrea/esperimenti/nimPNG/tester/test.nim(99, 9) `not data.isNil`  [AssertionError]
# nim c -r tester/testCodec.nim
codec test 1 1
codec test 2 2
codec test 1 1
codec test 7 7
codec test 127 127
codec test 127 127
codec other pattern 1
/Users/andrea/esperimenti/nimPNG/tester/testCodec.nim(1197) testCodec
/Users/andrea/esperimenti/nimPNG/tester/testCodec.nim(1176) doMain
/Users/andrea/esperimenti/nimPNG/tester/testCodec.nim(240) testPNGCodec
/Users/andrea/esperimenti/nimPNG/tester/testCodec.nim(116) testOtherPattern1
/Users/andrea/esperimenti/nimPNG/tester/testCodec.nim(83) doCodecTest
/Users/andrea/esperimenti/nimPNG/tester/testCodec.nim(62) doCodecTest
/Users/andrea/esperimenti/nimPNG/nimPNG.nim(3262) encodePNG
/Users/andrea/esperimenti/nimPNG/nimPNG.nim(3249) encodePNG
/Users/andrea/esperimenti/nimPNG/nimPNG.nim(3168) encoderCore
/Users/andrea/esperimenti/nimPNG/nimPNG.nim(3135) frameConvert
/Users/andrea/esperimenti/nimPNG/nimPNG.nim(2963) preProcessScanLines
/Users/andrea/esperimenti/nimPNG/nimPNG.nim(2903) filter
/Users/andrea/esperimenti/nimPNG/nimPNG.nim(2751) filterMinsum
/Users/andrea/esperimenti/nimPNG/nimPNG.nim(2685) filterScanLine
/Users/andrea/.mynim/devel/lib/system/fatal.nim(48) sysFatal
Error: unhandled exception: value out of range: -2 [RangeError]
# nim c -r tester/testSuite.nim
CC: stdlib_assertions.nim
CC: stdlib_dollars.nim
CC: stdlib_io.nim
CC: stdlib_system.nim
CC: stdlib_streams.nim
CC: stdlib_math.nim
CC: stdlib_strutils.nim
CC: stdlib_pathnorm.nim
CC: stdlib_posix.nim
CC: stdlib_times.nim
CC: stdlib_os.nim
CC: stdlib_tables.nim
CC: ../nimPNG/buffer.nim
CC: ../nimPNG/nimz.nim
CC: ../nimPNG.nim
CC: minibmp.nim
CC: testSuite.nim
Error: execution of an external compiler program 'clang -c  -w  -I/Users/andrea/.mynim/devel/lib -I/Users/andrea/esperimenti/nimPNG/tester -o /Users/andrea/.cache/nim/testSuite_d/@[email protected] /Users/andrea/.cache/nim/testSuite_d/@[email protected]' failed with exit code: 1

/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8627:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_500)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.r) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8633:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_502)] = ((NIM_CHAR)chckRange((mode_t)(p.r & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8659:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_505)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.r) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8665:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_507)] = ((NIM_CHAR)chckRange((mode_t)(p.r & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8671:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_509)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.g) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8677:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_511)] = ((NIM_CHAR)chckRange((mode_t)(p.g & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8683:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_513)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.b) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8689:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_515)] = ((NIM_CHAR)chckRange((mode_t)(p.b & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8711:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_518)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.r) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8717:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_520)] = ((NIM_CHAR)chckRange((mode_t)(p.r & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8723:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_522)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.a) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8729:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_524)] = ((NIM_CHAR)chckRange((mode_t)(p.a & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8759:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_527)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.r) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8765:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_529)] = ((NIM_CHAR)chckRange((mode_t)(p.r & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8771:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_531)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.g) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8777:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_533)] = ((NIM_CHAR)chckRange((mode_t)(p.g & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8783:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_535)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.b) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8789:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_537)] = ((NIM_CHAR)chckRange((mode_t)(p.b & ((NI) 255)), 0, 255));
                                                                                            ^
/Users/andrea/.cache/nim/testSuite_d/@[email protected]:8795:86: error: use of undeclared identifier 'mode_t'
        (*output).data->data[(NI)(TM__CaabljTnIulNq49cYzoUYeQ_539)] = ((NIM_CHAR)chckRange((mode_t)((NU16)((NU16)(p.a) >> (NU64)(((NI) 8))) & ((NI) 255)), 0, 255));
                                                                                            ^
fatal error: too many errors emitted, stopping now [-ferror-limit=]

Move tests to another repo

The nimPNG repo includes a tests folder which is quite large. Also, some of the links in the tests are very inappropriate. I don't want kids using my game engine (which depends on nimPNG) to see that πŸ™ˆ

image

If you install via Nimble, the test files don't get copied to the pkgs directory. However, they do remain if you are using Atlas or simply managing your own dependencies via git submodules.

Therefore I think it would be best to move the tests to another repo.

Error: unhandled exception: cannot read from stream [IOError]

When trying to parse large base64-string's I get the error:

Error: unhandled exception: cannot read from stream [IOError]

The base64-string origins from photos taken with iPhones, which then is converted to base64 in the browser. I can't reproduce exactly when this happens, so my guessing is, that it might be due to the details in the photo (?).

I have the base64-strings which works flawless with online converters and linux own base64 -d p.txt >> p.png. I have also validated the base64-string, and it is valid base64.

Test code:

import base64, streams
import nimPNG

const b = """loooong base64"""

proc fromBase64(input: string): string =
  result = base64.decode(input)

proc base64ToPng*(b64: string, path: string): bool =
  let
    s = newStringStream(fromBase64(b64))
    decoded = s.decodePNG(LCT_RGBA, 8) # <== This one, line 12

  result = (savePNG32(path, decoded.data, decoded.width, decoded.height))

echo base64ToPng(b, "debug.png")

GH does not like the long base64 string so here is a Gist with it: https://gist.github.com/ThomasTJdev/7a14a8b8309d67a7188c3cfe61eff869

The error message is:

/home/user/tmp/nim/image/base64debug.nim(22) base64debug
/home/user/tmp/nim/image/base64debug.nim(17) base64ToPng
/home/user/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(1917) decodePNG
/home/user/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(1842) decodePNG
/home/user/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(939) parsePNG
/home/user/.nimble/pkgs/nimPNG-0.3.1/nimPNG.nim(387) readInt32BE
/home/user/.choosenim/toolchains/nim-1.2.6/lib/pure/streams.nim(570) readInt32
/home/user/.choosenim/toolchains/nim-1.2.6/lib/pure/streams.nim(382) read
Error: unhandled exception: cannot read from stream [IOError]

Improve chunk parser flexibility when decoding incomplete input data.

Currently nimPNG parser and decoder works in 'strict' mode. It will reject any invalid input. But there are some classes of invalid input that can be just ignored or skipped. This 'relaxed' decoding mode of course will probably produce incomplete image, but in certain situation this is acceptable or even desired.

In the future perhaps we can add more relaxation. Right now we can focus to implement invalid chunk skipper.
This relaxed mode and any other future relaxation should be accessible via decoder flags/settings/configuration and not forced to user. User should be able to select how much data or which recovery algorithm they wanted. And user should be able to stick with 'strict' parsing mode if there is no room for corrupted/incomplete input.

see also issue #55.

Png is loaded incorrectly

The following code, which just loads and saves a PNG produces artifacts with image attached:

let png = loadPNG32(original_path)
discard savePNG32(output_path, png.data, png.width, png.height)

Original image: edit: removed
Output image: edit: removed

`decodePNG` colorType unexpected

expect: LCT_RGBA

import os
import streams
import nimPNG

createDir("outputs")

# 2 * 2
let data: seq[uint8] = @[
    uint8(255), 255, 255, 255,
    0, 0, 0, 255,                     # <--- black
    0, 0, 0, 255,
    255, 255, 255, 255,
]

discard savePNG32("outputs/test.png", data, 2, 2)

let png = decodePNG(newStringStream(readFile("outputs/test.png")))
let pngInfo = getInfo(png)

echo pngInfo.mode.colorType

output: LCT_GREY

import os
import streams
import nimPNG

createDir("outputs")

# 2 * 2
let data: seq[uint8] = @[
    uint8(255), 255, 255, 255,
    255, 0, 0, 255,                      # <--- red
    255, 0, 0, 255,
    255, 255, 255, 255,
]

discard savePNG32("outputs/test.png", data, 2, 2)

let png = decodePNG(newStringStream(readFile("outputs/test.png")))
let pngInfo = getInfo(png)

echo pngInfo.mode.colorType

output: LCT_RGB

Doesn't work by default on Nim v2.0.0

Someone brought up in the realtime chat that import nimPNG triggers this Error: undeclared identifier: 'shallowCopy' on Nim version 2.0.0. I told them to just use --gc:refc for now, but this should probably be fixed.

Warning: conversion to enum with holes is unsafe: PNGColorType(typ`gensym14) [HoleEnumConv]

Nim 1.6.2:

C:\Users\User\AppData\Roaming\Sublime Text\Packages\Theme - One\test.nim(3, 21) template/generic instantiation of `loadPNG32` from here
C:\Users\User\.nimble\pkgs\nimPNG-0.3.2\nimPNG.nim(1888, 12) template/generic instantiation of `loadPNG` from here
C:\Users\User\.nimble\pkgs\nimPNG-0.3.2\nimPNG.nim(1882, 26) template/generic instantiation of `decodePNG` from here
C:\Users\User\.nimble\pkgs\nimPNG-0.3.2\nimPNG.nim(1846, 24) template/generic instantiation of `parsePNG` from here
C:\Users\User\.nimble\pkgs\nimPNG-0.3.2\nimPNG.nim(950, 19) template/generic instantiation of `parseChunk` from here
C:\Users\User\.nimble\pkgs\nimPNG-0.3.2\nimPNG.nim(892, 31) template/generic instantiation of `parseChunk` from here
C:\Users\User\.nimble\pkgs\nimPNG-0.3.2\nimPNG.nim(457, 26) Warning: conversion to enum with holes is unsafe: PNGColorType(typ`gensym14) [HoleEnumConv]

Doesn't compile on Nim devel due to `shallowCopy` being removed from the language.

in Nim 2.0, shallowCopy will be removed. This means every occurrence of shallowCopy in the library must be replaced by something else.

See: nim-lang/Nim#20017

For simple situations, sink may be good enough:

e.g.

proc init*[T](b: var Buffer[T], d: T) =
  shallowCopy(b.data, d)
  b.offset = 0

becomes

proc init*[T](b: var Buffer[T], d: sink T) =
  b.data = d
  b.offset = 0

The compiler will then decide whether to move or copy the data depending on whether it can prove that buf.init(s) is the last usage of s or not.

However, there are some problems:

  1. If the compiler decides to copy, then there will be a performance hit. To keep good performance in this case, I believe some sort of buffer-via-pointer is needed, e.g. trick's View or collections' View or Nim's experimental view types.

  2. There are some uses of shallowCopy in the codebase that don't neatly fit the sink pattern, e.g.

    shallowCopy(png.apngPixels[0], png.pixels)
    frameConvert[T](png, modeIn, modeOut, png.width, png.height, 0, state)
    shallowCopy(png.pixels, png.apngPixels[0])

    (edit, maybe they can be solved like this?)

    png.apngPixels[0] = move(png.pixels)
    frameConvert[T](png, modeIn, modeOut, png.width, png.height, 0, state)
    png.pixels = move(png.apngPixels[0])

Can't write 32 bit unsigned ints

Hi. Since I haven't been able to find many easy PNG libraries out there, this has been already useful to me, but I have this question? How come I can't write 32 bit unsigned integers. Here is my sample code:

import nimPNG

var
  # This works:
#  data = @[
#    0xFF.uint8, 0xFF.uint8, 0xFF.uint8, 0x80.uint8, # 50% white
#    0xFF.uint8, 0x00.uint8, 0x00.uint8, 0xFF.uint8, # red
#    0x00.uint8, 0xFF.uint8, 0x00.uint8, 0xFF.uint8, # green
#    0x00.uint8, 0x00.uint8, 0xFF.uint8, 0xFF.uint8  # blue
#  ]
  # But this doesn't:
  data = @[
    0xFFFFFF80.uint32, # 50% white
    0xFF0000FF.uint32, # red
    0x00FF00FF.uint32, # green
    0x0000FFFF.uint32  # blue
  ]
  pixels: string = cast[string](data)

echo "saved: " & $savePNG32("test.png", pixels, 2, 2)

On the terminal, I'm also getting the message: "not enough input to encode". What's going on? the byte size of both things should be the same.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.