Gradients are serialized (and can be constructed) as SkColor4f + SkColorSpace
authorbrianosman <brianosman@google.com>
Wed, 28 Sep 2016 18:27:28 +0000 (11:27 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 28 Sep 2016 18:27:28 +0000 (11:27 -0700)
Added gradient shader factories that take SkColor4f + SkColorSpace.
Modified Descriptor to only store SkColor4f + SkColorSpace.
Existing factories make use of helper code to convert SkColor and
forward to the new factories.

Bumped SKP version to handle new gradient serialization format.
I was toying with using half-float when serializing SkColor4f,
despite my aggressive packing of flags, this format is significantly
bigger.

Also added GM to use 4f factories. This GM should (and does)
look identical to the existing gradients GM.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2370063002

Review-Url: https://codereview.chromium.org/2370063002

gm/gradients.cpp
include/core/SkPicture.h
include/effects/SkGradientShader.h
src/core/SkReadBuffer.h
src/effects/gradients/SkGradientShader.cpp
src/effects/gradients/SkGradientShaderPriv.h
src/effects/gradients/SkLinearGradient.cpp
src/effects/gradients/SkRadialGradient.cpp
src/effects/gradients/SkSweepGradient.cpp
src/effects/gradients/SkTwoPointConicalGradient.cpp

index 4a68702..d563814 100644 (file)
 namespace skiagm {
 
 struct GradData {
-    int             fCount;
-    const SkColor*  fColors;
-    const SkScalar* fPos;
+    int              fCount;
+    const SkColor*   fColors;
+    const SkColor4f* fColors4f;
+    const SkScalar*  fPos;
 };
 
 constexpr SkColor gColors[] = {
     SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
 };
+constexpr SkColor4f gColors4f[] ={
+    { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
+    { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
+    { 0.0f, 0.0f, 1.0f, 1.0f }, // Blue
+    { 1.0f, 1.0f, 1.0f, 1.0f }, // White
+    { 0.0f, 0.0f, 0.0f, 1.0f }  // Black
+};
 constexpr SkScalar gPos0[] = { 0, SK_Scalar1 };
 constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
 constexpr SkScalar gPos2[] = {
@@ -30,14 +38,19 @@ constexpr SkScalar gPosClamp[]   = {0.0f, 0.0f, 1.0f, 1.0f};
 constexpr SkColor  gColorClamp[] = {
     SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
 };
-
+constexpr SkColor4f gColor4fClamp[] ={
+    { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
+    { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
+    { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
+    { 0.0f, 0.0f, 1.0f, 1.0f }  // Blue
+};
 constexpr GradData gGradData[] = {
-    { 2, gColors, nullptr },
-    { 2, gColors, gPos0 },
-    { 2, gColors, gPos1 },
-    { 5, gColors, nullptr },
-    { 5, gColors, gPos2 },
-    { 4, gColorClamp, gPosClamp }
+    { 2, gColors, gColors4f, nullptr },
+    { 2, gColors, gColors4f, gPos0 },
+    { 2, gColors, gColors4f, gPos1 },
+    { 5, gColors, gColors4f, nullptr },
+    { 5, gColors, gColors4f, gPos2 },
+    { 4, gColorClamp, gColor4fClamp, gPosClamp }
 };
 
 static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data,
@@ -46,6 +59,13 @@ static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data,
                                         &localMatrix);
 }
 
+static sk_sp<SkShader> MakeLinear4f(const SkPoint pts[2], const GradData& data,
+                                    SkShader::TileMode tm, const SkMatrix& localMatrix) {
+    auto srgb = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)->makeLinearGamma();
+    return SkGradientShader::MakeLinear(pts, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0,
+                                        &localMatrix);
+}
+
 static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data,
                                   SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center;
@@ -55,6 +75,16 @@ static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data,
                                         tm, 0, &localMatrix);
 }
 
+static sk_sp<SkShader> MakeRadial4f(const SkPoint pts[2], const GradData& data,
+                                    SkShader::TileMode tm, const SkMatrix& localMatrix) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    auto srgb = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)->makeLinearGamma();
+    return SkGradientShader::MakeRadial(center, center.fX, data.fColors4f, srgb, data.fPos,
+                                        data.fCount, tm, 0, &localMatrix);
+}
+
 static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data,
                                  SkShader::TileMode, const SkMatrix& localMatrix) {
     SkPoint center;
@@ -64,6 +94,16 @@ static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data,
                                        0, &localMatrix);
 }
 
+static sk_sp<SkShader> MakeSweep4f(const SkPoint pts[2], const GradData& data,
+                                   SkShader::TileMode, const SkMatrix& localMatrix) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    auto srgb = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)->makeLinearGamma();
+    return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors4f, srgb, data.fPos,
+                                       data.fCount, 0, &localMatrix);
+}
+
 static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data,
                                    SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
@@ -77,8 +117,22 @@ static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data,
                                                  0, &localMatrix);
 }
 
