B2A part 3, turn it on

This CL structures things as we want them to end up:

    - prefer B2A over TRC/XYZ
    - allow B2A in MakeUsableAsDestination(), but not WithSingleCurve()

I figured we might as well try this first, knowing either of these
optimisms might lead to the CL being reverted.  Could stick, who knows?

I don't trust CMYK destinations at all, but they don't work at all
today, so leaving it in work-in-progress state should be harmless.

I think the non-CMYK profiles look pretty good.  Some profiles are newly
able to be destinations, with some so-so roundtrips and some wonderful.
And other profiles that had both TRC/XYZ and B2A show diffs, some
improved, some regressed, and some essentially unchanged.  It's this mix
of results that has me cautiously optimistic... if this were way off,
we'd expect to see uniformly bad results.  :)

Quick notes on particular profiles:

    - sRGB_ICC_v4_Appearance.icc:       new dest, not great, not so bad.
    - sRGB_ICC_v4_beta.icc:             ditto... new, not great, not bad.
    - sRGB_v4_ICC_preference.icc:       new, near perfect!
    - BenQ_RL2455.icc:                  much improved over XYZ/TRC
    - Calibrated_A2B_XYZ_Mismatch.icc:  bad before, now worse.
    - Kodak_sRGB.icc:                   stays not-great

    - Upper_Left.icc:
      By far most interesting, with an XYZ PCS where the others all use
      Lab.  Flailing around with this profile is what lead me to invert
      encoding_factor for B2A, nothing more principled.  I'd like to
      even underflow those principles further, with an exhaustive search
      over what encoding_factor results in the best roundtripping for
      this profile, to see if that leads to any enlightenment.

Change-Id: I6798da59d23474d45ff5857760631af81c2d4098
Reviewed-on: https://skia-review.googlesource.com/c/skcms/+/388596
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
diff --git a/profiles/color.org/Upper_Left.icc.txt b/profiles/color.org/Upper_Left.icc.txt
index 0075afe..2fdaa9c 100644
--- a/profiles/color.org/Upper_Left.icc.txt
+++ b/profiles/color.org/Upper_Left.icc.txt
@@ -37,9 +37,9 @@
   B0 : 1, 1, 0, 0, 0, 0, 0 (f(1) = 1) (~Identity)
   B1 : 1, 1, 0, 0, 0, 0, 0 (f(1) = 1) (~Identity)
   B2 : 1, 1, 0, 0, 0, 0, 0 (f(1) = 1) (~Identity)
-Mtrx : | 4.01385498 0 0 -0.00674428185 |
-       | 0 4.01382446 0 -0.00695790164 |
-       | 0 0 4.01385498 -0.00576773426 |
+Mtrx : | 1.00349426 0 0 -0.00168612192 |
+       | 0 1.00348663 0 -0.0017395285 |
+       | 0 0 1.00349426 -0.00144197757 |
  "M" : 3 inputs
   M0 : 1, 1, 0, 0, 0, 0, 0 (f(1) = 1) (~Identity)
   M1 : 1, 1, 0, 0, 0, 0, 0 (f(1) = 1) (~Identity)
@@ -78,6 +78,19 @@
     -0.09 -0.15  0.06     0.35  0.04 -0.15     0.37  0.79 -0.23    -0.37 -0.38 -0.06
      0.29  0.28  0.94     0.52  1.00  0.04     0.19  0.08  0.05     0.08  0.12  0.10
      0.39  0.20  0.03     0.33  0.70  0.16     0.29  0.14  0.01     0.26  0.30  0.63
+97 max error transforming back from XYZ:
+      9   3   1  30  29  13   0   0   1  26  66   7  47  65   2   5   3  51   0   0   3
+      0   0   0  18  22  15   0   2   0   2   0  16  24   1  53   0   0   0   0   1   0
+      0   0   0   1   0   0   0   0   0   0   0   2  85  89   4   4   1  39   0   1   0
+      0   0   1  56  93  12   1  35  10   0   0   0   2   1   6  60  97   3   1   0   0
+      0   0   1   0   1   0  52  83   5   0   0   0  49  20   5   0   1   0  18   1  46
+      0   0   0   0   0   3  13  18  82   0   0   0  59  38  43  10  61   1   1   1   0
+     25  93   2  33  15  85   0   0   0  62  36   7  11   1  33   0   2   2   0   0   0
+      0   0   0   3   1   8   0   1   1   0   0   0   0   0   0  22  56  17  58   8   5
+      3   0   3   4   1  39  36  33   2   3   3  31  42  57   5  73  96  11   0   4   1
+      0   0   0   0   4   0  21   1  42  21  35   3  65  95  28   2   0   0   0   0   0
+      1   0   2   1   4   0  11  65   6   4   1  77  43   0  37  94  83  23   4   0   0
+      3   0   0   0   1   0   0   0   0   0   4   0   0   0   0   0   2   3   2   0   0
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	00000000 00000041 000000ff  00004100 00004141 000041ff  0000ff00 0000ff41 0000ffff
 	00410000 00410041 004100ff  00414100 00414141 004141ff  0041ff00 0041ff41 0041ffff
diff --git a/profiles/color.org/sRGB_ICC_v4_Appearance.icc.txt b/profiles/color.org/sRGB_ICC_v4_Appearance.icc.txt
index 62bb630..4546225 100644
--- a/profiles/color.org/sRGB_ICC_v4_Appearance.icc.txt
+++ b/profiles/color.org/sRGB_ICC_v4_Appearance.icc.txt
@@ -78,6 +78,19 @@
      0.06  0.04  0.19     0.33  0.17  0.02     0.29  0.48  0.06     0.01  0.01  0.04
      0.31  0.34  0.64     0.32  0.52  0.11     0.26  0.15  0.16     0.22  0.27  0.22
      0.35  0.23  0.12     0.27  0.47  0.20     0.29  0.19  0.09     0.31  0.36  0.51
+30 max error transforming back from XYZ:
+     11   0   0   2   1   5   0   0   0   0   0   1   0   1   3   1   0   3   6   3   3
+      0   1   0   0   0   1   0   0   1   1   1   3   0   0  14   4   0   0   0   2   1
+      0   0   0   7   0   0   0   0   1   1   1   2   0   3   3   1   1   9   2   8   2
+      0   2   1  15   5   0   1   4   0   5  20   6   3   1   4   3   2   5   2   0   0
+      0   0   1   0   0   1   0   5   2   0   0   1   4   1   2   1   1   0   8   0  30
+      1   1   1   2   1   2   0  19   1  17   0   2   1   0   1  14   2   3   1   0   1
+      0   4   1   1   0  10   6   0   0   2   0   0   3   1   3   0   0   1   3   0   3
+      3   0   1   0   0   1   0   0   1  24   1   1   7   0   1   0   0   1   7   3   5
+      1   0   0   0   0   7   1   0   1   0   0   1   7   1   2   5   2   1   0   0   1
+      0   0   0   1   0   0   2   0   9   0  21   1   0  12   1   2   0   1   0   0   1
+      0   1   1   1   0   1   2   0   2   0   9   1  16   3  10  12   0   1   2   0   1
+     15   0   6   0   0   1   0   0   0   0   0   0  13   1   2   0   0   1   1   0   0
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	000b0b0b 0000007f 000b1af7  00007f01 00007f80 00007bfb  003cd94c 002ce37c 0000f7f9
 	007b0d00 007e007f 007800f6  007f7f00 007f7f7f 007b7ffe  0076de38 007eef88 0084fbfa
