add kRGBA_F16_SkColorType

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1666343002

Review URL: https://codereview.chromium.org/1666343002
diff --git a/include/core/SkColor.h b/include/core/SkColor.h
index 90453f5..b1571c7 100644
--- a/include/core/SkColor.h
+++ b/include/core/SkColor.h
@@ -180,6 +180,11 @@
 
     static SkPM4f FromPMColor(SkPMColor);
 
+    // half-float routines
+    void toF16(uint16_t[4]) const;
+    uint64_t toF16() const; // 4 float16 values packed into uint64_t
+    static SkPM4f FromF16(const uint16_t[4]);
+
 #ifdef SK_DEBUG
     void assertIsUnit() const;
 #else
diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h
index 4b82ae6..b2dda3f 100644
--- a/include/core/SkImageInfo.h
+++ b/include/core/SkImageInfo.h
@@ -73,8 +73,9 @@
     kBGRA_8888_SkColorType,
     kIndex_8_SkColorType,
     kGray_8_SkColorType,
+    kRGBA_F16_SkColorType,
 
-    kLastEnum_SkColorType = kGray_8_SkColorType,
+    kLastEnum_SkColorType = kRGBA_F16_SkColorType,
 
 #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
     kN32_SkColorType = kBGRA_8888_SkColorType,
@@ -95,6 +96,7 @@
         4,  // BGRA_8888
         1,  // kIndex_8
         1,  // kGray_8
+        8,  // kRGBA_F16
     };
     static_assert(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1),
                   "size_mismatch_with_SkColorType_enum");
