Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/sun/java2d/pipe/BufferedPaints.java
41159 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.pipe;
27
28
import java.awt.Color;
29
import java.awt.GradientPaint;
30
import java.awt.LinearGradientPaint;
31
import java.awt.MultipleGradientPaint;
32
import java.awt.MultipleGradientPaint.ColorSpaceType;
33
import java.awt.MultipleGradientPaint.CycleMethod;
34
import java.awt.Paint;
35
import java.awt.RadialGradientPaint;
36
import java.awt.TexturePaint;
37
import java.awt.geom.AffineTransform;
38
import java.awt.geom.Point2D;
39
import java.awt.geom.Rectangle2D;
40
import java.awt.image.AffineTransformOp;
41
import java.awt.image.BufferedImage;
42
import sun.awt.image.PixelConverter;
43
import sun.java2d.SunGraphics2D;
44
import sun.java2d.SurfaceData;
45
import sun.java2d.loops.CompositeType;
46
import sun.java2d.loops.SurfaceType;
47
import static sun.java2d.pipe.BufferedOpCodes.*;
48
49
import java.lang.annotation.Native;
50
51
public class BufferedPaints {
52
53
static void setPaint(RenderQueue rq, SunGraphics2D sg2d,
54
Paint paint, int ctxflags)
55
{
56
if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {
57
setColor(rq, sg2d.pixel);
58
} else {
59
boolean useMask = (ctxflags & BufferedContext.USE_MASK) != 0;
60
switch (sg2d.paintState) {
61
case SunGraphics2D.PAINT_GRADIENT:
62
setGradientPaint(rq, sg2d,
63
(GradientPaint)paint, useMask);
64
break;
65
case SunGraphics2D.PAINT_LIN_GRADIENT:
66
setLinearGradientPaint(rq, sg2d,
67
(LinearGradientPaint)paint, useMask);
68
break;
69
case SunGraphics2D.PAINT_RAD_GRADIENT:
70
setRadialGradientPaint(rq, sg2d,
71
(RadialGradientPaint)paint, useMask);
72
break;
73
case SunGraphics2D.PAINT_TEXTURE:
74
setTexturePaint(rq, sg2d,
75
(TexturePaint)paint, useMask);
76
break;
77
default:
78
break;
79
}
80
}
81
}
82
83
static void resetPaint(RenderQueue rq) {
84
// assert rq.lock.isHeldByCurrentThread();
85
rq.ensureCapacity(4);
86
RenderBuffer buf = rq.getBuffer();
87
buf.putInt(RESET_PAINT);
88
}
89
90
/****************************** Color support *******************************/
91
92
private static void setColor(RenderQueue rq, int pixel) {
93
// assert rq.lock.isHeldByCurrentThread();
94
rq.ensureCapacity(8);
95
RenderBuffer buf = rq.getBuffer();
96
buf.putInt(SET_COLOR);
97
buf.putInt(pixel);
98
}
99
100
/************************* GradientPaint support ****************************/
101
102
/**
103
* Note: This code is factored out into a separate static method
104
* so that it can be shared by both the Gradient and LinearGradient
105
* implementations. LinearGradient uses this code (for the
106
* two-color sRGB case only) because it can be much faster than the
107
* equivalent implementation that uses fragment shaders.
108
*
109
* We use OpenGL's texture coordinate generator to automatically
110
* apply a smooth gradient (either cyclic or acyclic) to the geometry
111
* being rendered. This technique is almost identical to the one
112
* described in the comments for BufferedPaints.setTexturePaint(),
113
* except the calculations take place in one dimension instead of two.
114
* Instead of an anchor rectangle in the TexturePaint case, we use
115
* the vector between the two GradientPaint end points in our
116
* calculations. The generator uses a single plane equation that
117
* takes the (x,y) location (in device space) of the fragment being
118
* rendered to calculate a (u) texture coordinate for that fragment:
119
* u = Ax + By + Cz + Dw
120
*
121
* The gradient renderer uses a two-pixel 1D texture where the first
122
* pixel contains the first GradientPaint color, and the second pixel
123
* contains the second GradientPaint color. (Note that we use the
124
* GL_CLAMP_TO_EDGE wrapping mode for acyclic gradients so that we
125
* clamp the colors properly at the extremes.) The following diagram
126
* attempts to show the layout of the texture containing the two
127
* GradientPaint colors (C1 and C2):
128
*
129
* +-----------------+
130
* | C1 | C2 |
131
* | | |
132
* +-----------------+
133
* u=0 .25 .5 .75 1
134
*
135
* We calculate our plane equation constants (A,B,D) such that u=0.25
136
* corresponds to the first GradientPaint end point in user space and
137
* u=0.75 corresponds to the second end point. This is somewhat
138
* non-obvious, but since the gradient colors are generated by
139
* interpolating between C1 and C2, we want the pure color at the
140
* end points, and we will get the pure color only when u correlates
141
* to the center of a texel. The following chart shows the expected
142
* color for some sample values of u (where C' is the color halfway
143
* between C1 and C2):
144
*
145
* u value acyclic (GL_CLAMP) cyclic (GL_REPEAT)
146
* ------- ------------------ ------------------
147
* -0.25 C1 C2
148
* 0.0 C1 C'
149
* 0.25 C1 C1
150
* 0.5 C' C'
151
* 0.75 C2 C2
152
* 1.0 C2 C'
153
* 1.25 C2 C1
154
*
155
* Original inspiration for this technique came from UMD's Agile2D
156
* project (GradientManager.java).
157
*/
158
private static void setGradientPaint(RenderQueue rq, AffineTransform at,
159
Color c1, Color c2,
160
Point2D pt1, Point2D pt2,
161
boolean isCyclic, boolean useMask)
162
{
163
// convert gradient colors to IntArgbPre format
164
PixelConverter pc = PixelConverter.ArgbPre.instance;
165
int pixel1 = pc.rgbToPixel(c1.getRGB(), null);
166
int pixel2 = pc.rgbToPixel(c2.getRGB(), null);
167
168
// calculate plane equation constants
169
double x = pt1.getX();
170
double y = pt1.getY();
171
at.translate(x, y);
172
// now gradient point 1 is at the origin
173
x = pt2.getX() - x;
174
y = pt2.getY() - y;
175
double len = Math.sqrt(x * x + y * y);
176
at.rotate(x, y);
177
// now gradient point 2 is on the positive x-axis
178
at.scale(2*len, 1);
179
// now gradient point 2 is at (0.5, 0)
180
at.translate(-0.25, 0);
181
// now gradient point 1 is at (0.25, 0), point 2 is at (0.75, 0)
182
183
double p0, p1, p3;
184
try {
185
at.invert();
186
p0 = at.getScaleX();
187
p1 = at.getShearX();
188
p3 = at.getTranslateX();
189
} catch (java.awt.geom.NoninvertibleTransformException e) {
190
p0 = p1 = p3 = 0.0;
191
}
192
193
// assert rq.lock.isHeldByCurrentThread();
194
rq.ensureCapacityAndAlignment(44, 12);
195
RenderBuffer buf = rq.getBuffer();
196
buf.putInt(SET_GRADIENT_PAINT);
197
buf.putInt(useMask ? 1 : 0);
198
buf.putInt(isCyclic ? 1 : 0);
199
buf.putDouble(p0).putDouble(p1).putDouble(p3);
200
buf.putInt(pixel1).putInt(pixel2);
201
}
202
203
private static void setGradientPaint(RenderQueue rq,
204
SunGraphics2D sg2d,
205
GradientPaint paint,
206
boolean useMask)
207
{
208
setGradientPaint(rq, (AffineTransform)sg2d.transform.clone(),
209
paint.getColor1(), paint.getColor2(),
210
paint.getPoint1(), paint.getPoint2(),
211
paint.isCyclic(), useMask);
212
}
213
214
/************************** TexturePaint support ****************************/
215
216
/**
217
* We use OpenGL's texture coordinate generator to automatically
218
* map the TexturePaint image to the geometry being rendered. The
219
* generator uses two separate plane equations that take the (x,y)
220
* location (in device space) of the fragment being rendered to
221
* calculate (u,v) texture coordinates for that fragment:
222
* u = Ax + By + Cz + Dw
223
* v = Ex + Fy + Gz + Hw
224
*
225
* Since we use a 2D orthographic projection, we can assume that z=0
226
* and w=1 for any fragment. So we need to calculate appropriate
227
* values for the plane equation constants (A,B,D) and (E,F,H) such
228
* that {u,v}=0 for the top-left of the TexturePaint's anchor
229
* rectangle and {u,v}=1 for the bottom-right of the anchor rectangle.
230
* We can easily make the texture image repeat for {u,v} values
231
* outside the range [0,1] by specifying the GL_REPEAT texture wrap
232
* mode.
233
*
234
* Calculating the plane equation constants is surprisingly simple.
235
* We can think of it as an inverse matrix operation that takes
236
* device space coordinates and transforms them into user space
237
* coordinates that correspond to a location relative to the anchor
238
* rectangle. First, we translate and scale the current user space
239
* transform by applying the anchor rectangle bounds. We then take
240
* the inverse of this affine transform. The rows of the resulting
241
* inverse matrix correlate nicely to the plane equation constants
242
* we were seeking.
243
*/
244
private static void setTexturePaint(RenderQueue rq,
245
SunGraphics2D sg2d,
246
TexturePaint paint,
247
boolean useMask)
248
{
249
BufferedImage bi = paint.getImage();
250
SurfaceData dstData = sg2d.surfaceData;
251
SurfaceData srcData =
252
dstData.getSourceSurfaceData(bi, SunGraphics2D.TRANSFORM_ISIDENT,
253
CompositeType.SrcOver, null);
254
boolean filter =
255
(sg2d.interpolationType !=
256
AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
257
258
// calculate plane equation constants
259
AffineTransform at = (AffineTransform)sg2d.transform.clone();
260
Rectangle2D anchor = paint.getAnchorRect();
261
at.translate(anchor.getX(), anchor.getY());
262
at.scale(anchor.getWidth(), anchor.getHeight());
263
264
double xp0, xp1, xp3, yp0, yp1, yp3;
265
try {
266
at.invert();
267
xp0 = at.getScaleX();
268
xp1 = at.getShearX();
269
xp3 = at.getTranslateX();
270
yp0 = at.getShearY();
271
yp1 = at.getScaleY();
272
yp3 = at.getTranslateY();
273
} catch (java.awt.geom.NoninvertibleTransformException e) {
274
xp0 = xp1 = xp3 = yp0 = yp1 = yp3 = 0.0;
275
}
276
277
// assert rq.lock.isHeldByCurrentThread();
278
rq.ensureCapacityAndAlignment(68, 12);
279
RenderBuffer buf = rq.getBuffer();
280
buf.putInt(SET_TEXTURE_PAINT);
281
buf.putInt(useMask ? 1 : 0);
282
buf.putInt(filter ? 1 : 0);
283
buf.putLong(srcData.getNativeOps());
284
buf.putDouble(xp0).putDouble(xp1).putDouble(xp3);
285
buf.putDouble(yp0).putDouble(yp1).putDouble(yp3);
286
}
287
288
/****************** Shared MultipleGradientPaint support ********************/
289
290
/**
291
* The maximum number of gradient "stops" supported by our native
292
* fragment shader implementations.
293
*
294
* This value has been empirically determined and capped to allow
295
* our native shaders to run on all shader-level graphics hardware,
296
* even on the older, more limited GPUs. Even the oldest Nvidia
297
* hardware could handle 16, or even 32 fractions without any problem.
298
* But the first-generation boards from ATI would fall back into
299
* software mode (which is unusably slow) for values larger than 12;
300
* it appears that those boards do not have enough native registers
301
* to support the number of array accesses required by our gradient
302
* shaders. So for now we will cap this value at 12, but we can
303
* re-evaluate this in the future as hardware becomes more capable.
304
*/
305
@Native public static final int MULTI_MAX_FRACTIONS = 12;
306
307
/**
308
* Helper function to convert a color component in sRGB space to
309
* linear RGB space. Copied directly from the
310
* MultipleGradientPaintContext class.
311
*/
312
public static int convertSRGBtoLinearRGB(int color) {
313
float input, output;
314
315
input = color / 255.0f;
316
if (input <= 0.04045f) {
317
output = input / 12.92f;
318
} else {
319
output = (float)Math.pow((input + 0.055) / 1.055, 2.4);
320
}
321
322
return Math.round(output * 255.0f);
323
}
324
325
/**
326
* Helper function to convert a (non-premultiplied) Color in sRGB
327
* space to an IntArgbPre pixel value, optionally in linear RGB space.
328
* Based on the PixelConverter.ArgbPre.rgbToPixel() method.
329
*/
330
private static int colorToIntArgbPrePixel(Color c, boolean linear) {
331
int rgb = c.getRGB();
332
if (!linear && ((rgb >> 24) == -1)) {
333
return rgb;
334
}
335
int a = rgb >>> 24;
336
int r = (rgb >> 16) & 0xff;
337
int g = (rgb >> 8) & 0xff;
338
int b = (rgb ) & 0xff;
339
if (linear) {
340
r = convertSRGBtoLinearRGB(r);
341
g = convertSRGBtoLinearRGB(g);
342
b = convertSRGBtoLinearRGB(b);
343
}
344
int a2 = a + (a >> 7);
345
r = (r * a2) >> 8;
346
g = (g * a2) >> 8;
347
b = (b * a2) >> 8;
348
return ((a << 24) | (r << 16) | (g << 8) | (b));
349
}
350
351
/**
352
* Converts the given array of Color objects into an int array
353
* containing IntArgbPre pixel values. If the linear parameter
354
* is true, the Color values will be converted into a linear RGB
355
* color space before being returned.
356
*/
357
private static int[] convertToIntArgbPrePixels(Color[] colors,
358
boolean linear)
359
{
360
int[] pixels = new int[colors.length];
361
for (int i = 0; i < colors.length; i++) {
362
pixels[i] = colorToIntArgbPrePixel(colors[i], linear);
363
}
364
return pixels;
365
}
366
367
/********************** LinearGradientPaint support *************************/
368
369
/**
370
* This method uses techniques that are nearly identical to those
371
* employed in setGradientPaint() above. The primary difference
372
* is that at the native level we use a fragment shader to manually
373
* apply the plane equation constants to the current fragment position
374
* to calculate the gradient position in the range [0,1] (the native
375
* code for GradientPaint does the same, except that it uses OpenGL's
376
* automatic texture coordinate generation facilities).
377
*
378
* One other minor difference worth mentioning is that
379
* setGradientPaint() calculates the plane equation constants
380
* such that the gradient end points are positioned at 0.25 and 0.75
381
* (for reasons discussed in the comments for that method). In
382
* contrast, for LinearGradientPaint we setup the equation constants
383
* such that the gradient end points fall at 0.0 and 1.0. The
384
* reason for this difference is that in the fragment shader we
385
* have more control over how the gradient values are interpreted
386
* (depending on the paint's CycleMethod).
387
*/
388
private static void setLinearGradientPaint(RenderQueue rq,
389
SunGraphics2D sg2d,
390
LinearGradientPaint paint,
391
boolean useMask)
392
{
393
boolean linear =
394
(paint.getColorSpace() == ColorSpaceType.LINEAR_RGB);
395
Color[] colors = paint.getColors();
396
int numStops = colors.length;
397
Point2D pt1 = paint.getStartPoint();
398
Point2D pt2 = paint.getEndPoint();
399
AffineTransform at = paint.getTransform();
400
at.preConcatenate(sg2d.transform);
401
402
if (!linear && numStops == 2 &&
403
paint.getCycleMethod() != CycleMethod.REPEAT)
404
{
405
// delegate to the optimized two-color gradient codepath
406
boolean isCyclic =
407
(paint.getCycleMethod() != CycleMethod.NO_CYCLE);
408
setGradientPaint(rq, at,
409
colors[0], colors[1],
410
pt1, pt2,
411
isCyclic, useMask);
412
return;
413
}
414
415
int cycleMethod = paint.getCycleMethod().ordinal();
416
float[] fractions = paint.getFractions();
417
int[] pixels = convertToIntArgbPrePixels(colors, linear);
418
419
// calculate plane equation constants
420
double x = pt1.getX();
421
double y = pt1.getY();
422
at.translate(x, y);
423
// now gradient point 1 is at the origin
424
x = pt2.getX() - x;
425
y = pt2.getY() - y;
426
double len = Math.sqrt(x * x + y * y);
427
at.rotate(x, y);
428
// now gradient point 2 is on the positive x-axis
429
at.scale(len, 1);
430
// now gradient point 1 is at (0.0, 0), point 2 is at (1.0, 0)
431
432
float p0, p1, p3;
433
try {
434
at.invert();
435
p0 = (float)at.getScaleX();
436
p1 = (float)at.getShearX();
437
p3 = (float)at.getTranslateX();
438
} catch (java.awt.geom.NoninvertibleTransformException e) {
439
p0 = p1 = p3 = 0.0f;
440
}
441
442
// assert rq.lock.isHeldByCurrentThread();
443
rq.ensureCapacity(20 + 12 + (numStops*4*2));
444
RenderBuffer buf = rq.getBuffer();
445
buf.putInt(SET_LINEAR_GRADIENT_PAINT);
446
buf.putInt(useMask ? 1 : 0);
447
buf.putInt(linear ? 1 : 0);
448
buf.putInt(cycleMethod);
449
buf.putInt(numStops);
450
buf.putFloat(p0);
451
buf.putFloat(p1);
452
buf.putFloat(p3);
453
buf.put(fractions);
454
buf.put(pixels);
455
}
456
457
/********************** RadialGradientPaint support *************************/
458
459
/**
460
* This method calculates six m** values and a focusX value that
461
* are used by the native fragment shader. These techniques are
462
* based on a whitepaper by Daniel Rice on radial gradient performance
463
* (attached to the bug report for 6521533). One can refer to that
464
* document for the complete set of formulas and calculations, but
465
* the basic goal is to compose a transform that will convert an
466
* (x,y) position in device space into a "u" value that represents
467
* the relative distance to the gradient focus point. The resulting
468
* value can be used to look up the appropriate color by linearly
469
* interpolating between the two nearest colors in the gradient.
470
*/
471
private static void setRadialGradientPaint(RenderQueue rq,
472
SunGraphics2D sg2d,
473
RadialGradientPaint paint,
474
boolean useMask)
475
{
476
boolean linear =
477
(paint.getColorSpace() == ColorSpaceType.LINEAR_RGB);
478
int cycleMethod = paint.getCycleMethod().ordinal();
479
float[] fractions = paint.getFractions();
480
Color[] colors = paint.getColors();
481
int numStops = colors.length;
482
int[] pixels = convertToIntArgbPrePixels(colors, linear);
483
Point2D center = paint.getCenterPoint();
484
Point2D focus = paint.getFocusPoint();
485
float radius = paint.getRadius();
486
487
// save original (untransformed) center and focus points
488
double cx = center.getX();
489
double cy = center.getY();
490
double fx = focus.getX();
491
double fy = focus.getY();
492
493
// transform from gradient coords to device coords
494
AffineTransform at = paint.getTransform();
495
at.preConcatenate(sg2d.transform);
496
focus = at.transform(focus, focus);
497
498
// transform unit circle to gradient coords; we start with the
499
// unit circle (center=(0,0), focus on positive x-axis, radius=1)
500
// and then transform into gradient space
501
at.translate(cx, cy);
502
at.rotate(fx - cx, fy - cy);
503
at.scale(radius, radius);
504
505
// invert to get mapping from device coords to unit circle
506
try {
507
at.invert();
508
} catch (Exception e) {
509
at.setToScale(0.0, 0.0);
510
}
511
focus = at.transform(focus, focus);
512
513
// clamp the focus point so that it does not rest on, or outside
514
// of, the circumference of the gradient circle
515
fx = Math.min(focus.getX(), 0.99);
516
517
// assert rq.lock.isHeldByCurrentThread();
518
rq.ensureCapacity(20 + 28 + (numStops*4*2));
519
RenderBuffer buf = rq.getBuffer();
520
buf.putInt(SET_RADIAL_GRADIENT_PAINT);
521
buf.putInt(useMask ? 1 : 0);
522
buf.putInt(linear ? 1 : 0);
523
buf.putInt(numStops);
524
buf.putInt(cycleMethod);
525
buf.putFloat((float)at.getScaleX());
526
buf.putFloat((float)at.getShearX());
527
buf.putFloat((float)at.getTranslateX());
528
buf.putFloat((float)at.getShearY());
529
buf.putFloat((float)at.getScaleY());
530
buf.putFloat((float)at.getTranslateY());
531
buf.putFloat((float)fx);
532
buf.put(fractions);
533
buf.put(pixels);
534
}
535
}
536
537