diff --git a/profiles/color.org/sRGB_v4_ICC_preference.icc.txt b/profiles/color.org/sRGB_v4_ICC_preference.icc.txt
index 1361bce..c4bd666 100644
--- a/profiles/color.org/sRGB_v4_ICC_preference.icc.txt
+++ b/profiles/color.org/sRGB_v4_ICC_preference.icc.txt
@@ -75,6 +75,19 @@
      0.06  0.04  0.18     0.32  0.16  0.02     0.22  0.39  0.03     0.01  0.01  0.05
      0.26  0.29  0.62     0.25  0.44  0.07     0.27  0.17  0.16     0.21  0.25  0.22
      0.38  0.25  0.14     0.16  0.33  0.15     0.31  0.20  0.10     0.27  0.32  0.51
+1 max error transforming back from XYZ:
+      1   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
+      0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
+      0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   1   0
+      0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
+      0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
+      0   0   0   0   0   0   0   1   0   1   0   1   0   0   0   0   0   0   0   0   0
+      0   0   0   0   0   0   1   0   1   0   0   0   0   0   0   0   0   0   0   0   0
+      0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
+      0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   1   0   0   0
+      0   0   0   0   0   0   0   0   0   1   1   0   0   0   0   0   0   0   0   0   0
+      0   0   0   0   0   0   0   0   0   0   0   0   1   0   1   0   0   0   0   1   0
+      0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	000b0b0b 00000078 000000f2  00006900 00006d78 00007fff  0000c600 0000d668 0000edff
 	00730b00 0068007d 006800f5  00755b00 007f7f7f 008087ff  0060c800 0076e168 007cf8ff
diff --git a/profiles/misc/BenQ_RL2455.icc.txt b/profiles/misc/BenQ_RL2455.icc.txt
index 6e67ffc..dfe9f22 100644
--- a/profiles/misc/BenQ_RL2455.icc.txt
+++ b/profiles/misc/BenQ_RL2455.icc.txt
@@ -94,19 +94,19 @@
      0.03  0.02  0.10     0.18  0.09  0.01     0.17  0.28  0.04     0.01  0.00  0.02
      0.15  0.18  0.33     0.18  0.32  0.05     0.14  0.08  0.09     0.11  0.14  0.11
      0.19  0.12  0.07     0.16  0.27  0.13     0.16  0.10  0.05     0.15  0.19  0.26
-69 max error transforming back from XYZ:
-      8  48  35  55  68  27  36  50  31  28  24  37  26  28  46  58  32  21   3  60  25
-     65  31  36  31  29  27  63  26  31  66  55  24  42  47  11  31  43  33  41  32  61
-     64  48  62  32  49  59  52  52  43  34  45  26  35  16  49  66  44  19  66  31  63
-     68  32  29  13   5  41  65  69  46  65  17  61  48  64  26  25  14  57  30  54  40
-     42  39  34  39  27  41  29  17  44  56  53  44  37  69  49  67  31  54  29  57   4
-     40  56  57  31  44  26  61   8  24  13  62  31  27  32  31  19  31  53  32  32  46
-     31  18  58  35  30  10  31  50  53  26  32  28  31  39  30  55  28  27  63  38  60
-     68  56  45  67  38  67  60  30  28  22  54  31  31  59  36  32  28  27  29  68  30
-     25  57  25  63  40  23  32  32  33  43  28  32  23  29  39  19   0  29  48  23  28
-     49  42  52  51  24  51  32  51  15  54   6  50  43   1  33  29  34  46  36  47  59
-     32  42  29  27  24  59  32  25  38  62  20  26  25  62   3   1  12  29  24  45  64
-     25  66  30  50  32  35  30  40  38  58  24  30  26  61  37  55  29  25  26  47  57
+8 max error transforming back from XYZ:
+      8   1   1   1   1   1   1   1   0   1   2   1   0   0   1   1   0   2   7   1   0
+      1   0   1   1   0   0   1   0   0   1   1   1   1   1   2   0   1   1   1   0   1
+      1   1   1   0   1   1   1   1   1   1   1   0   1   1   1   1   1   1   1   0   1
+      1   0   0   0   6   1   1   1   1   1   2   1   1   1   0   1   3   1   0   1   1
+      1   1   0   1   0   1   1   1   1   1   1   1   1   1   1   1   0   1   1   1   8
+      1   1   1   1   1   0   1   3   0   4   1   0   1   0   0   3   0   1   1   0   1
+      0   1   1   1   1   3   1   1   1   0   0   0   1   1   0   1   0   0   1   1   1
+      1   1   1   1   1   1   1   0   0   1   1   0   1   1   1   1   1   0   0   1   1
+      0   1   0   1   1   0   0   1   0   1   0   0   1   0   1   2   2   0   1   0   0
+      1   1   1   1   0   1   0   1   3   1   5   1   1   1   0   1   0   1   0   1   1
+      1   1   0   0   0   1   0   1   1   1   2   0   0   1   7   8   0   0   0   1   1
+      0   1   1   1   0   1   1   1   1   1   0   1   2   1   1   1   0   0   0   1   1
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	00000000 00000060 000000c2  00015b00 00005a5e 000058c1  0002b800 0000b859 0000b7bf
 	005d1100 005c0d5e 005c00c1  005d5d00 005c5d5c 005c5bc1  005db900 005cb957 005cb8be
diff --git a/profiles/misc/Calibrated_A2B_XYZ_Mismatch.icc.txt b/profiles/misc/Calibrated_A2B_XYZ_Mismatch.icc.txt
index 59536f7..d2e7e46 100644
--- a/profiles/misc/Calibrated_A2B_XYZ_Mismatch.icc.txt
+++ b/profiles/misc/Calibrated_A2B_XYZ_Mismatch.icc.txt
@@ -93,19 +93,19 @@
      0.04  0.02  0.10     0.21  0.10  0.01     0.14  0.28  0.05     0.01  0.00  0.02
      0.15  0.18  0.32     0.15  0.32  0.06     0.16  0.09  0.09     0.11  0.14  0.12
      0.21  0.13  0.07     0.13  0.27  0.13     0.18  0.10  0.05     0.15  0.19  0.26
