changeset 65:fd1bcb67bc32

New mechanism for layers to trigger repaints.
author Mikhail Kryshen <mikhail@kryshen.net>
date Fri, 27 Aug 2010 01:24:31 +0400
parents 702a4939312d
children a1999c1f7289
files src/net/kryshen/indyvon/component.clj src/net/kryshen/indyvon/core.clj src/net/kryshen/indyvon/demo.clj src/net/kryshen/indyvon/layers.clj
diffstat 4 files changed, 136 insertions(+), 110 deletions(-) [+]
line diff
     1.1 --- a/src/net/kryshen/indyvon/component.clj	Thu Aug 26 06:29:30 2010 +0400
     1.2 +++ b/src/net/kryshen/indyvon/component.clj	Fri Aug 27 01:24:31 2010 +0400
     1.3 @@ -17,22 +17,6 @@
     1.4  (defn- font-context [^Component component]
     1.5    (.getFontRenderContext (.getFontMetrics component (.getFont component))))
     1.6  
     1.7 -(defn paint-component
     1.8 -  [^Component component layer ^Graphics2D graphics event-dispatcher]
     1.9 -  (let [size (.getSize component)
    1.10 -        width (.width size)
    1.11 -        height (.height size)
    1.12 -        update-fn #(.repaint component)]
    1.13 -    (.clearRect graphics 0 0 width height)
    1.14 -    (context-draw! layer component graphics event-dispatcher update-fn
    1.15 -                   width height)))
    1.16 -
    1.17 -(defn preferred-size [component layer]
    1.18 -  (binding [*target* component
    1.19 -            *font-context*' (font-context component)]
    1.20 -    (let [s (layer-size layer)]
    1.21 -      (Dimension. (:width s) (:height s)))))
    1.22 -
    1.23  (defn make-jpanel
    1.24    ([layer]
    1.25       (make-jpanel layer (root-event-dispatcher)))
    1.26 @@ -40,9 +24,13 @@
    1.27       (let [panel
    1.28             (proxy [JPanel] []
    1.29               (paintComponent [g]
    1.30 -                (paint-component this layer g event-dispatcher))
    1.31 +                (let [size (.getSize ^Component this)]
    1.32 +                  (draw-root! layer g (.width size) (.height size)
    1.33 +                              event-dispatcher this)))
    1.34               (getPreferredSize []
    1.35 -                (preferred-size this layer)))]
    1.36 +                (let [s (root-size layer (font-context this) this)]
    1.37 +                  (Dimension. (:width s) (:height s)))))]
    1.38         (.setBackground panel (:back-color *theme*))
    1.39 +       (add-observer layer (fn [_] (.repaint panel)))
    1.40         (listen! event-dispatcher panel)
    1.41         panel)))
     2.1 --- a/src/net/kryshen/indyvon/core.clj	Thu Aug 26 06:29:30 2010 +0400
     2.2 +++ b/src/net/kryshen/indyvon/core.clj	Fri Aug 27 01:24:31 2010 +0400
     2.3 @@ -29,11 +29,10 @@
     2.4  
     2.5  (def ^Shape *clip*)
     2.6  
     2.7 +(def *root*)
     2.8 +
     2.9  (def *event-dispatcher*)
    2.10  
    2.11 -(def ^{:doc "Fn to be called in a layer context to request redraw."}
    2.12 -     *update*)
    2.13 -
    2.14  (def ^{:tag AffineTransform
    2.15         :doc "Initial transform associated with the graphics context"}
    2.16       *initial-transform*)
    2.17 @@ -143,6 +142,24 @@
    2.18    (doseq [f (reduce #(concat %1 (get %2 target)) nil (vals @observers))]
    2.19      (apply f target args)))
    2.20  
    2.21 +(defn repaint
    2.22 +  "Repaint the current scene."
    2.23 +  []
    2.24 +  (update *root*))
    2.25 +
    2.26 +(defn add-context-observer
    2.27 +  "Observer registered with this function will be automatically
    2.28 +  removed after the next frame rendering is complete."
    2.29 +  [target f]
    2.30 +  (let [root *root*]
    2.31 +    (add-observer target f root)))
    2.32 +
    2.33 +(defn repaint-on-update
    2.34 +  "Trigger repaint of the current scene when the target updates."
    2.35 +  [target]
    2.36 +  (let [root *root*]
    2.37 +    (add-observer target (fn [_] (update root)) root)))
    2.38 +
    2.39  ;;
    2.40  ;; Rendering
    2.41  ;;
    2.42 @@ -283,33 +300,6 @@
    2.43    ([layer x y width height]
    2.44       (with-bounds* x y width height render! layer)))
    2.45  
    2.46 -;; TODO: объект-сцена вместо context-draw!
    2.47 -;; Сцена устанавливает контекст и рисует слой. Сцена - слой?
    2.48 -;; Сцена идентифицирует группу обозревателя слоев.
    2.49 -;; Обновления любого слоя, изображенного в сцене, вызывает обновление сцены.
    2.50 -;; Обозреватель сцены вызывает repaint().
    2.51 -
    2.52 -(defn context-draw!
    2.53 -  "Sets up layer context, draws layer and commits event dispatcher."
    2.54 -  ([layer graphics event-dispatcher update-fn width height]
    2.55 -     (context-draw! layer nil graphics event-dispatcher update-fn width height))
    2.56 -  ([layer component ^Graphics2D graphics event-dispatcher update-fn
    2.57 -    width height]
    2.58 -     (binding [*graphics* graphics
    2.59 -               *font-context* (.getFontRenderContext graphics)
    2.60 -               *initial-transform* (.getTransform graphics)
    2.61 -               *inverse-initial-transform*
    2.62 -                 (-> graphics .getTransform .createInverse)
    2.63 -               *target* component
    2.64 -               *update* update-fn
    2.65 -               *event-dispatcher* event-dispatcher
    2.66 -               *width* width
    2.67 -               *height* height
    2.68 -               *clip* (Rectangle2D$Double. 0 0 width height)]
    2.69 -       (apply-theme)
    2.70 -       (render! layer)
    2.71 -       (commit event-dispatcher))))
    2.72 -
    2.73  (defn draw-anchored!
    2.74    "Draws layer. Location is relative to the layer's anchor point for
    2.75     the specified alignment."
    2.76 @@ -320,6 +310,41 @@
    2.77       (let [anchor (anchor layer h-align v-align)]
    2.78         (draw! layer (- x (:x anchor)) (- y (:y anchor)) w h))))
    2.79  
    2.80 +(defn draw-root!
    2.81 +  "Draws the root layer."
    2.82 +  ([layer graphics width height event-dispatcher]
    2.83 +     (draw-root! layer graphics width height event-dispatcher nil))
    2.84 +  ([layer ^Graphics2D graphics width height event-dispatcher target]
    2.85 +     (binding [*root* layer
    2.86 +               *target* target
    2.87 +               *graphics* graphics
    2.88 +               *font-context* (.getFontRenderContext graphics)
    2.89 +               *initial-transform* (.getTransform graphics)
    2.90 +               *inverse-initial-transform*
    2.91 +                 (-> graphics .getTransform .createInverse)
    2.92 +               *event-dispatcher* event-dispatcher
    2.93 +               *width* width
    2.94 +               *height* height
    2.95 +               *clip* (Rectangle2D$Double. 0 0 width height)]
    2.96 +       (let [tmp-group (Object.)]
    2.97 +         (apply-theme)
    2.98 +         (.clearRect graphics 0 0 width height)
    2.99 +         (change-group-id layer tmp-group)
   2.100 +         (try
   2.101 +           (render! layer)
   2.102 +           (finally
   2.103 +            (remove-group tmp-group)
   2.104 +            (commit event-dispatcher)))))))
   2.105 +
   2.106 +(defn root-size
   2.107 +  ([layer font-context]
   2.108 +     (root-size layer font-context nil))
   2.109 +  ([layer font-context target]
   2.110 +     (binding [*root* layer
   2.111 +               *target* target
   2.112 +               *font-context* font-context]
   2.113 +       (layer-size layer))))
   2.114 +
   2.115  ;;
   2.116  ;; EventDispatcher implementation
   2.117  ;;
     3.1 --- a/src/net/kryshen/indyvon/demo.clj	Thu Aug 26 06:29:30 2010 +0400
     3.2 +++ b/src/net/kryshen/indyvon/demo.clj	Fri Aug 27 01:24:31 2010 +0400
     3.3 @@ -76,7 +76,7 @@
     3.4       (reify
     3.5        Layer
     3.6        (render! [layer]
     3.7 -         ;;(*update*)
     3.8 +         ;;(repaint)
     3.9           (doto *graphics*
    3.10             (.setColor (rand-nth [Color/BLACK Color/BLUE Color/RED]))
    3.11             (.drawLine 0 0 *width* *height*))
     4.1 --- a/src/net/kryshen/indyvon/layers.clj	Thu Aug 26 06:29:30 2010 +0400
     4.2 +++ b/src/net/kryshen/indyvon/layers.clj	Fri Aug 27 01:24:31 2010 +0400
     4.3 @@ -145,11 +145,11 @@
     4.4                   height (text-height layouts)]
     4.5               (Size. width height)))))))
     4.6  
     4.7 -(defn- ^ImageObserver image-observer [update-fn]
     4.8 +(defn- ^ImageObserver image-observer [layer]
     4.9    (reify
    4.10     ImageObserver
    4.11     (imageUpdate [this img infoflags x y width height]
    4.12 -      (update-fn)
    4.13 +      (update layer)
    4.14        (zero? (bit-and infoflags
    4.15                        (bit-or ImageObserver/ALLBITS
    4.16                                ImageObserver/ABORT))))))
    4.17 @@ -164,72 +164,83 @@
    4.18      (reify
    4.19       Layer
    4.20       (render! [layer]
    4.21 -        (.drawImage *graphics* image 0 0 (image-observer *update*)))
    4.22 +        (repaint-on-update layer)
    4.23 +        (.drawImage *graphics* image 0 0 (image-observer layer)))
    4.24       (layer-size [layer]
    4.25 -        (let [observer (image-observer *update*)
    4.26 +        (let [observer (image-observer layer)
    4.27                width (.getWidth image observer)
    4.28                height (.getHeight image observer)
    4.29                width (if (pos? width) width 1)
    4.30                height (if (pos? height) height 1)]
    4.31            (Size. width height))))))
    4.32  
    4.33 -(defn async-layer
    4.34 +(defn- create-buffer [async-layer]
    4.35 +  (BufferedImage. (:width async-layer) (:height async-layer)
    4.36 +                  BufferedImage/TYPE_INT_ARGB))
    4.37 +
    4.38 +(defn- draw-offscreen [async-layer]
    4.39 +  ;;(Thread/sleep 3000)
    4.40 +  (let [buffers (:buffers async-layer)
    4.41 +        ^Image b (dosync
    4.42 +                  (let [b (peek @buffers)]
    4.43 +                    (alter buffers pop)
    4.44 +                    b))]
    4.45 +    (try
    4.46 +      ;; TODO: use operational event dispatcher.
    4.47 +      (draw-root! (:content async-layer)
    4.48 +                  (.getGraphics b)
    4.49 +                  (:width async-layer)
    4.50 +                  (:height async-layer)
    4.51 +                  dummy-event-dispatcher)
    4.52 +      (finally
    4.53 +       (dosync
    4.54 +        (alter buffers conj b)
    4.55 +        (ref-set (:updated async-layer) true))))
    4.56 +    (update async-layer)))
    4.57 +
    4.58 +(defn- draw-offscreen-async [async-layer]
    4.59 +  (.execute ^ThreadPoolExecutor (:executor async-layer)
    4.60 +            #(draw-offscreen async-layer)))
    4.61 +
    4.62 +(defrecord AsyncLayer [content width height executor buffers updated]
    4.63 +  Layer
    4.64 +  (render! [layer]
    4.65 +     (repaint-on-update layer)
    4.66 +     (add-context-observer content (fn [_] (draw-offscreen-async layer)))
    4.67 +     (when-not @buffers
    4.68 +       ;; TODO: dynamic size, recreate buffers when size increases.
    4.69 +       (let [new-buffers [(create-buffer layer) (create-buffer layer)]]
    4.70 +         (dosync
    4.71 +          (ref-set buffers new-buffers)))
    4.72 +       (draw-offscreen-async layer))
    4.73 +     (let [buffer (dosync
    4.74 +            (if @updated
    4.75 +              (let [b (peek @buffers)]
    4.76 +                (alter buffers pop)
    4.77 +                (ref-set updated false)
    4.78 +                b)
    4.79 +              (let [b (first @buffers)]
    4.80 +                (alter buffers subvec 1)
    4.81 +                b)))]
    4.82 +       (.drawImage *graphics* ^Image buffer 0 0 nil)
    4.83 +       (dosync
    4.84 +        (alter buffers #(vec (cons buffer %))))))
    4.85 +  (layer-size [layer]
    4.86 +     (Size. width height)))
    4.87 +
    4.88 +(defn async-layer 
    4.89    "Creates layer that draws the content asynchroniously in an
    4.90     offscreen buffer."
    4.91    [content width height]
    4.92 -  (let [executor (ThreadPoolExecutor. (int 1) (int 1) (long 0) TimeUnit/SECONDS
    4.93 -                   (ArrayBlockingQueue. 1)
    4.94 -                   (ThreadPoolExecutor$DiscardOldestPolicy.))
    4.95 -        buffers (ref nil)
    4.96 -        updated (ref false)
    4.97 -        update-fn (ref nil)
    4.98 -        create-buffer
    4.99 -          #(BufferedImage. width height BufferedImage/TYPE_INT_ARGB)
   4.100 -        draw-offscreen
   4.101 -          (fn draw-offscreen []
   4.102 -            ;;(Thread/sleep 3000)
   4.103 -            (let [^Image b (dosync
   4.104 -                            (let [b (peek @buffers)]
   4.105 -                              (alter buffers pop)
   4.106 -                              b))]
   4.107 -              (try
   4.108 -                ;; TODO: use operational event dispatcher.
   4.109 -                (context-draw! content
   4.110 -                               (.getGraphics b)
   4.111 -                               dummy-event-dispatcher
   4.112 -                               #(.execute executor draw-offscreen)
   4.113 -                               width height)
   4.114 -                (finally
   4.115 -                 (dosync
   4.116 -                  (alter buffers conj b)
   4.117 -                  (ref-set updated true))))
   4.118 -                (update-fn)))]
   4.119 -    (reify
   4.120 -     Layer
   4.121 -     (render! [layer]
   4.122 -        (when-not @buffers
   4.123 -          ;; TODO: dynamic size, recreate buffers when size increases.
   4.124 -          (let [new-buffers [(create-buffer) (create-buffer)]]
   4.125 -            (dosync
   4.126 -             (ref-set buffers new-buffers)
   4.127 -             (ref-set update-fn *update*)))
   4.128 -          (.execute executor draw-offscreen))
   4.129 -        (let [buffer (dosync
   4.130 -                 (ref-set update-fn *update*)
   4.131 -                 (if @updated
   4.132 -                   (let [b (peek @buffers)]
   4.133 -                     (alter buffers pop)
   4.134 -                     (ref-set updated false)
   4.135 -                     b)
   4.136 -                   (let [b (first @buffers)]
   4.137 -                     (alter buffers subvec 1)
   4.138 -                     b)))]
   4.139 -          (.drawImage *graphics* ^Image buffer 0 0 nil)
   4.140 -          (dosync
   4.141 -           (alter buffers #(vec (cons buffer %))))))
   4.142 -     (layer-size [layer]
   4.143 -        (Size. width height)))))
   4.144 -
   4.145 +  (AsyncLayer. content width height
   4.146 +               (ThreadPoolExecutor.
   4.147 +                (int 1) (int 1)
   4.148 +                (long 0) TimeUnit/SECONDS
   4.149 +                (ArrayBlockingQueue. 1)
   4.150 +                (ThreadPoolExecutor$DiscardOldestPolicy.))
   4.151 +               (ref nil)
   4.152 +               (ref false)))
   4.153 +    
   4.154  (defn miniature
   4.155    "Creates layer that asynchroniously renders view of the content
   4.156    scaled to the specified size."
   4.157 @@ -272,16 +283,18 @@
   4.158             (dosync
   4.159              (ref-set fix-x (:x-on-screen e))
   4.160              (ref-set fix-y (:y-on-screen e)))
   4.161 -           (->> Cursor/MOVE_CURSOR Cursor. (.setCursor *target*)))
   4.162 +           (when *target*
   4.163 +             (->> Cursor/MOVE_CURSOR Cursor. (.setCursor *target*))))
   4.164            (:mouse-released e
   4.165 -           (->> Cursor/DEFAULT_CURSOR Cursor. (.setCursor *target*)))
   4.166 +           (when *target*
   4.167 +             (->> Cursor/DEFAULT_CURSOR Cursor. (.setCursor *target*))))
   4.168            (:mouse-dragged e
   4.169             (dosync
   4.170              (alter x + (- @fix-x (:x-on-screen e)))
   4.171              (alter y + (- @fix-y (:y-on-screen e)))
   4.172              (ref-set fix-x (:x-on-screen e))
   4.173              (ref-set fix-y (:y-on-screen e)))
   4.174 -           (*update*))))
   4.175 +           (repaint))))
   4.176       (layer-size [layer] (layer-size content))))))
   4.177  
   4.178  ;;