3 <title>ZPNG - Create PNG files from Common Lisp
</title>
4 <style type=
"text/css">
5 a
, a:visited
{ text-decoration: none
}
6 a
[href
]:hover
{ text-decoration: underline
}
7 pre
{ background: #DDD; padding: 0.25em }
8 p
.download
{ color: red
}
9 .transparent { background-image: url
(background.gif) }
10 tt
.code
{ background: #DDD }
16 <h2>ZPNG - Create PNG files from Common Lisp
</h2>
18 <blockquote class='abstract'
>
21 <p>ZPNG is a Common Lisp library for creating PNG files. It
22 uses
<a href='http://www.xach.com/lisp/salza2/'
>Salza2
</a> for
25 The current version is
1.1.3, released on July
9,
2008.
27 <p class='download'
>Download shortcut:
29 <p><a href='http://www.xach.com/lisp/zpng.tgz'
>http://www.xach.com/lisp/zpng.tgz
</a>
36 <li> <a href='#sect-overview'
>Overview and Limitations
</a>
37 <li> <a href='#sect-examples'
>Examples
</a>
38 <li> <a href='#sect-dictionary'
>Dictionary
</a>
45 <li> <a href='#samples-per-pixel'
><tt>samples-per-pixel
</tt></a>
46 <li> <a href='#width'
><tt>width
</tt></a>
47 <li> <a href='#height'
><tt>height
</tt></a>
48 <li> <a href='#rowstride'
><tt>rowstride
</tt></a>
49 <li> <a href='#color-type'
><tt>color-type
</tt></a>
56 <li> <a href='#png'
><tt>png
</tt></a>
57 <li> <a href='#image-data'
><tt>image-data
</tt></a>
58 <li> <a href='#data-array'
><tt>data-array
</tt></a>
59 <li> <a href='#copy-png'
><tt>copy-png
</tt></a>
60 <li> <a href='#png='
><tt>png=
</tt></a>
61 <li> <a href='#write-png'
><tt>write-png
</tt></a>
62 <li> <a href='#write-png-stream'
><tt>write-png-stream
</tt></a>
65 <li> Streamed, row-at-a-time PNGs
68 <li> <a href='#streamed-png'
><tt>streamed-png
</tt></a>
69 <li> <a href='#start-png'
><tt>start-png
</tt></a>
70 <li> <a href='#write-row'
><tt>write-row
</tt></a>
71 <li> <a href='#rows-written'
><tt>rows-written
</tt></a>
72 <li> <a href='#rows-left'
><tt>rows-left
</tt></a>
73 <li> <a href='#finish-png'
><tt>finish-png
</tt></a>
79 <li> <a href='#zpng-error'
><tt>zpng-error
</tt></a>
80 <li> <a href='#invalid-size'
><tt>invalid-size
</tt></a>
81 <li> <a href='#invalid-size-width'
><tt>invalid-size-width
</tt></a>
82 <li> <a href='#invalid-size-height'
><tt>invalid-size-height
</tt></a>
83 <li> <a href='#invalid-row-length'
><tt>invalid-row-length
</tt></a>
84 <li> <a href='#insufficient-rows'
><tt>insufficient-rows
</tt></a>
85 <li> <a href='#too-many-rows'
><tt>too-many-rows
</tt></a>
90 <li> <a href='#sect-references'
>References
</a>
91 <li> <a href='#sect-feedback'
>Feedback
</a>
94 <a name='sect-overview'
><h3>Overview and Limitations
</h3></a>
96 <p>ZPNG provides two interfaces creating PNG files. The first is through a
97 <a href='#png'
><tt>PNG
</tt></a> object, which holds all image sample
98 data and which may be written out to a file all at once. The second is
99 through a
<a href='#streamed-png'
><tt>STREAMED-PNG
</tt></a> object,
100 which writes a single output row of the image at a time. By working
101 with only a single row at a time, images that are too big to fit in
102 memory may still be written out incrementally as a PNG file.
104 <p>The PNG file format has many options, and ZPNG supports only a
108 <li> does not load PNG files
109 <li> supports only
8 bits per sample
110 <li> does not support filtering
111 <li> does not support indexed color
115 <a name='sect-examples'
><h3>Examples
</h3></a>
118 <pre><div style='float: right' class='transparent'
><img src='mandelbrot.png'
119 ></div>(defun draw-mandelbrot (file)
120 (let* ((png (make-instance '
<a href='#png'
>png
</a>
121 :color-type :grayscale-alpha
124 (image (
<a href='#data-array'
>data-array
</a> png))
126 (dotimes (y
200 (
<a href='#write-png'
>write-png
</a> png file))
128 (let ((c (complex (- (/ x
100.0)
1.5) (- (/ y
100.0)
1.0)))
129 (z (complex
0.0 0.0))
132 (setf z (+ (* z z) c))
135 (setf (aref image y x
1) iteration)
138 (setf (aref image y x
1)
255)
142 <a name='sect-dictionary'
><h3>Dictionary
</h3></a>
144 <p>The following symbols are exported from the ZPNG package.
148 <p><a name='samples-per-pixel'
>[Function]
</a><br>
149 <b>samples-per-pixel
</b> <i>png
</i> =
> <i>samples
</i>
152 Returns the number of octet samples that make up a single pixel.
154 <p><table cellpadding=
0 cellspacing=
5>
156 <th>Image Color Type
</th>
157 <th>Samples per Pixel
</th>
160 <td>Grayscale
</td><td align=center
>1</td>
163 <td>Truecolor
</td><td align=center
>3</td>
166 <td>Grayscale with Alpha
</td><td align=center
>2</td>
169 <td>Truecolor with Alpha
</td><td align=center
>4</td>
176 <p><a name='width'
><a name='height'
>[Functions]
</a></a><br>
177 <b>width
</b> <i>png
</i> =
> <i>width
</i><br>
178 <b>height
</b> <i>png
</i> =
> <i>height
</i>
181 Returns the width or height of
<i>png
</i>.
184 <p><a name='rowstride'
>[Function]
</a><br>
185 <b>rowstride
</b> <i>png
</i> =
> <i>rowstride
</i>
188 Returns the number of samples in a single row of
<i>png
</i>. It is
190 to
<tt class=code
>(*
(
<a href='#width'
>width
</a> png)
(
<a href='#samples-per-pixel'
>samples-per-pixel
</a> png))
</tt>.
193 <p><a name='color-type'
>[Function]
</a><br>
194 <b>color-type
</b> <i>png
</i> =
> <i>color-type
</i>
197 Returns the color type of
<i>png
</i>, one of
198 of
<tt>:grayscale
</tt>,
<tt>:truecolor
</tt>,
199 <tt>:grayscale-alpha
</tt>, or
<tt>:truecolor-alpha
</tt>.
203 <p><a name='png'
>[Class]
</a><br>
207 Instances of this class may be created directly. Supported initargs:
211 <li> <tt>:width
</tt> - required, the width of the image
213 <li> <tt>:height
</tt> - required, the height of the image
215 <li> <tt>:color-type
</tt> - optional, the color type of the image, one
216 of
<tt>:grayscale
</tt>,
<tt>:truecolor
</tt> (the
217 default),
<tt>:grayscale-alpha
</tt>, or
<tt>:truecolor-alpha
</tt>
219 <li> <tt>:image-data
</tt> - optional, the sample data of the image. If
220 specified, this must be an octet vector with a length of
221 <i>width
</i> × <i>height
</i> × <i>samples-per-pixel
</i>. If
222 not specified, an image data vector of the appropriate size will be
223 created automatically.
230 <p><a name='image-data'
>[Function]
</a><br>
231 <b>image-data
</b> <i>png
</i> =
> <i>octet-vector
</i>
234 Returns the image data of
<i>png
</i>. Samples are laid out from left
235 to right, top to bottom, so the first samples of data affect the
236 upper-left of the image and the final samples affect the lower-right.
238 <p><table cellpadding=
0 cellspacing=
5>
240 <th>Image Color Type
</th>
241 <th>Pixel Sample Layout
</th>
244 <td>Grayscale
</td><td>S|S|S...
</td>
247 <td>Truecolor
</td><td>RGB|RGB|RGB...
</td>
250 <td>Grayscale with Alpha
</td><td>SA|SA|SA...
</td>
253 <td>Truecolor with Alpha
</td><td>RGBA|RGBA|RGBA...
</td>
257 <p>Layout of the samples into pixels is done according to the image's
258 color type and is fully documented in
259 the
<a href=
"http://www.w3.org/TR/PNG/">Portable Network Graphics
264 <p><a name='data-array'
>[Function]
</a><br>
265 <b>data-array
</b> <i>png
</i> =
> <i>data-array
</i>
268 Returns a multidimensional array representing the pixels
269 of
<i>png
</i>. The dimensions correspond to the height, width, and
270 pixel components, respectively. For example, to access the red
271 component at
<53,
100> of a truecolor PNG, you could use this:
274 (aref (data-array png)
100 53 0)
277 <p>Note the reversed order of the coordinate arguments; this is a
278 consequence of Common Lisp's row-major ordering of elements in a
279 multidimensional array and PNG's specified sample layout.
284 <p><a name='copy-png'
>[Function]
</a><br>
285 <b>copy-png
</b> <i>png
</i> =
> <i>png-copy
</i>
288 Create a copy of
<i>png
</i>.
292 <p><a name='png='
>[Function]
</a><br>
293 <b>png=
</b> <i>png1
</i> <i>png2
</i> =
> <i>boolean
</i>
296 Returns true if png1 and png2 are equal. Equality is defined as having
297 the same dimensions, color type, and image data.
301 <p><a name='write-png'
>[Function]
</a><br>
302 <b>write-png
</b> <i>png
</i> <i>file
</i>
303 <tt>&key
</tt> (
<i>if-exists
</i> <tt>:supersede
</tt>) =
> pathname
306 Writes
<i>png
</i> to
<i>file
</i> and returns the truename
307 of
<i>file
</i>.
<i>if-exists
</i> is passed to the
308 underlying
<tt>CL:OPEN
</tt> call for opening the output file.
312 <p><a name='write-png-stream'
>[Function]
</a><br>
313 <b>write-png-stream
</b> <i>png
</i> <i>stream
</i> =
> |
316 Writes
<i>png
</i> to
<i>stream
</i>, which should be an output stream
317 that can accept octets.
321 <p><a name='streamed-png'
>[Class]
</a><br>
325 Instances of this class may be created directly. Supports all the
326 initargs of the
<a href='#png'
><tt>PNG
</tt></a> class
327 except
<tt>:IMAGE-DATA
</tt>.
329 <p>In contrast to
<tt>PNG
</TT> instances,
<tt>STREAMED-PNG
</tt>
330 instances do not keep all the image data in one large vector. Instead,
331 instances are used to write out the image data of a PNG file one row
332 at a time. The protocol for incrementally writing out via
333 a
<tt>STREAMED-PNG
</tt> involves these generic functions:
336 <li> <a href='#row-data'
><tt>ROW-DATA
</tt></a>
337 <li> <a href='#start-png'
><tt>START-PNG
</tt></a>
338 <li> <a href='#write-row'
><tt>WRITE-ROW
</tt></a>
339 <li> <a href='#rows-written'
><tt>ROWS-WRITTEN
</tt></a>
340 <li> <a href='#rows-left'
><tt>ROWS-LEFT
</tt></a>
341 <li> <a href='#finish-png'
><tt>FINISH-PNG
</tt></a>
347 <p><a name='row-data'
>[Function]
</a><br>
348 <b>row-data
</b> <i>streamed-png
</i> =
> <i>octet-vector
</i>
351 Returns a vector suitable for passing
352 to
<a href='#write-row'
><tt>WRITE-ROW
</tt></a>
353 for
<i>streamed-png
</i>; it has the appropriate number of entries for
354 the image width and color type of the png. The initial contents are
355 all zeroes. For a given streamed png, each call to
<tt>row-data
</tt>
356 will return the
<i>same
</i> vector, not a fresh one.
360 <p><a name='start-png'
>[Function]
</a><br>
361 <b>start-png
</b> <i>png
</i> <i>stream
</i> =
> <i>png
</i>
364 Writes PNG file header data to
<i>stream
</i>, which must be an output
365 stream that supports writing
<tt>(unsigned-byte
8)
</tt> data. The
366 header data is taken from
<i>png
</i>, which must be
367 a
<a href='#streamed-png'
><tt>STREAMED-PNG
</tt></a> instance.
371 <p><a name='write-row'
>[Function]
</a><br>
372 <b>write-row
</b> <i>row
</i> <i>png
</i>
373 <tt>&key
</tt> (
<i>start
</i> 0)
<i>end
</i> =
> |
376 Writes
<i>row
</i> to the output stream of
<i>png
</i>.
<i>row
</i> must
378 <tt>(unsigned-byte
8)
</tt> vector with the appropriate number of
379 entries for
<i>png
</i>.
<i>start
</i> defaults to
0 and
<i>end
</i>
380 defaults to
<i>start
</i>
381 +
<a href='#rowstride'
><tt>ROWSTRIDE
</tt></a>.
383 The difference between
<i>end
</i> and
<i>start
</i> should be equal to
384 <tt class=code
>(
<a href='#rowstride'
>rowstride
</a> png
</i>)
</tt>.
<i>png
</i>
385 must be a
<a href='#streamed-png'
><tt>STREAMED-PNG
</tt></a> instance.
387 <p>If the row length, as defined by
<i>start
</i> and
<i>end
</i>, is
388 not the right size, an error of
389 type
<a href='#invalid-row-length'
><tt>INVALID-ROW-LENGTH
</tt></a> is
392 <p>If writing
<i>row
</i> would exceed the number of rows in the image
393 (as defined by
<a href='#height'
><tt>HEIGHT
</tt></a>), an error of
394 type
<a href='#too-many-rows'
><tt>TOO-MANY-ROWS
</tt></a> is signaled.
400 <p><a name='rows-written'
>[Function]
</a><br>
401 <b>rows-written
</b> <i>streamed-png
</i> =
> <i>count
</i>
404 Returns the number of rows written to
<i>streamed-png
</i> so far.
408 <p><a name='rows-left'
>[Function]
</a><br>
409 <b>rows-left
</b> <i>streamed-png
</i> =
> <i>count
</i>
412 Returns the number of rows left to write
413 to
<i>streamed-png
</i>. Equivalent to
414 <tt class=code
>(- (
<a href='#height'
>height
</a> png)
415 (
<a href='#rows-written'
>rows-written
</a> png))
</tt>.
420 <p><a name='finish-png'
>[Function]
</a><br>
421 <b>finish-png
</b> <i>streamed-png
</i> =
> |
424 Concludes writing PNG file data to the output stream
425 of
<i>streamed-png
</i>. The internal state of streamed-png is reset in
426 such a way that it can be re-used to write another PNG file, with the
427 same dimensions and color type parameters, using
428 another
<a href='#start-png'
><tt>START-PNG
</tt></a>/
<a href='#write-row'
><tt>WRITE-ROW
</tt></a>/
<a href='#finish-png'
><tt>FINISH-PNG
</tt></a>
431 <p>This function must be called only after
432 exactly
<a href='#height'
><tt>HEIGHT
</tt></a> rows have been written
433 to
<i>streamed-png
</i>
434 via
<a href='#write-row'
><tt>WRITE-ROW
</tt></a>. If too few rows have
435 been written to
<i>streamed-png
</i>, an error of
436 type
<a href='#insufficient-rows'
><tt>INSUFFICIENT-ROWS
</tt></a> is
441 <p><a name='zpng-error'
>[Condition]
</a><br>
445 All errors signaled by ZPNG are a subtype of
<tt>ZPNG-ERROR
</tt>,
446 which is a subtype of
<tt>CL:ERROR
</tt>.
450 <p><a name='invalid-size'
>[Condition]
</a><br>
454 A condition of this type is signaled when a PNG with invalid size is
455 created. Valid PNGs have positive width and height.
459 <p><a name='invalid-size-width'
>[Accessors]
</a><br>
460 <b>invalid-size-width
</b> <i>condition
</i> =
> <i>width
</i><br>
461 <b>invalid-size-height
</b> <i>condition
</i> =
> <i>height
</i>
464 These accessors provide the invalid size used for a PNG.
468 <p><a name='invalid-row-length'
>[Condition]
</a><br>
469 <b>invalid-row-length
</b>
472 A condition of this type is signaled when a row with an incorrect size
473 is passed to
<a href='#write-row'
><tt>WRITE-ROW
</tt></a>.
477 <p><a name='insufficient-rows'
>[Condition]
</a><br>
478 <b>insufficient-rows
</b>
481 A condition of this type is signaled
482 from
<a href='#finish-png'
><tt>FINISH-PNG
</tt></a> when it is called
483 before enough rows have been written
484 via
<a href='#write-row'
><tt>WRITE-ROW
</tt></a>.
487 <p><a name='too-many-rows'
>[Condition]
</a><br>
491 A condition of this type is signaled
492 from
<a href='#write-row'
><tt>WRITE-ROW
</tt></a> if it is called more
493 times than is necessary for the given PNG.
497 <a name='sect-references'
><h3>References
</h3></a>
501 <li> W3C,
<a href=
"http://www.w3.org/TR/PNG/">Portable Network
502 Graphics Specification, Second Edition
</a>
505 Wikipedia,
<a href='http://en.wikipedia.org/wiki/Mandelbrot_set'
>Mandelbrot
511 <a name='sect-feedback'
><h3>Feedback
</h3></a>
513 <p>Please direct any questions, comments, bug reports, or other
514 feedback to
<a href='mailto:xach@xach.com'
>Zach Beane
</a>.