/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.game; import totalcross.sys.*; import totalcross.ui.*; import totalcross.ui.event.*; import totalcross.ui.gfx.*; import totalcross.ui.image.*; /** * The Animation control class. <br> * This control displays an animation that can be loaded from indexed BMP files (one frame per image) or by a multi-frames BMP. This kind of BMP file * contains a list of images having all the same size and that lay side by side. * * @author Frank Diebolt * @author Guilherme Campos Hazan * @version 1.1 */ public class Animation extends Control { /** * Sets to true to stop the animation if its parent window is not the top most. */ public boolean pauseIfNotVisible; // guich@tc100b5_42 /** * Delay between two frames. */ public int framePeriod; /** * Reflects the animation play state. */ public boolean isPlaying; /** * Reflects the animation pause state. */ public boolean isPaused; /** * Frames buffer. */ public Image framesBuffer; // true if the animation has been constructed with a multi-frame BMP private boolean multiFramesImage; private TimerEvent animTimer; private int startFrame,endFrame,incFrame,frameCount,loopCount; /** * Event notification mask, whose value is <code>eventFinish<code>, which means that an event is posted only when the animation finishes. */ protected int eventsMask = eventFinish; // fdie@341_3 new notification mechanism /** * No notify at all. */ public static final int eventNone = 0x0; /** * Notifies animation endings. */ public static final int eventFinish = 0x1; /** * Notifies animation loops. */ public static final int eventLoop = 0x2; /** * Notifies animation frames. */ public static final int eventFrame = 0x4; // fdie@341_6+ /** * <code>start()</code> method <code>loops</code> argument special value to loop endlessly. */ public static final int LOOPS_UNLIMITED = 0x7FFFFFFF; /** * The current frame. */ protected int curFrame; /** * Dumb field to keep compilation compatibility with TC 1. */ public int drawOp; /** * Background image. */ protected Image background; // fdie@400_51 : save animation background /** * Animation constructor. * This constructor may be used by deriving classes. */ protected Animation() { super(); } /** * Animation constructor. * * @param frames Single image containing all frames. The number of frames and the transparent colors are fetched from the image. * @param framePeriod Delay in milliseconds between two frames. * @throws ImageException If an internal method throws it. */ public Animation(Image frames, int framePeriod) throws ImageException { this(frames, frames.getFrameCount(), framePeriod); } /** * Animation constructor. * * @param frames single image containing all frames. * @param frameCount width in pixels of one frame. * @param framePeriod delay in milliseconds between two frames. * @throws ImageException If an internal method throws it. */ public Animation(Image frames,int frameCount,int framePeriod) throws ImageException// fdie@341_2 : direct multi-frame image constructor { super(); setImage(frames,frameCount,framePeriod); } /** * Sets the image of an <code>Animation</code> object. * * @param frames single image containing all frames. * @param frameCount width in pixels of one frame. * @param framePeriod delay in milliseconds between two frames. * @throws ImageException If an internal method throws it. */ public void setImage(Image frames,int frameCount,int framePeriod) throws ImageException { this.focusTraversable = false; boolean currentlyPlaying = this.isPlaying; if (currentlyPlaying) stop(); frames.setFrameCount(frameCount); multiFramesImage = frames.getFrameCount() > 1; this.framePeriod=framePeriod; this.frameCount = frameCount; framesBuffer=frames; width=frames.getWidth(); height=frames.getHeight(); if (currentlyPlaying) this.start(loopCount); } /** * Process events for the <code>Animation</code> class. * * @param e The posted event. */ public void onEvent(Event e) { if (animTimer != null && animTimer.triggered && !(pauseIfNotVisible && !getParentWindow().isVisible())) paintNextFrame(); else super.onEvent(e); } /** * Number of frames in the animation. * * @return Frames amount */ public int size() { return framesBuffer.getFrameCount(); } /** * Returns the preferred width of this control. * * @return The preferred width of this control. */ public int getPreferredWidth() { return width; } /** * Returns the preferred height of this control. * * @return The preferred height of this control. */ public int getPreferredHeight() { return height; } /** * Called by the system to draw the animation. * * @param gfx The graphics object for drawing. */ public void onPaint(Graphics gfx) { // fdie@400_51 : save animation background - no need in OpenGL, since the screen is fully painted at each frame if (!Settings.isOpenGL && gfx.isControlSurface()) if (background == null) { // guich@tc100: on a screen rotation, we would have to re-get the background! try { background = new Image(width, height); // screen -> buffer background.getGraphics().copyRect(parent, x, y, width, height, 0, 0); } catch (ImageException e) { } } else if (background != null) gfx.drawImage(background,0,0); // frame lookup table, for special animations if (multiFramesImage) framesBuffer.setCurrentFrame(curFrame); // flsobral@tc100b5_6: argument doClip is now true, this avoids exceptions when the image is larger than the screen. gfx.drawImage(framesBuffer, 0, 0, true); } /** * Enable the posting of events. By default the posting of events are disabled. * */ public void enableEvents(int mask) // fdie@341_3 { eventsMask=mask; } /** * Pauses a running animation. If the animation is not playing, this call has no effect. */ public void pause() { if (isPlaying) isPaused=true; } /** * Resumes a paused animation. If the animation is not playing, this call has no effect. */ public void resume() { if (isPlaying) isPaused=false; } /** * Stops the animation. If the animation is not playing, this call has no effect. */ public void stop() { if (isPlaying) { removeTimer(animTimer); isPlaying = isPaused = false; animTimer = null; } } private void paintNextFrame() { if (isPlaying && !isPaused) { repaintNow(); if ((eventsMask & eventFrame) != 0) postEvent(new AnimationEvent(AnimationEvent.FRAME,this)); if (curFrame != endFrame) { curFrame += incFrame; if (curFrame < 0 || curFrame >= frameCount) curFrame = (curFrame+frameCount) % frameCount; framesBuffer.setCurrentFrame(curFrame); } else if (loopCount == LOOPS_UNLIMITED || --loopCount> 0) // fdie@341_6 will be the only loop management once the above boolean loop is removed { curFrame = startFrame; framesBuffer.setCurrentFrame(curFrame); if ((eventsMask & eventLoop) != 0) postEvent(new AnimationEvent(AnimationEvent.LOOP,this)); } else { stop(); if ((eventsMask & eventFinish) != 0) postEvent(new AnimationEvent(AnimationEvent.FINISH,this)); } } } /** * Starts the animation with a frame range. This method starts an animation by specifying the frame range and a loop flag. * If the application is already playing, this call has no effect. * * @param sFrame The start frame. * @param eFrame The end frame. * @param step The frame increment. * @param loops The number of animation iterations. */ public void start(int sFrame,int eFrame,int step,int loops) { if (isPlaying || loops <= 0 || animTimer != null) return; startFrame = Math.max(0,sFrame); endFrame = Math.min(eFrame,frameCount-1); incFrame = step; loopCount = loops; curFrame = startFrame; isPlaying=true; isPaused=false; animTimer = addTimer(framePeriod); } // fdie@341_6+ /** * Starts the animation. This method starts the animation and loops the specified amount of time. * If the application is already playing, this call has no effect. * * @param loops Integer value specifying the number of loops or <code>LOOPS_UNLIMITED</code> for an infinite loop. * * @see #LOOPS_UNLIMITED */ public void start(int loops) { start(0,frameCount-1,1,loops); } }