Use MatrixTransformation instead of wrapping its GlFrameProcssor.

ScaleToFitFrameProcessor, PresentationFrameProcessor,
and EncoderCompatibilityFrameProcessor now each implement
MatrixTransformation instead of wrapping
MatrixTransformationFrameProcessor.

PiperOrigin-RevId: 446480286
This commit is contained in:
hschlueter 2022-05-04 17:40:33 +01:00 committed by Ian Baker
parent 337b7f769d
commit 4b8ff72d30
12 changed files with 256 additions and 346 deletions

View File

@ -154,7 +154,7 @@ public final class FrameProcessorChainPixelTest {
setUpAndPrepareFirstFrame(
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
(MatrixTransformation) (long presentationTimeUs) -> translateRightMatrix,
() -> new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build());
new ScaleToFitTransformation.Builder().setRotationDegrees(45).build());
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_THEN_ROTATE_PNG_ASSET_PATH);
Bitmap actualBitmap = processFirstFrameAndEnd();
@ -176,7 +176,7 @@ public final class FrameProcessorChainPixelTest {
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
setUpAndPrepareFirstFrame(
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
() -> new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build(),
new ScaleToFitTransformation.Builder().setRotationDegrees(45).build(),
(MatrixTransformation) (long presentationTimeUs) -> translateRightMatrix);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_THEN_TRANSLATE_PNG_ASSET_PATH);
@ -195,8 +195,7 @@ public final class FrameProcessorChainPixelTest {
public void processData_withPresentation_setResolution_producesExpectedOutput() throws Exception {
String testId = "processData_withPresentation_setResolution";
setUpAndPrepareFirstFrame(
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
() -> new PresentationFrameProcessor.Builder().setResolution(480).build());
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, new Presentation.Builder().setResolution(480).build());
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(REQUEST_OUTPUT_HEIGHT_PNG_ASSET_PATH);
Bitmap actualBitmap = processFirstFrameAndEnd();
@ -216,7 +215,7 @@ public final class FrameProcessorChainPixelTest {
String testId = "processData_withScaleToFitTransformation_rotate45";
setUpAndPrepareFirstFrame(
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
() -> new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build());
new ScaleToFitTransformation.Builder().setRotationDegrees(45).build());
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE45_SCALE_TO_FIT_PNG_ASSET_PATH);
Bitmap actualBitmap = processFirstFrameAndEnd();

View File

