1 /* 2 * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 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 JDK1.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 JDK1.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 JDK1.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 && glyph < numGlyphs && 1029 (glyphToCharMap[glyph] == 1030 CharToGlyphMapper.INVISIBLE_GLYPH_ID)) { 1031 glyphToCharMap[glyph] = c; 1032 } 1033 } 1034 return glyphToCharMap; 1035 } 1036 1037 /** 1038 * Strokes the outline of a Shape using the settings of the current 1039 * graphics state. The rendering attributes applied include the 1040 * clip, transform, paint or color, composite and stroke attributes. 1041 * @param s The shape to be drawn. 1042 * @see #setStroke 1043 * @see #setPaint 1044 * @see java.awt.Graphics#setColor 1045 * @see #transform 1046 * @see #setTransform 1047 * @see #clip 1048 * @see #setClip 1049 * @see #setComposite 1050 */ 1051 public void draw(Shape s) { 1052 1053 fill(getStroke().createStrokedShape(s)); 1054 } 1055 1056 /** 1057 * Fills the interior of a Shape using the settings of the current 1058 * graphics state. The rendering attributes applied include the 1059 * clip, transform, paint or color, and composite. 1060 * @see #setPaint 1061 * @see java.awt.Graphics#setColor 1062 * @see #transform 1063 * @see #setTransform 1064 * @see #setComposite 1065 * @see #clip 1066 * @see #setClip 1067 */ 1068 public void fill(Shape s) { 1069 Paint paint = getPaint(); 1070 1071 try { 1072 fill(s, (Color) paint); 1073 1074 /* The PathGraphics class only supports filling with 1075 * solid colors and so we do not expect the cast of Paint 1076 * to Color to fail. If it does fail then something went 1077 * wrong, like the app draw a page with a solid color but 1078 * then redrew it with a Gradient. 1079 */ 1080 } catch (ClassCastException e) { 1081 throw new IllegalArgumentException("Expected a Color instance"); 1082 } 1083 } 1084 1085 public void fill(Shape s, Color color) { 1086 AffineTransform deviceTransform = getTransform(); 1087 1088 if (getClip() != null) { 1089 deviceClip(getClip().getPathIterator(deviceTransform)); 1090 } 1091 deviceFill(s.getPathIterator(deviceTransform), color); 1092 } 1093 1094 /** 1095 * Fill the path defined by <code>pathIter</code> 1096 * with the specified color. 1097 * The path is provided in device coordinates. 1098 */ 1099 protected abstract void deviceFill(PathIterator pathIter, Color color); 1100 1101 /* 1102 * Set the clipping path to that defined by 1103 * the passed in <code>PathIterator</code>. 1104 */ 1105 protected abstract void deviceClip(PathIterator pathIter); 1106 1107 /* 1108 * Draw the outline of the rectangle without using path 1109 * if supported by platform. 1110 */ 1111 protected abstract void deviceFrameRect(int x, int y, 1112 int width, int height, 1113 Color color); 1114 1115 /* 1116 * Draw a line without using path if supported by platform. 1117 */ 1118 protected abstract void deviceDrawLine(int xBegin, int yBegin, 1119 int xEnd, int yEnd, Color color); 1120 1121 /* 1122 * Fill a rectangle using specified color. 1123 */ 1124 protected abstract void deviceFillRect(int x, int y, 1125 int width, int height, Color color); 1126 1127 /* Obtain a BI from known implementations of java.awt.Image 1128 */ 1129 protected BufferedImage getBufferedImage(Image img) { 1130 if (img instanceof BufferedImage) { 1131 // Otherwise we expect a BufferedImage to behave as a standard BI 1132 return (BufferedImage)img; 1133 } else if (img instanceof ToolkitImage) { 1134 // This can be null if the image isn't loaded yet. 1135 // This is fine as in that case our caller will return 1136 // as it will only draw a fully loaded image 1137 return ((ToolkitImage)img).getBufferedImage(); 1138 } else if (img instanceof VolatileImage) { 1139 // VI needs to make a new BI: this is unavoidable but 1140 // I don't expect VI's to be "huge" in any case. 1141 return ((VolatileImage)img).getSnapshot(); 1142 } else { 1143 // may be null or may be some non-standard Image which 1144 // shouldn't happen as Image is implemented by the platform 1145 // not by applications 1146 // If you add a new Image implementation to the platform you 1147 // will need to support it here similarly to VI. 1148 return null; 1149 } 1150 } 1151 1152 /** 1153 * Return true if the BufferedImage argument has non-opaque 1154 * bits in it and therefore can not be directly rendered by 1155 * GDI. Return false if the image is opaque. If this function 1156 * can not tell for sure whether the image has transparent 1157 * pixels then it assumes that it does. 1158 */ 1159 protected boolean hasTransparentPixels(BufferedImage bufferedImage) { 1160 ColorModel colorModel = bufferedImage.getColorModel(); 1161 boolean hasTransparency = colorModel == null 1162 ? true 1163 : colorModel.getTransparency() != ColorModel.OPAQUE; 1164 1165 /* 1166 * For the default INT ARGB check the image to see if any pixels are 1167 * really transparent. If there are no transparent pixels then the 1168 * transparency of the color model can be ignored. 1169 * We assume that IndexColorModel images have already been 1170 * checked for transparency and will be OPAQUE unless they actually 1171 * have transparent pixels present. 1172 */ 1173 if (hasTransparency && bufferedImage != null) { 1174 if (bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB || 1175 bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB_PRE) { 1176 DataBuffer db = bufferedImage.getRaster().getDataBuffer(); 1177 SampleModel sm = bufferedImage.getRaster().getSampleModel(); 1178 if (db instanceof DataBufferInt && 1179 sm instanceof SinglePixelPackedSampleModel) { 1180 SinglePixelPackedSampleModel psm = 1181 (SinglePixelPackedSampleModel)sm; 1182 // Stealing the data array for reading only... 1183 int[] int_data = 1184 SunWritableRaster.stealData((DataBufferInt) db, 0); 1185 int x = bufferedImage.getMinX(); 1186 int y = bufferedImage.getMinY(); 1187 int w = bufferedImage.getWidth(); 1188 int h = bufferedImage.getHeight(); 1189 int stride = psm.getScanlineStride(); 1190 boolean hastranspixel = false; 1191 for (int j = y; j < y+h; j++) { 1192 int yoff = j * stride; 1193 for (int i = x; i < x+w; i++) { 1194 if ((int_data[yoff+i] & 0xff000000)!=0xff000000 ) { 1195 hastranspixel = true; 1196 break; 1197 } 1198 } 1199 if (hastranspixel) { 1200 break; 1201 } 1202 } 1203 if (hastranspixel == false) { 1204 hasTransparency = false; 1205 } 1206 } 1207 } 1208 } 1209 1210 return hasTransparency; 1211 } 1212 1213 protected boolean isBitmaskTransparency(BufferedImage bufferedImage) { 1214 ColorModel colorModel = bufferedImage.getColorModel(); 1215 return (colorModel != null && 1216 colorModel.getTransparency() == ColorModel.BITMASK); 1217 } 1218 1219 1220 /* An optimisation for the special case of ICM images which have 1221 * bitmask transparency. 1222 */ 1223 protected boolean drawBitmaskImage(BufferedImage bufferedImage, 1224 AffineTransform xform, 1225 Color bgcolor, 1226 int srcX, int srcY, 1227 int srcWidth, int srcHeight) { 1228 1229 ColorModel colorModel = bufferedImage.getColorModel(); 1230 IndexColorModel icm; 1231 int [] pixels; 1232 1233 if (!(colorModel instanceof IndexColorModel)) { 1234 return false; 1235 } else { 1236 icm = (IndexColorModel)colorModel; 1237 } 1238 1239 if (colorModel.getTransparency() != ColorModel.BITMASK) { 1240 return false; 1241 } 1242 1243 // to be compatible with 1.1 printing which treated b/g colors 1244 // with alpha 128 as opaque 1245 if (bgcolor != null && bgcolor.getAlpha() < 128) { 1246 return false; 1247 } 1248 1249 if ((xform.getType() 1250 & ~( AffineTransform.TYPE_UNIFORM_SCALE 1251 | AffineTransform.TYPE_TRANSLATION 1252 | AffineTransform.TYPE_QUADRANT_ROTATION 1253 )) != 0) { 1254 return false; 1255 } 1256 1257 if ((getTransform().getType() 1258 & ~( AffineTransform.TYPE_UNIFORM_SCALE 1259 | AffineTransform.TYPE_TRANSLATION 1260 | AffineTransform.TYPE_QUADRANT_ROTATION 1261 )) != 0) { 1262 return false; 1263 } 1264 1265 BufferedImage subImage = null; 1266 Raster raster = bufferedImage.getRaster(); 1267 int transpixel = icm.getTransparentPixel(); 1268 byte[] alphas = new byte[icm.getMapSize()]; 1269 icm.getAlphas(alphas); 1270 if (transpixel >= 0) { 1271 alphas[transpixel] = 0; 1272 } 1273 1274 /* don't just use srcWidth & srcHeight from application - they 1275 * may exceed the extent of the image - may need to clip. 1276 * The image xform will ensure that points are still mapped properly. 1277 */ 1278 int rw = raster.getWidth(); 1279 int rh = raster.getHeight(); 1280 if (srcX > rw || srcY > rh) { 1281 return false; 1282 } 1283 int right, bottom, wid, hgt; 1284 if (srcX+srcWidth > rw) { 1285 right = rw; 1286 wid = right - srcX; 1287 } else { 1288 right = srcX+srcWidth; 1289 wid = srcWidth; 1290 } 1291 if (srcY+srcHeight > rh) { 1292 bottom = rh; 1293 hgt = bottom - srcY; 1294 } else { 1295 bottom = srcY+srcHeight; 1296 hgt = srcHeight; 1297 } 1298 pixels = new int[wid]; 1299 for (int j=srcY; j<bottom; j++) { 1300 int startx = -1; 1301 raster.getPixels(srcX, j, wid, 1, pixels); 1302 for (int i=srcX; i<right; i++) { 1303 if (alphas[pixels[i-srcX]] == 0) { 1304 if (startx >=0) { 1305 subImage = bufferedImage.getSubimage(startx, j, 1306 i-startx, 1); 1307 xform.translate(startx, j); 1308 drawImageToPlatform(subImage, xform, bgcolor, 1309 0, 0, i-startx, 1, true); 1310 xform.translate(-startx, -j); 1311 startx = -1; 1312 } 1313 } else if (startx < 0) { 1314 startx = i; 1315 } 1316 } 1317 if (startx >= 0) { 1318 subImage = bufferedImage.getSubimage(startx, j, 1319 right - startx, 1); 1320 xform.translate(startx, j); 1321 drawImageToPlatform(subImage, xform, bgcolor, 1322 0, 0, right - startx, 1, true); 1323 xform.translate(-startx, -j); 1324 } 1325 } 1326 return true; 1327 } 1328 1329 1330 1331 /** 1332 * The various <code>drawImage()</code> methods for 1333 * <code>PathGraphics</code> are all decomposed 1334 * into an invocation of <code>drawImageToPlatform</code>. 1335 * The portion of the passed in image defined by 1336 * <code>srcX, srcY, srcWidth, and srcHeight</code> 1337 * is transformed by the supplied AffineTransform and 1338 * drawn using PS to the printer context. 1339 * 1340 * @param img The image to be drawn. 1341 * This method does nothing if <code>img</code> is null. 1342 * @param xform Used to tranform the image before drawing. 1343 * This can be null. 1344 * @param bgcolor This color is drawn where the image has transparent 1345 * pixels. If this parameter is null then the 1346 * pixels already in the destination should show 1347 * through. 1348 * @param srcX With srcY this defines the upper-left corner 1349 * of the portion of the image to be drawn. 1350 * 1351 * @param srcY With srcX this defines the upper-left corner 1352 * of the portion of the image to be drawn. 1353 * @param srcWidth The width of the portion of the image to 1354 * be drawn. 1355 * @param srcHeight The height of the portion of the image to 1356 * be drawn. 1357 * @param handlingTransparency if being recursively called to 1358 * print opaque region of transparent image 1359 */ 1360 protected abstract boolean 1361 drawImageToPlatform(Image img, AffineTransform xform, 1362 Color bgcolor, 1363 int srcX, int srcY, 1364 int srcWidth, int srcHeight, 1365 boolean handlingTransparency); 1366 1367 /** 1368 * Draws as much of the specified image as is currently available. 1369 * The image is drawn with its top-left corner at 1370 * (<i>x</i>, <i>y</i>) in this graphics context's coordinate 1371 * space. Transparent pixels in the image do not affect whatever 1372 * pixels are already there. 1373 * <p> 1374 * This method returns immediately in all cases, even if the 1375 * complete image has not yet been loaded, and it has not been dithered 1376 * and converted for the current output device. 1377 * <p> 1378 * If the image has not yet been completely loaded, then 1379 * <code>drawImage</code> returns <code>false</code>. As more of 1380 * the image becomes available, the process that draws the image notifies 1381 * the specified image observer. 1382 * @param img the specified image to be drawn. 1383 * @param x the <i>x</i> coordinate. 1384 * @param y the <i>y</i> coordinate. 1385 * @param observer object to be notified as more of 1386 * the image is converted. 1387 * @see java.awt.Image 1388 * @see java.awt.image.ImageObserver 1389 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1390 * @since JDK1.0 1391 */ 1392 public boolean drawImage(Image img, int x, int y, 1393 ImageObserver observer) { 1394 1395 return drawImage(img, x, y, null, observer); 1396 } 1397 1398 /** 1399 * Draws as much of the specified image as has already been scaled 1400 * to fit inside the specified rectangle. 1401 * <p> 1402 * The image is drawn inside the specified rectangle of this 1403 * graphics context's coordinate space, and is scaled if 1404 * necessary. Transparent pixels do not affect whatever pixels 1405 * are already there. 1406 * <p> 1407 * This method returns immediately in all cases, even if the 1408 * entire image has not yet been scaled, dithered, and converted 1409 * for the current output device. 1410 * If the current output representation is not yet complete, then 1411 * <code>drawImage</code> returns <code>false</code>. As more of 1412 * the image becomes available, the process that draws the image notifies 1413 * the image observer by calling its <code>imageUpdate</code> method. 1414 * <p> 1415 * A scaled version of an image will not necessarily be 1416 * available immediately just because an unscaled version of the 1417 * image has been constructed for this output device. Each size of 1418 * the image may be cached separately and generated from the original 1419 * data in a separate image production sequence. 1420 * @param img the specified image to be drawn. 1421 * @param x the <i>x</i> coordinate. 1422 * @param y the <i>y</i> coordinate. 1423 * @param width the width of the rectangle. 1424 * @param height the height of the rectangle. 1425 * @param observer object to be notified as more of 1426 * the image is converted. 1427 * @see java.awt.Image 1428 * @see java.awt.image.ImageObserver 1429 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1430 * @since JDK1.0 1431 */ 1432 public boolean drawImage(Image img, int x, int y, 1433 int width, int height, 1434 ImageObserver observer) { 1435 1436 return drawImage(img, x, y, width, height, null, observer); 1437 1438 } 1439 1440 /* 1441 * Draws as much of the specified image as is currently available. 1442 * The image is drawn with its top-left corner at 1443 * (<i>x</i>, <i>y</i>) in this graphics context's coordinate 1444 * space. Transparent pixels are drawn in the specified 1445 * background color. 1446 * <p> 1447 * This operation is equivalent to filling a rectangle of the 1448 * width and height of the specified image with the given color and then 1449 * drawing the image on top of it, but possibly more efficient. 1450 * <p> 1451 * This method returns immediately in all cases, even if the 1452 * complete image has not yet been loaded, and it has not been dithered 1453 * and converted for the current output device. 1454 * <p> 1455 * If the image has not yet been completely loaded, then 1456 * <code>drawImage</code> returns <code>false</code>. As more of 1457 * the image becomes available, the process that draws the image notifies 1458 * the specified image observer. 1459 * @param img the specified image to be drawn. 1460 * This method does nothing if <code>img</code> is null. 1461 * @param x the <i>x</i> coordinate. 1462 * @param y the <i>y</i> coordinate. 1463 * @param bgcolor the background color to paint under the 1464 * non-opaque portions of the image. 1465 * In this WPathGraphics implementation, 1466 * this parameter can be null in which 1467 * case that background is made a transparent 1468 * white. 1469 * @param observer object to be notified as more of 1470 * the image is converted. 1471 * @see java.awt.Image 1472 * @see java.awt.image.ImageObserver 1473 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1474 * @since JDK1.0 1475 */ 1476 public boolean drawImage(Image img, int x, int y, 1477 Color bgcolor, 1478 ImageObserver observer) { 1479 1480 if (img == null) { 1481 return true; 1482 } 1483 1484 boolean result; 1485 int srcWidth = img.getWidth(null); 1486 int srcHeight = img.getHeight(null); 1487 1488 if (srcWidth < 0 || srcHeight < 0) { 1489 result = false; 1490 } else { 1491 result = drawImage(img, x, y, srcWidth, srcHeight, bgcolor, observer); 1492 } 1493 1494 return result; 1495 } 1496 1497 /** 1498 * Draws as much of the specified image as has already been scaled 1499 * to fit inside the specified rectangle. 1500 * <p> 1501 * The image is drawn inside the specified rectangle of this 1502 * graphics context's coordinate space, and is scaled if 1503 * necessary. Transparent pixels are drawn in the specified 1504 * background color. 1505 * This operation is equivalent to filling a rectangle of the 1506 * width and height of the specified image with the given color and then 1507 * drawing the image on top of it, but possibly more efficient. 1508 * <p> 1509 * This method returns immediately in all cases, even if the 1510 * entire image has not yet been scaled, dithered, and converted 1511 * for the current output device. 1512 * If the current output representation is not yet complete then 1513 * <code>drawImage</code> returns <code>false</code>. As more of 1514 * the image becomes available, the process that draws the image notifies 1515 * the specified image observer. 1516 * <p> 1517 * A scaled version of an image will not necessarily be 1518 * available immediately just because an unscaled version of the 1519 * image has been constructed for this output device. Each size of 1520 * the image may be cached separately and generated from the original 1521 * data in a separate image production sequence. 1522 * @param img the specified image to be drawn. 1523 * This method does nothing if <code>img</code> is null. 1524 * @param x the <i>x</i> coordinate. 1525 * @param y the <i>y</i> coordinate. 1526 * @param width the width of the rectangle. 1527 * @param height the height of the rectangle. 1528 * @param bgcolor the background color to paint under the 1529 * non-opaque portions of the image. 1530 * @param observer object to be notified as more of 1531 * the image is converted. 1532 * @see java.awt.Image 1533 * @see java.awt.image.ImageObserver 1534 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1535 * @since JDK1.0 1536 */ 1537 public boolean drawImage(Image img, int x, int y, 1538 int width, int height, 1539 Color bgcolor, 1540 ImageObserver observer) { 1541 1542 if (img == null) { 1543 return true; 1544 } 1545 1546 boolean result; 1547 int srcWidth = img.getWidth(null); 1548 int srcHeight = img.getHeight(null); 1549 1550 if (srcWidth < 0 || srcHeight < 0) { 1551 result = false; 1552 } else { 1553 result = drawImage(img, 1554 x, y, x + width, y + height, 1555 0, 0, srcWidth, srcHeight, 1556 observer); 1557 } 1558 1559 return result; 1560 } 1561 1562 /** 1563 * Draws as much of the specified area of the specified image as is 1564 * currently available, scaling it on the fly to fit inside the 1565 * specified area of the destination drawable surface. Transparent pixels 1566 * do not affect whatever pixels are already there. 1567 * <p> 1568 * This method returns immediately in all cases, even if the 1569 * image area to be drawn has not yet been scaled, dithered, and converted 1570 * for the current output device. 1571 * If the current output representation is not yet complete then 1572 * <code>drawImage</code> returns <code>false</code>. As more of 1573 * the image becomes available, the process that draws the image notifies 1574 * the specified image observer. 1575 * <p> 1576 * This method always uses the unscaled version of the image 1577 * to render the scaled rectangle and performs the required 1578 * scaling on the fly. It does not use a cached, scaled version 1579 * of the image for this operation. Scaling of the image from source 1580 * to destination is performed such that the first coordinate 1581 * of the source rectangle is mapped to the first coordinate of 1582 * the destination rectangle, and the second source coordinate is 1583 * mapped to the second destination coordinate. The subimage is 1584 * scaled and flipped as needed to preserve those mappings. 1585 * @param img the specified image to be drawn 1586 * @param dx1 the <i>x</i> coordinate of the first corner of the 1587 * destination rectangle. 1588 * @param dy1 the <i>y</i> coordinate of the first corner of the 1589 * destination rectangle. 1590 * @param dx2 the <i>x</i> coordinate of the second corner of the 1591 * destination rectangle. 1592 * @param dy2 the <i>y</i> coordinate of the second corner of the 1593 * destination rectangle. 1594 * @param sx1 the <i>x</i> coordinate of the first corner of the 1595 * source rectangle. 1596 * @param sy1 the <i>y</i> coordinate of the first corner of the 1597 * source rectangle. 1598 * @param sx2 the <i>x</i> coordinate of the second corner of the 1599 * source rectangle. 1600 * @param sy2 the <i>y</i> coordinate of the second corner of the 1601 * source rectangle. 1602 * @param observer object to be notified as more of the image is 1603 * scaled and converted. 1604 * @see java.awt.Image 1605 * @see java.awt.image.ImageObserver 1606 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1607 * @since JDK1.1 1608 */ 1609 public boolean drawImage(Image img, 1610 int dx1, int dy1, int dx2, int dy2, 1611 int sx1, int sy1, int sx2, int sy2, 1612 ImageObserver observer) { 1613 1614 return drawImage(img, 1615 dx1, dy1, dx2, dy2, 1616 sx1, sy1, sx2, sy2, 1617 null, observer); 1618 } 1619 1620 /** 1621 * Draws as much of the specified area of the specified image as is 1622 * currently available, scaling it on the fly to fit inside the 1623 * specified area of the destination drawable surface. 1624 * <p> 1625 * Transparent pixels are drawn in the specified background color. 1626 * This operation is equivalent to filling a rectangle of the 1627 * width and height of the specified image with the given color and then 1628 * drawing the image on top of it, but possibly more efficient. 1629 * <p> 1630 * This method returns immediately in all cases, even if the 1631 * image area to be drawn has not yet been scaled, dithered, and converted 1632 * for the current output device. 1633 * If the current output representation is not yet complete then 1634 * <code>drawImage</code> returns <code>false</code>. As more of 1635 * the image becomes available, the process that draws the image notifies 1636 * the specified image observer. 1637 * <p> 1638 * This method always uses the unscaled version of the image 1639 * to render the scaled rectangle and performs the required 1640 * scaling on the fly. It does not use a cached, scaled version 1641 * of the image for this operation. Scaling of the image from source 1642 * to destination is performed such that the first coordinate 1643 * of the source rectangle is mapped to the first coordinate of 1644 * the destination rectangle, and the second source coordinate is 1645 * mapped to the second destination coordinate. The subimage is 1646 * scaled and flipped as needed to preserve those mappings. 1647 * @param img the specified image to be drawn 1648 * This method does nothing if <code>img</code> is null. 1649 * @param dx1 the <i>x</i> coordinate of the first corner of the 1650 * destination rectangle. 1651 * @param dy1 the <i>y</i> coordinate of the first corner of the 1652 * destination rectangle. 1653 * @param dx2 the <i>x</i> coordinate of the second corner of the 1654 * destination rectangle. 1655 * @param dy2 the <i>y</i> coordinate of the second corner of the 1656 * destination rectangle. 1657 * @param sx1 the <i>x</i> coordinate of the first corner of the 1658 * source rectangle. 1659 * @param sy1 the <i>y</i> coordinate of the first corner of the 1660 * source rectangle. 1661 * @param sx2 the <i>x</i> coordinate of the second corner of the 1662 * source rectangle. 1663 * @param sy2 the <i>y</i> coordinate of the second corner of the 1664 * source rectangle. 1665 * @param bgcolor the background color to paint under the 1666 * non-opaque portions of the image. 1667 * @param observer object to be notified as more of the image is 1668 * scaled and converted. 1669 * @see java.awt.Image 1670 * @see java.awt.image.ImageObserver 1671 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) 1672 * @since JDK1.1 1673 */ 1674 public boolean drawImage(Image img, 1675 int dx1, int dy1, int dx2, int dy2, 1676 int sx1, int sy1, int sx2, int sy2, 1677 Color bgcolor, 1678 ImageObserver observer) { 1679 1680 if (img == null) { 1681 return true; 1682 } 1683 int imgWidth = img.getWidth(null); 1684 int imgHeight = img.getHeight(null); 1685 1686 if (imgWidth < 0 || imgHeight < 0) { 1687 return true; 1688 } 1689 1690 int srcWidth = sx2 - sx1; 1691 int srcHeight = sy2 - sy1; 1692 1693 /* Create a transform which describes the changes 1694 * from the source coordinates to the destination 1695 * coordinates. The scaling is determined by the 1696 * ratio of the two rectangles, while the translation 1697 * comes from the difference of their origins. 1698 */ 1699 float scalex = (float) (dx2 - dx1) / srcWidth; 1700 float scaley = (float) (dy2 - dy1) / srcHeight; 1701 AffineTransform xForm 1702 = new AffineTransform(scalex, 1703 0, 1704 0, 1705 scaley, 1706 dx1 - (sx1 * scalex), 1707 dy1 - (sy1 * scaley)); 1708 1709 /* drawImageToPlatform needs the top-left of the source area and 1710 * a positive width and height. The xform describes how to map 1711 * src->dest, so that information is not lost. 1712 */ 1713 int tmp=0; 1714 if (sx2 < sx1) { 1715 tmp = sx1; 1716 sx1 = sx2; 1717 sx2 = tmp; 1718 } 1719 if (sy2 < sy1) { 1720 tmp = sy1; 1721 sy1 = sy2; 1722 sy2 = tmp; 1723 } 1724 1725 /* if src area is beyond the bounds of the image, we must clip it. 1726 * The transform is based on the specified area, not the clipped one. 1727 */ 1728 if (sx1 < 0) { 1729 sx1 = 0; 1730 } else if (sx1 > imgWidth) { // empty srcArea, nothing to draw 1731 sx1 = imgWidth; 1732 } 1733 if (sx2 < 0) { // empty srcArea, nothing to draw 1734 sx2 = 0; 1735 } else if (sx2 > imgWidth) { 1736 sx2 = imgWidth; 1737 } 1738 if (sy1 < 0) { 1739 sy1 = 0; 1740 } else if (sy1 > imgHeight) { // empty srcArea 1741 sy1 = imgHeight; 1742 } 1743 if (sy2 < 0) { // empty srcArea 1744 sy2 = 0; 1745 } else if (sy2 > imgHeight) { 1746 sy2 = imgHeight; 1747 } 1748 1749 srcWidth = sx2 - sx1; 1750 srcHeight = sy2 - sy1; 1751 1752 if (srcWidth <= 0 || srcHeight <= 0) { 1753 return true; 1754 } 1755 1756 return drawImageToPlatform(img, xForm, bgcolor, 1757 sx1, sy1, srcWidth, srcHeight, false); 1758 1759 1760 } 1761 1762 /** 1763 * Draws an image, applying a transform from image space into user space 1764 * before drawing. 1765 * The transformation from user space into device space is done with 1766 * the current transform in the Graphics2D. 1767 * The given transformation is applied to the image before the 1768 * transform attribute in the Graphics2D state is applied. 1769 * The rendering attributes applied include the clip, transform, 1770 * and composite attributes. Note that the result is 1771 * undefined, if the given transform is noninvertible. 1772 * @param img The image to be drawn. 1773 * This method does nothing if <code>img</code> is null. 1774 * @param xform The transformation from image space into user space. 1775 * @param obs The image observer to be notified as more of the image 1776 * is converted. 1777 * @see #transform 1778 * @see #setTransform 1779 * @see #setComposite 1780 * @see #clip 1781 * @see #setClip 1782 */ 1783 public boolean drawImage(Image img, 1784 AffineTransform xform, 1785 ImageObserver obs) { 1786 1787 if (img == null) { 1788 return true; 1789 } 1790 1791 boolean result; 1792 int srcWidth = img.getWidth(null); 1793 int srcHeight = img.getHeight(null); 1794 1795 if (srcWidth < 0 || srcHeight < 0) { 1796 result = false; 1797 } else { 1798 result = drawImageToPlatform(img, xform, null, 1799 0, 0, srcWidth, srcHeight, false); 1800 } 1801 1802 return result; 1803 } 1804 1805 /** 1806 * Draws a BufferedImage that is filtered with a BufferedImageOp. 1807 * The rendering attributes applied include the clip, transform 1808 * and composite attributes. This is equivalent to: 1809 * <pre> 1810 * img1 = op.filter(img, null); 1811 * drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null); 1812 * </pre> 1813 * @param op The filter to be applied to the image before drawing. 1814 * @param img The BufferedImage to be drawn. 1815 * This method does nothing if <code>img</code> is null. 1816 * @param x,y The location in user space where the image should be drawn. 1817 * @see #transform 1818 * @see #setTransform 1819 * @see #setComposite 1820 * @see #clip 1821 * @see #setClip 1822 */ 1823 public void drawImage(BufferedImage img, 1824 BufferedImageOp op, 1825 int x, 1826 int y) { 1827 1828 if (img == null) { 1829 return; 1830 } 1831 1832 int srcWidth = img.getWidth(null); 1833 int srcHeight = img.getHeight(null); 1834 1835 if (op != null) { 1836 img = op.filter(img, null); 1837 } 1838 if (srcWidth <= 0 || srcHeight <= 0) { 1839 return; 1840 } else { 1841 AffineTransform xform = new AffineTransform(1f,0f,0f,1f,x,y); 1842 drawImageToPlatform(img, xform, null, 1843 0, 0, srcWidth, srcHeight, false); 1844 } 1845 1846 } 1847 1848 /** 1849 * Draws an image, applying a transform from image space into user space 1850 * before drawing. 1851 * The transformation from user space into device space is done with 1852 * the current transform in the Graphics2D. 1853 * The given transformation is applied to the image before the 1854 * transform attribute in the Graphics2D state is applied. 1855 * The rendering attributes applied include the clip, transform, 1856 * and composite attributes. Note that the result is 1857 * undefined, if the given transform is noninvertible. 1858 * @param img The image to be drawn. 1859 * This method does nothing if <code>img</code> is null. 1860 * @param xform The transformation from image space into user space. 1861 * @see #transform 1862 * @see #setTransform 1863 * @see #setComposite 1864 * @see #clip 1865 * @see #setClip 1866 */ 1867 public void drawRenderedImage(RenderedImage img, 1868 AffineTransform xform) { 1869 1870 if (img == null) { 1871 return; 1872 } 1873 1874 BufferedImage bufferedImage = null; 1875 int srcWidth = img.getWidth(); 1876 int srcHeight = img.getHeight(); 1877 1878 if (srcWidth <= 0 || srcHeight <= 0) { 1879 return; 1880 } 1881 1882 if (img instanceof BufferedImage) { 1883 bufferedImage = (BufferedImage) img; 1884 } else { 1885 bufferedImage = new BufferedImage(srcWidth, srcHeight, 1886 BufferedImage.TYPE_INT_ARGB); 1887 Graphics2D imageGraphics = bufferedImage.createGraphics(); 1888 imageGraphics.drawRenderedImage(img, xform); 1889 } 1890 1891 drawImageToPlatform(bufferedImage, xform, null, 1892 0, 0, srcWidth, srcHeight, false); 1893 1894 } 1895 1896 }