;;;-*- Mode:Common-Lisp; Package:PICTURES; Base:10 -*-
;;;
;;;
;;;
;;;			 TEXAS INSTRUMENTS INCORPORATED
;;;				  P.O. BOX 149149
;;;			       AUSTIN, TEXAS 78714-9149
;;;
;;; Copyright (C)1987,1988,1989,1990 Texas Instruments Incorporated.
;;;
;;; Permission is granted to any individual or institution to use, copy, modify,
;;; and distribute this software, provided that this complete copyright and
;;; permission notice is maintained, intact, in all copies and supporting
;;; documentation.
;;;
;;; Texas Instruments Incorporated provides this software "as is" without
;;; express or implied warranty.
;;;
;;; Authors: Delmar Hager, James Dutton, Teri Crowe
;;; Contributors: Kerry Kimbrough, Patrick Hogan, Eric Mielke

(in-package "PICTURES")


(export '(
	  gstate-equal
	  make-gstate
	  gstate-value
	  pixelp
	  gstate-foreground
	  gstate-background
	  gstate-dashes
	  gstate-function
	  gstate-line-width
	  gstate-line-style
	  gstate-cap-style
	  gstate-join-style
	  gstate-fill-style
	  gstate-fill-rule
	  gstate-arc-mode
	  gstate-tile
	  gstate-stipple
	  remove-gstate-value
	  gstate-combine
	  gstate-copy
	  clear-gstate 
	  )
	'pictures)


;Private Macro: put-hash
;  A slightly nicer syntax for adding keys to a hash table.

(defmacro pixelp (pixel)
  `(AND (INTEGERP ,pixel) (<=  ,pixel 4294967296)))

(defmacro put-hash (key value table)
  `(setf (gethash ,key ,table) ,value))

(DEFPARAMETER  *gstate-signifigant-value* 15)

(DEFun gstate-equal (gstate1 gstate2)
  (LET ((ga1 (gstate-array gstate1))
	(ga2 (gstate-array gstate2)))
    (DOTIMES (pos *gstate-signifigant-value* t)
      (UNLESS (EQUAL (ELT ga1 pos) (ELT ga2 pos)) (RETURN nil)))))

(defclass gstate ()
  ((gstate-hash	:type		 hash-table
                :initarg	:gstate-hash
                :initform	(MAKE-HASH-TABLE :size 7)
		:accessor         gstate-hash
		:documentation "a data structure of visual attribute/value pairs")
  (gstate-array	:type		array
			:initform	(MAKE-ARRAY 21)
			:accessor         gstate-array
			:documentation "a data structure of visual attribute/value pairs"))
  (:documentation "The object that represents the visual state of a graphic object")
  )

