SkBitmap::ComputeIsOpaque and SkBitmap::eraseColor support F16

Also add a unit test.

Change-Id: I9b6635ce9dd504788ca36b3246eaac2b37c2f3a6
Reviewed-on: https://skia-review.googlesource.com/5443
Commit-Queue: Hal Canary <halcanary@google.com>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/include/core/SkColor.h b/include/core/SkColor.h
index 8f2776d..4d54773 100644
--- a/include/core/SkColor.h
+++ b/include/core/SkColor.h
@@ -183,6 +183,7 @@
     float* vec() { return &fR; }
 
     static SkColor4f Pin(float r, float g, float b, float a);
+    /** Convert to SkColor4f, assuming SkColor is sRGB */
     static SkColor4f FromColor(SkColor);
     static SkColor4f FromColor3f(SkColor3f, float a);
 
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index e4097b4..9b11743 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -11,6 +11,7 @@
 #include "SkConfig8888.h"
 #include "SkData.h"
 #include "SkFilterQuality.h"
+#include "SkHalf.h"
 #include "SkMallocPixelRef.h"
 #include "SkMask.h"
 #include "SkMath.h"
@@ -622,6 +623,18 @@
             }
             return true;
         }
+        case kRGBA_F16_SkColorType: {
+            const SkHalf* row = (const SkHalf*)pmap.addr();
+            for (int y = 0; y < height; ++y) {
+                for (int x = 0; x < width; ++x) {
+                    if (row[4 * x + 3] < SK_Half1) {
+                        return false;
+                    }
+                }
+                row += pmap.rowBytes() >> 1;
+            }
+            return true;
+        }
         default:
             break;
     }
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index 0a1a24f..c73b918 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -5,13 +5,18 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
+#include "SkCanvas.h"
 #include "SkColorPriv.h"
 #include "SkConfig8888.h"
 #include "SkData.h"
+#include "SkHalf.h"
 #include "SkMask.h"
-#include "SkPixmap.h"
-#include "SkUtils.h"
+#include "SkNx.h"
 #include "SkPM4f.h"
+#include "SkPixmap.h"
+#include "SkSurface.h"
+#include "SkUtils.h"
 
 void SkAutoPixmapUnlock::reset(const SkPixmap& pm, void (*unlock)(void*), void* ctx) {
     SkASSERT(pm.addr() != nullptr);
@@ -206,15 +211,19 @@
             }
             break;
         }
+        case kRGBA_F16_SkColorType:
+            // The colorspace is unspecified, so assume linear just like getColor().
+            this->erase(SkColor4f{(1 / 255.0f) * r,
+                                  (1 / 255.0f) * g,
+                                  (1 / 255.0f) * b,
+                                  (1 / 255.0f) * a}, &area);
+            break;
         default:
             return false; // no change, so don't call notifyPixelsChanged()
     }
     return true;
 }
 
-#include "SkNx.h"
-#include "SkHalf.h"
-
 bool SkPixmap::erase(const SkColor4f& origColor, const SkIRect* subset) const {
     SkPixmap pm;
     if (subset) {
@@ -238,10 +247,6 @@
     return true;
 }
 
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkSurface.h"
-
 bool SkPixmap::scalePixels(const SkPixmap& dst, SkFilterQuality quality) const {
     // Can't do anthing with empty src or dst
     if (this->width() <= 0 || this->height() <= 0 || dst.width() <= 0 || dst.height() <= 0) {
diff --git a/tests/BitmapTest.cpp b/tests/BitmapTest.cpp
index f939875..98b1ac4 100644
--- a/tests/BitmapTest.cpp
+++ b/tests/BitmapTest.cpp
@@ -166,3 +166,56 @@
     test_erasecolor_premul(r, kRGBA_8888_SkColorType, color, color);
     test_erasecolor_premul(r, kBGRA_8888_SkColorType, color, color);
 }
+
+// Test that SkBitmap::ComputeOpaque() is correct for various colortypes.
+DEF_TEST(Bitmap_compute_is_opaque, r) {
+    struct {
+        SkColorType fCT;
+        SkAlphaType fAT;
+    } types[] = {
+        { kGray_8_SkColorType,    kOpaque_SkAlphaType },
+        { kAlpha_8_SkColorType,   kPremul_SkAlphaType },
+        { kARGB_4444_SkColorType, kPremul_SkAlphaType },
+        { kRGB_565_SkColorType,   kOpaque_SkAlphaType },
+        { kBGRA_8888_SkColorType, kPremul_SkAlphaType },
+        { kRGBA_8888_SkColorType, kPremul_SkAlphaType },
+        { kRGBA_F16_SkColorType,  kPremul_SkAlphaType },
+    };
+    for (auto type : types) {
+        SkBitmap bm;
+        REPORTER_ASSERT(r, !SkBitmap::ComputeIsOpaque(bm));
+
+        bm.allocPixels(SkImageInfo::Make(13, 17, type.fCT, type.fAT));
+        bm.eraseColor(SkColorSetARGB(255, 10, 20, 30));
+        REPORTER_ASSERT(r, SkBitmap::ComputeIsOpaque(bm));
+
+        bm.eraseColor(SkColorSetARGB(128, 255, 255, 255));
+        bool isOpaque = SkBitmap::ComputeIsOpaque(bm);
+        bool shouldBeOpaque = (type.fAT == kOpaque_SkAlphaType);
+        REPORTER_ASSERT(r, isOpaque == shouldBeOpaque);
+    }
+}
+
+// Test that erase+getColor round trips with RGBA_F16 pixels.
+DEF_TEST(Bitmap_erase_f16_erase_getColor, r) {
+    SkRandom random;
+    SkPixmap pm;
+    SkBitmap bm;
+    bm.allocPixels(SkImageInfo::Make(1, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType));
+    REPORTER_ASSERT(r, bm.peekPixels(&pm));
+    for (unsigned i = 0; i < 0x100; ++i) {
+        // Test all possible values of blue component.
+        SkColor color1 = (SkColor)((random.nextU() & 0xFFFFFF00) | i);
+        // Test all possible values of alpha component.
+        SkColor color2 = (SkColor)((random.nextU() & 0x00FFFFFF) | (i << 24));
+        for (SkColor color : {color1, color2}) {
+            pm.erase(color);
+            if (SkColorGetA(color) != 0) {
+                REPORTER_ASSERT(r, color == pm.getColor(0, 0));
+            } else {
+                REPORTER_ASSERT(r, 0 == SkColorGetA(pm.getColor(0, 0)));
+            }
+        }
+    }
+}
+