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/DrawImage.java
41159 views
1
/*
2
* Copyright (c) 2001, 2020, 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.AlphaComposite;
29
import java.awt.Color;
30
import java.awt.Image;
31
import java.awt.Rectangle;
32
import java.awt.Transparency;
33
import java.awt.geom.AffineTransform;
34
import java.awt.geom.NoninvertibleTransformException;
35
import java.awt.image.AffineTransformOp;
36
import java.awt.image.BufferedImage;
37
import java.awt.image.BufferedImageOp;
38
import java.awt.image.ColorModel;
39
import java.awt.image.DataBuffer;
40
import java.awt.image.ImageObserver;
41
import java.awt.image.IndexColorModel;
42
import java.awt.image.Raster;
43
import java.awt.image.VolatileImage;
44
45
import sun.awt.SunHints;
46
import sun.awt.image.ImageRepresentation;
47
import sun.awt.image.SurfaceManager;
48
import sun.awt.image.ToolkitImage;
49
import sun.java2d.InvalidPipeException;
50
import sun.java2d.SunGraphics2D;
51
import sun.java2d.SurfaceData;
52
import sun.java2d.loops.Blit;
53
import sun.java2d.loops.BlitBg;
54
import sun.java2d.loops.CompositeType;
55
import sun.java2d.loops.MaskBlit;
56
import sun.java2d.loops.ScaledBlit;
57
import sun.java2d.loops.SurfaceType;
58
import sun.java2d.loops.TransformHelper;
59
60
public class DrawImage implements DrawImagePipe
61
{
62
public boolean copyImage(SunGraphics2D sg, Image img,
63
int x, int y,
64
Color bgColor)
65
{
66
int imgw = img.getWidth(null);
67
int imgh = img.getHeight(null);
68
if (isSimpleTranslate(sg)) {
69
return renderImageCopy(sg, img, bgColor,
70
x + sg.transX, y + sg.transY,
71
0, 0, imgw, imgh);
72
}
73
AffineTransform atfm = sg.transform;
74
if ((x | y) != 0) {
75
atfm = new AffineTransform(atfm);
76
atfm.translate(x, y);
77
}
78
transformImage(sg, img, atfm, sg.interpolationType,
79
0, 0, imgw, imgh, bgColor);
80
return true;
81
}
82
83
public boolean copyImage(SunGraphics2D sg, Image img,
84
int dx, int dy, int sx, int sy, int w, int h,
85
Color bgColor)
86
{
87
if (isSimpleTranslate(sg)) {
88
return renderImageCopy(sg, img, bgColor,
89
dx + sg.transX, dy + sg.transY,
90
sx, sy, w, h);
91
}
92
scaleImage(sg, img, dx, dy, (dx + w), (dy + h),
93
sx, sy, (sx + w), (sy + h), bgColor);
94
return true;
95
}
96
97
public boolean scaleImage(SunGraphics2D sg, Image img, int x, int y,
98
int width, int height,
99
Color bgColor)
100
{
101
int imgw = img.getWidth(null);
102
int imgh = img.getHeight(null);
103
// Only accelerate scale if:
104
// - w/h positive values
105
// - sg transform integer translate/identity only
106
// - no bgColor in operation
107
if ((width > 0) && (height > 0) && isSimpleTranslate(sg)) {
108
double dx1 = x + sg.transX;
109
double dy1 = y + sg.transY;
110
double dx2 = dx1 + width;
111
double dy2 = dy1 + height;
112
if (renderImageScale(sg, img, bgColor, sg.interpolationType,
113
0, 0, imgw, imgh,
114
dx1, dy1, dx2, dy2))
115
{
116
return true;
117
}
118
}
119
120
AffineTransform atfm = sg.transform;
121
if ((x | y) != 0 || width != imgw || height != imgh) {
122
atfm = new AffineTransform(atfm);
123
atfm.translate(x, y);
124
atfm.scale(((double)width)/imgw, ((double)height)/imgh);
125
}
126
transformImage(sg, img, atfm, sg.interpolationType,
127
0, 0, imgw, imgh, bgColor);
128
return true;
129
}
130
131
/*
132
* This method is only called in those circumstances where the
133
* operation has a non-null secondary transform specified. Its
134
* role is to check for various optimizations based on the types
135
* of both the secondary and SG2D transforms and to do some
136
* quick calculations to avoid having to combine the transforms
137
* and/or to call a more generalized method.
138
*/
139
protected void transformImage(SunGraphics2D sg, Image img, int x, int y,
140
AffineTransform extraAT, int interpType)
141
{
142
int txtype = extraAT.getType();
143
int imgw = img.getWidth(null);
144
int imgh = img.getHeight(null);
145
boolean checkfinalxform;
146
147
if (sg.transformState <= SunGraphics2D.TRANSFORM_ANY_TRANSLATE &&
148
(txtype == AffineTransform.TYPE_IDENTITY ||
149
txtype == AffineTransform.TYPE_TRANSLATION))
150
{
151
// First optimization - both are some kind of translate
152
153
// Combine the translations and check if interpolation is necessary.
154
double tx = extraAT.getTranslateX();
155
double ty = extraAT.getTranslateY();
156
tx += sg.transform.getTranslateX();
157
ty += sg.transform.getTranslateY();
158
int itx = (int) Math.floor(tx + 0.5);
159
int ity = (int) Math.floor(ty + 0.5);
160
if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||
161
(closeToInteger(itx, tx) && closeToInteger(ity, ty)))
162
{
163
renderImageCopy(sg, img, null, x+itx, y+ity, 0, 0, imgw, imgh);
164
return;
165
}
166
checkfinalxform = false;
167
} else if (sg.transformState <= SunGraphics2D.TRANSFORM_TRANSLATESCALE &&
168
((txtype & (AffineTransform.TYPE_FLIP |
169
AffineTransform.TYPE_MASK_ROTATION |
170
AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0))
171
{
172
// Second optimization - both are some kind of translate or scale
173
174
// Combine the scales and check if interpolation is necessary.
175
176
// Transform source bounds by extraAT,
177
// then translate the bounds again by x, y
178
// then transform the bounds again by sg.transform
179
double[] coords = new double[] {
180
0, 0, imgw, imgh,
181
};
182
extraAT.transform(coords, 0, coords, 0, 2);
183
coords[0] += x;
184
coords[1] += y;
185
coords[2] += x;
186
coords[3] += y;
187
sg.transform.transform(coords, 0, coords, 0, 2);
188
189
if (tryCopyOrScale(sg, img, 0, 0, imgw, imgh,
190
null, interpType, coords))
191
{
192
return;
193
}
194
checkfinalxform = false;
195
} else {
196
checkfinalxform = true;
197
}
198
199
// Begin Transform
200
AffineTransform tx = new AffineTransform(sg.transform);
201
tx.translate(x, y);
202
tx.concatenate(extraAT);
203
204
// Do not try any more optimizations if either of the cases
205
// above was tried as we have already verified that the
206
// resulting transform will not simplify.
207
if (checkfinalxform) {
208
// In this case neither of the above simple transform
209
// pairs was found so we will do some final tests on
210
// the final rendering transform which may be the
211
// simple product of two complex transforms.
212
transformImage(sg, img, tx, interpType, 0, 0, imgw, imgh, null);
213
} else {
214
renderImageXform(sg, img, tx, interpType, 0, 0, imgw, imgh, null);
215
}
216
}
217
218
/*
219
* This method is called with a final rendering transform that
220
* has combined all of the information about the Graphics2D
221
* transform attribute with the transformations specified by
222
* the arguments to the drawImage call.
223
* Its role is to see if the combined transform ends up being
224
* acceleratable by either a renderImageCopy or renderImageScale
225
* once all of the math is done.
226
*
227
* Note: The transform supplied here has an origin that is
228
* already adjusted to point to the device location where
229
* the (sx1, sy1) location of the source image should be placed.
230
*/
231
protected void transformImage(SunGraphics2D sg, Image img,
232
AffineTransform tx, int interpType,
233
int sx1, int sy1, int sx2, int sy2,
234
Color bgColor)
235
{
236
// Transform 3 source corners by tx and analyze them
237
// for simplified operations (Copy or Scale). Using
238
// 3 points lets us analyze any kind of transform,
239
// even transforms that involve very tiny amounts of
240
// rotation or skew to see if they degenerate to a
241
// simple scale or copy operation within the allowable
242
// error bounds.
243
// Note that we use (0,0,w,h) instead of (sx1,sy1,sx2,sy2)
244
// because the transform is already translated such that
245
// the origin is where sx1, sy1 should go.
246
double[] coords = new double[6];
247
/* index: 0 1 2 3 4 5 */
248
/* coord: (0, 0), (w, h), (0, h) */
249
coords[2] = sx2 - sx1;
250
coords[3] = coords[5] = sy2 - sy1;
251
tx.transform(coords, 0, coords, 0, 3);
252
// First test if the X coords of the transformed UL
253
// and LL points match and that the Y coords of the
254
// transformed LR and LL points also match.
255
// If they do then it is a "rectilinear" transform and
256
// tryCopyOrScale will make sure it is upright and
257
// integer-based.
258
if (Math.abs(coords[0] - coords[4]) < MAX_TX_ERROR &&
259
Math.abs(coords[3] - coords[5]) < MAX_TX_ERROR &&
260
tryCopyOrScale(sg, img, sx1, sy1, sx2, sy2,
261
bgColor, interpType, coords))
262
{
263
return;
264
}
265
266
renderImageXform(sg, img, tx, interpType, sx1, sy1, sx2, sy2, bgColor);
267
}
268
269
/*
270
* Check the bounding coordinates of the transformed source
271
* image to see if they fall on integer coordinates such
272
* that they will cause no interpolation anomalies if we
273
* use our simplified Blit or ScaledBlit operations instead
274
* of a full transform operation.
275
*/
276
protected boolean tryCopyOrScale(SunGraphics2D sg,
277
Image img,
278
int sx1, int sy1,
279
int sx2, int sy2,
280
Color bgColor, int interpType,
281
double[] coords)
282
{
283
double dx1 = coords[0];
284
double dy1 = coords[1];
285
double dx2 = coords[2];
286
double dy2 = coords[3];
287
double dw = dx2 - dx1;
288
double dh = dy2 - dy1;
289
290
/* If any of the destination coordinates exceed the integer range,
291
* then the calculations performed in calls made here cannot be
292
* guaranteed to be correct, or to converge (terminate).
293
* So return out of here, deferring to code that can handle this.
294
*/
295
if (dx1 < Integer.MIN_VALUE || dx1 > Integer.MAX_VALUE ||
296
dy1 < Integer.MIN_VALUE || dy1 > Integer.MAX_VALUE ||
297
dx2 < Integer.MIN_VALUE || dx2 > Integer.MAX_VALUE ||
298
dy2 < Integer.MIN_VALUE || dy2 > Integer.MAX_VALUE)
299
{
300
return false;
301
}
302
303
// First check if width and height are very close to img w&h.
304
if (closeToInteger(sx2-sx1, dw) && closeToInteger(sy2-sy1, dh)) {
305
// Round location to nearest pixel and then test
306
// if it will cause interpolation anomalies.
307
int idx = (int) Math.floor(dx1 + 0.5);
308
int idy = (int) Math.floor(dy1 + 0.5);
309
if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||
310
(closeToInteger(idx, dx1) && closeToInteger(idy, dy1)))
311
{
312
renderImageCopy(sg, img, bgColor,
313
idx, idy,
314
sx1, sy1, sx2-sx1, sy2-sy1);
315
return true;
316
}
317
}
318
// (For now) We can only use our ScaledBlits if the image
319
// is upright (i.e. dw & dh both > 0)
320
if (dw > 0 && dh > 0) {
321
if (renderImageScale(sg, img, bgColor, interpType,
322
sx1, sy1, sx2, sy2,
323
dx1, dy1, dx2, dy2))
324
{
325
return true;
326
}
327
}
328
return false;
329
}
330
331
/**
332
* Return a non-accelerated BufferedImage of the requested type with the
333
* indicated subimage of the original image located at 0,0 in the new image.
334
* If a bgColor is supplied, composite the original image over that color
335
* with a SrcOver operation, otherwise make a SrcNoEa copy.
336
* <p>
337
* Returned BufferedImage is not accelerated for two reasons:
338
* <ul>
339
* <li> Types of the image and surface are predefined, because these types
340
* correspond to the TransformHelpers, which we know we have. And
341
* acceleration can change the type of the surface
342
* <li> Image will be used only once and acceleration caching wouldn't help
343
* </ul>
344
*/
345
private BufferedImage makeBufferedImage(Image img, Color bgColor, int type,
346
int sx1, int sy1, int sx2, int sy2)
347
{
348
final int width = sx2 - sx1;
349
final int height = sy2 - sy1;
350
final BufferedImage bimg = new BufferedImage(width, height, type);
351
final SunGraphics2D g2d = (SunGraphics2D) bimg.createGraphics();
352
g2d.setComposite(AlphaComposite.Src);
353
bimg.setAccelerationPriority(0);
354
if (bgColor != null) {
355
g2d.setColor(bgColor);
356
g2d.fillRect(0, 0, width, height);
357
g2d.setComposite(AlphaComposite.SrcOver);
358
}
359
g2d.copyImage(img, 0, 0, sx1, sy1, width, height, null, null);
360
g2d.dispose();
361
return bimg;
362
}
363
364
protected void renderImageXform(SunGraphics2D sg, Image img,
365
AffineTransform tx, int interpType,
366
int sx1, int sy1, int sx2, int sy2,
367
Color bgColor)
368
{
369
final AffineTransform itx;
370
try {
371
itx = tx.createInverse();
372
} catch (final NoninvertibleTransformException ignored) {
373
// Non-invertible transform means no output
374
return;
375
}
376
377
/*
378
* Find the maximum bounds on the destination that will be
379
* affected by the transformed source. First, transform all
380
* four corners of the source and then min and max the resulting
381
* destination coordinates of the transformed corners.
382
* Note that tx already has the offset to sx1,sy1 accounted
383
* for so we use the box (0, 0, sx2-sx1, sy2-sy1) as the
384
* source coordinates.
385
*/
386
final double[] coords = new double[8];
387
/* corner: UL UR LL LR */
388
/* index: 0 1 2 3 4 5 6 7 */
389
/* coord: (0, 0), (w, 0), (0, h), (w, h) */
390
coords[2] = coords[6] = sx2 - sx1;
391
coords[5] = coords[7] = sy2 - sy1;
392
tx.transform(coords, 0, coords, 0, 4);
393
double ddx1, ddy1, ddx2, ddy2;
394
ddx1 = ddx2 = coords[0];
395
ddy1 = ddy2 = coords[1];
396
for (int i = 2; i < coords.length; i += 2) {
397
double d = coords[i];
398
if (ddx1 > d) ddx1 = d;
399
else if (ddx2 < d) ddx2 = d;
400
d = coords[i+1];
401
if (ddy1 > d) ddy1 = d;
402
else if (ddy2 < d) ddy2 = d;
403
}
404
405
Region clip = sg.getCompClip();
406
final int dx1 = Math.max((int) Math.floor(ddx1), clip.getLoX());
407
final int dy1 = Math.max((int) Math.floor(ddy1), clip.getLoY());
408
final int dx2 = Math.min((int) Math.ceil(ddx2), clip.getHiX());
409
final int dy2 = Math.min((int) Math.ceil(ddy2), clip.getHiY());
410
if (dx2 <= dx1 || dy2 <= dy1) {
411
// empty destination means no output
412
return;
413
}
414
415
final SurfaceData dstData = sg.surfaceData;
416
SurfaceData srcData = dstData.getSourceSurfaceData(img,
417
SunGraphics2D.TRANSFORM_GENERIC,
418
sg.imageComp,
419
bgColor);
420
421
if (srcData == null) {
422
img = getBufferedImage(img);
423
srcData = dstData.getSourceSurfaceData(img,
424
SunGraphics2D.TRANSFORM_GENERIC,
425
sg.imageComp,
426
bgColor);
427
if (srcData == null) {
428
// REMIND: Is this correct? Can this happen?
429
return;
430
}
431
}
432
433
if (isBgOperation(srcData, bgColor)) {
434
// We cannot perform bg operations during transform so make
435
// a temp image with the appropriate background based on
436
// background alpha value and work from there. If background
437
// alpha is opaque use INT_RGB else use INT_ARGB so that we
438
// will not lose translucency of background.
439
440
int bgAlpha = bgColor.getAlpha();
441
int type = ((bgAlpha == 255)
442
? BufferedImage.TYPE_INT_RGB
443
: BufferedImage.TYPE_INT_ARGB);
444
img = makeBufferedImage(img, bgColor, type, sx1, sy1, sx2, sy2);
445
// Temp image has appropriate subimage at 0,0 now.
446
sx2 -= sx1;
447
sy2 -= sy1;
448
sx1 = sy1 = 0;
449
450
srcData = dstData.getSourceSurfaceData(img,
451
SunGraphics2D.TRANSFORM_GENERIC,
452
sg.imageComp,
453
bgColor);
454
}
455
456
SurfaceType srcType = srcData.getSurfaceType();
457
TransformHelper helper = TransformHelper.getFromCache(srcType);
458
459
if (helper == null) {
460
/* We have no helper for this source image type.
461
* But we know that we do have helpers for both RGB and ARGB,
462
* so convert to one of those types depending on transparency.
463
* ARGB_PRE might be a better choice if the source image has
464
* alpha, but it may cause some recursion here since we only
465
* tend to have converters that convert to ARGB.
466
*/
467
int type = ((srcData.getTransparency() == Transparency.OPAQUE)
468
? BufferedImage.TYPE_INT_RGB
469
: BufferedImage.TYPE_INT_ARGB);
470
img = makeBufferedImage(img, null, type, sx1, sy1, sx2, sy2);
471
// Temp image has appropriate subimage at 0,0 now.
472
sx2 -= sx1;
473
sy2 -= sy1;
474
sx1 = sy1 = 0;
475
476
srcData = dstData.getSourceSurfaceData(img,
477
SunGraphics2D.TRANSFORM_GENERIC,
478
sg.imageComp,
479
null);
480
srcType = srcData.getSurfaceType();
481
helper = TransformHelper.getFromCache(srcType);
482
// assert(helper != null);
483
}
484
485
SurfaceType dstType = dstData.getSurfaceType();
486
if (sg.compositeState <= SunGraphics2D.COMP_ALPHA) {
487
/* NOTE: We either have, or we can make,
488
* a MaskBlit for any alpha composite type
489
*/
490
MaskBlit maskblit = MaskBlit.getFromCache(SurfaceType.IntArgbPre,
491
sg.imageComp, dstType);
492
493
/* NOTE: We can only use the native TransformHelper
494
* func to go directly to the dest if both the helper
495
* and the MaskBlit are native.
496
* All helpers are native at this point, but some MaskBlit
497
* objects are implemented in Java, so we need to check.
498
*/
499
if (maskblit.getNativePrim() != 0) {
500
// We can render directly.
501
helper.Transform(maskblit, srcData, dstData,
502
sg.composite, clip,
503
itx, interpType,
504
sx1, sy1, sx2, sy2,
505
dx1, dy1, dx2, dy2,
506
null, 0, 0);
507
return;
508
}
509
}
510
511
// We need to transform to a temp image and then copy
512
// just the pieces that are valid data to the dest.
513
final int w = dx2 - dx1;
514
final int h = dy2 - dy1;
515
BufferedImage tmpimg = new BufferedImage(w, h,
516
BufferedImage.TYPE_INT_ARGB_PRE);
517
SurfaceData tmpData = SurfaceData.getPrimarySurfaceData(tmpimg);
518
SurfaceType tmpType = tmpData.getSurfaceType();
519
MaskBlit tmpmaskblit = MaskBlit.getFromCache(SurfaceType.IntArgbPre,
520
CompositeType.SrcNoEa,
521
tmpType);
522
/*
523
* The helper function fills a temporary edges buffer
524
* for us with the bounding coordinates of each scanline
525
* in the following format:
526
*
527
* edges[0, 1] = [top y, bottom y)
528
* edges[2, 3] = [left x, right x) of top row
529
* ...
530
* edges[h*2, h*2+1] = [left x, right x) of bottom row
531
*
532
* all coordinates in the edges array will be relative to dx1, dy1
533
*
534
* edges thus has to be h*2+2 in length
535
*/
536
final int[] edges = new int[h * 2 + 2];
537
// It is important that edges[0]=edges[1]=0 when we call
538
// Transform in case it must return early and we would
539
// not want to render anything on an error condition.
540
helper.Transform(tmpmaskblit, srcData, tmpData,
541
AlphaComposite.Src, null,
542
itx, interpType,
543
sx1, sy1, sx2, sy2,
544
0, 0, w, h,
545
edges, dx1, dy1);
546
547
final Region region = Region.getInstance(dx1, dy1, dx2, dy2, edges);
548
clip = clip.getIntersection(region);
549
550
/* NOTE: We either have, or we can make,
551
* a Blit for any composite type, even Custom
552
*/
553
final Blit blit = Blit.getFromCache(tmpType, sg.imageComp, dstType);
554
blit.Blit(tmpData, dstData, sg.composite, clip, 0, 0, dx1, dy1, w, h);
555
}
556
557
// Render an image using only integer translation
558
// (no scale or transform or sub-pixel interpolated translations).
559
protected boolean renderImageCopy(SunGraphics2D sg, Image img,
560
Color bgColor,
561
int dx, int dy,
562
int sx, int sy,
563
int w, int h)
564
{
565
Region clip = sg.getCompClip();
566
SurfaceData dstData = sg.surfaceData;
567
568
int attempts = 0;
569
// Loop up to twice through; this gives us a chance to
570
// revalidate the surfaceData objects in case of an exception
571
// and try it once more
572
while (true) {
573
SurfaceData srcData =
574
dstData.getSourceSurfaceData(img,
575
SunGraphics2D.TRANSFORM_ISIDENT,
576
sg.imageComp,
577
bgColor);
578
if (srcData == null) {
579
return false;
580
}
581
582
try {
583
blitSurfaceData(sg, clip, srcData, dstData,
584
sx, sy, dx, dy, w, h, bgColor);
585
return true;
586
} catch (NullPointerException e) {
587
if (!(SurfaceData.isNull(dstData) ||
588
SurfaceData.isNull(srcData)))
589
{
590
// Something else caused the exception, throw it...
591
throw e;
592
}
593
return false;
594
// NOP if we have been disposed
595
} catch (InvalidPipeException e) {
596
// Always catch the exception; try this a couple of times
597
// and fail silently if the system is not yet ready to
598
// revalidate the source or dest surfaceData objects.
599
++attempts;
600
clip = sg.getCompClip(); // ensures sg.surfaceData is valid
601
dstData = sg.surfaceData;
602
if (SurfaceData.isNull(dstData) ||
603
SurfaceData.isNull(srcData) || (attempts > 1))
604
{
605
return false;
606
}
607
}
608
}
609
}
610
611
// Render an image using only integer scaling (no transform).
612
protected boolean renderImageScale(SunGraphics2D sg, Image img,
613
Color bgColor, int interpType,
614
int sx1, int sy1,
615
int sx2, int sy2,
616
double dx1, double dy1,
617
double dx2, double dy2)
618
{
619
// Currently only NEAREST_NEIGHBOR interpolation is implemented
620
// for ScaledBlit operations.
621
if (interpType != AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
622
return false;
623
}
624
625
Region clip = sg.getCompClip();
626
SurfaceData dstData = sg.surfaceData;
627
628
int attempts = 0;
629
// Loop up to twice through; this gives us a chance to
630
// revalidate the surfaceData objects in case of an exception
631
// and try it once more
632
while (true) {
633
SurfaceData srcData =
634
dstData.getSourceSurfaceData(img,
635
SunGraphics2D.TRANSFORM_TRANSLATESCALE,
636
sg.imageComp,
637
bgColor);
638
639
if (srcData == null || isBgOperation(srcData, bgColor)) {
640
return false;
641
}
642
643
try {
644
SurfaceType srcType = srcData.getSurfaceType();
645
SurfaceType dstType = dstData.getSurfaceType();
646
return scaleSurfaceData(sg, clip,
647
srcData, dstData, srcType, dstType,
648
sx1, sy1, sx2, sy2,
649
dx1, dy1, dx2, dy2);
650
} catch (NullPointerException e) {
651
if (!SurfaceData.isNull(dstData)) {
652
// Something else caused the exception, throw it...
653
throw e;
654
}
655
return false;
656
// NOP if we have been disposed
657
} catch (InvalidPipeException e) {
658
// Always catch the exception; try this a couple of times
659
// and fail silently if the system is not yet ready to
660
// revalidate the source or dest surfaceData objects.
661
++attempts;
662
clip = sg.getCompClip(); // ensures sg.surfaceData is valid
663
dstData = sg.surfaceData;
664
if (SurfaceData.isNull(dstData) ||
665
SurfaceData.isNull(srcData) || (attempts > 1))
666
{
667
return false;
668
}
669
}
670
}
671
}
672
673
public boolean scaleImage(SunGraphics2D sg, Image img,
674
int dx1, int dy1, int dx2, int dy2,
675
int sx1, int sy1, int sx2, int sy2,
676
Color bgColor)
677
{
678
int srcW, srcH, dstW, dstH;
679
int srcX, srcY, dstX, dstY;
680
boolean srcWidthFlip = false;
681
boolean srcHeightFlip = false;
682
boolean dstWidthFlip = false;
683
boolean dstHeightFlip = false;
684
685
if (sx2 > sx1) {
686
srcW = sx2 - sx1;
687
srcX = sx1;
688
} else {
689
srcWidthFlip = true;
690
srcW = sx1 - sx2;
691
srcX = sx2;
692
}
693
if (sy2 > sy1) {
694
srcH = sy2-sy1;
695
srcY = sy1;
696
} else {
697
srcHeightFlip = true;
698
srcH = sy1-sy2;
699
srcY = sy2;
700
}
701
if (dx2 > dx1) {
702
dstW = dx2 - dx1;
703
dstX = dx1;
704
} else {
705
dstW = dx1 - dx2;
706
dstWidthFlip = true;
707
dstX = dx2;
708
}
709
if (dy2 > dy1) {
710
dstH = dy2 - dy1;
711
dstY = dy1;
712
} else {
713
dstH = dy1 - dy2;
714
dstHeightFlip = true;
715
dstY = dy2;
716
}
717
if (srcW <= 0 || srcH <= 0) {
718
return true;
719
}
720
// Only accelerate scale if it does not involve a flip or transform
721
if ((srcWidthFlip == dstWidthFlip) &&
722
(srcHeightFlip == dstHeightFlip) &&
723
isSimpleTranslate(sg))
724
{
725
double ddx1 = dstX + sg.transX;
726
double ddy1 = dstY + sg.transY;
727
double ddx2 = ddx1 + dstW;
728
double ddy2 = ddy1 + dstH;
729
if (renderImageScale(sg, img, bgColor, sg.interpolationType,
730
srcX, srcY, srcX+srcW, srcY+srcH,
731
ddx1, ddy1, ddx2, ddy2))
732
{
733
return true;
734
}
735
}
736
737
AffineTransform atfm = new AffineTransform(sg.transform);
738
atfm.translate(dx1, dy1);
739
double m00 = (double)(dx2-dx1)/(sx2-sx1);
740
double m11 = (double)(dy2-dy1)/(sy2-sy1);
741
atfm.scale(m00, m11);
742
atfm.translate(srcX-sx1, srcY-sy1);
743
744
final double scaleX = SurfaceManager.getImageScaleX(img);
745
final double scaleY = SurfaceManager.getImageScaleY(img);
746
final int imgW = (int) Math.ceil(img.getWidth(null) * scaleX);
747
final int imgH = (int) Math.ceil(img.getHeight(null) * scaleY);
748
srcW += srcX;
749
srcH += srcY;
750
// Make sure we are not out of bounds
751
if (srcW > imgW) {
752
srcW = imgW;
753
}
754
if (srcH > imgH) {
755
srcH = imgH;
756
}
757
if (srcX < 0) {
758
atfm.translate(-srcX, 0);
759
srcX = 0;
760
}
761
if (srcY < 0) {
762
atfm.translate(0, -srcY);
763
srcY = 0;
764
}
765
if (srcX >= srcW || srcY >= srcH) {
766
return true;
767
}
768
// Note: src[WH] are currently the right and bottom coordinates.
769
// The following two lines would adjust src[WH] back to being
770
// dimensions.
771
// srcW -= srcX;
772
// srcH -= srcY;
773
// Since transformImage needs right and bottom coords we will
774
// omit this adjustment.
775
776
transformImage(sg, img, atfm, sg.interpolationType,
777
srcX, srcY, srcW, srcH, bgColor);
778
return true;
779
}
780
781
/**
782
** Utilities
783
** The following methods are used by the public methods above
784
** for performing various operations
785
**/
786
787
/*
788
* This constant represents a tradeoff between the
789
* need to make sure that image transformations are
790
* "very close" to integer device coordinates before
791
* we decide to use an integer scale or copy operation
792
* as a substitute and the fact that roundoff errors
793
* in AffineTransforms are frequently introduced by
794
* performing multiple sequential operations on them.
795
*
796
* The evaluation of bug 4990624 details the potential
797
* for this error cutoff to result in display anomalies
798
* in different types of image operations and how this
799
* value represents a good compromise here.
800
*/
801
private static final double MAX_TX_ERROR = .0001;
802
803
public static boolean closeToInteger(int i, double d) {
804
return (Math.abs(d-i) < MAX_TX_ERROR);
805
}
806
807
public static boolean isSimpleTranslate(SunGraphics2D sg) {
808
int ts = sg.transformState;
809
if (ts <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
810
// Integer translates are always "simple"
811
return true;
812
}
813
if (ts >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
814
// Scales and beyond are always "not simple"
815
return false;
816
}
817
// non-integer translates are only simple when not interpolating
818
if (sg.interpolationType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
819
return true;
820
}
821
return false;
822
}
823
824
protected static boolean isBgOperation(SurfaceData srcData, Color bgColor) {
825
// If we cannot get the srcData, then cannot assume anything about
826
// the image
827
return ((srcData == null) ||
828
((bgColor != null) &&
829
(srcData.getTransparency() != Transparency.OPAQUE)));
830
}
831
832
protected BufferedImage getBufferedImage(Image img) {
833
if (img instanceof BufferedImage) {
834
return (BufferedImage)img;
835
}
836
// Must be VolatileImage; get BufferedImage representation
837
return ((VolatileImage)img).getSnapshot();
838
}
839
840
/*
841
* Return the color model to be used with this BufferedImage and
842
* transform.
843
*/
844
private ColorModel getTransformColorModel(SunGraphics2D sg,
845
BufferedImage bImg,
846
AffineTransform tx) {
847
ColorModel cm = bImg.getColorModel();
848
ColorModel dstCM = cm;
849
850
if (tx.isIdentity()) {
851
return dstCM;
852
}
853
int type = tx.getType();
854
boolean needTrans =
855
((type & (AffineTransform.TYPE_MASK_ROTATION |
856
AffineTransform.TYPE_GENERAL_TRANSFORM)) != 0);
857
if (! needTrans &&
858
type != AffineTransform.TYPE_TRANSLATION &&
859
type != AffineTransform.TYPE_IDENTITY)
860
{
861
double[] mtx = new double[4];
862
tx.getMatrix(mtx);
863
// Check out the matrix. A non-integral scale will force ARGB
864
// since the edge conditions cannot be guaranteed.
865
needTrans = (mtx[0] != (int)mtx[0] || mtx[3] != (int)mtx[3]);
866
}
867
868
if (sg.renderHint != SunHints.INTVAL_RENDER_QUALITY) {
869
if (cm instanceof IndexColorModel) {
870
Raster raster = bImg.getRaster();
871
IndexColorModel icm = (IndexColorModel) cm;
872
// Just need to make sure that we have a transparent pixel
873
if (needTrans && cm.getTransparency() == Transparency.OPAQUE) {
874
// Fix 4221407
875
if (raster instanceof sun.awt.image.BytePackedRaster) {
876
dstCM = ColorModel.getRGBdefault();
877
}
878
else {
879
double[] matrix = new double[6];
880
tx.getMatrix(matrix);
881
if (matrix[1] == 0. && matrix[2] ==0.
882
&& matrix[4] == 0. && matrix[5] == 0.) {
883
// Only scaling so do not need to create
884
}
885
else {
886
int mapSize = icm.getMapSize();
887
if (mapSize < 256) {
888
int[] cmap = new int[mapSize+1];
889
icm.getRGBs(cmap);
890
cmap[mapSize] = 0x0000;
891
dstCM = new
892
IndexColorModel(icm.getPixelSize(),
893
mapSize+1,
894
cmap, 0, true, mapSize,
895
DataBuffer.TYPE_BYTE);
896
}
897
else {
898
dstCM = ColorModel.getRGBdefault();
899
}
900
} /* if (matrix[0] < 1.f ...) */
901
} /* raster instanceof sun.awt.image.BytePackedRaster */
902
} /* if (cm.getTransparency() == cm.OPAQUE) */
903
} /* if (cm instanceof IndexColorModel) */
904
else if (needTrans && cm.getTransparency() == Transparency.OPAQUE) {
905
// Need a bitmask transparency
906
// REMIND: for now, use full transparency since no loops
907
// for bitmask
908
dstCM = ColorModel.getRGBdefault();
909
}
910
} /* if (sg.renderHint == RENDER_QUALITY) */
911
else {
912
913
if (cm instanceof IndexColorModel ||
914
(needTrans && cm.getTransparency() == Transparency.OPAQUE))
915
{
916
// Need a bitmask transparency
917
// REMIND: for now, use full transparency since no loops
918
// for bitmask
919
dstCM = ColorModel.getRGBdefault();
920
}
921
}
922
923
return dstCM;
924
}
925
926
private static void blitSurfaceData(SunGraphics2D sg, Region clip,
927
SurfaceData srcData,
928
SurfaceData dstData,
929
int sx, int sy, int dx, int dy,
930
int w, int h, Color bgColor)
931
{
932
CompositeType comp = sg.imageComp;
933
if (CompositeType.SrcOverNoEa.equals(comp) &&
934
(srcData.getTransparency() == Transparency.OPAQUE ||
935
(bgColor != null &&
936
bgColor.getTransparency() == Transparency.OPAQUE)))
937
{
938
comp = CompositeType.SrcNoEa;
939
}
940
if (srcData == dstData && sx == dx && sy == dy
941
&& CompositeType.SrcNoEa.equals(comp)) {
942
// Performance optimization. We skip the Blit/BlitBG if we know that
943
// it will be noop.
944
return;
945
}
946
// The next optimization should be used by all our pipelines but for now
947
// some of the native pipelines "ogl", "d3d", "gdi", "xrender" relies to
948
// much on the native driver, which does not apply it automatically.
949
// At some point, we should remove it from here, since it affects the
950
// performance of the software loops, and move to the appropriate place.
951
Rectangle dst =
952
new Rectangle(dx, dy, w, h).intersection(dstData.getBounds());
953
if (dst.isEmpty()) {
954
// The check above also includes:
955
// if (w <= 0 || h <= 0) {
956
/*
957
* Fix for bugid 4783274 - BlitBg throws an exception for
958
* a particular set of anomalous parameters.
959
* REMIND: The native loops do proper clipping and would
960
* detect this situation themselves, but the Java loops
961
* all seem to trust their parameters a little too well
962
* to the point where they will try to process a negative
963
* area of pixels and throw exceptions. The real fix is
964
* to modify the Java loops to do proper clipping so that
965
* they can deal with negative dimensions as well as
966
* improperly large dimensions, but that fix is too risky
967
* to integrate for Mantis at this point. In the meantime
968
* eliminating the negative or zero dimensions here is
969
* "correct" and saves them from some nasty exceptional
970
* conditions, one of which is the test case of 4783274.
971
*/
972
// return;
973
// }
974
return;
975
}
976
// Adjust final src(x,y) based on the dst. The logic is that, when dst
977
// limits drawing on the destination, corresponding pixels from the src
978
// should be skipped.
979
sx += dst.x - dx;
980
sy += dst.y - dy;
981
982
SurfaceType srcType = srcData.getSurfaceType();
983
SurfaceType dstType = dstData.getSurfaceType();
984
if (!isBgOperation(srcData, bgColor)) {
985
Blit blit = Blit.getFromCache(srcType, comp, dstType);
986
blit.Blit(srcData, dstData, sg.composite, clip,
987
sx, sy, dst.x, dst.y, dst.width, dst.height);
988
} else {
989
BlitBg blit = BlitBg.getFromCache(srcType, comp, dstType);
990
blit.BlitBg(srcData, dstData, sg.composite, clip, bgColor.getRGB(),
991
sx, sy, dst.x, dst.y, dst.width, dst.height);
992
}
993
}
994
995
protected boolean scaleSurfaceData(SunGraphics2D sg,
996
Region clipRegion,
997
SurfaceData srcData,
998
SurfaceData dstData,
999
SurfaceType srcType,
1000
SurfaceType dstType,
1001
int sx1, int sy1,
1002
int sx2, int sy2,
1003
double dx1, double dy1,
1004
double dx2, double dy2)
1005
{
1006
CompositeType comp = sg.imageComp;
1007
if (CompositeType.SrcOverNoEa.equals(comp) &&
1008
(srcData.getTransparency() == Transparency.OPAQUE))
1009
{
1010
comp = CompositeType.SrcNoEa;
1011
}
1012
1013
ScaledBlit blit = ScaledBlit.getFromCache(srcType, comp, dstType);
1014
if (blit != null) {
1015
blit.Scale(srcData, dstData, sg.composite, clipRegion,
1016
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
1017
return true;
1018
}
1019
return false;
1020
}
1021
1022
protected static boolean imageReady(ToolkitImage sunimg,
1023
ImageObserver observer)
1024
{
1025
if (sunimg.hasError()) {
1026
if (observer != null) {
1027
observer.imageUpdate(sunimg,
1028
ImageObserver.ERROR|ImageObserver.ABORT,
1029
-1, -1, -1, -1);
1030
}
1031
return false;
1032
}
1033
return true;
1034
}
1035
1036
public boolean copyImage(SunGraphics2D sg, Image img,
1037
int x, int y,
1038
Color bgColor,
1039
ImageObserver observer) {
1040
if (!(img instanceof ToolkitImage)) {
1041
return copyImage(sg, img, x, y, bgColor);
1042
} else {
1043
ToolkitImage sunimg = (ToolkitImage)img;
1044
if (!imageReady(sunimg, observer)) {
1045
return false;
1046
}
1047
ImageRepresentation ir = sunimg.getImageRep();
1048
return ir.drawToBufImage(sg, sunimg, x, y, bgColor, observer);
1049
}
1050
}
1051
1052
public boolean copyImage(SunGraphics2D sg, Image img,
1053
int dx, int dy, int sx, int sy, int w, int h,
1054
Color bgColor,
1055
ImageObserver observer) {
1056
if (!(img instanceof ToolkitImage)) {
1057
return copyImage(sg, img, dx, dy, sx, sy, w, h, bgColor);
1058
} else {
1059
ToolkitImage sunimg = (ToolkitImage)img;
1060
if (!imageReady(sunimg, observer)) {
1061
return false;
1062
}
1063
ImageRepresentation ir = sunimg.getImageRep();
1064
return ir.drawToBufImage(sg, sunimg,
1065
dx, dy, (dx + w), (dy + h),
1066
sx, sy, (sx + w), (sy + h),
1067
bgColor, observer);
1068
}
1069
}
1070
1071
public boolean scaleImage(SunGraphics2D sg, Image img,
1072
int x, int y,
1073
int width, int height,
1074
Color bgColor,
1075
ImageObserver observer) {
1076
if (!(img instanceof ToolkitImage)) {
1077
return scaleImage(sg, img, x, y, width, height, bgColor);
1078
} else {
1079
ToolkitImage sunimg = (ToolkitImage)img;
1080
if (!imageReady(sunimg, observer)) {
1081
return false;
1082
}
1083
ImageRepresentation ir = sunimg.getImageRep();
1084
return ir.drawToBufImage(sg, sunimg, x, y, width, height, bgColor,
1085
observer);
1086
}
1087
}
1088
1089
public boolean scaleImage(SunGraphics2D sg, Image img,
1090
int dx1, int dy1, int dx2, int dy2,
1091
int sx1, int sy1, int sx2, int sy2,
1092
Color bgColor,
1093
ImageObserver observer) {
1094
if (!(img instanceof ToolkitImage)) {
1095
return scaleImage(sg, img, dx1, dy1, dx2, dy2,
1096
sx1, sy1, sx2, sy2, bgColor);
1097
} else {
1098
ToolkitImage sunimg = (ToolkitImage)img;
1099
if (!imageReady(sunimg, observer)) {
1100
return false;
1101
}
1102
ImageRepresentation ir = sunimg.getImageRep();
1103
return ir.drawToBufImage(sg, sunimg, dx1, dy1, dx2, dy2,
1104
sx1, sy1, sx2, sy2, bgColor, observer);
1105
}
1106
}
1107
1108
public boolean transformImage(SunGraphics2D sg, Image img,
1109
AffineTransform atfm,
1110
ImageObserver observer) {
1111
if (!(img instanceof ToolkitImage)) {
1112
transformImage(sg, img, 0, 0, atfm, sg.interpolationType);
1113
return true;
1114
} else {
1115
ToolkitImage sunimg = (ToolkitImage)img;
1116
if (!imageReady(sunimg, observer)) {
1117
return false;
1118
}
1119
ImageRepresentation ir = sunimg.getImageRep();
1120
return ir.drawToBufImage(sg, sunimg, atfm, observer);
1121
}
1122
}
1123
1124
public void transformImage(SunGraphics2D sg, BufferedImage img,
1125
BufferedImageOp op, int x, int y)
1126
{
1127
if (op != null) {
1128
if (op instanceof AffineTransformOp) {
1129
AffineTransformOp atop = (AffineTransformOp) op;
1130
transformImage(sg, img, x, y,
1131
atop.getTransform(),
1132
atop.getInterpolationType());
1133
return;
1134
} else {
1135
img = op.filter(img, null);
1136
}
1137
}
1138
copyImage(sg, img, x, y, null);
1139
}
1140
}
1141
1142