ICC: SkICC::WriteToICC Description Tag is function of input

BUG=skia:6720
Change-Id: I038079a6e15f884eb77b84d9c7c75f6b7fbedd37
Reviewed-on: https://skia-review.googlesource.com/20152
Commit-Queue: Hal Canary <halcanary@google.com>
Reviewed-by: Matt Sarett <msarett@google.com>
diff --git a/src/core/SkICC.cpp b/src/core/SkICC.cpp
index 40a733f..c26f6d8 100644
--- a/src/core/SkICC.cpp
+++ b/src/core/SkICC.cpp
@@ -14,6 +14,7 @@
 #include "SkFixed.h"
 #include "SkICC.h"
 #include "SkICCPriv.h"
+#include "SkMD5.h"
 
 SkICC::SkICC(sk_sp<SkColorSpace> colorSpace)
     : fColorSpace(std::move(colorSpace))
@@ -139,19 +140,19 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-// Google Skia (UTF-16)
-static constexpr uint8_t kDescriptionTagBody[] = {
-        0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00,
-        0x53, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x61, 0x00, 0x20,
-    };
-static_assert(SkIsAlign4(sizeof(kDescriptionTagBody)), "Description must be aligned to 4-bytes.");
+static constexpr char kDescriptionTagBodyPrefix[12] =
+        { 'G', 'o', 'o', 'g', 'l', 'e', '/', 'S', 'k', 'i', 'a' , '/'};
+static constexpr size_t kDescriptionTagBodySize =
+        (sizeof(kDescriptionTagBodyPrefix) + 2 * sizeof(SkMD5::Digest)) * 2;
+
+static_assert(SkIsAlign4(kDescriptionTagBodySize), "Description must be aligned to 4-bytes.");
 static constexpr uint32_t kDescriptionTagHeader[7] {
     SkEndian_SwapBE32(kTAG_TextType),                        // Type signature
     0,                                                       // Reserved
     SkEndian_SwapBE32(1),                                    // Number of records
     SkEndian_SwapBE32(12),                                   // Record size (must be 12)
     SkEndian_SwapBE32(SkSetFourByteTag('e', 'n', 'U', 'S')), // English USA
-    SkEndian_SwapBE32(sizeof(kDescriptionTagBody)),          // Length of string
+    SkEndian_SwapBE32(kDescriptionTagBodySize),              // Length of string
     SkEndian_SwapBE32(28),                                   // Offset of string
 };
 
@@ -185,7 +186,7 @@
 
 static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c');
 static constexpr uint32_t kTAG_desc_Bytes = sizeof(kDescriptionTagHeader) +
-                                            sizeof(kDescriptionTagBody);
+                                            kDescriptionTagBodySize;
 static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize +
                                              kICCNumEntries * kICCTagTableEntrySize;
 
@@ -307,6 +308,42 @@
            1.0f == toXYZD50.get(3, 3);
 }
 
