1 /* 2 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.print; 27 28 import java.lang.ref.SoftReference; 29 import java.util.Hashtable; 30 import sun.font.CharToGlyphMapper; 31 import sun.font.CompositeFont; 32 import sun.font.Font2D; 33 import sun.font.Font2DHandle; 34 import sun.font.FontManager; 35 import sun.font.FontManagerFactory; 36 import sun.font.FontUtilities; 37 38 import java.awt.Color; 39 import java.awt.Font; 40 import java.awt.Graphics2D; 41 import java.awt.Image; 42 import java.awt.Paint; 43 import java.awt.Polygon; 44 import java.awt.Shape; 45 46 import java.awt.geom.Path2D; 47 import java.text.AttributedCharacterIterator; 48 49 import java.awt.font.FontRenderContext; 50 import java.awt.font.GlyphVector; 51 import java.awt.font.TextAttribute; 52 import java.awt.font.TextLayout; 53 54 import java.awt.geom.AffineTransform; 55 import java.awt.geom.Arc2D; 56 import java.awt.geom.Ellipse2D; 57 import java.awt.geom.Line2D; 58 import java.awt.geom.Point2D; 59 import java.awt.geom.Rectangle2D; 60 import java.awt.geom.RoundRectangle2D; 61 import java.awt.geom.PathIterator; 62 63 import java.awt.image.BufferedImage; 64 import java.awt.image.BufferedImageOp; 65 import java.awt.image.ColorModel; 66 import java.awt.image.DataBuffer; 67 import java.awt.image.DataBufferInt; 68 import java.awt.image.ImageObserver; 69 import java.awt.image.IndexColorModel; 70 import java.awt.image.Raster; 71 import java.awt.image.RenderedImage; 72 import java.awt.image.SampleModel; 73 import java.awt.image.SinglePixelPackedSampleModel; 74 import java.awt.image.VolatileImage; 75 import sun.awt.image.ByteComponentRaster; 76 import sun.awt.image.ToolkitImage; 77 import sun.awt.image.SunWritableRaster; 78 79 import java.awt.print.PageFormat; 80 import java.awt.print.Printable; 81 import java.awt.print.PrinterException; 82 import java.awt.print.PrinterGraphics; 83 import java.awt.print.PrinterJob; 84 85 import java.util.Map; 86 87 public abstract class PathGraphics extends ProxyGraphics2D { 88 89 private Printable mPainter; 90 private PageFormat mPageFormat; 91 private int mPageIndex; 92 private boolean mCanRedraw; 93 protected boolean printingGlyphVector; 94 95 protected PathGraphics(Graphics2D graphics, PrinterJob printerJob, 96 Printable painter, PageFormat pageFormat, 97 int pageIndex, boolean canRedraw) { 98 super(graphics, printerJob); 99 100 mPainter = painter; 101 mPageFormat = pageFormat; 102 mPageIndex = pageIndex; 103 mCanRedraw = canRedraw; 104 } 105 106 /** 107 * Return the Printable instance responsible for drawing 108 * into this Graphics. 109 */ 110 protected Printable getPrintable() { 111 return mPainter; 112 } 113 114 /** 115 * Return the PageFormat associated with this page of 116 * Graphics. 117 */ 118 protected PageFormat getPageFormat() { 119 return mPageFormat; 120 } 121 122 /** 123 * Return the page index associated with this Graphics. 124 */ 125 protected int getPageIndex() { 126 return mPageIndex; 127 } 128 129 /** 130 * Return true if we are allowed to ask the application 131 * to redraw portions of the page. In general, with the 132 * PrinterJob API, the application can be asked to do a 133 * redraw. When PrinterJob is emulating PrintJob then we 134 * can not. 135 */ 136 public boolean canDoRedraws() { 137 return mCanRedraw; 138 } 139 140 /** 141 * Redraw a rectanglular area using a proxy graphics 142 */ 143 public abstract void redrawRegion(Rectangle2D region, 144 double scaleX, double scaleY, 145 Shape clip, 146 AffineTransform devTransform) 147 148 throws PrinterException ; 149 150 /** 151 * Draws a line, using the current color, between the points 152 * <code>(x1, y1)</code> and <code>(x2, y2)</code> 153 * in this graphics context's coordinate system. 154 * @param x1 the first point's <i>x</i> coordinate. 155 * @param y1 the first point's <i>y</i> coordinate. 156 * @param x2 the second point's <i>x</i> coordinate. 157 * @param y2 the second point's <i>y</i> coordinate. 158 */ 159 public void drawLine(int x1, int y1, int x2, int y2) { 160 161 Paint paint = getPaint(); 162 163 try { 164 AffineTransform deviceTransform = getTransform(); 165 if (getClip() != null) { 166 deviceClip(getClip().getPathIterator(deviceTransform)); 167 } 168 169 deviceDrawLine(x1, y1, x2, y2, (Color) paint); 170 171 } catch (ClassCastException e) { 172 throw new IllegalArgumentException("Expected a Color instance"); 173 } 174 } 175 176 177 /** 178 * Draws the outline of the specified rectangle. 179 * The left and right edges of the rectangle are at 180 * {@code x} and <code>x + width</code>. 181 * The top and bottom edges are at 182 * {@code y} and <code>y + height</code>. 183 * The rectangle is drawn using the graphics context's current color. 184 * @param x the <i>x</i> coordinate 185 * of the rectangle to be drawn. 186 * @param y the <i>y</i> coordinate 187 * of the rectangle to be drawn. 188 * @param width the width of the rectangle to be drawn. 189 * @param height the height of the rectangle to be drawn. 190 * @see java.awt.Graphics#fillRect 191 * @see java.awt.Graphics#clearRect 192 */ 193 public void drawRect(int x, int y, int width, int height) { 194 195 Paint paint = getPaint(); 196 197 try { 198 AffineTransform deviceTransform = getTransform(); 199 if (getClip() != null) { 200 deviceClip(getClip().getPathIterator(deviceTransform)); 201 } 202 203 deviceFrameRect(x, y, width, height, (Color) paint); 204 205 } catch (ClassCastException e) { 206 throw new IllegalArgumentException("Expected a Color instance"); 207 } 208 209 } 210 211 /** 212 * Fills the specified rectangle. 213 * The left and right edges of the rectangle are at 214 * {@code x} and <code>x + width - 1</code>. 215 * The top and bottom edges are at 216 * {@code y} and <code>y + height - 1</code>. 217 * The resulting rectangle covers an area 218 * {@code width} pixels wide by 219 * {@code height} pixels tall. 220 * The rectangle is filled using the graphics context's current color. 221 * @param x the <i>x</i> coordinate 222 * of the rectangle to be filled. 223 * @param y the <i>y</i> coordinate 224 * of the rectangle to be filled. 225 * @param width the width of the rectangle to be filled. 226 * @param height the height of the rectangle to be filled. 227 * @see java.awt.Graphics#clearRect 228 * @see java.awt.Graphics#drawRect 229 */ 230 public void fillRect(int x, int y, int width, int height){ 231 232 Paint paint = getPaint(); 233 234 try { 235 AffineTransform deviceTransform = getTransform(); 236 if (getClip() != null) { 237 deviceClip(getClip().getPathIterator(deviceTransform)); 238 } 239 240 deviceFillRect(x, y, width, height, (Color) paint); 241 242 } catch (ClassCastException e) { 243 throw new IllegalArgumentException("Expected a Color instance"); 244 } 245 } 246 247 /** 248 * Clears the specified rectangle by filling it with the background 249 * color of the current drawing surface. This operation does not 250 * use the current paint mode. 251 * <p> 252 * Beginning with Java 1.1, the background color 253 * of offscreen images may be system dependent. Applications should 254 * use {@code setColor} followed by {@code fillRect} to 255 * ensure that an offscreen image is cleared to a specific color. 256 * @param x the <i>x</i> coordinate of the rectangle to clear. 257 * @param y the <i>y</i> coordinate of the rectangle to clear. 258 * @param width the width of the rectangle to clear. 259 * @param height the height of the rectangle to clear. 260 * @see java.awt.Graphics#fillRect(int, int, int, int) 261 * @see java.awt.Graphics#drawRect 262 * @see java.awt.Graphics#setColor(java.awt.Color) 263 * @see java.awt.Graphics#setPaintMode 264 * @see java.awt.Graphics#setXORMode(java.awt.Color) 265 */ 266 public void clearRect(int x, int y, int width, int height) { 267 268 fill(new Rectangle2D.Float(x, y, width, height), getBackground()); 269 } 270 271 /** 272 * Draws an outlined round-cornered rectangle using this graphics 273 * context's current color. The left and right edges of the rectangle 274 * are at {@code x} and <code>x + width</code>, 275 * respectively. The top and bottom edges of the rectangle are at 276 * {@code y} and <code>y + height</code>. 277 * @param x the <i>x</i> coordinate of the rectangle to be drawn. 278 * @param y the <i>y</i> coordinate of the rectangle to be drawn. 279 * @param width the width of the rectangle to be drawn. 280 * @param height the height of the rectangle to be drawn. 281 * @param arcWidth the horizontal diameter of the arc 282 * at the four corners. 283 * @param arcHeight the vertical diameter of the arc 284 * at the four corners. 285 * @see java.awt.Graphics#fillRoundRect 286 */ 287 public void drawRoundRect(int x, int y, int width, int height, 288 int arcWidth, int arcHeight) { 289 290 draw(new RoundRectangle2D.Float(x, y, 291 width, height, 292 arcWidth, arcHeight)); 293 } 294 295 296 /** 297 * Fills the specified rounded corner rectangle with the current color. 298 * The left and right edges of the rectangle 299 * are at {@code x} and <code>x + width - 1</code>, 300 * respectively. The top and bottom edges of the rectangle are at 301 * {@code y} and <code>y + height - 1</code>. 302 * @param x the <i>x</i> coordinate of the rectangle to be filled. 303 * @param y the <i>y</i> coordinate of the rectangle to be filled. 304 * @param width the width of the rectangle to be filled. 305 * @param height the height of the rectangle to be filled. 306 * @param arcWidth the horizontal diameter 307 * of the arc at the four corners. 308 * @param arcHeight the vertical diameter 309 * of the arc at the four corners. 310 * @see java.awt.Graphics#drawRoundRect 311 */ 312 public void fillRoundRect(int x, int y, int width, int height, 313 int arcWidth, int arcHeight) { 314 315 fill(new RoundRectangle2D.Float(x, y, 316 width, height, 317 arcWidth, arcHeight)); 318 } 319 320 /** 321 * Draws the outline of an oval. 322 * The result is a circle or ellipse that fits within the 323 * rectangle specified by the {@code x}, {@code y}, 324 * {@code width}, and {@code height} arguments. 325 * <p> 326 * The oval covers an area that is 327 * <code>width + 1</code> pixels wide 328 * and <code>height + 1</code> pixels tall. 329 * @param x the <i>x</i> coordinate of the upper left 330 * corner of the oval to be drawn. 331 * @param y the <i>y</i> coordinate of the upper left 332 * corner of the oval to be drawn. 333 * @param width the width of the oval to be drawn. 334 * @param height the height of the oval to be drawn. 335 * @see java.awt.Graphics#fillOval 336 * @since 1.0 337 */ 338 public void drawOval(int x, int y, int width, int height) { 339 draw(new Ellipse2D.Float(x, y, width, height)); 340 } 341 342 /** 343 * Fills an oval bounded by the specified rectangle with the 344 * current color. 345 * @param x the <i>x</i> coordinate of the upper left corner 346 * of the oval to be filled. 347 * @param y the <i>y</i> coordinate of the upper left corner 348 * of the oval to be filled. 349 * @param width the width of the oval to be filled. 350 * @param height the height of the oval to be filled. 351 * @see java.awt.Graphics#drawOval 352 */ 353 public void fillOval(int x, int y, int width, int height){ 354 355 fill(new Ellipse2D.Float(x, y, width, height)); 356 } 357 358 /** 359 * Draws the outline of a circular or elliptical arc 360 * covering the specified rectangle. 361 * <p> 362 * The resulting arc begins at {@code startAngle} and extends 363 * for {@code arcAngle} degrees, using the current color. 364 * Angles are interpreted such that 0 degrees 365 * is at the 3 o'clock position. 366 * A positive value indicates a counter-clockwise rotation 367 * while a negative value indicates a clockwise rotation. 368 * <p> 369 * The center of the arc is the center of the rectangle whose origin 370 * is (<i>x</i>, <i>y</i>) and whose size is specified by the 371 * {@code width} and {@code height} arguments. 372 * <p> 373 * The resulting arc covers an area 374 * <code>width + 1</code> pixels wide 375 * by <code>height + 1</code> pixels tall. 376 * <p> 377 * The angles are specified relative to the non-square extents of 378 * the bounding rectangle such that 45 degrees always falls on the 379 * line from the center of the ellipse to the upper right corner of 380 * the bounding rectangle. As a result, if the bounding rectangle is 381 * noticeably longer in one axis than the other, the angles to the 382 * start and end of the arc segment will be skewed farther along the 383 * longer axis of the bounds. 384 * @param x the <i>x</i> coordinate of the 385 * upper-left corner of the arc to be drawn. 386 * @param y the <i>y</i> coordinate of the 387 * upper-left corner of the arc to be drawn. 388 * @param width the width of the arc to be drawn. 389 * @param height the height of the arc to be drawn. 390 * @param startAngle the beginning angle. 391 * @param arcAngle the angular extent of the arc, 392 * relative to the start angle. 393 * @see java.awt.Graphics#fillArc 394 */ 395 public void drawArc(int x, int y, int width, int height, 396 int startAngle, int arcAngle) { 397 draw(new Arc2D.Float(x, y, width, height, 398 startAngle, arcAngle, 399 Arc2D.OPEN)); 400 } 401 402 403 /** 404 * Fills a circular or elliptical arc covering the specified rectangle. 405 * <p> 406 * The resulting arc begins at {@code startAngle} and extends 407 * for {@code arcAngle} degrees. 408 * Angles are interpreted such that 0 degrees 409 * is at the 3 o'clock position. 410 * A positive value indicates a counter-clockwise rotation 411 * while a negative value indicates a clockwise rotation. 412 * <p> 413 * The center of the arc is the center of the rectangle whose origin 414 * is (<i>x</i>, <i>y</i>) and whose size is specified by the 415 * {@code width} and {@code height} arguments. 416 * <p> 417 * The resulting arc covers an area 418 * <code>width + 1</code> pixels wide 419 * by <code>height + 1</code> pixels tall. 420 * <p> 421 * The angles are specified relative to the non-square extents of 422 * the bounding rectangle such that 45 degrees always falls on the 423 * line from the center of the ellipse to the upper right corner of 424 * the bounding rectangle. As a result, if the bounding rectangle is 425 * noticeably longer in one axis than the other, the angles to the 426 * start and end of the arc segment will be skewed farther along the 427 * longer axis of the bounds. 428 * @param x the <i>x</i> coordinate of the 429 * upper-left corner of the arc to be filled. 430 * @param y the <i>y</i> coordinate of the 431 * upper-left corner of the arc to be filled. 432 * @param width the width of the arc to be filled. 433 * @param height the height of the arc to be filled. 434 * @param startAngle the beginning angle. 435 * @param arcAngle the angular extent of the arc, 436 * relative to the start angle. 437 * @see java.awt.Graphics#drawArc 438 */ 439 public void fillArc(int x, int y, int width, int height, 440 int startAngle, int arcAngle) { 441 442 fill(new Arc2D.Float(x, y, width, height, 443 startAngle, arcAngle, 444 Arc2D.PIE)); 445 } 446 447 /** 448 * Draws a sequence of connected lines defined by 449 * arrays of <i>x</i> and <i>y</i> coordinates. 450 * Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point. 451 * The figure is not closed if the first point 452 * differs from the last point. 453 * @param xPoints an array of <i>x</i> points 454 * @param yPoints an array of <i>y</i> points 455 * @param nPoints the total number of points 456 * @see java.awt.Graphics#drawPolygon(int[], int[], int) 457 * @since 1.1 458 */ 459 public void drawPolyline(int xPoints[], int yPoints[], 460 int nPoints) { 461 462 if (nPoints == 2) { 463 draw(new Line2D.Float(xPoints[0], yPoints[0], 464 xPoints[1], yPoints[1])); 465 } else if (nPoints > 2) { 466 Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD, nPoints); 467 path.moveTo(xPoints[0], yPoints[0]); 468 for(int i = 1; i < nPoints; i++) { 469 path.lineTo(xPoints[i], yPoints[i]); 470 } 471 draw(path); 472 } 473 } 474 475 476 /** 477 * Draws a closed polygon defined by 478 * arrays of <i>x</i> and <i>y</i> coordinates. 479 * Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point. 480 * <p> 481 * This method draws the polygon defined by {@code nPoint} line 482 * segments, where the first <code>nPoint - 1</code> 483 * line segments are line segments from 484 * <code>(xPoints[i - 1], yPoints[i - 1])</code> 485 * to <code>(xPoints[i], yPoints[i])</code>, for 486 * 1 ≤ <i>i</i> ≤ {@code nPoints}. 487 * The figure is automatically closed by drawing a line connecting 488 * the final point to the first point, if those points are different. 489 * @param xPoints a an array of {@code x} coordinates. 490 * @param yPoints a an array of {@code y} coordinates. 491 * @param nPoints a the total number of points. 492 * @see java.awt.Graphics#fillPolygon 493 * @see java.awt.Graphics#drawPolyline 494 */ 495 public void drawPolygon(int xPoints[], int yPoints[], 496 int nPoints) { 497 498 draw(new Polygon(xPoints, yPoints, nPoints)); 499 } 500 501 /** 502 * Draws the outline of a polygon defined by the specified 503 * {@code Polygon} object. 504 * @param p the polygon to draw. 505 * @see java.awt.Graphics#fillPolygon 506 * @see java.awt.Graphics#drawPolyline 507 */ 508 public void drawPolygon(Polygon p) { 509 draw(p); 510 } 511 512 /** 513 * Fills a closed polygon defined by 514 * arrays of <i>x</i> and <i>y</i> coordinates. 515 * <p> 516 * This method draws the polygon defined by {@code nPoint} line 517 * segments, where the first <code>nPoint - 1</code> 518 * line segments are line segments from 519 * <code>(xPoints[i - 1], yPoints[i - 1])</code> 520 * to <code>(xPoints[i], yPoints[i])</code>, for 521 * 1 ≤ <i>i</i> ≤ {@code nPoints}. 522 * The figure is automatically closed by drawing a line connecting 523 * the final point to the first point, if those points are different. 524 * <p> 525 * The area inside the polygon is defined using an 526 * even-odd fill rule, also known as the alternating rule. 527 * @param xPoints a an array of {@code x} coordinates. 528 * @param yPoints a an array of {@code y} coordinates. 529 * @param nPoints a the total number of points. 530 * @see java.awt.Graphics#drawPolygon(int[], int[], int) 531 */ 532 public void fillPolygon(int xPoints[], int yPoints[], 533 int nPoints) { 534 535 fill(new Polygon(xPoints, yPoints, nPoints)); 536 } 537 538 539 /** 540 * Fills the polygon defined by the specified Polygon object with 541 * the graphics context's current color. 542 * <p> 543 * The area inside the polygon is defined using an 544 * even-odd fill rule, also known as the alternating rule. 545 * @param p the polygon to fill. 546 * @see java.awt.Graphics#drawPolygon(int[], int[], int) 547 */ 548 public void fillPolygon(Polygon p) { 549 550 fill(p); 551 } 552 553 /** 554 * Draws the text given by the specified string, using this 555 * graphics context's current font and color. The baseline of the 556 * first character is at position (<i>x</i>, <i>y</i>) in this 557 * graphics context's coordinate system. 558 * @param str the string to be drawn. 559 * @param x the <i>x</i> coordinate. 560 * @param y the <i>y</i> coordinate. 561 * @see java.awt.Graphics#drawBytes 562 * @see java.awt.Graphics#drawChars 563 * @since 1.0 564 */ 565 public void drawString(String str, int x, int y) { 566 drawString(str, (float) x, (float) y); 567 } 568 569 public void drawString(String str, float x, float y) { 570 if (str.length() == 0) { 571 return; 572 } 573 TextLayout layout = 574 new TextLayout(str, getFont(), getFontRenderContext()); 575 layout.draw(this, x, y); 576 } 577 578 protected void drawString(String str, float x, float y, 579 Font font, FontRenderContext frc, float w) { 580 TextLayout layout = 581 new TextLayout(str, font, frc); 582 Shape textShape = 583 layout.getOutline(AffineTransform.getTranslateInstance(x, y)); 584 fill(textShape); 585 } 586 587 /** 588 * Draws the text given by the specified iterator, using this 589 * graphics context's current color. The iterator has to specify a font 590 * for each character. The baseline of the 591 * first character is at position (<i>x</i>, <i>y</i>) in this 592 * graphics context's coordinate system. 593 * @param iterator the iterator whose text is to be drawn 594 * @param x the <i>x</i> coordinate. 595 * @param y the <i>y</i> coordinate. 596 * @see java.awt.Graphics#drawBytes 597 * @see java.awt.Graphics#drawChars 598 */ 599 public void drawString(AttributedCharacterIterator iterator, 600 int x, int y) { 601 drawString(iterator, (float) x, (float) y); 602 } 603 public void drawString(AttributedCharacterIterator iterator, 604 float x, float y) { 605 if (iterator == null) { 606 throw 607 new NullPointerException("attributedcharacteriterator is null"); 608 } 609 TextLayout layout = 610 new TextLayout(iterator, getFontRenderContext()); 611 layout.draw(this, x, y); 612 } 613 614 /** 615 * Draws a GlyphVector. 616 * The rendering attributes applied include the clip, transform, 617 * paint or color, and composite attributes. The GlyphVector specifies 618 * individual glyphs from a Font. 619 * @param g The GlyphVector to be drawn. 620 * @param x,y The coordinates where the glyphs should be drawn. 621 * @see #setPaint 622 * @see java.awt.Graphics#setColor 623 * @see #transform 624 * @see #setTransform 625 * @see #setComposite 626 * @see #clip 627 * @see #setClip 628 */ 629 public void drawGlyphVector(GlyphVector g, 630 float x, 631 float y) { 632 633 /* We should not reach here if printingGlyphVector is already true. 634 * Add an assert so this can be tested if need be. 635 * But also ensure that we do at least render properly by filling 636 * the outline. 637 */ 638 if (printingGlyphVector) { 639 assert !printingGlyphVector; // ie false. 640 fill(g.getOutline(x, y)); 641 return; 642 } 643 644 try { 645 printingGlyphVector = true; 646 if (RasterPrinterJob.shapeTextProp || 647 !printedSimpleGlyphVector(g, x, y)) { 648 fill(g.getOutline(x, y)); 649 } 650 } finally { 651 printingGlyphVector = false; 652 } 653 } 654 655 protected static SoftReference<Hashtable<Font2DHandle,Object>> 656 fontMapRef = new SoftReference<Hashtable<Font2DHandle,Object>>(null); 657 658 protected int platformFontCount(Font font, String str) { 659 return 0; 660 } 661 662 /** 663 * Default implementation returns false. 664 * Callers of this method must always be prepared for this, 665 * and delegate to outlines or some other solution. 666 */ 667 protected boolean printGlyphVector(GlyphVector gv, float x, float y) { 668 return false; 669 } 670 671 /* GlyphVectors are usually encountered because TextLayout is in use. 672 * Some times TextLayout is needed to handle complex text or some 673 * rendering attributes trigger it. 674 * We try to print GlyphVectors by reconstituting into a String, 675 * as that is most recoverable for applications that export to formats 676 * such as Postscript or PDF. In some cases (eg where its not complex 677 * text and its just that positions aren't what we'd expect) we print 678 * one character at a time. positioning individually. 679 * Failing that, if we can directly send glyph codes to the printer 680 * then we do that (printGlyphVector). 681 * As a last resort we return false and let the caller print as filled 682 * shapes. 683 */ 684 boolean printedSimpleGlyphVector(GlyphVector g, float x, float y) { 685 686 int flags = g.getLayoutFlags(); 687 688 /* We can't handle RTL, re-ordering, complex glyphs etc by 689 * reconstituting glyphs into a String. So if any flags besides 690 * position adjustments are set, see if we can directly 691 * print the GlyphVector as glyph codes, using the positions 692 * layout has assigned. If that fails return false; 693 */ 694 if (flags != 0 && flags != GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) { 695 return printGlyphVector(g, x, y); 696 } 697 698 Font font = g.getFont(); 699 Font2D font2D = FontUtilities.getFont2D(font); 700 if (font2D.handle.font2D != font2D) { 701 /* suspicious, may be a bad font. lets bail */ 702 return false; 703 } 704 Hashtable<Font2DHandle,Object> fontMap; 705 synchronized (PathGraphics.class) { 706 fontMap = fontMapRef.get(); 707 if (fontMap == null) { 708 fontMap = new Hashtable<Font2DHandle,Object>(); 709 fontMapRef = 710 new SoftReference<Hashtable<Font2DHandle,Object>>(fontMap); 711 } 712 } 713 714 int numGlyphs = g.getNumGlyphs(); 715 int[] glyphCodes = g.getGlyphCodes(0, numGlyphs, null); 716 717 char[] glyphToCharMap = null; 718 char[][] mapArray = null; 719 CompositeFont cf = null; 720 721 /* Build the needed maps for this font in a synchronized block */ 722 synchronized (fontMap) { 723 if (font2D instanceof CompositeFont) { 724 cf = (CompositeFont)font2D; 725 int numSlots = cf.getNumSlots(); 726 mapArray = (char[][])fontMap.get(font2D.handle); 727 if (mapArray == null) { 728 mapArray = new char[numSlots][]; 729 fontMap.put(font2D.handle, mapArray); 730 } 731 for (int i=0; i<numGlyphs;i++) { 732 int slot = glyphCodes[i] >>> 24; 733 if (slot >= numSlots) { /* shouldn't happen */ 734 return false; 735 } 736 if (mapArray[slot] == null) { 737 Font2D slotFont = cf.getSlotFont(slot); 738 char[] map = (char[])fontMap.get(slotFont.handle); 739 if (map == null) { 740 map = getGlyphToCharMapForFont(slotFont); 741 } 742 mapArray[slot] = map; 743 } 744 } 745 } else { 746 glyphToCharMap = (char[])fontMap.get(font2D.handle); 747 if (glyphToCharMap == null) { 748 glyphToCharMap = getGlyphToCharMapForFont(font2D); 749 fontMap.put(font2D.handle, glyphToCharMap); 750 } 751 } 752 } 753 754 char[] chars = new char[numGlyphs]; 755 if (cf != null) { 756 for (int i=0; i<numGlyphs; i++) { 757 int gc = glyphCodes[i]; 758 char[] map = mapArray[gc >>> 24]; 759 gc = gc & 0xffffff; 760 if (map == null) { 761 return false; 762 } 763 /* X11 symbol & dingbats fonts used only for global metrics, 764 * so the glyph codes we have really refer to Lucida Sans 765 * Regular. 766 * So its possible the glyph code may appear out of range. 767 * Note that later on we double-check the glyph codes that 768 * we get from re-creating the GV from the string are the 769 * same as those we started with. 770 * 771 * If the glyphcode is INVISIBLE_GLYPH_ID then this may 772 * be \t, \n or \r which are mapped to that by layout. 773 * This is a case we can handle. It doesn't matter what 774 * character we use (we use \n) so long as layout maps it 775 * back to this in the verification, since the invisible 776 * glyph isn't visible :) 777 */ 778 char ch; 779 if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) { 780 ch = '\n'; 781 } else if (gc < 0 || gc >= map.length) { 782 return false; 783 } else { 784 ch = map[gc]; 785 } 786 if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) { 787 chars[i] = ch; 788 } else { 789 return false; 790 } 791 } 792 } else { 793 for (int i=0; i<numGlyphs; i++) { 794 int gc = glyphCodes[i]; 795 char ch; 796 if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) { 797 ch = '\n'; 798 } else if (gc < 0 || gc >= glyphToCharMap.length) { 799 return false; 800 } else { 801 ch = glyphToCharMap[gc]; 802 } 803 if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) { 804 chars[i] = ch; 805 } else { 806 return false; 807 } 808 } 809 } 810 811 FontRenderContext gvFrc = g.getFontRenderContext(); 812 GlyphVector gv2 = font.createGlyphVector(gvFrc, chars); 813 if (gv2.getNumGlyphs() != numGlyphs) { 814 return printGlyphVector(g, x, y); 815 } 816 int[] glyphCodes2 = gv2.getGlyphCodes(0, numGlyphs, null); 817 /* 818 * Needed to double-check remapping of X11 symbol & dingbats. 819 */ 820 for (int i=0; i<numGlyphs; i++) { 821 if (glyphCodes[i] != glyphCodes2[i]) { 822 return printGlyphVector(g, x, y); 823 } 824 } 825 826 FontRenderContext g2dFrc = getFontRenderContext(); 827 boolean compatibleFRC = gvFrc.equals(g2dFrc); 828 /* If differ only in specifying A-A or a translation, these are 829 * also compatible FRC's, and we can do one drawString call. 830 */ 831 if (!compatibleFRC && 832 gvFrc.usesFractionalMetrics() == g2dFrc.usesFractionalMetrics()) { 833 AffineTransform gvAT = gvFrc.getTransform(); 834 AffineTransform g2dAT = getTransform(); 835 double[] gvMatrix = new double[4]; 836 double[] g2dMatrix = new double[4]; 837 gvAT.getMatrix(gvMatrix); 838 g2dAT.getMatrix(g2dMatrix); 839 compatibleFRC = true; 840 for (int i=0;i<4;i++) { 841 if (gvMatrix[i] != g2dMatrix[i]) { 842 compatibleFRC = false; 843 break; 844 } 845 } 846 } 847 848 String str = new String(chars, 0, numGlyphs); 849 int numFonts = platformFontCount(font, str); 850 if (numFonts == 0) { 851 return false; 852 } 853 854 float[] positions = g.getGlyphPositions(0, numGlyphs, null); 855 boolean noPositionAdjustments = 856 ((flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) == 0) || 857 samePositions(gv2, glyphCodes2, glyphCodes, positions); 858 859 /* We have to consider that the application may be directly 860 * creating a GlyphVector, rather than one being created by 861 * TextLayout or indirectly from drawString. In such a case, if the 862 * font has layout attributes, the text may measure differently 863 * when we reconstitute it into a String and ask for the length that 864 * drawString would use. For example, KERNING will be applied in such 865 * a case but that Font attribute is not applied when the application 866 * directly created a GlyphVector. So in this case we need to verify 867 * that the text measures the same in both cases - ie that the 868 * layout attribute has no effect. If it does we can't always 869 * use the drawString call unless we can coerce the drawString call 870 * into measuring and displaying the string to the same length. 871 * That is the case where there is only one font used and we can 872 * specify the overall advance of the string. (See below). 873 */ 874 875 Point2D gvAdvancePt = g.getGlyphPosition(numGlyphs); 876 float gvAdvanceX = (float)gvAdvancePt.getX(); 877 boolean layoutAffectsAdvance = false; 878 if (font.hasLayoutAttributes() && printingGlyphVector && 879 noPositionAdjustments) { 880 881 /* If TRACKING is in use then the glyph vector will report 882 * position adjustments, then that ought to be sufficient to 883 * tell us we can't just ask native to do "drawString". But layout 884 * always sets the position adjustment flag, so we don't believe 885 * it and verify the positions are really different than 886 * createGlyphVector() (with no layout) would create. However 887 * inconsistently, TRACKING is applied when creating a GlyphVector, 888 * since it doesn't actually require "layout" (even though its 889 * considered a layout attribute), it just requires a fractional 890 * tweak to the[default]advances. So we need to specifically 891 * check for tracking until such time as we can trust 892 * the GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS bit. 893 */ 894 Map<TextAttribute, ?> map = font.getAttributes(); 895 Object o = map.get(TextAttribute.TRACKING); 896 boolean tracking = o != null && (o instanceof Number) && 897 (((Number)o).floatValue() != 0f); 898 899 if (tracking) { 900 noPositionAdjustments = false; 901 } else { 902 Rectangle2D bounds = font.getStringBounds(str, gvFrc); 903 float strAdvanceX = (float)bounds.getWidth(); 904 if (Math.abs(strAdvanceX - gvAdvanceX) > 0.00001) { 905 layoutAffectsAdvance = true; 906 } 907 } 908 } 909 910 if (compatibleFRC && noPositionAdjustments && !layoutAffectsAdvance) { 911 drawString(str, x, y, font, gvFrc, 0f); 912 return true; 913 } 914 915 /* If positions have not been explicitly assigned, we can 916 * ask the string to be drawn adjusted to this width. 917 * This call is supported only in the PS generator. 918 * GDI has API to specify the advance for each glyph in a 919 * string which could be used here too, but that is not yet 920 * implemented, and we'd need to update the signature of the 921 * drawString method to take the advances (ie relative positions) 922 * and use that instead of the width. 923 */ 924 if (numFonts == 1 && canDrawStringToWidth() && noPositionAdjustments) { 925 drawString(str, x, y, font, gvFrc, gvAdvanceX); 926 return true; 927 } 928 929 /* In some scripts chars drawn individually do not have the 930 * same representation (glyphs) as when combined with other chars. 931 * The logic here is erring on the side of caution, in particular 932 * in including supplementary characters. 933 */ 934 if (FontUtilities.isComplexText(chars, 0, chars.length)) { 935 return printGlyphVector(g, x, y); 936 } 937 938 /* If we reach here we have mapped all the glyphs back 939 * one-to-one to simple unicode chars that we know are in the font. 940 * We can call "drawChars" on each one of them in turn, setting 941 * the position based on the glyph positions. 942 * There's typically overhead in this. If numGlyphs is 'large', 943 * it may even be better to try printGlyphVector() in this case. 944 * This may be less recoverable for apps, but sophisticated apps 945 * should be able to recover the text from simple glyph vectors 946 * and we can avoid penalising the more common case - although 947 * this is already a minority case. 948 */ 949 if (numGlyphs > 10 && printGlyphVector(g, x, y)) { 950 return true; 951 } 952 953 for (int i=0; i<numGlyphs; i++) { 954 String s = new String(chars, i, 1); 955 drawString(s, x+positions[i*2], y+positions[i*2+1], 956 font, gvFrc, 0f); 957 } 958 return true; 959 } 960 961 /* The same codes must be in the same positions for this to return true. 962 * This would look cleaner if it took the original GV as a parameter but 963 * we already have the codes and will need to get the positions array 964 * too in most cases anyway. So its cheaper to pass them in. 965 * This call wouldn't be necessary if layout didn't always set the 966 * FLAG_HAS_POSITION_ADJUSTMENTS even if the default advances are used 967 * and there was no re-ordering (this should be fixed some day). 968 */ 969 private boolean samePositions(GlyphVector gv, int[] gvcodes, 970 int[] origCodes, float[] origPositions) { 971 972 int numGlyphs = gv.getNumGlyphs(); 973 float[] gvpos = gv.getGlyphPositions(0, numGlyphs, null); 974 975 /* this shouldn't happen here, but just in case */ 976 if (numGlyphs != gvcodes.length || /* real paranoia here */ 977 origCodes.length != gvcodes.length || 978 origPositions.length != gvpos.length) { 979 return false; 980 } 981 982 for (int i=0; i<numGlyphs; i++) { 983 if (gvcodes[i] != origCodes[i] || gvpos[i] != origPositions[i]) { 984 return false; 985 } 986 } 987 return true; 988 } 989 990 protected boolean canDrawStringToWidth() { 991 return false; 992 } 993 994 /* return an array which can map glyphs back to char codes. 995 * Glyphs which aren't mapped from a simple unicode code point 996 * will have no mapping in this array, and will be assumed to be 997 * because of some substitution that we can't handle. 998 */ 999 private static char[] getGlyphToCharMapForFont(Font2D font2D) { 1000 /* NB Composites report the number of glyphs in slot 0. 1001 * So if a string uses a char from a later slot, or a fallback slot, 1002 * it will not be able to use this faster path. 1003 */ 1004 int numGlyphs = font2D.getNumGlyphs(); 1005 int missingGlyph = font2D.getMissingGlyphCode(); 1006 char[] glyphToCharMap = new char[numGlyphs]; 1007 int glyph; 1008 1009 for (int i=0;i<numGlyphs; i++) { 1010 glyphToCharMap[i] = CharToGlyphMapper.INVISIBLE_GLYPH_ID; 1011 } 1012 1013 /* Consider refining the ranges to try to map by asking the font 1014 * what ranges it supports. 1015 * Since a glyph may be mapped by multiple code points, and this 1016 * code can't handle that, we always prefer the earlier code point. 1017 */ 1018 for (char c=0; c<0xFFFF; c++) { 1019 if (c >= CharToGlyphMapper.HI_SURROGATE_START && 1020 c <= CharToGlyphMapper.LO_SURROGATE_END) { 1021 continue; 1022 } 1023 glyph = font2D.charToGlyph(c); 1024 if (glyph != missingGlyph && 1025 glyph >= 0 && glyph < numGlyphs && 1026 (glyphToCharMap[glyph] == 1027 CharToGlyphMapper.INVISIBLE_GLYPH_ID)) { 1028 glyphToCharMap[glyph] = c; 1029 } 1030 } 1031 return glyphToCharMap; 1032 } 1033 1034 /** 1035 * Strokes the outline of a Shape using the settings of the current 1036 * graphics state. The rendering attributes applied include the 1037 * clip, transform, paint or color, composite and stroke attributes. 1038 * @param s The shape to be drawn. 1039 * @see #setStroke 1040 * @see #setPaint 1041 * @see java.awt.Graphics#setColor 1042 * @see #transform 1043 * @see #setTransform 1044 * @see #clip 1045 * @see #setClip 1046 * @see #setComposite 1047 */ 1048 public void draw(Shape s) { 1049 1050 fill(getStroke().createStrokedShape(s)); 1051 } 1052 1053 /** 1054 * Fills the interior of a Shape using the settings of the current 1055 * graphics state. The rendering attributes applied include the 1056 * clip, transform, paint or color, and composite. 1057 * @see #setPaint 1058 * @see java.awt.Graphics#setColor 1059 * @see #transform 1060 * @see #setTransform 1061 * @see #setComposite 1062 * @see #clip 1063 * @see #setClip 1064 */ 1065 public void fill(Shape s) { 1066 Paint paint = getPaint(); 1067 1068 try { 1069 fill(s, (Color) paint); 1070 1071 /* The PathGraphics class only supports filling with 1072 * solid colors and so we do not expect the cast of Paint 1073 * to Color to fail. If it does fail then something went 1074 * wrong, like the app draw a page with a solid color but 1075 * then redrew it with a Gradient. 1076 */ 1077 } catch (ClassCastException e) { 1078 throw new IllegalArgumentException("Expected a Color instance"); 1079 } 1080 } 1081 1082 public void fill(Shape s, Color color) { 1083 AffineTransform deviceTransform = getTransform(); 1084 1085 if (getClip() != null) { 1086 deviceClip(getClip().getPathIterator(deviceTransform)); 1087 } 1088 deviceFill(s.getPathIterator(deviceTransform), color); 1089 } 1090 1091 /** 1092 * Fill the path defined by {@code pathIter} 1093 * with the specified color. 1094 * The path is provided in device coordinates. 1095 */ 1096 protected abstract void deviceFill(PathIterator pathIter, Color color); 1097 1098 /* 1099 * Set the clipping path to that defined by 1100 * the passed in {@code PathIterator}. 1101 */ 1102 protected abstract void deviceClip(PathIterator pathIter); 1103 1104 /* 1105 * Draw the outline of the rectangle without using path 1106 * if supported by platform. 1107 */ 1108 protected abstract void deviceFrameRect(int x, int y, 1109 int width, int height, 1110 Color color); 1111 1112 /* 1113 * Draw a line without using path if supported by platform. 1114 */ 1115 protected abstract void deviceDrawLine(int xBegin, int yBegin, 1116 int xEnd, int yEnd, Color color); 1117 1118 /* 1119 * Fill a rectangle using specified color. 1120 */ 1121 protected abstract void deviceFillRect(int x, int y, 1122 int width, int height, Color color); 1123 1124 /* Obtain a BI from known implementations of java.awt.Image 1125 */ 1126 protected BufferedImage getBufferedImage(Image img) { 1127 if (img instanceof BufferedImage) { 1128 // Otherwise we expect a BufferedImage to behave as a standard BI 1129 return (BufferedImage)img; 1130 } else if (img instanceof ToolkitImage) { 1131 // This can be null if the image isn't loaded yet. 1132 // This is fine as in that case our caller will return 1133 // as it will only draw a fully loaded image 1134 return ((ToolkitImage)img).getBufferedImage(); 1135 } else if (img instanceof VolatileImage) { 1136 // VI needs to make a new BI: this is unavoidable but 1137 // I don't expect VI's to be "huge" in any case. 1138 return ((VolatileImage)img).getSnapshot(); 1139 } else { 1140 // may be null or may be some non-standard Image which 1141 // shouldn't happen as Image is implemented by the platform 1142 // not by applications 1143 // If you add a new Image implementation to the platform you 1144 // will need to support it here similarly to VI. 1145 return null; 1146 } 1147 } 1148 1149 /** 1150 * Return true if the BufferedImage argument has non-opaque 1151 * bits in it and therefore can not be directly rendered by 1152 * GDI. Return false if the image is opaque. If this function 1153 * can not tell for sure whether the image has transparent 1154 * pixels then it assumes that it does. 1155 */ 1156 protected boolean hasTransparentPixels(BufferedImage bufferedImage) { 1157 ColorModel colorModel = bufferedImage.getColorModel(); 1158 boolean hasTransparency = colorModel == null 1159 ? true 1160 : colorModel.getTransparency() != ColorModel.OPAQUE; 1161 1162 /* 1163 * For the default INT ARGB check the image to see if any pixels are 1164 * really transparent. If there are no transparent pixels then the 1165 * transparency of the color model can be ignored. 1166 * We assume that IndexColorModel images have already been 1167 * checked for transparency and will be OPAQUE unless they actually 1168 * have transparent pixels present. 1169 */ 1170 if (hasTransparency && bufferedImage != null) { 1171 if (bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB || 1172 bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB_PRE) { 1173 DataBuffer db = bufferedImage.getRaster().getDataBuffer(); 1174 SampleModel sm = bufferedImage.getRaster().getSampleModel(); 1175 if (db instanceof DataBufferInt && 1176 sm instanceof SinglePixelPackedSampleModel) { 1177 SinglePixelPackedSampleModel psm = 1178 (SinglePixelPackedSampleModel)sm; 1179 // Stealing the data array for reading only... 1180 int[] int_data = 1181 SunWritableRaster.stealData((DataBufferInt) db, 0); 1182 int x = bufferedImage.getMinX(); 1183 int y = bufferedImage.getMinY(); 1184 int w = bufferedImage.getWidth(); 1185 int h = bufferedImage.getHeight(); 1186 int stride = psm.getScanlineStride(); 1187 boolean hastranspixel = false; 1188 for (int j = y; j < y+h; j++) { 1189 int yoff = j * stride; 1190 for (int i = x; i < x+w; i++) { 1191 if ((int_data[yoff+i] & 0xff000000)!=0xff000000 ) { 1192 hastranspixel = true; 1193 break; 1194 } 1195 } 1196 if (hastranspixel) { 1197 break; 1198 } 1199 } 1200 if (hastranspixel == false) { 1201 hasTransparency = false; 1202 } 1203 } 1204 } 1205 } 1206 1207 return hasTransparency; 1208 } 1209 1210 protected boolean isBitmaskTransparency(BufferedImage bufferedImage) { 1211 ColorModel colorModel = bufferedImage.getColorModel(); 1212 return (colorModel != null && 1213 colorModel.getTransparency() == ColorModel.BITMASK); 1214 } 1215 1216 1217 /* An optimisation for the special case of ICM images which have 1218 * bitmask transparency. 1219 */ 1220 protected boolean drawBitmaskImage(BufferedImage bufferedImage, 1221 AffineTransform xform, 1222 Color bgcolor, 1223 int srcX, int srcY, 1224 int srcWidth, int srcHeight) { 1225 1226 ColorModel colorModel = bufferedImage.getColorModel(); 1227 IndexColorModel icm; 1228 int [] pixels; 1229 1230 if (!(colorModel instanceof IndexColorModel)) { 1231 return false; 1232 } else { 1233 icm = (IndexColorModel)colorModel; 1234 } 1235 1236 if (colorModel.getTransparency() != ColorModel.BITMASK) { 1237 return false; 1238 } 1239 1240 // to be compatible with 1.1 printing which treated b/g colors 1241 // with alpha 128 as opaque 1242 if (bgcolor != null && bgcolor.getAlpha() < 128) { 1243 return false; 1244 } 1245 1246 if ((xform.getType() 1247 & ~( AffineTransform.TYPE_UNIFORM_SCALE 1248 | AffineTransform.TYPE_TRANSLATION 1249 | AffineTransform.TYPE_QUADRANT_ROTATION 1250 )) != 0) { 1251 return false; 1252 } 1253 1254 if ((getTransform().getType() 1255 & ~( AffineTransform.TYPE_UNIFORM_SCALE 1256 | AffineTransform.TYPE_TRANSLATION 1257 | AffineTransform.TYPE_QUADRANT_ROTATION 1258 )) != 0) { 1259 return false; 1260 } 1261 1262 BufferedImage subImage = null; 1263 Raster raster = bufferedImage.getRaster(); 1264 int transpixel = icm.getTransparentPixel(); 1265 byte[] alphas = new byte[icm.getMapSize()]; 1266 icm.getAlphas(alphas); 1267 if (transpixel >= 0) { 1268 alphas[transpixel] = 0; 1269 } 1270 1271 /* don't just use srcWidth & srcHeight from application - they 1272 * may exceed the extent of the image - may need to clip. 1273 * The image xform will ensure that points are still mapped properly. 1274 */ 1275 int rw = raster.getWidth(); 1276 int rh = raster.getHeight(); 1277 if (srcX > rw || srcY > rh) { 1278 return false; 1279 } 1280 int right, bottom, wid, hgt; 1281 if (srcX+srcWidth > rw) { 1282 right = rw; 1283 wid = right - srcX; 1284 } else { 1285 right = srcX+srcWidth; 1286 wid = srcWidth; 1287 } 1288 if (srcY+srcHeight > rh) { 1289 bottom = rh; 1290 hgt = bottom - srcY; 1291 } else { 1292 bottom = srcY+srcHeight; 1293 hgt = srcHeight; 1294 } 1295 pixels = new int[wid]; 1296 for (int j=srcY; j<bottom; j++) { 1297 int startx = -1; 1298 raster.getPixels(srcX, j, wid, 1, pixels); 1299 for (int i=srcX; i<right; i++) { 1300 if (alphas[pixels[i-srcX]] == 0) { 1301 if (startx >=0) { 1302 subImage = bufferedImage.getSubimage(startx, j, 1303 i-startx, 1); 1304 xform.translate(startx, j); 1305 drawImageToPlatform(subImage, xform, bgcolor, 1306 0, 0, i-startx, 1, true); 1307 xform.translate(-startx, -j); 1308 startx = -1; 1309 } 1310 } else if (startx < 0) { 1311 startx = i; 1312 } 1313 } 1314 if (startx >= 0) { 1315 subImage = bufferedImage.getSubimage(startx, j, 1316 right - startx, 1); 1317 xform.translate(startx, j); 1318 drawImageToPlatform(subImage, xform, bgcolor, 1319 0, 0, right - startx, 1, true); 1320 xform.translate(-startx, -j); 1321 } 1322 } 1323 return true; 1324 } 1325 1326 1327 1328 /** 1329 * The various {@code drawImage()} methods for 1330 * {@code PathGraphics} are all decomposed 1331 * into an invocation of {@code drawImageToPlatform}. 1332 * The portion of the passed in image defined by 1333 * {@code srcX, srcY, srcWidth, and srcHeight} 1334 * is transformed by the supplied AffineTransform and 1335 * drawn using PS to the printer context. 1336 * 1337 * @param img The image to be drawn. 1338 * This method does nothing if {@code img} is null. 1339 * @param xform Used to transform the image before drawing. 1340 * This can be null. 1341 * @param bgcolor This color is drawn where the image has transparent 1342 * pixels. If this parameter is null then the 1343 * pixels already in the destination should show 1344 * through. 1345 * @param srcX With srcY this defines the upper-left corner 1346 * of the portion of the image to be drawn. 1347 * 1348 * @param srcY With srcX this defines the upper-left corner 1349 * of the portion of the image to be drawn. 1350 * @param srcWidth The width of the portion of the image to 1351 * be drawn. 1352 * @param srcHeight The height of the portion of the image to 1353 * be drawn. 1354 * @param handlingTransparency if being recursively called to 1355 * print opaque region of transparent image 1356 */ 1357 protected abstract boolean 1358 drawImageToPlatform(Image img, AffineTransform xform, 1359 Color bgcolor, 1360 int srcX, int srcY, 1361 int srcWidth, int srcHeight, 1362 boolean handlingTransparency); 1363 1364 /** 1365 * Draws as much of the specified image as is currently available. 1366 * The image is drawn with its top-left corner at 1367 * (<i>x</i>, <i>y</i>) in this graphics context's coordinate 1368 * space. Transparent pixels in the image do not affect whatever 1369 * pixels are already there. 1370 * <p> 1371 * This method returns immediately in all cases, even if the 1372 * complete image has not yet been loaded, and it has not been dithered 1373 * and converted for the current output device. 1374 * <p> 1375 * If the image has not yet been completely loaded, then 1376 * {@code drawImage} returns {@code false}. As more of 1377 * the image becomes available, the process that draws the image notifies 1378 * the specified image observer. 1379 * @param img the specified image to be drawn. 1380 * @param x the <i>x</i> coordinate. 1381 * @param y the <i>y</i> coordinate. 1382 * @param observer object to be notified as more of 1383 * the image is converted. 1384 * @see java.awt.Image 1385 * @see java.awt.image.ImageObserver 1386 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1387 * @since 1.0 1388 */ 1389 public boolean drawImage(Image img, int x, int y, 1390 ImageObserver observer) { 1391 1392 return drawImage(img, x, y, null, observer); 1393 } 1394 1395 /** 1396 * Draws as much of the specified image as has already been scaled 1397 * to fit inside the specified rectangle. 1398 * <p> 1399 * The image is drawn inside the specified rectangle of this 1400 * graphics context's coordinate space, and is scaled if 1401 * necessary. Transparent pixels do not affect whatever pixels 1402 * are already there. 1403 * <p> 1404 * This method returns immediately in all cases, even if the 1405 * entire image has not yet been scaled, dithered, and converted 1406 * for the current output device. 1407 * If the current output representation is not yet complete, then 1408 * {@code drawImage} returns {@code false}. As more of 1409 * the image becomes available, the process that draws the image notifies 1410 * the image observer by calling its {@code imageUpdate} method. 1411 * <p> 1412 * A scaled version of an image will not necessarily be 1413 * available immediately just because an unscaled version of the 1414 * image has been constructed for this output device. Each size of 1415 * the image may be cached separately and generated from the original 1416 * data in a separate image production sequence. 1417 * @param img the specified image to be drawn. 1418 * @param x the <i>x</i> coordinate. 1419 * @param y the <i>y</i> coordinate. 1420 * @param width the width of the rectangle. 1421 * @param height the height of the rectangle. 1422 * @param observer object to be notified as more of 1423 * the image is converted. 1424 * @see java.awt.Image 1425 * @see java.awt.image.ImageObserver 1426 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1427 * @since 1.0 1428 */ 1429 public boolean drawImage(Image img, int x, int y, 1430 int width, int height, 1431 ImageObserver observer) { 1432 1433 return drawImage(img, x, y, width, height, null, observer); 1434 1435 } 1436 1437 /* 1438 * Draws as much of the specified image as is currently available. 1439 * The image is drawn with its top-left corner at 1440 * (<i>x</i>, <i>y</i>) in this graphics context's coordinate 1441 * space. Transparent pixels are drawn in the specified 1442 * background color. 1443 * <p> 1444 * This operation is equivalent to filling a rectangle of the 1445 * width and height of the specified image with the given color and then 1446 * drawing the image on top of it, but possibly more efficient. 1447 * <p> 1448 * This method returns immediately in all cases, even if the 1449 * complete image has not yet been loaded, and it has not been dithered 1450 * and converted for the current output device. 1451 * <p> 1452 * If the image has not yet been completely loaded, then 1453 * {@code drawImage} returns {@code false}. As more of 1454 * the image becomes available, the process that draws the image notifies 1455 * the specified image observer. 1456 * @param img the specified image to be drawn. 1457 * This method does nothing if {@code img} is null. 1458 * @param x the <i>x</i> coordinate. 1459 * @param y the <i>y</i> coordinate. 1460 * @param bgcolor the background color to paint under the 1461 * non-opaque portions of the image. 1462 * In this WPathGraphics implementation, 1463 * this parameter can be null in which 1464 * case that background is made a transparent 1465 * white. 1466 * @param observer object to be notified as more of 1467 * the image is converted. 1468 * @see java.awt.Image 1469 * @see java.awt.image.ImageObserver 1470 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1471 * @since 1.0 1472 */ 1473 public boolean drawImage(Image img, int x, int y, 1474 Color bgcolor, 1475 ImageObserver observer) { 1476 1477 if (img == null) { 1478 return true; 1479 } 1480 1481 boolean result; 1482 int srcWidth = img.getWidth(null); 1483 int srcHeight = img.getHeight(null); 1484 1485 if (srcWidth < 0 || srcHeight < 0) { 1486 result = false; 1487 } else { 1488 result = drawImage(img, x, y, srcWidth, srcHeight, bgcolor, observer); 1489 } 1490 1491 return result; 1492 } 1493 1494 /** 1495 * Draws as much of the specified image as has already been scaled 1496 * to fit inside the specified rectangle. 1497 * <p> 1498 * The image is drawn inside the specified rectangle of this 1499 * graphics context's coordinate space, and is scaled if 1500 * necessary. Transparent pixels are drawn in the specified 1501 * background color. 1502 * This operation is equivalent to filling a rectangle of the 1503 * width and height of the specified image with the given color and then 1504 * drawing the image on top of it, but possibly more efficient. 1505 * <p> 1506 * This method returns immediately in all cases, even if the 1507 * entire image has not yet been scaled, dithered, and converted 1508 * for the current output device. 1509 * If the current output representation is not yet complete then 1510 * {@code drawImage} returns {@code false}. As more of 1511 * the image becomes available, the process that draws the image notifies 1512 * the specified image observer. 1513 * <p> 1514 * A scaled version of an image will not necessarily be 1515 * available immediately just because an unscaled version of the 1516 * image has been constructed for this output device. Each size of 1517 * the image may be cached separately and generated from the original 1518 * data in a separate image production sequence. 1519 * @param img the specified image to be drawn. 1520 * This method does nothing if {@code img} is null. 1521 * @param x the <i>x</i> coordinate. 1522 * @param y the <i>y</i> coordinate. 1523 * @param width the width of the rectangle. 1524 * @param height the height of the rectangle. 1525 * @param bgcolor the background color to paint under the 1526 * non-opaque portions of the image. 1527 * @param observer object to be notified as more of 1528 * the image is converted. 1529 * @see java.awt.Image 1530 * @see java.awt.image.ImageObserver 1531 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1532 * @since 1.0 1533 */ 1534 public boolean drawImage(Image img, int x, int y, 1535 int width, int height, 1536 Color bgcolor, 1537 ImageObserver observer) { 1538 1539 if (img == null) { 1540 return true; 1541 } 1542 1543 boolean result; 1544 int srcWidth = img.getWidth(null); 1545 int srcHeight = img.getHeight(null); 1546 1547 if (srcWidth < 0 || srcHeight < 0) { 1548 result = false; 1549 } else { 1550 result = drawImage(img, 1551 x, y, x + width, y + height, 1552 0, 0, srcWidth, srcHeight, 1553 observer); 1554 } 1555 1556 return result; 1557 } 1558 1559 /** 1560 * Draws as much of the specified area of the specified image as is 1561 * currently available, scaling it on the fly to fit inside the 1562 * specified area of the destination drawable surface. Transparent pixels 1563 * do not affect whatever pixels are already there. 1564 * <p> 1565 * This method returns immediately in all cases, even if the 1566 * image area to be drawn has not yet been scaled, dithered, and converted 1567 * for the current output device. 1568 * If the current output representation is not yet complete then 1569 * {@code drawImage} returns {@code false}. As more of 1570 * the image becomes available, the process that draws the image notifies 1571 * the specified image observer. 1572 * <p> 1573 * This method always uses the unscaled version of the image 1574 * to render the scaled rectangle and performs the required 1575 * scaling on the fly. It does not use a cached, scaled version 1576 * of the image for this operation. Scaling of the image from source 1577 * to destination is performed such that the first coordinate 1578 * of the source rectangle is mapped to the first coordinate of 1579 * the destination rectangle, and the second source coordinate is 1580 * mapped to the second destination coordinate. The subimage is 1581 * scaled and flipped as needed to preserve those mappings. 1582 * @param img the specified image to be drawn 1583 * @param dx1 the <i>x</i> coordinate of the first corner of the 1584 * destination rectangle. 1585 * @param dy1 the <i>y</i> coordinate of the first corner of the 1586 * destination rectangle. 1587 * @param dx2 the <i>x</i> coordinate of the second corner of the 1588 * destination rectangle. 1589 * @param dy2 the <i>y</i> coordinate of the second corner of the 1590 * destination rectangle. 1591 * @param sx1 the <i>x</i> coordinate of the first corner of the 1592 * source rectangle. 1593 * @param sy1 the <i>y</i> coordinate of the first corner of the 1594 * source rectangle. 1595 * @param sx2 the <i>x</i> coordinate of the second corner of the 1596 * source rectangle. 1597 * @param sy2 the <i>y</i> coordinate of the second corner of the 1598 * source rectangle. 1599 * @param observer object to be notified as more of the image is 1600 * scaled and converted. 1601 * @see java.awt.Image 1602 * @see java.awt.image.ImageObserver 1603 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1604 * @since 1.1 1605 */ 1606 public boolean drawImage(Image img, 1607 int dx1, int dy1, int dx2, int dy2, 1608 int sx1, int sy1, int sx2, int sy2, 1609 ImageObserver observer) { 1610 1611 return drawImage(img, 1612 dx1, dy1, dx2, dy2, 1613 sx1, sy1, sx2, sy2, 1614 null, observer); 1615 } 1616 1617 /** 1618 * Draws as much of the specified area of the specified image as is 1619 * currently available, scaling it on the fly to fit inside the 1620 * specified area of the destination drawable surface. 1621 * <p> 1622 * Transparent pixels are drawn in the specified background color. 1623 * This operation is equivalent to filling a rectangle of the 1624 * width and height of the specified image with the given color and then 1625 * drawing the image on top of it, but possibly more efficient. 1626 * <p> 1627 * This method returns immediately in all cases, even if the 1628 * image area to be drawn has not yet been scaled, dithered, and converted 1629 * for the current output device. 1630 * If the current output representation is not yet complete then 1631 * {@code drawImage} returns {@code false}. As more of 1632 * the image becomes available, the process that draws the image notifies 1633 * the specified image observer. 1634 * <p> 1635 * This method always uses the unscaled version of the image 1636 * to render the scaled rectangle and performs the required 1637 * scaling on the fly. It does not use a cached, scaled version 1638 * of the image for this operation. Scaling of the image from source 1639 * to destination is performed such that the first coordinate 1640 * of the source rectangle is mapped to the first coordinate of 1641 * the destination rectangle, and the second source coordinate is 1642 * mapped to the second destination coordinate. The subimage is 1643 * scaled and flipped as needed to preserve those mappings. 1644 * @param img the specified image to be drawn 1645 * This method does nothing if {@code img} is null. 1646 * @param dx1 the <i>x</i> coordinate of the first corner of the 1647 * destination rectangle. 1648 * @param dy1 the <i>y</i> coordinate of the first corner of the 1649 * destination rectangle. 1650 * @param dx2 the <i>x</i> coordinate of the second corner of the 1651 * destination rectangle. 1652 * @param dy2 the <i>y</i> coordinate of the second corner of the 1653 * destination rectangle. 1654 * @param sx1 the <i>x</i> coordinate of the first corner of the 1655 * source rectangle. 1656 * @param sy1 the <i>y</i> coordinate of the first corner of the 1657 * source rectangle. 1658 * @param sx2 the <i>x</i> coordinate of the second corner of the 1659 * source rectangle. 1660 * @param sy2 the <i>y</i> coordinate of the second corner of the 1661 * source rectangle. 1662 * @param bgcolor the background color to paint under the 1663 * non-opaque portions of the image. 1664 * @param observer object to be notified as more of the image is 1665 * scaled and converted. 1666 * @see java.awt.Image 1667 * @see java.awt.image.ImageObserver 1668 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1669 * @since 1.1 1670 */ 1671 public boolean drawImage(Image img, 1672 int dx1, int dy1, int dx2, int dy2, 1673 int sx1, int sy1, int sx2, int sy2, 1674 Color bgcolor, 1675 ImageObserver observer) { 1676 1677 if (img == null) { 1678 return true; 1679 } 1680 int imgWidth = img.getWidth(null); 1681 int imgHeight = img.getHeight(null); 1682 1683 if (imgWidth < 0 || imgHeight < 0) { 1684 return true; 1685 } 1686 1687 int srcWidth = sx2 - sx1; 1688 int srcHeight = sy2 - sy1; 1689 1690 /* Create a transform which describes the changes 1691 * from the source coordinates to the destination 1692 * coordinates. The scaling is determined by the 1693 * ratio of the two rectangles, while the translation 1694 * comes from the difference of their origins. 1695 */ 1696 float scalex = (float) (dx2 - dx1) / srcWidth; 1697 float scaley = (float) (dy2 - dy1) / srcHeight; 1698 AffineTransform xForm 1699 = new AffineTransform(scalex, 1700 0, 1701 0, 1702 scaley, 1703 dx1 - (sx1 * scalex), 1704 dy1 - (sy1 * scaley)); 1705 1706 /* drawImageToPlatform needs the top-left of the source area and 1707 * a positive width and height. The xform describes how to map 1708 * src->dest, so that information is not lost. 1709 */ 1710 int tmp=0; 1711 if (sx2 < sx1) { 1712 tmp = sx1; 1713 sx1 = sx2; 1714 sx2 = tmp; 1715 } 1716 if (sy2 < sy1) { 1717 tmp = sy1; 1718 sy1 = sy2; 1719 sy2 = tmp; 1720 } 1721 1722 /* if src area is beyond the bounds of the image, we must clip it. 1723 * The transform is based on the specified area, not the clipped one. 1724 */ 1725 if (sx1 < 0) { 1726 sx1 = 0; 1727 } else if (sx1 > imgWidth) { // empty srcArea, nothing to draw 1728 sx1 = imgWidth; 1729 } 1730 if (sx2 < 0) { // empty srcArea, nothing to draw 1731 sx2 = 0; 1732 } else if (sx2 > imgWidth) { 1733 sx2 = imgWidth; 1734 } 1735 if (sy1 < 0) { 1736 sy1 = 0; 1737 } else if (sy1 > imgHeight) { // empty srcArea 1738 sy1 = imgHeight; 1739 } 1740 if (sy2 < 0) { // empty srcArea 1741 sy2 = 0; 1742 } else if (sy2 > imgHeight) { 1743 sy2 = imgHeight; 1744 } 1745 1746 srcWidth = sx2 - sx1; 1747 srcHeight = sy2 - sy1; 1748 1749 if (srcWidth <= 0 || srcHeight <= 0) { 1750 return true; 1751 } 1752 1753 return drawImageToPlatform(img, xForm, bgcolor, 1754 sx1, sy1, srcWidth, srcHeight, false); 1755 1756 1757 } 1758 1759 /** 1760 * Draws an image, applying a transform from image space into user space 1761 * before drawing. 1762 * The transformation from user space into device space is done with 1763 * the current transform in the Graphics2D. 1764 * The given transformation is applied to the image before the 1765 * transform attribute in the Graphics2D state is applied. 1766 * The rendering attributes applied include the clip, transform, 1767 * and composite attributes. Note that the result is 1768 * undefined, if the given transform is noninvertible. 1769 * @param img The image to be drawn. 1770 * This method does nothing if {@code img} is null. 1771 * @param xform The transformation from image space into user space. 1772 * @param obs The image observer to be notified as more of the image 1773 * is converted. 1774 * @see #transform 1775 * @see #setTransform 1776 * @see #setComposite 1777 * @see #clip 1778 * @see #setClip 1779 */ 1780 public boolean drawImage(Image img, 1781 AffineTransform xform, 1782 ImageObserver obs) { 1783 1784 if (img == null) { 1785 return true; 1786 } 1787 1788 boolean result; 1789 int srcWidth = img.getWidth(null); 1790 int srcHeight = img.getHeight(null); 1791 1792 if (srcWidth < 0 || srcHeight < 0) { 1793 result = false; 1794 } else { 1795 result = drawImageToPlatform(img, xform, null, 1796 0, 0, srcWidth, srcHeight, false); 1797 } 1798 1799 return result; 1800 } 1801 1802 /** 1803 * Draws a BufferedImage that is filtered with a BufferedImageOp. 1804 * The rendering attributes applied include the clip, transform 1805 * and composite attributes. This is equivalent to: 1806 * <pre> 1807 * img1 = op.filter(img, null); 1808 * drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null); 1809 * </pre> 1810 * @param op The filter to be applied to the image before drawing. 1811 * @param img The BufferedImage to be drawn. 1812 * This method does nothing if {@code img} is null. 1813 * @param x,y The location in user space where the image should be drawn. 1814 * @see #transform 1815 * @see #setTransform 1816 * @see #setComposite 1817 * @see #clip 1818 * @see #setClip 1819 */ 1820 public void drawImage(BufferedImage img, 1821 BufferedImageOp op, 1822 int x, 1823 int y) { 1824 1825 if (img == null) { 1826 return; 1827 } 1828 1829 int srcWidth = img.getWidth(null); 1830 int srcHeight = img.getHeight(null); 1831 1832 if (op != null) { 1833 img = op.filter(img, null); 1834 } 1835 if (srcWidth <= 0 || srcHeight <= 0) { 1836 return; 1837 } else { 1838 AffineTransform xform = new AffineTransform(1f,0f,0f,1f,x,y); 1839 drawImageToPlatform(img, xform, null, 1840 0, 0, srcWidth, srcHeight, false); 1841 } 1842 1843 } 1844 1845 /** 1846 * Draws an image, applying a transform from image space into user space 1847 * before drawing. 1848 * The transformation from user space into device space is done with 1849 * the current transform in the Graphics2D. 1850 * The given transformation is applied to the image before the 1851 * transform attribute in the Graphics2D state is applied. 1852 * The rendering attributes applied include the clip, transform, 1853 * and composite attributes. Note that the result is 1854 * undefined, if the given transform is noninvertible. 1855 * @param img The image to be drawn. 1856 * This method does nothing if {@code img} is null. 1857 * @param xform The transformation from image space into user space. 1858 * @see #transform 1859 * @see #setTransform 1860 * @see #setComposite 1861 * @see #clip 1862 * @see #setClip 1863 */ 1864 public void drawRenderedImage(RenderedImage img, 1865 AffineTransform xform) { 1866 1867 if (img == null) { 1868 return; 1869 } 1870 1871 BufferedImage bufferedImage = null; 1872 int srcWidth = img.getWidth(); 1873 int srcHeight = img.getHeight(); 1874 1875 if (srcWidth <= 0 || srcHeight <= 0) { 1876 return; 1877 } 1878 1879 if (img instanceof BufferedImage) { 1880 bufferedImage = (BufferedImage) img; 1881 } else { 1882 bufferedImage = new BufferedImage(srcWidth, srcHeight, 1883 BufferedImage.TYPE_INT_ARGB); 1884 Graphics2D imageGraphics = bufferedImage.createGraphics(); 1885 imageGraphics.drawRenderedImage(img, xform); 1886 } 1887 1888 drawImageToPlatform(bufferedImage, xform, null, 1889 0, 0, srcWidth, srcHeight, false); 1890 1891 } 1892 1893 }