3 ;; Copyright (C) 2007-2016 Mario Rodriguez Riotorto
5 ;; This program is free software; you can redistribute
6 ;; it and/or modify it under the terms of the
7 ;; GNU General Public License as published by
8 ;; the Free Software Foundation; either version 2
9 ;; of the License, or (at your option) any later version.
11 ;; This program is distributed in the hope that it
12 ;; will be useful, but WITHOUT ANY WARRANTY;
13 ;; without even the implied warranty of MERCHANTABILITY
14 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details at
16 ;; http://www.gnu.org/copyleft/gpl.html
18 ;; The picture package. UNSTABLE !!
20 ;; For questions, suggestions, bugs and the like, feel free
22 ;; mario @@@ edu DOT xunta DOT es
23 ;; http://tecnostats.net/Maxima/gnuplot
27 (defun cut-and-round (seq)
30 (let ((fz (meval `($float
,z
))))
39 ;; Constructs a levels picture. This object contains four parts:
43 ;; 4) an integer array with pixel data ranging from 0 to 255.
44 ;; Argument data must contain only numbers ranged from 0 to 255;
45 ;; negative numbers are substituted by 0, and those which are
46 ;; greater than 255 are set to 255. If data is a Maxima list,
47 ;; width and height must be given.
48 (defun $make_level_picture
(data &optional
(wi nil
) (he nil
))
49 (let (width height picarray
)
52 (setf width
(length (cdadr data
))
53 height
(length (cdr data
)))
54 (setf picarray
(make-array (* height width
)
55 :element-type
'integer
56 :initial-contents
(cut-and-round (rest ($flatten
($args data
)))))) )
60 (= (* wi he
) ($length data
)))
63 picarray
(make-array (* wi he
)
64 :element-type
'integer
65 :initial-contents
(cut-and-round (rest data
)))))
67 (merror "Argument should be a matrix or a list of numbers")))
68 (list '(picture simp
) '$level width height picarray
) ))
72 ;; Returns true if the argument is a well formed image,
73 ;; and false otherwise
77 ((and (= (length im
) 5)
78 (equal (car im
) '(picture simp
)))
81 (and (equal (length im
) 5)
82 (equal (car im
) '(picture ))
83 (or (member (cadr im
) '($level $rgb
)))
85 (cond ((equal (nth 1 im
) '$level
)
86 (= (array-dimension (nth 4 im
) 0)
87 (* (nth 2 im
) (nth 3 im
))))
89 (= (array-dimension (nth 4 im
) 0)
90 (* 3 (nth 2 im
) (nth 3 im
)))))
91 (every #'(lambda (z) (and (integerp z
) (>= z
0) (<= z
255))) (nth 4 im
)) ))))
95 ;; Returns true in case of equal pictures, and false otherwise.
96 (defun $picture_equalp
(pic1 pic2
)
97 (if (and ($picturep pic1
) ($picturep pic2
))
99 (merror "Two picture objects are required")))
103 ;; Constructs a coloured rgb picture. This object contains four parts:
107 ;; 4) an integer array of length 3*width*height with pixel data ranging
108 ;; from 0 to 255. Each pixel is represented by three consecutive numbers
109 ;; (red, green, blue). Arguments must contain the three channels in
111 (defun $make_rgb_picture
(redlevel greenlevel bluelevel
)
112 (when (not (and ($picturep redlevel
)
113 (equal (cadr redlevel
) '$level
)
114 ($picturep greenlevel
)
115 (equal (cadr greenlevel
) '$level
)
116 ($picturep bluelevel
)
117 (equal (cadr bluelevel
) '$level
)))
118 (merror "Color channel is not a levels picture object"))
119 (when (not (and (= (caddr redlevel
) (caddr greenlevel
) (caddr bluelevel
))
120 (= (cadddr redlevel
) (cadddr greenlevel
) (cadddr bluelevel
)) ))
121 (merror "Color channels are not of equal dimensions"))
122 (let (width height leng picarray i3
)
123 (setf width
(caddr redlevel
)
124 height
(cadddr redlevel
))
125 (setf leng
(* width height
))
126 (setf picarray
(make-array (* 3 leng
) :element-type
'integer
))
127 (loop for i from
0 below leng do
129 (setf (aref picarray i3
) (aref (nth 4 redlevel
) i
))
130 (setf (aref picarray
(incf i3
)) (aref (nth 4 greenlevel
) i
))
131 (setf (aref picarray
(incf i3
)) (aref (nth 4 bluelevel
) i
)))
132 (list '(picture simp
) '$rgb width height picarray
) ))
136 ;; Extracts color channel ('red, 'green or 'blue) from a coloured picture.
137 ;; Returns a levels picture.
138 (defun $take_channel
(pic chn
)
139 (when (not (and ($picturep pic
)
140 (equal (cadr pic
) '$rgb
)))
141 (merror "Argument is not a coloured picture"))
142 (when (not (member chn
'($red $green $blue
)))
143 (merror "Incorrect colour channel"))
144 (let* ((width (caddr pic
))
145 (height (cadddr pic
))
146 (dim (* width height
))
147 (img (make-array dim
:element-type
'integer
))
154 (loop for i from
0 below dim do
155 (setf (aref img i
) (aref (nth 4 pic
) (+ (* 3 i
) idx
))))
156 (list '(picture simp
) '$level width height img
) ))
160 ;; Returns the negative of a (level or rgb) picture
161 (defun $negative_picture
(pic)
162 (if (not ($picturep pic
))
163 (merror "Argument is not a picture"))
164 (let ((dim (array-dimension (nth 4 pic
) 0))
165 (arr (make-array (array-dimension (nth 4 pic
) 0) :element-type
'integer
)))
166 (loop for i from
0 below dim do
167 (setf (aref arr i
) (- 255 (aref (nth 4 pic
) i
))))
168 (list '(picture simp
)
176 ;; Transforms an rgb picture into a level one by
177 ;; averaging the red, green and blue values.
178 (defun $rgb2level
(pic)
179 (if (or (not ($picturep pic
))
180 (not (equal (nth 1 pic
) '$rgb
)))
181 (merror "Argument is not an rgb picture"))
182 (let* ((dim (* (nth 2 pic
) (nth 3 pic
)))
183 (arr (make-array dim
:element-type
'integer
))
185 (loop for i from
0 below dim do
186 (setf (aref arr i
) (round (/ (+ (aref (nth 4 pic
) (incf k
))
187 (aref (nth 4 pic
) (incf k
))
188 (aref (nth 4 pic
) (incf k
)))
190 (list '(picture simp
)
198 ;; Returns pixel from picture. Coordinates x and y range from 0 to
199 ;; (width-1) and (height-1), respectively. We are working
200 ;; with arrays, not with lists.
201 (defun $get_pixel
(pic x y
)
202 (when (not ($picturep pic
))
203 (merror "Argument is not a well formed picture"))
204 (when (not (and (integerp x
) (integerp y
)))
205 (merror "Pixel coordinates must be positive integers"))
206 (when (not (and (> x -
1)
210 (merror "Pixel coordinates out of range"))
212 ($level
(aref (nth 4 pic
) (+ x
(* y
(nth 2 pic
)))))
213 ($rgb
(let ((pos (* 3 (+ x
(* y
(nth 2 pic
))))))
216 (aref (nth 4 pic
) pos
)
217 (aref (nth 4 pic
) (incf pos
))
218 (aref (nth 4 pic
) (incf pos
)))))))
227 ;;; XPM I M A G E F O R M A T S U P P O R T
229 ;; The following functions have been taken from
230 ;; http://common-lisp.net/project/gamelib/ (MIT license)
231 ;; Changes have been made to fit the Maxima environment.
234 (defvar *xpm-readtable
* nil
)
237 (defun init-readtable ()
238 (unless *xpm-readtable
*
239 (setf *xpm-readtable
* (copy-readtable))
240 (set-syntax-from-char #\
, #\Space
*xpm-readtable
*)))
243 (defun skip-whitespace (f)
244 (loop for c
= (read-char f
)
245 while
(member c
'(#\space
#\tab
) :test
#'char
=)
246 finally
(unread-char c f
)
250 (defun read-colour (f)
251 (let ((ctype (read-char f
)))
254 ; color in hexadecimal format
256 (let ((*read-base
* 16))
260 ; 0. read the rest of the name and append the first letter
261 ; 1. get the hexadecimal code from *color-table* defined in grcommon.lisp
262 ; 2. remove # and transform the code to an integer in base 10
265 (subseq (gethash (atom-to-downcased-string (format nil
"~a~a" ctype
(read f
))) *color-table
*) 1)
269 (defun read-charspec (f cnt
)
271 (loop for n from
0 below cnt
272 collect
(read-char f
))))
275 (defun read-cspec (str cnt hash
)
276 (with-input-from-string (cs str
)
277 (let ((chars (read-charspec cs cnt
)))
279 (let ((c (read-char cs
)))
284 (setf (gethash chars hash
) col
))
285 (merror "Unknown colourspec"))))))
288 (defun $read_xpm
(mfspec)
290 (let ((*readtable
* *xpm-readtable
*)
291 (fspec (string-trim "\"" (coerce (mstring mfspec
) 'string
))) )
292 (with-open-file (image fspec
:direction
:input
)
293 (read-line image
) ; Skip initial comment
294 (read-line image
) ; Skip C code
295 (let ((colspec (read image
))
298 (chartab (make-hash-table :test
#'equal
))
300 (with-input-from-string (cspec colspec
)
301 (setf width
(read cspec
))
302 (setf height
(read cspec
))
303 (let ((colours (read cspec
))
306 (loop for cix from
0 below colours
308 do
(read-cspec c cpp chartab
))
309 (setf img
(make-array (* 3 width height
) :element-type
'integer
))
310 (loop for y from
0 below height
311 for line
= (read image
)
313 (with-input-from-string (data line
)
314 (loop for x from
0 below width
315 for cs
= (read-charspec data cpp
) do
316 (setf rgb
(gethash cs chartab
))
317 (setf (aref img
(incf counter
)) (/ (logand rgb
16711680) 65536))
318 (setf (aref img
(incf counter
)) (/ (logand rgb
65280) 256))
319 (setf (aref img
(incf counter
)) (logand rgb
255))))) )
320 (list '(picture simp
) '$rgb width height img
)))))))