-70 max error transforming back from XYZ:
-      8  47  34  56  68   0  37  49  16  27   6  37  27   6  47  58  35   3   3  59  26
-     63  50  32  31  21  21  63  33  29  65  58   7  41  48   8  14  42  32  42  13  60
-     63  50  61  19  46  59  52  52  42  35  45  21  34   9  49  64  50  10  65  28  62
-     65  57  26   3  10  40  65  70  46  64  20  60  50  63  25  24  10  56  21  52  39
-     41  39  34  38  28  40  29   3  45  56  53  44  40  67  50  65  33  53  12  55  11
-     41  54  56  32  44  21  60   9   2  15  60  31  10  15  13   7   5  53  16  29  47
-     14  11  57  35  30   1  17  47  52   9  14  27  21  37   9  53  27  19  63  39  59
-     66  57  44  66  39  66  60  27  26  13  52  32  20  57  35  31  14  20  41  64  13
-     27  55  24  62  44   5  33  18  31  42  30  13   6   6  37   1   0  29  47  26  27
-     50  41  51  50  27  50  18  50   6  52   7  49  42   1  31  23  31  46  37  45  58
-     19  41  20  29  21  58  16   3  37  61  25   0  30  61  13   2   3  20  28  42  64
-     33  63   8  49  22  32  31  39  38  58  30  28  23  58  36  53  26  24  30  45  57
+100 max error transforming back from XYZ:
+     66  67  38  57  81  16  30  62  12  31  19  49  24  16  62  83  11   4  84  86  20
+     93  48  47  40  11  28  91  11  42  85  51   9  44  55  21  22  57  37  43  21  82
+     75  35  80  39  62  75  56  54  52  29  56  21  39  44  66  91  30   4  88  42  85
+    100  47  42  30  25  54  70  75  54  88  83  83  43  78  21   9  37  76  23  73  45
+     45  38  42  45  14  53  31  34  61  65  52  54   9  88  57  92  45  74  38  81  38
+     22  65  70  23  55  22  93  80  18  77  89  28   4  18  18  36   2  71  15  36  60
+     19  28  77  44  27   9  42  66  66   6  16  36   4  50   6  79  16  32  78  13  79
+     83  48  55  82   7  88  87  25  40  55  75  32  40  82  38  45  13  29  55  95  12
+      5  78  20  88  24   1  41   0  43  56  16  20  17   1  49  10  22  37  65   1  38
+     54  35  67  64   6  68  16  72  24  71  79  68  61  64  45   0  37  59  19  52  74
+      5  57  19  12  11  77   8   9  49  94  66  21   8  87  43  18   5  26  13  52  81
+     16  91  13  69  20  46  20  47  47  83  11  41  57  85  38  78  16  36   3  57  72
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	00020202 00000067 000000d0  00005e00 00005d59 000057cb  001fc000 001fbf00 001fbcb9
 	005d0009 005d0067 005c00d0  005e6000 005d5d5c 005e56cc  0061c000 0061bf00 0061bab9
diff --git a/profiles/misc/Coated_FOGRA27_CMYK.icc.txt b/profiles/misc/Coated_FOGRA27_CMYK.icc.txt
index 6f90905..584be6a 100644
--- a/profiles/misc/Coated_FOGRA27_CMYK.icc.txt
+++ b/profiles/misc/Coated_FOGRA27_CMYK.icc.txt
@@ -65,6 +65,19 @@
      0.21  0.24  0.07     0.09  0.10  0.07     0.07  0.06  0.08     0.04  0.04  0.05
      0.00  0.00  0.00     0.04  0.05  0.08     0.06  0.03  0.02     0.09  0.09  0.07
      0.03  0.02  0.01     0.09  0.07  0.06     0.12  0.12  0.14
+141 max error transforming back from XYZ:
+      2  20   8   9  66   7  28  40  12  16  11  21   3   3   2  14  52  20  29  70  33
+     76  22  66  29  18  19  52  23  23  36  43  19  98  73  29  10  25  13  24  23  73
+     49  72  45   6  27  28  74  60  44  89  63  41  53  23 116 141  86  71  27   2  21
+      3  11  35  28   9  48  68  57  84  79  11  64  56 119  45  53  34  69  21  51  59
+     37  27  21  46  47  63  41  27  52  53  41  82   9  16   8   7   6  23   4  14   5
+     28  22  26   2   3   0   1  40  85  56 130   4   3   3   3  18  44  48  31  55  72
+     28  33 114  66  64  34  45 117  74  58  10  13   8  19  20  68  25  41  15   4  10
+      5  87  53  75 103 116  79  37 101  22  83  34  41 104  56  55  51  43  41 111  44
+      8  19   5  13  21  10  18  11  46  52  35  42   1   9  31   4   0  44  36  34  20
+     30  19  39  80  40  66  52 101   2 101  22  55  36   1  53  22  30  28  40  53  55
+     21  69  19  20  15  41  17  16  22  47  30  44  43  89  33  77  57  45  52  87  84
+     90 102  28  74  50  26  18  21  33  88  37  50  36 113  56  78  85   5   5   8   6
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	ff010000 ff010210 ff050621  ff010700 ff020f0a ff051520  ff041400 ff041b00 ff04221f
 	ff030000 ff040014 ff050024  ff0b0200 ff0a090c ff0a0f21  ff130f00 ff121800 ff101f1d
diff --git a/profiles/misc/Coated_FOGRA39_CMYK.icc.txt b/profiles/misc/Coated_FOGRA39_CMYK.icc.txt
index 32c083e..ef6e6a4 100644
--- a/profiles/misc/Coated_FOGRA39_CMYK.icc.txt
+++ b/profiles/misc/Coated_FOGRA39_CMYK.icc.txt
@@ -69,6 +69,19 @@
      0.21  0.24  0.07     0.10  0.11  0.08     0.08  0.06  0.09     0.05  0.05  0.06
      0.01  0.01  0.00     0.05  0.05  0.08     0.05  0.03  0.02     0.10  0.09  0.08
      0.03  0.02  0.01     0.09  0.07  0.06     0.13  0.12  0.15
+139 max error transforming back from XYZ:
+      1   3   2   9  57   7  26  30  19  22  16  28   5   2   6  19  54  21  43  72  34
+     72  28  61  53  25  38  78  21  19  40  40  28  96  67  27   4   8   5   4  19  60
+     46  55  30   2  18   9  57  43  36  57  58  36  47  17 115 131  88  54  25   2  20
+      2  24  37  72  11  43  60  56  72  59   6  50  31 128  34  23  28  51  13  38  36
+     15  10  10  14  50  57  37  21  40  38  33  55   8  16   8   7   6  19   5  11   5
+     22  20  17   5   8   5  18  66  87  80 139   8   8  12   5  24  43  58  31  61  75
+      8  29 112  52  42  25  50 108  80  50  19  24  17  31  26  60  27  35  14   4   9
+      4  76  43  70  81 104  68  34  77  27  65  33  28  89  44  45  33  55  41 108  43
+      6  15   5   8   9   3   7   2  40  43  31  29  10   5  31   7   0  42  43  32  10
+     16  11  16  66  30  54  35 112  21  80  19  41  25   1  31  17  21  24  28  46  45
+     20  55  33  35  31  67  18  13  26  46  36  39  51  89  63  85  66  42  54  77  87
+     81  85  23  60  33  10   6   8   9  81  32  42  26  95  43  67  56   2   1   3   4
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	ff010000 ff01000f ff000020  ff010500 ff000c08 ff00101e  ff001200 ff001900 ff001e1b
 	ff010000 ff020013 ff010022  ff070100 ff04090b ff000d20  ff0f0e00 ff0c1700 ff091d1b
diff --git a/profiles/misc/ColorLogic_ISO_Coated_CMYK.icc.txt b/profiles/misc/ColorLogic_ISO_Coated_CMYK.icc.txt
index a33be61..ccb7519 100644
--- a/profiles/misc/ColorLogic_ISO_Coated_CMYK.icc.txt
+++ b/profiles/misc/ColorLogic_ISO_Coated_CMYK.icc.txt
@@ -65,6 +65,19 @@
      0.21  0.24  0.07     0.10  0.10  0.08     0.07  0.06  0.08     0.04  0.04  0.05
      0.01  0.01  0.00     0.05  0.05  0.08     0.05  0.03  0.02     0.10  0.09  0.08
      0.03  0.02  0.01     0.08  0.07  0.06     0.13  0.12  0.15