(DEFMACRO with-gstate-value-place (&body body)
  `(let ,*gstate-index*

     ,@body))

(defmethod  gstate-value ((gstate gstate) keyword)
  "print the value of a gstate keyword. Retrun nil if not defined"
  (declare (type keyword keyword))
  (declare (values value))
  (with-slots ((ga gstate-array)) gstate
    (with-gstate-value-place
      (CASE keyword
	(:foreground (ELT ga foreground))
	(:background (ELT ga background))
	(:line-style  (ELT ga line-style))
	(:line-width  (ELT ga line-width))
	(:function (ELT ga function))
	(:ts-x  (ELT ga ts-x))
	(:ts-y  (ELT ga ts-y))
	(:arc-mode  (ELT ga arc-mode))
	(:cap-style (ELT ga cap-style))
	(:clip-mask (ELT ga clip-mask))
	(:clip-x  (ELT ga clip-x))
	(:clip-y  (ELT ga clip-y))
	(:tile  (ELT ga tile))
	(:dash-offset (ELT ga dash-offset))
	(:dashes (ELT ga dashes))
	(:fill-rule (ELT ga fill-rule))
	(:fill-style (ELT ga fill-style))
	(:font (ELT ga font))
	(:join-style (ELT ga join-style))
	(:stipple (ELT ga stipple))
	(:stipple-pix (ELT ga stipple-pix))
	
	(otherwise (VALUES  (GETHASH keyword (slot-value gstate 'gstate-hash))))))))

(defmethod (SETF gstate-value) ((value xlib:pixmap) (gstate gstate) (keyword (eql :foreground)))
  (setf (gstate-value gstate :fill-style) :stippled
	(gstate-value gstate :stipple-pix) value))


;Function: make-gstate
;  Return a new gstate object with the given attribute values and return an alist of value not defined for X window gcontexts

(defun make-gstate (&rest options)
  
  (LET ((gstate (MAKE-INSTANCE 'gstate)) )
    (DO*
      ((options-list options (CDDR options-list))
       ( keyword (FIRST options-list)(FIRST options-list))
       ( value (SECOND options-list)(SECOND options-list)))
      ((EQ options-list nil) gstate)
      (IF  (KEYWORDP keyword)
	   (SETF (gstate-value gstate keyword) value) 
	   (ERROR "%keyword  ~a is not a keyword symbol" keyword)))
   ))




(DEFMETHOD print-object ((gstate gstate) stream)
  "print an alist of the values of the gstate hash table"
  (LET ((ga (gstate-array gstate)))
    (FORMAT stream "[")
    (DOTIMES (pos (LENGTH ga))
      (IF (ELT ga pos)
	  (FORMAT stream "[ ~a ~a  ] "  ;;(first (rassoc pos *gstate-index* :key #'first))
		                          (first (find pos *gstate-index* :key #'second))
					       (ELT ga pos) )
		  ))
    (FORMAT stream "]" ))
    )


;;Method: gstate-value
;;  Return or change the value associated with the KEYWORD in GSTATE.



(defmethod (SETF gstate-value) (value (gstate gstate) keyword )
  "set a gstate value
  if the value is of an incorrect type an error message will be returned
  if the keyword is not defined for X windows gcontext the keyword-value-list will
  be returned as a second value"
  (with-gstate-value-place
    (LET  ((ga (gstate-array gstate)))
      (LET* (( type-value (SECOND (ASSOC keyword *gstate-type-alist*)))	;get the type list from the master list
	     )
	
	(IF type-value				;if the type is "okay" place the keyword pair on the alist
	    (IF (DOLIST ( gtype type-value nil)	;is type type of the keyword value in the type list
		  (IF (TYPEP value gtype) (return t)))
						;(TYPEP value type-value)  ;is type type of the keyword value in the type list
		(CASE keyword
		  (:foreground (SETF (ELT ga foreground) value))
		  (:background (SETF (ELT ga background) value))
		  (:line-style  (SETF (ELT ga line-style) value))
		  (:line-width  (SETF (ELT ga line-width) value))
		  (:function (SETF (ELT ga function) value))
		  (:ts-x  (SETF (ELT ga ts-x) value))
		  (:ts-y  (SETF (ELT ga ts-y) value))
		  (:arc-mode  (SETF (ELT ga arc-mode) value))
		  (:cap-style (SETF (ELT ga cap-style) value))
		  (:clip-mask (SETF (ELT ga clip-mask) value))
		  (:clip-x  (SETF (ELT ga clip-x) value))
		  (:clip-y  (SETF (ELT ga clip-y) value))
		  (:tile  (SETF (ELT ga tile) value))
		  (:dash-offset (SETF (ELT ga dash-offset) value))
		  (:dashes (SETF (ELT ga dashes) value))
		  (:fill-rule (SETF (ELT ga fill-rule) value))
		  (:fill-style (SETF (ELT ga fill-style) value))
		  (:font (SETF (ELT ga font) value))
		  (:join-style (SETF (ELT ga join-style) value))
		  (:stipple (SETF (ELT ga stipple) value))
		  (:stipple-pix (SETF (ELT ga stipple-pix) value))
		  
		  (otherwise (PUT-HASH		;place type keyword pair on the gstate-hashtable
			       keyword value (slot-value gstate 'gstate-hash)))
		  )
		(ERROR  "~%keyword pair ~a ~a has an invalid type~%" keyword value)
		)
	    (PUT-HASH				;place unknown type keyword pair on the gstate-hashtable
	      keyword value (slot-value gstate 'gstate-hash))
	    ))))
    value
    )



(DEFMETHOD (SETF gstate-value) :around (value (gstate gstate) keyword)
  (DECLARE (SPECIAL *gstate-stack*))
  (CALL-next-method)
  (graphic-stack-purge *gstate-stack* )
  (VALUES 
    (WHEN value
      (LET ((ga (slot-value gstate 'gstate-array)))
	(with-gstate-value-place
	  (DECLARE  (IGNORE line-style line-width function ts-x ts-y arc-mode cap-style clip-mask
			    stipple-pix clip-x clip-y dash-offset dashes fill-rule fill-style font join-style ))
	  (COND
	    ((OR (EQ keyword :tile)
		 (EQ keyword :stipple))
	     
	     (IF (SYMBOLP value)
		 (CASE keyword
		   (:tile  (SETF (ELT ga tile) (LIST value (SYMBOL-VALUE value))))
		   (:stipple (SETF (ELT ga stipple) (LIST value (SYMBOL-VALUE value)))))
		 (CASE keyword
		   (:tile  (SETF (ELT ga tile) (LIST (image-name value)  value)))
		   (:stipple (SETF (ELT ga stipple) (LIST (image-name value)  value))))
		 ))
	    
	    ((OR (EQ keyword :foreground)
		 (EQ keyword :background))
	     (COND
	       ((STRINGP value)
		(CASE keyword
		  (:foreground  (SETF (ELT ga foreground) (LIST value value)))
		  (:background (SETF (ELT ga background) (LIST value value)))))
	       ((LISTP value) value)
	       ((pixelp value)
		(CASE keyword
		  (:foreground  (SETF (ELT ga foreground) (LIST value value)))
		  (:background (SETF (ELT ga background) (LIST value value)))))
	       ((pixmap-p value) value)
	       (t
		(CASE keyword
		  (:foreground  (SETF (ELT ga foreground) (LIST value (SYMBOL-VALUE value))))
		  (:background (SETF (ELT ga background) (LIST value (SYMBOL-VALUE value)))))
		)))
	    (t value)))
	))))





(DEFMETHOD (SETF gstate-foreground) :before (foreground (graphic graphic))
  (DECLARE (IGNORE foreground))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD gstate-foreground ((gstate gstate))
  (CADR (gstate-value gstate :foreground)))

(DEFMETHOD gstate-foreground ((graphic graphic))
  (CADR (gstate-value (graphic-gstate graphic) :foreground)))

(DEFMETHOD (SETF gstate-foreground) (foreground (gstate gstate))
  (COND
    ((NOT foreground) (remove-gstate-value gstate :foreground))
    ((STRINGP foreground)
     (CADR (SETF (gstate-value gstate :foreground) foreground)))
    ((NUMBERP foreground)
     (CADR (SETF (gstate-value gstate :foreground) foreground)))
    ((pixelp (SYMBOL-VALUE  foreground))
     (CADR (SETF (gstate-value gstate :foreground) foreground)))
    (t (ERROR "The value ~a for foreground is not of type PIXEL"))))

(DEFMETHOD (SETF gstate-foreground) (foreground (graphic graphic))
  (COND
    ((NOT foreground) (remove-gstate-value (graphic-gstate graphic) :foreground))
    ((STRINGP foreground)
     (CADR (SETF (gstate-value (graphic-gstate graphic) :foreground) foreground)))
    ((NUMBERP foreground)
     (CADR (SETF (gstate-value (graphic-gstate graphic) :foreground) foreground)))
    ((pixelp (SYMBOL-VALUE  foreground))
     (CADR (SETF (gstate-value (graphic-gstate graphic) :foreground) foreground)))
   (t (ERROR "The value ~a for foreground is not of type PIXEL"))))

(DEFMETHOD gstate-background ((gstate gstate))
  (CADR (gstate-value gstate :background)))

(DEFMETHOD gstate-background ((graphic graphic))
  (CADR (gstate-value (graphic-gstate graphic) :background)))

(DEFMETHOD (SETF gstate-background) (background (gstate gstate))
  (COND
    ((NOT background) (remove-gstate-value gstate :background))
    ((STRINGP background)
     (CADR (SETF (gstate-value gstate :background) background)))
    ((NUMBERP background)
     (CADR (SETF (gstate-value gstate :background) background)))
    ((pixelp (SYMBOL-VALUE  background))
     (CADR (SETF (gstate-value gstate :background) background)))
   (t (ERROR "The value ~a for background is not of type PIXEL"))))

(DEFMETHOD (SETF gstate-background) :before (background (graphic graphic))
  (DECLARE (IGNORE background))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-background) (background (graphic graphic))
  (COND
    ((NOT background) (remove-gstate-value (graphic-gstate graphic) :background))
    ((STRINGP background)
     (CADR (SETF (gstate-value (graphic-gstate graphic) :background) background)))
    ((NUMBERP background)
     (CADR (SETF (gstate-value (graphic-gstate graphic) :background) background)))
    ((pixelp (SYMBOL-VALUE  background))
     (CADR (SETF (gstate-value (graphic-gstate graphic) :background) background)))
   (t (ERROR "The value ~a for background is not of type PIXEL")))
  )

(DEFMETHOD gstate-dashes ((gstate gstate))
  (gstate-value gstate :dashes))

(DEFMETHOD gstate-dashes ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :dashes))

(DEFMETHOD (SETF gstate-dashes) (dashes (gstate gstate))
  (IF (OR (listp dashes) (ARRAYP dashes) (AND (NUMBERP dashes) (< -1 dashes 255)))
      (SETF (gstate-value gstate :dashes) dashes)
      (UNLESS dashes (remove-gstate-value gstate :dashes))))

(DEFMETHOD (SETF gstate-dashes) :before (dashes (graphic graphic))
  (DECLARE (IGNORE dashes))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-dashes) (dashes (graphic graphic))
  (IF (OR (listp dashes) (ARRAYP dashes) (AND (NUMBERP dashes) (< -1 dashes 255))) 
      (SETF (gstate-value (graphic-gstate graphic) :dashes) dashes)
      (UNLESS dashes (remove-gstate-value (graphic-gstate graphic) :dashes))))


(DEFMETHOD gstate-function ((gstate gstate))
  (gstate-value gstate :function))

(DEFMETHOD gstate-function ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :function))

(DEFMETHOD (SETF gstate-function) (function (gstate gstate))
  (IF (OR (MEMBER function '(boole-1 boole-2 boole-and boole-andc1 booleandc2 boole-xor
				 boole-c1 boole-c2 boole-clr boole-eqv boole-ior
				 boole-nand boole-nor boole-orc1 boole-orc2 boole-set))
	  (< -1 function 17))
      (SETF (gstate-value gstate :function) function)
      (UNLESS function (remove-gstate-value gstate :function))))

(DEFMETHOD (SETF gstate-function) :before (function (graphic graphic))
  (DECLARE (IGNORE function))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-function) (function (graphic graphic))
  (IF (OR (MEMBER function '(boole-1 boole-2 boole-and boole-andc1 booleandc2 boole-xor
				 boole-c1 boole-c2 boole-clr boole-eqv boole-ior
				 boole-nand boole-nor boole-orc1 boole-orc2 boole-set))
	  (< -1 function 17))
      (SETF (gstate-value (graphic-gstate graphic) :function) function)
      (UNLESS function (remove-gstate-value (graphic-gstate graphic) :function))))

(DEFMETHOD gstate-line-width ((gstate gstate))
  (gstate-value gstate :line-width))

(DEFMETHOD gstate-line-width ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :line-width))

(DEFMETHOD (SETF gstate-line-width) (line-width (gstate gstate))
  (IF (AND  (NUMBERP line-width)(<= 0 line-width))
      (SETF (gstate-value gstate :line-width) line-width)
      (UNLESS line-width (remove-gstate-value gstate :line-width))))

(DEFMETHOD (SETF gstate-line-width) :before (line-width (graphic graphic))
  (DECLARE (IGNORE line-width))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-line-width) (line-width (graphic graphic))
  (extent-changed graphic)
  (IF (AND  (NUMBERP line-width)(<= 0 line-width))
      (SETF (gstate-value (graphic-gstate graphic) :line-width) line-width)
      (UNLESS line-width (remove-gstate-value (graphic-gstate graphic) :line-width)))
  )

(DEFMETHOD gstate-font ((gstate gstate))
  (gstate-value gstate :font))

(DEFMETHOD (SETF gstate-font) (font (gstate gstate))
  (IF (font-p font)
      (SETF (gstate-value gstate :font) font)
      (UNLESS font (remove-gstate-value gstate :font))))

(DEFMETHOD (SETF gstate-font) (font (graphic graphic))
  (IF (font-p font)
      (SETF (gstate-value (graphic-gstate graphic) :font) font)))

(DEFMETHOD gstate-font ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :font))

(DEFMETHOD gstate-line-style ((gstate gstate))
  (gstate-value gstate :line-style))

(DEFMETHOD gstate-line-style ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :line-style))

(DEFMETHOD (SETF gstate-line-style) (line-style (gstate gstate))
  (IF (MEMBER line-style '(:solid :dash :double-dash))
      (SETF (gstate-value gstate :line-style) line-style)
      (UNLESS line-style (remove-gstate-value gstate :line-style))))


(DEFMETHOD (SETF gstate-line-style) :before (line-style (graphic graphic))
  (DECLARE (IGNORE line-style))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-line-style) (line-style (graphic graphic))
  (IF (MEMBER line-style '(:solid :dash :double-dash))
      (SETF (gstate-value (graphic-gstate graphic) :line-style) line-style)
      (UNLESS line-style (remove-gstate-value (graphic-gstate graphic) :line-style))))

(DEFMETHOD gstate-cap-style ((gstate gstate))
  (gstate-value gstate :cap-style))

(DEFMETHOD gstate-cap-style ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :cap-style))

(DEFMETHOD (SETF gstate-cap-style) (cap-style (gstate gstate))
  (IF (MEMBER cap-style '(:not-last :butt :round :projecting))
      (SETF (gstate-value gstate :cap-style) cap-style)
      (UNLESS cap-style (remove-gstate-value gstate :cap-style))))

(DEFMETHOD (SETF gstate-cap-style) :before (cap-style (graphic graphic))
  (DECLARE (IGNORE cap-style))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-cap-style) (cap-style (graphic graphic))
  (IF (MEMBER cap-style '(:not-last :butt :round :projecting))
      (SETF (gstate-value (graphic-gstate graphic) :cap-style) cap-style)
      (UNLESS cap-style (remove-gstate-value (graphic-gstate graphic) :cap-style))))

(DEFMETHOD gstate-join-style ((gstate gstate))
  (gstate-value gstate :join-style))

(DEFMETHOD gstate-join-style ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :join-style))

(DEFMETHOD (SETF gstate-join-style) (join-style (gstate gstate))
  (IF (MEMBER join-style '(:miter :round :bevel))
      (SETF (gstate-value gstate :join-style) join-style)
      (UNLESS join-style (remove-gstate-value gstate :join-style))))

(DEFMETHOD (SETF gstate-join-style) :before (join-style (graphic graphic))
  (DECLARE (IGNORE join-style))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-join-style) (join-style (graphic graphic))
  (IF (MEMBER join-style '(:miter :round :bevel))
      (SETF (gstate-value (graphic-gstate graphic) :join-style) join-style)
      (UNLESS join-style (remove-gstate-value (graphic-gstate graphic) :join-style))))

(DEFMETHOD gstate-fill-style ((gstate gstate))
  (gstate-value gstate :fill-style))

(DEFMETHOD gstate-fill-style ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :fill-style))

(DEFMETHOD (SETF gstate-fill-style) (fill-style (gstate gstate))
  (IF (MEMBER fill-style '(:solid :tiled :opaque-stippled :stippled))
      (SETF (gstate-value gstate :fill-style) fill-style)
      (UNLESS fill-style (remove-gstate-value gstate :fill-style))))

(DEFMETHOD (SETF gstate-fill-style) :before (fill-style (graphic graphic))
  (DECLARE (IGNORE fill-style))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-fill-style) (fill-style (graphic graphic))
  (IF (MEMBER fill-style '(:solid :tiled :opaque-stippled :stippled))
      (SETF (gstate-value (graphic-gstate graphic) :fill-style) fill-style)
      (UNLESS fill-style (remove-gstate-value (graphic-gstate graphic) :fill-style))))

(DEFMETHOD gstate-fill-rule ((gstate gstate))
  (gstate-value gstate :fill-rule))

(DEFMETHOD gstate-fill-rule ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :fill-rule))

(DEFMETHOD (SETF gstate-fill-rule) (fill-rule (gstate gstate))
  (IF (MEMBER fill-rule '(:even-odd :winding))
      (SETF (gstate-value gstate :fill-rule) fill-rule)
      (UNLESS fill-rule (remove-gstate-value gstate :fill-rule))))

(DEFMETHOD (SETF gstate-fill-rule) :before (fill-rule (graphic graphic))
  (DECLARE (IGNORE fill-rule))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-fill-rule) (fill-rule (graphic graphic))
  (IF (MEMBER fill-rule '(:even-odd :winding))
      (SETF (gstate-value (graphic-gstate graphic) :fill-rule) fill-rule)
      (UNLESS fill-rule (remove-gstate-value (graphic-gstate graphic) :fill-rule))))

(DEFMETHOD gstate-arc-mode ((gstate gstate))
  (gstate-value gstate :arc-mode))

(DEFMETHOD gstate-arc-mode ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :arc-mode))

(DEFMETHOD (SETF gstate-arc-mode) (arc-mode (gstate gstate))
  (IF (MEMBER arc-mode '(:chord :pie-slice))
      (SETF (gstate-value gstate :arc-mode) arc-mode)
      (UNLESS arc-mode (remove-gstate-value gstate :arc-mode))))

(DEFMETHOD (SETF gstate-arc-mode) (arc-mode (graphic graphic))
  (IF (MEMBER arc-mode '(:chord :pie-slice))
      (SETF (gstate-value (graphic-gstate graphic) :arc-mode) arc-mode)
      (UNLESS arc-mode (remove-gstate-value (graphic-gstate graphic) :arc-mode))))

(DEFMETHOD gstate-tile ((gstate gstate))
  (CADR  (gstate-value gstate :tile)))

(DEFMETHOD gstate-tile ((graphic graphic))
  (CAdR  (gstate-value (graphic-gstate graphic) :tile)))

(DEFMETHOD (SETF gstate-tile) (tile (gstate gstate))
  (IF  tile
       (LET ((value (IF (SYMBOLP tile)
			(SYMBOL-VALUE tile)
			tile)))
	 (IF (OR (image-x-p value)(image-xy-p value)(image-z-p value))
	     (CADR (SETF (gstate-value gstate :tile)
			 tile))
	     (ERROR "the symbol ~a does not evaluate to an IMAGE" tile)))
       (UNLESS tile (remove-gstate-value gstate :tile))))

(DEFMETHOD (SETF gstate-tile) :before (tile (graphic graphic))
  (DECLARE (IGNORE tile))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-tile) (tile (graphic graphic))
  (IF  tile
       (LET ((value (IF (SYMBOLP tile)
			(SYMBOL-VALUE tile)
			tile)))
	 (IF (OR (image-x-p value)(image-xy-p value)(image-z-p value))
	     (CADR (SETF (gstate-value (graphic-gstate graphic) :tile)
			 tile))
	     (ERROR "the symbol ~a does not evaluate to an IMAGE" tile)))
       (UNLESS tile (remove-gstate-value (graphic-gstate graphic) :tile))))

(DEFMETHOD gstate-stipple ((gstate gstate))
  (CADR (gstate-value gstate :stipple)))

(DEFMETHOD gstate-stipple ((graphic graphic))
  (CADR (gstate-value (graphic-gstate graphic) :stipple)))

(DEFMETHOD (SETF gstate-stipple) (stipple (gstate gstate))
  (IF  stipple
       (LET ((value (IF (SYMBOLP stipple)
			(SYMBOL-VALUE stipple)
			stipple)))
	 (IF (OR (image-x-p value)(image-xy-p value)(image-z-p value))
	     (IF (= (image-depth value) 1)
		 (CADR (SETF (gstate-value gstate :stipple)
		       stipple))
		 (ERROR "image depth of ~a is greater than 1" (image-depth value)))
	     (ERROR "the symbol ~a does not evaluate to an IMAGE" stipple)))
       (UNLESS stipple (remove-gstate-value gstate :stipple))))

(DEFMETHOD (SETF gstate-stipple) :before (stipple (graphic graphic))
  (DECLARE (IGNORE stipple))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-stipple) (stipple (graphic graphic))
  (IF  stipple
       (LET ((value (IF (SYMBOLP stipple)
			(SYMBOL-VALUE stipple)
			stipple)))
	 (IF (OR (image-x-p value)(image-xy-p value)(image-z-p value))
	     (IF (= (image-depth value) 1)
		 (CADR (SETF (gstate-value (graphic-gstate graphic) :stipple)
		       stipple))
		 (ERROR "image depth of ~a is greater than 1" (image-depth value)))
	     (ERROR "the symbol ~a does not evaluate to an IMAGE" stipple)))
       (UNLESS stipple (remove-gstate-value (graphic-gstate graphic) :stipple))))

(DEFMETHOD gstate-clip-mask ((gstate gstate))
  (gstate-value gstate :clip-mask))

(DEFMETHOD gstate-clip-mask ((graphic graphic))
  (gstate-value (graphic-gstate graphic) :clip-mask))

(DEFMACRO sequencep (value)
  `(OR (LISTP ,value) (ARRAYP ,value)))

