Specify COLRv1 gradient interpolation

Stage this behind a new SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX flag so
that users can be updated in an orderly fashion.

Bug: skia:40045100
Change-Id: I12b0b58f83476635b31b31deaa29e015bdf5010b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/747671
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
diff --git a/src/ports/SkFontHost_FreeType_common.cpp b/src/ports/SkFontHost_FreeType_common.cpp
index 70af197..885db58 100644
--- a/src/ports/SkFontHost_FreeType_common.cpp
+++ b/src/ports/SkFontHost_FreeType_common.cpp
@@ -434,6 +434,7 @@
 // truncating and drawing color lines. When drawing into N32 surfaces, this is expected to be true.
 // If that changes, or if we support other color spaces in CPAL tables at some point, this needs to
 // be looked at.
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
 SkColor lerpSkColor(SkColor c0, SkColor c1, float t) {
     // Due to the floating point calculation in the caller, when interpolating between very narrow
     // stops, we may get values outside the interpolation range, guard against these.
@@ -448,6 +449,26 @@
 
     return Sk4f_toL32(c_4f);
 }
+#else
+SkColor4f lerpSkColor(SkColor4f c0, SkColor4f c1, float t) {
+    // Due to the floating point calculation in the caller, when interpolating between very narrow
+    // stops, we may get values outside the interpolation range, guard against these.
+    if (t < 0) {
+        return c0;
+    }
+    if (t > 1) {
+        return c1;
+    }
+
+    const auto c0_4f = skvx::float4::Load(c0.vec());
+    const auto c1_4f = skvx::float4::Load(c1.vec());
+    const auto c_4f = c0_4f + (c1_4f - c0_4f) * t;
+
+    SkColor4f l;
+    c_4f.store(l.vec());
+    return l;
+}
+#endif
 
 enum TruncateStops {
     TruncateStart,
@@ -457,7 +478,11 @@
 // Truncate a vector of color stops at a previously computed stop position and insert at that
 // position the color interpolated between the surrounding stops.
 void truncateToStopInterpolating(SkScalar zeroRadiusStop,
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
                                  std::vector<SkColor>& colors,
+#else
+                                 std::vector<SkColor4f>& colors,
+#endif
                                  std::vector<SkScalar>& stops,
                                  TruncateStops truncateStops) {
     if (stops.size() <= 1u ||
@@ -472,7 +497,11 @@
 
     const float t = (zeroRadiusStop - stops[afterIndex - 1]) /
                     (stops[afterIndex] - stops[afterIndex - 1]);
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
     SkColor lerpColor = lerpSkColor(colors[afterIndex - 1], colors[afterIndex], t);
+#else
+    SkColor4f lerpColor = lerpSkColor(colors[afterIndex - 1], colors[afterIndex], t);
+#endif
 
     if (truncateStops == TruncateStart) {
         stops.erase(stops.begin(), stops.begin() + afterIndex);
@@ -602,7 +631,11 @@
     auto fetchColorStops = [&face, &palette, &foregroundColor](
                                                const FT_ColorStopIterator& colorStopIterator,
                                                std::vector<SkScalar>& stops,
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
                                                std::vector<SkColor>& colors) -> bool {
+#else
+                                               std::vector<SkColor4f>& colors) -> bool {
+#endif
         const FT_UInt colorStopCount = colorStopIterator.num_color_stops;
         if (colorStopCount == 0) {
             return false;
@@ -612,29 +645,52 @@
         // "Applications shall apply the colorStops in increasing stopOffset order."
         struct ColorStop {
             SkScalar pos;
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
             SkColor color;
+#else
+            SkColor4f color;
+#endif
         };
         std::vector<ColorStop> colorStopsSorted;
         colorStopsSorted.resize(colorStopCount);
 
-        FT_ColorStop color_stop;
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
+       FT_ColorStop color_stop;
+       FT_ColorStopIterator mutable_color_stop_iterator = colorStopIterator;
+       while (FT_Get_Colorline_Stops(face, &color_stop, &mutable_color_stop_iterator)) {
+           FT_UInt index = mutable_color_stop_iterator.current_color_stop - 1;
+           colorStopsSorted[index].pos = color_stop.stop_offset / kColorStopShift;
+           FT_UInt16& palette_index = color_stop.color.palette_index;
+           if (palette_index == kForegroundColorPaletteIndex) {
+               U8CPU newAlpha = SkColorGetA(foregroundColor) *
+                                SkColrV1AlphaToFloat(color_stop.color.alpha);
+               colorStopsSorted[index].color = SkColorSetA(foregroundColor, newAlpha);
+           } else if (palette_index >= palette.size()) {
+               return false;
+           } else {
+               U8CPU newAlpha = SkColorGetA(palette[palette_index]) *
+                                SkColrV1AlphaToFloat(color_stop.color.alpha);
+               colorStopsSorted[index].color = SkColorSetA(palette[palette_index], newAlpha);
+           }
+       }
+#else
+        FT_ColorStop ftStop;
         FT_ColorStopIterator mutable_color_stop_iterator = colorStopIterator;
-        while (FT_Get_Colorline_Stops(face, &color_stop, &mutable_color_stop_iterator)) {
+        while (FT_Get_Colorline_Stops(face, &ftStop, &mutable_color_stop_iterator)) {
             FT_UInt index = mutable_color_stop_iterator.current_color_stop - 1;
-            colorStopsSorted[index].pos = color_stop.stop_offset / kColorStopShift;
-            FT_UInt16& palette_index = color_stop.color.palette_index;
+            ColorStop& skStop = colorStopsSorted[index];
+            skStop.pos = ftStop.stop_offset / kColorStopShift;
+            FT_UInt16& palette_index = ftStop.color.palette_index;
             if (palette_index == kForegroundColorPaletteIndex) {
-                U8CPU newAlpha = SkColorGetA(foregroundColor) *
-                                 SkColrV1AlphaToFloat(color_stop.color.alpha);
-                colorStopsSorted[index].color = SkColorSetA(foregroundColor, newAlpha);
+                skStop.color = SkColor4f::FromColor(foregroundColor);
             } else if (palette_index >= palette.size()) {
                 return false;
             } else {
-                U8CPU newAlpha = SkColorGetA(palette[palette_index]) *
-                                 SkColrV1AlphaToFloat(color_stop.color.alpha);
-                colorStopsSorted[index].color = SkColorSetA(palette[palette_index], newAlpha);
+                skStop.color = SkColor4f::FromColor(palette[palette_index]);
             }
+            skStop.color.fA *= SkColrV1AlphaToFloat(ftStop.color.alpha);
         }
+#endif
 
         std::stable_sort(colorStopsSorted.begin(), colorStopsSorted.end(),
                          [](const ColorStop& a, const ColorStop& b) { return a.pos < b.pos; });
@@ -653,6 +709,7 @@
             FT_PaintSolid solid = colrPaint.u.solid;
 
             // Dont' draw anything with this color if the palette index is out of bounds.
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
             SkColor color = SK_ColorTRANSPARENT;
             if (solid.color.palette_index == kForegroundColorPaletteIndex) {
                 U8CPU newAlpha = SkColorGetA(foregroundColor) *
@@ -665,6 +722,17 @@
                                  SkColrV1AlphaToFloat(solid.color.alpha);
                 color = SkColorSetA(palette[solid.color.palette_index], newAlpha);
             }
+#else
+            SkColor4f color = SkColors::kTransparent;
+            if (solid.color.palette_index == kForegroundColorPaletteIndex) {
+                color = SkColor4f::FromColor(foregroundColor);
+            } else if (solid.color.palette_index >= palette.size()) {
+                return false;
+            } else {
+                color = SkColor4f::FromColor(palette[solid.color.palette_index]);
+            }
+            color.fA *= SkColrV1AlphaToFloat(solid.color.alpha);
+#endif
             paint->setShader(nullptr);
             paint->setColor(color);
             return true;
@@ -672,7 +740,11 @@
         case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: {
             const FT_PaintLinearGradient& linearGradient = colrPaint.u.linear_gradient;
             std::vector<SkScalar> stops;
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
             std::vector<SkColor> colors;
+#else
+            std::vector<SkColor4f> colors;
+#endif
 
             if (!fetchColorStops(linearGradient.colorline.color_stop_iterator, stops, colors)) {
                 return false;
@@ -761,10 +833,24 @@
                 }
             }
 
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
             sk_sp<SkShader> shader(SkGradientShader::MakeLinear(
                                    linePositions,
                                    colors.data(), stops.data(), stops.size(),
                                    tileMode));
+#else
+            sk_sp<SkShader> shader(SkGradientShader::MakeLinear(
+                linePositions,
+                colors.data(), SkColorSpace::MakeSRGB(), stops.data(), stops.size(),
+                tileMode,
+                SkGradientShader::Interpolation{
+                    SkGradientShader::Interpolation::InPremul::kNo,
+                    SkGradientShader::Interpolation::ColorSpace::kSRGB,
+                    SkGradientShader::Interpolation::HueMethod::kShorter
+                },
+                nullptr));
+#endif
+
             SkASSERT(shader);
             // An opaque color is needed to ensure the gradient is not modulated by alpha.
             paint->setColor(SK_ColorBLACK);
@@ -782,7 +868,11 @@
 
 
             std::vector<SkScalar> stops;
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
             std::vector<SkColor> colors;
+#else
+            std::vector<SkColor4f> colors;
+#endif
             if (!fetchColorStops(radialGradient.colorline.color_stop_iterator, stops, colors)) {
                 return false;
             }
@@ -938,9 +1028,23 @@
             // An opaque color is needed to ensure the gradient is not modulated by alpha.
             paint->setColor(SK_ColorBLACK);
 
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
             paint->setShader(SkGradientShader::MakeTwoPointConical(
                     start, startRadius, end, endRadius, colors.data(), stops.data(), stops.size(),
                     tileMode));
+#else
+            paint->setShader(SkGradientShader::MakeTwoPointConical(
+                start, startRadius, end, endRadius,
+                colors.data(), SkColorSpace::MakeSRGB(), stops.data(), stops.size(),
+                tileMode,
+                SkGradientShader::Interpolation{
+                    SkGradientShader::Interpolation::InPremul::kNo,
+                    SkGradientShader::Interpolation::ColorSpace::kSRGB,
+                    SkGradientShader::Interpolation::HueMethod::kShorter
+                },
+                nullptr));
+#endif
+
             return true;
         }
         case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
@@ -957,7 +1061,11 @@
             endAngle += 180.0f;
 
             std::vector<SkScalar> stops;
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
             std::vector<SkColor> colors;
+#else
+            std::vector<SkColor4f> colors;
+#endif
             if (!fetchColorStops(sweepGradient.colorline.color_stop_iterator, stops, colors)) {
                 return false;
             }
@@ -1042,6 +1150,7 @@
                 }
             }
 
+#ifdef SK_IGNORE_COLRV1_GRADIENT_COLORSPACE_FIX
             paint->setShader(SkGradientShader::MakeSweep(center.x(), center.y(),
                                                          colors.data(),
                                                          stops.data(), stops.size(),
@@ -1049,6 +1158,20 @@
                                                          startAngleScaled,
                                                          endAngleScaled,
                                                          0, nullptr));
+#else
+            paint->setShader(SkGradientShader::MakeSweep(
+                center.x(), center.y(),
+                colors.data(), SkColorSpace::MakeSRGB(), stops.data(), stops.size(),
+                tileMode,
+                startAngleScaled, endAngleScaled,
+                SkGradientShader::Interpolation{
+                    SkGradientShader::Interpolation::InPremul::kNo,
+                    SkGradientShader::Interpolation::ColorSpace::kSRGB,
+                    SkGradientShader::Interpolation::HueMethod::kShorter
+                },
+                nullptr));
+#endif
+
             return true;
         }
         default: {
diff --git a/src/ports/SkScalerContext_win_dw.cpp b/src/ports/SkScalerContext_win_dw.cpp
index 608374a..68b4744 100644
--- a/src/ports/SkScalerContext_win_dw.cpp
+++ b/src/ports/SkScalerContext_win_dw.cpp
@@ -781,16 +781,24 @@
             }
         }
 
-        std::unique_ptr<SkColor[]> skColors(new SkColor[stops.size()]);
+        std::unique_ptr<SkColor4f[]> skColors(new SkColor4f[stops.size()]);
         std::unique_ptr<SkScalar[]> skStops(new SkScalar[stops.size()]);
         for (size_t i = 0; i < stops.size(); ++i) {
-            skColors[i] = sk_color_from(stops[i].color).toSkColor();
+            skColors[i] = sk_color_from(stops[i].color);
             skStops[i] = stops[i].position;
         }
 
         sk_sp<SkShader> shader(SkGradientShader::MakeLinear(
             linePositions,
-            skColors.get(), skStops.get(), stops.size(), tileMode));
+            skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
+            tileMode,
+            SkGradientShader::Interpolation{
+                SkGradientShader::Interpolation::InPremul::kNo,
+                SkGradientShader::Interpolation::ColorSpace::kSRGB,
+                SkGradientShader::Interpolation::HueMethod::kShorter
+            },
+            nullptr));
+
         SkASSERT(shader);
         // An opaque color is needed to ensure the gradient is not modulated by alpha.
         skPaint.setColor(SK_ColorBLACK);
