view src/indyvon/async.clj @ 185:83241889daac

BorderBox: properly wrap Geometry of the center view.
author Mikhail Kryshen <mikhail@kryshen.net>
date Tue, 21 Nov 2017 19:06:25 +0300
parents eb1bedf22731
children
line source
1 ;;
2 ;; Copyright 2010-2015 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 indyvon.async
21 "Asynchronous drawing."
22 (:use
23 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 Buffers [;; BufferedImage for copying on screen.
33 front
34 ;; Number of processes using the front buffer.
35 front-readers
36 ;; BufferedImage used for asynchronous drawing.
37 back
38 ;; True after drawing to the back buffer is finished.
39 back-ready
40 ;; True after the buffers were flipped,
41 ;; indicates that AsyncView needs to be redrawn.
42 flipped
43 ;; Used to synchronize initialization of the buffers.
44 new])
46 (defn- create-image [async-view ^GraphicsConfiguration device-conf]
47 ;; TODO: support different image types.
48 (.createCompatibleImage device-conf
49 (:width async-view)
50 (:height async-view)
51 Transparency/TRANSLUCENT))
53 (defn- create-buffers [async-view device-conf]
54 (->Buffers (create-image async-view device-conf)
55 0
56 (create-image async-view device-conf)
57 false
58 false
59 true))
61 (defn- maybe-flip [buffers]
62 (if (and (:back-ready buffers)
63 (zero? (:front-readers buffers)))
64 (assoc buffers
65 :front (:back buffers)
66 :back (:front buffers)
67 :back-ready false
68 :flipped true)
69 (assoc buffers
70 :flipped false)))
72 (defn- apply-flip [buffers f & args]
73 (maybe-flip (apply f buffers args)))
75 (defn- take-front! [av]
76 (-> (:buffers av)
77 (swap! update-in [:front-readers] inc)
78 :front))
80 (defn- release-front! [av]
81 (when (-> (:buffers av)
82 (swap! apply-flip update-in [:front-readers] dec)
83 :flipped)
84 (notify! av)))
86 (defn- take-back! [av]
87 (-> (:buffers av)
88 (swap! assoc :back-ready false)
89 :back))
91 (defn- release-back! [av]
92 (when (-> (:buffers av)
93 (swap! apply-flip assoc :back-ready true)
94 :flipped)
95 (notify! av)))
97 (defn- draw-offscreen! [async-view]
98 ;;(Thread/sleep 1000)
99 (let [b (take-back! async-view)
100 g (.createGraphics ^BufferedImage b)]
101 ;; Clear the buffer.
102 (.setComposite g AlphaComposite/Clear)
103 (.fillRect g 0 0 (:width async-view) (:height async-view))
104 (.setComposite g AlphaComposite/Src)
105 (draw-scene! (:scene async-view)
106 g
107 (:width async-view)
108 (:height async-view))
109 ;; Will not be called if an exception is thrown in the code above.
110 (release-back! async-view)))
112 (defn- pool-execute! [async-view f]
113 (.execute ^ThreadPoolExecutor (:executor async-view) f))
115 (defn- ensure-buffers! [async-view device-conf]
116 (let [buffers (:buffers async-view)]
117 (when (and (not @buffers)
118 (:new (swap! buffers
119 #(if %
120 (assoc % :new false)
121 (create-buffers async-view device-conf)))))
122 (let [bound-draw! (bound-fn* draw-offscreen!)
123 async-draw! #(pool-execute! % (partial bound-draw! %))]
124 (add-observer! async-view
125 (:scene async-view)
126 ;; Must not hold onto async-view.
127 (fn [a _] (async-draw! a)))
128 (async-draw! async-view)))))
130 (defrecord AsyncView [scene width height executor buffers]
131 View
132 (render! [view]
133 (let [g *graphics*]
134 (ensure-buffers! view (.getDeviceConfiguration g))
135 (let [^Image b (take-front! view)]
136 (try
137 (.drawImage g b 0 0 nil)
138 (finally
139 (release-front! view))))
140 (repaint-on-update! view)))
141 (geometry [view]
142 (->Size width height)))
144 (defn- create-thread-factory [priority]
145 (reify
146 ThreadFactory
147 (newThread [_ runnable]
148 (let [thread (Thread. runnable)]
149 (when priority
150 (.setPriority thread priority))
151 (.setDaemon thread true)
152 thread))))
154 (defn- create-executor [priority]
155 (doto (ThreadPoolExecutor.
156 (int 1) (int 1)
157 (long 0) TimeUnit/SECONDS
158 (ArrayBlockingQueue. 1)
159 (ThreadPoolExecutor$DiscardOldestPolicy.))
160 (.setThreadFactory (create-thread-factory priority))))
162 (defn async-view
163 "Creates a View that draws the content asynchronously using an
164 offscreen buffer."
165 ([width height content]
166 (async-view width height nil content))
167 ([width height priority content]
168 ;; TODO: use operational event dispatcher.
169 (->AsyncView (make-scene content)
170 width
171 height
172 (create-executor priority)
173 (atom nil))))