/*
 * Copyright (c) 2011, 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;

import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.nio.*;

import sun.awt.*;
import sun.awt.image.*;
import sun.java2d.loops.*;
import sun.java2d.pipe.*;
import sun.lwawt.macosx.*;

/*
 * This is the SurfaceData for a CGContextRef.
 */
public abstract class OSXSurfaceData extends BufImgSurfaceData {
    final static float UPPER_BND = Float.MAX_VALUE / 2.0f;
    final static float LOWER_BND = -UPPER_BND;

    protected static CRenderer sQuartzPipe = null;
    protected static CTextPipe sCocoaTextPipe = null;
    protected static CompositeCRenderer sQuartzCompositePipe = null;

    private GraphicsConfiguration fConfig;
    private Rectangle fBounds; // bounds in user coordinates

    static {
        sQuartzPipe = new CRenderer(); // Creates the singleton quartz pipe.
    }

    // NOTE: Any subclasses must eventually call QuartzSurfaceData_InitOps in OSXSurfaceData.h
    // This sets up the native side for the SurfaceData, and is required.
    public OSXSurfaceData(SurfaceType sType, ColorModel cm) {
        this(sType, cm, null, new Rectangle());
    }

    public OSXSurfaceData(SurfaceType sType, ColorModel cm, GraphicsConfiguration config, Rectangle bounds) {
        super(sType, cm);

        this.fConfig = config;

        this.fBounds = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.y + bounds.height);

        this.fGraphicsStates = getBufferOfSize(kSizeOfParameters);
        this.fGraphicsStatesInt = this.fGraphicsStates.asIntBuffer();
        this.fGraphicsStatesFloat = this.fGraphicsStates.asFloatBuffer();
        this.fGraphicsStatesLong = this.fGraphicsStates.asLongBuffer();
        this.fGraphicsStatesObject = new Object[6]; // clip coordinates + clip types + texture paint image + stroke dash
                                                    // array + font + font paint

        // NOTE: All access to the DrawingQueue comes through this OSXSurfaceData instance. Therefore
        // every instance method of OSXSurfaceData that accesses the fDrawingQueue is synchronized.

