DngCreator: more lens distortion handling

Handling the case where RAW image contains optical black regions.

Test: visually check DNG captured on Pixel 2 camera 0.
Bug: 118690688
Change-Id: I0586c37f30744e1badc6ff1e96d22a826972d656
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 1c247cb..7052ed6 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -1008,9 +1008,11 @@
         double x, double y,
         const std::array<float, 6>& distortion,
         const float cx, const float cy, const float f,
-        int preCorrW, int preCorrH) {
+        const int preCorrW, const int preCorrH, const int xMin, const int yMin) {
     undistort(x, y, distortion, cx, cy, f);
-    if (x < 0.0 || y < 0.0 || x > preCorrW - 1 || y > preCorrH - 1) {
+    int xMax = xMin + preCorrW - 1;
+    int yMax = yMin + preCorrH - 1;
+    if (x < xMin || y < yMin || x > xMax || y > yMax) {
         return false;
     }
     return true;
@@ -1019,40 +1021,48 @@
 static inline bool boxWithinPrecorrectionArray(
         int left, int top, int right, int bottom,
         const std::array<float, 6>& distortion,
-        const float& cx, const float& cy, const float& f,
-        const int& preCorrW, const int& preCorrH){
+        const float cx, const float cy, const float f,
+        const int preCorrW, const int preCorrH, const int xMin, const int yMin){
     // Top row
-    if (!unDistortWithinPreCorrArray(left, top, distortion, cx, cy, f, preCorrW, preCorrH)) {
+    if (!unDistortWithinPreCorrArray(left, top,
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
         return false;
     }
 
-    if (!unDistortWithinPreCorrArray(cx, top, distortion, cx, cy, f, preCorrW, preCorrH)) {
+    if (!unDistortWithinPreCorrArray(cx, top,
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
         return false;
     }
 
-    if (!unDistortWithinPreCorrArray(right, top, distortion, cx, cy, f, preCorrW, preCorrH)) {
+    if (!unDistortWithinPreCorrArray(right, top,
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
         return false;
     }
 
     // Middle row
-    if (!unDistortWithinPreCorrArray(left, cy, distortion, cx, cy, f, preCorrW, preCorrH)) {
+    if (!unDistortWithinPreCorrArray(left, cy,
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
         return false;
     }
 
-    if (!unDistortWithinPreCorrArray(right, cy, distortion, cx, cy, f, preCorrW, preCorrH)) {
+    if (!unDistortWithinPreCorrArray(right, cy,
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
         return false;
     }
 
     // Bottom row
-    if (!unDistortWithinPreCorrArray(left, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) {
+    if (!unDistortWithinPreCorrArray(left, bottom,
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
         return false;
     }
 
-    if (!unDistortWithinPreCorrArray(cx, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) {
+    if (!unDistortWithinPreCorrArray(cx, bottom,
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
         return false;
     }
 
-    if (!unDistortWithinPreCorrArray(right, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) {
+    if (!unDistortWithinPreCorrArray(right, bottom,
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
         return false;
     }
     return true;
@@ -1062,7 +1072,8 @@
         double scale/*must be <= 1.0*/,
         const std::array<float, 6>& distortion,
         const float cx, const float cy, const float f,
-        const int preCorrW, const int preCorrH){
+        const int preCorrW, const int preCorrH,
+        const int xMin, const int yMin){
 
     double left = cx * (1.0 - scale);
     double right = (preCorrW - 1) * scale + cx * (1.0 - scale);
@@ -1070,14 +1081,14 @@
     double bottom = (preCorrH - 1) * scale + cy * (1.0 - scale);
 
     return boxWithinPrecorrectionArray(left, top, right, bottom,
-            distortion, cx, cy, f, preCorrW, preCorrH);
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin);
 }
 
 static status_t findPostCorrectionScale(
         double stepSize, double minScale,
         const std::array<float, 6>& distortion,
         const float cx, const float cy, const float f,
-        const int preCorrW, const int preCorrH,
+        const int preCorrW, const int preCorrH, const int xMin, const int yMin,
         /*out*/ double* outScale) {
     if (outScale == nullptr) {
         ALOGE("%s: outScale must not be null", __FUNCTION__);
@@ -1086,7 +1097,7 @@
 
     for (double scale = 1.0; scale > minScale; scale -= stepSize) {
         if (scaledBoxWithinPrecorrectionArray(
-                scale, distortion, cx, cy, f, preCorrW, preCorrH)) {
+                scale, distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
             *outScale = scale;
             return OK;
         }
@@ -1100,16 +1111,18 @@
 // are sampled within the precorrection array
 static void normalizeLensDistortion(
         /*inout*/std::array<float, 6>& distortion,
-        float cx, float cy, float f, int preCorrW, int preCorrH) {
-    ALOGV("%s: distortion [%f, %f, %f, %f, %f, %f], (cx,cy) (%f, %f), f %f, (W,H) (%d, %d)",
+        float cx, float cy, float f, int preCorrW, int preCorrH, int xMin = 0, int yMin = 0) {
+    ALOGV("%s: distortion [%f, %f, %f, %f, %f, %f], (cx,cy) (%f, %f), f %f, (W,H) (%d, %d)"
+            ", (xmin, ymin, xmax, ymax) (%d, %d, %d, %d)",
             __FUNCTION__, distortion[0], distortion[1], distortion[2],
             distortion[3], distortion[4], distortion[5],
-            cx, cy, f, preCorrW, preCorrH);
+            cx, cy, f, preCorrW, preCorrH,
+            xMin, yMin, xMin + preCorrW - 1, yMin + preCorrH - 1);
 
     // Only update distortion coeffients if we can find a good bounding box
     double scale = 1.0;
     if (OK == findPostCorrectionScale(0.002, 0.5,
-            distortion, cx, cy, f, preCorrW, preCorrH,
+            distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin,
             /*out*/&scale)) {
         ALOGV("%s: scaling distortion coefficients by %f", __FUNCTION__, scale);
         // The formula:
@@ -1216,6 +1229,8 @@
 
     sp<TiffWriter> writer = new TiffWriter();
 
+    uint32_t preXMin = 0;
+    uint32_t preYMin = 0;
     uint32_t preWidth = 0;
     uint32_t preHeight = 0;
     uint8_t colorFilter = 0;
@@ -1225,6 +1240,8 @@
         camera_metadata_entry entry =
                 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
         BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
+        preXMin = static_cast<uint32_t>(entry.data.i32[0]);
+        preYMin = static_cast<uint32_t>(entry.data.i32[1]);
         preWidth = static_cast<uint32_t>(entry.data.i32[2]);
         preHeight = static_cast<uint32_t>(entry.data.i32[3]);
 
@@ -1966,9 +1983,16 @@
                     distortion[i+1] = entry3.data.f[i];
                 }
 
-                // TODO b/118690688: deal with the case where RAW size != preCorrSize
                 if (preWidth == imageWidth && preHeight == imageHeight) {
                     normalizeLensDistortion(distortion, cx, cy, f, preWidth, preHeight);
+                } else {
+                    // image size == pixel array size (contains optical black pixels)
+                    // cx/cy is defined in preCorrArray so adding the offset
+                    // Also changes default xmin/ymin so that pixels are only
+                    // sampled within preCorrection array
+                    normalizeLensDistortion(
+                            distortion, cx + preXMin, cy + preYMin, f, preWidth, preHeight,
+                            preXMin, preYMin);
                 }
 
                 float m_x = std::fmaxf(preWidth-1 - cx, cx);