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) {