All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.scenario.effect.impl.Renderer Maven / Gradle / Ivy

There is a newer version: 24-ea+15
Show newest version
/*
 * Copyright (c) 2008, 2021, 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 com.sun.scenario.effect.impl;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.sun.javafx.geom.Rectangle;
import com.sun.javafx.geom.transform.Affine2D;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.scenario.effect.Effect;
import com.sun.scenario.effect.Effect.AccelType;
import com.sun.scenario.effect.FilterContext;
import com.sun.scenario.effect.Filterable;
import com.sun.scenario.effect.FloatMap;
import com.sun.scenario.effect.ImageData;
import com.sun.scenario.effect.LockableResource;

public abstract class Renderer {

    /**
     * Enumeration describing the lifecycle states of the renderer.
     * When the renderer is created, it will either start in the {@code OK}
     * state, or it will start in the {@code NOTREADY} state until
     * the first time it is used during rendering, then transition
     * to the {@code OK} state.
     *
     * It could become {@code LOST} at some point. This may happen for
     * example if the renderer is susceptible to display changes.
     * 

* When the renderer is in the {@code LOST} state it can't be used * for rendering, instead a {@link #getBackupRenderer() backup renderer} * must be used. *

* Sometime later the renderer could enter the {@code DISPOSED} state, * at which point it will be removed from the cache and a new renderer * will be created for the particular context. *

* Thus the lifecycle of a renderer is: * [{@code NOTREADY} =>] {@code OK} [=> {@code LOST} [=> {@code DISPOSED}]] */ public static enum RendererState { /** * Renderer is not ready; it might be able to be used for rendering. */ NOTREADY, /** * Renderer can be used for rendering. */ OK, /** * Renderer is lost, a backup renderer must be used. */ LOST, /** * Renderer is disposed, it is no longer usable and must be replaced. */ DISPOSED } public static final String rootPkg = "com.sun.scenario.effect"; private static final Map rendererMap = new HashMap(1); private Map peerCache = Collections.synchronizedMap(new HashMap(5)); private final ImagePool imagePool; @SuppressWarnings("removal") protected static final boolean verbose = AccessController.doPrivileged( (PrivilegedAction) () -> Boolean.getBoolean("decora.verbose")); protected Renderer() { this.imagePool = new ImagePool(); } /** * Returns the {@link AccelType} used by default for peers of this renderer. * * Note that the Renderer may specialize in peers of this type, and * it may create them in general by default, but the renderers all * look for an Intrinsic peer for a given operation as well so the * actual peer implementaiton for a given effect may sometimes differ * from this {@code AccelType}. Care should be taken if the actual * {@code AccelType} for a specific operation is needed, then the * {@link EffectPeer#getAccelType()} should be consulted directly * in those cases. * * @return the {@code AccelType} used by typical peers of this renderer */ public abstract AccelType getAccelType(); public abstract int getCompatibleWidth(int w); public abstract int getCompatibleHeight(int h); public abstract PoolFilterable createCompatibleImage(int w, int h); public PoolFilterable getCompatibleImage(int w, int h) { return imagePool.checkOut(this, w, h); } public void releaseCompatibleImage(Filterable image) { if (image instanceof PoolFilterable) { ImagePool pool = ((PoolFilterable) image).getImagePool(); if (pool != null) { pool.checkIn((PoolFilterable) image); return; } // } else { // Error? } image.unlock(); } /** * This is a temporary workaround for a PowerVR SGX issue. See * ImagePool for more details. */ public void releasePurgatory() { imagePool.releasePurgatory(); } /** * Mainly used by {@code ImagePool} for the purpose of clearing * an image before handing it back to the user. * * @param image the image to be cleared */ public abstract void clearImage(Filterable image); /** * Mainly used by the {@code Identity} effect for the purpose of * creating a cached {@code ImageData} from the given platform-specific * image (e.g. a {@code BufferedImage} wrapped in a {@code J2DImage}). * * @param fctx the filter context * @param platformImage the platform-specific source image to be copied * into the new {@code ImageData} object * @return a new {@code ImageData} */ public abstract ImageData createImageData(FilterContext fctx, Filterable src); public ImageData transform(FilterContext fctx, ImageData img, int xpow2scales, int ypow2scales) { if (!img.getTransform().isIdentity()) { throw new InternalError("transform by powers of 2 requires untransformed source"); } if ((xpow2scales | ypow2scales) == 0) { return img; } Affine2D at = new Affine2D(); // Any amount of upscaling and up to 1 level of downscaling // can be handled by the filters themselves... while (xpow2scales < -1 || ypow2scales < -1) { Rectangle origbounds = img.getUntransformedBounds(); Rectangle newbounds = new Rectangle(origbounds); double xscale = 1.0; double yscale = 1.0; if (xpow2scales < 0) { // To avoid loss, only scale down one step at a time xscale = 0.5; newbounds.width = (origbounds.width + 1) / 2; newbounds.x /= 2; xpow2scales++; } if (ypow2scales < 0) { // To avoid loss, only scale down one step at a time yscale = 0.5; newbounds.height = (origbounds.height + 1) / 2; newbounds.y /= 2; ypow2scales++; } at.setToScale(xscale, yscale); img = transform(fctx, img, at, origbounds, newbounds); } if ((xpow2scales | ypow2scales) != 0) { // assert xscale >= -1 and yscale >= -1 double xscale = (xpow2scales < 0) ? 0.5 : 1 << xpow2scales; double yscale = (ypow2scales < 0) ? 0.5 : 1 << ypow2scales; at.setToScale(xscale, yscale); img = img.transform(at); } return img; } public abstract Filterable transform(FilterContext fctx, Filterable original, BaseTransform transform, Rectangle origBounds, Rectangle xformBounds); public abstract ImageData transform(FilterContext fctx, ImageData original, BaseTransform transform, Rectangle origBounds, Rectangle xformBounds); // NOTE: these two methods are only relevant to HW codepaths; should // find a way to push them down a level... public LockableResource createFloatTexture(int w, int h) { throw new InternalError(); } public void updateFloatTexture(LockableResource texture, FloatMap map) { throw new InternalError(); } /** * Returns a (cached) instance of peer given the context, name and unroll * count. * * @param fctx filter context - same as this renderer's context * @param name not-unrolled name of the peer * @param unrollCount * @return cached peer for this name and unroll count */ public final synchronized EffectPeer getPeerInstance(FilterContext fctx, String name, int unrollCount) { // first look for a previously cached peer using only the base name // (e.g. GaussianBlur); software peers do not (currently) have // unrolled loops, so this step should locate those... EffectPeer peer = peerCache.get(name); if (peer != null) { return peer; } // failing that, if there is a positive unrollCount, we attempt // to find a previously cached hardware peer for that unrollCount if (unrollCount > 0) { peer = peerCache.get(name + "_" + unrollCount); if (peer != null) { return peer; } } peer = createPeer(fctx, name, unrollCount); if (peer == null) { throw new RuntimeException("Could not create peer " + name + " for renderer " + this); } // use the peer's unique name as the hashmap key peerCache.put(peer.getUniqueName(), peer); return peer; } /** * Returns this renderer's current state. * * @return the state * @see RendererState */ public abstract RendererState getRendererState(); /** * Creates a new peer given the context, name and unroll count. * * @param fctx context shared with the renderer * @param name of the peer * @param unrollCount unroll count * @return new peer */ protected abstract EffectPeer createPeer(FilterContext fctx, String name, int unrollCount); /** * Returns current cache of peers. */ protected Collection getPeers() { return peerCache.values(); } /** * This method can be used by subclasses to create a backup renderer, * either a SW (Java) renderer or an SSE (native) renderer, depending * on what is available. * * @return an instance of Renderer that uses CPU filtering */ protected static Renderer getSoftwareRenderer() { return RendererFactory.getSoftwareRenderer(); } /** * Returns an instance of backup renderer to be used if this renderer * is in {@code LOST} state. * * @return backup renderer */ protected abstract Renderer getBackupRenderer(); /** * Returns a {@code Renderer} instance that is most appropriate * for the given size of the source data. The default implementation * simply returns "this" renderer, but subclasses may override this * method and return a different renderer depending on the size of * the operation. For example, a GPU-based renderer may wish to * return a software renderer for small-sized operations (because of * lower overhead, etc). * * @param approxW approximate input width * @param approxH approximate input height * @return the {@code Renderer} best suited for this size */ protected Renderer getRendererForSize(Effect effect, int approxW, int approxH) { return this; } /** * Returns a renderer associated with given filter context based on the * environment and flags set. * * Renderers are per filter context cached. * * @param fctx context to create the renderer for * @return renderer */ public static synchronized Renderer getRenderer(FilterContext fctx) { if (fctx == null) { throw new IllegalArgumentException("FilterContext must be non-null"); } Renderer r = rendererMap.get(fctx); if (r != null) { if (r.getRendererState() == RendererState.NOTREADY) { return r; } if (r.getRendererState() == RendererState.OK) { return r; } if (r.getRendererState() == RendererState.LOST) { // use the backup while the renderer is in lost state, until // it is disposed (or forever if it can't be disposed/reset) // Note: we don't add it to the cache to prevent permanent // association of the backup renderer and this filter context. return r.getBackupRenderer(); } if (r.getRendererState() == RendererState.DISPOSED) { r = null; // we remove disposed renderers below instead of here to cover // cases where we never use a context which the disposed // renderer is associated with } } if (r == null) { // clean up all disposed renderers first Collection renderers = rendererMap.values(); for (Iterator iter = renderers.iterator(); iter.hasNext();) { Renderer ren = iter.next(); if (ren.getRendererState() == RendererState.DISPOSED) { ren.imagePool.dispose(); iter.remove(); } } r = RendererFactory.createRenderer(fctx); if (r == null) { throw new RuntimeException("Error creating a Renderer"); } else { if (verbose) { String klassName = r.getClass().getName(); String rname = klassName.substring(klassName.lastIndexOf(".")+1); Object screen = fctx.getReferent(); System.out.println("Created " + rname + " (AccelType=" + r.getAccelType() + ") for " + screen); } } rendererMap.put(fctx, r); } return r; } /** * Returns a renderer that is most optimal for the approximate size * of the filtering operation. * * @param fctx context to create the renderer for * @param effect uses in the rendering * @param approxW approximate input width * @param approxH approximate input height * @return renderer */ public static Renderer getRenderer(FilterContext fctx, Effect effect, int approxW, int approxH) { return getRenderer(fctx).getRendererForSize(effect, approxW, approxH); } /** * Determines whether the passed {@code ImageData} is compatible with this * renderer (that is, if it can be used as a input source for this * renderer's peers). * * @param id {@code ImageData} to be checked * @return true if this image data is compatible, false otherwise */ public abstract boolean isImageDataCompatible(ImageData id); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy