view src/net/kryshen/indyvon/async.clj @ 106:f42e2b9e1ad9

Removed Anchored protocol, "layer-size" function in Layer replaced with "geometry" which returns a structure describing both layer size and anchor point. Indyvon now requires Clojure 1.3.
author Mikhail Kryshen <mikhail@kryshen.net>
date Wed, 21 Sep 2011 02:27:11 +0300
parents 649d12b6c9ec
children f3dedece38f3
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 java.awt.GraphicsConfiguration
26 (java.awt Image AlphaComposite Transparency)
27 (java.awt.image BufferedImage)
28 (java.util.concurrent ThreadFactory ThreadPoolExecutor
29 ThreadPoolExecutor$DiscardOldestPolicy
30 ArrayBlockingQueue TimeUnit)))
32 (defrecord Buffer [id image readers state])
33 ;; Buffer states:
34 ;; :front, readers > 0
35 ;; being copied on screen
36 ;; :back
37 ;; being rendered to (offscreen)
38 ;; :fresh
39 ;; most recently updated
40 ;; :free
41 ;; not in use
43 (defn- create-image [async-layer ^GraphicsConfiguration device-conf]
44 ;; TODO: support different image types.
45 (.createCompatibleImage device-conf
46 (:width async-layer)
47 (:height async-layer)
48 Transparency/TRANSLUCENT))
50 (defn- create-buffer [async-layer device-conf]
51 (Buffer. (Object.) (create-image async-layer device-conf) 0 :free))
53 (defn- find-buffer
54 "Find a buffer with the one of the specified states given
55 in the order of preference."
56 [buffers & states]
57 (some identity
58 (for [state states]
59 (some #(if (= (:state %) state) % nil) buffers))))
61 (defn- replace-buffer [buffers buffer]
62 (conj (remove #(= (:id %) (:id buffer)) buffers)
63 buffer))
65 (defn- take-buffer [al type]
66 (dosync
67 (let [buffers @(:buffers al)
68 b (case type
69 :front (find-buffer buffers :front :fresh :free)
70 :back (find-buffer buffers :free :fresh)
71 (throw (IllegalArgumentException.)))
72 readers (if (= type :front)
73 (inc (:readers b))
74 (:readers b))
75 b (assoc b
76 :state type
77 :readers readers)]
78 (alter (:buffers al) replace-buffer b)
79 b)))
81 (defn- release-buffer [al buffer]
82 (dosync
83 (let [state (:state buffer)
84 readers (if (= state :front)
85 (dec (:readers buffer))
86 (:readers buffer))
87 fresh (delay (find-buffer @(:buffers al) :fresh))
88 state (cond
89 (pos? readers) :front
90 (= :back state) :fresh
91 @fresh :free
92 :default :fresh)]
93 (if (and (= state :fresh) @fresh)
94 ;; Change state of the prefiously fresh buffer to :free.
95 (alter (:buffers al)
96 replace-buffer (assoc @fresh
97 :state :free)))
98 (alter (:buffers al)
99 replace-buffer (assoc buffer
100 :state state
101 :readers readers)))))
103 (defmacro with-buffer
104 {:private true}
105 [al type [name] & body]
106 `(let [al# ~al
107 ~name (take-buffer al# ~type)]
108 (try
109 ~@body
110 (finally
111 (release-buffer al# ~name)))))
113 (defn- draw-offscreen [async-layer]
114 ;;(Thread/sleep 1000)
115 (with-buffer async-layer :back [b]
116 (let [g (.createGraphics ^BufferedImage (:image b))]
117 ;; Clear the buffer.
118 (.setComposite g AlphaComposite/Clear)
119 (.fillRect g 0 0 (:width async-layer) (:height async-layer))
120 (.setComposite g AlphaComposite/Src)
121 (draw-root! (:content async-layer)
122 g
123 (:width async-layer)
124 (:height async-layer)
125 ;; TODO: use operational event dispatcher.
126 dummy-event-dispatcher))
127 (update async-layer)))
129 (defn- draw-offscreen-async [async-layer]
130 (.execute ^ThreadPoolExecutor (:executor async-layer)
131 #(draw-offscreen async-layer)))
133 (defrecord AsyncLayer [content width height executor buffers]
134 Layer
135 (render! [layer]
136 (repaint-on-update layer)
137 (add-context-observer content (fn [_ _] (draw-offscreen-async layer)))
138 (when-not @buffers
139 ;; TODO: dynamic size, recreate buffers when size increases.
140 (let [device-conf (.getDeviceConfiguration *graphics*)
141 new-buffers (repeatedly 2
142 (partial create-buffer layer device-conf))]
143 (dosync
144 (ref-set buffers new-buffers)))
145 (draw-offscreen-async layer))
146 (with-buffer layer :front [b]
147 (.drawImage *graphics* ^Image (:image b) 0 0 nil)))
148 (geometry [layer]
149 (->Size width height)))
151 (defn- create-thread-factory [priority]
152 (reify
153 ThreadFactory
154 (newThread [_ runnable]
155 (let [thread (Thread. runnable)]
156 (when priority
157 (.setPriority thread priority))
158 (.setDaemon thread true)
159 thread))))
161 (defn- create-executor [priority]
162 (doto (ThreadPoolExecutor.
163 (int 1) (int 1)
164 (long 0) TimeUnit/SECONDS
165 (ArrayBlockingQueue. 1)
166 (ThreadPoolExecutor$DiscardOldestPolicy.))
167 (.setThreadFactory (create-thread-factory priority))))
169 (defn async-layer
170 "Creates layer that draws the content asynchronously using
171 offscreen buffer."
172 ([content width height]
173 (async-layer content width height nil))
174 ([content width height priority]
175 (AsyncLayer. content width height
176 (create-executor priority)
177 (ref nil))))