support f16norm in skcms

Change-Id: Ic1699f8d4180b23e9b2f3aa336991207cc175894
Reviewed-on: https://skia-review.googlesource.com/c/skcms/+/199978
Commit-Queue: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: Mike Klein <mtklein@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/skcms.cc b/skcms.cc
index b965c56..874b55d 100644
--- a/skcms.cc
+++ b/skcms.cc
@@ -1984,6 +1984,8 @@
         case skcms_PixelFormat_RGBA_16161616LE    >> 1: return  8;
         case skcms_PixelFormat_RGB_161616BE       >> 1: return  6;
         case skcms_PixelFormat_RGBA_16161616BE    >> 1: return  8;
+        case skcms_PixelFormat_RGB_hhh_Norm       >> 1: return  6;
+        case skcms_PixelFormat_RGBA_hhhh_Norm     >> 1: return  8;
         case skcms_PixelFormat_RGB_hhh            >> 1: return  6;
         case skcms_PixelFormat_RGBA_hhhh          >> 1: return  8;
         case skcms_PixelFormat_RGB_fff            >> 1: return 12;
@@ -2083,6 +2085,8 @@
         case skcms_PixelFormat_RGBA_16161616LE >> 1: *ops++ = Op_load_16161616LE; break;
         case skcms_PixelFormat_RGB_161616BE    >> 1: *ops++ = Op_load_161616BE;   break;
         case skcms_PixelFormat_RGBA_16161616BE >> 1: *ops++ = Op_load_16161616BE; break;
+        case skcms_PixelFormat_RGB_hhh_Norm    >> 1: *ops++ = Op_load_hhh;        break;
+        case skcms_PixelFormat_RGBA_hhhh_Norm  >> 1: *ops++ = Op_load_hhhh;       break;
         case skcms_PixelFormat_RGB_hhh         >> 1: *ops++ = Op_load_hhh;        break;
         case skcms_PixelFormat_RGBA_hhhh       >> 1: *ops++ = Op_load_hhhh;       break;
         case skcms_PixelFormat_RGB_fff         >> 1: *ops++ = Op_load_fff;        break;
@@ -2092,6 +2096,10 @@
                                                         *args++ = palette;
                                                         break;
     }
+    if (srcFmt == skcms_PixelFormat_RGB_hhh_Norm ||
+        srcFmt == skcms_PixelFormat_RGBA_hhhh_Norm) {
+        *ops++ = Op_clamp;
+    }
     if (srcFmt & 1) {
         *ops++ = Op_swap_rb;
     }
@@ -2213,8 +2221,8 @@
         if (!is_identity_tf(&inv_dst_tf_b)) { *ops++ = Op_tf_b; *args++ = &inv_dst_tf_b; }
     }
 
-    // Clamp here before premul to make sure we're clamping to fixed-point values _and_ gamut,
-    // not just to values that fit in the fixed point representation.
+    // Clamp here before premul to make sure we're clamping to normalized values _and_ gamut,
+    // not just to values that fit in [0,1].
     //
     // E.g. r = 1.1, a = 0.5 would fit fine in fixed point after premul (ra=0.55,a=0.5),
     // but would be carrying r > 1, which is really unexpected for downstream consumers.
@@ -2242,6 +2250,8 @@
         case skcms_PixelFormat_RGBA_16161616LE >> 1: *ops++ = Op_store_16161616LE; break;
         case skcms_PixelFormat_RGB_161616BE    >> 1: *ops++ = Op_store_161616BE;   break;
         case skcms_PixelFormat_RGBA_16161616BE >> 1: *ops++ = Op_store_16161616BE; break;
+        case skcms_PixelFormat_RGB_hhh_Norm    >> 1: *ops++ = Op_store_hhh;        break;
+        case skcms_PixelFormat_RGBA_hhhh_Norm  >> 1: *ops++ = Op_store_hhhh;       break;
         case skcms_PixelFormat_RGB_hhh         >> 1: *ops++ = Op_store_hhh;        break;
         case skcms_PixelFormat_RGBA_hhhh       >> 1: *ops++ = Op_store_hhhh;       break;
         case skcms_PixelFormat_RGB_fff         >> 1: *ops++ = Op_store_fff;        break;
diff --git a/skcms.h b/skcms.h
index d924f34..6ca8236 100644
--- a/skcms.h
+++ b/skcms.h
@@ -208,6 +208,11 @@
     skcms_PixelFormat_RGBA_16161616 = skcms_PixelFormat_RGBA_16161616BE,
     skcms_PixelFormat_BGRA_16161616 = skcms_PixelFormat_BGRA_16161616BE,
 
+    skcms_PixelFormat_RGB_hhh_Norm,   // 1-5-10 half-precision float in [0,1]
+    skcms_PixelFormat_BGR_hhh_Norm,   // Pointers must be 16-bit aligned.
+    skcms_PixelFormat_RGBA_hhhh_Norm,
+    skcms_PixelFormat_BGRA_hhhh_Norm,
+
     skcms_PixelFormat_RGB_hhh,        // 1-5-10 half-precision float.
     skcms_PixelFormat_BGR_hhh,        // Pointers must be 16-bit aligned.
     skcms_PixelFormat_RGBA_hhhh,
diff --git a/tests.c b/tests.c
index 9e217a8..2f7ade4 100644
--- a/tests.c
+++ b/tests.c
@@ -434,6 +434,42 @@
     expect(back[5] == src[6] || back[5] == 0x0000);
 }
 
+static void test_FormatConversions_half_norm() {
+    const uint16_t src[] = {
+        0x3800,  //  0.5
+        0x3c00,  //  1.0
+        0xbc00,  // -1.0
+        0x4000,  //  2.0
+    };
+    uint16_t dst[ARRAY_COUNT(src)];
+
+    const skcms_AlphaFormat upm = skcms_AlphaFormat_Unpremul;
+
+    // No-op, no clamp, should preserve all values.
+    expect(skcms_Transform(&src, skcms_PixelFormat_RGBA_hhhh, upm, NULL,
+                           &dst, skcms_PixelFormat_RGBA_hhhh, upm, NULL, 1));
+    expect(dst[0] == src[0]);
+    expect(dst[1] == src[1]);
+    expect(dst[2] == src[2]);
+    expect(dst[3] == src[3]);
+
+    // Clamp on read.
+    expect(skcms_Transform(&src, skcms_PixelFormat_RGBA_hhhh_Norm, upm, NULL,
+                           &dst, skcms_PixelFormat_RGBA_hhhh     , upm, NULL, 1));
+    expect(dst[0] == src[0]);
+    expect(dst[1] == src[1]);
+    expect(dst[2] == 0x0000);
+    expect(dst[3] == src[1]);
+
+    // Clamp on write.
+    expect(skcms_Transform(&src, skcms_PixelFormat_RGBA_hhhh     , upm, NULL,
+                           &dst, skcms_PixelFormat_RGBA_hhhh_Norm, upm, NULL, 1));
+    expect(dst[0] == src[0]);
+    expect(dst[1] == src[1]);
+    expect(dst[2] == 0x0000);
+    expect(dst[3] == src[1]);
+}
+
 static void test_FormatConversions_float() {
     float src[] = { 1.0f, 0.5f, 1/255.0f, 1/512.0f };
 
@@ -1305,6 +1341,7 @@
     test_FormatConversions_161616BE();
     test_FormatConversions_101010();
     test_FormatConversions_half();
+    test_FormatConversions_half_norm();
     test_FormatConversions_float();
     test_Parse(regenTestData);
     test_ApproximateCurve_clamped();