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 wrap: on
line diff
--- a/project.clj Fri Aug 27 19:47:28 2010 +0400 +++ b/project.clj Sat Aug 28 02:37:30 2010 +0400 @@ -4,6 +4,7 @@ [org.clojure/clojure-contrib "1.2.0"]] :dev-dependencies [[swank-clojure/swank-clojure "1.2.1"]] :namespaces [net.kryshen.indyvon.core + net.kryshen.indyvon.async net.kryshen.indyvon.layers net.kryshen.indyvon.component net.kryshen.indyvon.demo])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/net/kryshen/indyvon/async.clj Sat Aug 28 02:37:30 2010 +0400 @@ -0,0 +1,138 @@ +;; +;; Copyright (C) 2010 Mikhail Kryshen <mikhail@kryshen.net> +;; +;; This file is part of Indyvon. +;; + +(ns net.kryshen.indyvon.async + "Asynchronous drawing." + (:use + net.kryshen.indyvon.core) + (:import + (net.kryshen.indyvon.core Size Location) + (java.awt Image) + (java.awt.image BufferedImage) + (java.util.concurrent ThreadPoolExecutor + ThreadPoolExecutor$DiscardOldestPolicy + ArrayBlockingQueue TimeUnit))) + +(defrecord Buffer [id image readers state]) +;; Buffer states: +;; :front, readers > 0 +;; being copied on screen +;; :back +;; being rendered to (offscreen) +;; :fresh +;; offscreen rendering finished +;; :free +;; not in use + +(defn- create-image [async-layer] + (BufferedImage. (:width async-layer) (:height async-layer) + BufferedImage/TYPE_INT_ARGB)) + +(defn- create-buffer [async-layer id] + (Buffer. id (create-image async-layer) 0 :free)) + +(defn- find-buffer [buffers & states] + "Find a buffer with the one of the specified states given + in the order of preference." + (some identity + (for [state states] + (some #(if (= (:state %) state) % nil) buffers)))) + +(defn- replace-buffer + [buffers buffer] + (conj (remove #(= (:id %) (:id buffer)) buffers) + buffer)) + +(defn- take-buffer [al type] + (dosync + (let [buffers @(:buffers al) + b (case type + :front (find-buffer buffers :front :fresh :free) + :back (find-buffer buffers :free :fresh) + (throw (IllegalArgumentException.))) + readers (if (= type :front) + (inc (:readers b)) + (:readers b)) + b (assoc b + :state type + :readers readers)] + (alter (:buffers al) replace-buffer b) + b))) + +(defn- release-buffer + [al buffer] + (dosync + (let [buffers @(:buffers al) + state (:state buffer) + readers (if (= state :front) + (dec (:readers buffer)) + (:readers buffer)) + state (cond + (pos? readers) :front + (= :back (:state buffer)) :fresh + :default :free) + buffers + (if (= state :fresh) + ;; Change state of all the other buffers from :fresh to :free. + (reduce replace-buffer buffers + (for [b buffers :when (= (:state b) :fresh)] + (assoc b :state :free))) + buffers) + buffers (replace-buffer buffers (assoc buffer + :state state + :readers readers))] + (ref-set (:buffers al) buffers)))) + +(defmacro with-buffer [al type [name] & body] + `(let [al# ~al + ~name (take-buffer al# ~type)] + (try + ~@body + (finally + (release-buffer al# ~name))))) + +(defn- draw-offscreen [async-layer] + ;;(Thread/sleep 1000) + (with-buffer async-layer :back [b] + (draw-root! (:content async-layer) + (.getGraphics (:image b)) + (:width async-layer) + (:height async-layer) + dummy-event-dispatcher)) + (update async-layer)) + +(defn- draw-offscreen-async [async-layer] + (.execute ^ThreadPoolExecutor (:executor async-layer) + #(draw-offscreen async-layer))) + +(defrecord AsyncLayer [content width height executor buffers] + Layer + (render! [layer] + (repaint-on-update layer) + (add-context-observer content (fn [_] (draw-offscreen-async layer))) + (when-not @buffers + ;; TODO: dynamic size, recreate buffers when size increases. + (let [new-buffers (list (create-buffer layer 1) + (create-buffer layer 2))] + (dosync + (ref-set buffers new-buffers))) + (draw-offscreen-async layer)) + (with-buffer layer :front [b] + (.drawImage *graphics* ^Image (:image b) 0 0 nil))) + (layer-size [layer] + (Size. width height))) + +(defn async-layer + "Creates layer that draws the content asynchronously in an + offscreen buffer." + [content width height] + (AsyncLayer. content width height + (ThreadPoolExecutor. + (int 1) (int 1) + (long 0) TimeUnit/SECONDS + (ArrayBlockingQueue. 1) + (ThreadPoolExecutor$DiscardOldestPolicy.)) + (ref nil)))
--- a/src/net/kryshen/indyvon/core.clj Fri Aug 27 19:47:28 2010 +0400 +++ b/src/net/kryshen/indyvon/core.clj Sat Aug 28 02:37:30 2010 +0400 @@ -6,7 +6,7 @@ (ns net.kryshen.indyvon.core (:import - (java.awt Graphics2D Component Color Font AWTEvent Shape) + (java.awt Graphics2D RenderingHints Component Color Font AWTEvent Shape) (java.awt.geom AffineTransform Point2D$Double Rectangle2D$Double Area) (java.awt.event MouseListener MouseMotionListener) (java.awt.font FontRenderContext))) @@ -328,6 +328,15 @@ *width* width *height* height *clip* (Rectangle2D$Double. 0 0 width height)] + ;; (.setRenderingHint graphics + ;; RenderingHints/KEY_INTERPOLATION + ;; RenderingHints/VALUE_INTERPOLATION_BILINEAR) + ;; (.setRenderingHint graphics + ;; RenderingHints/KEY_ALPHA_INTERPOLATION + ;; RenderingHints/VALUE_ALPHA_INTERPOLATION_QUALITY) + ;; (.setRenderingHint graphics + ;; RenderingHints/KEY_ANTIALIASING + ;; RenderingHints/VALUE_ANTIALIAS_ON) (apply-theme) (with-color (:back-color *theme*) (.fillRect graphics 0 0 width height))
--- a/src/net/kryshen/indyvon/demo.clj Fri Aug 27 19:47:28 2010 +0400 +++ b/src/net/kryshen/indyvon/demo.clj Sat Aug 28 02:37:30 2010 +0400 @@ -88,9 +88,7 @@ (defn show-frame [layer] (doto (JFrame. "Test") - (.addWindowListener - (proxy [java.awt.event.WindowAdapter] [] - (windowClosing [event] (.dispose frame)))) + (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE) (.. (getContentPane) (add (make-jpanel layer))) (.pack) (.setVisible true)))
--- a/src/net/kryshen/indyvon/layers.clj Fri Aug 27 19:47:28 2010 +0400 +++ b/src/net/kryshen/indyvon/layers.clj Sat Aug 28 02:37:30 2010 +0400 @@ -7,17 +7,14 @@ (ns net.kryshen.indyvon.layers "Implementations of Layer protocol." (:use - net.kryshen.indyvon.core) + (net.kryshen.indyvon core async)) (:import (net.kryshen.indyvon.core Size Location) (java.lang.ref SoftReference) (java.awt Font Cursor Image Toolkit) - (java.awt.image ImageObserver BufferedImage) - (java.awt.font FontRenderContext TextLayout) - (java.util.concurrent ThreadPoolExecutor - ThreadPoolExecutor$DiscardOldestPolicy - ArrayBlockingQueue TimeUnit))) - + (java.awt.image ImageObserver) + (java.awt.font FontRenderContext TextLayout))) + ;; Define as macro to avoid unnecessary calculation of inner and outer ;; sizes in the first case. (defmacro align-xy [inner outer align first center last] @@ -173,76 +170,9 @@ width (if (pos? width) width 1) height (if (pos? height) height 1)] (Size. width height)))))) - -(defn- create-buffer [async-layer] - (BufferedImage. (:width async-layer) (:height async-layer) - BufferedImage/TYPE_INT_ARGB)) - -(defn- draw-offscreen [async-layer] - ;;(Thread/sleep 3000) - (let [buffers (:buffers async-layer) - ^Image b (dosync - (let [b (peek @buffers)] - (alter buffers pop) - b))] - (try - ;; TODO: use operational event dispatcher. - (draw-root! (:content async-layer) - (.getGraphics b) - (:width async-layer) - (:height async-layer) - dummy-event-dispatcher) - (finally - (dosync - (alter buffers conj b) - (ref-set (:updated async-layer) true)))) - (update async-layer))) - -(defn- draw-offscreen-async [async-layer] - (.execute ^ThreadPoolExecutor (:executor async-layer) - #(draw-offscreen async-layer))) - -(defrecord AsyncLayer [content width height executor buffers updated] - Layer - (render! [layer] - (repaint-on-update layer) - (add-context-observer content (fn [_] (draw-offscreen-async layer))) - (when-not @buffers - ;; TODO: dynamic size, recreate buffers when size increases. - (let [new-buffers [(create-buffer layer) (create-buffer layer)]] - (dosync - (ref-set buffers new-buffers))) - (draw-offscreen-async layer)) - (let [buffer (dosync - (if @updated - (let [b (peek @buffers)] - (alter buffers pop) - (ref-set updated false) - b) - (let [b (first @buffers)] - (alter buffers subvec 1) - b)))] - (.drawImage *graphics* ^Image buffer 0 0 nil) - (dosync - (alter buffers #(vec (cons buffer %)))))) - (layer-size [layer] - (Size. width height))) - -(defn async-layer - "Creates layer that draws the content asynchroniously in an - offscreen buffer." - [content width height] - (AsyncLayer. content width height - (ThreadPoolExecutor. - (int 1) (int 1) - (long 0) TimeUnit/SECONDS - (ArrayBlockingQueue. 1) - (ThreadPoolExecutor$DiscardOldestPolicy.)) - (ref nil) - (ref false))) (defn miniature - "Creates layer that asynchroniously renders view of the content + "Creates layer that asynchronously renders view of the content scaled to the specified size." [content width height] (async-layer