www.pudn.com > j2mewireless_examples.zip > PhotoFrame.java
/*
* @(#)PhotoFrame.java 1.6 01/04/04
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*/
package examples.photoalbum;
import javax.microedition.lcdui.*;
import java.io.IOException;
/**
* This class provides the picture frame and drives the animation
* of the frames and the picture. It handles the starting and stopping
* of the Animation and contains the Thread used to do
* the timing and requests that the Canvas is repainted
* periodically. Additionally, it has controls for border
* style and animation speed.
*/
class PhotoFrame extends Canvas implements Runnable {
private Animation animation; // The Animation sequencer.
private int speed; // Animation speed set
private Thread thread; // Thread used for triggering repaints
private long paintTime; // Time of most recent paint
private Image image; // Buffer image of screen
private Image bimage; // Pattern used for border
private int style; // Border style
/*
* Mapping of speed values to delays in milliseconds.
* Indices map to those in the ChoiceGroup.
*/
private final static int speeds[] = {999999999, 500, 250, 100};
/**
* Create a new PhotoFrame. Creates an offscreen mutable
* image into which the border is drawn.
* Border style is none (0).
* Speed is stopped (0) until set.
*/
PhotoFrame() {
animation = new Animation();
image = Image.createImage(getWidth(), getHeight());
setStyle(0);
setSpeed(0);
}
/**
* Load a new photo into the frame.
* Load the images into the Animation and pick
* where the image should be placed on the canvas.
* Also draw the frame into the buffered image based
* on the animation size.
* If the images can't be loaded, just reset the origin
* and throw an IOException.
* @param name the prefix of the resource to load.
* @throws IOException when no images can be loaded.
*/
void setImage(String prefix) throws IOException {
try {
animation.loadImage(prefix);
animation.x = (getWidth() - animation.width) / 2;
animation.y = (getHeight() - animation.height) / 2;
paintFrame(style, animation.x, animation.y,
animation.width, animation.height);
} catch (java.io.IOException ex) {
// No image to display just show an empty frame.
animation.x = 0;
animation.y = 0;
paintFrame(style, 10, 10,
getWidth()-20, getHeight()-20);
throw ex;
}
}
/**
* Reset the PhotoFrame so it holds minimal resources
* by resetting the animation.
* The animation thread is stopped.
*/
void reset() {
animation.reset();
image = null;
thread = null;
}
/**
* Handle key events. FIRE events toggle between
* running and stopped. LEFT and RIGHT key events
* when stopped show the previous or next image.
*/
protected void keyPressed(int keyCode) {
int action = getGameAction(keyCode);
switch (action) {
case RIGHT:
if (thread == null) {
animation.next();
repaint();
}
break;
case LEFT:
if (thread == null) {
animation.previous();
repaint();
}
break;
case FIRE:
// Use FIRE to toggle the activity of the thread
if (thread == null) {
thread = new Thread(this);
thread.start();
} else {
synchronized (this) {
// Wake up the thread
this.notify();
thread = null;
}
}
break;
}
}
/**
* Handle key repeat events as regular key events.
*/
protected void keyRepeated(int keyCode) {
keyPressed(keyCode);
}
/**
* Set the animation speed.
* @param speed speedo of animation 0-3;
* 0 == stop; 1 = slow, 2 = medium, 3 = fast.
*/
void setSpeed(int speed) {
this.speed = speed;
}
/**
* Set the frame style.
* Recreate the photo frame image from the current animation
* and the new style.
*/
void setStyle(int style) {
this.style = style;
paintFrame(style, animation.x, animation.y,
animation.width, animation.height);
}
/**
* Notified when Canvas is made visible.
* Create the thread to run the animation timing.
*/
protected void showNotify() {
thread = new Thread(this);
thread.start();
}
/**
* Notified when the Canvas is no longer visible.
* Signal the running Thread that it should stop.
*/
protected void hideNotify() {
thread = null;
}
/**
* Paint is called whenever the canvas should be redrawn.
* It clears the canvas and draws the frame and the
* current frame from the animation.
* @param g the Graphics context to which to draw
*/
protected void paint(Graphics g) {
paintTime = System.currentTimeMillis();
if (image != null) {
// Draw the frame unless only the picture is being
// re-drawn.
// This is the inverse of the usual clip check.
int cx = 0, cy = 0, cw = 0, ch = 0;
if ((cx = g.getClipX()) < animation.x ||
(cy = g.getClipY()) < animation.y ||
((cx + (cw = g.getClipWidth())) >
(animation.x + animation.width)) ||
((cy + (ch = g.getClipHeight())) >
(animation.y + animation.height))) {
g.drawImage(image, 0, 0,
Graphics.LEFT|Graphics.TOP);
}
// Draw the image if it intersects the clipping region
if (intersectsClip(g, animation.x, animation.y,
animation.width, animation.height)) {
animation.paint(g);
}
}
}
/**
* Return true if the specified rectangle does intersect the
* clipping rectangle of the graphics object. If it returns true
* then the object must be drawn otherwise it would be clipped
* completely.
* The checks are done in a order with early exits to make this
* as inexpensive as possible.
* @param g the Graphics context to check
* @param x the upper left corner of the rectangle
* @param y the upper left corner of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @return true if the rectangle intersects the clipping region
*/
boolean intersectsClip(Graphics g, int x, int y, int w, int h) {
int cx = g.getClipX();
if (x + w <= cx)
return false;
int cw = g.getClipWidth();
if (x > cx + cw)
return false;
int cy = g.getClipY();
if (y + h <= cy)
return false;
int ch = g.getClipHeight();
if (y > cy + ch)
return false;
return true;
}
/**
* Paint the photo frame into the buffered screen image.
* This will avoid drawing each of its parts on each repaint.
* Paint will only need to put the image into the frame.
* @param style the style of frame to draw.
* @param x the x offset of the image.
* @param y the y offset of the image
* @param width the width of the anmiation image
* @param height the height of the animation image
*/
private void paintFrame(int style, int x, int y,
int width, int height) {
Graphics g = image.getGraphics();
// Clear the entire canvas to white
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth()+1, getHeight()+1);
// Set the origin of the image and paint the border and image.
g.translate(x, y);
paintBorder(g, style, width, height);
}
/**
* Runs the animation and makes the repaint requests.
* The thread will exit when it is no longer the current
* Animation thread.
*/
public void run() {
Thread me = Thread.currentThread();
long scheduled = System.currentTimeMillis();
paintTime = scheduled;
while (me == thread) {
synchronized (this) {
try {
// Update when the next frame should
// be drawn and compute the delta
scheduled += speeds[speed];
long delta = scheduled - paintTime;
if (delta > 0) {
this.wait(delta);
}
animation.next();
// Request a repaint only of the image
repaint(animation.x, animation.y,
animation.width,
animation.height);
} catch (InterruptedException e) {
}
}
}
}
/**
* Draw a border of the selected style.
* Style:
*
* - Style 0: No border is drawn.
*
- Style 1: A simple border is drawn
*
- Style 2: The border is outlined and an image
* is created to tile within the border.
*
* @param g graphics context to which to draw.
* @param x the horizontal offset in the frame of the image.
* @param y the vertical offset in the frame
* @param w the width reserved for the image
* @param h the height reserved of the image
*/
private void paintBorder(Graphics g, int style, int w, int h) {
if (style == 1) {
g.setColor(0x808080);
g.drawRect(-1, -1, w + 1, h + 1);
g.drawRect(-2, -2, w + 3, h + 3);
}
if (style == 2) {
// Draw fancy border with image between outer
// and inner rectangles
if (bimage == null)
bimage = genBorder();
int bw = bimage.getWidth();
int bh = bimage.getHeight();
int i;
// Draw the inner and outer solid border
g.setColor(0x808080);
g.drawRect(-1, -1, w + 1, h + 1);
g.drawRect(-bw - 2, -bh - 2,
w + bw * 2 + 3, h + bh * 2 + 3);
// Draw it in each corner
g.drawImage(bimage, -1, -1,
Graphics.BOTTOM|Graphics.RIGHT);
g.drawImage(bimage, -1, h + 1,
Graphics.TOP|Graphics.RIGHT);
g.drawImage(bimage, w + 1, -1,
Graphics.BOTTOM|Graphics.LEFT);
g.drawImage(bimage, w + 1, h + 1,
Graphics.TOP|Graphics.LEFT);
// Draw the embedded image down left and right sides
for (i = ((h % bh) / 2); i < h - bh; i += bh) {
g.drawImage(bimage, -1, i,
Graphics.RIGHT|Graphics.TOP);
g.drawImage(bimage, w + 1, i,
Graphics.LEFT|Graphics.TOP);
}
// Draw the embedded image across the top and bottom
for (i = ((w % bw) / 2); i < w - bw; i += bw) {
g.drawImage(bimage, i, -1,
Graphics.LEFT|Graphics.BOTTOM);
g.drawImage(bimage, i, h + 1,
Graphics.LEFT|Graphics.TOP);
}
}
}
/**
* Create an image for the border.
* The border consists of a simple "+" drawn in a 5x5 image.
* Fill the image with white and draw the "+" as magenta.
*/
private Image genBorder() {
Image image = Image.createImage(5, 5);
Graphics g = image.getGraphics();
g.setColor(255, 255, 255);
g.fillRect(0, 0, 5, 5);
g.setColor(128, 0, 255);
g.drawLine(2, 1, 2, 3); // vertical
g.drawLine(1, 2, 3, 2); // horizontal
return image;
}
}