Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java
38918 views
1
/*
2
* Copyright (c) 2007, 2013, 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
26
package sun.java2d.pisces;
27
28
import java.awt.Shape;
29
import java.awt.BasicStroke;
30
import java.awt.geom.Path2D;
31
import java.awt.geom.AffineTransform;
32
import java.awt.geom.PathIterator;
33
34
import sun.awt.geom.PathConsumer2D;
35
import sun.java2d.pipe.Region;
36
import sun.java2d.pipe.RenderingEngine;
37
import sun.java2d.pipe.AATileGenerator;
38
39
public class PiscesRenderingEngine extends RenderingEngine {
40
private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA}
41
42
/**
43
* Create a widened path as specified by the parameters.
44
* <p>
45
* The specified {@code src} {@link Shape} is widened according
46
* to the specified attribute parameters as per the
47
* {@link BasicStroke} specification.
48
*
49
* @param src the source path to be widened
50
* @param width the width of the widened path as per {@code BasicStroke}
51
* @param caps the end cap decorations as per {@code BasicStroke}
52
* @param join the segment join decorations as per {@code BasicStroke}
53
* @param miterlimit the miter limit as per {@code BasicStroke}
54
* @param dashes the dash length array as per {@code BasicStroke}
55
* @param dashphase the initial dash phase as per {@code BasicStroke}
56
* @return the widened path stored in a new {@code Shape} object
57
* @since 1.7
58
*/
59
public Shape createStrokedShape(Shape src,
60
float width,
61
int caps,
62
int join,
63
float miterlimit,
64
float dashes[],
65
float dashphase)
66
{
67
final Path2D p2d = new Path2D.Float();
68
69
strokeTo(src,
70
null,
71
width,
72
NormMode.OFF,
73
caps,
74
join,
75
miterlimit,
76
dashes,
77
dashphase,
78
new PathConsumer2D() {
79
public void moveTo(float x0, float y0) {
80
p2d.moveTo(x0, y0);
81
}
82
public void lineTo(float x1, float y1) {
83
p2d.lineTo(x1, y1);
84
}
85
public void closePath() {
86
p2d.closePath();
87
}
88
public void pathDone() {}
89
public void curveTo(float x1, float y1,
90
float x2, float y2,
91
float x3, float y3) {
92
p2d.curveTo(x1, y1, x2, y2, x3, y3);
93
}
94
public void quadTo(float x1, float y1, float x2, float y2) {
95
p2d.quadTo(x1, y1, x2, y2);
96
}
97
public long getNativeConsumer() {
98
throw new InternalError("Not using a native peer");
99
}
100
});
101
return p2d;
102
}
103
104
/**
105
* Sends the geometry for a widened path as specified by the parameters
106
* to the specified consumer.
107
* <p>
108
* The specified {@code src} {@link Shape} is widened according
109
* to the parameters specified by the {@link BasicStroke} object.
110
* Adjustments are made to the path as appropriate for the
111
* {@link VALUE_STROKE_NORMALIZE} hint if the {@code normalize}
112
* boolean parameter is true.
113
* Adjustments are made to the path as appropriate for the
114
* {@link VALUE_ANTIALIAS_ON} hint if the {@code antialias}
115
* boolean parameter is true.
116
* <p>
117
* The geometry of the widened path is forwarded to the indicated
118
* {@link PathConsumer2D} object as it is calculated.
119
*
120
* @param src the source path to be widened
121
* @param bs the {@code BasicSroke} object specifying the
122
* decorations to be applied to the widened path
123
* @param normalize indicates whether stroke normalization should
124
* be applied
125
* @param antialias indicates whether or not adjustments appropriate
126
* to antialiased rendering should be applied
127
* @param consumer the {@code PathConsumer2D} instance to forward
128
* the widened geometry to
129
* @since 1.7
130
*/
131
public void strokeTo(Shape src,
132
AffineTransform at,
133
BasicStroke bs,
134
boolean thin,
135
boolean normalize,
136
boolean antialias,
137
final PathConsumer2D consumer)
138
{
139
NormMode norm = (normalize) ?
140
((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
141
: NormMode.OFF;
142
strokeTo(src, at, bs, thin, norm, antialias, consumer);
143
}
144
145
void strokeTo(Shape src,
146
AffineTransform at,
147
BasicStroke bs,
148
boolean thin,
149
NormMode normalize,
150
boolean antialias,
151
PathConsumer2D pc2d)
152
{
153
float lw;
154
if (thin) {
155
if (antialias) {
156
lw = userSpaceLineWidth(at, 0.5f);
157
} else {
158
lw = userSpaceLineWidth(at, 1.0f);
159
}
160
} else {
161
lw = bs.getLineWidth();
162
}
163
strokeTo(src,
164
at,
165
lw,
166
normalize,
167
bs.getEndCap(),
168
bs.getLineJoin(),
169
bs.getMiterLimit(),
170
bs.getDashArray(),
171
bs.getDashPhase(),
172
pc2d);
173
}
174
175
private float userSpaceLineWidth(AffineTransform at, float lw) {
176
177
double widthScale;
178
179
if ((at.getType() & (AffineTransform.TYPE_GENERAL_TRANSFORM |
180
AffineTransform.TYPE_GENERAL_SCALE)) != 0) {
181
widthScale = Math.sqrt(at.getDeterminant());
182
} else {
183
/* First calculate the "maximum scale" of this transform. */
184
double A = at.getScaleX(); // m00
185
double C = at.getShearX(); // m01
186
double B = at.getShearY(); // m10
187
double D = at.getScaleY(); // m11
188
189
/*
190
* Given a 2 x 2 affine matrix [ A B ] such that
191
* [ C D ]
192
* v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
193
* find the maximum magnitude (norm) of the vector v'
194
* with the constraint (x^2 + y^2 = 1).
195
* The equation to maximize is
196
* |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
197
* or |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
198
* Since sqrt is monotonic we can maximize |v'|^2
199
* instead and plug in the substitution y = sqrt(1 - x^2).
200
* Trigonometric equalities can then be used to get
201
* rid of most of the sqrt terms.
202
*/
203
204
double EA = A*A + B*B; // x^2 coefficient
205
double EB = 2*(A*C + B*D); // xy coefficient
206
double EC = C*C + D*D; // y^2 coefficient
207
208
/*
209
* There is a lot of calculus omitted here.
210
*
211
* Conceptually, in the interests of understanding the
212
* terms that the calculus produced we can consider
213
* that EA and EC end up providing the lengths along
214
* the major axes and the hypot term ends up being an
215
* adjustment for the additional length along the off-axis
216
* angle of rotated or sheared ellipses as well as an
217
* adjustment for the fact that the equation below
218
* averages the two major axis lengths. (Notice that
219
* the hypot term contains a part which resolves to the
220
* difference of these two axis lengths in the absence
221
* of rotation.)
222
*
223
* In the calculus, the ratio of the EB and (EA-EC) terms
224
* ends up being the tangent of 2*theta where theta is
225
* the angle that the long axis of the ellipse makes
226
* with the horizontal axis. Thus, this equation is
227
* calculating the length of the hypotenuse of a triangle
228
* along that axis.
229
*/
230
231
double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
232
/* sqrt omitted, compare to squared limits below. */
233
double widthsquared = ((EA + EC + hypot)/2.0);
234
235
widthScale = Math.sqrt(widthsquared);
236
}
237
238
return (float) (lw / widthScale);
239
}
240
241
void strokeTo(Shape src,
242
AffineTransform at,
243
float width,
244
NormMode normalize,
245
int caps,
246
int join,
247
float miterlimit,
248
float dashes[],
249
float dashphase,
250
PathConsumer2D pc2d)
251
{
252
// We use strokerat and outat so that in Stroker and Dasher we can work only
253
// with the pre-transformation coordinates. This will repeat a lot of
254
// computations done in the path iterator, but the alternative is to
255
// work with transformed paths and compute untransformed coordinates
256
// as needed. This would be faster but I do not think the complexity
257
// of working with both untransformed and transformed coordinates in
258
// the same code is worth it.
259
// However, if a path's width is constant after a transformation,
260
// we can skip all this untransforming.
261
262
// If normalization is off we save some transformations by not
263
// transforming the input to pisces. Instead, we apply the
264
// transformation after the path processing has been done.
265
// We can't do this if normalization is on, because it isn't a good
266
// idea to normalize before the transformation is applied.
267
AffineTransform strokerat = null;
268
AffineTransform outat = null;
269
270
PathIterator pi = null;
271
272
if (at != null && !at.isIdentity()) {
273
final double a = at.getScaleX();
274
final double b = at.getShearX();
275
final double c = at.getShearY();
276
final double d = at.getScaleY();
277
final double det = a * d - c * b;
278
if (Math.abs(det) <= 2 * Float.MIN_VALUE) {
279
// this rendering engine takes one dimensional curves and turns
280
// them into 2D shapes by giving them width.
281
// However, if everything is to be passed through a singular
282
// transformation, these 2D shapes will be squashed down to 1D
283
// again so, nothing can be drawn.
284
285
// Every path needs an initial moveTo and a pathDone. If these
286
// are not there this causes a SIGSEGV in libawt.so (at the time
287
// of writing of this comment (September 16, 2010)). Actually,
288
// I am not sure if the moveTo is necessary to avoid the SIGSEGV
289
// but the pathDone is definitely needed.
290
pc2d.moveTo(0, 0);
291
pc2d.pathDone();
292
return;
293
}
294
295
// If the transform is a constant multiple of an orthogonal transformation
296
// then every length is just multiplied by a constant, so we just
297
// need to transform input paths to stroker and tell stroker
298
// the scaled width. This condition is satisfied if
299
// a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
300
// leave a bit of room for error.
301
if (nearZero(a*b + c*d, 2) && nearZero(a*a+c*c - (b*b+d*d), 2)) {
302
double scale = Math.sqrt(a*a + c*c);
303
if (dashes != null) {
304
dashes = java.util.Arrays.copyOf(dashes, dashes.length);
305
for (int i = 0; i < dashes.length; i++) {
306
dashes[i] = (float)(scale * dashes[i]);
307
}
308
dashphase = (float)(scale * dashphase);
309
}
310
width = (float)(scale * width);
311
pi = src.getPathIterator(at);
312
if (normalize != NormMode.OFF) {
313
pi = new NormalizingPathIterator(pi, normalize);
314
}
315
// by now strokerat == null && outat == null. Input paths to
316
// stroker (and maybe dasher) will have the full transform at
317
// applied to them and nothing will happen to the output paths.
318
} else {
319
if (normalize != NormMode.OFF) {
320
strokerat = at;
321
pi = src.getPathIterator(at);
322
pi = new NormalizingPathIterator(pi, normalize);
323
// by now strokerat == at && outat == null. Input paths to
324
// stroker (and maybe dasher) will have the full transform at
325
// applied to them, then they will be normalized, and then
326
// the inverse of *only the non translation part of at* will
327
// be applied to the normalized paths. This won't cause problems
328
// in stroker, because, suppose at = T*A, where T is just the
329
// translation part of at, and A is the rest. T*A has already
330
// been applied to Stroker/Dasher's input. Then Ainv will be
331
// applied. Ainv*T*A is not equal to T, but it is a translation,
332
// which means that none of stroker's assumptions about its
333
// input will be violated. After all this, A will be applied
334
// to stroker's output.
335
} else {
336
outat = at;
337
pi = src.getPathIterator(null);
338
// outat == at && strokerat == null. This is because if no
339
// normalization is done, we can just apply all our
340
// transformations to stroker's output.
341
}
342
}
343
} else {
344
// either at is null or it's the identity. In either case
345
// we don't transform the path.
346
pi = src.getPathIterator(null);
347
if (normalize != NormMode.OFF) {
348
pi = new NormalizingPathIterator(pi, normalize);
349
}
350
}
351
352
// by now, at least one of outat and strokerat will be null. Unless at is not
353
// a constant multiple of an orthogonal transformation, they will both be
354
// null. In other cases, outat == at if normalization is off, and if
355
// normalization is on, strokerat == at.
356
pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat);
357
pc2d = TransformingPathConsumer2D.deltaTransformConsumer(pc2d, strokerat);
358
pc2d = new Stroker(pc2d, width, caps, join, miterlimit);
359
if (dashes != null) {
360
pc2d = new Dasher(pc2d, dashes, dashphase);
361
}
362
pc2d = TransformingPathConsumer2D.inverseDeltaTransformConsumer(pc2d, strokerat);
363
pathTo(pi, pc2d);
364
}
365
366
private static boolean nearZero(double num, int nulps) {
367
return Math.abs(num) < nulps * Math.ulp(num);
368
}
369
370
private static class NormalizingPathIterator implements PathIterator {
371
372
private final PathIterator src;
373
374
// the adjustment applied to the current position.
375
private float curx_adjust, cury_adjust;
376
// the adjustment applied to the last moveTo position.
377
private float movx_adjust, movy_adjust;
378
379
// constants used in normalization computations
380
private final float lval, rval;
381
382
NormalizingPathIterator(PathIterator src, NormMode mode) {
383
this.src = src;
384
switch (mode) {
385
case ON_NO_AA:
386
// round to nearest (0.25, 0.25) pixel
387
lval = rval = 0.25f;
388
break;
389
case ON_WITH_AA:
390
// round to nearest pixel center
391
lval = 0f;
392
rval = 0.5f;
393
break;
394
case OFF:
395
throw new InternalError("A NormalizingPathIterator should " +
396
"not be created if no normalization is being done");
397
default:
398
throw new InternalError("Unrecognized normalization mode");
399
}
400
}
401
402
public int currentSegment(float[] coords) {
403
int type = src.currentSegment(coords);
404
405
int lastCoord;
406
switch(type) {
407
case PathIterator.SEG_CUBICTO:
408
lastCoord = 4;
409
break;
410
case PathIterator.SEG_QUADTO:
411
lastCoord = 2;
412
break;
413
case PathIterator.SEG_LINETO:
414
case PathIterator.SEG_MOVETO:
415
lastCoord = 0;
416
break;
417
case PathIterator.SEG_CLOSE:
418
// we don't want to deal with this case later. We just exit now
419
curx_adjust = movx_adjust;
420
cury_adjust = movy_adjust;
421
return type;
422
default:
423
throw new InternalError("Unrecognized curve type");
424
}
425
426
// normalize endpoint
427
float x_adjust = (float)Math.floor(coords[lastCoord] + lval) +
428
rval - coords[lastCoord];
429
float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) +
430
rval - coords[lastCoord + 1];
431
432
coords[lastCoord ] += x_adjust;
433
coords[lastCoord + 1] += y_adjust;
434
435
// now that the end points are done, normalize the control points
436
switch(type) {
437
case PathIterator.SEG_CUBICTO:
438
coords[0] += curx_adjust;
439
coords[1] += cury_adjust;
440
coords[2] += x_adjust;
441
coords[3] += y_adjust;
442
break;
443
case PathIterator.SEG_QUADTO:
444
coords[0] += (curx_adjust + x_adjust) / 2;
445
coords[1] += (cury_adjust + y_adjust) / 2;
446
break;
447
case PathIterator.SEG_LINETO:
448
break;
449
case PathIterator.SEG_MOVETO:
450
movx_adjust = x_adjust;
451
movy_adjust = y_adjust;
452
break;
453
case PathIterator.SEG_CLOSE:
454
throw new InternalError("This should be handled earlier.");
455
}
456
curx_adjust = x_adjust;
457
cury_adjust = y_adjust;
458
return type;
459
}
460
461
public int currentSegment(double[] coords) {
462
float[] tmp = new float[6];
463
int type = this.currentSegment(tmp);
464
for (int i = 0; i < 6; i++) {
465
coords[i] = (float) tmp[i];
466
}
467
return type;
468
}
469
470
public int getWindingRule() {
471
return src.getWindingRule();
472
}
473
474
public boolean isDone() {
475
return src.isDone();
476
}
477
478
public void next() {
479
src.next();
480
}
481
}
482
483
static void pathTo(PathIterator pi, PathConsumer2D pc2d) {
484
RenderingEngine.feedConsumer(pi, pc2d);
485
pc2d.pathDone();
486
}
487
488
/**
489
* Construct an antialiased tile generator for the given shape with
490
* the given rendering attributes and store the bounds of the tile
491
* iteration in the bbox parameter.
492
* The {@code at} parameter specifies a transform that should affect
493
* both the shape and the {@code BasicStroke} attributes.
494
* The {@code clip} parameter specifies the current clip in effect
495
* in device coordinates and can be used to prune the data for the
496
* operation, but the renderer is not required to perform any
497
* clipping.
498
* If the {@code BasicStroke} parameter is null then the shape
499
* should be filled as is, otherwise the attributes of the
500
* {@code BasicStroke} should be used to specify a draw operation.
501
* The {@code thin} parameter indicates whether or not the
502
* transformed {@code BasicStroke} represents coordinates smaller
503
* than the minimum resolution of the antialiasing rasterizer as
504
* specified by the {@code getMinimumAAPenWidth()} method.
505
* <p>
506
* Upon returning, this method will fill the {@code bbox} parameter
507
* with 4 values indicating the bounds of the iteration of the
508
* tile generator.
509
* The iteration order of the tiles will be as specified by the
510
* pseudo-code:
511
* <pre>
512
* for (y = bbox[1]; y < bbox[3]; y += tileheight) {
513
* for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
514
* }
515
* }
516
* </pre>
517
* If there is no output to be rendered, this method may return
518
* null.
519
*
520
* @param s the shape to be rendered (fill or draw)
521
* @param at the transform to be applied to the shape and the
522
* stroke attributes
523
* @param clip the current clip in effect in device coordinates
524
* @param bs if non-null, a {@code BasicStroke} whose attributes
525
* should be applied to this operation
526
* @param thin true if the transformed stroke attributes are smaller
527
* than the minimum dropout pen width
528
* @param normalize true if the {@code VALUE_STROKE_NORMALIZE}
529
* {@code RenderingHint} is in effect
530
* @param bbox returns the bounds of the iteration
531
* @return the {@code AATileGenerator} instance to be consulted
532
* for tile coverages, or null if there is no output to render
533
* @since 1.7
534
*/
535
public AATileGenerator getAATileGenerator(Shape s,
536
AffineTransform at,
537
Region clip,
538
BasicStroke bs,
539
boolean thin,
540
boolean normalize,
541
int bbox[])
542
{
543
Renderer r;
544
NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
545
if (bs == null) {
546
PathIterator pi;
547
if (normalize) {
548
pi = new NormalizingPathIterator(s.getPathIterator(at), norm);
549
} else {
550
pi = s.getPathIterator(at);
551
}
552
r = new Renderer(3, 3,
553
clip.getLoX(), clip.getLoY(),
554
clip.getWidth(), clip.getHeight(),
555
pi.getWindingRule());
556
pathTo(pi, r);
557
} else {
558
r = new Renderer(3, 3,
559
clip.getLoX(), clip.getLoY(),
560
clip.getWidth(), clip.getHeight(),
561
PathIterator.WIND_NON_ZERO);
562
strokeTo(s, at, bs, thin, norm, true, r);
563
}
564
r.endRendering();
565
PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA);
566
ptg.getBbox(bbox);
567
return ptg;
568
}
569
570
public AATileGenerator getAATileGenerator(double x, double y,
571
double dx1, double dy1,
572
double dx2, double dy2,
573
double lw1, double lw2,
574
Region clip,
575
int bbox[])
576
{
577
// REMIND: Deal with large coordinates!
578
double ldx1, ldy1, ldx2, ldy2;
579
boolean innerpgram = (lw1 > 0 && lw2 > 0);
580
581
if (innerpgram) {
582
ldx1 = dx1 * lw1;
583
ldy1 = dy1 * lw1;
584
ldx2 = dx2 * lw2;
585
ldy2 = dy2 * lw2;
586
x -= (ldx1 + ldx2) / 2.0;
587
y -= (ldy1 + ldy2) / 2.0;
588
dx1 += ldx1;
589
dy1 += ldy1;
590
dx2 += ldx2;
591
dy2 += ldy2;
592
if (lw1 > 1 && lw2 > 1) {
593
// Inner parallelogram was entirely consumed by stroke...
594
innerpgram = false;
595
}
596
} else {
597
ldx1 = ldy1 = ldx2 = ldy2 = 0;
598
}
599
600
Renderer r = new Renderer(3, 3,
601
clip.getLoX(), clip.getLoY(),
602
clip.getWidth(), clip.getHeight(),
603
PathIterator.WIND_EVEN_ODD);
604
605
r.moveTo((float) x, (float) y);
606
r.lineTo((float) (x+dx1), (float) (y+dy1));
607
r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
608
r.lineTo((float) (x+dx2), (float) (y+dy2));
609
r.closePath();
610
611
if (innerpgram) {
612
x += ldx1 + ldx2;
613
y += ldy1 + ldy2;
614
dx1 -= 2.0 * ldx1;
615
dy1 -= 2.0 * ldy1;
616
dx2 -= 2.0 * ldx2;
617
dy2 -= 2.0 * ldy2;
618
r.moveTo((float) x, (float) y);
619
r.lineTo((float) (x+dx1), (float) (y+dy1));
620
r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
621
r.lineTo((float) (x+dx2), (float) (y+dy2));
622
r.closePath();
623
}
624
625
r.pathDone();
626
627
r.endRendering();
628
PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA);
629
ptg.getBbox(bbox);
630
return ptg;
631
}
632
633
/**
634
* Returns the minimum pen width that the antialiasing rasterizer
635
* can represent without dropouts occurring.
636
* @since 1.7
637
*/
638
public float getMinimumAAPenSize() {
639
return 0.5f;
640
}
641
642
static {
643
if (PathIterator.WIND_NON_ZERO != Renderer.WIND_NON_ZERO ||
644
PathIterator.WIND_EVEN_ODD != Renderer.WIND_EVEN_ODD ||
645
BasicStroke.JOIN_MITER != Stroker.JOIN_MITER ||
646
BasicStroke.JOIN_ROUND != Stroker.JOIN_ROUND ||
647
BasicStroke.JOIN_BEVEL != Stroker.JOIN_BEVEL ||
648
BasicStroke.CAP_BUTT != Stroker.CAP_BUTT ||
649
BasicStroke.CAP_ROUND != Stroker.CAP_ROUND ||
650
BasicStroke.CAP_SQUARE != Stroker.CAP_SQUARE)
651
{
652
throw new InternalError("mismatched renderer constants");
653
}
654
}
655
}
656
657
658