[mcclim-cvs] CVS mcclim

thenriksen thenriksen at common-lisp.net
Mon Apr 14 16:46:39 UTC 2008


Update of /project/mcclim/cvsroot/mcclim
In directory clnet:/tmp/cvs-serv15385

Modified Files:
	NEWS clim-examples.asd clim-listener.asd decls.lisp 
	design.lisp graphics.lisp mcclim.asd package.lisp 
Added Files:
	mcclim-gif-bitmaps.asd mcclim-jpeg-bitmaps.asd xpm.lisp 
Log Message:
Converted MCCLIM-IMAGES to CLIM 2.2 bitmap functions (with extensions).

Includes new demo application.


--- /project/mcclim/cvsroot/mcclim/NEWS	2008/01/31 10:47:07	1.34
+++ /project/mcclim/cvsroot/mcclim/NEWS	2008/04/14 16:46:37	1.35
@@ -4,8 +4,9 @@
 ** Bug fix: Some missing methods and functions have been implemented
    for the Null backend, allowing headless operation for many
    applications.
-** New extension: MCCLIM-IMAGES. This extension makes it easy to use
-   McCLIM for loading and displaying images of various formats.
+** Specification compliance: READ-BITMAP-FILE and
+   MAKE-PATTERN-FROM-BITMAP-FILE from CLIM 2.2. Includes new example
+   program, IMAGE-VIEWER.
 ** Drei improvements
 *** New redisplay engine that is faster and has more features.
 *** Support for "views" concept.
--- /project/mcclim/cvsroot/mcclim/clim-examples.asd	2007/02/05 03:47:40	1.3
+++ /project/mcclim/cvsroot/mcclim/clim-examples.asd	2008/04/14 16:46:37	1.4
@@ -37,7 +37,8 @@
                (:file "font-selector")
                (:file "tabdemo")
                (:file "bordered-output-examples")
-               (:file "misc-tests")))
+               (:file "misc-tests")
+               (:file "image-viewer")))
      (:module "Goatee"
 	      :components
 	      ((:file "goatee-test")))))
