<commit_before>//
// $Id: MediaPanel.java,v 1.37 2003/05/04 23:28:18 mdb Exp $

package com.threerings.media;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;

import javax.swing.JComponent;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;

import java.util.Arrays;

import com.samskivert.swing.Label;
import com.samskivert.swing.event.AncestorAdapter;
import com.samskivert.util.IntListUtil;
import com.samskivert.util.RuntimeAdjust;
import com.samskivert.util.StringUtil;

import com.threerings.media.animation.Animation;
import com.threerings.media.animation.AnimationManager;

import com.threerings.media.sprite.Sprite;
import com.threerings.media.sprite.SpriteManager;

/**
 * Provides a useful extensible framework for rendering animated displays
 * that use sprites and animations. Sprites and animations can be added to
 * this panel and they will automatically be ticked and rendered (see
 * {@link #addSprite} and {@link #addAnimation}).
 *
 * <p> To facilitate optimized sprite and animation rendering, the panel
 * provides a dirty region manager which is used to only repaint dirtied
 * regions on each frame. Derived classes can add dirty regions to the
 * scene and/or augment the dirty regions created by moving sprites and
 * changing animations.
 *
 * <p> Sprite and animation rendering is done in two layers: front and
 * back. Callbacks are provided to render behind the back layer ({@link
 * #paintBehind}), in between front and back ({@link #paintBetween}) and
 * in front of the front layer ({@link #paintInFront}).
 *
 * <p> The animated panel automatically registers with the {@link
 * FrameManager} to participate in the frame tick. It only does so while
 * it is a visible part of the UI hierarchy, so animations and sprites are
 * paused while the animated panel is hidden.
 */
