Unify the YUV_420_888 as NV21 in emulator HALs.

Historically, our HAL treats format YUV_420_888 differently for
Camera (NV21) and non-Camera(YV12). This introduced code
complexity/workaround and issues such as displaying black camera
images on emulator if using format as YUV420_888.

Bug: 129974322
Bug: 135953734
Bug: 129973408

Test: CtsCameraTestCases CtsNativeHardwareTestCases
Change-Id: I404344290a918b0925f9c80bd06150e89f8d90a1
diff --git a/system/OpenglSystemCommon/EmulatorFeatureInfo.h b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
index b2add4a..dd9f444 100644
--- a/system/OpenglSystemCommon/EmulatorFeatureInfo.h
+++ b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
@@ -85,6 +85,9 @@
 // Vulkan create resources with requirements
 static const char kVulkanCreateResourcesWithRequirements[] = "ANDROID_EMU_vulkan_create_resources_with_requirements";
 
+// YUV420_888 to NV21
+static const char kYUV420888toNV21[] = "ANDROID_EMU_YUV420_888_to_NV21";
+
 // Struct describing available emulator features
 struct EmulatorFeatureInfo {
 
@@ -97,7 +100,8 @@
         hasVulkan(false),
         hasDeferredVulkanCommands(false),
         hasVulkanNullOptionalStrings(false),
-        hasVulkanCreateResourcesWithRequirements(false) { }
+        hasVulkanCreateResourcesWithRequirements(false),
+        hasYUV420888toNV21(false) { }
 
     SyncImpl syncImpl;
     DmaImpl dmaImpl;
@@ -108,6 +112,7 @@
     bool hasDeferredVulkanCommands;
     bool hasVulkanNullOptionalStrings;
     bool hasVulkanCreateResourcesWithRequirements;
+    bool hasYUV420888toNV21;
 };
 
 #endif // __COMMON_EMULATOR_FEATURE_INFO_H
diff --git a/system/OpenglSystemCommon/FormatConversions.cpp b/system/OpenglSystemCommon/FormatConversions.cpp
index 92d7f0b..4b5c31c 100644
--- a/system/OpenglSystemCommon/FormatConversions.cpp
+++ b/system/OpenglSystemCommon/FormatConversions.cpp
@@ -55,9 +55,9 @@
                                 uint32_t* totalSz_out) {
     uint32_t align = 1;
     uint32_t yStride = (width + (align - 1)) & ~(align-1);
-    uint32_t uvStride = (yStride / 2 + (align - 1)) & ~(align-1);
+    uint32_t uvStride = yStride;
     uint32_t uvHeight = height / 2;
-    uint32_t sz = yStride * height + 2 * (uvHeight * uvStride);
+    uint32_t sz = yStride * height + uvHeight * uvStride;
 
     if (yStride_out) *yStride_out = yStride;
     if (cStride_out) *cStride_out = uvStride;
@@ -218,6 +218,44 @@
         }
     }
 }