+150 max error transforming back from XYZ:
+      6  20  10  23  31   5  14  23  40  44  31  47  24  12  30  38  76  25  48  83  16
+     36  13  29  59  28  39  78   7   6  12  15  21  68  47  18   6  12   8  13  12  36
+     26  39   6   1   3   5  44  33  28  42  60  37  52  19 102 115  77  43   0   0   0
+      1  16  28  54  10  33  46  42  58  43   7  35  33 111  42  38  27  35   9  26  29
+      6   3   3   6  40  44  33  16  28  27  23  39   0   4   1   3   7  18   6  19   3
+      5   3   8  24  26  16  43  77 101  92 150  12   8  11   6  11  16  17  10  44  49
+     10  19 104  60  57  26  37  69  55  31  34  40  32  46  12  28  13  16   3   1   2
+      3  60  33  54  62  82  51  25  56  16  43  22  19  86  43  45  33  35  32  71  27
+      7  15   5  18  11   5   9   5  26  26  19  18   2   3  18   2   5   4   7   3   2
+      2   2   3  57  26  46  31  97   4  88  23  20   9   5  15   1   2   2   3  24  24
+     11  29  47  46  43  81  36  26  53  71  52  58  76 114  67  87  66  44  37  52  60
+     50  86  23  60  38   4   2   3   4  76  31  45  26  84  39  59  50  16  13  20  30
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	ff000000 ff00000e ff00001e  ff000600 ff000a09 ff000e1c  ff001100 ff001700 ff001c1a
 	ff050003 ff000010 ff00001f  ff080400 ff06080a ff030c1d  ff0d0f00 ff0c1500 ff0b1b1a
diff --git a/profiles/misc/DisplayCal_ASUS_NonMonotonic.icc.txt b/profiles/misc/DisplayCal_ASUS_NonMonotonic.icc.txt
index 5950426..1f14b02 100644
--- a/profiles/misc/DisplayCal_ASUS_NonMonotonic.icc.txt
+++ b/profiles/misc/DisplayCal_ASUS_NonMonotonic.icc.txt
@@ -91,19 +91,19 @@
      0.03  0.01  0.10     0.17  0.09  0.00     0.17  0.30  0.03     0.00  0.00  0.03
      0.16  0.18  0.33     0.19  0.34  0.03     0.13  0.08  0.09     0.11  0.14  0.11
      0.18  0.11  0.07     0.16  0.28  0.12     0.15  0.09  0.05     0.16  0.19  0.27
-65 max error transforming back from XYZ:
-      6  41  30  51  63  17  31  43  13  23   3  32  22   4  41  55  23  26   3  55  20
-     62   0  30  26   5  17  60  14  25  63  50  30  36  40  12  15  35  29  38   0  58
-     61  41  58  13  42  56  48  46  38  29  38  17  32   4  44  63  37  22  64   3  60
-     65   0  23  14  31  28  62  64  40  63   2  58  43  59  20  18   4  53  11  48  35
-     37  31  29  35  15  36  26   4  40  53  47  39  31  64  45  64   1  50  15  51   5
-     35  51  53  25  36  17  58   0  20   3  57  26   2   1  11  24   2  50  13  22  42
-     36  10  55  30  20   4  16  44  50  14   0  23  12  31  13  52   7  16  60  29  56
-     64  50  39  64  28  64  57   4  23   1  48  27  13  54  32  28   1  16  17  63  14
-     18  52  19  60  32  28  28   1  28  38  17  12  29   7  31  23  21  16  44  10  23
-     46  34  48  48  11  47  10  46  17  51   3  46  40   3  29  15  24  41  31  40  55
-     11  34  15  22  11  55  14   7  32  59   1  18  18  57   4   2  14   8  17  37  61
-     16  61  14  47   2  30  24  32  33  56  11  24  10  55  32  52   6  19  20  40  54
+116 max error transforming back from XYZ:
+     89  58  34  49  77  26  21  55   1  27  30  43  19  24  54  91   8  25 109  78  13
+    108  47  40  35   1  14 102  12  31  90  49  27  40  47  43  39  50  32  45  21  77
+     80  33  76  57  56  72  52  51  46  20  48  10  41  50  59 100  26  32 100  40  80
+    116  47  29  48  50  42  67  71  47 101  81  77  33  73  13   2  47  71  42  65  41
+     43  33  35  45   9  46  28  44  53  64  49  47   9  82  52 106  43  67  59  72  54
+     12  61  67  12  47  11 107  81  17 102  80  24  18   2   1  48  11  66  29  31  54
+     28  39  72  41  21  40  59  60  62  27   0  26  13  44  10  89  17  14  85  12  74
+     88  47  48  91   7  84  98  26  27  77  67  27  61  75  34  44  23  14  82  88  20
+     22  71  12  96  20  25  38   8  36  59  12   0  33  15  43  37  54  26  71   4  26
+     53  31  61  70   8  61  34  64  40  81  78  61  67  71  36  13  31  53   9  46  71
+     22  49   7   5   6  73   3  25  43 108  66  14  29  78  56  54  38  11  29  46  79
+     41  84  21  77  21  39   9  41  41  93  13  30  80  77  34  88  17  21  18  50  69
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	00191919 0000005b 000000bc  00005c00 00005c5c 00005abd  0000be00 0000be59 0000bdbb
 	00670400 005e005b 005d00bc  005b5d00 005c5c5c 005b5abc  0053be00 0054bd59 0052bdbb
diff --git a/profiles/misc/Japan_Color_2001_Coated.icc.txt b/profiles/misc/Japan_Color_2001_Coated.icc.txt
index aaf71b0..b12908d 100644
--- a/profiles/misc/Japan_Color_2001_Coated.icc.txt
+++ b/profiles/misc/Japan_Color_2001_Coated.icc.txt
@@ -65,6 +65,19 @@
      0.22  0.25  0.07     0.10  0.10  0.08     0.07  0.06  0.09     0.04  0.04  0.06
      0.00  0.00  0.00     0.04  0.05  0.09     0.05  0.03  0.02     0.10  0.09  0.08
      0.02  0.02  0.00     0.08  0.07  0.05     0.13  0.12  0.15
+171 max error transforming back from XYZ:
+      8  44  21  44  85  18  46  70   8  11  10  13  17  12  22  29   9   7  15  26  44
+    101  52 105   1   1   3  10  23  25  40  50  27 132 115  63  22  51  31  64  22  71
+     48  77  46   9  29  35  88  75  62 122 106  65  98  53 131 171 127  94  24   3  19
+      3  38  76  15  46  46  63  56  84  90  17  77  78 162  79  77  64  89  34  74  97
+     57  45  43  85  69 100  84  51  52  55  45  91   7  15   7   7  13  39  16  44   5
+     36  30  45  13  16  10  28  23  50  51  84  39  26  45  27  24  49 109  61  78 107
+     44  62 149 101  94  62  50 149 113  88   9  14  12  18  37 100  57  74  13   4   9
+      5  89  57  80 116 134  99  69 137  27 120  66  72 129  85 105  77  61  41 161  65
+     10  22   8  22  60  28  63  41  66  80  71  67  20  21  97  40   0  73  79  70  25
+     39  26  62 105  65 102  82 144   2 151  59  81  58   1  95  36  53  52  82  62  66
+     37  98   1   1   1   0   2   2   5   1  10  20  19  43  15  26  26  15  66 111 111
+    131 136  44 118  82  46  37  44  72 124  70 107  64 133  78 108 118  17  18  22  39
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	ff010000 ff010000 ff020020  ff010000 ff010000 ff000023  ff000700 ff001600 ff001c22
 	ff010000 ff010000 ff02001f  ff010000 ff010000 ff010024  ff090400 ff021400 ff001b24
diff --git a/profiles/misc/Kodak_sRGB.icc.txt b/profiles/misc/Kodak_sRGB.icc.txt
index 2b44502..3269f84 100644
--- a/profiles/misc/Kodak_sRGB.icc.txt
+++ b/profiles/misc/Kodak_sRGB.icc.txt
@@ -87,19 +87,19 @@
      0.06  0.04  0.19     0.34  0.17  0.03     0.36  0.58  0.07     0.01  0.01  0.05
      0.31  0.36  0.65     0.39  0.66  0.10     0.26  0.16  0.17     0.23  0.27  0.23
      0.35  0.23  0.14     0.33  0.55  0.25     0.30  0.19  0.10     0.31  0.38  0.52
