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