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