GlProgramCopy.java

/*
 * Copyright 2023 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.effects.opengl;

import static androidx.camera.effects.opengl.Utils.checkGlErrorOrThrow;
import static androidx.camera.effects.opengl.Utils.createFbo;
import static androidx.camera.effects.opengl.Utils.drawArrays;

import android.opengl.GLES11Ext;
import android.opengl.GLES20;

import androidx.annotation.RequiresApi;

/**
 * A GL program that copies the input texture to the given 2D texture.
 *
 * <p>It assumes that the output texture has the same size as the input, so no transformation
 * needed.
 */
@RequiresApi(21)
class GlProgramCopy extends GlProgram {

    private static final String VERTEX_SHADER = "attribute vec4 " + POSITION_ATTRIBUTE + ";\n"
            + "attribute vec4 " + TEXTURE_ATTRIBUTE + ";\n"
            + "varying vec2 " + TEXTURE_COORDINATES + ";\n"
            + "void main() {\n"
            + "    gl_Position = " + POSITION_ATTRIBUTE + ";\n"
            + "    " + TEXTURE_COORDINATES + "= " + TEXTURE_ATTRIBUTE + ".xy;\n"
            + "}";

    private static final String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n"
            + "precision mediump float;\n"
            + "varying vec2 " + TEXTURE_COORDINATES + ";\n"
            + "uniform samplerExternalOES " + INPUT_SAMPLER + ";\n"
            + "void main() {\n"
            + "    gl_FragColor = texture2D(" + INPUT_SAMPLER + ", "
            + TEXTURE_COORDINATES + ");\n"
            + "}";

    // A FBO object for attaching the output texture.
    private int mFbo = -1;

    GlProgramCopy() {
        super(VERTEX_SHADER, FRAGMENT_SHADER);
    }

    @Override
    protected void configure() {
        super.configure();
        // Create a FBO for attaching the output texture.
        mFbo = createFbo();
    }

    @Override
    protected void release() {
        super.release();
        // Delete the FBO.
        if (mFbo != -1) {
            GLES20.glDeleteFramebuffers(1, new int[]{mFbo}, 0);
            checkGlErrorOrThrow("glDeleteFramebuffers");
            mFbo = -1;
        }
    }

    /**
     * Copies the input texture to the output texture.
     *
     * @param inputTextureId  the input texture ID. Usually this is an external texture.
     * @param outputTextureId the output texture ID. This must be a 2D texture.
     * @param outputWidth     the width of the output textures.
     * @param outputHeight    the height of the output textures.
     */
    void draw(int inputTextureId, int outputTextureId, int outputWidth, int outputHeight) {
        use();

        // Bind external texture to TEXTURE0 as input texture.
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        checkGlErrorOrThrow("glActiveTexture");
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, inputTextureId);
        checkGlErrorOrThrow("glBindTexture");

        // Bind FBO and attach the output texture.
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFbo);
        checkGlErrorOrThrow("glBindFramebuffer");
        GLES20.glFramebufferTexture2D(
                GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
                GLES20.GL_TEXTURE_2D, outputTextureId, 0
        );
        checkGlErrorOrThrow("glFramebufferTexture2D");

        // Copy the input texture to the output texture
        drawArrays(outputWidth, outputHeight);

        // Unbind FBO.
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
        checkGlErrorOrThrow("glBindFramebuffer");
    }
}