drm_hwcomposer: Correct CTM conversion

Correctly compute the CTM given the HAL interface.

HAL provides a 4x4 float type matrix:
| 0  1  2  3|
| 4  5  6  7|
| 8  9 10 11|
|12 13 14 15|

This is to be interpreted as
out   matrix     in    offset
|R|   |0 4  8|   |R|   |12|
|G| = |1 5  9| x |G| + |13|
|B|   |2 6 10|   |B|   |14|

DRM expects a 3x3 s31.32 fixed point matrix interpreted as
|R|   |0 1 2|   |R|
|G| = |3 4 5| x |G|
|B|   |6 7 8|   |B|

DRM does not support an offset vector, so we reject any color transform
matrices that have one.

Change-Id: I323fde281c8a97cf319bb9774eb232abcc8bde34
Signed-off-by: Sasha McIntosh <sashamcintosh@google.com>
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 3b9dfd9..7772175 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -709,6 +709,15 @@
 
 #include <xf86drmMode.h>
 
+static uint64_t To3132FixPt(float in) {
+  constexpr uint64_t kSignMask = (1ULL << 63);
+  constexpr uint64_t kValueMask = ~(1ULL << 63);
+  constexpr auto kValueScale = static_cast<float>(1ULL << 32);
+  if (in < 0)
+    return (static_cast<uint64_t>(-in * kValueScale) & kValueMask) | kSignMask;
+  return static_cast<uint64_t>(in * kValueScale) & kValueMask;
+}
+
 HWC2::Error HwcDisplay::SetColorTransform(const float *matrix, int32_t hint) {
   if (hint < HAL_COLOR_TRANSFORM_IDENTITY ||
       hint > HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA)
@@ -730,14 +739,37 @@
       SetColorMatrixToIdentity();
       break;
     case HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX:
+      // Without HW support, we cannot correctly process matrices with an offset.
+      for (int i = 12; i < 14; i++) {
+        if (matrix[i] != 0.F)
+          return HWC2::Error::Unsupported;
+      }
+
+      /* HAL provides a 4x4 float type matrix:
+       * | 0  1  2  3|
+       * | 4  5  6  7|
+       * | 8  9 10 11|
+       * |12 13 14 15|
+       *
+       * R_out = R*0 + G*4 + B*8 + 12
+       * G_out = R*1 + G*5 + B*9 + 13
+       * B_out = R*2 + G*6 + B*10 + 14
+       *
+       * DRM expects a 3x3 s31.32 fixed point matrix:
+       * out   matrix    in
+       * |R|   |0 1 2|   |R|
+       * |G| = |3 4 5| x |G|
+       * |B|   |6 7 8|   |B|
+       *
+       * R_out = R*0 + G*1 + B*2
+       * G_out = R*3 + G*4 + B*5
+       * B_out = R*6 + G*7 + B*8
+       */
       color_matrix_ = std::make_shared<drm_color_ctm>();
-      /* DRM expects a 3x3 matrix, but the HAL provides a 4x4 matrix. */
       for (int i = 0; i < kCtmCols; i++) {
         for (int j = 0; j < kCtmRows; j++) {
           constexpr int kInCtmRows = 4;
-          /* HAL matrix type is float, but DRM expects a s31.32 fix point */
-          auto value = uint64_t(matrix[i * kInCtmRows + j] * float(1ULL << 32));
-          color_matrix_->matrix[i * kCtmRows + j] = value;
+          color_matrix_->matrix[i * kCtmRows + j] = To3132FixPt(matrix[j * kInCtmRows + i]);
         }
       }
       break;