History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/java2d/pipe/DrawImage.java (1 of 18352 files). Show all

Unified · Split

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