/* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.java2d; import java.awt.Color; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import sun.java2d.loops.RenderCache; import sun.java2d.loops.RenderLoops; import sun.java2d.loops.CompositeType; import sun.java2d.loops.SurfaceType; import sun.java2d.loops.MaskFill; import sun.java2d.loops.DrawLine; import sun.java2d.loops.FillRect; import sun.java2d.loops.DrawRect; import sun.java2d.loops.DrawPolygons; import sun.java2d.loops.DrawPath; import sun.java2d.loops.FillPath; import sun.java2d.loops.FillSpans; import sun.java2d.loops.FillParallelogram; import sun.java2d.loops.DrawParallelogram; import sun.java2d.loops.FontInfo; import sun.java2d.loops.DrawGlyphList; import sun.java2d.loops.DrawGlyphListAA; import sun.java2d.loops.DrawGlyphListLCD; import sun.java2d.pipe.LoopPipe; import sun.java2d.pipe.ShapeDrawPipe; import sun.java2d.pipe.ParallelogramPipe; import sun.java2d.pipe.CompositePipe; import sun.java2d.pipe.GeneralCompositePipe; import sun.java2d.pipe.SpanClipRenderer; import sun.java2d.pipe.SpanShapeRenderer; import sun.java2d.pipe.AAShapePipe; import sun.java2d.pipe.AlphaPaintPipe; import sun.java2d.pipe.AlphaColorPipe; import sun.java2d.pipe.PixelToShapeConverter; import sun.java2d.pipe.PixelToParallelogramConverter; import sun.java2d.pipe.TextPipe; import sun.java2d.pipe.TextRenderer; import sun.java2d.pipe.AATextRenderer; import sun.java2d.pipe.LCDTextRenderer; import sun.java2d.pipe.SolidTextRenderer; import sun.java2d.pipe.OutlineTextRenderer; import sun.java2d.pipe.DrawImagePipe; import sun.java2d.pipe.DrawImage; import sun.awt.SunHints; import sun.awt.image.SurfaceManager; import sun.java2d.pipe.LoopBasedPipe; /** * This class provides various pieces of information relevant to a * particular drawing surface. The information obtained from this * object describes the pixels of a particular instance of a drawing * surface and can only be shared among the various graphics objects * that target the same BufferedImage or the same screen Component. *

* Each SurfaceData object holds a StateTrackableDelegate object * which tracks both changes to the content of the pixels of this * surface and changes to the overall state of the pixels - such * as becoming invalid or losing the surface. The delegate is * marked "dirty" whenever the setSurfaceLost() or invalidate() * methods are called and should also be marked "dirty" by the * rendering pipelines whenever they modify the pixels of this * SurfaceData. *

* If you get a StateTracker from a SurfaceData and it reports * that it is still "current", then you can trust that the pixels * have not changed and that the SurfaceData is still valid and * has not lost its underlying storage (surfaceLost) since you * retrieved the tracker. */ public abstract class SurfaceData implements Transparency, DisposerTarget, StateTrackable, Surface { private long pData; private boolean valid; private boolean surfaceLost; // = false; private SurfaceType surfaceType; private ColorModel colorModel; private Object disposerReferent = new Object(); private static native void initIDs(); private Object blitProxyKey; private StateTrackableDelegate stateDelegate; static { initIDs(); } protected SurfaceData(SurfaceType surfaceType, ColorModel cm) { this(State.STABLE, surfaceType, cm); } protected SurfaceData(State state, SurfaceType surfaceType, ColorModel cm) { this(StateTrackableDelegate.createInstance(state), surfaceType, cm); } protected SurfaceData(StateTrackableDelegate trackable, SurfaceType surfaceType, ColorModel cm) { this.stateDelegate = trackable; this.colorModel = cm; this.surfaceType = surfaceType; valid = true; } protected SurfaceData(State state) { this.stateDelegate = StateTrackableDelegate.createInstance(state); valid = true; } /** * Subclasses can set a "blit proxy key" which will be used * along with the SurfaceManager.getCacheData() mechanism to * store acceleration-compatible cached copies of source images. * This key is a "tag" used to identify which cached copies * are compatible with this destination SurfaceData. * The getSourceSurfaceData() method uses this key to manage * cached copies of a source image as described below. *

* The Object used as this key should be as unique as it needs * to be to ensure that multiple acceleratible destinations can * each store their cached copies separately under different keys * without interfering with each other or getting back the wrong * cached copy. *

* Many acceleratable SurfaceData objects can use their own * GraphicsConfiguration as their proxy key as the GC object will * typically be unique to a given screen and pixel format, but * other rendering destinations may have more or less stringent * sharing requirements. For instance, X11 pixmaps can be * shared on a given screen by any GraphicsConfiguration that * has the same depth and SurfaceType. Multiple such GCs with * the same depth and SurfaceType can exist per screen so storing * a different cached proxy for each would be a waste. One can * imagine platforms where a single cached copy can be created * and shared across all screens and pixel formats - such * implementations could use a single heavily shared key Object. */ protected void setBlitProxyKey(Object key) { // Caching is effectively disabled if we never have a proxy key // since the getSourceSurfaceData() method only does caching // if the key is not null. if (SurfaceDataProxy.isCachingAllowed()) { this.blitProxyKey = key; } } /** * This method is called on a destination SurfaceData to choose * the best SurfaceData from a source Image for an imaging * operation, with help from its SurfaceManager. * The method may determine that the default SurfaceData was * really the best choice in the first place, or it may decide * to use a cached surface. Some general decisions about whether * acceleration is enabled are made by this method, but any * decision based on the type of the source image is made in * the makeProxyFor method below when it comes up with the * appropriate SurfaceDataProxy instance. * The parameters describe the type of imaging operation being performed. *

* If a blitProxyKey was supplied by the subclass then it is * used to potentially override the choice of source SurfaceData. * The outline of this process is: *

    *
  1. Image pipeline asks destSD to find an appropriate * srcSD for a given source Image object. *
  2. destSD gets the SurfaceManager of the source Image * and first retrieves the default SD from it using * getPrimarySurfaceData() *
  3. destSD uses its "blit proxy key" (if set) to look for * some cached data stored in the source SurfaceManager *
  4. If the cached data is null then makeProxyFor() is used * to create some cached data which is stored back in the * source SurfaceManager under the same key for future uses. *
  5. The cached data will be a SurfaceDataProxy object. *
  6. The SurfaceDataProxy object is then consulted to * return a replacement SurfaceData object (typically * a cached copy if appropriate, or the original if not). *
*/ public SurfaceData getSourceSurfaceData(Image img, int txtype, CompositeType comp, Color bgColor) { SurfaceManager srcMgr = SurfaceManager.getManager(img); SurfaceData srcData = srcMgr.getPrimarySurfaceData(); if (img.getAccelerationPriority() > 0.0f && blitProxyKey != null) { SurfaceDataProxy sdp = (SurfaceDataProxy) srcMgr.getCacheData(blitProxyKey); if (sdp == null || !sdp.isValid()) { if (srcData.getState() == State.UNTRACKABLE) { sdp = SurfaceDataProxy.UNCACHED; } else { sdp = makeProxyFor(srcData); } srcMgr.setCacheData(blitProxyKey, sdp); } srcData = sdp.replaceData(srcData, txtype, comp, bgColor); } return srcData; } /** * This method is called on a destination SurfaceData to choose * a proper SurfaceDataProxy subclass for a source SurfaceData * to use to control when and with what surface to override a * given image operation. The argument is the default SurfaceData * for the source Image. *

* The type of the return object is chosen based on the * acceleration capabilities of this SurfaceData and the * type of the given source SurfaceData object. *

* In some cases the original SurfaceData will always be the * best choice to use to blit to this SurfaceData. This can * happen if the source image is a hardware surface of the * same type as this one and so acceleration will happen without * any caching. It may also be the case that the source image * can never be accelerated on this SurfaceData - for example * because it is translucent and there are no accelerated * translucent image ops for this surface. *

* In those cases there is a special SurfaceDataProxy.UNCACHED * instance that represents a NOP for caching purposes - it * always returns the original sourceSD object as the replacement * copy so no caching is ever performed. */ public SurfaceDataProxy makeProxyFor(SurfaceData srcData) { return SurfaceDataProxy.UNCACHED; } /** * Extracts the SurfaceManager from the given Image, and then * returns the SurfaceData object that would best be suited as the * destination surface in some rendering operation. */ public static SurfaceData getPrimarySurfaceData(Image img) { SurfaceManager sMgr = SurfaceManager.getManager(img); return sMgr.getPrimarySurfaceData(); } /** * Restores the contents of the given Image and then returns the new * SurfaceData object in use by the Image's SurfaceManager. */ public static SurfaceData restoreContents(Image img) { SurfaceManager sMgr = SurfaceManager.getManager(img); return sMgr.restoreContents(); } public State getState() { return stateDelegate.getState(); } public StateTracker getStateTracker() { return stateDelegate.getStateTracker(); } /** * Marks this surface as dirty. */ public final void markDirty() { stateDelegate.markDirty(); } /** * Sets the value of the surfaceLost variable, which indicates whether * something has happened to the rendering surface such that it needs * to be restored and re-rendered. */ public void setSurfaceLost(boolean lost) { surfaceLost = lost; stateDelegate.markDirty(); } public boolean isSurfaceLost() { return surfaceLost; } /** * Returns a boolean indicating whether or not this SurfaceData is valid. */ public final boolean isValid() { return valid; } public Object getDisposerReferent() { return disposerReferent; } public long getNativeOps() { return pData; } /** * Sets this SurfaceData object to the invalid state. All Graphics * objects must get a new SurfaceData object via the refresh method * and revalidate their pipelines before continuing. */ public void invalidate() { valid = false; stateDelegate.markDirty(); } /** * Certain changes in the configuration of a surface require the * invalidation of existing associated SurfaceData objects and * the creation of brand new ones. These changes include size, * ColorModel, or SurfaceType. Existing Graphics objects * which are directed at such surfaces, however, must continue * to render to them even after the change occurs underneath * the covers. The getReplacement() method is called from * SunGraphics2D.revalidateAll() when the associated SurfaceData * is found to be invalid so that a Graphics object can continue * to render to the surface in its new configuration. * * Such changes only tend to happen to window based surfaces since * most image based surfaces never change size or pixel format. * Even VolatileImage objects never change size and they only * change their pixel format when manually validated against a * new GraphicsConfiguration, at which point old Graphics objects * are no longer expected to render to them after the validation * step. Thus, only window based surfaces really need to deal * with this form of replacement. */ public abstract SurfaceData getReplacement(); protected static final LoopPipe colorPrimitives; public static final TextPipe outlineTextRenderer; public static final TextPipe solidTextRenderer; public static final TextPipe aaTextRenderer; public static final TextPipe lcdTextRenderer; protected static final AlphaColorPipe colorPipe; protected static final PixelToShapeConverter colorViaShape; protected static final PixelToParallelogramConverter colorViaPgram; protected static final TextPipe colorText; protected static final CompositePipe clipColorPipe; protected static final TextPipe clipColorText; protected static final AAShapePipe AAColorShape; protected static final PixelToParallelogramConverter AAColorViaShape; protected static final PixelToParallelogramConverter AAColorViaPgram; protected static final AAShapePipe AAClipColorShape; protected static final PixelToParallelogramConverter AAClipColorViaShape; protected static final CompositePipe paintPipe; protected static final SpanShapeRenderer paintShape; protected static final PixelToShapeConverter paintViaShape; protected static final TextPipe paintText; protected static final CompositePipe clipPaintPipe; protected static final TextPipe clipPaintText; protected static final AAShapePipe AAPaintShape; protected static final PixelToParallelogramConverter AAPaintViaShape; protected static final AAShapePipe AAClipPaintShape; protected static final PixelToParallelogramConverter AAClipPaintViaShape; protected static final CompositePipe compPipe; protected static final SpanShapeRenderer compShape; protected static final PixelToShapeConverter compViaShape; protected static final TextPipe compText; protected static final CompositePipe clipCompPipe; protected static final TextPipe clipCompText; protected static final AAShapePipe AACompShape; protected static final PixelToParallelogramConverter AACompViaShape; protected static final AAShapePipe AAClipCompShape; protected static final PixelToParallelogramConverter AAClipCompViaShape; protected static final DrawImagePipe imagepipe; // Utility subclass to add the LoopBasedPipe tagging interface static class PixelToShapeLoopConverter extends PixelToShapeConverter implements LoopBasedPipe { public PixelToShapeLoopConverter(ShapeDrawPipe pipe) { super(pipe); } } // Utility subclass to add the LoopBasedPipe tagging interface static class PixelToPgramLoopConverter extends PixelToParallelogramConverter implements LoopBasedPipe { public PixelToPgramLoopConverter(ShapeDrawPipe shapepipe, ParallelogramPipe pgrampipe, double minPenSize, double normPosition, boolean adjustfill) { super(shapepipe, pgrampipe, minPenSize, normPosition, adjustfill); } } private static PixelToParallelogramConverter makeConverter(AAShapePipe renderer, ParallelogramPipe pgrampipe) { return new PixelToParallelogramConverter(renderer, pgrampipe, 1.0/8.0, 0.499, false); } private static PixelToParallelogramConverter makeConverter(AAShapePipe renderer) { return makeConverter(renderer, renderer); } static { colorPrimitives = new LoopPipe(); outlineTextRenderer = new OutlineTextRenderer(); solidTextRenderer = new SolidTextRenderer(); aaTextRenderer = new AATextRenderer(); lcdTextRenderer = new LCDTextRenderer(); colorPipe = new AlphaColorPipe(); // colorShape = colorPrimitives; colorViaShape = new PixelToShapeLoopConverter(colorPrimitives); colorViaPgram = new PixelToPgramLoopConverter(colorPrimitives, colorPrimitives, 1.0, 0.25, true); colorText = new TextRenderer(colorPipe); clipColorPipe = new SpanClipRenderer(colorPipe); clipColorText = new TextRenderer(clipColorPipe); AAColorShape = new AAShapePipe(colorPipe); AAColorViaShape = makeConverter(AAColorShape); AAColorViaPgram = makeConverter(AAColorShape, colorPipe); AAClipColorShape = new AAShapePipe(clipColorPipe); AAClipColorViaShape = makeConverter(AAClipColorShape); paintPipe = new AlphaPaintPipe(); paintShape = new SpanShapeRenderer.Composite(paintPipe); paintViaShape = new PixelToShapeConverter(paintShape); paintText = new TextRenderer(paintPipe); clipPaintPipe = new SpanClipRenderer(paintPipe); clipPaintText = new TextRenderer(clipPaintPipe); AAPaintShape = new AAShapePipe(paintPipe); AAPaintViaShape = makeConverter(AAPaintShape); AAClipPaintShape = new AAShapePipe(clipPaintPipe); AAClipPaintViaShape = makeConverter(AAClipPaintShape); compPipe = new GeneralCompositePipe(); compShape = new SpanShapeRenderer.Composite(compPipe); compViaShape = new PixelToShapeConverter(compShape); compText = new TextRenderer(compPipe); clipCompPipe = new SpanClipRenderer(compPipe); clipCompText = new TextRenderer(clipCompPipe); AACompShape = new AAShapePipe(compPipe); AACompViaShape = makeConverter(AACompShape); AAClipCompShape = new AAShapePipe(clipCompPipe); AAClipCompViaShape = makeConverter(AAClipCompShape); imagepipe = new DrawImage(); } /* Not all surfaces and rendering mode combinations support LCD text. */ static final int LOOP_UNKNOWN = 0; static final int LOOP_FOUND = 1; static final int LOOP_NOTFOUND = 2; int haveLCDLoop; int havePgramXORLoop; int havePgramSolidLoop; public boolean canRenderLCDText(SunGraphics2D sg2d) { // For now the answer can only be true in the following cases: if (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY && sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR && sg2d.clipState <= SunGraphics2D.CLIP_RECTANGULAR && sg2d.surfaceData.getTransparency() == Transparency.OPAQUE) { if (haveLCDLoop == LOOP_UNKNOWN) { DrawGlyphListLCD loop = DrawGlyphListLCD.locate(SurfaceType.AnyColor, CompositeType.SrcNoEa, getSurfaceType()); haveLCDLoop = (loop != null) ? LOOP_FOUND : LOOP_NOTFOUND; } return haveLCDLoop == LOOP_FOUND; } return false; /* for now - in the future we may want to search */ } public boolean canRenderParallelograms(SunGraphics2D sg2d) { if (sg2d.paintState <= sg2d.PAINT_ALPHACOLOR) { if (sg2d.compositeState == sg2d.COMP_XOR) { if (havePgramXORLoop == LOOP_UNKNOWN) { FillParallelogram loop = FillParallelogram.locate(SurfaceType.AnyColor, CompositeType.Xor, getSurfaceType()); havePgramXORLoop = (loop != null) ? LOOP_FOUND : LOOP_NOTFOUND; } return havePgramXORLoop == LOOP_FOUND; } else if (sg2d.compositeState <= sg2d.COMP_ISCOPY && sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON && sg2d.clipState != sg2d.CLIP_SHAPE) { if (havePgramSolidLoop == LOOP_UNKNOWN) { FillParallelogram loop = FillParallelogram.locate(SurfaceType.AnyColor, CompositeType.SrcNoEa, getSurfaceType()); havePgramSolidLoop = (loop != null) ? LOOP_FOUND : LOOP_NOTFOUND; } return havePgramSolidLoop == LOOP_FOUND; } } return false; } public void validatePipe(SunGraphics2D sg2d) { sg2d.imagepipe = imagepipe; if (sg2d.compositeState == sg2d.COMP_XOR) { if (sg2d.paintState > sg2d.PAINT_ALPHACOLOR) { sg2d.drawpipe = paintViaShape; sg2d.fillpipe = paintViaShape; sg2d.shapepipe = paintShape; // REMIND: Ideally custom paint mode would use glyph // rendering as opposed to outline rendering but the // glyph paint rendering pipeline uses MaskBlit which // is not defined for XOR. This means that text drawn // in XOR mode with a Color object is different than // text drawn in XOR mode with a Paint object. sg2d.textpipe = outlineTextRenderer; } else { PixelToShapeConverter converter; if (canRenderParallelograms(sg2d)) { converter = colorViaPgram; // Note that we use the transforming pipe here because it // will examine the shape and possibly perform an optimized // operation if it can be simplified. The simplifications // will be valid for all STROKE and TRANSFORM types. sg2d.shapepipe = colorViaPgram; } else { converter = colorViaShape; sg2d.shapepipe = colorPrimitives; } if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.drawpipe = converter; sg2d.fillpipe = converter; // REMIND: We should not be changing text strategies // between outline and glyph rendering based upon the // presence of a complex clip as that could cause a // mismatch when drawing the same text both clipped // and unclipped on two separate rendering passes. // Unfortunately, all of the clipped glyph rendering // pipelines rely on the use of the MaskBlit operation // which is not defined for XOR. sg2d.textpipe = outlineTextRenderer; } else { if (sg2d.transformState >= sg2d.TRANSFORM_TRANSLATESCALE) { sg2d.drawpipe = converter; sg2d.fillpipe = converter; } else { if (sg2d.strokeState != sg2d.STROKE_THIN) { sg2d.drawpipe = converter; } else { sg2d.drawpipe = colorPrimitives; } sg2d.fillpipe = colorPrimitives; } sg2d.textpipe = solidTextRenderer; } // assert(sg2d.surfaceData == this); } } else if (sg2d.compositeState == sg2d.COMP_CUSTOM) { if (sg2d.antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) { if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.drawpipe = AAClipCompViaShape; sg2d.fillpipe = AAClipCompViaShape; sg2d.shapepipe = AAClipCompViaShape; sg2d.textpipe = clipCompText; } else { sg2d.drawpipe = AACompViaShape; sg2d.fillpipe = AACompViaShape; sg2d.shapepipe = AACompViaShape; sg2d.textpipe = compText; } } else { sg2d.drawpipe = compViaShape; sg2d.fillpipe = compViaShape; sg2d.shapepipe = compShape; if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.textpipe = clipCompText; } else { sg2d.textpipe = compText; } } } else if (sg2d.antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) { sg2d.alphafill = getMaskFill(sg2d); // assert(sg2d.surfaceData == this); if (sg2d.alphafill != null) { if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.drawpipe = AAClipColorViaShape; sg2d.fillpipe = AAClipColorViaShape; sg2d.shapepipe = AAClipColorViaShape; sg2d.textpipe = clipColorText; } else { PixelToParallelogramConverter converter = (sg2d.alphafill.canDoParallelograms() ? AAColorViaPgram : AAColorViaShape); sg2d.drawpipe = converter; sg2d.fillpipe = converter; sg2d.shapepipe = converter; if (sg2d.paintState > sg2d.PAINT_ALPHACOLOR || sg2d.compositeState > sg2d.COMP_ISCOPY) { sg2d.textpipe = colorText; } else { sg2d.textpipe = getTextPipe(sg2d, true /* AA==ON */); } } } else { if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.drawpipe = AAClipPaintViaShape; sg2d.fillpipe = AAClipPaintViaShape; sg2d.shapepipe = AAClipPaintViaShape; sg2d.textpipe = clipPaintText; } else { sg2d.drawpipe = AAPaintViaShape; sg2d.fillpipe = AAPaintViaShape; sg2d.shapepipe = AAPaintViaShape; sg2d.textpipe = paintText; } } } else if (sg2d.paintState > sg2d.PAINT_ALPHACOLOR || sg2d.compositeState > sg2d.COMP_ISCOPY || sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.drawpipe = paintViaShape; sg2d.fillpipe = paintViaShape; sg2d.shapepipe = paintShape; sg2d.alphafill = getMaskFill(sg2d); // assert(sg2d.surfaceData == this); if (sg2d.alphafill != null) { if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.textpipe = clipColorText; } else { sg2d.textpipe = colorText; } } else { if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.textpipe = clipPaintText; } else { sg2d.textpipe = paintText; } } } else { PixelToShapeConverter converter; if (canRenderParallelograms(sg2d)) { converter = colorViaPgram; // Note that we use the transforming pipe here because it // will examine the shape and possibly perform an optimized // operation if it can be simplified. The simplifications // will be valid for all STROKE and TRANSFORM types. sg2d.shapepipe = colorViaPgram; } else { converter = colorViaShape; sg2d.shapepipe = colorPrimitives; } if (sg2d.transformState >= sg2d.TRANSFORM_TRANSLATESCALE) { sg2d.drawpipe = converter; sg2d.fillpipe = converter; } else { if (sg2d.strokeState != sg2d.STROKE_THIN) { sg2d.drawpipe = converter; } else { sg2d.drawpipe = colorPrimitives; } sg2d.fillpipe = colorPrimitives; } sg2d.textpipe = getTextPipe(sg2d, false /* AA==OFF */); // assert(sg2d.surfaceData == this); } // check for loops if (sg2d.textpipe instanceof LoopBasedPipe || sg2d.shapepipe instanceof LoopBasedPipe || sg2d.fillpipe instanceof LoopBasedPipe || sg2d.drawpipe instanceof LoopBasedPipe || sg2d.imagepipe instanceof LoopBasedPipe) { sg2d.loops = getRenderLoops(sg2d); } } /* Return the text pipe to be used based on the graphics AA hint setting, * and the rest of the graphics state is compatible with these loops. * If the text AA hint is "DEFAULT", then the AA graphics hint requests * the AA text renderer, else it requests the B&W text renderer. */ private TextPipe getTextPipe(SunGraphics2D sg2d, boolean aaHintIsOn) { /* Try to avoid calling getFontInfo() unless its needed to * resolve one of the new AA types. */ switch (sg2d.textAntialiasHint) { case SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT: if (aaHintIsOn) { return aaTextRenderer; } else { return solidTextRenderer; } case SunHints.INTVAL_TEXT_ANTIALIAS_OFF: return solidTextRenderer; case SunHints.INTVAL_TEXT_ANTIALIAS_ON: return aaTextRenderer; default: switch (sg2d.getFontInfo().aaHint) { case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB: case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB: return lcdTextRenderer; case SunHints.INTVAL_TEXT_ANTIALIAS_ON: return aaTextRenderer; case SunHints.INTVAL_TEXT_ANTIALIAS_OFF: return solidTextRenderer; /* This should not be reached as the FontInfo will * always explicitly set its hint value. So whilst * this could be collapsed to returning say just * solidTextRenderer, or even removed, its left * here in case DEFAULT is ever passed in. */ default: if (aaHintIsOn) { return aaTextRenderer; } else { return solidTextRenderer; } } } } private static SurfaceType getPaintSurfaceType(SunGraphics2D sg2d) { switch (sg2d.paintState) { case SunGraphics2D.PAINT_OPAQUECOLOR: return SurfaceType.OpaqueColor; case SunGraphics2D.PAINT_ALPHACOLOR: return SurfaceType.AnyColor; case SunGraphics2D.PAINT_GRADIENT: if (sg2d.paint.getTransparency() == OPAQUE) { return SurfaceType.OpaqueGradientPaint; } else { return SurfaceType.GradientPaint; } case SunGraphics2D.PAINT_LIN_GRADIENT: if (sg2d.paint.getTransparency() == OPAQUE) { return SurfaceType.OpaqueLinearGradientPaint; } else { return SurfaceType.LinearGradientPaint; } case SunGraphics2D.PAINT_RAD_GRADIENT: if (sg2d.paint.getTransparency() == OPAQUE) { return SurfaceType.OpaqueRadialGradientPaint; } else { return SurfaceType.RadialGradientPaint; } case SunGraphics2D.PAINT_TEXTURE: if (sg2d.paint.getTransparency() == OPAQUE) { return SurfaceType.OpaqueTexturePaint; } else { return SurfaceType.TexturePaint; } default: case SunGraphics2D.PAINT_CUSTOM: return SurfaceType.AnyPaint; } } private static CompositeType getFillCompositeType(SunGraphics2D sg2d) { CompositeType compType = sg2d.imageComp; if (sg2d.compositeState == sg2d.COMP_ISCOPY) { if (compType == CompositeType.SrcOverNoEa) { compType = CompositeType.OpaqueSrcOverNoEa; } else { compType = CompositeType.SrcNoEa; } } return compType; } /** * Returns a MaskFill object that can be used on this destination * with the source (paint) and composite types determined by the given * SunGraphics2D, or null if no such MaskFill object can be located. * Subclasses can override this method if they wish to filter other * attributes (such as the hardware capabilities of the destination * surface) before returning a specific MaskFill object. */ protected MaskFill getMaskFill(SunGraphics2D sg2d) { SurfaceType src = getPaintSurfaceType(sg2d); CompositeType comp = getFillCompositeType(sg2d); SurfaceType dst = getSurfaceType(); return MaskFill.getFromCache(src, comp, dst); } private static RenderCache loopcache = new RenderCache(30); /** * Return a RenderLoops object containing all of the basic * GraphicsPrimitive objects for rendering to the destination * surface with the current attributes of the given SunGraphics2D. */ public RenderLoops getRenderLoops(SunGraphics2D sg2d) { SurfaceType src = getPaintSurfaceType(sg2d); CompositeType comp = getFillCompositeType(sg2d); SurfaceType dst = sg2d.getSurfaceData().getSurfaceType(); Object o = loopcache.get(src, comp, dst); if (o != null) { return (RenderLoops) o; } RenderLoops loops = makeRenderLoops(src, comp, dst); loopcache.put(src, comp, dst, loops); return loops; } /** * Construct and return a RenderLoops object containing all of * the basic GraphicsPrimitive objects for rendering to the * destination surface with the given source, destination, and * composite types. */ public static RenderLoops makeRenderLoops(SurfaceType src, CompositeType comp, SurfaceType dst) { RenderLoops loops = new RenderLoops(); loops.drawLineLoop = DrawLine.locate(src, comp, dst); loops.fillRectLoop = FillRect.locate(src, comp, dst); loops.drawRectLoop = DrawRect.locate(src, comp, dst); loops.drawPolygonsLoop = DrawPolygons.locate(src, comp, dst); loops.drawPathLoop = DrawPath.locate(src, comp, dst); loops.fillPathLoop = FillPath.locate(src, comp, dst); loops.fillSpansLoop = FillSpans.locate(src, comp, dst); loops.fillParallelogramLoop = FillParallelogram.locate(src, comp, dst); loops.drawParallelogramLoop = DrawParallelogram.locate(src, comp, dst); loops.drawGlyphListLoop = DrawGlyphList.locate(src, comp, dst); loops.drawGlyphListAALoop = DrawGlyphListAA.locate(src, comp, dst); loops.drawGlyphListLCDLoop = DrawGlyphListLCD.locate(src, comp, dst); /* System.out.println("drawLine: "+loops.drawLineLoop); System.out.println("fillRect: "+loops.fillRectLoop); System.out.println("drawRect: "+loops.drawRectLoop); System.out.println("drawPolygons: "+loops.drawPolygonsLoop); System.out.println("fillSpans: "+loops.fillSpansLoop); System.out.println("drawGlyphList: "+loops.drawGlyphListLoop); System.out.println("drawGlyphListAA: "+loops.drawGlyphListAALoop); System.out.println("drawGlyphListLCD: "+loops.drawGlyphListLCDLoop); */ return loops; } /** * Return the GraphicsConfiguration object that describes this * destination surface. */ public abstract GraphicsConfiguration getDeviceConfiguration(); /** * Return the SurfaceType object that describes the destination * surface. */ public final SurfaceType getSurfaceType() { return surfaceType; } /** * Return the ColorModel for the destination surface. */ public final ColorModel getColorModel() { return colorModel; } /** * Returns the type of this Transparency. * @return the field type of this Transparency, which is * either OPAQUE, BITMASK or TRANSLUCENT. */ public int getTransparency() { return getColorModel().getTransparency(); } /** * Return a readable Raster which contains the pixels for the * specified rectangular region of the destination surface. * The coordinate origin of the returned Raster is the same as * the device space origin of the destination surface. * In some cases the returned Raster might also be writeable. * In most cases, the returned Raster might contain more pixels * than requested. * * @see useTightBBoxes */ public abstract Raster getRaster(int x, int y, int w, int h); /** * Does the pixel accessibility of the destination surface * suggest that rendering algorithms might want to take * extra time to calculate a more accurate bounding box for * the operation being performed? * The typical case when this will be true is when a copy of * the pixels has to be made when doing a getRaster. The * fewer pixels copied, the faster the operation will go. * * @see getRaster */ public boolean useTightBBoxes() { // Note: The native equivalent would trigger on VISIBLE_TO_NATIVE // REMIND: This is not used - should be obsoleted maybe return true; } /** * Returns the pixel data for the specified Argb value packed * into an integer for easy storage and conveyance. */ public int pixelFor(int rgb) { return surfaceType.pixelFor(rgb, colorModel); } /** * Returns the pixel data for the specified color packed into an * integer for easy storage and conveyance. * * This method will use the getRGB() method of the Color object * and defer to the pixelFor(int rgb) method if not overridden. * * For now this is a convenience function, but for cases where * the highest quality color conversion is requested, this method * should be overridden in those cases so that a more direct * conversion of the color to the destination color space * can be done using the additional information in the Color * object. */ public int pixelFor(Color c) { return pixelFor(c.getRGB()); } /** * Returns the Argb representation for the specified integer value * which is packed in the format of the associated ColorModel. */ public int rgbFor(int pixel) { return surfaceType.rgbFor(pixel, colorModel); } /** * Returns the bounds of the destination surface. */ public abstract Rectangle getBounds(); static java.security.Permission compPermission; /** * Performs Security Permissions checks to see if a Custom * Composite object should be allowed access to the pixels * of this surface. */ protected void checkCustomComposite() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { if (compPermission == null) { compPermission = new java.awt.AWTPermission("readDisplayPixels"); } sm.checkPermission(compPermission); } } /** * Fetches private field IndexColorModel.allgrayopaque * which is true when all palette entries in the color * model are gray and opaque. */ protected static native boolean isOpaqueGray(IndexColorModel icm); /** * For our purposes null and NullSurfaceData are the same as * they represent a disposed surface. */ public static boolean isNull(SurfaceData sd) { if (sd == null || sd == NullSurfaceData.theInstance) { return true; } return false; } /** * Performs a copyarea within this surface. Returns * false if there is no algorithm to perform the copyarea * given the current settings of the SunGraphics2D. */ public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) { return false; } /** * Synchronously releases resources associated with this surface. */ public void flush() {} /** * Returns destination associated with this SurfaceData. This could be * either an Image or a Component; subclasses of SurfaceData are * responsible for returning the appropriate object. */ public abstract Object getDestination(); /** * Returns default scale factor of the destination surface. Scale factor * describes the mapping between virtual and physical coordinates of the * SurfaceData. If the scale is 2 then virtual pixel coordinates need to be * doubled for physical pixels. */ public int getDefaultScale() { return 1; } }