+
+//HAL_PIXEL_FORMAT_YCbCr_420_888, or yuv420p is treated as NV21 across our
+//gralloc and camera module when feature YUV420888toNV21 enabled
+void rgb888_to_nv21(char* dest, char* src, int width, int height,
+                    int left, int top, int right, int bottom) {
+    const int rgb_stride = 3;
+
+    DD("%s convert %d by %d", __func__, width, height);
+    int yStride = width;
+    int cStride = yStride;
+    int yOffset = 0;
+
+    uint8_t *rgb_ptr0 = (uint8_t *)src;
+    uint8_t *nv21_y0 = (uint8_t *)dest;
+    uint8_t *nv21_v0 = nv21_y0 + yStride * height;
+
+    for (int j = top; j <= bottom; ++j) {
+        uint8_t *nv21_y = nv21_y0 + j * yStride;
+        uint8_t *nv21_v = nv21_v0 + (j/2) * cStride;
+        uint8_t *nv21_u = nv21_v + 1;
+        uint8_t *rgb_ptr = rgb_ptr0 + get_rgb_offset(j, width, rgb_stride);
+        bool jeven = (j & 1) == 0;
+        for (int i = left; i <= right; ++i) {
+            uint8_t R = rgb_ptr[i*rgb_stride];
+            uint8_t G = rgb_ptr[i*rgb_stride+1];
+            uint8_t B = rgb_ptr[i*rgb_stride+2];
+            // convert to YV12
+            // frameworks/base/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+            nv21_y[i] = clamp_rgb((77 * R + 150 * G +  29 * B) >> 8);
+            bool ieven = (i & 1) == 0;
+            if (jeven && ieven) {
+                nv21_u[i] = clamp_rgb((( -43 * R - 85 * G + 128 * B) >> 8) + 128);
+                nv21_v[i] = clamp_rgb((( 128 * R - 107 * G - 21 * B) >> 8) + 128);
+            }
+        }
+    }
+}
+
 // YV12 is aka YUV420Planar, or YUV420p; the only difference is that YV12 has
 // certain stride requirements for Y and UV respectively.
 void yv12_to_rgb565(char* dest, char* src, int width, int height,
@@ -305,8 +343,6 @@
     }
 }
 
-// YV12 is aka YUV420Planar, or YUV420p; the only difference is that YV12 has
-// certain stride requirements for Y and UV respectively.
 void yuv420p_to_rgb888(char* dest, char* src, int width, int height,
         int left, int top, int right, int bottom) {
     const int rgb_stride = 3;
@@ -350,6 +386,50 @@
     }
 }
 
+//HAL_PIXEL_FORMAT_YCbCr_420_888, or yuv420p is treated as NV21 across our
+//gralloc and camera module when feature YUV420888toNV21 enabled
+void nv21_to_rgb888(char* dest, char* src, int width, int height,
+                    int left, int top, int right, int bottom) {
+    const int rgb_stride = 3;
+
+    DD("%s convert %d by %d", __func__, width, height);
+    int yStride = width;
+    int cStride = yStride;
+    int yOffset = 0;
+
+    uint8_t *rgb_ptr0 = (uint8_t *)dest;
+    uint8_t *nv21_y0 = (uint8_t *)src;
+    uint8_t *nv21_v0 = nv21_y0 + yStride * height;
+
+    for (int j = top; j <= bottom; ++j) {
+        uint8_t *nv21_y = nv21_y0 + j * yStride;
+        uint8_t *nv21_v = nv21_v0 + (j/2) * cStride;
+        uint8_t *nv21_u = nv21_v + 1;
+        uint8_t *rgb_ptr = rgb_ptr0 + get_rgb_offset(j - top, right - left + 1, rgb_stride);
+        for (int i = left; i <= right; ++i) {
+            // convert to rgb
+            // frameworks/av/media/libstagefright/colorconversion/ColorConverter.cpp
+            signed y1 = (signed)nv21_y[i] - 16;
+            signed u = (signed)nv21_u[i / 2] - 128;
+            signed v = (signed)nv21_v[i / 2] - 128;
+
+            signed u_b = u * 517;
+            signed u_g = -u * 100;
+            signed v_g = -v * 208;
+            signed v_r = v * 409;
+
+            signed tmp1 = y1 * 298;
+            signed b1 = clamp_rgb((tmp1 + u_b) / 256);
+            signed g1 = clamp_rgb((tmp1 + v_g + u_g) / 256);
+            signed r1 = clamp_rgb((tmp1 + v_r) / 256);
+
+            rgb_ptr[(i-left)*rgb_stride] = r1;
+            rgb_ptr[(i-left)*rgb_stride+1] = g1;
+            rgb_ptr[(i-left)*rgb_stride+2] = b1;
+        }
+    }
+}
+
 void copy_rgb_buffer_from_unlocked(
         char* _dst, char* raw_data,
         int unlockedWidth,
diff --git a/system/OpenglSystemCommon/FormatConversions.h b/system/OpenglSystemCommon/FormatConversions.h
index 6e15f36..196ca37 100644
--- a/system/OpenglSystemCommon/FormatConversions.h
+++ b/system/OpenglSystemCommon/FormatConversions.h
@@ -33,12 +33,16 @@
                     int left, int top, int right, int bottom);
 void rgb888_to_yuv420p(char* dest, char* src, int width, int height,
                        int left, int top, int right, int bottom);
