Update of /project/mcclim/cvsroot/mcclim/Drei In directory clnet:/tmp/cvs-serv4573/Drei
Modified Files: drei-redisplay.lisp Log Message: Baseline-adjusted drawing for Drei. Please test.
Is very slightly slower than it used to be, but enables an optimisation (reduction in number of distinct calls to draw-rectangle*) that I'll finish up shortly.
--- /project/mcclim/cvsroot/mcclim/Drei/drei-redisplay.lisp 2008/01/14 12:43:05 1.26 +++ /project/mcclim/cvsroot/mcclim/Drei/drei-redisplay.lisp 2008/01/14 18:42:43 1.27 @@ -100,11 +100,12 @@ (style nil))
(defconstant +default-stroke-drawer-dispatcher+ - #'(lambda (stream view stroke cursor-x cursor-y default-drawing-fn) - (funcall default-drawing-fn stream view stroke cursor-x cursor-y)) - "A simple function of six arguments that simply calls the first -argument as a function with the remaining five arguments. Used as -the default drawing-function of `drawing-options' objects.") + #'(lambda (stream view stroke cursor-x cursor-y default-drawing-fn draw) + (funcall default-drawing-fn stream view stroke cursor-x cursor-y draw)) + "A simple function of seven arguments that simply calls the +first argument as a function with the remaining sex +arguments. Used as the default drawing-function of +`drawing-options' objects.")
(defstruct drawing-options "A set of options for how to display a stroke." @@ -142,7 +143,7 @@ be absolute coordinates in the coordinate system of a sheet. A special `center' slot is also provided to enable the recording of what might be considered a *logical* centre of the dimensions on -the vertical axis." +the vertical axis. `Center' should be relative to `y1'." (x1 0) (y1 0) (x2 0) @@ -189,6 +190,13 @@ (widths) (parts))
+(defun stroke-at-end-of-line (buffer stroke) + "Return true if the end offset of `stroke' is at the end of a +line in `buffer'. Otherwise, return nil. The end offset of +`stroke' must be a valid offset for `buffer' or an error will be +signalled." + (offset-end-of-line-p buffer (stroke-end-offset stroke))) + (defstruct (displayed-line (:conc-name line-)) "A line on display. A line delimits a buffer region (always bounded by newline objects or border beginning/end) and contains @@ -391,8 +399,7 @@ sets the modified-bit of `stroke' to false, as it updates the dimensions." (let ((dimensions (stroke-dimensions stroke))) - (setf (stroke-dirty stroke) (and (stroke-dirty stroke) - (not drawn)) + (setf (stroke-dirty stroke) (and (stroke-dirty stroke) (not drawn)) (stroke-modified stroke) nil (stroke-parts stroke) parts (stroke-widths stroke) widths @@ -443,12 +450,11 @@ "Calculate the width information of `stroke-string' when displayed with `text-style' (which must be fully specified) on `stream', starting at the horizontal device unit offset -`x-position'. Four values will be returned: the total width of -the stroke, the baseline, the parts of the stroke and the widths -of the parts of the stroke." +`x-position'. Three values will be returned: the total width of +the stroke, the parts of the stroke and the widths of the parts +of the stroke." (loop with parts = (analyse-stroke-string stroke-string) with width = 0 - with baseline = 0 with widths = (make-array (length parts) :adjustable t :fill-pointer t) with tab-width for (start end object) in parts @@ -459,32 +465,32 @@ (mod (+ width x-position) tab-width))) (vector-push-extend width widths)) (object - (multiple-value-bind (w ignore1 ignore2 ignore3 b) + (multiple-value-bind (w ignore1 ignore2 ignore3 ignore4) (text-size stream object :text-style text-style) - (declare (ignore ignore1 ignore2 ignore3)) + (declare (ignore ignore1 ignore2 ignore3 ignore4)) (incf width w) - (setf baseline (max baseline b)) (vector-push-extend width widths))) (t - (multiple-value-bind (w ignore1 ignore2 ignore3 b) + (multiple-value-bind (w ignore1 ignore2 ignore3 ignore4) (text-size stream stroke-string :start start :end end :text-style text-style) - (declare (ignore ignore1 ignore2 ignore3)) + (declare (ignore ignore1 ignore2 ignore3 ignore4)) (incf width w) - (setf baseline (max baseline b)) (vector-push-extend width widths)))) - finally (return (values width baseline parts widths)))) + finally (return (values width parts widths))))
-(defun stroke-drawing-fn (stream view stroke cursor-x cursor-y) - "Draw `stroke' to `stream' at the position (`cursor-x', +(defun stroke-drawing-fn (stream view stroke cursor-x cursor-y draw) + "Draw `stroke' to `stream' baseline-adjusted at the position (`cursor-x', `cursor-y'). `View' is the view object that `stroke' belongs -to. It is assumed that the buffer region delimited by `stroke' -only contains characters. `Stroke' is drawn with face given by -the drawing options of `stroke', using the default text style of -`stream' to fill out any holes. The screen area beneath `stroke' -will be cleared before any actual output takes place." +to. If `draw' is true, actually draw the stroke to `stream', +otherwise, just calculate its size. It is assumed that the buffer +region delimited by `stroke' only contains characters. `Stroke' +is drawn with face given by the drawing options of `stroke', +using the default text style of `stream' to fill out any +holes. The screen area beneath `stroke' will be cleared before +any actual output takes place." (with-accessors ((start-offset stroke-start-offset) (end-offset stroke-end-offset) (dimensions stroke-dimensions) @@ -500,56 +506,56 @@ ;; Ignore face when computing height, otherwise we get ;; bouncy lines when things like parenmatching bolds parts ;; of the line. - (roman-text-style (merge-text-styles +roman-face-style+ - merged-text-style)) + (roman-text-style (merge-text-styles +roman-face-style+ merged-text-style)) (text-style-ascent (text-style-ascent roman-text-style (sheet-medium stream))) - (text-style-descent (text-style-descent roman-text-style (sheet-medium stream))) - (text-style-height (+ text-style-ascent text-style-descent))) + (text-style-descent (text-style-descent roman-text-style (sheet-medium stream)))) (with-accessors ((x1 x1) (x2 x2) (center center)) dimensions - (multiple-value-bind (width baseline stroke-parts part-widths) + (multiple-value-bind (width stroke-parts part-widths) (if (stroke-modified stroke) (calculate-stroke-width stroke-string merged-text-style stream cursor-x) - (values (- x2 x1) center parts widths)) - (clear-rectangle* stream cursor-x cursor-y - (+ cursor-x width) (+ cursor-y text-style-height - (stream-vertical-spacing stream))) - (loop for (start end object) in stroke-parts - for width across part-widths - do (cond ((and object (eq object #\Tab)) - nil) - (object - (draw-text* stream object (+ cursor-x width) - (+ cursor-y - (- text-style-ascent - baseline)) - :text-style merged-text-style - :ink +darkblue+ - :align-y :top)) - (t - (draw-text* stream stroke-string (+ cursor-x width) - (+ cursor-y - (- text-style-ascent - baseline)) - :start start :end end - :text-style merged-text-style - :ink (face-ink (drawing-options-face drawing-options)) - :align-y :top)))) - (record-stroke stroke stroke-parts part-widths cursor-x cursor-y - (+ width cursor-x) (+ text-style-height cursor-y) - t baseline)))))) + (values (- x2 x1) parts widths)) + (when draw + (loop for (start end object) in stroke-parts + for width across part-widths + do (cond ((and object (eq object #\Tab)) + nil) + (object + (draw-text* stream object (+ cursor-x width) + cursor-y + :text-style merged-text-style + :ink +darkblue+ + :align-y :baseline)) + (t + (draw-text* stream stroke-string (+ cursor-x width) + cursor-y + :start start :end end + :text-style merged-text-style + :ink (face-ink (drawing-options-face drawing-options)) + :align-y :baseline))))) + (record-stroke stroke stroke-parts part-widths + cursor-x (- cursor-y text-style-ascent) + (+ width cursor-x) (+ cursor-y text-style-descent) + draw text-style-ascent)))))) + +(defun update-stroke-dimensions (stream view stroke cursor-x cursor-y) + "Calculate the dimensions of `stroke' on `stream' +at (`cursor-x', `cursor-y'), but without actually drawing +anything. Will use the function specified in the drawing-options +of `stroke' to carry out the actual calculations." + (unless (= cursor-x (x1 (stroke-dimensions stroke))) + (invalidate-stroke stroke :modified t)) + (when (stroke-dirty stroke) + (funcall (drawing-options-function (stroke-drawing-options stroke)) stream view stroke + cursor-x cursor-y #'stroke-drawing-fn nil)))
(defun draw-stroke (stream view stroke cursor-x cursor-y) - "Draw `stroke' on `stream' at (`cursor-x', `cursor-y'). Nothing -will be done unless `stroke' is dirty. Will use the function -specified in the drawing-options of `stroke' to carry out the -actual drawing." - (let* ((drawing-options (stroke-drawing-options stroke))) - (unless (and (= cursor-x (x1 (stroke-dimensions stroke))) - (= cursor-y (y1 (stroke-dimensions stroke)))) - (invalidate-stroke stroke :modified t)) - (when (stroke-dirty stroke) - (funcall (drawing-options-function drawing-options) stream view stroke - cursor-x cursor-y #'stroke-drawing-fn)))) + "Draw `stroke' on `stream' with a baseline at +`cursor-y'. Drawing starts at the horizontal offset +`cursor-x'. Stroke must thus have updated dimensional +informational. Nothing will be done unless `stroke' is dirty." + (when (stroke-dirty stroke) + (funcall (drawing-options-function (stroke-drawing-options stroke)) + stream view stroke cursor-x cursor-y #'stroke-drawing-fn t)))
(defun end-line (line x1 y1 line-width line-height) "End the addition of strokes to `line' for now, and update the @@ -568,21 +574,20 @@ associated dimensions. Also clear from the bottom of strokes to the bottom of the line, and from the end of the line to the end of the sheet." + (declare (ignore old-line-width)) (end-line line line-x1 line-y1 line-width line-height) (with-accessors ((line-x1 x1) (line-y1 y1) (line-x2 x2) (line-y2 y2)) (line-dimensions line) - ;; If a has a lesser height than the line, clear from the bottom - ;; of the stroke to the bottom of the line, to avoid artifacts - ;; left over from prefvious redisplays. + ;; If a has a lesser height than the line, clear from the top of + ;; the line stroke to the top of the stroke, to avoid artifacts + ;; left over from previous redisplays. (do-displayed-line-strokes (stroke line) (let ((stroke-dimensions (stroke-dimensions stroke))) (with-accessors ((stroke-x1 x1) (stroke-y1 y1) (stroke-x2 x2) (stroke-y2 y2)) stroke-dimensions (when (> line-height (dimensions-height stroke-dimensions)) - (clear-rectangle* stream stroke-x1 stroke-y2 - stroke-x2 (+ stroke-y2 (- line-height - (dimensions-height stroke-dimensions)) - (stream-vertical-spacing stream))))))) + (clear-rectangle* stream stroke-x1 line-y1 + stroke-x2 stroke-y1))))) ;; Reset the dimensions of undisplayed lines. (do-undisplayed-line-strokes (stroke line) (let ((stroke-dimensions (stroke-dimensions stroke))) @@ -594,43 +599,58 @@ (clear-rectangle* stream line-x2 line-y1 (bounding-rectangle-width stream) (+ line-y1 (max line-height old-line-height) - (stream-vertical-spacing stream))) - (when (or (> old-line-height line-height) - (> old-line-width line-width)) - (clear-rectangle* stream line-x1 (+ line-y1 line-height) - (+ line-x1 (max old-line-width line-width)) - (+ line-y1 (max old-line-height line-height)))))) + (stream-vertical-spacing stream)))))
(defun draw-line-strokes (stream view initial-pump-state start-offset cursor-x cursor-y) "Pump strokes from `view', using `initial-pump-state' to begin with, and draw them on `stream'. The line is set to start at the buffer offset `start-offset', and will be drawn starting -at (`cursor-x', `cursor-y')" +at (`cursor-x', `cursor-y')." (let* ((line (line-information view (displayed-lines-count view))) (old-line-height (dimensions-height (line-dimensions line))) (old-line-width (dimensions-width (line-dimensions line))) (orig-x-offset cursor-x) - (offset-change (- start-offset (line-start-offset line)))) + (offset-change (- start-offset (line-start-offset line))) + (line-spacing (stream-vertical-spacing stream))) (setf (line-start-offset line) start-offset (line-stroke-count line) 0) - (loop for index from 0 - for stroke = (line-stroke-information line index) - for stroke-dimensions = (stroke-dimensions stroke) - for pump-state = (put-stroke view line initial-pump-state offset-change) then - (put-stroke view line pump-state offset-change) - do (draw-stroke stream view stroke cursor-x cursor-y) - (setf cursor-x (x2 stroke-dimensions)) - maximizing (dimensions-height stroke-dimensions) into line-height - summing (- (x2 stroke-dimensions) - (x1 stroke-dimensions)) into line-width - when (or (= (stroke-end-offset stroke) (size (buffer view))) - (eql (buffer-object (buffer view) (stroke-end-offset stroke)) #\Newline)) - return (progn (end-line-cleaning-up stream line orig-x-offset cursor-y - line-width old-line-width - line-height old-line-height) - (incf (displayed-lines-count view)) - (values pump-state line-height))))) + ;; So yeah, this is fairly black magic, but it's not actually + ;; ugly, just complex. + (multiple-value-bind (line-width line-height baseline pump-state) + ;; Pump all the line strokes and calculate their dimensions. + (loop for index from 0 + for stroke = (line-stroke-information line index) + for stroke-dimensions = (stroke-dimensions stroke) + for pump-state = (put-stroke view line initial-pump-state offset-change) then + (put-stroke view line pump-state offset-change) + do (update-stroke-dimensions stream view stroke cursor-x cursor-y) + (setf cursor-x (x2 stroke-dimensions)) + maximizing (dimensions-height stroke-dimensions) into line-height + maximizing (+ (center stroke-dimensions) cursor-y) into baseline + summing (dimensions-width stroke-dimensions) into line-width + when (stroke-at-end-of-line (buffer view) stroke) + return (values line-width line-height baseline pump-state)) + ;; Now actually draw them in a way that makes sure they all + ;; touch the bottom of the line. + (loop with last-clear-x = orig-x-offset + for stroke-index below (line-stroke-count line) + for stroke = (aref (line-strokes line) stroke-index) + for stroke-dimensions = (stroke-dimensions stroke) + do (unless (= baseline (+ cursor-y (center stroke-dimensions))) + (invalidate-stroke stroke)) + (when (stroke-dirty stroke) + (clear-rectangle* stream (x1 stroke-dimensions) cursor-y + (x2 stroke-dimensions) + (+ cursor-y line-height line-spacing)) + (setf last-clear-x (x2 stroke-dimensions))) + (draw-stroke stream view stroke + (x1 stroke-dimensions) baseline) + finally (progn (end-line-cleaning-up stream line orig-x-offset cursor-y + line-width old-line-width + line-height old-line-height) + (incf (displayed-lines-count view)) + (return (values pump-state line-height)))))))
(defun clear-stale-lines (pane view) "Clear from the last displayed line to the end of `pane'." @@ -652,7 +672,7 @@ (widths (make-array 2 :initial-contents (list 0 0))) (parts (list 0 1))) #'(lambda (stream view stroke cursor-x cursor-y - default-drawing-fn) + default-drawing-fn draw) (declare (ignore default-drawing-fn)) (with-accessors ((start-offset stroke-start-offset) (drawing-options stroke-drawing-options)) stroke @@ -665,20 +685,17 @@ ;; like the changing position is ignored. So add some ;; minuscule amount to it, and all will be well. 0.1 ;; device units shouldn't even be visible. - (setf (output-record-position output-record) (values (+ cursor-x 0.1) cursor-y)) (let ((width (bounding-rectangle-width output-record)) (height (bounding-rectangle-height output-record))) - (clear-rectangle* stream cursor-x cursor-y - (+ cursor-x width) (+ cursor-y height - (stream-vertical-spacing stream))) - (replay output-record stream) + (setf (output-record-position output-record) + (values (+ cursor-x 0.1) (- cursor-y height))) + (when draw + (replay output-record stream)) (setf (aref widths 1) width) (record-stroke stroke parts widths - cursor-x cursor-y (+ width cursor-x) - (+ (if (zerop height) - (text-style-height (medium-text-style stream) stream) - height) - cursor-y)))))))) + cursor-x (- cursor-y height) + (+ width cursor-x) cursor-y + draw height)))))))
(defmethod pump-state-for-offset ((view drei-buffer-view) (offset integer)) "For a `drei-buffer-view' a pump-state is merely an offset into @@ -767,7 +784,7 @@
(defgeneric offset-to-screen-position (pane view offset) (:documentation "Returns the position of offset as a screen -position. Returns `x', `y', `line-height', `OBJECT-WIDTH' as +position. Returns `x', `y', `stroke-height', `object-width' as values if offset is on the screen, NIL if offset is before the beginning of the screen, and T if offset is after the end of the screen. `Object-width' may be an approximation if `offset' is at @@ -786,7 +803,7 @@ (/= start-offset end-offset)) (return-from worker (values (x1 stroke-dimensions) (y1 stroke-dimensions) - (dimensions-height line-dimensions) + (dimensions-height stroke-dimensions) (if (= end-offset (1+ start-offset)) (dimensions-width stroke-dimensions) (offset-in-stroke-position pane view stroke (1+ offset)))))) @@ -796,7 +813,7 @@ (let* ((relative-x-position (offset-in-stroke-position pane view stroke offset)) (absolute-x-position (+ (x1 stroke-dimensions) relative-x-position))) (values absolute-x-position (y1 stroke-dimensions) - (dimensions-height line-dimensions) + (dimensions-height stroke-dimensions) (if (= (1+ offset) end-offset) (- (x2 stroke-dimensions) absolute-x-position) (- (offset-in-stroke-position pane view stroke (1+ offset)) @@ -815,9 +832,9 @@ ;; Search through strokes, returning when we find one that ;; `offset' is in. Strokes with >1 object are assumed to be ;; strings. - (multiple-value-bind (x y line-height object-width) (worker) - (if (and x y line-height) - (values x y line-height (or object-width default-object-width))
[21 lines skipped]