skeleton for float <-> half optimized procs

Nothing fancy yet, just calls the serial code in a loop.

I will try to folow this up with at least some of:
   - SSE2 version of serial code
   - NEON version of serial code
   - NEON version using vcvt.f32.f16/vcvt.f16.f32
   - F16C (between AVX and AVX2) version using vcvtph2ps/vcvtps2ph
The last two are fastest but need runtime detection.

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

Review URL: https://codereview.chromium.org/1686543003
diff --git a/bench/HalfBench.cpp b/bench/HalfBench.cpp
new file mode 100644
index 0000000..1c76c57
--- /dev/null
+++ b/bench/HalfBench.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 "Benchmark.h"
+#include "SkOpts.h"
+#include "SkRandom.h"
+
+struct FloatToHalfBench : public Benchmark {
+    const char* onGetName() override { return "float_to_half"; }
+    bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
+
+    void onDraw(int loops, SkCanvas* canvas) override {
+        SkRandom rand;
+        float fs[1023];
+        for (float& f : fs) {
+            f = rand.nextF();
+        }
+
+        uint16_t hs[1023];
+        while (loops --> 0) {
+            SkOpts::float_to_half(hs, fs, 1023);
+        }
+    }
+};
+DEF_BENCH(return new FloatToHalfBench;)
+
+struct HalfToFloatBench : public Benchmark {
+    const char* onGetName() override { return "half_to_float"; }
+    bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
+
+    void onDraw(int loops, SkCanvas* canvas) override {
+        SkRandom rand;
+        uint16_t hs[1023];
+        for (uint16_t& h : hs) {
+            h = rand.nextU16();
+        }
+
+        float fs[1023];
+        while (loops --> 0) {
+            SkOpts::half_to_float(fs, hs, 1023);
+        }
+    }
+};
+DEF_BENCH(return new HalfToFloatBench;)
diff --git a/src/core/SkOpts.cpp b/src/core/SkOpts.cpp
index fbc4f6c..7534ac5 100644
--- a/src/core/SkOpts.cpp
+++ b/src/core/SkOpts.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkHalf.h"
 #include "SkOnce.h"
 #include "SkOpts.h"
 
@@ -20,6 +21,19 @@
 #include "SkUtils_opts.h"
 #include "SkXfermode_opts.h"
 
+namespace SK_OPTS_NS {
+    static void float_to_half(uint16_t dst[], const float src[], int n) {
+        while (n-->0) {
+            *dst++ = SkFloatToHalf(*src++);
+        }
+    }
+    static void half_to_float(float dst[], const uint16_t src[], int n) {
+        while (n-->0) {
+            *dst++ = SkHalfToFloat(*src++);
+        }
+    }
+}
+
 #if defined(SK_CPU_X86) && !defined(SK_BUILD_FOR_IOS)
     #if defined(SK_BUILD_FOR_WIN32)
         #include <intrin.h>
@@ -90,6 +104,9 @@
     decltype(inverted_CMYK_to_RGB1) inverted_CMYK_to_RGB1 = sk_default::inverted_CMYK_to_RGB1;
     decltype(inverted_CMYK_to_BGR1) inverted_CMYK_to_BGR1 = sk_default::inverted_CMYK_to_BGR1;
 
+    decltype(half_to_float) half_to_float = sk_default::half_to_float;
+    decltype(float_to_half) float_to_half = sk_default::float_to_half;
+
     // Each Init_foo() is defined in src/opts/SkOpts_foo.cpp.
     void Init_ssse3();
     void Init_sse41();
diff --git a/src/core/SkOpts.h b/src/core/SkOpts.h
index 2e8778e..c717526 100644
--- a/src/core/SkOpts.h
+++ b/src/core/SkOpts.h
@@ -67,6 +67,9 @@
                         grayA_to_rgbA,         // i.e. expand to color channels and premultiply
                         inverted_CMYK_to_RGB1, // i.e. convert color space
                         inverted_CMYK_to_BGR1; // i.e. convert color space
+
+    extern void (*half_to_float)(float[], const uint16_t[], int);
+    extern void (*float_to_half)(uint16_t[], const float[], int);
 }
 
 #endif//SkOpts_DEFINED
diff --git a/tests/Float16Test.cpp b/tests/Float16Test.cpp
index 0a2c3d5..f437268 100644
--- a/tests/Float16Test.cpp
+++ b/tests/Float16Test.cpp
@@ -8,6 +8,7 @@
 #include "Test.h"
 #include "SkColor.h"
 #include "SkHalf.h"
+#include "SkOpts.h"
 #include "SkPixmap.h"
 
 static bool eq_within_half_float(float a, float b) {
@@ -50,3 +51,16 @@
         }
     }
 }
+
+DEF_TEST(float_to_half, reporter) {
+    const float    fs[] = {    1.0,    2.0,    3.0,    4.0,    5.0,    6.0,    7.0 };
+    const uint16_t hs[] = { 0x3c00, 0x4000, 0x4200, 0x4400, 0x4500, 0x4600, 0x4700 };
+
+    uint16_t hscratch[7];
+    SkOpts::float_to_half(hscratch, fs, 7);
+    REPORTER_ASSERT(reporter, 0 == memcmp(hscratch, hs, sizeof(hs)));
+
+    float fscratch[7];
+    SkOpts::half_to_float(fscratch, hs, 7);
+    REPORTER_ASSERT(reporter, 0 == memcmp(fscratch, fs, sizeof(fs)));
+}