view src/net/kryshen/indyvon/async.clj @ 67:a19cf5007d14

Asynchronous drawing.
author Mikhail Kryshen <mikhail@kryshen.net>
date Sat, 28 Aug 2010 02:37:30 +0400
parents
children 9b511fe09867
line source
1 ;;
2 ;; Copyright (C) 2010 Mikhail Kryshen <mikhail@kryshen.net>
3 ;;
4 ;; This file is part of Indyvon.
5 ;;
7 (ns net.kryshen.indyvon.async
8 "Asynchronous drawing."
9 (:use
10 net.kryshen.indyvon.core)
11 (:import
12 (net.kryshen.indyvon.core Size Location)
13 (java.awt Image)
14 (java.awt.image BufferedImage)
15 (java.util.concurrent ThreadPoolExecutor
16 ThreadPoolExecutor$DiscardOldestPolicy
17 ArrayBlockingQueue TimeUnit)))
19 (defrecord Buffer [id image readers state])
20 ;; Buffer states:
21 ;; :front, readers > 0
22 ;; being copied on screen
23 ;; :back
24 ;; being rendered to (offscreen)
25 ;; :fresh
26 ;; offscreen rendering finished
27 ;; :free
28 ;; not in use
30 (defn- create-image [async-layer]
31 (BufferedImage. (:width async-layer) (:height async-layer)
32 BufferedImage/TYPE_INT_ARGB))
34 (defn- create-buffer [async-layer id]
35 (Buffer. id (create-image async-layer) 0 :free))
37 (defn- find-buffer [buffers & states]
38 "Find a buffer with the one of the specified states given
39 in the order of preference."
40 (some identity
41 (for [state states]
42 (some #(if (= (:state %) state) % nil) buffers))))
44 (defn- replace-buffer
45 [buffers buffer]
46 (conj (remove #(= (:id %) (:id buffer)) buffers)
47 buffer))
49 (defn- take-buffer [al type]
50 (dosync
51 (let [buffers @(:buffers al)
52 b (case type
53 :front (find-buffer buffers :front :fresh :free)
54 :back (find-buffer buffers :free :fresh)
55 (throw (IllegalArgumentException.)))
56 readers (if (= type :front)
57 (inc (:readers b))
58 (:readers b))
59 b (assoc b
60 :state type
61 :readers readers)]
62 (alter (:buffers al) replace-buffer b)
63 b)))
65 (defn- release-buffer
66 [al buffer]
67 (dosync
68 (let [buffers @(:buffers al)
69 state (:state buffer)
70 readers (if (= state :front)
71 (dec (:readers buffer))
72 (:readers buffer))
73 state (cond
74 (pos? readers) :front
75 (= :back (:state buffer)) :fresh
76 :default :free)
77 buffers
78 (if (= state :fresh)
79 ;; Change state of all the other buffers from :fresh to :free.
80 (reduce replace-buffer buffers
81 (for [b buffers :when (= (:state b) :fresh)]
82 (assoc b :state :free)))
83 buffers)
84 buffers (replace-buffer buffers (assoc buffer
85 :state state
86 :readers readers))]
87 (ref-set (:buffers al) buffers))))
89 (defmacro with-buffer [al type [name] & body]
90 `(let [al# ~al
91 ~name (take-buffer al# ~type)]
92 (try
93 ~@body
94 (finally
95 (release-buffer al# ~name)))))
97 (defn- draw-offscreen [async-layer]
98 ;;(Thread/sleep 1000)
99 (with-buffer async-layer :back [b]
100 (draw-root! (:content async-layer)
101 (.getGraphics (:image b))
102 (:width async-layer)
103 (:height async-layer)
104 dummy-event-dispatcher))
105 (update async-layer))
107 (defn- draw-offscreen-async [async-layer]
108 (.execute ^ThreadPoolExecutor (:executor async-layer)
109 #(draw-offscreen async-layer)))
111 (defrecord AsyncLayer [content width height executor buffers]
112 Layer
113 (render! [layer]
114 (repaint-on-update layer)
115 (add-context-observer content (fn [_] (draw-offscreen-async layer)))
116 (when-not @buffers
117 ;; TODO: dynamic size, recreate buffers when size increases.
118 (let [new-buffers (list (create-buffer layer 1)
119 (create-buffer layer 2))]
120 (dosync
121 (ref-set buffers new-buffers)))
122 (draw-offscreen-async layer))
123 (with-buffer layer :front [b]
124 (.drawImage *graphics* ^Image (:image b) 0 0 nil)))
125 (layer-size [layer]
126 (Size. width height)))
128 (defn async-layer
129 "Creates layer that draws the content asynchronously in an
130 offscreen buffer."
131 [content width height]
132 (AsyncLayer. content width height
133 (ThreadPoolExecutor.
134 (int 1) (int 1)
135 (long 0) TimeUnit/SECONDS
136 (ArrayBlockingQueue. 1)
137 (ThreadPoolExecutor$DiscardOldestPolicy.))
138 (ref nil)))