Path: blob/master/src/java.desktop/share/classes/sun/print/PathGraphics.java
41153 views
/*1* Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.print;2627import java.lang.ref.SoftReference;28import java.util.Hashtable;29import sun.font.CharToGlyphMapper;30import sun.font.CompositeFont;31import sun.font.Font2D;32import sun.font.Font2DHandle;33import sun.font.FontManager;34import sun.font.FontManagerFactory;35import sun.font.FontUtilities;3637import java.awt.AlphaComposite;38import java.awt.Composite;39import java.awt.Color;40import java.awt.Font;41import java.awt.Graphics2D;42import java.awt.Image;43import java.awt.Paint;44import java.awt.Polygon;45import java.awt.Shape;4647import java.awt.geom.Path2D;48import java.text.AttributedCharacterIterator;4950import java.awt.font.FontRenderContext;51import java.awt.font.GlyphVector;52import java.awt.font.TextAttribute;53import java.awt.font.TextLayout;5455import java.awt.geom.AffineTransform;56import java.awt.geom.Arc2D;57import java.awt.geom.Ellipse2D;58import java.awt.geom.Line2D;59import java.awt.geom.Point2D;60import java.awt.geom.Rectangle2D;61import java.awt.geom.RoundRectangle2D;62import java.awt.geom.PathIterator;6364import java.awt.image.BufferedImage;65import java.awt.image.BufferedImageOp;66import java.awt.image.ColorModel;67import java.awt.image.DataBuffer;68import java.awt.image.DataBufferInt;69import java.awt.image.ImageObserver;70import java.awt.image.IndexColorModel;71import java.awt.image.Raster;72import java.awt.image.RenderedImage;73import java.awt.image.SampleModel;74import java.awt.image.SinglePixelPackedSampleModel;75import java.awt.image.VolatileImage;76import sun.awt.image.ByteComponentRaster;77import sun.awt.image.ToolkitImage;78import sun.awt.image.SunWritableRaster;7980import java.awt.print.PageFormat;81import java.awt.print.Printable;82import java.awt.print.PrinterException;83import java.awt.print.PrinterGraphics;84import java.awt.print.PrinterJob;8586import java.util.Map;8788public abstract class PathGraphics extends ProxyGraphics2D {8990private Printable mPainter;91private PageFormat mPageFormat;92private int mPageIndex;93private boolean mCanRedraw;94protected boolean printingGlyphVector;9596protected PathGraphics(Graphics2D graphics, PrinterJob printerJob,97Printable painter, PageFormat pageFormat,98int pageIndex, boolean canRedraw) {99super(graphics, printerJob);100101mPainter = painter;102mPageFormat = pageFormat;103mPageIndex = pageIndex;104mCanRedraw = canRedraw;105}106107/**108* Return the Printable instance responsible for drawing109* into this Graphics.110*/111protected Printable getPrintable() {112return mPainter;113}114115/**116* Return the PageFormat associated with this page of117* Graphics.118*/119protected PageFormat getPageFormat() {120return mPageFormat;121}122123/**124* Return the page index associated with this Graphics.125*/126protected int getPageIndex() {127return mPageIndex;128}129130/**131* Return true if we are allowed to ask the application132* to redraw portions of the page. In general, with the133* PrinterJob API, the application can be asked to do a134* redraw. When PrinterJob is emulating PrintJob then we135* can not.136*/137public boolean canDoRedraws() {138return mCanRedraw;139}140141/**142* Redraw a rectanglular area using a proxy graphics143*/144public abstract void redrawRegion(Rectangle2D region,145double scaleX, double scaleY,146Shape clip,147AffineTransform devTransform)148149throws PrinterException ;150151/**152* Draws a line, using the current color, between the points153* <code>(x1, y1)</code> and <code>(x2, y2)</code>154* in this graphics context's coordinate system.155* @param x1 the first point's <i>x</i> coordinate.156* @param y1 the first point's <i>y</i> coordinate.157* @param x2 the second point's <i>x</i> coordinate.158* @param y2 the second point's <i>y</i> coordinate.159*/160public void drawLine(int x1, int y1, int x2, int y2) {161162Paint paint = getPaint();163164try {165AffineTransform deviceTransform = getTransform();166if (getClip() != null) {167deviceClip(getClip().getPathIterator(deviceTransform));168}169170deviceDrawLine(x1, y1, x2, y2, (Color) paint);171172} catch (ClassCastException e) {173throw new IllegalArgumentException("Expected a Color instance");174}175}176177178/**179* Draws the outline of the specified rectangle.180* The left and right edges of the rectangle are at181* {@code x} and <code>x + width</code>.182* The top and bottom edges are at183* {@code y} and <code>y + height</code>.184* The rectangle is drawn using the graphics context's current color.185* @param x the <i>x</i> coordinate186* of the rectangle to be drawn.187* @param y the <i>y</i> coordinate188* of the rectangle to be drawn.189* @param width the width of the rectangle to be drawn.190* @param height the height of the rectangle to be drawn.191* @see java.awt.Graphics#fillRect192* @see java.awt.Graphics#clearRect193*/194public void drawRect(int x, int y, int width, int height) {195196Paint paint = getPaint();197198try {199AffineTransform deviceTransform = getTransform();200if (getClip() != null) {201deviceClip(getClip().getPathIterator(deviceTransform));202}203204deviceFrameRect(x, y, width, height, (Color) paint);205206} catch (ClassCastException e) {207throw new IllegalArgumentException("Expected a Color instance");208}209210}211212/**213* Fills the specified rectangle.214* The left and right edges of the rectangle are at215* {@code x} and <code>x + width - 1</code>.216* The top and bottom edges are at217* {@code y} and <code>y + height - 1</code>.218* The resulting rectangle covers an area219* {@code width} pixels wide by220* {@code height} pixels tall.221* The rectangle is filled using the graphics context's current color.222* @param x the <i>x</i> coordinate223* of the rectangle to be filled.224* @param y the <i>y</i> coordinate225* of the rectangle to be filled.226* @param width the width of the rectangle to be filled.227* @param height the height of the rectangle to be filled.228* @see java.awt.Graphics#clearRect229* @see java.awt.Graphics#drawRect230*/231public void fillRect(int x, int y, int width, int height){232233Paint paint = getPaint();234235try {236AffineTransform deviceTransform = getTransform();237if (getClip() != null) {238deviceClip(getClip().getPathIterator(deviceTransform));239}240241deviceFillRect(x, y, width, height, (Color) paint);242243} catch (ClassCastException e) {244throw new IllegalArgumentException("Expected a Color instance");245}246}247248/**249* Clears the specified rectangle by filling it with the background250* color of the current drawing surface. This operation does not251* use the current paint mode.252* <p>253* Beginning with Java 1.1, the background color254* of offscreen images may be system dependent. Applications should255* use {@code setColor} followed by {@code fillRect} to256* ensure that an offscreen image is cleared to a specific color.257* @param x the <i>x</i> coordinate of the rectangle to clear.258* @param y the <i>y</i> coordinate of the rectangle to clear.259* @param width the width of the rectangle to clear.260* @param height the height of the rectangle to clear.261* @see java.awt.Graphics#fillRect(int, int, int, int)262* @see java.awt.Graphics#drawRect263* @see java.awt.Graphics#setColor(java.awt.Color)264* @see java.awt.Graphics#setPaintMode265* @see java.awt.Graphics#setXORMode(java.awt.Color)266*/267public void clearRect(int x, int y, int width, int height) {268269fill(new Rectangle2D.Float(x, y, width, height), getBackground());270}271272/**273* Draws an outlined round-cornered rectangle using this graphics274* context's current color. The left and right edges of the rectangle275* are at {@code x} and <code>x + width</code>,276* respectively. The top and bottom edges of the rectangle are at277* {@code y} and <code>y + height</code>.278* @param x the <i>x</i> coordinate of the rectangle to be drawn.279* @param y the <i>y</i> coordinate of the rectangle to be drawn.280* @param width the width of the rectangle to be drawn.281* @param height the height of the rectangle to be drawn.282* @param arcWidth the horizontal diameter of the arc283* at the four corners.284* @param arcHeight the vertical diameter of the arc285* at the four corners.286* @see java.awt.Graphics#fillRoundRect287*/288public void drawRoundRect(int x, int y, int width, int height,289int arcWidth, int arcHeight) {290291draw(new RoundRectangle2D.Float(x, y,292width, height,293arcWidth, arcHeight));294}295296297/**298* Fills the specified rounded corner rectangle with the current color.299* The left and right edges of the rectangle300* are at {@code x} and <code>x + width - 1</code>,301* respectively. The top and bottom edges of the rectangle are at302* {@code y} and <code>y + height - 1</code>.303* @param x the <i>x</i> coordinate of the rectangle to be filled.304* @param y the <i>y</i> coordinate of the rectangle to be filled.305* @param width the width of the rectangle to be filled.306* @param height the height of the rectangle to be filled.307* @param arcWidth the horizontal diameter308* of the arc at the four corners.309* @param arcHeight the vertical diameter310* of the arc at the four corners.311* @see java.awt.Graphics#drawRoundRect312*/313public void fillRoundRect(int x, int y, int width, int height,314int arcWidth, int arcHeight) {315316fill(new RoundRectangle2D.Float(x, y,317width, height,318arcWidth, arcHeight));319}320321/**322* Draws the outline of an oval.323* The result is a circle or ellipse that fits within the324* rectangle specified by the {@code x}, {@code y},325* {@code width}, and {@code height} arguments.326* <p>327* The oval covers an area that is328* <code>width + 1</code> pixels wide329* and <code>height + 1</code> pixels tall.330* @param x the <i>x</i> coordinate of the upper left331* corner of the oval to be drawn.332* @param y the <i>y</i> coordinate of the upper left333* corner of the oval to be drawn.334* @param width the width of the oval to be drawn.335* @param height the height of the oval to be drawn.336* @see java.awt.Graphics#fillOval337* @since 1.0338*/339public void drawOval(int x, int y, int width, int height) {340draw(new Ellipse2D.Float(x, y, width, height));341}342343/**344* Fills an oval bounded by the specified rectangle with the345* current color.346* @param x the <i>x</i> coordinate of the upper left corner347* of the oval to be filled.348* @param y the <i>y</i> coordinate of the upper left corner349* of the oval to be filled.350* @param width the width of the oval to be filled.351* @param height the height of the oval to be filled.352* @see java.awt.Graphics#drawOval353*/354public void fillOval(int x, int y, int width, int height){355356fill(new Ellipse2D.Float(x, y, width, height));357}358359/**360* Draws the outline of a circular or elliptical arc361* covering the specified rectangle.362* <p>363* The resulting arc begins at {@code startAngle} and extends364* for {@code arcAngle} degrees, using the current color.365* Angles are interpreted such that 0 degrees366* is at the 3 o'clock position.367* A positive value indicates a counter-clockwise rotation368* while a negative value indicates a clockwise rotation.369* <p>370* The center of the arc is the center of the rectangle whose origin371* is (<i>x</i>, <i>y</i>) and whose size is specified by the372* {@code width} and {@code height} arguments.373* <p>374* The resulting arc covers an area375* <code>width + 1</code> pixels wide376* by <code>height + 1</code> pixels tall.377* <p>378* The angles are specified relative to the non-square extents of379* the bounding rectangle such that 45 degrees always falls on the380* line from the center of the ellipse to the upper right corner of381* the bounding rectangle. As a result, if the bounding rectangle is382* noticeably longer in one axis than the other, the angles to the383* start and end of the arc segment will be skewed farther along the384* longer axis of the bounds.385* @param x the <i>x</i> coordinate of the386* upper-left corner of the arc to be drawn.387* @param y the <i>y</i> coordinate of the388* upper-left corner of the arc to be drawn.389* @param width the width of the arc to be drawn.390* @param height the height of the arc to be drawn.391* @param startAngle the beginning angle.392* @param arcAngle the angular extent of the arc,393* relative to the start angle.394* @see java.awt.Graphics#fillArc395*/396public void drawArc(int x, int y, int width, int height,397int startAngle, int arcAngle) {398draw(new Arc2D.Float(x, y, width, height,399startAngle, arcAngle,400Arc2D.OPEN));401}402403404/**405* Fills a circular or elliptical arc covering the specified rectangle.406* <p>407* The resulting arc begins at {@code startAngle} and extends408* for {@code arcAngle} degrees.409* Angles are interpreted such that 0 degrees410* is at the 3 o'clock position.411* A positive value indicates a counter-clockwise rotation412* while a negative value indicates a clockwise rotation.413* <p>414* The center of the arc is the center of the rectangle whose origin415* is (<i>x</i>, <i>y</i>) and whose size is specified by the416* {@code width} and {@code height} arguments.417* <p>418* The resulting arc covers an area419* <code>width + 1</code> pixels wide420* by <code>height + 1</code> pixels tall.421* <p>422* The angles are specified relative to the non-square extents of423* the bounding rectangle such that 45 degrees always falls on the424* line from the center of the ellipse to the upper right corner of425* the bounding rectangle. As a result, if the bounding rectangle is426* noticeably longer in one axis than the other, the angles to the427* start and end of the arc segment will be skewed farther along the428* longer axis of the bounds.429* @param x the <i>x</i> coordinate of the430* upper-left corner of the arc to be filled.431* @param y the <i>y</i> coordinate of the432* upper-left corner of the arc to be filled.433* @param width the width of the arc to be filled.434* @param height the height of the arc to be filled.435* @param startAngle the beginning angle.436* @param arcAngle the angular extent of the arc,437* relative to the start angle.438* @see java.awt.Graphics#drawArc439*/440public void fillArc(int x, int y, int width, int height,441int startAngle, int arcAngle) {442443fill(new Arc2D.Float(x, y, width, height,444startAngle, arcAngle,445Arc2D.PIE));446}447448/**449* Draws a sequence of connected lines defined by450* arrays of <i>x</i> and <i>y</i> coordinates.451* Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point.452* The figure is not closed if the first point453* differs from the last point.454* @param xPoints an array of <i>x</i> points455* @param yPoints an array of <i>y</i> points456* @param nPoints the total number of points457* @see java.awt.Graphics#drawPolygon(int[], int[], int)458* @since 1.1459*/460public void drawPolyline(int[] xPoints, int[] yPoints,461int nPoints) {462463if (nPoints == 2) {464draw(new Line2D.Float(xPoints[0], yPoints[0],465xPoints[1], yPoints[1]));466} else if (nPoints > 2) {467Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD, nPoints);468path.moveTo(xPoints[0], yPoints[0]);469for(int i = 1; i < nPoints; i++) {470path.lineTo(xPoints[i], yPoints[i]);471}472draw(path);473}474}475476477/**478* Draws a closed polygon defined by479* arrays of <i>x</i> and <i>y</i> coordinates.480* Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point.481* <p>482* This method draws the polygon defined by {@code nPoint} line483* segments, where the first <code>nPoint - 1</code>484* line segments are line segments from485* <code>(xPoints[i - 1], yPoints[i - 1])</code>486* to <code>(xPoints[i], yPoints[i])</code>, for487* 1 ≤ <i>i</i> ≤ {@code nPoints}.488* The figure is automatically closed by drawing a line connecting489* the final point to the first point, if those points are different.490* @param xPoints a an array of {@code x} coordinates.491* @param yPoints a an array of {@code y} coordinates.492* @param nPoints a the total number of points.493* @see java.awt.Graphics#fillPolygon494* @see java.awt.Graphics#drawPolyline495*/496public void drawPolygon(int[] xPoints, int[] yPoints,497int nPoints) {498499draw(new Polygon(xPoints, yPoints, nPoints));500}501502/**503* Draws the outline of a polygon defined by the specified504* {@code Polygon} object.505* @param p the polygon to draw.506* @see java.awt.Graphics#fillPolygon507* @see java.awt.Graphics#drawPolyline508*/509public void drawPolygon(Polygon p) {510draw(p);511}512513/**514* Fills a closed polygon defined by515* arrays of <i>x</i> and <i>y</i> coordinates.516* <p>517* This method draws the polygon defined by {@code nPoint} line518* segments, where the first <code>nPoint - 1</code>519* line segments are line segments from520* <code>(xPoints[i - 1], yPoints[i - 1])</code>521* to <code>(xPoints[i], yPoints[i])</code>, for522* 1 ≤ <i>i</i> ≤ {@code nPoints}.523* The figure is automatically closed by drawing a line connecting524* the final point to the first point, if those points are different.525* <p>526* The area inside the polygon is defined using an527* even-odd fill rule, also known as the alternating rule.528* @param xPoints a an array of {@code x} coordinates.529* @param yPoints a an array of {@code y} coordinates.530* @param nPoints a the total number of points.531* @see java.awt.Graphics#drawPolygon(int[], int[], int)532*/533public void fillPolygon(int[] xPoints, int[] yPoints,534int nPoints) {535536fill(new Polygon(xPoints, yPoints, nPoints));537}538539540/**541* Fills the polygon defined by the specified Polygon object with542* the graphics context's current color.543* <p>544* The area inside the polygon is defined using an545* even-odd fill rule, also known as the alternating rule.546* @param p the polygon to fill.547* @see java.awt.Graphics#drawPolygon(int[], int[], int)548*/549public void fillPolygon(Polygon p) {550551fill(p);552}553554/**555* Draws the text given by the specified string, using this556* graphics context's current font and color. The baseline of the557* first character is at position (<i>x</i>, <i>y</i>) in this558* graphics context's coordinate system.559* @param str the string to be drawn.560* @param x the <i>x</i> coordinate.561* @param y the <i>y</i> coordinate.562* @see java.awt.Graphics#drawBytes563* @see java.awt.Graphics#drawChars564* @since 1.0565*/566public void drawString(String str, int x, int y) {567drawString(str, (float) x, (float) y);568}569570public void drawString(String str, float x, float y) {571if (str.length() == 0) {572return;573}574TextLayout layout =575new TextLayout(str, getFont(), getFontRenderContext());576layout.draw(this, x, y);577}578579protected void drawString(String str, float x, float y,580Font font, FontRenderContext frc, float w) {581TextLayout layout =582new TextLayout(str, font, frc);583Shape textShape =584layout.getOutline(AffineTransform.getTranslateInstance(x, y));585fill(textShape);586}587588/**589* Draws the text given by the specified iterator, using this590* graphics context's current color. The iterator has to specify a font591* for each character. The baseline of the592* first character is at position (<i>x</i>, <i>y</i>) in this593* graphics context's coordinate system.594* @param iterator the iterator whose text is to be drawn595* @param x the <i>x</i> coordinate.596* @param y the <i>y</i> coordinate.597* @see java.awt.Graphics#drawBytes598* @see java.awt.Graphics#drawChars599*/600public void drawString(AttributedCharacterIterator iterator,601int x, int y) {602drawString(iterator, (float) x, (float) y);603}604public void drawString(AttributedCharacterIterator iterator,605float x, float y) {606if (iterator == null) {607throw608new NullPointerException("attributedcharacteriterator is null");609}610TextLayout layout =611new TextLayout(iterator, getFontRenderContext());612layout.draw(this, x, y);613}614615/**616* Draws a GlyphVector.617* The rendering attributes applied include the clip, transform,618* paint or color, and composite attributes. The GlyphVector specifies619* individual glyphs from a Font.620* @param g The GlyphVector to be drawn.621* @param x,y The coordinates where the glyphs should be drawn.622* @see #setPaint623* @see java.awt.Graphics#setColor624* @see #transform625* @see #setTransform626* @see #setComposite627* @see #clip628* @see #setClip629*/630public void drawGlyphVector(GlyphVector g,631float x,632float y) {633634/* We should not reach here if printingGlyphVector is already true.635* Add an assert so this can be tested if need be.636* But also ensure that we do at least render properly by filling637* the outline.638*/639if (printingGlyphVector) {640assert !printingGlyphVector; // ie false.641fill(g.getOutline(x, y));642return;643}644645try {646printingGlyphVector = true;647if (RasterPrinterJob.shapeTextProp ||648!printedSimpleGlyphVector(g, x, y)) {649fill(g.getOutline(x, y));650}651} finally {652printingGlyphVector = false;653}654}655656protected static SoftReference<Hashtable<Font2DHandle,Object>>657fontMapRef = new SoftReference<Hashtable<Font2DHandle,Object>>(null);658659protected int platformFontCount(Font font, String str) {660return 0;661}662663/**664* Default implementation returns false.665* Callers of this method must always be prepared for this,666* and delegate to outlines or some other solution.667*/668protected boolean printGlyphVector(GlyphVector gv, float x, float y) {669return false;670}671672/* GlyphVectors are usually encountered because TextLayout is in use.673* Some times TextLayout is needed to handle complex text or some674* rendering attributes trigger it.675* We try to print GlyphVectors by reconstituting into a String,676* as that is most recoverable for applications that export to formats677* such as Postscript or PDF. In some cases (eg where its not complex678* text and its just that positions aren't what we'd expect) we print679* one character at a time. positioning individually.680* Failing that, if we can directly send glyph codes to the printer681* then we do that (printGlyphVector).682* As a last resort we return false and let the caller print as filled683* shapes.684*/685boolean printedSimpleGlyphVector(GlyphVector g, float x, float y) {686687int flags = g.getLayoutFlags();688689/* We can't handle RTL, re-ordering, complex glyphs etc by690* reconstituting glyphs into a String. So if any flags besides691* position adjustments are set, see if we can directly692* print the GlyphVector as glyph codes, using the positions693* layout has assigned. If that fails return false;694*/695if (flags != 0 && flags != GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) {696return printGlyphVector(g, x, y);697}698699Font font = g.getFont();700Font2D font2D = FontUtilities.getFont2D(font);701if (font2D.handle.font2D != font2D) {702/* suspicious, may be a bad font. lets bail */703return false;704}705Hashtable<Font2DHandle,Object> fontMap;706synchronized (PathGraphics.class) {707fontMap = fontMapRef.get();708if (fontMap == null) {709fontMap = new Hashtable<Font2DHandle,Object>();710fontMapRef =711new SoftReference<Hashtable<Font2DHandle,Object>>(fontMap);712}713}714715int numGlyphs = g.getNumGlyphs();716int[] glyphCodes = g.getGlyphCodes(0, numGlyphs, null);717718char[] glyphToCharMap = null;719char[][] mapArray = null;720CompositeFont cf = null;721722/* Build the needed maps for this font in a synchronized block */723synchronized (fontMap) {724if (font2D instanceof CompositeFont) {725cf = (CompositeFont)font2D;726int numSlots = cf.getNumSlots();727mapArray = (char[][])fontMap.get(font2D.handle);728if (mapArray == null) {729mapArray = new char[numSlots][];730fontMap.put(font2D.handle, mapArray);731}732for (int i=0; i<numGlyphs;i++) {733int slot = glyphCodes[i] >>> 24;734if (slot >= numSlots) { /* shouldn't happen */735return false;736}737if (mapArray[slot] == null) {738Font2D slotFont = cf.getSlotFont(slot);739char[] map = (char[])fontMap.get(slotFont.handle);740if (map == null) {741map = getGlyphToCharMapForFont(slotFont);742}743mapArray[slot] = map;744}745}746} else {747glyphToCharMap = (char[])fontMap.get(font2D.handle);748if (glyphToCharMap == null) {749glyphToCharMap = getGlyphToCharMapForFont(font2D);750fontMap.put(font2D.handle, glyphToCharMap);751}752}753}754755char[] chars = new char[numGlyphs];756if (cf != null) {757for (int i=0; i<numGlyphs; i++) {758int gc = glyphCodes[i];759char[] map = mapArray[gc >>> 24];760gc = gc & 0xffffff;761if (map == null) {762return false;763}764/* X11 symbol & dingbats fonts used only for global metrics,765* so the glyph codes we have really refer to Lucida Sans766* Regular.767* So its possible the glyph code may appear out of range.768* Note that later on we double-check the glyph codes that769* we get from re-creating the GV from the string are the770* same as those we started with.771*772* If the glyphcode is INVISIBLE_GLYPH_ID then this may773* be \t, \n or \r which are mapped to that by layout.774* This is a case we can handle. It doesn't matter what775* character we use (we use \n) so long as layout maps it776* back to this in the verification, since the invisible777* glyph isn't visible :)778*/779char ch;780if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {781ch = '\n';782} else if (gc < 0 || gc >= map.length) {783return false;784} else {785ch = map[gc];786}787if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {788chars[i] = ch;789} else {790return false;791}792}793} else {794for (int i=0; i<numGlyphs; i++) {795int gc = glyphCodes[i];796char ch;797if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {798ch = '\n';799} else if (gc < 0 || gc >= glyphToCharMap.length) {800return false;801} else {802ch = glyphToCharMap[gc];803}804if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {805chars[i] = ch;806} else {807return false;808}809}810}811812FontRenderContext gvFrc = g.getFontRenderContext();813GlyphVector gv2 = font.createGlyphVector(gvFrc, chars);814if (gv2.getNumGlyphs() != numGlyphs) {815return printGlyphVector(g, x, y);816}817int[] glyphCodes2 = gv2.getGlyphCodes(0, numGlyphs, null);818/*819* Needed to double-check remapping of X11 symbol & dingbats.820*/821for (int i=0; i<numGlyphs; i++) {822if (glyphCodes[i] != glyphCodes2[i]) {823return printGlyphVector(g, x, y);824}825}826827FontRenderContext g2dFrc = getFontRenderContext();828boolean compatibleFRC = gvFrc.equals(g2dFrc);829/* If differ only in specifying A-A or a translation, these are830* also compatible FRC's, and we can do one drawString call.831*/832if (!compatibleFRC &&833gvFrc.usesFractionalMetrics() == g2dFrc.usesFractionalMetrics()) {834AffineTransform gvAT = gvFrc.getTransform();835AffineTransform g2dAT = getTransform();836double[] gvMatrix = new double[4];837double[] g2dMatrix = new double[4];838gvAT.getMatrix(gvMatrix);839g2dAT.getMatrix(g2dMatrix);840compatibleFRC = true;841for (int i=0;i<4;i++) {842if (gvMatrix[i] != g2dMatrix[i]) {843compatibleFRC = false;844break;845}846}847}848849String str = new String(chars, 0, numGlyphs);850int numFonts = platformFontCount(font, str);851if (numFonts == 0) {852return false;853}854855float[] positions = g.getGlyphPositions(0, numGlyphs, null);856boolean noPositionAdjustments =857((flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) == 0) ||858samePositions(gv2, glyphCodes2, glyphCodes, positions);859860/* We have to consider that the application may be directly861* creating a GlyphVector, rather than one being created by862* TextLayout or indirectly from drawString. In such a case, if the863* font has layout attributes, the text may measure differently864* when we reconstitute it into a String and ask for the length that865* drawString would use. For example, KERNING will be applied in such866* a case but that Font attribute is not applied when the application867* directly created a GlyphVector. So in this case we need to verify868* that the text measures the same in both cases - ie that the869* layout attribute has no effect. If it does we can't always870* use the drawString call unless we can coerce the drawString call871* into measuring and displaying the string to the same length.872* That is the case where there is only one font used and we can873* specify the overall advance of the string. (See below).874*/875876Point2D gvAdvancePt = g.getGlyphPosition(numGlyphs);877float gvAdvanceX = (float)gvAdvancePt.getX();878boolean layoutAffectsAdvance = false;879if (font.hasLayoutAttributes() && printingGlyphVector &&880noPositionAdjustments) {881882/* If TRACKING is in use then the glyph vector will report883* position adjustments, then that ought to be sufficient to884* tell us we can't just ask native to do "drawString". But layout885* always sets the position adjustment flag, so we don't believe886* it and verify the positions are really different than887* createGlyphVector() (with no layout) would create. However888* inconsistently, TRACKING is applied when creating a GlyphVector,889* since it doesn't actually require "layout" (even though its890* considered a layout attribute), it just requires a fractional891* tweak to the[default]advances. So we need to specifically892* check for tracking until such time as we can trust893* the GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS bit.894*/895Map<TextAttribute, ?> map = font.getAttributes();896Object o = map.get(TextAttribute.TRACKING);897boolean tracking = o != null && (o instanceof Number) &&898(((Number)o).floatValue() != 0f);899900if (tracking) {901noPositionAdjustments = false;902} else {903Rectangle2D bounds = font.getStringBounds(str, gvFrc);904float strAdvanceX = (float)bounds.getWidth();905if (Math.abs(strAdvanceX - gvAdvanceX) > 0.00001) {906layoutAffectsAdvance = true;907}908}909}910911if (compatibleFRC && noPositionAdjustments && !layoutAffectsAdvance) {912drawString(str, x, y, font, gvFrc, 0f);913return true;914}915916/* If positions have not been explicitly assigned, we can917* ask the string to be drawn adjusted to this width.918* This call is supported only in the PS generator.919* GDI has API to specify the advance for each glyph in a920* string which could be used here too, but that is not yet921* implemented, and we'd need to update the signature of the922* drawString method to take the advances (ie relative positions)923* and use that instead of the width.924*/925if (numFonts == 1 && canDrawStringToWidth() && noPositionAdjustments) {926drawString(str, x, y, font, gvFrc, gvAdvanceX);927return true;928}929930/* In some scripts chars drawn individually do not have the931* same representation (glyphs) as when combined with other chars.932* The logic here is erring on the side of caution, in particular933* in including supplementary characters.934*/935if (FontUtilities.isComplexText(chars, 0, chars.length)) {936return printGlyphVector(g, x, y);937}938939/* If we reach here we have mapped all the glyphs back940* one-to-one to simple unicode chars that we know are in the font.941* We can call "drawChars" on each one of them in turn, setting942* the position based on the glyph positions.943* There's typically overhead in this. If numGlyphs is 'large',944* it may even be better to try printGlyphVector() in this case.945* This may be less recoverable for apps, but sophisticated apps946* should be able to recover the text from simple glyph vectors947* and we can avoid penalising the more common case - although948* this is already a minority case.949*/950if (numGlyphs > 10 && printGlyphVector(g, x, y)) {951return true;952}953954for (int i=0; i<numGlyphs; i++) {955String s = new String(chars, i, 1);956drawString(s, x+positions[i*2], y+positions[i*2+1],957font, gvFrc, 0f);958}959return true;960}961962/* The same codes must be in the same positions for this to return true.963* This would look cleaner if it took the original GV as a parameter but964* we already have the codes and will need to get the positions array965* too in most cases anyway. So its cheaper to pass them in.966* This call wouldn't be necessary if layout didn't always set the967* FLAG_HAS_POSITION_ADJUSTMENTS even if the default advances are used968* and there was no re-ordering (this should be fixed some day).969*/970private boolean samePositions(GlyphVector gv, int[] gvcodes,971int[] origCodes, float[] origPositions) {972973int numGlyphs = gv.getNumGlyphs();974float[] gvpos = gv.getGlyphPositions(0, numGlyphs, null);975976/* this shouldn't happen here, but just in case */977if (numGlyphs != gvcodes.length || /* real paranoia here */978origCodes.length != gvcodes.length ||979origPositions.length != gvpos.length) {980return false;981}982983for (int i=0; i<numGlyphs; i++) {984if (gvcodes[i] != origCodes[i] || gvpos[i] != origPositions[i]) {985return false;986}987}988return true;989}990991protected boolean canDrawStringToWidth() {992return false;993}994995/* return an array which can map glyphs back to char codes.996* Glyphs which aren't mapped from a simple unicode code point997* will have no mapping in this array, and will be assumed to be998* because of some substitution that we can't handle.999*/1000private static char[] getGlyphToCharMapForFont(Font2D font2D) {1001/* NB Composites report the number of glyphs in slot 0.1002* So if a string uses a char from a later slot, or a fallback slot,1003* it will not be able to use this faster path.1004*/1005int numGlyphs = font2D.getNumGlyphs();1006int missingGlyph = font2D.getMissingGlyphCode();1007char[] glyphToCharMap = new char[numGlyphs];1008int glyph;10091010for (int i=0;i<numGlyphs; i++) {1011glyphToCharMap[i] = CharToGlyphMapper.INVISIBLE_GLYPH_ID;1012}10131014/* Consider refining the ranges to try to map by asking the font1015* what ranges it supports.1016* Since a glyph may be mapped by multiple code points, and this1017* code can't handle that, we always prefer the earlier code point.1018*/1019for (char c=0; c<0xFFFF; c++) {1020if (c >= CharToGlyphMapper.HI_SURROGATE_START &&1021c <= CharToGlyphMapper.LO_SURROGATE_END) {1022continue;1023}1024glyph = font2D.charToGlyph(c);1025if (glyph != missingGlyph &&1026glyph >= 0 && glyph < numGlyphs &&1027(glyphToCharMap[glyph] ==1028CharToGlyphMapper.INVISIBLE_GLYPH_ID)) {1029glyphToCharMap[glyph] = c;1030}1031}1032return glyphToCharMap;1033}10341035/**1036* Strokes the outline of a Shape using the settings of the current1037* graphics state. The rendering attributes applied include the1038* clip, transform, paint or color, composite and stroke attributes.1039* @param s The shape to be drawn.1040* @see #setStroke1041* @see #setPaint1042* @see java.awt.Graphics#setColor1043* @see #transform1044* @see #setTransform1045* @see #clip1046* @see #setClip1047* @see #setComposite1048*/1049public void draw(Shape s) {10501051fill(getStroke().createStrokedShape(s));1052}10531054/**1055* Fills the interior of a Shape using the settings of the current1056* graphics state. The rendering attributes applied include the1057* clip, transform, paint or color, and composite.1058* @see #setPaint1059* @see java.awt.Graphics#setColor1060* @see #transform1061* @see #setTransform1062* @see #setComposite1063* @see #clip1064* @see #setClip1065*/1066public void fill(Shape s) {1067Paint paint = getPaint();10681069try {1070fill(s, (Color) paint);10711072/* The PathGraphics class only supports filling with1073* solid colors and so we do not expect the cast of Paint1074* to Color to fail. If it does fail then something went1075* wrong, like the app draw a page with a solid color but1076* then redrew it with a Gradient.1077*/1078} catch (ClassCastException e) {1079throw new IllegalArgumentException("Expected a Color instance");1080}1081}10821083public void fill(Shape s, Color color) {1084AffineTransform deviceTransform = getTransform();10851086if (getClip() != null) {1087deviceClip(getClip().getPathIterator(deviceTransform));1088}1089deviceFill(s.getPathIterator(deviceTransform), color);1090}10911092/**1093* Fill the path defined by {@code pathIter}1094* with the specified color.1095* The path is provided in device coordinates.1096*/1097protected abstract void deviceFill(PathIterator pathIter, Color color);10981099/*1100* Set the clipping path to that defined by1101* the passed in {@code PathIterator}.1102*/1103protected abstract void deviceClip(PathIterator pathIter);11041105/*1106* Draw the outline of the rectangle without using path1107* if supported by platform.1108*/1109protected abstract void deviceFrameRect(int x, int y,1110int width, int height,1111Color color);11121113/*1114* Draw a line without using path if supported by platform.1115*/1116protected abstract void deviceDrawLine(int xBegin, int yBegin,1117int xEnd, int yEnd, Color color);11181119/*1120* Fill a rectangle using specified color.1121*/1122protected abstract void deviceFillRect(int x, int y,1123int width, int height, Color color);11241125/* Obtain a BI from known implementations of java.awt.Image1126*/1127protected BufferedImage getBufferedImage(Image img) {1128if (img instanceof BufferedImage) {1129// Otherwise we expect a BufferedImage to behave as a standard BI1130return (BufferedImage)img;1131} else if (img instanceof ToolkitImage) {1132// This can be null if the image isn't loaded yet.1133// This is fine as in that case our caller will return1134// as it will only draw a fully loaded image1135return ((ToolkitImage)img).getBufferedImage();1136} else if (img instanceof VolatileImage) {1137// VI needs to make a new BI: this is unavoidable but1138// I don't expect VI's to be "huge" in any case.1139return ((VolatileImage)img).getSnapshot();1140} else {1141// may be null or may be some non-standard Image which1142// shouldn't happen as Image is implemented by the platform1143// not by applications1144// If you add a new Image implementation to the platform you1145// will need to support it here similarly to VI.1146return null;1147}1148}11491150/**1151* Return true if the BufferedImage argument has non-opaque1152* bits in it and therefore can not be directly rendered by1153* GDI. Return false if the image is opaque. If this function1154* can not tell for sure whether the image has transparent1155* pixels then it assumes that it does.1156*/1157protected boolean hasTransparentPixels(BufferedImage bufferedImage) {1158ColorModel colorModel = bufferedImage.getColorModel();1159boolean hasTransparency = colorModel == null1160? true1161: colorModel.getTransparency() != ColorModel.OPAQUE;11621163/*1164* For the default INT ARGB check the image to see if any pixels are1165* really transparent. If there are no transparent pixels then the1166* transparency of the color model can be ignored.1167* We assume that IndexColorModel images have already been1168* checked for transparency and will be OPAQUE unless they actually1169* have transparent pixels present.1170*/1171if (hasTransparency && bufferedImage != null) {1172if (bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB ||1173bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB_PRE) {1174DataBuffer db = bufferedImage.getRaster().getDataBuffer();1175SampleModel sm = bufferedImage.getRaster().getSampleModel();1176if (db instanceof DataBufferInt &&1177sm instanceof SinglePixelPackedSampleModel) {1178SinglePixelPackedSampleModel psm =1179(SinglePixelPackedSampleModel)sm;1180// Stealing the data array for reading only...1181int[] int_data =1182SunWritableRaster.stealData((DataBufferInt) db, 0);1183int x = bufferedImage.getMinX();1184int y = bufferedImage.getMinY();1185int w = bufferedImage.getWidth();1186int h = bufferedImage.getHeight();1187int stride = psm.getScanlineStride();1188boolean hastranspixel = false;1189for (int j = y; j < y+h; j++) {1190int yoff = j * stride;1191for (int i = x; i < x+w; i++) {1192if ((int_data[yoff+i] & 0xff000000)!=0xff000000 ) {1193hastranspixel = true;1194break;1195}1196}1197if (hastranspixel) {1198break;1199}1200}1201if (hastranspixel == false) {1202hasTransparency = false;1203}1204}1205}1206}12071208return hasTransparency;1209}12101211protected boolean isBitmaskTransparency(BufferedImage bufferedImage) {1212ColorModel colorModel = bufferedImage.getColorModel();1213return (colorModel != null &&1214colorModel.getTransparency() == ColorModel.BITMASK);1215}121612171218/* An optimisation for the special case of ICM images which have1219* bitmask transparency.1220*/1221protected boolean drawBitmaskImage(BufferedImage bufferedImage,1222AffineTransform xform,1223Color bgcolor,1224int srcX, int srcY,1225int srcWidth, int srcHeight) {12261227ColorModel colorModel = bufferedImage.getColorModel();1228IndexColorModel icm;1229int [] pixels;12301231if (!(colorModel instanceof IndexColorModel)) {1232return false;1233} else {1234icm = (IndexColorModel)colorModel;1235}12361237if (colorModel.getTransparency() != ColorModel.BITMASK) {1238return false;1239}12401241// to be compatible with 1.1 printing which treated b/g colors1242// with alpha 128 as opaque1243if (bgcolor != null && bgcolor.getAlpha() < 128) {1244return false;1245}12461247if ((xform.getType()1248& ~( AffineTransform.TYPE_UNIFORM_SCALE1249| AffineTransform.TYPE_TRANSLATION1250| AffineTransform.TYPE_QUADRANT_ROTATION1251)) != 0) {1252return false;1253}12541255if ((getTransform().getType()1256& ~( AffineTransform.TYPE_UNIFORM_SCALE1257| AffineTransform.TYPE_TRANSLATION1258| AffineTransform.TYPE_QUADRANT_ROTATION1259)) != 0) {1260return false;1261}12621263BufferedImage subImage = null;1264Raster raster = bufferedImage.getRaster();1265int transpixel = icm.getTransparentPixel();1266byte[] alphas = new byte[icm.getMapSize()];1267icm.getAlphas(alphas);1268if (transpixel >= 0) {1269alphas[transpixel] = 0;1270}12711272/* don't just use srcWidth & srcHeight from application - they1273* may exceed the extent of the image - may need to clip.1274* The image xform will ensure that points are still mapped properly.1275*/1276int rw = raster.getWidth();1277int rh = raster.getHeight();1278if (srcX > rw || srcY > rh) {1279return false;1280}1281int right, bottom, wid, hgt;1282if (srcX+srcWidth > rw) {1283right = rw;1284wid = right - srcX;1285} else {1286right = srcX+srcWidth;1287wid = srcWidth;1288}1289if (srcY+srcHeight > rh) {1290bottom = rh;1291hgt = bottom - srcY;1292} else {1293bottom = srcY+srcHeight;1294hgt = srcHeight;1295}1296pixels = new int[wid];1297for (int j=srcY; j<bottom; j++) {1298int startx = -1;1299raster.getPixels(srcX, j, wid, 1, pixels);1300for (int i=srcX; i<right; i++) {1301if (alphas[pixels[i-srcX]] == 0) {1302if (startx >=0) {1303subImage = bufferedImage.getSubimage(startx, j,1304i-startx, 1);1305xform.translate(startx, j);1306drawImageToPlatform(subImage, xform, bgcolor,13070, 0, i-startx, 1, true);1308xform.translate(-startx, -j);1309startx = -1;1310}1311} else if (startx < 0) {1312startx = i;1313}1314}1315if (startx >= 0) {1316subImage = bufferedImage.getSubimage(startx, j,1317right - startx, 1);1318xform.translate(startx, j);1319drawImageToPlatform(subImage, xform, bgcolor,13200, 0, right - startx, 1, true);1321xform.translate(-startx, -j);1322}1323}1324return true;1325}1326132713281329/**1330* The various {@code drawImage()} methods for1331* {@code PathGraphics} are all decomposed1332* into an invocation of {@code drawImageToPlatform}.1333* The portion of the passed in image defined by1334* {@code srcX, srcY, srcWidth, and srcHeight}1335* is transformed by the supplied AffineTransform and1336* drawn using PS to the printer context.1337*1338* @param img The image to be drawn.1339* This method does nothing if {@code img} is null.1340* @param xform Used to transform the image before drawing.1341* This can be null.1342* @param bgcolor This color is drawn where the image has transparent1343* pixels. If this parameter is null then the1344* pixels already in the destination should show1345* through.1346* @param srcX With srcY this defines the upper-left corner1347* of the portion of the image to be drawn.1348*1349* @param srcY With srcX this defines the upper-left corner1350* of the portion of the image to be drawn.1351* @param srcWidth The width of the portion of the image to1352* be drawn.1353* @param srcHeight The height of the portion of the image to1354* be drawn.1355* @param handlingTransparency if being recursively called to1356* print opaque region of transparent image1357*/1358protected abstract boolean1359drawImageToPlatform(Image img, AffineTransform xform,1360Color bgcolor,1361int srcX, int srcY,1362int srcWidth, int srcHeight,1363boolean handlingTransparency);13641365/**1366* Draws as much of the specified image as is currently available.1367* The image is drawn with its top-left corner at1368* (<i>x</i>, <i>y</i>) in this graphics context's coordinate1369* space. Transparent pixels in the image do not affect whatever1370* pixels are already there.1371* <p>1372* This method returns immediately in all cases, even if the1373* complete image has not yet been loaded, and it has not been dithered1374* and converted for the current output device.1375* <p>1376* If the image has not yet been completely loaded, then1377* {@code drawImage} returns {@code false}. As more of1378* the image becomes available, the process that draws the image notifies1379* the specified image observer.1380* @param img the specified image to be drawn.1381* @param x the <i>x</i> coordinate.1382* @param y the <i>y</i> coordinate.1383* @param observer object to be notified as more of1384* the image is converted.1385* @see java.awt.Image1386* @see java.awt.image.ImageObserver1387* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1388* @since 1.01389*/1390public boolean drawImage(Image img, int x, int y,1391ImageObserver observer) {13921393return drawImage(img, x, y, null, observer);1394}13951396/**1397* Draws as much of the specified image as has already been scaled1398* to fit inside the specified rectangle.1399* <p>1400* The image is drawn inside the specified rectangle of this1401* graphics context's coordinate space, and is scaled if1402* necessary. Transparent pixels do not affect whatever pixels1403* are already there.1404* <p>1405* This method returns immediately in all cases, even if the1406* entire image has not yet been scaled, dithered, and converted1407* for the current output device.1408* If the current output representation is not yet complete, then1409* {@code drawImage} returns {@code false}. As more of1410* the image becomes available, the process that draws the image notifies1411* the image observer by calling its {@code imageUpdate} method.1412* <p>1413* A scaled version of an image will not necessarily be1414* available immediately just because an unscaled version of the1415* image has been constructed for this output device. Each size of1416* the image may be cached separately and generated from the original1417* data in a separate image production sequence.1418* @param img the specified image to be drawn.1419* @param x the <i>x</i> coordinate.1420* @param y the <i>y</i> coordinate.1421* @param width the width of the rectangle.1422* @param height the height of the rectangle.1423* @param observer object to be notified as more of1424* the image is converted.1425* @see java.awt.Image1426* @see java.awt.image.ImageObserver1427* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1428* @since 1.01429*/1430public boolean drawImage(Image img, int x, int y,1431int width, int height,1432ImageObserver observer) {14331434return drawImage(img, x, y, width, height, null, observer);14351436}14371438/*1439* Draws as much of the specified image as is currently available.1440* The image is drawn with its top-left corner at1441* (<i>x</i>, <i>y</i>) in this graphics context's coordinate1442* space. Transparent pixels are drawn in the specified1443* background color.1444* <p>1445* This operation is equivalent to filling a rectangle of the1446* width and height of the specified image with the given color and then1447* drawing the image on top of it, but possibly more efficient.1448* <p>1449* This method returns immediately in all cases, even if the1450* complete image has not yet been loaded, and it has not been dithered1451* and converted for the current output device.1452* <p>1453* If the image has not yet been completely loaded, then1454* {@code drawImage} returns {@code false}. As more of1455* the image becomes available, the process that draws the image notifies1456* the specified image observer.1457* @param img the specified image to be drawn.1458* This method does nothing if {@code img} is null.1459* @param x the <i>x</i> coordinate.1460* @param y the <i>y</i> coordinate.1461* @param bgcolor the background color to paint under the1462* non-opaque portions of the image.1463* In this WPathGraphics implementation,1464* this parameter can be null in which1465* case that background is made a transparent1466* white.1467* @param observer object to be notified as more of1468* the image is converted.1469* @see java.awt.Image1470* @see java.awt.image.ImageObserver1471* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1472* @since 1.01473*/1474public boolean drawImage(Image img, int x, int y,1475Color bgcolor,1476ImageObserver observer) {14771478if (img == null) {1479return true;1480}14811482boolean result;1483int srcWidth = img.getWidth(null);1484int srcHeight = img.getHeight(null);14851486if (srcWidth < 0 || srcHeight < 0) {1487result = false;1488} else {1489result = drawImage(img, x, y, srcWidth, srcHeight, bgcolor, observer);1490}14911492return result;1493}14941495/**1496* Draws as much of the specified image as has already been scaled1497* to fit inside the specified rectangle.1498* <p>1499* The image is drawn inside the specified rectangle of this1500* graphics context's coordinate space, and is scaled if1501* necessary. Transparent pixels are drawn in the specified1502* background color.1503* This operation is equivalent to filling a rectangle of the1504* width and height of the specified image with the given color and then1505* drawing the image on top of it, but possibly more efficient.1506* <p>1507* This method returns immediately in all cases, even if the1508* entire image has not yet been scaled, dithered, and converted1509* for the current output device.1510* If the current output representation is not yet complete then1511* {@code drawImage} returns {@code false}. As more of1512* the image becomes available, the process that draws the image notifies1513* the specified image observer.1514* <p>1515* A scaled version of an image will not necessarily be1516* available immediately just because an unscaled version of the1517* image has been constructed for this output device. Each size of1518* the image may be cached separately and generated from the original1519* data in a separate image production sequence.1520* @param img the specified image to be drawn.1521* This method does nothing if {@code img} is null.1522* @param x the <i>x</i> coordinate.1523* @param y the <i>y</i> coordinate.1524* @param width the width of the rectangle.1525* @param height the height of the rectangle.1526* @param bgcolor the background color to paint under the1527* non-opaque portions of the image.1528* @param observer object to be notified as more of1529* the image is converted.1530* @see java.awt.Image1531* @see java.awt.image.ImageObserver1532* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1533* @since 1.01534*/1535public boolean drawImage(Image img, int x, int y,1536int width, int height,1537Color bgcolor,1538ImageObserver observer) {15391540if (img == null) {1541return true;1542}15431544boolean result;1545int srcWidth = img.getWidth(null);1546int srcHeight = img.getHeight(null);15471548if (srcWidth < 0 || srcHeight < 0) {1549result = false;1550} else {1551result = drawImage(img,1552x, y, x + width, y + height,15530, 0, srcWidth, srcHeight,1554observer);1555}15561557return result;1558}15591560/**1561* Draws as much of the specified area of the specified image as is1562* currently available, scaling it on the fly to fit inside the1563* specified area of the destination drawable surface. Transparent pixels1564* do not affect whatever pixels are already there.1565* <p>1566* This method returns immediately in all cases, even if the1567* image area to be drawn has not yet been scaled, dithered, and converted1568* for the current output device.1569* If the current output representation is not yet complete then1570* {@code drawImage} returns {@code false}. As more of1571* the image becomes available, the process that draws the image notifies1572* the specified image observer.1573* <p>1574* This method always uses the unscaled version of the image1575* to render the scaled rectangle and performs the required1576* scaling on the fly. It does not use a cached, scaled version1577* of the image for this operation. Scaling of the image from source1578* to destination is performed such that the first coordinate1579* of the source rectangle is mapped to the first coordinate of1580* the destination rectangle, and the second source coordinate is1581* mapped to the second destination coordinate. The subimage is1582* scaled and flipped as needed to preserve those mappings.1583* @param img the specified image to be drawn1584* @param dx1 the <i>x</i> coordinate of the first corner of the1585* destination rectangle.1586* @param dy1 the <i>y</i> coordinate of the first corner of the1587* destination rectangle.1588* @param dx2 the <i>x</i> coordinate of the second corner of the1589* destination rectangle.1590* @param dy2 the <i>y</i> coordinate of the second corner of the1591* destination rectangle.1592* @param sx1 the <i>x</i> coordinate of the first corner of the1593* source rectangle.1594* @param sy1 the <i>y</i> coordinate of the first corner of the1595* source rectangle.1596* @param sx2 the <i>x</i> coordinate of the second corner of the1597* source rectangle.1598* @param sy2 the <i>y</i> coordinate of the second corner of the1599* source rectangle.1600* @param observer object to be notified as more of the image is1601* scaled and converted.1602* @see java.awt.Image1603* @see java.awt.image.ImageObserver1604* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1605* @since 1.11606*/1607public boolean drawImage(Image img,1608int dx1, int dy1, int dx2, int dy2,1609int sx1, int sy1, int sx2, int sy2,1610ImageObserver observer) {16111612return drawImage(img,1613dx1, dy1, dx2, dy2,1614sx1, sy1, sx2, sy2,1615null, observer);1616}16171618/**1619* Draws as much of the specified area of the specified image as is1620* currently available, scaling it on the fly to fit inside the1621* specified area of the destination drawable surface.1622* <p>1623* Transparent pixels are drawn in the specified background color.1624* This operation is equivalent to filling a rectangle of the1625* width and height of the specified image with the given color and then1626* drawing the image on top of it, but possibly more efficient.1627* <p>1628* This method returns immediately in all cases, even if the1629* image area to be drawn has not yet been scaled, dithered, and converted1630* for the current output device.1631* If the current output representation is not yet complete then1632* {@code drawImage} returns {@code false}. As more of1633* the image becomes available, the process that draws the image notifies1634* the specified image observer.1635* <p>1636* This method always uses the unscaled version of the image1637* to render the scaled rectangle and performs the required1638* scaling on the fly. It does not use a cached, scaled version1639* of the image for this operation. Scaling of the image from source1640* to destination is performed such that the first coordinate1641* of the source rectangle is mapped to the first coordinate of1642* the destination rectangle, and the second source coordinate is1643* mapped to the second destination coordinate. The subimage is1644* scaled and flipped as needed to preserve those mappings.1645* @param img the specified image to be drawn1646* This method does nothing if {@code img} is null.1647* @param dx1 the <i>x</i> coordinate of the first corner of the1648* destination rectangle.1649* @param dy1 the <i>y</i> coordinate of the first corner of the1650* destination rectangle.1651* @param dx2 the <i>x</i> coordinate of the second corner of the1652* destination rectangle.1653* @param dy2 the <i>y</i> coordinate of the second corner of the1654* destination rectangle.1655* @param sx1 the <i>x</i> coordinate of the first corner of the1656* source rectangle.1657* @param sy1 the <i>y</i> coordinate of the first corner of the1658* source rectangle.1659* @param sx2 the <i>x</i> coordinate of the second corner of the1660* source rectangle.1661* @param sy2 the <i>y</i> coordinate of the second corner of the1662* source rectangle.1663* @param bgcolor the background color to paint under the1664* non-opaque portions of the image.1665* @param observer object to be notified as more of the image is1666* scaled and converted.1667* @see java.awt.Image1668* @see java.awt.image.ImageObserver1669* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1670* @since 1.11671*/1672public boolean drawImage(Image img,1673int dx1, int dy1, int dx2, int dy2,1674int sx1, int sy1, int sx2, int sy2,1675Color bgcolor,1676ImageObserver observer) {16771678if (img == null) {1679return true;1680}1681int imgWidth = img.getWidth(null);1682int imgHeight = img.getHeight(null);16831684if (imgWidth < 0 || imgHeight < 0) {1685return true;1686}16871688int srcWidth = sx2 - sx1;1689int srcHeight = sy2 - sy1;16901691/* Create a transform which describes the changes1692* from the source coordinates to the destination1693* coordinates. The scaling is determined by the1694* ratio of the two rectangles, while the translation1695* comes from the difference of their origins.1696*/1697float scalex = (float) (dx2 - dx1) / srcWidth;1698float scaley = (float) (dy2 - dy1) / srcHeight;1699AffineTransform xForm1700= new AffineTransform(scalex,17010,17020,1703scaley,1704dx1 - (sx1 * scalex),1705dy1 - (sy1 * scaley));17061707/* drawImageToPlatform needs the top-left of the source area and1708* a positive width and height. The xform describes how to map1709* src->dest, so that information is not lost.1710*/1711int tmp=0;1712if (sx2 < sx1) {1713tmp = sx1;1714sx1 = sx2;1715sx2 = tmp;1716}1717if (sy2 < sy1) {1718tmp = sy1;1719sy1 = sy2;1720sy2 = tmp;1721}17221723/* if src area is beyond the bounds of the image, we must clip it.1724* The transform is based on the specified area, not the clipped one.1725*/1726if (sx1 < 0) {1727sx1 = 0;1728} else if (sx1 > imgWidth) { // empty srcArea, nothing to draw1729sx1 = imgWidth;1730}1731if (sx2 < 0) { // empty srcArea, nothing to draw1732sx2 = 0;1733} else if (sx2 > imgWidth) {1734sx2 = imgWidth;1735}1736if (sy1 < 0) {1737sy1 = 0;1738} else if (sy1 > imgHeight) { // empty srcArea1739sy1 = imgHeight;1740}1741if (sy2 < 0) { // empty srcArea1742sy2 = 0;1743} else if (sy2 > imgHeight) {1744sy2 = imgHeight;1745}17461747srcWidth = sx2 - sx1;1748srcHeight = sy2 - sy1;17491750if (srcWidth <= 0 || srcHeight <= 0) {1751return true;1752}17531754return drawImageToPlatform(img, xForm, bgcolor,1755sx1, sy1, srcWidth, srcHeight, false);175617571758}17591760/**1761* Draws an image, applying a transform from image space into user space1762* before drawing.1763* The transformation from user space into device space is done with1764* the current transform in the Graphics2D.1765* The given transformation is applied to the image before the1766* transform attribute in the Graphics2D state is applied.1767* The rendering attributes applied include the clip, transform,1768* and composite attributes. Note that the result is1769* undefined, if the given transform is noninvertible.1770* @param img The image to be drawn.1771* This method does nothing if {@code img} is null.1772* @param xform The transformation from image space into user space.1773* @param obs The image observer to be notified as more of the image1774* is converted.1775* @see #transform1776* @see #setTransform1777* @see #setComposite1778* @see #clip1779* @see #setClip1780*/1781public boolean drawImage(Image img,1782AffineTransform xform,1783ImageObserver obs) {17841785if (img == null) {1786return true;1787}17881789boolean result;1790int srcWidth = img.getWidth(null);1791int srcHeight = img.getHeight(null);17921793if (srcWidth < 0 || srcHeight < 0) {1794result = false;1795} else {1796result = drawImageToPlatform(img, xform, null,17970, 0, srcWidth, srcHeight, false);1798}17991800return result;1801}18021803/**1804* Draws a BufferedImage that is filtered with a BufferedImageOp.1805* The rendering attributes applied include the clip, transform1806* and composite attributes. This is equivalent to:1807* <pre>1808* img1 = op.filter(img, null);1809* drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null);1810* </pre>1811* @param op The filter to be applied to the image before drawing.1812* @param img The BufferedImage to be drawn.1813* This method does nothing if {@code img} is null.1814* @param x,y The location in user space where the image should be drawn.1815* @see #transform1816* @see #setTransform1817* @see #setComposite1818* @see #clip1819* @see #setClip1820*/1821public void drawImage(BufferedImage img,1822BufferedImageOp op,1823int x,1824int y) {18251826if (img == null) {1827return;1828}18291830int srcWidth = img.getWidth(null);1831int srcHeight = img.getHeight(null);18321833if (op != null) {1834img = op.filter(img, null);1835}1836if (srcWidth <= 0 || srcHeight <= 0) {1837return;1838} else {1839AffineTransform xform = new AffineTransform(1f,0f,0f,1f,x,y);1840drawImageToPlatform(img, xform, null,18410, 0, srcWidth, srcHeight, false);1842}18431844}18451846/**1847* Draws an image, applying a transform from image space into user space1848* before drawing.1849* The transformation from user space into device space is done with1850* the current transform in the Graphics2D.1851* The given transformation is applied to the image before the1852* transform attribute in the Graphics2D state is applied.1853* The rendering attributes applied include the clip, transform,1854* and composite attributes. Note that the result is1855* undefined, if the given transform is noninvertible.1856* @param img The image to be drawn.1857* This method does nothing if {@code img} is null.1858* @param xform The transformation from image space into user space.1859* @see #transform1860* @see #setTransform1861* @see #setComposite1862* @see #clip1863* @see #setClip1864*/1865public void drawRenderedImage(RenderedImage img,1866AffineTransform xform) {18671868if (img == null) {1869return;1870}18711872BufferedImage bufferedImage = null;1873int srcWidth = img.getWidth();1874int srcHeight = img.getHeight();18751876if (srcWidth <= 0 || srcHeight <= 0) {1877return;1878}18791880if (img instanceof BufferedImage) {1881bufferedImage = (BufferedImage) img;1882} else {1883bufferedImage = new BufferedImage(srcWidth, srcHeight,1884BufferedImage.TYPE_INT_ARGB);1885Graphics2D imageGraphics = bufferedImage.createGraphics();1886imageGraphics.drawRenderedImage(img, xform);1887}18881889drawImageToPlatform(bufferedImage, xform, null,18900, 0, srcWidth, srcHeight, false);18911892}18931894protected boolean isCompositing(Composite composite) {18951896boolean isCompositing = false;18971898if (composite instanceof AlphaComposite) {1899AlphaComposite alphaComposite = (AlphaComposite) composite;1900float alpha = alphaComposite.getAlpha();1901int rule = alphaComposite.getRule();19021903if (alpha != 1.01904|| (rule != AlphaComposite.SRC1905&& rule != AlphaComposite.SRC_OVER))1906{1907isCompositing = true;1908}19091910} else {1911isCompositing = true;1912}1913return isCompositing;1914}1915}191619171918