/*
 * Copyright (c) 2005, 2016, 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.pipe;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Paint;
import java.awt.geom.AffineTransform;
import sun.java2d.pipe.hw.AccelSurface;
import sun.java2d.InvalidPipeException;
import sun.java2d.SunGraphics2D;
import sun.java2d.loops.XORComposite;
import static sun.java2d.pipe.BufferedOpCodes.*;
import static sun.java2d.pipe.BufferedRenderPipe.BYTES_PER_SPAN;

import java.lang.annotation.Native;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;

Base context class for managing state in a single-threaded rendering environment. Each state-setting operation (e.g. SET_COLOR) is added to the provided RenderQueue, which will be processed at a later time by a single thread. Note that the RenderQueue lock must be acquired before calling the validate() method (or any other method in this class). See the RenderQueue class comments for a sample usage scenario.
See Also:
  • RenderQueue
/** * Base context class for managing state in a single-threaded rendering * environment. Each state-setting operation (e.g. SET_COLOR) is added to * the provided RenderQueue, which will be processed at a later time by a * single thread. Note that the RenderQueue lock must be acquired before * calling the validate() method (or any other method in this class). See * the RenderQueue class comments for a sample usage scenario. * * @see RenderQueue */
public abstract class BufferedContext { /* * The following flags help the internals of validate() determine * the appropriate (meaning correct, or optimal) code path when * setting up the current context. The flags can be bitwise OR'd * together as needed. */
Indicates that no flags are needed; take all default code paths.
/** * Indicates that no flags are needed; take all default code paths. */
@Native public static final int NO_CONTEXT_FLAGS = (0 << 0);
Indicates that the source surface (or color value, if it is a simple rendering operation) is opaque (has an alpha value of 1.0). If this flag is present, it allows us to disable blending in certain situations in order to improve performance.
/** * Indicates that the source surface (or color value, if it is a simple * rendering operation) is opaque (has an alpha value of 1.0). If this * flag is present, it allows us to disable blending in certain * situations in order to improve performance. */
@Native public static final int SRC_IS_OPAQUE = (1 << 0);
Indicates that the operation uses an alpha mask, which may determine the code path that is used when setting up the current paint state.
/** * Indicates that the operation uses an alpha mask, which may determine * the code path that is used when setting up the current paint state. */
@Native public static final int USE_MASK = (1 << 1); protected RenderQueue rq; protected RenderBuffer buf;
This is a reference to the most recently validated BufferedContext. If this value is null, it means that there is no current context. It is provided here so that validate() only needs to do a quick reference check to see if the BufferedContext passed to that method is the same as the one we've cached here.
/** * This is a reference to the most recently validated BufferedContext. If * this value is null, it means that there is no current context. It is * provided here so that validate() only needs to do a quick reference * check to see if the BufferedContext passed to that method is the same * as the one we've cached here. */
protected static BufferedContext currentContext; private Reference<AccelSurface> validSrcDataRef = new WeakReference<>(null); private Reference<AccelSurface> validDstDataRef = new WeakReference<>(null); private Reference<Region> validClipRef = new WeakReference<>(null); private Reference<Composite> validCompRef = new WeakReference<>(null); private Reference<Paint> validPaintRef = new WeakReference<>(null); // renamed from isValidatedPaintAColor as part of a work around for 6764257 private boolean isValidatedPaintJustAColor; private int validatedRGB; private int validatedFlags; private boolean xformInUse; private AffineTransform transform; protected BufferedContext(RenderQueue rq) { this.rq = rq; this.buf = rq.getBuffer(); }
Fetches the BufferedContextContext associated with the dst. surface and validates the context using the given parameters. Most rendering operations will call this method first in order to set the necessary state before issuing rendering commands. Note: must be called while the RenderQueue lock is held. It's assumed that the type of surfaces has been checked by the Renderer
Throws:
  • InvalidPipeException – if either src or dest surface is not valid or lost
See Also:
/** * Fetches the BufferedContextContext associated with the dst. surface * and validates the context using the given parameters. Most rendering * operations will call this method first in order to set the necessary * state before issuing rendering commands. * * Note: must be called while the RenderQueue lock is held. * * It's assumed that the type of surfaces has been checked by the Renderer * * @throws InvalidPipeException if either src or dest surface is not valid * or lost * @see RenderQueue#lock * @see RenderQueue#unlock */
public static void validateContext(AccelSurface srcData, AccelSurface dstData, Region clip, Composite comp, AffineTransform xform, Paint paint, SunGraphics2D sg2d, int flags) { // assert rq.lock.isHeldByCurrentThread(); BufferedContext context = dstData.getContext(); context.validate(srcData, dstData, clip, comp, xform, paint, sg2d, flags); }
Fetches the BufferedContextassociated with the surface and disables all context state settings. Note: must be called while the RenderQueue lock is held. It's assumed that the type of surfaces has been checked by the Renderer
Throws:
  • InvalidPipeException – if the surface is not valid or lost
See Also:
/** * Fetches the BufferedContextassociated with the surface * and disables all context state settings. * * Note: must be called while the RenderQueue lock is held. * * It's assumed that the type of surfaces has been checked by the Renderer * * @throws InvalidPipeException if the surface is not valid * or lost * @see RenderQueue#lock * @see RenderQueue#unlock */
public static void validateContext(AccelSurface surface) { // assert rt.lock.isHeldByCurrentThread(); validateContext(surface, surface, null, null, null, null, null, NO_CONTEXT_FLAGS); }
Validates the given parameters against the current state for this context. If this context is not current, it will be made current for the given source and destination surfaces, and the viewport will be updated. Then each part of the context state (clip, composite, etc.) is checked against the previous value. If the value has changed since the last call to validate(), it will be updated accordingly. Note that the SunGraphics2D parameter is only used for the purposes of validating a (non-null) Paint parameter. In all other cases it is safe to pass a null SunGraphics2D and it will be ignored. Note: must be called while the RenderQueue lock is held. It's assumed that the type of surfaces has been checked by the Renderer
Throws:
  • InvalidPipeException – if either src or dest surface is not valid or lost
/** * Validates the given parameters against the current state for this * context. If this context is not current, it will be made current * for the given source and destination surfaces, and the viewport will * be updated. Then each part of the context state (clip, composite, * etc.) is checked against the previous value. If the value has changed * since the last call to validate(), it will be updated accordingly. * * Note that the SunGraphics2D parameter is only used for the purposes * of validating a (non-null) Paint parameter. In all other cases it * is safe to pass a null SunGraphics2D and it will be ignored. * * Note: must be called while the RenderQueue lock is held. * * It's assumed that the type of surfaces has been checked by the Renderer * * @throws InvalidPipeException if either src or dest surface is not valid * or lost */
public void validate(AccelSurface srcData, AccelSurface dstData, Region clip, Composite comp, AffineTransform xform, Paint paint, SunGraphics2D sg2d, int flags) { // assert rq.lock.isHeldByCurrentThread(); boolean updateClip = false; boolean updatePaint = false; if (!dstData.isValid() || dstData.isSurfaceLost() || srcData.isSurfaceLost()) { invalidateContext(); throw new InvalidPipeException("bounds changed or surface lost"); } if (paint instanceof Color) { // REMIND: not 30-bit friendly int newRGB = ((Color)paint).getRGB(); if (isValidatedPaintJustAColor) { if (newRGB != validatedRGB) { validatedRGB = newRGB; updatePaint = true; } } else { validatedRGB = newRGB; updatePaint = true; isValidatedPaintJustAColor = true; } } else if (validPaintRef.get() != paint) { updatePaint = true; // this should be set when we are switching from paint to color // in which case this condition will be true isValidatedPaintJustAColor = false; } final AccelSurface validatedSrcData = validSrcDataRef.get(); final AccelSurface validatedDstData = validDstDataRef.get(); if ((currentContext != this) || (srcData != validatedSrcData) || (dstData != validatedDstData)) { if (dstData != validatedDstData) { // the clip is dependent on the destination surface, so we // need to update it if we have a new destination surface updateClip = true; } if (paint == null) { // make sure we update the color state (otherwise, it might // not be updated if this is the first time the context // is being validated) updatePaint = true; } // update the current source and destination surfaces setSurfaces(srcData, dstData); currentContext = this; validSrcDataRef = new WeakReference<>(srcData); validDstDataRef = new WeakReference<>(dstData); } // validate clip final Region validatedClip = validClipRef.get(); if ((clip != validatedClip) || updateClip) { if (clip != null) { if (updateClip || validatedClip == null || !(validatedClip.isRectangular() && clip.isRectangular()) || ((clip.getLoX() != validatedClip.getLoX() || clip.getLoY() != validatedClip.getLoY() || clip.getHiX() != validatedClip.getHiX() || clip.getHiY() != validatedClip.getHiY()))) { setClip(clip); } } else { resetClip(); } validClipRef = new WeakReference<>(clip); } // validate composite (note that a change in the context flags // may require us to update the composite state, even if the // composite has not changed) if ((comp != validCompRef.get()) || (flags != validatedFlags)) { if (comp != null) { setComposite(comp, flags); } else { resetComposite(); } // the paint state is dependent on the composite state, so make // sure we update the color below updatePaint = true; validCompRef = new WeakReference<>(comp); validatedFlags = flags; } // validate transform boolean txChanged = false; if (xform == null) { if (xformInUse) { resetTransform(); xformInUse = false; txChanged = true; } else if (sg2d != null && !sg2d.transform.equals(transform)) { txChanged = true; } if (sg2d != null && txChanged) { transform = new AffineTransform(sg2d.transform); } } else { setTransform(xform); xformInUse = true; txChanged = true; } // non-Color paints may require paint revalidation if (!isValidatedPaintJustAColor && txChanged) { updatePaint = true; } // validate paint if (updatePaint) { if (paint != null) { BufferedPaints.setPaint(rq, sg2d, paint, flags); } else { BufferedPaints.resetPaint(rq); } validPaintRef = new WeakReference<>(paint); } // mark dstData dirty // REMIND: is this really needed now? we do it in SunGraphics2D.. dstData.markDirty(); }
Invalidates the surfaces associated with this context. This is useful when the context is no longer needed, and we want to break the chain caused by these surface references. Note: must be called while the RenderQueue lock is held.
See Also:
/** * Invalidates the surfaces associated with this context. This is * useful when the context is no longer needed, and we want to break * the chain caused by these surface references. * * Note: must be called while the RenderQueue lock is held. * * @see RenderQueue#lock * @see RenderQueue#unlock */
private void invalidateSurfaces() { validSrcDataRef.clear(); validDstDataRef.clear(); } private void setSurfaces(AccelSurface srcData, AccelSurface dstData) { // assert rq.lock.isHeldByCurrentThread(); rq.ensureCapacityAndAlignment(20, 4); buf.putInt(SET_SURFACES); buf.putLong(srcData.getNativeOps()); buf.putLong(dstData.getNativeOps()); } private void resetClip() { // assert rq.lock.isHeldByCurrentThread(); rq.ensureCapacity(4); buf.putInt(RESET_CLIP); } private void setClip(Region clip) { // assert rq.lock.isHeldByCurrentThread(); if (clip.isRectangular()) { rq.ensureCapacity(20); buf.putInt(SET_RECT_CLIP); buf.putInt(clip.getLoX()).putInt(clip.getLoY()); buf.putInt(clip.getHiX()).putInt(clip.getHiY()); } else { rq.ensureCapacity(28); // so that we have room for at least a span buf.putInt(BEGIN_SHAPE_CLIP); buf.putInt(SET_SHAPE_CLIP_SPANS); // include a placeholder for the span count int countIndex = buf.position(); buf.putInt(0); int spanCount = 0; int remainingSpans = buf.remaining() / BYTES_PER_SPAN; int span[] = new int[4]; SpanIterator si = clip.getSpanIterator(); while (si.nextSpan(span)) { if (remainingSpans == 0) { buf.putInt(countIndex, spanCount); rq.flushNow(); buf.putInt(SET_SHAPE_CLIP_SPANS); countIndex = buf.position(); buf.putInt(0); spanCount = 0; remainingSpans = buf.remaining() / BYTES_PER_SPAN; } buf.putInt(span[0]); // x1 buf.putInt(span[1]); // y1 buf.putInt(span[2]); // x2 buf.putInt(span[3]); // y2 spanCount++; remainingSpans--; } buf.putInt(countIndex, spanCount); rq.ensureCapacity(4); buf.putInt(END_SHAPE_CLIP); } } private void resetComposite() { // assert rq.lock.isHeldByCurrentThread(); rq.ensureCapacity(4); buf.putInt(RESET_COMPOSITE); } private void setComposite(Composite comp, int flags) { // assert rq.lock.isHeldByCurrentThread(); if (comp instanceof AlphaComposite) { AlphaComposite ac = (AlphaComposite)comp; rq.ensureCapacity(16); buf.putInt(SET_ALPHA_COMPOSITE); buf.putInt(ac.getRule()); buf.putFloat(ac.getAlpha()); buf.putInt(flags); } else if (comp instanceof XORComposite) { int xorPixel = ((XORComposite)comp).getXorPixel(); rq.ensureCapacity(8); buf.putInt(SET_XOR_COMPOSITE); buf.putInt(xorPixel); } else { throw new InternalError("not yet implemented"); } } private void resetTransform() { // assert rq.lock.isHeldByCurrentThread(); rq.ensureCapacity(4); buf.putInt(RESET_TRANSFORM); } private void setTransform(AffineTransform xform) { // assert rq.lock.isHeldByCurrentThread(); rq.ensureCapacityAndAlignment(52, 4); buf.putInt(SET_TRANSFORM); buf.putDouble(xform.getScaleX()); buf.putDouble(xform.getShearY()); buf.putDouble(xform.getShearX()); buf.putDouble(xform.getScaleY()); buf.putDouble(xform.getTranslateX()); buf.putDouble(xform.getTranslateY()); }
Resets this context's surfaces and all attributes. Note: must be called while the RenderQueue lock is held.
See Also:
/** * Resets this context's surfaces and all attributes. * * Note: must be called while the RenderQueue lock is held. * * @see RenderQueue#lock * @see RenderQueue#unlock */
public void invalidateContext() { resetTransform(); resetComposite(); resetClip(); BufferedPaints.resetPaint(rq); invalidateSurfaces(); validCompRef.clear(); validClipRef.clear(); validPaintRef.clear(); isValidatedPaintJustAColor = false; xformInUse = false; }
Returns a singleton RenderQueue object used by the rendering pipeline.
See Also:
Returns:a render queue
/** * Returns a singleton {@code RenderQueue} object used by the rendering * pipeline. * * @return a render queue * @see RenderQueue */
public abstract RenderQueue getRenderQueue();
Saves the the state of this context. It may reset the current context. Note: must be called while the RenderQueue lock is held.
See Also:
/** * Saves the the state of this context. * It may reset the current context. * * Note: must be called while the RenderQueue lock is held. * * @see RenderQueue#lock * @see RenderQueue#unlock */
public abstract void saveState();
Restores the native state of this context. It may reset the current context. Note: must be called while the RenderQueue lock is held.
See Also:
/** * Restores the native state of this context. * It may reset the current context. * * Note: must be called while the RenderQueue lock is held. * * @see RenderQueue#lock * @see RenderQueue#unlock */
public abstract void restoreState(); }