-23 max error transforming back from XYZ:
-      1   1   1   1   1  13   1   1   3   5   2   1   5   1   1   3   1   5  16   1   3
-      4   5   0   2   1   1   4   2   1   3   0  12   1   1  10   1   1   1   5   2   1
-      4   1   0   4   1   0   2   0   2   1   1   2   5   1   1   3   1  10   5   4   0
-      4   4   1  14   5   1   2   1   3   5  11   1   1   1   5   7   1   1   0   1   1
-      2   0   1   3   0   0   4   1   1   2   0   2   1   1   2   5   4   0   6   1  17
-      2   1   1   0   1   2   4   7   0  13   1   3   4   3   2  23   0   1   4   1   0
-     13   0   1   2   1   0   0   1   0   7   2   1   1   1   2   3   2   0   4   1   0
-      3   0   2   5   1   0   4   2   0   5   1   2   3   1   2   3   1   1  10   2  11
-      1   1   3   3   1   7   3   0   1   3   0   1  13   2   1  15   0   1   3   1   0
-      3   0   0   4   2   0   1   1   9   4   9   0   4   0   1   3   1   0   4   0   0
-      1   1   2   6   1   1   6   3   1   4   5   0   3   1  22   2  11   2   3   0   0
-      5   1  10   4   1   0   2   1   0   4   2   0   3   1   2   4   2   0   4   1   0
+24 max error transforming back from XYZ:
+      1   1   1   0   2   3   1   1   1   3   3   0   4   3   0   1   0   4  11   2   2
+      2   3   0   0   1   1   2   1   1   1   2   5   0   1   6   3   1   1   4   4   1
+      3   1   1   6   1   0   0   1   1   1   1   2   3   7   0   1   1   4   3   3   1
+      2   2   1  14   7   0   1   2   0   3   9   1   0   2   2   6   9   1   0   1   1
+      0   0   0   2   0   0   3   6   0   1   1   0   1   2   0   3   3   1   7   2   9
+      2   1   0   1   1   2   2   3   2  10   2   1   1   0   1  24   4   0   3   0   0
+     13   8   1   0   0   4   2   1   0   3   1   1   1   1   3   1   1   1   3   0   1
+      2   1   0   3   0   1   2   1   1   4   2   1   3   2   1   1   2   1   9   2   4
+      2   2   2   1   0   5   1   2   0   1   0   2   9   2   0   6   4   0   1   1   1
+      2   0   0   2   2   0   3   1   5   3  10   0   2   5   0   2   0   0   3   1   0
+      1   1   2   6   2   1   4   3   0   2   2   2   4   2  10   2   1   1   4   0   1
+      5   2   4   2   1   0   0   1   0   2   1   1   1   2   0   1   1   1   5   1   0
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	00010000 0003007e 000a00fd  00007e15 00007e81 00057cfe  0000fc37 0000fc89 0000fbff
 	00801200 00800e7c 008000fc  007f7f01 00807f7f 00807efd  007efd31 007efd87 007ffcff
diff --git a/profiles/misc/PrintOpen_ISO_Coated_CMYK.icc.txt b/profiles/misc/PrintOpen_ISO_Coated_CMYK.icc.txt
index 1e184c4..5650036 100644
--- a/profiles/misc/PrintOpen_ISO_Coated_CMYK.icc.txt
+++ b/profiles/misc/PrintOpen_ISO_Coated_CMYK.icc.txt
@@ -62,6 +62,19 @@
      0.21  0.23  0.07     0.08  0.09  0.07     0.08  0.06  0.09     0.04  0.04  0.05
      0.00  0.00  0.00     0.04  0.05  0.07     0.05  0.03  0.02     0.08  0.08  0.07
      0.02  0.02  0.01     0.07  0.06  0.05     0.13  0.12  0.15
+137 max error transforming back from XYZ:
+     12  47  21  44  22   3  10  15  29  33  24  38  27  12  34  40  70  26  50  80  17
+     35  13  27  48  23  33  69   8  11  17  26  12  69  64  17   4   9   6  10   7  25
+     17  23   2   0   1   1  53  40  34  55  70  43  57  22 114 129  87  54   1   1   0
+      1   9  39  48  13  34  48  44  60   7   1   5   4 109  40  53  28  28   7  21  21
+     16  11  11  18  49  57  42  22  31  33  28  45   0   7   2   7   5  17   5  16  11
+     25  25  36  19  20  12  35  63  88  78 137   4   5   3   2   3  11  25  27  40  44
+     27  17 112  66  68  29  34  62  50  25  22  27  21  33  15  35  16  20   2   0   1
+      0  66  36  60  71  69  42  21  43  14  30  15  13 100  52  54  42  33  35  60  25
+      3   9   3  10   8   5   7   3  41  38  32  29  11   8  50   1   9  14  15  11   2
+      3   2   4  70  35  58  43 116  11 107  18  27  17   8  18   7   9  10  15  19  18
+      8  22  36  36  33  66  35  24  48  67  47  53  65  99  55  75  54  39  38  51  63
+     52  72  19  47  28   8   6   7  10  88  33  51  33  97  47  70  62  12   9  14  19
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	ff010000 ff010000 ff020a1b  ff010000 ff010c0a ff011119  ff0d1800 ff021704 ff011918
 	ff010000 ff010000 ff02031d  ff010000 ff030303 ff050c19  ff171800 ff0c1300 ff0e1716
diff --git a/profiles/misc/SWOP_Coated_20_GCR_CMYK.icc.txt b/profiles/misc/SWOP_Coated_20_GCR_CMYK.icc.txt
index b9d866d..94f10ac 100644
--- a/profiles/misc/SWOP_Coated_20_GCR_CMYK.icc.txt
+++ b/profiles/misc/SWOP_Coated_20_GCR_CMYK.icc.txt
@@ -67,6 +67,19 @@
      0.18  0.21  0.06     0.09  0.10  0.07     0.07  0.05  0.08     0.04  0.04  0.06
      0.00  0.00  0.00     0.04  0.04  0.06     0.04  0.02  0.01     0.08  0.08  0.06
      0.01  0.01  0.00     0.06  0.05  0.04     0.12  0.11  0.14
+204 max error transforming back from XYZ:
+      1  10   3   8  55  11  40  47   6   8   8   4  10   5  14  17  29  14  27  44  34
+     75  27  59  14   9  16  25  15  15  26  29   4 108 135  35  17  31  22  32  24  57
+     40  64  20   5  16  21  82  73  63  94 137  40  96  41 152 193 137  64   3   0   3
+      2  30  62  22  34  51  62  54  80  54   7  56  46 204  55  56  48  65  20  65  59
+     49  41  42  61  73 104  74  41  54  56  48  79   4   6   4   7   4  10   2   8   4
+     15   7  14   3   5   3   6  36  46  61  93  70   5  32  29   6  13 132  28  91 123
+     22  47 185  85  77  43  38 138 143  53   7  10   8   7  29  91  40  47   5   1   4
+      5  87  55  84  96 119  96  57 109  17 125  67  51 158  89  99  66  55  11 182  40
+      3   7   2   8  87   3  77  37  77  89  73  56   3   3 104  27   0  44  72  34  20
+     30  22  41 110  51 116  65 162  28 162  35  63  61   1  68  29  36  42  45  53  60
+     30  72   7   9   8  17  20  13  35  42  27  27  49  67  24  41  27   9  60  86 101
+     79 139  28 126  64  38  29  41  47 161  56  98  54 131  72 117  92   4   3   6   6
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	ff010000 ff010000 ff010000  ff010000 ff010000 ff010000  ff010000 ff010000 ff010000
 	ff010000 ff010000 ff010000  ff010000 ff010000 ff010000  ff010000 ff010000 ff010000