public class MediaPanel extends JComponent
    implements FrameParticipant, MediaConstants
{
    /**
     * Constructs a media panel.
     */
    public MediaPanel (FrameManager framemgr)
    {
        // keep this for later
        _framemgr = framemgr;

        // create our region manager
        _remgr = new RegionManager();

        // create our animation and sprite managers
        _animmgr = new AnimationManager(_remgr);
        _spritemgr = new SpriteManager(_remgr);

        // participate in the frame when we're visible
        addAncestorListener(new AncestorAdapter() {
            public void ancestorAdded (AncestorEvent event) {
                _framemgr.registerFrameParticipant(MediaPanel.this);
            }
            public void ancestorRemoved (AncestorEvent event) {
                _framemgr.removeFrameParticipant(MediaPanel.this);
            }
        });
    }

    /**
     * Pauses the sprites and animations that are currently active on this
     * media panel. Also stops listening to the frame tick while paused.
     */
    public void setPaused (boolean paused)
    {
        // sanity check
        if ((paused && (_pauseTime != 0)) ||
            (!paused && (_pauseTime == 0))) {
            Log.warning("Requested to pause when paused or vice-versa " +
                        "[paused=" + paused + "].");
            return;
        }

        if (_paused = paused) {
            // make a note of our pause time
            _pauseTime = _framemgr.getTimeStamp();

        } else {
            // let the animation and sprite managers know that we just
            // warped into the future
            long delta = _framemgr.getTimeStamp() - _pauseTime;
            _animmgr.fastForward(delta);
            _spritemgr.fastForward(delta);

            // clear out our pause time
            _pauseTime = 0;
        }
    }

    /**
     * Returns a reference to the region manager that is used to
     * accumulate dirty regions each frame.
     */
    public RegionManager getRegionManager ()
    {
        return _remgr;
    }

    /**
     * Returns a timestamp from the {@link MediaTimer} used to track time
     * intervals for this media panel. <em>Note:</em> this should only be
     * called from the AWT thread.
     */
    public long getTimeStamp ()
    {
        return _framemgr.getTimeStamp();
    }

    // documentation inherited from interface
    public void getPerformanceStatus (StringBuffer buf)
    {
    }

    /**
     * Adds a sprite to this panel.
     */
    public void addSprite (Sprite sprite)
    {
        _spritemgr.addSprite(sprite);
    }

    /**
     * Removes a sprite from this panel.
     */
    public void removeSprite (Sprite sprite)
    {
        _spritemgr.removeSprite(sprite);
    }

    /**
     * Removes all sprites from this panel.
     */
    public void clearSprites ()
    {
        _spritemgr.clearMedia();
    }

    /**
     * Adds an animation to this panel. Animations are automatically
     * removed when they finish.
     */
    public void addAnimation (Animation anim)
    {
        _animmgr.registerAnimation(anim);
    }

    /**
     * Aborts a currently running animation and removes it from this
     * panel. Animations are normally automatically removed when they
     * finish.
     */
    public void abortAnimation (Animation anim)
    {
        _animmgr.unregisterAnimation(anim);
    }

    /**
     * Removes all animations from this panel.
     */
    public void clearAnimations ()
    {
        _animmgr.clearMedia();
    }

    // documentation inherited from interface
    public void tick (long tickStamp)
    {
        // bail if ticking is currently disabled
        if (_paused) {
            return;
        }

        // let derived classes do their business
        willTick(tickStamp);

        // now tick our animations and sprites
        _animmgr.tick(tickStamp);
        _spritemgr.tick(tickStamp);

        // if performance debugging is enabled, 
        if (_perfDebug.getValue()) {
            if (_perfLabel == null) {
                _perfLabel = new Label(
                    "", Label.OUTLINE, Color.white, Color.black,
                    new Font("Arial", Font.PLAIN, 10));
                _perfRect = new Rectangle(5, 5, 0, 0);
            }

            StringBuffer perf = new StringBuffer();
            perf.append("[FPS: ");
            perf.append(_framemgr.getPerfTicks()).append("/");
            perf.append(_framemgr.getPerfTries());
            perf.append(" PM:");
            StringUtil.toString(perf, _framemgr.getPerfMetrics());
//             perf.append(" MP:").append(_dirtyPerTick);
            perf.append("]");

            String perfStatus = perf.toString();
            if (!_perfStatus.equals(perfStatus)) {
                _perfStatus = perfStatus;
                _perfLabel.setText(perfStatus);

                Graphics2D gfx = (Graphics2D)getGraphics();
                if (gfx != null) {
                    _perfLabel.layout(gfx);
                    gfx.dispose();

                    // make sure the region we dirty contains the old and
                    // the new text (which we ensure by never letting the
                    // rect shrink)
                    Dimension psize = _perfLabel.getSize();
                    _perfRect.width = Math.max(_perfRect.width, psize.width);
                    _perfRect.height = Math.max(_perfRect.height, psize.height);
                    dirtyScreenRect(new Rectangle(_perfRect));
                }
            }
        }

        // let derived classes do their business
        didTick(tickStamp);

        // make a note that the next paint will correspond to a call to
        // tick()
        _tickPaintPending = true;
    }

    /**
     * Derived classes can override this method and perform computation
     * prior to the ticking of the sprite and animation managers.
     */
    protected void willTick (long tickStamp)
    {
    }

    /**
     * Derived classes can override this method and perform computation
     * subsequent to the ticking of the sprite and animation managers.
     */
    protected void didTick (long tickStamp)
    {
    }

    // documentation inherited from interface
    public boolean needsPaint ()
    {
        // compute our average dirty regions per tick
        if (_tick++ == 99) {
            _tick = 0;
            int dirty = IntListUtil.sum(_dirty);
            Arrays.fill(_dirty, 0);
            _dirtyPerTick = (float)dirty/100;
        }

        // if we have no dirty regions, clear our pending tick indicator
        // because we're not going to get painted
        boolean needsPaint = _remgr.haveDirtyRegions();
        if (!needsPaint) {
            _tickPaintPending = false;
        }
        return needsPaint;
    }

    // documentation inherited from interface
    public Component getComponent ()
    {
        return this;
    }

    // documentation inherited
    public void repaint (long tm, int x, int y, int width, int height)
    {
        if (width > 0 && height > 0) {
            dirtyScreenRect(new Rectangle(x, y, width, height));
        }
    }

    // documentation inherited
    public void paint (Graphics g)
    {
        Graphics2D gfx = (Graphics2D)g;

        // regardless of whether or not we paint, we need to let our
        // abstract media managers know that we've gotten to the point of
        // painting because they need to remain prepared to deal with
        // media changes that happen any time between the tick() and the
        // paint() and thus need to know when paint() happens
        _animmgr.willPaint();
        _spritemgr.willPaint();

        // no use in painting if we're not showing or if we've not yet
        // been validated
        if (!isValid() || !isShowing()) {
            return;
        }

        // if this isn't a tick paint, then we need to grab the clipping
        // rectangle and mark it as dirty
        if (!_tickPaintPending) {
            Shape clip = g.getClip();
            if (clip == null) {
                // mark the whole component as dirty
                repaint();
            } else {
                dirtyScreenRect(clip.getBounds());
            }

            // we used to bail out here and not render until our next
            // tick, but it turns out that we need to render here because
            // Swing may have repainted our parent over us and expect that
            // we're going to paint ourselves on top of whatever it just<commit_msg>The frame manager may decide not to paint us if needsPaint() returns false, but we still need to call willPaint() after *every* tick, so we have to do that in needsPaint() where we can be sure it will get called.<commit_after>//
// $Id: MediaPanel.java,v 1.38 2003/05/08 05:46:45 mdb Exp $

package com.threerings.media;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;

import javax.swing.JComponent;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;

import java.util.Arrays;

import com.samskivert.swing.Label;
import com.samskivert.swing.event.AncestorAdapter;
import com.samskivert.util.IntListUtil;
import com.samskivert.util.RuntimeAdjust;
import com.samskivert.util.StringUtil;

import com.threerings.media.animation.Animation;
import com.threerings.media.animation.AnimationManager;

import com.threerings.media.sprite.Sprite;
import com.threerings.media.sprite.SpriteManager;

/**
 * Provides a useful extensible framework for rendering animated displays
 * that use sprites and animations. Sprites and animations can be added to
 * this panel and they will automatically be ticked and rendered (see
 * {@link #addSprite} and {@link #addAnimation}).
 *
 * <p> To facilitate optimized sprite and animation rendering, the panel
 * provides a dirty region manager which is used to only repaint dirtied
 * regions on each frame. Derived classes can add dirty regions to the
 * scene and/or augment the dirty regions created by moving sprites and
 * changing animations.
 *
 * <p> Sprite and animation rendering is done in two layers: front and
 * back. Callbacks are provided to render behind the back layer ({@link
 * #paintBehind}), in between front and back ({@link #paintBetween}) and
 * in front of the front layer ({@link #paintInFront}).
 *
 * <p> The animated panel automatically registers with the {@link
 * FrameManager} to participate in the frame tick. It only does so while
 * it is a visible part of the UI hierarchy, so animations and sprites are
 * paused while the animated panel is hidden.
 */
public class MediaPanel extends JComponent
    implements FrameParticipant, MediaConstants
{
    /**
     * Constructs a media panel.
     */
    public MediaPanel (FrameManager framemgr)
    {
        // keep this for later
        _framemgr = framemgr;

        // create our region manager
        _remgr = new RegionManager();

        // create our animation and sprite managers
        _animmgr = new AnimationManager(_remgr);
        _spritemgr = new SpriteManager(_remgr);

        // participate in the frame when we're visible
        addAncestorListener(new AncestorAdapter() {
            public void ancestorAdded (AncestorEvent event) {
                _framemgr.registerFrameParticipant(MediaPanel.this);
            }
            public void ancestorRemoved (AncestorEvent event) {
                _framemgr.removeFrameParticipant(MediaPanel.this);
            }
        });
    }

    /**
     * Pauses the sprites and animations that are currently active on this
     * media panel. Also stops listening to the frame tick while paused.
     */
    public void setPaused (boolean paused)
    {
        // sanity check
        if ((paused && (_pauseTime != 0)) ||
            (!paused && (_pauseTime == 0))) {
            Log.warning("Requested to pause when paused or vice-versa " +
                        "[paused=" + paused + "].");
            return;
        }

        if (_paused = paused) {
            // make a note of our pause time
            _pauseTime = _framemgr.getTimeStamp();

        } else {
            // let the animation and sprite managers know that we just
            // warped into the future
            long delta = _framemgr.getTimeStamp() - _pauseTime;
            _animmgr.fastForward(delta);
            _spritemgr.fastForward(delta);

            // clear out our pause time
            _pauseTime = 0;
        }
    }

    /**
     * Returns a reference to the region manager that is used to
     * accumulate dirty regions each frame.
     */
    public RegionManager getRegionManager ()
    {
        return _remgr;
    }

    /**
     * Returns a timestamp from the {@link MediaTimer} used to track time
     * intervals for this media panel. <em>Note:</em> this should only be
     * called from the AWT thread.
     */
    public long getTimeStamp ()
    {
        return _framemgr.getTimeStamp();
    }

    // documentation inherited from interface
    public void getPerformanceStatus (StringBuffer buf)
    {
    }

    /**
     * Adds a sprite to this panel.
     */
    public void addSprite (Sprite sprite)
    {
        _spritemgr.addSprite(sprite);
    }

    /**
     * Removes a sprite from this panel.
     */
    public void removeSprite (Sprite sprite)
    {
        _spritemgr.removeSprite(sprite);
    }

    /**
     * Removes all sprites from this panel.
     */
    public void clearSprites ()
    {
        _spritemgr.clearMedia();
    }

    /**
     * Adds an animation to this panel. Animations are automatically
     * removed when they finish.
     */
    public void addAnimation (Animation anim)
    {
        _animmgr.registerAnimation(anim);
    }

    /**
     * Aborts a currently running animation and removes it from this
     * panel. Animations are normally automatically removed when they
     * finish.
     */
    public void abortAnimation (Animation anim)
    {
        _animmgr.unregisterAnimation(anim);
    }

    /**
     * Removes all animations from this panel.
     */
    public void clearAnimations ()
    {
        _animmgr.clearMedia();
    }

    // documentation inherited from interface
    public void tick (long tickStamp)
    {
        // bail if ticking is currently disabled
        if (_paused) {
            return;
        }

        // let derived classes do their business
        willTick(tickStamp);

        // now tick our animations and sprites
        _animmgr.tick(tickStamp);
        _spritemgr.tick(tickStamp);

        // if performance debugging is enabled, 
        if (_perfDebug.getValue()) {
            if (_perfLabel == null) {
                _perfLabel = new Label(
                    "", Label.OUTLINE, Color.white, Color.black,
                    new Font("Arial", Font.PLAIN, 10));
                _perfRect = new Rectangle(5, 5, 0, 0);
            }

            StringBuffer perf = new StringBuffer();
            perf.append("[FPS: ");
            perf.append(_framemgr.getPerfTicks()).append("/");
            perf.append(_framemgr.getPerfTries());
            perf.append(" PM:");
            StringUtil.toString(perf, _framemgr.getPerfMetrics());
//             perf.append(" MP:").append(_dirtyPerTick);
            perf.append("]");

            String perfStatus = perf.toString();
            if (!_perfStatus.equals(perfStatus)) {
                _perfStatus = perfStatus;
                _perfLabel.setText(perfStatus);

                Graphics2D gfx = (Graphics2D)getGraphics();
                if (gfx != null) {
                    _perfLabel.layout(gfx);
                    gfx.dispose();

                    // make sure the region we dirty contains the old and
                    // the new text (which we ensure by never letting the
                    // rect shrink)
                    Dimension psize = _perfLabel.getSize();
                    _perfRect.width = Math.max(_perfRect.width, psize.width);
                    _perfRect.height = Math.max(_perfRect.height, psize.height);
                    dirtyScreenRect(new Rectangle(_perfRect));
                }
            }
        }

        // let derived classes do their business
        didTick(tickStamp);

        // make a note that the next paint will correspond to a call to
        // tick()
        _tickPaintPending = true;
    }

    /**
     * Derived classes can override this method and perform computation
     * prior to the ticking of the sprite and animation managers.
     */
    protected void willTick (long tickStamp)
    {
    }

    /**
     * Derived classes can override this method and perform computation
     * subsequent to the ticking of the sprite and animation managers.
     */
    protected void didTick (long tickStamp)
    {
    }

    // documentation inherited from interface
    public boolean needsPaint ()
    {
        // compute our average dirty regions per tick
        if (_tick++ == 99) {
            _tick = 0;
            int dirty = IntListUtil.sum(_dirty);
            Arrays.fill(_dirty, 0);
            _dirtyPerTick = (float)dirty/100;
        }

        // if we have no dirty regions, clear our pending tick indicator
        // because we're not going to get painted
        boolean needsPaint = _remgr.haveDirtyRegions();
        if (!needsPaint) {
            _tickPaintPending = false;
        }

        // regardless of whether or not we paint, we need to let our
        // abstract media managers know that we've gotten to the point of
        // painting because they need to remain prepared to deal with
        // media changes that happen any time between the tick() and the
        // paint() and thus need to know when paint() happens
        _animmgr.willPaint();
        _spritemgr.willPaint();

        return needsPaint;
    }

    // documentation inherited from interface
    public Component getComponent ()
    {
        return this;
    }

    // documentation inherited
    public void repaint (long tm, int x, int y, int width, int height)
    {
        if (width > 0 && height > 0) {
            dirtyScreenRect(new Rectangle(x, y, width, height));
        }
    }

    // documentation inherited
    public void paint (Graphics g)
    {
        Graphics2D gfx = (Graphics2D)g;

        // no use in painting if we're not showing or if we've not yet
        // been validated
        if (!isValid() || !isShowing()) {
            return;
        }

        // if this isn't a tick paint, then we need to grab the clipping
        // rectangle and mark it as dirty
        if (!_tickPaintPending) {
            Shape clip = g.getClip();
            if (clip == null) {
                // mark the whole component as dirty
                repaint();
            } else {
                dirtyScreenRect(clip.getBounds());
            }

            // we used to bail out here and not render until our next
            // tick, but it turns out that we need to render here because
            // Swing may have repainted our parent over us and expect that
            // we're going to paint ourselves on top of whatever it just
{'fraction_non_alphanumeric': 0.08511335956529886, 'fraction_numerical': 0.003747423646243208, 'mean_word_length': 2.034252416145537, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '361723', 'n_tokens_mistral': 5829, 'n_tokens_neox': 5601, 'n_words': 1964}