ScaleTransform.java

/*
 * Copyright 2020 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.view.preview.transform;

import android.view.View;

import androidx.annotation.NonNull;
import androidx.camera.view.preview.transform.transformation.ScaleTransformation;

/**
 * Computes the scale on both the x and y axes to uniformly scale up or down a view inside its
 * container, so that it entirely fills it, or is entirely container within it.
 */
final class ScaleTransform {

    private ScaleTransform() {
    }

    /**
     * Computes the scale on both the x and y axes so that the view can uniformly fill its
     * container.
     */
    static ScaleTransformation fill(@NonNull final View container, @NonNull final View view,
            final int deviceRotation) {
        return computeScale(container, view, Math::max, deviceRotation);
    }

    /**
     * Computes the scale on both the x and y axes so that the view can uniformly fit inside its
     * container.
     */
    static ScaleTransformation fit(@NonNull final View container, @NonNull final View view,
            final int deviceRotation) {
        return computeScale(container, view, Math::min, deviceRotation);
    }

    private static ScaleTransformation computeScale(@NonNull final View container,
            @NonNull final View view, @NonNull final FloatBiFunction function,
            final int deviceRotation) {
        // Scaling only makes sense when none of the dimensions are equal to zero. In the
        // opposite case, a default scale of 1 is returned,
        if (container.getWidth() == 0 || container.getHeight() == 0 || view.getWidth() == 0
                || view.getHeight() == 0) {
            return new ScaleTransformation(1);
        }

        final int rotationDegrees = (int) RotationTransform.getRotationDegrees(view,
                deviceRotation);
        float bufferRotatedWidth;
        float bufferRotatedHeight;
        if (rotationDegrees == 0 || rotationDegrees == 180) {
            bufferRotatedWidth = view.getWidth() * view.getScaleX();
            bufferRotatedHeight = view.getHeight() * view.getScaleY();
        } else {
            bufferRotatedWidth = view.getHeight() * view.getScaleY();
            bufferRotatedHeight = view.getWidth() * view.getScaleX();
        }

        final float scale = function.apply(container.getWidth() / bufferRotatedWidth,
                container.getHeight() / bufferRotatedHeight);
        return new ScaleTransformation(scale);
    }

    private interface FloatBiFunction {
        float apply(float a, float b);
    }
}