+size_t SkICCWriteDescriptionTag(uint8_t* ptr,
+                                const SkColorSpaceTransferFn& fn,
+                                const SkMatrix44& toXYZD50) {
+    if (ptr) {
+        SkDEBUGCODE(const uint8_t* const ptrCheck = ptr);
+        memcpy(ptr, kDescriptionTagHeader, sizeof(kDescriptionTagHeader));
+        ptr += sizeof(kDescriptionTagHeader);
+
+        for (unsigned i = 0; i < sizeof(kDescriptionTagBodyPrefix); ++i) {
+            *ptr++ = 0;
+            *ptr++ = kDescriptionTagBodyPrefix[i];
+        }
+        SkMD5 md5;
+        for (int i = 0; i < 3; ++i) {
+            for (int j = 0; j < 3; ++j) {
+                float value = toXYZD50.getFloat(i,j);
+                md5.write(&value, sizeof(value));
+            }
+        }
+        static_assert(sizeof(fn) == sizeof(float) * 7, "packed");
+        md5.write(&fn, sizeof(fn));
+        SkMD5::Digest digest;
+        md5.finish(digest);
+        for (unsigned i = 0; i < sizeof(SkMD5::Digest); ++i) {
+            static const char gHex[] = "0123456789ABCDEF";
+            *ptr++ = 0;
+            *ptr++ = gHex[digest.data[i] >> 4];
+            *ptr++ = 0;
+            *ptr++ = gHex[digest.data[i] & 0xF];
+        }
+        SkASSERT(ptr == ptrCheck + kDescriptionTagBodySize + sizeof(kDescriptionTagHeader));
+    }
+    return kDescriptionTagBodySize + sizeof(kDescriptionTagHeader);
+}
+
+
 sk_sp<SkData> SkICC::WriteToICC(const SkColorSpaceTransferFn& fn, const SkMatrix44& toXYZD50) {
     if (!is_3x3(toXYZD50) || !is_valid_transfer_fn(fn)) {
         return nullptr;
@@ -324,10 +361,7 @@
     ptr += sizeof(kICCTagTable);
 
     // Write profile description tag
-    memcpy(ptr, kDescriptionTagHeader, sizeof(kDescriptionTagHeader));
-    ptr += sizeof(kDescriptionTagHeader);
-    memcpy(ptr, kDescriptionTagBody, sizeof(kDescriptionTagBody));
-    ptr += sizeof(kDescriptionTagBody);
+    ptr += SkICCWriteDescriptionTag(ptr, fn, toXYZD50);
 
     // Write XYZ tags
     write_xyz_tag((uint32_t*) ptr, toXYZD50, 0);
diff --git a/src/core/SkICCPriv.h b/src/core/SkICCPriv.h
index 4c656f1..eb5c13d 100644
--- a/src/core/SkICCPriv.h
+++ b/src/core/SkICCPriv.h
@@ -49,4 +49,15 @@
     kGABDE_ParaCurveType       = 3,
     kGABCDEF_ParaCurveType     = 4,
 };
+
+/*
+ * Given fn and toXYZD50, generate a ICC decription tag that includes a hash of
+ * the input.  If ptr is not nullptr, write the tag there.  Always returns
+ * length of the tag.
+ *
+ * Exposed for unit testing.
+ */
+size_t SkICCWriteDescriptionTag(uint8_t* ptr,
+                                const SkColorSpaceTransferFn& fn,
+                                const SkMatrix44& toXYZD50);
 #endif  // SkICCPriv_DEFINED
diff --git a/tests/ICCTest.cpp b/tests/ICCTest.cpp
index 5d541bd..3c855fb 100644
--- a/tests/ICCTest.cpp
+++ b/tests/ICCTest.cpp
@@ -11,6 +11,7 @@
 #include "SkColorSpace_XYZ.h"
 #include "SkData.h"
 #include "SkICC.h"
+#include "SkICCPriv.h"
 #include "SkMatrix44.h"
 #include "SkStream.h"
 #include "Test.h"
@@ -112,6 +113,14 @@
     REPORTER_ASSERT(r, SkColorSpace::Equals(reference, colorSpace.get()));
 }
 
+template <typename T>
+static bool equal(const SkTArray<T>& u, const SkTArray<T>& v) {
+    if (u.count() != v.count()) {
+        return false;
+    }
+    return u.count() == 0 || 0 == memcmp(&u[0], &v[0], sizeof(T) * u.count());
+}
+
 DEF_TEST(ICC_WriteICC, r) {
     SkColorSpaceTransferFn adobeFn;
     adobeFn.fA = 1.0f;
@@ -138,6 +147,16 @@
     srgbMatrix.set3x3RowMajorf(gSRGB_toXYZD50);
     test_write_icc(r, srgbFn, srgbMatrix, SkColorSpace::MakeSRGB().get(),
                    false);
+
+    SkTArray<uint8_t> adobeTag;
+    adobeTag.reset(SkToInt(SkICCWriteDescriptionTag(nullptr, adobeFn, adobeMatrix)));
+    SkICCWriteDescriptionTag(&adobeTag[0], adobeFn, adobeMatrix);
+
+    SkTArray<uint8_t> srgbTag;
+    srgbTag.reset(SkToInt(SkICCWriteDescriptionTag(nullptr, srgbFn, srgbMatrix)));
+    SkICCWriteDescriptionTag(&srgbTag[0], srgbFn, srgbMatrix);
+
+    REPORTER_ASSERT(r, !equal(adobeTag, srgbTag));
 }
 
 static inline void test_raw_transfer_fn(skiatest::Reporter* r, SkICC* icc) {