//
// $Id: RegionManager.java,v 1.7 2002/10/30 00:26:53 mdb Exp $

package com.threerings.media;

import java.awt.Rectangle;

import java.util.ArrayList;
import java.util.List;

import com.samskivert.util.StringUtil;

/**
 * Manages regions (rectangles) that are invalidated in the process of
 * ticking animations and sprites and generally doing other display
 * related business.
 */
public class RegionManager
{
    /**
     * Invalidates the specified region.
     */
    public void invalidateRegion (int x, int y, int width, int height)
    {
        if (isValidSize(width, height)) {
            addDirtyRegion(new Rectangle(x, y, width, height));
        }
    }

    /**
     * Invalidates the specified region (the supplied rectangle will be
     * cloned as the region manager fiddles with the rectangles it uses
     * internally).
     */
    public void invalidateRegion (Rectangle rect)
    {
        if (isValidSize(rect.width, rect.height)) {
            addDirtyRegion((Rectangle)rect.clone());
        }
    }

    /**
     * Adds the supplied rectangle to the dirty regions. Control of the
     * rectangle is given to the region manager as it may choose to bend,
     * fold or mutilate it later. If you don't want the region manager
     * messing with your rectangle, use {@link #invalidateRegion}.
     */
    public void addDirtyRegion (Rectangle rect)
    {
        // sanity check
        if (rect == null) {
            Log.warning("Attempt to dirty a null rect!?");
            Thread.dumpStack();
            return;
        }

        if (isValidSize(rect.width, rect.height)) {
            // Log.info("Invalidating " + StringUtil.toString(rect));
            _dirty.add(rect);
        }
    }

    /** Used to ensure our dirty regions are not invalid. */
    protected final boolean isValidSize (int width, int height)
    {
        if (width < 0 || height < 0) {
            Log.warning("Attempt to add invalid dirty region?! " +
                        "[size=" + width + "x" + height + "].");
            Thread.dumpStack();
            return false;

        } else if (width == 0 || height == 0) {
            // no need to complain about zero sized rectangles, just
            // ignore them
            return false;

        } else {
            return true;
        }
    }

    /**
     * Returns true if dirty regions have been accumulated since the last
     * call to {@link #getDirtyRegions}.
     */
    public boolean haveDirtyRegions ()
    {
        return (_dirty.size() > 0);
    }

    /**
     * Merges all outstanding dirty regions into a single list of
     * rectangles and returns that to the caller. Interally, the list of
     * accumulated dirty regions is cleared out and prepared for the next
     * frame.
     */
    public Rectangle[] getDirtyRegions ()
    {
        ArrayList merged = new ArrayList();

        for (int ii=_dirty.size() - 1; ii >= 0; ii--) {
            // pop the next rectangle from the dirty list
            Rectangle mr = (Rectangle)_dirty.remove(ii);

            // merge in any overlapping rectangles
            for (int jj = ii - 1; jj >= 0; jj--) {
                Rectangle r = (Rectangle)_dirty.get(jj);
                if (mr.intersects(r)) {
                    // remove the overlapping rectangle from the list
                    _dirty.remove(jj);
                    ii--;
                    // grow the merged dirty rectangle
                    mr.add(r);
                }
            }

            // add the merged rectangle to the list
            merged.add(mr);
        }

        Rectangle[] rects = new Rectangle[merged.size()];
        merged.toArray(rects);
        return rects;
    }

    /** A list of dirty rectangles. */
    protected ArrayList _dirty = new ArrayList();
}

@@ -31,29 +31,29 @@
 v 1.
-7
+8
  2002/1
-0/30 00:26:53
+1/07 21:45:20
  mdb
@@ -1369,32 +1369,484 @@
 gle rect)%0A    %7B%0A
+        // make sure we're on what we believe to be the AWT thread%0A        Thread current = Thread.currentThread();%0A        if (_awtThread == null) %7B%0A            // grab the first thread we see and call it the AWT%0A            _awtThread = current;%0A        %7D else if (_awtThread != current) %7B%0A            Log.warning(%22Oi! Region dirtied from non-AWT thread %22 +%0A                        %22%5Brect=%22 + rect + %22%5D.%22);%0A            Thread.dumpStack();%0A        %7D%0A%0A
         // sanit
@@ -3337,17 +3337,19 @@
  (int ii
-=
+ = 
 _dirty.s
@@ -4189,24 +4189,24 @@
 tangles. */%0A
-
     protecte
@@ -4242,11 +4242,95 @@
 yList();
+%0A%0A    /** Used for debugging thread sketchiness. */%0A    protected Thread _awtThread;
 %0A%7D%0A

Add sanity check to catch circumstances where dirty regions are posted on threads other than the AWT thread.