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