CameraEffect.java

/*
 * Copyright 2022 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 androidx.camera.core;

import static androidx.core.util.Preconditions.checkState;

import android.os.Build;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;

/**
 * A CameraX post-processing effects.
 *
 * <p>A {@link CameraEffect} class contains two types of information, the processor and the
 * configuration.
 * <ul>
 * <li> The processor is an implementation of a CameraX interface e.g. {@link SurfaceProcessor}.
 * It consumes original camera frames from CameraX, applies the effect, and returns the processed
 * frames back to CameraX.
 * <li> The configuration provides information on how the processor should be injected into the
 * CameraX pipeline. For example, the target {@link UseCase}s where the effect should be applied.
 * </ul>
 *
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CameraEffect {

    /**
     * Bitmask options for the effect targets.
     *
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    @IntDef(flag = true, value = {PREVIEW, VIDEO_CAPTURE, IMAGE_CAPTURE})
    public @interface Targets {
    }

    /**
     * Bitmask option to indicate that CameraX should apply this effect to {@link Preview}.
     */
    public static final int PREVIEW = 1;

    /**
     * Bitmask option to indicate that CameraX should apply this effect to {@code VideoCapture}.
     */
    public static final int VIDEO_CAPTURE = 1 << 1;

    /**
     * Bitmask option to indicate that CameraX should apply this effect to {@link ImageCapture}.
     */
    public static final int IMAGE_CAPTURE = 1 << 2;

    @Targets
    private final int mTargets;
    @NonNull
    private final Executor mProcessorExecutor;
    @Nullable
    private final SurfaceProcessor mSurfaceProcessor;
    @Nullable
    private final ImageProcessor mImageProcessor;

    /**
     * @param targets           the target {@link UseCase} to which this effect should be applied.
     * @param processorExecutor the {@link Executor} on which the processor will be invoked.
     * @param imageProcessor    a {@link ImageProcessor} implementation.
     */
    protected CameraEffect(
            @Targets int targets,
            @NonNull Executor processorExecutor,
            @NonNull ImageProcessor imageProcessor) {
        mTargets = targets;
        mProcessorExecutor = processorExecutor;
        mSurfaceProcessor = null;
        mImageProcessor = imageProcessor;
    }

    /**
     * @param targets           the target {@link UseCase} to which this effect should be applied.
     * @param processorExecutor the {@link Executor} on which the processor will be invoked.
     * @param surfaceProcessor  a {@link SurfaceProcessor} implementation.
     */
    protected CameraEffect(
            @Targets int targets,
            @NonNull Executor processorExecutor,
            @NonNull SurfaceProcessor surfaceProcessor) {
        mTargets = targets;
        mProcessorExecutor = processorExecutor;
        mSurfaceProcessor = surfaceProcessor;
        mImageProcessor = null;
    }

    /**
     * Ges the target {@link UseCase}s of this effect.
     */
    @Targets
    public int getTargets() {
        return mTargets;
    }

    /**
     * Gets the {@link Executor} for calling processors.
     *
     * <p>This method returns the value set via {@link Builder#setSurfaceProcessor}.
     */
    @NonNull
    public Executor getProcessorExecutor() {
        return mProcessorExecutor;
    }

    /**
     * Gets the {@link SurfaceProcessor} associated with this effect.
     *
     * <p>This method returns the value set via {@link Builder#setSurfaceProcessor}.
     */
    @Nullable
    public SurfaceProcessor getSurfaceProcessor() {
        return mSurfaceProcessor;
    }

    /**
     * Gets the {@link ImageProcessor} associated with this effect.
     *
     * <p>This method returns the value set via {@link Builder#setImageProcessor}.
     */
    @Nullable
    public ImageProcessor getImageProcessor() {
        return mImageProcessor;
    }

    /**
     * Builder class for {@link CameraEffect}.
     */
    public static class Builder {
        @Targets
        private final int mTargets;
        @Nullable
        private Executor mProcessorExecutor;
        @Nullable
        private SurfaceProcessor mSurfaceProcessor;
        @Nullable
        private ImageProcessor mImageProcessor;

        /**
         * @param targets the target {@link UseCase} of the Effect. e.g. if the
         *                value is {@link #PREVIEW}, CameraX will apply the effect to
         *                {@link Preview}.
         */
        public Builder(@Targets int targets) {
            mTargets = targets;
        }

        /**
         * Sets a {@link SurfaceProcessor} for the effect.
         *
         * <p>Once the effect is active, CameraX will send original camera frames to the
         * {@link SurfaceProcessor} on the {@link Executor}, and deliver the processed output
         * frames to the app.
         *
         * <p>Only one processor can be set via {@code #setImageProcessor()} /
         * {@code #setSurfaceProcessor}, or the {@link #build()} call will throw error.
         *
         * @param executor  on which the {@link SurfaceProcessor} will be invoked.
         * @param processor the post processor to be injected into CameraX pipeline.
         */
        @NonNull
        public Builder setSurfaceProcessor(@NonNull Executor executor,
                @NonNull SurfaceProcessor processor) {
            mProcessorExecutor = executor;
            mSurfaceProcessor = processor;
            return this;
        }

        /**
         * Sets a {@link ImageProcessor} for the effect.
         *
         * <p>Once the effect is active, CameraX will send original camera frames to the
         * {@link ImageProcessor} on the {@link Executor}, and deliver the processed output
         * frames to the app.
         *
         * <p>Only one processor can be set via {@code #setImageProcessor()} /
         * {@code #setSurfaceProcessor}, or the {@link #build()} call will throw error.
         *
         * @param executor  on which the {@link ImageProcessor} will be invoked.
         * @param processor the post processor to be injected into CameraX pipeline.
         */
        @NonNull
        public Builder setImageProcessor(@NonNull Executor executor,
                @NonNull ImageProcessor processor) {
            mProcessorExecutor = executor;
            mImageProcessor = processor;
            return this;
        }

        /**
         * Builds a {@link CameraEffect} instance.
         *
         * <p>CameraX supports a selected set of configuration/processor combinations. This method
         * throws a {@link UnsupportedOperationException} if the current combination is not
         * supported.
         */
        @NonNull
        public CameraEffect build() {
            checkState(mProcessorExecutor != null, "Must have a executor");
            checkState(mImageProcessor != null ^ mSurfaceProcessor != null,
                    "Must have one and only one processor");
            if (mSurfaceProcessor != null) {
                return new CameraEffect(mTargets, mProcessorExecutor, mSurfaceProcessor);
            } else {
                return new CameraEffect(mTargets, mProcessorExecutor, mImageProcessor);
            }
        }
    }
}