SurfaceOrientedMeteringPointFactory.java

/*
 * Copyright 2019 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 android.graphics.PointF;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;

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

/**
 * A {@link MeteringPointFactory} that can create {@link MeteringPoint} by surface oriented x, y
 * on an area defined by (0, 0) - (width, height). {@link MeteringPoint} can then be used to
 * construct a {@link FocusMeteringAction} to start a focus and metering action.
 *
 * <p>The {@link MeteringPoint} defines a normalized coordinate system whose left-top is (0, 0)
 * and right-bottom is (1.0, 1.0). This coordinate system is the normalized coordinate
 * system of a {@link Surface} of certain aspect ratio.
 * {@link SurfaceOrientedMeteringPointFactory} is the simplest factory to create this normalized
 * (x, y) by dividing the (x, y) with (width, height).
 *
 * <p>This factory is suitable for apps that already have coordinates converted into surface
 * oriented coordinates. It is also useful for apps that want to focus on something detected in
 * {@link ImageAnalysis}. Apps can pass the {@link ImageAnalysis} instance for useCaseForSurface
 * argument and CameraX will then adjust the final sensor coordinates by aspect ratio of
 * ImageAnalysis.
 *
 * @see MeteringPoint
 */
public class SurfaceOrientedMeteringPointFactory extends MeteringPointFactory {
    /** the width of the area in surface orientation */
    private final float mWidth;
    /** The height of the area in surface orientation */
    private final float mHeight;

    /**
     * Creates the {@link SurfaceOrientedMeteringPointFactory} by width and height
     *
     * <p>The width/height is the width/height in surface orientation which defines an area (0, 0)
     * - (width, height) within which apps can specify metering points by
     * {@link #createPoint(float, float)}. Setting width and height to 1.0 will allow points to
     * be created by specifying normalized coordinates.
     *
     * <p>By default, it will use active {@link Preview} to get the surface aspect ratio for final
     * coordinates conversion.
     *
     * @param width the width of the area in surface orientation.
     * @param height the height of the area in surface orientation.
     */
    public SurfaceOrientedMeteringPointFactory(float width, float height) {
        mWidth = width;
        mHeight = height;
    }

    /**
     * Creates the {@link SurfaceOrientedMeteringPointFactory} by width, height and the surface
     * aspect ratio. The surface aspect ratio is retrieved from the {@link UseCase}.
     *
     * <p>The width/height is the width/height in surface orientation which defines an
     * area (0, 0) - (width, height) within which apps can specify metering points by
     * {@link #createPoint(float, float)}. Setting width and height to 1.0 will allow points to
     * be created by specifying normalized coordinates.
     *
     * <p>A {@link UseCase} is passed in order to determine the surface aspect ratio for final
     * coordinates conversion. This use case needs to be bound at the time this method is called,
     * otherwise an {@link IllegalStateException} will be thrown.
     *
     * @param width the width of the area in surface orientation.
     * @param height the height of the area in surface orientation.
     * @param useCaseForAspectRatio the {@link UseCase} to get the surface aspect ratio.
     */
    public SurfaceOrientedMeteringPointFactory(float width, float height,
            @NonNull UseCase useCaseForAspectRatio) {
        super(getUseCaseAspectRatio(useCaseForAspectRatio));
        mWidth = width;
        mHeight = height;
    }

    @Nullable
    private static Rational getUseCaseAspectRatio(@Nullable UseCase useCase) {
        if (useCase == null) {
            return null;
        }

        Size resolution = useCase.getAttachedSurfaceResolution();
        if (resolution == null) {
            throw new IllegalStateException("UseCase " + useCase + " is not bound.");
        }

        // Returns an aspect ratio of first found attachedSurfaceResolution.
        return new Rational(resolution.getWidth(), resolution.getHeight());
    }

    /**
     * {@inheritDoc}
     *
     * @hide
     */
    @Override
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @NonNull
    protected PointF convertPoint(float x, float y) {
        PointF pt = new PointF(x / mWidth, y / mHeight);
        return pt;
    }
}