70f64dff49b5

Add actual documentation
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Sat, 23 Feb 2019 14:23:28 -0500
parents 8a6db152fb11
children 3a43bc28e3fc
branches/tags (none)
files LICENSE.markdown Makefile README.markdown cl-netpbm.asd docs/01-installation.markdown docs/02-usage.markdown docs/03-reference.markdown docs/assets/kitten-inverted.jpg docs/assets/kitten.jpg docs/index.markdown src/main.lisp test/run.lisp vendor/quickutils.lisp

Changes

diff -r 8a6db152fb11 -r 70f64dff49b5 LICENSE.markdown
--- 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
diff -r 8a6db152fb11 -r 70f64dff49b5 Makefile
--- 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
 
diff -r 8a6db152fb11 -r 70f64dff49b5 README.markdown
--- 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/>
diff -r 8a6db152fb11 -r 70f64dff49b5 cl-netpbm.asd
--- 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")
diff -r 8a6db152fb11 -r 70f64dff49b5 docs/01-installation.markdown
--- 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/
diff -r 8a6db152fb11 -r 70f64dff49b5 docs/02-usage.markdown
--- 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.
+
diff -r 8a6db152fb11 -r 70f64dff49b5 docs/03-reference.markdown
--- 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)
diff -r 8a6db152fb11 -r 70f64dff49b5 docs/assets/kitten-inverted.jpg
Binary file docs/assets/kitten-inverted.jpg has changed
diff -r 8a6db152fb11 -r 70f64dff49b5 docs/assets/kitten.jpg
Binary file docs/assets/kitten.jpg has changed
diff -r 8a6db152fb11 -r 70f64dff49b5 docs/index.markdown
--- 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
diff -r 8a6db152fb11 -r 70f64dff49b5 src/main.lisp
--- 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)))
+
diff -r 8a6db152fb11 -r 70f64dff49b5 test/run.lisp
--- 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)
diff -r 8a6db152fb11 -r 70f64dff49b5 vendor/quickutils.lisp
--- 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`,