1255 lines · 42.3 KB · Java
RawDownload
/* * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.java2d.pipe; import java.awt.Rectangle;import java.awt.Shape;import java.awt.geom.AffineTransform;import java.awt.geom.RectangularShape; /** * This class encapsulates a definition of a two dimensional region which * consists of a number of Y ranges each containing multiple X bands. * <p> * A rectangular Region is allowed to have a null band list in which * case the rectangular shape is defined by the bounding box parameters * (lox, loy, hix, hiy). * <p> * The band list, if present, consists of a list of rows in ascending Y * order, ending at endIndex which is the index beyond the end of the * last row.  Each row consists of at least 3 + 2n entries (n >= 1) * where the first 3 entries specify the Y range as start, end, and * the number of X ranges in that Y range.  These 3 entries are * followed by pairs of X coordinates in ascending order: * <pre> * bands[rowstart+0] = Y0;        // starting Y coordinate * bands[rowstart+1] = Y1;        // ending Y coordinate - endY > startY * bands[rowstart+2] = N;         // number of X bands - N >= 1 * * bands[rowstart+3] = X10;       // starting X coordinate of first band * bands[rowstart+4] = X11;       // ending X coordinate of first band * bands[rowstart+5] = X20;       // starting X coordinate of second band * bands[rowstart+6] = X21;       // ending X coordinate of second band * ... * bands[rowstart+3+N*2-2] = XN0; // starting X coord of last band * bands[rowstart+3+N*2-1] = XN1; // ending X coord of last band * * bands[rowstart+3+N*2] = ...    // start of next Y row * </pre> */public class Region {    static final int INIT_SIZE = 50;    static final int GROW_SIZE = 50;     /**     * Immutable Region.     */    private static final class ImmutableRegion extends Region {        protected ImmutableRegion(int lox, int loy, int hix, int hiy) {            super(lox, loy, hix, hiy);        }         // Override all the methods that mutate the object        public void appendSpans(sun.java2d.pipe.SpanIterator si) {}        public void setOutputArea(java.awt.Rectangle r) {}        public void setOutputAreaXYWH(int x, int y, int w, int h) {}        public void setOutputArea(int[] box) {}        public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {}    }     public static final Region EMPTY_REGION = new ImmutableRegion(0, 0, 0, 0);    public static final Region WHOLE_REGION = new ImmutableRegion(            Integer.MIN_VALUE,            Integer.MIN_VALUE,            Integer.MAX_VALUE,            Integer.MAX_VALUE);     int lox;    int loy;    int hix;    int hiy;     int endIndex;    int[] bands;     private static native void initIDs();     static {        initIDs();    }     /**     * Adds the dimension <code>dim</code> to the coordinate     * <code>start</code> with appropriate clipping.  If     * <code>dim</code> is non-positive then the method returns     * the start coordinate.  If the sum overflows an integer     * data type then the method returns <code>Integer.MAX_VALUE</code>.     */    public static int dimAdd(int start, int dim) {        if (dim <= 0) return start;        if ((dim += start) < start) return Integer.MAX_VALUE;        return dim;    }     /**     * Adds the delta {@code dv} to the value {@code v} with     * appropriate clipping to the bounds of Integer resolution.     * If the answer would be greater than {@code Integer.MAX_VALUE}     * then {@code Integer.MAX_VALUE} is returned.     * If the answer would be less than {@code Integer.MIN_VALUE}     * then {@code Integer.MIN_VALUE} is returned.     * Otherwise the sum is returned.     */    public static int clipAdd(int v, int dv) {        int newv = v + dv;        if ((newv > v) != (dv > 0)) {            newv = (dv < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;        }        return newv;    }     protected Region(int lox, int loy, int hix, int hiy) {        this.lox = lox;        this.loy = loy;        this.hix = hix;        this.hiy = hiy;    }     /**     * Returns a Region object covering the pixels which would be     * touched by a fill or clip operation on a Graphics implementation     * on the specified Shape object under the optionally specified     * AffineTransform object.     *     * @param s a non-null Shape object specifying the geometry enclosing     *          the pixels of interest     * @param at an optional <code>AffineTransform</code> to be applied to the     *          coordinates as they are returned in the iteration, or     *          <code>null</code> if untransformed coordinates are desired     */    public static Region getInstance(Shape s, AffineTransform at) {        return getInstance(WHOLE_REGION, false, s, at);    }     /**     * Returns a Region object covering the pixels which would be     * touched by a fill or clip operation on a Graphics implementation     * on the specified Shape object under the optionally specified     * AffineTransform object further restricted by the specified     * device bounds.     * <p>     * Note that only the bounds of the specified Region are used to     * restrict the resulting Region.     * If devBounds is non-rectangular and clipping to the specific     * bands of devBounds is needed, then an intersection of the     * resulting Region with devBounds must be performed in a     * subsequent step.     *     * @param devBounds a non-null Region specifying some bounds to     *          clip the geometry to     * @param s a non-null Shape object specifying the geometry enclosing     *          the pixels of interest     * @param at an optional <code>AffineTransform</code> to be applied to the     *          coordinates as they are returned in the iteration, or     *          <code>null</code> if untransformed coordinates are desired     */    public static Region getInstance(Region devBounds,                                     Shape s, AffineTransform at)    {        return getInstance(devBounds, false, s, at);    }     /**     * Returns a Region object covering the pixels which would be     * touched by a fill or clip operation on a Graphics implementation     * on the specified Shape object under the optionally specified     * AffineTransform object further restricted by the specified     * device bounds.     * If the normalize parameter is true then coordinate normalization     * is performed as per the 2D Graphics non-antialiasing implementation     * of the VALUE_STROKE_NORMALIZE hint.     * <p>     * Note that only the bounds of the specified Region are used to     * restrict the resulting Region.     * If devBounds is non-rectangular and clipping to the specific     * bands of devBounds is needed, then an intersection of the     * resulting Region with devBounds must be performed in a     * subsequent step.     *     * @param devBounds a non-null Region specifying some bounds to     *          clip the geometry to     * @param normalize a boolean indicating whether or not to apply     *          normalization     * @param s a non-null Shape object specifying the geometry enclosing     *          the pixels of interest     * @param at an optional <code>AffineTransform</code> to be applied to the     *          coordinates as they are returned in the iteration, or     *          <code>null</code> if untransformed coordinates are desired     */    public static Region getInstance(Region devBounds, boolean normalize,                                     Shape s, AffineTransform at)    {        // Optimize for empty shapes to avoid involving the SpanIterator        if (s instanceof RectangularShape &&                ((RectangularShape)s).isEmpty())        {            return EMPTY_REGION;        }         int box[] = new int[4];        ShapeSpanIterator sr = new ShapeSpanIterator(normalize);        try {            sr.setOutputArea(devBounds);            sr.appendPath(s.getPathIterator(at));            sr.getPathBox(box);            Region r = Region.getInstance(box);            r.appendSpans(sr);            return r;        } finally {            sr.dispose();        }    }     /**     * Returns a Region object with a rectangle of interest specified     * by the indicated Rectangle object.     * <p>     * This method can also be used to create a simple rectangular     * region.     */    public static Region getInstance(Rectangle r) {        return Region.getInstanceXYWH(r.x, r.y, r.width, r.height);    }     /**     * Returns a Region object with a rectangle of interest specified     * by the indicated rectangular area in x, y, width, height format.     * <p>     * This method can also be used to create a simple rectangular     * region.     */    public static Region getInstanceXYWH(int x, int y, int w, int h) {        return Region.getInstanceXYXY(x, y, dimAdd(x, w), dimAdd(y, h));    }     /**     * Returns a Region object with a rectangle of interest specified     * by the indicated span array.     * <p>     * This method can also be used to create a simple rectangular     * region.     */    public static Region getInstance(int box[]) {        return new Region(box[0], box[1], box[2], box[3]);    }     /**     * Returns a Region object with a rectangle of interest specified     * by the indicated rectangular area in lox, loy, hix, hiy format.     * <p>     * This method can also be used to create a simple rectangular     * region.     */    public static Region getInstanceXYXY(int lox, int loy, int hix, int hiy) {        return new Region(lox, loy, hix, hiy);    }     /**     * Sets the rectangle of interest for storing and returning     * region bands.     * <p>     * This method can also be used to initialize a simple rectangular     * region.     */    public void setOutputArea(Rectangle r) {        setOutputAreaXYWH(r.x, r.y, r.width, r.height);    }     /**     * Sets the rectangle of interest for storing and returning     * region bands.  The rectangle is specified in x, y, width, height     * format and appropriate clipping is performed as per the method     * <code>dimAdd</code>.     * <p>     * This method can also be used to initialize a simple rectangular     * region.     */    public void setOutputAreaXYWH(int x, int y, int w, int h) {        setOutputAreaXYXY(x, y, dimAdd(x, w), dimAdd(y, h));    }     /**     * Sets the rectangle of interest for storing and returning     * region bands.  The rectangle is specified as a span array.     * <p>     * This method can also be used to initialize a simple rectangular     * region.     */    public void setOutputArea(int box[]) {        this.lox = box[0];        this.loy = box[1];        this.hix = box[2];        this.hiy = box[3];    }     /**     * Sets the rectangle of interest for storing and returning     * region bands.  The rectangle is specified in lox, loy,     * hix, hiy format.     * <p>     * This method can also be used to initialize a simple rectangular     * region.     */    public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {        this.lox = lox;        this.loy = loy;        this.hix = hix;        this.hiy = hiy;    }     /**     * Appends the list of spans returned from the indicated     * SpanIterator.  Each span must be at a higher starting     * Y coordinate than the previous data or it must have a     * Y range equal to the highest Y band in the region and a     * higher X coordinate than any of the spans in that band.     */    public void appendSpans(SpanIterator si) {        int[] box = new int[6];         while (si.nextSpan(box)) {            appendSpan(box);        }         endRow(box);        calcBBox();    }     /**     * Returns a Region object that represents the same list of     * rectangles as the current Region object, translated by     * the specified dx, dy translation factors.     */    public Region getTranslatedRegion(int dx, int dy) {        if ((dx | dy) == 0) {            return this;        }        int tlox = lox + dx;        int tloy = loy + dy;        int thix = hix + dx;        int thiy = hiy + dy;        if ((tlox > lox) != (dx > 0) ||            (tloy > loy) != (dy > 0) ||            (thix > hix) != (dx > 0) ||            (thiy > hiy) != (dy > 0))        {            return getSafeTranslatedRegion(dx, dy);        }        Region ret = new Region(tlox, tloy, thix, thiy);        int bands[] = this.bands;        if (bands != null) {            int end = endIndex;            ret.endIndex = end;            int newbands[] = new int[end];            ret.bands = newbands;            int i = 0;            int ncol;            while (i < end) {                newbands[i] = bands[i] + dy; i++;                newbands[i] = bands[i] + dy; i++;                newbands[i] = ncol = bands[i]; i++;                while (--ncol >= 0) {                    newbands[i] = bands[i] + dx; i++;                    newbands[i] = bands[i] + dx; i++;                }            }        }        return ret;    }     private Region getSafeTranslatedRegion(int dx, int dy) {        int tlox = clipAdd(lox, dx);        int tloy = clipAdd(loy, dy);        int thix = clipAdd(hix, dx);        int thiy = clipAdd(hiy, dy);        Region ret = new Region(tlox, tloy, thix, thiy);        int bands[] = this.bands;        if (bands != null) {            int end = endIndex;            int newbands[] = new int[end];            int i = 0; // index for source bands            int j = 0; // index for translated newbands            int ncol;            while (i < end) {                int y1, y2;                newbands[j++] = y1   = clipAdd(bands[i++], dy);                newbands[j++] = y2   = clipAdd(bands[i++], dy);                newbands[j++] = ncol = bands[i++];                int savej = j;                if (y1 < y2) {                    while (--ncol >= 0) {                        int x1 = clipAdd(bands[i++], dx);                        int x2 = clipAdd(bands[i++], dx);                        if (x1 < x2) {                            newbands[j++] = x1;                            newbands[j++] = x2;                        }                    }                } else {                    i += ncol * 2;                }                // Did we get any non-empty bands in this row?                if (j > savej) {                    newbands[savej-1] = (j - savej) / 2;                } else {                    j = savej - 3;                }            }            if (j <= 5) {                if (j < 5) {                    // No rows or bands were generated...                    ret.lox = ret.loy = ret.hix = ret.hiy = 0;                } else {                    // Only generated one single rect in the end...                    ret.loy = newbands[0];                    ret.hiy = newbands[1];                    ret.lox = newbands[3];                    ret.hix = newbands[4];                }                // ret.endIndex and ret.bands were never initialized...                // ret.endIndex = 0;                // ret.newbands = null;            } else {                // Generated multiple bands and/or multiple rows...                ret.endIndex = j;                ret.bands = newbands;            }        }        return ret;    }     /**     * Returns a Region object that represents the intersection of     * this object with the specified Rectangle.  The return value     * may be this same object if no clipping occurs.     */    public Region getIntersection(Rectangle r) {        return getIntersectionXYWH(r.x, r.y, r.width, r.height);    }     /**     * Returns a Region object that represents the intersection of     * this object with the specified rectangular area.  The return     * value may be this same object if no clipping occurs.     */    public Region getIntersectionXYWH(int x, int y, int w, int h) {        return getIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h));    }     /**     * Returns a Region object that represents the intersection of     * this object with the specified rectangular area.  The return     * value may be this same object if no clipping occurs.     */    public Region getIntersectionXYXY(int lox, int loy, int hix, int hiy) {        if (isInsideXYXY(lox, loy, hix, hiy)) {            return this;        }        Region ret = new Region((lox < this.lox) ? this.lox : lox,                                (loy < this.loy) ? this.loy : loy,                                (hix > this.hix) ? this.hix : hix,                                (hiy > this.hiy) ? this.hiy : hiy);        if (bands != null) {            ret.appendSpans(this.getSpanIterator());        }        return ret;    }     /**     * Returns a Region object that represents the intersection of this     * object with the specified Region object.     * <p>     * If {@code A} and {@code B} are both Region Objects and     * <code>C = A.getIntersection(B);</code> then a point will     * be contained in {@code C} iff it is contained in both     * {@code A} and {@code B}.     * <p>     * The return value may be this same object or the argument

Lines 1–500 of 1255.