diff --git a/profiles/misc/US_Web_Coated_SWOP_CMYK.icc.txt b/profiles/misc/US_Web_Coated_SWOP_CMYK.icc.txt
index 2ae79bc..808ee99 100644
--- a/profiles/misc/US_Web_Coated_SWOP_CMYK.icc.txt
+++ b/profiles/misc/US_Web_Coated_SWOP_CMYK.icc.txt
@@ -65,6 +65,19 @@
      0.18  0.21  0.07     0.10  0.11  0.08     0.08  0.07  0.10     0.05  0.05  0.07
      0.00  0.00  0.00     0.04  0.05  0.08     0.05  0.03  0.02     0.08  0.08  0.07
      0.02  0.01  0.00     0.08  0.06  0.05     0.12  0.12  0.15
+133 max error transforming back from XYZ:
+      4  18  11   9  61  13  41  40  17  20  16  21   0   1   2   7  52  24  48  58  42
+     73  33  58  30  20  26  42  28  29  43  41  16  76  73  35  13  25  18  21  30  73
+     52  70  39  10  31  26  74  62  57  75  62  15  39  24 115 133  93  65  23   2  20
+      3   7  26  70  20  59  72  61  83  75  16  70  53 120  37  21  35  66  26  58  52
+     37  30  30  36  40  54  35  24  57  55  49  73  11  15  10   7   8  19   5  11   5
+     31  27  25   0   0   1   4  60  80  73 108   1  23  24   3   6  13  62  25  46  63
+     10  33 111  60  41  33  39 108  85  55  12  14  11  16  28  61  30  40  16   6  12
+      5  90  63  81  93 111  82  58  93  22  79  41  42  89  61  63  43  37  11 106  35
+      9  16   7  11  33  11  20  10  44  48  37  33  26  35  46  12   0  39  41  31  22
+     29  22  34  74  44  70  49 102  53  96  31  56  40   1  46  27  36  35  36  55  54
+     30  60  20  21  21  33  16  14  21  33  33  39  49  66  62  76  78  41  64  95  92
+     88  93  34  85  52  27  23  26  27  80  43  52  33 103  66  86  76   7   6   9   6
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	ff010000 ff010000 ff020015  ff010000 ff010000 ff01001b  ff010500 ff001400 ff001a17
 	ff010000 ff010000 ff020015  ff010000 ff010000 ff01001e  ff090500 ff061500 ff001c1d
diff --git a/profiles/misc/XRite_GRACol7_340_CMYK.icc.txt b/profiles/misc/XRite_GRACol7_340_CMYK.icc.txt
index 3a470c0..9fcb33f 100644
--- a/profiles/misc/XRite_GRACol7_340_CMYK.icc.txt
+++ b/profiles/misc/XRite_GRACol7_340_CMYK.icc.txt
@@ -63,6 +63,19 @@
      0.19  0.21  0.07     0.10  0.11  0.08     0.08  0.07  0.09     0.05  0.05  0.07
      0.01  0.01  0.01     0.05  0.05  0.08     0.06  0.04  0.02     0.10  0.09  0.08
      0.03  0.02  0.01     0.08  0.07  0.06     0.13  0.12  0.15
+160 max error transforming back from XYZ:
+      4   7   4   8  43   9  29  35   2   2   1   6  16  10  19  22  39  16  26  42  36
+     71  28  69  25  14  17  33  11  11  14  19  27  64  58   9  11  20  13  24  18  46
+     31  48  17   4  14  15  75  60  55  83  86  67  84  25 139 160 119  77   5   0   5
+      2  22  26  15   7  50  62  52  80  41   9  37  35 142  79  75  41  56  21  50  52
+     41  31  30  50  70  79  60  28  48  48  43  65   3   9   4   7   4   9   3  10   5
+     12   8  13   2   2   2   3  44  63  55 103  25  19  21  18  10   1  10   0  83  93
+     45  41 137  95  95  37  44  89  69  41   1   1   1   2  31  73  41  57   5   2   4
+      5  79  49  71  83 119  85  53 112  24  75  44  44 132  82  91  73  31  24  50  15
+      7  12   5  15  37  21  32  26  67  71  58  60   2   4  18   6   3   2   8   6  16
+     20  15  27  91  54  84  66  87  16  88  11  63  46   0  70  19  24  25  34  55  56
+     29  73  21  22  19  36  28  23  41  50  34  46  54  79  36  53  51  30  48  72  72
+     70 106  37  92  63  29  22  26  37 117  63  83  55 118  66  98  92   5   4   6   7
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	ff000000 ff000011 ff000021  ff000700 ff000708 ff000d1d  ff001900 ff001800 ff001d1a
 	ff060007 ff000016 ff000024  ff090400 ff020409 ff000a1d  ff101500 ff0b1600 ff0c1c1a
diff --git a/profiles/misc/sRGB_ICC_v4_beta.icc.txt b/profiles/misc/sRGB_ICC_v4_beta.icc.txt
index 0c38578..d211f61 100644
--- a/profiles/misc/sRGB_ICC_v4_beta.icc.txt
+++ b/profiles/misc/sRGB_ICC_v4_beta.icc.txt
@@ -79,6 +79,19 @@
      0.06  0.04  0.19     0.33  0.17  0.02     0.29  0.48  0.06     0.01  0.01  0.04
      0.31  0.34  0.64     0.32  0.52  0.11     0.26  0.15  0.16     0.22  0.27  0.22
      0.35  0.23  0.12     0.27  0.47  0.20     0.29  0.19  0.09     0.31  0.36  0.51
+20 max error transforming back from XYZ:
+      1   0   0   2   2   4   0   0   0   0   0   1   0   1   3   1   0   3   3   3   2
+      0   3   0   0   0   1   0   0   1   1   1   1   0   0   1   5   0   0   0   2   1
+      0   0   0   6   0   1   0   0   1   1   1   2   0   1   3   1   1   3   2   8   2
+      0   2   1  14   0   0   0   0   0   4  20   4   3   1   4   3   2   5   2   0   0
+      0   0   1   0   0   1   0   0   1   0   0   1   4   1   2   0   1   0   2   1  12
+      1   1   1   2   1   2   0   2   3   9   0   2   1   0   1  10   3   5   3   0   1
+     10   4   1   1   0   2   3   0   0   2   0   0   4   1   3   0   0   1   3   1   3
+      0   0   0   0   0   0   0   0   1   4   1   2   4   0   1   0   0   1   7   3   5
+      1   0   1   1   1   2   1   0   1   0   0   1   8   1   1   4   0   1   0   0   1
+      1   0   0   1   0   0  10   1   5   0   0   1   0   0   1   2   0   1   0   0   1
+      0   1   1   1   0   1   2   0   2   0   1   4  12   2   4   2   0   1   3   0   1
+     15   0   6   0   0   1   0   0   0   0   0   0   3   0   2   0   0   1   1   0   0
 81 edge-case pixels transformed to sRGB 8888 (unpremul):
 	000b0b0b 0000007f 000b1af7  00007f01 00007f80 00007bfb  003cd94c 002ce37c 0000f7f9
 	007b0d00 007e007f 007800f6  007f7f00 007f7f7f 007b7ffe  0076de38 007eef88 0084fbfa
