view src/net/kryshen/indyvon/async.clj @ 68:9b511fe09867

Code cleanup, docstrings.
author Mikhail Kryshen <mikhail@kryshen.net>
date Sun, 29 Aug 2010 03:59:10 +0400
parents a19cf5007d14
children a823dd0c2736
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 ;; TODO: support different image types.
32 (BufferedImage. (:width async-layer) (:height async-layer)
33 BufferedImage/TYPE_INT_ARGB_PRE))
35 (defn- create-buffer [async-layer]
36 (Buffer. (Object.) (create-image async-layer) 0 :free))
38 (defn- find-buffer
39 "Find a buffer with the one of the specified states given
40 in the order of preference."
41 [buffers & states]
42 (some identity
43 (for [state states]
44 (some #(if (= (:state %) state) % nil) buffers))))
46 (defn- replace-buffer [buffers buffer]
47 (conj (remove #(= (:id %) (:id buffer)) buffers)
48 buffer))
50 (defn- take-buffer [al type]
51 (dosync
52 (let [buffers @(:buffers al)
53 b (case type
54 :front (find-buffer buffers :front :fresh :free)
55 :back (find-buffer buffers :free :fresh)
56 (throw (IllegalArgumentException.)))
57 readers (if (= type :front)
58 (inc (:readers b))
59 (:readers b))
60 b (assoc b
61 :state type
62 :readers readers)]
63 (alter (:buffers al) replace-buffer b)
64 b)))
66 (defn- release-buffer [al buffer]
67 (dosync
68 (let [state (:state buffer)
69 readers (if (= state :front)
70 (dec (:readers buffer))
71 (:readers buffer))
72 state (cond
73 (pos? readers) :front
74 (= :back (:state buffer)) :fresh
75 :default :free)]
76 (when (= state :fresh)
77 ;; Change state of the prefiously fresh buffer to :free.
78 (when-let [fresh (find-buffer @(:buffers al) :fresh)]
79 (alter (:buffers al) replace-buffer (assoc fresh
80 :state :free))))
81 (alter (:buffers al) replace-buffer (assoc buffer
82 :state state
83 :readers readers)))))
85 (defmacro with-buffer
86 {:private true}
87 [al type [name] & body]
88 `(let [al# ~al
89 ~name (take-buffer al# ~type)]
90 (try
91 ~@body
92 (finally
93 (release-buffer al# ~name)))))
95 (defn- draw-offscreen [async-layer]
96 ;;(Thread/sleep 1000)
97 (with-buffer async-layer :back [b]
98 (draw-root! (:content async-layer)
99 (.getGraphics ^Image (:image b))
100 (:width async-layer)
101 (:height async-layer)
102 dummy-event-dispatcher))
103 (update async-layer))
105 (defn- draw-offscreen-async [async-layer]
106 (.execute ^ThreadPoolExecutor (:executor async-layer)
107 #(draw-offscreen async-layer)))
109 (defrecord AsyncLayer [content width height executor buffers]
110 Layer
111 (render! [layer]
112 (repaint-on-update layer)
113 (add-context-observer content (fn [_] (draw-offscreen-async layer)))
114 (when-not @buffers
115 ;; TODO: dynamic size, recreate buffers when size increases.
116 (let [new-buffers (repeatedly 2 (partial create-buffer layer))]
117 (dosync
118 (ref-set buffers new-buffers)))
119 (draw-offscreen-async layer))
120 (with-buffer layer :front [b]
121 (.drawImage *graphics* ^Image (:image b) 0 0 nil)))
122 (layer-size [layer]
123 (Size. width height)))
125 (defn async-layer
126 "Creates layer that draws the content asynchronously using
127 offscreen buffer."
128 [content width height]
129 (AsyncLayer. content width height
130 (ThreadPoolExecutor.
131 (int 1) (int 1)
132 (long 0) TimeUnit/SECONDS
133 (ArrayBlockingQueue. 1)
134 (ThreadPoolExecutor$DiscardOldestPolicy.))
135 (ref nil)))