Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/sun/print/PathGraphics.java
41153 views
1
/*
2
* Copyright (c) 1998, 2018, 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.AlphaComposite;
39
import java.awt.Composite;
40
import java.awt.Color;
41
import java.awt.Font;
42
import java.awt.Graphics2D;
43
import java.awt.Image;
44
import java.awt.Paint;
45
import java.awt.Polygon;
46
import java.awt.Shape;
47
48
import java.awt.geom.Path2D;
49
import java.text.AttributedCharacterIterator;
50
51
import java.awt.font.FontRenderContext;
52
import java.awt.font.GlyphVector;
53
import java.awt.font.TextAttribute;
54
import java.awt.font.TextLayout;
55
56
import java.awt.geom.AffineTransform;
57
import java.awt.geom.Arc2D;
58
import java.awt.geom.Ellipse2D;
59
import java.awt.geom.Line2D;
60
import java.awt.geom.Point2D;
61
import java.awt.geom.Rectangle2D;
62
import java.awt.geom.RoundRectangle2D;
63
import java.awt.geom.PathIterator;
64
65
import java.awt.image.BufferedImage;
66
import java.awt.image.BufferedImageOp;
67
import java.awt.image.ColorModel;
68
import java.awt.image.DataBuffer;
69
import java.awt.image.DataBufferInt;
70
import java.awt.image.ImageObserver;
71
import java.awt.image.IndexColorModel;
72
import java.awt.image.Raster;
73
import java.awt.image.RenderedImage;
74
import java.awt.image.SampleModel;
75
import java.awt.image.SinglePixelPackedSampleModel;
76
import java.awt.image.VolatileImage;
77
import sun.awt.image.ByteComponentRaster;
78
import sun.awt.image.ToolkitImage;
79
import sun.awt.image.SunWritableRaster;
80
81
import java.awt.print.PageFormat;
82
import java.awt.print.Printable;
83
import java.awt.print.PrinterException;
84
import java.awt.print.PrinterGraphics;
85
import java.awt.print.PrinterJob;
86
87
import java.util.Map;
88
89
public abstract class PathGraphics extends ProxyGraphics2D {
90
91
private Printable mPainter;
92
private PageFormat mPageFormat;
93
private int mPageIndex;
94
private boolean mCanRedraw;
95
protected boolean printingGlyphVector;
96
97
protected PathGraphics(Graphics2D graphics, PrinterJob printerJob,
98
Printable painter, PageFormat pageFormat,
99
int pageIndex, boolean canRedraw) {
100
super(graphics, printerJob);
101
102
mPainter = painter;
103
mPageFormat = pageFormat;
104
mPageIndex = pageIndex;
105
mCanRedraw = canRedraw;
106
}
107
108
/**
109
* Return the Printable instance responsible for drawing
110
* into this Graphics.
111
*/
112
protected Printable getPrintable() {
113
return mPainter;
114
}
115
116
/**
117
* Return the PageFormat associated with this page of
118
* Graphics.
119
*/
120
protected PageFormat getPageFormat() {
121
return mPageFormat;
122
}
123
124
/**
125
* Return the page index associated with this Graphics.
126
*/
127
protected int getPageIndex() {
128
return mPageIndex;
129
}
130
131
/**
132
* Return true if we are allowed to ask the application
133
* to redraw portions of the page. In general, with the
134
* PrinterJob API, the application can be asked to do a
135
* redraw. When PrinterJob is emulating PrintJob then we
136
* can not.
137
*/
138
public boolean canDoRedraws() {
139
return mCanRedraw;
140
}
141
142
/**
143
* Redraw a rectanglular area using a proxy graphics
144
*/
145
public abstract void redrawRegion(Rectangle2D region,
146
double scaleX, double scaleY,
147
Shape clip,
148
AffineTransform devTransform)
149
150
throws PrinterException ;
151
152
/**
153
* Draws a line, using the current color, between the points
154
* <code>(x1,&nbsp;y1)</code> and <code>(x2,&nbsp;y2)</code>
155
* in this graphics context's coordinate system.
156
* @param x1 the first point's <i>x</i> coordinate.
157
* @param y1 the first point's <i>y</i> coordinate.
158
* @param x2 the second point's <i>x</i> coordinate.
159
* @param y2 the second point's <i>y</i> coordinate.
160
*/
161
public void drawLine(int x1, int y1, int x2, int y2) {
162
163
Paint paint = getPaint();
164
165
try {
166
AffineTransform deviceTransform = getTransform();
167
if (getClip() != null) {
168
deviceClip(getClip().getPathIterator(deviceTransform));
169
}
170
171
deviceDrawLine(x1, y1, x2, y2, (Color) paint);
172
173
} catch (ClassCastException e) {
174
throw new IllegalArgumentException("Expected a Color instance");
175
}
176
}
177
178
179
/**
180
* Draws the outline of the specified rectangle.
181
* The left and right edges of the rectangle are at
182
* {@code x} and <code>x&nbsp;+&nbsp;width</code>.
183
* The top and bottom edges are at
184
* {@code y} and <code>y&nbsp;+&nbsp;height</code>.
185
* The rectangle is drawn using the graphics context's current color.
186
* @param x the <i>x</i> coordinate
187
* of the rectangle to be drawn.
188
* @param y the <i>y</i> coordinate
189
* of the rectangle to be drawn.
190
* @param width the width of the rectangle to be drawn.
191
* @param height the height of the rectangle to be drawn.
192
* @see java.awt.Graphics#fillRect
193
* @see java.awt.Graphics#clearRect
194
*/
195
public void drawRect(int x, int y, int width, int height) {
196
197
Paint paint = getPaint();
198
199
try {
200
AffineTransform deviceTransform = getTransform();
201
if (getClip() != null) {
202
deviceClip(getClip().getPathIterator(deviceTransform));
203
}
204
205
deviceFrameRect(x, y, width, height, (Color) paint);
206
207
} catch (ClassCastException e) {
208
throw new IllegalArgumentException("Expected a Color instance");
209
}
210
211
}
212
213
/**
214
* Fills the specified rectangle.
215
* The left and right edges of the rectangle are at
216
* {@code x} and <code>x&nbsp;+&nbsp;width&nbsp;-&nbsp;1</code>.
217
* The top and bottom edges are at
218
* {@code y} and <code>y&nbsp;+&nbsp;height&nbsp;-&nbsp;1</code>.
219
* The resulting rectangle covers an area
220
* {@code width} pixels wide by
221
* {@code height} pixels tall.
222
* The rectangle is filled using the graphics context's current color.
223
* @param x the <i>x</i> coordinate
224
* of the rectangle to be filled.
225
* @param y the <i>y</i> coordinate
226
* of the rectangle to be filled.
227
* @param width the width of the rectangle to be filled.
228
* @param height the height of the rectangle to be filled.
229
* @see java.awt.Graphics#clearRect
230
* @see java.awt.Graphics#drawRect
231
*/
232
public void fillRect(int x, int y, int width, int height){
233
234
Paint paint = getPaint();
235
236
try {
237
AffineTransform deviceTransform = getTransform();
238
if (getClip() != null) {
239
deviceClip(getClip().getPathIterator(deviceTransform));
240
}
241
242
deviceFillRect(x, y, width, height, (Color) paint);
243
244
} catch (ClassCastException e) {
245
throw new IllegalArgumentException("Expected a Color instance");
246
}
247
}
248
249
/**
250
* Clears the specified rectangle by filling it with the background
251
* color of the current drawing surface. This operation does not
252
* use the current paint mode.
253
* <p>
254
* Beginning with Java&nbsp;1.1, the background color
255
* of offscreen images may be system dependent. Applications should
256
* use {@code setColor} followed by {@code fillRect} to
257
* ensure that an offscreen image is cleared to a specific color.
258
* @param x the <i>x</i> coordinate of the rectangle to clear.
259
* @param y the <i>y</i> coordinate of the rectangle to clear.
260
* @param width the width of the rectangle to clear.
261
* @param height the height of the rectangle to clear.
262
* @see java.awt.Graphics#fillRect(int, int, int, int)
263
* @see java.awt.Graphics#drawRect
264
* @see java.awt.Graphics#setColor(java.awt.Color)
265
* @see java.awt.Graphics#setPaintMode
266
* @see java.awt.Graphics#setXORMode(java.awt.Color)
267
*/
268
public void clearRect(int x, int y, int width, int height) {
269
270
fill(new Rectangle2D.Float(x, y, width, height), getBackground());
271
}
272
273
/**
274
* Draws an outlined round-cornered rectangle using this graphics
275
* context's current color. The left and right edges of the rectangle
276
* are at {@code x} and <code>x&nbsp;+&nbsp;width</code>,
277
* respectively. The top and bottom edges of the rectangle are at
278
* {@code y} and <code>y&nbsp;+&nbsp;height</code>.
279
* @param x the <i>x</i> coordinate of the rectangle to be drawn.
280
* @param y the <i>y</i> coordinate of the rectangle to be drawn.
281
* @param width the width of the rectangle to be drawn.
282
* @param height the height of the rectangle to be drawn.
283
* @param arcWidth the horizontal diameter of the arc
284
* at the four corners.
285
* @param arcHeight the vertical diameter of the arc
286
* at the four corners.
287
* @see java.awt.Graphics#fillRoundRect
288
*/
289
public void drawRoundRect(int x, int y, int width, int height,
290
int arcWidth, int arcHeight) {
291
292
draw(new RoundRectangle2D.Float(x, y,
293
width, height,
294
arcWidth, arcHeight));
295
}
296
297
298
/**
299
* Fills the specified rounded corner rectangle with the current color.
300
* The left and right edges of the rectangle
301
* are at {@code x} and <code>x&nbsp;+&nbsp;width&nbsp;-&nbsp;1</code>,
302
* respectively. The top and bottom edges of the rectangle are at
303
* {@code y} and <code>y&nbsp;+&nbsp;height&nbsp;-&nbsp;1</code>.
304
* @param x the <i>x</i> coordinate of the rectangle to be filled.
305
* @param y the <i>y</i> coordinate of the rectangle to be filled.
306
* @param width the width of the rectangle to be filled.
307
* @param height the height of the rectangle to be filled.
308
* @param arcWidth the horizontal diameter
309
* of the arc at the four corners.
310
* @param arcHeight the vertical diameter
311
* of the arc at the four corners.
312
* @see java.awt.Graphics#drawRoundRect
313
*/
314
public void fillRoundRect(int x, int y, int width, int height,
315
int arcWidth, int arcHeight) {
316
317
fill(new RoundRectangle2D.Float(x, y,
318
width, height,
319
arcWidth, arcHeight));
320
}
321
322
/**
323
* Draws the outline of an oval.
324
* The result is a circle or ellipse that fits within the
325
* rectangle specified by the {@code x}, {@code y},
326
* {@code width}, and {@code height} arguments.
327
* <p>
328
* The oval covers an area that is
329
* <code>width&nbsp;+&nbsp;1</code> pixels wide
330
* and <code>height&nbsp;+&nbsp;1</code> pixels tall.
331
* @param x the <i>x</i> coordinate of the upper left
332
* corner of the oval to be drawn.
333
* @param y the <i>y</i> coordinate of the upper left
334
* corner of the oval to be drawn.
335
* @param width the width of the oval to be drawn.
336
* @param height the height of the oval to be drawn.
337
* @see java.awt.Graphics#fillOval
338
* @since 1.0
339
*/
340
public void drawOval(int x, int y, int width, int height) {
341
draw(new Ellipse2D.Float(x, y, width, height));
342
}
343
344
/**
345
* Fills an oval bounded by the specified rectangle with the
346
* current color.
347
* @param x the <i>x</i> coordinate of the upper left corner
348
* of the oval to be filled.
349
* @param y the <i>y</i> coordinate of the upper left corner
350
* of the oval to be filled.
351
* @param width the width of the oval to be filled.
352
* @param height the height of the oval to be filled.
353
* @see java.awt.Graphics#drawOval
354
*/
355
public void fillOval(int x, int y, int width, int height){
356
357
fill(new Ellipse2D.Float(x, y, width, height));
358
}
359
360
/**
361
* Draws the outline of a circular or elliptical arc
362
* covering the specified rectangle.
363
* <p>
364
* The resulting arc begins at {@code startAngle} and extends
365
* for {@code arcAngle} degrees, using the current color.
366
* Angles are interpreted such that 0&nbsp;degrees
367
* is at the 3&nbsp;o'clock position.
368
* A positive value indicates a counter-clockwise rotation
369
* while a negative value indicates a clockwise rotation.
370
* <p>
371
* The center of the arc is the center of the rectangle whose origin
372
* is (<i>x</i>,&nbsp;<i>y</i>) and whose size is specified by the
373
* {@code width} and {@code height} arguments.
374
* <p>
375
* The resulting arc covers an area
376
* <code>width&nbsp;+&nbsp;1</code> pixels wide
377
* by <code>height&nbsp;+&nbsp;1</code> pixels tall.
378
* <p>
379
* The angles are specified relative to the non-square extents of
380
* the bounding rectangle such that 45 degrees always falls on the
381
* line from the center of the ellipse to the upper right corner of
382
* the bounding rectangle. As a result, if the bounding rectangle is
383
* noticeably longer in one axis than the other, the angles to the
384
* start and end of the arc segment will be skewed farther along the
385
* longer axis of the bounds.
386
* @param x the <i>x</i> coordinate of the
387
* upper-left corner of the arc to be drawn.
388
* @param y the <i>y</i> coordinate of the
389
* upper-left corner of the arc to be drawn.
390
* @param width the width of the arc to be drawn.
391
* @param height the height of the arc to be drawn.
392
* @param startAngle the beginning angle.
393
* @param arcAngle the angular extent of the arc,
394
* relative to the start angle.
395
* @see java.awt.Graphics#fillArc
396
*/
397
public void drawArc(int x, int y, int width, int height,
398
int startAngle, int arcAngle) {
399
draw(new Arc2D.Float(x, y, width, height,
400
startAngle, arcAngle,
401
Arc2D.OPEN));
402
}
403
404
405
/**
406
* Fills a circular or elliptical arc covering the specified rectangle.
407
* <p>
408
* The resulting arc begins at {@code startAngle} and extends
409
* for {@code arcAngle} degrees.
410
* Angles are interpreted such that 0&nbsp;degrees
411
* is at the 3&nbsp;o'clock position.
412
* A positive value indicates a counter-clockwise rotation
413
* while a negative value indicates a clockwise rotation.
414
* <p>
415
* The center of the arc is the center of the rectangle whose origin
416
* is (<i>x</i>,&nbsp;<i>y</i>) and whose size is specified by the
417
* {@code width} and {@code height} arguments.
418
* <p>
419
* The resulting arc covers an area
420
* <code>width&nbsp;+&nbsp;1</code> pixels wide
421
* by <code>height&nbsp;+&nbsp;1</code> pixels tall.
422
* <p>
423
* The angles are specified relative to the non-square extents of
424
* the bounding rectangle such that 45 degrees always falls on the
425
* line from the center of the ellipse to the upper right corner of
426
* the bounding rectangle. As a result, if the bounding rectangle is
427
* noticeably longer in one axis than the other, the angles to the
428
* start and end of the arc segment will be skewed farther along the
429
* longer axis of the bounds.
430
* @param x the <i>x</i> coordinate of the
431
* upper-left corner of the arc to be filled.
432
* @param y the <i>y</i> coordinate of the
433
* upper-left corner of the arc to be filled.
434
* @param width the width of the arc to be filled.
435
* @param height the height of the arc to be filled.
436
* @param startAngle the beginning angle.
437
* @param arcAngle the angular extent of the arc,
438
* relative to the start angle.
439
* @see java.awt.Graphics#drawArc
440
*/
441
public void fillArc(int x, int y, int width, int height,
442
int startAngle, int arcAngle) {
443
444
fill(new Arc2D.Float(x, y, width, height,
445
startAngle, arcAngle,
446
Arc2D.PIE));
447
}
448
449
/**
450
* Draws a sequence of connected lines defined by
451
* arrays of <i>x</i> and <i>y</i> coordinates.
452
* Each pair of (<i>x</i>,&nbsp;<i>y</i>) coordinates defines a point.
453
* The figure is not closed if the first point
454
* differs from the last point.
455
* @param xPoints an array of <i>x</i> points
456
* @param yPoints an array of <i>y</i> points
457
* @param nPoints the total number of points
458
* @see java.awt.Graphics#drawPolygon(int[], int[], int)
459
* @since 1.1
460
*/
461
public void drawPolyline(int[] xPoints, int[] yPoints,
462
int nPoints) {
463
464
if (nPoints == 2) {
465
draw(new Line2D.Float(xPoints[0], yPoints[0],
466
xPoints[1], yPoints[1]));
467
} else if (nPoints > 2) {
468
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD, nPoints);
469
path.moveTo(xPoints[0], yPoints[0]);
470
for(int i = 1; i < nPoints; i++) {
471
path.lineTo(xPoints[i], yPoints[i]);
472
}
473
draw(path);
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} 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}.
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} coordinates.
492
* @param yPoints a an array of {@code y} 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} 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} 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}.
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} coordinates.
530
* @param yPoints a an array of {@code y} 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 1.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 = FontUtilities.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 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 (FontUtilities.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 &&
1027
glyph >= 0 && glyph < numGlyphs &&
1028
(glyphToCharMap[glyph] ==
1029
CharToGlyphMapper.INVISIBLE_GLYPH_ID)) {
1030
glyphToCharMap[glyph] = c;
1031
}
1032
}
1033
return glyphToCharMap;
1034
}
1035
1036
/**
1037
* Strokes the outline of a Shape using the settings of the current
1038
* graphics state. The rendering attributes applied include the
1039
* clip, transform, paint or color, composite and stroke attributes.
1040
* @param s The shape to be drawn.
1041
* @see #setStroke
1042
* @see #setPaint
1043
* @see java.awt.Graphics#setColor
1044
* @see #transform
1045
* @see #setTransform
1046
* @see #clip
1047
* @see #setClip
1048
* @see #setComposite
1049
*/
1050
public void draw(Shape s) {
1051
1052
fill(getStroke().createStrokedShape(s));
1053
}
1054
1055
/**
1056
* Fills the interior of a Shape using the settings of the current
1057
* graphics state. The rendering attributes applied include the
1058
* clip, transform, paint or color, and composite.
1059
* @see #setPaint
1060
* @see java.awt.Graphics#setColor
1061
* @see #transform
1062
* @see #setTransform
1063
* @see #setComposite
1064
* @see #clip
1065
* @see #setClip
1066
*/
1067
public void fill(Shape s) {
1068
Paint paint = getPaint();
1069
1070
try {
1071
fill(s, (Color) paint);
1072
1073
/* The PathGraphics class only supports filling with
1074
* solid colors and so we do not expect the cast of Paint
1075
* to Color to fail. If it does fail then something went
1076
* wrong, like the app draw a page with a solid color but
1077
* then redrew it with a Gradient.
1078
*/
1079
} catch (ClassCastException e) {
1080
throw new IllegalArgumentException("Expected a Color instance");
1081
}
1082
}
1083
1084
public void fill(Shape s, Color color) {
1085
AffineTransform deviceTransform = getTransform();
1086
1087
if (getClip() != null) {
1088
deviceClip(getClip().getPathIterator(deviceTransform));
1089
}
1090
deviceFill(s.getPathIterator(deviceTransform), color);
1091
}
1092
1093
/**
1094
* Fill the path defined by {@code pathIter}
1095
* with the specified color.
1096
* The path is provided in device coordinates.
1097
*/
1098
protected abstract void deviceFill(PathIterator pathIter, Color color);
1099
1100
/*
1101
* Set the clipping path to that defined by
1102
* the passed in {@code PathIterator}.
1103
*/
1104
protected abstract void deviceClip(PathIterator pathIter);
1105
1106
/*
1107
* Draw the outline of the rectangle without using path
1108
* if supported by platform.
1109
*/
1110
protected abstract void deviceFrameRect(int x, int y,
1111
int width, int height,
1112
Color color);
1113
1114
/*
1115
* Draw a line without using path if supported by platform.
1116
*/
1117
protected abstract void deviceDrawLine(int xBegin, int yBegin,
1118
int xEnd, int yEnd, Color color);
1119
1120
/*
1121
* Fill a rectangle using specified color.
1122
*/
1123
protected abstract void deviceFillRect(int x, int y,
1124
int width, int height, Color color);
1125
1126
/* Obtain a BI from known implementations of java.awt.Image
1127
*/
1128
protected BufferedImage getBufferedImage(Image img) {
1129
if (img instanceof BufferedImage) {
1130
// Otherwise we expect a BufferedImage to behave as a standard BI
1131
return (BufferedImage)img;
1132
} else if (img instanceof ToolkitImage) {
1133
// This can be null if the image isn't loaded yet.
1134
// This is fine as in that case our caller will return
1135
// as it will only draw a fully loaded image
1136
return ((ToolkitImage)img).getBufferedImage();
1137
} else if (img instanceof VolatileImage) {
1138
// VI needs to make a new BI: this is unavoidable but
1139
// I don't expect VI's to be "huge" in any case.
1140
return ((VolatileImage)img).getSnapshot();
1141
} else {
1142
// may be null or may be some non-standard Image which
1143
// shouldn't happen as Image is implemented by the platform
1144
// not by applications
1145
// If you add a new Image implementation to the platform you
1146
// will need to support it here similarly to VI.
1147
return null;
1148
}
1149
}
1150
1151
/**
1152
* Return true if the BufferedImage argument has non-opaque
1153
* bits in it and therefore can not be directly rendered by
1154
* GDI. Return false if the image is opaque. If this function
1155
* can not tell for sure whether the image has transparent
1156
* pixels then it assumes that it does.
1157
*/
1158
protected boolean hasTransparentPixels(BufferedImage bufferedImage) {
1159
ColorModel colorModel = bufferedImage.getColorModel();
1160
boolean hasTransparency = colorModel == null
1161
? true
1162
: colorModel.getTransparency() != ColorModel.OPAQUE;
1163
1164
/*
1165
* For the default INT ARGB check the image to see if any pixels are
1166
* really transparent. If there are no transparent pixels then the
1167
* transparency of the color model can be ignored.
1168
* We assume that IndexColorModel images have already been
1169
* checked for transparency and will be OPAQUE unless they actually
1170
* have transparent pixels present.
1171
*/
1172
if (hasTransparency && bufferedImage != null) {
1173
if (bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB ||
1174
bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB_PRE) {
1175
DataBuffer db = bufferedImage.getRaster().getDataBuffer();
1176
SampleModel sm = bufferedImage.getRaster().getSampleModel();
1177
if (db instanceof DataBufferInt &&
1178
sm instanceof SinglePixelPackedSampleModel) {
1179
SinglePixelPackedSampleModel psm =
1180
(SinglePixelPackedSampleModel)sm;
1181
// Stealing the data array for reading only...
1182
int[] int_data =
1183
SunWritableRaster.stealData((DataBufferInt) db, 0);
1184
int x = bufferedImage.getMinX();
1185
int y = bufferedImage.getMinY();
1186
int w = bufferedImage.getWidth();
1187
int h = bufferedImage.getHeight();
1188
int stride = psm.getScanlineStride();
1189
boolean hastranspixel = false;
1190
for (int j = y; j < y+h; j++) {
1191
int yoff = j * stride;
1192
for (int i = x; i < x+w; i++) {
1193
if ((int_data[yoff+i] & 0xff000000)!=0xff000000 ) {
1194
hastranspixel = true;
1195
break;
1196
}
1197
}
1198
if (hastranspixel) {
1199
break;
1200
}
1201
}
1202
if (hastranspixel == false) {
1203
hasTransparency = false;
1204
}
1205
}
1206
}
1207
}
1208
1209
return hasTransparency;
1210
}
1211
1212
protected boolean isBitmaskTransparency(BufferedImage bufferedImage) {
1213
ColorModel colorModel = bufferedImage.getColorModel();
1214
return (colorModel != null &&
1215
colorModel.getTransparency() == ColorModel.BITMASK);
1216
}
1217
1218
1219
/* An optimisation for the special case of ICM images which have
1220
* bitmask transparency.
1221
*/
1222
protected boolean drawBitmaskImage(BufferedImage bufferedImage,
1223
AffineTransform xform,
1224
Color bgcolor,
1225
int srcX, int srcY,
1226
int srcWidth, int srcHeight) {
1227
1228
ColorModel colorModel = bufferedImage.getColorModel();
1229
IndexColorModel icm;
1230
int [] pixels;
1231
1232
if (!(colorModel instanceof IndexColorModel)) {
1233
return false;
1234
} else {
1235
icm = (IndexColorModel)colorModel;
1236
}
1237
1238
if (colorModel.getTransparency() != ColorModel.BITMASK) {
1239
return false;
1240
}
1241
1242
// to be compatible with 1.1 printing which treated b/g colors
1243
// with alpha 128 as opaque
1244
if (bgcolor != null && bgcolor.getAlpha() < 128) {
1245
return false;
1246
}
1247
1248
if ((xform.getType()
1249
& ~( AffineTransform.TYPE_UNIFORM_SCALE
1250
| AffineTransform.TYPE_TRANSLATION
1251
| AffineTransform.TYPE_QUADRANT_ROTATION
1252
)) != 0) {
1253
return false;
1254
}
1255
1256
if ((getTransform().getType()
1257
& ~( AffineTransform.TYPE_UNIFORM_SCALE
1258
| AffineTransform.TYPE_TRANSLATION
1259
| AffineTransform.TYPE_QUADRANT_ROTATION
1260
)) != 0) {
1261
return false;
1262
}
1263
1264
BufferedImage subImage = null;
1265
Raster raster = bufferedImage.getRaster();
1266
int transpixel = icm.getTransparentPixel();
1267
byte[] alphas = new byte[icm.getMapSize()];
1268
icm.getAlphas(alphas);
1269
if (transpixel >= 0) {
1270
alphas[transpixel] = 0;
1271
}
1272
1273
/* don't just use srcWidth & srcHeight from application - they
1274
* may exceed the extent of the image - may need to clip.
1275
* The image xform will ensure that points are still mapped properly.
1276
*/
1277
int rw = raster.getWidth();
1278
int rh = raster.getHeight();
1279
if (srcX > rw || srcY > rh) {
1280
return false;
1281
}
1282
int right, bottom, wid, hgt;
1283
if (srcX+srcWidth > rw) {
1284
right = rw;
1285
wid = right - srcX;
1286
} else {
1287
right = srcX+srcWidth;
1288
wid = srcWidth;
1289
}
1290
if (srcY+srcHeight > rh) {
1291
bottom = rh;
1292
hgt = bottom - srcY;
1293
} else {
1294
bottom = srcY+srcHeight;
1295
hgt = srcHeight;
1296
}
1297
pixels = new int[wid];
1298
for (int j=srcY; j<bottom; j++) {
1299
int startx = -1;
1300
raster.getPixels(srcX, j, wid, 1, pixels);
1301
for (int i=srcX; i<right; i++) {
1302
if (alphas[pixels[i-srcX]] == 0) {
1303
if (startx >=0) {
1304
subImage = bufferedImage.getSubimage(startx, j,
1305
i-startx, 1);
1306
xform.translate(startx, j);
1307
drawImageToPlatform(subImage, xform, bgcolor,
1308
0, 0, i-startx, 1, true);
1309
xform.translate(-startx, -j);
1310
startx = -1;
1311
}
1312
} else if (startx < 0) {
1313
startx = i;
1314
}
1315
}
1316
if (startx >= 0) {
1317
subImage = bufferedImage.getSubimage(startx, j,
1318
right - startx, 1);
1319
xform.translate(startx, j);
1320
drawImageToPlatform(subImage, xform, bgcolor,
1321
0, 0, right - startx, 1, true);
1322
xform.translate(-startx, -j);
1323
}
1324
}
1325
return true;
1326
}
1327
1328
1329
1330
/**
1331
* The various {@code drawImage()} methods for
1332
* {@code PathGraphics} are all decomposed
1333
* into an invocation of {@code drawImageToPlatform}.
1334
* The portion of the passed in image defined by
1335
* {@code srcX, srcY, srcWidth, and srcHeight}
1336
* is transformed by the supplied AffineTransform and
1337
* drawn using PS to the printer context.
1338
*
1339
* @param img The image to be drawn.
1340
* This method does nothing if {@code img} is null.
1341
* @param xform Used to transform the image before drawing.
1342
* This can be null.
1343
* @param bgcolor This color is drawn where the image has transparent
1344
* pixels. If this parameter is null then the
1345
* pixels already in the destination should show
1346
* through.
1347
* @param srcX With srcY this defines the upper-left corner
1348
* of the portion of the image to be drawn.
1349
*
1350
* @param srcY With srcX this defines the upper-left corner
1351
* of the portion of the image to be drawn.
1352
* @param srcWidth The width of the portion of the image to
1353
* be drawn.
1354
* @param srcHeight The height of the portion of the image to
1355
* be drawn.
1356
* @param handlingTransparency if being recursively called to
1357
* print opaque region of transparent image
1358
*/
1359
protected abstract boolean
1360
drawImageToPlatform(Image img, AffineTransform xform,
1361
Color bgcolor,
1362
int srcX, int srcY,
1363
int srcWidth, int srcHeight,
1364
boolean handlingTransparency);
1365
1366
/**
1367
* Draws as much of the specified image as is currently available.
1368
* The image is drawn with its top-left corner at
1369
* (<i>x</i>,&nbsp;<i>y</i>) in this graphics context's coordinate
1370
* space. Transparent pixels in the image do not affect whatever
1371
* pixels are already there.
1372
* <p>
1373
* This method returns immediately in all cases, even if the
1374
* complete image has not yet been loaded, and it has not been dithered
1375
* and converted for the current output device.
1376
* <p>
1377
* If the image has not yet been completely loaded, then
1378
* {@code drawImage} returns {@code false}. As more of
1379
* the image becomes available, the process that draws the image notifies
1380
* the specified image observer.
1381
* @param img the specified image to be drawn.
1382
* @param x the <i>x</i> coordinate.
1383
* @param y the <i>y</i> coordinate.
1384
* @param observer object to be notified as more of
1385
* the image is converted.
1386
* @see java.awt.Image
1387
* @see java.awt.image.ImageObserver
1388
* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1389
* @since 1.0
1390
*/
1391
public boolean drawImage(Image img, int x, int y,
1392
ImageObserver observer) {
1393
1394
return drawImage(img, x, y, null, observer);
1395
}
1396
1397
/**
1398
* Draws as much of the specified image as has already been scaled
1399
* to fit inside the specified rectangle.
1400
* <p>
1401
* The image is drawn inside the specified rectangle of this
1402
* graphics context's coordinate space, and is scaled if
1403
* necessary. Transparent pixels do not affect whatever pixels
1404
* are already there.
1405
* <p>
1406
* This method returns immediately in all cases, even if the
1407
* entire image has not yet been scaled, dithered, and converted
1408
* for the current output device.
1409
* If the current output representation is not yet complete, then
1410
* {@code drawImage} returns {@code false}. As more of
1411
* the image becomes available, the process that draws the image notifies
1412
* the image observer by calling its {@code imageUpdate} method.
1413
* <p>
1414
* A scaled version of an image will not necessarily be
1415
* available immediately just because an unscaled version of the
1416
* image has been constructed for this output device. Each size of
1417
* the image may be cached separately and generated from the original
1418
* data in a separate image production sequence.
1419
* @param img the specified image to be drawn.
1420
* @param x the <i>x</i> coordinate.
1421
* @param y the <i>y</i> coordinate.
1422
* @param width the width of the rectangle.
1423
* @param height the height of the rectangle.
1424
* @param observer object to be notified as more of
1425
* the image is converted.
1426
* @see java.awt.Image
1427
* @see java.awt.image.ImageObserver
1428
* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1429
* @since 1.0
1430
*/
1431
public boolean drawImage(Image img, int x, int y,
1432
int width, int height,
1433
ImageObserver observer) {
1434
1435
return drawImage(img, x, y, width, height, null, observer);
1436
1437
}
1438
1439
/*
1440
* Draws as much of the specified image as is currently available.
1441
* The image is drawn with its top-left corner at
1442
* (<i>x</i>,&nbsp;<i>y</i>) in this graphics context's coordinate
1443
* space. Transparent pixels are drawn in the specified
1444
* background color.
1445
* <p>
1446
* This operation is equivalent to filling a rectangle of the
1447
* width and height of the specified image with the given color and then
1448
* drawing the image on top of it, but possibly more efficient.
1449
* <p>
1450
* This method returns immediately in all cases, even if the
1451
* complete image has not yet been loaded, and it has not been dithered
1452
* and converted for the current output device.
1453
* <p>
1454
* If the image has not yet been completely loaded, then
1455
* {@code drawImage} returns {@code false}. As more of
1456
* the image becomes available, the process that draws the image notifies
1457
* the specified image observer.
1458
* @param img the specified image to be drawn.
1459
* This method does nothing if {@code img} is null.
1460
* @param x the <i>x</i> coordinate.
1461
* @param y the <i>y</i> coordinate.
1462
* @param bgcolor the background color to paint under the
1463
* non-opaque portions of the image.
1464
* In this WPathGraphics implementation,
1465
* this parameter can be null in which
1466
* case that background is made a transparent
1467
* white.
1468
* @param observer object to be notified as more of
1469
* the image is converted.
1470
* @see java.awt.Image
1471
* @see java.awt.image.ImageObserver
1472
* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1473
* @since 1.0
1474
*/
1475
public boolean drawImage(Image img, int x, int y,
1476
Color bgcolor,
1477
ImageObserver observer) {
1478
1479
if (img == null) {
1480
return true;
1481
}
1482
1483
boolean result;
1484
int srcWidth = img.getWidth(null);
1485
int srcHeight = img.getHeight(null);
1486
1487
if (srcWidth < 0 || srcHeight < 0) {
1488
result = false;
1489
} else {
1490
result = drawImage(img, x, y, srcWidth, srcHeight, bgcolor, observer);
1491
}
1492
1493
return result;
1494
}
1495
1496
/**
1497
* Draws as much of the specified image as has already been scaled
1498
* to fit inside the specified rectangle.
1499
* <p>
1500
* The image is drawn inside the specified rectangle of this
1501
* graphics context's coordinate space, and is scaled if
1502
* necessary. Transparent pixels are drawn in the specified
1503
* background color.
1504
* This operation is equivalent to filling a rectangle of the
1505
* width and height of the specified image with the given color and then
1506
* drawing the image on top of it, but possibly more efficient.
1507
* <p>
1508
* This method returns immediately in all cases, even if the
1509
* entire image has not yet been scaled, dithered, and converted
1510
* for the current output device.
1511
* If the current output representation is not yet complete then
1512
* {@code drawImage} returns {@code false}. As more of
1513
* the image becomes available, the process that draws the image notifies
1514
* the specified image observer.
1515
* <p>
1516
* A scaled version of an image will not necessarily be
1517
* available immediately just because an unscaled version of the
1518
* image has been constructed for this output device. Each size of
1519
* the image may be cached separately and generated from the original
1520
* data in a separate image production sequence.
1521
* @param img the specified image to be drawn.
1522
* This method does nothing if {@code img} is null.
1523
* @param x the <i>x</i> coordinate.
1524
* @param y the <i>y</i> coordinate.
1525
* @param width the width of the rectangle.
1526
* @param height the height of the rectangle.
1527
* @param bgcolor the background color to paint under the
1528
* non-opaque portions of the image.
1529
* @param observer object to be notified as more of
1530
* the image is converted.
1531
* @see java.awt.Image
1532
* @see java.awt.image.ImageObserver
1533
* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1534
* @since 1.0
1535
*/
1536
public boolean drawImage(Image img, int x, int y,
1537
int width, int height,
1538
Color bgcolor,
1539
ImageObserver observer) {
1540
1541
if (img == null) {
1542
return true;
1543
}
1544
1545
boolean result;
1546
int srcWidth = img.getWidth(null);
1547
int srcHeight = img.getHeight(null);
1548
1549
if (srcWidth < 0 || srcHeight < 0) {
1550
result = false;
1551
} else {
1552
result = drawImage(img,
1553
x, y, x + width, y + height,
1554
0, 0, srcWidth, srcHeight,
1555
observer);
1556
}
1557
1558
return result;
1559
}
1560
1561
/**
1562
* Draws as much of the specified area of the specified image as is
1563
* currently available, scaling it on the fly to fit inside the
1564
* specified area of the destination drawable surface. Transparent pixels
1565
* do not affect whatever pixels are already there.
1566
* <p>
1567
* This method returns immediately in all cases, even if the
1568
* image area to be drawn has not yet been scaled, dithered, and converted
1569
* for the current output device.
1570
* If the current output representation is not yet complete then
1571
* {@code drawImage} returns {@code false}. As more of
1572
* the image becomes available, the process that draws the image notifies
1573
* the specified image observer.
1574
* <p>
1575
* This method always uses the unscaled version of the image
1576
* to render the scaled rectangle and performs the required
1577
* scaling on the fly. It does not use a cached, scaled version
1578
* of the image for this operation. Scaling of the image from source
1579
* to destination is performed such that the first coordinate
1580
* of the source rectangle is mapped to the first coordinate of
1581
* the destination rectangle, and the second source coordinate is
1582
* mapped to the second destination coordinate. The subimage is
1583
* scaled and flipped as needed to preserve those mappings.
1584
* @param img the specified image to be drawn
1585
* @param dx1 the <i>x</i> coordinate of the first corner of the
1586
* destination rectangle.
1587
* @param dy1 the <i>y</i> coordinate of the first corner of the
1588
* destination rectangle.
1589
* @param dx2 the <i>x</i> coordinate of the second corner of the
1590
* destination rectangle.
1591
* @param dy2 the <i>y</i> coordinate of the second corner of the
1592
* destination rectangle.
1593
* @param sx1 the <i>x</i> coordinate of the first corner of the
1594
* source rectangle.
1595
* @param sy1 the <i>y</i> coordinate of the first corner of the
1596
* source rectangle.
1597
* @param sx2 the <i>x</i> coordinate of the second corner of the
1598
* source rectangle.
1599
* @param sy2 the <i>y</i> coordinate of the second corner of the
1600
* source rectangle.
1601
* @param observer object to be notified as more of the image is
1602
* scaled and converted.
1603
* @see java.awt.Image
1604
* @see java.awt.image.ImageObserver
1605
* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1606
* @since 1.1
1607
*/
1608
public boolean drawImage(Image img,
1609
int dx1, int dy1, int dx2, int dy2,
1610
int sx1, int sy1, int sx2, int sy2,
1611
ImageObserver observer) {
1612
1613
return drawImage(img,
1614
dx1, dy1, dx2, dy2,
1615
sx1, sy1, sx2, sy2,
1616
null, observer);
1617
}
1618
1619
/**
1620
* Draws as much of the specified area of the specified image as is
1621
* currently available, scaling it on the fly to fit inside the
1622
* specified area of the destination drawable surface.
1623
* <p>
1624
* Transparent pixels are drawn in the specified background color.
1625
* This operation is equivalent to filling a rectangle of the
1626
* width and height of the specified image with the given color and then
1627
* drawing the image on top of it, but possibly more efficient.
1628
* <p>
1629
* This method returns immediately in all cases, even if the
1630
* image area to be drawn has not yet been scaled, dithered, and converted
1631
* for the current output device.
1632
* If the current output representation is not yet complete then
1633
* {@code drawImage} returns {@code false}. As more of
1634
* the image becomes available, the process that draws the image notifies
1635
* the specified image observer.
1636
* <p>
1637
* This method always uses the unscaled version of the image
1638
* to render the scaled rectangle and performs the required
1639
* scaling on the fly. It does not use a cached, scaled version
1640
* of the image for this operation. Scaling of the image from source
1641
* to destination is performed such that the first coordinate
1642
* of the source rectangle is mapped to the first coordinate of
1643
* the destination rectangle, and the second source coordinate is
1644
* mapped to the second destination coordinate. The subimage is
1645
* scaled and flipped as needed to preserve those mappings.
1646
* @param img the specified image to be drawn
1647
* This method does nothing if {@code img} is null.
1648
* @param dx1 the <i>x</i> coordinate of the first corner of the
1649
* destination rectangle.
1650
* @param dy1 the <i>y</i> coordinate of the first corner of the
1651
* destination rectangle.
1652
* @param dx2 the <i>x</i> coordinate of the second corner of the
1653
* destination rectangle.
1654
* @param dy2 the <i>y</i> coordinate of the second corner of the
1655
* destination rectangle.
1656
* @param sx1 the <i>x</i> coordinate of the first corner of the
1657
* source rectangle.
1658
* @param sy1 the <i>y</i> coordinate of the first corner of the
1659
* source rectangle.
1660
* @param sx2 the <i>x</i> coordinate of the second corner of the
1661
* source rectangle.
1662
* @param sy2 the <i>y</i> coordinate of the second corner of the
1663
* source rectangle.
1664
* @param bgcolor the background color to paint under the
1665
* non-opaque portions of the image.
1666
* @param observer object to be notified as more of the image is
1667
* scaled and converted.
1668
* @see java.awt.Image
1669
* @see java.awt.image.ImageObserver
1670
* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1671
* @since 1.1
1672
*/
1673
public boolean drawImage(Image img,
1674
int dx1, int dy1, int dx2, int dy2,
1675
int sx1, int sy1, int sx2, int sy2,
1676
Color bgcolor,
1677
ImageObserver observer) {
1678
1679
if (img == null) {
1680
return true;
1681
}
1682
int imgWidth = img.getWidth(null);
1683
int imgHeight = img.getHeight(null);
1684
1685
if (imgWidth < 0 || imgHeight < 0) {
1686
return true;
1687
}
1688
1689
int srcWidth = sx2 - sx1;
1690
int srcHeight = sy2 - sy1;
1691
1692
/* Create a transform which describes the changes
1693
* from the source coordinates to the destination
1694
* coordinates. The scaling is determined by the
1695
* ratio of the two rectangles, while the translation
1696
* comes from the difference of their origins.
1697
*/
1698
float scalex = (float) (dx2 - dx1) / srcWidth;
1699
float scaley = (float) (dy2 - dy1) / srcHeight;
1700
AffineTransform xForm
1701
= new AffineTransform(scalex,
1702
0,
1703
0,
1704
scaley,
1705
dx1 - (sx1 * scalex),
1706
dy1 - (sy1 * scaley));
1707
1708
/* drawImageToPlatform needs the top-left of the source area and
1709
* a positive width and height. The xform describes how to map
1710
* src->dest, so that information is not lost.
1711
*/
1712
int tmp=0;
1713
if (sx2 < sx1) {
1714
tmp = sx1;
1715
sx1 = sx2;
1716
sx2 = tmp;
1717
}
1718
if (sy2 < sy1) {
1719
tmp = sy1;
1720
sy1 = sy2;
1721
sy2 = tmp;
1722
}
1723
1724
/* if src area is beyond the bounds of the image, we must clip it.
1725
* The transform is based on the specified area, not the clipped one.
1726
*/
1727
if (sx1 < 0) {
1728
sx1 = 0;
1729
} else if (sx1 > imgWidth) { // empty srcArea, nothing to draw
1730
sx1 = imgWidth;
1731
}
1732
if (sx2 < 0) { // empty srcArea, nothing to draw
1733
sx2 = 0;
1734
} else if (sx2 > imgWidth) {
1735
sx2 = imgWidth;
1736
}
1737
if (sy1 < 0) {
1738
sy1 = 0;
1739
} else if (sy1 > imgHeight) { // empty srcArea
1740
sy1 = imgHeight;
1741
}
1742
if (sy2 < 0) { // empty srcArea
1743
sy2 = 0;
1744
} else if (sy2 > imgHeight) {
1745
sy2 = imgHeight;
1746
}
1747
1748
srcWidth = sx2 - sx1;
1749
srcHeight = sy2 - sy1;
1750
1751
if (srcWidth <= 0 || srcHeight <= 0) {
1752
return true;
1753
}
1754
1755
return drawImageToPlatform(img, xForm, bgcolor,
1756
sx1, sy1, srcWidth, srcHeight, false);
1757
1758
1759
}
1760
1761
/**
1762
* Draws an image, applying a transform from image space into user space
1763
* before drawing.
1764
* The transformation from user space into device space is done with
1765
* the current transform in the Graphics2D.
1766
* The given transformation is applied to the image before the
1767
* transform attribute in the Graphics2D state is applied.
1768
* The rendering attributes applied include the clip, transform,
1769
* and composite attributes. Note that the result is
1770
* undefined, if the given transform is noninvertible.
1771
* @param img The image to be drawn.
1772
* This method does nothing if {@code img} is null.
1773
* @param xform The transformation from image space into user space.
1774
* @param obs The image observer to be notified as more of the image
1775
* is converted.
1776
* @see #transform
1777
* @see #setTransform
1778
* @see #setComposite
1779
* @see #clip
1780
* @see #setClip
1781
*/
1782
public boolean drawImage(Image img,
1783
AffineTransform xform,
1784
ImageObserver obs) {
1785
1786
if (img == null) {
1787
return true;
1788
}
1789
1790
boolean result;
1791
int srcWidth = img.getWidth(null);
1792
int srcHeight = img.getHeight(null);
1793
1794
if (srcWidth < 0 || srcHeight < 0) {
1795
result = false;
1796
} else {
1797
result = drawImageToPlatform(img, xform, null,
1798
0, 0, srcWidth, srcHeight, false);
1799
}
1800
1801
return result;
1802
}
1803
1804
/**
1805
* Draws a BufferedImage that is filtered with a BufferedImageOp.
1806
* The rendering attributes applied include the clip, transform
1807
* and composite attributes. This is equivalent to:
1808
* <pre>
1809
* img1 = op.filter(img, null);
1810
* drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null);
1811
* </pre>
1812
* @param op The filter to be applied to the image before drawing.
1813
* @param img The BufferedImage to be drawn.
1814
* This method does nothing if {@code img} is null.
1815
* @param x,y The location in user space where the image should be drawn.
1816
* @see #transform
1817
* @see #setTransform
1818
* @see #setComposite
1819
* @see #clip
1820
* @see #setClip
1821
*/
1822
public void drawImage(BufferedImage img,
1823
BufferedImageOp op,
1824
int x,
1825
int y) {
1826
1827
if (img == null) {
1828
return;
1829
}
1830
1831
int srcWidth = img.getWidth(null);
1832
int srcHeight = img.getHeight(null);
1833
1834
if (op != null) {
1835
img = op.filter(img, null);
1836
}
1837
if (srcWidth <= 0 || srcHeight <= 0) {
1838
return;
1839
} else {
1840
AffineTransform xform = new AffineTransform(1f,0f,0f,1f,x,y);
1841
drawImageToPlatform(img, xform, null,
1842
0, 0, srcWidth, srcHeight, false);
1843
}
1844
1845
}
1846
1847
/**
1848
* Draws an image, applying a transform from image space into user space
1849
* before drawing.
1850
* The transformation from user space into device space is done with
1851
* the current transform in the Graphics2D.
1852
* The given transformation is applied to the image before the
1853
* transform attribute in the Graphics2D state is applied.
1854
* The rendering attributes applied include the clip, transform,
1855
* and composite attributes. Note that the result is
1856
* undefined, if the given transform is noninvertible.
1857
* @param img The image to be drawn.
1858
* This method does nothing if {@code img} is null.
1859
* @param xform The transformation from image space into user space.
1860
* @see #transform
1861
* @see #setTransform
1862
* @see #setComposite
1863
* @see #clip
1864
* @see #setClip
1865
*/
1866
public void drawRenderedImage(RenderedImage img,
1867
AffineTransform xform) {
1868
1869
if (img == null) {
1870
return;
1871
}
1872
1873
BufferedImage bufferedImage = null;
1874
int srcWidth = img.getWidth();
1875
int srcHeight = img.getHeight();
1876
1877
if (srcWidth <= 0 || srcHeight <= 0) {
1878
return;
1879
}
1880
1881
if (img instanceof BufferedImage) {
1882
bufferedImage = (BufferedImage) img;
1883
} else {
1884
bufferedImage = new BufferedImage(srcWidth, srcHeight,
1885
BufferedImage.TYPE_INT_ARGB);
1886
Graphics2D imageGraphics = bufferedImage.createGraphics();
1887
imageGraphics.drawRenderedImage(img, xform);
1888
}
1889
1890
drawImageToPlatform(bufferedImage, xform, null,
1891
0, 0, srcWidth, srcHeight, false);
1892
1893
}
1894
1895
protected boolean isCompositing(Composite composite) {
1896
1897
boolean isCompositing = false;
1898
1899
if (composite instanceof AlphaComposite) {
1900
AlphaComposite alphaComposite = (AlphaComposite) composite;
1901
float alpha = alphaComposite.getAlpha();
1902
int rule = alphaComposite.getRule();
1903
1904
if (alpha != 1.0
1905
|| (rule != AlphaComposite.SRC
1906
&& rule != AlphaComposite.SRC_OVER))
1907
{
1908
isCompositing = true;
1909
}
1910
1911
} else {
1912
isCompositing = true;
1913
}
1914
return isCompositing;
1915
}
1916
}
1917
1918