Extend evs_app configuration

This change allows the users to rotate the preview around the x-axis and
flip the preview horizontally and vertically by modifying config.json
file.

Below new fields are added to config.json's definition:
- roll: rotation angle around the optical axis
- hflip: the preview will be flipped horizontally when this is true
- vflip: the preview will be flipped vertically when this is true

Fix: 171239414
Test: Build and run evs_app with different combinations of newly
      introduced roll, hflip, and vflip values
Change-Id: I73b737a545046f960b077313a6bc93bb1c21a138
Merged-In: I73b737a545046f960b077313a6bc93bb1c21a138
(cherry picked from commit d57e636a124baea73ba388351fa2b96f69d63c61)
diff --git a/evs/apps/default/ConfigManager.cpp b/evs/apps/default/ConfigManager.cpp
index 76c142c..cc55518 100644
--- a/evs/apps/default/ConfigManager.cpp
+++ b/evs/apps/default/ConfigManager.cpp
@@ -142,8 +142,11 @@
 
             float yaw   = node.get("yaw", 0).asFloat();
             float pitch = node.get("pitch", 0).asFloat();
+            float roll  = node.get("roll", 0).asFloat();
             float hfov  = node.get("hfov", 0).asFloat();
             float vfov  = node.get("vfov", 0).asFloat();
+            bool  hflip = node.get("hflip", false).asBool();
+            bool  vflip = node.get("vflip", false).asBool();
 
             // Wrap the direction angles to be in the 180deg to -180deg range
             // Rotate 180 in yaw if necessary to flip the pitch into the +/-90degree range
@@ -157,6 +160,7 @@
                 pitch = -180.0f + pitch;
             }
             yaw = normalizeToPlusMinus180degrees(yaw);
+            roll = normalizeToPlusMinus180degrees(roll);
 
             // Range check the FOV values to ensure they are postive and less than 180degrees
             if (hfov > 179.0f) {
@@ -183,8 +187,11 @@
             info.position[2] = node.get("z", 0).asFloat();
             info.yaw         = yaw   * kDegreesToRadians;
             info.pitch       = pitch * kDegreesToRadians;
+            info.roll        = roll  * kDegreesToRadians;
             info.hfov        = hfov  * kDegreesToRadians;
             info.vfov        = vfov  * kDegreesToRadians;
+            info.hflip       = hflip;
+            info.vflip       = vflip;
             info.cameraId    = cameraId;
             info.function    = function;
 
diff --git a/evs/apps/default/ConfigManager.h b/evs/apps/default/ConfigManager.h
index eb89770..bf22d0c 100644
--- a/evs/apps/default/ConfigManager.h
+++ b/evs/apps/default/ConfigManager.h
@@ -31,8 +31,11 @@
         float position[3] = {0};    // x, y, z -> right, fwd, up in the units of car space
         float yaw   = 0;    // radians positive to the left (right hand rule about global z axis)
         float pitch = 0;    // positive upward (ie: right hand rule about local x axis)
+        float roll  = 0;    // radians positively increasing clockwisely around the optical axis
         float hfov  = 0;    // radians
         float vfov  = 0;    // radians
+        bool  hflip = false;// boolean to flip the preview horizontally
+        bool  vflip = false;// boolean to flip the preview vertically
     };
 
     struct DisplayInfo {
diff --git a/evs/apps/default/RenderDirectView.cpp b/evs/apps/default/RenderDirectView.cpp
index 28c1f68..c2263ce 100644
--- a/evs/apps/default/RenderDirectView.cpp
+++ b/evs/apps/default/RenderDirectView.cpp
@@ -15,15 +15,16 @@
  */
 
 #include "RenderDirectView.h"
+
 #include "VideoTex.h"
 #include "glError.h"
 #include "shader.h"
 #include "shader_simpleTex.h"
 
+#include <android-base/logging.h>
+#include <android/hardware/camera/device/3.2/ICameraDevice.h>
 #include <math/mat4.h>
 #include <system/camera_metadata.h>
-#include <android/hardware/camera/device/3.2/ICameraDevice.h>
-#include <android-base/logging.h>
 
 using ::android::hardware::camera::device::V3_2::Stream;
 using ::android::hardware::graphics::common::V1_0::PixelFormat;
@@ -47,7 +48,21 @@
     mEnumerator(enumerator),
     mCameraDesc(camDesc),
     mConfig(config) {
-    /* Nothing to do */
+    // Find and store the target camera configuration
+    const auto& camList = mConfig.getCameras();
+    const auto target = std::find_if(camList.begin(), camList.end(),
+                                     [this](const ConfigManager::CameraInfo& info) {
+                                         return info.cameraId == mCameraDesc.v1.cameraId;
+                                     });
+    if (target != camList.end()) {
+        // Store the info
+        mCameraInfo = *target;
+
+        // Calculate a rotation matrix
+        float sinRoll, cosRoll;
+        sincosf(mCameraInfo.roll, &sinRoll, &cosRoll);
+        mRotationMat = {cosRoll, -sinRoll, sinRoll, cosRoll};
+    }
 }
 
 
@@ -148,6 +163,10 @@
     glUseProgram(mShaderProgram);
 
     // Set up the model to clip space transform (identity matrix if we're modeling in screen space)
+    android::vec2 leftTop = {-0.5f, 0.5f};
+    android::vec2 rightTop = {0.5f, 0.5f};
+    android::vec2 leftBottom = {-0.5f, -0.5f};
+    android::vec2 rightBottom = {0.5f, -0.5f};
     GLint loc = glGetUniformLocation(mShaderProgram, "cameraMat");
     if (loc < 0) {
         LOG(ERROR) << "Couldn't set shader parameter 'cameraMat'";
@@ -155,8 +174,13 @@
     } else {
         const android::mat4 identityMatrix;
         glUniformMatrix4fv(loc, 1, false, identityMatrix.asArray());
-    }
 
+        // Rotate the preview
+        leftTop     = mRotationMat * leftTop;
+        leftBottom  = mRotationMat * leftBottom;
+        rightTop    = mRotationMat * rightTop;
+        rightBottom = mRotationMat * rightBottom;
+    }
 
     // Bind the texture and assign it to the shader's sampler
     mTexture->refresh();
