Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/make/src/classes/build/tools/generatenimbus/PainterGenerator.java
32287 views
1
/*
2
* Copyright (c) 2002, 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
package build.tools.generatenimbus;
26
27
import java.awt.geom.Point2D;
28
import java.util.ArrayList;
29
import java.util.HashMap;
30
import java.util.LinkedHashMap;
31
import java.util.List;
32
import java.util.Map;
33
34
35
/**
36
* PainterGenerator - Class for generating Painter class java source from a Canvas
37
*
38
* Following in the general theory that is used to generate a Painter file.
39
*
40
* Each Painter file represents a Region. So there is one painter file per region. In
41
* skin.laf we support Icon subregions, which are really just hacked versions of the
42
* parent region.
43
*
44
* In order to generate the most compact and efficient bytecode possible for the
45
* Painters, we actually perform the generation sequence in two steps. The first
46
* step is the analysis phase, where we walk through the SynthModel for the region
47
* and discover commonality among the different states in the region. For example,
48
* do they have common paths? Do they have common colors? Gradients? Is the painting
49
* code for the different states identical other than for colors?
50
*
51
* We gather this information up. On the second pass, we use this data to determine the
52
* methods that need to be generated, and the class variables that need to be generated.
53
* We try to keep the actual bytecode count as small as possible so that we may reduce
54
* the overall size of the look and feel significantly.
55
*
56
* @author Richard Bair
57
* @author Jasper Potts
58
*/
59
public class PainterGenerator {
60
//a handful of counters, incremented whenever the associated object type is encounted.
61
//These counters form the basis of the field and method suffixes.
62
//These are all 1 based, because I felt like it :-)
63
private int colorCounter = 1;
64
private int gradientCounter = 1;
65
private int radialCounter = 1;
66
private int pathCounter = 1;
67
private int rectCounter = 1;
68
private int roundRectCounter = 1;
69
private int ellipseCounter = 1;
70
71
private int stateTypeCounter = 1;
72
73
//during the first pass, we will construct these maps
74
private Map<String, String> colors = new HashMap<String, String>();
75
/**
76
* Code=>method name.
77
*/
78
private Map<String, String> methods = new HashMap<String, String>();
79
80
//these variables hold the generated code
81
/**
82
* The source code in this variable will be used to define the various state types
83
*/
84
private StringBuilder stateTypeCode = new StringBuilder();
85
/**
86
* The source code in this variable will be used to define the switch statement for painting
87
*/
88
private StringBuilder switchCode = new StringBuilder();
89
/**
90
* The source code in this variable will be used to define the methods for painting each state
91
*/
92
private StringBuilder paintingCode = new StringBuilder();
93
/**
94
* The source code in this variable will be used to add getExtendedCacheKeys
95
* implementation if needed.
96
*/
97
private StringBuilder getExtendedCacheKeysCode = new StringBuilder();
98
/**
99
* The source code in this variable will be used to define the methods for decoding gradients
100
* and shapes.
101
*/
102
private StringBuilder gradientsCode = new StringBuilder();
103
private StringBuilder colorCode = new StringBuilder();
104
private StringBuilder shapesCode = new StringBuilder();
105
/**
106
* Map of component colors keyed by state constant name
107
*/
108
private Map<String, List<ComponentColor>> componentColorsMap =
109
new LinkedHashMap<String, List<ComponentColor>>();
110
/**
111
* For the current state the list of all component colors used by this
112
* painter, the index in this list is also the index in the runtime array
113
* of defaults and keys.
114
*/
115
private List<ComponentColor> componentColors = null;
116
117
PainterGenerator(UIRegion r) {
118
generate(r);
119
}
120
121
private void generate(UIRegion r) {
122
for (UIState state : r.getBackgroundStates()) {
123
Canvas canvas = state.getCanvas();
124
String type = (r instanceof UIIconRegion ? r.getKey() : "Background");
125
generate(state, canvas, type);
126
}
127
for (UIState state : r.getForegroundStates()) {
128
Canvas canvas = state.getCanvas();
129
generate(state, canvas, "Foreground");
130
}
131
for (UIState state : r.getBorderStates()) {
132
Canvas canvas = state.getCanvas();
133
generate(state, canvas, "Border");
134
}
135
//now check for any uiIconRegions, since these are collapsed together.
136
for (UIRegion sub : r.getSubRegions()) {
137
if (sub instanceof UIIconRegion) {
138
generate(sub);
139
}
140
}
141
//generate all the code for component colors
142
if (!componentColorsMap.isEmpty()) {
143
getExtendedCacheKeysCode
144
.append(" protected Object[] getExtendedCacheKeys(JComponent c) {\n")
145
.append(" Object[] extendedCacheKeys = null;\n")
146
.append(" switch(state) {\n");
147
for (Map.Entry<String, List<ComponentColor>> entry : componentColorsMap.entrySet()) {
148
getExtendedCacheKeysCode
149
.append(" case ")
150
.append(entry.getKey()).append(":\n")
151
.append(" extendedCacheKeys = new Object[] {\n");
152
for (int i=0; i<entry.getValue().size(); i++) {
153
ComponentColor cc = entry.getValue().get(i);
154
cc.write(getExtendedCacheKeysCode);
155
if (i + 1 < entry.getValue().size()) {
156
getExtendedCacheKeysCode.append("),\n");
157
} else {
158
getExtendedCacheKeysCode.append(")");
159
}
160
}
161
getExtendedCacheKeysCode.append("};\n")
162
.append(" break;\n");
163
}
164
getExtendedCacheKeysCode
165
.append(" }\n")
166
.append(" return extendedCacheKeys;\n")
167
.append(" }");
168
}
169
}
170
171
//type is background, foreground, border, upArrowIcon, etc.
172
private void generate(UIState state, Canvas canvas, String type) {
173
String states = state.getStateKeys();
174
String stateType = Utils.statesToConstantName(type + "_" + states);
175
String paintMethodName = "paint" + type + Utils.statesToClassName(states);
176
//create new array for component colors for this state
177
componentColors = new ArrayList<ComponentColor>();
178
179
stateTypeCode.append(" static final int ").append(stateType).append(" = ").append(stateTypeCounter++).append(";\n");
180
181
if (canvas.isBlank()) {
182
return;
183
}
184
185
switchCode.append(" case ").append(stateType).append(": ").append(paintMethodName).append("(g); break;\n");
186
paintingCode.append(" private void ").append(paintMethodName).append("(Graphics2D g) {\n");
187
188
//start by setting up common info needed to encode the control points
189
Insets in = canvas.getStretchingInsets();
190
float a = in.left;
191
float b = canvas.getSize().width - in.right;
192
float c = in.top;
193
float d = canvas.getSize().height - in.bottom;
194
float width = canvas.getSize().width;
195
float height = canvas.getSize().height;
196
float cw = b - a;
197
float ch = d - c;
198
199
Layer[] layers = canvas.getLayers().toArray(new Layer[0]);
200
for (int index=layers.length-1; index >= 0; index--) {
201
Layer layer = layers[index];
202
203
//shapes must be painted in reverse order
204
List<Shape> shapes = layer.getShapes();
205
for (int i=shapes.size()-1; i>=0; i--) {
206
Shape shape = shapes.get(i);
207
Paint paint = shape.getPaint();
208
209
/*
210
We attempt to write the minimal number of bytecodes as possible when
211
generating code. Due to the inherit complexities in determining what
212
is extraneous, we use the following system:
213
214
We first generate the code for the shape. Then, we check to see if
215
this shape has already been generated. If so, then we defer to an
216
existing method. If not, then we will create a new methods, stick
217
the code in it, and refer to that method.
218
*/
219
220
String shapeMethodName = null; // will contain the name of the method which creates the shape
221
String shapeVariable = null; // will be one of rect, roundRect, ellipse, or path.
222
String shapeMethodBody = null;
223
224
if (shape instanceof Rectangle) {
225
Rectangle rshape = (Rectangle) shape;
226
float x1 = encode((float)rshape.getX1(), a, b, width);
227
float y1 = encode((float)rshape.getY1(), c, d, height);
228
float x2 = encode((float)rshape.getX2(), a, b, width);
229
float y2 = encode((float)rshape.getY2(), c, d, height);
230
if (rshape.isRounded()) {
231
//it is a rounded rectangle
232
float rounding = (float)rshape.getRounding();
233
234
shapeMethodBody =
235
" roundRect.setRoundRect(" +
236
writeDecodeX(x1) + ", //x\n" +
237
" " + writeDecodeY(y1) + ", //y\n" +
238
" " + writeDecodeX(x2) + " - " + writeDecodeX(x1) + ", //width\n" +
239
" " + writeDecodeY(y2) + " - " + writeDecodeY(y1) + ", //height\n" +
240
" " + rounding + "f, " + rounding + "f); //rounding";
241
shapeVariable = "roundRect";
242
} else {
243
shapeMethodBody =
244
" rect.setRect(" +
245
writeDecodeX(x1) + ", //x\n" +
246
" " + writeDecodeY(y1) + ", //y\n" +
247
" " + writeDecodeX(x2) + " - " + writeDecodeX(x1) + ", //width\n" +
248
" " + writeDecodeY(y2) + " - " + writeDecodeY(y1) + "); //height";
249
shapeVariable = "rect";
250
}
251
} else if (shape instanceof Ellipse) {
252
Ellipse eshape = (Ellipse) shape;
253
float x1 = encode((float)eshape.getX1(), a, b, width);
254
float y1 = encode((float)eshape.getY1(), c, d, height);
255
float x2 = encode((float)eshape.getX2(), a, b, width);
256
float y2 = encode((float)eshape.getY2(), c, d, height);
257
shapeMethodBody =
258
" ellipse.setFrame(" +
259
writeDecodeX(x1) + ", //x\n" +
260
" " + writeDecodeY(y1) + ", //y\n" +
261
" " + writeDecodeX(x2) + " - " + writeDecodeX(x1) + ", //width\n" +
262
" " + writeDecodeY(y2) + " - " + writeDecodeY(y1) + "); //height";
263
shapeVariable = "ellipse";
264
} else if (shape instanceof Path) {
265
Path pshape = (Path) shape;
266
List<Point> controlPoints = pshape.getControlPoints();
267
Point first, last;
268
first = last = controlPoints.get(0);
269
StringBuilder buffer = new StringBuilder();
270
buffer.append(" path.reset();\n");
271
buffer.append(" path.moveTo(" + writeDecodeX(encode((float)first.getX(), a, b, width)) + ", " + writeDecodeY(encode((float)first.getY(), c, d, height)) + ");\n");
272
for (int j=1; j<controlPoints.size(); j++) {
273
Point cp = controlPoints.get(j);
274
if (last.isP2Sharp() && cp.isP1Sharp()) {
275
float x = encode((float)cp.getX(), a, b, width);
276
float y = encode((float)cp.getY(), c, d, height);
277
buffer.append(" path.lineTo(" + writeDecodeX(x) + ", " + writeDecodeY(y) + ");\n");
278
} else {
279
float x1 = encode((float)last.getX(), a, b, width);
280
float y1 = encode((float)last.getY(), c, d, height);
281
float x2 = encode((float)cp.getX(), a, b, width);
282
float y2 = encode((float)cp.getY(), c, d, height);
283
buffer.append(
284
" path.curveTo(" + writeDecodeBezierX(x1, last.getX(), last.getCp2X()) + ", "
285
+ writeDecodeBezierY(y1, last.getY(), last.getCp2Y()) + ", "
286
+ writeDecodeBezierX(x2, cp.getX(), cp.getCp1X()) + ", "
287
+ writeDecodeBezierY(y2, cp.getY(), cp.getCp1Y()) + ", "
288
+ writeDecodeX(x2) + ", " + writeDecodeY(y2) + ");\n");
289
}
290
last = cp;
291
}
292
if (last.isP2Sharp() && first.isP1Sharp()) {
293
float x = encode((float)first.getX(), a, b, width);
294
float y = encode((float)first.getY(), c, d, height);
295
buffer.append(" path.lineTo(" + writeDecodeX(x) + ", " + writeDecodeY(y) + ");\n");
296
} else {
297
float x1 = encode((float)last.getX(), a, b, width);
298
float y1 = encode((float)last.getY(), c, d, height);
299
float x2 = encode((float)first.getX(), a, b, width);
300
float y2 = encode((float)first.getY(), c, d, height);
301
buffer.append(
302
" path.curveTo(" + writeDecodeBezierX(x1, last.getX(), last.getCp2X()) + ", "
303
+ writeDecodeBezierY(y1, last.getY(), last.getCp2Y()) + ", "
304
+ writeDecodeBezierX(x2, first.getX(), first.getCp1X()) + ", "
305
+ writeDecodeBezierY(y2, first.getY(), first.getCp1Y()) + ", "
306
+ writeDecodeX(x2) + ", " + writeDecodeY(y2) + ");\n");
307
}
308
buffer.append(" path.closePath();");
309
shapeMethodBody = buffer.toString();
310
shapeVariable = "path";
311
} else {
312
throw new RuntimeException("Cannot happen unless a new Shape has been defined");
313
}
314
315
//now that we have the shape defined in shapeMethodBody, and a shapeVariable name,
316
//look to see if such a body has been previously defined.
317
shapeMethodName = methods.get(shapeMethodBody);
318
String returnType = null;
319
if (shapeMethodName == null) {
320
if ("rect".equals(shapeVariable)) {
321
shapeMethodName = "decodeRect" + rectCounter++;
322
returnType = "Rectangle2D";
323
} else if ("roundRect".equals(shapeVariable)) {
324
shapeMethodName = "decodeRoundRect" + roundRectCounter++;
325
returnType = "RoundRectangle2D";
326
} else if ("ellipse".equals(shapeVariable)) {
327
shapeMethodName = "decodeEllipse" + ellipseCounter++;
328
returnType = "Ellipse2D";
329
} else {
330
shapeMethodName = "decodePath" + pathCounter++;
331
returnType = "Path2D";
332
}
333
methods.put(shapeMethodBody, shapeMethodName);
334
335
//since the method wasn't previously defined, time to define it
336
shapesCode.append(" private ").append(returnType).append(" ").append(shapeMethodName).append("() {\n");
337
shapesCode.append(shapeMethodBody);
338
shapesCode.append("\n");
339
shapesCode.append(" return " + shapeVariable + ";\n");
340
shapesCode.append(" }\n\n");
341
}
342
343
//now that the method has been defined, I can go on and decode the
344
//paint. After the paint is decoded, I can write the g.fill() method call,
345
//using the result of the shapeMethodName. Yay!
346
347
// if (shapeVariable != null) {
348
//first, calculate the bounds of the shape being painted and store in variables
349
paintingCode.append(" ").append(shapeVariable).append(" = ").append(shapeMethodName).append("();\n");
350
351
if (paint instanceof Matte) {
352
String colorVariable = encodeMatte((Matte)paint);
353
paintingCode.append(" g.setPaint(").append(colorVariable).append(");\n");
354
} else if (paint instanceof Gradient) {
355
String gradientMethodName = encodeGradient(shape, (Gradient)paint);
356
paintingCode.append(" g.setPaint(").append(gradientMethodName).append("(").append(shapeVariable).append("));\n");
357
} else if (paint instanceof RadialGradient) {
358
String radialMethodName = encodeRadial(shape, (RadialGradient)paint);
359
paintingCode.append(" g.setPaint(").append(radialMethodName).append("(").append(shapeVariable).append("));\n");
360
}
361
paintingCode.append(" g.fill(").append(shapeVariable).append(");\n");
362
}
363
}
364
365
paintingCode.append("\n }\n\n");
366
367
//collect component colors
368
if (!componentColors.isEmpty()) {
369
componentColorsMap.put(stateType, componentColors);
370
componentColors = null;
371
}
372
}
373
374
private float encode(float x, float a, float b, float w) {
375
float r = 0;
376
if (x < a) {
377
r = (x / a);
378
} else if (x > b) {
379
r = 2 + ((x - b) / (w - b));
380
} else if (x == a && x == b) {
381
return 1.5f;
382
} else {
383
r = 1 + ((x - a) / (b - a));
384
}
385
386
if (Float.isNaN(r)) {
387
System.err.println("[Error] Encountered NaN: encode(" + x + ", " + a + ", " + b + ", " + w + ")");
388
return 0;
389
} else if (Float.isInfinite(r)) {
390
System.err.println("[Error] Encountered Infinity: encode(" + x + ", " + a + ", " + b + ", " + w + ")");
391
return 0;
392
} else if (r < 0) {
393
System.err.println("[Error] encoded value was less than 0: encode(" + x + ", " + a + ", " + b + ", " + w + ")");
394
return 0;
395
} else if (r > 3) {
396
System.err.println("[Error] encoded value was greater than 3: encode(" + x + ", " + a + ", " + b + ", " + w + ")");
397
return 3;
398
} else {
399
return r;
400
}
401
}
402
403
private String writeDecodeX(float encodedX) {
404
return "decodeX(" + encodedX + "f)";
405
}
406
407
private String writeDecodeY(float encodedY) {
408
return "decodeY(" + encodedY + "f)";
409
}
410
411
/**
412
*
413
* @param ex encoded x value
414
* @param x unencoded x value
415
* @param cpx unencoded cpx value
416
* @return
417
*/
418
private static String writeDecodeBezierX(double ex, double x, double cpx) {
419
return "decodeAnchorX(" + ex + "f, " + (cpx - x) + "f)";
420
}
421
422
/**
423
*
424
* @param ey encoded y value
425
* @param y unencoded y value
426
* @param cpy unencoded cpy value
427
* @return
428
*/
429
private static String writeDecodeBezierY(double ey, double y, double cpy) {
430
return "decodeAnchorY(" + ey + "f, " + (cpy - y) + "f)";
431
}
432
433
private String encodeMatte(Matte m) {
434
String declaration = m.getDeclaration();
435
String variableName = colors.get(declaration);
436
if (variableName == null) {
437
variableName = "color" + colorCounter++;
438
colors.put(declaration, variableName);
439
colorCode.append(String.format(" private Color %s = %s;\n",
440
variableName, declaration));
441
}
442
// handle component colors
443
if (m.getComponentPropertyName() != null) {
444
ComponentColor cc = m.createComponentColor(variableName);
445
int index = componentColors.indexOf(cc);
446
if (index == -1) {
447
index = componentColors.size();
448
componentColors.add(cc);
449
}
450
return "(Color)componentColors[" + index + "]";
451
} else {
452
return variableName;
453
}
454
}
455
456
private String encodeGradient(Shape ps, Gradient g) {
457
StringBuilder b = new StringBuilder();
458
float x1 = (float)ps.getPaintX1();
459
float y1 = (float)ps.getPaintY1();
460
float x2 = (float)ps.getPaintX2();
461
float y2 = (float)ps.getPaintY2();
462
b.append(" return decodeGradient((");
463
b.append(x1);
464
b.append("f * w) + x, (");
465
b.append(y1);
466
b.append("f * h) + y, (");
467
b.append(x2);
468
b.append("f * w) + x, (");
469
b.append(y2);
470
b.append("f * h) + y,\n");
471
encodeGradientColorsAndFractions(g,b);
472
b.append(");");
473
474
String methodBody = b.toString();
475
String methodName = methods.get(methodBody);
476
if (methodName == null) {
477
methodName = "decodeGradient" + gradientCounter++;
478
gradientsCode.append(" private Paint ").append(methodName).append("(Shape s) {\n");
479
gradientsCode.append(" Rectangle2D bounds = s.getBounds2D();\n");
480
gradientsCode.append(" float x = (float)bounds.getX();\n");
481
gradientsCode.append(" float y = (float)bounds.getY();\n");
482
gradientsCode.append(" float w = (float)bounds.getWidth();\n");
483
gradientsCode.append(" float h = (float)bounds.getHeight();\n");
484
gradientsCode.append(methodBody);
485
gradientsCode.append("\n }\n\n");
486
methods.put(methodBody, methodName);
487
}
488
return methodName;
489
}
490
491
/**
492
* Takes a abstract gradient and creates the code for the fractions float
493
* array and the colors array that can be used in the constructors of linear
494
* and radial gradients.
495
*
496
* @param g The abstract gradient to get stops from
497
* @param b Append code string of the form "new float[]{...},
498
* new Color[]{...}" to this StringBuilder
499
*/
500
private void encodeGradientColorsAndFractions(AbstractGradient g,
501
StringBuilder b) {
502
List<GradientStop> stops = g.getStops();
503
// there are stops.size() number of main stops. Between each is a
504
// fractional stop. Thus, there are: stops.size() + stops.size() - 1
505
// number of fractions and colors.
506
float[] fractions = new float[stops.size() + stops.size() - 1];
507
String[] colors = new String[fractions.length];
508
//for each stop, create the stop and it's associated fraction
509
int index = 0; // the index into fractions and colors
510
for (int i = 0; i < stops.size(); i++) {
511
GradientStop s = stops.get(i);
512
//copy over the stop's data
513
colors[index] = encodeMatte(s.getColor());
514
fractions[index] = s.getPosition();
515
516
//If this isn't the last stop, then add in the fraction
517
if (index < fractions.length - 1) {
518
float f1 = s.getPosition();
519
float f2 = stops.get(i + 1).getPosition();
520
index++;
521
fractions[index] = f1 + (f2 - f1) * s.getMidpoint();
522
colors[index] = "decodeColor("+
523
colors[index - 1]+","+
524
encodeMatte(stops.get(i + 1).getColor())+",0.5f)";
525
}
526
index++;
527
}
528
// Check boundry conditions
529
for (int i = 1; i < fractions.length; i++) {
530
//to avoid an error with LinearGradientPaint where two fractions
531
//are identical, bump up the fraction value by a miniscule amount
532
//if it is identical to the previous one
533
//NOTE: The <= is critical because the previous value may already
534
//have been bumped up
535
if (fractions[i] <= fractions[i - 1]) {
536
fractions[i] = fractions[i - 1] + .000001f;
537
}
538
}
539
//another boundary condition where multiple stops are all at the end. The
540
//previous loop bumped all but one of these past 1.0, which is bad.
541
//so remove any fractions (and their colors!) that are beyond 1.0
542
int outOfBoundsIndex = -1;
543
for (int i = 0; i < fractions.length; i++) {
544
if (fractions[i] > 1) {
545
outOfBoundsIndex = i;
546
break;
547
}
548
}
549
if (outOfBoundsIndex >= 0) {
550
float[] f = fractions;
551
String[] c = colors;
552
fractions = new float[outOfBoundsIndex];
553
colors = new String[outOfBoundsIndex];
554
System.arraycopy(f, 0, fractions, 0, outOfBoundsIndex);
555
System.arraycopy(c, 0, colors, 0, outOfBoundsIndex);
556
}
557
// build string
558
b.append(" new float[] { ");
559
for (int i = 0; i < fractions.length; i++) {
560
if (i>0)b.append(',');
561
b.append(fractions[i]);
562
b.append('f');
563
}
564
b.append(" },\n new Color[] { ");
565
for (int i = 0; i < colors.length; i++) {
566
if (i>0) b.append(",\n ");
567
b.append(colors[i]);
568
}
569
b.append("}");
570
}
571
572
private String encodeRadial(Shape ps, RadialGradient g) {
573
float centerX1 = (float)ps.getPaintX1();
574
float centerY1 = (float)ps.getPaintY1();
575
float x2 = (float)ps.getPaintX2();
576
float y2 = (float)ps.getPaintY2();
577
float radius = (float)Point2D.distance(centerX1, centerY1, x2, y2);
578
StringBuilder b = new StringBuilder();
579
580
b.append(" return decodeRadialGradient((");
581
b.append(centerX1);
582
b.append("f * w) + x, (");
583
b.append(centerY1);
584
b.append("f * h) + y, ");
585
b.append(radius);
586
b.append("f,\n");
587
encodeGradientColorsAndFractions(g,b);
588
b.append(");");
589
590
String methodBody = b.toString();
591
String methodName = methods.get(methodBody);
592
if (methodName == null) {
593
methodName = "decodeRadial" + radialCounter++;
594
gradientsCode.append(" private Paint ").append(methodName).append("(Shape s) {\n");
595
gradientsCode.append(" Rectangle2D bounds = s.getBounds2D();\n");
596
gradientsCode.append(" float x = (float)bounds.getX();\n");
597
gradientsCode.append(" float y = (float)bounds.getY();\n");
598
gradientsCode.append(" float w = (float)bounds.getWidth();\n");
599
gradientsCode.append(" float h = (float)bounds.getHeight();\n");
600
gradientsCode.append(methodBody);
601
gradientsCode.append("\n }\n\n");
602
methods.put(methodBody, methodName);
603
}
604
return methodName;
605
}
606
607
//note that this method is not thread-safe. In fact, none of this class is.
608
public static void writePainter(UIRegion r, String painterName) {
609
//Need only write out the stuff for this region, don't need to worry about subregions
610
//since this method will be called for each of those (and they go in their own file, anyway).
611
//The only subregion that we compound into this is the one for icons.
612
PainterGenerator gen = new PainterGenerator(r);
613
System.out.println("Generating source file: " + painterName + ".java");
614
615
Map<String, String> variables = Generator.getVariables();
616
variables.put("PAINTER_NAME", painterName);
617
variables.put("STATIC_DECL", gen.stateTypeCode.toString());
618
variables.put("COLORS_DECL", gen.colorCode.toString());
619
variables.put("DO_PAINT_SWITCH_BODY", gen.switchCode.toString());
620
variables.put("PAINTING_DECL", gen.paintingCode.toString());
621
variables.put("GET_EXTENDED_CACHE_KEYS", gen.getExtendedCacheKeysCode.toString());
622
variables.put("SHAPES_DECL", gen.shapesCode.toString());
623
variables.put("GRADIENTS_DECL", gen.gradientsCode.toString());
624
625
Generator.writeSrcFile("PainterImpl", variables, painterName);
626
}
627
}
628
629