# HG changeset patch # User Steve Losh # Date 1516162345 18000 # Node ID 66846306020f7809a6ff6a89490fa3b4294064e2 # Parent b8cc0a56b57e1adca8b13499de02b3ca811071ba Add some docstrings diff -r b8cc0a56b57e -r 66846306020f src/main.lisp --- a/src/main.lisp Tue Jan 16 19:56:02 2018 -0500 +++ b/src/main.lisp Tue Jan 16 23:12:25 2018 -0500 @@ -9,14 +9,12 @@ (loop :while (eql #\# (peek-char t stream nil nil)) :do (skip-comment stream))) -(defun peek (stream) - (peek-char nil stream nil nil)) - (defun read-number (stream) + "Read the next ASCII-encoded number from `stream`." (skip-whitespace stream) (loop :with i = 0 - :for ch = (peek stream) + :for ch = (peek-char nil stream nil nil) :while ch :for digit = (digit-char-p ch) :while digit @@ -25,16 +23,18 @@ :finally (return i))) (defun write-number (value stream) + "Write `value` to stream as an ASCII-encoded number." (format stream "~D " value)) (defun read-magic-byte (stream) + "Read the initial `P#` from `stream`, returning the magic `#` character." (assert (eql (read-byte stream) (char-code #\P))) (code-char (read-byte stream))) (defun file-format (magic-byte) - "Return `(values format binary?)` for the given magic byte." + "Return `(values format binary?)` for the given magic byte character." (ecase magic-byte (#\1 (values :pbm nil)) (#\2 (values :pgm nil)) @@ -44,6 +44,7 @@ (#\6 (values :ppm t)))) (defun magic-byte (file-format binary?) + "Return the magic byte character to use for the given format/encoding combination." (if binary? (ecase file-format (:pbm #\4) @@ -56,6 +57,7 @@ (defun pixel-type (format bit-depth) + "Return the type specifier for a pixel of an image with the given `format` and `bit-depth`." (ecase format (:pbm 'bit) (:pgm `(integer 0 ,bit-depth)) @@ -83,7 +85,7 @@ (funcall reader stream) (funcall reader stream)) :element-type 'fixnum)))))) - data)) + (values data format bit-depth))) (defun write% (data stream format binary? maximum-value) @@ -106,6 +108,24 @@ ;;;; API ---------------------------------------------------------------------- (defun 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. + + 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 bit-depth)` + * PPM: `(simple-array (integer 0 bit-depth) (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. + + " (multiple-value-bind (format binary?) (file-format (read-magic-byte stream)) (read% (flexi-streams:make-flexi-stream stream :external-format :ascii) @@ -115,18 +135,86 @@ (format :ppm) (encoding :binary) (bit-depth (ecase format (:pbm 1) ((:pgm :ppm) 255)))) + "Write a PPM image array `data` to `stream`. + + Nothing is returned. + + `format` must be one of `:pbm`, `:pgm`, `:ppm`. + + `encoding` must be one of `:binary`, `:ascii`. + + `bit-depth` 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 `bit-depth` 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 `bit-depth` + inclusive. + + " (check-type format (member :ppm :pgm :pbm)) (check-type encoding (member :binary :ascii)) - (write% data stream format (eql :binary encoding) bit-depth)) + (if (eql format :pbm) + (check-type bit-depth (eql 1)) + (check-type bit-depth (integer 1 *))) + (write% data stream format (eql :binary encoding) bit-depth) + (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 bit-depth)` + * PPM: `(simple-array (integer 0 bit-depth) (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 write-to-file (path data &key + (if-exists nil if-exists-given) (format :ppm) (encoding :binary) (bit-depth (ecase format (:pbm 1) ((:pgm :ppm) 255)))) - (with-open-file (s path :direction :output :if-exists :supersede :element-type '(unsigned-byte 8)) - (write-to-stream s data :format format :encoding encoding :bit-depth bit-depth))) + "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`. + + `bit-depth` 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 `bit-depth` 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 `bit-depth` + inclusive. + + " + (flet ((write-it (stream) + (write-to-stream stream data + :format format + :encoding encoding + :bit-depth bit-depth))) + (if if-exists-given + (with-open-file (s path :direction :output :if-exists if-exists :element-type '(unsigned-byte 8)) + (write-it s)) + (with-open-file (s path :direction :output :element-type '(unsigned-byte 8)) + (write-it s)))) + (values))