1/*
2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.java2d;
27
28import java.awt.*;
29import java.awt.font.*;
30import java.awt.geom.*;
31import java.awt.image.*;
32import java.nio.*;
33
34import sun.awt.*;
35import sun.awt.image.*;
36import sun.java2d.loops.*;
37import sun.java2d.pipe.*;
38import sun.lwawt.macosx.*;
39
40import java.lang.annotation.Native;
41
42/*
43 * This is the SurfaceData for a CGContextRef.
44 */
45public abstract class OSXSurfaceData extends BufImgSurfaceData {
46    static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
47    static final float LOWER_BND = -UPPER_BND;
48
49    protected static CRenderer sQuartzPipe = null;
50    protected static CTextPipe sCocoaTextPipe = null;
51    protected static CompositeCRenderer sQuartzCompositePipe = null;
52
53    private GraphicsConfiguration fConfig;
54    private Rectangle fBounds; // bounds in user coordinates
55
56    static {
57        sQuartzPipe = new CRenderer(); // Creates the singleton quartz pipe.
58    }
59
60    // NOTE: Any subclasses must eventually call QuartzSurfaceData_InitOps in OSXSurfaceData.h
61    // This sets up the native side for the SurfaceData, and is required.
62    public OSXSurfaceData(SurfaceType sType, ColorModel cm) {
63        this(sType, cm, null, new Rectangle());
64    }
65
66    public OSXSurfaceData(SurfaceType sType, ColorModel cm, GraphicsConfiguration config, Rectangle bounds) {
67        super(sType, cm);
68
69        this.fConfig = config;
70
71        this.fBounds = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.y + bounds.height);
72
73        this.fGraphicsStates = getBufferOfSize(kSizeOfParameters);
74        this.fGraphicsStatesInt = this.fGraphicsStates.asIntBuffer();
75        this.fGraphicsStatesFloat = this.fGraphicsStates.asFloatBuffer();
76        this.fGraphicsStatesLong = this.fGraphicsStates.asLongBuffer();
77        this.fGraphicsStatesObject = new Object[8]; // clip coordinates +
78                                                    // clip types +
79                                                    // texture paint image +
80                                                    // stroke dash array +
81                                                    // font + font paint +
82                                                    // linear/radial gradient color +
83                                                    // linear/radial gradient fractions
84
85        // NOTE: All access to the DrawingQueue comes through this OSXSurfaceData instance. Therefore
86        // every instance method of OSXSurfaceData that accesses the fDrawingQueue is synchronized.
87
88        // Thread.dumpStack();
89    }
90
91    public void validatePipe(SunGraphics2D sg2d) {
92
93        if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) {
94            if (sCocoaTextPipe == null) {
95                sCocoaTextPipe = new CTextPipe();
96            }
97
98            sg2d.imagepipe = sQuartzPipe;
99            sg2d.drawpipe = sQuartzPipe;
100            sg2d.fillpipe = sQuartzPipe;
101            sg2d.shapepipe = sQuartzPipe;
102            sg2d.textpipe = sCocoaTextPipe;
103        } else {
104            setPipesToQuartzComposite(sg2d);
105        }
106    }
107
108    protected void setPipesToQuartzComposite(SunGraphics2D sg2d) {
109        if (sQuartzCompositePipe == null) {
110            sQuartzCompositePipe = new CompositeCRenderer();
111        }
112
113        if (sCocoaTextPipe == null) {
114            sCocoaTextPipe = new CTextPipe();
115        }
116
117        sg2d.imagepipe = sQuartzCompositePipe;
118        sg2d.drawpipe = sQuartzCompositePipe;
119        sg2d.fillpipe = sQuartzCompositePipe;
120        sg2d.shapepipe = sQuartzCompositePipe;
121        sg2d.textpipe = sCocoaTextPipe;
122    }
123
124    public Rectangle getBounds() {
125        // gznote: always return a copy, not the rect itself and translate into device space
126        return new Rectangle(fBounds.x, fBounds.y, fBounds.width, fBounds.height - fBounds.y);
127    }
128
129    public GraphicsConfiguration getDeviceConfiguration() {
130        return fConfig;
131    }
132
133    protected void setBounds(int x, int y, int w, int h) {
134        fBounds.setBounds(x, y, w, y + h);
135    }
136
137    // START compositing support API
138    public abstract BufferedImage copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, BufferedImage image);
139
140    public abstract boolean xorSurfacePixels(SunGraphics2D sg2d, BufferedImage srcPixels, int x, int y, int w, int h, int colorXOR);
141
142    GraphicsConfiguration sDefaultGraphicsConfiguration = null;
143
144    protected BufferedImage getCompositingImage(int w, int h) {
145        if (sDefaultGraphicsConfiguration == null) {
146            sDefaultGraphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
147        }
148
149        BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
150        // clear the image.
151        clearRect(img, w, h);
152        return img;
153    }
154
155    protected BufferedImage getCompositingImageSame(BufferedImage img, int w, int h) {
156        if ((img == null) || (img.getWidth() != w) || (img.getHeight() != h)) {
157            img = getCompositingImage(w, h);
158        }
159        return img;
160    }
161
162    BufferedImage sSrcComposite = null;
163
164    public BufferedImage getCompositingSrcImage(int w, int h) {
165        // <rdar://problem/3720263>. Changed from getCompositingImageBiggerOrSame() to
166        // getCompositingImageSame(). (vm)
167        BufferedImage bim = getCompositingImageSame(sSrcComposite, w, h);
168        sSrcComposite = bim;
169        return bim;
170    }
171
172    BufferedImage sDstInComposite = null;
173
174    public BufferedImage getCompositingDstInImage(int w, int h) {
175        BufferedImage bim = getCompositingImageSame(sDstInComposite, w, h);
176        sDstInComposite = bim;
177        return bim;
178    }
179
180    BufferedImage sDstOutComposite = null;
181
182    public BufferedImage getCompositingDstOutImage(int w, int h) {
183        BufferedImage bim = getCompositingImageSame(sDstOutComposite, w, h);
184        sDstOutComposite = bim;
185        return bim;
186    }
187
188    public void clearRect(BufferedImage bim, int w, int h) {
189        Graphics2D g = bim.createGraphics();
190        g.setComposite(AlphaComposite.Clear);
191        g.fillRect(0, 0, w, h);
192        g.dispose();
193    }
194
195    // END compositing support API
196
197    public void invalidate() {
198        // always valid
199    }
200
201     // graphics primitives drawing implementation:
202
203    // certain primitives don't care about all the states (ex. drawing an image needs not involve setting current paint)
204    @Native static final int kPrimitive = 0;
205    @Native static final int kImage = 1;
206    @Native static final int kText = 2;
207    @Native static final int kCopyArea = 3;
208    @Native static final int kExternal = 4;
209
210    @Native static final int kLine = 5; // belongs to kPrimitive
211    @Native static final int kRect = 6; // belongs to kPrimitive
212    @Native static final int kRoundRect = 7; // belongs to kPrimitive
213    @Native static final int kOval = 8; // belongs to kPrimitive
214    @Native static final int kArc = 9; // belongs to kPrimitive
215    @Native static final int kPolygon = 10; // belongs to kPrimitive
216    @Native static final int kShape = 11; // belongs to kPrimitive
217    // static final int kImage = 12; // belongs to kImage
218    @Native static final int kString = 13; // belongs to kText
219    @Native static final int kGlyphs = 14; // belongs to kText
220    @Native static final int kUnicodes = 15; // belongs to kText
221    // static final int kCopyArea = 16; // belongs to kCopyArea
222    // static final int kExternal = 17; // belongs to kExternal
223
224    @Native static final int kCommonParameterCount = 1 + 1 + 4 + 4; // type + change flags + color info (type(1) align(1) and
225                                                            // value(2)) + parameters ((x1, y1, x2, y2) OR (x, y, w, h))
226    @Native static final int kLineParametersCount = kCommonParameterCount; // kCommonParameterCount
227    @Native static final int kRectParametersCount = kCommonParameterCount + 1; // kCommonParameterCount + isfill
228    @Native static final int kRoundRectParametersCount = kCommonParameterCount + 2 + 1; // kCommonParameterCount + arcW + arcH +
229                                                                                // isfill
230    @Native static final int kOvalParametersCount = kCommonParameterCount + 1; // kCommonParameterCount + isfill
231    @Native static final int kArcParametersCount = kCommonParameterCount + 2 + 1 + 1;// kCommonParameterCount + startAngle +
232                                                                             // arcAngle + isfill + type
233    @Native static final int kPolygonParametersCount = 0; // not supported
234    @Native static final int kShapeParametersCount = 0; // not supported
235    @Native static final int kImageParametersCount = kCommonParameterCount + 2 + 2 + 4 + 4; // flip horz vert + w&h + src + dst
236    @Native static final int kStringParametersCount = 0; // not supported
237    @Native static final int kGlyphsParametersCount = 0; // not supported
238    @Native static final int kUnicodesParametersCount = 0; // not supported
239    @Native static final int kPixelParametersCount = 0; // not supported
240    @Native static final int kExternalParametersCount = 0; // not supported
241
242    // for intParameters
243    // states info
244    @Native static final int kChangeFlagIndex = 0; // kBoundsChangedBit | .. | kFontChangedBit
245    // bounds info
246    @Native static final int kBoundsXIndex = 1;
247    @Native static final int kBoundsYIndex = 2;
248    @Native static final int kBoundsWidthIndex = 3;
249    @Native static final int kBoundsHeightIndex = 4;
250    // clip info
251    @Native static final int kClipStateIndex = 5;
252    @Native static final int kClipNumTypesIndex = 6;
253    @Native static final int kClipNumCoordsIndex = 7;
254    @Native static final int kClipWindingRuleIndex = 8;
255    @Native static final int kClipXIndex = 9;
256    @Native static final int kClipYIndex = 10;
257    @Native static final int kClipWidthIndex = 11;
258    @Native static final int kClipHeightIndex = 12;
259    // ctm info
260    @Native static final int kCTMaIndex = 13;
261    @Native static final int kCTMbIndex = 14;
262    @Native static final int kCTMcIndex = 15;
263    @Native static final int kCTMdIndex = 16;
264    @Native static final int kCTMtxIndex = 17;
265    @Native static final int kCTMtyIndex = 18;
266    // color info
267    @Native static final int kColorStateIndex = 19; // kColorSimple or kColorGradient or kColorTexture
268    @Native static final int kColorRGBValueIndex = 20; // if kColorSimple
269    @Native static final int kColorIndexValueIndex = 21; // if kColorSystem
270    @Native static final int kColorPointerIndex = 22; //
271    @Native static final int kColorPointerIndex2 = 23; //
272    @Native static final int kColorRGBValue1Index = 24; // if kColorGradient
273    @Native static final int kColorWidthIndex = 25; // if kColorTexture
274    @Native static final int kColorRGBValue2Index = 26; // if kColorGradient
275    @Native static final int kColorHeightIndex = 27; // if kColorTexture
276    @Native static final int kColorIsCyclicIndex = 28; // if kColorGradient (kColorNonCyclic or kColorCyclic)
277    @Native static final int kColorx1Index = 29;
278    @Native static final int kColortxIndex = 30;
279    @Native static final int kColory1Index = 31;
280    @Native static final int kColortyIndex = 32;
281    @Native static final int kColorx2Index = 33;
282    @Native static final int kColorsxIndex = 34;
283    @Native static final int kColory2Index = 35;
284    @Native static final int kColorsyIndex = 36;
285    // composite info
286    @Native static final int kCompositeRuleIndex = 37; // kCGCompositeClear or ... or kCGCompositeXor
287    @Native static final int kCompositeValueIndex = 38;
288    // stroke info
289    @Native static final int kStrokeJoinIndex = 39; // see BasicStroke.java
290    @Native static final int kStrokeCapIndex = 40; // see BasicStroke.java
291    @Native static final int kStrokeWidthIndex = 41;
292    @Native static final int kStrokeDashPhaseIndex = 42;
293    @Native static final int kStrokeLimitIndex = 43;
294    // hints info
295    @Native static final int kHintsAntialiasIndex = 44;
296    @Native static final int kHintsTextAntialiasIndex = 45;
297    @Native static final int kHintsFractionalMetricsIndex = 46;
298    @Native static final int kHintsRenderingIndex = 47;
299    @Native static final int kHintsInterpolationIndex = 48;
300    //gradient info
301    @Native static final int kRadiusIndex = 49;
302
303    @Native static final int kSizeOfParameters = kRadiusIndex + 1;
304
305    // for objectParameters
306    @Native static final int kClipCoordinatesIndex = 0;
307    @Native static final int kClipTypesIndex = 1;
308    @Native static final int kTextureImageIndex = 2;
309    @Native static final int kStrokeDashArrayIndex = 3;
310    @Native static final int kFontIndex = 4;
311    @Native static final int kFontPaintIndex = 5;
312    @Native static final int kColorArrayIndex = 6;
313    @Native static final int kFractionsArrayIndex = 7;
314
315    // possible state changes
316    @Native static final int kBoundsChangedBit = 1 << 0;
317    @Native static final int kBoundsNotChangedBit = ~kBoundsChangedBit;
318    @Native static final int kClipChangedBit = 1 << 1;
319    @Native static final int kClipNotChangedBit = ~kClipChangedBit;
320    @Native static final int kCTMChangedBit = 1 << 2;
321    @Native static final int kCTMNotChangedBit = ~kCTMChangedBit;
322    @Native static final int kColorChangedBit = 1 << 3;
323    @Native static final int kColorNotChangedBit = ~kColorChangedBit;
324    @Native static final int kCompositeChangedBit = 1 << 4;
325    @Native static final int kCompositeNotChangedBit = ~kCompositeChangedBit;
326    @Native static final int kStrokeChangedBit = 1 << 5;
327    @Native static final int kStrokeNotChangedBit = ~kStrokeChangedBit;
328    @Native static final int kHintsChangedBit = 1 << 6;
329    @Native static final int kHintsNotChangedBit = ~kHintsChangedBit;
330    @Native static final int kFontChangedBit = 1 << 7;
331    @Native static final int kFontNotChangedBit = ~kFontChangedBit;
332    @Native static final int kEverythingChangedFlag = 0xffffffff;
333
334    // possible color states
335    @Native static final int kColorSimple = 0;
336    @Native static final int kColorSystem = 1;
337    @Native static final int kColorGradient = 2;
338    @Native static final int kColorTexture = 3;
339    @Native static final int kColorLinearGradient = 4;
340    @Native static final int kColorRadialGradient = 5;
341
342    // possible gradient color states
343    @Native static final int kColorNonCyclic = 0;
344    @Native static final int kColorCyclic = 1;
345
346    // possible clip states
347    @Native static final int kClipRect = 0;
348    @Native static final int kClipShape = 1;
349
350    static int getRendererTypeForPrimitive(int primitiveType) {
351        switch (primitiveType) {
352            case kImage:
353                return kImage;
354            case kCopyArea:
355                return kCopyArea;
356            case kExternal:
357                return kExternal;
358            case kString:
359            case kGlyphs:
360            case kUnicodes:
361                return kText;
362            default:
363                return kPrimitive;
364        }
365    }
366
367    int fChangeFlag;
368    protected ByteBuffer fGraphicsStates = null;
369    IntBuffer fGraphicsStatesInt = null;
370    FloatBuffer fGraphicsStatesFloat = null;
371    LongBuffer fGraphicsStatesLong = null;
372    protected Object[] fGraphicsStatesObject = null;
373
374    Rectangle userBounds = new Rectangle();
375    float lastUserX = 0;
376    float lastUserY = 0;
377    float lastUserW = 0;
378    float lastUserH = 0;
379
380    void setUserBounds(SunGraphics2D sg2d, int x, int y, int width, int height) {
381        if ((lastUserX != x) || (lastUserY != y) || (lastUserW != width) || (lastUserH != height)) {
382            lastUserX = x;
383            lastUserY = y;
384            lastUserW = width;
385            lastUserH = height;
386
387            this.fGraphicsStatesInt.put(kBoundsXIndex, x);
388            this.fGraphicsStatesInt.put(kBoundsYIndex, y);
389            this.fGraphicsStatesInt.put(kBoundsWidthIndex, width);
390            this.fGraphicsStatesInt.put(kBoundsHeightIndex, height);
391
392            userBounds.setBounds(x, y, width, height);
393
394            this.fChangeFlag = (this.fChangeFlag | kBoundsChangedBit);
395        } else {
396            this.fChangeFlag = (this.fChangeFlag & kBoundsNotChangedBit);
397        }
398    }
399
400    static ByteBuffer getBufferOfSize(int size) {
401        ByteBuffer buffer = ByteBuffer.allocateDirect(size * 4);
402        buffer.order(ByteOrder.nativeOrder());
403        return buffer;
404    }
405
406    FloatBuffer clipCoordinatesArray = null;
407    IntBuffer clipTypesArray = null;
408    Shape lastClipShape = null;
409    float lastClipX = 0;
410    float lastClipY = 0;
411    float lastClipW = 0;
412    float lastClipH = 0;
413
414    void setupClip(SunGraphics2D sg2d) {
415        switch (sg2d.clipState) {
416            case SunGraphics2D.CLIP_DEVICE:
417            case SunGraphics2D.CLIP_RECTANGULAR: {
418                Region clip = sg2d.getCompClip();
419                float x = clip.getLoX();
420                float y = clip.getLoY();
421                float w = clip.getWidth();
422                float h = clip.getHeight();
423                if ((this.fGraphicsStatesInt.get(kClipStateIndex) != kClipRect) ||
424                        (x != lastClipX) ||
425                            (y != lastClipY) ||
426                                (w != lastClipW) ||
427                                    (h != lastClipH)) {
428                    this.fGraphicsStatesFloat.put(kClipXIndex, x);
429                    this.fGraphicsStatesFloat.put(kClipYIndex, y);
430                    this.fGraphicsStatesFloat.put(kClipWidthIndex, w);
431                    this.fGraphicsStatesFloat.put(kClipHeightIndex, h);
432
433                    lastClipX = x;
434                    lastClipY = y;
435                    lastClipW = w;
436                    lastClipH = h;
437
438                    this.fChangeFlag = (this.fChangeFlag | kClipChangedBit);
439                } else {
440                    this.fChangeFlag = (this.fChangeFlag & kClipNotChangedBit);
441                }
442                this.fGraphicsStatesInt.put(kClipStateIndex, kClipRect);
443                break;
444            }
445            case SunGraphics2D.CLIP_SHAPE: {
446                // if (lastClipShape != sg2d.usrClip) shapes are mutable!, and doing "equals" traverses all
447                // the coordinates, so we might as well do all of it anyhow
448                lastClipShape = sg2d.usrClip;
449
450                GeneralPath gp = null;
451
452                if (sg2d.usrClip instanceof GeneralPath) {
453                    gp = (GeneralPath) sg2d.usrClip;
454                } else {
455                    gp = new GeneralPath(sg2d.usrClip);
456                }
457
458                int shapeLength = getPathLength(gp);
459
460                if ((clipCoordinatesArray == null) || (clipCoordinatesArray.capacity() < (shapeLength * 6))) {
461                    clipCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a
462                                                                                             // max of 6 coordinates
463                }
464                if ((clipTypesArray == null) || (clipTypesArray.capacity() < shapeLength)) {
465                    clipTypesArray = getBufferOfSize(shapeLength).asIntBuffer();
466                }
467
468                int windingRule = getPathCoordinates(gp, clipCoordinatesArray, clipTypesArray);
469
470                this.fGraphicsStatesInt.put(kClipNumTypesIndex, clipTypesArray.position());
471                this.fGraphicsStatesInt.put(kClipNumCoordsIndex, clipCoordinatesArray.position());
472                this.fGraphicsStatesInt.put(kClipWindingRuleIndex, windingRule);
473                this.fGraphicsStatesObject[kClipTypesIndex] = clipTypesArray;
474                this.fGraphicsStatesObject[kClipCoordinatesIndex] = clipCoordinatesArray;
475
476                this.fChangeFlag = (this.fChangeFlag | kClipChangedBit);
477                this.fGraphicsStatesInt.put(kClipStateIndex, kClipShape);
478                break;
479            }
480        }
481
482    }
483
484    final double[] lastCTM = new double[6];
485    float lastCTMa = 0;
486    float lastCTMb = 0;
487    float lastCTMc = 0;
488    float lastCTMd = 0;
489    float lastCTMtx = 0;
490    float lastCTMty = 0;
491
492    void setupTransform(SunGraphics2D sg2d) {
493        sg2d.transform.getMatrix(lastCTM);
494
495        float a = (float) lastCTM[0];
496        float b = (float) lastCTM[1];
497        float c = (float) lastCTM[2];
498        float d = (float) lastCTM[3];
499        float tx = (float) lastCTM[4];
500        float ty = (float) lastCTM[5];
501        if (tx != lastCTMtx ||
502                ty != lastCTMty ||
503                    a != lastCTMa ||
504                        b != lastCTMb ||
505                            c != lastCTMc ||
506                                d != lastCTMd) {
507            this.fGraphicsStatesFloat.put(kCTMaIndex, a);
508            this.fGraphicsStatesFloat.put(kCTMbIndex, b);
509            this.fGraphicsStatesFloat.put(kCTMcIndex, c);
510            this.fGraphicsStatesFloat.put(kCTMdIndex, d);
511            this.fGraphicsStatesFloat.put(kCTMtxIndex, tx);
512            this.fGraphicsStatesFloat.put(kCTMtyIndex, ty);
513
514            lastCTMa = a;
515            lastCTMb = b;
516            lastCTMc = c;
517            lastCTMd = d;
518            lastCTMtx = tx;
519            lastCTMty = ty;
520
521            this.fChangeFlag = (this.fChangeFlag | kCTMChangedBit);
522        } else {
523            this.fChangeFlag = (this.fChangeFlag & kCTMNotChangedBit);
524        }
525    }
526
527    static AffineTransform sIdentityMatrix = new AffineTransform();
528    Paint lastPaint = null;
529    long lastPaintPtr = 0;
530    int lastPaintRGB = 0;
531    int lastPaintIndex = 0;
532    BufferedImage texturePaintImage = null;
533
534    void setGradientViaRasterPath(SunGraphics2D sg2d) {
535        if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint) || ((this.fChangeFlag & kBoundsChangedBit) != 0)) {
536            PaintContext context = sg2d.paint.createContext(sg2d.getDeviceColorModel(), userBounds, userBounds, sIdentityMatrix, sg2d.getRenderingHints());
537            WritableRaster raster = (WritableRaster) (context.getRaster(userBounds.x, userBounds.y, userBounds.width, userBounds.height));
538            ColorModel cm = context.getColorModel();
539            texturePaintImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
540
541            this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture);
542            this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth());
543            this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight());
544            this.fGraphicsStatesFloat.put(kColortxIndex, (float) userBounds.getX());
545            this.fGraphicsStatesFloat.put(kColortyIndex, (float) userBounds.getY());
546            this.fGraphicsStatesFloat.put(kColorsxIndex, 1.0f);
547            this.fGraphicsStatesFloat.put(kColorsyIndex, 1.0f);
548            this.fGraphicsStatesObject[kTextureImageIndex] = OSXOffScreenSurfaceData.createNewSurface(texturePaintImage);
549
550            this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
551        } else {
552            this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
553        }
554    }
555
556    void setupPaint(SunGraphics2D sg2d, int x, int y, int w, int h) {
557        if (sg2d.paint instanceof SystemColor) {
558            SystemColor color = (SystemColor) sg2d.paint;
559            int index = color.hashCode(); // depends on Color.java hashCode implementation! (returns "value" of color)
560            if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSystem) || (index != this.lastPaintIndex)) {
561                this.lastPaintIndex = index;
562
563                this.fGraphicsStatesInt.put(kColorStateIndex, kColorSystem);
564                this.fGraphicsStatesInt.put(kColorIndexValueIndex, index);
565
566                this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
567            } else {
568                this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
569            }
570        } else if (sg2d.paint instanceof Color) {
571            Color color = (Color) sg2d.paint;
572            int rgb = color.getRGB();
573            if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSimple) || (rgb != this.lastPaintRGB)) {
574                this.lastPaintRGB = rgb;
575
576                this.fGraphicsStatesInt.put(kColorStateIndex, kColorSimple);
577                this.fGraphicsStatesInt.put(kColorRGBValueIndex, rgb);
578
579                this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
580            } else {
581                this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
582            }
583        } else if (sg2d.paint instanceof GradientPaint) {
584            if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorGradient) || (lastPaint != sg2d.paint)) {
585                GradientPaint color = (GradientPaint) sg2d.paint;
586                this.fGraphicsStatesInt.put(kColorStateIndex, kColorGradient);
587                this.fGraphicsStatesInt.put(kColorRGBValue1Index, color.getColor1().getRGB());
588                this.fGraphicsStatesInt.put(kColorRGBValue2Index, color.getColor2().getRGB());
589                this.fGraphicsStatesInt.put(kColorIsCyclicIndex, (color.isCyclic()) ? kColorCyclic : kColorNonCyclic);
590                Point2D p = color.getPoint1();
591                this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX());
592                this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY());
593                p = color.getPoint2();
594                this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX());
595                this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY());
596
597                this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
598            } else {
599                this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
600            }
601        } else if (sg2d.paint instanceof LinearGradientPaint) {
602            LinearGradientPaint color = (LinearGradientPaint) sg2d.paint;
603            if (color.getCycleMethod() == LinearGradientPaint.CycleMethod.NO_CYCLE) {
604                if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorLinearGradient) || (lastPaint != sg2d.paint)) {
605
606                    this.fGraphicsStatesInt.put(kColorStateIndex, kColorLinearGradient);
607                    int numColor = color.getColors().length;
608                    int colorArray[] = new int[numColor];
609                    for (int i = 0; i < numColor; i++) {
610                        colorArray[i] = color.getColors()[i].getRGB();
611                    }
612                    this.fGraphicsStatesObject[kColorArrayIndex] = colorArray;
613
614                    int numFractions = color.getFractions().length;
615                    float fractionArray[] = new float[numFractions];
616                    for (int i = 0; i < numFractions; i++) {
617                        fractionArray[i] = color.getFractions()[i];
618                    }
619                    this.fGraphicsStatesObject[kFractionsArrayIndex] = color.getFractions();
620
621                    Point2D p = color.getStartPoint();
622                    this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX());
623                    this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY());
624                    p = color.getEndPoint();
625                    this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX());
626                    this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY());
627
628                    this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
629                } else {
630                    this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
631                }
632            } else {
633                setGradientViaRasterPath(sg2d);
634            }
635        } else if (sg2d.paint instanceof RadialGradientPaint) {
636            RadialGradientPaint color = (RadialGradientPaint) sg2d.paint;
637            if (color.getCycleMethod() == RadialGradientPaint.CycleMethod.NO_CYCLE) {
638                if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorRadialGradient) || (lastPaint != sg2d.paint)) {
639
640                    this.fGraphicsStatesInt.put(kColorStateIndex, kColorRadialGradient);
641                    int numColor = color.getColors().length;
642                    int colorArray[] = new int[numColor];
643                    for (int i = 0; i < numColor; i++) {
644                        colorArray[i] = color.getColors()[i].getRGB();
645                    }
646                    this.fGraphicsStatesObject[kColorArrayIndex] = colorArray;
647
648                    int numStops = color.getFractions().length;
649                    float stopsArray[] = new float[numStops];
650                    for (int i = 0; i < numStops; i++) {
651                        stopsArray[i] = color.getFractions()[i];
652                    }
653                    this.fGraphicsStatesObject[kFractionsArrayIndex] = color.getFractions();
654
655                    Point2D p = color.getFocusPoint();
656                    this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX());
657                    this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY());
658                    p = color.getCenterPoint();
659                    this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX());
660                    this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY());
661                    this.fGraphicsStatesFloat.put(kRadiusIndex,     color.getRadius());
662
663                    this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
664                } else {
665                    this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
666                }
667            } else {
668                setGradientViaRasterPath(sg2d);
669            }
670        } else if (sg2d.paint instanceof TexturePaint) {
671            if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint)) {
672                TexturePaint color = (TexturePaint) sg2d.paint;
673                this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture);
674                texturePaintImage = color.getImage();
675                SurfaceData textureSurfaceData = OSXOffScreenSurfaceData.createNewSurface(texturePaintImage);
676                this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth());
677                this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight());
678                Rectangle2D anchor = color.getAnchorRect();
679                this.fGraphicsStatesFloat.put(kColortxIndex, (float) anchor.getX());
680                this.fGraphicsStatesFloat.put(kColortyIndex, (float) anchor.getY());
681                this.fGraphicsStatesFloat.put(kColorsxIndex, (float) (anchor.getWidth() / texturePaintImage.getWidth()));
682                this.fGraphicsStatesFloat.put(kColorsyIndex, (float) (anchor.getHeight() / texturePaintImage.getHeight()));
683                this.fGraphicsStatesObject[kTextureImageIndex] = textureSurfaceData;
684
685                this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
686            } else {
687                this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
688            }
689        } else {
690            setGradientViaRasterPath(sg2d);
691        }
692        lastPaint = sg2d.paint;
693    }
694
695    Composite lastComposite;
696    int lastCompositeAlphaRule = 0;
697    float lastCompositeAlphaValue = 0;
698
699    void setupComposite(SunGraphics2D sg2d) {
700        Composite composite = sg2d.composite;
701
702        if (lastComposite != composite) {
703            lastComposite = composite;
704
705            // For composite state COMP_ISCOPY, COMP_XOR or COMP_CUSTOM set alpha compositor to COPY:
706            int alphaRule = AlphaComposite.SRC_OVER;
707            float alphaValue = 1.0f;
708
709            // For composite state COMP_ISCOPY composite could be null. If it's not (or composite state == COMP_ALPHA)
710            // get alpha compositor's values:
711            if ((sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) && (composite != null)) {
712                AlphaComposite alphaComposite = (AlphaComposite) composite;
713                alphaRule = alphaComposite.getRule();
714                alphaValue = alphaComposite.getAlpha();
715            }
716
717            // 2-17-03 VL: [Radar 3174922]
718            // For COMP_XOR and COMP_CUSTOM compositing modes we should be setting alphaRule = AlphaComposite.SRC
719            // which should map to kCGCompositeCopy.
720
721            if ((lastCompositeAlphaRule != alphaRule) || (lastCompositeAlphaValue != alphaValue)) {
722                this.fGraphicsStatesInt.put(kCompositeRuleIndex, alphaRule);
723                this.fGraphicsStatesFloat.put(kCompositeValueIndex, alphaValue);
724
725                lastCompositeAlphaRule = alphaRule;
726                lastCompositeAlphaValue = alphaValue;
727
728                this.fChangeFlag = (this.fChangeFlag | kCompositeChangedBit);
729            } else {
730                this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
731            }
732        } else {
733            this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
734        }
735    }
736
737    BasicStroke lastStroke = null;
738    static BasicStroke defaultBasicStroke = new BasicStroke();
739
740    void setupStroke(SunGraphics2D sg2d) {
741        BasicStroke stroke = defaultBasicStroke;
742
743        if (sg2d.stroke instanceof BasicStroke) {
744            stroke = (BasicStroke) sg2d.stroke;
745        }
746
747        if (lastStroke != stroke) {
748            this.fGraphicsStatesObject[kStrokeDashArrayIndex] = stroke.getDashArray();
749            this.fGraphicsStatesFloat.put(kStrokeDashPhaseIndex, stroke.getDashPhase());
750            this.fGraphicsStatesInt.put(kStrokeCapIndex, stroke.getEndCap());
751            this.fGraphicsStatesInt.put(kStrokeJoinIndex, stroke.getLineJoin());
752            this.fGraphicsStatesFloat.put(kStrokeWidthIndex, stroke.getLineWidth());
753            this.fGraphicsStatesFloat.put(kStrokeLimitIndex, stroke.getMiterLimit());
754
755            this.fChangeFlag = (this.fChangeFlag | kStrokeChangedBit);
756
757            lastStroke = stroke;
758        } else {
759            this.fChangeFlag = (this.fChangeFlag & kStrokeNotChangedBit);
760        }
761    }
762
763    Font lastFont;
764
765    void setupFont(Font font, Paint paint) {
766        if (font == null) { return; }
767
768        // We have to setup the kFontPaintIndex if we have changed the color so we added the last
769        // test to see if the color has changed - needed for complex strings
770        // see Radar 3368674
771        if ((font != lastFont) || ((this.fChangeFlag & kColorChangedBit) != 0)) {
772            this.fGraphicsStatesObject[kFontIndex] = font;
773            this.fGraphicsStatesObject[kFontPaintIndex] = paint;
774
775            this.fChangeFlag = (this.fChangeFlag | kFontChangedBit);
776
777            lastFont = font;
778        } else {
779            this.fChangeFlag = (this.fChangeFlag & kFontNotChangedBit);
780        }
781    }
782
783    void setupRenderingHints(SunGraphics2D sg2d) {
784        boolean hintsChanged = false;
785
786        // Significant for draw, fill, text, and image ops:
787        int antialiasHint = sg2d.antialiasHint;
788        if (this.fGraphicsStatesInt.get(kHintsAntialiasIndex) != antialiasHint) {
789            this.fGraphicsStatesInt.put(kHintsAntialiasIndex, antialiasHint);
790            hintsChanged = true;
791        }
792
793        // Significant only for text ops:
794        int textAntialiasHint = sg2d.textAntialiasHint;
795        if (this.fGraphicsStatesInt.get(kHintsTextAntialiasIndex) != textAntialiasHint) {
796            this.fGraphicsStatesInt.put(kHintsTextAntialiasIndex, textAntialiasHint);
797            hintsChanged = true;
798        }
799
800        // Significant only for text ops:
801        int fractionalMetricsHint = sg2d.fractionalMetricsHint;
802        if (this.fGraphicsStatesInt.get(kHintsFractionalMetricsIndex) != fractionalMetricsHint) {
803            this.fGraphicsStatesInt.put(kHintsFractionalMetricsIndex, fractionalMetricsHint);
804            hintsChanged = true;
805        }
806
807        // Significant only for image ops:
808        int renderHint = sg2d.renderHint;
809        if (this.fGraphicsStatesInt.get(kHintsRenderingIndex) != renderHint) {
810            this.fGraphicsStatesInt.put(kHintsRenderingIndex, renderHint);
811            hintsChanged = true;
812        }
813
814        // Significant only for image ops:
815        Object hintValue = sg2d.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
816        int interpolationHint = (hintValue != null ? ((SunHints.Value) hintValue).getIndex() : -1);
817        if (this.fGraphicsStatesInt.get(kHintsInterpolationIndex) != interpolationHint) {
818            this.fGraphicsStatesInt.put(kHintsInterpolationIndex, interpolationHint);
819            hintsChanged = true;
820        }
821
822        if (hintsChanged) {
823            this.fChangeFlag = (this.fChangeFlag | kHintsChangedBit);
824        } else {
825            this.fChangeFlag = (this.fChangeFlag & kHintsNotChangedBit);
826        }
827    }
828
829    SunGraphics2D sg2dCurrent = null;
830    Thread threadCurrent = null;
831
832    void setupGraphicsState(SunGraphics2D sg2d, int primitiveType) {
833        setupGraphicsState(sg2d, primitiveType, sg2d.font, 0, 0, fBounds.width, fBounds.height); // deviceBounds into userBounds
834    }
835
836    void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, int x, int y, int w, int h) {
837        setupGraphicsState(sg2d, primitiveType, sg2d.font, x, y, w, h);
838    }
839
840    // the method below is overriden by CPeerSurface to check the last peer used to draw
841    // if the peer changed we finish lazy drawing
842    void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, Font font, int x, int y, int w, int h) {
843        this.fChangeFlag = 0;
844
845        setUserBounds(sg2d, x, y, w, h);
846
847        Thread thread = Thread.currentThread();
848        if ((this.sg2dCurrent != sg2d) || (this.threadCurrent != thread)) {
849            this.sg2dCurrent = sg2d;
850            this.threadCurrent = thread;
851
852            setupClip(sg2d);
853            setupTransform(sg2d);
854            setupPaint(sg2d, x, y, w, h);
855            setupComposite(sg2d);
856            setupStroke(sg2d);
857            setupFont(font, sg2d.paint);
858            setupRenderingHints(sg2d);
859
860            this.fChangeFlag = kEverythingChangedFlag;
861        } else {
862            int rendererType = getRendererTypeForPrimitive(primitiveType);
863
864            setupClip(sg2d);
865            setupTransform(sg2d);
866
867            if (rendererType != kCopyArea) {
868                setupComposite(sg2d);
869                setupRenderingHints(sg2d);
870
871                if ((rendererType != kImage)) {
872                    setupPaint(sg2d, x, y, w, h);
873                    setupStroke(sg2d);
874                }
875                if (rendererType != kPrimitive) {
876                    setupFont(font, sg2d.paint);
877                }
878
879            }
880        }
881
882        this.fGraphicsStatesInt.put(kChangeFlagIndex, this.fChangeFlag);
883    }
884
885    boolean isCustomPaint(SunGraphics2D sg2d) {
886        if ((sg2d.paint instanceof Color) || (sg2d.paint instanceof SystemColor) || (sg2d.paint instanceof GradientPaint) || (sg2d.paint instanceof TexturePaint)) { return false; }
887
888        return true;
889    }
890
891    final float[] segmentCoordinatesArray = new float[6];
892
893    int getPathLength(GeneralPath gp) {
894        int length = 0;
895
896        PathIterator pi = gp.getPathIterator(null);
897        while (pi.isDone() == false) {
898            pi.next();
899            length++;
900        }
901
902        return length;
903    }
904
905    int getPathCoordinates(GeneralPath gp, FloatBuffer coordinates, IntBuffer types) {
906        // System.err.println("getPathCoordinates");
907        boolean skip = false;
908
909        coordinates.clear();
910        types.clear();
911
912        int type;
913
914        PathIterator pi = gp.getPathIterator(null);
915        while (pi.isDone() == false) {
916            skip = false;
917            type = pi.currentSegment(segmentCoordinatesArray);
918
919            switch (type) {
920                case PathIterator.SEG_MOVETO:
921                    // System.err.println(" SEG_MOVETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
922                    if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
923                            segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
924                        coordinates.put(segmentCoordinatesArray[0]);
925                        coordinates.put(segmentCoordinatesArray[1]);
926                    } else {
927                        skip = true;
928                    }
929                    break;
930                case PathIterator.SEG_LINETO:
931                    // System.err.println(" SEG_LINETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
932                    if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
933                            segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
934                        coordinates.put(segmentCoordinatesArray[0]);
935                        coordinates.put(segmentCoordinatesArray[1]);
936                    } else {
937                        skip = true;
938                    }
939                    break;
940                case PathIterator.SEG_QUADTO:
941                    // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+")");
942                    if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
943                            segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
944                            segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
945                            segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND) {
946                        coordinates.put(segmentCoordinatesArray[0]);
947                        coordinates.put(segmentCoordinatesArray[1]);
948                        coordinates.put(segmentCoordinatesArray[2]);
949                        coordinates.put(segmentCoordinatesArray[3]);
950                    } else {
951                        skip = true;
952                    }
953                    break;
954                case PathIterator.SEG_CUBICTO:
955                    // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+"), ("+segmentCoordinatesArray[4]+", "+segmentCoordinatesArray[5]+")");
956                    if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
957                            segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
958                            segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
959                            segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND &&
960                            segmentCoordinatesArray[4] < UPPER_BND && segmentCoordinatesArray[4] > LOWER_BND &&
961                            segmentCoordinatesArray[5] < UPPER_BND && segmentCoordinatesArray[5] > LOWER_BND) {
962                        coordinates.put(segmentCoordinatesArray[0]);
963                        coordinates.put(segmentCoordinatesArray[1]);
964                        coordinates.put(segmentCoordinatesArray[2]);
965                        coordinates.put(segmentCoordinatesArray[3]);
966                        coordinates.put(segmentCoordinatesArray[4]);
967                        coordinates.put(segmentCoordinatesArray[5]);
968                    } else {
969                        skip = true;
970                    }
971                    break;
972                case PathIterator.SEG_CLOSE:
973                    // System.err.println(" SEG_CLOSE");
974                    break;
975            }
976
977            if (!skip) {
978                types.put(type);
979            }
980
981            pi.next();
982        }
983
984        return pi.getWindingRule();
985    }
986
987    public void doLine(CRenderer renderer, SunGraphics2D sg2d, float x1, float y1, float x2, float y2) {
988        // System.err.println("-- doLine x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2+" paint="+sg2d.paint);
989        setupGraphicsState(sg2d, kLine, sg2d.font, 0, 0, fBounds.width, fBounds.height);
990        renderer.doLine(this, x1, y1, x2, y2);
991    }
992
993    public void doRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
994        // System.err.println("-- doRect x="+x+" y="+y+" w="+width+" h="+height+" isfill="+isfill+" paint="+sg2d.paint);
995        if ((isfill) && (isCustomPaint(sg2d))) {
996            setupGraphicsState(sg2d, kRect, (int) x, (int) y, (int) width, (int) height);
997        } else {
998            setupGraphicsState(sg2d, kRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
999        }
1000        renderer.doRect(this, x, y, width, height, isfill);
1001    }
1002
1003    public void doRoundRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float arcW, float arcH, boolean isfill) {
1004        // System.err.println("--- doRoundRect");
1005        if ((isfill) && (isCustomPaint(sg2d))) {
1006            setupGraphicsState(sg2d, kRoundRect, (int) x, (int) y, (int) width, (int) height);
1007        } else {
1008            setupGraphicsState(sg2d, kRoundRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1009        }
1010        renderer.doRoundRect(this, x, y, width, height, arcW, arcH, isfill);
1011    }
1012
1013    public void doOval(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
1014        // System.err.println("--- doOval");
1015        if ((isfill) && (isCustomPaint(sg2d))) {
1016            setupGraphicsState(sg2d, kOval, (int) x, (int) y, (int) width, (int) height);
1017        } else {
1018            setupGraphicsState(sg2d, kOval, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1019        }
1020        renderer.doOval(this, x, y, width, height, isfill);
1021    }
1022
1023    public void doArc(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float startAngle, float arcAngle, int type, boolean isfill) {
1024        // System.err.println("--- doArc");
1025        if ((isfill) && (isCustomPaint(sg2d))) {
1026            setupGraphicsState(sg2d, kArc, (int) x, (int) y, (int) width, (int) height);
1027        } else {
1028            setupGraphicsState(sg2d, kArc, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1029        }
1030
1031        renderer.doArc(this, x, y, width, height, startAngle, arcAngle, type, isfill);
1032    }
1033
1034    public void doPolygon(CRenderer renderer, SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints, boolean ispolygon, boolean isfill) {
1035        // System.err.println("--- doPolygon");
1036
1037        if ((isfill) && (isCustomPaint(sg2d))) {
1038            int minx = xpoints[0];
1039            int miny = ypoints[0];
1040            int maxx = minx;
1041            int maxy = miny;
1042            for (int i = 1; i < npoints; i++) {
1043                int x = xpoints[i];
1044                if (x < minx) {
1045                    minx = x;
1046                } else if (x > maxx) {
1047                    maxx = x;
1048                }
1049
1050                int y = ypoints[i];
1051                if (y < miny) {
1052                    miny = y;
1053                } else if (y > maxy) {
1054                    maxy = y;
1055                }
1056            }
1057            setupGraphicsState(sg2d, kPolygon, minx, miny, maxx - minx, maxy - miny);
1058        } else {
1059            setupGraphicsState(sg2d, kPolygon, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1060        }
1061        renderer.doPoly(this, xpoints, ypoints, npoints, ispolygon, isfill);
1062    }
1063
1064    FloatBuffer shapeCoordinatesArray = null;
1065    IntBuffer shapeTypesArray = null;
1066
1067    public void drawfillShape(CRenderer renderer, SunGraphics2D sg2d, GeneralPath gp, boolean isfill, boolean shouldApplyOffset) {
1068        // System.err.println("--- drawfillShape");
1069
1070        if ((isfill) && (isCustomPaint(sg2d))) {
1071            Rectangle bounds = gp.getBounds();
1072            setupGraphicsState(sg2d, kShape, bounds.x, bounds.y, bounds.width, bounds.height);
1073        } else {
1074            setupGraphicsState(sg2d, kShape, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1075        }
1076
1077        int shapeLength = getPathLength(gp);
1078
1079        if ((shapeCoordinatesArray == null) || (shapeCoordinatesArray.capacity() < (shapeLength * 6))) {
1080            shapeCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a max of 6
1081                                                                                      // coordinates
1082        }
1083        if ((shapeTypesArray == null) || (shapeTypesArray.capacity() < shapeLength)) {
1084            shapeTypesArray = getBufferOfSize(shapeLength).asIntBuffer();
1085        }
1086
1087        int windingRule = getPathCoordinates(gp, shapeCoordinatesArray, shapeTypesArray);
1088
1089        renderer.doShape(this, shapeLength, shapeCoordinatesArray, shapeTypesArray, windingRule, isfill, shouldApplyOffset);
1090    }
1091
1092    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) {
1093        // System.err.println("--- blitImage sx="+sx+", sy="+sy+", sw="+sw+", sh="+sh+", img="+img);
1094        OSXOffScreenSurfaceData osxsd = (OSXOffScreenSurfaceData) img;
1095        synchronized (osxsd.getLockObject()) {
1096            int w = osxsd.bim.getWidth();
1097            int h = osxsd.bim.getHeight();
1098
1099            // the image itself can have outstanding graphics primitives that might need to be flushed
1100            setupGraphicsState(sg2d, kImage, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1101
1102            // 04/06/04 cmc: radr://3612381 Graphics.drawImage ignores bgcolor parameter
1103            if (bgColor != null) {
1104                img = osxsd.getCopyWithBgColor(bgColor);
1105            }
1106
1107            renderer.doImage(this, img, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh);
1108        }
1109    }
1110
1111    public interface CGContextDrawable {
1112        public void drawIntoCGContext(final long cgContext);
1113    }
1114
1115    public void drawString(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, String str, double x, double y) {
1116        // System.err.println("--- drawString str=\""+str+"\"");
1117        // see <rdar://problem/3825795>. We don't want to call anything if the string is empty!
1118        if (str.length() == 0) { return; }
1119
1120        setupGraphicsState(sg2d, kString, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1121        renderer.doDrawString(this, nativeStrikePtr, str, x, y);
1122    }
1123
1124    public void drawGlyphs(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, GlyphVector gv, float x, float y) {
1125        // System.err.println("--- drawGlyphs");
1126        setupGraphicsState(sg2d, kGlyphs, gv.getFont(), 0, 0, fBounds.width, fBounds.height);
1127        renderer.doDrawGlyphs(this, nativeStrikePtr, gv, x, y);
1128    }
1129
1130    public void drawUnicodes(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, char unicodes[], int offset, int length, float x, float y) {
1131        // System.err.println("--- drawUnicodes "+(new String(unicodes, offset, length)));
1132        setupGraphicsState(sg2d, kUnicodes, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1133        if (length == 1) {
1134            renderer.doOneUnicode(this, nativeStrikePtr, unicodes[offset], x, y);
1135        } else {
1136            renderer.doUnicodes(this, nativeStrikePtr, unicodes, offset, length, x, y);
1137        }
1138    }
1139
1140    // used by copyArea:
1141
1142    Rectangle srcCopyAreaRect = new Rectangle();
1143    Rectangle dstCopyAreaRect = new Rectangle();
1144    Rectangle finalCopyAreaRect = new Rectangle();
1145    Rectangle copyAreaBounds = new Rectangle();
1146
1147    void intersection(Rectangle r1, Rectangle r2, Rectangle r3) {
1148        // this code is taken from Rectangle.java (modified to put results in r3)
1149        int tx1 = r1.x;
1150        int ty1 = r1.y;
1151        long tx2 = tx1 + r1.width;
1152        long ty2 = ty1 + r1.height;
1153
1154        int rx1 = r2.x;
1155        int ry1 = r2.y;
1156        long rx2 = rx1 + r2.width;
1157        long ry2 = ry1 + r2.height;
1158
1159        if (tx1 < rx1) tx1 = rx1;
1160        if (ty1 < ry1) ty1 = ry1;
1161        if (tx2 > rx2) tx2 = rx2;
1162        if (ty2 > ry2) ty2 = ry2;
1163
1164        tx2 -= tx1;
1165        ty2 -= ty1;
1166
1167        // tx2,ty2 will never overflow (they will never be
1168        // larger than the smallest of the two source w,h)
1169        // they might underflow, though...
1170        if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE;
1171        if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE;
1172
1173        r3.setBounds(tx1, ty1, (int) tx2, (int) ty2);
1174    }
1175
1176    /**
1177     * Clips the copy area to the heavyweight bounds and returns the clipped rectangle.
1178     * The returned clipped rectangle is in the coordinate space of the surface.
1179     */
1180    protected Rectangle clipCopyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) {
1181        // we need to clip against the heavyweight bounds
1182        copyAreaBounds.setBounds(sg2d.devClip.getLoX(), sg2d.devClip.getLoY(), sg2d.devClip.getWidth(), sg2d.devClip.getHeight());
1183
1184        // clip src rect
1185        srcCopyAreaRect.setBounds(x, y, w, h);
1186        intersection(srcCopyAreaRect, copyAreaBounds, srcCopyAreaRect);
1187        if ((srcCopyAreaRect.width <= 0) || (srcCopyAreaRect.height <= 0)) {
1188            // src rect outside bounds
1189            return null;
1190        }
1191
1192        // clip dst rect
1193        dstCopyAreaRect.setBounds(srcCopyAreaRect.x + dx, srcCopyAreaRect.y + dy, srcCopyAreaRect.width, srcCopyAreaRect.height);
1194        intersection(dstCopyAreaRect, copyAreaBounds, dstCopyAreaRect);
1195        if ((dstCopyAreaRect.width <= 0) || (dstCopyAreaRect.height <= 0)) {
1196            // dst rect outside clip
1197            return null;
1198        }
1199
1200        x = dstCopyAreaRect.x - dx;
1201        y = dstCopyAreaRect.y - dy;
1202        w = dstCopyAreaRect.width;
1203        h = dstCopyAreaRect.height;
1204
1205        finalCopyAreaRect.setBounds(x, y, w, h);
1206
1207        return finalCopyAreaRect;
1208    }
1209
1210    // <rdar://3785539> We only need to mark dirty on screen surfaces. This method is
1211    // marked as protected and it is intended for subclasses to override if they need to
1212    // be notified when the surface is dirtied. See CPeerSurfaceData.markDirty() for implementation.
1213    // We don't do anything for buffered images.
1214    protected void markDirty(boolean markAsDirty) {
1215        // do nothing by default
1216    }
1217
1218    // LazyDrawing optimization implementation:
1219
1220    @Override
1221    public boolean canRenderLCDText(SunGraphics2D sg2d) {
1222        if (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY &&
1223                sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR &&
1224                sg2d.clipState <= SunGraphics2D.CLIP_RECTANGULAR &&
1225                // sg2d.surfaceData.getTransparency() == Transparency.OPAQUE &&
1226                // This last test is a workaround until we fix loop selection
1227                // in the pipe validation
1228                sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { return true; }
1229        return false; /* for now - in the future we may want to search */
1230    }
1231
1232    public static boolean IsSimpleColor(Object c) {
1233        return ((c instanceof Color) || (c instanceof SystemColor) || (c instanceof javax.swing.plaf.ColorUIResource));
1234    }
1235
1236    static {
1237        if ((kColorPointerIndex % 2) != 0) {
1238            System.err.println("kColorPointerIndex=" + kColorPointerIndex + " is NOT aligned for 64 bit");
1239            System.exit(0);
1240        }
1241    }
1242}
1243