+static sk_sp<SkShader> Make2Radial4f(const SkPoint pts[2], const GradData& data,
+                                     SkShader::TileMode tm, const SkMatrix& localMatrix) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3) / 5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1) / 4));
+    auto srgb = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)->makeLinearGamma();
+    return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
+                                                 center0, (pts[1].fX - pts[0].fX) / 2,
+                                                 data.fColors4f, srgb, data.fPos, data.fCount, tm,
+                                                 0, &localMatrix);
+}
+
 static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data,
-                             SkShader::TileMode tm, const SkMatrix& localMatrix) {
+                                    SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
     SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
     SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
@@ -89,11 +143,27 @@ static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data,
                                                  data.fCount, tm, 0, &localMatrix);
 }
 
+static sk_sp<SkShader> Make2Conical4f(const SkPoint pts[2], const GradData& data,
+                                      SkShader::TileMode tm, const SkMatrix& localMatrix) {
+    SkPoint center0, center1;
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
+    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
+    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
+    auto srgb = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)->makeLinearGamma();
+    return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
+                                                 data.fColors4f, srgb, data.fPos,
+                                                 data.fCount, tm, 0, &localMatrix);
+}
+
 typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data,
                                      SkShader::TileMode tm, const SkMatrix& localMatrix);
 constexpr GradMaker gGradMakers[] = {
     MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
 };
+constexpr GradMaker gGradMakers4f[] ={
+    MakeLinear4f, MakeRadial4f, MakeSweep4f, Make2Radial4f, Make2Conical4f
+};
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -152,6 +222,62 @@ private:
 DEF_GM( return new GradientsGM(true); )
 DEF_GM( return new GradientsGM(false); )
 