--- /project/mcclim/cvsroot/mcclim/clim-listener.asd	2008/01/06 15:32:12	1.3
+++ /project/mcclim/cvsroot/mcclim/clim-listener.asd	2008/04/14 16:46:37	1.4
@@ -6,13 +6,10 @@
 (in-package :clim-listener.system)
 
 (defsystem :clim-listener
-    :depends-on (:mcclim #+sbcl :sb-posix :mcclim-images :mcclim-images-xpm)
+    :depends-on (:mcclim #+sbcl :sb-posix)
     :components
-    ((:file "Experimental/xpm"
-            :pathname #.(make-pathname :directory '(:relative "Experimental") :name "xpm" :type "lisp"))
-     (:module "Apps/Listener"
+    ((:module "Apps/Listener"
               :pathname #.(make-pathname :directory '(:relative "Apps" "Listener"))
-              :depends-on ("Experimental/xpm")
               :components
               ((:file "package")
                (:file "util" :depends-on ("package"))
@@ -22,4 +19,4 @@
                (:file "wholine" :depends-on ("package" "dev-commands" "util"))
                (:file "listener" :depends-on ("package" "wholine" "file-types" "icons" "dev-commands" "util"))
                
-               #+CMU (:file "cmu-hacks" :depends-on ("package"))))))
\ No newline at end of file
+               #+CMU (:file "cmu-hacks" :depends-on ("package"))))))
--- /project/mcclim/cvsroot/mcclim/decls.lisp	2008/01/19 20:35:47	1.49
+++ /project/mcclim/cvsroot/mcclim/decls.lisp	2008/04/14 16:46:37	1.50
@@ -400,6 +400,21 @@
 (defgeneric medium-clear-area (medium left top right bottom))
 (defgeneric medium-beep (medium))
 
+;;;; 14.2
+
+(defgeneric pattern-width (pattern)
+  (:documentation "Return the width of `pattern'."))
+
+(defgeneric pattern-height (pattern)
+  (:documentation "Return the height of `pattern'."))
+
+(defgeneric pattern-array (pattern)
+  (:documentation "Returns the array associated with `pattern'."))
+
+(defgeneric pattern-designs (pattern)
+  (:documentation "Returns the array of designs associated with
+`pattern'."))
+
 ;;;; 14.5
 (defgeneric draw-design
     (medium design
--- /project/mcclim/cvsroot/mcclim/design.lisp	2008/01/21 20:54:48	1.28
+++ /project/mcclim/cvsroot/mcclim/design.lisp	2008/04/14 16:46:37	1.29
@@ -2,7 +2,7 @@
 
 ;;;  (c) copyright 1998,1999,2000 by Michael McDonald (mikemac at mikemac.com)
 ;;;  (c) copyright 2000 by Robert Strandh (strandh at labri.u-bordeaux.fr)
-;;;  (c) copyright 2002 by Gilbert Baumann <unk6 at rz.uni-karlsruhe.de>
+;;;  (c) copyright 1998,2002 by Gilbert Baumann <unk6 at rz.uni-karlsruhe.de>
 
 ;;; This library is free software; you can redistribute it and/or
 ;;; modify it under the terms of the GNU Library General Public
@@ -402,6 +402,73 @@
 (defgeneric compose-in (ink mask))
 (defgeneric compose-out (ink mask))
 
+;;; RGB image designs, efficient support for truecolor images. ARGB
+;;; image data represented as an (unsigned-byte 32) array
+
+(defclass rgb-image ()
+    ((width :initarg :width :accessor image-width)
+     (height :initarg :height :accessor image-height)
+     (data :initarg :data
+	   :accessor image-data
+	   :type (or null (simple-array (unsigned-byte 32) (* *))))
+     (alphap :initarg :alphap
+	     :initform nil
+	     :accessor image-alpha-p)))
+
+;; Applications (closure in particular) might want to cache any
+;; backend-specific data required to draw an RGB-IMAGE.
+;;
+;; To implement this caching, designs must be created separately for each
+;; medium, so that mediums can put their own data into them.
+
+(defclass rgb-image-design (design)
+    ((medium :initform nil :initarg :medium)
+     (image :reader image
+            :initarg :image)
+     (medium-data :initform nil)))
+
+(defun make-rgb-image-design (image)
+  (make-instance 'rgb-image-design :image image))
+
+
+;; Protocol to free cached data
+
+(defgeneric medium-free-image-design (medium design))
+
+(defun free-image-design (design)
+  (medium-free-image-design (slot-value design 'medium) design))
+
+
+;; Drawing protocol
+
+(defgeneric medium-draw-image-design* (medium design x y))
+
+;; Fetching protocol
+
+(defun sheet-rgb-image (sheet &key x y width height)
+  (multiple-value-bind (data alphap)
+      (sheet-rgb-data (port sheet)
+		      sheet
+		      :x x
+		      :y y
+		      :width width
+		      :height height)
+    (destructuring-bind (height width)
+	(array-dimensions data)
+      (make-instance 'rgb-image
+	:width width
+	:height height
+	:data data
+	:alphap alphap))))
+
+(defgeneric sheet-rgb-data (port sheet &key x y width height))
+
+(defmethod draw-design
+    (medium (design rgb-image-design) &rest options
+     &key (x 0) (y 0) &allow-other-keys)
+  (with-medium-options (medium options)
+    (medium-draw-image-design* medium design x y)))
+
 ;; PATTERN is just the an abstract class of all pattern-like design. 
 
 ;; For performance might consider to sort out pattern, which consists
@@ -410,23 +477,17 @@
 (define-protocol-class pattern (design))
 
 (defclass indexed-pattern (pattern)
-  ((array   :initarg :array)
-   (designs :initarg :designs)))
+  ((array   :initarg :array :reader pattern-array)
+   (designs :initarg :designs :reader pattern-designs)))
    
 (defun make-pattern (array designs)
   (make-instance 'indexed-pattern :array array :designs designs))
 
-(defgeneric pattern-width (pattern))
-
 (defmethod pattern-width ((pattern indexed-pattern))
-  (with-slots (array) pattern
-    (array-dimension array 1)))
-
-(defgeneric pattern-height (pattern))
+  (array-dimension (pattern-array pattern) 1))
 
 (defmethod pattern-height ((pattern indexed-pattern))
-  (with-slots (array) pattern
-    (array-dimension array 0)))
+  (array-dimension (pattern-array pattern) 0))
 
 (defclass stencil (pattern)
   ((array :initarg :array)))
@@ -442,6 +503,37 @@
   (with-slots (array) pattern
     (array-dimension array 0)))
 
+;; These methods are included mostly for completeness and are likely
+;; of little use in practice.
+(defmethod pattern-array ((pattern stencil))
+  (let ((array (make-array (list (pattern-height pattern)
+                                 (pattern-width pattern)))))
+    (dotimes (i (pattern-height pattern))
+      (dotimes (j (pattern-width pattern))
+        (setf (aref array i j) (+ (* i (array-dimension array 1)) j))))
+    array))
+
+(defmethod pattern-designs ((pattern stencil))
+  (with-slots (array) pattern
+    (let ((designs (make-array (* (pattern-height pattern)
+                                  (pattern-width pattern)))))
+      (dotimes (i (length designs))
+        (setf (aref designs i) (make-opacity (row-major-aref array i))))
+      array)))
+
+(defclass rgb-pattern (pattern rgb-image-design)
+  ())
+   
+(defmethod pattern-width ((pattern rgb-pattern))
+  (image-width (image pattern)))
+
+(defmethod pattern-height ((pattern rgb-pattern))
+  (image-height (image pattern)))
+
+;; RGB-PATTERNs must be treated specially...
+(defmethod medium-draw-pattern* (medium (pattern rgb-pattern) x y)
+  (medium-draw-image-design* medium pattern x y))
+
 ;;;
 
 (defclass transformed-design (design)
--- /project/mcclim/cvsroot/mcclim/graphics.lisp	2008/01/21 01:26:42	1.60
+++ /project/mcclim/cvsroot/mcclim/graphics.lisp	2008/04/14 16:46:37	1.61
@@ -902,6 +902,23 @@
 			       align-x align-y
 			       toward-x toward-y transform-glyphs))
 
+;;; Some image junk...
+
+(defmethod medium-free-image-design ((sheet sheet-with-medium-mixin) design)
+  (medium-free-image-design (sheet-medium sheet) design))
+
+(defmethod medium-draw-image-design* :before (current-medium design x y)
+  (with-slots (medium medium-data) design
+    (unless (eq medium current-medium)
+      (when medium
+	(medium-free-image-design medium design))
+      (setf medium current-medium)
+      (setf medium-data nil))))
+
+(defmethod medium-draw-image-design*
+    ((medium sheet-with-medium-mixin) design x y)
+  (medium-draw-image-design* (sheet-medium medium) design x y))
+
 ;;;;
 ;;;; DRAW-DESIGN
 ;;;;
@@ -995,6 +1012,14 @@
 
 ;;;;
 
+(defmethod draw-design
+    (medium (design rgb-image-design) &rest options
+     &key (x 0) (y 0) &allow-other-keys)
+  (with-medium-options (medium options)
+    (medium-draw-image-design* medium design x y)))
+
+;;;;
+
 (defmethod draw-design (medium (pattern pattern) &key clipping-region transformation
                         &allow-other-keys)
   (draw-pattern* medium pattern 0 0
@@ -1101,3 +1126,92 @@
                                   :radius-left :radius-right
                                   :radius-top  :radius-bottom))
      args)))
+
+;;; Bitmap images
+;;;
+;;; Based on CLIM 2.2, with an extension permitting the definition of
+;;; new image formats by the user.
+
+(defvar *bitmap-file-readers* (make-hash-table :test 'equalp)
+  "A hash table mapping keyword symbols naming bitmap image
+formats to a function that can read an image of that format. The
+functions will be called with one argument, the pathname of the
+file to be read. The functions should return two values as per
+`read-bitmap-file'.")
+
+(defmacro define-bitmap-file-reader (bitmap-format (&rest args) &body body)
+  "Define a method for reading bitmap images of format
+BITMAP-FORMAT that will be used by `read-bitmap-file' and
+MAKE-PATTERN-FROM-BITMAP-FILE. BODY should return two values as
+per `read-bitmap-file'."
+  `(setf (gethash ,bitmap-format *bitmap-file-readers*)
+         #'(lambda (, at args)
+             , at body)))
+
+(defun bitmap-format-supported-p (format)
+  "Return true if FORMAT is supported by `read-bitmap-file'."
+  (not (null (gethash format *bitmap-file-readers*))))
+
+(define-condition unsupported-bitmap-format (error)
+  ((%format :reader bitmap-format
+            :initarg :bitmap-format
+            :initform (error "The bitmap format must be supplied")
+            :documentation "The bitmap format that cannot be loaded"))
+  (:report (lambda (condition stream)
+             (format
+              stream "Cannot read bitmap of unknown format \"~A\""
+              (bitmap-format condition))))
+  (:documentation "This exception is signalled when
+`read-bitmap-file' is called on an bitmap of a type that no reader
+has been defined for."))
+
+(defun unsupported-bitmap-format (format)
+  "Signal an error of type `unsupported-bitmap-format' for the
+bitmap format `format'."
+  (error 'unsupported-bitmap-format :bitmap-format format))
+
+(defun read-bitmap-file (pathname &key (format :bitmap) (port (find-port)))
+  "Read a bitmap file named by `pathname'. `Port' specifies the
+port that the bitmap is to be used on. `Format' is a keyword
+symbol naming any defined bitmap file format defined by
+`clim-extensions:define-bitmap-file-reader'. Two values are
+returned: a two-dimensional array of pixel values and an array of
+either colors or color names. If the second value is non-NIL, the
+pixel values are assumed to be indexes into this
+array. Otherwise, the pixel values are taken to be RGB values
+encoded in 32 bit unsigned integers, with the three most
+significant octets being the values R, G and B, in order."
+  (declare (ignore port)) ; XXX?
+  (funcall (or (gethash format *bitmap-file-readers*)
+               (unsupported-bitmap-format format))
+           pathname))
+
+(defun make-pattern-from-bitmap-file (pathname &key designs
+                                      (format :bitmap) (port (find-port)))
+  "Read a bitmap file named by `pathname'. `Port' specifies the
+port that the bitmap is to be used on. `Format' is a keyword
+symbol naming any defined bitmap file format defined by
+`clim-extensions:define-bitmap-file-reader'. Two values are
+returned: a two-dimensional array of pixel values and an array of
+either colors or color names. If the second value is non-NIL, the
+pixel values are assumed to be indexes into this
+array. Otherwise, the pixel values are taken to be RGB values
+encoded in 32 bit unsigned integers, with the three most
+significant octets being the values R, G and B, in order."
+  (multiple-value-bind (res read-designs)
+      (read-bitmap-file pathname :format format :port port)
+    (if read-designs
+        (make-pattern res (or designs read-designs))
+        (make-instance 'rgb-pattern :image (make-instance 'rgb-image
+                                            :width (array-dimension res 0)
+                                            :height (array-dimension res 1)
+                                            :data res)))))
+
+(define-bitmap-file-reader :xpm (pathname)
+  (xpm-parse-file pathname))
+
+(define-bitmap-file-reader :pixmap (pathname)
+  (read-bitmap-file pathname :format :xpm))
+
+(define-bitmap-file-reader :pixmap-3 (pathname)
+  (read-bitmap-file pathname :format :xpm))
--- /project/mcclim/cvsroot/mcclim/mcclim.asd	2008/03/28 19:53:19	1.77
+++ /project/mcclim/cvsroot/mcclim/mcclim.asd	2008/04/14 16:46:37	1.78
@@ -219,6 +219,7 @@
   :components ((:file "text-formatting")
                (:file "defresource")
                (:file "presentations")
+               (:file "xpm")
                (:file "bordered-output" :depends-on ("presentations"))
                (:file "table-formatting" :depends-on ("presentations"))
                (:file "input-editing" :depends-on ("presentations" "bordered-output" "table-formatting"))
@@ -362,8 +363,6 @@
    (:file "input-editing-goatee")
    (:file "input-editing-drei")
    (:file "text-editor-gadget")
-   (:file "Extensions/rgb-image" :pathname #.(make-pathname :directory '(:relative "Extensions")
-                                                            :name "rgb-image"))
    (:file "Extensions/tab-layout"
 	  :pathname #.(make-pathname :directory '(:relative "Extensions")
 				     :name "tab-layout"))))
@@ -547,35 +546,6 @@
 		 (:file "Looks/pixie"
                         :pathname #.(make-pathname :directory '(:relative "Looks") :name "pixie" :type "lisp"))))
 
-(defsystem :mcclim-images
-           :depends-on (:clim)
-           :components ((:module "Extensions/Images"
-                         :pathname #.(make-pathname :directory '(:relative "Extensions" "Images"))
-                         :components ((:file "package")
-                                      (:file "images" :depends-on ("package"))
-                                      (:file "image-viewer" :depends-on ("images"))))))
-
-(defmacro support-format (format &rest depends-on)
-  "Generate the ASDF `defsystem' form for a single-file system
-consisting of a file with the name `format' in
-Extensions/Images. It will depend on the ASDF systems listed in
-`depends-on' as well as MCCLIM-IMAGES."
-  `(defsystem ,(intern (format nil "MCCLIM-IMAGES-~A" (string-upcase format))
-                       (find-package :keyword))
-    :depends-on (:mcclim-images , at depends-on)
-    :components
-    ((:file ,format
-      :pathname ,(make-pathname :directory '(:relative "Extensions" "Images")
-                                :name format)))))
-
-(defmacro support-formats (&rest formats)
-  "Generate the ASDF `defsystem' forms for supporting
-`formats'."
-  `(progn ,@(loop for (format . depends-on) in formats
-               collecting `(support-format ,format , at depends-on))))
-
-(support-formats ("gif" :skippy) ("xpm") ("jpeg" :cl-jpeg))
-
 ;;; The actual McCLIM system that people should to use in their ASDF
 ;;; package dependency lists.
 (defsystem :mcclim
--- /project/mcclim/cvsroot/mcclim/package.lisp	2008/02/03 18:49:57	1.67
+++ /project/mcclim/cvsroot/mcclim/package.lisp	2008/04/14 16:46:38	1.68
@@ -1648,6 +1648,8 @@
    #:+list-pane-view+                   ;constant
    #:option-pane-view                   ;class
    #:+option-pane-view+                 ;constant
+   #:pattern-array                      ;generic function (in franz user guide)
+   #:pattern-designs                    ;generic function (in franz user guide)
    #:pointer-input-rectangle            ;function (in franz user guide)
    #:pointer-input-rectangle*           ;function (in franz user guide)
    #:pointer-place-rubber-band-line*    ;function (in franz user guide)
@@ -1657,6 +1659,7 @@
    #:+push-button-view+                 ;constant
    #:radio-box-view                     ;class
    #:+radio-box-view+                   ;class
+   #:read-bitmap-file                   ;function
    #:slider-view                        ;slider-view
    #:+slider-view+                      ;constant
    #:text-editor-view                   ;class
@@ -1963,7 +1966,11 @@
    #:font-face-family
    #:font-face-all-sizes
    #:font-face-scalable-p
-   #:font-face-text-style))
+   #:font-face-text-style
+
+   #:define-bitmap-file-reader
+   #:unsupported-bitmap-format
+   #:bitmap-format))
 
 ;;; Symbols that must be defined by a backend.
 ;;;

--- /project/mcclim/cvsroot/mcclim/mcclim-gif-bitmaps.asd	2008/04/14 16:46:39	NONE
+++ /project/mcclim/cvsroot/mcclim/mcclim-gif-bitmaps.asd	2008/04/14 16:46:39	1.1
;;; -*- Mode: Lisp -*-

;;;  (c) copyright 2008 by 
;;;           Troels Henriksen (athas at sigkill.dk)
;;;
;;; This library is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Library General Public
;;; License as published by the Free Software Foundation; either
;;; version 2 of the License, or (at your option) any later version.
;;;
;;; This library is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; Library General Public License for more details.
;;;
;;; You should have received a copy of the GNU Library General Public
;;; License along with this library; if not, write to the 
;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
;;; Boston, MA  02111-1307  USA.

(cl:defpackage :mcclim-gif-bitmaps.system
  (:use :asdf :cl))

(cl:in-package :mcclim-gif-bitmaps.system)

(defsystem :mcclim-gif-bitmaps
           :description "Support for GIF images in McCLIM bitmap reading functions."
           :depends-on (:mcclim :skippy)
           :components ((:file "gif" :pathname #P"Extensions/Bitmap-formats/gif")))
--- /project/mcclim/cvsroot/mcclim/mcclim-jpeg-bitmaps.asd	2008/04/14 16:46:39	NONE
+++ /project/mcclim/cvsroot/mcclim/mcclim-jpeg-bitmaps.asd	2008/04/14 16:46:39	1.1
;;; -*- Mode: Lisp -*-

;;;  (c) copyright 2008 by 
;;;           Troels Henriksen (athas at sigkill.dk)
;;;
;;; This library is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Library General Public
;;; License as published by the Free Software Foundation; either
;;; version 2 of the License, or (at your option) any later version.
;;;
;;; This library is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; Library General Public License for more details.
;;;
;;; You should have received a copy of the GNU Library General Public
;;; License along with this library; if not, write to the 
;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
;;; Boston, MA  02111-1307  USA.

(cl:defpackage :mcclim-gif-bitmaps.system
  (:use :asdf :cl))

(cl:in-package :mcclim-gif-bitmaps.system)

(defsystem :mcclim-jpeg-bitmaps
           :description "Support for JPEG images in McCLIM bitmap reading functions."
           :depends-on (:mcclim :cl-jpeg)
           :components ((:file "gif" :pathname #P"Extensions/Bitmap-formats/jpeg")))
--- /project/mcclim/cvsroot/mcclim/xpm.lisp	2008/04/14 16:46:39	NONE
+++ /project/mcclim/cvsroot/mcclim/xpm.lisp	2008/04/14 16:46:39	1.1
;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: CLIM-INTERNALS; -*-
;;; ---------------------------------------------------------------------------
;;;     Title: XPM Parser
;;;   Created: 2003-05-25
;;;   Authors: Gilbert Baumann <unk6 at rz.uni-karlsruhe.de>
;;;            Andy Hefner <ahefner at gmail.com>
;;;   License: LGPL (See file COPYING for details).
;;; ---------------------------------------------------------------------------
;;;  (c) copyright 2003 by Gilbert Baumann
;;;  (c) copyright 2006 by Andy Hefner

;;; This library is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Library General Public
;;; License as published by the Free Software Foundation; either
;;; version 2 of the License, or (at your option) any later version.
;;;
;;; This library is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; Library General Public License for more details.
;;;
;;; You should have received a copy of the GNU Library General Public
;;; License along with this library; if not, write to the 
;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
;;; Boston, MA  02111-1307  USA.

(in-package :clim-internals)

;;;; Notes

;;; This is essentially a rewrite/transliteration of Gilbert's original code,
;;; modified to improve performance. This is achieved primarily by using 
;;; read-sequence into an (unsigned-byte 8) array and parsing directly
;;; from this array (the original code read a list of strings using read-line
;;; and further divided these into substrings in various places. It is
;;; substantially faster than the original code, but there are opportunities
;;; to further improve performance by perhaps several times, including:
;;;  - Use an array rather than hash table to resolve color tokens
;;;    (I avoided doing this for now due to a pathological case of a file
;;;     with a small palette but high CPP and sparse color tokens)
;;;  - Stricter type declarations (some but not all of the code assumes cpp<3)
;;;  - In the worst case (photographs), we spent most of our time parsing
;;;    the palette (it may have thousands or millions of entries).
;;;  - For the above case, we should be generating an RGB or RGBA image
;;;    rather than an indexed-pattern (and consing a ton of color objects).
;;;  - People who save photographs in XPM format are morons, so it isn't
;;;    worth optimizing.

;;; Gilbert's Notes:

;; - We lose when the XPM image only specifies colors for say the mono
;;   visual.
;;
;; - We need a little refactoring:
;;
;;   . The list of colors below is now actually the second place we have
;;     that.
;;
;;   . Parsing of #rgb style colors is now the upteens place we have
;;     that in general.
;;
;;   => Put that in utils.lisp and document its interface.
;;
;; - The ASCII-centric approach of XPM makes it suitable for embedding
;;   it into sources files. I want a macro which takes a list of
;;   strings according the XPM format and turns it into a make-pattern
;;   call.
;;
;; - This needs to be incorporated into READ-BITMAP-FILE or what ever
;;   that is called.
;;
;; - We might be interested in the hot spot also.
;;
;; --GB 2003-05-25

;;;; Summary of the File Format

;; [as of the XPM-3.4i documentation by Arnaud Le Hors].

;; | The XPM Format
;; | 
;; | The XPM format presents a C syntax, in order to provide the ability to
;; | include XPM files in C and C++ programs. It is in fact an array of
;; | strings composed of six different sections as follows:
;; | 
;; | /* XPM */
;; | static char* <variable_name>[] = {
;; | <Values>
;; | <Colors>
;; | <Pixels>
;; | <Extensions>
;; | };
;; | 
;; | The words are separated by a white space which can be composed of
;; | space and tabulation characters. The <Values> section is a string
;; | containing four or six integers in base 10 that correspond to: the
;; | pixmap width and height, the number of colors, the number of
;; | characters per pixel (so there is no limit on the number of colors),
;; | and, optionally the hotspot coordinates and the XPMEXT tag if there is
;; | any extension following the <Pixels> section.
;; | 
;; | <width> <height> <ncolors> <cpp> [<x_hotspot> <y_hotspot>] [XPMEXT]
;; | 
;; | The Colors section contains as many strings as there are colors, and
;; | each string is as follows:
;; | 
;; | <chars> {<key> <color>}+
;; | 
;; | Where <chars> is the <chars_per_pixel> length string (not surrounded
;; | by anything) representing the pixels, <color> is the specified color,
;; | and <key> is a keyword describing in which context this color should
;; | be used. Currently the keys may have the following values:
;; | 
;; |     m  for mono visual
;; |     s  for symbolic name
;; |     g4 for 4-level grayscale
;; |     g  for grayscale with more than 4 levels
;; |     c  for color visual
;; | 
;; | Colors can be specified by giving the colorname, a # followed by the
;; | RGB code in hexadecimal, or a % followed by the HSV code (not
;; | implemented). The symbolic name provides the ability of specifying the
;; | colors at load time and not to hardcode them in the file.
;; | 
;; | Also the string None can be given as a colorname to mean
;; | ``transparent''. Transparency is supported by the XPM library by
;; | providing a masking bitmap in addition to the pixmap. This mask can
;; | then be used either as a clip-mask of an Xlib GC, or a shape-mask of a
;; | window using the X11 Nonrectangular Window Shape Extension [XShape]. 
;; | The <Pixels> section is composed by <height> strings of <width> *
;; | <chars_per_pixel> characters, where every <chars_per_pixel> length
;; | string must be one of the previously defined groups in the <Colors>
;; | section.
;; | 
;; | Then follows the <Extensions> section which must be labeled, if not
;; | empty, in the <Values> section as previously described. This section
;; | may be composed by several <Extension> subsections which may be of two
;; | types:
;; | 
;; |   . one stand alone string composed as follows:
;; | 
;; |     XPMEXT <extension-name> <extension-data>
;; | 
;; |   . or a block composed by several strings:
;; | 
;; |     XPMEXT <extension-name>
;; |     <related extension-data composed of several strings>
;; | 
;; | Finally, if not empty, this section must end by the following string:
;; | 
;; | XPMENDEXT
;; | 
;; | Extensions can be used to store any type of data one might want to
;; | store along with a pixmap, as long as they are properly encoded so
;; | they do not conflict with the general syntax. To avoid possible
;; | conflicts with extension names in shared files, they should be
;; | prefixed by the name of the company. This would ensure uniqueness.
;; | 

(deftype xpm-data-array () `(simple-array (unsigned-byte 8) 1))
(deftype array-index ()
  #-sbcl '(integer 0 #.array-dimension-limit)
  #+sbcl 'sb-int:index)
(deftype xpm-pixcode () `(unsigned-byte 24)) ; Bogus upper limit for speed.. =/

(defmacro xpm-over-array ((arrayform elt0 idx0 elt1 idx1 start) &body body)
  (let ((arraysym  (gensym))
        (lengthsym (gensym)))
    `(let* ((,arraysym ,arrayform)
            (,lengthsym (length ,arraysym)))
       (declare (type xpm-data-array ,arraysym)
                (optimize (speed 3)))
       (loop for ,idx0 of-type array-index from ,start below (1- ,lengthsym)
             as ,idx1 of-type array-index = (1+ ,idx0)
             as ,elt0 = (aref ,arraysym ,idx0)
             as ,elt1 = (aref ,arraysym ,idx1)
             do (progn , at body)))))

(declaim (inline xpm-whitespace-p)
         (ftype (function ((unsigned-byte 8)) t) xpm-whitespace-p))
(defun xpm-white-space-p (code)
  (declare (type (unsigned-byte 8) code)
           (optimize (speed 3)))
  (or (= code 32)                       ; #\Space
      (= code 9)                        ; #\Tab
      (= code 10)))                     ; #\Newline

(defun xpm-token-terminator-p (code)
  (declare (type (unsigned-byte 8) code))
  (or (xpm-white-space-p code)
      (= code 34)))                     ; #\"

(defun xpm-token-bounds (data start)
  (xpm-over-array (data b0 start b1 i1 start)
    (when (not (xpm-white-space-p b0))
      (xpm-over-array (data b0 end b1 i1 start)
        (when (xpm-token-terminator-p b0) (return-from xpm-token-bounds (values start end))))
      (error "Unbounded token")))
  (error "Missing token"))

(defun xpm-extract-color-token (data start end)
  (declare (type xpm-data-array data)
           (type array-index start end)
           (optimize (speed 3)))
  (let ((x 0))
    (declare (type xpm-pixcode x))      ; Bah, this didn't help.
    (loop for i from start below end do (setf x (+ (ash x 8) (elt data i))))
    x))

(defun xpm-parse-color (data cpp index)
  (declare (type xpm-data-array data)
           (type (integer 1 4) cpp)     ; ??? =p
           (type array-index index)
           (optimize (speed 3) (safety 0)))
  (let* ((color-token-end (the array-index (+ index cpp)))
         (code (xpm-extract-color-token data index color-token-end))
         (string-end (1- (xpm-exit-string data color-token-end)))
         (color (xpm-parse-color-spec data color-token-end string-end)))
    (declare (type array-index color-token-end string-end)
             (type xpm-pixcode code))
    (unless color
      (error "Color ~S does not parse." (map 'string #'code-char (subseq data color-token-end string-end))))
    (values code color (1+ string-end))))

(declaim (inline xpm-key-p))
(defun xpm-key-p (x)
  (or (= x 109)
      (= x 115)
      (= x 103)
      (= x 99)))

(defun xpm-parse-color-spec (data start end)
  ;; Gilbert says:
  ;; > Lossage!
  ;; > There exist files which say e.g. "c light yellow".
  ;; > How am I supposed to parse that?
  ;; >
  ;; > It seems that the C code just parse everything until one of keys. 
  ;; > That is we do the same although it is quite stupid.
                                        ;(declare (optimize (debug 3) (safety 3)))
  (declare (optimize (speed 3) (space 0) (safety 0))
           (type xpm-data-array data)
           (type array-index start end))
  (let ((original-start start)
        key last-was-key
        color-token-start
        color-token-end)
    (declare (type (or null array-index) color-token-start color-token-end)
             (type (or null (unsigned-byte 8)) key))
    (flet ((find-token (start end)
             (let* ((p1 (position-if-not #'xpm-white-space-p data :start start :end end))
                    (p2 (and p1 (or (position-if #'xpm-white-space-p data :start p1 :end end) end))))
               (values p1 p2)))
           (quux (key color-token-start color-token-end)
             (let ((ink (xpm-parse-single-color key data color-token-start color-token-end)))
               (when ink
                 (return-from xpm-parse-color-spec ink))))
           (stringize () (map 'string #'code-char (subseq data original-start end))))
      (loop
       (multiple-value-bind (p1 p2) (find-token start end)
         (unless p1
           (when last-was-key
             (error "Premature end of color line (no color present after key): ~S." (stringize)))
           (when color-token-start (quux key color-token-start color-token-end))
           (error "We failed to parse a color out of ~S." (stringize)))
         (cond (last-was-key
                (setf last-was-key      nil
                      color-token-start p1
                      color-token-end   p2))              
               ((xpm-key-p (elt data p1))
                (when color-token-start (quux key color-token-start color-token-end))               
                (setf last-was-key t
                      color-token-start nil
                      color-token-end nil
                      key (elt data p1)))
               (t (when (null color-token-start)
                    (error "Color not prefixed by a key: ~S." (stringize)))
                  (setf last-was-key nil)                 
                  (setf color-token-end p2)))
         (setf start p2))))))
                     
(defun xpm-subvector-eql-p (data start end vector) ; FIXME: Guarantee type of input 'vector' and strengthen declaration
  (declare (type xpm-data-array data)
           (type array-index start end)
           (type simple-array vector)
           (optimize (speed 3)))
  (and (= (length vector) (- end start))
       (loop for i from start below end
             do (unless (= (elt data i) (elt vector (- i start))) (return nil))
             return t)))
             
(defun xpm-parse-single-color (key data start end)
  (declare (type xpm-data-array data)
           (type array-index start end)
           (type (unsigned-byte 8) key)
           (optimize (speed 3)))  
  (cond ((and (= key 115)
              (or
               (xpm-subvector-eql-p data start end #|"None"|# #(78 111 110 101))
               (xpm-subvector-eql-p data start end #|"background"|# #(98 97 99 107 103 114 111 117 110 100))))
         clim:+transparent-ink+)
        ((= key 99) (xpm-parse-single-color-2 data start end))
        (t (error "Unimplemented key type ~A" key))))

(declaim (ftype (function ((unsigned-byte 8)) t) xpm-hex-digit-p))
(defun xpm-hex-digit-p (byte)
  (declare (type (unsigned-byte 8) byte)
           (optimize (speed 3)))
  (or (<= 48 byte 57)
      (<= 65 byte 70)

[893 lines skipped]



More information about the Mcclim-cvs mailing list