EglRectBlt.java

/*
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * 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.heifwriter;

import android.graphics.Bitmap;
import android.graphics.Rect;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

/**
 * This class represents a viewport-sized sprite that will be rendered with
 * a subrect from a texture.
 *
 * @hide
 */
public class EglRectBlt {
    private static final int SIZEOF_FLOAT = 4;

    /**
     * A "full" square, extending from -1 to +1 in both dimensions. When the
     * model/view/projection matrix is identity, this will exactly cover the viewport.
     */
    private static final float FULL_RECTANGLE_COORDS[] = {
            -1.0f, -1.0f,   // 0 bottom left
             1.0f, -1.0f,   // 1 bottom right
            -1.0f,  1.0f,   // 2 top left
             1.0f,  1.0f,   // 3 top right
    };

    private static final FloatBuffer FULL_RECTANGLE_BUF =
            createFloatBuffer(FULL_RECTANGLE_COORDS);

    private final float mTexCoords[] = new float[8];
    private final FloatBuffer mTexCoordArray = createFloatBuffer(mTexCoords);
    private final int mTexWidth;
    private final int mTexHeight;

    private Texture2dProgram mProgram;

    /**
     * Allocates a direct float buffer, and populates it with the float array data.
     */
    public static FloatBuffer createFloatBuffer(float[] coords) {
        // Allocate a direct ByteBuffer, using 4 bytes per float, and copy coords into it.
        ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * SIZEOF_FLOAT);
        bb.order(ByteOrder.nativeOrder());
        FloatBuffer fb = bb.asFloatBuffer();
        fb.put(coords);
        fb.position(0);
        return fb;
    }

    /**
     * Prepares the object.
     *
     * @param program The program to use. EglRectBlitter takes ownership, and will release
     *     the program when no longer needed.
     */
    public EglRectBlt(Texture2dProgram program, int texWidth, int texHeight) {
        mProgram = program;

        mTexWidth = texWidth;
        mTexHeight = texHeight;
    }

    /**
     * Releases resources.
     * <p>
     * This must be called with the appropriate EGL context current (i.e. the one that was
     * current when the constructor was called). If we're about to destroy the EGL context,
     * there's no value in having the caller make it current just to do this cleanup, so you
     * can pass a flag that will tell this function to skip any EGL-context-specific cleanup.
     */
    public void release(boolean doEglCleanup) {
        if (mProgram != null) {
            if (doEglCleanup) {
                mProgram.release();
            }
            mProgram = null;
        }
    }

    /**
     * Creates a texture object suitable for use with drawFrame().
     */
    public int createTextureObject() {
        return mProgram.createTextureObject();
    }

    /**
     * Load texture from a bitmap.
     */
    public void loadTexture(int texId, Bitmap bitmap) {
        mProgram.loadTexture(texId, bitmap);
    }

    /**
     * Draws a viewport-filling rect, texturing it with the specified texture object and rect.
     */
    public void copyRect(int textureId, float[] texMatrix, Rect texRect) {
        setTexRect(texRect);

        // Use the identity matrix for MVP so our 2x2 FULL_RECTANGLE covers the viewport.
        mProgram.draw(Texture2dProgram.IDENTITY_MATRIX, FULL_RECTANGLE_BUF, 0,
                4, 2, 2 * SIZEOF_FLOAT,
                texMatrix, mTexCoordArray, textureId, 2 * SIZEOF_FLOAT);
    }

    void setTexRect(Rect rect) {
        mTexCoords[0] = rect.left / (float)mTexWidth;
        mTexCoords[1] = 1.0f - rect.bottom / (float)mTexHeight;
        mTexCoords[2] = rect.right / (float)mTexWidth;
        mTexCoords[3] = 1.0f - rect.bottom / (float)mTexHeight;
        mTexCoords[4] = rect.left / (float)mTexWidth;
        mTexCoords[5] = 1.0f - rect.top / (float)mTexHeight;
        mTexCoords[6] = rect.right / (float)mTexWidth;
        mTexCoords[7] = 1.0f - rect.top / (float)mTexHeight;

        mTexCoordArray.put(mTexCoords);
        mTexCoordArray.position(0);
    }
}