Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/print/PathGraphics.java
38829 views
/*1* Copyright (c) 1998, 2013, 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.Color;38import java.awt.Font;39import java.awt.Graphics2D;40import java.awt.Image;41import java.awt.Paint;42import java.awt.Polygon;43import java.awt.Shape;4445import java.text.AttributedCharacterIterator;4647import java.awt.font.FontRenderContext;48import java.awt.font.GlyphVector;49import java.awt.font.TextAttribute;50import java.awt.font.TextLayout;5152import java.awt.geom.AffineTransform;53import java.awt.geom.Arc2D;54import java.awt.geom.Ellipse2D;55import java.awt.geom.Line2D;56import java.awt.geom.Point2D;57import java.awt.geom.Rectangle2D;58import java.awt.geom.RoundRectangle2D;59import java.awt.geom.PathIterator;6061import java.awt.image.BufferedImage;62import java.awt.image.BufferedImageOp;63import java.awt.image.ColorModel;64import java.awt.image.DataBuffer;65import java.awt.image.DataBufferInt;66import java.awt.image.ImageObserver;67import java.awt.image.IndexColorModel;68import java.awt.image.Raster;69import java.awt.image.RenderedImage;70import java.awt.image.SampleModel;71import java.awt.image.SinglePixelPackedSampleModel;72import java.awt.image.VolatileImage;73import sun.awt.image.ByteComponentRaster;74import sun.awt.image.ToolkitImage;75import sun.awt.image.SunWritableRaster;7677import java.awt.print.PageFormat;78import java.awt.print.Printable;79import java.awt.print.PrinterException;80import java.awt.print.PrinterGraphics;81import java.awt.print.PrinterJob;8283import java.util.Map;8485public abstract class PathGraphics extends ProxyGraphics2D {8687private Printable mPainter;88private PageFormat mPageFormat;89private int mPageIndex;90private boolean mCanRedraw;91protected boolean printingGlyphVector;9293protected PathGraphics(Graphics2D graphics, PrinterJob printerJob,94Printable painter, PageFormat pageFormat,95int pageIndex, boolean canRedraw) {96super(graphics, printerJob);9798mPainter = painter;99mPageFormat = pageFormat;100mPageIndex = pageIndex;101mCanRedraw = canRedraw;102}103104/**105* Return the Printable instance responsible for drawing106* into this Graphics.107*/108protected Printable getPrintable() {109return mPainter;110}111112/**113* Return the PageFormat associated with this page of114* Graphics.115*/116protected PageFormat getPageFormat() {117return mPageFormat;118}119120/**121* Return the page index associated with this Graphics.122*/123protected int getPageIndex() {124return mPageIndex;125}126127/**128* Return true if we are allowed to ask the application129* to redraw portions of the page. In general, with the130* PrinterJob API, the application can be asked to do a131* redraw. When PrinterJob is emulating PrintJob then we132* can not.133*/134public boolean canDoRedraws() {135return mCanRedraw;136}137138/**139* Redraw a rectanglular area using a proxy graphics140*/141public abstract void redrawRegion(Rectangle2D region,142double scaleX, double scaleY,143Shape clip,144AffineTransform devTransform)145146throws PrinterException ;147148/**149* Draws a line, using the current color, between the points150* <code>(x1, y1)</code> and <code>(x2, y2)</code>151* in this graphics context's coordinate system.152* @param x1 the first point's <i>x</i> coordinate.153* @param y1 the first point's <i>y</i> coordinate.154* @param x2 the second point's <i>x</i> coordinate.155* @param y2 the second point's <i>y</i> coordinate.156*/157public void drawLine(int x1, int y1, int x2, int y2) {158159Paint paint = getPaint();160161try {162AffineTransform deviceTransform = getTransform();163if (getClip() != null) {164deviceClip(getClip().getPathIterator(deviceTransform));165}166167deviceDrawLine(x1, y1, x2, y2, (Color) paint);168169} catch (ClassCastException e) {170throw new IllegalArgumentException("Expected a Color instance");171}172}173174175/**176* Draws the outline of the specified rectangle.177* The left and right edges of the rectangle are at178* <code>x</code> and <code>x + width</code>.179* The top and bottom edges are at180* <code>y</code> and <code>y + height</code>.181* The rectangle is drawn using the graphics context's current color.182* @param x the <i>x</i> coordinate183* of the rectangle to be drawn.184* @param y the <i>y</i> coordinate185* of the rectangle to be drawn.186* @param width the width of the rectangle to be drawn.187* @param height the height of the rectangle to be drawn.188* @see java.awt.Graphics#fillRect189* @see java.awt.Graphics#clearRect190*/191public void drawRect(int x, int y, int width, int height) {192193Paint paint = getPaint();194195try {196AffineTransform deviceTransform = getTransform();197if (getClip() != null) {198deviceClip(getClip().getPathIterator(deviceTransform));199}200201deviceFrameRect(x, y, width, height, (Color) paint);202203} catch (ClassCastException e) {204throw new IllegalArgumentException("Expected a Color instance");205}206207}208209/**210* Fills the specified rectangle.211* The left and right edges of the rectangle are at212* <code>x</code> and <code>x + width - 1</code>.213* The top and bottom edges are at214* <code>y</code> and <code>y + height - 1</code>.215* The resulting rectangle covers an area216* <code>width</code> pixels wide by217* <code>height</code> pixels tall.218* The rectangle is filled using the graphics context's current color.219* @param x the <i>x</i> coordinate220* of the rectangle to be filled.221* @param y the <i>y</i> coordinate222* of the rectangle to be filled.223* @param width the width of the rectangle to be filled.224* @param height the height of the rectangle to be filled.225* @see java.awt.Graphics#clearRect226* @see java.awt.Graphics#drawRect227*/228public void fillRect(int x, int y, int width, int height){229230Paint paint = getPaint();231232try {233AffineTransform deviceTransform = getTransform();234if (getClip() != null) {235deviceClip(getClip().getPathIterator(deviceTransform));236}237238deviceFillRect(x, y, width, height, (Color) paint);239240} catch (ClassCastException e) {241throw new IllegalArgumentException("Expected a Color instance");242}243}244245/**246* Clears the specified rectangle by filling it with the background247* color of the current drawing surface. This operation does not248* use the current paint mode.249* <p>250* Beginning with Java 1.1, the background color251* of offscreen images may be system dependent. Applications should252* use <code>setColor</code> followed by <code>fillRect</code> to253* ensure that an offscreen image is cleared to a specific color.254* @param x the <i>x</i> coordinate of the rectangle to clear.255* @param y the <i>y</i> coordinate of the rectangle to clear.256* @param width the width of the rectangle to clear.257* @param height the height of the rectangle to clear.258* @see java.awt.Graphics#fillRect(int, int, int, int)259* @see java.awt.Graphics#drawRect260* @see java.awt.Graphics#setColor(java.awt.Color)261* @see java.awt.Graphics#setPaintMode262* @see java.awt.Graphics#setXORMode(java.awt.Color)263*/264public void clearRect(int x, int y, int width, int height) {265266fill(new Rectangle2D.Float(x, y, width, height), getBackground());267}268269/**270* Draws an outlined round-cornered rectangle using this graphics271* context's current color. The left and right edges of the rectangle272* are at <code>x</code> and <code>x + width</code>,273* respectively. The top and bottom edges of the rectangle are at274* <code>y</code> and <code>y + height</code>.275* @param x the <i>x</i> coordinate of the rectangle to be drawn.276* @param y the <i>y</i> coordinate of the rectangle to be drawn.277* @param width the width of the rectangle to be drawn.278* @param height the height of the rectangle to be drawn.279* @param arcWidth the horizontal diameter of the arc280* at the four corners.281* @param arcHeight the vertical diameter of the arc282* at the four corners.283* @see java.awt.Graphics#fillRoundRect284*/285public void drawRoundRect(int x, int y, int width, int height,286int arcWidth, int arcHeight) {287288draw(new RoundRectangle2D.Float(x, y,289width, height,290arcWidth, arcHeight));291}292293294/**295* Fills the specified rounded corner rectangle with the current color.296* The left and right edges of the rectangle297* are at <code>x</code> and <code>x + width - 1</code>,298* respectively. The top and bottom edges of the rectangle are at299* <code>y</code> and <code>y + height - 1</code>.300* @param x the <i>x</i> coordinate of the rectangle to be filled.301* @param y the <i>y</i> coordinate of the rectangle to be filled.302* @param width the width of the rectangle to be filled.303* @param height the height of the rectangle to be filled.304* @param arcWidth the horizontal diameter305* of the arc at the four corners.306* @param arcHeight the vertical diameter307* of the arc at the four corners.308* @see java.awt.Graphics#drawRoundRect309*/310public void fillRoundRect(int x, int y, int width, int height,311int arcWidth, int arcHeight) {312313fill(new RoundRectangle2D.Float(x, y,314width, height,315arcWidth, arcHeight));316}317318/**319* Draws the outline of an oval.320* The result is a circle or ellipse that fits within the321* rectangle specified by the <code>x</code>, <code>y</code>,322* <code>width</code>, and <code>height</code> arguments.323* <p>324* The oval covers an area that is325* <code>width + 1</code> pixels wide326* and <code>height + 1</code> pixels tall.327* @param x the <i>x</i> coordinate of the upper left328* corner of the oval to be drawn.329* @param y the <i>y</i> coordinate of the upper left330* corner of the oval to be drawn.331* @param width the width of the oval to be drawn.332* @param height the height of the oval to be drawn.333* @see java.awt.Graphics#fillOval334* @since JDK1.0335*/336public void drawOval(int x, int y, int width, int height) {337draw(new Ellipse2D.Float(x, y, width, height));338}339340/**341* Fills an oval bounded by the specified rectangle with the342* current color.343* @param x the <i>x</i> coordinate of the upper left corner344* of the oval to be filled.345* @param y the <i>y</i> coordinate of the upper left corner346* of the oval to be filled.347* @param width the width of the oval to be filled.348* @param height the height of the oval to be filled.349* @see java.awt.Graphics#drawOval350*/351public void fillOval(int x, int y, int width, int height){352353fill(new Ellipse2D.Float(x, y, width, height));354}355356/**357* Draws the outline of a circular or elliptical arc358* covering the specified rectangle.359* <p>360* The resulting arc begins at <code>startAngle</code> and extends361* for <code>arcAngle</code> degrees, using the current color.362* Angles are interpreted such that 0 degrees363* is at the 3 o'clock position.364* A positive value indicates a counter-clockwise rotation365* while a negative value indicates a clockwise rotation.366* <p>367* The center of the arc is the center of the rectangle whose origin368* is (<i>x</i>, <i>y</i>) and whose size is specified by the369* <code>width</code> and <code>height</code> arguments.370* <p>371* The resulting arc covers an area372* <code>width + 1</code> pixels wide373* by <code>height + 1</code> pixels tall.374* <p>375* The angles are specified relative to the non-square extents of376* the bounding rectangle such that 45 degrees always falls on the377* line from the center of the ellipse to the upper right corner of378* the bounding rectangle. As a result, if the bounding rectangle is379* noticeably longer in one axis than the other, the angles to the380* start and end of the arc segment will be skewed farther along the381* longer axis of the bounds.382* @param x the <i>x</i> coordinate of the383* upper-left corner of the arc to be drawn.384* @param y the <i>y</i> coordinate of the385* upper-left corner of the arc to be drawn.386* @param width the width of the arc to be drawn.387* @param height the height of the arc to be drawn.388* @param startAngle the beginning angle.389* @param arcAngle the angular extent of the arc,390* relative to the start angle.391* @see java.awt.Graphics#fillArc392*/393public void drawArc(int x, int y, int width, int height,394int startAngle, int arcAngle) {395draw(new Arc2D.Float(x, y, width, height,396startAngle, arcAngle,397Arc2D.OPEN));398}399400401/**402* Fills a circular or elliptical arc covering the specified rectangle.403* <p>404* The resulting arc begins at <code>startAngle</code> and extends405* for <code>arcAngle</code> degrees.406* Angles are interpreted such that 0 degrees407* is at the 3 o'clock position.408* A positive value indicates a counter-clockwise rotation409* while a negative value indicates a clockwise rotation.410* <p>411* The center of the arc is the center of the rectangle whose origin412* is (<i>x</i>, <i>y</i>) and whose size is specified by the413* <code>width</code> and <code>height</code> arguments.414* <p>415* The resulting arc covers an area416* <code>width + 1</code> pixels wide417* by <code>height + 1</code> pixels tall.418* <p>419* The angles are specified relative to the non-square extents of420* the bounding rectangle such that 45 degrees always falls on the421* line from the center of the ellipse to the upper right corner of422* the bounding rectangle. As a result, if the bounding rectangle is423* noticeably longer in one axis than the other, the angles to the424* start and end of the arc segment will be skewed farther along the425* longer axis of the bounds.426* @param x the <i>x</i> coordinate of the427* upper-left corner of the arc to be filled.428* @param y the <i>y</i> coordinate of the429* upper-left corner of the arc to be filled.430* @param width the width of the arc to be filled.431* @param height the height of the arc to be filled.432* @param startAngle the beginning angle.433* @param arcAngle the angular extent of the arc,434* relative to the start angle.435* @see java.awt.Graphics#drawArc436*/437public void fillArc(int x, int y, int width, int height,438int startAngle, int arcAngle) {439440fill(new Arc2D.Float(x, y, width, height,441startAngle, arcAngle,442Arc2D.PIE));443}444445/**446* Draws a sequence of connected lines defined by447* arrays of <i>x</i> and <i>y</i> coordinates.448* Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point.449* The figure is not closed if the first point450* differs from the last point.451* @param xPoints an array of <i>x</i> points452* @param yPoints an array of <i>y</i> points453* @param nPoints the total number of points454* @see java.awt.Graphics#drawPolygon(int[], int[], int)455* @since JDK1.1456*/457public void drawPolyline(int xPoints[], int yPoints[],458int nPoints) {459float fromX;460float fromY;461float toX;462float toY;463464if (nPoints > 0) {465fromX = xPoints[0];466fromY = yPoints[0];467for(int i = 1; i < nPoints; i++) {468toX = xPoints[i];469toY = yPoints[i];470draw(new Line2D.Float(fromX, fromY, toX, toY));471fromX = toX;472fromY = toY;473}474}475476}477478479/**480* Draws a closed polygon defined by481* arrays of <i>x</i> and <i>y</i> coordinates.482* Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point.483* <p>484* This method draws the polygon defined by <code>nPoint</code> line485* segments, where the first <code>nPoint - 1</code>486* line segments are line segments from487* <code>(xPoints[i - 1], yPoints[i - 1])</code>488* to <code>(xPoints[i], yPoints[i])</code>, for489* 1 ≤ <i>i</i> ≤ <code>nPoints</code>.490* The figure is automatically closed by drawing a line connecting491* the final point to the first point, if those points are different.492* @param xPoints a an array of <code>x</code> coordinates.493* @param yPoints a an array of <code>y</code> coordinates.494* @param nPoints a the total number of points.495* @see java.awt.Graphics#fillPolygon496* @see java.awt.Graphics#drawPolyline497*/498public void drawPolygon(int xPoints[], int yPoints[],499int nPoints) {500501draw(new Polygon(xPoints, yPoints, nPoints));502}503504/**505* Draws the outline of a polygon defined by the specified506* <code>Polygon</code> object.507* @param p the polygon to draw.508* @see java.awt.Graphics#fillPolygon509* @see java.awt.Graphics#drawPolyline510*/511public void drawPolygon(Polygon p) {512draw(p);513}514515/**516* Fills a closed polygon defined by517* arrays of <i>x</i> and <i>y</i> coordinates.518* <p>519* This method draws the polygon defined by <code>nPoint</code> line520* segments, where the first <code>nPoint - 1</code>521* line segments are line segments from522* <code>(xPoints[i - 1], yPoints[i - 1])</code>523* to <code>(xPoints[i], yPoints[i])</code>, for524* 1 ≤ <i>i</i> ≤ <code>nPoints</code>.525* The figure is automatically closed by drawing a line connecting526* the final point to the first point, if those points are different.527* <p>528* The area inside the polygon is defined using an529* even-odd fill rule, also known as the alternating rule.530* @param xPoints a an array of <code>x</code> coordinates.531* @param yPoints a an array of <code>y</code> coordinates.532* @param nPoints a the total number of points.533* @see java.awt.Graphics#drawPolygon(int[], int[], int)534*/535public void fillPolygon(int xPoints[], int yPoints[],536int nPoints) {537538fill(new Polygon(xPoints, yPoints, nPoints));539}540541542/**543* Fills the polygon defined by the specified Polygon object with544* the graphics context's current color.545* <p>546* The area inside the polygon is defined using an547* even-odd fill rule, also known as the alternating rule.548* @param p the polygon to fill.549* @see java.awt.Graphics#drawPolygon(int[], int[], int)550*/551public void fillPolygon(Polygon p) {552553fill(p);554}555556/**557* Draws the text given by the specified string, using this558* graphics context's current font and color. The baseline of the559* first character is at position (<i>x</i>, <i>y</i>) in this560* graphics context's coordinate system.561* @param str the string to be drawn.562* @param x the <i>x</i> coordinate.563* @param y the <i>y</i> coordinate.564* @see java.awt.Graphics#drawBytes565* @see java.awt.Graphics#drawChars566* @since JDK1.0567*/568public void drawString(String str, int x, int y) {569drawString(str, (float) x, (float) y);570}571572public void drawString(String str, float x, float y) {573if (str.length() == 0) {574return;575}576TextLayout layout =577new TextLayout(str, getFont(), getFontRenderContext());578layout.draw(this, x, y);579}580581protected void drawString(String str, float x, float y,582Font font, FontRenderContext frc, float w) {583TextLayout layout =584new TextLayout(str, font, frc);585Shape textShape =586layout.getOutline(AffineTransform.getTranslateInstance(x, y));587fill(textShape);588}589590/**591* Draws the text given by the specified iterator, using this592* graphics context's current color. The iterator has to specify a font593* for each character. The baseline of the594* first character is at position (<i>x</i>, <i>y</i>) in this595* graphics context's coordinate system.596* @param iterator the iterator whose text is to be drawn597* @param x the <i>x</i> coordinate.598* @param y the <i>y</i> coordinate.599* @see java.awt.Graphics#drawBytes600* @see java.awt.Graphics#drawChars601*/602public void drawString(AttributedCharacterIterator iterator,603int x, int y) {604drawString(iterator, (float) x, (float) y);605}606public void drawString(AttributedCharacterIterator iterator,607float x, float y) {608if (iterator == null) {609throw610new NullPointerException("attributedcharacteriterator is null");611}612TextLayout layout =613new TextLayout(iterator, getFontRenderContext());614layout.draw(this, x, y);615}616617/**618* Draws a GlyphVector.619* The rendering attributes applied include the clip, transform,620* paint or color, and composite attributes. The GlyphVector specifies621* individual glyphs from a Font.622* @param g The GlyphVector to be drawn.623* @param x,y The coordinates where the glyphs should be drawn.624* @see #setPaint625* @see java.awt.Graphics#setColor626* @see #transform627* @see #setTransform628* @see #setComposite629* @see #clip630* @see #setClip631*/632public void drawGlyphVector(GlyphVector g,633float x,634float y) {635636/* We should not reach here if printingGlyphVector is already true.637* Add an assert so this can be tested if need be.638* But also ensure that we do at least render properly by filling639* the outline.640*/641if (printingGlyphVector) {642assert !printingGlyphVector; // ie false.643fill(g.getOutline(x, y));644return;645}646647try {648printingGlyphVector = true;649if (RasterPrinterJob.shapeTextProp ||650!printedSimpleGlyphVector(g, x, y)) {651fill(g.getOutline(x, y));652}653} finally {654printingGlyphVector = false;655}656}657658protected static SoftReference<Hashtable<Font2DHandle,Object>>659fontMapRef = new SoftReference<Hashtable<Font2DHandle,Object>>(null);660661protected int platformFontCount(Font font, String str) {662return 0;663}664665/**666* Default implementation returns false.667* Callers of this method must always be prepared for this,668* and delegate to outlines or some other solution.669*/670protected boolean printGlyphVector(GlyphVector gv, float x, float y) {671return false;672}673674/* GlyphVectors are usually encountered because TextLayout is in use.675* Some times TextLayout is needed to handle complex text or some676* rendering attributes trigger it.677* We try to print GlyphVectors by reconstituting into a String,678* as that is most recoverable for applications that export to formats679* such as Postscript or PDF. In some cases (eg where its not complex680* text and its just that positions aren't what we'd expect) we print681* one character at a time. positioning individually.682* Failing that, if we can directly send glyph codes to the printer683* then we do that (printGlyphVector).684* As a last resort we return false and let the caller print as filled685* shapes.686*/687boolean printedSimpleGlyphVector(GlyphVector g, float x, float y) {688689int flags = g.getLayoutFlags();690691/* We can't handle RTL, re-ordering, complex glyphs etc by692* reconstituting glyphs into a String. So if any flags besides693* position adjustments are set, see if we can directly694* print the GlyphVector as glyph codes, using the positions695* layout has assigned. If that fails return false;696*/697if (flags != 0 && flags != GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) {698return printGlyphVector(g, x, y);699}700701Font font = g.getFont();702Font2D font2D = FontUtilities.getFont2D(font);703if (font2D.handle.font2D != font2D) {704/* suspicious, may be a bad font. lets bail */705return false;706}707Hashtable<Font2DHandle,Object> fontMap;708synchronized (PathGraphics.class) {709fontMap = fontMapRef.get();710if (fontMap == null) {711fontMap = new Hashtable<Font2DHandle,Object>();712fontMapRef =713new SoftReference<Hashtable<Font2DHandle,Object>>(fontMap);714}715}716717int numGlyphs = g.getNumGlyphs();718int[] glyphCodes = g.getGlyphCodes(0, numGlyphs, null);719720char[] glyphToCharMap = null;721char[][] mapArray = null;722CompositeFont cf = null;723724/* Build the needed maps for this font in a synchronized block */725synchronized (fontMap) {726if (font2D instanceof CompositeFont) {727cf = (CompositeFont)font2D;728int numSlots = cf.getNumSlots();729mapArray = (char[][])fontMap.get(font2D.handle);730if (mapArray == null) {731mapArray = new char[numSlots][];732fontMap.put(font2D.handle, mapArray);733}734for (int i=0; i<numGlyphs;i++) {735int slot = glyphCodes[i] >>> 24;736if (slot >= numSlots) { /* shouldn't happen */737return false;738}739if (mapArray[slot] == null) {740Font2D slotFont = cf.getSlotFont(slot);741char[] map = (char[])fontMap.get(slotFont.handle);742if (map == null) {743map = getGlyphToCharMapForFont(slotFont);744}745mapArray[slot] = map;746}747}748} else {749glyphToCharMap = (char[])fontMap.get(font2D.handle);750if (glyphToCharMap == null) {751glyphToCharMap = getGlyphToCharMapForFont(font2D);752fontMap.put(font2D.handle, glyphToCharMap);753}754}755}756757char[] chars = new char[numGlyphs];758if (cf != null) {759for (int i=0; i<numGlyphs; i++) {760int gc = glyphCodes[i];761char[] map = mapArray[gc >>> 24];762gc = gc & 0xffffff;763if (map == null) {764return false;765}766/* X11 symbol & dingbats fonts used only for global metrics,767* so the glyph codes we have really refer to Lucida Sans768* Regular.769* So its possible the glyph code may appear out of range.770* Note that later on we double-check the glyph codes that771* we get from re-creating the GV from the string are the772* same as those we started with.773*774* If the glyphcode is INVISIBLE_GLYPH_ID then this may775* be \t, \n or \r which are mapped to that by layout.776* This is a case we can handle. It doesn't matter what777* character we use (we use \n) so long as layout maps it778* back to this in the verification, since the invisible779* glyph isn't visible :)780*/781char ch;782if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {783ch = '\n';784} else if (gc < 0 || gc >= map.length) {785return false;786} else {787ch = map[gc];788}789if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {790chars[i] = ch;791} else {792return false;793}794}795} else {796for (int i=0; i<numGlyphs; i++) {797int gc = glyphCodes[i];798char ch;799if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {800ch = '\n';801} else if (gc < 0 || gc >= glyphToCharMap.length) {802return false;803} else {804ch = glyphToCharMap[gc];805}806if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {807chars[i] = ch;808} else {809return false;810}811}812}813814FontRenderContext gvFrc = g.getFontRenderContext();815GlyphVector gv2 = font.createGlyphVector(gvFrc, chars);816if (gv2.getNumGlyphs() != numGlyphs) {817return printGlyphVector(g, x, y);818}819int[] glyphCodes2 = gv2.getGlyphCodes(0, numGlyphs, null);820/*821* Needed to double-check remapping of X11 symbol & dingbats.822*/823for (int i=0; i<numGlyphs; i++) {824if (glyphCodes[i] != glyphCodes2[i]) {825return printGlyphVector(g, x, y);826}827}828829FontRenderContext g2dFrc = getFontRenderContext();830boolean compatibleFRC = gvFrc.equals(g2dFrc);831/* If differ only in specifying A-A or a translation, these are832* also compatible FRC's, and we can do one drawString call.833*/834if (!compatibleFRC &&835gvFrc.usesFractionalMetrics() == g2dFrc.usesFractionalMetrics()) {836AffineTransform gvAT = gvFrc.getTransform();837AffineTransform g2dAT = getTransform();838double[] gvMatrix = new double[4];839double[] g2dMatrix = new double[4];840gvAT.getMatrix(gvMatrix);841g2dAT.getMatrix(g2dMatrix);842compatibleFRC = true;843for (int i=0;i<4;i++) {844if (gvMatrix[i] != g2dMatrix[i]) {845compatibleFRC = false;846break;847}848}849}850851String str = new String(chars, 0, numGlyphs);852int numFonts = platformFontCount(font, str);853if (numFonts == 0) {854return false;855}856857float[] positions = g.getGlyphPositions(0, numGlyphs, null);858boolean noPositionAdjustments =859((flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) == 0) ||860samePositions(gv2, glyphCodes2, glyphCodes, positions);861862/* We have to consider that the application may be directly863* creating a GlyphVector, rather than one being created by864* TextLayout or indirectly from drawString. In such a case, if the865* font has layout attributes, the text may measure differently866* when we reconstitute it into a String and ask for the length that867* drawString would use. For example, KERNING will be applied in such868* a case but that Font attribute is not applied when the application869* directly created a GlyphVector. So in this case we need to verify870* that the text measures the same in both cases - ie that the871* layout attribute has no effect. If it does we can't always872* use the drawString call unless we can coerce the drawString call873* into measuring and displaying the string to the same length.874* That is the case where there is only one font used and we can875* specify the overall advance of the string. (See below).876*/877878Point2D gvAdvancePt = g.getGlyphPosition(numGlyphs);879float gvAdvanceX = (float)gvAdvancePt.getX();880boolean layoutAffectsAdvance = false;881if (font.hasLayoutAttributes() && printingGlyphVector &&882noPositionAdjustments) {883884/* If TRACKING is in use then the glyph vector will report885* position adjustments, then that ought to be sufficient to886* tell us we can't just ask native to do "drawString". But layout887* always sets the position adjustment flag, so we don't believe888* it and verify the positions are really different than889* createGlyphVector() (with no layout) would create. However890* inconsistently, TRACKING is applied when creating a GlyphVector,891* since it doesn't actually require "layout" (even though its892* considered a layout attribute), it just requires a fractional893* tweak to the[default]advances. So we need to specifically894* check for tracking until such time as as we can trust895* the GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS bit.896*/897Map<TextAttribute, ?> map = font.getAttributes();898Object o = map.get(TextAttribute.TRACKING);899boolean tracking = o != null && (o instanceof Number) &&900(((Number)o).floatValue() != 0f);901902if (tracking) {903noPositionAdjustments = false;904} else {905Rectangle2D bounds = font.getStringBounds(str, gvFrc);906float strAdvanceX = (float)bounds.getWidth();907if (Math.abs(strAdvanceX - gvAdvanceX) > 0.00001) {908layoutAffectsAdvance = true;909}910}911}912913if (compatibleFRC && noPositionAdjustments && !layoutAffectsAdvance) {914drawString(str, x, y, font, gvFrc, 0f);915return true;916}917918/* If positions have not been explicitly assigned, we can919* ask the string to be drawn adjusted to this width.920* This call is supported only in the PS generator.921* GDI has API to specify the advance for each glyph in a922* string which could be used here too, but that is not yet923* implemented, and we'd need to update the signature of the924* drawString method to take the advances (ie relative positions)925* and use that instead of the width.926*/927if (numFonts == 1 && canDrawStringToWidth() && noPositionAdjustments) {928drawString(str, x, y, font, gvFrc, gvAdvanceX);929return true;930}931932/* In some scripts chars drawn individually do not have the933* same representation (glyphs) as when combined with other chars.934* The logic here is erring on the side of caution, in particular935* in including supplementary characters.936*/937if (FontUtilities.isComplexText(chars, 0, chars.length)) {938return printGlyphVector(g, x, y);939}940941/* If we reach here we have mapped all the glyphs back942* one-to-one to simple unicode chars that we know are in the font.943* We can call "drawChars" on each one of them in turn, setting944* the position based on the glyph positions.945* There's typically overhead in this. If numGlyphs is 'large',946* it may even be better to try printGlyphVector() in this case.947* This may be less recoverable for apps, but sophisticated apps948* should be able to recover the text from simple glyph vectors949* and we can avoid penalising the more common case - although950* this is already a minority case.951*/952if (numGlyphs > 10 && printGlyphVector(g, x, y)) {953return true;954}955956for (int i=0; i<numGlyphs; i++) {957String s = new String(chars, i, 1);958drawString(s, x+positions[i*2], y+positions[i*2+1],959font, gvFrc, 0f);960}961return true;962}963964/* The same codes must be in the same positions for this to return true.965* This would look cleaner if it took the original GV as a parameter but966* we already have the codes and will need to get the positions array967* too in most cases anyway. So its cheaper to pass them in.968* This call wouldn't be necessary if layout didn't always set the969* FLAG_HAS_POSITION_ADJUSTMENTS even if the default advances are used970* and there was no re-ordering (this should be fixed some day).971*/972private boolean samePositions(GlyphVector gv, int[] gvcodes,973int[] origCodes, float[] origPositions) {974975int numGlyphs = gv.getNumGlyphs();976float[] gvpos = gv.getGlyphPositions(0, numGlyphs, null);977978/* this shouldn't happen here, but just in case */979if (numGlyphs != gvcodes.length || /* real paranoia here */980origCodes.length != gvcodes.length ||981origPositions.length != gvpos.length) {982return false;983}984985for (int i=0; i<numGlyphs; i++) {986if (gvcodes[i] != origCodes[i] || gvpos[i] != origPositions[i]) {987return false;988}989}990return true;991}992993protected boolean canDrawStringToWidth() {994return false;995}996997/* return an array which can map glyphs back to char codes.998* Glyphs which aren't mapped from a simple unicode code point999* will have no mapping in this array, and will be assumed to be1000* because of some substitution that we can't handle.1001*/1002private static char[] getGlyphToCharMapForFont(Font2D font2D) {1003/* NB Composites report the number of glyphs in slot 0.1004* So if a string uses a char from a later slot, or a fallback slot,1005* it will not be able to use this faster path.1006*/1007int numGlyphs = font2D.getNumGlyphs();1008int missingGlyph = font2D.getMissingGlyphCode();1009char[] glyphToCharMap = new char[numGlyphs];1010int glyph;10111012for (int i=0;i<numGlyphs; i++) {1013glyphToCharMap[i] = CharToGlyphMapper.INVISIBLE_GLYPH_ID;1014}10151016/* Consider refining the ranges to try to map by asking the font1017* what ranges it supports.1018* Since a glyph may be mapped by multiple code points, and this1019* code can't handle that, we always prefer the earlier code point.1020*/1021for (char c=0; c<0xFFFF; c++) {1022if (c >= CharToGlyphMapper.HI_SURROGATE_START &&1023c <= CharToGlyphMapper.LO_SURROGATE_END) {1024continue;1025}1026glyph = font2D.charToGlyph(c);1027if (glyph != missingGlyph &&1028glyph >= 0 && glyph < numGlyphs &&1029(glyphToCharMap[glyph] ==1030CharToGlyphMapper.INVISIBLE_GLYPH_ID)) {1031glyphToCharMap[glyph] = c;1032}1033}1034return glyphToCharMap;1035}10361037/**1038* Strokes the outline of a Shape using the settings of the current1039* graphics state. The rendering attributes applied include the1040* clip, transform, paint or color, composite and stroke attributes.1041* @param s The shape to be drawn.1042* @see #setStroke1043* @see #setPaint1044* @see java.awt.Graphics#setColor1045* @see #transform1046* @see #setTransform1047* @see #clip1048* @see #setClip1049* @see #setComposite1050*/1051public void draw(Shape s) {10521053fill(getStroke().createStrokedShape(s));1054}10551056/**1057* Fills the interior of a Shape using the settings of the current1058* graphics state. The rendering attributes applied include the1059* clip, transform, paint or color, and composite.1060* @see #setPaint1061* @see java.awt.Graphics#setColor1062* @see #transform1063* @see #setTransform1064* @see #setComposite1065* @see #clip1066* @see #setClip1067*/1068public void fill(Shape s) {1069Paint paint = getPaint();10701071try {1072fill(s, (Color) paint);10731074/* The PathGraphics class only supports filling with1075* solid colors and so we do not expect the cast of Paint1076* to Color to fail. If it does fail then something went1077* wrong, like the app draw a page with a solid color but1078* then redrew it with a Gradient.1079*/1080} catch (ClassCastException e) {1081throw new IllegalArgumentException("Expected a Color instance");1082}1083}10841085public void fill(Shape s, Color color) {1086AffineTransform deviceTransform = getTransform();10871088if (getClip() != null) {1089deviceClip(getClip().getPathIterator(deviceTransform));1090}1091deviceFill(s.getPathIterator(deviceTransform), color);1092}10931094/**1095* Fill the path defined by <code>pathIter</code>1096* with the specified color.1097* The path is provided in device coordinates.1098*/1099protected abstract void deviceFill(PathIterator pathIter, Color color);11001101/*1102* Set the clipping path to that defined by1103* the passed in <code>PathIterator</code>.1104*/1105protected abstract void deviceClip(PathIterator pathIter);11061107/*1108* Draw the outline of the rectangle without using path1109* if supported by platform.1110*/1111protected abstract void deviceFrameRect(int x, int y,1112int width, int height,1113Color color);11141115/*1116* Draw a line without using path if supported by platform.1117*/1118protected abstract void deviceDrawLine(int xBegin, int yBegin,1119int xEnd, int yEnd, Color color);11201121/*1122* Fill a rectangle using specified color.1123*/1124protected abstract void deviceFillRect(int x, int y,1125int width, int height, Color color);11261127/* Obtain a BI from known implementations of java.awt.Image1128*/1129protected BufferedImage getBufferedImage(Image img) {1130if (img instanceof BufferedImage) {1131// Otherwise we expect a BufferedImage to behave as a standard BI1132return (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 return1136// as it will only draw a fully loaded image1137return ((ToolkitImage)img).getBufferedImage();1138} else if (img instanceof VolatileImage) {1139// VI needs to make a new BI: this is unavoidable but1140// I don't expect VI's to be "huge" in any case.1141return ((VolatileImage)img).getSnapshot();1142} else {1143// may be null or may be some non-standard Image which1144// shouldn't happen as Image is implemented by the platform1145// not by applications1146// If you add a new Image implementation to the platform you1147// will need to support it here similarly to VI.1148return null;1149}1150}11511152/**1153* Return true if the BufferedImage argument has non-opaque1154* bits in it and therefore can not be directly rendered by1155* GDI. Return false if the image is opaque. If this function1156* can not tell for sure whether the image has transparent1157* pixels then it assumes that it does.1158*/1159protected boolean hasTransparentPixels(BufferedImage bufferedImage) {1160ColorModel colorModel = bufferedImage.getColorModel();1161boolean hasTransparency = colorModel == null1162? true1163: colorModel.getTransparency() != ColorModel.OPAQUE;11641165/*1166* For the default INT ARGB check the image to see if any pixels are1167* really transparent. If there are no transparent pixels then the1168* transparency of the color model can be ignored.1169* We assume that IndexColorModel images have already been1170* checked for transparency and will be OPAQUE unless they actually1171* have transparent pixels present.1172*/1173if (hasTransparency && bufferedImage != null) {1174if (bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB ||1175bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB_PRE) {1176DataBuffer db = bufferedImage.getRaster().getDataBuffer();1177SampleModel sm = bufferedImage.getRaster().getSampleModel();1178if (db instanceof DataBufferInt &&1179sm instanceof SinglePixelPackedSampleModel) {1180SinglePixelPackedSampleModel psm =1181(SinglePixelPackedSampleModel)sm;1182// Stealing the data array for reading only...1183int[] int_data =1184SunWritableRaster.stealData((DataBufferInt) db, 0);1185int x = bufferedImage.getMinX();1186int y = bufferedImage.getMinY();1187int w = bufferedImage.getWidth();1188int h = bufferedImage.getHeight();1189int stride = psm.getScanlineStride();1190boolean hastranspixel = false;1191for (int j = y; j < y+h; j++) {1192int yoff = j * stride;1193for (int i = x; i < x+w; i++) {1194if ((int_data[yoff+i] & 0xff000000)!=0xff000000 ) {1195hastranspixel = true;1196break;1197}1198}1199if (hastranspixel) {1200break;1201}1202}1203if (hastranspixel == false) {1204hasTransparency = false;1205}1206}1207}1208}12091210return hasTransparency;1211}12121213protected boolean isBitmaskTransparency(BufferedImage bufferedImage) {1214ColorModel colorModel = bufferedImage.getColorModel();1215return (colorModel != null &&1216colorModel.getTransparency() == ColorModel.BITMASK);1217}121812191220/* An optimisation for the special case of ICM images which have1221* bitmask transparency.1222*/1223protected boolean drawBitmaskImage(BufferedImage bufferedImage,1224AffineTransform xform,1225Color bgcolor,1226int srcX, int srcY,1227int srcWidth, int srcHeight) {12281229ColorModel colorModel = bufferedImage.getColorModel();1230IndexColorModel icm;1231int [] pixels;12321233if (!(colorModel instanceof IndexColorModel)) {1234return false;1235} else {1236icm = (IndexColorModel)colorModel;1237}12381239if (colorModel.getTransparency() != ColorModel.BITMASK) {1240return false;1241}12421243// to be compatible with 1.1 printing which treated b/g colors1244// with alpha 128 as opaque1245if (bgcolor != null && bgcolor.getAlpha() < 128) {1246return false;1247}12481249if ((xform.getType()1250& ~( AffineTransform.TYPE_UNIFORM_SCALE1251| AffineTransform.TYPE_TRANSLATION1252| AffineTransform.TYPE_QUADRANT_ROTATION1253)) != 0) {1254return false;1255}12561257if ((getTransform().getType()1258& ~( AffineTransform.TYPE_UNIFORM_SCALE1259| AffineTransform.TYPE_TRANSLATION1260| AffineTransform.TYPE_QUADRANT_ROTATION1261)) != 0) {1262return false;1263}12641265BufferedImage subImage = null;1266Raster raster = bufferedImage.getRaster();1267int transpixel = icm.getTransparentPixel();1268byte[] alphas = new byte[icm.getMapSize()];1269icm.getAlphas(alphas);1270if (transpixel >= 0) {1271alphas[transpixel] = 0;1272}12731274/* don't just use srcWidth & srcHeight from application - they1275* may exceed the extent of the image - may need to clip.1276* The image xform will ensure that points are still mapped properly.1277*/1278int rw = raster.getWidth();1279int rh = raster.getHeight();1280if (srcX > rw || srcY > rh) {1281return false;1282}1283int right, bottom, wid, hgt;1284if (srcX+srcWidth > rw) {1285right = rw;1286wid = right - srcX;1287} else {1288right = srcX+srcWidth;1289wid = srcWidth;1290}1291if (srcY+srcHeight > rh) {1292bottom = rh;1293hgt = bottom - srcY;1294} else {1295bottom = srcY+srcHeight;1296hgt = srcHeight;1297}1298pixels = new int[wid];1299for (int j=srcY; j<bottom; j++) {1300int startx = -1;1301raster.getPixels(srcX, j, wid, 1, pixels);1302for (int i=srcX; i<right; i++) {1303if (alphas[pixels[i-srcX]] == 0) {1304if (startx >=0) {1305subImage = bufferedImage.getSubimage(startx, j,1306i-startx, 1);1307xform.translate(startx, j);1308drawImageToPlatform(subImage, xform, bgcolor,13090, 0, i-startx, 1, true);1310xform.translate(-startx, -j);1311startx = -1;1312}1313} else if (startx < 0) {1314startx = i;1315}1316}1317if (startx >= 0) {1318subImage = bufferedImage.getSubimage(startx, j,1319right - startx, 1);1320xform.translate(startx, j);1321drawImageToPlatform(subImage, xform, bgcolor,13220, 0, right - startx, 1, true);1323xform.translate(-startx, -j);1324}1325}1326return true;1327}1328132913301331/**1332* The various <code>drawImage()</code> methods for1333* <code>PathGraphics</code> are all decomposed1334* into an invocation of <code>drawImageToPlatform</code>.1335* The portion of the passed in image defined by1336* <code>srcX, srcY, srcWidth, and srcHeight</code>1337* is transformed by the supplied AffineTransform and1338* 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 transform the image before drawing.1343* This can be null.1344* @param bgcolor This color is drawn where the image has transparent1345* pixels. If this parameter is null then the1346* pixels already in the destination should show1347* through.1348* @param srcX With srcY this defines the upper-left corner1349* of the portion of the image to be drawn.1350*1351* @param srcY With srcX this defines the upper-left corner1352* of the portion of the image to be drawn.1353* @param srcWidth The width of the portion of the image to1354* be drawn.1355* @param srcHeight The height of the portion of the image to1356* be drawn.1357* @param handlingTransparency if being recursively called to1358* print opaque region of transparent image1359*/1360protected abstract boolean1361drawImageToPlatform(Image img, AffineTransform xform,1362Color bgcolor,1363int srcX, int srcY,1364int srcWidth, int srcHeight,1365boolean handlingTransparency);13661367/**1368* Draws as much of the specified image as is currently available.1369* The image is drawn with its top-left corner at1370* (<i>x</i>, <i>y</i>) in this graphics context's coordinate1371* space. Transparent pixels in the image do not affect whatever1372* pixels are already there.1373* <p>1374* This method returns immediately in all cases, even if the1375* complete image has not yet been loaded, and it has not been dithered1376* and converted for the current output device.1377* <p>1378* If the image has not yet been completely loaded, then1379* <code>drawImage</code> returns <code>false</code>. As more of1380* the image becomes available, the process that draws the image notifies1381* 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 of1386* the image is converted.1387* @see java.awt.Image1388* @see java.awt.image.ImageObserver1389* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1390* @since JDK1.01391*/1392public boolean drawImage(Image img, int x, int y,1393ImageObserver observer) {13941395return drawImage(img, x, y, null, observer);1396}13971398/**1399* Draws as much of the specified image as has already been scaled1400* to fit inside the specified rectangle.1401* <p>1402* The image is drawn inside the specified rectangle of this1403* graphics context's coordinate space, and is scaled if1404* necessary. Transparent pixels do not affect whatever pixels1405* are already there.1406* <p>1407* This method returns immediately in all cases, even if the1408* entire image has not yet been scaled, dithered, and converted1409* for the current output device.1410* If the current output representation is not yet complete, then1411* <code>drawImage</code> returns <code>false</code>. As more of1412* the image becomes available, the process that draws the image notifies1413* the image observer by calling its <code>imageUpdate</code> method.1414* <p>1415* A scaled version of an image will not necessarily be1416* available immediately just because an unscaled version of the1417* image has been constructed for this output device. Each size of1418* the image may be cached separately and generated from the original1419* 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 of1426* the image is converted.1427* @see java.awt.Image1428* @see java.awt.image.ImageObserver1429* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1430* @since JDK1.01431*/1432public boolean drawImage(Image img, int x, int y,1433int width, int height,1434ImageObserver observer) {14351436return drawImage(img, x, y, width, height, null, observer);14371438}14391440/*1441* Draws as much of the specified image as is currently available.1442* The image is drawn with its top-left corner at1443* (<i>x</i>, <i>y</i>) in this graphics context's coordinate1444* space. Transparent pixels are drawn in the specified1445* background color.1446* <p>1447* This operation is equivalent to filling a rectangle of the1448* width and height of the specified image with the given color and then1449* drawing the image on top of it, but possibly more efficient.1450* <p>1451* This method returns immediately in all cases, even if the1452* complete image has not yet been loaded, and it has not been dithered1453* and converted for the current output device.1454* <p>1455* If the image has not yet been completely loaded, then1456* <code>drawImage</code> returns <code>false</code>. As more of1457* the image becomes available, the process that draws the image notifies1458* 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 the1464* non-opaque portions of the image.1465* In this WPathGraphics implementation,1466* this parameter can be null in which1467* case that background is made a transparent1468* white.1469* @param observer object to be notified as more of1470* the image is converted.1471* @see java.awt.Image1472* @see java.awt.image.ImageObserver1473* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1474* @since JDK1.01475*/1476public boolean drawImage(Image img, int x, int y,1477Color bgcolor,1478ImageObserver observer) {14791480if (img == null) {1481return true;1482}14831484boolean result;1485int srcWidth = img.getWidth(null);1486int srcHeight = img.getHeight(null);14871488if (srcWidth < 0 || srcHeight < 0) {1489result = false;1490} else {1491result = drawImage(img, x, y, srcWidth, srcHeight, bgcolor, observer);1492}14931494return result;1495}14961497/**1498* Draws as much of the specified image as has already been scaled1499* to fit inside the specified rectangle.1500* <p>1501* The image is drawn inside the specified rectangle of this1502* graphics context's coordinate space, and is scaled if1503* necessary. Transparent pixels are drawn in the specified1504* background color.1505* This operation is equivalent to filling a rectangle of the1506* width and height of the specified image with the given color and then1507* drawing the image on top of it, but possibly more efficient.1508* <p>1509* This method returns immediately in all cases, even if the1510* entire image has not yet been scaled, dithered, and converted1511* for the current output device.1512* If the current output representation is not yet complete then1513* <code>drawImage</code> returns <code>false</code>. As more of1514* the image becomes available, the process that draws the image notifies1515* the specified image observer.1516* <p>1517* A scaled version of an image will not necessarily be1518* available immediately just because an unscaled version of the1519* image has been constructed for this output device. Each size of1520* the image may be cached separately and generated from the original1521* 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 the1529* non-opaque portions of the image.1530* @param observer object to be notified as more of1531* the image is converted.1532* @see java.awt.Image1533* @see java.awt.image.ImageObserver1534* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1535* @since JDK1.01536*/1537public boolean drawImage(Image img, int x, int y,1538int width, int height,1539Color bgcolor,1540ImageObserver observer) {15411542if (img == null) {1543return true;1544}15451546boolean result;1547int srcWidth = img.getWidth(null);1548int srcHeight = img.getHeight(null);15491550if (srcWidth < 0 || srcHeight < 0) {1551result = false;1552} else {1553result = drawImage(img,1554x, y, x + width, y + height,15550, 0, srcWidth, srcHeight,1556observer);1557}15581559return result;1560}15611562/**1563* Draws as much of the specified area of the specified image as is1564* currently available, scaling it on the fly to fit inside the1565* specified area of the destination drawable surface. Transparent pixels1566* do not affect whatever pixels are already there.1567* <p>1568* This method returns immediately in all cases, even if the1569* image area to be drawn has not yet been scaled, dithered, and converted1570* for the current output device.1571* If the current output representation is not yet complete then1572* <code>drawImage</code> returns <code>false</code>. As more of1573* the image becomes available, the process that draws the image notifies1574* the specified image observer.1575* <p>1576* This method always uses the unscaled version of the image1577* to render the scaled rectangle and performs the required1578* scaling on the fly. It does not use a cached, scaled version1579* of the image for this operation. Scaling of the image from source1580* to destination is performed such that the first coordinate1581* of the source rectangle is mapped to the first coordinate of1582* the destination rectangle, and the second source coordinate is1583* mapped to the second destination coordinate. The subimage is1584* scaled and flipped as needed to preserve those mappings.1585* @param img the specified image to be drawn1586* @param dx1 the <i>x</i> coordinate of the first corner of the1587* destination rectangle.1588* @param dy1 the <i>y</i> coordinate of the first corner of the1589* destination rectangle.1590* @param dx2 the <i>x</i> coordinate of the second corner of the1591* destination rectangle.1592* @param dy2 the <i>y</i> coordinate of the second corner of the1593* destination rectangle.1594* @param sx1 the <i>x</i> coordinate of the first corner of the1595* source rectangle.1596* @param sy1 the <i>y</i> coordinate of the first corner of the1597* source rectangle.1598* @param sx2 the <i>x</i> coordinate of the second corner of the1599* source rectangle.1600* @param sy2 the <i>y</i> coordinate of the second corner of the1601* source rectangle.1602* @param observer object to be notified as more of the image is1603* scaled and converted.1604* @see java.awt.Image1605* @see java.awt.image.ImageObserver1606* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1607* @since JDK1.11608*/1609public boolean drawImage(Image img,1610int dx1, int dy1, int dx2, int dy2,1611int sx1, int sy1, int sx2, int sy2,1612ImageObserver observer) {16131614return drawImage(img,1615dx1, dy1, dx2, dy2,1616sx1, sy1, sx2, sy2,1617null, observer);1618}16191620/**1621* Draws as much of the specified area of the specified image as is1622* currently available, scaling it on the fly to fit inside the1623* 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 the1627* width and height of the specified image with the given color and then1628* drawing the image on top of it, but possibly more efficient.1629* <p>1630* This method returns immediately in all cases, even if the1631* image area to be drawn has not yet been scaled, dithered, and converted1632* for the current output device.1633* If the current output representation is not yet complete then1634* <code>drawImage</code> returns <code>false</code>. As more of1635* the image becomes available, the process that draws the image notifies1636* the specified image observer.1637* <p>1638* This method always uses the unscaled version of the image1639* to render the scaled rectangle and performs the required1640* scaling on the fly. It does not use a cached, scaled version1641* of the image for this operation. Scaling of the image from source1642* to destination is performed such that the first coordinate1643* of the source rectangle is mapped to the first coordinate of1644* the destination rectangle, and the second source coordinate is1645* mapped to the second destination coordinate. The subimage is1646* scaled and flipped as needed to preserve those mappings.1647* @param img the specified image to be drawn1648* This method does nothing if <code>img</code> is null.1649* @param dx1 the <i>x</i> coordinate of the first corner of the1650* destination rectangle.1651* @param dy1 the <i>y</i> coordinate of the first corner of the1652* destination rectangle.1653* @param dx2 the <i>x</i> coordinate of the second corner of the1654* destination rectangle.1655* @param dy2 the <i>y</i> coordinate of the second corner of the1656* destination rectangle.1657* @param sx1 the <i>x</i> coordinate of the first corner of the1658* source rectangle.1659* @param sy1 the <i>y</i> coordinate of the first corner of the1660* source rectangle.1661* @param sx2 the <i>x</i> coordinate of the second corner of the1662* source rectangle.1663* @param sy2 the <i>y</i> coordinate of the second corner of the1664* source rectangle.1665* @param bgcolor the background color to paint under the1666* non-opaque portions of the image.1667* @param observer object to be notified as more of the image is1668* scaled and converted.1669* @see java.awt.Image1670* @see java.awt.image.ImageObserver1671* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)1672* @since JDK1.11673*/1674public boolean drawImage(Image img,1675int dx1, int dy1, int dx2, int dy2,1676int sx1, int sy1, int sx2, int sy2,1677Color bgcolor,1678ImageObserver observer) {16791680if (img == null) {1681return true;1682}1683int imgWidth = img.getWidth(null);1684int imgHeight = img.getHeight(null);16851686if (imgWidth < 0 || imgHeight < 0) {1687return true;1688}16891690int srcWidth = sx2 - sx1;1691int srcHeight = sy2 - sy1;16921693/* Create a transform which describes the changes1694* from the source coordinates to the destination1695* coordinates. The scaling is determined by the1696* ratio of the two rectangles, while the translation1697* comes from the difference of their origins.1698*/1699float scalex = (float) (dx2 - dx1) / srcWidth;1700float scaley = (float) (dy2 - dy1) / srcHeight;1701AffineTransform xForm1702= new AffineTransform(scalex,17030,17040,1705scaley,1706dx1 - (sx1 * scalex),1707dy1 - (sy1 * scaley));17081709/* drawImageToPlatform needs the top-left of the source area and1710* a positive width and height. The xform describes how to map1711* src->dest, so that information is not lost.1712*/1713int tmp=0;1714if (sx2 < sx1) {1715tmp = sx1;1716sx1 = sx2;1717sx2 = tmp;1718}1719if (sy2 < sy1) {1720tmp = sy1;1721sy1 = sy2;1722sy2 = tmp;1723}17241725/* 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*/1728if (sx1 < 0) {1729sx1 = 0;1730} else if (sx1 > imgWidth) { // empty srcArea, nothing to draw1731sx1 = imgWidth;1732}1733if (sx2 < 0) { // empty srcArea, nothing to draw1734sx2 = 0;1735} else if (sx2 > imgWidth) {1736sx2 = imgWidth;1737}1738if (sy1 < 0) {1739sy1 = 0;1740} else if (sy1 > imgHeight) { // empty srcArea1741sy1 = imgHeight;1742}1743if (sy2 < 0) { // empty srcArea1744sy2 = 0;1745} else if (sy2 > imgHeight) {1746sy2 = imgHeight;1747}17481749srcWidth = sx2 - sx1;1750srcHeight = sy2 - sy1;17511752if (srcWidth <= 0 || srcHeight <= 0) {1753return true;1754}17551756return drawImageToPlatform(img, xForm, bgcolor,1757sx1, sy1, srcWidth, srcHeight, false);175817591760}17611762/**1763* Draws an image, applying a transform from image space into user space1764* before drawing.1765* The transformation from user space into device space is done with1766* the current transform in the Graphics2D.1767* The given transformation is applied to the image before the1768* 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 is1771* 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 image1776* is converted.1777* @see #transform1778* @see #setTransform1779* @see #setComposite1780* @see #clip1781* @see #setClip1782*/1783public boolean drawImage(Image img,1784AffineTransform xform,1785ImageObserver obs) {17861787if (img == null) {1788return true;1789}17901791boolean result;1792int srcWidth = img.getWidth(null);1793int srcHeight = img.getHeight(null);17941795if (srcWidth < 0 || srcHeight < 0) {1796result = false;1797} else {1798result = drawImageToPlatform(img, xform, null,17990, 0, srcWidth, srcHeight, false);1800}18011802return result;1803}18041805/**1806* Draws a BufferedImage that is filtered with a BufferedImageOp.1807* The rendering attributes applied include the clip, transform1808* 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 #transform1818* @see #setTransform1819* @see #setComposite1820* @see #clip1821* @see #setClip1822*/1823public void drawImage(BufferedImage img,1824BufferedImageOp op,1825int x,1826int y) {18271828if (img == null) {1829return;1830}18311832int srcWidth = img.getWidth(null);1833int srcHeight = img.getHeight(null);18341835if (op != null) {1836img = op.filter(img, null);1837}1838if (srcWidth <= 0 || srcHeight <= 0) {1839return;1840} else {1841AffineTransform xform = new AffineTransform(1f,0f,0f,1f,x,y);1842drawImageToPlatform(img, xform, null,18430, 0, srcWidth, srcHeight, false);1844}18451846}18471848/**1849* Draws an image, applying a transform from image space into user space1850* before drawing.1851* The transformation from user space into device space is done with1852* the current transform in the Graphics2D.1853* The given transformation is applied to the image before the1854* 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 is1857* 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 #transform1862* @see #setTransform1863* @see #setComposite1864* @see #clip1865* @see #setClip1866*/1867public void drawRenderedImage(RenderedImage img,1868AffineTransform xform) {18691870if (img == null) {1871return;1872}18731874BufferedImage bufferedImage = null;1875int srcWidth = img.getWidth();1876int srcHeight = img.getHeight();18771878if (srcWidth <= 0 || srcHeight <= 0) {1879return;1880}18811882if (img instanceof BufferedImage) {1883bufferedImage = (BufferedImage) img;1884} else {1885bufferedImage = new BufferedImage(srcWidth, srcHeight,1886BufferedImage.TYPE_INT_ARGB);1887Graphics2D imageGraphics = bufferedImage.createGraphics();1888imageGraphics.drawRenderedImage(img, xform);1889}18901891drawImageToPlatform(bufferedImage, xform, null,18920, 0, srcWidth, srcHeight, false);18931894}18951896}189718981899