001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.perseus.model;
028:
029: import com.sun.perseus.j2d.RenderGraphics;
030:
031: import java.io.InputStream;
032:
033: import com.sun.perseus.util.RunnableQueue;
034: import com.sun.perseus.util.RunnableQueue.RunnableHandler;
035:
036: import com.sun.perseus.model.DirtyAreaManager.TileElement;
037:
038: import com.sun.perseus.j2d.RGB;
039: import com.sun.perseus.j2d.Transform;
040:
041: /**
042: * <p>The <code>SimpleCanvasManager</code> class is responsible for
043: * keeping the rendering of a <code>ModelNode</code> tree on a
044: * <code>RenderGraphics</code> current.</p>
045: *
046: * <p>Specifically, the <code>SimpleCanvasManager</code> listens to
047: * update events in a <code>ModelNode</code> tree and
048: * triggers repaint into the <code>RenderGraphics</code> when
049: * necessary.</p>
050: *
051: * <p>The <code>SimpleCanvasManager</code> does not handle documents
052: * that have not been loaded and does nothing on <code>loadComplete</code>
053: * calls.</p>
054: *
055: * @version $Id: SimpleCanvasManager.java,v 1.15 2006/06/29 10:47:34 ln156897 Exp $
056: */
057: public class SimpleCanvasManager implements UpdateListener,
058: RunnableHandler {
059: /**
060: * The object used as a lock to synchronize access
061: * to the paint surface.
062: */
063: public final Object lock = new Object();
064:
065: /**
066: * This flag should be used by the consumer of the offscreen
067: * buffer to let the SimpleCanvasManager know when the updated
068: * offscreen has been consumed.
069: */
070: protected boolean canvasConsumed = true;
071:
072: /**
073: * The <code>RenderGraphics</code> which this
074: * <code>SimpleCanvasManager</code> keeps up to date
075: * with its associated model changes.
076: */
077: protected RenderGraphics rg;
078:
079: /**
080: * Model which this <code>SimpleCanvasManager</code> renders to
081: * the associated <code>RenderGraphics</code>
082: */
083: protected DocumentNode documentNode;
084:
085: /**
086: * Listens to canvas updates
087: */
088: protected CanvasUpdateListener canvasUpdateListener;
089:
090: /**
091: * Controls whether a repaint is needed or not
092: */
093: protected boolean needRepaint;
094:
095: /**
096: * The color used to clear the canvas.
097: */
098: protected RGB clearPaint = RGB.white;
099:
100: /**
101: * Dirty area manager. May be null.
102: */
103: protected DirtyAreaManager dirtyAreaManager;
104:
105: /**
106: * When off, no updates are made to the rendering surface.
107: */
108: protected boolean isOff = false;
109:
110: /**
111: * @param rg the <code>RenderGraphics</code> which this
112: * instance will keep up to date with the
113: * model changes.
114: * @param documentNode the <code>DocumentNode</code>, root of the
115: * tree that this <code>SimpleCanvasManager</code> will
116: * draw and keep current on the <code>RenderGraphics</code>
117: * @param canvasUpdateListener the <code>CanvasUpdateListener</code>
118: * which listens to completed updates on the associated
119: * <code>RenderGraphics</code>
120: *
121: * @throws IllegalArgumentException if rg, documentNode or listener is null.
122: */
123: public SimpleCanvasManager(final RenderGraphics rg,
124: final DocumentNode documentNode,
125: final CanvasUpdateListener canvasUpdateListener) {
126:
127: if (rg == null || documentNode == null
128: || canvasUpdateListener == null) {
129: throw new IllegalArgumentException("RenderGraphics : " + rg
130: + " DocumentNode : " + documentNode
131: + " CanvasUpdateListener : " + canvasUpdateListener);
132: }
133:
134: this .rg = rg;
135: this .documentNode = documentNode;
136: this .canvasUpdateListener = canvasUpdateListener;
137: this .documentNode.setUpdateListener(this );
138: this .dirtyAreaManager = new DirtyAreaManager(documentNode);
139: }
140:
141: /**
142: * @param rg the <code>RenderGraphics</code> which this
143: * instance should now update. Setting the
144: * <code>RenderGraphics</code> causes a full repaint
145: * to be scheduled.
146: * @throws IllegalArgumentException if rg is null.
147: */
148: public void setRenderGraphics(final RenderGraphics rg) {
149: if (rg == null) {
150: throw new IllegalArgumentException();
151: }
152:
153: synchronized (lock) {
154: this .rg = rg;
155:
156: // Repaint the documentNode into the new graphics
157: needRepaint = true;
158:
159: // The new buffer has not been painted and has not been
160: // consumed.
161: canvasConsumed = false;
162: }
163: }
164:
165: /**
166: * @return the RenderGraphics to the canvas managed by this
167: * SimpleCanvasManager.
168: */
169: public RenderGraphics getRenderGraphics() {
170: return rg;
171: }
172:
173: /**
174: * Should be called by the SimpleCanvasManager user to notify the
175: * SimpleCanvasManager when an update to the canvas has been consumed.
176: */
177: public void consume() {
178: synchronized (lock) {
179: canvasConsumed = true;
180: lock.notifyAll();
181: }
182: }
183:
184: /**
185: * Invoked when a node has been inserted into the tree
186: *
187: * @param node the newly inserted node
188: */
189: public void nodeInserted(final ModelNode node) {
190: if (DirtyAreaManager.ON) {
191: dirtyAreaManager.nodeInserted(node);
192: }
193: needRepaint = true;
194: }
195:
196: /**
197: * Invoked when a node is about to be modified. This will
198: * be used in the future to track dirty areas.
199: *
200: * @param node the node which is about to be modified
201: */
202: public void modifyingNode(final ModelNode node) {
203: if ((node.hasNodeRendering() || node.hasDescendants())
204: && (node.canRenderState == 0)) {
205: needRepaint = true;
206: }
207: }
208:
209: /**
210: * Invoked when a node's rendering is about to be modified
211: *
212: * @param node the node which is about to be modified
213: */
214: public void modifyingNodeRendering(ModelNode node) {
215: // Note that this is redundant with the the check done in
216: // DirtyAreaManager. However, this is needed because DirtyAreaManager
217: // is sometimes used stand-alone (e.g. from ScalableGraphics).
218: // Having the call in the if statement makes the check redundant only
219: // when evaluating to true.
220: if (DirtyAreaManager.ON) {
221: dirtyAreaManager.modifyingNodeRendering(node);
222: }
223: }
224:
225: /**
226: * Invoked when a node modification completed.
227: *
228: * @param node the node which was just modified.
229: */
230: public void modifiedNode(final ModelNode node) {
231: if (!needRepaint
232: && (node.hasNodeRendering() || node.hasDescendants())) {
233: needRepaint = true;
234: }
235: }
236:
237: /**
238: * Invoked when the input node has finished loading.
239: *
240: * @param node the <code>node</code> for which loading
241: * is complete.
242: */
243: public void loadComplete(final ModelNode node) {
244: // Do nothing.
245: }
246:
247: /**
248: * Invoked when a document error happened before finishing loading.
249: *
250: * @param documentNode the <code>DocumentNode</code> for which loading
251: * has failed.
252: * @param error the exception which describes the reason why loading
253: * failed.
254: */
255: public void loadingFailed(final DocumentNode documentNode,
256: final Exception error) {
257: // Do nothing.
258: }
259:
260: /**
261: * Invoked when the document starts loading
262: *
263: * @param documentNode the <code>DocumentNode</code> for which loading
264: * is starting
265: * @param is the <code>InputStream</code> from which SVG content
266: * is loaded.
267: */
268: public void loadStarting(final DocumentNode documentNode,
269: final InputStream is) {
270: }
271:
272: /**
273: * Invoked when the input node has started loading
274: *
275: * @param node the <code>ModelNode</code> for which loading
276: * has started.
277: */
278: public void loadBegun(final ModelNode node) {
279: }
280:
281: /**
282: * Invoked when a string has been appended, during a load
283: * phase. This is only used when parsing a document and is
284: * used in support of progressive download, like the other
285: * loadXXX methods.
286: *
287: * @param node the <code>ModelNode</code> on which text has been
288: * inserted.
289: */
290: public void textInserted(final ModelNode node) {
291: }
292:
293: /**
294: * Utility method used to update the canvas appropriately
295: * depending on what is needed.
296: *
297: * During the loading phase, while we do progressive
298: * rendering, the canvas will only redraw nodes in the
299: * progressiveNodes list, unless a repaint has been
300: * requested.
301: *
302: * Important Note: this method should only be called from
303: * the update thread, i.e., the thread that also manages
304: * the model node tree.
305: */
306: public void updateCanvas() {
307: if (needRepaint) {
308: if (canvasConsumed) {
309: fullPaint();
310: needRepaint = false;
311: } else {
312: // There is a request to update the canvas
313: // (likely after a Runnable was invoked),
314: // but the last update was not consumed.
315: // If there is a Runnable in the RunnableQueue,
316: // we just skip this rendering update. Otherwise,
317: // schedule a fake Runnable to force a later repaint.
318: if (documentNode.getUpdateQueue().getSize() == 0) {
319: documentNode.getUpdateQueue().preemptLater(
320: new Runnable() {
321: public void run() {
322: }
323: }, this );
324: }
325: }
326: }
327: }
328:
329: /**
330: * Utility method used to do a full repaint. This method should be called
331: * from the update thread only.
332: */
333: protected void fullPaint() {
334: synchronized (lock) {
335: if (DirtyAreaManager.ON) {
336: dirtyAreaManager.refresh(documentNode, rg, clearPaint);
337: } else {
338: rg.setRenderingTile(null);
339: rg.setFill(clearPaint);
340: rg.setTransform(null);
341: rg.setFillOpacity(1);
342: rg.fillRect(0, 0, documentNode.getWidth(), documentNode
343: .getHeight(), 0, 0);
344: documentNode.paint(rg);
345: }
346: canvasConsumed = false;
347: }
348:
349: canvasUpdateListener.updateComplete(this );
350: }
351:
352: /**
353: * Sets the paint used to clear the canvas.
354: *
355: * @param clearPaint the new paint.
356: */
357: public void setClearPaint(final RGB clearPaint) {
358: if (clearPaint == null) {
359: throw new NullPointerException();
360: }
361:
362: this .clearPaint = clearPaint;
363: needRepaint = true;
364: }
365:
366: /**
367: * Turns off any rendering updates.
368: */
369: public void turnOff() {
370: isOff = true;
371: }
372:
373: /**
374: * Turns rendering updates on.
375: */
376: public void turnOn() {
377: isOff = false;
378: }
379:
380: /**
381: * @return true if the SimpleCanvasManager is currently bypassing canvas
382: * updates.
383: */
384: public boolean isOff() {
385: return isOff;
386: }
387:
388: // ========================================================================
389: // RunnableHandler implementation
390: // ========================================================================
391:
392: /**
393: * Called when the given Runnable has just been invoked and
394: * has returned.
395: * @param r the <code>Runnable</code> which just got executed
396: * @param rq the <code>RunnableQueue</code> which executed the
397: * input <code>Runnable</code>
398: */
399: public void runnableInvoked(final RunnableQueue rq, final Runnable r) {
400: if (!isOff) {
401: updateCanvas();
402: }
403: }
404: }
|