/* * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.camera2.cts.rs; import static android.hardware.camera2.cts.helpers.Preconditions.*; import android.hardware.camera2.cts.helpers.UncheckedCloseable; import android.renderscript.Allocation; import android.renderscript.RenderScript; import android.util.Log; import java.util.HashMap; /** * Base class for all renderscript script abstractions. * *

Each script has exactly one input and one output allocation, and is able to execute * one {@link android.renderscript.Script} script file.

* *

Each script owns it's input allocation, but not the output allocation.

* *

Subclasses of this class must implement exactly one of two constructors: *

* * @param A concrete subclass of {@link android.renderscript.Script} */ public abstract class Script implements UncheckedCloseable { /** * A type-safe heterogenous parameter map for script parameters. * * @param A concrete subclass of {@link Script}. */ public static class ParameterMap> { private final HashMap, Object> mParameterMap = new HashMap, Object>(); /** * Create a new parameter map with 0 parameters.

*/ public ParameterMap() {} /** * Get the value associated with the given parameter key. * * @param parameter A type-safe key corresponding to a parameter. * * @return The value, or {@code null} if none was set. * * @param The type of the value * * @throws NullPointerException if parameter was {@code null} */ @SuppressWarnings("unchecked") public T get(Script.ScriptParameter parameter) { checkNotNull("parameter", parameter); return (T) mParameterMap.get(parameter); } /** * Sets the value associated with the given parameter key. * * @param parameter A type-safe key corresponding to a parameter. * @param value The value * * @param The type of the value * * @throws NullPointerException if parameter was {@code null} * @throws NullPointerException if value was {@code null} */ public void set(Script.ScriptParameter parameter, T value) { checkNotNull("parameter", parameter); checkNotNull("value", value); if (!parameter.getValueClass().isInstance(value)) { throw new IllegalArgumentException( "Runtime type mismatch between " + parameter + " and value " + value); } mParameterMap.put(parameter, value); } /** * Whether or not at least one parameter has been {@link #set}. * * @return true if there is at least one element in the map */ public boolean isEmpty() { return mParameterMap.isEmpty(); } /** * Check if the parameter has been {@link #set} to a value. * * @param parameter A type-safe key corresponding to a parameter. * @return true if there is a value corresponding to this parameter, false otherwise. */ public boolean contains(Script.ScriptParameter parameter) { checkNotNull("parameter", parameter); return mParameterMap.containsKey(parameter); } } /** * A type-safe parameter key to be used with {@link ParameterMap}. * * @param A concrete subclass of {@link Script}. * @param The type of the value that the parameter holds. */ public static class ScriptParameter, K> { private final Class mScriptClass; private final Class mValueClass; ScriptParameter(Class jClass, Class kClass) { checkNotNull("jClass", jClass); checkNotNull("kClass", kClass); mScriptClass = jClass; mValueClass = kClass; } /** * Get the runtime class associated with the value. */ public Class getValueClass() { return mValueClass; } /** * Compare with another object. * *

Two script parameters are considered equal only if their script class and value * class are both equal.

*/ @SuppressWarnings("unchecked") @Override public boolean equals(Object other) { if (other instanceof ScriptParameter) { ScriptParameter otherParam = (ScriptParameter) other; return mScriptClass.equals(otherParam.mScriptClass) && mValueClass.equals(otherParam.mValueClass); } return false; } /** * Gets the hash code for this object. */ @Override public int hashCode() { return mScriptClass.hashCode() ^ mValueClass.hashCode(); } } private static final String TAG = "Script"; private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); protected final AllocationCache mCache = RenderScriptSingleton.getCache(); protected final RenderScript mRS = RenderScriptSingleton.getRS(); protected final AllocationInfo mInputInfo; protected final AllocationInfo mOutputInfo; protected Allocation mOutputAllocation; protected Allocation mInputAllocation; protected final T mScript; private boolean mClosed = false; /** * Gets the {@link AllocationInfo info} associated with this script's input. * * @return A non-{@code null} {@link AllocationInfo} object. * * @throws IllegalStateException If the script has already been {@link #close closed}. */ public AllocationInfo getInputInfo() { checkNotClosed(); return mInputInfo; } /** * Gets the {@link AllocationInfo info} associated with this script's output. * * @return A non-{@code null} {@link AllocationInfo} object. * * @throws IllegalStateException If the script has already been {@link #close closed}. */ public AllocationInfo getOutputInfo() { checkNotClosed(); return mOutputInfo; } /** * Set the input. * *

Must be called before executing any scripts.

* * @throws IllegalStateException If the script has already been {@link #close closed}. */ void setInput(Allocation allocation) { checkNotClosed(); checkNotNull("allocation", allocation); checkEquals("allocation info", AllocationInfo.newInstance(allocation), "input info", mInputInfo); // Scripts own the input, so return old input to cache if the input changes if (mInputAllocation != allocation) { mCache.returnToCacheIfNotNull(mInputAllocation); } mInputAllocation = allocation; updateScriptInput(); } protected abstract void updateScriptInput(); /** * Set the output. * *

Must be called before executing any scripts.

* * @throws IllegalStateException If the script has already been {@link #close closed}. */ void setOutput(Allocation allocation) { checkNotClosed(); checkNotNull("allocation", allocation); checkEquals("allocation info", AllocationInfo.newInstance(allocation), "output info", mOutputInfo); // Scripts do not own the output, simply set a reference to the new one. mOutputAllocation = allocation; } protected Script(AllocationInfo inputInfo, AllocationInfo outputInfo, T rsScript) { checkNotNull("inputInfo", inputInfo); checkNotNull("outputInfo", outputInfo); checkNotNull("rsScript", rsScript); mInputInfo = inputInfo; mOutputInfo = outputInfo; mScript = rsScript; if (VERBOSE) { Log.v(TAG, String.format("%s - inputInfo = %s, outputInfo = %s, rsScript = %s", getName(), inputInfo, outputInfo, rsScript)); } } /** * Get the {@link Allocation} associated with this script's input.

* * @return The input {@link Allocation}, which is never {@code null}. * * @throws IllegalStateException If the script has already been {@link #close closed}. */ public Allocation getInput() { checkNotClosed(); return mInputAllocation; } /** * Get the {@link Allocation} associated with this script's output.

* * @return The output {@link Allocation}, which is never {@code null}. * * @throws IllegalStateException If the script has already been {@link #close closed}. */ public Allocation getOutput() { checkNotClosed(); return mOutputAllocation; } /** * Execute the script's kernel against the input/output {@link Allocation allocations}. * *

Once this is complete, the output will have the new data available (for either * the next script, or to read out with a copy).

* * @throws IllegalStateException If the script has already been {@link #close closed}. */ public void execute() { checkNotClosed(); if (mInputAllocation == null || mOutputAllocation == null) { throw new IllegalStateException("Both inputs and outputs must have been set"); } executeUnchecked(); } /** * Get the name of this script. * *

The name is the short hand name of the concrete class backing this script.

* *

This method works even if the script has already been {@link #close closed}.

* * @return A string representing the script name. */ public String getName() { return getClass().getSimpleName(); } protected abstract void executeUnchecked(); protected void checkNotClosed() { if (mClosed) { throw new IllegalStateException("Script has been closed"); } } /** * Destroy the underlying script object and return the input allocation back to the * {@link AllocationCache cache}. * *

This method has no effect if called more than once.

*/ @Override public void close() { if (mClosed) return; // Scripts own the input allocation. They do NOT own outputs. mCache.returnToCacheIfNotNull(mInputAllocation); mScript.destroy(); mClosed = true; } @Override protected void finalize() throws Throwable { try { close(); } finally { super.finalize(); } } protected static RenderScript getRS() { return RenderScriptSingleton.getRS(); } }