001: /*
002: *
003: * Copyright (c) 2007, Sun Microsystems, Inc.
004: *
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * * Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * * Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: * * Neither the name of Sun Microsystems nor the names of its contributors
017: * may be used to endorse or promote products derived from this software
018: * without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032: package com.sun.perseus.demo.optimizedmenu;
033:
034: import java.io.IOException;
035: import java.io.InputStream;
036:
037: import java.util.Vector;
038:
039: import javax.microedition.lcdui.*;
040: import javax.microedition.m2g.SVGImage;
041: import javax.microedition.m2g.ScalableGraphics;
042: import javax.microedition.midlet.*;
043:
044: import org.w3c.dom.Document;
045: import org.w3c.dom.svg.SVGElement;
046: import org.w3c.dom.svg.SVGLocatableElement;
047: import org.w3c.dom.svg.SVGMatrix;
048: import org.w3c.dom.svg.SVGRect;
049: import org.w3c.dom.svg.SVGSVGElement;
050:
051: import com.sun.perseus.demo.SplashCanvas;
052:
053: /**
054: * The optimized SVG menu demonstrates how the SVG graphics engine can be used
055: * to pre-rasterize menu content. The rasterized images can then be shown as
056: * frames in animations. The trade-off is to reduce computer power while using a
057: * little more space to hold the rasterized content.
058: */
059: public class OptimizedSVGMenuDemo extends MIDlet implements
060: CommandListener {
061: private final Command exitCommand = new Command("Exit",
062: Command.EXIT, 1);
063: MenuCanvas svgCanvas = null;
064:
065: public OptimizedSVGMenuDemo() {
066: }
067:
068: public void startApp() {
069: System.gc();
070:
071: String svgImageFile = "/svg/optimizedSVGMenuG.svg";
072: String splashImageFile = "/images/OptimizedMenuHelp.png";
073:
074: if (svgCanvas == null) {
075: SplashCanvas splashCanvas = new SplashCanvas(
076: splashImageFile);
077: splashCanvas.display(Display.getDisplay(this ));
078:
079: InputStream svgDemoStream = getClass().getResourceAsStream(
080: svgImageFile);
081:
082: if (svgDemoStream == null) {
083: throw new Error("Could not load " + svgImageFile);
084: }
085:
086: try {
087: System.out.print("Loading SVGImage .... ");
088:
089: SVGImage svgImage = (SVGImage) SVGImage.createImage(
090: svgDemoStream, null);
091: System.out.println(" ... Done");
092:
093: svgCanvas = new MenuCanvas(svgImage, 3, 3, 0.1f, 8,
094: 0.0625f);
095: svgCanvas.addCommand(exitCommand);
096: svgCanvas.setCommandListener(this );
097: } catch (IOException e) {
098: e.printStackTrace();
099: throw new Error("Could not load " + svgImageFile);
100: }
101:
102: splashCanvas.switchTo(Display.getDisplay(this ), svgCanvas);
103: }
104:
105: Display.getDisplay(this ).setCurrent(svgCanvas);
106: }
107:
108: public void pauseApp() {
109: }
110:
111: public void destroyApp(boolean unconditional) {
112: }
113:
114: public void commandAction(Command c, Displayable d) {
115: if (c == exitCommand) {
116: destroyApp(false);
117: notifyDestroyed();
118: }
119: }
120:
121: /**
122: * The MenuCanvas class loads the icons found in the SVG image given
123: * at construction time and turns each icon into a bitmap.
124: *
125: */
126: class MenuCanvas extends Canvas {
127: /**
128: * The SVGImage painted by the canvas.
129: */
130: protected SVGImage svgImage;
131:
132: /**
133: * The ScalableGraphics used to paint into the midp
134: * Graphics instance.
135: */
136: protected ScalableGraphics sg = ScalableGraphics
137: .createInstance();
138:
139: /**
140: * The number of icons, vertically.
141: */
142: protected int numRows;
143:
144: /**
145: * The number of icons, horizontally.
146: */
147: protected int numCols;
148:
149: /**
150: * The size of a single icon.
151: */
152: protected int iconWidth;
153:
154: /**
155: * The size of a single icon.
156: */
157: protected int iconHeight;
158:
159: /**
160: * Number of frames in focus selection.
161: */
162: protected int numFramesFocus;
163:
164: /**
165: * Frame length.
166: */
167: protected float frameLength;
168:
169: /**
170: * The menu raster images.
171: */
172: protected Image[][][] menuIcons;
173:
174: /**
175: * The index of the current frame for each icon
176: */
177: protected int[][] currentFrame;
178:
179: /**
180: * The row/col index of the currently-focused icon.
181: */
182: protected int focusRow;
183:
184: /**
185: * The row/col index of the currently-focused icon.
186: */
187: protected int focusCol;
188:
189: /**
190: * The padding ratio.
191: */
192: protected float padding;
193:
194: /**
195: * @param svgImage the SVGImage this canvas should paint.
196: * @param numRows the number of rows of icons.
197: * @param numCols the number of columns of icons.
198: * @param padding the margin around each icons, as a percentage of the
199: * icon's bounding box.
200: * @param numFramesFocus the number of frames to sample in order to get
201: * from the unselected frame to the focused state.
202: * @param frameLength the amount of time between frames.
203: */
204: protected MenuCanvas(final SVGImage svgImage,
205: final int numRows, final int numCols,
206: final float padding, final int numFramesFocus,
207: final float frameLength) {
208: if ((svgImage == null) || (numRows <= 0) || (numCols <= 0)
209: || (padding < 0) || (numFramesFocus < 1)
210: || (frameLength <= 0)) {
211: throw new IllegalArgumentException();
212: }
213:
214: this .svgImage = svgImage;
215: this .numRows = numRows;
216: this .numCols = numCols;
217: this .numFramesFocus = numFramesFocus;
218: this .frameLength = frameLength;
219: this .padding = padding;
220:
221: // The input svgImage should have numRows * numCols icons under the
222: // root svg element.
223: final int numIcons = numRows * numCols;
224: Document doc = svgImage.getDocument();
225: SVGSVGElement svg = (SVGSVGElement) doc
226: .getDocumentElement();
227:
228: // Load all the icons in a Vector for future manipulation
229: Vector iconVector = new Vector();
230:
231: for (int i = 0; i < numIcons; i++) {
232: SVGElement iconElt = (SVGElement) doc
233: .getElementById("icon_" + i);
234:
235: if (iconElt == null) {
236: throw new IllegalArgumentException(
237: "The SVG Image does not have "
238: + numIcons
239: + " icons under the root svg element"
240: + " icon_" + i
241: + " does not exist in the document");
242: }
243:
244: if (!(iconElt instanceof SVGLocatableElement)) {
245: throw new IllegalArgumentException("The " + (i + 1)
246: + "th icon under the "
247: + "root svg element is not a <g>");
248: }
249:
250: // Hide all icons initially
251: iconElt.setTrait("display", "none");
252:
253: iconVector.addElement(iconElt);
254: }
255:
256: // Now, compute the size allocated to each icon.
257: int width = getWidth();
258: int height = getHeight();
259:
260: iconWidth = width / numCols;
261: iconHeight = height / numRows;
262:
263: // Render each icon in a bitmap.
264: svgImage.setViewportWidth(iconWidth);
265: svgImage.setViewportHeight(iconHeight);
266:
267: final int numFrames = 1 + numFramesFocus;
268: menuIcons = new Image[numRows][numCols][numFrames];
269: currentFrame = new int[numRows][numCols];
270:
271: // calculate viewBox for each icon
272:
273: // svg -> screen
274: SVGMatrix svgCTM = svg.getScreenCTM();
275:
276: // screen -> svg
277: SVGMatrix svgICTM = svgCTM.inverse();
278:
279: SVGRect[] iconViewBox = new SVGRect[numIcons];
280:
281: for (int i = 0; i < numIcons; ++i) {
282: SVGLocatableElement icon = (SVGLocatableElement) iconVector
283: .elementAt(i);
284:
285: // Get the user space bounding box for the icon
286: SVGRect bbox = icon.getBBox();
287: if (bbox == null) {
288: // If someone tampered with the svg menu file, the bbox
289: // could be null
290: iconViewBox[i] = null;
291: continue;
292: }
293:
294: // icon -> svg -> screen
295: SVGMatrix iconCTM = icon.getScreenCTM();
296:
297: // icon -> svg
298: SVGMatrix iconToSvg = svg.createSVGMatrixComponents(
299: svgICTM.getComponent(0), svgICTM
300: .getComponent(1), svgICTM
301: .getComponent(2), svgICTM
302: .getComponent(3), svgICTM
303: .getComponent(4), svgICTM
304: .getComponent(5));
305: iconToSvg.mMultiply(iconCTM);
306:
307: // get the icon bounding box in svg coordinates
308: float x0 = bbox.getX();
309: float y0 = bbox.getY();
310: float x1 = x0 + bbox.getWidth();
311: float y1 = y0 + bbox.getHeight();
312: float[] pointsX = { x0, x0, x1, x1 };
313: float[] pointsY = { y0, y1, y0, y1 };
314: float minX = Float.MAX_VALUE;
315: float minY = Float.MAX_VALUE;
316: float maxX = -Float.MAX_VALUE;
317: float maxY = -Float.MAX_VALUE;
318: float a = iconToSvg.getComponent(0);
319: float b = iconToSvg.getComponent(1);
320: float c = iconToSvg.getComponent(2);
321: float d = iconToSvg.getComponent(3);
322: float e = iconToSvg.getComponent(4);
323: float f = iconToSvg.getComponent(5);
324:
325: for (int j = 0; j < pointsX.length; ++j) {
326: float nx = (a * pointsX[j]) + (c * pointsY[j]) + e;
327: float ny = (b * pointsX[j]) + (d * pointsY[j]) + f;
328:
329: if (nx < minX) {
330: minX = nx;
331: }
332:
333: if (nx > maxX) {
334: maxX = nx;
335: }
336:
337: if (ny < minY) {
338: minY = ny;
339: }
340:
341: if (ny > maxY) {
342: maxY = ny;
343: }
344: }
345:
346: bbox.setX(minX);
347: bbox.setY(minY);
348: bbox.setWidth(maxX - minX);
349: bbox.setHeight(maxY - minY);
350:
351: iconViewBox[i] = pad(bbox);
352: }
353:
354: // do the rendering
355: int i = 0;
356:
357: for (int ri = 0; ri < numRows; ri++) {
358: for (int ci = 0; ci < numCols; ci++, i++) {
359: // Get the icon we want to draw
360: SVGLocatableElement icon = (SVGLocatableElement) iconVector
361: .elementAt(i);
362:
363: // Now, set the icon's display to 'inline' before drawing
364: // it to the offscreen.
365: icon.setTrait("display", "inline");
366:
367: // "zoom" the icon
368: if (iconViewBox[i] != null) {
369: svg.setRectTrait("viewBox", iconViewBox[i]);
370: }
371:
372: // Create a bitmap to draw into
373: svg.setCurrentTime(0);
374:
375: for (int fi = 0; fi < numFrames; fi++) {
376: menuIcons[ri][ci][fi] = Image.createImage(
377: iconWidth, iconHeight);
378:
379: // Get a Graphics instance that we can draw into
380: Graphics g = menuIcons[ri][ci][fi]
381: .getGraphics();
382: g.setColor(255, 0, 0);
383: g.fillRect(0, 0, iconWidth, iconHeight);
384: sg.bindTarget(g);
385: sg.render(0, 0, svgImage);
386: sg.releaseTarget();
387:
388: svgImage.incrementTime(frameLength);
389: }
390:
391: icon.setTrait("display", "none");
392: }
393: }
394:
395: // The following thread handles animating the currently focused item.
396: final long frameLengthMs = (long) (frameLength * 1000);
397: Thread th = new Thread() {
398: public void run() {
399: long start = 0;
400: long end = 0;
401: long sleep = 0;
402: boolean interrupted = false;
403:
404: while (!interrupted) {
405: start = System.currentTimeMillis();
406:
407: int cr = focusRow;
408: int cc = focusCol;
409: boolean needUpdate = false;
410:
411: for (int ri = 0; ri < numRows; ri++) {
412: for (int ci = 0; ci < numCols; ci++) {
413: // Process icon (ri, ci)
414:
415: // Frames are:
416: // [0] : unselected
417: // [1, numFramesFocusIn -1] : focusIn anim
418: // [numFramesFocus] : focused
419: int curFrame = currentFrame[ri][ci];
420:
421: if ((cr == ri) && (cc == ci)) {
422: // We are processing the focused icon.
423: // If we are below the focused frame, just increase the frame index
424: if (curFrame < numFramesFocus) {
425: // Move towards focused state on the focusIn animation
426: curFrame += 1;
427: needUpdate = true;
428: } else {
429: // Do nothing, we are in the right frame already.
430: }
431: } else {
432: // We are _not_ on the focused frame.
433: if (curFrame > 0) {
434: curFrame -= 1;
435: needUpdate = true;
436: }
437: }
438:
439: currentFrame[ri][ci] = curFrame;
440: }
441: }
442:
443: if (needUpdate) {
444: repaint();
445: serviceRepaints();
446: }
447:
448: end = System.currentTimeMillis();
449: sleep = frameLengthMs - (end - start);
450:
451: if (sleep < 10) {
452: sleep = 10;
453: }
454:
455: try {
456: sleep(sleep);
457: } catch (InterruptedException ie) {
458: interrupted = true;
459: }
460: }
461: }
462: };
463:
464: th.start();
465: }
466:
467: /**
468: * Helper method. Pads the input bounding box.
469: *
470: * @param bbox the box to pad.
471: */
472: SVGRect pad(final SVGRect bbox) {
473: float hPad = bbox.getWidth() * padding;
474: float vPad = bbox.getHeight() * padding;
475: bbox.setX(bbox.getX() - hPad);
476: bbox.setY(bbox.getY() - vPad);
477: bbox.setWidth(bbox.getWidth() + (2 * hPad));
478: bbox.setHeight(bbox.getHeight() + (2 * vPad));
479:
480: return bbox;
481: }
482:
483: public void keyPressed(int keyCode) {
484: int r = focusRow;
485: int c = focusCol;
486:
487: switch (getGameAction(keyCode)) {
488: case LEFT:
489: c--;
490:
491: if (c < 0) {
492: c = numCols - 1;
493: }
494:
495: break;
496:
497: case RIGHT:
498: c++;
499:
500: if (c == numCols) {
501: c = 0;
502: }
503:
504: break;
505:
506: case UP:
507: r--;
508:
509: if (r < 0) {
510: r = numRows - 1;
511: }
512:
513: break;
514:
515: case DOWN:
516: r++;
517:
518: if (r == numRows) {
519: r = 0;
520: }
521:
522: break;
523:
524: default:
525:
526: // do nothing
527: break;
528: }
529:
530: focusRow = r;
531: focusCol = c;
532: }
533:
534: public void paint(Graphics g) {
535: int fi = 0;
536:
537: for (int ri = 0; ri < numRows; ri++) {
538: for (int ci = 0; ci < numCols; ci++) {
539: fi = currentFrame[ri][ci];
540: g.drawImage(menuIcons[ri][ci][fi], ci * iconWidth,
541: ri * iconHeight, Graphics.TOP
542: | Graphics.LEFT);
543: }
544: }
545: }
546: }
547: }
|