Actually support profiles with 1D and 2D CLUTs

Parse accepts these, but transform would fail unless input_channels was
3 or 4. The other option would be to fail in parse. We're still pretty
sloppy about channel counts in data (pixel formats) vs. what the profile
expects. This is obviously a garbage image created by a fuzzer, but are
there situations where this is useful?

Bug: chromium:874433
Change-Id: Ie9672024e5d7c6b4b6f6a856f9d1454fb53a6cbb
Reviewed-on: https://skia-review.googlesource.com/147203
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/profiles/fuzz/one_d_clut.icc b/profiles/fuzz/one_d_clut.icc
new file mode 100644
index 0000000..8d8100d
--- /dev/null
+++ b/profiles/fuzz/one_d_clut.icc
Binary files differ
diff --git a/profiles/fuzz/one_d_clut.icc.txt b/profiles/fuzz/one_d_clut.icc.txt
new file mode 100644
index 0000000..5b278d5
--- /dev/null
+++ b/profiles/fuzz/one_d_clut.icc.txt
@@ -0,0 +1,45 @@
+                Size : 0x00000FD8 : 4056
+    Data color space : 0x52474220 : 'RGB '
+                 PCS : 0x58595A20 : 'XYZ '
+           Tag count : 0x00000008 : 8
+
+ Tag    : Type   : Size   : Offset
+ ------ : ------ : ------ : --------
+ 'cprt' : 'mluc' :    110 : 228
+ 'desc' : 'mluc' :     48 : 340
+ 'wtpt' : 'XYZ ' :     20 : 388
+ 'bkpt' : 'XYZ ' :     20 : 408
+ 'chad' : 'sf32' :     44 : 428
+ 'A2B0' : 'mAB ' :   1792 : 472
+ 'A2B2' : 'mAB ' :   1792 : 472
+ 'A2B1' : 'mAF ' :   1792 : 2264
+
+ A2B : "A", CLUT, "M", Matrix, "B"
+ "A" : 1 inputs
+  A0 : 16-bit table with 4 entries
+CLUT : 2 (16 bpp)
+ "M" : 3 inputs
+  M0 : 16-bit table with 256 entries
+  ~= : 13.98541, 0.5743484, 0.6578237, 0.9400777, 0.05882353, 0.04953171, 0 (Max error: 0.410781) (D-gap: -7.51019e-06)
+  M1 : 16-bit table with 256 entries
+  M2 : 16-bit table with 256 entries
+Mtrx : | 2.202053785 1.944855571 4.722492695 -0.733356714 |
+       | 1.123395920 3.620794296 0.306147665 -0.760608494 |
+       | 0.070189357 0.490257412 3.605535746 -0.627431810 |
+ "B" : 3 outputs
+  B0 : 1, 1, 0, 0, 0, 0, 0 (~Identity)
+  B1 : 1, 1, 0, 0, 0, 0, 0 (~Identity)
+  B2 : 1, 1, 0, 0, 0, 0, 0 (~Identity)
+252 random bytes transformed to linear XYZD50 bytes:
+	000000 000800 000400 000000 000000 000800 000000
+	000800 000000 000800 000800 000600 000000 000600
+	000800 000000 000800 000200 000300 000800 000800
+	000800 000000 000800 000800 000800 000000 000000
+	000600 000500 000000 000800 000400 000800 000000
+	000500 000000 000800 000000 000000 000000 000000
+	000000 000300 000000 000000 000000 000800 000800
+	000800 000800 000800 000000 000000 000100 000000
+	000000 000800 000200 000700 000000 000000 000800
+	000800 000800 000000 000800 000700 000000 000400
+	000000 000000 000000 000800 000000 000000 000000
+	000000 000800 000000 000800 000000 000800 000000
diff --git a/skcms.cc b/skcms.cc
index 6fb4af9..f6f7158 100644
--- a/skcms.cc
+++ b/skcms.cc
@@ -1782,6 +1782,10 @@
     Op_table_16_b,
     Op_table_16_a,
 
+    Op_clut_1D_8,
+    Op_clut_1D_16,
+    Op_clut_2D_8,
+    Op_clut_2D_16,
     Op_clut_3D_8,
     Op_clut_3D_16,
     Op_clut_4D_8,
@@ -2133,6 +2137,8 @@
                     }
                 }
                 switch (srcProfile->A2B.input_channels) {
+                    case 1: *ops++ = srcProfile->A2B.grid_8 ? Op_clut_1D_8 : Op_clut_1D_16; break;
+                    case 2: *ops++ = srcProfile->A2B.grid_8 ? Op_clut_2D_8 : Op_clut_2D_16; break;
                     case 3: *ops++ = srcProfile->A2B.grid_8 ? Op_clut_3D_8 : Op_clut_3D_16; break;
                     case 4: *ops++ = srcProfile->A2B.grid_8 ? Op_clut_4D_8 : Op_clut_4D_16; break;
                     default: return false;
diff --git a/src/Transform_inl.h b/src/Transform_inl.h
index 2ddfc48..bdf7b5f 100644
--- a/src/Transform_inl.h
+++ b/src/Transform_inl.h
@@ -869,6 +869,26 @@
             case Op_table_16_b:{ b = table_16((const skcms_Curve*)*args++, b); } break;
             case Op_table_16_a:{ a = table_16((const skcms_Curve*)*args++, a); } break;
 
+            case Op_clut_1D_8:{
+                const skcms_A2B* a2b = (const skcms_A2B*) *args++;
+                clut_1_8(a2b, cast<I32>(F0),cast<I32>(F1), &r,&g,&b,a);
+            } break;
+
+            case Op_clut_1D_16:{
+                const skcms_A2B* a2b = (const skcms_A2B*) *args++;
+                clut_1_16(a2b, cast<I32>(F0),cast<I32>(F1), &r,&g,&b,a);
+            } break;
+
+            case Op_clut_2D_8:{
+                const skcms_A2B* a2b = (const skcms_A2B*) *args++;
+                clut_2_8(a2b, cast<I32>(F0),cast<I32>(F1), &r,&g,&b,a);
+            } break;
+
+            case Op_clut_2D_16:{
+                const skcms_A2B* a2b = (const skcms_A2B*) *args++;
+                clut_2_16(a2b, cast<I32>(F0),cast<I32>(F1), &r,&g,&b,a);
+            } break;
+
             case Op_clut_3D_8:{
                 const skcms_A2B* a2b = (const skcms_A2B*) *args++;
                 clut_3_8(a2b, cast<I32>(F0),cast<I32>(F1), &r,&g,&b,a);
diff --git a/tests.c b/tests.c
index b9ac3e1..640352e 100644
--- a/tests.c
+++ b/tests.c
@@ -506,6 +506,9 @@
     // Table is approximated by a TF whose inverse has g > 16M (timeout in approx_pow)
     "profiles/fuzz/inverse_tf_huge_g.icc",            // chromium:842374
 
+    // mAB has a CLUT with 1 input channel
+    "profiles/fuzz/one_d_clut.icc",                   // chromium:874433
+
     // Non-D50 profiles.
     "profiles/misc/SM245B.icc",
     "profiles/misc/BenQ_GL2450.icc",