OverlaySettings.java

package androidx.media3.effect;

/*
 * 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.
 */
import static androidx.media3.common.util.Assertions.checkArgument;

import android.util.Pair;
import androidx.annotation.FloatRange;
import androidx.media3.common.util.UnstableApi;
import com.google.errorprone.annotations.CanIgnoreReturnValue;

/**
 * Contains information to control how an input texture (for example, a {@link VideoCompositor} or
 * {@link TextureOverlay}) is displayed on a background.
 */
@UnstableApi
public final class OverlaySettings {
  public final boolean useHdr;
  public final float alphaScale;
  public final Pair<Float, Float> backgroundFrameAnchor;
  public final Pair<Float, Float> overlayFrameAnchor;
  public final Pair<Float, Float> scale;
  public final float rotationDegrees;

  private OverlaySettings(
      boolean useHdr,
      float alphaScale,
      Pair<Float, Float> backgroundFrameAnchor,
      Pair<Float, Float> overlayFrameAnchor,
      Pair<Float, Float> scale,
      float rotationDegrees) {
    this.useHdr = useHdr;
    this.alphaScale = alphaScale;
    this.backgroundFrameAnchor = backgroundFrameAnchor;
    this.overlayFrameAnchor = overlayFrameAnchor;
    this.scale = scale;
    this.rotationDegrees = rotationDegrees;
  }

  /** Returns a new {@link Builder} initialized with the values of this instance. */
  /* package */ Builder buildUpon() {
    return new Builder(this);
  }

  /** A builder for {@link OverlaySettings} instances. */
  public static final class Builder {
    private boolean useHdr;
    private float alphaScale;
    private Pair<Float, Float> backgroundFrameAnchor;
    private Pair<Float, Float> overlayFrameAnchor;
    private Pair<Float, Float> scale;
    private float rotationDegrees;

    /** Creates a new {@link Builder}. */
    public Builder() {
      alphaScale = 1f;
      backgroundFrameAnchor = Pair.create(0f, 0f);
      overlayFrameAnchor = Pair.create(0f, 0f);
      scale = Pair.create(1f, 1f);
      rotationDegrees = 0f;
    }

    private Builder(OverlaySettings overlaySettings) {
      this.useHdr = overlaySettings.useHdr;
      this.alphaScale = overlaySettings.alphaScale;
      this.backgroundFrameAnchor = overlaySettings.backgroundFrameAnchor;
      this.overlayFrameAnchor = overlaySettings.overlayFrameAnchor;
      this.scale = overlaySettings.scale;
      this.rotationDegrees = overlaySettings.rotationDegrees;
    }

    /**
     * Sets whether input overlay comes from an HDR source. If {@code true}, colors will be in
     * linear RGB BT.2020. If {@code false}, colors will be in linear RGB BT.709.
     *
     * <p>Set to {@code false} by default.
     */
    @CanIgnoreReturnValue
    public Builder setUsesHdr(boolean useHdr) {
      this.useHdr = useHdr;
      return this;
    }

    /**
     * Sets the alpha scale value of the overlay, altering its translucency.
     *
     * <p>An {@code alphaScale} value of {@code 1} means no change is applied. A value below {@code
     * 1} increases translucency, and a value above {@code 1} reduces translucency.
     *
     * <p>Set to always return {@code 1} by default.
     */
    @CanIgnoreReturnValue
    public Builder setAlphaScale(@FloatRange(from = 0) float alphaScale) {
      checkArgument(0 <= alphaScale, "alphaScale needs to be greater than or equal to zero.");
      this.alphaScale = alphaScale;
      return this;
    }

    /**
     * Sets the coordinates for the anchor point of the overlay within the background frame.
     *
     * <p>The coordinates are specified in Normalised Device Coordinates (NDCs) relative to the
     * background frame. Set to always return {@code (0,0)} (the center of the background frame) by
     * default.
     *
     * <p>For example, a value of {@code (+1,+1)} will move the overlay frames's {@linkplain
     * #setOverlayFrameAnchor anchor point} to the top right corner of the background frame.
     *
     * @param x The NDC x-coordinate in the range [-1, 1].
     * @param y The NDC y-coordinate in the range [-1, 1].
     */
    @CanIgnoreReturnValue
    public Builder setBackgroundFrameAnchor(
        @FloatRange(from = -1, to = 1) float x, @FloatRange(from = -1, to = 1) float y) {
      checkArgument(-1 <= x && x <= 1);
      checkArgument(-1 <= y && y <= 1);
      this.backgroundFrameAnchor = Pair.create(x, y);
      return this;
    }

    /**
     * Sets the coordinates for the anchor point of the overlay frame.
     *
     * <p>The anchor point is the point inside the overlay frame that is placed on the {@linkplain
     * #setBackgroundFrameAnchor background frame anchor}
     *
     * <p>The coordinates are specified in Normalised Device Coordinates (NDCs) relative to the
     * overlay frame. Set to return {@code (0,0)} (the center of the overlay frame) by default.
     *
     * <p>For example, a value of {@code (+1,-1)} will result in the overlay frame being positioned
     * with its bottom right corner positioned at the background frame anchor.
     *
     * @param x The NDC x-coordinate in the range [-1, 1].
     * @param y The NDC y-coordinate in the range [-1, 1].
     */
    @CanIgnoreReturnValue
    public Builder setOverlayFrameAnchor(
        @FloatRange(from = -1, to = 1) float x, @FloatRange(from = -1, to = 1) float y) {
      checkArgument(-1 <= x && x <= 1);
      checkArgument(-1 <= y && y <= 1);
      this.overlayFrameAnchor = Pair.create(x, y);
      return this;
    }

    /**
     * Sets the scaling of the overlay.
     *
     * @param x The desired scaling in the x axis of the overlay.
     * @param y The desired scaling in the y axis of the overlay.
     */
    @CanIgnoreReturnValue
    public Builder setScale(float x, float y) {
      this.scale = Pair.create(x, y);
      return this;
    }

    /**
     * Sets the rotation of the overlay, counter-clockwise.
     *
     * <p>The overlay is rotated at the center of its frame.
     *
     * @param rotationDegree The desired degrees of rotation, counter-clockwise.
     */
    @CanIgnoreReturnValue
    public Builder setRotationDegrees(float rotationDegree) {
      this.rotationDegrees = rotationDegree;
      return this;
    }

    /** Creates an instance of {@link OverlaySettings}, using defaults if values are unset. */
    public OverlaySettings build() {
      return new OverlaySettings(
          useHdr, alphaScale, backgroundFrameAnchor, overlayFrameAnchor, scale, rotationDegrees);
    }
  }
}