MatrixExt.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.impl.utils;

import android.opengl.Matrix;

import androidx.annotation.NonNull;

import java.util.Locale;

/**
 * Utility class to extend the {@link Matrix}.
 */
public final class MatrixExt {
    private static final float[] sTemp = new float[16];

    // Prevent instantiation.
    private MatrixExt() {
    }

    /**
     * Set the matrix to rotate by the specified number of degrees, with a pivot point at
     * (px, py).
     *
     * <p>The pivot point is the coordinate that should remain unchanged by the specified
     * transformation.
     *
     * @param matrix the matrix to rotate
     * @param degrees the rotation degrees
     * @param px px of pivot point at (px, py)
     * @param py py of pivot point at (px, py)
     */
    public static void setRotate(@NonNull float[] matrix, float degrees, float px, float py) {
        Matrix.setIdentityM(matrix, 0);
        preRotate(matrix, degrees, px, py);
    }

    /**
     * Preconcats the matrix with the specified rotation. M' = M * R(degrees, px, py)
     *
     * <p>The pivot point is the coordinate that should remain unchanged by the specified
     * transformation.
     *
     * @param matrix the matrix to rotate
     * @param degrees the rotation degrees
     * @param px px of pivot point at (px, py)
     * @param py py of pivot point at (px, py)
     */
    public static void preRotate(@NonNull float[] matrix, float degrees, float px, float py) {
        normalize(matrix, px, py);
        Matrix.rotateM(matrix, 0, degrees, 0, 0, 1);
        denormalize(matrix, px, py);
    }

    /**
     * Postconcats the matrix with the specified rotation. M' = R(degrees, px, py) * M
     *
     * <p>The pivot point is the coordinate that should remain unchanged by the specified
     * transformation.
     *
     * @param matrix the matrix to rotate
     * @param degrees the rotation degrees
     * @param px px of pivot point at (px, py)
     * @param py py of pivot point at (px, py)
     */
    public static void postRotate(@NonNull float[] matrix, float degrees, float px, float py) {
        synchronized (sTemp) {
            Matrix.setIdentityM(sTemp, 0);
            normalize(sTemp, px, py);
            Matrix.rotateM(sTemp, 0, degrees, 0, 0, 1);
            denormalize(sTemp, px, py);
            Matrix.multiplyMM(matrix, 0, sTemp, 0, matrix, 0);
        }
    }

    /**
     * Converts to a well-formed matrix for debugging.
     *
     * <p>Get the first 16 floats from the offset.
     *
     * @param matrix the matrix to convert
     * @param offset the offset of the matrix
     */
    @NonNull
    public static String toString(@NonNull float[] matrix, int offset) {
        return String.format(Locale.US, "Matrix:\n"
                        + "%2.1f %2.1f %2.1f %2.1f\n"
                        + "%2.1f %2.1f %2.1f %2.1f\n"
                        + "%2.1f %2.1f %2.1f %2.1f\n"
                        + "%2.1f %2.1f %2.1f %2.1f",
                matrix[offset], matrix[offset + 4], matrix[offset + 8], matrix[offset + 12],
                matrix[offset + 1], matrix[offset + 5], matrix[offset + 9], matrix[offset + 13],
                matrix[offset + 2], matrix[offset + 6], matrix[offset + 10], matrix[offset + 14],
                matrix[offset + 3], matrix[offset + 7], matrix[offset + 11], matrix[offset + 15]);
    }

    private static void normalize(float[] matrix, float px, float py) {
        Matrix.translateM(matrix, 0, px, py, 0f);
    }

    private static void denormalize(float[] matrix, float px, float py) {
        Matrix.translateM(matrix, 0, -px, -py, 0f);
    }
}