changeset 67:a19cf5007d14

Asynchronous drawing.
author Mikhail Kryshen <mikhail@kryshen.net>
date Sat, 28 Aug 2010 02:37:30 +0400
parents a1999c1f7289
children 9b511fe09867
files project.clj src/net/kryshen/indyvon/async.clj src/net/kryshen/indyvon/core.clj src/net/kryshen/indyvon/demo.clj src/net/kryshen/indyvon/layers.clj
diffstat 5 files changed, 155 insertions(+), 79 deletions(-) [+]
line diff
     1.1 --- a/project.clj	Fri Aug 27 19:47:28 2010 +0400
     1.2 +++ b/project.clj	Sat Aug 28 02:37:30 2010 +0400
     1.3 @@ -4,6 +4,7 @@
     1.4                   [org.clojure/clojure-contrib "1.2.0"]]
     1.5    :dev-dependencies [[swank-clojure/swank-clojure "1.2.1"]]
     1.6    :namespaces [net.kryshen.indyvon.core
     1.7 +               net.kryshen.indyvon.async
     1.8                 net.kryshen.indyvon.layers
     1.9                 net.kryshen.indyvon.component
    1.10                 net.kryshen.indyvon.demo])
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/src/net/kryshen/indyvon/async.clj	Sat Aug 28 02:37:30 2010 +0400
     2.3 @@ -0,0 +1,138 @@
     2.4 +;;
     2.5 +;; Copyright (C) 2010 Mikhail Kryshen <mikhail@kryshen.net>
     2.6 +;;
     2.7 +;; This file is part of Indyvon.
     2.8 +;;
     2.9 +
    2.10 +(ns net.kryshen.indyvon.async
    2.11 +  "Asynchronous drawing."
    2.12 +  (:use
    2.13 +   net.kryshen.indyvon.core)
    2.14 +  (:import
    2.15 +   (net.kryshen.indyvon.core Size Location)
    2.16 +   (java.awt Image)
    2.17 +   (java.awt.image BufferedImage)
    2.18 +   (java.util.concurrent ThreadPoolExecutor
    2.19 +                         ThreadPoolExecutor$DiscardOldestPolicy
    2.20 +                         ArrayBlockingQueue TimeUnit)))
    2.21 +
    2.22 +(defrecord Buffer [id image readers state])
    2.23 +;; Buffer states:
    2.24 +;;   :front, readers > 0
    2.25 +;;      being copied on screen
    2.26 +;;   :back
    2.27 +;;      being rendered to (offscreen)
    2.28 +;;   :fresh
    2.29 +;;      offscreen rendering finished
    2.30 +;;   :free
    2.31 +;;      not in use
    2.32 +
    2.33 +(defn- create-image [async-layer]
    2.34 +  (BufferedImage. (:width async-layer) (:height async-layer)
    2.35 +                  BufferedImage/TYPE_INT_ARGB))
    2.36 +
    2.37 +(defn- create-buffer [async-layer id]
    2.38 +  (Buffer. id (create-image async-layer) 0 :free))
    2.39 +
    2.40 +(defn- find-buffer [buffers & states]
    2.41 +  "Find a buffer with the one of the specified states given
    2.42 +   in the order of preference."
    2.43 +  (some identity
    2.44 +    (for [state states]
    2.45 +      (some #(if (= (:state %) state) % nil) buffers))))
    2.46 +
    2.47 +(defn- replace-buffer
    2.48 +  [buffers buffer]
    2.49 +  (conj (remove #(= (:id %) (:id buffer)) buffers)
    2.50 +        buffer))
    2.51 +
    2.52 +(defn- take-buffer [al type]
    2.53 +  (dosync
    2.54 +   (let [buffers @(:buffers al)
    2.55 +         b (case type
    2.56 +             :front (find-buffer buffers :front :fresh :free)
    2.57 +             :back (find-buffer buffers :free :fresh)
    2.58 +             (throw (IllegalArgumentException.)))
    2.59 +         readers (if (= type :front)
    2.60 +                   (inc (:readers b))
    2.61 +                   (:readers b))
    2.62 +         b (assoc b
    2.63 +             :state type
    2.64 +             :readers readers)]
    2.65 +     (alter (:buffers al) replace-buffer b)
    2.66 +     b)))
    2.67 +
    2.68 +(defn- release-buffer
    2.69 +  [al buffer]
    2.70 +  (dosync
    2.71 +   (let [buffers @(:buffers al)
    2.72 +         state (:state buffer)
    2.73 +         readers (if (= state :front)
    2.74 +                   (dec (:readers buffer))
    2.75 +                   (:readers buffer))
    2.76 +         state (cond
    2.77 +                (pos? readers) :front
    2.78 +                (= :back (:state buffer)) :fresh
    2.79 +                :default :free)
    2.80 +         buffers
    2.81 +          (if (= state :fresh)
    2.82 +            ;; Change state of all the other buffers from :fresh to :free.
    2.83 +            (reduce replace-buffer buffers 
    2.84 +                    (for [b buffers :when (= (:state b) :fresh)]
    2.85 +                      (assoc b :state :free)))
    2.86 +            buffers)
    2.87 +         buffers (replace-buffer buffers (assoc buffer
    2.88 +                                           :state state
    2.89 +                                           :readers readers))]
    2.90 +     (ref-set (:buffers al) buffers))))
    2.91 +  
    2.92 +(defmacro with-buffer [al type [name] & body]
    2.93 +  `(let [al# ~al
    2.94 +         ~name (take-buffer al# ~type)]
    2.95 +     (try
    2.96 +       ~@body
    2.97 +       (finally
    2.98 +        (release-buffer al# ~name)))))
    2.99 +
   2.100 +(defn- draw-offscreen [async-layer]
   2.101 +  ;;(Thread/sleep 1000)
   2.102 +  (with-buffer async-layer :back [b]
   2.103 +    (draw-root! (:content async-layer)
   2.104 +                (.getGraphics (:image b))
   2.105 +                (:width async-layer)
   2.106 +                (:height async-layer)
   2.107 +                dummy-event-dispatcher))
   2.108 +  (update async-layer))
   2.109 +
   2.110 +(defn- draw-offscreen-async [async-layer]
   2.111 +  (.execute ^ThreadPoolExecutor (:executor async-layer)
   2.112 +            #(draw-offscreen async-layer)))
   2.113 +
   2.114 +(defrecord AsyncLayer [content width height executor buffers]
   2.115 +  Layer
   2.116 +  (render! [layer]
   2.117 +     (repaint-on-update layer)
   2.118 +     (add-context-observer content (fn [_] (draw-offscreen-async layer)))
   2.119 +     (when-not @buffers
   2.120 +       ;; TODO: dynamic size, recreate buffers when size increases.
   2.121 +       (let [new-buffers (list (create-buffer layer 1)
   2.122 +                               (create-buffer layer 2))]
   2.123 +         (dosync
   2.124 +          (ref-set buffers new-buffers)))
   2.125 +       (draw-offscreen-async layer))
   2.126 +     (with-buffer layer :front [b]
   2.127 +       (.drawImage *graphics* ^Image (:image b) 0 0 nil)))
   2.128 +  (layer-size [layer]
   2.129 +     (Size. width height)))
   2.130 +
   2.131 +(defn async-layer 
   2.132 +  "Creates layer that draws the content asynchronously in an
   2.133 +   offscreen buffer."
   2.134 +  [content width height]
   2.135 +  (AsyncLayer. content width height
   2.136 +               (ThreadPoolExecutor.
   2.137 +                (int 1) (int 1)
   2.138 +                (long 0) TimeUnit/SECONDS
   2.139 +                (ArrayBlockingQueue. 1)
   2.140 +                (ThreadPoolExecutor$DiscardOldestPolicy.))
   2.141 +               (ref nil)))
     3.1 --- a/src/net/kryshen/indyvon/core.clj	Fri Aug 27 19:47:28 2010 +0400
     3.2 +++ b/src/net/kryshen/indyvon/core.clj	Sat Aug 28 02:37:30 2010 +0400
     3.3 @@ -6,7 +6,7 @@
     3.4  
     3.5  (ns net.kryshen.indyvon.core
     3.6    (:import
     3.7 -   (java.awt Graphics2D Component Color Font AWTEvent Shape)
     3.8 +   (java.awt Graphics2D RenderingHints Component Color Font AWTEvent Shape)
     3.9     (java.awt.geom AffineTransform Point2D$Double Rectangle2D$Double Area)
    3.10     (java.awt.event MouseListener MouseMotionListener)
    3.11     (java.awt.font FontRenderContext)))
    3.12 @@ -328,6 +328,15 @@
    3.13                 *width* width
    3.14                 *height* height
    3.15                 *clip* (Rectangle2D$Double. 0 0 width height)]
    3.16 +       ;; (.setRenderingHint graphics
    3.17 +       ;;                    RenderingHints/KEY_INTERPOLATION
    3.18 +       ;;                    RenderingHints/VALUE_INTERPOLATION_BILINEAR)
    3.19 +       ;; (.setRenderingHint graphics
    3.20 +       ;;                    RenderingHints/KEY_ALPHA_INTERPOLATION
    3.21 +       ;;                    RenderingHints/VALUE_ALPHA_INTERPOLATION_QUALITY)
    3.22 +       ;; (.setRenderingHint graphics
    3.23 +       ;;                    RenderingHints/KEY_ANTIALIASING
    3.24 +       ;;                    RenderingHints/VALUE_ANTIALIAS_ON)
    3.25         (apply-theme)
    3.26         (with-color (:back-color *theme*)
    3.27           (.fillRect graphics 0 0 width height))
     4.1 --- a/src/net/kryshen/indyvon/demo.clj	Fri Aug 27 19:47:28 2010 +0400
     4.2 +++ b/src/net/kryshen/indyvon/demo.clj	Sat Aug 28 02:37:30 2010 +0400
     4.3 @@ -88,9 +88,7 @@
     4.4  
     4.5  (defn show-frame [layer]
     4.6    (doto (JFrame. "Test")
     4.7 -    (.addWindowListener
     4.8 -     (proxy [java.awt.event.WindowAdapter] []
     4.9 -       (windowClosing [event] (.dispose frame))))
    4.10 +    (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE)
    4.11      (.. (getContentPane) (add (make-jpanel layer)))
    4.12      (.pack)
    4.13      (.setVisible true)))
     5.1 --- a/src/net/kryshen/indyvon/layers.clj	Fri Aug 27 19:47:28 2010 +0400
     5.2 +++ b/src/net/kryshen/indyvon/layers.clj	Sat Aug 28 02:37:30 2010 +0400
     5.3 @@ -7,17 +7,14 @@
     5.4  (ns net.kryshen.indyvon.layers
     5.5    "Implementations of Layer protocol."
     5.6    (:use
     5.7 -   net.kryshen.indyvon.core)
     5.8 +   (net.kryshen.indyvon core async))
     5.9    (:import
    5.10     (net.kryshen.indyvon.core Size Location)
    5.11     (java.lang.ref SoftReference)
    5.12     (java.awt Font Cursor Image Toolkit)
    5.13 -   (java.awt.image ImageObserver BufferedImage)
    5.14 -   (java.awt.font FontRenderContext TextLayout)
    5.15 -   (java.util.concurrent ThreadPoolExecutor
    5.16 -                         ThreadPoolExecutor$DiscardOldestPolicy
    5.17 -                         ArrayBlockingQueue TimeUnit)))
    5.18 -
    5.19 +   (java.awt.image ImageObserver)
    5.20 +   (java.awt.font FontRenderContext TextLayout)))
    5.21 +  
    5.22  ;; Define as macro to avoid unnecessary calculation of inner and outer
    5.23  ;; sizes in the first case.
    5.24  (defmacro align-xy [inner outer align first center last]
    5.25 @@ -173,76 +170,9 @@
    5.26                width (if (pos? width) width 1)
    5.27                height (if (pos? height) height 1)]
    5.28            (Size. width height))))))
    5.29 -
    5.30 -(defn- create-buffer [async-layer]
    5.31 -  (BufferedImage. (:width async-layer) (:height async-layer)
    5.32 -                  BufferedImage/TYPE_INT_ARGB))
    5.33 -
    5.34 -(defn- draw-offscreen [async-layer]
    5.35 -  ;;(Thread/sleep 3000)
    5.36 -  (let [buffers (:buffers async-layer)
    5.37 -        ^Image b (dosync
    5.38 -                  (let [b (peek @buffers)]
    5.39 -                    (alter buffers pop)
    5.40 -                    b))]
    5.41 -    (try
    5.42 -      ;; TODO: use operational event dispatcher.
    5.43 -      (draw-root! (:content async-layer)
    5.44 -                  (.getGraphics b)
    5.45 -                  (:width async-layer)
    5.46 -                  (:height async-layer)
    5.47 -                  dummy-event-dispatcher)
    5.48 -      (finally
    5.49 -       (dosync
    5.50 -        (alter buffers conj b)
    5.51 -        (ref-set (:updated async-layer) true))))
    5.52 -    (update async-layer)))
    5.53 -
    5.54 -(defn- draw-offscreen-async [async-layer]
    5.55 -  (.execute ^ThreadPoolExecutor (:executor async-layer)
    5.56 -            #(draw-offscreen async-layer)))
    5.57 -
    5.58 -(defrecord AsyncLayer [content width height executor buffers updated]
    5.59 -  Layer
    5.60 -  (render! [layer]
    5.61 -     (repaint-on-update layer)
    5.62 -     (add-context-observer content (fn [_] (draw-offscreen-async layer)))
    5.63 -     (when-not @buffers
    5.64 -       ;; TODO: dynamic size, recreate buffers when size increases.
    5.65 -       (let [new-buffers [(create-buffer layer) (create-buffer layer)]]
    5.66 -         (dosync
    5.67 -          (ref-set buffers new-buffers)))
    5.68 -       (draw-offscreen-async layer))
    5.69 -     (let [buffer (dosync
    5.70 -            (if @updated
    5.71 -              (let [b (peek @buffers)]
    5.72 -                (alter buffers pop)
    5.73 -                (ref-set updated false)
    5.74 -                b)
    5.75 -              (let [b (first @buffers)]
    5.76 -                (alter buffers subvec 1)
    5.77 -                b)))]
    5.78 -       (.drawImage *graphics* ^Image buffer 0 0 nil)
    5.79 -       (dosync
    5.80 -        (alter buffers #(vec (cons buffer %))))))
    5.81 -  (layer-size [layer]
    5.82 -     (Size. width height)))
    5.83 -
    5.84 -(defn async-layer 
    5.85 -  "Creates layer that draws the content asynchroniously in an
    5.86 -   offscreen buffer."
    5.87 -  [content width height]
    5.88 -  (AsyncLayer. content width height
    5.89 -               (ThreadPoolExecutor.
    5.90 -                (int 1) (int 1)
    5.91 -                (long 0) TimeUnit/SECONDS
    5.92 -                (ArrayBlockingQueue. 1)
    5.93 -                (ThreadPoolExecutor$DiscardOldestPolicy.))
    5.94 -               (ref nil)
    5.95 -               (ref false)))
    5.96      
    5.97  (defn miniature
    5.98 -  "Creates layer that asynchroniously renders view of the content
    5.99 +  "Creates layer that asynchronously renders view of the content
   5.100    scaled to the specified size."
   5.101    [content width height]
   5.102    (async-layer