--- a/LICENSE.markdown Sat Feb 02 14:30:59 2019 -0500
+++ b/LICENSE.markdown Sat Feb 23 14:23:28 2019 -0500
@@ -1,4 +1,4 @@
-Copyright (c) 2017 Steve Losh and contributors
+Copyright (c) 2019 Steve Losh and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
--- a/Makefile Sat Feb 02 14:30:59 2019 -0500
+++ b/Makefile Sat Feb 23 14:23:28 2019 -0500
@@ -36,6 +36,8 @@
docs/build/index.html: $(docfiles) $(apidocs) docs/title
cd docs && ~/.virtualenvs/d/bin/d
+ cd docs && rm -rf build/assets
+ cd docs && cp -R ./assets build/assets
docs: docs/build/index.html
--- a/README.markdown Sat Feb 02 14:30:59 2019 -0500
+++ b/README.markdown Sat Feb 23 14:23:28 2019 -0500
@@ -1,5 +1,8 @@
-cl-netpbm is a Common Lisp library for reading and writing the [netpbm image
-formats (PPM, PGM, and PBM)](https://en.wikipedia.org/wiki/Netpbm_format).
+cl-netpbm is a pure Common Lisp library for reading and writing the [netpbm
+image formats (PPM, PGM, and PBM)](https://en.wikipedia.org/wiki/Netpbm_format).
+
+These image formats are very simple, but not efficient. If you need extreme
+performance you should not use these formats (or this library).
* **License:** MIT/X11
* **Documentation:** <https://sjl.bitbucket.io/cl-netpbm/>
--- a/cl-netpbm.asd Sat Feb 02 14:30:59 2019 -0500
+++ b/cl-netpbm.asd Sat Feb 23 14:23:28 2019 -0500
@@ -9,7 +9,7 @@
:depends-on ()
- :in-order-to ((asdf:test-op (asdf:test-op :netpbm/test)))
+ :in-order-to ((asdf:test-op (asdf:test-op :cl-netpbm/test)))
:serial t
:components ((:module "vendor" :serial t
@@ -25,7 +25,7 @@
:author "Steve Losh <steve@stevelosh.com>"
:license "MIT/X11"
- :depends-on (:netpbm :1am)
+ :depends-on (:cl-netpbm :1am)
:serial t
:components ((:file "package.test")
--- a/docs/01-installation.markdown Sat Feb 02 14:30:59 2019 -0500
+++ b/docs/01-installation.markdown Sat Feb 23 14:23:28 2019 -0500
@@ -5,4 +5,11 @@
can clone the repository into your [Quicklisp local-projects][local] directory
for now.
+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
+`uiop`, and also requires [ImageMagick][im] for its fuzz testing.
+
[local]: https://www.quicklisp.org/beta/faq.html#local-project
+[im]: https://www.imagemagick.org/
--- a/docs/02-usage.markdown Sat Feb 02 14:30:59 2019 -0500
+++ b/docs/02-usage.markdown Sat Feb 23 14:23:28 2019 -0500
@@ -1,10 +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]
-Docs
-----
+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`):
-foo
+ (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.
+
--- a/docs/03-reference.markdown Sat Feb 02 14:30:59 2019 -0500
+++ b/docs/03-reference.markdown Sat Feb 23 14:23:28 2019 -0500
@@ -57,6 +57,42 @@
+### `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)
Binary file docs/assets/kitten-inverted.jpg has changed
Binary file docs/assets/kitten.jpg has changed
--- a/docs/index.markdown Sat Feb 02 14:30:59 2019 -0500
+++ b/docs/index.markdown Sat Feb 23 14:23:28 2019 -0500
@@ -1,7 +1,1 @@
-cl-netpbm is a Common Lisp library for reading and writing the [netpbm image
-formats (PPM, PGM, and PBM)](https://en.wikipedia.org/wiki/Netpbm_format).
-
-* **License:** MIT/X11
-* **Documentation:** <https://sjl.bitbucket.io/cl-netpbm/>
-* **Mercurial:** <http://bitbucket.org/sjl/cl-netpbm/>
-* **Git:** <http://github.com/sjl/cl-netpbm/>
+../README.markdown
\ No newline at end of file
--- a/src/main.lisp Sat Feb 02 14:30:59 2019 -0500
+++ b/src/main.lisp Sat Feb 23 14:23:28 2019 -0500
@@ -363,28 +363,25 @@
(file-format (read-magic-byte stream))
(read-netpbm (make-peekable-stream stream) format binary? nil)))
-(defun read-texture-from-stream (stream)
- "Read a PPM image file from `stream`, returning an OpenGL-style array and more.
+(defun 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:
- `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:
+ * PBM: `bit`
+ * PGM: `(integer 0 maximum-value)`
+ * PPM: `(simple-array (integer 0 maximum-value) (3))`
- (simple-array (single-float 0.0 1.0) (* width height 3))
+ Two other values are returned:
- 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.
+ * The format of the image that was read (one of `:pbm`, `:pgm`, `:ppm`).
+ * The bit depth of the image.
"
- (check-type stream stream)
- (assert (input-stream-p stream) (stream)
- "Stream ~S is not an input stream." stream)
- (multiple-value-bind (format binary?)
- (file-format (read-magic-byte stream))
- (read-netpbm (make-peekable-stream stream) format binary? t)))
+ (with-open-file (s path :direction :input :element-type '(unsigned-byte 8))
+ (read-from-stream s)))
(defun write-to-stream (stream data &key
@@ -424,42 +421,6 @@
(write-netpbm data stream format (eql :binary encoding) maximum-value)
(values))
-
-(defun 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.
-
- "
- (with-open-file (s path :direction :input :element-type '(unsigned-byte 8))
- (read-from-stream s)))
-
-(defun 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.
-
- "
- (with-open-file (s path :direction :input :element-type '(unsigned-byte 8))
- (read-texture-from-stream s)))
-
(defun write-to-file (path data &key
(if-exists nil if-exists-given)
(format :ppm)
@@ -500,3 +461,43 @@
(with-open-file (s path :direction :output :element-type '(unsigned-byte 8))
(write-it s))))
(values))
+
+
+(defun 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.
+
+ "
+ (with-open-file (s path :direction :input :element-type '(unsigned-byte 8))
+ (read-texture-from-stream s)))
+
+(defun 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.
+
+ "
+ (check-type stream stream)
+ (assert (input-stream-p stream) (stream)
+ "Stream ~S is not an input stream." stream)
+ (multiple-value-bind (format binary?)
+ (file-format (read-magic-byte stream))
+ (read-netpbm (make-peekable-stream stream) format binary? t)))
+
--- a/test/run.lisp Sat Feb 02 14:30:59 2019 -0500
+++ b/test/run.lisp Sat Feb 23 14:23:28 2019 -0500
@@ -1,5 +1,5 @@
#+ecl (setf compiler:*user-cc-flags* "-Wno-shift-negative-value")
-(ql:quickload :netpbm)
-(time (asdf:test-system :netpbm))
+(ql:quickload :cl-netpbm)
+(time (asdf:test-system :cl-netpbm))
(quit)
--- a/vendor/quickutils.lisp Sat Feb 02 14:30:59 2019 -0500
+++ b/vendor/quickutils.lisp Sat Feb 23 14:23:28 2019 -0500
@@ -2,20 +2,19 @@
;;;; See http://quickutil.org for details.
;;;; To regenerate:
-;;;; (qtlc:save-utils-as "quickutils.lisp" :utilities '(:CURRY :SYMB :WITH-GENSYMS :TRANSPOSE) :ensure-package T :package "TRIVIAL-PPM.QUICKUTILS")
+;;;; (qtlc:save-utils-as "quickutils.lisp" :utilities '(:CURRY :SYMB :WITH-GENSYMS :TRANSPOSE) :ensure-package T :package "NETPBM.QUICKUTILS")
(eval-when (:compile-toplevel :load-toplevel :execute)
- (unless (find-package "TRIVIAL-PPM.QUICKUTILS")
- (defpackage "TRIVIAL-PPM.QUICKUTILS"
+ (unless (find-package "NETPBM.QUICKUTILS")
+ (defpackage "NETPBM.QUICKUTILS"
(:documentation "Package that contains Quickutil utility functions.")
(:use #:cl))))
-(in-package "TRIVIAL-PPM.QUICKUTILS")
+(in-package "NETPBM.QUICKUTILS")
(when (boundp '*utilities*)
- (setf *utilities* (union *utilities* '(:MAKE-GENSYM-LIST :ENSURE-FUNCTION
- :CURRY :MKSTR :SYMB :STRING-DESIGNATOR
- :WITH-GENSYMS :TRANSPOSE))))
+ (setf *utilities* (union *utilities* '(:MAKE-GENSYM-LIST :ENSURE-FUNCTION :CURRY :MKSTR :SYMB
+ :STRING-DESIGNATOR :WITH-GENSYMS :TRANSPOSE))))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun make-gensym-list (length &optional (x "G"))
"Returns a list of `length` gensyms, each generated as if with a call to `make-gensym`,