# HG changeset patch # User Steve Losh # Date 1577132824 18000 # Node ID 6b760535aa94a2b2555e72fa9183ae901be4cef0 # Parent 9ddd1cab15a00b2b4ef289568acee3b232f5868c Update documentation diff -r 9ddd1cab15a0 -r 6b760535aa94 docs/01-installation.markdown --- a/docs/01-installation.markdown Mon Dec 23 15:26:57 2019 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -Installation -============ - -cl-netpbm can be installed with Quicklisp: - - (ql:quickload :cl-netpbm) - -The `cl-netpbm` system provides all the functionality of the library. It has no -dependencies. - -The `cl-netpbm/test` system provides the unit tests. It depends on `1am` and -`external-program`, and also requires [ImageMagick][im] for its fuzz testing. - -[im]: https://www.imagemagick.org/ diff -r 9ddd1cab15a0 -r 6b760535aa94 docs/01-usage.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/01-usage.markdown Mon Dec 23 15:27:04 2019 -0500 @@ -0,0 +1,164 @@ +Usage +===== + +The [netpbm image formats (PPM, PGM, and PBM)][netpbm] are a family of very +simple image formats. You can convert to/from these formats with third-party +tools like [ImageMagick][im]. + +Instead of trying to link `libjpeg` into your Lisp program with CFFI, you can +use this library to read/write images in the simple netpbm format and then use +a third-party tool to convert to whatever other format(s) you need. + +cl-netpbm provides functions for both reading and writing images, as well as +a little bit of sugar for working with OpenGL textures. + +[netpbm]: https://en.wikipedia.org/wiki/Netpbm_format +[im]: https://www.imagemagick.org/ + +[TOC] + +Reading Images +-------------- + +The `netpbm:read-from-stream` function can be used to read a netpbm file from +stream. The stream *must* be a binary input stream with `element-type` of +`(unsigned-byte 8)`. + +Three values are returned: a 2D array of pixels, the format of the image +(`:pbm`, `:pgm`, or `:ppm`), and the bit depth of the image: + + (with-open-file (f "foo.ppm" :element-type '(unsigned-byte 8)) + (netpbm:read-from-stream f)) + ; => + #2A((#(255 0 0) #(0 255 0) #(0 0 255)) + (#(255 0 0) #(0 255 0) #(0 0 255)) + (#(255 0 0) #(0 255 0) #(0 0 255)) + (#(255 0 0) #(0 255 0) #(0 0 255))) + :PPM + 255 + +A `netpbm:read-from-file` function is also provided to save you some +boilerplate: + + (netpbm:read-from-file "foo.ppm") + +See the [API reference](../reference) for these functions for more information. + +Image Arrays +------------ + +When an image is read a 2D array of pixels is returned. + +The first array dimension is the columns of the image and the second array +dimension is the rows. This means to access pixel `(x, y)` of the image you use +`(aref image x y)`. The `y` dimension starts at the top of the image and grows +downwards, so: + +* `(aref image 0 0)` returns the top-left pixel. +* `(aref image width height)` returns the bottom-right pixel. + +The `element-type` of the array (i.e. the type of the pixels) depends on the +format of the image that was read: + +* Pixels of PBM images are of type `bit`. +* Pixels of PGM images are of type `(integer 0 ,bit-depth)`. +* Pixels of PPM images are of type `(simple-array (integer 0 ,bit-depth) (3))`. + +Lower values represent darker colors, e.g. for PBM images `0` is black and `1` +is white, for PGM `0` is black, `1` is very dark gray, etc. + +(Note that the actual PBM image format on disk is backwards from all the other +netpbm formats — in PBM a `1` bit represents black. cl-netpbm flips the bits +when reading/writing PBM files for consistency on the Lisp side of things.) + +Writing Images +-------------- + +An image array can be written to a stream with `netpbm:write-to-stream`. The +stream *must* be a binary output stream with `element-type` of `(unsigned-byte +8)`. The input image array must have the appropriate contents for the desired +output format (e.g. integers for `:pbm` and `:pgm`, 3-element vectors for +`:pgm`): + + (with-open-file (f "foo.pbm" + :direction :output + :element-type '(unsigned-byte 8)) + (netpbm:write-to-stream + f #2A((0 0 0 0 0 0 0 0 0 0) + (0 1 1 1 1 1 1 1 1 0) + (0 1 0 0 1 1 0 0 0 0) + (0 1 0 0 1 0 1 0 0 0) + (0 0 1 1 0 0 0 1 1 0) + (0 0 0 0 0 0 0 0 0 0)) + :format :pbm + :encoding :binary)) + ; => Write an "R" character into "foo.pbm" + +`netpbm:write-to-file` is provided for convenience: + + (netpbm:write-to-file "foo.pbm" image :format :pbm) + +See the [API reference](../reference) for these functions for more information. + +Example: Inverting an Image +--------------------------- + +For a concrete example, let's invert an image. + +First we'll get a kitten photo to work with and convert it to PPM with +ImageMagick: + + wget 'https://upload.wikimedia.org/wikipedia/commons/7/75/Cute_grey_kitten.jpg' -O kitten.jpg + convert -resize x600 kitten.jpg kitten.ppm + +The initial kitten ([source](https://en.m.wikipedia.org/wiki/File:Cute_grey_kitten.jpg)): + +![kitten photo](../assets/kitten.jpg) + +Now we can write our Lisp code: + + (defun invert-value (value) + (- 255 value)) + + (defun invert-pixel (pixel) + (map-into pixel #'invert-value pixel)) + + (defun invert-image (image) + (destructuring-bind (width height) (array-dimensions image) + (dotimes (y height) + (dotimes (x width) + (invert-pixel (aref image x y)))))) + + (let ((image (netpbm:read-from-file "kitten.ppm"))) + (invert-image image) + (netpbm:write-to-file "kitten-inverted.ppm" image)) + +And convert it back into JPG: + + convert kitten-inverted.ppm kitten-inverted.jpg + +And now we have an inverted kitten: + +![kitten photo](../assets/kitten-inverted.jpg) + +OpenGL Textures +--------------- + +cl-netpbm's image array layout (column major, 3-element vectors for RGB pixels, +y-axis from top to bottom) is meant to be simple for humans to code against. +If you're working with OpenGL and were hoping to use cl-netpbm to easily load +textures (instead of fiddling around with FFI'ing out to something like +[STB](https://github.com/nothings/stb)) this won't work, because OpenGL expects +a different format (a flat array of `single-float`s, y-axis from bottom to top). + +For your convenience, cl-netpbm provides two additional functions that will +return an array in the format OpenGL expects: `netpbm:read-texture-from-stream` +and `netpbm:read-texture-from-file`. + +Remember, though, that the netpbm formats are designed for simplicity, not +efficiency. If you're just going through [an OpenGL +tutorial](https://learnopengl.com/) and want to load a texture without screwing +around with CFFI, cl-netpbm can help you out. But if you're creating an actual +game where performance matters, you'll likely want to replace it with something +much more efficient. + diff -r 9ddd1cab15a0 -r 6b760535aa94 docs/02-reference.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/02-reference.markdown Mon Dec 23 15:27:04 2019 -0500 @@ -0,0 +1,148 @@ +# API Reference + +The following is a list of all user-facing parts of cl-netpbm. + +If there are backwards-incompatible changes to anything listed here, they will +be noted in the changelog and the author will feel bad. + +Anything not listed here is subject to change at any time with no warning, so +don't touch it. + +[TOC] + +## Package `NETPBM` + +### `READ-FROM-FILE` (function) + + (READ-FROM-FILE PATH) + +Read a PPM image file from `path`, returning an array of pixels and more. + + The primary return value will be a 2D array with dimensions `(width height)`. + Each element of the array will be a single pixel whose type depends on the + image file format: + + * PBM: `bit` + * PGM: `(integer 0 maximum-value)` + * PPM: `(simple-array (integer 0 maximum-value) (3))` + + Two other values are returned: + + * The format of the image that was read (one of `:pbm`, `:pgm`, `:ppm`). + * The bit depth of the image. + + + +### `READ-FROM-STREAM` (function) + + (READ-FROM-STREAM STREAM) + +Read a PPM image file from `stream`, returning an array of pixels and more. + + `stream` must be a binary input stream, specifically of `(unsigned-byte 8)`s + unless you *really* know what you're doing. + + The primary return value will be a 2D array with dimensions `(width height)`. + Each element of the array will be a single pixel whose type depends on the + image file format: + + * PBM: `bit` + * PGM: `(integer 0 maximum-value)` + * PPM: `(simple-array (integer 0 maximum-value) (3))` + + Two other values are returned: + + * The format of the image that was read (one of `:pbm`, `:pgm`, `:ppm`). + * The bit depth of the image. + + + +### `READ-TEXTURE-FROM-FILE` (function) + + (READ-TEXTURE-FROM-FILE PATH) + +Read a PPM image file from `path`, returning an OpenGL-style array and more. + + The primary return value will be an OpenGL-style array of type: + + (simple-array (single-float 0.0 1.0) (* width height 3)) + + The vertical axis of the image will be flipped, which is what OpenGL expects. + + Three values are returned: the array, the width, and the height. + + + +### `READ-TEXTURE-FROM-STREAM` (function) + + (READ-TEXTURE-FROM-STREAM STREAM) + +Read a PPM image file from `stream`, returning an OpenGL-style array and more. + + `stream` must be a binary input stream, specifically of `(unsigned-byte 8)`s + unless you *really* know what you're doing. The stream must contain a PPM + formatted image — PBM and PGM images are not supported. + + The primary return value will be an OpenGL-style array of type: + + (simple-array (single-float 0.0 1.0) (* width height 3)) + + The vertical axis of the image will be flipped, which is what OpenGL expects. + + Three values are returned: the array, the width, and the height. + + + +### `WRITE-TO-FILE` (function) + + (WRITE-TO-FILE PATH DATA &KEY (IF-EXISTS NIL IF-EXISTS-GIVEN) (FORMAT :PPM) (ENCODING :BINARY) + (MAXIMUM-VALUE (ECASE FORMAT (:PBM 1) ((:PGM :PPM) 255)))) + +Write a PPM image array `data` to a file at `path`. + + Nothing is returned. + + `format` must be one of `:pbm`, `:pgm`, `:ppm`. + + `encoding` must be one of `:binary`, `:ascii`. + + `maximum-value` must be the desired bit depth of the image (the maximum value + any particular pixel can have). For PBM images it must be `1`. + + For PBM and PGM images, `data` must be a two dimensional array of integers + between `0` and `maximum-value` inclusive. + + For PPM images, `data` must be a two dimensional array of pixels, each of + which must be a 3 element vector of integers between `0` and `maximum-value` + inclusive. + + + +### `WRITE-TO-STREAM` (function) + + (WRITE-TO-STREAM STREAM DATA &KEY (FORMAT :PPM) (ENCODING :BINARY) + (MAXIMUM-VALUE (ECASE FORMAT (:PBM 1) ((:PGM :PPM) 255)))) + +Write a PPM image array `data` to `stream`. + + Nothing is returned. + + `stream` must be a binary output stream, specifically of `(unsigned-byte 8)`s + unless you *really* know what you're doing. + + `format` must be one of `:pbm`, `:pgm`, `:ppm`. + + `encoding` must be one of `:binary`, `:ascii`. + + `maximum-value` must be the desired bit depth of the image (the maximum value + any particular pixel can have). For PBM images it must be `1`. + + For PBM and PGM images, `data` must be a two dimensional array of integers + between `0` and `maximum-value` inclusive. + + For PPM images, `data` must be a two dimensional array of pixels, each of + which must be a 3 element vector of integers between `0` and `maximum-value` + inclusive. + + + diff -r 9ddd1cab15a0 -r 6b760535aa94 docs/02-usage.markdown --- a/docs/02-usage.markdown Mon Dec 23 15:26:57 2019 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +0,0 @@ -Usage -===== - -The [netpbm image formats (PPM, PGM, and PBM)][netpbm] are a family of very -simple image formats. You can convert to/from these formats with third-party -tools like [ImageMagick][im]. - -Instead of trying to link `libjpeg` into your Lisp program with CFFI, you can -use this library to read/write images in the simple netpbm format and then use -a third-party tool to convert to whatever other format(s) you need. - -cl-netpbm provides functions for both reading and writing images, as well as -a little bit of sugar for working with OpenGL textures. - -[netpbm]: https://en.wikipedia.org/wiki/Netpbm_format -[im]: https://www.imagemagick.org/ - -[TOC] - -Reading Images --------------- - -The `netpbm:read-from-stream` function can be used to read a netpbm file from -stream. The stream *must* be a binary input stream with `element-type` of -`(unsigned-byte 8)`. - -Three values are returned: a 2D array of pixels, the format of the image -(`:pbm`, `:pgm`, or `:ppm`), and the bit depth of the image: - - (with-open-file (f "foo.ppm" :element-type '(unsigned-byte 8)) - (netpbm:read-from-stream f)) - ; => - #2A((#(255 0 0) #(0 255 0) #(0 0 255)) - (#(255 0 0) #(0 255 0) #(0 0 255)) - (#(255 0 0) #(0 255 0) #(0 0 255)) - (#(255 0 0) #(0 255 0) #(0 0 255))) - :PPM - 255 - -A `netpbm:read-from-file` function is also provided to save you some -boilerplate: - - (netpbm:read-from-file "foo.ppm") - -See the [API reference](../reference) for these functions for more information. - -Image Arrays ------------- - -When an image is read a 2D array of pixels is returned. - -The first array dimension is the columns of the image and the second array -dimension is the rows. This means to access pixel `(x, y)` of the image you use -`(aref image x y)`. The `y` dimension starts at the top of the image and grows -downwards, so: - -* `(aref image 0 0)` returns the top-left pixel. -* `(aref image width height)` returns the bottom-right pixel. - -The `element-type` of the array (i.e. the type of the pixels) depends on the -format of the image that was read: - -* Pixels of PBM images are of type `bit`. -* Pixels of PGM images are of type `(integer 0 ,bit-depth)`. -* Pixels of PPM images are of type `(simple-array (integer 0 ,bit-depth) (3))`. - -Lower values represent darker colors, e.g. for PBM images `0` is black and `1` -is white, for PGM `0` is black, `1` is very dark gray, etc. - -(Note that the actual PBM image format on disk is backwards from all the other -netpbm formats — in PBM a `1` bit represents black. cl-netpbm flips the bits -when reading/writing PBM files for consistency on the Lisp side of things.) - -Writing Images --------------- - -An image array can be written to a stream with `netpbm:write-to-stream`. The -stream *must* be a binary output stream with `element-type` of `(unsigned-byte -8)`. The input image array must have the appropriate contents for the desired -output format (e.g. integers for `:pbm` and `:pgm`, 3-element vectors for -`:pgm`): - - (with-open-file (f "foo.pbm" - :direction :output - :element-type '(unsigned-byte 8)) - (netpbm:write-to-stream - f #2A((0 0 0 0 0 0 0 0 0 0) - (0 1 1 1 1 1 1 1 1 0) - (0 1 0 0 1 1 0 0 0 0) - (0 1 0 0 1 0 1 0 0 0) - (0 0 1 1 0 0 0 1 1 0) - (0 0 0 0 0 0 0 0 0 0)) - :format :pbm - :encoding :binary)) - ; => Write an "R" character into "foo.pbm" - -`netpbm:write-to-file` is provided for convenience: - - (netpbm:write-to-file "foo.pbm" image :format :pbm) - -See the [API reference](../reference) for these functions for more information. - -Example: Inverting an Image ---------------------------- - -For a concrete example, let's invert an image. - -First we'll get a kitten photo to work with and convert it to PPM with -ImageMagick: - - wget 'https://upload.wikimedia.org/wikipedia/commons/7/75/Cute_grey_kitten.jpg' -O kitten.jpg - convert -resize x600 kitten.jpg kitten.ppm - -The initial kitten ([source](https://en.m.wikipedia.org/wiki/File:Cute_grey_kitten.jpg)): - -![kitten photo](../assets/kitten.jpg) - -Now we can write our Lisp code: - - (defun invert-value (value) - (- 255 value)) - - (defun invert-pixel (pixel) - (map-into pixel #'invert-value pixel)) - - (defun invert-image (image) - (destructuring-bind (width height) (array-dimensions image) - (dotimes (y height) - (dotimes (x width) - (invert-pixel (aref image x y)))))) - - (let ((image (netpbm:read-from-file "kitten.ppm"))) - (invert-image image) - (netpbm:write-to-file "kitten-inverted.ppm" image)) - -And convert it back into JPG: - - convert kitten-inverted.ppm kitten-inverted.jpg - -And now we have an inverted kitten: - -![kitten photo](../assets/kitten-inverted.jpg) - -OpenGL Textures ---------------- - -cl-netpbm's image array layout (column major, 3-element vectors for RGB pixels, -y-axis from top to bottom) is meant to be simple for humans to code against. -If you're working with OpenGL and were hoping to use cl-netpbm to easily load -textures (instead of fiddling around with FFI'ing out to something like -[STB](https://github.com/nothings/stb)) this won't work, because OpenGL expects -a different format (a flat array of `single-float`s, y-axis from bottom to top). - -For your convenience, cl-netpbm provides two additional functions that will -return an array in the format OpenGL expects: `netpbm:read-texture-from-stream` -and `netpbm:read-texture-from-file`. - -Remember, though, that the netpbm formats are designed for simplicity, not -efficiency. If you're just going through [an OpenGL -tutorial](https://learnopengl.com/) and want to load a texture without screwing -around with CFFI, cl-netpbm can help you out. But if you're creating an actual -game where performance matters, you'll likely want to replace it with something -much more efficient. - diff -r 9ddd1cab15a0 -r 6b760535aa94 docs/03-changelog.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/03-changelog.markdown Mon Dec 23 15:27:04 2019 -0500 @@ -0,0 +1,11 @@ +Changelog +========= + +Here's the list of changes in each released version. + +[TOC] + +v1.0.0 +------ + +Initial version. diff -r 9ddd1cab15a0 -r 6b760535aa94 docs/03-reference.markdown --- a/docs/03-reference.markdown Mon Dec 23 15:26:57 2019 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,148 +0,0 @@ -# API Reference - -The following is a list of all user-facing parts of cl-netpbm. - -If there are backwards-incompatible changes to anything listed here, they will -be noted in the changelog and the author will feel bad. - -Anything not listed here is subject to change at any time with no warning, so -don't touch it. - -[TOC] - -## Package `NETPBM` - -### `READ-FROM-FILE` (function) - - (READ-FROM-FILE PATH) - -Read a PPM image file from `path`, returning an array of pixels and more. - - The primary return value will be a 2D array with dimensions `(width height)`. - Each element of the array will be a single pixel whose type depends on the - image file format: - - * PBM: `bit` - * PGM: `(integer 0 maximum-value)` - * PPM: `(simple-array (integer 0 maximum-value) (3))` - - Two other values are returned: - - * The format of the image that was read (one of `:pbm`, `:pgm`, `:ppm`). - * The bit depth of the image. - - - -### `READ-FROM-STREAM` (function) - - (READ-FROM-STREAM STREAM) - -Read a PPM image file from `stream`, returning an array of pixels and more. - - `stream` must be a binary input stream, specifically of `(unsigned-byte 8)`s - unless you *really* know what you're doing. - - The primary return value will be a 2D array with dimensions `(width height)`. - Each element of the array will be a single pixel whose type depends on the - image file format: - - * PBM: `bit` - * PGM: `(integer 0 maximum-value)` - * PPM: `(simple-array (integer 0 maximum-value) (3))` - - Two other values are returned: - - * The format of the image that was read (one of `:pbm`, `:pgm`, `:ppm`). - * The bit depth of the image. - - - -### `READ-TEXTURE-FROM-FILE` (function) - - (READ-TEXTURE-FROM-FILE PATH) - -Read a PPM image file from `path`, returning an OpenGL-style array and more. - - The primary return value will be an OpenGL-style array of type: - - (simple-array (single-float 0.0 1.0) (* width height 3)) - - The vertical axis of the image will be flipped, which is what OpenGL expects. - - Three values are returned: the array, the width, and the height. - - - -### `READ-TEXTURE-FROM-STREAM` (function) - - (READ-TEXTURE-FROM-STREAM STREAM) - -Read a PPM image file from `stream`, returning an OpenGL-style array and more. - - `stream` must be a binary input stream, specifically of `(unsigned-byte 8)`s - unless you *really* know what you're doing. The stream must contain a PPM - formatted image — PBM and PGM images are not supported. - - The primary return value will be an OpenGL-style array of type: - - (simple-array (single-float 0.0 1.0) (* width height 3)) - - The vertical axis of the image will be flipped, which is what OpenGL expects. - - Three values are returned: the array, the width, and the height. - - - -### `WRITE-TO-FILE` (function) - - (WRITE-TO-FILE PATH DATA &KEY (IF-EXISTS NIL IF-EXISTS-GIVEN) (FORMAT :PPM) (ENCODING :BINARY) - (MAXIMUM-VALUE (ECASE FORMAT (:PBM 1) ((:PGM :PPM) 255)))) - -Write a PPM image array `data` to a file at `path`. - - Nothing is returned. - - `format` must be one of `:pbm`, `:pgm`, `:ppm`. - - `encoding` must be one of `:binary`, `:ascii`. - - `maximum-value` must be the desired bit depth of the image (the maximum value - any particular pixel can have). For PBM images it must be `1`. - - For PBM and PGM images, `data` must be a two dimensional array of integers - between `0` and `maximum-value` inclusive. - - For PPM images, `data` must be a two dimensional array of pixels, each of - which must be a 3 element vector of integers between `0` and `maximum-value` - inclusive. - - - -### `WRITE-TO-STREAM` (function) - - (WRITE-TO-STREAM STREAM DATA &KEY (FORMAT :PPM) (ENCODING :BINARY) - (MAXIMUM-VALUE (ECASE FORMAT (:PBM 1) ((:PGM :PPM) 255)))) - -Write a PPM image array `data` to `stream`. - - Nothing is returned. - - `stream` must be a binary output stream, specifically of `(unsigned-byte 8)`s - unless you *really* know what you're doing. - - `format` must be one of `:pbm`, `:pgm`, `:ppm`. - - `encoding` must be one of `:binary`, `:ascii`. - - `maximum-value` must be the desired bit depth of the image (the maximum value - any particular pixel can have). For PBM images it must be `1`. - - For PBM and PGM images, `data` must be a two dimensional array of integers - between `0` and `maximum-value` inclusive. - - For PPM images, `data` must be a two dimensional array of pixels, each of - which must be a 3 element vector of integers between `0` and `maximum-value` - inclusive. - - - diff -r 9ddd1cab15a0 -r 6b760535aa94 docs/04-changelog.markdown --- a/docs/04-changelog.markdown Mon Dec 23 15:26:57 2019 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -Changelog -========= - -Here's the list of changes in each released version. - -[TOC] - -v1.0.0 ------- - -Initial version.