+// Like the original gradients GM, but using the SkColor4f shader factories. Should be identical.
+class Gradients4fGM : public GM {
+public:
+    Gradients4fGM(bool dither) : fDither(dither) {
+        this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD));
+    }
+
+protected:
+
+    SkString onShortName() {
+        return SkString(fDither ? "gradients4f" : "gradients4f_nodither");
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(840, 815); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        SkPoint pts[2] ={
+            { 0, 0 },
+            { SkIntToScalar(100), SkIntToScalar(100) }
+        };
+        SkShader::TileMode tm = SkShader::kClamp_TileMode;
+        SkRect r ={ 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setDither(fDither);
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
+            canvas->save();
+            for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers4f); j++) {
+                SkMatrix scale = SkMatrix::I();
+
+                if (i == 5) { // if the clamp case
+                    scale.setScale(0.5f, 0.5f);
+                    scale.postTranslate(25.f, 25.f);
+                }
+
+                paint.setShader(gGradMakers4f[j](pts, gGradData[i], tm, scale));
+                canvas->drawRect(r, paint);
+                canvas->translate(0, SkIntToScalar(120));
+            }
+            canvas->restore();
+            canvas->translate(SkIntToScalar(120), 0);
+        }
+    }
+
+protected:
+    bool fDither;
+
+private:
+    typedef GM INHERITED;
+};
+DEF_GM(return new Gradients4fGM(true); )
+DEF_GM(return new Gradients4fGM(false); )
+
 // Based on the original gradient slide, but with perspective applied to the
 // gradient shaders' local matrices
 class GradientsLocalPerspectiveGM : public GM {
index c8de7da..1dcbcc5 100644 (file)
@@ -223,10 +223,11 @@ private:
     // V46: Add drawTextRSXform
     // V47: Add occluder rect to SkBlurMaskFilter
     // V48: Read and write extended SkTextBlobs.
+    // V49: Gradients serialized as SkColor4f + SkColorSpace
 
     // Only SKPs within the min/current picture version range (inclusive) can be read.
     static const uint32_t     MIN_PICTURE_VERSION = 35;     // Produced by Chrome M39.
-    static const uint32_t CURRENT_PICTURE_VERSION = 48;
+    static const uint32_t CURRENT_PICTURE_VERSION = 49;
 
     static_assert(MIN_PICTURE_VERSION <= 41,
                   "Remove kFontFileName and related code from SkFontDescriptor.cpp.");
@@ -240,6 +241,9 @@ private:
     static_assert(MIN_PICTURE_VERSION <= 45,
                   "Remove decoding of old SkTypeface::Style from SkFontDescriptor.cpp.");
 
+    static_assert(MIN_PICTURE_VERSION <= 48,
+                  "Remove legacy gradient deserialization code from SkGradientShader.cpp.");
+
     static bool IsValidPictInfo(const SkPictInfo& info);
     static sk_sp<SkPicture> Forwardport(const SkPictInfo&,
                                         const SkPictureData*,
index 6b86f44..2fcce75 100644 (file)
@@ -48,6 +48,28 @@ public:
         return MakeLinear(pts, colors, pos, count, mode, 0, NULL);
     }
 
+    /** Returns a shader that generates a linear gradient between the two specified points.
+        <p />
+        @param  pts     The start and end points for the gradient.
+        @param  colors  The array[count] of colors, to be distributed between the two points
+        @param  pos     May be NULL. array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the start and end point.
+                        If this is not null, the values must begin with 0, end with 1.0, and
+                        intermediate values must be strictly increasing.
+        @param  count   Must be >=2. The number of colors (and pos if not NULL) entries.
+        @param  mode    The tiling mode
+    */
+    static sk_sp<SkShader> MakeLinear(const SkPoint pts[2],
+                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
+                                      const SkScalar pos[], int count, SkShader::TileMode mode,
+                                      uint32_t flags, const SkMatrix* localMatrix);
+    static sk_sp<SkShader> MakeLinear(const SkPoint pts[2],
+                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
+                                      const SkScalar pos[], int count, SkShader::TileMode mode) {
+        return MakeLinear(pts, colors, std::move(colorSpace), pos, count, mode, 0, NULL);
+    }
+
     /** Returns a shader that generates a radial gradient given the center and radius.
         <p />
         @param  center  The center of the circle for this gradient
@@ -71,6 +93,29 @@ public:
         return MakeRadial(center, radius, colors, pos, count, mode, 0, NULL);
     }
 
+    /** Returns a shader that generates a radial gradient given the center and radius.
+        <p />
+        @param  center  The center of the circle for this gradient
+        @param  radius  Must be positive. The radius of the circle for this gradient
+        @param  colors  The array[count] of colors, to be distributed between the center and edge of the circle
+        @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the center and edge of the circle.
+                        If this is not null, the values must begin with 0, end with 1.0, and
+                        intermediate values must be strictly increasing.
+        @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
+        @param  mode    The tiling mode
+    */
+    static sk_sp<SkShader> MakeRadial(const SkPoint& center, SkScalar radius,
+                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
+                                      const SkScalar pos[], int count, SkShader::TileMode mode,
+                                      uint32_t flags, const SkMatrix* localMatrix);
+    static sk_sp<SkShader> MakeRadial(const SkPoint& center, SkScalar radius,
+                                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
+                                      const SkScalar pos[], int count, SkShader::TileMode mode) {
+        return MakeRadial(center, radius, colors, std::move(colorSpace), pos, count, mode, 0, NULL);
+    }
+
     /**
      *  Returns a shader that generates a conical gradient given two circles, or
      *  returns NULL if the inputs are invalid. The gradient interprets the
@@ -90,6 +135,27 @@ public:
                                    0, NULL);
     }
 
+    /**
+     *  Returns a shader that generates a conical gradient given two circles, or
+     *  returns NULL if the inputs are invalid. The gradient interprets the
+     *  two circles according to the following HTML spec.
+     *  http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient
+     */
+    static sk_sp<SkShader> MakeTwoPointConical(const SkPoint& start, SkScalar startRadius,
+                                               const SkPoint& end, SkScalar endRadius,
+                                               const SkColor4f colors[],
+                                               sk_sp<SkColorSpace> colorSpace, const SkScalar pos[],
+                                               int count, SkShader::TileMode mode,
+                                               uint32_t flags, const SkMatrix* localMatrix);
+    static sk_sp<SkShader> MakeTwoPointConical(const SkPoint& start, SkScalar startRadius,
+                                               const SkPoint& end, SkScalar endRadius,
+                                               const SkColor4f colors[],
+                                               sk_sp<SkColorSpace> colorSpace, const SkScalar pos[],
+                                               int count, SkShader::TileMode mode) {
+        return MakeTwoPointConical(start, startRadius, end, endRadius, colors,
+                                   std::move(colorSpace), pos, count, mode, 0, NULL);
+    }
+
     /** Returns a shader that generates a sweep gradient given a center.
         <p />
         @param  cx      The X coordinate of the center of the sweep
@@ -110,6 +176,28 @@ public:
         return MakeSweep(cx, cy, colors, pos, count, 0, NULL);
     }
 
+    /** Returns a shader that generates a sweep gradient given a center.
+        <p />
+        @param  cx      The X coordinate of the center of the sweep
+        @param  cx      The Y coordinate of the center of the sweep
+        @param  colors  The array[count] of colors, to be distributed around the center.
+        @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the center and edge of the circle.
+                        If this is not null, the values must begin with 0, end with 1.0, and
+                        intermediate values must be strictly increasing.
+        @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
+    */
+    static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
+                                     const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
+                                     const SkScalar pos[], int count,
+                                     uint32_t flags, const SkMatrix* localMatrix);
+    static sk_sp<SkShader> MakeSweep(SkScalar cx, SkScalar cy,
+                                     const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
+                                     const SkScalar pos[], int count) {
+        return MakeSweep(cx, cy, colors, std::move(colorSpace), pos, count, 0, NULL);
+    }
+
 #ifdef SK_SUPPORT_LEGACY_CREATESHADER_PTR
     static SkShader* CreateLinear(const SkPoint pts[2],
                                   const SkColor colors[], const SkScalar pos[], int count,
index d29fef8..3e6742f 100644 (file)
@@ -68,6 +68,7 @@ public:
         kAnnotationsMovedToCanvas_Version  = 44,
         kLightingShaderWritesInvNormRotation = 45,
         kBlurMaskFilterWritesOccluder      = 47,
+        kGradientShaderFloatColor_Version  = 49,
     };
 
     /**
index 0faf006..df23039 100644 (file)
 #include "SkTwoPointConicalGradient.h"
 #include "SkSweepGradient.h"
 
+enum GradientSerializationFlags {
+    // Bits 29:31 used for various boolean flags
+    kHasPosition_GSF    = 0x80000000,
+    kHasLocalMatrix_GSF = 0x40000000,
+    kHasColorSpace_GSF  = 0x20000000,
+
+    // Bits 12:28 unused
+
+    // Bits 8:11 for fTileMode
+    kTileModeShift_GSF  = 8,
+    kTileModeMask_GSF   = 0xF,
+
+    // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80)
+    kGradFlagsShift_GSF = 0,
+    kGradFlagsMask_GSF  = 0xFF,
+};
+
 void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeColorArray(fColors, fCount);
-    // TODO: Flatten fColors4f and fColorSpace
+    uint32_t flags = 0;
+    if (fPos) {
+        flags |= kHasPosition_GSF;
+    }
+    if (fLocalMatrix) {
+        flags |= kHasLocalMatrix_GSF;
+    }
+    sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
+    if (colorSpaceData) {
+        flags |= kHasColorSpace_GSF;
+    }
+    SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
+    flags |= (fTileMode << kTileModeShift_GSF);
+    SkASSERT(fGradFlags <= kGradFlagsMask_GSF);
+    flags |= (fGradFlags << kGradFlagsShift_GSF);
+
+    buffer.writeUInt(flags);
+
+    buffer.writeColor4fArray(fColors, fCount);
+    if (colorSpaceData) {
+        buffer.writeDataAsByteArray(colorSpaceData.get());
+    }
     if (fPos) {
-        buffer.writeBool(true);
         buffer.writeScalarArray(fPos, fCount);
-    } else {
-        buffer.writeBool(false);
     }
-    buffer.write32(fTileMode);
-    buffer.write32(fGradFlags);
     if (fLocalMatrix) {
-        buffer.writeBool(true);
         buffer.writeMatrix(*fLocalMatrix);
-    } else {
-        buffer.writeBool(false);
     }
 }
 
 bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
-    // TODO: Unflatten fColors4f and fColorSpace
-    fCount = buffer.getArrayCount();
-    if (fCount > kStorageCount) {
-        size_t allocSize = (sizeof(SkColor) + sizeof(SkScalar)) * fCount;
-        fDynamicStorage.reset(allocSize);
-        fColors = (SkColor*)fDynamicStorage.get();
-        fPos = (SkScalar*)(fColors + fCount);
-    } else {
-        fColors = fColorStorage;
-        fPos = fPosStorage;
-    }
+    if (buffer.isVersionLT(SkReadBuffer::kGradientShaderFloatColor_Version)) {
+        fCount = buffer.getArrayCount();
+        if (fCount > kStorageCount) {
+            size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount;
+            fDynamicStorage.reset(allocSize);
+            fColors = (SkColor4f*)fDynamicStorage.get();
+            fPos = (SkScalar*)(fColors + fCount);
+        } else {
+            fColors = fColorStorage;
+            fPos = fPosStorage;
+        }
 
-    if (!buffer.readColorArray(const_cast<SkColor*>(fColors), fCount)) {
-        return false;
-    }
-    if (buffer.readBool()) {
-        if (!buffer.readScalarArray(const_cast<SkScalar*>(fPos), fCount)) {
+        // Old gradients serialized SkColor. Read that to a temporary location, then convert.
+        SkSTArray<2, SkColor, true> colors;
+        colors.resize_back(fCount);
+        if (!buffer.readColorArray(colors.begin(), fCount)) {
             return false;
         }
-    } else {
-        fPos = nullptr;
-    }
+        for (int i = 0; i < fCount; ++i) {
+            mutableColors()[i] = SkColor4f::FromColor(colors[i]);
+        }
+
+        if (buffer.readBool()) {
+            if (!buffer.readScalarArray(const_cast<SkScalar*>(fPos), fCount)) {
+                return false;
+            }
+        } else {
+            fPos = nullptr;
+        }
 
-    fTileMode = (SkShader::TileMode)buffer.read32();
-    fGradFlags = buffer.read32();
+        fColorSpace = nullptr;
+        fTileMode = (SkShader::TileMode)buffer.read32();
+        fGradFlags = buffer.read32();
 
-    if (buffer.readBool()) {
-        fLocalMatrix = &fLocalMatrixStorage;
-        buffer.readMatrix(&fLocalMatrixStorage);
+        if (buffer.readBool()) {
+            fLocalMatrix = &fLocalMatrixStorage;
+            buffer.readMatrix(&fLocalMatrixStorage);
+        } else {
+            fLocalMatrix = nullptr;
+        }
     } else {
-        fLocalMatrix = nullptr;
+        // New gradient format. Includes floating point color, color space, densely packed flags
+        uint32_t flags = buffer.readUInt();
+
+        fTileMode = (SkShader::TileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
+        fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF;
+
+        fCount = buffer.getArrayCount();
+        if (fCount > kStorageCount) {
+            size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount;
+            fDynamicStorage.reset(allocSize);
+            fColors = (SkColor4f*)fDynamicStorage.get();
+            fPos = (SkScalar*)(fColors + fCount);
+        } else {
+            fColors = fColorStorage;
+            fPos = fPosStorage;
+        }
+        if (!buffer.readColor4fArray(mutableColors(), fCount)) {
+            return false;
+        }
+        if (SkToBool(flags & kHasColorSpace_GSF)) {
+            sk_sp<SkData> data = buffer.readByteArrayAsData();
+            fColorSpace = SkColorSpace::Deserialize(data->data(), data->size());
+        } else {
+            fColorSpace = nullptr;
+        }
+        if (SkToBool(flags & kHasPosition_GSF)) {
+            if (!buffer.readScalarArray(mutablePos(), fCount)) {
+                return false;
+            }
+        } else {
+            fPos = nullptr;
+        }
+        if (SkToBool(flags & kHasLocalMatrix_GSF)) {
+            fLocalMatrix = &fLocalMatrixStorage;
+            buffer.readMatrix(&fLocalMatrixStorage);
+        } else {
+            fLocalMatrix = nullptr;
+        }
     }
     return buffer.isValid();
 }
@@ -110,8 +187,7 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatri
         if (desc.fPos) {
             size += sizeof(SkScalar);
         }
-        fOrigColors = reinterpret_cast<SkColor*>(
-                                        sk_malloc_throw(size * fColorCount));
+        fOrigColors = reinterpret_cast<SkColor*>(sk_malloc_throw(size * fColorCount));
     }
     else {
         fOrigColors = fStorage;
@@ -119,50 +195,31 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatri
 
     fOrigColors4f = (SkColor4f*)(fOrigColors + fColorCount);
 
-    // We should have been supplied with either fColors *or* (fColors4f and fColorSpace)
-    if (desc.fColors) {
-        // TODO: Should we support alternate gamma-encoded colorspaces with SkColor inputs?
-        SkASSERT(!desc.fColors4f && !desc.fColorSpace);
-
-        // Now copy over the colors, adding the dummies as needed
-        SkColor* origColors = fOrigColors;
-        if (dummyFirst) {
-            *origColors++ = desc.fColors[0];
-        }
-        memcpy(origColors, desc.fColors, desc.fCount * sizeof(SkColor));
-        if (dummyLast) {
-            origColors += desc.fCount;
-            *origColors = desc.fColors[desc.fCount - 1];
-        }
+    // Now copy over the colors, adding the dummies as needed
+    SkColor4f* origColors = fOrigColors4f;
+    if (dummyFirst) {
+        *origColors++ = desc.fColors[0];
+    }
+    memcpy(origColors, desc.fColors, desc.fCount * sizeof(SkColor4f));
+    if (dummyLast) {
+        origColors += desc.fCount;
+        *origColors = desc.fColors[desc.fCount - 1];
+    }
 
-        // Convert our SkColor colors to SkColor4f as well
-        for (int i = 0; i < fColorCount; ++i) {
-            fOrigColors4f[i] = SkColor4f::FromColor(fOrigColors[i]);
-        }
+    // Convert our SkColor4f colors to SkColor as well. Note that this is incorrect if the
+    // source colors are not in sRGB gamut. We would need to do a gamut transformation, but
+    // SkColorSpaceXform can't do that (yet). GrColorSpaceXform can, but we may not have GPU
+    // support compiled in here. For the common case (sRGB colors), this does the right thing.
+    for (int i = 0; i < fColorCount; ++i) {
+        fOrigColors[i] = fOrigColors4f[i].toSkColor();
+    }
 
-        // Color space refers to fColors4f, so it's always linear gamma
+    if (!desc.fColorSpace) {
+        // This happens if we were constructed from SkColors, so our colors are really sRGB
         fColorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGBLinear_Named);
     } else {
-        SkASSERT(desc.fColors4f && desc.fColorSpace && desc.fColorSpace->gammaIsLinear());
-
-        // Now copy over the colors, adding the dummies as needed
-        SkColor4f* origColors = fOrigColors4f;
-        if (dummyFirst) {
-            *origColors++ = desc.fColors4f[0];
-        }
-        memcpy(origColors, desc.fColors4f, desc.fCount * sizeof(SkColor4f));
-        if (dummyLast) {
-            origColors += desc.fCount;
-            *origColors = desc.fColors4f[desc.fCount - 1];
-        }
-
-        // Convert our SkColor4f colors to SkColor as well. Note that this is incorrect if the
-        // source colors are not in sRGB gamut. We would need to do a gamut transformation, but
-        // SkColorSpaceXform can't do that (yet). GrColorSpaceXform can, but we may not have GPU
-        // support compiled in here.
-        for (int i = 0; i < fColorCount; ++i) {
-            fOrigColors[i] = fOrigColors4f[i].toSkColor();
-        }
+        // The color space refers to the float colors, so it must be linear gamma
+        SkASSERT(desc.fColorSpace->gammaIsLinear());
         fColorSpace = desc.fColorSpace;
     }
 
@@ -256,8 +313,7 @@ void SkGradientShaderBase::initCommon() {
 
 void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
     Descriptor desc;
-    desc.fColors = fOrigColors;
-    desc.fColors4f = fOrigColors4f;
+    desc.fColors = fOrigColors4f;
     desc.fColorSpace = fColorSpace;
     desc.fPos = fOrigPos;
     desc.fCount = fColorCount;
@@ -772,18 +828,19 @@ void SkGradientShaderBase::toString(SkString* str) const {
 
 // Return true if these parameters are valid/legal/safe to construct a gradient
 //
-static bool valid_grad(const SkColor colors[], const SkScalar pos[], int count, unsigned tileMode) {
+static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count,
+                       unsigned tileMode) {
     return nullptr != colors && count >= 1 && tileMode < (unsigned)SkShader::kTileModeCount;
 }
 
 static void desc_init(SkGradientShaderBase::Descriptor* desc,
-                      const SkColor colors[], const SkScalar pos[], int colorCount,
+                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
+                      const SkScalar pos[], int colorCount,
                       SkShader::TileMode mode, uint32_t flags, const SkMatrix* localMatrix) {
     SkASSERT(colorCount > 1);
 
     desc->fColors       = colors;
-    desc->fColors4f     = nullptr;
-    desc->fColorSpace   = nullptr; // SkColor is always sRGB
+    desc->fColorSpace   = std::move(colorSpace);
     desc->fPos          = pos;
     desc->fCount        = colorCount;
     desc->fTileMode     = mode;
@@ -791,9 +848,9 @@ static void desc_init(SkGradientShaderBase::Descriptor* desc,
     desc->fLocalMatrix  = localMatrix;
 }
 
-// assumes colors is SkColor* and pos is SkScalar*
+// assumes colors is SkColor4f* and pos is SkScalar*
 #define EXPAND_1_COLOR(count)                \
-     SkColor tmp[2];                         \
+     SkColor4f tmp[2];                       \
      do {                                    \
          if (1 == count) {                   \
              tmp[0] = tmp[1] = colors[0];    \
@@ -804,7 +861,7 @@ static void desc_init(SkGradientShaderBase::Descriptor* desc,
      } while (0)
 
 struct ColorStopOptimizer {
-    ColorStopOptimizer(const SkColor* colors, const SkScalar* pos,
+    ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos,
                        int count, SkShader::TileMode mode)
         : fColors(colors)
         , fPos(pos)
@@ -841,9 +898,19 @@ struct ColorStopOptimizer {
             }
     }
 
-    const SkColor*  fColors;
-    const SkScalar* fPos;
-    int             fCount;
+    const SkColor4f* fColors;
+    const SkScalar*  fPos;
+    int              fCount;
+};
+
+struct ColorConverter {
+    ColorConverter(const SkColor* colors, int count) {
+        for (int i = 0; i < count; ++i) {
+            fColors4f.push_back(SkColor4f::FromColor(colors[i]));
+        }
+    }
+
+    SkSTArray<2, SkColor4f, true> fColors4f;
 };
 
 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
@@ -852,6 +919,18 @@ sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
                                              SkShader::TileMode mode,
                                              uint32_t flags,
                                              const SkMatrix* localMatrix) {
+    ColorConverter converter(colors, colorCount);
+    return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
+                      localMatrix);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
+                                             const SkColor4f colors[],
+                                             sk_sp<SkColorSpace> colorSpace,
+                                             const SkScalar pos[], int colorCount,
+                                             SkShader::TileMode mode,
+                                             uint32_t flags,
+                                             const SkMatrix* localMatrix) {
     if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
         return nullptr;
     }
@@ -859,22 +938,35 @@ sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
         return nullptr;
     }
     if (1 == colorCount) {
-        return SkShader::MakeColorShader(colors[0]);
+        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
     }
 
     ColorStopOptimizer opt(colors, pos, colorCount, mode);
 
     SkGradientShaderBase::Descriptor desc;
-    desc_init(&desc, opt.fColors, opt.fPos, opt.fCount, mode, flags, localMatrix);
+    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
+              localMatrix);
     return sk_make_sp<SkLinearGradient>(pts, desc);
 }
 
 sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
-                                         const SkColor colors[],
-                                         const SkScalar pos[], int colorCount,
-                                         SkShader::TileMode mode,
-                                         uint32_t flags,
-                                         const SkMatrix* localMatrix) {
+                                             const SkColor colors[],
+                                             const SkScalar pos[], int colorCount,
+                                             SkShader::TileMode mode,
+                                             uint32_t flags,
+                                             const SkMatrix* localMatrix) {
+    ColorConverter converter(colors, colorCount);
+    return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode,
+                      flags, localMatrix);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
+                                             const SkColor4f colors[],
+                                             sk_sp<SkColorSpace> colorSpace,
+                                             const SkScalar pos[], int colorCount,
+                                             SkShader::TileMode mode,
+                                             uint32_t flags,
+                                             const SkMatrix* localMatrix) {
     if (radius <= 0) {
         return nullptr;
     }
@@ -882,26 +974,43 @@ sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar rad
         return nullptr;
     }
     if (1 == colorCount) {
-        return SkShader::MakeColorShader(colors[0]);
+        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
     }
 
     ColorStopOptimizer opt(colors, pos, colorCount, mode);
 
     SkGradientShaderBase::Descriptor desc;
-    desc_init(&desc, opt.fColors, opt.fPos, opt.fCount, mode, flags, localMatrix);
+    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
+              localMatrix);
     return sk_make_sp<SkRadialGradient>(center, radius, desc);
 }
 
 sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
-                                                  SkScalar startRadius,
-                                                  const SkPoint& end,
-                                                  SkScalar endRadius,
-                                                  const SkColor colors[],
-                                                  const SkScalar pos[],
-                                                  int colorCount,
-                                                  SkShader::TileMode mode,
-                                                  uint32_t flags,
-                                                  const SkMatrix* localMatrix) {
+                                                      SkScalar startRadius,
+                                                      const SkPoint& end,
+                                                      SkScalar endRadius,
+                                                      const SkColor colors[],
+                                                      const SkScalar pos[],
+                                                      int colorCount,
+                                                      SkShader::TileMode mode,
+                                                      uint32_t flags,
+                                                      const SkMatrix* localMatrix) {
+    ColorConverter converter(colors, colorCount);
+    return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
+                               nullptr, pos, colorCount, mode, flags, localMatrix);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
+                                                      SkScalar startRadius,
+                                                      const SkPoint& end,
+                                                      SkScalar endRadius,
+                                                      const SkColor4f colors[],
+                                                      sk_sp<SkColorSpace> colorSpace,
+                                                      const SkScalar pos[],
+                                                      int colorCount,
+                                                      SkShader::TileMode mode,
+                                                      uint32_t flags,
+                                                      const SkMatrix* localMatrix) {
     if (startRadius < 0 || endRadius < 0) {
         return nullptr;
     }
@@ -922,11 +1031,12 @@ sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
     SkGradientShaderBase::Descriptor desc;
 
     if (!flipGradient) {
-        desc_init(&desc, opt.fColors, opt.fPos, opt.fCount, mode, flags, localMatrix);
+        desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
+                  localMatrix);
         return sk_make_sp<SkTwoPointConicalGradient>(start, startRadius, end, endRadius,
                                                      flipGradient, desc);
     } else {
-        SkAutoSTArray<8, SkColor> colorsNew(opt.fCount);
+        SkAutoSTArray<8, SkColor4f> colorsNew(opt.fCount);
         SkAutoSTArray<8, SkScalar> posNew(opt.fCount);
         for (int i = 0; i < opt.fCount; ++i) {
             colorsNew[i] = opt.fColors[opt.fCount - i - 1];
@@ -936,9 +1046,11 @@ sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
             for (int i = 0; i < opt.fCount; ++i) {
                 posNew[i] = 1 - opt.fPos[opt.fCount - i - 1];
             }
-            desc_init(&desc, colorsNew.get(), posNew.get(), opt.fCount, mode, flags, localMatrix);
+            desc_init(&desc, colorsNew.get(), std::move(colorSpace), posNew.get(), opt.fCount, mode,
+                      flags, localMatrix);
         } else {
-            desc_init(&desc, colorsNew.get(), nullptr, opt.fCount, mode, flags, localMatrix);
+            desc_init(&desc, colorsNew.get(), std::move(colorSpace), nullptr, opt.fCount, mode,
+                      flags, localMatrix);
         }
 
         return sk_make_sp<SkTwoPointConicalGradient>(end, endRadius, start, startRadius,
@@ -947,16 +1059,28 @@ sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
 }
 
 sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
-                                        const SkColor colors[],
-                                        const SkScalar pos[],
-                                        int colorCount,
-                                        uint32_t flags,
-                                        const SkMatrix* localMatrix) {
+                                            const SkColor colors[],
+                                            const SkScalar pos[],
+                                            int colorCount,
+                                            uint32_t flags,
+                                            const SkMatrix* localMatrix) {
+    ColorConverter converter(colors, colorCount);
+    return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount, flags,
+                     localMatrix);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
+                                            const SkColor4f colors[],
+                                            sk_sp<SkColorSpace> colorSpace,
+                                            const SkScalar pos[],
+                                            int colorCount,
+                                            uint32_t flags,
+                                            const SkMatrix* localMatrix) {
     if (!valid_grad(colors, pos, colorCount, SkShader::kClamp_TileMode)) {
         return nullptr;
     }
     if (1 == colorCount) {
-        return SkShader::MakeColorShader(colors[0]);
+        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
     }
 
     auto mode = SkShader::kClamp_TileMode;
@@ -964,7 +1088,8 @@ sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
     ColorStopOptimizer opt(colors, pos, colorCount, mode);
 
     SkGradientShaderBase::Descriptor desc;
-    desc_init(&desc, opt.fColors, opt.fPos, opt.fCount, mode, flags, localMatrix);
+    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
+              localMatrix);
     return sk_make_sp<SkSweepGradient>(cx, cy, desc);
 }
 
index 4e6a325..df7ac78 100644 (file)
@@ -84,8 +84,7 @@ public:
         }
 
         const SkMatrix*     fLocalMatrix;
-        const SkColor*      fColors;
-        const SkColor4f*    fColors4f;
+        const SkColor4f*    fColors;
         sk_sp<SkColorSpace> fColorSpace;
         const SkScalar*     fPos;
         int                 fCount;
@@ -103,14 +102,14 @@ public:
 
         // fColors and fPos always point into local memory, so they can be safely mutated
         //
-        SkColor* mutableColors() { return const_cast<SkColor*>(fColors); }
+        SkColor4f* mutableColors() { return const_cast<SkColor4f*>(fColors); }
         SkScalar* mutablePos() { return const_cast<SkScalar*>(fPos); }
 
     private:
         enum {
             kStorageCount = 16
         };
-        SkColor fColorStorage[kStorageCount];
+        SkColor4f fColorStorage[kStorageCount];
         SkScalar fPosStorage[kStorageCount];
         SkMatrix fLocalMatrixStorage;
         SkAutoMalloc fDynamicStorage;
index 4bf5e3c..3372499 100644 (file)
@@ -63,8 +63,9 @@ sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
     SkPoint pts[2];
     pts[0] = buffer.readPoint();
     pts[1] = buffer.readPoint();
-    return SkGradientShader::MakeLinear(pts, desc.fColors, desc.fPos, desc.fCount, desc.fTileMode,
-                                        desc.fGradFlags, desc.fLocalMatrix);
+    return SkGradientShader::MakeLinear(pts, desc.fColors, std::move(desc.fColorSpace), desc.fPos,
+                                        desc.fCount, desc.fTileMode, desc.fGradFlags,
+                                        desc.fLocalMatrix);
 }
 
 void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
index 6eaecff..15d2da4 100644 (file)
@@ -67,8 +67,9 @@ sk_sp<SkFlattenable> SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
     }
     const SkPoint center = buffer.readPoint();
     const SkScalar radius = buffer.readScalar();
-    return SkGradientShader::MakeRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
-                                        desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
+    return SkGradientShader::MakeRadial(center, radius, desc.fColors, std::move(desc.fColorSpace),
+                                        desc.fPos, desc.fCount, desc.fTileMode, desc.fGradFlags,
+                                        desc.fLocalMatrix);
 }
 
 void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
index 58bb8aa..9ca207a 100644 (file)
@@ -35,8 +35,9 @@ sk_sp<SkFlattenable> SkSweepGradient::CreateProc(SkReadBuffer& buffer) {
         return nullptr;
     }
     const SkPoint center = buffer.readPoint();
-    return SkGradientShader::MakeSweep(center.x(), center.y(), desc.fColors, desc.fPos,
-                                       desc.fCount, desc.fGradFlags, desc.fLocalMatrix);
+    return SkGradientShader::MakeSweep(center.x(), center.y(), desc.fColors,
+                                       std::move(desc.fColorSpace), desc.fPos, desc.fCount,
+                                       desc.fGradFlags, desc.fLocalMatrix);
 }
 
 void SkSweepGradient::flatten(SkWriteBuffer& buffer) const {
index 8e3671b..fd48a62 100644 (file)
@@ -318,7 +318,7 @@ sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer)
         SkTSwap(c1, c2);
         SkTSwap(r1, r2);
 
-        SkColor* colors = desc.mutableColors();
+        SkColor4f* colors = desc.mutableColors();
         SkScalar* pos = desc.mutablePos();
         const int last = desc.fCount - 1;
         const int half = desc.fCount >> 1;
@@ -337,7 +337,8 @@ sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer)
         }
     }
 
-    return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors, desc.fPos,
+    return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
+                                                 std::move(desc.fColorSpace), desc.fPos,
                                                  desc.fCount, desc.fTileMode, desc.fGradFlags,
                                                  desc.fLocalMatrix);
 }