CtsVerifier / Camera format test: Apply a rotation to the format view when necessary.

Foldable devices break assumptions made by the camera format test, namely that mPreviewRotation is
always either 0 or 180. It is possible on a foldable device to change from portrait to landscape
while the camera sensor orientation remains the same, for example by unfolding the device. This
patch introduces a new matrix transformation to both the preview and format views when this
assumption has been broken.

Bug: 205503901
Test: Ran the Camera Format tests on all cameras on all orientations.
Change-Id: I34b1e28cb4828289df613f692a6491fe5dc8f22a
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
index 3fec587..793cbf7 100755
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java
@@ -22,6 +22,7 @@
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.ImageFormat;
 import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.hardware.Camera.CameraInfo;
@@ -467,10 +468,6 @@
         } else {  // back-facing
             mPreviewRotation = (info.orientation - degrees + 360) % 360;
         }
-        if (mPreviewRotation != 0 && mPreviewRotation != 180) {
-            Log.w(TAG,
-                "Display orientation correction is not 0 or 180, as expected!");
-        }
 
         mCamera.setDisplayOrientation(mPreviewRotation);
 
@@ -492,6 +489,36 @@
         }
     }
 
+    /**
+     * Rotate and scale the matrix to be applied to the preview or format view, such that no
+     * stretching of the image occurs. To achieve this, the image is centered in the SurfaceTexture
+     * with black bars filling the excess space.
+     */
+    private void concatPreviewTransform(Matrix transform) {
+        float widthRatio = mNextPreviewSize.width / (float) mPreviewTexWidth;
+        float heightRatio = mNextPreviewSize.height / (float) mPreviewTexHeight;
+        float scaledWidth = (float) mPreviewTexWidth;
+        float scaledHeight = (float) mPreviewTexHeight;
+
+        if (heightRatio < widthRatio) {
+            scaledHeight = mPreviewTexHeight * (heightRatio / widthRatio);
+            transform.postScale(1, heightRatio / widthRatio);
+            transform.postTranslate(0,
+                    mPreviewTexHeight * (1 - heightRatio / widthRatio) / 2);
+        } else {
+            scaledWidth = mPreviewTexWidth * (widthRatio / heightRatio);
+            transform.postScale(widthRatio / heightRatio, 1);
+            transform.postTranslate(mPreviewTexWidth * (1 - widthRatio / heightRatio) / 2, 0);
+        }
+
+        if (mPreviewRotation == 90 || mPreviewRotation == 270) {
+            float scaledAspect = scaledWidth / scaledHeight;
+            float previewAspect = (float) mNextPreviewSize.width / (float) mNextPreviewSize.height;
+            transform.postScale(1.0f, scaledAspect * previewAspect,
+                                (float) mPreviewTexWidth / 2, (float) mPreviewTexHeight / 2);
+        }
+    }
+
     private void startPreview() {
         if (mState != STATE_OFF) {
             // Stop for a while to drain callbacks
@@ -510,19 +537,7 @@
         mState = STATE_PREVIEW;
 
         Matrix transform = new Matrix();
-        float widthRatio = mNextPreviewSize.width / (float)mPreviewTexWidth;
-        float heightRatio = mNextPreviewSize.height / (float)mPreviewTexHeight;
-
-        if (heightRatio < widthRatio) {
-            transform.setScale(1, heightRatio/widthRatio);
-            transform.postTranslate(0,
-                mPreviewTexHeight * (1 - heightRatio/widthRatio)/2);
-        } else {
-            transform.setScale(widthRatio/heightRatio, 1);
-            transform.postTranslate(mPreviewTexWidth * (1 - widthRatio/heightRatio)/2,
-            0);
-        }
-
+        concatPreviewTransform(transform);
         mPreviewView.setTransform(transform);
 
         mPreviewFormat = mNextPreviewFormat;
@@ -629,6 +644,31 @@
         protected void onPostExecute(Boolean result) {
             if (result) {
                 mFormatView.setImageBitmap(mCallbackBitmap);
+
+                CameraInfo info = new CameraInfo();
+                Camera.getCameraInfo(mCurrentCameraId, info);
+
+                int rotation = mPreviewRotation;
+                if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+                    rotation = (360 - rotation) % 360;  // de-compensate the mirror
+                }
+
+                if (rotation != 0) {
+                    Matrix transform = new Matrix();
+                    mFormatView.setScaleType(ImageView.ScaleType.MATRIX);
+                    Rect viewRect = mFormatView.getDrawable().getBounds();
+                    transform.postTranslate(-viewRect.width() / 2, -viewRect.height() / 2);
+                    transform.postRotate(rotation);
+                    transform.postTranslate(viewRect.height() / 2, viewRect.width() / 2);
+                    transform.postScale(
+                            mPreviewView.getMeasuredWidth() / (float) viewRect.height(),
+                            mPreviewView.getMeasuredHeight() / (float) viewRect.width());
+                    concatPreviewTransform(transform);
+                    mFormatView.setImageMatrix(transform);
+                } else {
+                    mFormatView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+                }
+
                 if (mProcessingFirstFrame) {
                     mProcessingFirstFrame = false;