Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  //
  // 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;
 
 
 import java.util.Map;
 
 import java.awt.Image;
 
 
 
 
 import static com.threerings.media.Log.log;

Used to get Swing's repainting to jive with our active rendering strategy.

See also:
FrameManager
 
 public class ActiveRepaintManager extends RepaintManager
 {
    
Components that are rooted in this component (which must be a java.awt.Window or an java.applet.Applet) will be rendered into the offscreen buffer managed by the frame manager. Other components will be rendered into separate offscreen buffers and repainted in the normal Swing manner.
 
     public ActiveRepaintManager (Component root)
     {
          = root;
     }
 
     @Override
     public synchronized void addInvalidComponent (JComponent comp)
     {
         Component vroot = null;
         if () {
             .info("Maybe invalidating " + toString(comp) + ".");
         }
 
         // locate the validation root for this component
         for (Component c = compc != nullc = c.getParent()) {
             // if the component is not part of an active widget hierarcy, we can stop now; if the
             // component is a cell render pane, we're apparently supposed to ignore it as wel
             if (!c.isDisplayable() || c instanceof CellRendererPane) {
                 return;
             }
 
             // skip non-Swing components
             if (!(c instanceof JComponent)) {
                 continue;
             }
 
             // if we find our validate root, we can stop looking; NOTE: JTextField incorrectly
             // claims to be a validate root thereby fucking up the program something serious; we
             // jovially ignore its claims here and restore order to the universe; see bug #403550
             // for more fallout from Sun's fuckup
             if (!(c instanceof JTextField) && !(c instanceof JScrollPane) &&
                 ((JComponent)c).isValidateRoot()) {
                 vroot = c;
                 break;
             }
         }
 
        // if we found no validation root we can abort as this component is not part of any valid
        // widget hierarchy
        if (vroot == null) {
            if () {
                .info("Skipping vrootless component: " + toString(comp));
            }
            return;
        }
        // make sure that the component is actually in a window or applet that is showing
        if (getRoot(vroot) == null) {
            if () {
                .info("Skipping rootless component",
                    "comp"toString(comp), "vroot"toString(vroot));
            }
            return;
        }
        // add the invalid component to our list and we'll validate it on the next frame
        if (!ListUtil.containsRef(vroot)) {
            if () {
                .info("Invalidating " + toString(vroot) + ".");
            }
             = ListUtil.add(vroot);
        }
    }
    @Override
    public synchronized void addDirtyRegion (JComponent compint xint yint widthint height)
    {
        // ignore invalid requests
        if ((width <= 0) || (height <= 0) || (comp == null) ||
            (comp.getWidth() <= 0) || (comp.getHeight() <= 0)) {
//             Log.info("Skipping bogus region " + comp.getClass().getName() +
//                      ", x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + ".");
            return;
        }
        // if this component is already dirty, simply expand their existing dirty rectangle
        Rectangle drect = .get(comp);
        if (drect != null) {
            drect.add(xy);
            drect.add(x+widthy+height);
            return;
        }
        // make sure this component has a valid root
        if (getRoot(comp) == null) {
//             Log.info("Skipping rootless repaint " + comp + ".");
            return;
        }
        drect = new Rectangle(xywidthheight);
        if () {
            .info("Dirtying component""comp"toString(comp), "drect"drect);
        }
        // if we made it this far, we can queue up a dirty region for this component to be
        // repainted on the next tick
        .put(compdrect);
    }

    
Returns the root component for the supplied component or null if it is not part of a rooted hierarchy or if any parent along the way is found to be hidden or without a peer.
    protected Component getRoot (Component comp)
    {
        for (Component c = compc != nullc = c.getParent()) {
            boolean hidden = !c.isDisplayable();
            // on the mac, the JRootPane is invalidated before it is visible and is never again
            // invalidated or repainted, so we punt and allow all invisible components to be
            // invalidated and revalidated
            if (!RunAnywhere.isMacOS()) {
                hidden |= !c.isVisible();
            }
            if (hidden) {
                return null;
            }
            if (c instanceof Window || c instanceof Applet) {
                return c;
            }
        }
        return null;
    }
    @Override
    public synchronized Rectangle getDirtyRegion (JComponent comp)
    {
        Rectangle drect = .get(comp);
        // copy the rectangle if we found one, otherwise create an empty rectangle because we don't
        // want them leaving empty handed
        return (drect == null) ? new Rectangle(0, 0, 0, 0) : new Rectangle(drect);
    }
    @Override
    public synchronized void markCompletelyClean (JComponent comp)
    {
        .remove(comp);
    }

    
Validates the invalid components that have been queued up since the last frame tick.
    public void validateComponents ()
    {
        // swap out our invalid array
        Object[] invalid = null;
        synchronized (this) {
            invalid = ;
             = null;
        }
        // if there's nothing to validate, we're home free
        if (invalid == null) {
            return;
        }
        // validate everything therein
        int icount = invalid.length;
        for (int ii = 0; ii < icountii++) {
            if (invalid[ii] != null) {
                if () {
                    .info("Validating " + invalid[ii]);
                }
                ((Component)invalid[ii]).validate();
            }
        }
    }

    
Paints the components that have become dirty since the last tick.

Returns:
true if any components were painted.
    public boolean paintComponents (Graphics gFrameManager fmgr)
    {
        synchronized (this) {
            // exit now if there are no dirty rectangles to paint
            if (.isEmpty()) {
                return false;
            }
            // otherwise, swap our hashmaps
            Map<JComponent,Rectangletmap = ;
             = ;
             = tmap;
        }
        // scan through the list, looking for components for whom a parent component is also dirty.
        // in such a case, the dirty rectangle for the parent component is expanded to contain the
        // dirty rectangle of the child and the child is removed from the repaint list (painting
        // the parent will repaint the child)
        Iterator<Map.Entry<JComponent,Rectangle>> iter = .entrySet().iterator();
      PRUNE:
        while (iter.hasNext()) {
            Map.Entry<JComponent,Rectangleentry = iter.next();
            JComponent comp = entry.getKey();
            Rectangle drect = entry.getValue();
            int x = comp.getX() + drect.xy = comp.getY() + drect.y;
            // climb up the parent hierarchy, looking for the first opaque parent as well as the
            // root component
            for (Component c = comp.getParent(); c != nullc = c.getParent()) {
                // stop looking for combinable parents for non-visible or non-JComponents
                if (!c.isVisible() || !c.isDisplayable() || !(c instanceof JComponent)) {
                    break;
                }
                // check to see if this parent is dirty
                Rectangle prect = .get(c);
                if (prect != null) {
                    // that we were going to merge it with its parent and blow it away
                    drect.x = x;
                    drect.y = y;
                    if () {
                        .info("Found dirty parent",
                            "comp"toString(comp), "drect", StringUtil.toString(drect),
                            "pcomp"toString(c), "prect", StringUtil.toString(prect));
                    }
                    prect.add(drect);
                    if () {
                        .info("New prect " + StringUtil.toString(prect));
                    }
                    // remove the child component and be on our way
                    iter.remove();
                    continue PRUNE;
                }
                // translate the coordinates into this component's coordinate system
                x += c.getX();
                y += c.getY();
            }
        }
        // now paint each of the dirty components, by setting the clipping rectangle appropriately
        // and calling paint() on the associated root component
        iter = .entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<JComponent,Rectangleentry = iter.next();
            JComponent comp = entry.getKey();
            Rectangle drect = entry.getValue();
            // get the root component, adjust the clipping (dirty) rectangle and obtain the bounds
            // of the client in absolute coordinates
            Component root = nullocomp = null;
            // start with the components bounds which we'll switch to the opaque parent component's
            // bounds if and when we find one
            .setBounds(0, 0, comp.getWidth(), comp.getHeight());
            // climb up the parent hierarchy, looking for the first opaque parent as well as the
            // root component
            for (Component c = compc != nullc = c.getParent()) {
                if (!c.isVisible() || !c.isDisplayable()) {
                    break;
                }
                if (c instanceof JComponent) {
                    // make a note of the first opaque parent we find
                    if (ocomp == null && ((JComponent)c).isOpaque()) {
                        ocomp = c;
                        // we need to obtain the opaque parent's coordinates in the root coordinate
                        // system for when we repaint
                        .setBounds(0, 0, ocomp.getWidth(), ocomp.getHeight());
                    }
                } else {
                    // oh god the hackery. apparently the fscking JEditorPane wraps a heavy weight
                    // component around every swing component it uses when doing forms
                    Component tp = c.getParent();
                    if (!(tp instanceof JEditorPane)) {
                        root = c;
                        break;
                    }
                }
                // translate the coordinates into this component's coordinate system
                drect.x += c.getX();
                drect.y += c.getY();
                . += c.getX();
                . += c.getY();
                // clip the dirty region to the bounds of this component
                SwingUtilities.computeIntersection(
                    c.getX(), c.getY(), c.getWidth(), c.getHeight(), drect);
            }
            // if we found no opaque parent, just paint the component itself (this seems to happen
            // with the top-level layered pane)
            if (ocomp == null) {
                ocomp = comp;
            }
            // if this component is rooted in our frame, repaint it into the supplied graphics
            // instance
            if (root == ) {
                if () {
                    .info("Repainting""comp"toString(comp) + StringUtil.toString(),
                        "ocomp"toString(ocomp), "drect", StringUtil.toString(drect));
                }
                g.setClip(drect);
                g.translate(..);
                try {
                    // some components are ill-behaved and may throw an exception while painting
                    // themselves, and so we needs must deal with these fellows gracefully
                    ocomp.paint(g);
                } catch (Exception e) {
                    .warning("Exception while painting component""comp"ocompe);
                }
                g.translate(-., -.);
                // we also need to repaint any components in this layer that are above our freshly
                // repainted component
                fmgr.renderLayers((Graphics2D)gocompdrect);
            } else if (root != null) {
                if () {
                    .info("Repainting old-school",
                        "comp"toString(comp), "ocomp"toString(ocomp), "root"toString(root),
                        "bounds", StringUtil.toString());
                    dumpHierarchy(comp);
                }
                // otherwise, repaint with standard swing double buffers
                Image obuf = getOffscreenBuffer(ocomp..);
                Graphics og = nullcg = null;
                try {
                    og = obuf.getGraphics();
                    ocomp.paint(og);
                    cg = ocomp.getGraphics();
                    cg.drawImage(obuf, 0, 0, null);
                } finally {
                    if (og != null) {
                        og.dispose();
                    }
                    if (cg != null) {
                        cg.dispose();
                    }
                }
            }
        }
        // clear out the mapping of dirty components
        .clear();
        return true;
    }

    
Used to dump a component when debugging.
    protected static String toString (Component comp)
    {
        return comp.getClass().getName() + StringUtil.toString(comp.getBounds());
    }

    
Dumps the containment hierarchy for the supplied component.
    protected static void dumpHierarchy (Component comp)
    {
        for (String indent = ""comp != nullindent += " ") {
            .info(indent + toString(comp));
            comp = comp.getParent();
        }
    }

    
The root of our interface.
    protected Component _root;

    
A list of invalid components.
    protected Object[] _invalid;

    
A mapping of invalid rectangles for each widget that is dirty.
    protected Map<JComponentRectangle_dirty = Maps.newHashMap();

    
A spare hashmap that we swap in while repainting dirty components in the old hashmap.
    protected Map<JComponentRectangle_spare = Maps.newHashMap();

    
Used to compute dirty components' bounds.
    protected Rectangle _cbounds = new Rectangle();

    
Used when rendering "layered" components.
    protected boolean[] _clipped = new boolean[] { true };

    
We debug so much that we have to make it easy to enable and disable debug logging. Yay!
    protected static final boolean DEBUG = false;
New to GrepCode? Check out our FAQ X