Moved AIMAGE drawing routines into McCLIM.
[closure-html.git] / src / renderer / clim-device.lisp
blobaf9cc74644033b66fc06708af5f67e3659ce7dd1
1 ;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: CLOSURE/CLIM-DEVICE; -*-
2 ;;; ---------------------------------------------------------------------------
3 ;;; Title: CLIM device for the renderer
4 ;;; Created: ???
5 ;;; Author: Gilbert Baumann <gilbert@base-engineering.com>
6 ;;; License: MIT style (see below)
7 ;;; ---------------------------------------------------------------------------
8 ;;; (c) copyright 2005 by Gilbert Baumann
10 ;;; Permission is hereby granted, free of charge, to any person obtaining
11 ;;; a copy of this software and associated documentation files (the
12 ;;; "Software"), to deal in the Software without restriction, including
13 ;;; without limitation the rights to use, copy, modify, merge, publish,
14 ;;; distribute, sublicense, and/or sell copies of the Software, and to
15 ;;; permit persons to whom the Software is furnished to do so, subject to
16 ;;; the following conditions:
17 ;;;
18 ;;; The above copyright notice and this permission notice shall be
19 ;;; included in all copies or substantial portions of the Software.
20 ;;;
21 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 ;;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 ;;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 ;;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 ;;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 (in-package :CLOSURE/CLIM-DEVICE)
31 (defclass clim-device ()
32 ((medium :accessor clim-device-medium :initarg :medium)
33 (font-database :initform nil)
34 (zoom-factor :initform gui:*zoom-factor* :initarg :zoom-factor)))
36 (defmethod device-dpi ((device clim-device))
37 (with-slots (zoom-factor) device
38 (* (graft-pixels-per-inch (graft (clim-device-medium device)))
39 zoom-factor)))
41 (defmethod device-font-ascent ((device clim-device) font)
42 (text-style-ascent font (clim-device-medium device))
45 (defmethod device-font-descent ((device clim-device) font)
46 (text-style-descent font (clim-device-medium device))
49 (defmethod device-font-underline-position ((self clim-device) font)
53 (defmethod device-font-underline-thickness ((self clim-device) font)
57 (defmethod device-font-has-glyph-p ((self clim-device) font code-point)
58 (<= 0 code-point 255) ;hmm
61 (defresource one-character-string (char)
62 :constructor (make-string 1)
63 :initializer (setf (aref one-character-string 0) char)
64 :matcher t)
66 (defmethod device-font-glyph-width ((self clim-device) font code-point)
67 (using-resource (string one-character-string (code-char code-point))
68 (text-size (clim-device-medium self) string :text-style font)))
70 (defmethod scale-font-desc ((self clim-device) font-desc size)
71 font-desc)
73 (defmethod device-realize-font-desc ((self clim-device) font-desc)
74 (font-desc-ddp font-desc))
76 (defmethod device-font-database ((self clim-device))
77 (with-slots (font-database) self
78 (or font-database
79 (setf font-database
80 (let ((fdb (make-font-database ;xxx
81 :cache (make-hash-table :test #'equal)
82 :device self)))
83 (loop
84 for family in '("Times" "Helvetica" "Courier")
85 for clim-family in '(:serif :sans-serif :fix)
86 for size-adjust in '(-2 -2 -2) do
87 (loop
88 for weight+style in '((400 :normal) (400 :italic) (700 :normal) (700 :italic))
89 for clim-face in '(:roman :italic :bold (:bold :italic)) do
90 (loop
91 for size in '(8 10 12 14 18 24) do
92 (font-database-relate fdb
93 (make-font-desc
94 :family family
95 :weight (first weight+style)
96 :style (second weight+style)
97 :size (+ size size-adjust)
98 :ddp (make-text-style clim-family clim-face size)
99 :charset (ws/charset:find-charset :iso-8859-1) ;xxx
100 )))))
101 fdb)))))
103 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
105 (defmethod scale-font-desc ((self clim-device) font-desc size)
106 (let ((r (r2::copy-font-desc font-desc)))
107 (incf size
108 (if (eql (nth-value 0 (text-style-components (font-desc-ddp r)))
109 :sans-serif)
110 0 ;;-2
112 (setf (font-desc-size r) size
113 (font-desc-ddp r) (make-text-style (nth-value 0 (text-style-components (font-desc-ddp r)))
114 (nth-value 1 (text-style-components (font-desc-ddp r)))
115 size))
118 (defmethod device-realize-font-desc ((self clim-device) font-desc)
119 (font-desc-ddp font-desc))
121 (defmethod device-font-database ((self clim-device))
122 (with-slots (font-database) self
123 (or font-database
124 (setf font-database
125 (let ((fdb (make-font-database ;xxx
126 :cache (make-hash-table :test #'equal)
127 :device self)))
128 (loop
129 for family in '("Times" "Helvetica" "Courier")
130 for clim-family in '(:serif :sans-serif :fix)
131 for size-adjust in '(-2 0 -2) do
132 (loop
133 for weight+style in '((400 :normal) (400 :italic) (700 :normal) (700 :italic))
134 for clim-face in '(:roman :italic :bold (:bold :italic)) do
135 (font-database-relate fdb
136 (make-font-desc
137 :family family
138 :weight (first weight+style)
139 :style (second weight+style)
140 :size 0
141 :ddp (make-text-style clim-family clim-face 12)
142 :charset (ws/charset:find-charset :iso-8859-1) ;xxx
143 ))))
144 fdb)))))
147 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
150 (defparameter *buffer* (make-array 1000))
152 (defun resolve-background-position (spec image-dim box-dim)
153 (cond ((css:percentage-p spec)
154 (let ((i (r2::maybe-resolve-percentage spec image-dim))
155 (b (r2::maybe-resolve-percentage spec box-dim)))
156 (- b i)))
157 ((null spec)
158 (warn "~S: null spec." 'resolve-background-position)
161 spec)))
163 ;; newer definition below
164 ;;;(defun background-pixmap+mask (document drawable bg)
165 ;;; (cond ((r2::background-%pixmap bg)
166 ;;; (values (r2::background-%pixmap bg)
167 ;;; (r2::background-%mask bg)))
168 ;;; (t
169 ;;; (setf (r2::background-%pixmap bg) :none)
170 ;;; (funcall ;;r2::run-process-on-behalf-of-document document
171 ;;; (lambda ()
172 ;;; (let ((aimage (clue-gui2::aimage-from-url document (r2::background-image bg))))
173 ;;; (cond ((eq aimage :error)
174 ;;; (setf (r2::background-%pixmap bg) :none)
175 ;;; (values (r2::background-%pixmap bg)
176 ;;; (r2::background-%mask bg)))
177 ;;; (t
178 ;;; (let ((pm (ws/x11::aimage->pixmap+mask drawable aimage)))
179 ;;; (setf (r2::background-%pixmap bg) (car pm)
180 ;;; (r2::background-%mask bg) (cadr pm))
181 ;;; #+NIL
182 ;;; (clue-gui2::gui-post
183 ;;; nil
184 ;;; ;; we do it the hard way via an exposure round trip.
185 ;;; 'xlib:clear-area
186 ;;; drawable
187 ;;; :exposures-p t))))))
188 ;;; ;;:name "Lazy Document background fetch."
189 ;;; )
190 ;;; (values (r2::background-%pixmap bg)
191 ;;; (r2::background-%mask bg)))))
193 ;; apparently unused --dfl
194 ;;;(defmethod update-lazy-object (document (self null))
195 ;;; nil)
197 (defun map-region-rectangles (fun region)
198 (clim:map-over-region-set-regions
199 (lambda (r)
200 (apply fun (mapcar #'round
201 (multiple-value-list (clim:rectangle-edges* r)))))
202 region
203 :normalize :y-banding))
205 (defun region-to-x11-rectangle-list (region)
206 (let ((res nil))
207 (map-region-rectangles (lambda (x1 y1 x2 y2)
208 (push (- y2 y1) res)
209 (push (- x2 x1) res)
210 (push y1 res)
211 (push x1 res))
212 region)
213 (mapcar #'round res)))
215 (defun region-from-x11-rectangle-list (rectangle-sequence)
216 (let ((res clim:+nowhere+))
217 (do ((q rectangle-sequence (nthcdr 4 q)))
218 ((endp q))
219 (setf res (region-union res
220 (make-rectangle* (first q) (second q)
221 (+ (first q) (third q))
222 (+ (second q) (fourth q))))))
223 res))
225 ;; apparently unused --dfl
227 ;;;(defun background-pixmap+mask (document drawable bg)
228 ;;; (cond ((r2::background-%pixmap bg)
229 ;;; ;; already there
230 ;;; (values (r2::background-%pixmap bg)
231 ;;; (r2::background-%mask bg)))
232 ;;; (t
233 ;;; (let ((aimage #+NIL(r2::document-fetch-image document nil (r2::background-image bg))
234 ;;; (r2::url->aimage document (r2::background-image bg) nil)
235 ;;; ))
236 ;;; ;; arg, jetzt haben wir wieder broken images
237 ;;; (cond ((eql nil aimage)
238 ;;; (values :none))
239 ;;; (t
240 ;;; (cond ((eq aimage :error)
241 ;;; (setf (r2::background-%pixmap bg) :none) )
242 ;;; (t
243 ;;; (let ((pm (ws/x11::aimage->pixmap+mask drawable aimage)))
244 ;;; (setf (r2::background-%pixmap bg) (car pm)
245 ;;; (r2::background-%mask bg) (cadr pm)))))
246 ;;; (values (r2::background-%pixmap bg)
247 ;;; (r2::background-%mask bg)))))) ))
249 (defun ws/x11::x11-put-pixmap-tiled (drawable ggc pixmap mask x y w h &optional (xo 0) (yo 0))
250 (cond ((null mask) ;; xxx
251 (xlib:with-gcontext (ggc :exposures :off
252 :fill-style :tiled
253 :tile pixmap
254 :ts-x xo
255 :ts-y yo)
256 ;;mask wird momentan noch ignoriert!
257 (xlib:draw-rectangle drawable ggc x y w h t)))
259 (let* ((old-clip-mask (car (or (ignore-errors (list (xlib:gcontext-clip-mask ggc)))
260 (list :none))))
261 (clip-region (let ((q old-clip-mask))
262 (if (consp q)
263 (region-from-x11-rectangle-list q)
264 +everywhere+)))
265 (paint-region (region-intersection
266 clip-region
267 (make-rectangle* x y (+ x w) (+ y h)))) )
268 ;; There is a bug in CLX wrt to clip-x / clip-y
269 ;; Turning off caching helps
270 (setf (xlib:gcontext-cache-p ggc) nil)
272 ;; we have to do our own clipping here.
273 (let ((iw (xlib:drawable-width pixmap))
274 (ih (xlib:drawable-height pixmap)))
275 (loop for i from (floor (- x xo) iw) to (ceiling (- (+ x w) (+ xo iw)) iw)
277 (loop for j from (floor (- y yo) ih) to (ceiling (- (+ y h) (+ yo ih)) ih)
279 (let ((rect (make-rectangle*
280 (+ xo (* i iw))
281 (+ yo (* j ih))
282 (+ (+ xo (* i iw)) iw)
283 (+ (+ yo (* j ih)) ih))))
284 (map-region-rectangles
285 (lambda (rx0 ry0 rx1 ry1)
286 (xlib:with-gcontext (ggc :exposures :off
287 :fill-style :tiled
288 :tile pixmap
289 :clip-mask mask
290 :clip-x (+ xo (* i iw))
291 :clip-y (+ yo (* j ih))
292 :ts-x xo
293 :ts-y yo)
294 (xlib:draw-rectangle drawable ggc
295 rx0 ry0 (max 0 (- rx1 rx0)) (max 0 (- ry1 ry0))
296 t)))
297 (region-intersection paint-region rect))))) )
298 ;; turn on caching again (see above)
299 (setf (xlib:gcontext-cache-p ggc) t)
301 ;; and xlib:with-gcontext also is broken!
302 (setf (xlib:gcontext-clip-mask ggc) old-clip-mask)))))
304 #+emarsden
305 #.((lambda (x)
306 #+:CMU `(eval ',x) ;compiler bug
307 #-:CMU x)
308 '(defun ws/x11::x11-put-pixmap-tiled (drawable ggc pixmap mask x y w h &optional (xo 0) (yo 0))
309 (cond ((null mask) ;; xxx
310 (xlib:with-gcontext (ggc :exposures :off
311 :fill-style :tiled
312 :tile pixmap
313 :ts-x xo
314 :ts-y yo)
315 ;;mask wird momentan noch ignoriert!
316 (xlib:draw-rectangle drawable ggc x y w h t)))
318 (let* ((old-clip-mask (car (or (ignore-errors (list (xlib:gcontext-clip-mask ggc)))
319 (list :none))))
320 (clip-region (let ((q old-clip-mask))
321 (if (consp q)
322 (region-from-x11-rectangle-list q)
323 +everywhere+)))
324 (paint-region (region-intersection
325 clip-region
326 (make-rectangle* x y (+ x w) (+ y h)))) )
327 ;; There is a bug in CLX wrt to clip-x / clip-y
328 ;; Turning off caching helps
329 (setf (xlib:gcontext-cache-p ggc) nil)
331 ;; we have to do our own clipping here.
332 (let ((iw (xlib:drawable-width pixmap))
333 (ih (xlib:drawable-height pixmap)))
334 (loop for i from (floor (- x xo) iw) to (ceiling (- (+ x w) (+ xo iw)) iw)
336 (loop for j from (floor (- y yo) ih) to (ceiling (- (+ y h) (+ yo ih)) ih)
338 (let ((rect (make-rectangle*
339 (+ xo (* i iw))
340 (+ yo (* j ih))
341 (+ (+ xo (* i iw)) iw)
342 (+ (+ yo (* j ih)) ih))))
343 (map-region-rectangles
344 (lambda (rx0 ry0 rx1 ry1)
345 (xlib:with-gcontext (ggc :exposures :off
346 :fill-style :tiled
347 :tile pixmap
348 :clip-mask mask
349 :clip-x (+ xo (* i iw))
350 :clip-y (+ yo (* j ih))
351 :ts-x xo
352 :ts-y yo)
353 (xlib:draw-rectangle drawable ggc
354 rx0 ry0 (max 0 (- rx1 rx0)) (max 0 (- ry1 ry0))
355 t)))
356 (region-intersection paint-region rect))))) )
357 ;; turn on caching again (see above)
358 (setf (xlib:gcontext-cache-p ggc) t)
360 ;; and xlib:with-gcontext also is broken!
361 (setf (xlib:gcontext-clip-mask ggc) old-clip-mask))))))
363 ;; apparently unused --dfl
365 ;;;(defun x11-draw-background (document medium bg x y width height
366 ;;; &optional (bix x) (biy y) (biwidth width) (biheight height))
367 ;;; (when bg
368 ;;; ;; #+NIL
369 ;;; ;; (unless (eql (background-color bg) :transparent)
370 ;;; ;; (ws/x11::fill-rectangle* drawable gcontext
371 ;;; ;; (round x) (round y)
372 ;;; ;; (max 0 (round width))
373 ;;; ;; (max 0 (round height))
374 ;;; ;; (background-color bg)) )
375 ;;; (unless (eql (r2::background-image bg) :none)
376 ;;; (multiple-value-bind (pixmap mask)
377 ;;; (background-pixmap+mask document (sheet-direct-mirror (medium-sheet medium)) bg)
378 ;;; #+emarsden2005-07-15
379 ;;; (print (list 'x11-draw-background pixmap mask))
380 ;;; (unless (eql pixmap :none)
381 ;;; (let* ((iw (xlib:drawable-width pixmap))
382 ;;; (ih (xlib:drawable-height pixmap))
383 ;;; (w (ecase (r2::background-repeat bg)
384 ;;; ((:repeat :repeat-x) width)
385 ;;; ((:no-repeat :repeat-y) iw)))
386 ;;; (h (ecase (r2::background-repeat bg)
387 ;;; ((:repeat :repeat-y) height)
388 ;;; ((:no-repeat :repeat-x) ih))) )
389 ;;; (let ((hp (car (r2::background-position bg)))
390 ;;; (vp (cdr (r2::background-position bg))))
391 ;;; (let ((xo (+ bix (resolve-background-position hp iw biwidth)))
392 ;;; (yo (+ biy (resolve-background-position vp ih biheight))))
393 ;;; (medium-draw-pm3-tiled* medium pixmap mask
394 ;;; (round (ecase (r2::background-repeat bg)
395 ;;; ((:repeat :repeat-x) x)
396 ;;; ((:no-repeat :repeat-y) (+ xo))))
397 ;;; (round (ecase (r2::background-repeat bg)
398 ;;; ((:repeat :repeat-y) y)
399 ;;; ((:no-repeat :repeat-x) (+ yo))))
400 ;;; (round w) (round h)
401 ;;; (round (+ xo)) (round (+ yo)))))) ))) ))
403 ;;;; --------------------------------------------------------------------------------
405 (defclass ro/img ()
406 ((url :initarg :url)
407 (aim :initarg :aim)
408 (actual-width :initarg :actual-width
409 :initform nil
410 :documentation "The actual (scaled) width of this image.")
411 (actual-height :initarg :actual-height
412 :initform nil
413 :documentation "The actual (scaled) height of this image.")
414 (design :initform nil)))
416 (defmethod gui::deconstruct-robj ((self ro/img))
417 ;; no deconstructor for now ...
420 (defmethod r2:ro/intrinsic-size ((self ro/img)) ;; -> width; height; depth
421 (with-slots (aim) self
422 (values (r2::aimage-width aim)
423 (r2::aimage-height aim)
424 0)))
426 (defmethod r2:ro/size ((self ro/img));; -> width; height; depth
427 (with-slots (aim actual-width actual-height) self
428 (values (or actual-width (r2::aimage-width aim))
429 (or actual-height (r2::aimage-height aim))
430 0)))
432 (defmethod r2:ro/resize ((self ro/img) new-width new-height)
433 (with-slots (actual-width actual-height) self
434 (setf actual-width new-width
435 actual-height new-height)))
437 (defmethod gui::make-image-replacement ((device clim-device) document &key url width height)
438 (let (aim)
439 (cond (nil (and width height)
440 ;; when width and height are known, we do not bother to fetch
441 ;; the image _now_.
444 (setf aim (r2::url->aimage document url))
445 (when (eql aim :error)
446 (setf aim (renderer::broken-aimage document)))))
447 (make-instance 'ro/img
448 :url url
449 :aim aim
450 :actual-width (or width (r2::aimage-width aim))
451 :actual-height (or height (r2::aimage-height aim)))))
453 (climi::def-grecording draw-ro (() ro x y) ()
454 (values x
455 (- y (nth-value 1 (r2::ro/size ro)))
456 (+ x (nth-value 0 (r2::ro/size ro)))
457 (+ y 0)))
458 (climi::def-graphic-op draw-ro (ro x y))
460 (defun draw-ro* (sheet ro x y &rest args)
461 (climi::with-medium-options (sheet args)
462 (medium-draw-ro* medium ro x y)))
465 (defmethod medium-draw-ro* ((medium clim:medium) (self ro/img) x y)
466 (assert (realp x))
467 (assert (realp y))
468 (with-slots (aim design actual-width actual-height) self
469 (when aim ;only draw something, if the image is already there.
470 ;; xxx
471 (when (and actual-width actual-height (not design)) ;xxx
472 (setf design
473 (clue-gui2::make-design-from-aimage medium
475 (max 1 (round actual-width))
476 (max 1 (round actual-height)))))
477 (climi::medium-draw-image-design* medium design x y))))
479 ;; apparently unused --dfl
480 ;;;#+NIL
481 ;;;(climi::def-grecording draw-pm3-tiled (() pixmap mask x1 y1 w h x0 y0)
482 ;;; (values x1 y1 (+ x1 w) (+ y1 h)))
484 ;;;(defmethod medium-draw-pm3-tiled* (medium pixmap mask x1 y1 w h x0 y0)
485 ;;; (let* ((da (sheet-direct-mirror (medium-sheet medium)))
486 ;;; #+NIL
487 ;;; (pixmap+mask (clue-gui2::make-pixmap-from-aimage da aim
488 ;;; (r2::aimage-width aim)
489 ;;; (r2::aimage-height aim)))
490 ;;; #+NIL
491 ;;; (pixmap (first pixmap+mask))
492 ;;; #+NIL
493 ;;; (mask (second pixmap+mask)))
494 ;;; (multiple-value-bind (x1 y1) (transform-position (sheet-device-transformation (medium-sheet medium))
495 ;;; x1 y1)
496 ;;; (setf x1 (round x1))
497 ;;; (setf y1 (round y1))
498 ;;; ;;;
499 ;;; (let ((gcontext (xlib:create-gcontext :drawable da)))
500 ;;; (ws/x11::x11-put-pixmap-tiled da gcontext pixmap mask x1 y1 w h x0 y0) ))))
503 #+NIL
504 (setf clim-clx::*clx-text-family+face-map*
505 '(:fix
506 ("*-courier new"
507 (:roman "medium-r"
508 :bold "bold-r"
509 :italic "medium-o"
510 :bold-italic "bold-o"
511 :italic-bold "bold-o"))
512 :sans-serif
513 ("*-verdana"
514 (:roman "medium-r"
515 :bold "bold-r"
516 :italic "medium-i"
517 :bold-italic "bold-i"
518 :italic-bold "bold-i"))
519 :serif
520 ("*-times new roman"
521 (:roman "medium-r"
522 :bold "bold-r"
523 :italic "medium-i"
524 :bold-italic "bold-i"
525 :italic-bold "bold-i")) ))