// // Nenya library - tools for developing networked games // Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved // https://github.com/threerings/nenya // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published // by the Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // // This library 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. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package com.threerings.media.animation; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; import java.awt.Transparency; import com.threerings.media.sprite.Sprite; import com.threerings.media.sprite.SpriteManager; import com.threerings.media.util.LinearTimeFunction; import com.threerings.media.util.TimeFunction; /** * Washes all non-transparent pixels in a sprite with a particular color (by compositing them with * the solid color with progressively higher alpha values) and then back again. */ public class GleamAnimation extends Animation { /** * Creates a gleam animation with the supplied sprite. The sprite will be faded to the * specified color and then back again. * * @param fadeIn if true, the sprite itself will be faded in as we fade up to the gleam color * and the gleam color will fade out, leaving just the sprite imagery. */ public GleamAnimation (Sprite sprite, Color color, int upmillis, int downmillis, boolean fadeIn) { this(null, sprite, color, upmillis, downmillis, fadeIn); } /** * Creates a gleam animation with the supplied sprite. The sprite will be faded to the * specified color and then back again. The sprite may be already added to the supplied sprite * manager or not, but when the animation is complete, it will have been added. * * @param fadeIn if true, the sprite itself will be faded in as we fade up to the gleam color * and the gleam color will fade out, leaving just the sprite imagery. */ public GleamAnimation (SpriteManager spmgr, Sprite sprite, Color color, int upmillis, int downmillis, boolean fadeIn) { super(new Rectangle(sprite.getBounds())); _spmgr = spmgr; _sprite = sprite; _color = color; _upmillis = upmillis; _downmillis = downmillis; _fadeIn = fadeIn; } @Override public void tick (long timestamp) { if (timestamp - _lastUpdate < _millisBetweenUpdates) { return; } _lastUpdate = timestamp; int alpha; if (_upfunc != null) { if ((alpha = _upfunc.getValue(timestamp)) >= _maxAlpha) { _upfunc = null; } } else if (_downfunc != null) { if ((alpha = _downfunc.getValue(timestamp)) <= _minAlpha) { _downfunc = null; } } else { _finished = true; return; } // if the sprite is moved or changed size while we're gleaming it, track those changes if (!_bounds.equals(_sprite.getBounds())) { Rectangle obounds = new Rectangle(_bounds); _bounds.setBounds(_sprite.getBounds()); invalidateAfterChange(obounds); } if (_alpha != alpha) { _alpha = alpha; invalidate(); } } @Override public void fastForward (long timeDelta) { if (_upfunc != null) { _upfunc.fastForward(timeDelta); } else if (_downfunc != null) { _downfunc.fastForward(timeDelta); } } @Override public void paint (Graphics2D gfx) { // TODO: recreate our off image if the sprite bounds changed; we // also need to change the bounds of our animation which might // require some jockeying (especially if we shrink) if (_offimg == null) { _offimg = gfx.getDeviceConfiguration().createCompatibleImage(_bounds.width, _bounds.height, Transparency.TRANSLUCENT); } // create a mask image with our sprite and the appropriate color Graphics2D ogfx = (Graphics2D)_offimg.getGraphics(); try { ogfx.setColor(_color); ogfx.fillRect(0, 0, _bounds.width, _bounds.height); ogfx.setComposite(AlphaComposite.DstAtop); ogfx.translate(-_sprite.getX(), -_sprite.getY()); _sprite.paint(ogfx); } finally { ogfx.dispose(); } Composite ocomp = null; Composite ncomp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, _alpha / 1000f); // if we're fading the sprite in on the way up, set our alpha // composite before we render the sprite if (_fadeIn && _upfunc != null) { ocomp = gfx.getComposite(); gfx.setComposite(ncomp); } // next render the sprite _sprite.paint(gfx); // if we're not fading in, we still need to alpha the white bits if (ocomp == null) { ocomp = gfx.getComposite(); gfx.setComposite(ncomp); } // now alpha composite our mask atop the sprite gfx.drawImage(_offimg, _sprite.getX(), _sprite.getY(), null); gfx.setComposite(ocomp); } @Override protected void willStart (long tickStamp) { _upfunc = new LinearTimeFunction(_minAlpha, _maxAlpha, _upmillis); _downfunc = new LinearTimeFunction(_maxAlpha, _minAlpha, _downmillis); super.willStart(tickStamp); // remove the sprite we're fiddling with from the manager; we'll // add it back when we're done if (_spmgr != null && _spmgr.isManaged(_sprite)) { _spmgr.removeSprite(_sprite); } } @Override protected void shutdown () { super.shutdown(); if (_spmgr != null && !_spmgr.isManaged(_sprite)) { _spmgr.addSprite(_sprite); } } protected SpriteManager _spmgr; protected Sprite _sprite; protected Color _color; protected Image _offimg; protected boolean _fadeIn; protected TimeFunction _upfunc; protected TimeFunction _downfunc; protected int _upmillis; protected int _downmillis; protected int _maxAlpha = 750; protected int _minAlpha; protected int _alpha; protected long _lastUpdate; protected int _millisBetweenUpdates; }