SVGCanvaspublic class SVGCanvas extends javax.microedition.lcdui.Canvas implements com.sun.perseus.model.CanvasUpdateListenerThis class provides support for an LCDUI Canvas extension which can display
an SVG Document. |
Fields Summary |
---|
public static final int | CLEAR_COLORColor used to clear the canvas' background. | public static final int | STATE_STOPPEDInitial state. | public static final int | STATE_PLAYINGPlaying state, i.e., playing animations and repainting buffer. | public static final int | STATE_PAUSEDPaused state, i.e., repainting buffer but no longer advancing the
time. | public static final int | SMIL_ANIMATION_FRAME_LENGTHSMIL Animation's frame length, in milliseconds | protected int | lastXLast x position on a pointer pressed event. | protected int | lastYLast y position on a pointer pressed event. | protected boolean | lastWasPressedTrue if the last pointer event was a pointer pressed event. | protected int | stateThe current player state. | protected com.sun.perseus.model.SimpleCanvasManager | canvasManagerThe SimpleCanvasManager manages the area where the SVG
content is rendered. | protected com.sun.perseus.model.DocumentNode | documentNodeThis component displays a DocumentNode object, which
is built from the URI | protected com.sun.pisces.NativeSurface | offscreenOffscreen image | protected com.sun.pisces.GraphicsSurfaceDestination | gsdUsed to blit the offscreen onto the graphics destination. | protected int | offscreenWidthOffscreen width | protected int | offscreenHeightOffscreen height | protected com.sun.pisces.PiscesRenderer | prThe PiscesRenderer associated with the offscreen. | protected com.sun.perseus.j2d.RenderGraphics | rgRenderGraphics used to draw into the current offscreen | protected javax.microedition.m2g.SVGEventListener | svgEventListenerThe associated SVGEventListener. | protected com.sun.perseus.util.RunnableQueue | updateQueueThe RunnableQueue is the _only_ valid way to access the
model tree. No access to the model should be done other
than from the RunnableQueue thread. | protected com.sun.perseus.model.SMILSample | smilSampleThe animation sampler, which runs animations in the update thread. | protected SMILSample.DocumentWallClock | clockThe animation clock. | protected float | timeIncrementThe time increment for the animation. | protected com.sun.perseus.model.ModelNode | lastMouseTargetThe last mouse event target. | private boolean | ignoreCanvasUpdateBoolean flag used to control when the SVGCanvas ignores a
canvas manager update because it asked for a a full paint
in response to a prior repaint. This avoid queuing an extra
initial repaint() when building a new offscreen buffer. |
Constructors Summary |
---|
public SVGCanvas(com.sun.perseus.model.DocumentNode documentNode)
if (documentNode == null) {
throw new NullPointerException();
}
if (!documentNode.isLoaded()) {
throw new IllegalStateException();
}
this.documentNode = documentNode;
// Set-up RunnableQueue
updateQueue = RunnableQueue.getDefault();
documentNode.setUpdateQueue(updateQueue);
// Hook in the SimpleCanvasManager after creating the offscreen buffer.
buildOffscreen(1, 1);
canvasManager = new SimpleCanvasManager(rg,
documentNode,
this);
canvasManager.turnOff(); // disabled until we call play or pause.
documentNode.setRunnableHandler(canvasManager);
// Create a SMILSample instance that will be scheduled with the
// RunnableQueue whenever the component plays.
clock = new SMILSample.DocumentWallClock(documentNode);
smilSample = new SMILSample(documentNode, clock);
// Initialize the timing engine.
documentNode.initializeTimingEngine();
// Apply animations at time 0
documentNode.sample(new Time(0));
documentNode.applyAnimations();
|
Methods Summary |
---|
protected void | buildOffscreen(int width, int height)The offscreen buffer has the size of the component. This method
is called in the MIDP painting thread.
if (width > 0 && height > 0) {
// We build an offscreen of the requested size.
offscreen = new NativeSurface(width, height);
offscreenWidth = width;
offscreenHeight = height;
} else {
// This is a degenerate case, just build with 1x1 offscreen
if (offscreenWidth == 1 && offscreenHeight == 1) {
return;
}
offscreen = new NativeSurface(1, 1);
offscreenWidth = 1;
offscreenHeight = 1;
}
// Build a new PiscesRenderer for the new rendering surface.
pr = new PiscesRenderer(offscreen,
offscreenWidth, offscreenHeight, 0, offscreenWidth, 1,
RendererBase.TYPE_INT_ARGB);
// Build a corresponding RenderGraphics
rg = new RenderGraphics(pr, offscreenWidth, offscreenHeight);
if (canvasManager != null) {
// We need to force painting the offscreen buffer.
// Offscreen buffer rendering happens in the update
// thread.
try {
updateQueue.invokeAndWait(new Runnable() {
public void run() {
synchronized (canvasManager.lock) {
// Automatically adjust the SVG image's viewport size.
documentNode.setSize(width, height);
// Switch the SimpleCanvasManager to the new RenderGraphics
canvasManager.setRenderGraphics(rg);
// Set the consumed flag to true to force painting
// immediately.
canvasManager.consume();
}
// Now, update the new canvas.
// We set the ignoreCanvasUpdate flag to true so that the
// canvas update does not trigger a repaint() request.
ignoreCanvasUpdate = true;
canvasManager.updateCanvas();
ignoreCanvasUpdate = false;
}
}, null);
} catch (InterruptedException ie) {
// This is a serious error, because it means the
// default Runnable Queue thread has been
// interrupted.
ie.printStackTrace();
}
} else {
pr.setColor(255, 255, 255);
pr.clearRect(0, 0, offscreenWidth, offscreenHeight);
}
| protected void | checkOffscreen()Checks if the offscreen buffer needs to be built or rebuilt.
if (offscreen == null) {
// This is the very first time we build an offscreen.
buildOffscreen(getWidth(), getHeight());
} else {
// Check that the offscreen is large enough for the current size.
int width = getWidth();
int height = getHeight();
// We use an offscreen size with is the smallest of the viewport
// size and the canvas size.
if (width > documentNode.getWidth()) {
width = documentNode.getWidth();
}
if (height > documentNode.getHeight()) {
height = documentNode.getHeight();
}
if (width != offscreenWidth
||
height != offscreenHeight) {
buildOffscreen(width, height);
}
}
| protected void | dispatchKeyEvent(java.lang.String eventType, int keyCode)Dispatches a key event to the DOM tree.
Runnable r = new Runnable() {
public void run() {
documentNode.dispatchEvent
(new ModelEvent(eventType,
documentNode, (char) keyCode));
}
};
if (state != STATE_STOPPED) {
invokeLater(r);
}
| protected void | dispatchPointerEvent(java.lang.String eventType, float[] pt)Dispatches a mouse event to the DOM tree.
if (state == STATE_STOPPED) {
return;
}
invokeLater(new Runnable() {
public void run() {
ModelNode target = documentNode.nodeHitAt(pt);
if (target == null) {
target = documentNode;
}
// If the target is different from the lastMouseTarget
// dispatch a 'mouseout' event to the lastMouseTarget
// and dispatch a 'mouseover' to the new target
if (lastMouseTarget != target) {
if (lastMouseTarget != null && lastMouseTarget != documentNode) {
ModelEvent e =
new ModelEvent(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE,
lastMouseTarget);
documentNode.dispatchEvent(e);
}
ModelEvent e =
new ModelEvent(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE,
target);
documentNode.dispatchEvent(e);
lastMouseTarget = target;
}
// Map the event type
// Build the DOM Event
ModelEvent evt = new ModelEvent(eventType, target);
// Dispatch to the target tree
documentNode.dispatchEvent(evt);
}
});
| public float | getTimeIncrement()Get the current time increment for animation rendering. The
SVGAnimator increments the SVG document's current time by this amount
upon each rendering. The default value is 0.1 (100 milliseconds).
return timeIncrement;
| protected void | hideNotify()Invoked when the component is hidden.
if (svgEventListener != null) {
svgEventListener.hideNotify();
}
| public void | initialLoadComplete(java.lang.Exception e)Called by the SimpleCanvasManager when the initial load is
complete. This method is called in the RunnableQueue thread.
if (e != null) {
e.printStackTrace();
}
| void | invokeAndWait(java.lang.Runnable runnable)Invoke the Runnable in the Document update thread and
return only after this Runnable has finished.
if (runnable == null) {
throw new NullPointerException();
}
if (state == STATE_STOPPED) {
throw new IllegalStateException
(Messages.formatMessage(Messages.ERROR_INVALID_STATE,
new Object[] {getClass().getName(),
stateToString(),
"invokeAndWait()",
"paused, playing"}));
}
updateQueue.invokeAndWait(runnable, canvasManager);
| void | invokeLater(java.lang.Runnable runnable)Schedule execution of the input Runnable in the update thread at a later time.
if (runnable == null) {
throw new NullPointerException();
}
if (state == STATE_STOPPED) {
throw new IllegalStateException
(Messages.formatMessage(Messages.ERROR_INVALID_STATE,
new Object[] {getClass().getName(),
stateToString(),
"invokeLater()",
"paused, playing"}));
}
updateQueue.invokeLater(runnable, canvasManager);
| protected void | keyPressed(int keyCode)Invoked when a key has been pressed.
if (svgEventListener != null) {
svgEventListener.keyPressed(keyCode);
}
dispatchKeyEvent(SVGConstants.SVG_KEYDOWN_EVENT_TYPE,
keyCode);
| protected void | keyReleased(int keyCode)Invoked when a key has been released.
if (svgEventListener != null) {
svgEventListener.keyReleased(keyCode);
}
dispatchKeyEvent(SVGConstants.SVG_KEYUP_EVENT_TYPE,
keyCode);
| protected void | paint(javax.microedition.lcdui.Graphics g)
checkOffscreen();
int x = g.getClipX();
int y = g.getClipY();
int w = g.getClipWidth();
int h = g.getClipHeight();
synchronized (canvasManager.lock) {
if (x!= 0
||
y != 0
||
w != documentNode.getWidth()
||
h != documentNode.getHeight()) {
// The repaint area is not exactly the same as the viewport area
// so we need to clear the background first.
g.setColor(CLEAR_COLOR);
g.fillRect(x, y, w, h);
}
if (gsd == null) {
gsd = new GraphicsSurfaceDestination(g);
}
gsd.drawSurface(offscreen, 0, 0, 0, 0, offscreenWidth, offscreenHeight, 1);
canvasManager.consume();
}
| public void | pause()Transition this SVGAnimator to the paused state.
The SVGAnimator stops advancing the document's current time
automatically (see the SVGDocument's setCurrentTime method). In consequence,
animation playback will be paused until another call to the play method
is made, at which points animations will resume from the document's current
time. SVGImage updates (through API calls) cause a rendering update
while the SVGAnimator is in the paused state.
if (state != STATE_PLAYING) {
throw new IllegalStateException
(Messages.formatMessage(Messages.ERROR_INVALID_STATE,
new Object[] {getClass().getName(),
stateToString(),
"pause()",
"playing"}));
}
state = STATE_PAUSED;
// Mark the document as _not_ playing.
updateQueue.preemptLater(new Runnable() {
public void run() {
documentNode.setPlaying(false);
}
}, canvasManager);
// Remove the SMILSampler
updateQueue.unschedule(smilSample);
// Turn on any updates to the offscreen canvas.
canvasManager.turnOn();
| public void | play()Transition this SVGAnimator to the playing
state. In the playing state, both Animation and SVGImage
updates cause rendering updates. Note that in the playing state,
when the document's current time changes, the animator will seek
to the new time, and continue to play animations from this place.
if (state == STATE_PLAYING) {
throw new IllegalStateException
(Messages.formatMessage(Messages.ERROR_INVALID_STATE,
new Object[] {getClass().getName(),
stateToString(),
"play()",
"stopped, paused"}));
}
// Mark the document as playing.
updateQueue.preemptLater(new Runnable() {
public void run() {
documentNode.setPlaying(true);
}
}, canvasManager);
// Now, schedule the SMILSampler
clock.start();
updateQueue.scheduleAtFixedRate(smilSample, canvasManager, (long) (1000 * timeIncrement));
state = STATE_PLAYING;
// Turn on any updates to the offscreen canvas.
canvasManager.turnOn();
| protected void | pointerPressed(int x, int y)Invoked when a mouse button has been pressed on a component.
if (svgEventListener != null) {
svgEventListener.pointerPressed(x, y);
}
lastX = x;
lastY = y;
lastWasPressed = true;
float[] pt = {x, y};
dispatchPointerEvent(SVGConstants.SVG_MOUSEDOWN_EVENT_TYPE, pt);
| protected void | pointerReleased(int x, int y)Invoked when a mouse button has been released on a component.
if (svgEventListener != null) {
svgEventListener.pointerReleased(x, y);
}
float[] pt = {x, y};
dispatchPointerEvent(SVGConstants.SVG_MOUSEUP_EVENT_TYPE, pt);
if (lastWasPressed && lastX == x && lastY == y) {
dispatchPointerEvent(SVGConstants.SVG_CLICK_EVENT_TYPE, pt);
}
lastWasPressed = false;
| public void | setSVGEventListener(javax.microedition.m2g.SVGEventListener svgEventListener)Associate the specified SVGEventListener with this
SVGAnimator .
this.svgEventListener = svgEventListener;
| public void | setTimeIncrement(float timeIncrement)Set the time increment to be used for animation rendering.
if (timeIncrement <= 0) {
throw new IllegalArgumentException();
}
this.timeIncrement = timeIncrement;
if (state == STATE_PLAYING) {
updateQueue.unschedule(smilSample);
updateQueue.scheduleAtFixedRate(smilSample, canvasManager, (long) (1000 * timeIncrement));
}
| protected void | showNotify()Invoked when the component is shown.
if (svgEventListener != null) {
svgEventListener.showNotify();
}
| protected void | sizeChanged(int w, int h)Invoked when the component's size changes.
if (svgEventListener != null) {
svgEventListener.sizeChanged(w, h);
}
| java.lang.String | stateToString()Helper method. Converts the current state to a String.
switch (state) {
case STATE_PLAYING:
return "playing";
case STATE_PAUSED:
return "paused";
case STATE_STOPPED:
default:
return "stopped";
}
| public void | stop()Transition this SVGAnimator to the stopped state.
In this state, no rendering updates are performed.
if (state == STATE_STOPPED) {
throw new IllegalStateException
(Messages.formatMessage(Messages.ERROR_INVALID_STATE,
new Object[] {getClass().getName(),
stateToString(),
"stop()",
"paused, playing"}));
}
state = STATE_STOPPED;
// Remove the SMILSampler
updateQueue.unschedule(smilSample);
// Mark the document as _not_ playing.
documentNode.setPlaying(false);
// To unlock the canvasManager if it is waiting on the
// consumed flag.
canvasManager.consume();
// Turn off any updates to the offscreen canvas.
canvasManager.turnOff();
| public void | updateComplete(java.lang.Object canvasManager)Invoked by the SimpleCanvasManager when it is done updating the
canvas. This is used during the progressive rendering loading phase and
when a Runnable has been invoked on the RunnableQueue associated with the
SVG image. This method is called in the RunnableQueue thread.
if (!ignoreCanvasUpdate) {
repaint(0, 0, documentNode.getWidth(), documentNode.getHeight());
}
|
|