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