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