diff --git a/skcms.cc b/skcms.cc
index 710f598..21e1c83 100644
--- a/skcms.cc
+++ b/skcms.cc
@@ -975,7 +975,7 @@
         if (tag->size < matrix_offset + 12 * SAFE_SIZEOF(uint32_t)) {
             return false;
         }
-        float encoding_factor = pcs_is_xyz ? (65535 / 32768.0f) : 1.0f;
+        float encoding_factor = pcs_is_xyz ? (32768 / 65535.0f) : 1.0f;  // TODO: understand
         const uint8_t* mtx_buf = tag->buf + matrix_offset;
         b2a->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf +  0);
         b2a->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf +  4);
@@ -2270,7 +2270,9 @@
     Op_unpremul,
     Op_matrix_3x3,
     Op_matrix_3x4,
+
     Op_lab_to_xyz,
+    Op_xyz_to_lab,
 
     Op_tf_r,
     Op_tf_g,
@@ -2548,8 +2550,9 @@
                                  skcms_TransferFunction* invR,
                                  skcms_TransferFunction* invG,
                                  skcms_TransferFunction* invB) {
-    // We only support destinations with parametric transfer functions
-    // and with gamuts that can be transformed from XYZD50.
+    // skcms_Transform() supports B2A destinations...
+    if (profile->has_B2A) { return true; }
+    // ...and destinations with parametric transfer functions and an XYZD50 gamut matrix.
     return profile->has_trc
         && profile->has_toXYZD50
         && profile->trc[0].table_entries == 0
@@ -2755,38 +2758,94 @@
             return false;
         }
 
-        // A2B sources should already be in XYZD50 at this point.
-        // Others still need to be transformed using their toXYZD50 matrix.
-        // N.B. There are profiles that contain both A2B tags and toXYZD50 matrices.
-        // If we use the A2B tags, we need to ignore the XYZD50 matrix entirely.
+        // A2B sources are in XYZD50 by now, but TRC sources are still in their original gamut.
         assert (srcProfile->has_A2B || srcProfile->has_toXYZD50);
-        static const skcms_Matrix3x3 I = {{
-            { 1.0f, 0.0f, 0.0f },
-            { 0.0f, 1.0f, 0.0f },
-            { 0.0f, 0.0f, 1.0f },
-        }};
-        const skcms_Matrix3x3* to_xyz = srcProfile->has_A2B ? &I : &srcProfile->toXYZD50;
 
