pngload

2022-07-08

A reader for the PNG image format.

Upstream URL

github.com/bufferswap/pngload

Author

Michael Fiano <mail@mfiano.net>, Bart Botta <00003b@gmail.com>

License

MIT
README

pngload

A PNG (Portable Network Graphics) image format decoder.

Overview

pngload can be used to load images in the PNG image format, both from files on disk, or streams in memory. This library was written out of frustration with png-read, which was the only native Common Lisp code that supports PNG.

What makes pngload different than png-read?

Speed

pngload is optimized for speed and portability across many different Common Lisp implementation and architecture combinations. On 64-bit SBCL it is more than 3x faster than the png-read library when decoding a particular large 4096x4096 RGBA image. Rough benchmarking in our particular environment at the time of writing shows:

  • pngload: 0.901s
  • png-read: 3.058s

To overcome some performance bottlenecks, we wrote our own decompressor, as the alternatives were too slow and not easily optimizable.

Also, we use the mmap library on operating systems that support it, with a fallback path when not supported.

Cleaner code

pngload should be a lot more hackable, and have more of an educational value than png-read, even after adding lots of type declarations and restructuring the code away from its original cleanliness in favor of performance.

Full support for all chunks

The entire concrete syntax tree is parsed, and is visible as a slot in the returned PNG object when decoding an image. png-read does not support some of these. Additionally, human-readable formats are stored outside of the parse tree in the top-level object. For instance, if a chunk specifying gamma correction is parsed, this will be stored as a floating-point value, rather than multiplied by 100,000 and stored as an integer. Again, the raw data is stored in the PARSE-TREE slot of the returned object, should you ever need more.

Fully conforming with the PNG specification

pngload is able to load all images in PNGSuite correctly. png-read claims that it can load them all, but they were not checked for validity.

Stores data in a format that is expected by opticl

opticl has supported pngload since its first release, which gives you faster PNG loading automatically if you were already using opticl.

Support for PNG extensions

pngload supports additional extension chunk types, such as EXIF information.

Optionally parse metadata only

pngload can optionally parse only the metadata, skipping decoding completely, in order to quickly retrieve information about an image.

Optionally decode as a 1-dimensional array

Instead of decoding to a format which is compatible with opticl, pngload can now decode to a flat 1-D array. This is useful for OpenGL texture uploading and some other applications.

Optionally flip the Y axis

pngload can optionally flip the Y axis when decoding, for when the origin is expected to be at the bottom left instead of the top left, as with OpenGL texture rendering.

Optionally write to foreign memory

pngload can optionally write to foreign memory using static-vectors. This is useful when needing to efficiently pass a pointer to the image data with a foreign library, such as with OpenGL.

Install

(ql:quickload :pngload)

Usage

Usage is quite simple:

(pngload:load-file #p"/path/to/file.png")

This will return an object which includes everything you would need to render the image data, or query it for other useful data.

Additionally, you may load a PNG datastream from a Common Lisp stream with:

(pngload:load-stream stream)

Both LOAD-FILE and LOAD-STREAM accept an optional keyword argument, which can be used to disable the slow process of decoding the image data. This can be used to very quickly get information about the file, including but not limited to, the dimensions, last modification date, or palette information. Image data will be unavailable with this option, obviously. To use this fast reading method:

(pngload:load-file #p"/path/to/file.png" :decode nil)

or:

(pngload:load-stream stream :decode nil)

Additionally, both LOAD-FILE and LOAD-STREAM may take the following keyword arguments:

FLATTEN when non-NIL, will decode the image data to a 1-dimensional array, rather than the default method which is to be compatible with opticl.

FLIP-Y when non-NIL, will flip the pixels on the Y axis, for when the origin is expected to be at the bottom/left instead of the top/left.

STATIC-VECTOR when non-NIL, will decode to foreign memory. It is up to the user to free memory when they are finished with it. Alternatively, you can use WITH-PNG-IN-STATIC-VECTOR which will automatically free the memory for you.

Querying Metadata

pngload has a unified API for querying different metadata that may be stored in a PNG datastream. The get-metadata method can be used to query any metadata available. It accepts a PNG object, which is returned by load-file or load-stream as per the above, as well as a key identifying the type of metadata you want to query. If a PNG datastream does not have the metadata requested, NIL will be returned. The following keys are recognized:

:width

The image width in pixels. This is the same as (width png) and is only for convenience.

:height

The image height in pixels. This is the same as (height png) and is only for convenience.

``:bit-depth`

The number of bits per sample. This is the same as (bit-depth png) and is only for convenience.

:color-type

The color type of the image. This is the same as (color-type png) and is only for convenience. One of the following is returned:

  • :indexed-colour: each pixel consists of an index into a palette.
  • :greyscale: each pixel consists of a single sample: grey.
  • :greyscale-alpha: each pixel consists of two samples: grey and alpha.
  • :truecolour: each pixel consists of three samples: red, green, and blue.
  • :truecolour-alpha: each pixel consists of four samples: red, green, blue, and alpha.

:compression-method

The method used to compress image data chunks. For standard PNG, this can only be :zlib. This will return :unknown if any other compression scheme was used.

:interlace-method

The interlacing method. For standard PNG, this can be either :null or :adam7. This will return :unknown if any other interlacing method was used.

:filter-method

The filtering method used to decode the image. For standard PNG, this can only be :standard. This will return :unknown if any other filter method was used.

:palette

The palette of an :indexed-colour image. This will return a 2-dimensional array of (color-count 3) representing the red, green, and blue values of each indexed color in the palette.

:white-point

The CIE 1931 reference white point. Returns two floating point values for the X and Y values.

:chromaticity-red

The CIE 1931 primary red chromaticity. Returns two floating point values for the X and Y values.

:chromaticity-green

The CIE 1931 primary green chromaticity. Returns two floating point values for the X and Y values.

:chromaticity-blue

The CIE 1931 primary blue chromaticity. Returns two floating point values for the X and Y values.

:gamma

The gamma adjustment for the desired display output intensity. Returns a floating point value.

:color-profile

The ICC color profile of the image. Returns a octet vector to be decoded by any application wishing to make use of this.

:significant-bits

The original number of sample significant bits. Returns multiple values; one for each color channel.

:srgb-rendering-intent

Specifies how the samples should be displayed in the sRGB color space. This can be one of the following:

  • :perceptual: for images preferring good adaptation to the output device gamut at the expense of colorimetric accuracy, such as photographs.
  • :relative-colorimetric: for images requiring colour appearance matching (relative to the output device white point), such as logos.
  • :saturation: for images preferring preservation of saturation at the expense of hue and lightness, such as charts and graphs.
  • :absolute-colorimetric: for images requiring preservation of absolute colorimetry, such as previews of images destined for a different output device (proofs).

:background-color

The default background colour to present the image against. Returns 3 values; the red, green, and blue values.

:histogram

The approximate usage frequency of each colour in the palette. Returns an association list mapping each palette color as a vector of 3 components to their frequencies.

:transparency

The transparency color of the image. For :indexed-colour images, this returns an association list mapping each palette color as a vector of 3 components their transparency value. For :truecolour images, this returns 3 values; the red, green, and blue sample value of the transparency color. For :greyscale images, this returns a single value for the transparency.

:pixel-dimensions

Specifies the aspect ratio and physical size of image pixels. Returns a property list specifying the X and Y dimensions of each pixel, as well as a unit. :unit can be one of :meter or :unknown. If :unit is :unknown, physical size of the pixels is not stored, and only the aspect ratio is present.

:suggested-palettes

Some PNG images store a "suggested palette" chunk, which stores multiple palettes, histograms, and transparency information together. This returns an association list mapping a palette name to a property list of suggested palette information.

:last-modified

The timestamp the image was last modified. Returns an integer in the universal time format.

:text

Arbitrary textual metadata stored in the PNG datastream. Returns a list of property lists.

License

Copyright © 2017-2021 Michael Fiano mail@mfiano.net, Bart Botta 00003b@gmail.com.

Licensed under the MIT License.

Dependencies (12)

  • 3bz
  • alexandria
  • cffi
  • local-time
  • mmap
  • opticl
  • parse-float
  • png-read
  • static-vectors
  • swap-bytes
  • uiop
  • zpb-exif
  • GitHub
  • Quicklisp