+void rgb888_to_nv21(char* dest, char* src, int width, int height,
+                    int left, int top, int right, int bottom);
 void yv12_to_rgb565(char* dest, char* src, int width, int height,
                     int left, int top, int right, int bottom);
 void yv12_to_rgb888(char* dest, char* src, int width, int height,
                     int left, int top, int right, int bottom);
 void yuv420p_to_rgb888(char* dest, char* src, int width, int height,
                        int left, int top, int right, int bottom);
+void nv21_to_rgb888(char* dest, char* src, int width, int height,
+                    int left, int top, int right, int bottom);
 void copy_rgb_buffer_from_unlocked(char* _dst, char* raw_data,
                                    int unlockedWidth,
                                    int width, int height, int top, int left,
diff --git a/system/OpenglSystemCommon/HostConnection.cpp b/system/OpenglSystemCommon/HostConnection.cpp
index cfe31b5..aafd9aa 100644
--- a/system/OpenglSystemCommon/HostConnection.cpp
+++ b/system/OpenglSystemCommon/HostConnection.cpp
@@ -295,6 +295,7 @@
         queryAndSetDeferredVulkanCommandsSupport(m_rcEnc);
         queryAndSetVulkanNullOptionalStringsSupport(m_rcEnc);
         queryAndSetVulkanCreateResourcesWithRequirementsSupport(m_rcEnc);
+        queryAndSetYUV420888toNV21(m_rcEnc);
         if (m_processPipe) {
             m_processPipe->processPipeInit(m_rcEnc);
         }
@@ -464,3 +465,10 @@
         rcEnc->featureInfo()->hasVulkanCreateResourcesWithRequirements = true;
     }
 }
+
+void HostConnection::queryAndSetYUV420888toNV21(ExtendedRCEncoderContext* rcEnc) {
+    std::string glExtensions = queryGLExtensions(rcEnc);
+    if (glExtensions.find(kYUV420888toNV21) != std::string::npos) {
+        rcEnc->featureInfo()->hasYUV420888toNV21 = true;
+    }
+}
diff --git a/system/OpenglSystemCommon/HostConnection.h b/system/OpenglSystemCommon/HostConnection.h
index 228aebd..5ba85dd 100644
--- a/system/OpenglSystemCommon/HostConnection.h
+++ b/system/OpenglSystemCommon/HostConnection.h
@@ -57,6 +57,8 @@
     bool hasNativeSyncV3() const { return m_featureInfo.syncImpl >= SYNC_IMPL_NATIVE_SYNC_V3; }
     bool hasHostCompositionV1() const {
         return m_featureInfo.hostComposition == HOST_COMPOSITION_V1; }