@@ -964,10 +972,10 @@
             }
         }
 
-        std::unique_ptr<SkColor[]> skColors(new SkColor[stops.size()]);
+        std::unique_ptr<SkColor4f[]> skColors(new SkColor4f[stops.size()]);
         std::unique_ptr<SkScalar[]> skStops(new SkScalar[stops.size()]);
         for (size_t i = 0; i < stops.size(); ++i) {
-            skColors[i] = sk_color_from(stops[i].color).toSkColor();
+            skColors[i] = sk_color_from(stops[i].color);
             skStops[i] = stops[i].position;
         }
 
@@ -975,7 +983,14 @@
         skPaint.setColor(SK_ColorBLACK);
         skPaint.setShader(SkGradientShader::MakeTwoPointConical(
             start, startRadius, end, endRadius,
-            skColors.get(), skStops.get(), stops.size(), tileMode));
+            skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
+            tileMode,
+            SkGradientShader::Interpolation{
+                SkGradientShader::Interpolation::InPremul::kNo,
+                SkGradientShader::Interpolation::ColorSpace::kSRGB,
+                SkGradientShader::Interpolation::HueMethod::kShorter
+            },
+            nullptr));
         canvas.drawPaint(skPaint);
         return true;
     }
@@ -1082,18 +1097,24 @@
             }
         }
 
-        std::unique_ptr<SkColor[]> skColors(new SkColor[stops.size()]);
+        std::unique_ptr<SkColor4f[]> skColors(new SkColor4f[stops.size()]);
         std::unique_ptr<SkScalar[]> skStops(new SkScalar[stops.size()]);
         for (size_t i = 0; i < stops.size(); ++i) {
-            skColors[i] = sk_color_from(stops[i].color).toSkColor();
+            skColors[i] = sk_color_from(stops[i].color);
             skStops[i] = stops[i].position;
         }
 
         skPaint.setShader(SkGradientShader::MakeSweep(
             center.x(), center.y(),
-            skColors.get(), skStops.get(), stops.size(), tileMode,
+            skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
+            tileMode,
             startAngleScaled, endAngleScaled,
-            0, nullptr));
+            SkGradientShader::Interpolation{
+                SkGradientShader::Interpolation::InPremul::kNo,
+                SkGradientShader::Interpolation::ColorSpace::kSRGB,
+                SkGradientShader::Interpolation::HueMethod::kShorter
+            },
+            nullptr));
         canvas.drawPaint(skPaint);
         return true;
     }