blob: 21397906da37d1d1fc9025227e7dce4f22103d55 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 helper.Transform(tmpmaskblit, srcData, tmpData,
513 AlphaComposite.Src, null,
514 itx, interpType,
515 sx1, sy1, sx2, sy2,
516 0, 0, dx2-dx1, dy2-dy1,
517 edges, dx1, dy1);
518
519 /*
520 * Now copy the results, scanline by scanline, into the dest.
521 * The edges array helps us minimize the work.
522 */
523 int index = 2;
524 for (int y = edges[0]; y < edges[1]; y++) {
525 int relx1 = edges[index++];
526 int relx2 = edges[index++];
527 if (relx1 >= relx2) {
528 continue;
529 }
530 if (maskblit != null) {
531 maskblit.MaskBlit(tmpData, dstData,
532 sg.composite, clip,
533 relx1, y,
534 dx1+relx1, dy1+y,
535 relx2 - relx1, 1,
536 null, 0, 0);
537 } else {
538 blit.Blit(tmpData, dstData,
539 sg.composite, clip,
540 relx1, y,
541 dx1+relx1, dy1+y,
542 relx2 - relx1, 1);
543 }
544 }
545 }
546
547 // Render an image using only integer translation
548 // (no scale or transform or sub-pixel interpolated translations).
549 protected boolean renderImageCopy(SunGraphics2D sg, Image img,
550 Color bgColor,
551 int dx, int dy,
552 int sx, int sy,
553 int w, int h)
554 {
555 Region clip = sg.getCompClip();
556 SurfaceData dstData = sg.surfaceData;
557
558 int attempts = 0;
559 // Loop up to twice through; this gives us a chance to
560 // revalidate the surfaceData objects in case of an exception
561 // and try it once more
562 while (true) {
563 SurfaceData srcData =
564 dstData.getSourceSurfaceData(img,
565 sg.TRANSFORM_ISIDENT,
566 sg.imageComp,
567 bgColor);
568 if (srcData == null) {
569 return false;
570 }
571
572 try {
573 SurfaceType srcType = srcData.getSurfaceType();
574 SurfaceType dstType = dstData.getSurfaceType();
575 blitSurfaceData(sg, clip,
576 srcData, dstData, srcType, dstType,
577 sx, sy, dx, dy, w, h, bgColor);
578 return true;
579 } catch (NullPointerException e) {
580 if (!(SurfaceData.isNull(dstData) ||
581 SurfaceData.isNull(srcData)))
582 {
583 // Something else caused the exception, throw it...
584 throw e;
585 }
586 return false;
587 // NOP if we have been disposed
588 } catch (InvalidPipeException e) {
589 // Always catch the exception; try this a couple of times
590 // and fail silently if the system is not yet ready to
591 // revalidate the source or dest surfaceData objects.
592 ++attempts;
593 clip = sg.getCompClip(); // ensures sg.surfaceData is valid
594 dstData = sg.surfaceData;
595 if (SurfaceData.isNull(dstData) ||
596 SurfaceData.isNull(srcData) || (attempts > 1))
597 {
598 return false;
599 }
600 }
601 }
602 }
603
604 // Render an image using only integer scaling (no transform).
605 protected boolean renderImageScale(SunGraphics2D sg, Image img,
606 Color bgColor, int interpType,
607 int sx1, int sy1,
608 int sx2, int sy2,
609 double dx1, double dy1,
610 double dx2, double dy2)
611 {
612 // Currently only NEAREST_NEIGHBOR interpolation is implemented
613 // for ScaledBlit operations.
614 if (interpType != AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
615 return false;
616 }
617
618 Region clip = sg.getCompClip();
619 SurfaceData dstData = sg.surfaceData;
620
621 int attempts = 0;
622 // Loop up to twice through; this gives us a chance to
623 // revalidate the surfaceData objects in case of an exception
624 // and try it once more
625 while (true) {
626 SurfaceData srcData =
627 dstData.getSourceSurfaceData(img,
628 sg.TRANSFORM_TRANSLATESCALE,
629 sg.imageComp,
630 bgColor);
631
632 if (srcData == null || isBgOperation(srcData, bgColor)) {
633 return false;
634 }
635
636 try {
637 SurfaceType srcType = srcData.getSurfaceType();
638 SurfaceType dstType = dstData.getSurfaceType();
639 return scaleSurfaceData(sg, clip,
640 srcData, dstData, srcType, dstType,
641 sx1, sy1, sx2, sy2,
642 dx1, dy1, dx2, dy2);
643 } catch (NullPointerException e) {
644 if (!SurfaceData.isNull(dstData)) {
645 // Something else caused the exception, throw it...
646 throw e;
647 }
648 return false;
649 // NOP if we have been disposed
650 } catch (InvalidPipeException e) {
651 // Always catch the exception; try this a couple of times
652 // and fail silently if the system is not yet ready to
653 // revalidate the source or dest surfaceData objects.
654 ++attempts;
655 clip = sg.getCompClip(); // ensures sg.surfaceData is valid
656 dstData = sg.surfaceData;
657 if (SurfaceData.isNull(dstData) ||
658 SurfaceData.isNull(srcData) || (attempts > 1))
659 {
660 return false;
661 }
662 }
663 }
664 }
665
666 public boolean scaleImage(SunGraphics2D sg, Image img,
667 int dx1, int dy1, int dx2, int dy2,
668 int sx1, int sy1, int sx2, int sy2,
669 Color bgColor)
670 {
671 int srcW, srcH, dstW, dstH;
672 int srcX, srcY, dstX, dstY;
673 boolean srcWidthFlip = false;
674 boolean srcHeightFlip = false;
675 boolean dstWidthFlip = false;
676 boolean dstHeightFlip = false;
677
678 if (sx2 > sx1) {
679 srcW = sx2 - sx1;
680 srcX = sx1;
681 } else {
682 srcWidthFlip = true;
683 srcW = sx1 - sx2;
684 srcX = sx2;
685 }
686 if (sy2 > sy1) {
687 srcH = sy2-sy1;
688 srcY = sy1;
689 } else {
690 srcHeightFlip = true;
691 srcH = sy1-sy2;
692 srcY = sy2;
693 }
694 if (dx2 > dx1) {
695 dstW = dx2 - dx1;
696 dstX = dx1;
697 } else {
698 dstW = dx1 - dx2;
699 dstWidthFlip = true;
700 dstX = dx2;
701 }
702 if (dy2 > dy1) {
703 dstH = dy2 - dy1;
704 dstY = dy1;
705 } else {
706 dstH = dy1 - dy2;
707 dstHeightFlip = true;
708 dstY = dy2;
709 }
710 if (srcW <= 0 || srcH <= 0) {
711 return true;
712 }
713 // Only accelerate scale if it does not involve a flip or transform
714 if ((srcWidthFlip == dstWidthFlip) &&
715 (srcHeightFlip == dstHeightFlip) &&
716 isSimpleTranslate(sg))
717 {
718 double ddx1 = dstX + sg.transX;
719 double ddy1 = dstY + sg.transY;
720 double ddx2 = ddx1 + dstW;
721 double ddy2 = ddy1 + dstH;
722 if (renderImageScale(sg, img, bgColor, sg.interpolationType,
723 srcX, srcY, srcX+srcW, srcY+srcH,
724 ddx1, ddy1, ddx2, ddy2))
725 {
726 return true;
727 }
728 }
729
730 AffineTransform atfm = new AffineTransform(sg.transform);
731 atfm.translate(dx1, dy1);
732 double m00 = (double)(dx2-dx1)/(sx2-sx1);
733 double m11 = (double)(dy2-dy1)/(sy2-sy1);
734 atfm.scale(m00, m11);
735 atfm.translate(srcX-sx1, srcY-sy1);
736
737 int imgW = img.getWidth(null);
738 int imgH = img.getHeight(null);
739 srcW += srcX;
740 srcH += srcY;
741 // Make sure we are not out of bounds
742 if (srcW > imgW) {
743 srcW = imgW;
744 }
745 if (srcH > imgH) {
746 srcH = imgH;
747 }
748 if (srcX < 0) {
749 atfm.translate(-srcX, 0);
750 srcX = 0;
751 }
752 if (srcY < 0) {
753 atfm.translate(0, -srcY);
754 srcY = 0;
755 }
756 if (srcX >= srcW || srcY >= srcH) {
757 return true;
758 }
759 // Note: src[WH] are currently the right and bottom coordinates.
760 // The following two lines would adjust src[WH] back to being
761 // dimensions.
762 // srcW -= srcX;
763 // srcH -= srcY;
764 // Since transformImage needs right and bottom coords we will
765 // omit this adjustment.
766
767 transformImage(sg, img, atfm, sg.interpolationType,
768 srcX, srcY, srcW, srcH, bgColor);
769 return true;
770 }
771
772 /**
773 ** Utilities
774 ** The following methods are used by the public methods above
775 ** for performing various operations
776 **/
777
778 /*
779 * This constant represents a tradeoff between the
780 * need to make sure that image transformations are
781 * "very close" to integer device coordinates before
782 * we decide to use an integer scale or copy operation
783 * as a substitute and the fact that roundoff errors
784 * in AffineTransforms are frequently introduced by
785 * performing multiple sequential operations on them.
786 *
787 * The evaluation of bug 4990624 details the potential
788 * for this error cutoff to result in display anomalies
789 * in different types of image operations and how this
790 * value represents a good compromise here.
791 */
792 private static final double MAX_TX_ERROR = .0001;
793
794 public static boolean closeToInteger(int i, double d) {
795 return (Math.abs(d-i) < MAX_TX_ERROR);
796 }
797
798 public static boolean isSimpleTranslate(SunGraphics2D sg) {
799 int ts = sg.transformState;
800 if (ts <= sg.TRANSFORM_INT_TRANSLATE) {
801 // Integer translates are always "simple"
802 return true;
803 }
804 if (ts >= sg.TRANSFORM_TRANSLATESCALE) {
805 // Scales and beyond are always "not simple"
806 return false;
807 }
808 // non-integer translates are only simple when not interpolating
809 if (sg.interpolationType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
810 return true;
811 }
812 return false;
813 }
814
815 protected static boolean isBgOperation(SurfaceData srcData, Color bgColor) {
816 // If we cannot get the srcData, then cannot assume anything about
817 // the image
818 return ((srcData == null) ||
819 ((bgColor != null) &&
820 (srcData.getTransparency() != Transparency.OPAQUE)));
821 }
822
823 protected BufferedImage getBufferedImage(Image img) {
824 if (img instanceof BufferedImage) {
825 return (BufferedImage)img;
826 }
827 // Must be VolatileImage; get BufferedImage representation
828 return ((VolatileImage)img).getSnapshot();
829 }
830
831 /*
832 * Return the color model to be used with this BufferedImage and
833 * transform.
834 */
835 private ColorModel getTransformColorModel(SunGraphics2D sg,
836 BufferedImage bImg,
837 AffineTransform tx) {
838 ColorModel cm = bImg.getColorModel();
839 ColorModel dstCM = cm;
840
841 if (tx.isIdentity()) {
842 return dstCM;
843 }
844 int type = tx.getType();
845 boolean needTrans =
846 ((type&(tx.TYPE_MASK_ROTATION|tx.TYPE_GENERAL_TRANSFORM)) != 0);
847 if (! needTrans && type != tx.TYPE_TRANSLATION && type != tx.TYPE_IDENTITY)
848 {
849 double[] mtx = new double[4];
850 tx.getMatrix(mtx);
851 // Check out the matrix. A non-integral scale will force ARGB
852 // since the edge conditions cannot be guaranteed.
853 needTrans = (mtx[0] != (int)mtx[0] || mtx[3] != (int)mtx[3]);
854 }
855
856 if (sg.renderHint != SunHints.INTVAL_RENDER_QUALITY) {
857 if (cm instanceof IndexColorModel) {
858 Raster raster = bImg.getRaster();
859 IndexColorModel icm = (IndexColorModel) cm;
860 // Just need to make sure that we have a transparent pixel
861 if (needTrans && cm.getTransparency() == cm.OPAQUE) {
862 // Fix 4221407
863 if (raster instanceof sun.awt.image.BytePackedRaster) {
864 dstCM = ColorModel.getRGBdefault();
865 }
866 else {
867 double[] matrix = new double[6];
868 tx.getMatrix(matrix);
869 if (matrix[1] == 0. && matrix[2] ==0.
870 && matrix[4] == 0. && matrix[5] == 0.) {
871 // Only scaling so do not need to create
872 }
873 else {
874 int mapSize = icm.getMapSize();
875 if (mapSize < 256) {
876 int[] cmap = new int[mapSize+1];
877 icm.getRGBs(cmap);
878 cmap[mapSize] = 0x0000;
879 dstCM = new
880 IndexColorModel(icm.getPixelSize(),
881 mapSize+1,
882 cmap, 0, true, mapSize,
883 DataBuffer.TYPE_BYTE);
884 }
885 else {
886 dstCM = ColorModel.getRGBdefault();
887 }
888 } /* if (matrix[0] < 1.f ...) */
889 } /* raster instanceof sun.awt.image.BytePackedRaster */
890 } /* if (cm.getTransparency() == cm.OPAQUE) */
891 } /* if (cm instanceof IndexColorModel) */
892 else if (needTrans && cm.getTransparency() == cm.OPAQUE) {
893 // Need a bitmask transparency
894 // REMIND: for now, use full transparency since no loops
895 // for bitmask
896 dstCM = ColorModel.getRGBdefault();
897 }
898 } /* if (sg.renderHint == RENDER_QUALITY) */
899 else {
900
901 if (cm instanceof IndexColorModel ||
902 (needTrans && cm.getTransparency() == cm.OPAQUE))
903 {
904 // Need a bitmask transparency
905 // REMIND: for now, use full transparency since no loops
906 // for bitmask
907 dstCM = ColorModel.getRGBdefault();
908 }
909 }
910
911 return dstCM;
912 }
913
914 protected void blitSurfaceData(SunGraphics2D sg,
915 Region clipRegion,
916 SurfaceData srcData,
917 SurfaceData dstData,
918 SurfaceType srcType,
919 SurfaceType dstType,
920 int sx, int sy, int dx, int dy,
921 int w, int h,
922 Color bgColor)
923 {
924 if (w <= 0 || h <= 0) {
925 /*
926 * Fix for bugid 4783274 - BlitBg throws an exception for
927 * a particular set of anomalous parameters.
928 * REMIND: The native loops do proper clipping and would
929 * detect this situation themselves, but the Java loops
930 * all seem to trust their parameters a little too well
931 * to the point where they will try to process a negative
932 * area of pixels and throw exceptions. The real fix is
933 * to modify the Java loops to do proper clipping so that
934 * they can deal with negative dimensions as well as
935 * improperly large dimensions, but that fix is too risky
936 * to integrate for Mantis at this point. In the meantime
937 * eliminating the negative or zero dimensions here is
938 * "correct" and saves them from some nasty exceptional
939 * conditions, one of which is the test case of 4783274.
940 */
941 return;
942 }
943 CompositeType comp = sg.imageComp;
944 if (CompositeType.SrcOverNoEa.equals(comp) &&
945 (srcData.getTransparency() == Transparency.OPAQUE ||
946 (bgColor != null &&
947 bgColor.getTransparency() == Transparency.OPAQUE)))
948 {
949 comp = CompositeType.SrcNoEa;
950 }
951 if (!isBgOperation(srcData, bgColor)) {
952 Blit blit = Blit.getFromCache(srcType, comp, dstType);
953 blit.Blit(srcData, dstData, sg.composite, clipRegion,
954 sx, sy, dx, dy, w, h);
955 } else {
956 BlitBg blit = BlitBg.getFromCache(srcType, comp, dstType);
957 blit.BlitBg(srcData, dstData, sg.composite, clipRegion,
958 bgColor, sx, sy, dx, dy, w, h);
959 }
960 }
961
962 protected boolean scaleSurfaceData(SunGraphics2D sg,
963 Region clipRegion,
964 SurfaceData srcData,
965 SurfaceData dstData,
966 SurfaceType srcType,
967 SurfaceType dstType,
968 int sx1, int sy1,
969 int sx2, int sy2,
970 double dx1, double dy1,
971 double dx2, double dy2)
972 {
973 CompositeType comp = sg.imageComp;
974 if (CompositeType.SrcOverNoEa.equals(comp) &&
975 (srcData.getTransparency() == Transparency.OPAQUE))
976 {
977 comp = CompositeType.SrcNoEa;
978 }
979
980 ScaledBlit blit = ScaledBlit.getFromCache(srcType, comp, dstType);
981 if (blit != null) {
982 blit.Scale(srcData, dstData, sg.composite, clipRegion,
983 sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
984 return true;
985 }
986 return false;
987 }
988
989 protected static boolean imageReady(ToolkitImage sunimg,
990 ImageObserver observer)
991 {
992 if (sunimg.hasError()) {
993 if (observer != null) {
994 observer.imageUpdate(sunimg,
995 ImageObserver.ERROR|ImageObserver.ABORT,
996 -1, -1, -1, -1);
997 }
998 return false;
999 }
1000 return true;
1001 }
1002
1003 public boolean copyImage(SunGraphics2D sg, Image img,
1004 int x, int y,
1005 Color bgColor,
1006 ImageObserver observer) {
1007 if (!(img instanceof ToolkitImage)) {
1008 return copyImage(sg, img, x, y, bgColor);
1009 } else {
1010 ToolkitImage sunimg = (ToolkitImage)img;
1011 if (!imageReady(sunimg, observer)) {
1012 return false;
1013 }
1014 ImageRepresentation ir = sunimg.getImageRep();
1015 return ir.drawToBufImage(sg, sunimg, x, y, bgColor, observer);
1016 }
1017 }
1018
1019 public boolean copyImage(SunGraphics2D sg, Image img,
1020 int dx, int dy, int sx, int sy, int w, int h,
1021 Color bgColor,
1022 ImageObserver observer) {
1023 if (!(img instanceof ToolkitImage)) {
1024 return copyImage(sg, img, dx, dy, sx, sy, w, h, bgColor);
1025 } else {
1026 ToolkitImage sunimg = (ToolkitImage)img;
1027 if (!imageReady(sunimg, observer)) {
1028 return false;
1029 }
1030 ImageRepresentation ir = sunimg.getImageRep();
1031 return ir.drawToBufImage(sg, sunimg,
1032 dx, dy, (dx + w), (dy + h),
1033 sx, sy, (sx + w), (sy + h),
1034 bgColor, observer);
1035 }
1036 }
1037
1038 public boolean scaleImage(SunGraphics2D sg, Image img,
1039 int x, int y,
1040 int width, int height,
1041 Color bgColor,
1042 ImageObserver observer) {
1043 if (!(img instanceof ToolkitImage)) {
1044 return scaleImage(sg, img, x, y, width, height, bgColor);
1045 } else {
1046 ToolkitImage sunimg = (ToolkitImage)img;
1047 if (!imageReady(sunimg, observer)) {
1048 return false;
1049 }
1050 ImageRepresentation ir = sunimg.getImageRep();
1051 return ir.drawToBufImage(sg, sunimg, x, y, width, height, bgColor,
1052 observer);
1053 }
1054 }
1055
1056 public boolean scaleImage(SunGraphics2D sg, Image img,
1057 int dx1, int dy1, int dx2, int dy2,
1058 int sx1, int sy1, int sx2, int sy2,
1059 Color bgColor,
1060 ImageObserver observer) {
1061 if (!(img instanceof ToolkitImage)) {
1062 return scaleImage(sg, img, dx1, dy1, dx2, dy2,
1063 sx1, sy1, sx2, sy2, bgColor);
1064 } else {
1065 ToolkitImage sunimg = (ToolkitImage)img;
1066 if (!imageReady(sunimg, observer)) {
1067 return false;
1068 }
1069 ImageRepresentation ir = sunimg.getImageRep();
1070 return ir.drawToBufImage(sg, sunimg, dx1, dy1, dx2, dy2,
1071 sx1, sy1, sx2, sy2, bgColor, observer);
1072 }
1073 }
1074
1075 public boolean transformImage(SunGraphics2D sg, Image img,
1076 AffineTransform atfm,
1077 ImageObserver observer) {
1078 if (!(img instanceof ToolkitImage)) {
1079 transformImage(sg, img, 0, 0, atfm, sg.interpolationType);
1080 return true;
1081 } else {
1082 ToolkitImage sunimg = (ToolkitImage)img;
1083 if (!imageReady(sunimg, observer)) {
1084 return false;
1085 }
1086 ImageRepresentation ir = sunimg.getImageRep();
1087 return ir.drawToBufImage(sg, sunimg, atfm, observer);
1088 }
1089 }
1090
1091 public void transformImage(SunGraphics2D sg, BufferedImage img,
1092 BufferedImageOp op, int x, int y)
1093 {
1094 if (op != null) {
1095 if (op instanceof AffineTransformOp) {
1096 AffineTransformOp atop = (AffineTransformOp) op;
1097 transformImage(sg, img, x, y,
1098 atop.getTransform(),
1099 atop.getInterpolationType());
1100 return;
1101 } else {
1102 img = op.filter(img, null);
1103 }
1104 }
1105 copyImage(sg, img, x, y, null);
1106 }
1107}