@ -35,7 +35,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Pixel test for frame processing via {@link PresentationFrameProcessor}.
* Pixel test for frame processing via {@link Presentation}.
*
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
* devices may fail. To test on other devices, please increase the {@link
@ -43,7 +43,7 @@ import org.junit.runner.RunWith;
* as recommended in {@link FrameProcessorChainPixelTest}.
*/
@RunWith(AndroidJUnit4.class)
public final class PresentationFrameProcessorPixelTest {
public final class PresentationPixelTest {
public static final String ORIGINAL_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/original.png";
public static final String CROP_SMALLER_PNG_ASSET_PATH =
@ -97,7 +97,7 @@ public final class PresentationFrameProcessorPixelTest {
@Test
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
String testId = "drawFrame_noEdits";
presentationFrameProcessor = new PresentationFrameProcessor.Builder().build();
presentationFrameProcessor = new Presentation.Builder().build().toGlFrameProcessor();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -122,9 +122,10 @@ public final class PresentationFrameProcessorPixelTest {
public void drawFrame_cropSmaller_producesExpectedOutput() throws Exception {
String testId = "drawFrame_cropSmaller";
GlFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
new Presentation.Builder()
.setCrop(/* left= */ -.9f, /* right= */ .1f, /* bottom= */ -1f, /* top= */ .5f)
.build();
.build()
.toGlFrameProcessor();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -149,9 +150,10 @@ public final class PresentationFrameProcessorPixelTest {
public void drawFrame_cropLarger_producesExpectedOutput() throws Exception {
String testId = "drawFrame_cropSmaller";
GlFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
new Presentation.Builder()
.setCrop(/* left= */ -2f, /* right= */ 2f, /* bottom= */ -1f, /* top= */ 2f)
.build();
.build()
.toGlFrameProcessor();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -177,9 +179,10 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_scaleToFit_narrow";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
.setAspectRatio(1f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT)
.build();
new Presentation.Builder()
.setAspectRatio(1f, Presentation.LAYOUT_SCALE_TO_FIT)
.build()
.toGlFrameProcessor();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -206,9 +209,10 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_scaleToFit_wide";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
.setAspectRatio(2f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT)
.build();
new Presentation.Builder()
.setAspectRatio(2f, Presentation.LAYOUT_SCALE_TO_FIT)
.build()
.toGlFrameProcessor();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -235,9 +239,10 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_narrow";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
.setAspectRatio(1f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT_WITH_CROP)
.build();
new Presentation.Builder()
.setAspectRatio(1f, Presentation.LAYOUT_SCALE_TO_FIT_WITH_CROP)
.build()
.toGlFrameProcessor();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -264,9 +269,10 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_wide";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
.setAspectRatio(2f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT_WITH_CROP)
.build();
new Presentation.Builder()
.setAspectRatio(2f, Presentation.LAYOUT_SCALE_TO_FIT_WITH_CROP)
.build()
.toGlFrameProcessor();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -293,9 +299,10 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_stretchToFit_narrow";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
.setAspectRatio(1f, PresentationFrameProcessor.LAYOUT_STRETCH_TO_FIT)
.build();
new Presentation.Builder()
.setAspectRatio(1f, Presentation.LAYOUT_STRETCH_TO_FIT)
.build()
.toGlFrameProcessor();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -322,9 +329,10 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_stretchToFit_wide";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
.setAspectRatio(2f, PresentationFrameProcessor.LAYOUT_STRETCH_TO_FIT)
.build();
new Presentation.Builder()
.setAspectRatio(2f, Presentation.LAYOUT_STRETCH_TO_FIT)
.build()
.toGlFrameProcessor();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -346,7 +354,7 @@ public final class PresentationFrameProcessorPixelTest {
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
}
private void setupOutputTexture(int outputWidth, int outputHeight) throws IOException {
private void setupOutputTexture(int outputWidth, int outputHeight) {
outputTexId = GlUtil.createTexture(outputWidth, outputHeight);
int frameBuffer = GlUtil.createFboForTexture(outputTexId);
GlUtil.focusFramebuffer(

View File

@ -20,23 +20,22 @@ import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import android.content.Context;
import android.graphics.Matrix;
import android.util.Size;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.util.GlUtil;
import java.io.IOException;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* Copies frames from a texture and applies {@link Format#rotationDegrees} for encoder
* compatibility, if needed.
* Specifies a {@link Format#rotationDegrees} to apply to each frame for encoder compatibility, if
* needed.
*
* <p>Encoders commonly support higher maximum widths than maximum heights. This may rotate the
* decoded frame before encoding, so the encoded frame's width >= height, and set {@link
* Format#rotationDegrees} to ensure the frame is displayed in the correct orientation.
*/
/* package */ class EncoderCompatibilityFrameProcessor implements GlFrameProcessor {
/* package */ class EncoderCompatibilityTransformation implements MatrixTransformation {
// TODO(b/218488308): Allow reconfiguration of the output size, as encoders may not support the
// requested output resolution.
@ -45,26 +44,32 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
private int outputRotationDegrees;
private @MonotonicNonNull ScaleToFitFrameProcessor rotateFrameProcessor;
private @MonotonicNonNull Matrix transformationMatrix;
/** Creates a new instance. */
/* package */ EncoderCompatibilityFrameProcessor() {
public EncoderCompatibilityTransformation() {
outputRotationDegrees = C.LENGTH_UNSET;
}
@Override
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException {
configureOutputSizeAndRotation(inputWidth, inputHeight);
rotateFrameProcessor =
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(outputRotationDegrees).build();
rotateFrameProcessor.initialize(context, inputTexId, inputWidth, inputHeight);
public Size configure(int inputWidth, int inputHeight) {
checkArgument(inputWidth > 0, "inputWidth must be positive");
checkArgument(inputHeight > 0, "inputHeight must be positive");
transformationMatrix = new Matrix();
if (inputHeight > inputWidth) {
outputRotationDegrees = 90;
transformationMatrix.postRotate(outputRotationDegrees);
return new Size(inputHeight, inputWidth);
} else {
outputRotationDegrees = 0;
return new Size(inputWidth, inputHeight);
}
}
@Override
public Size getOutputSize() {
return checkStateNotNull(rotateFrameProcessor).getOutputSize();
public Matrix getMatrix(long presentationTimeUs) {
return checkStateNotNull(transformationMatrix, "configure must be called first");
}
/**
@ -78,28 +83,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public int getOutputRotationDegrees() {
checkState(
outputRotationDegrees != C.LENGTH_UNSET,
"configureOutputSizeAndTransformationMatrix must be called before"
+ " getOutputRotationDegrees");
"configure must be called before getOutputRotationDegrees");
return outputRotationDegrees;
}
@Override
public void drawFrame(long presentationTimeUs) {
checkStateNotNull(rotateFrameProcessor).drawFrame(presentationTimeUs);
}
@Override
public void release() {
if (rotateFrameProcessor != null) {
rotateFrameProcessor.release();
}
}
@VisibleForTesting // Allows robolectric testing of output size calculation without OpenGL.
/* package */ void configureOutputSizeAndRotation(int inputWidth, int inputHeight) {
checkArgument(inputWidth > 0, "inputWidth must be positive");
checkArgument(inputHeight > 0, "inputHeight must be positive");
outputRotationDegrees = (inputHeight > inputWidth) ? 90 : 0;
}
}

View File

@ -190,14 +190,16 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
// Scale to expand the frame to apply the pixelWidthHeightRatio.
if (pixelWidthHeightRatio > 1f) {
frameProcessors.add(
new ScaleToFitFrameProcessor.Builder()
new ScaleToFitTransformation.Builder()
.setScale(/* scaleX= */ pixelWidthHeightRatio, /* scaleY= */ 1f)
.build());
.build()
.toGlFrameProcessor());
} else if (pixelWidthHeightRatio < 1f) {
frameProcessors.add(
new ScaleToFitFrameProcessor.Builder()
new ScaleToFitTransformation.Builder()
.setScale(/* scaleX= */ 1f, /* scaleY= */ 1f / pixelWidthHeightRatio)
.build());
.build()
.toGlFrameProcessor());
}
for (int i = 0; i < effects.size(); i++) {

View File

@ -32,8 +32,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* Applies a transformation matrix in the vertex shader, and copies input pixels into an output
* frame based on their locations after applying this matrix.
*
* <p>Operations are done on normalized device coordinates (-1 to 1 on x and y axes). No automatic
* adjustments (like done in {@link ScaleToFitFrameProcessor}) are applied on the transformation.
* <p>Operations are done on normalized device coordinates (-1 to 1 on x and y axes).
*
* <p>The background color of the output frame will be black.
*/
@ -41,7 +40,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// after each matrix.
@UnstableApi
@SuppressWarnings("FunctionalInterfaceClash") // b/228192298
public final class MatrixTransformationFrameProcessor implements GlFrameProcessor {
/* package */ final class MatrixTransformationFrameProcessor implements GlFrameProcessor {
static {
GlUtil.glAssertionsEnabled = true;

View File

@ -21,36 +21,30 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.Context;
import android.graphics.Matrix;
import android.util.Size;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.C;
import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.UnstableApi;
import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/**
* Controls how a frame is presented, by copying input pixels into an output frame, with options to
* set the output resolution, crop the input, and choose how to map the input pixels onto the output
* frame geometry (for example, by stretching the input frame to match the specified output frame,
* or fitting the input frame using letterboxing).
* Controls how a frame is presented with options to set the output resolution, crop the input, and
* choose how to map the input pixels onto the output frame geometry (for example, by stretching the
* input frame to match the specified output frame, or fitting the input frame using letterboxing).
*
* <p>Cropping or aspect ratio is applied before setting resolution.
*
* <p>The background color of the output frame will be black.
*/
@UnstableApi
// TODO(b/227625423): Implement MatrixTransformation instead of wrapping
// MatrixTransformationFrameProcessor.
public final class PresentationFrameProcessor implements GlFrameProcessor {
public final class Presentation implements MatrixTransformation {
/**
* Strategies controlling the layout of input pixels in the output frame.
*
@ -108,7 +102,7 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
*/
public static final int LAYOUT_STRETCH_TO_FIT = 2;
/** A builder for {@link PresentationFrameProcessor} instances. */
/** A builder for {@link Presentation} instances. */
public static final class Builder {
// Optional fields.
@ -161,7 +155,7 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
* applied after cropping changes.
*
* <p>Only one of {@code setCrop} or {@link #setAspectRatio(float, int)} can be called for one
* {@link PresentationFrameProcessor}.
* {@link Presentation}.
*
* @param left The left edge of the output frame, in NDC. Must be less than {@code right}.
* @param right The right edge of the output frame, in NDC. Must be greater than {@code left}.
@ -196,7 +190,7 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
* applied after aspect ratio changes.
*
* <p>Only one of {@link #setCrop(float, float, float, float)} or {@code setAspectRatio} can be
* called for one {@link PresentationFrameProcessor}.
* called for one {@link Presentation}.
*
* @param aspectRatio The aspect ratio (width/height ratio) of the output frame. Must be
* positive.
@ -217,8 +211,8 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
return this;
}
public PresentationFrameProcessor build() {
return new PresentationFrameProcessor(
public Presentation build() {
return new Presentation(
heightPixels, cropLeft, cropRight, cropBottom, cropTop, aspectRatio, layout);
}
}
@ -237,12 +231,10 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
private float outputWidth;
private float outputHeight;
private @MonotonicNonNull Size outputSize;
private @MonotonicNonNull Matrix transformationMatrix;
private @MonotonicNonNull MatrixTransformationFrameProcessor matrixTransformationFrameProcessor;
/** Creates a new instance. */
private PresentationFrameProcessor(
private Presentation(
int requestedHeightPixels,
float cropLeft,
float cropRight,
@ -264,39 +256,7 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
}
@Override
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException {
configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
matrixTransformationFrameProcessor =
new MatrixTransformationFrameProcessor(
/* matrixTransformation= */ (long presentationTimeUs) ->
checkStateNotNull(transformationMatrix));
matrixTransformationFrameProcessor.initialize(context, inputTexId, inputWidth, inputHeight);
}
@Override
public Size getOutputSize() {
checkStateNotNull(
outputSize,
"configureOutputSizeAndTransformationMatrix must be called before getOutputSize");
return outputSize;
}
@Override
public void drawFrame(long presentationTimeUs) {
checkStateNotNull(matrixTransformationFrameProcessor).drawFrame(presentationTimeUs);
}
@Override
public void release() {
if (matrixTransformationFrameProcessor != null) {
matrixTransformationFrameProcessor.release();
}
}
@EnsuresNonNull("transformationMatrix")
@VisibleForTesting // Allows robolectric testing of output size calculation without OpenGL.
/* package */ void configureOutputSizeAndTransformationMatrix(int inputWidth, int inputHeight) {
public Size configure(int inputWidth, int inputHeight) {
checkArgument(inputWidth > 0, "inputWidth must be positive");
checkArgument(inputHeight > 0, "inputHeight must be positive");
@ -318,7 +278,12 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
outputWidth = requestedHeightPixels * outputWidth / outputHeight;
outputHeight = requestedHeightPixels;
}
outputSize = new Size(Math.round(outputWidth), Math.round(outputHeight));
return new Size(Math.round(outputWidth), Math.round(outputHeight));
}
@Override
public Matrix getMatrix(long presentationTimeUs) {
return checkStateNotNull(transformationMatrix, "configure must be called first");
}
@RequiresNonNull("transformationMatrix")

View File

@ -20,18 +20,14 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static java.lang.Math.max;
import static java.lang.Math.min;
import android.content.Context;
import android.graphics.Matrix;
import android.util.Size;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.UnstableApi;
import java.io.IOException;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* Applies a simple rotation and/or scale in the vertex shader.
* Specifies a simple rotation and/or scale to apply in the vertex shader.
*
* <p>All input frames' pixels will be preserved and copied into an output frame, potentially
* changing the width and height of the frame by scaling dimensions to fit.
@ -39,11 +35,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* <p>The background color of the output frame will be black.
*/
@UnstableApi
// TODO(b/227625423): Implement MatrixTransformation instead of wrapping
// MatrixTransformationFrameProcessor.
public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
public final class ScaleToFitTransformation implements MatrixTransformation {
/** A builder for {@link ScaleToFitFrameProcessor} instances. */
/** A builder for {@link ScaleToFitTransformation} instances. */
public static final class Builder {
// Optional fields.
@ -86,8 +80,8 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
return this;
}
public ScaleToFitFrameProcessor build() {
return new ScaleToFitFrameProcessor(scaleX, scaleY, rotationDegrees);
public ScaleToFitTransformation build() {
return new ScaleToFitTransformation(scaleX, scaleY, rotationDegrees);
}
}
@ -96,9 +90,6 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
}
private final Matrix transformationMatrix;
private @MonotonicNonNull MatrixTransformationFrameProcessor matrixTransformationFrameProcessor;
private @MonotonicNonNull Size outputSize;
private @MonotonicNonNull Matrix adjustedTransformationMatrix;
/**
@ -108,51 +99,21 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
* @param scaleY The multiplier by which the frame will scale vertically, along the y-axis.
* @param rotationDegrees How much to rotate the frame counterclockwise, in degrees.
*/
private ScaleToFitFrameProcessor(float scaleX, float scaleY, float rotationDegrees) {
private ScaleToFitTransformation(float scaleX, float scaleY, float rotationDegrees) {
this.transformationMatrix = new Matrix();
this.transformationMatrix.postScale(scaleX, scaleY);
this.transformationMatrix.postRotate(rotationDegrees);
}
@Override
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException {
configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
matrixTransformationFrameProcessor =
new MatrixTransformationFrameProcessor(
/* matrixTransformation= */ (long presentationTimeUs) ->
checkStateNotNull(adjustedTransformationMatrix));
matrixTransformationFrameProcessor.initialize(context, inputTexId, inputWidth, inputHeight);
}
@Override
public Size getOutputSize() {
return checkStateNotNull(outputSize);
}
@Override
public void drawFrame(long presentationTimeUs) {
checkStateNotNull(matrixTransformationFrameProcessor).drawFrame(presentationTimeUs);
}
@Override
public void release() {
if (matrixTransformationFrameProcessor != null) {
matrixTransformationFrameProcessor.release();
}
}
@EnsuresNonNull("adjustedTransformationMatrix")
@VisibleForTesting // Allows robolectric testing of output size calculation without OpenGL.
/* package */ void configureOutputSizeAndTransformationMatrix(int inputWidth, int inputHeight) {
public Size configure(int inputWidth, int inputHeight) {
checkArgument(inputWidth > 0, "inputWidth must be positive");
checkArgument(inputHeight > 0, "inputHeight must be positive");
adjustedTransformationMatrix = new Matrix(transformationMatrix);
if (transformationMatrix.isIdentity()) {
outputSize = new Size(inputWidth, inputHeight);
return;
return new Size(inputWidth, inputHeight);
}
float inputAspectRatio = (float) inputWidth / inputHeight;
@ -181,6 +142,11 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
float scaleX = (maxX - minX) / GlUtil.LENGTH_NDC;
float scaleY = (maxY - minY) / GlUtil.LENGTH_NDC;
adjustedTransformationMatrix.postScale(1f / scaleX, 1f / scaleY);
outputSize = new Size(Math.round(inputWidth * scaleX), Math.round(inputHeight * scaleY));
return new Size(Math.round(inputWidth * scaleX), Math.round(inputHeight * scaleY));
}
@Override
public Matrix getMatrix(long presentationTimeUs) {
return checkStateNotNull(adjustedTransformationMatrix, "configure must be called first");
}
}

View File

@ -74,22 +74,18 @@ import org.checkerframework.dataflow.qual.Pure;
|| transformationRequest.scaleY != 1f
|| transformationRequest.rotationDegrees != 0f) {
effectsListBuilder.add(
() ->
new ScaleToFitFrameProcessor.Builder()
.setScale(transformationRequest.scaleX, transformationRequest.scaleY)
.setRotationDegrees(transformationRequest.rotationDegrees)
.build());
new ScaleToFitTransformation.Builder()
.setScale(transformationRequest.scaleX, transformationRequest.scaleY)
.setRotationDegrees(transformationRequest.rotationDegrees)
.build());
}
if (transformationRequest.outputHeight != C.LENGTH_UNSET) {
effectsListBuilder.add(
() ->
new PresentationFrameProcessor.Builder()
.setResolution(transformationRequest.outputHeight)
.build());
new Presentation.Builder().setResolution(transformationRequest.outputHeight).build());
}
EncoderCompatibilityFrameProcessor encoderCompatibilityFrameProcessor =
new EncoderCompatibilityFrameProcessor();
effectsListBuilder.add(() -> encoderCompatibilityFrameProcessor);
EncoderCompatibilityTransformation encoderCompatibilityTransformation =
new EncoderCompatibilityTransformation();
effectsListBuilder.add(encoderCompatibilityTransformation);
frameProcessorChain =
FrameProcessorChain.create(
context,
@ -99,7 +95,7 @@ import org.checkerframework.dataflow.qual.Pure;
effectsListBuilder.build(),
transformationRequest.enableHdrEditing);
Size requestedEncoderSize = frameProcessorChain.getOutputSize();
outputRotationDegrees = encoderCompatibilityFrameProcessor.getOutputRotationDegrees();
outputRotationDegrees = encoderCompatibilityTransformation.getOutputRotationDegrees();
Format requestedEncoderFormat =
new Format.Builder()

View File

@ -1,73 +0,0 @@
/*
* 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.media3.transformer;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit tests for {@link EncoderCompatibilityFrameProcessor}. */
@RunWith(AndroidJUnit4.class)
public final class EncoderCompatibilityFrameProcessorTest {
@Test
public void getOutputSize_noEditsLandscape_leavesOrientationUnchanged() {
int inputWidth = 200;
int inputHeight = 150;
EncoderCompatibilityFrameProcessor encoderCompatibilityFrameProcessor =
new EncoderCompatibilityFrameProcessor();
encoderCompatibilityFrameProcessor.configureOutputSizeAndRotation(inputWidth, inputHeight);
assertThat(encoderCompatibilityFrameProcessor.getOutputRotationDegrees()).isEqualTo(0);
}
@Test
public void getOutputSize_noEditsSquare_leavesOrientationUnchanged() {
int inputWidth = 150;
int inputHeight = 150;
EncoderCompatibilityFrameProcessor encoderCompatibilityFrameProcessor =
new EncoderCompatibilityFrameProcessor();
encoderCompatibilityFrameProcessor.configureOutputSizeAndRotation(inputWidth, inputHeight);
assertThat(encoderCompatibilityFrameProcessor.getOutputRotationDegrees()).isEqualTo(0);
}
@Test
public void getOutputSize_noEditsPortrait_flipsOrientation() {
int inputWidth = 150;
int inputHeight = 200;
EncoderCompatibilityFrameProcessor encoderCompatibilityFrameProcessor =
new EncoderCompatibilityFrameProcessor();
encoderCompatibilityFrameProcessor.configureOutputSizeAndRotation(inputWidth, inputHeight);
assertThat(encoderCompatibilityFrameProcessor.getOutputRotationDegrees()).isEqualTo(90);
}
@Test
public void getOutputRotationDegreesBeforeConfigure_throwsIllegalStateException() {
EncoderCompatibilityFrameProcessor encoderCompatibilityFrameProcessor =
new EncoderCompatibilityFrameProcessor();
// configureOutputSize not called before getOutputRotationDegrees.
assertThrows(
IllegalStateException.class, encoderCompatibilityFrameProcessor::getOutputRotationDegrees);
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.media3.transformer;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import android.util.Size;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit tests for {@link EncoderCompatibilityTransformation}. */
@RunWith(AndroidJUnit4.class)
public final class EncoderCompatibilityTransformationTest {
@Test
public void configure_noEditsLandscape_leavesOrientationUnchanged() {
int inputWidth = 200;
int inputHeight = 150;
EncoderCompatibilityTransformation encoderCompatibilityTransformation =
new EncoderCompatibilityTransformation();
Size outputSize = encoderCompatibilityTransformation.configure(inputWidth, inputHeight);
assertThat(encoderCompatibilityTransformation.getOutputRotationDegrees()).isEqualTo(0);
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@Test
public void configure_noEditsSquare_leavesOrientationUnchanged() {
int inputWidth = 150;
int inputHeight = 150;
EncoderCompatibilityTransformation encoderCompatibilityTransformation =
new EncoderCompatibilityTransformation();
Size outputSize = encoderCompatibilityTransformation.configure(inputWidth, inputHeight);
assertThat(encoderCompatibilityTransformation.getOutputRotationDegrees()).isEqualTo(0);
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@Test
public void configure_noEditsPortrait_flipsOrientation() {
int inputWidth = 150;
int inputHeight = 200;
EncoderCompatibilityTransformation encoderCompatibilityTransformation =
new EncoderCompatibilityTransformation();
Size outputSize = encoderCompatibilityTransformation.configure(inputWidth, inputHeight);
assertThat(encoderCompatibilityTransformation.getOutputRotationDegrees()).isEqualTo(90);
assertThat(outputSize.getWidth()).isEqualTo(inputHeight);
assertThat(outputSize.getHeight()).isEqualTo(inputWidth);
}
@Test
public void getOutputRotationDegreesBeforeConfigure_throwsIllegalStateException() {
EncoderCompatibilityTransformation encoderCompatibilityTransformation =
new EncoderCompatibilityTransformation();
// configure not called before getOutputRotationDegrees.
assertThrows(
IllegalStateException.class, encoderCompatibilityTransformation::getOutputRotationDegrees);
}
}

View File

@ -25,55 +25,49 @@ import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Unit tests for {@link PresentationFrameProcessor}.
* Unit tests for {@link Presentation}.
*
* <p>See {@code PresentationFrameProcessorPixelTest} for pixel tests testing {@link
* PresentationFrameProcessor}.
* <p>See {@code PresentationFrameProcessorPixelTest} for pixel tests testing {@link Presentation}.
*/
@RunWith(AndroidJUnit4.class)
public final class PresentationFrameProcessorTest {
public final class PresentationTest {
@Test
public void getOutputSize_noEdits_leavesFramesUnchanged() {
public void configure_noEdits_leavesFramesUnchanged() {
int inputWidth = 200;
int inputHeight = 150;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder().build();
Presentation presentation = new Presentation.Builder().build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
Size outputSize = presentation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@Test
public void getOutputSize_setResolution_changesDimensions() {
public void configure_setResolution_changesDimensions() {
int inputWidth = 200;
int inputHeight = 150;
int requestedHeight = 300;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder().setResolution(requestedHeight).build();
Presentation presentation = new Presentation.Builder().setResolution(requestedHeight).build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
Size outputSize = presentation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(requestedHeight * inputWidth / inputHeight);
assertThat(outputSize.getHeight()).isEqualTo(requestedHeight);
}
@Test
public void getOutputSize_setCrop_changesDimensions() {
public void configure_setCrop_changesDimensions() {
int inputWidth = 300;
int inputHeight = 200;
float left = -.5f;
float right = .5f;
float bottom = .5f;
float top = 1f;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder().setCrop(left, right, bottom, top).build();
Presentation presentation =
new Presentation.Builder().setCrop(left, right, bottom, top).build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
Size outputSize = presentation.configure(inputWidth, inputHeight);
int expectedPostCropWidth = Math.round(inputWidth * (right - left) / GlUtil.LENGTH_NDC);
int expectedPostCropHeight = Math.round(inputHeight * (top - bottom) / GlUtil.LENGTH_NDC);
@ -82,7 +76,7 @@ public final class PresentationFrameProcessorTest {
}
@Test
public void getOutputSize_setCropAndSetResolution_changesDimensions() {
public void configure_setCropAndSetResolution_changesDimensions() {
int inputWidth = 300;
int inputHeight = 200;
float left = -.5f;
@ -90,14 +84,13 @@ public final class PresentationFrameProcessorTest {
float bottom = .5f;
float top = 1f;
int requestedHeight = 100;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
Presentation presentation =
new Presentation.Builder()
.setCrop(left, right, bottom, top)
.setResolution(requestedHeight)
.build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
Size outputSize = presentation.configure(inputWidth, inputHeight);
int expectedPostCropWidth = Math.round(inputWidth * (right - left) / GlUtil.LENGTH_NDC);
int expectedPostCropHeight = Math.round(inputHeight * (top - bottom) / GlUtil.LENGTH_NDC);
@ -108,7 +101,7 @@ public final class PresentationFrameProcessorTest {
}
@Test
public void getOutputSize_setResolutionAndCrop_changesDimensions() {
public void configure_setResolutionAndCrop_changesDimensions() {
int inputWidth = 300;
int inputHeight = 200;
float left = -.5f;
@ -116,14 +109,13 @@ public final class PresentationFrameProcessorTest {
float bottom = .5f;
float top = 1f;
int requestedHeight = 100;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
Presentation presentation =
new Presentation.Builder()
.setResolution(requestedHeight)
.setCrop(left, right, bottom, top)
.build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
Size outputSize = presentation.configure(inputWidth, inputHeight);
int expectedPostCropWidth = Math.round(inputWidth * (right - left) / GlUtil.LENGTH_NDC);
int expectedPostCropHeight = Math.round(inputHeight * (top - bottom) / GlUtil.LENGTH_NDC);
@ -134,46 +126,44 @@ public final class PresentationFrameProcessorTest {
}
@Test
public void getOutputSize_setAspectRatio_changesDimensions() {
public void configure_setAspectRatio_changesDimensions() {
int inputWidth = 300;
int inputHeight = 200;
float aspectRatio = 2f;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
.setAspectRatio(aspectRatio, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT)
Presentation presentation =
new Presentation.Builder()
.setAspectRatio(aspectRatio, Presentation.LAYOUT_SCALE_TO_FIT)
.build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
Size outputSize = presentation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(Math.round(aspectRatio * inputHeight));
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@Test
public void getOutputSize_setAspectRatioAndResolution_changesDimensions() {
public void configure_setAspectRatioAndResolution_changesDimensions() {
int inputWidth = 300;
int inputHeight = 200;
float aspectRatio = 2f;
int requestedHeight = 100;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
.setAspectRatio(aspectRatio, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT)
Presentation presentation =
new Presentation.Builder()
.setAspectRatio(aspectRatio, Presentation.LAYOUT_SCALE_TO_FIT)
.setResolution(requestedHeight)
.build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
Size outputSize = presentation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(Math.round(aspectRatio * requestedHeight));
assertThat(outputSize.getHeight()).isEqualTo(requestedHeight);
}
@Test
public void getOutputSize_setAspectRatioAndCrop_throwsIllegalStateException() {
PresentationFrameProcessor.Builder presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
.setAspectRatio(/* aspectRatio= */ 2f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT);
public void configure_setAspectRatioAndCrop_throwsIllegalStateException() {
Presentation.Builder presentationFrameProcessor =
new Presentation.Builder()
.setAspectRatio(/* aspectRatio= */ 2f, Presentation.LAYOUT_SCALE_TO_FIT);
assertThrows(
IllegalStateException.class,
@ -183,15 +173,15 @@ public final class PresentationFrameProcessorTest {
}
@Test
public void getOutputSize_setCropAndAspectRatio_throwsIllegalStateException() {
PresentationFrameProcessor.Builder presentationFrameProcessor =
new PresentationFrameProcessor.Builder()
public void configure_setCropAndAspectRatio_throwsIllegalStateException() {
Presentation.Builder presentationFrameProcessor =
new Presentation.Builder()
.setCrop(/* left= */ -.5f, /* right= */ .5f, /* bottom= */ .5f, /* top= */ 1f);
assertThrows(
IllegalStateException.class,
() ->
presentationFrameProcessor.setAspectRatio(
/* aspectRatio= */ 2f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT));
/* aspectRatio= */ 2f, Presentation.LAYOUT_SCALE_TO_FIT));
}
}

View File

@ -23,96 +23,90 @@ import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Unit tests for {@link ScaleToFitFrameProcessor}.
* Unit tests for {@link ScaleToFitTransformation}.
*
* <p>See {@code AdvancedFrameProcessorPixelTest} for pixel tests testing {@link
* AdvancedFrameProcessor} given a transformation matrix.
* <p>See {@code MatrixTransformationFrameProcessorText} for pixel tests testing {@link
* MatrixTransformationFrameProcessor} given a transformation matrix.
*/
@RunWith(AndroidJUnit4.class)
public final class ScaleToFitFrameProcessorTest {
public final class ScaleToFitTransformationTest {
@Test
public void getOutputSize_noEdits_leavesFramesUnchanged() {
public void configure_noEdits_leavesFramesUnchanged() {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder().build();
ScaleToFitTransformation scaleToFitTransformation =
new ScaleToFitTransformation.Builder().build();
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@Test
public void getOutputSize_scaleNarrow_decreasesWidth() {
public void configure_scaleNarrow_decreasesWidth() {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder()
ScaleToFitTransformation scaleToFitTransformation =
new ScaleToFitTransformation.Builder()
.setScale(/* scaleX= */ .5f, /* scaleY= */ 1f)
.build();
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(Math.round(inputWidth * .5f));
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@Test
public void getOutputSize_scaleWide_increasesWidth() {
public void configure_scaleWide_increasesWidth() {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder().setScale(/* scaleX= */ 2f, /* scaleY= */ 1f).build();
ScaleToFitTransformation scaleToFitTransformation =
new ScaleToFitTransformation.Builder().setScale(/* scaleX= */ 2f, /* scaleY= */ 1f).build();
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(inputWidth * 2);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@Test
public void getOutputSize_scaleTall_increasesHeight() {
public void configure_scaleTall_increasesHeight() {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder().setScale(/* scaleX= */ 1f, /* scaleY= */ 2f).build();
ScaleToFitTransformation scaleToFitTransformation =
new ScaleToFitTransformation.Builder().setScale(/* scaleX= */ 1f, /* scaleY= */ 2f).build();
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight * 2);
}
@Test
public void getOutputSize_rotate90_swapsDimensions() {
public void configure_rotate90_swapsDimensions() {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(90).build();
ScaleToFitTransformation scaleToFitTransformation =
new ScaleToFitTransformation.Builder().setRotationDegrees(90).build();
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(inputHeight);
assertThat(outputSize.getHeight()).isEqualTo(inputWidth);
}
@Test
public void getOutputSize_rotate45_changesDimensions() {
public void configure_rotate45_changesDimensions() {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build();
ScaleToFitTransformation scaleToFitTransformation =
new ScaleToFitTransformation.Builder().setRotationDegrees(45).build();
long expectedOutputWidthHeight = 247;
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
assertThat(outputSize.getWidth()).isEqualTo(expectedOutputWidthHeight);
assertThat(outputSize.getHeight()).isEqualTo(expectedOutputWidthHeight);