tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SkPDFGradientShader.cpp (44437B)


      1 /*
      2 * Copyright 2017 Google Inc.
      3 *
      4 * Use of this source code is governed by a BSD-style license that can be
      5 * found in the LICENSE file.
      6 */
      7 
      8 #include "src/pdf/SkPDFGradientShader.h"
      9 
     10 #include "include/core/SkAlphaType.h"
     11 #include "include/core/SkPaint.h"
     12 #include "include/core/SkPathTypes.h"
     13 #include "include/core/SkSpan.h"
     14 #include "include/core/SkStream.h"
     15 #include "include/core/SkTileMode.h"
     16 #include "include/effects/SkGradientShader.h"
     17 #include "include/private/base/SkTemplates.h"
     18 #include "include/private/base/SkTo.h"
     19 #include "src/core/SkChecksum.h"
     20 #include "src/core/SkTHash.h"
     21 #include "src/pdf/SkPDFDocumentPriv.h"
     22 #include "src/pdf/SkPDFFormXObject.h"
     23 #include "src/pdf/SkPDFGraphicState.h"
     24 #include "src/pdf/SkPDFResourceDict.h"
     25 #include "src/pdf/SkPDFTypes.h"
     26 #include "src/pdf/SkPDFUtils.h"
     27 
     28 #include <cmath>
     29 #include <cstddef>
     30 #include <utility>
     31 #include <vector>
     32 
     33 using namespace skia_private;
     34 
     35 static uint32_t hash(const SkShaderBase::GradientInfo& v) {
     36    uint32_t buffer[] = {
     37        (uint32_t)v.fColorCount,
     38        SkChecksum::Hash32(v.fColors, v.fColorCount * sizeof(SkColor)),
     39        SkChecksum::Hash32(v.fColorOffsets, v.fColorCount * sizeof(SkScalar)),
     40        SkChecksum::Hash32(v.fPoint, 2 * sizeof(SkPoint)),
     41        SkChecksum::Hash32(v.fRadius, 2 * sizeof(SkScalar)),
     42        (uint32_t)v.fTileMode,
     43        v.fGradientFlags,
     44    };
     45    return SkChecksum::Hash32(buffer, sizeof(buffer));
     46 }
     47 
     48 static uint32_t hash(const SkPDFGradientShader::Key& k) {
     49    uint32_t buffer[] = {
     50        (uint32_t)k.fType,
     51        hash(k.fInfo),
     52        SkChecksum::Hash32(&k.fCanvasTransform, sizeof(SkMatrix)),
     53        SkChecksum::Hash32(&k.fShaderTransform, sizeof(SkMatrix)),
     54        SkChecksum::Hash32(&k.fBBox, sizeof(SkIRect))
     55    };
     56    return SkChecksum::Hash32(buffer, sizeof(buffer));
     57 }
     58 
     59 static void unit_to_points_matrix(const SkPoint pts[2], SkMatrix* matrix) {
     60    SkVector    vec = pts[1] - pts[0];
     61    SkScalar    mag = vec.length();
     62    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
     63 
     64    vec.scale(inv);
     65    matrix->setSinCos(vec.fY, vec.fX);
     66    matrix->preScale(mag, mag);
     67    matrix->postTranslate(pts[0].fX, pts[0].fY);
     68 }
     69 
     70 static bool is_premul(const SkShaderBase::GradientInfo& info) {
     71    return SkToBool(info.fGradientFlags & SkGradientShader::kInterpolateColorsInPremul_Flag);
     72 }
     73 
     74 enum NumComponents {
     75    Three = 3,
     76    Four = 4,
     77    Max = Four,
     78 };
     79 
     80 /* Assumes t - startOffset is on the stack and does a linear interpolation on t
     81   between startOffset and endOffset from prevColor to curColor (for each color
     82   component), leaving the result in component order on the stack.
     83   @param range         endOffset - startOffset
     84   @param beginColor    The previous color.
     85   @param endColor      The current color.
     86   @param numComponents The number of components (3 or 4 if alpha is needed).
     87   @param result        The result ps function.
     88 */
     89 static void interpolate_color_code(SkScalar range, NumComponents numComponents,
     90                                   SkColor4f prevColor, SkColor4f curColor,
     91                                   SkDynamicMemoryWStream* result) {
     92    SkASSERT(range != SkIntToScalar(0));
     93 
     94    /* Linearly interpolate from the previous color to the current.
     95       Take the components 0..1 and determine the multipliers for interpolation.
     96       C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
     97     */
     98 
     99    // Figure out how to scale each color component.
    100    SkScalar multiplier[NumComponents::Max];
    101    for (int i = 0; i < numComponents; i++) {
    102        multiplier[i] = (curColor[i] - prevColor[i]) / range;
    103    }
    104 
    105    // Calculate when we no longer need to keep a copy of the input parameter t.
    106    // If the last component to use t is i, then dupInput[0..i - 1] = true
    107    // and dupInput[i .. components] = false.
    108    bool dupInput[NumComponents::Max];
    109    dupInput[numComponents - 1] = false;
    110    for (int i = numComponents - 2; i >= 0; i--) {
    111        dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
    112    }
    113 
    114    if (!dupInput[0] && multiplier[0] == 0) {
    115        result->writeText("pop ");
    116    }
    117 
    118    for (int i = 0; i < numComponents; i++) {
    119        // If the next components needs t and this component will consume a
    120        // copy, make another copy.
    121        if (dupInput[i] && multiplier[i] != 0) {
    122            result->writeText("dup ");
    123        }
    124 
    125        if (multiplier[i] == 0) {
    126            SkPDFUtils::AppendColorComponentF(prevColor[i], result);
    127            result->writeText(" ");
    128        } else {
    129            if (multiplier[i] != 1) {
    130                SkPDFUtils::AppendScalar(multiplier[i], result);
    131                result->writeText(" mul ");
    132            }
    133            if (prevColor[i] != 0) {
    134                SkPDFUtils::AppendColorComponentF(prevColor[i], result);
    135                result->writeText(" add ");
    136            }
    137        }
    138 
    139        if (dupInput[i]) {
    140            result->writeText("exch ");
    141        }
    142    }
    143 }
    144 
    145 // Convert { r, g, b, a } to a == 0 ? {0 0 0} : { r/a, g/a, b/a }
    146 static void unpremul(const SkShaderBase::GradientInfo& info, SkDynamicMemoryWStream* function) {
    147    // Preview Version 11.0 (1069.7.1) aborts the function if the predicate is like
    148    // "dup 0 eq" or any other use of "eq" with "a".
    149    function->writeText("dup abs 0.00001 lt"
    150                        "{ pop pop pop pop 0 0 0 }"
    151                        "{"
    152                        " dup"         // r g b a a
    153                        " 3 1 roll"    // r g a b a
    154                        " div"         // r g a b/a
    155                        " 4 1 roll"    // b/a r g a
    156                        " dup"         // b/a r g a a
    157                        " 3 1 roll"    // b/a r a g a
    158                        " div"         // b/a r a g/a
    159                        " 4 1 roll"    // g/a b/a r a
    160                        " div"         // g/a b/a r/a
    161                        " 3 1 roll"    // r/a g/a b/a
    162                        "} ifelse\n");
    163 }
    164 
    165 static void write_gradient_ranges(const SkShaderBase::GradientInfo& info, SkSpan<size_t> rangeEnds,
    166                                  NumComponents numComponents,
    167                                  bool top, bool first, SkDynamicMemoryWStream* result) {
    168    SkASSERT(!rangeEnds.empty());
    169 
    170    size_t rangeEndIndex = rangeEnds[rangeEnds.size() - 1];
    171    SkScalar rangeEnd = info.fColorOffsets[rangeEndIndex];
    172 
    173    // Each range check tests 0 < t <= end.
    174    if (top) {
    175        SkASSERT(first);
    176        // t may have been set to 0 to signal that the answer has already been found.
    177        result->writeText("dup dup 0 gt exch ");  // In Preview 11.0 (1033.3) `0. 0 ne` is true.
    178        SkPDFUtils::AppendScalar(rangeEnd, result);
    179        result->writeText(" le and {\n");
    180    } else if (first) {
    181        // After the top level check, only t <= end needs to be tested on if (lo) side.
    182        result->writeText("dup ");
    183        SkPDFUtils::AppendScalar(rangeEnd, result);
    184        result->writeText(" le {\n");
    185    } else {
    186        // The else (hi) side.
    187        result->writeText("{\n");
    188    }
    189 
    190    if (rangeEnds.size() == 1) {
    191        // Set the stack to [r g b].
    192        size_t rangeBeginIndex = rangeEndIndex - 1;
    193        SkScalar rangeBegin = info.fColorOffsets[rangeBeginIndex];
    194        SkPDFUtils::AppendScalar(rangeBegin, result);
    195        result->writeText(" sub ");  // consume t, put t - startOffset on the stack.
    196        interpolate_color_code(rangeEnd - rangeBegin, numComponents,
    197                               info.fColors[rangeBeginIndex], info.fColors[rangeEndIndex], result);
    198        result->writeText("\n");
    199    } else {
    200        size_t loCount = rangeEnds.size() / 2;
    201        SkSpan<size_t> loSpan = rangeEnds.subspan(0, loCount);
    202        write_gradient_ranges(info, loSpan, numComponents, false, true, result);
    203 
    204        SkSpan<size_t> hiSpan = rangeEnds.subspan(loCount, rangeEnds.size() - loCount);
    205        write_gradient_ranges(info, hiSpan, numComponents, false, false, result);
    206    }
    207 
    208    if (top) {
    209        // Put 0 on the stack for t once here instead of after every call to interpolate_color_code.
    210        result->writeText("0} if\n");
    211    } else if (first) {
    212        result->writeText("}");  // The else (hi) side will come next.
    213    } else {
    214        result->writeText("} ifelse\n");
    215    }
    216 }
    217 
    218 /* Generate Type 4 function code to map t to the passed gradient, clamping at the ends.
    219   The types integer, real, and boolean are available.
    220   There are no string, array, procedure, variable, or name types available.
    221 
    222   The generated code will be of the following form with all values hard coded.
    223 
    224  if (t <= 0) {
    225    ret = color[0];
    226    t = 0;
    227  }
    228  if (t > 0 && t <= stop[4]) {
    229    if (t <= stop[2]) {
    230      if (t <= stop[1]) {
    231        ret = interp(t - stop[0], stop[1] - stop[0], color[0], color[1]);
    232      } else {
    233        ret = interp(t - stop[1], stop[2] - stop[1], color[1], color[2]);
    234      }
    235    } else {
    236      if (t <= stop[3] {
    237        ret = interp(t - stop[2], stop[3] - stop[2], color[2], color[3]);
    238      } else {
    239        ret = interp(t - stop[3], stop[4] - stop[3], color[3], color[4]);
    240      }
    241    }
    242    t = 0;
    243  }
    244  if (t > 0) {
    245    ret = color[4];
    246  }
    247 
    248   which in PDF will be represented like
    249 
    250  dup 0 le {pop 0 0 0 0} if
    251  dup dup 0 gt exch 1 le and {
    252    dup .5 le {
    253      dup .25 le {
    254        0 sub 2 mul 0 0
    255      }{
    256        .25 sub .5 exch 2 mul 0
    257      } ifelse
    258    }{
    259      dup .75 le {
    260        .5 sub .5 exch .5 exch 2 mul
    261      }{
    262        .75 sub dup 2 mul .5 add exch dup 2 mul .5 add exch 2 mul .5 add
    263      } ifelse
    264    } ifelse
    265  0} if
    266  0 gt {1 1 1} if
    267 */
    268 static void gradient_function_code(const SkShaderBase::GradientInfo& info,
    269                                   SkDynamicMemoryWStream* result) {
    270    // While looking for a hit the stack is [t].
    271    // After finding a hit the stack is [r g b 0].
    272    // The 0 is consumed just before returning.
    273 
    274    const bool premul = is_premul(info);
    275    NumComponents numComponents = premul ? NumComponents::Four : NumComponents::Three;
    276 
    277    // The initial range has no previous and contains a solid color.
    278    // Any t <= 0 will be handled by this initial range, so later t == 0 indicates a hit was found.
    279    result->writeText("dup 0 le {pop ");
    280    SkPDFUtils::AppendColorComponentF(info.fColors[0].fR, result);
    281    result->writeText(" ");
    282    SkPDFUtils::AppendColorComponentF(info.fColors[0].fG, result);
    283    result->writeText(" ");
    284    SkPDFUtils::AppendColorComponentF(info.fColors[0].fB, result);
    285    if (numComponents == NumComponents::Four) {
    286        result->writeText(" ");
    287        SkPDFUtils::AppendColorComponentF(info.fColors[0].fA, result);
    288    }
    289    result->writeText(" 0} if\n");
    290 
    291    // Optimize out ranges which don't make any visual difference.
    292    AutoSTMalloc<4, size_t> rangeEnds(info.fColorCount);
    293    size_t rangeEndsCount = 0;
    294    for (int i = 1; i < info.fColorCount; ++i) {
    295        // Ignoring the alpha, is this range the same solid color as the next range?
    296        // This optimizes gradients where sometimes only the color or only the alpha is changing.
    297        auto eqIgnoringAlpha = [&](SkColor4f a, SkColor4f b) {
    298            if (premul) {
    299                return a == b;
    300            } else {
    301                return a.makeOpaque() == b.makeOpaque();
    302            }
    303        };
    304        bool constantColorBothSides =
    305            eqIgnoringAlpha(info.fColors[i-1], info.fColors[i]) &&// This range is a solid color.
    306            i != info.fColorCount-1 &&                            // This is not the last range.
    307            eqIgnoringAlpha(info.fColors[i], info.fColors[i+1]);  // Next range is same solid color.
    308 
    309        // Does this range have zero size?
    310        bool degenerateRange = info.fColorOffsets[i-1] == info.fColorOffsets[i];
    311 
    312        if (!degenerateRange && !constantColorBothSides) {
    313            rangeEnds[rangeEndsCount] = i;
    314            ++rangeEndsCount;
    315        }
    316    }
    317 
    318    // If a cap on depth is needed, loop here.
    319    write_gradient_ranges(info, SkSpan(rangeEnds.get(), rangeEndsCount),
    320                          numComponents, true, true, result);
    321 
    322    // Clamp the final color.
    323    result->writeText("0 gt {");
    324    SkPDFUtils::AppendColorComponentF(info.fColors[info.fColorCount - 1].fR, result);
    325    result->writeText(" ");
    326    SkPDFUtils::AppendColorComponentF(info.fColors[info.fColorCount - 1].fG, result);
    327    result->writeText(" ");
    328    SkPDFUtils::AppendColorComponentF(info.fColors[info.fColorCount - 1].fB, result);
    329    if (numComponents == NumComponents::Four) {
    330        result->writeText(" ");
    331        SkPDFUtils::AppendColorComponentF(info.fColors[info.fColorCount - 1].fA, result);
    332    }
    333    result->writeText("} if\n");
    334 
    335    if (premul) {
    336        unpremul(info, result);
    337    }
    338 }
    339 
    340 static std::unique_ptr<SkPDFDict> createInterpolationFunction(const SkColor4f& color1,
    341                                                              const SkColor4f& color2) {
    342    auto retval = SkPDFMakeDict();
    343 
    344    auto c0 = SkPDFMakeArray();
    345    c0->appendColorComponentF(color1.fR);
    346    c0->appendColorComponentF(color1.fG);
    347    c0->appendColorComponentF(color1.fB);
    348    retval->insertObject("C0", std::move(c0));
    349 
    350    auto c1 = SkPDFMakeArray();
    351    c1->appendColorComponentF(color2.fR);
    352    c1->appendColorComponentF(color2.fG);
    353    c1->appendColorComponentF(color2.fB);
    354    retval->insertObject("C1", std::move(c1));
    355 
    356    retval->insertObject("Domain", SkPDFMakeArray(0, 1));
    357 
    358    retval->insertInt("FunctionType", 2);
    359    retval->insertScalar("N", 1.0f);
    360 
    361    return retval;
    362 }
    363 
    364 static std::unique_ptr<SkPDFDict> gradientStitchCode(const SkShaderBase::GradientInfo& info) {
    365    auto retval = SkPDFMakeDict();
    366 
    367    // normalize color stops
    368    int colorCount = info.fColorCount;
    369    std::vector<SkColor4f> colors(info.fColors, info.fColors + colorCount);
    370    std::vector<SkScalar> colorOffsets(info.fColorOffsets, info.fColorOffsets + colorCount);
    371 
    372    int i = 1;
    373    while (i < colorCount - 1) {
    374        // ensure stops are in order
    375        if (colorOffsets[i - 1] > colorOffsets[i]) {
    376            colorOffsets[i] = colorOffsets[i - 1];
    377        }
    378 
    379        // remove points that are between 2 coincident points
    380        if ((colorOffsets[i - 1] == colorOffsets[i]) && (colorOffsets[i] == colorOffsets[i + 1])) {
    381            colorCount -= 1;
    382            colors.erase(colors.begin() + i);
    383            colorOffsets.erase(colorOffsets.begin() + i);
    384        } else {
    385            i++;
    386        }
    387    }
    388    // find coincident points and slightly move them over
    389    for (i = 1; i < colorCount - 1; i++) {
    390        if (colorOffsets[i - 1] == colorOffsets[i]) {
    391            colorOffsets[i] += 0.00001f;
    392        }
    393    }
    394    // check if last 2 stops coincide
    395    if (colorOffsets[i - 1] == colorOffsets[i]) {
    396        colorOffsets[i - 1] -= 0.00001f;
    397    }
    398 
    399    // no need for a stitch function if there are only 2 stops.
    400    if (colorCount == 2) {
    401        return createInterpolationFunction(colors[0], colors[1]);
    402    }
    403 
    404    auto encode = SkPDFMakeArray();
    405    auto bounds = SkPDFMakeArray();
    406    auto functions = SkPDFMakeArray();
    407 
    408    retval->insertObject("Domain", SkPDFMakeArray(0, 1));
    409    retval->insertInt("FunctionType", 3);
    410 
    411    for (int idx = 1; idx < colorCount; idx++) {
    412        if (idx > 1) {
    413            bounds->appendScalar(colorOffsets[idx-1]);
    414        }
    415 
    416        encode->appendScalar(0);
    417        encode->appendScalar(1.0f);
    418 
    419        functions->appendObject(createInterpolationFunction(colors[idx-1], colors[idx]));
    420    }
    421 
    422    retval->insertObject("Encode", std::move(encode));
    423    retval->insertObject("Bounds", std::move(bounds));
    424    retval->insertObject("Functions", std::move(functions));
    425 
    426    return retval;
    427 }
    428 
    429 /* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
    430 static void tileModeCode(SkTileMode mode, SkDynamicMemoryWStream* result) {
    431    if (mode == SkTileMode::kRepeat) {
    432        result->writeText("dup truncate sub\n");  // Get the fractional part.
    433        result->writeText("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
    434        return;
    435    }
    436 
    437    if (mode == SkTileMode::kMirror) {
    438        // In Preview 11.0 (1033.3) `a n mod r eq` (with a and n both integers, r integer or real)
    439        // early aborts the function when false would be put on the stack.
    440        // Work around this by re-writing `t 2 mod 1 eq` as `t 2 mod 0 gt`.
    441 
    442        // Map t mod 2 into [0, 1, 1, 0].
    443        //                Code                 Stack t
    444        result->writeText("abs "                 // +t
    445                          "dup "                 // +t.s +t.s
    446                          "truncate "            // +t.s +t
    447                          "dup "                 // +t.s +t +t
    448                          "cvi "                 // +t.s +t +T
    449                          "2 mod "               // +t.s +t (+T mod 2)
    450              /*"1 eq "*/ "0 gt "                // +t.s +t true|false
    451                          "3 1 roll "            // true|false +t.s +t
    452                          "sub "                 // true|false 0.s
    453                          "exch "                // 0.s true|false
    454                          "{1 exch sub} if\n");  // 1 - 0.s|0.s
    455    }
    456 }
    457 
    458 /**
    459 *  Returns PS function code that applies inverse perspective
    460 *  to a x, y point.
    461 *  The function assumes that the stack has at least two elements,
    462 *  and that the top 2 elements are numeric values.
    463 *  After executing this code on a PS stack, the last 2 elements are updated
    464 *  while the rest of the stack is preserved intact.
    465 *  inversePerspectiveMatrix is the inverse perspective matrix.
    466 */
    467 static void apply_perspective_to_coordinates(const SkMatrix& inversePerspectiveMatrix,
    468                                             SkDynamicMemoryWStream* code) {
    469    if (!inversePerspectiveMatrix.hasPerspective()) {
    470        return;
    471    }
    472 
    473    // Perspective matrix should be:
    474    // 1   0  0
    475    // 0   1  0
    476    // p0 p1 p2
    477 
    478    const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0];
    479    const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1];
    480    const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2];
    481 
    482    // y = y / (p2 + p0 x + p1 y)
    483    // x = x / (p2 + p0 x + p1 y)
    484 
    485    // Input on stack: x y
    486    code->writeText(" dup ");             // x y y
    487    SkPDFUtils::AppendScalar(p1, code);   // x y y p1
    488    code->writeText(" mul "               // x y y*p1
    489                    " 2 index ");         // x y y*p1 x
    490    SkPDFUtils::AppendScalar(p0, code);   // x y y p1 x p0
    491    code->writeText(" mul ");             // x y y*p1 x*p0
    492    SkPDFUtils::AppendScalar(p2, code);   // x y y p1 x*p0 p2
    493    code->writeText(" add "               // x y y*p1 x*p0+p2
    494                    "add "                // x y y*p1+x*p0+p2
    495                    "3 1 roll "           // y*p1+x*p0+p2 x y
    496                    "2 index "            // z x y y*p1+x*p0+p2
    497                    "div "                // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2)
    498                    "3 1 roll "           // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x
    499                    "exch "               // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2
    500                    "div "                // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2)
    501                    "exch\n");            // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2)
    502 }
    503 
    504 static void linearCode(const SkShaderBase::GradientInfo& info,
    505                       const SkMatrix& perspectiveRemover,
    506                       SkDynamicMemoryWStream* function) {
    507    function->writeText("{");
    508 
    509    apply_perspective_to_coordinates(perspectiveRemover, function);
    510 
    511    function->writeText("pop\n");  // Just ditch the y value.
    512    tileModeCode((SkTileMode)info.fTileMode, function);
    513    gradient_function_code(info, function);
    514    function->writeText("}");
    515 }
    516 
    517 static void radialCode(const SkShaderBase::GradientInfo& info,
    518                       const SkMatrix& perspectiveRemover,
    519                       SkDynamicMemoryWStream* function) {
    520    function->writeText("{");
    521 
    522    apply_perspective_to_coordinates(perspectiveRemover, function);
    523 
    524    // Find the distance from the origin.
    525    function->writeText("dup "      // x y y
    526                    "mul "      // x y^2
    527                    "exch "     // y^2 x
    528                    "dup "      // y^2 x x
    529                    "mul "      // y^2 x^2
    530                    "add "      // y^2+x^2
    531                    "sqrt\n");  // sqrt(y^2+x^2)
    532 
    533    tileModeCode((SkTileMode)info.fTileMode, function);
    534    gradient_function_code(info, function);
    535    function->writeText("}");
    536 }
    537 
    538 /* Conical gradient shader, based on the Canvas spec for radial gradients
    539   See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
    540 */
    541 static void twoPointConicalCode(const SkShaderBase::GradientInfo& info,
    542                                const SkMatrix& perspectiveRemover,
    543                                SkDynamicMemoryWStream* function) {
    544    SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
    545    SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
    546    SkScalar r0 = info.fRadius[0];
    547    SkScalar dr = info.fRadius[1] - info.fRadius[0];
    548    SkScalar a = dx * dx + dy * dy - dr * dr;
    549 
    550    // First compute t, if the pixel falls outside the cone, then we'll end
    551    // with 'false' on the stack, otherwise we'll push 'true' with t below it
    552 
    553    // We start with a stack of (x y), copy it and then consume one copy in
    554    // order to calculate b and the other to calculate c.
    555    function->writeText("{");
    556 
    557    apply_perspective_to_coordinates(perspectiveRemover, function);
    558 
    559    function->writeText("2 copy ");
    560 
    561    // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
    562    SkPDFUtils::AppendScalar(dy, function);
    563    function->writeText(" mul exch ");
    564    SkPDFUtils::AppendScalar(dx, function);
    565    function->writeText(" mul add ");
    566    SkPDFUtils::AppendScalar(r0 * dr, function);
    567    function->writeText(" add -2 mul dup dup mul\n");
    568 
    569    // c = x^2 + y^2 + radius0^2
    570    function->writeText("4 2 roll dup mul exch dup mul add ");
    571    SkPDFUtils::AppendScalar(r0 * r0, function);
    572    function->writeText(" sub dup 4 1 roll\n");
    573 
    574    // Contents of the stack at this point: c, b, b^2, c
    575 
    576    // if a = 0, then we collapse to a simpler linear case
    577    if (a == 0) {
    578 
    579        // t = -c/b
    580        function->writeText("pop pop div neg dup ");
    581 
    582        // compute radius(t)
    583        SkPDFUtils::AppendScalar(dr, function);
    584        function->writeText(" mul ");
    585        SkPDFUtils::AppendScalar(r0, function);
    586        function->writeText(" add\n");
    587 
    588        // if r(t) < 0, then it's outside the cone
    589        function->writeText("0 lt {pop false} {true} ifelse\n");
    590 
    591    } else {
    592 
    593        // quadratic case: the Canvas spec wants the largest
    594        // root t for which radius(t) > 0
    595 
    596        // compute the discriminant (b^2 - 4ac)
    597        SkPDFUtils::AppendScalar(a * 4, function);
    598        function->writeText(" mul sub dup\n");
    599 
    600        // if d >= 0, proceed
    601        function->writeText("0 ge {\n");
    602 
    603        // an intermediate value we'll use to compute the roots:
    604        // q = -0.5 * (b +/- sqrt(d))
    605        function->writeText("sqrt exch dup 0 lt {exch -1 mul} if");
    606        function->writeText(" add -0.5 mul dup\n");
    607 
    608        // first root = q / a
    609        SkPDFUtils::AppendScalar(a, function);
    610        function->writeText(" div\n");
    611 
    612        // second root = c / q
    613        function->writeText("3 1 roll div\n");
    614 
    615        // put the larger root on top of the stack
    616        function->writeText("2 copy gt {exch} if\n");
    617 
    618        // compute radius(t) for larger root
    619        function->writeText("dup ");
    620        SkPDFUtils::AppendScalar(dr, function);
    621        function->writeText(" mul ");
    622        SkPDFUtils::AppendScalar(r0, function);
    623        function->writeText(" add\n");
    624 
    625        // if r(t) > 0, we have our t, pop off the smaller root and we're done
    626        function->writeText(" 0 gt {exch pop true}\n");
    627 
    628        // otherwise, throw out the larger one and try the smaller root
    629        function->writeText("{pop dup\n");
    630        SkPDFUtils::AppendScalar(dr, function);
    631        function->writeText(" mul ");
    632        SkPDFUtils::AppendScalar(r0, function);
    633        function->writeText(" add\n");
    634 
    635        // if r(t) < 0, push false, otherwise the smaller root is our t
    636        function->writeText("0 le {pop false} {true} ifelse\n");
    637        function->writeText("} ifelse\n");
    638 
    639        // d < 0, clear the stack and push false
    640        function->writeText("} {pop pop pop false} ifelse\n");
    641    }
    642 
    643    // if the pixel is in the cone, proceed to compute a color
    644    function->writeText("{");
    645    tileModeCode((SkTileMode)info.fTileMode, function);
    646    gradient_function_code(info, function);
    647 
    648    // otherwise, just write black
    649    // TODO: Correctly draw gradients_local_persepective, need to mask out this black
    650    // The "gradients" gm works as falls into the 8.7.4.5.4 "Type 3 (Radial) Shadings" case.
    651    function->writeText("} {0 0 0} ifelse }");
    652 }
    653 
    654 static void sweepCode(const SkShaderBase::GradientInfo& info,
    655                      const SkMatrix& perspectiveRemover,
    656                      SkDynamicMemoryWStream* function) {
    657    function->writeText("{exch atan 360 div\n");
    658    const SkScalar bias = info.fPoint[1].y();
    659    if (bias != 0.0f) {
    660        SkPDFUtils::AppendScalar(bias, function);
    661        function->writeText(" add\n");
    662    }
    663    const SkScalar scale = info.fPoint[1].x();
    664    if (scale != 1.0f) {
    665        SkPDFUtils::AppendScalar(scale, function);
    666        function->writeText(" mul\n");
    667    }
    668    tileModeCode((SkTileMode)info.fTileMode, function);
    669    gradient_function_code(info, function);
    670    function->writeText("}");
    671 }
    672 
    673 
    674 // catch cases where the inner just touches the outer circle
    675 // and make the inner circle just inside the outer one to match raster
    676 static void FixUpRadius(const SkPoint& p1, SkScalar& r1, const SkPoint& p2, SkScalar& r2) {
    677    // detect touching circles
    678    SkScalar distance = SkPoint::Distance(p1, p2);
    679    SkScalar subtractRadii = fabs(r1 - r2);
    680    if (fabs(distance - subtractRadii) < 0.002f) {
    681        if (r1 > r2) {
    682            r1 += 0.002f;
    683        } else {
    684            r2 += 0.002f;
    685        }
    686    }
    687 }
    688 
    689 // Finds affine and persp such that in = affine * persp.
    690 // but it returns the inverse of perspective matrix.
    691 static bool split_perspective(const SkMatrix in, SkMatrix* affine,
    692                              SkMatrix* perspectiveInverse) {
    693    const SkScalar p2 = in[SkMatrix::kMPersp2];
    694 
    695    if (SkScalarNearlyZero(p2)) {
    696        return false;
    697    }
    698 
    699    const SkScalar zero = SkIntToScalar(0);
    700    const SkScalar one = SkIntToScalar(1);
    701 
    702    const SkScalar sx = in[SkMatrix::kMScaleX];
    703    const SkScalar kx = in[SkMatrix::kMSkewX];
    704    const SkScalar tx = in[SkMatrix::kMTransX];
    705    const SkScalar ky = in[SkMatrix::kMSkewY];
    706    const SkScalar sy = in[SkMatrix::kMScaleY];
    707    const SkScalar ty = in[SkMatrix::kMTransY];
    708    const SkScalar p0 = in[SkMatrix::kMPersp0];
    709    const SkScalar p1 = in[SkMatrix::kMPersp1];
    710 
    711    // Perspective matrix would be:
    712    // 1  0  0
    713    // 0  1  0
    714    // p0 p1 p2
    715    // But we need the inverse of persp.
    716    perspectiveInverse->setAll(one,          zero,       zero,
    717                               zero,         one,        zero,
    718                               -p0/p2,     -p1/p2,     1/p2);
    719 
    720    affine->setAll(sx - p0 * tx / p2,       kx - p1 * tx / p2,      tx / p2,
    721                   ky - p0 * ty / p2,       sy - p1 * ty / p2,      ty / p2,
    722                   zero,                    zero,                   one);
    723 
    724    return true;
    725 }
    726 
    727 static SkPDFIndirectReference make_ps_function(std::unique_ptr<SkStreamAsset> psCode,
    728                                               std::unique_ptr<SkPDFArray> domain,
    729                                               std::unique_ptr<SkPDFObject> range,
    730                                               SkPDFDocument* doc) {
    731    std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict();
    732    dict->insertInt("FunctionType", 4);
    733    dict->insertObject("Domain", std::move(domain));
    734    dict->insertObject("Range", std::move(range));
    735    return SkPDFStreamOut(std::move(dict), std::move(psCode), doc);
    736 }
    737 
    738 static SkPDFIndirectReference make_function_shader(SkPDFDocument* doc,
    739                                                   const SkPDFGradientShader::Key& state) {
    740    SkPoint transformPoints[2];
    741    const SkShaderBase::GradientInfo& info = state.fInfo;
    742    SkMatrix finalMatrix = state.fCanvasTransform;
    743    finalMatrix.preConcat(state.fShaderTransform);
    744 
    745    bool doStitchFunctions = (state.fType == SkShaderBase::GradientType::kLinear ||
    746                              state.fType == SkShaderBase::GradientType::kRadial ||
    747                              state.fType == SkShaderBase::GradientType::kConical) &&
    748                             (info.fTileMode == SkTileMode::kClamp ||
    749                              info.fTileMode == SkTileMode::kDecal) &&
    750                             !finalMatrix.hasPerspective() &&
    751                             !is_premul(info);
    752 
    753    enum class ShadingType : int32_t {
    754        Function = 1,
    755        Axial = 2,
    756        Radial = 3,
    757        FreeFormGouraudTriangleMesh = 4,
    758        LatticeFormGouraudTriangleMesh = 5,
    759        CoonsPatchMesh = 6,
    760        TensorProductPatchMesh = 7,
    761    } shadingType;
    762 
    763    auto pdfShader = SkPDFMakeDict();
    764    if (doStitchFunctions) {
    765        pdfShader->insertObject("Function", gradientStitchCode(info));
    766 
    767        if (info.fTileMode == SkTileMode::kClamp) {
    768            auto extend = SkPDFMakeArray();
    769            extend->reserve(2);
    770            extend->appendBool(true);
    771            extend->appendBool(true);
    772            pdfShader->insertObject("Extend", std::move(extend));
    773        }
    774 
    775        std::unique_ptr<SkPDFArray> coords;
    776        switch (state.fType) {
    777            case SkShaderBase::GradientType::kLinear: {
    778                shadingType = ShadingType::Axial;
    779                const SkPoint& pt1 = info.fPoint[0];
    780                const SkPoint& pt2 = info.fPoint[1];
    781                coords = SkPDFMakeArray(pt1.x(), pt1.y(),
    782                                        pt2.x(), pt2.y());
    783            } break;
    784            case SkShaderBase::GradientType::kRadial: {
    785                shadingType = ShadingType::Radial;
    786                const SkPoint& pt1 = info.fPoint[0];
    787                coords = SkPDFMakeArray(pt1.x(), pt1.y(), 0,
    788                                        pt1.x(), pt1.y(), info.fRadius[0]);
    789            } break;
    790            case SkShaderBase::GradientType::kConical: {
    791                shadingType = ShadingType::Radial;
    792                SkScalar r1 = info.fRadius[0];
    793                SkScalar r2 = info.fRadius[1];
    794                SkPoint pt1 = info.fPoint[0];
    795                SkPoint pt2 = info.fPoint[1];
    796                FixUpRadius(pt1, r1, pt2, r2);
    797 
    798                coords = SkPDFMakeArray(pt1.x(), pt1.y(), r1,
    799                                        pt2.x(), pt2.y(), r2);
    800                break;
    801            }
    802            case SkShaderBase::GradientType::kSweep:
    803            case SkShaderBase::GradientType::kNone:
    804            default:
    805                SkASSERT(false);
    806                return SkPDFIndirectReference();
    807        }
    808        pdfShader->insertObject("Coords", std::move(coords));
    809    } else {
    810        shadingType = ShadingType::Function;
    811 
    812        // Transform the coordinate space for the type of gradient.
    813        transformPoints[0] = info.fPoint[0];
    814        transformPoints[1] = info.fPoint[1];
    815        switch (state.fType) {
    816            case SkShaderBase::GradientType::kLinear:
    817                break;
    818            case SkShaderBase::GradientType::kRadial:
    819                transformPoints[1] = transformPoints[0];
    820                transformPoints[1].fX += info.fRadius[0];
    821                break;
    822            case SkShaderBase::GradientType::kConical: {
    823                transformPoints[1] = transformPoints[0];
    824                transformPoints[1].fX += SK_Scalar1;
    825                break;
    826            }
    827            case SkShaderBase::GradientType::kSweep:
    828                transformPoints[1] = transformPoints[0];
    829                transformPoints[1].fX += SK_Scalar1;
    830                break;
    831            case SkShaderBase::GradientType::kNone:
    832            default:
    833                return SkPDFIndirectReference();
    834        }
    835 
    836        // Move any scaling (assuming a unit gradient) or translation
    837        // (and rotation for linear gradient), of the final gradient from
    838        // info.fPoints to the matrix (updating bbox appropriately).  Now
    839        // the gradient can be drawn on on the unit segment.
    840        SkMatrix mapperMatrix;
    841        unit_to_points_matrix(transformPoints, &mapperMatrix);
    842 
    843        finalMatrix.preConcat(mapperMatrix);
    844 
    845        // Preserves as much as possible in the final matrix, and only removes
    846        // the perspective. The inverse of the perspective is stored in
    847        // perspectiveInverseOnly matrix and has 3 useful numbers
    848        // (p0, p1, p2), while everything else is either 0 or 1.
    849        // In this way the shader will handle it eficiently, with minimal code.
    850        SkMatrix perspectiveInverseOnly = SkMatrix::I();
    851        if (finalMatrix.hasPerspective()) {
    852            if (!split_perspective(finalMatrix,
    853                                   &finalMatrix, &perspectiveInverseOnly)) {
    854                return SkPDFIndirectReference();
    855            }
    856        }
    857 
    858        SkRect bbox;
    859        bbox.set(state.fBBox);
    860        if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &bbox)) {
    861            return SkPDFIndirectReference();
    862        }
    863 
    864        SkDynamicMemoryWStream functionCode;
    865        switch (state.fType) {
    866            case SkShaderBase::GradientType::kLinear:
    867                linearCode(info, perspectiveInverseOnly, &functionCode);
    868                break;
    869            case SkShaderBase::GradientType::kRadial:
    870                radialCode(info, perspectiveInverseOnly, &functionCode);
    871                break;
    872            case SkShaderBase::GradientType::kConical: {
    873                // The two point radial gradient further references state.fInfo
    874                // in translating from x, y coordinates to the t parameter. So, we have
    875                // to transform the points and radii according to the calculated matrix.
    876                auto inverseMapperMatrix = mapperMatrix.invert();
    877                if (!inverseMapperMatrix) {
    878                    return SkPDFIndirectReference();
    879                }
    880                SkShaderBase::GradientInfo infoCopy = info;
    881                inverseMapperMatrix->mapPoints(infoCopy.fPoint);
    882                infoCopy.fRadius[0] = inverseMapperMatrix->mapRadius(info.fRadius[0]);
    883                infoCopy.fRadius[1] = inverseMapperMatrix->mapRadius(info.fRadius[1]);
    884                twoPointConicalCode(infoCopy, perspectiveInverseOnly, &functionCode);
    885            } break;
    886            case SkShaderBase::GradientType::kSweep:
    887                sweepCode(info, perspectiveInverseOnly, &functionCode);
    888                break;
    889            default:
    890                SkASSERT(false);
    891        }
    892        pdfShader->insertObject(
    893                "Domain", SkPDFMakeArray(bbox.left(), bbox.right(), bbox.top(), bbox.bottom()));
    894 
    895        auto domain = SkPDFMakeArray(bbox.left(), bbox.right(), bbox.top(), bbox.bottom());
    896        std::unique_ptr<SkPDFArray> rangeObject = SkPDFMakeArray(0, 1, 0, 1, 0, 1);
    897        pdfShader->insertRef("Function",
    898                             make_ps_function(functionCode.detachAsStream(), std::move(domain),
    899                                              std::move(rangeObject), doc));
    900    }
    901 
    902    pdfShader->insertInt("ShadingType", SkToS32(shadingType));
    903    pdfShader->insertName("ColorSpace", "DeviceRGB");
    904 
    905    SkPDFDict pdfFunctionShader("Pattern");
    906    pdfFunctionShader.insertInt("PatternType", 2);
    907    pdfFunctionShader.insertObject("Matrix", SkPDFUtils::MatrixToArray(finalMatrix));
    908    pdfFunctionShader.insertObject("Shading", std::move(pdfShader));
    909    return doc->emit(pdfFunctionShader);
    910 }
    911 
    912 static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
    913                                              SkPDFGradientShader::Key key,
    914                                              bool makeAlphaShader);
    915 
    916 static std::unique_ptr<SkPDFDict> get_gradient_resource_dict(SkPDFIndirectReference functionShader,
    917                                                   SkPDFIndirectReference gState) {
    918    std::vector<SkPDFIndirectReference> patternShaders;
    919    if (functionShader != SkPDFIndirectReference()) {
    920        patternShaders.push_back(functionShader);
    921    }
    922    std::vector<SkPDFIndirectReference> graphicStates;
    923    if (gState != SkPDFIndirectReference()) {
    924        graphicStates.push_back(gState);
    925    }
    926    return SkPDFMakeResourceDict(std::move(graphicStates),
    927                                 std::move(patternShaders),
    928                                 std::vector<SkPDFIndirectReference>(),
    929                                 std::vector<SkPDFIndirectReference>());
    930 }
    931 
    932 // Creates a content stream which fills the pattern P0 across bounds.
    933 // @param gsIndex A graphics state resource index to apply, or <0 if no
    934 // graphics state to apply.
    935 static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex,
    936                                                                  int patternIndex,
    937                                                                  SkRect& bounds) {
    938    SkDynamicMemoryWStream content;
    939    if (gsIndex >= 0) {
    940        SkPDFUtils::ApplyGraphicState(gsIndex, &content);
    941    }
    942    SkPDFUtils::ApplyPattern(patternIndex, &content);
    943    SkPDFUtils::AppendRectangle(bounds, &content);
    944    SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPathFillType::kEvenOdd, &content);
    945    return content.detachAsStream();
    946 }
    947 
    948 static bool gradient_has_alpha(const SkPDFGradientShader::Key& key) {
    949    SkASSERT(key.fType != SkShaderBase::GradientType::kNone);
    950    for (int i = 0; i < key.fInfo.fColorCount; i++) {
    951        if (!key.fInfo.fColors[i].isOpaque()) {
    952            return true;
    953        }
    954    }
    955    return false;
    956 }
    957 
    958 // warning: does not set fHash on new key.  (Both callers need to change fields.)
    959 static SkPDFGradientShader::Key clone_key(const SkPDFGradientShader::Key& k) {
    960    SkPDFGradientShader::Key clone = {
    961        k.fType,
    962        k.fInfo,  // change pointers later.
    963        std::unique_ptr<SkColor4f[]>(new SkColor4f[k.fInfo.fColorCount]),
    964        std::unique_ptr<SkScalar[]>(new SkScalar[k.fInfo.fColorCount]),
    965        k.fCanvasTransform,
    966        k.fShaderTransform,
    967        k.fBBox, 0};
    968    clone.fInfo.fColors = clone.fColors.get();
    969    clone.fInfo.fColorOffsets = clone.fStops.get();
    970    for (int i = 0; i < clone.fInfo.fColorCount; i++) {
    971        clone.fInfo.fColorOffsets[i] = k.fInfo.fColorOffsets[i];
    972        clone.fInfo.fColors[i] = k.fInfo.fColors[i];
    973    }
    974    return clone;
    975 }
    976 
    977 static SkPDFIndirectReference create_smask_graphic_state(SkPDFDocument* doc,
    978                                                     const SkPDFGradientShader::Key& state) {
    979    SkASSERT(state.fType != SkShaderBase::GradientType::kNone);
    980    SkPDFGradientShader::Key luminosityState = clone_key(state);
    981    for (int i = 0; i < luminosityState.fInfo.fColorCount; i++) {
    982        float alpha = luminosityState.fInfo.fColors[i].fA;
    983        luminosityState.fInfo.fColors[i] = SkColor4f{alpha, alpha, alpha, 1.0f};
    984    }
    985    luminosityState.fInfo.fGradientFlags &= ~SkGradientShader::kInterpolateColorsInPremul_Flag;
    986    luminosityState.fHash = hash(luminosityState);
    987 
    988    SkASSERT(!gradient_has_alpha(luminosityState));
    989    SkPDFIndirectReference luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
    990    std::unique_ptr<SkPDFDict> resources = get_gradient_resource_dict(luminosityShader,
    991                                                            SkPDFIndirectReference());
    992    SkRect bbox = SkRect::Make(state.fBBox);
    993    SkPDFIndirectReference alphaMask =
    994            SkPDFMakeFormXObject(doc,
    995                                 create_pattern_fill_content(-1, luminosityShader.fValue, bbox),
    996                                 SkPDFUtils::RectToArray(bbox),
    997                                 std::move(resources),
    998                                 SkMatrix::I(),
    999                                 "DeviceRGB");
   1000    return SkPDFGraphicState::GetSMaskGraphicState(
   1001            alphaMask, false, SkPDFGraphicState::kLuminosity_SMaskMode, doc);
   1002 }
   1003 
   1004 static SkPDFIndirectReference make_alpha_function_shader(SkPDFDocument* doc,
   1005                                                         const SkPDFGradientShader::Key& state) {
   1006    SkASSERT(state.fType != SkShaderBase::GradientType::kNone);
   1007    SkPDFGradientShader::Key opaqueState = clone_key(state);
   1008    const bool keepAlpha = is_premul(opaqueState.fInfo);
   1009    if (!keepAlpha) {
   1010        for (int i = 0; i < opaqueState.fInfo.fColorCount; i++) {
   1011            opaqueState.fInfo.fColors[i].fA = 1.0f;
   1012        }
   1013        opaqueState.fHash = hash(opaqueState);
   1014 
   1015        SkASSERT(!gradient_has_alpha(opaqueState));
   1016    }
   1017    SkRect bbox = SkRect::Make(state.fBBox);
   1018    SkPDFIndirectReference colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
   1019    if (!colorShader) {
   1020        return SkPDFIndirectReference();
   1021    }
   1022    // Create resource dict with alpha graphics state as G0 and
   1023    // pattern shader as P0, then write content stream.
   1024    SkPDFIndirectReference alphaGsRef = create_smask_graphic_state(doc, state);
   1025 
   1026    std::unique_ptr<SkPDFDict> resourceDict = get_gradient_resource_dict(colorShader, alphaGsRef);
   1027 
   1028    std::unique_ptr<SkStreamAsset> colorStream =
   1029            create_pattern_fill_content(alphaGsRef.fValue, colorShader.fValue, bbox);
   1030    std::unique_ptr<SkPDFDict> alphaFunctionShader = SkPDFMakeDict();
   1031    SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader.get(), bbox,
   1032                                 std::move(resourceDict), SkMatrix::I());
   1033    return SkPDFStreamOut(std::move(alphaFunctionShader), std::move(colorStream), doc);
   1034 }
   1035 
   1036 static SkPDFGradientShader::Key make_key(const SkShader* shader,
   1037                                         const SkMatrix& canvasTransform,
   1038                                         const SkIRect& bbox) {
   1039    SkPDFGradientShader::Key key = {
   1040         SkShaderBase::GradientType::kNone,
   1041         {0, nullptr, nullptr, {{0, 0}, {0, 0}}, {0, 0}, SkTileMode::kClamp, 0},
   1042         nullptr,
   1043         nullptr,
   1044         canvasTransform,
   1045         SkPDFUtils::GetShaderLocalMatrix(shader),
   1046         bbox, 0};
   1047    key.fType = as_SB(shader)->asGradient(&key.fInfo);
   1048    SkASSERT(SkShaderBase::GradientType::kNone != key.fType);
   1049    SkASSERT(key.fInfo.fColorCount > 0);
   1050    key.fColors = std::make_unique<SkColor4f[]>(key.fInfo.fColorCount);
   1051    key.fStops = std::make_unique<SkScalar[]>(key.fInfo.fColorCount);
   1052    key.fInfo.fColors = key.fColors.get();
   1053    key.fInfo.fColorOffsets = key.fStops.get();
   1054    as_SB(shader)->asGradient(&key.fInfo);
   1055    if (is_premul(key.fInfo)) {
   1056        bool changedByPremul = false;
   1057        for (auto&& c : SkSpan(key.fInfo.fColors, key.fInfo.fColorCount)) {
   1058            if (c.fA != 1.0) {
   1059                changedByPremul = true;
   1060            }
   1061            SkRGBA4f<kPremul_SkAlphaType> pm = c.premul();
   1062            c = SkColor4f{pm.fR, pm.fG, pm.fB, pm.fA};
   1063        }
   1064        if (!changedByPremul) {
   1065            key.fInfo.fGradientFlags &= ~SkGradientShader::kInterpolateColorsInPremul_Flag;
   1066        }
   1067    }
   1068    key.fHash = hash(key);
   1069    return key;
   1070 }
   1071 
   1072 static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
   1073                                              SkPDFGradientShader::Key key,
   1074                                              bool makeAlphaShader) {
   1075    auto& gradientPatternMap = doc->fGradientPatternMap;
   1076    if (SkPDFIndirectReference* ptr = gradientPatternMap.find(key)) {
   1077        return *ptr;
   1078    }
   1079    SkPDFIndirectReference pdfShader;
   1080    if (makeAlphaShader) {
   1081        pdfShader = make_alpha_function_shader(doc, key);
   1082    } else {
   1083        pdfShader = make_function_shader(doc, key);
   1084    }
   1085    gradientPatternMap.set(std::move(key), pdfShader);
   1086    return pdfShader;
   1087 }
   1088 
   1089 SkPDFIndirectReference SkPDFGradientShader::Make(SkPDFDocument* doc,
   1090                                                 SkShader* shader,
   1091                                                 const SkMatrix& canvasTransform,
   1092                                                 const SkIRect& bbox) {
   1093    SkASSERT(shader);
   1094    SkASSERT(as_SB(shader)->asGradient() != SkShaderBase::GradientType::kNone);
   1095    SkPDFGradientShader::Key key = make_key(shader, canvasTransform, bbox);
   1096    const bool makeAlphaShader = gradient_has_alpha(key);
   1097    return find_pdf_shader(doc, std::move(key), makeAlphaShader);
   1098 }