+    bool hasYUV420toNV21() const {
+        return m_featureInfo.hasYUV420888toNV21; }
     DmaImpl getDmaVersion() const { return m_featureInfo.dmaImpl; }
     void bindDmaContext(struct goldfish_dma_context* cxt) { m_dmaCxt = cxt; }
     void bindAddressSpaceBlock(GoldfishAddressSpaceBlock* block) {
@@ -194,6 +196,7 @@
     void queryAndSetDeferredVulkanCommandsSupport(ExtendedRCEncoderContext *rcEnc);
     void queryAndSetVulkanNullOptionalStringsSupport(ExtendedRCEncoderContext *rcEnc);
     void queryAndSetVulkanCreateResourcesWithRequirementsSupport(ExtendedRCEncoderContext *rcEnc);
+    void queryAndSetYUV420888toNV21(ExtendedRCEncoderContext *mrcEnc);
 
 private:
     IOStream *m_stream;
diff --git a/system/gralloc/gralloc.cpp b/system/gralloc/gralloc.cpp
index 8b1a0de..b67800e 100644
--- a/system/gralloc/gralloc.cpp
+++ b/system/gralloc/gralloc.cpp
@@ -490,12 +490,17 @@
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
             convertedBuf.resize(rgbSz);
             to_send = &convertedBuf.front();
-            yuv420p_to_rgb888(to_send, pixels,
-                              width, height, left, top,
-                              left + width - 1, top + height - 1);
+            if (rcEnc->hasYUV420toNV21()) {
+                nv21_to_rgb888(to_send, pixels,
+                               width, height, left, top,
+                               left + width - 1, top + height - 1);
+            } else {
+                yuv420p_to_rgb888(to_send, pixels,
+                                  width, height, left, top,
+                                  left + width - 1, top + height - 1);
+            }
             break;
         }
-
         rcEnc->rcUpdateColorBuffer(rcEnc, cb->hostHandle,
                 left, top, width, height,
                 cb->glFormat, cb->glType, to_send);
@@ -1273,7 +1278,11 @@
                     D("convert rgb888 to yv12 here");
                     rgb888_to_yv12((char*)cpu_addr, tmpBuf, cb->width, cb->height, l, t, l+w-1, t+h-1);
                 } else if (cb->frameworkFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) {
-                    rgb888_to_yuv420p((char*)cpu_addr, tmpBuf, cb->width, cb->height, l, t, l+w-1, t+h-1);
+                    if (rcEnc->hasYUV420toNV21()) {
+                        rgb888_to_nv21((char*)cpu_addr, tmpBuf, cb->width, cb->height, l, t, l+w-1, t+h-1);
+                    } else {
+                        rgb888_to_yuv420p((char*)cpu_addr, tmpBuf, cb->width, cb->height, l, t, l+w-1, t+h-1);
+                    }
                 }
                 delete [] tmpBuf;
             }
@@ -1423,8 +1432,9 @@
             uOffset = vOffset + cSize;
             cStep = 1;
             break;
-        case HAL_PIXEL_FORMAT_YCbCr_420_888:
-            if (usage & GRALLOC_USAGE_HW_CAMERA_MASK) {
+        case HAL_PIXEL_FORMAT_YCbCr_420_888: {
+            DEFINE_AND_VALIDATE_HOST_CONNECTION
+            if (rcEnc->hasYUV420toNV21()) {
                 yStride = cb->width;
                 cStride = cb->width;
                 yOffset = 0;
@@ -1432,14 +1442,24 @@
                 uOffset = vOffset + 1;
                 cStep = 2;
             } else {
-                yStride = cb->width;
-                cStride = yStride / 2;
-                yOffset = 0;
-                uOffset = cb->height * yStride;
-                vOffset = uOffset + cStride * cb->height / 2;
-                cStep = 1;
+                if (usage & GRALLOC_USAGE_HW_CAMERA_MASK) {
+                    yStride = cb->width;
+                    cStride = cb->width;
+                    yOffset = 0;
+                    vOffset = yStride * cb->height;
+                    uOffset = vOffset + 1;
+                    cStep = 2;
+                } else {
+                    yStride = cb->width;
+                    cStride = yStride / 2;
+                    yOffset = 0;
+                    uOffset = cb->height * yStride;
+                    vOffset = uOffset + cStride * cb->height / 2;
+                    cStep = 1;
+                }
             }
             break;
+        }
         default:
             ALOGE("gralloc_lock_ycbcr unexpected internal format %x",
                     cb->format);