Mercurial > hg > indyvon
changeset 67:a19cf5007d14
Asynchronous drawing.
author | Mikhail Kryshen <mikhail@kryshen.net> |
---|---|
date | Sat, 28 Aug 2010 02:37:30 +0400 |
parents | a1999c1f7289 |
children | 9b511fe09867 |
files | project.clj src/net/kryshen/indyvon/async.clj src/net/kryshen/indyvon/core.clj src/net/kryshen/indyvon/demo.clj src/net/kryshen/indyvon/layers.clj |
diffstat | 5 files changed, 155 insertions(+), 79 deletions(-) [+] |
line diff
1.1 --- a/project.clj Fri Aug 27 19:47:28 2010 +0400 1.2 +++ b/project.clj Sat Aug 28 02:37:30 2010 +0400 1.3 @@ -4,6 +4,7 @@ 1.4 [org.clojure/clojure-contrib "1.2.0"]] 1.5 :dev-dependencies [[swank-clojure/swank-clojure "1.2.1"]] 1.6 :namespaces [net.kryshen.indyvon.core 1.7 + net.kryshen.indyvon.async 1.8 net.kryshen.indyvon.layers 1.9 net.kryshen.indyvon.component 1.10 net.kryshen.indyvon.demo])
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/src/net/kryshen/indyvon/async.clj Sat Aug 28 02:37:30 2010 +0400 2.3 @@ -0,0 +1,138 @@ 2.4 +;; 2.5 +;; Copyright (C) 2010 Mikhail Kryshen <mikhail@kryshen.net> 2.6 +;; 2.7 +;; This file is part of Indyvon. 2.8 +;; 2.9 + 2.10 +(ns net.kryshen.indyvon.async 2.11 + "Asynchronous drawing." 2.12 + (:use 2.13 + net.kryshen.indyvon.core) 2.14 + (:import 2.15 + (net.kryshen.indyvon.core Size Location) 2.16 + (java.awt Image) 2.17 + (java.awt.image BufferedImage) 2.18 + (java.util.concurrent ThreadPoolExecutor 2.19 + ThreadPoolExecutor$DiscardOldestPolicy 2.20 + ArrayBlockingQueue TimeUnit))) 2.21 + 2.22 +(defrecord Buffer [id image readers state]) 2.23 +;; Buffer states: 2.24 +;; :front, readers > 0 2.25 +;; being copied on screen 2.26 +;; :back 2.27 +;; being rendered to (offscreen) 2.28 +;; :fresh 2.29 +;; offscreen rendering finished 2.30 +;; :free 2.31 +;; not in use 2.32 + 2.33 +(defn- create-image [async-layer] 2.34 + (BufferedImage. (:width async-layer) (:height async-layer) 2.35 + BufferedImage/TYPE_INT_ARGB)) 2.36 + 2.37 +(defn- create-buffer [async-layer id] 2.38 + (Buffer. id (create-image async-layer) 0 :free)) 2.39 + 2.40 +(defn- find-buffer [buffers & states] 2.41 + "Find a buffer with the one of the specified states given 2.42 + in the order of preference." 2.43 + (some identity 2.44 + (for [state states] 2.45 + (some #(if (= (:state %) state) % nil) buffers)))) 2.46 + 2.47 +(defn- replace-buffer 2.48 + [buffers buffer] 2.49 + (conj (remove #(= (:id %) (:id buffer)) buffers) 2.50 + buffer)) 2.51 + 2.52 +(defn- take-buffer [al type] 2.53 + (dosync 2.54 + (let [buffers @(:buffers al) 2.55 + b (case type 2.56 + :front (find-buffer buffers :front :fresh :free) 2.57 + :back (find-buffer buffers :free :fresh) 2.58 + (throw (IllegalArgumentException.))) 2.59 + readers (if (= type :front) 2.60 + (inc (:readers b)) 2.61 + (:readers b)) 2.62 + b (assoc b 2.63 + :state type 2.64 + :readers readers)] 2.65 + (alter (:buffers al) replace-buffer b) 2.66 + b))) 2.67 + 2.68 +(defn- release-buffer 2.69 + [al buffer] 2.70 + (dosync 2.71 + (let [buffers @(:buffers al) 2.72 + state (:state buffer) 2.73 + readers (if (= state :front) 2.74 + (dec (:readers buffer)) 2.75 + (:readers buffer)) 2.76 + state (cond 2.77 + (pos? readers) :front 2.78 + (= :back (:state buffer)) :fresh 2.79 + :default :free) 2.80 + buffers 2.81 + (if (= state :fresh) 2.82 + ;; Change state of all the other buffers from :fresh to :free. 2.83 + (reduce replace-buffer buffers 2.84 + (for [b buffers :when (= (:state b) :fresh)] 2.85 + (assoc b :state :free))) 2.86 + buffers) 2.87 + buffers (replace-buffer buffers (assoc buffer 2.88 + :state state 2.89 + :readers readers))] 2.90 + (ref-set (:buffers al) buffers)))) 2.91 + 2.92 +(defmacro with-buffer [al type [name] & body] 2.93 + `(let [al# ~al 2.94 + ~name (take-buffer al# ~type)] 2.95 + (try 2.96 + ~@body 2.97 + (finally 2.98 + (release-buffer al# ~name))))) 2.99 + 2.100 +(defn- draw-offscreen [async-layer] 2.101 + ;;(Thread/sleep 1000) 2.102 + (with-buffer async-layer :back [b] 2.103 + (draw-root! (:content async-layer) 2.104 + (.getGraphics (:image b)) 2.105 + (:width async-layer) 2.106 + (:height async-layer) 2.107 + dummy-event-dispatcher)) 2.108 + (update async-layer)) 2.109 + 2.110 +(defn- draw-offscreen-async [async-layer] 2.111 + (.execute ^ThreadPoolExecutor (:executor async-layer) 2.112 + #(draw-offscreen async-layer))) 2.113 + 2.114 +(defrecord AsyncLayer [content width height executor buffers] 2.115 + Layer 2.116 + (render! [layer] 2.117 + (repaint-on-update layer) 2.118 + (add-context-observer content (fn [_] (draw-offscreen-async layer))) 2.119 + (when-not @buffers 2.120 + ;; TODO: dynamic size, recreate buffers when size increases. 2.121 + (let [new-buffers (list (create-buffer layer 1) 2.122 + (create-buffer layer 2))] 2.123 + (dosync 2.124 + (ref-set buffers new-buffers))) 2.125 + (draw-offscreen-async layer)) 2.126 + (with-buffer layer :front [b] 2.127 + (.drawImage *graphics* ^Image (:image b) 0 0 nil))) 2.128 + (layer-size [layer] 2.129 + (Size. width height))) 2.130 + 2.131 +(defn async-layer 2.132 + "Creates layer that draws the content asynchronously in an 2.133 + offscreen buffer." 2.134 + [content width height] 2.135 + (AsyncLayer. content width height 2.136 + (ThreadPoolExecutor. 2.137 + (int 1) (int 1) 2.138 + (long 0) TimeUnit/SECONDS 2.139 + (ArrayBlockingQueue. 1) 2.140 + (ThreadPoolExecutor$DiscardOldestPolicy.)) 2.141 + (ref nil)))
3.1 --- a/src/net/kryshen/indyvon/core.clj Fri Aug 27 19:47:28 2010 +0400 3.2 +++ b/src/net/kryshen/indyvon/core.clj Sat Aug 28 02:37:30 2010 +0400 3.3 @@ -6,7 +6,7 @@ 3.4 3.5 (ns net.kryshen.indyvon.core 3.6 (:import 3.7 - (java.awt Graphics2D Component Color Font AWTEvent Shape) 3.8 + (java.awt Graphics2D RenderingHints Component Color Font AWTEvent Shape) 3.9 (java.awt.geom AffineTransform Point2D$Double Rectangle2D$Double Area) 3.10 (java.awt.event MouseListener MouseMotionListener) 3.11 (java.awt.font FontRenderContext))) 3.12 @@ -328,6 +328,15 @@ 3.13 *width* width 3.14 *height* height 3.15 *clip* (Rectangle2D$Double. 0 0 width height)] 3.16 + ;; (.setRenderingHint graphics 3.17 + ;; RenderingHints/KEY_INTERPOLATION 3.18 + ;; RenderingHints/VALUE_INTERPOLATION_BILINEAR) 3.19 + ;; (.setRenderingHint graphics 3.20 + ;; RenderingHints/KEY_ALPHA_INTERPOLATION 3.21 + ;; RenderingHints/VALUE_ALPHA_INTERPOLATION_QUALITY) 3.22 + ;; (.setRenderingHint graphics 3.23 + ;; RenderingHints/KEY_ANTIALIASING 3.24 + ;; RenderingHints/VALUE_ANTIALIAS_ON) 3.25 (apply-theme) 3.26 (with-color (:back-color *theme*) 3.27 (.fillRect graphics 0 0 width height))
4.1 --- a/src/net/kryshen/indyvon/demo.clj Fri Aug 27 19:47:28 2010 +0400 4.2 +++ b/src/net/kryshen/indyvon/demo.clj Sat Aug 28 02:37:30 2010 +0400 4.3 @@ -88,9 +88,7 @@ 4.4 4.5 (defn show-frame [layer] 4.6 (doto (JFrame. "Test") 4.7 - (.addWindowListener 4.8 - (proxy [java.awt.event.WindowAdapter] [] 4.9 - (windowClosing [event] (.dispose frame)))) 4.10 + (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE) 4.11 (.. (getContentPane) (add (make-jpanel layer))) 4.12 (.pack) 4.13 (.setVisible true)))
5.1 --- a/src/net/kryshen/indyvon/layers.clj Fri Aug 27 19:47:28 2010 +0400 5.2 +++ b/src/net/kryshen/indyvon/layers.clj Sat Aug 28 02:37:30 2010 +0400 5.3 @@ -7,17 +7,14 @@ 5.4 (ns net.kryshen.indyvon.layers 5.5 "Implementations of Layer protocol." 5.6 (:use 5.7 - net.kryshen.indyvon.core) 5.8 + (net.kryshen.indyvon core async)) 5.9 (:import 5.10 (net.kryshen.indyvon.core Size Location) 5.11 (java.lang.ref SoftReference) 5.12 (java.awt Font Cursor Image Toolkit) 5.13 - (java.awt.image ImageObserver BufferedImage) 5.14 - (java.awt.font FontRenderContext TextLayout) 5.15 - (java.util.concurrent ThreadPoolExecutor 5.16 - ThreadPoolExecutor$DiscardOldestPolicy 5.17 - ArrayBlockingQueue TimeUnit))) 5.18 - 5.19 + (java.awt.image ImageObserver) 5.20 + (java.awt.font FontRenderContext TextLayout))) 5.21 + 5.22 ;; Define as macro to avoid unnecessary calculation of inner and outer 5.23 ;; sizes in the first case. 5.24 (defmacro align-xy [inner outer align first center last] 5.25 @@ -173,76 +170,9 @@ 5.26 width (if (pos? width) width 1) 5.27 height (if (pos? height) height 1)] 5.28 (Size. width height)))))) 5.29 - 5.30 -(defn- create-buffer [async-layer] 5.31 - (BufferedImage. (:width async-layer) (:height async-layer) 5.32 - BufferedImage/TYPE_INT_ARGB)) 5.33 - 5.34 -(defn- draw-offscreen [async-layer] 5.35 - ;;(Thread/sleep 3000) 5.36 - (let [buffers (:buffers async-layer) 5.37 - ^Image b (dosync 5.38 - (let [b (peek @buffers)] 5.39 - (alter buffers pop) 5.40 - b))] 5.41 - (try 5.42 - ;; TODO: use operational event dispatcher. 5.43 - (draw-root! (:content async-layer) 5.44 - (.getGraphics b) 5.45 - (:width async-layer) 5.46 - (:height async-layer) 5.47 - dummy-event-dispatcher) 5.48 - (finally 5.49 - (dosync 5.50 - (alter buffers conj b) 5.51 - (ref-set (:updated async-layer) true)))) 5.52 - (update async-layer))) 5.53 - 5.54 -(defn- draw-offscreen-async [async-layer] 5.55 - (.execute ^ThreadPoolExecutor (:executor async-layer) 5.56 - #(draw-offscreen async-layer))) 5.57 - 5.58 -(defrecord AsyncLayer [content width height executor buffers updated] 5.59 - Layer 5.60 - (render! [layer] 5.61 - (repaint-on-update layer) 5.62 - (add-context-observer content (fn [_] (draw-offscreen-async layer))) 5.63 - (when-not @buffers 5.64 - ;; TODO: dynamic size, recreate buffers when size increases. 5.65 - (let [new-buffers [(create-buffer layer) (create-buffer layer)]] 5.66 - (dosync 5.67 - (ref-set buffers new-buffers))) 5.68 - (draw-offscreen-async layer)) 5.69 - (let [buffer (dosync 5.70 - (if @updated 5.71 - (let [b (peek @buffers)] 5.72 - (alter buffers pop) 5.73 - (ref-set updated false) 5.74 - b) 5.75 - (let [b (first @buffers)] 5.76 - (alter buffers subvec 1) 5.77 - b)))] 5.78 - (.drawImage *graphics* ^Image buffer 0 0 nil) 5.79 - (dosync 5.80 - (alter buffers #(vec (cons buffer %)))))) 5.81 - (layer-size [layer] 5.82 - (Size. width height))) 5.83 - 5.84 -(defn async-layer 5.85 - "Creates layer that draws the content asynchroniously in an 5.86 - offscreen buffer." 5.87 - [content width height] 5.88 - (AsyncLayer. content width height 5.89 - (ThreadPoolExecutor. 5.90 - (int 1) (int 1) 5.91 - (long 0) TimeUnit/SECONDS 5.92 - (ArrayBlockingQueue. 1) 5.93 - (ThreadPoolExecutor$DiscardOldestPolicy.)) 5.94 - (ref nil) 5.95 - (ref false))) 5.96 5.97 (defn miniature 5.98 - "Creates layer that asynchroniously renders view of the content 5.99 + "Creates layer that asynchronously renders view of the content 5.100 scaled to the specified size." 5.101 [content width height] 5.102 (async-layer