(DEFMETHOD (SETF gstate-clip-mask) (clip-mask (gstate gstate))
  (IF (OR (MEMBER clip-mask '(:none))(sequencep clip-mask))
      (SETF (gstate-value gstate :clip-mask) clip-mask)
      (UNLESS clip-mask (remove-gstate-value gstate :clip-mask))))

(DEFMETHOD (SETF gstate-clip-mask) :before (clip-mask (graphic graphic))
  (DECLARE (IGNORE clip-mask))
  (UNLESS  (slot-value graphic 'gstate)
    (SETF (slot-value graphic 'gstate) (make-gstate))))

(DEFMETHOD (SETF gstate-clip-mask) (clip-mask (graphic graphic))
  (IF (OR (MEMBER clip-mask '(:none))(sequencep clip-mask))
      (SETF (gstate-value (graphic-gstate graphic) :clip-mask) clip-mask)
      (UNLESS clip-mask (remove-gstate-value (graphic-gstate graphic) :clip-mask))))


(DEFMETHOD remove-gstate-value ((gstate gstate) keyword)
  (with-slots ((ga gstate-array) gstate-hash) gstate
    (with-gstate-value-place
      (CASE keyword
	(:foreground (SETF (ELT ga foreground) nil))
	(:background (SETF (ELT ga background) nil))
	(:line-style  (SETF (ELT ga line-style) nil))
	(:line-width  (SETF (ELT ga line-width) nil))
	(:function (SETF (ELT ga function) nil))
	(:ts-x  (SETF (ELT ga ts-x) nil))
	(:ts-y  (SETF (ELT ga ts-y) nil))
	(:arc-mode  (SETF (ELT ga arc-mode) nil))
	(:cap-style (SETF (ELT ga cap-style) nil))
	(:clip-mask (SETF (ELT ga clip-mask) nil))
	(:clip-x  (SETF (ELT ga clip-x) nil))
	(:clip-y  (SETF (ELT ga clip-y) nil))
	(:tile  (SETF (ELT ga tile) nil))
	(:dash-offset (SETF (ELT ga dash-offset) nil))
	(:dashes (SETF (ELT ga dashes) nil))
	(:fill-rule (SETF (ELT ga fill-rule) nil))
	(:fill-style (SETF (ELT ga fill-style) nil))
	(:font (SETF (ELT ga font) nil))
	(:join-style (SETF (ELT ga join-style) nil))
	(:stipple (SETF (ELT ga stipple) nil))
	(:stipple-pix (SETF (ELT ga stipple-pix) nil))
	
	(otherwise (REMHASH keyword gstate-hash))
	))))

;Private Variable: temp-gstate
;  Used in gstate-combine to hold temporary result
(DEFPARAMETER  *temp-gstate* (make-gstate))


;Function: gstate-combine
;  Combine GSTATE-1 with GSTATE-2 and put the result in RESULT.  If
;  RESULT is not given, then the result replaces GSTATE-2.  If both
;  GSTATE-2 and RESULT are nil, then a new gstate is created to hold the
;  result.  A nil gstate represents the empty gstate.  The new value of RESULT
;  is returned.

(defun gstate-combine (gstate-1 gstate-2
                         &optional (result gstate-2))
  (cond ((not gstate-1)				; G-1 is empty
         (gstate-copy gstate-2 result))			;   Just use G-2
        ((not gstate-2)				; G-2 is empty
         (gstate-copy gstate-1 result))			;   Just use G-1
        ((eq gstate-2 result)				; G-2 and RESULT are the same
         (combine-into gstate-1 result))		;   Combine G-1 into RESULT
        ((eq gstate-1 result)				; G-1 and RESULT are the same
         (gstate-copy gstate-1 *temp-gstate*)		;   Use a temporary
         (gstate-copy gstate-2 result)
         (combine-into *temp-gstate* result))		;   Combine temp into RESULT
        (t                                              ; Otherwise, combine G-1 and G-2 into RESULT
        (gstate-copy gstate-2 result)			;   Start with G-2
         (combine-into gstate-1 result))))		;   Combine G-1 into RESULT


;Function: gstate-copy
;  Copy GSTATE-1 into GSTATE-2.  Either or both can be nil.  Return the new
;  GSTATE-2.

					; Return G-2

(DEFMACRO clear-gstate (gstate)
  `(PROGN
    (dotimes (var (LENGTH (gstate-array ,gstate)))
      (SETF (ELT (gstate-array ,gstate) var) nil))
    (clrhash (gstate-hash ,gstate))))

(defun  gstate-copy ( gstate-1 gstate-2)
  (declare (type (or null gstate) gstate-1 gstate-2))
  (declare (values gstate))

  (cond ((eq gstate-1 gstate-2))			; They are already identical!
        (gstate-1					; G-1 is not the empty gstate
         (if gstate-2
	     (clear-gstate gstate-2)		;   Must clean out G-2 first
             (setf gstate-2 (make-gstate)))		;   Must make G-2 first
         (combine-into gstate-1 gstate-2))		;   Combine G-1 into G-2
        (t						; G-1 is the empty gstate
         (if gstate-2
	     (clear-gstate gstate-2)        ;   Make G-2 empty as well
             (setf gstate-2 (make-gstate)))))		;   Or create one if not there yet

  gstate-2)



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Private Function: combine-into
;  Combine the hash table entries of GSTATE-1 into those of GSTATE-2.
;  Return the result GSTATE-2



(DEFMETHOD combine-into ((gstate-1 gstate) (gstate-2 gstate))

  (maphash #'(lambda (keyword value)		;   Combine G-1 into G-2
               (PUT-HASH keyword value (gstate-hash gstate-2)))
           (gstate-hash gstate-1))
  (dotimes (var (LENGTH (gstate-array gstate-2)))
     
		 (UNLESS (ELT (gstate-array gstate-2) var)
		   (SETF (ELT (gstate-array gstate-2) var)(ELT (gstate-array gstate-1) var))))
  gstate-2)



(DEFMETHOD combine-into ((gstate-1 t) (gstate-2 gstate))

  gstate-2)

(DEFMETHOD combine-into ((gstate-1 t) (gstate-2 t))

  gstate-2)

(DEFMETHOD combine-into ((gstate-1 gstate) (gstate-2 t))
  (LET ((gstate (make-gstate)))
    (maphash #'(lambda (keyword value)		;   Combine G-1 into G-2
		 (PUT-HASH keyword value (gstate-hash gstate)))
	     (gstate-hash gstate-1))
    (dotimes (var (LENGTH (gstate-array gstate)))
      
      (UNLESS (ELT (gstate-array gstate) var)
	(SETF (ELT (gstate-array gstate) var)(ELT (gstate-array gstate-1) var))))
    
    gstate))









