root/trunk/thirdparty/cl-gd-0.5.6/images.lisp

Revision 2873, 19.4 kB (checked in by hans, 9 months ago)

Add new method to import-image to import an image from a URL. Change
IMPORT-IMAGE call sites

Line 
1 ;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-GD; Base: 10 -*-
2 ;;; $Header: /usr/local/cvsrep/gd/images.lisp,v 1.33 2007/01/01 23:41:00 edi Exp $
3
4 ;;; Copyright (c) 2003-2007, Dr. Edmund Weitz.  All rights reserved.
5
6 ;;; Redistribution and use in source and binary forms, with or without
7 ;;; modification, are permitted provided that the following conditions
8 ;;; are met:
9
10 ;;;   * Redistributions of source code must retain the above copyright
11 ;;;     notice, this list of conditions and the following disclaimer.
12
13 ;;;   * Redistributions in binary form must reproduce the above
14 ;;;     copyright notice, this list of conditions and the following
15 ;;;     disclaimer in the documentation and/or other materials
16 ;;;     provided with the distribution.
17
18 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
19 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 (in-package :cl-gd)
31
32 (defun create-image (width height &optional true-color)
33   "Allocates and returns a GD image structure with size WIDTH x
34 HEIGHT. Creates a true color image if TRUE-COLOR is true. You are
35 responsible for destroying the image after you're done with it. It is
36 advisable to use WITH-IMAGE instead."
37   (check-type width integer)
38   (check-type height integer)
39   (let ((image-ptr
40           (if true-color
41             (gd-image-create-true-color width height)
42             (gd-image-create width height))))
43     (when (null-pointer-p image-ptr)
44       (error "Could not allocate image of size ~A x ~A" width height))
45     (let ((image (make-image image-ptr)))
46       image)))
47
48 (defun destroy-image (image)
49   "Destroys \(deallocates) IMAGE which has been created by
50 CREATE-IMAGE, CREATE-IMAGE-FROM-FILE, or CREATE-IMAGE-FROM-GD2-PART."
51   (check-type image image)
52   (gd-image-destroy (img image))
53   nil)
54
55 (defmacro with-default-image ((image) &body body)
56   "Executes BODY with *DEFAULT-IMAGE* bound to IMAGE so that you don't
57 have to provide the IMAGE keyword/optional argument to CL-GD
58 functions."
59   `(let ((*default-image* ,image))
60     ,@body))
61
62 (defmacro with-image ((name width height &optional true-color) &body body)
63   "Creates an image with size WIDTH x HEIGHT, and executes BODY with
64 the image bound to NAME. If TRUE-COLOR is true, creates a true color
65 image. The image is guaranteed to be destroyed before this macro
66 exits."
67   ;; we rebind everything so we have left-to-right evaluation
68   (with-rebinding (width height true-color)
69     `(with-safe-alloc (,name
70                        (create-image ,width ,height ,true-color)
71                        (destroy-image ,name))
72        ,@body)))
73
74 (defmacro with-image* ((width height &optional true-color) &body body)
75   "Creates an image with size WIDTH x HEIGHT and executes BODY with
76 the image bound to *DEFAULT-IMAGE*. If TRUE-COLOR is true, creates a
77 true color image. The image is guaranteed to be destroyed before this
78 macro exits."
79   `(with-image (*default-image* ,width ,height ,true-color)
80     ,@body))
81
82 (defun create-image-from-file (file-name &optional type)
83   "Creates an image from the file specified by FILE-NAME \(which is
84 either a pathname or a string). The type of the image can be provided
85 as TYPE or otherwise it will be guessed from the PATHNAME-TYPE of
86 FILE-NAME. You are responsible for destroying the image after you're
87 done with it. It is advisable to use WITH-IMAGE-FROM-FILE instead."
88   (check-type file-name (or pathname string))
89   (let* ((file-name (truename file-name))
90          (pathname-type (pathname-type file-name))
91          (%type (or type
92                     (cond ((or (string-equal pathname-type "jpg")
93                                (string-equal pathname-type "jpeg"))
94                             :jpg)
95                           ((string-equal pathname-type "png")
96                             :png)
97                           ((string-equal pathname-type "gd")
98                             :gd)
99                           ((string-equal pathname-type "gd2")
100                             :gd2)
101                           ((string-equal pathname-type "xbm")
102                             :xbm)
103                           #-:win32
104                           ((string-equal pathname-type "xpm")
105                            :xpm)
106                           #-:cl-gd-no-gif
107                           ((string-equal pathname-type "gif")
108                             :gif)))))
109     (unless %type
110       (error "No type provided and it couldn't be guessed from filename"))
111     (unless (probe-file file-name)
112       (error "File ~S could not be found" file-name))
113     (when (pathnamep file-name)
114       (setq file-name
115               #+:cmu (ext:unix-namestring file-name)
116               #-:cmu (namestring file-name)))
117     (with-foreign-object (err :int)
118       (with-cstring (c-file-name file-name)
119         (let ((image (ecase %type
120                        ((:jpg :jpeg)
121                          (gd-image-create-from-jpeg-file c-file-name err))
122                        ((:png)
123                          (gd-image-create-from-png-file c-file-name err))
124                        ((:gd)
125                          (gd-image-create-from-gd-file c-file-name err))
126                        ((:gd2)
127                          (gd-image-create-from-gd2-file c-file-name err))
128                        ((:xbm)
129                          (gd-image-create-from-xbm-file c-file-name err))
130                        #-:win32
131                        ((:xpm)
132                          (gd-image-create-from-xpm c-file-name))
133                        #-:cl-gd-no-gif
134                        ((:gif)
135                          (gd-image-create-from-gif-file c-file-name err)))))
136           (cond ((null-pointer-p image)
137                   (cond ((or (eq %type :xpm)
138                              (zerop (deref-pointer err :int)))
139                           (error "Could not create image from ~A file ~S"
140                                  %type file-name))
141                         (t
142                           (error "Could not create image from ~A file ~S: errno was ~A"
143                                  %type file-name (deref-pointer err :int)))))
144                 (t (let ((image (make-image image)))
145                      image))))))))
146
147 (defmacro with-image-from-file ((name file-name &optional type) &body body)
148   "Creates an image from the file specified by FILE-NAME \(which is
149 either a pathname or a string) and executes BODY with the image bound
150 to NAME. The type of the image can be provied as TYPE or otherwise it
151 will be guessed from the PATHNAME-TYPE of FILE-NAME. The image is
152 guaranteed to be destroyed before this macro exits."
153   ;; we rebind everything so we have left-to-right evaluation
154   (with-rebinding (file-name type)
155     `(with-safe-alloc (,name
156                        (create-image-from-file ,file-name ,type)
157                        (destroy-image ,name))
158        ,@body)))
159      
160 (defmacro with-image-from-file* ((file-name &optional type) &body body)
161   "Creates an image from the file specified by FILE-NAME \(which is
162 either a pathname or a string) and executes BODY with the image bound
163 to *DEFAULT-IMAGE*. The type of the image can be provied as TYPE or
164 otherwise it will be guessed from the PATHNAME-TYPE of FILE-NAME. The
165 image is guaranteed to be destroyed before this macro exits."
166   `(with-image-from-file (*default-image* ,file-name ,type)
167     ,@body))
168
169 (defun create-image-from-gd2-part (file-name src-x src-y width height)
170   "Creates an image from the part of the GD2 file FILE-NAME \(which is
171 either a pathname or a string) specified by SRC-X, SRC-Y, WIDTH, and
172 HEIGHT. You are responsible for destroying the image after you're done
173 with it. It is advisable to use WITH-IMAGE-FROM-GD2-PART instead."
174   (check-type file-name (or string pathname))
175   (check-type src-x integer)
176   (check-type src-y integer)
177   (check-type width integer)
178   (check-type height integer)
179   (unless (probe-file file-name)
180     (error "File ~S could not be found" file-name))
181   (when (pathnamep file-name)
182     (setq file-name
183             #+:cmu (ext:unix-namestring file-name)
184             #-:cmu (namestring file-name)))
185   (with-foreign-object (err :int)
186     (with-cstring (c-file-name file-name)
187       (let ((image (gd-image-create-from-gd2-part-file c-file-name err src-x src-y width height)))
188         (cond ((null-pointer-p image)
189                 (error "Could not create GD2 image from file ~S: errno was ~A"
190                        file-name (deref-pointer err :int)))
191               (t image))))))
192
193 (defmacro with-image-from-gd2-part ((name file-name src-x src-y width height) &body body)
194   "Creates an image from the part of the GD2 file FILE-NAME \(which is
195 either a pathname or a string) specified SRC-X, SRC-Y, WIDTH, and
196 HEIGHT and executes BODY with the image bound to NAME. The type of the
197 image can be provied as TYPE or otherwise it will be guessed from the
198 PATHNAME-TYPE of FILE-NAME. The image is guaranteed to be destroyed
199 before this macro exits."
200   ;; we rebind everything so we have left-to-right evaluation
201   (with-rebinding (file-name src-x src-y width height)
202     `(with-safe-alloc (,name
203                        (create-image-from-gd2-part ,file-name ,src-x ,src-y ,width ,height)
204                        (destroy-image ,name))
205        ,@body)))
206
207 (defmacro with-image-from-gd2-part* ((file-name src-x src-y width height) &body body)
208   "Creates an image from the part of the GD2 file FILE-NAME \(which is
209 either a pathname or a string) specified SRC-X, SRC-Y, WIDTH, and
210 HEIGHT and executes BODY with the image bound to *DEFAULT-IMAGE*. The
211 type of the image can be provied as TYPE or otherwise it will be
212 guessed from the PATHNAME-TYPE of FILE-NAME. The image is guaranteed
213 to be destroyed before this macro exits."
214   `(with-image-from-gd2-part (*default-image* ,file-name ,src-x ,src-y ,width ,height)
215     ,@body))
216  
217 (defmacro make-stream-fn (name signature gd-call type-checks docstring)
218   "Internal macro used to generate WRITE-JPEG-TO-STREAM and friends."
219   `(defun ,name ,signature
220     ,docstring
221     ,@type-checks
222     (cond ((or #+(and :allegro :allegro-version>= (version>= 6 0))
223                (typep stream 'excl:simple-stream)
224                #+:lispworks4.3
225                (subtypep 'base-char (stream-element-type stream))
226                (subtypep '(unsigned-byte 8) (stream-element-type stream)))
227             (with-foreign-object (size :int)
228               (with-safe-alloc (memory ,gd-call (gd-free memory))
229                 (let (#+:lispworks4.3
230                       (temp-array (make-array 1 :element-type
231                                                 '(unsigned-byte 8))))
232                   (with-cast-pointer (temp memory :unsigned-byte)
233                     (dotimes (i (deref-pointer size :int))
234                       ;; LispWorks workaround, WRITE-BYTE won't work - see
235                       ;; <http://article.gmane.org/gmane.lisp.lispworks.general/1827>
236                       #+:lispworks4.3
237                       (setf (aref temp-array 0)
238                               (deref-array temp '(:array :unsigned-byte) i))
239                       #+:lispworks4.3
240                       (write-sequence temp-array stream)
241                       #-:lispworks4.3
242                       (write-byte (deref-array temp '(:array :unsigned-byte) i)
243                                   stream))
244                     image)))))
245           ((subtypep 'character (stream-element-type stream))
246            (with-foreign-object (size :int)
247              (with-safe-alloc (memory ,gd-call (gd-free memory))
248                (with-cast-pointer (temp memory
249                                         #+(or :cmu :scl :sbcl) :unsigned-char
250                                         #-(or :cmu :scl :sbcl) :char)
251                  (dotimes (i (deref-pointer size :int))
252                    (write-char (ensure-char-character
253                                 (deref-array temp '(:array :char) i))
254                                stream))
255                  image))))
256           (t (error "Can't use a stream with element-type ~A"
257                     (stream-element-type stream))))))
258
259 (make-stream-fn write-jpeg-to-stream (stream &key (quality -1) (image *default-image*))
260                 (gd-image-jpeg-ptr (img image) size quality)
261                 ((check-type stream stream)
262                  (check-type quality (integer -1 100))
263                  (check-type image image))
264                 "Writes image IMAGE to stream STREAM as JPEG. If
265 QUALITY is not specified, the default IJG JPEG quality value is
266 used. Otherwise, for practical purposes, quality should be a value in
267 the range 0-95. STREAM must be a character stream or a binary stream
268 of element type \(UNSIGNED-BYTE 8). If STREAM is a character stream,
269 the user of this function has to make sure the external format is
270 yields faithful output of all 8-bit characters.")
271
272 (make-stream-fn write-png-to-stream (stream &key compression-level (image *default-image*))
273                 (cond (compression-level
274                         (gd-image-png-ptr-ex (img image) size compression-level))
275                       (t
276                         (gd-image-png-ptr (img image) size)))
277                 ((check-type stream stream)
278                  (check-type compression-level (or null (integer -1 9)))
279                  (check-type image image))
280                 "Writes image IMAGE to stream STREAM as PNG. If
281 COMPRESSION-LEVEL is not specified, the default compression level at
282 the time zlib was compiled on your system will be used.  Otherwise, a
283 compression level of 0 means 'no compression', a compression level of
284 1 means 'compressed, but as quickly as possible', a compression level
285 of 9 means 'compressed as much as possible to produce the smallest
286 possible file.' STREAM must be a character stream or a binary stream
287 of element type \(UNSIGNED-BYTE 8). If STREAM is a character stream,
288 the user of this function has to make sure the external format yields
289 faithful output of all 8-bit characters.")
290
291 #-:cl-gd-no-gif
292 (make-stream-fn write-gif-to-stream (stream &key (image *default-image*))
293                 (gd-image-gif-ptr (img image) size)
294                 ((check-type stream stream)
295                  (check-type image image))
296                 "Writes image IMAGE to stream STREAM as GIF. STREAM
297 must be a character stream or a binary stream of element type
298 \(UNSIGNED-BYTE 8). If STREAM is a character stream, the user of this
299 function has to make sure the external format yields faithful output
300 of all 8-bit characters.")
301
302 (make-stream-fn write-wbmp-to-stream (stream &key foreground (image *default-image*))
303                 (gd-image-wbmp-ptr (img image) size foreground)
304                 ((check-type stream stream)
305                  (check-type foreground integer)
306                  (check-type image image))
307                 "Writes image IMAGE to stream STREAM as WBMP. STREAM
308 must be a character stream or a binary stream of element type
309 \(UNSIGNED-BYTE 8). If STREAM is a character stream, the user of this
310 function has to make sure the external format yields faithful output
311 of all 8-bit characters. WBMP file support is black and white
312 only. The color index specified by the FOREGOUND argument is the
313 \"foreground,\" and only pixels of this color will be set in the WBMP
314 file")
315
316 (make-stream-fn write-gd-to-stream (stream &key (image *default-image*))
317                 (gd-image-gd-ptr (img image) size)
318                 ((check-type stream stream)
319                  (check-type image image))
320                 "Writes image IMAGE to stream STREAM as GD. STREAM
321 must be a character stream or a binary stream of element type
322 \(UNSIGNED-BYTE 8). If STREAM is a character stream, the user of this
323 function has to make sure the external format yields faithful output
324 of all 8-bit characters.")
325
326 (make-stream-fn write-gd2-to-stream (stream &key (image *default-image*))
327                 (gd-image-gd2-ptr (img image) size)
328                 ((check-type stream stream)
329                  (check-type image image))
330                 "Writes image IMAGE to stream STREAM as GD2. STREAM
331 must be a character stream or a binary stream of element type
332 \(UNSIGNED-BYTE 8). If STREAM is a character stream, the user of this
333 function has to make sure the external format yields faithful output
334 of all 8-bit characters.")
335
336 (defun write-image-to-stream (stream type &rest rest &key &allow-other-keys)
337   "Writes image to STREAM. The type of the image is determined by TYPE
338 which must be one of :JPG, :JPEG, :PNG, :WBMP, :GD, or :GD2. STREAM
339 must be a character stream or a binary stream of element type
340 \(UNSIGNED-BYTE 8). If STREAM is a character stream, the user of this
341 function has to make sure the external format yields faithful output
342 of all 8-bit characters."
343   (apply (ecase type
344            ((:jpg :jpeg)
345              #'write-jpeg-to-stream)
346            ((:png)
347              #'write-png-to-stream)
348            ((:wbmp)
349              #'write-wbmp-to-stream)
350            ((:gd)
351              #'write-gd-to-stream)
352            ((:gd2)
353              #'write-gd2-to-stream)
354            #-:cl-gd-no-gif
355            ((:gif)
356              #'write-gif-to-stream))
357          stream rest))
358
359 (defun write-image-to-file (file-name &rest rest &key type (if-exists :error) &allow-other-keys)
360   "Writes image to the file specified by FILE-NAME \(a pathname or a
361 string). The TYPE argument is interpreted as in
362 WRITE-IMAGE-TO-STREAM. If it is not provided it is guessed from the
363 PATHNAME-TYPE of FILE-NAME. The IF-EXISTS keyword argument is given to
364 OPEN. Other keyword argument like QUALITY or COMPRESSION-LEVEL can be
365 provided depending on the images's type."
366   (with-open-file (stream file-name :direction :output
367                                     :if-exists if-exists
368                                     :element-type '(unsigned-byte 8))
369     (apply #'write-image-to-stream
370            stream
371            (or type
372                (let ((pathname-type (pathname-type (truename file-name))))
373                  (cond ((or (string-equal pathname-type "jpg")
374                             (string-equal pathname-type "jpeg"))
375                          :jpg)
376                        ((string-equal pathname-type "png")
377                          :png)
378                        ((string-equal pathname-type "wbmp")
379                          :wbmp)
380                        ((string-equal pathname-type "gd")
381                          :gd)
382                        ((string-equal pathname-type "gd2")
383                          :gd2)
384                        #-:cl-gd-no-gif
385                        ((string-equal pathname-type "gif")
386                          :gif)
387                        (t
388                          (error "Can't determine the type of the image")))))
389            (sans rest :type :if-exists))))
390
391 (defun image-width (&optional (image *default-image*))
392   "Returns width of IMAGE."
393   (check-type image image)
394   (with-transformed-alternative
395       (((gd-image-get-sx (img image)) w-inv-transformer))
396     (gd-image-get-sx (img image))))
397
398 (defun image-height (&optional (image *default-image*))
399   (check-type image image)
400   "Returns height of IMAGE."
401   (with-transformed-alternative
402       (((gd-image-get-sy (img image)) h-inv-transformer))
403     (gd-image-get-sy (img image))))
404
405 (defun image-size (&optional (image *default-image*))
406   (check-type image image)
407   "Returns width and height of IMAGE as two values."
408   (with-transformed-alternative
409       (((gd-image-get-sx (img image)) w-inv-transformer)
410        ((gd-image-get-sy (img image)) h-inv-transformer))
411     (values (gd-image-get-sx (img image))
412             (gd-image-get-sy (img image)))))
Note: See TracBrowser for help on using the browser.