        // Thread.dumpStack();
    }

    public void validatePipe(SunGraphics2D sg2d) {

        if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) {
            if (sCocoaTextPipe == null) {
                sCocoaTextPipe = new CTextPipe();
            }

            sg2d.imagepipe = sQuartzPipe;
            sg2d.drawpipe = sQuartzPipe;
            sg2d.fillpipe = sQuartzPipe;
            sg2d.shapepipe = sQuartzPipe;
            sg2d.textpipe = sCocoaTextPipe;
        } else {
            setPipesToQuartzComposite(sg2d);
        }
    }

    protected void setPipesToQuartzComposite(SunGraphics2D sg2d) {
        if (sQuartzCompositePipe == null) {
            sQuartzCompositePipe = new CompositeCRenderer();
        }

        if (sCocoaTextPipe == null) {
            sCocoaTextPipe = new CTextPipe();
        }

        sg2d.imagepipe = sQuartzCompositePipe;
        sg2d.drawpipe = sQuartzCompositePipe;
        sg2d.fillpipe = sQuartzCompositePipe;
        sg2d.shapepipe = sQuartzCompositePipe;
        sg2d.textpipe = sCocoaTextPipe;
    }

    public Rectangle getBounds() {
        // gznote: always return a copy, not the rect itself and translate into device space
        return new Rectangle(fBounds.x, fBounds.y, fBounds.width, fBounds.height - fBounds.y);
    }

    public GraphicsConfiguration getDeviceConfiguration() {
        return fConfig;
    }

    protected void setBounds(int x, int y, int w, int h) {
        fBounds.reshape(x, y, w, y + h);
    }

    // START compositing support API
    public abstract BufferedImage copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, BufferedImage image);

    public abstract boolean xorSurfacePixels(SunGraphics2D sg2d, BufferedImage srcPixels, int x, int y, int w, int h, int colorXOR);

    GraphicsConfiguration sDefaultGraphicsConfiguration = null;

    protected BufferedImage getCompositingImage(int w, int h) {
        if (sDefaultGraphicsConfiguration == null) {
            sDefaultGraphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        }

        BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
        // clear the image.
        clearRect(img, w, h);
        return img;
    }

    protected BufferedImage getCompositingImageSame(BufferedImage img, int w, int h) {
        if ((img == null) || (img.getWidth() != w) || (img.getHeight() != h)) {
            img = getCompositingImage(w, h);
        }
        return img;
    }

    BufferedImage sSrcComposite = null;

    public BufferedImage getCompositingSrcImage(int w, int h) {
        // <rdar://problem/3720263>. Changed from getCompositingImageBiggerOrSame() to
        // getCompositingImageSame(). (vm)
        BufferedImage bim = getCompositingImageSame(sSrcComposite, w, h);
        sSrcComposite = bim;
        return bim;
    }

    BufferedImage sDstInComposite = null;

    public BufferedImage getCompositingDstInImage(int w, int h) {
        BufferedImage bim = getCompositingImageSame(sDstInComposite, w, h);
        sDstInComposite = bim;
        return bim;
    }

    BufferedImage sDstOutComposite = null;

    public BufferedImage getCompositingDstOutImage(int w, int h) {
        BufferedImage bim = getCompositingImageSame(sDstOutComposite, w, h);
        sDstOutComposite = bim;
        return bim;
    }

    public void clearRect(BufferedImage bim, int w, int h) {
        Graphics2D g = bim.createGraphics();
        g.setComposite(AlphaComposite.Clear);
        g.fillRect(0, 0, w, h);
        g.dispose();
    }

    // END compositing support API

    public void invalidate() {
        // always valid
    }

     // graphics primitives drawing implementation:

    // certain primitives don't care about all the states (ex. drawing an image needs not involve setting current paint)
    static final int kPrimitive = 0;
    static final int kImage = 1;
    static final int kText = 2;
    static final int kCopyArea = 3;
    static final int kExternal = 4;

    static final int kLine = 5; // belongs to kPrimitive
    static final int kRect = 6; // belongs to kPrimitive
    static final int kRoundRect = 7; // belongs to kPrimitive
    static final int kOval = 8; // belongs to kPrimitive
    static final int kArc = 9; // belongs to kPrimitive
    static final int kPolygon = 10; // belongs to kPrimitive
    static final int kShape = 11; // belongs to kPrimitive
    // static final int kImage = 12; // belongs to kImage
    static final int kString = 13; // belongs to kText
    static final int kGlyphs = 14; // belongs to kText
    static final int kUnicodes = 15; // belongs to kText
    // static final int kCopyArea = 16; // belongs to kCopyArea
    // static final int kExternal = 17; // belongs to kExternal

    static final int kCommonParameterCount = 1 + 1 + 4 + 4; // type + change flags + color info (type(1) align(1) and
                                                            // value(2)) + parameters ((x1, y1, x2, y2) OR (x, y, w, h))
    static final int kLineParametersCount = kCommonParameterCount; // kCommonParameterCount
    static final int kRectParametersCount = kCommonParameterCount + 1; // kCommonParameterCount + isfill
    static final int kRoundRectParametersCount = kCommonParameterCount + 2 + 1; // kCommonParameterCount + arcW + arcH +
                                                                                // isfill
    static final int kOvalParametersCount = kCommonParameterCount + 1; // kCommonParameterCount + isfill
    static final int kArcParametersCount = kCommonParameterCount + 2 + 1 + 1;// kCommonParameterCount + startAngle +
                                                                             // arcAngle + isfill + type
    static final int kPolygonParametersCount = 0; // not supported
    static final int kShapeParametersCount = 0; // not supported
    static final int kImageParametersCount = kCommonParameterCount + 2 + 2 + 4 + 4; // flip horz vert + w&h + src + dst
    static final int kStringParametersCount = 0; // not supported
    static final int kGlyphsParametersCount = 0; // not supported
    static final int kUnicodesParametersCount = 0; // not supported
    static final int kPixelParametersCount = 0; // not supported
    static final int kExternalParametersCount = 0; // not supported

    // for intParameters
    // states info
    static final int kChangeFlagIndex = 0; // kBoundsChangedBit | .. | kFontChangedBit
    // bounds info
    static final int kBoundsXIndex = 1;
    static final int kBoundsYIndex = 2;
    static final int kBoundsWidthIndex = 3;
    static final int kBoundsHeightIndex = 4;
    // clip info
    static final int kClipStateIndex = 5;
    static final int kClipNumTypesIndex = 6;
    static final int kClipNumCoordsIndex = 7;
    static final int kClipWindingRuleIndex = 8;
    static final int kClipXIndex = 9;
    static final int kClipYIndex = 10;
    static final int kClipWidthIndex = 11;
    static final int kClipHeightIndex = 12;
    // ctm info
    static final int kCTMaIndex = 13;
    static final int kCTMbIndex = 14;
    static final int kCTMcIndex = 15;
    static final int kCTMdIndex = 16;
    static final int kCTMtxIndex = 17;
    static final int kCTMtyIndex = 18;
    // color info
    static final int kColorStateIndex = 19; // kColorSimple or kColorGradient or kColorTexture
    static final int kColorRGBValueIndex = 20; // if kColorSimple
    static final int kColorIndexValueIndex = 21; // if kColorSystem
    static final int kColorPointerIndex = 22; //
    static final int kColorPointerIndex2 = 23; //
    static final int kColorRGBValue1Index = 24; // if kColorGradient
    static final int kColorWidthIndex = 25; // if kColorTexture
    static final int kColorRGBValue2Index = 26; // if kColorGradient
    static final int kColorHeightIndex = 27; // if kColorTexture
    static final int kColorIsCyclicIndex = 28; // if kColorGradient (kColorNonCyclic or kColorCyclic)
    static final int kColorx1Index = 29;
    static final int kColortxIndex = 30;
    static final int kColory1Index = 31;
    static final int kColortyIndex = 32;
    static final int kColorx2Index = 33;
    static final int kColorsxIndex = 34;
    static final int kColory2Index = 35;
    static final int kColorsyIndex = 36;
    // composite info
    static final int kCompositeRuleIndex = 37; // kCGCompositeClear or ... or kCGCompositeXor
    static final int kCompositeValueIndex = 38;
    // stroke info
    static final int kStrokeJoinIndex = 39; // see BasicStroke.java
    static final int kStrokeCapIndex = 40; // see BasicStroke.java
    static final int kStrokeWidthIndex = 41;
    static final int kStrokeDashPhaseIndex = 42;
    static final int kStrokeLimitIndex = 43;
    // hints info
    static final int kHintsAntialiasIndex = 44;
    static final int kHintsTextAntialiasIndex = 45;
    static final int kHintsFractionalMetricsIndex = 46;
    static final int kHintsRenderingIndex = 47;
    static final int kHintsInterpolationIndex = 48;
    // live resizing info
    static final int kCanDrawDuringLiveResizeIndex = 49;

    static final int kSizeOfParameters = kCanDrawDuringLiveResizeIndex + 1;

    // for objectParameters
    static final int kClipCoordinatesIndex = 0;
    static final int kClipTypesIndex = 1;
    static final int kTextureImageIndex = 2;
    static final int kStrokeDashArrayIndex = 3;
    static final int kFontIndex = 4;
    static final int kFontPaintIndex = 5;

    // possible state changes
    static final int kBoundsChangedBit = 1 << 0;
    static final int kBoundsNotChangedBit = ~kBoundsChangedBit;
    static final int kClipChangedBit = 1 << 1;
    static final int kClipNotChangedBit = ~kClipChangedBit;
    static final int kCTMChangedBit = 1 << 2;
    static final int kCTMNotChangedBit = ~kCTMChangedBit;
    static final int kColorChangedBit = 1 << 3;
    static final int kColorNotChangedBit = ~kColorChangedBit;
    static final int kCompositeChangedBit = 1 << 4;
    static final int kCompositeNotChangedBit = ~kCompositeChangedBit;
    static final int kStrokeChangedBit = 1 << 5;
    static final int kStrokeNotChangedBit = ~kStrokeChangedBit;
    static final int kHintsChangedBit = 1 << 6;
    static final int kHintsNotChangedBit = ~kHintsChangedBit;
    static final int kFontChangedBit = 1 << 7;
    static final int kFontNotChangedBit = ~kFontChangedBit;
    static final int kEverythingChangedFlag = 0xffffffff;

    // possible color states
    static final int kColorSimple = 0;
    static final int kColorSystem = 1;
    static final int kColorGradient = 2;
    static final int kColorTexture = 3;

    // possible gradient color states
    static final int kColorNonCyclic = 0;
    static final int kColorCyclic = 1;

    // possible clip states
    static final int kClipRect = 0;
    static final int kClipShape = 1;

    static int getRendererTypeForPrimitive(int primitiveType) {
        switch (primitiveType) {
            case kImage:
                return kImage;
            case kCopyArea:
                return kCopyArea;
            case kExternal:
                return kExternal;
            case kString:
            case kGlyphs:
            case kUnicodes:
                return kText;
            default:
                return kPrimitive;
        }
    }

    int fChangeFlag;
    protected ByteBuffer fGraphicsStates = null;
    IntBuffer fGraphicsStatesInt = null;
    FloatBuffer fGraphicsStatesFloat = null;
    LongBuffer fGraphicsStatesLong = null;
    protected Object[] fGraphicsStatesObject = null;

    Rectangle userBounds = new Rectangle();
    float lastUserX = 0;
    float lastUserY = 0;
    float lastUserW = 0;
    float lastUserH = 0;

    void setUserBounds(SunGraphics2D sg2d, int x, int y, int width, int height) {
        if ((lastUserX != x) || (lastUserY != y) || (lastUserW != width) || (lastUserH != height)) {
            lastUserX = x;
            lastUserY = y;
            lastUserW = width;
            lastUserH = height;

            this.fGraphicsStatesInt.put(kBoundsXIndex, x);
            this.fGraphicsStatesInt.put(kBoundsYIndex, y);
            this.fGraphicsStatesInt.put(kBoundsWidthIndex, width);
            this.fGraphicsStatesInt.put(kBoundsHeightIndex, height);

            userBounds.setBounds(x, y, width, height);

            this.fChangeFlag = (this.fChangeFlag | kBoundsChangedBit);
        } else {
            this.fChangeFlag = (this.fChangeFlag & kBoundsNotChangedBit);
        }
    }

    static ByteBuffer getBufferOfSize(int size) {
        ByteBuffer buffer = ByteBuffer.allocateDirect(size * 4);
        buffer.order(ByteOrder.nativeOrder());
        return buffer;
    }

    FloatBuffer clipCoordinatesArray = null;
    IntBuffer clipTypesArray = null;
    Shape lastClipShape = null;
    float lastClipX = 0;
    float lastClipY = 0;
    float lastClipW = 0;
    float lastClipH = 0;

    void setupClip(SunGraphics2D sg2d) {
        switch (sg2d.clipState) {
            case SunGraphics2D.CLIP_DEVICE:
            case SunGraphics2D.CLIP_RECTANGULAR: {
                Region clip = sg2d.getCompClip();
                float x = clip.getLoX();
                float y = clip.getLoY();
                float w = clip.getWidth();
                float h = clip.getHeight();
                if ((this.fGraphicsStatesInt.get(kClipStateIndex) != kClipRect) ||
                        (x != lastClipX) ||
                            (y != lastClipY) ||
                                (w != lastClipW) ||
                                    (h != lastClipH)) {
                    this.fGraphicsStatesFloat.put(kClipXIndex, x);
                    this.fGraphicsStatesFloat.put(kClipYIndex, y);
                    this.fGraphicsStatesFloat.put(kClipWidthIndex, w);
                    this.fGraphicsStatesFloat.put(kClipHeightIndex, h);

                    lastClipX = x;
                    lastClipY = y;
                    lastClipW = w;
                    lastClipH = h;

                    this.fChangeFlag = (this.fChangeFlag | kClipChangedBit);
                } else {
                    this.fChangeFlag = (this.fChangeFlag & kClipNotChangedBit);
                }
                this.fGraphicsStatesInt.put(kClipStateIndex, kClipRect);
                break;
            }
            case SunGraphics2D.CLIP_SHAPE: {
                // if (lastClipShape != sg2d.usrClip) shapes are mutable!, and doing "equals" traverses all
                // the coordinates, so we might as well do all of it anyhow
                lastClipShape = sg2d.usrClip;

                GeneralPath gp = null;

                if (sg2d.usrClip instanceof GeneralPath) {
                    gp = (GeneralPath) sg2d.usrClip;
                } else {
                    gp = new GeneralPath(sg2d.usrClip);
                }

                int shapeLength = getPathLength(gp);

                if ((clipCoordinatesArray == null) || (clipCoordinatesArray.capacity() < (shapeLength * 6))) {
                    clipCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a
                                                                                             // max of 6 coordinates
                }
                if ((clipTypesArray == null) || (clipTypesArray.capacity() < shapeLength)) {
                    clipTypesArray = getBufferOfSize(shapeLength).asIntBuffer();
                }

                int windingRule = getPathCoordinates(gp, clipCoordinatesArray, clipTypesArray);

                this.fGraphicsStatesInt.put(kClipNumTypesIndex, clipTypesArray.position());
                this.fGraphicsStatesInt.put(kClipNumCoordsIndex, clipCoordinatesArray.position());
                this.fGraphicsStatesInt.put(kClipWindingRuleIndex, windingRule);
                this.fGraphicsStatesObject[kClipTypesIndex] = clipTypesArray;
                this.fGraphicsStatesObject[kClipCoordinatesIndex] = clipCoordinatesArray;

                this.fChangeFlag = (this.fChangeFlag | kClipChangedBit);
                this.fGraphicsStatesInt.put(kClipStateIndex, kClipShape);
                break;
            }
        }

    }

    final double[] lastCTM = new double[6];
    float lastCTMa = 0;
    float lastCTMb = 0;
    float lastCTMc = 0;
    float lastCTMd = 0;
    float lastCTMtx = 0;
    float lastCTMty = 0;

    void setupTransform(SunGraphics2D sg2d) {
        sg2d.transform.getMatrix(lastCTM);

        float a = (float) lastCTM[0];
        float b = (float) lastCTM[1];
        float c = (float) lastCTM[2];
        float d = (float) lastCTM[3];
        float tx = (float) lastCTM[4];
        float ty = (float) lastCTM[5];
        if (tx != lastCTMtx ||
                ty != lastCTMty ||
                    a != lastCTMa ||
                        b != lastCTMb ||
                            c != lastCTMc ||
                                d != lastCTMd) {
            this.fGraphicsStatesFloat.put(kCTMaIndex, a);
            this.fGraphicsStatesFloat.put(kCTMbIndex, b);
            this.fGraphicsStatesFloat.put(kCTMcIndex, c);
            this.fGraphicsStatesFloat.put(kCTMdIndex, d);
            this.fGraphicsStatesFloat.put(kCTMtxIndex, tx);
            this.fGraphicsStatesFloat.put(kCTMtyIndex, ty);

            lastCTMa = a;
            lastCTMb = b;
            lastCTMc = c;
            lastCTMd = d;
            lastCTMtx = tx;
            lastCTMty = ty;

            this.fChangeFlag = (this.fChangeFlag | kCTMChangedBit);
        } else {
            this.fChangeFlag = (this.fChangeFlag & kCTMNotChangedBit);
        }
    }

    static AffineTransform sIdentityMatrix = new AffineTransform();
    Paint lastPaint = null;
    long lastPaintPtr = 0;
    int lastPaintRGB = 0;
    int lastPaintIndex = 0;
    BufferedImage texturePaintImage = null;

    void setupPaint(SunGraphics2D sg2d, int x, int y, int w, int h) {
        if (sg2d.paint instanceof SystemColor) {
            SystemColor color = (SystemColor) sg2d.paint;
            int index = color.hashCode(); // depends on Color.java hashCode implementation! (returns "value" of color)
            if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSystem) || (index != this.lastPaintIndex)) {
                this.lastPaintIndex = index;

                this.fGraphicsStatesInt.put(kColorStateIndex, kColorSystem);
                this.fGraphicsStatesInt.put(kColorIndexValueIndex, index);

                this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
            } else {
                this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
            }
        } else if (sg2d.paint instanceof Color) {
            Color color = (Color) sg2d.paint;
            int rgb = color.getRGB();
            if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSimple) || (rgb != this.lastPaintRGB)) {
                this.lastPaintRGB = rgb;

                this.fGraphicsStatesInt.put(kColorStateIndex, kColorSimple);
                this.fGraphicsStatesInt.put(kColorRGBValueIndex, rgb);

                this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
            } else {
                this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
            }
        } else if (sg2d.paint instanceof GradientPaint) {
            if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorGradient) || (lastPaint != sg2d.paint)) {
                GradientPaint color = (GradientPaint) sg2d.paint;
                this.fGraphicsStatesInt.put(kColorStateIndex, kColorGradient);
                this.fGraphicsStatesInt.put(kColorRGBValue1Index, color.getColor1().getRGB());
                this.fGraphicsStatesInt.put(kColorRGBValue2Index, color.getColor2().getRGB());
                this.fGraphicsStatesInt.put(kColorIsCyclicIndex, (color.isCyclic()) ? kColorCyclic : kColorNonCyclic);
                Point2D p = color.getPoint1();
                this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX());
                this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY());
                p = color.getPoint2();
                this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX());
                this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY());

                this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
            } else {
                this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
            }
        } else if (sg2d.paint instanceof TexturePaint) {
            if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint)) {
                TexturePaint color = (TexturePaint) sg2d.paint;
                this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture);
                texturePaintImage = color.getImage();
                SurfaceData textureSurfaceData = BufImgSurfaceData.createData(texturePaintImage);
                this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth());
                this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight());
                Rectangle2D anchor = color.getAnchorRect();
                this.fGraphicsStatesFloat.put(kColortxIndex, (float) anchor.getX());
                this.fGraphicsStatesFloat.put(kColortyIndex, (float) anchor.getY());
                this.fGraphicsStatesFloat.put(kColorsxIndex, (float) (anchor.getWidth() / texturePaintImage.getWidth()));
                this.fGraphicsStatesFloat.put(kColorsyIndex, (float) (anchor.getHeight() / texturePaintImage.getHeight()));
                this.fGraphicsStatesObject[kTextureImageIndex] = textureSurfaceData;

                this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
            } else {
                this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
            }
        } else {
            if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint) || ((this.fChangeFlag & kBoundsChangedBit) != 0)) {
                PaintContext context = sg2d.paint.createContext(sg2d.getDeviceColorModel(), userBounds, userBounds, sIdentityMatrix, sg2d.getRenderingHints());
                WritableRaster raster = (WritableRaster) (context.getRaster(userBounds.x, userBounds.y, userBounds.width, userBounds.height));
                ColorModel cm = context.getColorModel();
                texturePaintImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);

                this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture);
                this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth());
                this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight());
                this.fGraphicsStatesFloat.put(kColortxIndex, (float) userBounds.getX());
                this.fGraphicsStatesFloat.put(kColortyIndex, (float) userBounds.getY());
                this.fGraphicsStatesFloat.put(kColorsxIndex, 1.0f);
                this.fGraphicsStatesFloat.put(kColorsyIndex, 1.0f);
                this.fGraphicsStatesObject[kTextureImageIndex] = sun.awt.image.BufImgSurfaceData.createData(texturePaintImage);

                context.dispose();

                this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
            } else {
                this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
            }
        }
        lastPaint = sg2d.paint;
    }

    Composite lastComposite;
    int lastCompositeAlphaRule = 0;
    float lastCompositeAlphaValue = 0;

    void setupComposite(SunGraphics2D sg2d) {
        Composite composite = sg2d.composite;

        if (lastComposite != composite) {
            lastComposite = composite;

            // For composite state COMP_ISCOPY, COMP_XOR or COMP_CUSTOM set alpha compositor to COPY:
            int alphaRule = AlphaComposite.SRC_OVER;
            float alphaValue = 1.0f;

            // For composite state COMP_ISCOPY composite could be null. If it's not (or composite state == COMP_ALPHA)
            // get alpha compositor's values:
            if ((sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) && (composite != null)) {
                AlphaComposite alphaComposite = (AlphaComposite) composite;
                alphaRule = alphaComposite.getRule();
                alphaValue = alphaComposite.getAlpha();
            }

            // 2-17-03 VL: [Radar 3174922]
            // For COMP_XOR and COMP_CUSTOM compositing modes we should be setting alphaRule = AlphaComposite.SRC
            // which should map to kCGCompositeCopy.

            if ((lastCompositeAlphaRule != alphaRule) || (lastCompositeAlphaValue != alphaValue)) {
                this.fGraphicsStatesInt.put(kCompositeRuleIndex, alphaRule);
                this.fGraphicsStatesFloat.put(kCompositeValueIndex, alphaValue);

                lastCompositeAlphaRule = alphaRule;
                lastCompositeAlphaValue = alphaValue;

                this.fChangeFlag = (this.fChangeFlag | kCompositeChangedBit);
            } else {
                this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
            }
        } else {
            this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
        }
    }

    BasicStroke lastStroke = null;
    static BasicStroke defaultBasicStroke = new BasicStroke();

    void setupStroke(SunGraphics2D sg2d) {
        BasicStroke stroke = defaultBasicStroke;

        if (sg2d.stroke instanceof BasicStroke) {
            stroke = (BasicStroke) sg2d.stroke;
        }

        if (lastStroke != stroke) {
            this.fGraphicsStatesObject[kStrokeDashArrayIndex] = stroke.getDashArray();
            this.fGraphicsStatesFloat.put(kStrokeDashPhaseIndex, stroke.getDashPhase());
            this.fGraphicsStatesInt.put(kStrokeCapIndex, stroke.getEndCap());
            this.fGraphicsStatesInt.put(kStrokeJoinIndex, stroke.getLineJoin());
            this.fGraphicsStatesFloat.put(kStrokeWidthIndex, stroke.getLineWidth());
            this.fGraphicsStatesFloat.put(kStrokeLimitIndex, stroke.getMiterLimit());

            this.fChangeFlag = (this.fChangeFlag | kStrokeChangedBit);

            lastStroke = stroke;
        } else {
            this.fChangeFlag = (this.fChangeFlag & kStrokeNotChangedBit);
        }
    }

    Font lastFont;

    void setupFont(Font font, Paint paint) {
        if (font == null) { return; }

        // We have to setup the kFontPaintIndex if we have changed the color so we added the last
        // test to see if the color has changed - needed for complex strings
        // see Radar 3368674
        if ((font != lastFont) || ((this.fChangeFlag & kColorChangedBit) != 0)) {
            this.fGraphicsStatesObject[kFontIndex] = font;
            this.fGraphicsStatesObject[kFontPaintIndex] = paint;

            this.fChangeFlag = (this.fChangeFlag | kFontChangedBit);

            lastFont = font;
        } else {
            this.fChangeFlag = (this.fChangeFlag & kFontNotChangedBit);
        }
    }

    void setupRenderingHints(SunGraphics2D sg2d) {
        boolean hintsChanged = false;

        // Significant for draw, fill, text, and image ops:
        int antialiasHint = sg2d.antialiasHint;
        if (this.fGraphicsStatesInt.get(kHintsAntialiasIndex) != antialiasHint) {
            this.fGraphicsStatesInt.put(kHintsAntialiasIndex, antialiasHint);
            hintsChanged = true;
        }

        // Significant only for text ops:
        int textAntialiasHint = sg2d.textAntialiasHint;
        if (this.fGraphicsStatesInt.get(kHintsTextAntialiasIndex) != textAntialiasHint) {
            this.fGraphicsStatesInt.put(kHintsTextAntialiasIndex, textAntialiasHint);
            hintsChanged = true;
        }

        // Significant only for text ops:
        int fractionalMetricsHint = sg2d.fractionalMetricsHint;
        if (this.fGraphicsStatesInt.get(kHintsFractionalMetricsIndex) != fractionalMetricsHint) {
            this.fGraphicsStatesInt.put(kHintsFractionalMetricsIndex, fractionalMetricsHint);
            hintsChanged = true;
        }

        // Significant only for image ops:
        int renderHint = sg2d.renderHint;
        if (this.fGraphicsStatesInt.get(kHintsRenderingIndex) != renderHint) {
            this.fGraphicsStatesInt.put(kHintsRenderingIndex, renderHint);
            hintsChanged = true;
        }

        // Significant only for image ops:
        Object hintValue = sg2d.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
        int interpolationHint = (hintValue != null ? ((SunHints.Value) hintValue).getIndex() : -1);
        if (this.fGraphicsStatesInt.get(kHintsInterpolationIndex) != interpolationHint) {
            this.fGraphicsStatesInt.put(kHintsInterpolationIndex, interpolationHint);
            hintsChanged = true;
        }

        if (hintsChanged) {
            this.fChangeFlag = (this.fChangeFlag | kHintsChangedBit);
        } else {
            this.fChangeFlag = (this.fChangeFlag & kHintsNotChangedBit);
        }
    }

    SunGraphics2D sg2dCurrent = null;
    Thread threadCurrent = null;

    void setupGraphicsState(SunGraphics2D sg2d, int primitiveType) {
        setupGraphicsState(sg2d, primitiveType, sg2d.font, 0, 0, fBounds.width, fBounds.height); // deviceBounds into userBounds
    }

    void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, int x, int y, int w, int h) {
        setupGraphicsState(sg2d, primitiveType, sg2d.font, x, y, w, h);
    }

    // the method below is overriden by CPeerSurface to check the last peer used to draw
    // if the peer changed we finish lazy drawing
    void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, Font font, int x, int y, int w, int h) {
        this.fChangeFlag = 0;

        setUserBounds(sg2d, x, y, w, h);

        Thread thread = Thread.currentThread();
        if ((this.sg2dCurrent != sg2d) || (this.threadCurrent != thread)) {
            this.sg2dCurrent = sg2d;
            this.threadCurrent = thread;

            setupClip(sg2d);
            setupTransform(sg2d);
            setupPaint(sg2d, x, y, w, h);
            setupComposite(sg2d);
            setupStroke(sg2d);
            setupFont(font, sg2d.paint);
            setupRenderingHints(sg2d);

            this.fChangeFlag = kEverythingChangedFlag;
        } else {
            int rendererType = getRendererTypeForPrimitive(primitiveType);

            setupClip(sg2d);
            setupTransform(sg2d);

            if (rendererType != kCopyArea) {
                setupComposite(sg2d);
                setupRenderingHints(sg2d);

                if ((rendererType != kImage)) {
                    setupPaint(sg2d, x, y, w, h);
                    setupStroke(sg2d);
                }
                if (rendererType != kPrimitive) {
                    setupFont(font, sg2d.paint);
                }

            }
        }

        this.fGraphicsStatesInt.put(kChangeFlagIndex, this.fChangeFlag);
    }

    boolean isCustomPaint(SunGraphics2D sg2d) {
        if ((sg2d.paint instanceof Color) || (sg2d.paint instanceof SystemColor) || (sg2d.paint instanceof GradientPaint) || (sg2d.paint instanceof TexturePaint)) { return false; }

        return true;
    }

    final float[] segmentCoordinatesArray = new float[6];

    int getPathLength(GeneralPath gp) {
        int length = 0;

        PathIterator pi = gp.getPathIterator(null);
        while (pi.isDone() == false) {
            pi.next();
            length++;
        }

        return length;
    }

    int getPathCoordinates(GeneralPath gp, FloatBuffer coordinates, IntBuffer types) {
        // System.err.println("getPathCoordinates");
        boolean skip = false;

        coordinates.clear();
        types.clear();

        int type;

        PathIterator pi = gp.getPathIterator(null);
        while (pi.isDone() == false) {
            skip = false;
            type = pi.currentSegment(segmentCoordinatesArray);

            switch (type) {
                case PathIterator.SEG_MOVETO:
                    // System.err.println(" SEG_MOVETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
                    if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
                            segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
                        coordinates.put(segmentCoordinatesArray[0]);
                        coordinates.put(segmentCoordinatesArray[1]);
                    } else {
                        skip = true;
                    }
                    break;
                case PathIterator.SEG_LINETO:
                    // System.err.println(" SEG_LINETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
                    if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
                            segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
                        coordinates.put(segmentCoordinatesArray[0]);
                        coordinates.put(segmentCoordinatesArray[1]);
                    } else {
                        skip = true;
                    }
                    break;
                case PathIterator.SEG_QUADTO:
                    // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+")");
                    if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
                            segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
                            segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
                            segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND) {
                        coordinates.put(segmentCoordinatesArray[0]);
                        coordinates.put(segmentCoordinatesArray[1]);
                        coordinates.put(segmentCoordinatesArray[2]);
                        coordinates.put(segmentCoordinatesArray[3]);
                    } else {
                        skip = true;
                    }
                    break;
                case PathIterator.SEG_CUBICTO:
                    // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+"), ("+segmentCoordinatesArray[4]+", "+segmentCoordinatesArray[5]+")");
                    if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
                            segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
                            segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
                            segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND &&
                            segmentCoordinatesArray[4] < UPPER_BND && segmentCoordinatesArray[4] > LOWER_BND &&
                            segmentCoordinatesArray[5] < UPPER_BND && segmentCoordinatesArray[5] > LOWER_BND) {
                        coordinates.put(segmentCoordinatesArray[0]);
                        coordinates.put(segmentCoordinatesArray[1]);
                        coordinates.put(segmentCoordinatesArray[2]);
                        coordinates.put(segmentCoordinatesArray[3]);
                        coordinates.put(segmentCoordinatesArray[4]);
                        coordinates.put(segmentCoordinatesArray[5]);
                    } else {
                        skip = true;
                    }
                    break;
                case PathIterator.SEG_CLOSE:
                    // System.err.println(" SEG_CLOSE");
                    break;
            }

            if (!skip) {
                types.put(type);
            }

            pi.next();
        }

        return pi.getWindingRule();
    }

    public void doLine(CRenderer renderer, SunGraphics2D sg2d, float x1, float y1, float x2, float y2) {
        // System.err.println("-- doLine x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2+" paint="+sg2d.paint);
        setupGraphicsState(sg2d, kLine, sg2d.font, 0, 0, fBounds.width, fBounds.height);
        renderer.doLine(this, x1, y1, x2, y2);
    }

    public void doRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
        // System.err.println("-- doRect x="+x+" y="+y+" w="+width+" h="+height+" isfill="+isfill+" paint="+sg2d.paint);
        if ((isfill) && (isCustomPaint(sg2d))) {
            setupGraphicsState(sg2d, kRect, (int) x, (int) y, (int) width, (int) height);
        } else {
            setupGraphicsState(sg2d, kRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
        }
        renderer.doRect(this, x, y, width, height, isfill);
    }

    public void doRoundRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float arcW, float arcH, boolean isfill) {
        // System.err.println("--- doRoundRect");
        if ((isfill) && (isCustomPaint(sg2d))) {
            setupGraphicsState(sg2d, kRoundRect, (int) x, (int) y, (int) width, (int) height);
        } else {
            setupGraphicsState(sg2d, kRoundRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
        }
        renderer.doRoundRect(this, x, y, width, height, arcW, arcH, isfill);
    }

    public void doOval(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
        // System.err.println("--- doOval");
        if ((isfill) && (isCustomPaint(sg2d))) {
            setupGraphicsState(sg2d, kOval, (int) x, (int) y, (int) width, (int) height);
        } else {
            setupGraphicsState(sg2d, kOval, sg2d.font, 0, 0, fBounds.width, fBounds.height);
        }
        renderer.doOval(this, x, y, width, height, isfill);
    }

    public void doArc(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float startAngle, float arcAngle, int type, boolean isfill) {
        // System.err.println("--- doArc");
        if ((isfill) && (isCustomPaint(sg2d))) {
            setupGraphicsState(sg2d, kArc, (int) x, (int) y, (int) width, (int) height);
        } else {
            setupGraphicsState(sg2d, kArc, sg2d.font, 0, 0, fBounds.width, fBounds.height);
        }

        renderer.doArc(this, x, y, width, height, startAngle, arcAngle, type, isfill);
    }

    public void doPolygon(CRenderer renderer, SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints, boolean ispolygon, boolean isfill) {
        // System.err.println("--- doPolygon");

        if ((isfill) && (isCustomPaint(sg2d))) {
            int minx = xpoints[0];
            int miny = ypoints[0];
            int maxx = minx;
            int maxy = miny;
            for (int i = 1; i < npoints; i++) {
                int x = xpoints[i];
                if (x < minx) {
                    minx = x;
                } else if (x > maxx) {
                    maxx = x;
                }

                int y = ypoints[i];
                if (y < miny) {
                    miny = y;
                } else if (y > maxy) {
                    maxy = y;
                }
            }
            setupGraphicsState(sg2d, kPolygon, minx, miny, maxx - minx, maxy - miny);
        } else {
            setupGraphicsState(sg2d, kPolygon, sg2d.font, 0, 0, fBounds.width, fBounds.height);
        }
        renderer.doPoly(this, xpoints, ypoints, npoints, ispolygon, isfill);
    }

    FloatBuffer shapeCoordinatesArray = null;
    IntBuffer shapeTypesArray = null;

    public void drawfillShape(CRenderer renderer, SunGraphics2D sg2d, GeneralPath gp, boolean isfill, boolean shouldApplyOffset) {
        // System.err.println("--- drawfillShape");

        if ((isfill) && (isCustomPaint(sg2d))) {
            Rectangle bounds = gp.getBounds();
            setupGraphicsState(sg2d, kShape, bounds.x, bounds.y, bounds.width, bounds.height);
        } else {
            setupGraphicsState(sg2d, kShape, sg2d.font, 0, 0, fBounds.width, fBounds.height);
        }

        int shapeLength = getPathLength(gp);

        if ((shapeCoordinatesArray == null) || (shapeCoordinatesArray.capacity() < (shapeLength * 6))) {
            shapeCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a max of 6
                                                                                      // coordinates
        }
        if ((shapeTypesArray == null) || (shapeTypesArray.capacity() < shapeLength)) {
            shapeTypesArray = getBufferOfSize(shapeLength).asIntBuffer();
        }

        int windingRule = getPathCoordinates(gp, shapeCoordinatesArray, shapeTypesArray);

        renderer.doShape(this, shapeLength, shapeCoordinatesArray, shapeTypesArray, windingRule, isfill, shouldApplyOffset);
    }

    public void blitImage(CRenderer renderer, SunGraphics2D sg2d, SurfaceData img, boolean fliph, boolean flipv, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, Color bgColor) {
        // System.err.println("--- blitImage sx="+sx+", sy="+sy+", sw="+sw+", sh="+sh+", img="+img);
        OSXOffScreenSurfaceData osxsd = (OSXOffScreenSurfaceData) img;
        synchronized (osxsd.getLockObject()) {
            int w = osxsd.bim.getWidth();
            int h = osxsd.bim.getHeight();

            // the image itself can have outstanding graphics primitives that might need to be flushed
            setupGraphicsState(sg2d, kImage, sg2d.font, 0, 0, fBounds.width, fBounds.height);

            // 04/06/04 cmc: radr://3612381 Graphics.drawImage ignores bgcolor parameter
            if (bgColor != null) {
                img = osxsd.getCopyWithBgColor(bgColor);
            }

            renderer.doImage(this, img, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh);
        }
    }

    public interface CGContextDrawable {
        public void drawIntoCGContext(final long cgContext);
    }

    public void drawString(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, String str, double x, double y) {
        // System.err.println("--- drawString str=\""+str+"\"");
        // see <rdar://problem/3825795>. We don't want to call anything if the string is empty!
        if (str.length() == 0) { return; }

        setupGraphicsState(sg2d, kString, sg2d.font, 0, 0, fBounds.width, fBounds.height);
        renderer.doDrawString(this, nativeStrikePtr, str, x, y);
    }

    public void drawGlyphs(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, GlyphVector gv, float x, float y) {
        // System.err.println("--- drawGlyphs");
        setupGraphicsState(sg2d, kGlyphs, gv.getFont(), 0, 0, fBounds.width, fBounds.height);
        renderer.doDrawGlyphs(this, nativeStrikePtr, gv, x, y);
    }

    public void drawUnicodes(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, char unicodes[], int offset, int length, float x, float y) {
        // System.err.println("--- drawUnicodes "+(new String(unicodes, offset, length)));
        setupGraphicsState(sg2d, kUnicodes, sg2d.font, 0, 0, fBounds.width, fBounds.height);
        if (length == 1) {
            renderer.doOneUnicode(this, nativeStrikePtr, unicodes[offset], x, y);
        } else {
            renderer.doUnicodes(this, nativeStrikePtr, unicodes, offset, length, x, y);
        }
    }

    // used by copyArea:

    Rectangle srcCopyAreaRect = new Rectangle();
    Rectangle dstCopyAreaRect = new Rectangle();
    Rectangle finalCopyAreaRect = new Rectangle();
    Rectangle copyAreaBounds = new Rectangle();

    void intersection(Rectangle r1, Rectangle r2, Rectangle r3) {
        // this code is taken from Rectangle.java (modified to put results in r3)
        int tx1 = r1.x;
        int ty1 = r1.y;
        long tx2 = tx1 + r1.width;
        long ty2 = ty1 + r1.height;

        int rx1 = r2.x;
        int ry1 = r2.y;
        long rx2 = rx1 + r2.width;
        long ry2 = ry1 + r2.height;

        if (tx1 < rx1) tx1 = rx1;
        if (ty1 < ry1) ty1 = ry1;
        if (tx2 > rx2) tx2 = rx2;
        if (ty2 > ry2) ty2 = ry2;

        tx2 -= tx1;
        ty2 -= ty1;

        // tx2,ty2 will never overflow (they will never be
        // larger than the smallest of the two source w,h)
        // they might underflow, though...
        if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE;
        if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE;

        r3.setBounds(tx1, ty1, (int) tx2, (int) ty2);
    }

    
Clips the copy area to the heavywieght bounds and returns the cliped rectangle. The tricky part here is the the passed arguments x, y are in the coordinate space of the sg2d/lightweight comp. In order to do the clipping we translate them to the coordinate space of the surface, and the returned clipped rectangle is in the coordinate space of the surface.
/** * Clips the copy area to the heavywieght bounds and returns the cliped rectangle. The tricky part here is the the * passed arguments x, y are in the coordinate space of the sg2d/lightweight comp. In order to do the clipping we * translate them to the coordinate space of the surface, and the returned clipped rectangle is in the coordinate * space of the surface. */
protected Rectangle clipCopyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) { // we need to clip against the heavyweight bounds copyAreaBounds.setBounds(sg2d.devClip.getLoX(), sg2d.devClip.getLoY(), sg2d.devClip.getWidth(), sg2d.devClip.getHeight()); // put src rect into surface coordinate space x += sg2d.transX; y += sg2d.transY; // clip src rect srcCopyAreaRect.setBounds(x, y, w, h); intersection(srcCopyAreaRect, copyAreaBounds, srcCopyAreaRect); if ((srcCopyAreaRect.width <= 0) || (srcCopyAreaRect.height <= 0)) { // src rect outside bounds return null; } // clip dst rect dstCopyAreaRect.setBounds(srcCopyAreaRect.x + dx, srcCopyAreaRect.y + dy, srcCopyAreaRect.width, srcCopyAreaRect.height); intersection(dstCopyAreaRect, copyAreaBounds, dstCopyAreaRect); if ((dstCopyAreaRect.width <= 0) || (dstCopyAreaRect.height <= 0)) { // dst rect outside clip return null; } x = dstCopyAreaRect.x - dx; y = dstCopyAreaRect.y - dy; w = dstCopyAreaRect.width; h = dstCopyAreaRect.height; finalCopyAreaRect.setBounds(x, y, w, h); return finalCopyAreaRect; } // <rdar://3785539> We only need to mark dirty on screen surfaces. This method is // marked as protected and it is intended for subclasses to override if they need to // be notified when the surface is dirtied. See CPeerSurfaceData.markDirty() for implementation. // We don't do anything for buffered images. protected void markDirty(boolean markAsDirty) { // do nothing by default } // LazyDrawing optimization implementation: @Override public boolean canRenderLCDText(SunGraphics2D sg2d) { if (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY && sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR && sg2d.clipState <= SunGraphics2D.CLIP_RECTANGULAR && // sg2d.surfaceData.getTransparency() == Transparency.OPAQUE && // This last test is a workaround until we fix loop selection // in the pipe validation sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { return true; } return false; /* for now - in the future we may want to search */ } public static boolean IsSimpleColor(Object c) { return ((c instanceof Color) || (c instanceof SystemColor) || (c instanceof javax.swing.plaf.ColorUIResource)); } static { if ((kColorPointerIndex % 2) != 0) { System.err.println("kColorPointerIndex=" + kColorPointerIndex + " is NOT aligned for 64 bit"); System.exit(0); } } }