I just realized that this response from 6 March doesn't seem to have made it to the list. Is it because I answered directly to Raffael and cc:ed the list?
Anyway, I would be most grateful if someone could enlighten me when it comes to my questions regarding invalidate-rectangle etc.
/Sven again
---
Thanks for the nice demo Raffael!
Possibly I can use image copying for parts of the scrolling, but a
difficulty is that I draw a note head when a "note on" event arrives
and later when the corresponding "note off" event arrives, I change
the note head (now scrolled to the left) into its right shape
(quarter note, half note etc).
Anyway, I have made some progress. I stuffed all drawing and also
updates using set-horizontal-scroll-parameters into with-atomic-
redisplay and now have a smoother scroll.
I lack understanding of the underpinnings of updating the pane though...
-I realize that calls to invalidate-rectangle, queue up calls to the
display-callback and that some of these calls are thrown away. But
what calls are thrown away?
-What happens when calling set-horizontal-scroll-parameters? It
doesn't seem to call invalidate-rectangle and even if I don't call
invalidate-rectangle after set-horizontal-scroll-parameters, things
are redrawn.
Are the above things described somewhere?
Thanks again!
/Sven
5 mar 2010 kl. 20.11 skrev Raffael Cavallaro:
On Mar 5, 2010, at 6:39 AM, Sven Emtell wrote:
During MIDI input, a timer is calling an update function (note-
pane-update) every 20ms. This updating function changes the :max-
range of the horizontal scroll-bar using capi:set-horizontal-
scroll-parameters followed by a "scroll :horizontal :move :end",
and then calls the display-callback function (display-note-pane).
The idea is to set the slug in its right-most position, so that
score notes enter the note-pane from the right and scroll to the
left.
In the display-callback function, I have wrapped the drawing
inside a with-atomic-redisplay.
Note:
The score notes displayed will change during scrolling, so I have
to redraw them instead of just copying a bitmap.
Some observations:
-It works, but the drawing is not very smooth. It seems to jump a
little back and forth while scrolling.
-When updating every 200ms instead, it looks OK, but slow.
Other sorts of capi animation I've done as well as apple's docs
suggest that you'll never get more than 60fps screen refresh from a
Cocoa/AppKit application. If you do much computation and drawing
you'll start to see aliasing (e.g., wheels appearing to rotate
backwards) even at 60fps. So 20ms=50fps is right on the edge of
what is possible even with minimal computation and drawing per
frame. Have you tried a 33ms timer (i.e., 30 fps)?
Another possibility: You could grab the already drawn notes in an
image, draw that to the pane, then draw the new notes over it. For
example, if you shift the notes by 1/10 of the visible pane width
each frame, you grab the rightmost 9/10 of the pane in an image
(gp:make-image-from-port), draw this image full left in the pane,
then draw the new notes in the rightmost 1/10 of the pane.
Of course whether this would be faster than just redrawing is
entirely dependent on how much redrawing you're doing. My
benchmarks suggest that you can draw just under 200 polygons (i.e.,
gp:draw-polygon) at 30 fps on a 2+GHz machine. Anything more than
this and it's worthwhile to grab an image first, then do any new
drawing for each frame over the grabbed image from the previous frame.
Something like this would do the side scrolling:
(defun anim-test-sidescroll (&key (framerate 10) (time 6)) ;; up
framerate to see effect
(declare (optimize speed (safety 1) (debug 0)))
(let* ((frame-x-offset 70) ;; how far we'll shift the pane
contents left each frame
(circle-base-x-coordinate 600) ;; where we'll plunk down
new circles
(current-image nil) ;; the image we've grabbed of the
port, left end choppped
(old-image nil) ;; previous frame so we can free it and
avoid a leak
(still-running t))
(labels ((display-a-frame (self x y width height)
(declare (ignorable x y width height))
(when current-image (gp:draw-image self current-
image 0 0))
(dotimes (n 4) ;; draw 4 circles in a vertical line
at the right edge
(gp:draw-circle self
(+ circle-base-x-coordinate (* n
3)) ;; x
(+ 50 (* n 100) (random
100)) ;; y
(+ 20 (random
15)) ;; radius
:filled t
:foreground (nth n
'(:orange :blue :green :purple))))
(setf old-image current-image) ;; swap these cause
we need a ref to old-image
(setf current-image ;; grab the current port
contents with the left end chopped
(gp:make-image-from-port self frame-x-offset 0
;; need to truncate
width by frame-x-offset
;; or the image will
be stretched when displayed
(- (gp:port-width
self) frame-x-offset)
(gp:port-height self)))
(when old-image (gp:free-image self old-image))) ;;
free the old image
(pane-destroyed (self) ;; stop the animation loop if
pane goes bye bye
(declare (ignorable self))
(setf still-running nil)))
(let* ((the-pane ;; make a pane that uses the above func for
display
(capi:contain
(make-instance 'capi:output-pane
:display-callback #'display-
a-frame
:destroy-callback #'pane-
destroyed)
:best-height 600 :best-width 700)))
(mp:process-run-function
"anim-test-sidescroll-process" ()
(lambda ()
(loop for i below (round (* framerate time))
while still-running do ;; loop for time
redisplaying at framerate
(sleep (float (/ framerate) 1.0s0))
(gp:invalidate-rectangle the-pane))))))))
As you'll see if you experiment with the framerate keyword arg, you
don't see any increase in speed beyond :framerate 60 -
i.e., :framerate 300 looks just the same.
hth,
warmest regards,
Ralph
Raffael Cavallaro
raffaelcavallaro@me.com