0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: */
0026:
0027: package javax.microedition.lcdui.game;
0028:
0029: import javax.microedition.lcdui.Image;
0030: import javax.microedition.lcdui.Graphics;
0031:
0032: import com.sun.midp.log.Logging;
0033: import com.sun.midp.log.LogChannels;
0034:
0035: /**
0036: * A Sprite is a basic visual element that can be rendered with one of
0037: * several frames stored in an Image; different frames can be shown to
0038: * animate the Sprite. Several transforms such as flipping and rotation
0039: * can also be applied to a Sprite to further vary its appearance. As with
0040: * all Layer subclasses, a Sprite's location can be changed and it can also
0041: * be made visible or invisible.
0042: * <P>
0043: * <h3>Sprite Frames</h3>
0044: * The raw frames used to render a Sprite are provided in a single Image
0045: * object, which may be mutable or immutable. If more than one frame is used,
0046: * the Image is broken up into a series of equally-sized frames of a specified
0047: * width and height. As shown in the figure below, the same set of frames may
0048: * be stored in several different arrangements depending on what is the most
0049: * convenient for the game developer.
0050: * <br>
0051: * <center><img src="doc-files/frames.gif" width=777 height=402
0052: * ALT="Sprite Frames"></center>
0053: * <br>
0054: * <p>
0055: * Each frame is assigned a unique index number. The frame located in the
0056: * upper-left corner of the Image is assigned an index of 0. The remaining
0057: * frames are then numbered consecutively in row-major order (indices are
0058: * assigned across the first row, then the second row, and so on). The method
0059: * {@link #getRawFrameCount()} returns the total number of raw frames.
0060: * <P>
0061: * <h3>Frame Sequence</h3>
0062: * A Sprite's frame sequence defines an ordered list of frames to be displayed.
0063: * The default frame sequence mirrors the list of available frames, so
0064: * there is a direct mapping between the sequence index and the corresponding
0065: * frame index. This also means that the length of the default frame sequence
0066: * is equal to the number of raw frames. For example, if a Sprite has 4
0067: * frames, its default frame sequence is {0, 1, 2, 3}.
0068: * <center><img src="doc-files/defaultSequence.gif" width=182 height=269
0069: * ALT="Default Frame Sequence"></center>
0070: * <P>
0071: * The developer must manually switch the current frame in the frame sequence.
0072: * This may be accomplished by calling {@link #setFrame},
0073: * {@link #prevFrame()}, or {@link #nextFrame()}. Note that these methods
0074: * always operate on the sequence index, they do not operate on frame indices;
0075: * however, if the default frame sequence is used, then the sequence indices
0076: * and the frame indices are interchangeable.
0077: * <P>
0078: * If desired, an arbitrary frame sequence may be defined for a Sprite.
0079: * The frame sequence must contain at least one element, and each element must
0080: * reference a valid frame index. By defining a new frame sequence, the
0081: * developer can conveniently display the Sprite's frames in any order
0082: * desired; frames may be repeated, omitted, shown in reverse order, etc.
0083: * <P>
0084: * For example, the diagram below shows how a special frame sequence might be
0085: * used to animate a mosquito. The frame sequence is designed so that the
0086: * mosquito flaps its wings three times and then pauses for a moment before
0087: * the cycle is repeated.
0088: * <center><img src="doc-files/specialSequence.gif" width=346 height=510
0089: * ALT="Special Frame Sequence"></center>
0090: * By calling {@link #nextFrame} each time the display is updated, the
0091: * resulting animation would like this:
0092: * <br>
0093: * <center><img src="doc-files/sequenceDemo.gif" width=96 height=36></center>
0094: * <P>
0095: * <h3>Reference Pixel</h3>
0096: * Being a subclass of Layer, Sprite inherits various methods for setting and
0097: * retrieving its location such as {@link #setPosition setPosition(x,y)},
0098: * {@link #getX getX()}, and {@link #getY getY()}. These methods all define
0099: * position in terms of the upper-left corner of the Sprite's visual bounds;
0100: * however, in some cases, it is more convenient to define the Sprite's position
0101: * in terms of an arbitrary pixel within its frame, especially if transforms
0102: * are applied to the Sprite.
0103: * <P>
0104: * Therefore, Sprite includes the concept of a <em>reference pixel</em>.
0105: * The reference pixel is defined by specifying its location in the
0106: * Sprite's untransformed frame using
0107: * {@link #defineReferencePixel defineReferencePixel(x,y)}.
0108: * By default, the reference pixel is defined to be the pixel at (0,0)
0109: * in the frame. If desired, the reference pixel may be defined outside
0110: * of the frame's bounds.
0111: * <p>
0112: * In this example, the reference pixel is defined to be the pixel that
0113: * the monkey appears to be hanging from:
0114: * <p>
0115: * <center><img src="doc-files/refpixel.gif" width=304 height=199
0116: * ALT="Defining The Reference Pixel"></center>
0117: * <p>
0118: * {@link #getRefPixelX getRefPixelX()} and {@link #getRefPixelY getRefPixelY()}
0119: * can be used to query the location of the reference pixel in the painter's
0120: * coordinate system. The developer can also use
0121: * {@link #setRefPixelPosition setRefPixelPosition(x,y)} to position the Sprite
0122: * so that reference pixel appears at a specific location in the painter's
0123: * coordinate system. These methods automatically account for any transforms
0124: * applied to the Sprite.
0125: * <p>
0126: * In this example, the reference pixel's position is set to a point at the end
0127: * of a tree branch; the Sprite's location changes so that the reference pixel
0128: * appears at this point and the monkey appears to be hanging from the branch:
0129: * <p>
0130: * <center><img src="doc-files/setrefposition.gif" width=332 height=350
0131: * ALT="Setting The Reference Pixel Position"></center>
0132: * <p>
0133: * <a name="transforms"></a>
0134: * <h3>Sprite Transforms</h3>
0135: * Various transforms can be applied to a Sprite. The available transforms
0136: * include rotations in multiples of 90 degrees, and mirrored (about
0137: * the vertical axis) versions of each of the rotations. A Sprite's transform
0138: * is set by calling {@link #setTransform setTransform(transform)}.
0139: * <p>
0140: * <center><img src="doc-files/transforms.gif" width=355 height=575
0141: * ALT="Transforms"></center>
0142: * <br>
0143: * When a transform is applied, the Sprite is automatically repositioned
0144: * such that the reference pixel appears stationary in the painter's
0145: * coordinate system. Thus, the reference pixel effectively becomes the
0146: * center of the transform operation. Since the reference pixel does not
0147: * move, the values returned by {@link #getRefPixelX()} and
0148: * {@link #getRefPixelY()} remain the same; however, the values returned by
0149: * {@link #getX getX()} and {@link #getY getY()} may change to reflect the
0150: * movement of the Sprite's upper-left corner.
0151: * <p>
0152: * Referring to the monkey example once again, the position of the
0153: * reference pixel remains at (48, 22) when a 90 degree rotation
0154: * is applied, thereby making it appear as if the monkey is swinging
0155: * from the branch:
0156: * <p>
0157: * <center><img src="doc-files/transcenter.gif" width=333 height=350
0158: * ALT="Transform Center"></center>
0159: * <p>
0160: * <h3>Sprite Drawing</h3>
0161: * Sprites can be drawn at any time using the {@link #paint(Graphics)} method.
0162: * The Sprite will be drawn on the Graphics object according to the current
0163: * state information maintained by the Sprite (i.e. position, frame,
0164: * visibility). Erasing the Sprite is always the responsibility of code
0165: * outside the Sprite class.<p>
0166: * <p>
0167: * Sprites can be implemented using whatever techniques a manufacturers
0168: * wishes to use (e.g hardware acceleration may be used for all Sprites, for
0169: * certain sizes of Sprites, or not at all).
0170: * <p>
0171: * For some platforms, certain Sprite sizes may be more efficient than others;
0172: * manufacturers may choose to provide developers with information about
0173: * device-specific characteristics such as these.
0174: * <p>
0175: */
0176:
0177: public class Sprite extends Layer {
0178:
0179: // ----- definitions for the various transformations possible -----
0180:
0181: /**
0182: * No transform is applied to the Sprite.
0183: * This constant has a value of <code>0</code>.
0184: */
0185: public static final int TRANS_NONE = 0;
0186:
0187: /**
0188: * Causes the Sprite to appear rotated clockwise by 90 degrees.
0189: * This constant has a value of <code>5</code>.
0190: */
0191: public static final int TRANS_ROT90 = 5;
0192:
0193: /**
0194: * Causes the Sprite to appear rotated clockwise by 180 degrees.
0195: * This constant has a value of <code>3</code>.
0196: */
0197: public static final int TRANS_ROT180 = 3;
0198:
0199: /**
0200: * Causes the Sprite to appear rotated clockwise by 270 degrees.
0201: * This constant has a value of <code>6</code>.
0202: */
0203: public static final int TRANS_ROT270 = 6;
0204:
0205: /**
0206: * Causes the Sprite to appear reflected about its vertical
0207: * center.
0208: * This constant has a value of <code>2</code>.
0209: */
0210: public static final int TRANS_MIRROR = 2;
0211:
0212: /**
0213: * Causes the Sprite to appear reflected about its vertical
0214: * center and then rotated clockwise by 90 degrees.
0215: * This constant has a value of <code>7</code>.
0216: */
0217: public static final int TRANS_MIRROR_ROT90 = 7;
0218:
0219: /**
0220: * Causes the Sprite to appear reflected about its vertical
0221: * center and then rotated clockwise by 180 degrees.
0222: * This constant has a value of <code>1</code>.
0223: */
0224: public static final int TRANS_MIRROR_ROT180 = 1;
0225:
0226: /**
0227: * Causes the Sprite to appear reflected about its vertical
0228: * center and then rotated clockwise by 270 degrees.
0229: * This constant has a value of <code>4</code>.
0230: */
0231: public static final int TRANS_MIRROR_ROT270 = 4;
0232:
0233: // ----- Constructors -----
0234:
0235: /**
0236: * Creates a new non-animated Sprite using the provided Image.
0237: * This constructor is functionally equivalent to calling
0238: * <code>new Sprite(image, image.getWidth(), image.getHeight())</code>
0239: * <p>
0240: * By default, the Sprite is visible and its upper-left
0241: * corner is positioned at (0,0) in the painter's coordinate system.
0242: * <br>
0243: * @param image the <code>Image</code> to use as the single frame
0244: * for the </code>Sprite
0245: * @throws NullPointerException if <code>img</code> is <code>null</code>
0246: */
0247: public Sprite(Image image) {
0248: super (image.getWidth(), image.getHeight());
0249:
0250: initializeFrames(image, image.getWidth(), image.getHeight(),
0251: false);
0252:
0253: // initialize collision rectangle
0254: initCollisionRectBounds();
0255:
0256: // current transformation is TRANS_NONE
0257: setTransformImpl(TRANS_NONE);
0258:
0259: }
0260:
0261: /**
0262: * Creates a new animated Sprite using frames contained in
0263: * the provided Image. The frames must be equally sized, with the
0264: * dimensions specified by <code>frameWidth</code> and
0265: * <code>frameHeight</code>. They may be laid out in the image
0266: * horizontally, vertically, or as a grid. The width of the source
0267: * image must be an integer multiple of the frame width, and the height
0268: * of the source image must be an integer multiple of the frame height.
0269: * The values returned by {@link Layer#getWidth} and
0270: * {@link Layer#getHeight} will reflect the frame width and frame height
0271: * subject to the Sprite's current transform.
0272: * <p>
0273: * Sprites have a default frame sequence corresponding to the raw frame
0274: * numbers, starting with frame 0. The frame sequence may be modified
0275: * with {@link #setFrameSequence(int[])}.
0276: * <p>
0277: * By default, the Sprite is visible and its upper-left corner is
0278: * positioned at (0,0) in the painter's coordinate system.
0279: * <p>
0280: * @param image the <code>Image</code> to use for <code>Sprite</code>
0281: * @param frameWidth the <code>width</code>, in pixels, of the
0282: * individual raw frames
0283: * @param frameHeight the <code>height</code>, in pixels, of the
0284: * individual raw frames
0285: * @throws NullPointerException if <code>img</code> is <code>null</code>
0286: * @throws IllegalArgumentException if <code>frameHeight</code> or
0287: * <code>frameWidth</code> is less than <code>1</code>
0288: * @throws IllegalArgumentException if the <code>image</code>
0289: * width is not an integer multiple of the <code>frameWidth</code>
0290: * @throws IllegalArgumentException if the <code>image</code>
0291: * height is not an integer multiple of the <code>frameHeight</code>
0292: */
0293: public Sprite(Image image, int frameWidth, int frameHeight) {
0294:
0295: super (frameWidth, frameHeight);
0296: // if img is null img.getWidth() will throw NullPointerException
0297: if ((frameWidth < 1 || frameHeight < 1)
0298: || ((image.getWidth() % frameWidth) != 0)
0299: || ((image.getHeight() % frameHeight) != 0)) {
0300: throw new IllegalArgumentException();
0301: }
0302:
0303: // construct the array of images that
0304: // we use as "frames" for the sprite.
0305: // use default frame , sequence index = 0
0306: initializeFrames(image, frameWidth, frameHeight, false);
0307:
0308: // initialize collision rectangle
0309: initCollisionRectBounds();
0310:
0311: // current transformation is TRANS_NONE
0312: setTransformImpl(TRANS_NONE);
0313:
0314: }
0315:
0316: /**
0317: * Creates a new Sprite from another Sprite. <p>
0318: *
0319: * All instance attributes (raw frames, position, frame sequence, current
0320: * frame, reference point, collision rectangle, transform, and visibility)
0321: * of the source Sprite are duplicated in the new Sprite.
0322: *
0323: * @param s the <code>Sprite</code> to create a copy of
0324: * @throws NullPointerException if <code>s</code> is <code>null</code>
0325: *
0326: */
0327: public Sprite(Sprite s) {
0328:
0329: super (s != null ? s.getWidth() : 0, s != null ? s.getHeight()
0330: : 0);
0331:
0332: if (s == null) {
0333: throw new NullPointerException();
0334: }
0335:
0336: this .sourceImage = Image.createImage(s.sourceImage);
0337:
0338: this .numberFrames = s.numberFrames;
0339:
0340: this .frameCoordsX = new int[this .numberFrames];
0341: this .frameCoordsY = new int[this .numberFrames];
0342:
0343: System.arraycopy(s.frameCoordsX, 0, this .frameCoordsX, 0, s
0344: .getRawFrameCount());
0345:
0346: System.arraycopy(s.frameCoordsY, 0, this .frameCoordsY, 0, s
0347: .getRawFrameCount());
0348:
0349: this .x = s.getX();
0350: this .y = s.getY();
0351:
0352: // these fields are set by defining a reference point
0353: this .dRefX = s.dRefX;
0354: this .dRefY = s.dRefY;
0355:
0356: // these fields are set when defining a collision rectangle
0357: this .collisionRectX = s.collisionRectX;
0358: this .collisionRectY = s.collisionRectY;
0359: this .collisionRectWidth = s.collisionRectWidth;
0360: this .collisionRectHeight = s.collisionRectHeight;
0361:
0362: // these fields are set when creating a Sprite from an Image
0363: this .srcFrameWidth = s.srcFrameWidth;
0364: this .srcFrameHeight = s.srcFrameHeight;
0365:
0366: // the above fields are used in setTransform()
0367: // which is why we set them first, then call setTransformImpl()
0368: // to set up internally used data structures.
0369: setTransformImpl(s.t_currentTransformation);
0370:
0371: this .setVisible(s.isVisible());
0372:
0373: this .frameSequence = new int[s.getFrameSequenceLength()];
0374: this .setFrameSequence(s.frameSequence);
0375: this .setFrame(s.getFrame());
0376:
0377: this .setRefPixelPosition(s.getRefPixelX(), s.getRefPixelY());
0378:
0379: }
0380:
0381: // ----- public methods -----
0382:
0383: /**
0384: * Defines the reference pixel for this Sprite. The pixel is
0385: * defined by its location relative to the upper-left corner of
0386: * the Sprite's un-transformed frame, and it may lay outside of
0387: * the frame's bounds.
0388: * <p>
0389: * When a transformation is applied, the reference pixel is
0390: * defined relative to the Sprite's initial upper-left corner
0391: * before transformation. This corner may no longer appear as the
0392: * upper-left corner in the painter's coordinate system under
0393: * current transformation.
0394: * <p>
0395: * By default, a Sprite's reference pixel is located at (0,0); that is,
0396: * the pixel in the upper-left corner of the raw frame.
0397: * <p>
0398: * Changing the reference pixel does not change the
0399: * Sprite's physical position in the painter's coordinate system;
0400: * that is, the values returned by {@link #getX getX()} and
0401: * {@link #getY getY()} will not change as a result of defining the
0402: * reference pixel. However, subsequent calls to methods that
0403: * involve the reference pixel will be impacted by its new definition.
0404: *
0405: * @param inp_x the horizontal location of the reference pixel, relative
0406: * to the left edge of the un-transformed frame
0407: * @param inp_y the vertical location of the reference pixel, relative
0408: * to the top edge of the un-transformed frame
0409: * @see #setRefPixelPosition
0410: * @see #getRefPixelX
0411: * @see #getRefPixelY
0412: */
0413: public void defineReferencePixel(int inp_x, int inp_y) {
0414: dRefX = inp_x;
0415: dRefY = inp_y;
0416: }
0417:
0418: /**
0419: * Sets this Sprite's position such that its reference pixel is located
0420: * at (x,y) in the painter's coordinate system.
0421: * @param inp_x the horizontal location at which to place
0422: * the reference pixel
0423: * @param inp_y the vertical location at which to place the reference pixel
0424: * @see #defineReferencePixel
0425: * @see #getRefPixelX
0426: * @see #getRefPixelY
0427: */
0428: public void setRefPixelPosition(int inp_x, int inp_y) {
0429:
0430: // update x and y
0431: x = inp_x
0432: - getTransformedPtX(dRefX, dRefY,
0433: t_currentTransformation);
0434: y = inp_y
0435: - getTransformedPtY(dRefX, dRefY,
0436: t_currentTransformation);
0437:
0438: }
0439:
0440: /**
0441: * Gets the horizontal position of this Sprite's reference pixel
0442: * in the painter's coordinate system.
0443: * @return the horizontal location of the reference pixel
0444: * @see #defineReferencePixel
0445: * @see #setRefPixelPosition
0446: * @see #getRefPixelY
0447: */
0448: public int getRefPixelX() {
0449: return (this .x + getTransformedPtX(dRefX, dRefY,
0450: this .t_currentTransformation));
0451: }
0452:
0453: /**
0454: * Gets the vertical position of this Sprite's reference pixel
0455: * in the painter's coordinate system.
0456: * @return the vertical location of the reference pixel
0457: * @see #defineReferencePixel
0458: * @see #setRefPixelPosition
0459: * @see #getRefPixelX
0460: */
0461: public int getRefPixelY() {
0462: return (this .y + getTransformedPtY(dRefX, dRefY,
0463: this .t_currentTransformation));
0464: }
0465:
0466: /**
0467: * Selects the current frame in the frame sequence. <p>
0468: * The current frame is rendered when {@link #paint(Graphics)} is called.
0469: * <p>
0470: * The index provided refers to the desired entry in the frame sequence,
0471: * not the index of the actual frame itself.
0472: * @param inp_sequenceIndex the index of of the desired entry in the frame
0473: * sequence
0474: * @throws IndexOutOfBoundsException if <code>frameIndex</code> is
0475: * less than<code>0</code>
0476: * @throws IndexOutOfBoundsException if <code>frameIndex</code> is
0477: * equal to or greater than the length of the current frame
0478: * sequence (or the number of raw frames for the default sequence)
0479: * @see #setFrameSequence(int[])
0480: * @see #getFrame
0481: */
0482: public void setFrame(int inp_sequenceIndex) {
0483: if (inp_sequenceIndex < 0
0484: || inp_sequenceIndex >= frameSequence.length) {
0485: throw new IndexOutOfBoundsException();
0486: }
0487: sequenceIndex = inp_sequenceIndex;
0488: }
0489:
0490: /**
0491: * Gets the current index in the frame sequence. <p>
0492: * The index returned refers to the current entry in the frame sequence,
0493: * not the index of the actual frame that is displayed.
0494: *
0495: * @return the current index in the frame sequence
0496: * @see #setFrameSequence(int[])
0497: * @see #setFrame
0498: */
0499: public final int getFrame() {
0500: return sequenceIndex;
0501: }
0502:
0503: /**
0504: * Gets the number of raw frames for this Sprite. The value returned
0505: * reflects the number of frames; it does not reflect the length of the
0506: * Sprite's frame sequence. However, these two values will be the same
0507: * if the default frame sequence is used.
0508: *
0509: * @return the number of raw frames for this Sprite
0510: * @see #getFrameSequenceLength
0511: */
0512: public int getRawFrameCount() {
0513: return numberFrames;
0514: }
0515:
0516: /**
0517: * Gets the number of elements in the frame sequence. The value returned
0518: * reflects the length of the Sprite's frame sequence; it does not reflect
0519: * the number of raw frames. However, these two values will be the same
0520: * if the default frame sequence is used.
0521: *
0522: * @return the number of elements in this Sprite's frame sequence
0523: * @see #getRawFrameCount
0524: */
0525: public int getFrameSequenceLength() {
0526: return frameSequence.length;
0527: }
0528:
0529: /**
0530: * Selects the next frame in the frame sequence. <p>
0531: *
0532: * The frame sequence is considered to be circular, i.e. if
0533: * {@link #nextFrame} is called when at the end of the sequence,
0534: * this method will advance to the first entry in the sequence.
0535: *
0536: * @see #setFrameSequence(int[])
0537: * @see #prevFrame
0538: */
0539: public void nextFrame() {
0540: sequenceIndex = (sequenceIndex + 1) % frameSequence.length;
0541: }
0542:
0543: /**
0544: * Selects the previous frame in the frame sequence. <p>
0545: *
0546: * The frame sequence is considered to be circular, i.e. if
0547: * {@link #prevFrame} is called when at the start of the sequence,
0548: * this method will advance to the last entry in the sequence.
0549: *
0550: * @see #setFrameSequence(int[])
0551: * @see #nextFrame
0552: */
0553: public void prevFrame() {
0554: if (sequenceIndex == 0) {
0555: sequenceIndex = frameSequence.length - 1;
0556: } else {
0557: sequenceIndex--;
0558: }
0559: }
0560:
0561: /**
0562: * Draws the Sprite.
0563: * <P>
0564: * Draws current frame of Sprite using the provided Graphics object.
0565: * The Sprite's upper left corner is rendered at the Sprite's current
0566: * position relative to the origin of the Graphics object. The current
0567: * position of the Sprite's upper-left corner can be retrieved by
0568: * calling {@link #getX()} and {@link #getY()}.
0569: * <P>
0570: * Rendering is subject to the clip region of the Graphics object.
0571: * The Sprite will be drawn only if it is visible.
0572: * <p>
0573: * If the Sprite's Image is mutable, the Sprite is rendered using the
0574: * current contents of the Image.
0575: *
0576: * @param g the graphics object to draw <code>Sprite</code> on
0577: * @throws NullPointerException if <code>g</code> is <code>null</code>
0578: *
0579: */
0580: public final void paint(Graphics g) {
0581: // managing the painting order is the responsibility of
0582: // the layermanager, so depth is ignored
0583: if (g == null) {
0584: throw new NullPointerException();
0585: }
0586:
0587: if (visible) {
0588:
0589: // width and height of the source
0590: // image is the width and height
0591: // of the original frame
0592: g.drawRegion(sourceImage,
0593: frameCoordsX[frameSequence[sequenceIndex]],
0594: frameCoordsY[frameSequence[sequenceIndex]],
0595: srcFrameWidth, srcFrameHeight,
0596: t_currentTransformation, this .x, this .y,
0597: Graphics.TOP | Graphics.LEFT);
0598: }
0599:
0600: }
0601:
0602: /**
0603: * Set the frame sequence for this Sprite. <p>
0604: *
0605: * All Sprites have a default sequence that displays the Sprites
0606: * frames in order. This method allows for the creation of an
0607: * arbitrary sequence using the available frames. The current
0608: * index in the frame sequence is reset to zero as a result of
0609: * calling this method.
0610: * <p>
0611: * The contents of the sequence array are copied when this method
0612: * is called; thus, any changes made to the array after this method
0613: * returns have no effect on the Sprite's frame sequence.
0614: * <P>
0615: * Passing in <code>null</code> causes the Sprite to revert to the
0616: * default frame sequence.<p>
0617: *
0618: * @param sequence an array of integers, where each integer represents
0619: * a frame index
0620: *
0621: * @throws ArrayIndexOutOfBoundsException if seq is non-null and any member
0622: * of the array has a value less than <code>0</code> or
0623: * greater than or equal to the
0624: * number of frames as reported by {@link #getRawFrameCount()}
0625: * @throws IllegalArgumentException if the array has less than
0626: * <code>1</code> element
0627: * @see #nextFrame
0628: * @see #prevFrame
0629: * @see #setFrame
0630: * @see #getFrame
0631: *
0632: */
0633: public void setFrameSequence(int sequence[]) {
0634:
0635: if (sequence == null) {
0636: // revert to the default sequence
0637: sequenceIndex = 0;
0638: customSequenceDefined = false;
0639: frameSequence = new int[numberFrames];
0640: // copy frames indices into frameSequence
0641: for (int i = 0; i < numberFrames; i++) {
0642: frameSequence[i] = i;
0643: }
0644: return;
0645: }
0646:
0647: if (sequence.length < 1) {
0648: throw new IllegalArgumentException();
0649: }
0650:
0651: for (int i = 0; i < sequence.length; i++) {
0652: if (sequence[i] < 0 || sequence[i] >= numberFrames) {
0653: throw new ArrayIndexOutOfBoundsException();
0654: }
0655: }
0656: customSequenceDefined = true;
0657: frameSequence = new int[sequence.length];
0658: System
0659: .arraycopy(sequence, 0, frameSequence, 0,
0660: sequence.length);
0661: sequenceIndex = 0;
0662: }
0663:
0664: /**
0665: * Changes the Image containing the Sprite's frames.
0666: * <p>
0667: * Replaces the current raw frames of the Sprite with a new set of raw
0668: * frames. See the constructor {@link #Sprite(Image, int, int)} for
0669: * information on how the frames are created from the image. The
0670: * values returned by {@link Layer#getWidth} and {@link Layer#getHeight}
0671: * will reflect the new frame width and frame height subject to the
0672: * Sprite's current transform.
0673: * <p>
0674: * Changing the image for the Sprite could change the number of raw
0675: * frames. If the new frame set has as many or more raw frames than the
0676: * previous frame set, then:
0677: * <ul>
0678: * <li>The current frame will be unchanged
0679: * <li>If a custom frame sequence has been defined (using
0680: * {@link #setFrameSequence(int[])}), it will remain unchanged. If no
0681: * custom frame sequence is defined (i.e. the default frame
0682: * sequence
0683: * is in use), the default frame sequence will be updated to
0684: * be the default frame sequence for the new frame set. In other
0685: * words, the new default frame sequence will include all of the
0686: * frames from the new raw frame set, as if this new image had been
0687: * used in the constructor.
0688: * </ul>
0689: * <p>
0690: * If the new frame set has fewer frames than the previous frame set,
0691: * then:
0692: * <ul>
0693: * <li>The current frame will be reset to entry 0
0694: * <li>Any custom frame sequence will be discarded and the frame sequence
0695: * will revert to the default frame sequence for the new frame
0696: * set.
0697: * </ul>
0698: * <p>
0699: * The reference point location is unchanged as a result of calling this
0700: * method, both in terms of its defined location within the Sprite and its
0701: * position in the painter's coordinate system. However, if the frame
0702: * size is changed and the Sprite has been transformed, the position of
0703: * the Sprite's upper-left corner may change such that the reference
0704: * point remains stationary.
0705: * <p>
0706: * If the Sprite's frame size is changed by this method, the collision
0707: * rectangle is reset to its default value (i.e. it is set to the new
0708: * bounds of the untransformed Sprite).
0709: * <p>
0710: * @param img the <code>Image</code> to use for
0711: * <code>Sprite</code>
0712: * @param frameWidth the width in pixels of the individual raw frames
0713: * @param frameHeight the height in pixels of the individual raw frames
0714: * @throws NullPointerException if <code>img</code> is <code>null</code>
0715: * @throws IllegalArgumentException if <code>frameHeight</code> or
0716: * <code>frameWidth</code> is less than <code>1</code>
0717: * @throws IllegalArgumentException if the image width is not an integer
0718: * multiple of the <code>frameWidth</code>
0719: * @throws IllegalArgumentException if the image height is not an integer
0720: * multiple of the <code>frameHeight</code>
0721: */
0722: public void setImage(Image img, int frameWidth, int frameHeight) {
0723:
0724: // if image is null image.getWidth() will throw NullPointerException
0725: if ((frameWidth < 1 || frameHeight < 1)
0726: || ((img.getWidth() % frameWidth) != 0)
0727: || ((img.getHeight() % frameHeight) != 0)) {
0728: throw new IllegalArgumentException();
0729: }
0730:
0731: int noOfFrames = (img.getWidth() / frameWidth)
0732: * (img.getHeight() / frameHeight);
0733:
0734: boolean maintainCurFrame = true;
0735: if (noOfFrames < numberFrames) {
0736: // use default frame , sequence index = 0
0737: maintainCurFrame = false;
0738: customSequenceDefined = false;
0739: }
0740:
0741: if (!((srcFrameWidth == frameWidth) && (srcFrameHeight == frameHeight))) {
0742:
0743: // computing is the location
0744: // of the reference pixel in the painter's coordinate system.
0745: // and then use this to find x and y position of the Sprite
0746: int oldX = this .x
0747: + getTransformedPtX(dRefX, dRefY,
0748: this .t_currentTransformation);
0749:
0750: int oldY = this .y
0751: + getTransformedPtY(dRefX, dRefY,
0752: this .t_currentTransformation);
0753:
0754: setWidthImpl(frameWidth);
0755: setHeightImpl(frameHeight);
0756:
0757: initializeFrames(img, frameWidth, frameHeight,
0758: maintainCurFrame);
0759:
0760: // initialize collision rectangle
0761: initCollisionRectBounds();
0762:
0763: // set the new x and y position of the Sprite
0764: this .x = oldX
0765: - getTransformedPtX(dRefX, dRefY,
0766: this .t_currentTransformation);
0767:
0768: this .y = oldY
0769: - getTransformedPtY(dRefX, dRefY,
0770: this .t_currentTransformation);
0771:
0772: // Calculate transformed sprites collision rectangle
0773: // and transformed width and height
0774:
0775: computeTransformedBounds(this .t_currentTransformation);
0776:
0777: } else {
0778: // just reinitialize the animation frames.
0779: initializeFrames(img, frameWidth, frameHeight,
0780: maintainCurFrame);
0781: }
0782:
0783: }
0784:
0785: /**
0786: * Defines the Sprite's bounding rectangle that is used for collision
0787: * detection purposes. This rectangle is specified relative to the
0788: * un-transformed Sprite's upper-left corner and defines the area that is
0789: * checked for collision detection. For pixel-level detection, only those
0790: * pixels within the collision rectangle are checked.
0791: *
0792: * By default, a Sprite's collision rectangle is located at 0,0 as has the
0793: * same dimensions as the Sprite. The collision rectangle may be
0794: * specified to be larger or smaller than the default rectangle; if made
0795: * larger, the pixels outside the bounds of the Sprite are considered to be
0796: * transparent for pixel-level collision detection.
0797: *
0798: * @param inp_x the horizontal location of the collision
0799: * rectangle relative to the untransformed Sprite's left edge
0800: * @param inp_y the vertical location of the collision rectangle relative to
0801: * the untransformed Sprite's top edge
0802: * @param width the width of the collision rectangle
0803: * @param height the height of the collision rectangle
0804: * @throws IllegalArgumentException if the specified
0805: * <code>width</code> or <code>height</code> is
0806: * less than <code>0</code>
0807: */
0808: public void defineCollisionRectangle(int inp_x, int inp_y,
0809: int width, int height) {
0810:
0811: if (width < 0 || height < 0) {
0812: throw new IllegalArgumentException();
0813: }
0814:
0815: collisionRectX = inp_x;
0816: collisionRectY = inp_y;
0817: collisionRectWidth = width;
0818: collisionRectHeight = height;
0819:
0820: // call set transform with current transformation to
0821: // update transformed sprites collision rectangle
0822: setTransformImpl(t_currentTransformation);
0823: }
0824:
0825: /**
0826: * Sets the transform for this Sprite. Transforms can be
0827: * applied to a Sprite to change its rendered appearance. Transforms
0828: * are applied to the original Sprite image; they are not cumulative,
0829: * nor can they be combined. By default, a Sprite's transform is
0830: * {@link #TRANS_NONE}.
0831: * <P>
0832: * Since some transforms involve rotations of 90 or 270 degrees, their
0833: * use may result in the overall width and height of the Sprite
0834: * being swapped. As a result, the values returned by
0835: * {@link Layer#getWidth} and {@link Layer#getHeight} may change.
0836: * <p>
0837: * The collision rectangle is also modified by the transform so that
0838: * it remains static relative to the pixel data of the Sprite.
0839: * Similarly, the defined reference pixel is unchanged by this method,
0840: * but its visual location within the Sprite may change as a result.
0841: * <P>
0842: * This method repositions the Sprite so that the location of
0843: * the reference pixel in the painter's coordinate system does not change
0844: * as a result of changing the transform. Thus, the reference pixel
0845: * effectively becomes the centerpoint for the transform. Consequently,
0846: * the values returned by {@link #getRefPixelX} and {@link #getRefPixelY}
0847: * will be the same both before and after the transform is applied, but
0848: * the values returned by {@link #getX getX()} and {@link #getY getY()}
0849: * may change.
0850: * <p>
0851: * @param transform the desired transform for this <code>Sprite</code>
0852: * @throws IllegalArgumentException if the requested
0853: * <code>transform</code> is invalid
0854: * @see #TRANS_NONE
0855: * @see #TRANS_ROT90
0856: * @see #TRANS_ROT180
0857: * @see #TRANS_ROT270
0858: * @see #TRANS_MIRROR
0859: * @see #TRANS_MIRROR_ROT90
0860: * @see #TRANS_MIRROR_ROT180
0861: * @see #TRANS_MIRROR_ROT270
0862: *
0863: */
0864: public void setTransform(int transform) {
0865: setTransformImpl(transform);
0866: }
0867:
0868: /**
0869: * Checks for a collision between this Sprite and the specified Sprite.
0870: * <P>
0871: * If pixel-level detection is used, a collision is detected only if
0872: * opaque pixels collide. That is, an opaque pixel in the first
0873: * Sprite would have to collide with an opaque pixel in the second
0874: * Sprite for a collision to be detected. Only those pixels within
0875: * the Sprites' respective collision rectangles are checked.
0876: * <P>
0877: * If pixel-level detection is not used, this method simply
0878: * checks if the Sprites' collision rectangles intersect.
0879: * <P>
0880: * Any transforms applied to the Sprites are automatically accounted for.
0881: * <P>
0882: * Both Sprites must be visible in order for a collision to be
0883: * detected.
0884: * <P>
0885: * @param s the <code>Sprite</code> to test for collision with
0886: * @param pixelLevel <code>true</code> to test for collision on a
0887: * pixel-by-pixel basis, <code>false</code> to test using simple
0888: * bounds checking.
0889: * @return <code>true</code> if the two Sprites have collided, otherwise
0890: * <code>false</code>
0891: * @throws NullPointerException if Sprite <code>s</code> is
0892: * <code>null</code>
0893: */
0894: public final boolean collidesWith(Sprite s, boolean pixelLevel) {
0895:
0896: // check if either of the Sprite's are not visible
0897: if (!(s.visible && this .visible)) {
0898: return false;
0899: }
0900:
0901: // these are package private
0902: // and can be accessed directly
0903: int otherLeft = s.x + s.t_collisionRectX;
0904: int otherTop = s.y + s.t_collisionRectY;
0905: int otherRight = otherLeft + s.t_collisionRectWidth;
0906: int otherBottom = otherTop + s.t_collisionRectHeight;
0907:
0908: int left = this .x + this .t_collisionRectX;
0909: int top = this .y + this .t_collisionRectY;
0910: int right = left + this .t_collisionRectWidth;
0911: int bottom = top + this .t_collisionRectHeight;
0912:
0913: // check if the collision rectangles of the two sprites intersect
0914: if (intersectRect(otherLeft, otherTop, otherRight, otherBottom,
0915: left, top, right, bottom)) {
0916:
0917: // collision rectangles intersect
0918: if (pixelLevel) {
0919:
0920: // we need to check pixel level collision detection.
0921: // use only the coordinates within the Sprite frame if
0922: // the collision rectangle is larger than the Sprite
0923: // frame
0924: if (this .t_collisionRectX < 0) {
0925: left = this .x;
0926: }
0927: if (this .t_collisionRectY < 0) {
0928: top = this .y;
0929: }
0930: if ((this .t_collisionRectX + this .t_collisionRectWidth) > this .width) {
0931: right = this .x + this .width;
0932: }
0933: if ((this .t_collisionRectY + this .t_collisionRectHeight) > this .height) {
0934: bottom = this .y + this .height;
0935: }
0936:
0937: // similarly for the other Sprite
0938: if (s.t_collisionRectX < 0) {
0939: otherLeft = s.x;
0940: }
0941: if (s.t_collisionRectY < 0) {
0942: otherTop = s.y;
0943: }
0944: if ((s.t_collisionRectX + s.t_collisionRectWidth) > s.width) {
0945: otherRight = s.x + s.width;
0946: }
0947: if ((s.t_collisionRectY + s.t_collisionRectHeight) > s.height) {
0948: otherBottom = s.y + s.height;
0949: }
0950:
0951: // recheck if the updated collision area rectangles intersect
0952: if (!intersectRect(otherLeft, otherTop, otherRight,
0953: otherBottom, left, top, right, bottom)) {
0954:
0955: // if they don't intersect, return false;
0956: return false;
0957: }
0958:
0959: // the updated collision rectangles intersect,
0960: // go ahead with collision detection
0961:
0962: // find intersecting region,
0963: // within the collision rectangles
0964: int intersectLeft = (left < otherLeft) ? otherLeft
0965: : left;
0966: int intersectTop = (top < otherTop) ? otherTop : top;
0967:
0968: // used once, optimize.
0969: int intersectRight = (right < otherRight) ? right
0970: : otherRight;
0971: int intersectBottom = (bottom < otherBottom) ? bottom
0972: : otherBottom;
0973:
0974: int intersectWidth = Math.abs(intersectRight
0975: - intersectLeft);
0976: int intersectHeight = Math.abs(intersectBottom
0977: - intersectTop);
0978:
0979: // have the coordinates in painter space,
0980: // need coordinates of top left and width, height
0981: // in source image of Sprite.
0982:
0983: int this ImageXOffset = getImageTopLeftX(intersectLeft,
0984: intersectTop, intersectRight, intersectBottom);
0985:
0986: int this ImageYOffset = getImageTopLeftY(intersectLeft,
0987: intersectTop, intersectRight, intersectBottom);
0988:
0989: int otherImageXOffset = s.getImageTopLeftX(
0990: intersectLeft, intersectTop, intersectRight,
0991: intersectBottom);
0992:
0993: int otherImageYOffset = s.getImageTopLeftY(
0994: intersectLeft, intersectTop, intersectRight,
0995: intersectBottom);
0996:
0997: // check if opaque pixels intersect.
0998:
0999: return doPixelCollision(this ImageXOffset,
1000: this ImageYOffset, otherImageXOffset,
1001: otherImageYOffset, this .sourceImage,
1002: this .t_currentTransformation, s.sourceImage,
1003: s.t_currentTransformation, intersectWidth,
1004: intersectHeight);
1005:
1006: } else {
1007: // collides!
1008: return true;
1009: }
1010: }
1011: return false;
1012:
1013: }
1014:
1015: /**
1016: * Checks for a collision between this Sprite and the specified
1017: * TiledLayer. If pixel-level detection is used, a collision is
1018: * detected only if opaque pixels collide. That is, an opaque pixel in
1019: * the Sprite would have to collide with an opaque pixel in TiledLayer
1020: * for a collision to be detected. Only those pixels within the Sprite's
1021: * collision rectangle are checked.
1022: * <P>
1023: * If pixel-level detection is not used, this method simply checks if the
1024: * Sprite's collision rectangle intersects with a non-empty cell in the
1025: * TiledLayer.
1026: * <P>
1027: * Any transform applied to the Sprite is automatically accounted for.
1028: * <P>
1029: * The Sprite and the TiledLayer must both be visible in order for
1030: * a collision to be detected.
1031: * <P>
1032: * @param t the <code>TiledLayer</code> to test for collision with
1033: * @param pixelLevel <code>true</code> to test for collision on a
1034: * pixel-by-pixel basis, <code>false</code> to test using simple bounds
1035: * checking against non-empty cells.
1036: * @return <code>true</code> if this <code>Sprite</code> has
1037: * collided with the <code>TiledLayer</code>, otherwise
1038: * <code>false</code>
1039: * @throws NullPointerException if <code>t</code> is <code>null</code>
1040: */
1041: public final boolean collidesWith(TiledLayer t, boolean pixelLevel) {
1042:
1043: // check if either this Sprite or the TiledLayer is not visible
1044: if (!(t.visible && this .visible)) {
1045: return false;
1046: }
1047:
1048: // dimensions of tiledLayer, cell, and
1049: // this Sprite's collision rectangle
1050:
1051: // these are package private
1052: // and can be accessed directly
1053: int tLx1 = t.x;
1054: int tLy1 = t.y;
1055: int tLx2 = tLx1 + t.width;
1056: int tLy2 = tLy1 + t.height;
1057:
1058: int tW = t.getCellWidth();
1059: int tH = t.getCellHeight();
1060:
1061: int sx1 = this .x + this .t_collisionRectX;
1062: int sy1 = this .y + this .t_collisionRectY;
1063: int sx2 = sx1 + this .t_collisionRectWidth;
1064: int sy2 = sy1 + this .t_collisionRectHeight;
1065:
1066: // number of cells
1067: int tNumCols = t.getColumns();
1068: int tNumRows = t.getRows();
1069:
1070: // temporary loop variables.
1071: int startCol; // = 0;
1072: int endCol; // = 0;
1073: int startRow; // = 0;
1074: int endRow; // = 0;
1075:
1076: if (!intersectRect(tLx1, tLy1, tLx2, tLy2, sx1, sy1, sx2, sy2)) {
1077: // if the collision rectangle of the sprite
1078: // does not intersect with the dimensions of the entire
1079: // tiled layer
1080: return false;
1081: }
1082:
1083: // so there is an intersection
1084:
1085: // note sx1 < sx2, tLx1 < tLx2, sx2 > tLx1 from intersectRect()
1086: // use <= for comparison as this saves us some
1087: // computation - the result will be 0
1088: startCol = (sx1 <= tLx1) ? 0 : (sx1 - tLx1) / tW;
1089: startRow = (sy1 <= tLy1) ? 0 : (sy1 - tLy1) / tH;
1090: // since tLx1 < sx2 < tLx2, the computation will yield
1091: // a result between 0 and tNumCols - 1
1092: // subtract by 1 because sx2,sy2 represent
1093: // the enclosing bounds of the sprite, not the
1094: // locations in the coordinate system.
1095: endCol = (sx2 < tLx2) ? ((sx2 - 1 - tLx1) / tW) : tNumCols - 1;
1096: endRow = (sy2 < tLy2) ? ((sy2 - 1 - tLy1) / tH) : tNumRows - 1;
1097:
1098: if (!pixelLevel) {
1099: // check for intersection with a non-empty cell,
1100: for (int row = startRow; row <= endRow; row++) {
1101: for (int col = startCol; col <= endCol; col++) {
1102: if (t.getCell(col, row) != 0) {
1103: return true;
1104: }
1105: }
1106: }
1107: // worst case! we scanned through entire
1108: // overlapping region and
1109: // all the cells are empty!
1110: return false;
1111: } else {
1112: // do pixel level
1113:
1114: // we need to check pixel level collision detection.
1115: // use only the coordinates within the Sprite frame if
1116: // the collision rectangle is larger than the Sprite
1117: // frame
1118: if (this .t_collisionRectX < 0) {
1119: sx1 = this .x;
1120: }
1121: if (this .t_collisionRectY < 0) {
1122: sy1 = this .y;
1123: }
1124: if ((this .t_collisionRectX + this .t_collisionRectWidth) > this .width) {
1125: sx2 = this .x + this .width;
1126: }
1127: if ((this .t_collisionRectY + this .t_collisionRectHeight) > this .height) {
1128: sy2 = this .y + this .height;
1129: }
1130:
1131: if (!intersectRect(tLx1, tLy1, tLx2, tLy2, sx1, sy1, sx2,
1132: sy2)) {
1133: return (false);
1134: }
1135:
1136: // we have an intersection between the Sprite and
1137: // one or more cells of the tiledlayer
1138:
1139: // note sx1 < sx2, tLx1 < tLx2, sx2 > tLx1 from intersectRect()
1140: // use <= for comparison as this saves us some
1141: // computation - the result will be 0
1142: startCol = (sx1 <= tLx1) ? 0 : (sx1 - tLx1) / tW;
1143: startRow = (sy1 <= tLy1) ? 0 : (sy1 - tLy1) / tH;
1144: // since tLx1 < sx2 < tLx2, the computation will yield
1145: // a result between 0 and tNumCols - 1
1146: // subtract by 1 because sx2,sy2 represent
1147: // the enclosing bounds of the sprite, not the
1148: // locations in the coordinate system.
1149: endCol = (sx2 < tLx2) ? ((sx2 - 1 - tLx1) / tW)
1150: : tNumCols - 1;
1151: endRow = (sy2 < tLy2) ? ((sy2 - 1 - tLy1) / tH)
1152: : tNumRows - 1;
1153:
1154: // current cell coordinates
1155: int cellTop = startRow * tH + tLy1;
1156: int cellBottom = cellTop + tH;
1157:
1158: // the index of the current tile.
1159: int tileIndex; // = 0;
1160:
1161: for (int row = startRow; row <= endRow; row++, cellTop += tH, cellBottom += tH) {
1162:
1163: // current cell coordinates
1164: int cellLeft = startCol * tW + tLx1;
1165: int cellRight = cellLeft + tW;
1166:
1167: for (int col = startCol; col <= endCol; col++, cellLeft += tW, cellRight += tW) {
1168:
1169: tileIndex = t.getCell(col, row);
1170:
1171: if (tileIndex != 0) {
1172:
1173: // current cell/sprite intersection coordinates
1174: // in painter coordinate system.
1175: // find intersecting region,
1176: int intersectLeft = (sx1 < cellLeft) ? cellLeft
1177: : sx1;
1178: int intersectTop = (sy1 < cellTop) ? cellTop
1179: : sy1;
1180:
1181: // used once, optimize.
1182: int intersectRight = (sx2 < cellRight) ? sx2
1183: : cellRight;
1184: int intersectBottom = (sy2 < cellBottom) ? sy2
1185: : cellBottom;
1186:
1187: if (intersectLeft > intersectRight) {
1188: int temp = intersectRight;
1189: intersectRight = intersectLeft;
1190: intersectLeft = temp;
1191: }
1192:
1193: if (intersectTop > intersectBottom) {
1194: int temp = intersectBottom;
1195: intersectBottom = intersectTop;
1196: intersectTop = temp;
1197: }
1198:
1199: int intersectWidth = intersectRight
1200: - intersectLeft;
1201: int intersectHeight = intersectBottom
1202: - intersectTop;
1203:
1204: int image1XOffset = getImageTopLeftX(
1205: intersectLeft, intersectTop,
1206: intersectRight, intersectBottom);
1207:
1208: int image1YOffset = getImageTopLeftY(
1209: intersectLeft, intersectTop,
1210: intersectRight, intersectBottom);
1211:
1212: int image2XOffset = t.tileSetX[tileIndex]
1213: + (intersectLeft - cellLeft);
1214: int image2YOffset = t.tileSetY[tileIndex]
1215: + (intersectTop - cellTop);
1216:
1217: if (doPixelCollision(image1XOffset,
1218: image1YOffset, image2XOffset,
1219: image2YOffset, this .sourceImage,
1220: this .t_currentTransformation,
1221: t.sourceImage, TRANS_NONE,
1222: intersectWidth, intersectHeight)) {
1223: // intersection found with this tile
1224: return true;
1225: }
1226: }
1227: } // end of for col
1228: }// end of for row
1229:
1230: // worst case! we scanned through entire
1231: // overlapping region and
1232: // no pixels collide!
1233: return false;
1234: }
1235:
1236: }
1237:
1238: /**
1239: * Checks for a collision between this Sprite and the specified Image
1240: * with its upper left corner at the specified location. If pixel-level
1241: * detection is used, a collision is detected only if opaque pixels
1242: * collide. That is, an opaque pixel in the Sprite would have to collide
1243: * with an opaque pixel in Image for a collision to be detected. Only
1244: * those pixels within the Sprite's collision rectangle are checked.
1245: * <P>
1246: * If pixel-level detection is not used, this method simply checks if the
1247: * Sprite's collision rectangle intersects with the Image's bounds.
1248: * <P>
1249: * Any transform applied to the Sprite is automatically accounted for.
1250: * <P>
1251: * The Sprite must be visible in order for a collision to be
1252: * detected.
1253: * <P>
1254: * @param image the <code>Image</code> to test for collision
1255: * @param inp_x the horizontal location of the <code>Image</code>'s
1256: * upper left corner
1257: * @param inp_y the vertical location of the <code>Image</code>'s
1258: * upper left corner
1259: * @param pixelLevel <code>true</code> to test for collision on a
1260: * pixel-by-pixel basis, <code>false</code> to test using simple
1261: * bounds checking
1262: * @return <code>true</code> if this <code>Sprite</code> has
1263: * collided with the <code>Image</code>, otherwise
1264: * <code>false</code>
1265: * @throws NullPointerException if <code>image</code> is
1266: * <code>null</code>
1267: */
1268: public final boolean collidesWith(Image image, int inp_x,
1269: int inp_y, boolean pixelLevel) {
1270:
1271: // check if this Sprite is not visible
1272: if (!(visible)) {
1273: return false;
1274: }
1275:
1276: // if image is null
1277: // image.getWidth() will throw NullPointerException
1278: int otherLeft = inp_x;
1279: int otherTop = inp_y;
1280: int otherRight = inp_x + image.getWidth();
1281: int otherBottom = inp_y + image.getHeight();
1282:
1283: int left = x + t_collisionRectX;
1284: int top = y + t_collisionRectY;
1285: int right = left + t_collisionRectWidth;
1286: int bottom = top + t_collisionRectHeight;
1287:
1288: // first check if the collision rectangles of the two sprites intersect
1289: if (intersectRect(otherLeft, otherTop, otherRight, otherBottom,
1290: left, top, right, bottom)) {
1291:
1292: // collision rectangles intersect
1293: if (pixelLevel) {
1294:
1295: // find intersecting region,
1296:
1297: // we need to check pixel level collision detection.
1298: // use only the coordinates within the Sprite frame if
1299: // the collision rectangle is larger than the Sprite
1300: // frame
1301: if (this .t_collisionRectX < 0) {
1302: left = this .x;
1303: }
1304: if (this .t_collisionRectY < 0) {
1305: top = this .y;
1306: }
1307: if ((this .t_collisionRectX + this .t_collisionRectWidth) > this .width) {
1308: right = this .x + this .width;
1309: }
1310: if ((this .t_collisionRectY + this .t_collisionRectHeight) > this .height) {
1311: bottom = this .y + this .height;
1312: }
1313:
1314: // recheck if the updated collision area rectangles intersect
1315: if (!intersectRect(otherLeft, otherTop, otherRight,
1316: otherBottom, left, top, right, bottom)) {
1317:
1318: // if they don't intersect, return false;
1319: return false;
1320: }
1321:
1322: // within the collision rectangles
1323: int intersectLeft = (left < otherLeft) ? otherLeft
1324: : left;
1325: int intersectTop = (top < otherTop) ? otherTop : top;
1326:
1327: // used once, optimize.
1328: int intersectRight = (right < otherRight) ? right
1329: : otherRight;
1330: int intersectBottom = (bottom < otherBottom) ? bottom
1331: : otherBottom;
1332:
1333: int intersectWidth = Math.abs(intersectRight
1334: - intersectLeft);
1335: int intersectHeight = Math.abs(intersectBottom
1336: - intersectTop);
1337:
1338: // have the coordinates in painter space,
1339: // need coordinates of top left and width, height
1340: // in source image of Sprite.
1341:
1342: int this ImageXOffset = getImageTopLeftX(intersectLeft,
1343: intersectTop, intersectRight, intersectBottom);
1344:
1345: int this ImageYOffset = getImageTopLeftY(intersectLeft,
1346: intersectTop, intersectRight, intersectBottom);
1347:
1348: int otherImageXOffset = intersectLeft - inp_x;
1349: int otherImageYOffset = intersectTop - inp_y;
1350:
1351: // check if opaque pixels intersect.
1352: return doPixelCollision(this ImageXOffset,
1353: this ImageYOffset, otherImageXOffset,
1354: otherImageYOffset, this .sourceImage,
1355: this .t_currentTransformation, image,
1356: Sprite.TRANS_NONE, intersectWidth,
1357: intersectHeight);
1358:
1359: } else {
1360: // collides!
1361: return true;
1362: }
1363: }
1364: return false;
1365:
1366: }
1367:
1368: // -----
1369:
1370: // ----- private -----
1371:
1372: /**
1373: * create the Image Array.
1374: *
1375: * @param image Image to use for Sprite
1376: * @param fWidth width, in pixels, of the individual raw frames
1377: * @param fHeight height, in pixels, of the individual raw frames
1378: * @param maintainCurFrame true if Current Frame is maintained
1379: */
1380: private void initializeFrames(Image image, int fWidth, int fHeight,
1381: boolean maintainCurFrame) {
1382:
1383: int imageW = image.getWidth();
1384: int imageH = image.getHeight();
1385:
1386: int numHorizontalFrames = imageW / fWidth;
1387: int numVerticalFrames = imageH / fHeight;
1388:
1389: sourceImage = image;
1390:
1391: srcFrameWidth = fWidth;
1392: srcFrameHeight = fHeight;
1393:
1394: numberFrames = numHorizontalFrames * numVerticalFrames;
1395:
1396: frameCoordsX = new int[numberFrames];
1397: frameCoordsY = new int[numberFrames];
1398:
1399: if (!maintainCurFrame) {
1400: sequenceIndex = 0;
1401: }
1402:
1403: if (!customSequenceDefined) {
1404: frameSequence = new int[numberFrames];
1405: }
1406:
1407: int currentFrame = 0;
1408:
1409: for (int yy = 0; yy < imageH; yy += fHeight) {
1410: for (int xx = 0; xx < imageW; xx += fWidth) {
1411:
1412: frameCoordsX[currentFrame] = xx;
1413: frameCoordsY[currentFrame] = yy;
1414:
1415: if (!customSequenceDefined) {
1416: frameSequence[currentFrame] = currentFrame;
1417: }
1418: currentFrame++;
1419:
1420: }
1421: }
1422: }
1423:
1424: /**
1425: * initialize the collision rectangle
1426: */
1427: private void initCollisionRectBounds() {
1428:
1429: // reset x and y of collision rectangle
1430: collisionRectX = 0;
1431: collisionRectY = 0;
1432:
1433: // intialize the collision rectangle bounds to that of the sprite
1434: collisionRectWidth = this .width;
1435: collisionRectHeight = this .height;
1436:
1437: }
1438:
1439: /**
1440: * Detect rectangle intersection
1441: *
1442: * @param r1x1 left co-ordinate of first rectangle
1443: * @param r1y1 top co-ordinate of first rectangle
1444: * @param r1x2 right co-ordinate of first rectangle
1445: * @param r1y2 bottom co-ordinate of first rectangle
1446: * @param r2x1 left co-ordinate of second rectangle
1447: * @param r2y1 top co-ordinate of second rectangle
1448: * @param r2x2 right co-ordinate of second rectangle
1449: * @param r2y2 bottom co-ordinate of second rectangle
1450: * @return True if there is rectangle intersection
1451: */
1452: private boolean intersectRect(int r1x1, int r1y1, int r1x2,
1453: int r1y2, int r2x1, int r2y1, int r2x2, int r2y2) {
1454: if (r2x1 >= r1x2 || r2y1 >= r1y2 || r2x2 <= r1x1
1455: || r2y2 <= r1y1) {
1456: return false;
1457: } else {
1458: return true;
1459: }
1460: }
1461:
1462: /**
1463: * Detect opaque pixel intersection between regions of two images
1464: *
1465: * @param image1XOffset left coordinate in the first image
1466: * @param image1YOffset top coordinate in the first image
1467: * @param image2XOffset left coordinate in the second image
1468: * @param image2YOffset top coordinate in the second image
1469: * @param image1 first source image
1470: * @param transform1 The transform for the first image
1471: * @param image2 second source image
1472: * @param transform2 transform set on the second image
1473: * @param width width of overlapping region, when transformed
1474: * @param height height of overlapping region, when transformed
1475: *
1476: * Clarification required on parameters:
1477: * XOffset and YOffset are the offsets from the top left
1478: * hand corner of the image.
1479: * width, height is the dimensions of the intersecting regions
1480: * in the two transformed images.
1481: * there fore appropriate conversions have to be made on these
1482: * dimensions when using the values, according to the transformation
1483: * that has been set.
1484: *
1485: * @return True if there is a pixel level collision
1486: */
1487: private static boolean doPixelCollision(int image1XOffset,
1488: int image1YOffset, int image2XOffset, int image2YOffset,
1489: Image image1, int transform1, Image image2, int transform2,
1490: int width, int height) {
1491:
1492: // starting point of comparison
1493: int startY1;
1494: // x and y increments
1495: int xIncr1, yIncr1;
1496:
1497: // .. for image 2
1498: int startY2;
1499: int xIncr2, yIncr2;
1500:
1501: int numPixels = height * width;
1502:
1503: int[] argbData1 = new int[numPixels];
1504: int[] argbData2 = new int[numPixels];
1505:
1506: if (0x0 != (transform1 & INVERTED_AXES)) {
1507: // inverted axes
1508:
1509: // scanlength = height
1510:
1511: if (0x0 != (transform1 & Y_FLIP)) {
1512: xIncr1 = -(height); // - scanlength
1513:
1514: startY1 = numPixels - height; // numPixels - scanlength
1515: } else {
1516: xIncr1 = height; // + scanlength
1517:
1518: startY1 = 0;
1519: }
1520:
1521: if (0x0 != (transform1 & X_FLIP)) {
1522: yIncr1 = -1;
1523:
1524: startY1 += (height - 1);
1525: } else {
1526: yIncr1 = +1;
1527: }
1528:
1529: image1.getRGB(argbData1, 0, height, // scanlength = height
1530: image1XOffset, image1YOffset, height, width);
1531:
1532: } else {
1533:
1534: // scanlength = width
1535:
1536: if (0x0 != (transform1 & Y_FLIP)) {
1537:
1538: startY1 = numPixels - width; // numPixels - scanlength
1539:
1540: yIncr1 = -(width); // - scanlength
1541: } else {
1542: startY1 = 0;
1543:
1544: yIncr1 = width; // + scanlength
1545: }
1546:
1547: if (0x0 != (transform1 & X_FLIP)) {
1548: xIncr1 = -1;
1549:
1550: startY1 += (width - 1);
1551: } else {
1552: xIncr1 = +1;
1553: }
1554:
1555: image1.getRGB(argbData1, 0, width, // scanlength = width
1556: image1XOffset, image1YOffset, width, height);
1557:
1558: }
1559:
1560: if (0x0 != (transform2 & INVERTED_AXES)) {
1561: // inverted axes
1562:
1563: if (0x0 != (transform2 & Y_FLIP)) {
1564: xIncr2 = -(height);
1565:
1566: startY2 = numPixels - height;
1567: } else {
1568: xIncr2 = height;
1569:
1570: startY2 = 0;
1571: }
1572:
1573: if (0x0 != (transform2 & X_FLIP)) {
1574: yIncr2 = -1;
1575:
1576: startY2 += height - 1;
1577: } else {
1578: yIncr2 = +1;
1579: }
1580:
1581: image2.getRGB(argbData2, 0, height, image2XOffset,
1582: image2YOffset, height, width);
1583:
1584: } else {
1585:
1586: if (0x0 != (transform2 & Y_FLIP)) {
1587: startY2 = numPixels - width;
1588:
1589: yIncr2 = -(width);
1590: } else {
1591: startY2 = 0;
1592:
1593: yIncr2 = +width;
1594: }
1595:
1596: if (0x0 != (transform2 & X_FLIP)) {
1597: xIncr2 = -1;
1598:
1599: startY2 += (width - 1);
1600: } else {
1601: xIncr2 = +1;
1602: }
1603:
1604: image2.getRGB(argbData2, 0, width, image2XOffset,
1605: image2YOffset, width, height);
1606:
1607: }
1608:
1609: int x1, x2;
1610: int xLocalBegin1, xLocalBegin2;
1611:
1612: // the loop counters
1613: int numIterRows;
1614: int numIterColumns;
1615:
1616: for (numIterRows = 0, xLocalBegin1 = startY1, xLocalBegin2 = startY2; numIterRows < height; xLocalBegin1 += yIncr1, xLocalBegin2 += yIncr2, numIterRows++) {
1617:
1618: for (numIterColumns = 0, x1 = xLocalBegin1, x2 = xLocalBegin2; numIterColumns < width; x1 += xIncr1, x2 += xIncr2, numIterColumns++) {
1619:
1620: if (((argbData1[x1] & ALPHA_BITMASK) == FULLY_OPAQUE_ALPHA)
1621: && ((argbData2[x2] & ALPHA_BITMASK) == FULLY_OPAQUE_ALPHA)) {
1622:
1623: return true;
1624: }
1625:
1626: } // end for x
1627:
1628: } // end for y
1629:
1630: // worst case! couldn't find a single colliding pixel!
1631: return false;
1632: }
1633:
1634: /**
1635: * Given a rectangle that lies within the sprite
1636: * in the painter's coordinates,
1637: * find the X coordinate of the top left corner
1638: * in the source image of the sprite
1639: *
1640: * @param x1 the x coordinate of the top left of the rectangle
1641: * @param y1 the y coordinate of the top left of the rectangle
1642: * @param x2 the x coordinate of the bottom right of the rectangle
1643: * @param y2 the y coordinate of the bottom right of the rectangle
1644: *
1645: * @return the X coordinate in the source image
1646: *
1647: */
1648: private int getImageTopLeftX(int x1, int y1, int x2, int y2) {
1649: int retX = 0;
1650:
1651: // left = this.x
1652: // right = this.x + this.width
1653: // top = this.y
1654: // bottom = this.y + this.height
1655:
1656: switch (this .t_currentTransformation) {
1657:
1658: case TRANS_NONE:
1659: case TRANS_MIRROR_ROT180:
1660: retX = x1 - this .x;
1661: break;
1662:
1663: case TRANS_MIRROR:
1664: case TRANS_ROT180:
1665: retX = (this .x + this .width) - x2;
1666: break;
1667:
1668: case TRANS_ROT90:
1669: case TRANS_MIRROR_ROT270:
1670: retX = y1 - this .y;
1671: break;
1672:
1673: case TRANS_ROT270:
1674: case TRANS_MIRROR_ROT90:
1675: retX = (this .y + this .height) - y2;
1676: break;
1677:
1678: default:
1679: // for safety/completeness.
1680: Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
1681: "Sprite: t_currentTransformation="
1682: + t_currentTransformation);
1683: return retX;
1684: }
1685:
1686: retX += frameCoordsX[frameSequence[sequenceIndex]];
1687:
1688: return retX;
1689: }
1690:
1691: /**
1692: * Given a rectangle that lies within the sprite
1693: * in the painter's coordinates,
1694: * find the Y coordinate of the top left corner
1695: * in the source image of the sprite
1696: *
1697: * @param x1 the x coordinate of the top left of the rectangle
1698: * @param y1 the y coordinate of the top left of the rectangle
1699: * @param x2 the x coordinate of the bottom right of the rectangle
1700: * @param y2 the y coordinate of the bottom right of the rectangle
1701: *
1702: * @return the Y coordinate in the source image
1703: *
1704: */
1705: private int getImageTopLeftY(int x1, int y1, int x2, int y2) {
1706: int retY = 0;
1707:
1708: // left = this.x
1709: // right = this.x + this.width
1710: // top = this.y
1711: // bottom = this.y + this.height
1712:
1713: switch (this .t_currentTransformation) {
1714:
1715: case TRANS_NONE:
1716: case TRANS_MIRROR:
1717: retY = y1 - this .y;
1718: break;
1719:
1720: case TRANS_ROT180:
1721: case TRANS_MIRROR_ROT180:
1722: retY = (this .y + this .height) - y2;
1723: break;
1724:
1725: case TRANS_ROT270:
1726: case TRANS_MIRROR_ROT270:
1727: retY = x1 - this .x;
1728: break;
1729:
1730: case TRANS_ROT90:
1731: case TRANS_MIRROR_ROT90:
1732: retY = (this .x + this .width) - x2;
1733: break;
1734:
1735: default:
1736: // for safety/completeness.
1737: Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
1738: "Sprite: t_currentTransformation="
1739: + this .t_currentTransformation);
1740: return retY;
1741: }
1742:
1743: retY += frameCoordsY[frameSequence[sequenceIndex]];
1744:
1745: return retY;
1746: }
1747:
1748: /**
1749: * Sets the transform for this Sprite
1750: *
1751: * @param transform the desired transform for this Sprite
1752: */
1753: private void setTransformImpl(int transform) {
1754:
1755: // ---
1756:
1757: // setTransform sets up all transformation related data structures
1758: // except transforming the current frame's bitmap.
1759:
1760: // x, y, width, height, dRefX, dRefY,
1761: // collisionRectX, collisionRectY, collisionRectWidth,
1762: // collisionRectHeight, t_currentTransformation,
1763: // t_bufferImage
1764:
1765: // The actual transformed frame is drawn at paint time.
1766:
1767: // ---
1768:
1769: // update top-left corner position
1770: this .x = this .x
1771: + getTransformedPtX(dRefX, dRefY,
1772: this .t_currentTransformation)
1773: - getTransformedPtX(dRefX, dRefY, transform);
1774:
1775: this .y = this .y
1776: + getTransformedPtY(dRefX, dRefY,
1777: this .t_currentTransformation)
1778: - getTransformedPtY(dRefX, dRefY, transform);
1779:
1780: // Calculate transformed sprites collision rectangle
1781: // and transformed width and height
1782: computeTransformedBounds(transform);
1783:
1784: // set the current transform to be the one requested
1785: t_currentTransformation = transform;
1786:
1787: }
1788:
1789: /**
1790: * Calculate transformed sprites collision rectangle
1791: * and transformed width and height
1792: * @param transform the desired transform for this <code>Sprite</code>
1793: */
1794: private void computeTransformedBounds(int transform) {
1795: switch (transform) {
1796:
1797: case TRANS_NONE:
1798:
1799: t_collisionRectX = collisionRectX;
1800: t_collisionRectY = collisionRectY;
1801: t_collisionRectWidth = collisionRectWidth;
1802: t_collisionRectHeight = collisionRectHeight;
1803: this .width = srcFrameWidth;
1804: this .height = srcFrameHeight;
1805:
1806: break;
1807:
1808: case TRANS_MIRROR:
1809:
1810: // flip across vertical
1811:
1812: // NOTE: top left x and y coordinate must reflect the transformation
1813: // performed around the reference point
1814:
1815: // the X-offset of the reference point from the top left corner
1816: // changes.
1817: t_collisionRectX = srcFrameWidth
1818: - (collisionRectX + collisionRectWidth);
1819:
1820: t_collisionRectY = collisionRectY;
1821: t_collisionRectWidth = collisionRectWidth;
1822: t_collisionRectHeight = collisionRectHeight;
1823:
1824: // the Y-offset of the reference point from the top left corner
1825: // remains the same,
1826: // top left X-co-ordinate changes
1827:
1828: this .width = srcFrameWidth;
1829: this .height = srcFrameHeight;
1830:
1831: break;
1832:
1833: case TRANS_MIRROR_ROT180:
1834:
1835: // flip across horizontal
1836:
1837: // NOTE: top left x and y coordinate must reflect the transformation
1838: // performed around the reference point
1839:
1840: // the Y-offset of the reference point from the top left corner
1841: // changes
1842: t_collisionRectY = srcFrameHeight
1843: - (collisionRectY + collisionRectHeight);
1844:
1845: t_collisionRectX = collisionRectX;
1846: t_collisionRectWidth = collisionRectWidth;
1847: t_collisionRectHeight = collisionRectHeight;
1848:
1849: // width and height are as before
1850: this .width = srcFrameWidth;
1851: this .height = srcFrameHeight;
1852:
1853: // the X-offset of the reference point from the top left corner
1854: // remains the same.
1855: // top left Y-co-ordinate changes
1856:
1857: break;
1858:
1859: case TRANS_ROT90:
1860:
1861: // NOTE: top left x and y coordinate must reflect the transformation
1862: // performed around the reference point
1863:
1864: // the bottom-left corner of the rectangle becomes the
1865: // top-left when rotated 90.
1866:
1867: // both X- and Y-offset to the top left corner may change
1868:
1869: // update the position information for the collision rectangle
1870:
1871: t_collisionRectX = srcFrameHeight
1872: - (collisionRectHeight + collisionRectY);
1873: t_collisionRectY = collisionRectX;
1874:
1875: t_collisionRectHeight = collisionRectWidth;
1876: t_collisionRectWidth = collisionRectHeight;
1877:
1878: // set width and height
1879: this .width = srcFrameHeight;
1880: this .height = srcFrameWidth;
1881:
1882: break;
1883:
1884: case TRANS_ROT180:
1885:
1886: // NOTE: top left x and y coordinate must reflect the transformation
1887: // performed around the reference point
1888:
1889: // width and height are as before
1890:
1891: // both X- and Y- offsets from the top left corner may change
1892:
1893: t_collisionRectX = srcFrameWidth
1894: - (collisionRectWidth + collisionRectX);
1895: t_collisionRectY = srcFrameHeight
1896: - (collisionRectHeight + collisionRectY);
1897:
1898: t_collisionRectWidth = collisionRectWidth;
1899: t_collisionRectHeight = collisionRectHeight;
1900:
1901: // set width and height
1902: this .width = srcFrameWidth;
1903: this .height = srcFrameHeight;
1904:
1905: break;
1906:
1907: case TRANS_ROT270:
1908:
1909: // the top-right corner of the rectangle becomes the
1910: // top-left when rotated 270.
1911:
1912: // both X- and Y-offset to the top left corner may change
1913:
1914: // update the position information for the collision rectangle
1915:
1916: t_collisionRectX = collisionRectY;
1917: t_collisionRectY = srcFrameWidth
1918: - (collisionRectWidth + collisionRectX);
1919:
1920: t_collisionRectHeight = collisionRectWidth;
1921: t_collisionRectWidth = collisionRectHeight;
1922:
1923: // set width and height
1924: this .width = srcFrameHeight;
1925: this .height = srcFrameWidth;
1926:
1927: break;
1928:
1929: case TRANS_MIRROR_ROT90:
1930:
1931: // both X- and Y- offset from the top left corner may change
1932:
1933: // update the position information for the collision rectangle
1934:
1935: t_collisionRectX = srcFrameHeight
1936: - (collisionRectHeight + collisionRectY);
1937: t_collisionRectY = srcFrameWidth
1938: - (collisionRectWidth + collisionRectX);
1939:
1940: t_collisionRectHeight = collisionRectWidth;
1941: t_collisionRectWidth = collisionRectHeight;
1942:
1943: // set width and height
1944: this .width = srcFrameHeight;
1945: this .height = srcFrameWidth;
1946:
1947: break;
1948:
1949: case TRANS_MIRROR_ROT270:
1950:
1951: // both X- and Y- offset from the top left corner may change
1952:
1953: // update the position information for the collision rectangle
1954:
1955: t_collisionRectY = collisionRectX;
1956: t_collisionRectX = collisionRectY;
1957:
1958: t_collisionRectHeight = collisionRectWidth;
1959: t_collisionRectWidth = collisionRectHeight;
1960:
1961: // set width and height
1962: this .width = srcFrameHeight;
1963: this .height = srcFrameWidth;
1964:
1965: break;
1966:
1967: default:
1968: // INVALID TRANSFORMATION!
1969: throw new IllegalArgumentException();
1970:
1971: }
1972: }
1973:
1974: /**
1975: * Given the x and y offsets off a pixel from the top left
1976: * corner, in an untransformed sprite,
1977: * calculates the x coordinate of the pixel when the same sprite
1978: * is transformed, with the coordinates of the top-left pixel
1979: * of the transformed sprite as (0,0).
1980: *
1981: * @param inp_x Horizontal offset within the untransformed sprite
1982: * @param inp_y Vertical offset within the untransformed sprite
1983: * @param transform transform for the sprite
1984: * @return The x-offset, of the coordinates of the pixel,
1985: * with the top-left corner as 0 when transformed.
1986: */
1987: int getTransformedPtX(int inp_x, int inp_y, int transform) {
1988:
1989: int t_x = 0;
1990:
1991: switch (transform) {
1992:
1993: case TRANS_NONE:
1994: t_x = inp_x;
1995: break;
1996: case TRANS_MIRROR:
1997: t_x = srcFrameWidth - inp_x - 1;
1998: break;
1999: case TRANS_MIRROR_ROT180:
2000: t_x = inp_x;
2001: break;
2002: case TRANS_ROT90:
2003: t_x = srcFrameHeight - inp_y - 1;
2004: break;
2005: case TRANS_ROT180:
2006: t_x = srcFrameWidth - inp_x - 1;
2007: break;
2008: case TRANS_ROT270:
2009: t_x = inp_y;
2010: break;
2011: case TRANS_MIRROR_ROT90:
2012: t_x = srcFrameHeight - inp_y - 1;
2013: break;
2014: case TRANS_MIRROR_ROT270:
2015: t_x = inp_y;
2016: break;
2017: default:
2018: // for safety/completeness.
2019: Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
2020: "Sprite: transform=" + transform);
2021: break;
2022: }
2023:
2024: return t_x;
2025:
2026: }
2027:
2028: /**
2029: * Given the x and y offsets off a pixel from the top left
2030: * corner, in an untransformed sprite,
2031: * calculates the y coordinate of the pixel when the same sprite
2032: * is transformed, with the coordinates of the top-left pixel
2033: * of the transformed sprite as (0,0).
2034: *
2035: * @param inp_x Horizontal offset within the untransformed sprite
2036: * @param inp_y Vertical offset within the untransformed sprite
2037: * @param transform transform for the sprite
2038: * @return The y-offset, of the coordinates of the pixel,
2039: * with the top-left corner as 0 when transformed.
2040: */
2041: int getTransformedPtY(int inp_x, int inp_y, int transform) {
2042:
2043: int t_y = 0;
2044:
2045: switch (transform) {
2046:
2047: case TRANS_NONE:
2048: t_y = inp_y;
2049: break;
2050: case TRANS_MIRROR:
2051: t_y = inp_y;
2052: break;
2053: case TRANS_MIRROR_ROT180:
2054: t_y = srcFrameHeight - inp_y - 1;
2055: break;
2056: case TRANS_ROT90:
2057: t_y = inp_x;
2058: break;
2059: case TRANS_ROT180:
2060: t_y = srcFrameHeight - inp_y - 1;
2061: break;
2062: case TRANS_ROT270:
2063: t_y = srcFrameWidth - inp_x - 1;
2064: break;
2065: case TRANS_MIRROR_ROT90:
2066: t_y = srcFrameWidth - inp_x - 1;
2067: break;
2068: case TRANS_MIRROR_ROT270:
2069: t_y = inp_x;
2070: break;
2071: default:
2072: // for safety/completeness.
2073: Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI,
2074: "Sprite: transform=" + transform);
2075: break;
2076: }
2077:
2078: return t_y;
2079:
2080: }
2081:
2082: // --- member variables
2083:
2084: /**
2085: * If this bit is set, it denotes that the transform causes the
2086: * axes to be interchanged
2087: */
2088: private static final int INVERTED_AXES = 0x4;
2089:
2090: /**
2091: * If this bit is set, it denotes that the transform causes the
2092: * x axis to be flipped.
2093: */
2094: private static final int X_FLIP = 0x2;
2095:
2096: /**
2097: * If this bit is set, it denotes that the transform causes the
2098: * y axis to be flipped.
2099: */
2100: private static final int Y_FLIP = 0x1;
2101:
2102: /**
2103: * Bit mask for channel value in ARGB pixel.
2104: */
2105: private static final int ALPHA_BITMASK = 0xff000000;
2106:
2107: /**
2108: * Alpha channel value for full opacity.
2109: */
2110: private static final int FULLY_OPAQUE_ALPHA = 0xff000000;
2111:
2112: /**
2113: * Source image
2114: */
2115: Image sourceImage;
2116:
2117: /**
2118: * The number of frames
2119: */
2120: int numberFrames; // = 0;
2121:
2122: /**
2123: * list of X coordinates of individual frames
2124: */
2125: int[] frameCoordsX;
2126: /**
2127: * list of Y coordinates of individual frames
2128: */
2129: int[] frameCoordsY;
2130:
2131: /**
2132: * Width of each frame in the source image
2133: */
2134: int srcFrameWidth;
2135:
2136: /**
2137: * Height of each frame in the source image
2138: */
2139: int srcFrameHeight;
2140:
2141: /**
2142: * The sequence in which to display the Sprite frames
2143: */
2144: int[] frameSequence;
2145:
2146: /**
2147: * The sequence index
2148: */
2149: private int sequenceIndex; // = 0
2150:
2151: /**
2152: * Set to true if custom sequence is used.
2153: */
2154: private boolean customSequenceDefined; // = false;
2155:
2156: // -- reference point
2157: /**
2158: * Horizontal offset of the reference point
2159: * from the top left of the sprite.
2160: */
2161: int dRefX; // =0
2162:
2163: /**
2164: * Vertical offset of the reference point
2165: * from the top left of the sprite.
2166: */
2167: int dRefY; // =0
2168:
2169: // --- collision rectangle
2170:
2171: /**
2172: * Horizontal offset of the top left of the collision
2173: * rectangle from the top left of the sprite.
2174: */
2175: int collisionRectX; // =0
2176:
2177: /**
2178: * Vertical offset of the top left of the collision
2179: * rectangle from the top left of the sprite.
2180: */
2181: int collisionRectY; // =0
2182:
2183: /**
2184: * Width of the bounding rectangle for collision detection.
2185: */
2186: int collisionRectWidth;
2187:
2188: /**
2189: * Height of the bounding rectangle for collision detection.
2190: */
2191: int collisionRectHeight;
2192:
2193: // --- transformation(s)
2194: // --- values that may change on setting transformations
2195: // start with t_
2196:
2197: /**
2198: * The current transformation in effect.
2199: */
2200: int t_currentTransformation;
2201:
2202: /**
2203: * Horizontal offset of the top left of the collision
2204: * rectangle from the top left of the sprite.
2205: */
2206: int t_collisionRectX;
2207:
2208: /**
2209: * Vertical offset of the top left of the collision
2210: * rectangle from the top left of the sprite.
2211: */
2212: int t_collisionRectY;
2213:
2214: /**
2215: * Width of the bounding rectangle for collision detection,
2216: * with the current transformation in effect.
2217: */
2218: int t_collisionRectWidth;
2219:
2220: /**
2221: * Height of the bounding rectangle for collision detection,
2222: * with the current transformation in effect.
2223: */
2224: int t_collisionRectHeight;
2225:
2226: }
|