@@ -114,6 +116,7 @@
 static inline size_t SkColorTypeComputeOffset(SkColorType ct, int x, int y, size_t rowBytes) {
     int shift = 0;
     switch (SkColorTypeBytesPerPixel(ct)) {
+        case 8: shift = 3; break;
         case 4: shift = 2; break;
         case 2: shift = 1; break;
         case 1: shift = 0; break;
diff --git a/include/core/SkPixmap.h b/include/core/SkPixmap.h
index 523c40f..894b238 100644
--- a/include/core/SkPixmap.h
+++ b/include/core/SkPixmap.h
@@ -75,51 +75,75 @@
     uint64_t getSafeSize64() const { return fInfo.getSafeSize64(fRowBytes); }
     size_t getSafeSize() const { return fInfo.getSafeSize(fRowBytes); }
 
-    const uint32_t* addr32() const {
-        SkASSERT(4 == SkColorTypeBytesPerPixel(fInfo.colorType()));
-        return reinterpret_cast<const uint32_t*>(fPixels);
+    const void* addr(int x, int y) const {
+        return (const char*)fPixels + fInfo.computeOffset(x, y, fRowBytes);
     }
-
-    const uint16_t* addr16() const {
-        SkASSERT(2 == SkColorTypeBytesPerPixel(fInfo.colorType()));
-        return reinterpret_cast<const uint16_t*>(fPixels);
-    }
-
     const uint8_t* addr8() const {
         SkASSERT(1 == SkColorTypeBytesPerPixel(fInfo.colorType()));
         return reinterpret_cast<const uint8_t*>(fPixels);
     }
+    const uint16_t* addr16() const {
+        SkASSERT(2 == SkColorTypeBytesPerPixel(fInfo.colorType()));
+        return reinterpret_cast<const uint16_t*>(fPixels);
+    }
+    const uint32_t* addr32() const {
+        SkASSERT(4 == SkColorTypeBytesPerPixel(fInfo.colorType()));
+        return reinterpret_cast<const uint32_t*>(fPixels);
+    }
+    const uint64_t* addr64() const {
+        SkASSERT(8 == SkColorTypeBytesPerPixel(fInfo.colorType()));
+        return reinterpret_cast<const uint64_t*>(fPixels);
+    }
+    const uint16_t* addrF16() const {
+        SkASSERT(8 == SkColorTypeBytesPerPixel(fInfo.colorType()));
+        SkASSERT(kRGBA_F16_SkColorType == fInfo.colorType());
+        return reinterpret_cast<const uint16_t*>(fPixels);
+    }
 
-    const uint32_t* addr32(int x, int y) const {
+    // Offset by the specified x,y coordinates
+
+    const uint8_t* addr8(int x, int y) const {
         SkASSERT((unsigned)x < (unsigned)fInfo.width());
         SkASSERT((unsigned)y < (unsigned)fInfo.height());
-        return (const uint32_t*)((const char*)this->addr32() + y * fRowBytes + (x << 2));
+        return (const uint8_t*)((const char*)this->addr8() + y * fRowBytes + (x << 0));
     }
     const uint16_t* addr16(int x, int y) const {
         SkASSERT((unsigned)x < (unsigned)fInfo.width());
         SkASSERT((unsigned)y < (unsigned)fInfo.height());
         return (const uint16_t*)((const char*)this->addr16() + y * fRowBytes + (x << 1));
     }
-    const uint8_t* addr8(int x, int y) const {
+    const uint32_t* addr32(int x, int y) const {
         SkASSERT((unsigned)x < (unsigned)fInfo.width());
         SkASSERT((unsigned)y < (unsigned)fInfo.height());
-        return (const uint8_t*)((const char*)this->addr8() + y * fRowBytes + (x << 0));
+        return (const uint32_t*)((const char*)this->addr32() + y * fRowBytes + (x << 2));
     }
-    const void* addr(int x, int y) const {
-        return (const char*)fPixels + fInfo.computeOffset(x, y, fRowBytes);
+    const uint64_t* addr64(int x, int y) const {
+        SkASSERT((unsigned)x < (unsigned)fInfo.width());
+        SkASSERT((unsigned)y < (unsigned)fInfo.height());
+        return (const uint64_t*)((const char*)this->addr64() + y * fRowBytes + (x << 3));
+    }
+    const uint16_t* addrF16(int x, int y) const {
+        SkASSERT(kRGBA_F16_SkColorType == fInfo.colorType());
+        return reinterpret_cast<const uint16_t*>(this->addr64(x, y));
     }
 
     // Writable versions
 
     void* writable_addr() const { return const_cast<void*>(fPixels); }
-    uint32_t* writable_addr32(int x, int y) const {
-        return const_cast<uint32_t*>(this->addr32(x, y));
+    uint8_t* writable_addr8(int x, int y) const {
+        return const_cast<uint8_t*>(this->addr8(x, y));
     }
     uint16_t* writable_addr16(int x, int y) const {
         return const_cast<uint16_t*>(this->addr16(x, y));
     }
-    uint8_t* writable_addr8(int x, int y) const {
-        return const_cast<uint8_t*>(this->addr8(x, y));
+    uint32_t* writable_addr32(int x, int y) const {
+        return const_cast<uint32_t*>(this->addr32(x, y));
+    }
+    uint64_t* writable_addr64(int x, int y) const {
+        return const_cast<uint64_t*>(this->addr64(x, y));
+    }
+    uint16_t* writable_addrF16(int x, int y) const {
+        return reinterpret_cast<uint16_t*>(writable_addr64(x, y));
     }
 
     // copy methods
@@ -152,6 +176,7 @@
     bool erase(SkColor, const SkIRect& subset) const;
 
     bool erase(SkColor color) const { return this->erase(color, this->bounds()); }
+    bool erase(const SkColor4f&, const SkIRect* subset = nullptr) const;
 
 private:
     const void*     fPixels;
diff --git a/src/core/SkColor.cpp b/src/core/SkColor.cpp
index 87e3a9d..c0a3895 100644
--- a/src/core/SkColor.cpp
+++ b/src/core/SkColor.cpp
@@ -103,6 +103,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 #include "SkNx.h"
+#include "SkHalf.h"
 
 SkPM4f SkPM4f::FromPMColor(SkPMColor c) {
     Sk4f value = SkNx_cast<float>(Sk4b::Load(&c));
@@ -121,6 +122,36 @@
     }
 }
 
+void SkPM4f::toF16(uint16_t half[4]) const {
+    for (int i = 0; i < 4; ++i) {
+        half[i] = SkFloatToHalf(fVec[i]);
+    }
+}
+
+uint64_t SkPM4f::toF16() const {
+    uint64_t value;
+    this->toF16(reinterpret_cast<uint16_t*>(&value));
+    return value;
+}
+
+SkPM4f SkPM4f::FromF16(const uint16_t half[4]) {
+    return {{
+        SkHalfToFloat(half[0]),
+        SkHalfToFloat(half[1]),
+        SkHalfToFloat(half[2]),
+        SkHalfToFloat(half[3])
+    }};
+}
+
+#ifdef SK_DEBUG
+void SkPM4f::assertIsUnit() const {
+    auto c4 = Sk4f::Load(fVec);
+    SkASSERT((c4 >= Sk4f(0)).allTrue() && (c4 <= Sk4f(1)).allTrue());
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 SkColor4f SkColor4f::FromColor(SkColor c) {
     Sk4f value = SkNx_shuffle<3,2,1,0>(SkNx_cast<float>(Sk4b::Load(&c)));
     SkColor4f c4;
@@ -151,10 +182,3 @@
     dst.store(&pm4);
     return pm4;
 }
-
-#ifdef SK_DEBUG
-void SkPM4f::assertIsUnit() const {
-    auto c4 = Sk4f::Load(fVec);
-    SkASSERT((c4 >= Sk4f(0)).allTrue() && (c4 <= Sk4f(1)).allTrue());
-}
-#endif
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index e2d4d30..f285668 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -207,6 +207,41 @@
     return true;
 }
 
+#include "SkNx.h"
+#include "SkHalf.h"
+
+static void sk_memset64(uint64_t dst[], uint64_t value, int count) {
+    for (int i = 0; i < count; ++i) {
+        dst[i] = value;
+    }
+}
+
+bool SkPixmap::erase(const SkColor4f& origColor, const SkIRect* subset) const {
+    SkPixmap pm;
+    if (subset) {
+        if (!this->extractSubset(&pm, *subset)) {
+            return false;
+        }
+    } else {
+        pm = *this;
+    }
+
+    const SkColor4f color = origColor.pin();
+
+    if (kRGBA_F16_SkColorType != pm.colorType()) {
+        Sk4f c4 = Sk4f::Load(color.vec());
+        SkColor c;
+        (c4 * Sk4f(255) + Sk4f(0.5f)).store(&c);
+        return pm.erase(c);
+    }
+
+    const uint64_t half4 = color.premul().toF16();
+    for (int y = 0; y < pm.height(); ++y) {
+        sk_memset64(pm.writable_addr64(0, y), half4, pm.width());
+    }
+    return true;
+}
+
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkSurface.h"
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 62b7ff1..e4dc467 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -318,6 +318,8 @@
             return kIndex_8_GrPixelConfig;
         case kGray_8_SkColorType:
             return kAlpha_8_GrPixelConfig; // TODO: gray8 support on gpu
+        case kRGBA_F16_SkColorType:
+            return kRGBA_half_GrPixelConfig;
     }
     SkASSERT(0);    // shouldn't get here
     return kUnknown_GrPixelConfig;
diff --git a/tests/Float16Test.cpp b/tests/Float16Test.cpp
new file mode 100644
index 0000000..0a2c3d5
--- /dev/null
+++ b/tests/Float16Test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkColor.h"
+#include "SkHalf.h"
+#include "SkPixmap.h"
+
+static bool eq_within_half_float(float a, float b) {
+    const float kTolerance = 1.0f / (1 << (8 + 10));
+
+    SkHalf ha = SkFloatToHalf(a);
+    SkHalf hb = SkFloatToHalf(b);
+    float a2 = SkHalfToFloat(ha);
+    float b2 = SkHalfToFloat(hb);
+    return fabsf(a2 - b2) <= kTolerance;
+}
+
+static bool eq_within_half_float(const SkPM4f& a, const SkPM4f& b) {
+    for (int i = 0; i < 4; ++i) {
+        if (!eq_within_half_float(a.fVec[i], b.fVec[i])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+DEF_TEST(color_half_float, reporter) {
+    const int w = 100;
+    const int h = 100;
+
+    SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_F16_SkColorType, kPremul_SkAlphaType);
+
+    SkAutoPixmapStorage pm;
+    pm.alloc(info);
+    REPORTER_ASSERT(reporter, pm.getSafeSize() == SkToSizeT(w * h * sizeof(uint64_t)));
+
+    SkColor4f c4 { 0.5f, 1, 0.5f, 0.25f };
+    pm.erase(c4);
+
+    SkPM4f origpm4 = c4.premul();
+    for (int y = 0; y < pm.height(); ++y) {
+        for (int x = 0; x < pm.width(); ++x) {
+            SkPM4f pm4 = SkPM4f::FromF16(pm.addrF16(x, y));
+            REPORTER_ASSERT(reporter, eq_within_half_float(origpm4, pm4));
+        }
+    }
+}