Merge "Support 10-bit YUV in color converter for thumbnails" into pi-dev
am: 3994704062

Change-Id: I002b09c63e90e1ed4b7580acbb0a1a5eb3fe9591
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 1b38852..05f4104 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -25,7 +25,7 @@
 
 #include "libyuv/convert_from.h"
 #include "libyuv/video_common.h"
-
+#include <functional>
 #include <sys/time.h>
 
 #define USE_LIBYUV
@@ -58,14 +58,16 @@
 
 bool ColorConverter::isValid() const {
     switch (mSrcFormat) {
+        case OMX_COLOR_FormatYUV420Planar16:
+            if (mDstFormat == OMX_COLOR_FormatYUV444Y410) {
+                return true;
+            }
+            // fall-thru
         case OMX_COLOR_FormatYUV420Planar:
             return mDstFormat == OMX_COLOR_Format16bitRGB565
                     || mDstFormat == OMX_COLOR_Format32BitRGBA8888
                     || mDstFormat == OMX_COLOR_Format32bitBGRA8888;
 
-        case OMX_COLOR_FormatYUV420Planar16:
-            return mDstFormat == OMX_COLOR_FormatYUV444Y410;
-
         case OMX_COLOR_FormatCbYCrY:
         case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
         case OMX_COLOR_FormatYUV420SemiPlanar:
@@ -311,84 +313,120 @@
     return OK;
 }
 
-void ColorConverter::writeToDst(
-        void *dst_ptr, uint8_t *kAdjustedClip, bool uncropped,
-        signed r1, signed g1, signed b1,
-        signed r2, signed g2, signed b2) {
-    switch (mDstFormat) {
+std::function<void (void *, void *, void *, size_t,
+                    signed *, signed *, signed *, signed *)>
+getReadFromSrc(OMX_COLOR_FORMATTYPE srcFormat) {
+    switch(srcFormat) {
+    case OMX_COLOR_FormatYUV420Planar:
+        return [](void *src_y, void *src_u, void *src_v, size_t x,
+                  signed *y1, signed *y2, signed *u, signed *v) {
+            *y1 = ((uint8_t*)src_y)[x] - 16;
+            *y2 = ((uint8_t*)src_y)[x + 1] - 16;
+            *u = ((uint8_t*)src_u)[x / 2] - 128;
+            *v = ((uint8_t*)src_v)[x / 2] - 128;
+        };
+    case OMX_COLOR_FormatYUV420Planar16:
+        return [](void *src_y, void *src_u, void *src_v, size_t x,
+                signed *y1, signed *y2, signed *u, signed *v) {
+            *y1 = (signed)(((uint16_t*)src_y)[x] >> 2) - 16;
+            *y2 = (signed)(((uint16_t*)src_y)[x + 1] >> 2) - 16;
+            *u = (signed)(((uint16_t*)src_u)[x / 2] >> 2) - 128;
+            *v = (signed)(((uint16_t*)src_v)[x / 2] >> 2) - 128;
+        };
+    default:
+        TRESPASS();
+    }
+    return nullptr;
+}
+
+std::function<void (void *, bool, signed, signed, signed, signed, signed, signed)>
+getWriteToDst(OMX_COLOR_FORMATTYPE dstFormat, uint8_t *kAdjustedClip) {
+    switch (dstFormat) {
     case OMX_COLOR_Format16bitRGB565:
     {
-        uint32_t rgb1 =
-            ((kAdjustedClip[r1] >> 3) << 11)
-            | ((kAdjustedClip[g1] >> 2) << 5)
-            | (kAdjustedClip[b1] >> 3);
+        return [kAdjustedClip](void *dst_ptr, bool uncropped,
+                               signed r1, signed g1, signed b1,
+                               signed r2, signed g2, signed b2) {
+            uint32_t rgb1 =
+                ((kAdjustedClip[r1] >> 3) << 11)
+                | ((kAdjustedClip[g1] >> 2) << 5)
+                | (kAdjustedClip[b1] >> 3);
 
-        if (uncropped) {
-            uint32_t rgb2 =
-                ((kAdjustedClip[r2] >> 3) << 11)
-                | ((kAdjustedClip[g2] >> 2) << 5)
-                | (kAdjustedClip[b2] >> 3);
+            if (uncropped) {
+                uint32_t rgb2 =
+                    ((kAdjustedClip[r2] >> 3) << 11)
+                    | ((kAdjustedClip[g2] >> 2) << 5)
+                    | (kAdjustedClip[b2] >> 3);
 
-            *(uint32_t *)dst_ptr = (rgb2 << 16) | rgb1;
-        } else {
-            *(uint16_t *)dst_ptr = rgb1;
-        }
-        break;
+                *(uint32_t *)dst_ptr = (rgb2 << 16) | rgb1;
+            } else {
+                *(uint16_t *)dst_ptr = rgb1;
+            }
+        };
     }
     case OMX_COLOR_Format32BitRGBA8888:
     {
-        ((uint32_t *)dst_ptr)[0] =
-                (kAdjustedClip[r1])
-                | (kAdjustedClip[g1] << 8)
-                | (kAdjustedClip[b1] << 16)
-                | (0xFF << 24);
-
-        if (uncropped) {
-            ((uint32_t *)dst_ptr)[1] =
-                    (kAdjustedClip[r2])
-                    | (kAdjustedClip[g2] << 8)
-                    | (kAdjustedClip[b2] << 16)
+        return [kAdjustedClip](void *dst_ptr, bool uncropped,
+                               signed r1, signed g1, signed b1,
+                               signed r2, signed g2, signed b2) {
+            ((uint32_t *)dst_ptr)[0] =
+                    (kAdjustedClip[r1])
+                    | (kAdjustedClip[g1] << 8)
+                    | (kAdjustedClip[b1] << 16)
                     | (0xFF << 24);
-        }
-        break;
+
+            if (uncropped) {
+                ((uint32_t *)dst_ptr)[1] =
+                        (kAdjustedClip[r2])
+                        | (kAdjustedClip[g2] << 8)
+                        | (kAdjustedClip[b2] << 16)
+                        | (0xFF << 24);
+            }
+        };
     }
     case OMX_COLOR_Format32bitBGRA8888:
     {
-        ((uint32_t *)dst_ptr)[0] =
-                (kAdjustedClip[b1])
-                | (kAdjustedClip[g1] << 8)
-                | (kAdjustedClip[r1] << 16)
-                | (0xFF << 24);
-
-        if (uncropped) {
-            ((uint32_t *)dst_ptr)[1] =
-                    (kAdjustedClip[b2])
-                    | (kAdjustedClip[g2] << 8)
-                    | (kAdjustedClip[r2] << 16)
+        return [kAdjustedClip](void *dst_ptr, bool uncropped,
+                               signed r1, signed g1, signed b1,
+                               signed r2, signed g2, signed b2) {
+            ((uint32_t *)dst_ptr)[0] =
+                    (kAdjustedClip[b1])
+                    | (kAdjustedClip[g1] << 8)
+                    | (kAdjustedClip[r1] << 16)
                     | (0xFF << 24);
-        }
-        break;
+
+            if (uncropped) {
+                ((uint32_t *)dst_ptr)[1] =
+                        (kAdjustedClip[b2])
+                        | (kAdjustedClip[g2] << 8)
+                        | (kAdjustedClip[r2] << 16)
+                        | (0xFF << 24);
+            }
+        };
     }
     default:
-        break;
+        TRESPASS();
     }
+    return nullptr;
 }
+
 status_t ColorConverter::convertYUV420Planar(
         const BitmapParams &src, const BitmapParams &dst) {
     uint8_t *kAdjustedClip = initClip();
 
+    auto readFromSrc = getReadFromSrc(mSrcFormat);
+    auto writeToDst = getWriteToDst(mDstFormat, kAdjustedClip);
+
     uint8_t *dst_ptr = (uint8_t *)dst.mBits
-        + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
+            + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
 
-    const uint8_t *src_y =
-        (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
+    uint8_t *src_y = (uint8_t *)src.mBits
+            + src.mCropTop * src.mStride + src.mCropLeft * src.mBpp;
 
-    const uint8_t *src_u =
-        (const uint8_t *)src.mBits + src.mStride * src.mHeight
-        + (src.mCropTop / 2) * (src.mStride / 2) + src.mCropLeft / 2;
+    uint8_t *src_u = (uint8_t *)src.mBits + src.mStride * src.mHeight
+            + (src.mCropTop / 2) * (src.mStride / 2) + src.mCropLeft / 2 * src.mBpp;
 
-    const uint8_t *src_v =
-        src_u + (src.mStride / 2) * (src.mHeight / 2);
+    uint8_t *src_v = src_u + (src.mStride / 2) * (src.mHeight / 2);
 
     for (size_t y = 0; y < src.cropHeight(); ++y) {
         for (size_t x = 0; x < src.cropWidth(); x += 2) {
@@ -410,11 +448,8 @@
 
             // clip range -278 .. 535
 
-            signed y1 = (signed)src_y[x] - 16;
-            signed y2 = (signed)src_y[x + 1] - 16;
-
-            signed u = (signed)src_u[x / 2] - 128;
-            signed v = (signed)src_v[x / 2] - 128;
+            signed y1, y2, u, v;
+            readFromSrc(src_y, src_u, src_v, x, &y1, &y2, &u, &v);
 
             signed u_b = u * 517;
             signed u_g = -u * 100;
@@ -432,8 +467,7 @@
             signed r2 = (tmp2 + v_r) / 256;
 
             bool uncropped = x + 1 < src.cropWidth();
-            (void)writeToDst(dst_ptr + x * dst.mBpp,
-                    kAdjustedClip, uncropped, r1, g1, b1, r2, g2, b2);
+            writeToDst(dst_ptr + x * dst.mBpp, uncropped, r1, g1, b1, r2, g2, b2);
         }
 
         src_y += src.mStride;
@@ -449,6 +483,15 @@
     return OK;
 }
 
+status_t ColorConverter::convertYUV420Planar16(
+        const BitmapParams &src, const BitmapParams &dst) {
+    if (mDstFormat == OMX_COLOR_FormatYUV444Y410) {
+        return convertYUV420Planar16ToY410(src, dst);
+    }
+
+    return convertYUV420Planar(src, dst);
+}
+
 /*
  * Pack 10-bit YUV into RGBA_1010102.
  *
@@ -480,7 +523,7 @@
 
 #if !USE_NEON_Y410
 
-status_t ColorConverter::convertYUV420Planar16(
+status_t ColorConverter::convertYUV420Planar16ToY410(
         const BitmapParams &src, const BitmapParams &dst) {
     uint8_t *dst_ptr = (uint8_t *)dst.mBits
         + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
@@ -554,7 +597,7 @@
 
 #else
 
-status_t ColorConverter::convertYUV420Planar16(
+status_t ColorConverter::convertYUV420Planar16ToY410(
         const BitmapParams &src, const BitmapParams &dst) {
     uint8_t *out = (uint8_t *)dst.mBits
         + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 838bc5f..657a05b 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -79,13 +79,26 @@
         cropBottomNew = heightNew - 1;
     }
 
+    // The native window buffer format for high-bitdepth content could
+    // depend on the dataspace also.
+    android_dataspace dataSpace;
+    bool dataSpaceChangedForPlanar16 = false;
+    if (colorFormatNew == OMX_COLOR_FormatYUV420Planar16
+            && format->findInt32("android._dataspace", (int32_t *)&dataSpace)
+            && dataSpace != mDataSpace) {
+        // Do not modify mDataSpace here, it's only modified at last
+        // when we do native_window_set_buffers_data_space().
+        dataSpaceChangedForPlanar16 = true;
+    }
+
     if (static_cast<int32_t>(mColorFormat) == colorFormatNew &&
         mWidth == widthNew &&
         mHeight == heightNew &&
         mCropLeft == cropLeftNew &&
         mCropTop == cropTopNew &&
         mCropRight == cropRightNew &&
-        mCropBottom == cropBottomNew) {
+        mCropBottom == cropBottomNew &&
+        !dataSpaceChangedForPlanar16) {
         // Nothing changed, no need to reset renderer.
         return;
     }
@@ -135,11 +148,16 @@
             }
             case OMX_COLOR_FormatYUV420Planar16:
             {
-                // Here we would convert OMX_COLOR_FormatYUV420Planar16 into
-                // OMX_COLOR_FormatYUV444Y410, and put it inside a buffer with
-                // format HAL_PIXEL_FORMAT_RGBA_1010102. Surfaceflinger will
-                // use render engine to convert it to RGB if needed.
-                halFormat = HAL_PIXEL_FORMAT_RGBA_1010102;
+                if (((dataSpace & HAL_DATASPACE_STANDARD_MASK) == HAL_DATASPACE_STANDARD_BT2020)
+                 && ((dataSpace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_ST2084)) {
+                    // Here we would convert OMX_COLOR_FormatYUV420Planar16 into
+                    // OMX_COLOR_FormatYUV444Y410, and put it inside a buffer with
+                    // format HAL_PIXEL_FORMAT_RGBA_1010102. Surfaceflinger will
+                    // use render engine to convert it to RGB if needed.
+                    halFormat = HAL_PIXEL_FORMAT_RGBA_1010102;
+                } else {
+                    halFormat = HAL_PIXEL_FORMAT_YV12;
+                }
                 bufWidth = (mCropWidth + 1) & ~1;
                 bufHeight = (mCropHeight + 1) & ~1;
                 break;
@@ -155,7 +173,7 @@
         mConverter = new ColorConverter(
                 mColorFormat, OMX_COLOR_Format16bitRGB565);
         CHECK(mConverter->isValid());
-    } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar16) {
+    } else if (halFormat == HAL_PIXEL_FORMAT_RGBA_1010102) {
         mConverter = new ColorConverter(
                 mColorFormat, OMX_COLOR_FormatYUV444Y410);
         CHECK(mConverter->isValid());
@@ -300,6 +318,46 @@
             dst_u += dst_c_stride;
             dst_v += dst_c_stride;
         }
+    } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar16) {
+        const uint16_t *src_y = (const uint16_t *)data;
+        const uint16_t *src_u = (const uint16_t *)data + mWidth * mHeight;
+        const uint16_t *src_v = src_u + (mWidth / 2 * mHeight / 2);
+
+        src_y += mCropLeft + mCropTop * mWidth;
+        src_u += (mCropLeft + mCropTop * mWidth / 2) / 2;
+        src_v += (mCropLeft + mCropTop * mWidth / 2) / 2;
+
+        uint8_t *dst_y = (uint8_t *)dst;
+        size_t dst_y_size = buf->stride * buf->height;
+        size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
+        size_t dst_c_size = dst_c_stride * buf->height / 2;
+        uint8_t *dst_v = dst_y + dst_y_size;
+        uint8_t *dst_u = dst_v + dst_c_size;
+
+        dst_y += mCropTop * buf->stride + mCropLeft;
+        dst_v += (mCropTop / 2) * dst_c_stride + mCropLeft / 2;
+        dst_u += (mCropTop / 2) * dst_c_stride + mCropLeft / 2;
+
+        for (int y = 0; y < mCropHeight; ++y) {
+            for (int x = 0; x < mCropWidth; ++x) {
+                dst_y[x] = (uint8_t)(src_y[x] >> 2);
+            }
+
+            src_y += mWidth;
+            dst_y += buf->stride;
+        }
+
+        for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
+            for (int x = 0; x < (mCropWidth + 1) / 2; ++x) {
+                dst_u[x] = (uint8_t)(src_u[x] >> 2);
+                dst_v[x] = (uint8_t)(src_v[x] >> 2);
+            }
+
+            src_u += mWidth / 2;
+            src_v += mWidth / 2;
+            dst_u += dst_c_stride;
+            dst_v += dst_c_stride;
+        }
     } else if (mColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
             || mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
         const uint8_t *src_y = (const uint8_t *)data;
diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h
index a6c8981..5b3543d 100644
--- a/media/libstagefright/include/media/stagefright/ColorConverter.h
+++ b/media/libstagefright/include/media/stagefright/ColorConverter.h
@@ -75,10 +75,16 @@
     status_t convertYUV420Planar(
             const BitmapParams &src, const BitmapParams &dst);
 
+    status_t convertYUV420PlanarUseLibYUV(
+            const BitmapParams &src, const BitmapParams &dst);
+
     status_t convertYUV420Planar16(
             const BitmapParams &src, const BitmapParams &dst);
 
-    status_t convertYUV420PlanarUseLibYUV(
+    status_t convertYUV420Planar16ToY410(
+            const BitmapParams &src, const BitmapParams &dst);
+
+    status_t convertYUV420Planar16ToRGB(
             const BitmapParams &src, const BitmapParams &dst);
 
     status_t convertQCOMYUV420SemiPlanar(
@@ -90,10 +96,6 @@
     status_t convertTIYUV420PackedSemiPlanar(
             const BitmapParams &src, const BitmapParams &dst);
 
-    void writeToDst(void *dst_ptr, uint8_t *kAdjustedClip, bool uncropped,
-            signed r1, signed g1, signed b1,
-            signed r2, signed g2, signed b2);
-
     ColorConverter(const ColorConverter &);
     ColorConverter &operator=(const ColorConverter &);
 };