Add support for Alpha8, Gray8, and 4444 pixel formats

Gray 8 is particularly special: It's always computed as luminance (Y),
transformed by the destination transfer function. When invoked with no
color profiles, src and dst are assumed to be sRGB.

Change-Id: I2218924d2f43e1e81b3aa48db55398acea4a7a41
Reviewed-on: https://skia-review.googlesource.com/136604
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/skcms.h b/skcms.h
index 97728c4..abc604b 100644
--- a/skcms.h
+++ b/skcms.h
@@ -166,9 +166,17 @@
 };
 
 typedef enum skcms_PixelFormat {
+    skcms_PixelFormat_A_8,
+    skcms_PixelFormat_A_8_,
+    skcms_PixelFormat_G_8,
+    skcms_PixelFormat_G_8_,
+
     skcms_PixelFormat_RGB_565,
     skcms_PixelFormat_BGR_565,
 
+    skcms_PixelFormat_ABGR_4444,
+    skcms_PixelFormat_ARGB_4444,
+
     skcms_PixelFormat_RGB_888,
     skcms_PixelFormat_BGR_888,
     skcms_PixelFormat_RGBA_8888,
diff --git a/src/Transform.c b/src/Transform.c
index f07c5e7..ebfd0ce 100644
--- a/src/Transform.c
+++ b/src/Transform.c
@@ -354,6 +354,9 @@
 
 static size_t bytes_per_pixel(skcms_PixelFormat fmt) {
     switch (fmt >> 1) {   // ignore rgb/bgr
+        case skcms_PixelFormat_A_8           >> 1: return  1;
+        case skcms_PixelFormat_G_8           >> 1: return  1;
+        case skcms_PixelFormat_ABGR_4444     >> 1: return  2;
         case skcms_PixelFormat_RGB_565       >> 1: return  2;
         case skcms_PixelFormat_RGB_888       >> 1: return  3;
         case skcms_PixelFormat_RGBA_8888     >> 1: return  4;
@@ -430,6 +433,9 @@
 
     switch (srcFmt >> 1) {
         default: return false;
+        case skcms_PixelFormat_A_8           >> 1: *ops++ = Op_load_a8;       break;
+        case skcms_PixelFormat_G_8           >> 1: *ops++ = Op_load_g8;       break;
+        case skcms_PixelFormat_ABGR_4444     >> 1: *ops++ = Op_load_4444;     break;
         case skcms_PixelFormat_RGB_565       >> 1: *ops++ = Op_load_565;      break;
         case skcms_PixelFormat_RGB_888       >> 1: *ops++ = Op_load_888;      break;
         case skcms_PixelFormat_RGBA_8888     >> 1: *ops++ = Op_load_8888;     break;
@@ -444,6 +450,14 @@
     if (srcFmt & 1) {
         *ops++ = Op_swap_rb;
     }
+    skcms_ICCProfile gray_dst_profile;
+    if ((dstFmt >> 1) == (skcms_PixelFormat_G_8 >> 1)) {
+        // When transforming to gray, stop at XYZ (by setting toXYZ to identity), then transform
+        // luminance (Y) by the destination transfer function.
+        gray_dst_profile = *dstProfile;
+        skcms_SetXYZD50(&gray_dst_profile, &skcms_XYZD50_profile()->toXYZD50);
+        dstProfile = &gray_dst_profile;
+    }
 
     if (srcProfile->data_color_space == skcms_Signature_CMYK) {
         // Photoshop creates CMYK images as inverse CMYK.
@@ -585,6 +599,9 @@
     }
     switch (dstFmt >> 1) {
         default: return false;
+        case skcms_PixelFormat_A_8           >> 1: *ops++ = Op_store_a8;       break;
+        case skcms_PixelFormat_G_8           >> 1: *ops++ = Op_store_g8;       break;
+        case skcms_PixelFormat_ABGR_4444     >> 1: *ops++ = Op_store_4444;     break;
         case skcms_PixelFormat_RGB_565       >> 1: *ops++ = Op_store_565;      break;
         case skcms_PixelFormat_RGB_888       >> 1: *ops++ = Op_store_888;      break;
         case skcms_PixelFormat_RGBA_8888     >> 1: *ops++ = Op_store_8888;     break;
diff --git a/src/Transform.h b/src/Transform.h
index 1c4c15b..6eda899 100644
--- a/src/Transform.h
+++ b/src/Transform.h
@@ -11,6 +11,9 @@
 
 #define FOREACH_Op(M) \
     M(noop)           \
+    M(load_a8)        \
+    M(load_g8)        \
+    M(load_4444)      \
     M(load_565)       \
     M(load_888)       \
     M(load_8888)      \
@@ -46,6 +49,9 @@
     M(clut_3D_16)     \
     M(clut_4D_8)      \
     M(clut_4D_16)     \
+    M(store_a8)       \
+    M(store_g8)       \
+    M(store_4444)     \
     M(store_565)      \
     M(store_888)      \
     M(store_8888)     \
diff --git a/src/Transform_inl.h b/src/Transform_inl.h
index 224412b..fd3100a 100644
--- a/src/Transform_inl.h
+++ b/src/Transform_inl.h
@@ -576,6 +576,28 @@
         switch (profile_next_op(*ops++)) {
             case Op_noop: break;
 
+            case Op_load_a8:{
+                U8 alpha;
+                small_memcpy(&alpha, src + i, N);
+                a = F_from_U8(alpha);
+            } break;
+
+            case Op_load_g8:{
+                U8 gray;
+                small_memcpy(&gray, src + i, N);
+                r = g = b = F_from_U8(gray);
+            } break;
+
+            case Op_load_4444:{
+                U16 abgr;
+                small_memcpy(&abgr, src + 2*i, 2*N);
+
+                r = CAST(F, (abgr >> 12) & 0xf) * (1/15.0f);
+                g = CAST(F, (abgr >>  8) & 0xf) * (1/15.0f);
+                b = CAST(F, (abgr >>  4) & 0xf) * (1/15.0f);
+                a = CAST(F, (abgr >>  0) & 0xf) * (1/15.0f);
+            } break;
+
             case Op_load_565:{
                 U16 rgb;
                 small_memcpy(&rgb, src + 2*i, 2*N);
@@ -879,6 +901,25 @@
 
     // Notice, from here on down the store_ ops all return, ending the loop.
 
+            case Op_store_a8: {
+                U8 alpha = CAST(U8, to_fixed(a * 255));
+                small_memcpy(dst + i, &alpha, N);
+            } return;
+
+            case Op_store_g8: {
+                // g should be holding luminance (Y) (r,g,b ~~~> X,Y,Z)
+                U8 gray = CAST(U8, to_fixed(g * 255));
+                small_memcpy(dst + i, &gray, N);
+            } return;
+
+            case Op_store_4444: {
+                U16 abgr = CAST(U16, to_fixed(r * 15) << 12)
+                         | CAST(U16, to_fixed(g * 15) <<  8)
+                         | CAST(U16, to_fixed(b * 15) <<  4)
+                         | CAST(U16, to_fixed(a * 15) <<  0);
+                small_memcpy(dst + 2*i, &abgr, 2*N);
+            } return;
+
             case Op_store_565: {
                 U16 rgb = CAST(U16, to_fixed(r * 31) <<  0 )
                         | CAST(U16, to_fixed(g * 63) <<  5 )