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