@@ -183,11 +207,22 @@
                               -1.0, -1.0, 0.0f,   // left bottom
                                1.0, -1.0, 0.0f    // right bottom
     };
-    // TODO:  We're flipping horizontally here, but should do it only for specified cameras!
-    GLfloat vertsCarTex[] = { 1.0f, 1.0f,   // left top
-                              0.0f, 1.0f,   // right top
-                              1.0f, 0.0f,   // left bottom
-                              0.0f, 0.0f    // right bottom
+
+    // Flip the preview if needed
+    if (mCameraInfo.hflip) {
+        std::swap(leftTop.x, rightTop.x);
+        std::swap(leftBottom.x, rightBottom.x);
+    }
+
+    if (mCameraInfo.vflip) {
+        std::swap(leftTop.y, leftBottom.y);
+        std::swap(rightTop.y, rightBottom.y);
+    }
+
+    GLfloat vertsCarTex[] = { leftTop.x + 0.5f, leftTop.y + 0.5f,
+                              rightTop.x + 0.5f, rightTop.y + 0.5f,
+                              leftBottom.x + 0.5f, leftBottom.y + 0.5f,
+                              rightBottom.x + 0.5f, rightBottom.y + 0.5f
     };
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
diff --git a/evs/apps/default/RenderDirectView.h b/evs/apps/default/RenderDirectView.h
index 65a94e2..2f9c11e 100644
--- a/evs/apps/default/RenderDirectView.h
+++ b/evs/apps/default/RenderDirectView.h
@@ -17,13 +17,12 @@
 #ifndef CAR_EVS_APP_RENDERDIRECTVIEW_H
 #define CAR_EVS_APP_RENDERDIRECTVIEW_H
 
-
-#include "RenderBase.h"
-
-#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
 #include "ConfigManager.h"
+#include "RenderBase.h"
 #include "VideoTex.h"
 
+#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
+#include <math/mat2.h>
 
 using namespace ::android::hardware::automotive::evs::V1_1;
 using ::android::hardware::camera::device::V3_2::Stream;
@@ -52,6 +51,8 @@
     std::unique_ptr<VideoTex>       mTexture;
 
     GLuint                          mShaderProgram = 0;
+
+    android::mat2                   mRotationMat;
 };
 
 
diff --git a/evs/apps/default/config.json b/evs/apps/default/config.json
index c914e5b..16148a7 100644
--- a/evs/apps/default/config.json
+++ b/evs/apps/default/config.json
@@ -31,8 +31,11 @@
       "z" : 48,
       "yaw" : 180,
       "pitch" : -10,
+      "roll" : 0,
       "hfov" : 115,
-      "vfov" : 80
+      "vfov" : 80,
+      "hflip" : true,
+      "vflip" : false
     },
     {
       "cameraId" : "/dev/video11",
@@ -42,8 +45,11 @@
       "z" : 48,
       "yaw" : 0,
       "pitch" : -10,
+      "roll" : 0,
       "hfov" : 115,
-      "vfov" : 80
+      "vfov" : 80,
+      "hflip" : false,
+      "vflip" : false
     },
     {
       "cameraId" : "/dev/video12",
@@ -53,8 +59,11 @@
       "z" : 88,
       "yaw" : -90,
       "pitch" : -10,
+      "roll" : 0,
       "hfov" : 60,
-      "vfov" : 62
+      "vfov" : 62,
+      "hflip" : false,
+      "vflip" : false
     },
     {
       "cameraId" : "/dev/video13",
@@ -64,8 +73,11 @@
       "z" : 88,
       "yaw" : 90,
       "pitch" : -10,
+      "roll" : 0,
       "hfov" : 60,
-      "vfov" : 62
+      "vfov" : 62,
+      "hflip" : false,
+      "vflip" : false
     }
   ]
 }
diff --git a/evs/apps/default/config.json.readme b/evs/apps/default/config.json.readme
index 561adcc..893eb86 100644
--- a/evs/apps/default/config.json.readme
+++ b/evs/apps/default/config.json.readme
@@ -38,8 +38,11 @@
       "z" : 48,                     // Optical center distance above ground
       "yaw" : 180,                  // Optical axis degrees to the left of straight ahead
       "pitch" : -30,                // Optical axis degrees above the horizon
+      "roll" : 0,                   // Rotation degrees around the optical axis
       "hfov" : 125,                 // Horizontal field of view in degrees
-      "vfov" :103                   // Vertical field of view in degrees
+      "vfov" :103,                  // Vertical field of view in degrees
+      "hflip" : true,               // Flip the view horizontally
+      "vflip" : true,               // Flip the view vertically
     }
   ]
 }