view src/net/kryshen/indyvon/async.clj @ 94:649d12b6c9ec

Async layer: create-image does not depend on graphics context.
author Mikhail Kryshen <mikhail@kryshen.net>
date Thu, 03 Mar 2011 03:13:14 +0300
parents 017f5f14b876
children 491152048c89 f42e2b9e1ad9
line source
1 ;;
2 ;; Copyright 2010, 2011 Mikhail Kryshen <mikhail@kryshen.net>
3 ;;
4 ;; This file is part of Indyvon.
5 ;;
6 ;; Indyvon is free software: you can redistribute it and/or modify it
7 ;; under the terms of the GNU Lesser General Public License version 3
8 ;; only, as published by the Free Software Foundation.
9 ;;
10 ;; Indyvon is distributed in the hope that it will be useful, but
11 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;; Lesser General Public License for more details.
14 ;;
15 ;; You should have received a copy of the GNU Lesser General Public
16 ;; License along with Indyvon. If not, see
17 ;; <http://www.gnu.org/licenses/>.
18 ;;
20 (ns net.kryshen.indyvon.async
21 "Asynchronous drawing."
22 (:use
23 net.kryshen.indyvon.core)
24 (:import
25 (net.kryshen.indyvon.core Size Location)
26 java.awt.GraphicsConfiguration
27 (java.awt Image AlphaComposite Transparency)
28 (java.awt.image BufferedImage)
29 (java.util.concurrent ThreadFactory ThreadPoolExecutor
30 ThreadPoolExecutor$DiscardOldestPolicy
31 ArrayBlockingQueue TimeUnit)))
33 (defrecord Buffer [id image readers state])
34 ;; Buffer states:
35 ;; :front, readers > 0
36 ;; being copied on screen
37 ;; :back
38 ;; being rendered to (offscreen)
39 ;; :fresh
40 ;; most recently updated
41 ;; :free
42 ;; not in use
44 (defn- create-image [async-layer ^GraphicsConfiguration device-conf]
45 ;; TODO: support different image types.
46 (.createCompatibleImage device-conf
47 (:width async-layer)
48 (:height async-layer)
49 Transparency/TRANSLUCENT))
51 (defn- create-buffer [async-layer device-conf]
52 (Buffer. (Object.) (create-image async-layer device-conf) 0 :free))
54 (defn- find-buffer
55 "Find a buffer with the one of the specified states given
56 in the order of preference."
57 [buffers & states]
58 (some identity
59 (for [state states]
60 (some #(if (= (:state %) state) % nil) buffers))))
62 (defn- replace-buffer [buffers buffer]
63 (conj (remove #(= (:id %) (:id buffer)) buffers)
64 buffer))
66 (defn- take-buffer [al type]
67 (dosync
68 (let [buffers @(:buffers al)
69 b (case type
70 :front (find-buffer buffers :front :fresh :free)
71 :back (find-buffer buffers :free :fresh)
72 (throw (IllegalArgumentException.)))
73 readers (if (= type :front)
74 (inc (:readers b))
75 (:readers b))
76 b (assoc b
77 :state type
78 :readers readers)]
79 (alter (:buffers al) replace-buffer b)
80 b)))
82 (defn- release-buffer [al buffer]
83 (dosync
84 (let [state (:state buffer)
85 readers (if (= state :front)
86 (dec (:readers buffer))
87 (:readers buffer))
88 fresh (delay (find-buffer @(:buffers al) :fresh))
89 state (cond
90 (pos? readers) :front
91 (= :back state) :fresh
92 @fresh :free
93 :default :fresh)]
94 (if (and (= state :fresh) @fresh)
95 ;; Change state of the prefiously fresh buffer to :free.
96 (alter (:buffers al)
97 replace-buffer (assoc @fresh
98 :state :free)))
99 (alter (:buffers al)
100 replace-buffer (assoc buffer
101 :state state
102 :readers readers)))))
104 (defmacro with-buffer
105 {:private true}
106 [al type [name] & body]
107 `(let [al# ~al
108 ~name (take-buffer al# ~type)]
109 (try
110 ~@body
111 (finally
112 (release-buffer al# ~name)))))
114 (defn- draw-offscreen [async-layer]
115 ;;(Thread/sleep 1000)
116 (with-buffer async-layer :back [b]
117 (let [g (.createGraphics ^BufferedImage (:image b))]
118 ;; Clear the buffer.
119 (.setComposite g AlphaComposite/Clear)
120 (.fillRect g 0 0 (:width async-layer) (:height async-layer))
121 (.setComposite g AlphaComposite/Src)
122 (draw-root! (:content async-layer)
123 g
124 (:width async-layer)
125 (:height async-layer)
126 ;; TODO: use operational event dispatcher.
127 dummy-event-dispatcher))
128 (update async-layer)))
130 (defn- draw-offscreen-async [async-layer]
131 (.execute ^ThreadPoolExecutor (:executor async-layer)
132 #(draw-offscreen async-layer)))
134 (defrecord AsyncLayer [content width height executor buffers]
135 Layer
136 (render! [layer]
137 (repaint-on-update layer)
138 (add-context-observer content (fn [_ _] (draw-offscreen-async layer)))
139 (when-not @buffers
140 ;; TODO: dynamic size, recreate buffers when size increases.
141 (let [device-conf (.getDeviceConfiguration *graphics*)
142 new-buffers (repeatedly 2
143 (partial create-buffer layer device-conf))]
144 (dosync
145 (ref-set buffers new-buffers)))
146 (draw-offscreen-async layer))
147 (with-buffer layer :front [b]
148 (.drawImage *graphics* ^Image (:image b) 0 0 nil)))
149 (layer-size [layer]
150 (Size. width height)))
152 (defn- create-thread-factory [priority]
153 (reify
154 ThreadFactory
155 (newThread [_ runnable]
156 (let [thread (Thread. runnable)]
157 (when priority
158 (.setPriority thread priority))
159 (.setDaemon thread true)
160 thread))))
162 (defn- create-executor [priority]
163 (doto (ThreadPoolExecutor.
164 (int 1) (int 1)
165 (long 0) TimeUnit/SECONDS
166 (ArrayBlockingQueue. 1)
167 (ThreadPoolExecutor$DiscardOldestPolicy.))
168 (.setThreadFactory (create-thread-factory priority))))
170 (defn async-layer
171 "Creates layer that draws the content asynchronously using
172 offscreen buffer."
173 ([content width height]
174 (async-layer content width height nil))
175 ([content width height priority]
176 (AsyncLayer. content width height
177 (create-executor priority)
178 (ref nil))))