-        // There's a chance the source and destination gamuts are identical,
-        // in which case we can skip the gamut transform.
-        if (0 != memcmp(&dstProfile->toXYZD50, to_xyz, sizeof(skcms_Matrix3x3))) {
-            // Concat the entire gamut transform into from_xyz,
-            // now slightly misnamed but it's a handy spot to stash the result.
-            from_xyz = skcms_Matrix3x3_concat(&from_xyz, to_xyz);
-            *ops++  = Op_matrix_3x3;
-            *args++ = &from_xyz;
-        }
+        if (dstProfile->has_B2A) {
+            // B2A needs its input in XYZD50, so transform TRC sources now.
+            if (!srcProfile->has_A2B) {
+                *ops++  = Op_matrix_3x3;
+                *args++ = &srcProfile->toXYZD50;
+            }
 
-        // Encode back to dst RGB using its parametric transfer functions.
-        for (int i = 0; i < 3; i++) {
-            OpAndArg oa = select_curve_op(dst_curves+i, i);
-            if (oa.arg) {
-                assert (oa.op != Op_table_r &&
-                        oa.op != Op_table_g &&
-                        oa.op != Op_table_b &&
-                        oa.op != Op_table_a);
-                *ops++  = oa.op;
-                *args++ = oa.arg;
+            if (dstProfile->pcs == skcms_Signature_Lab) {
+                *ops++ = Op_xyz_to_lab;
+            }
+
+            if (dstProfile->B2A.input_channels == 3) {
+                for (int i = 0; i < 3; i++) {
+                    OpAndArg oa = select_curve_op(&dstProfile->B2A.input_curves[i], i);
+                    if (oa.arg) {
+                        *ops++  = oa.op;
+                        *args++ = oa.arg;
+                    }
+                }
+            }
+
+            if (dstProfile->B2A.matrix_channels == 3) {
+                static const skcms_Matrix3x4 I = {{
+                    {1,0,0,0},
+                    {0,1,0,0},
+                    {0,0,1,0},
+                }};
+                if (0 != memcmp(&I, &dstProfile->B2A.matrix, sizeof(I))) {
+                    *ops++  = Op_matrix_3x4;
+                    *args++ = &dstProfile->B2A.matrix;
+                }
+
+                for (int i = 0; i < 3; i++) {
+                    OpAndArg oa = select_curve_op(&dstProfile->B2A.matrix_curves[i], i);
+                    if (oa.arg) {
+                        *ops++  = oa.op;
+                        *args++ = oa.arg;
+                    }
+                }
+            }
+
+            if (dstProfile->B2A.output_channels) {
+                *ops++  = Op_clamp;
+                *ops++  = Op_clut_B2A;
+                *args++ = &dstProfile->B2A;
+                for (int i = 0; i < (int)dstProfile->B2A.output_channels; i++) {
+                    OpAndArg oa = select_curve_op(&dstProfile->B2A.output_curves[i], i);
+                    if (oa.arg) {
+                        *ops++  = oa.op;
+                        *args++ = oa.arg;
+                    }
+                }
+            }
+        } else {
+            // This is a TRC destination.
+            // We'll concat any src->xyz matrix with our xyz->dst matrix into one src->dst matrix.
+            // (A2B sources are already in XYZD50, making that src->xyz matrix I.)
+            static const skcms_Matrix3x3 I = {{
+                { 1.0f, 0.0f, 0.0f },
+                { 0.0f, 1.0f, 0.0f },
+                { 0.0f, 0.0f, 1.0f },
+            }};
+            const skcms_Matrix3x3* to_xyz = srcProfile->has_A2B ? &I : &srcProfile->toXYZD50;
+
+            // There's a chance the source and destination gamuts are identical,
+            // in which case we can skip the gamut transform.
+            if (0 != memcmp(&dstProfile->toXYZD50, to_xyz, sizeof(skcms_Matrix3x3))) {
+                // Concat the entire gamut transform into from_xyz,
+                // now slightly misnamed but it's a handy spot to stash the result.
+                from_xyz = skcms_Matrix3x3_concat(&from_xyz, to_xyz);
+                *ops++  = Op_matrix_3x3;
+                *args++ = &from_xyz;
+            }
+
+            // Encode back to dst RGB using its parametric transfer functions.
+            for (int i = 0; i < 3; i++) {
+                OpAndArg oa = select_curve_op(dst_curves+i, i);
+                if (oa.arg) {
+                    assert (oa.op != Op_table_r &&
+                            oa.op != Op_table_g &&
+                            oa.op != Op_table_b &&
+                            oa.op != Op_table_a);
+                    *ops++  = oa.op;
+                    *args++ = oa.arg;
+                }
             }
         }
     }
@@ -2799,6 +2858,16 @@
     if (dstFmt < skcms_PixelFormat_RGB_hhh) {
         *ops++ = Op_clamp;
     }
+
+    if (dstProfile->data_color_space == skcms_Signature_CMYK) {
+        // Photoshop creates CMYK images as inverse CMYK.
+        // These happen to be the only ones we've _ever_ seen.
+        *ops++ = Op_invert;
+
+        // CMYK has no alpha channel, so make sure dstAlpha is a no-op.
+        dstAlpha = skcms_AlphaFormat_Unpremul;
+    }
+
     if (dstAlpha == skcms_AlphaFormat_Opaque) {
         *ops++ = Op_force_opaque;
     } else if (dstAlpha == skcms_AlphaFormat_PremulAsEncoded) {
@@ -2865,44 +2934,48 @@
 }
 
 bool skcms_MakeUsableAsDestination(skcms_ICCProfile* profile) {
-    skcms_Matrix3x3 fromXYZD50;
-    if (!profile->has_trc || !profile->has_toXYZD50
-        || !skcms_Matrix3x3_invert(&profile->toXYZD50, &fromXYZD50)) {
-        return false;
-    }
-
-    skcms_TransferFunction tf[3];
-    for (int i = 0; i < 3; i++) {
-        skcms_TransferFunction inv;
-        if (profile->trc[i].table_entries == 0
-            && skcms_TransferFunction_invert(&profile->trc[i].parametric, &inv)) {
-            tf[i] = profile->trc[i].parametric;
-            continue;
-        }
-
-        float max_error;
-        // Parametric curves from skcms_ApproximateCurve() are guaranteed to be invertible.
-        if (!skcms_ApproximateCurve(&profile->trc[i], &tf[i], &max_error)) {
+    if (!profile->has_B2A) {
+        skcms_Matrix3x3 fromXYZD50;
+        if (!profile->has_trc || !profile->has_toXYZD50
+            || !skcms_Matrix3x3_invert(&profile->toXYZD50, &fromXYZD50)) {
             return false;
         }
-    }
 
-    for (int i = 0; i < 3; ++i) {
-        profile->trc[i].table_entries = 0;
-        profile->trc[i].parametric = tf[i];
-    }
+        skcms_TransferFunction tf[3];
+        for (int i = 0; i < 3; i++) {
+            skcms_TransferFunction inv;
+            if (profile->trc[i].table_entries == 0
+                && skcms_TransferFunction_invert(&profile->trc[i].parametric, &inv)) {
+                tf[i] = profile->trc[i].parametric;
+                continue;
+            }
 
+            float max_error;
+            // Parametric curves from skcms_ApproximateCurve() are guaranteed to be invertible.
+            if (!skcms_ApproximateCurve(&profile->trc[i], &tf[i], &max_error)) {
+                return false;
+            }
+        }
+
+        for (int i = 0; i < 3; ++i) {
+            profile->trc[i].table_entries = 0;
+            profile->trc[i].parametric = tf[i];
+        }
+    }
     assert_usable_as_destination(profile);
     return true;
 }
 
 bool skcms_MakeUsableAsDestinationWithSingleCurve(skcms_ICCProfile* profile) {
-    // Operate on a copy of profile, so we can choose the best TF for the original curves
+    // Call skcms_MakeUsableAsDestination() with B2A disabled;
+    // on success that'll return a TRC/XYZ profile with three skcms_TransferFunctions.
     skcms_ICCProfile result = *profile;
+    result.has_B2A = false;
     if (!skcms_MakeUsableAsDestination(&result)) {
         return false;
     }
 
+    // Of the three, pick the transfer function that best fits the other two.
     int best_tf = 0;
     float min_max_error = INFINITY_;
     for (int i = 0; i < 3; i++) {
diff --git a/src/Transform_inl.h b/src/Transform_inl.h
index 8eef6a6..f35bc64 100644
--- a/src/Transform_inl.h
+++ b/src/Transform_inl.h
@@ -1179,6 +1179,25 @@
                 b = Z * 0.8249f;
             } break;
 
+            // As above, in reverse.
+            case Op_xyz_to_lab:{
+                F X = r * (1/0.9642f),
+                  Y = g,
+                  Z = b * (1/0.8249f);
+
+                X = if_then_else(X > 0.008856f, approx_pow(X, 1/3.0f), X*7.787f + (16/116.0f));
+                Y = if_then_else(Y > 0.008856f, approx_pow(Y, 1/3.0f), Y*7.787f + (16/116.0f));
+                Z = if_then_else(Z > 0.008856f, approx_pow(Z, 1/3.0f), Z*7.787f + (16/116.0f));
+
+                F L = Y*116.0f - 16.0f,
+                  A = (X-Y)*500.0f,
+                  B = (Y-Z)*200.0f;
+
+                r = L * (1/100.f);
+                g = (A + 128.0f) * (1/255.0f);
+                b = (B + 128.0f) * (1/255.0f);
+            } break;
+
             case Op_tf_r:{ r = apply_tf((const skcms_TransferFunction*)*args++, r); } break;
             case Op_tf_g:{ g = apply_tf((const skcms_TransferFunction*)*args++, g); } break;
             case Op_tf_b:{ b = apply_tf((const skcms_TransferFunction*)*args++, b); } break;
diff --git a/tests.c b/tests.c
index 401777c..de44d1d 100644
--- a/tests.c
+++ b/tests.c
@@ -1799,6 +1799,56 @@
     free(ptr);
 }
 
+static void test_B2A() {
+    void*  ptr;
+    size_t len;
+    expect(load_file("profiles/color.org/Upper_Left.icc", &ptr,&len));
+
+    skcms_ICCProfile profile;
+    expect(skcms_Parse(ptr, len, &profile));
+    expect(!profile.has_trc);
+    expect(!profile.has_toXYZD50);
+    expect( profile.has_A2B);
+    expect( profile.has_B2A);
+
+    // A B2A profile is usable as a destination unchanged.
+    skcms_ICCProfile copy = profile;
+    expect(skcms_MakeUsableAsDestination(&copy));
+    expect(0 == memcmp(&copy, &profile, sizeof(profile)));
+
+    // A B2A-only profile does not have the TRC curves that …WithSingleCurve() needs.
+    expect(!skcms_MakeUsableAsDestinationWithSingleCurve(&profile));
+
+    // A2B transform should be well-supported.
+    const uint8_t* src = skcms_252_random_bytes;
+    float xyza[252];
+    expect(skcms_Transform(
+            src,  skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul, &profile,
+            xyza, skcms_PixelFormat_RGBA_ffff, skcms_AlphaFormat_Unpremul, skcms_XYZD50_profile(),
+            252/4));
+
+    // Now convert back using B2A.
+    uint8_t dst[252];
+    expect(skcms_Transform(
+            xyza, skcms_PixelFormat_RGBA_ffff, skcms_AlphaFormat_Unpremul, skcms_XYZD50_profile(),
+            dst,  skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul, &profile,
+            252/4));
+
+    for (int i = 0; i < 252; i++) {
+        // Alpha should not be changed.
+        if (i % 4 == 3) {
+            expect(dst[i] == src[i]);
+            continue;
+        }
+#if 0  // TODO: this looks nothing like an identity transform!
+        fprintf(stderr, "%3d   %02x  % .4f   %02x\n", i, src[i], xyza[i], dst[i]);
+        //expect(dst[i] == src[i]);
+#endif
+    }
+
+    free(ptr);
+}
+
 int main(int argc, char** argv) {
     bool regenTestData = false;
     for (int i = 1; i < argc; ++i) {
@@ -1842,6 +1892,7 @@
     test_HLG_invert();
     test_RGBA_8888_sRGB();
     test_ParseWithA2BPriority();
+    test_B2A();
 
     // Temporarily disable some tests while getting FP16 compute working.
     if (!kFP16) {