194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary/*
294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary * Copyright 2017 Google Inc.
394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary *
494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary * Use of this source code is governed by a BSD-style license that can be
594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary * found in the LICENSE file.
694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary */
794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary#include "SkPDFGradientShader.h"
994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
1094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary#include "SkOpts.h"
1194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary#include "SkPDFDocument.h"
1294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary#include "SkPDFFormXObject.h"
1394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary#include "SkPDFResourceDict.h"
1494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary#include "SkPDFUtils.h"
1594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
1694fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic uint32_t hash(const SkShader::GradientInfo& v) {
1794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    uint32_t buffer[] = {
1894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        (uint32_t)v.fColorCount,
1994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkOpts::hash(v.fColors, v.fColorCount * sizeof(SkColor)),
2094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkOpts::hash(v.fColorOffsets, v.fColorCount * sizeof(SkScalar)),
2194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkOpts::hash(v.fPoint, 2 * sizeof(SkPoint)),
2294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkOpts::hash(v.fRadius, 2 * sizeof(SkScalar)),
2394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        (uint32_t)v.fTileMode,
2494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        v.fGradientFlags,
2594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    };
2694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return SkOpts::hash(buffer, sizeof(buffer));
2794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
2894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
2994fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic uint32_t hash(const SkPDFGradientShader::Key& k) {
3094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    uint32_t buffer[] = {
3194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        (uint32_t)k.fType,
3294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        hash(k.fInfo),
3394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkOpts::hash(&k.fCanvasTransform, sizeof(SkMatrix)),
3494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkOpts::hash(&k.fShaderTransform, sizeof(SkMatrix)),
3594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkOpts::hash(&k.fBBox, sizeof(SkIRect))
3694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    };
3794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return SkOpts::hash(buffer, sizeof(buffer));
3894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
3994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
4094fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void unit_to_points_matrix(const SkPoint pts[2], SkMatrix* matrix) {
4194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkVector    vec = pts[1] - pts[0];
4294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar    mag = vec.length();
4394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
4494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
4594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    vec.scale(inv);
4694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    matrix->setSinCos(vec.fY, vec.fX);
4794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    matrix->preScale(mag, mag);
4894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    matrix->postTranslate(pts[0].fX, pts[0].fY);
4994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
5094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
5194fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic const int kColorComponents = 3;
5294fd66cc2502383628b2c5fb72a445460b752c35Hal Canarytypedef uint8_t ColorTuple[kColorComponents];
5394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
5494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary/* Assumes t + startOffset is on the stack and does a linear interpolation on t
5594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary   between startOffset and endOffset from prevColor to curColor (for each color
5694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary   component), leaving the result in component order on the stack. It assumes
5794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary   there are always 3 components per color.
5894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary   @param range                  endOffset - startOffset
5994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary   @param curColor[components]   The current color components.
6094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary   @param prevColor[components]  The previous color components.
6194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary   @param result                 The result ps function.
6294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary */
6394fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void interpolate_color_code(SkScalar range, const ColorTuple& curColor,
6494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                   const ColorTuple& prevColor,
6594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                   SkDynamicMemoryWStream* result) {
6694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(range != SkIntToScalar(0));
6794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
6894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Figure out how to scale each color component.
6994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar multiplier[kColorComponents];
7094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 0; i < kColorComponents; i++) {
7194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        static const SkScalar kColorScale = SkScalarInvert(255);
7294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        multiplier[i] = kColorScale * (curColor[i] - prevColor[i]) / range;
7394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
7494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
7594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Calculate when we no longer need to keep a copy of the input parameter t.
7694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // If the last component to use t is i, then dupInput[0..i - 1] = true
7794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // and dupInput[i .. components] = false.
7894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    bool dupInput[kColorComponents];
7994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    dupInput[kColorComponents - 1] = false;
8094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = kColorComponents - 2; i >= 0; i--) {
8194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
8294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
8394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
8494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (!dupInput[0] && multiplier[0] == 0) {
8594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        result->writeText("pop ");
8694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
8794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
8894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 0; i < kColorComponents; i++) {
8994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // If the next components needs t and this component will consume a
9094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // copy, make another copy.
9194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (dupInput[i] && multiplier[i] != 0) {
9294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            result->writeText("dup ");
9394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
9494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
9594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (multiplier[i] == 0) {
9694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            SkPDFUtils::AppendColorComponent(prevColor[i], result);
9794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            result->writeText(" ");
9894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        } else {
9994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            if (multiplier[i] != 1) {
10094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                SkPDFUtils::AppendScalar(multiplier[i], result);
10194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                result->writeText(" mul ");
10294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            }
10394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            if (prevColor[i] != 0) {
10494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                SkPDFUtils::AppendColorComponent(prevColor[i], result);
10594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                result->writeText(" add ");
10694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            }
10794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
10894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
10994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (dupInput[i]) {
11094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            result->writeText("exch\n");
11194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
11294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
11394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
11494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
11594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary/* Generate Type 4 function code to map t=[0,1) to the passed gradient,
11694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary   clamping at the edges of the range.  The generated code will be of the form:
11794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary       if (t < 0) {
11894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary           return colorData[0][r,g,b];
11994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary       } else {
12094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary           if (t < info.fColorOffsets[1]) {
12194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary               return linearinterpolation(colorData[0][r,g,b],
12294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                          colorData[1][r,g,b]);
12394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary           } else {
12494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary               if (t < info.fColorOffsets[2]) {
12594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                   return linearinterpolation(colorData[1][r,g,b],
12694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                              colorData[2][r,g,b]);
12794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary               } else {
12894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
12994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                ...    } else {
13094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                           return colorData[info.fColorCount - 1][r,g,b];
13194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                       }
13294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                ...
13394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary           }
13494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary       }
13594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary */
13694fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void gradient_function_code(const SkShader::GradientInfo& info,
13794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                 SkDynamicMemoryWStream* result) {
13894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    /* We want to linearly interpolate from the previous color to the next.
13994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary       Scale the colors from 0..255 to 0..1 and determine the multipliers
14094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary       for interpolation.
14194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary       C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
14294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary     */
14394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
14494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
14594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    ColorTuple *colorData = colorDataAlloc.get();
14694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 0; i < info.fColorCount; i++) {
14794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        colorData[i][0] = SkColorGetR(info.fColors[i]);
14894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        colorData[i][1] = SkColorGetG(info.fColors[i]);
14994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        colorData[i][2] = SkColorGetB(info.fColors[i]);
15094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
15194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
15294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Clamp the initial color.
15394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->writeText("dup 0 le {pop ");
15494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendColorComponent(colorData[0][0], result);
15594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->writeText(" ");
15694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendColorComponent(colorData[0][1], result);
15794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->writeText(" ");
15894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendColorComponent(colorData[0][2], result);
15994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->writeText(" }\n");
16094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
16194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // The gradient colors.
16294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    int gradients = 0;
16394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 1 ; i < info.fColorCount; i++) {
16494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) {
16594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            continue;
16694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
16794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        gradients++;
16894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
16994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        result->writeText("{dup ");
17094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::AppendScalar(info.fColorOffsets[i], result);
17194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        result->writeText(" le {");
17294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (info.fColorOffsets[i - 1] != 0) {
17394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            SkPDFUtils::AppendScalar(info.fColorOffsets[i - 1], result);
17494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            result->writeText(" sub\n");
17594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
17694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
17794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        interpolate_color_code(info.fColorOffsets[i] - info.fColorOffsets[i - 1],
17894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                             colorData[i], colorData[i - 1], result);
17994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        result->writeText("}\n");
18094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
18194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
18294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Clamp the final color.
18394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->writeText("{pop ");
18494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][0], result);
18594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->writeText(" ");
18694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][1], result);
18794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->writeText(" ");
18894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][2], result);
18994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
19094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 0 ; i < gradients + 1; i++) {
19194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        result->writeText("} ifelse\n");
19294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
19394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
19494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
19594fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic sk_sp<SkPDFDict> createInterpolationFunction(const ColorTuple& color1,
19694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                                    const ColorTuple& color2) {
19794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto retval = sk_make_sp<SkPDFDict>();
19894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
19994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto c0 = sk_make_sp<SkPDFArray>();
20094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    c0->appendColorComponent(color1[0]);
20194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    c0->appendColorComponent(color1[1]);
20294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    c0->appendColorComponent(color1[2]);
20394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertObject("C0", std::move(c0));
20494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
20594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto c1 = sk_make_sp<SkPDFArray>();
20694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    c1->appendColorComponent(color2[0]);
20794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    c1->appendColorComponent(color2[1]);
20894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    c1->appendColorComponent(color2[2]);
20994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertObject("C1", std::move(c1));
21094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
21194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto domain = sk_make_sp<SkPDFArray>();
21294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    domain->appendScalar(0);
21394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    domain->appendScalar(1.0f);
21494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertObject("Domain", std::move(domain));
21594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
21694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertInt("FunctionType", 2);
21794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertScalar("N", 1.0f);
21894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
21994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return retval;
22094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
22194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
22294fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic sk_sp<SkPDFDict> gradientStitchCode(const SkShader::GradientInfo& info) {
22394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto retval = sk_make_sp<SkPDFDict>();
22494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
22594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // normalize color stops
22694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    int colorCount = info.fColorCount;
22794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkTDArray<SkColor>    colors(info.fColors, colorCount);
22894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkTDArray<SkScalar>   colorOffsets(info.fColorOffsets, colorCount);
22994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
23094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    int i = 1;
23194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    while (i < colorCount - 1) {
23294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // ensure stops are in order
23394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (colorOffsets[i - 1] > colorOffsets[i]) {
23494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            colorOffsets[i] = colorOffsets[i - 1];
23594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
23694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
23794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // remove points that are between 2 coincident points
23894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if ((colorOffsets[i - 1] == colorOffsets[i]) && (colorOffsets[i] == colorOffsets[i + 1])) {
23994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            colorCount -= 1;
24094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            colors.remove(i);
24194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            colorOffsets.remove(i);
24294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        } else {
24394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            i++;
24494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
24594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
24694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // find coincident points and slightly move them over
24794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (i = 1; i < colorCount - 1; i++) {
24894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (colorOffsets[i - 1] == colorOffsets[i]) {
24994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            colorOffsets[i] += 0.00001f;
25094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
25194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
25294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // check if last 2 stops coincide
25394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (colorOffsets[i - 1] == colorOffsets[i]) {
25494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        colorOffsets[i - 1] -= 0.00001f;
25594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
25694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
25794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(colorCount);
25894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    ColorTuple *colorData = colorDataAlloc.get();
25994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 0; i < colorCount; i++) {
26094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        colorData[i][0] = SkColorGetR(colors[i]);
26194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        colorData[i][1] = SkColorGetG(colors[i]);
26294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        colorData[i][2] = SkColorGetB(colors[i]);
26394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
26494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
26594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // no need for a stitch function if there are only 2 stops.
26694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (colorCount == 2)
26794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        return createInterpolationFunction(colorData[0], colorData[1]);
26894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
26994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto encode = sk_make_sp<SkPDFArray>();
27094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto bounds = sk_make_sp<SkPDFArray>();
27194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto functions = sk_make_sp<SkPDFArray>();
27294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
27394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto domain = sk_make_sp<SkPDFArray>();
27494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    domain->appendScalar(0);
27594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    domain->appendScalar(1.0f);
27694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertObject("Domain", std::move(domain));
27794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertInt("FunctionType", 3);
27894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
27994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 1; i < colorCount; i++) {
28094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (i > 1) {
28194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            bounds->appendScalar(colorOffsets[i-1]);
28294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
28394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
28494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        encode->appendScalar(0);
28594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        encode->appendScalar(1.0f);
28694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
28794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        functions->appendObject(createInterpolationFunction(colorData[i-1], colorData[i]));
28894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
28994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
29094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertObject("Encode", std::move(encode));
29194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertObject("Bounds", std::move(bounds));
29294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    retval->insertObject("Functions", std::move(functions));
29394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
29494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return retval;
29594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
29694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
29794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
29894fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void tileModeCode(SkShader::TileMode mode,
29994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                         SkDynamicMemoryWStream* result) {
30094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (mode == SkShader::kRepeat_TileMode) {
30194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        result->writeText("dup truncate sub\n");  // Get the fractional part.
30294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        result->writeText("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
30394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        return;
30494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
30594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
30694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (mode == SkShader::kMirror_TileMode) {
30794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // Map t mod 2 into [0, 1, 1, 0].
30894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        //               Code                     Stack
30994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        result->writeText("abs "                 // Map negative to positive.
31094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "dup "                 // t.s t.s
31194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "truncate "            // t.s t
31294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "dup "                 // t.s t t
31394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "cvi "                 // t.s t T
31494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "2 mod "               // t.s t (i mod 2)
31594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "1 eq "                // t.s t true|false
31694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "3 1 roll "            // true|false t.s t
31794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "sub "                 // true|false 0.s
31894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "exch "                // 0.s true|false
31994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          "{1 exch sub} if\n");  // 1 - 0.s|0.s
32094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
32194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
32294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
32394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary/**
32494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary *  Returns PS function code that applies inverse perspective
32594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary *  to a x, y point.
32694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary *  The function assumes that the stack has at least two elements,
32794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary *  and that the top 2 elements are numeric values.
32894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary *  After executing this code on a PS stack, the last 2 elements are updated
32994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary *  while the rest of the stack is preserved intact.
33094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary *  inversePerspectiveMatrix is the inverse perspective matrix.
33194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary */
33294fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void apply_perspective_to_coordinates(const SkMatrix& inversePerspectiveMatrix,
33394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                             SkDynamicMemoryWStream* code) {
33494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (!inversePerspectiveMatrix.hasPerspective()) {
33594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        return;
33694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
33794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
33894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Perspective matrix should be:
33994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // 1   0  0
34094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // 0   1  0
34194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // p0 p1 p2
34294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
34394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0];
34494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1];
34594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2];
34694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
34794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // y = y / (p2 + p0 x + p1 y)
34894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // x = x / (p2 + p0 x + p1 y)
34994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
35094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Input on stack: x y
35194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    code->writeText(" dup ");             // x y y
35294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendScalar(p1, code);   // x y y p1
35394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    code->writeText(" mul "               // x y y*p1
35494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    " 2 index ");         // x y y*p1 x
35594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendScalar(p0, code);   // x y y p1 x p0
35694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    code->writeText(" mul ");             // x y y*p1 x*p0
35794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendScalar(p2, code);   // x y y p1 x*p0 p2
35894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    code->writeText(" add "               // x y y*p1 x*p0+p2
35994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "add "                // x y y*p1+x*p0+p2
36094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "3 1 roll "           // y*p1+x*p0+p2 x y
36194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "2 index "            // z x y y*p1+x*p0+p2
36294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "div "                // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2)
36394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "3 1 roll "           // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x
36494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "exch "               // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2
36594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "div "                // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2)
36694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "exch\n");            // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2)
36794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
36894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
36994fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void linearCode(const SkShader::GradientInfo& info,
37094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                       const SkMatrix& perspectiveRemover,
37194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                       SkDynamicMemoryWStream* function) {
37294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("{");
37394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
37494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    apply_perspective_to_coordinates(perspectiveRemover, function);
37594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
37694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("pop\n");  // Just ditch the y value.
37794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    tileModeCode(info.fTileMode, function);
37894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    gradient_function_code(info, function);
37994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("}");
38094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
38194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
38294fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void radialCode(const SkShader::GradientInfo& info,
38394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                       const SkMatrix& perspectiveRemover,
38494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                       SkDynamicMemoryWStream* function) {
38594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("{");
38694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
38794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    apply_perspective_to_coordinates(perspectiveRemover, function);
38894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
38994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Find the distance from the origin.
39094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("dup "      // x y y
39194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "mul "      // x y^2
39294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "exch "     // y^2 x
39394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "dup "      // y^2 x x
39494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "mul "      // y^2 x^2
39594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "add "      // y^2+x^2
39694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                    "sqrt\n");  // sqrt(y^2+x^2)
39794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
39894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    tileModeCode(info.fTileMode, function);
39994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    gradient_function_code(info, function);
40094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("}");
40194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
40294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
40394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary/* Conical gradient shader, based on the Canvas spec for radial gradients
40494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary   See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
40594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary */
40694fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void twoPointConicalCode(const SkShader::GradientInfo& info,
40794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                const SkMatrix& perspectiveRemover,
40894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                SkDynamicMemoryWStream* function) {
40994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
41094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
41194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar r0 = info.fRadius[0];
41294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar dr = info.fRadius[1] - info.fRadius[0];
41394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar a = dx * dx + dy * dy - dr * dr;
41494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
41594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // First compute t, if the pixel falls outside the cone, then we'll end
41694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // with 'false' on the stack, otherwise we'll push 'true' with t below it
41794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
41894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // We start with a stack of (x y), copy it and then consume one copy in
41994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // order to calculate b and the other to calculate c.
42094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("{");
42194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
42294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    apply_perspective_to_coordinates(perspectiveRemover, function);
42394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
42494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("2 copy ");
42594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
42694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
42794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendScalar(dy, function);
42894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText(" mul exch ");
42994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendScalar(dx, function);
43094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText(" mul add ");
43194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendScalar(r0 * dr, function);
43294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText(" add -2 mul dup dup mul\n");
43394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
43494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // c = x^2 + y^2 + radius0^2
43594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("4 2 roll dup mul exch dup mul add ");
43694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendScalar(r0 * r0, function);
43794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText(" sub dup 4 1 roll\n");
43894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
43994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Contents of the stack at this point: c, b, b^2, c
44094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
44194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // if a = 0, then we collapse to a simpler linear case
44294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (a == 0) {
44394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
44494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // t = -c/b
44594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("pop pop div neg dup ");
44694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
44794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // compute radius(t)
44894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::AppendScalar(dr, function);
44994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" mul ");
45094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::AppendScalar(r0, function);
45194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" add\n");
45294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
45394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // if r(t) < 0, then it's outside the cone
45494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("0 lt {pop false} {true} ifelse\n");
45594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
45694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    } else {
45794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
45894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // quadratic case: the Canvas spec wants the largest
45994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // root t for which radius(t) > 0
46094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
46194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // compute the discriminant (b^2 - 4ac)
46294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::AppendScalar(a * 4, function);
46394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" mul sub dup\n");
46494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
46594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // if d >= 0, proceed
46694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("0 ge {\n");
46794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
46894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // an intermediate value we'll use to compute the roots:
46994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // q = -0.5 * (b +/- sqrt(d))
47094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("sqrt exch dup 0 lt {exch -1 mul} if");
47194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" add -0.5 mul dup\n");
47294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
47394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // first root = q / a
47494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::AppendScalar(a, function);
47594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" div\n");
47694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
47794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // second root = c / q
47894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("3 1 roll div\n");
47994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
48094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // put the larger root on top of the stack
48194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("2 copy gt {exch} if\n");
48294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
48394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // compute radius(t) for larger root
48494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("dup ");
48594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::AppendScalar(dr, function);
48694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" mul ");
48794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::AppendScalar(r0, function);
48894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" add\n");
48994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
49094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // if r(t) > 0, we have our t, pop off the smaller root and we're done
49194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" 0 gt {exch pop true}\n");
49294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
49394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // otherwise, throw out the larger one and try the smaller root
49494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("{pop dup\n");
49594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::AppendScalar(dr, function);
49694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" mul ");
49794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::AppendScalar(r0, function);
49894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText(" add\n");
49994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
50094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // if r(t) < 0, push false, otherwise the smaller root is our t
50194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("0 le {pop false} {true} ifelse\n");
50294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("} ifelse\n");
50394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
50494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // d < 0, clear the stack and push false
50594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        function->writeText("} {pop pop pop false} ifelse\n");
50694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
50794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
50894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // if the pixel is in the cone, proceed to compute a color
50994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("{");
51094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    tileModeCode(info.fTileMode, function);
51194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    gradient_function_code(info, function);
51294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
51394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // otherwise, just write black
51494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("} {0 0 0} ifelse }");
51594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
51694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
51794fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void sweepCode(const SkShader::GradientInfo& info,
51894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          const SkMatrix& perspectiveRemover,
51994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                          SkDynamicMemoryWStream* function) {
52094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("{exch atan 360 div\n");
52194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    tileModeCode(info.fTileMode, function);
52294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    gradient_function_code(info, function);
52394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    function->writeText("}");
52494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
52594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
52694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
52794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary// catch cases where the inner just touches the outer circle
52894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary// and make the inner circle just inside the outer one to match raster
52994fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic void FixUpRadius(const SkPoint& p1, SkScalar& r1, const SkPoint& p2, SkScalar& r2) {
53094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // detect touching circles
53194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar distance = SkPoint::Distance(p1, p2);
53294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkScalar subtractRadii = fabs(r1 - r2);
53394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (fabs(distance - subtractRadii) < 0.002f) {
53494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (r1 > r2) {
53594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            r1 += 0.002f;
53694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        } else {
53794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            r2 += 0.002f;
53894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
53994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
54094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
54194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
54294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary// Finds affine and persp such that in = affine * persp.
54394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary// but it returns the inverse of perspective matrix.
54494fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic bool split_perspective(const SkMatrix in, SkMatrix* affine,
54594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                              SkMatrix* perspectiveInverse) {
54694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar p2 = in[SkMatrix::kMPersp2];
54794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
54894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (SkScalarNearlyZero(p2)) {
54994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        return false;
55094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
55194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
55294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar zero = SkIntToScalar(0);
55394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar one = SkIntToScalar(1);
55494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
55594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar sx = in[SkMatrix::kMScaleX];
55694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar kx = in[SkMatrix::kMSkewX];
55794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar tx = in[SkMatrix::kMTransX];
55894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar ky = in[SkMatrix::kMSkewY];
55994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar sy = in[SkMatrix::kMScaleY];
56094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar ty = in[SkMatrix::kMTransY];
56194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar p0 = in[SkMatrix::kMPersp0];
56294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkScalar p1 = in[SkMatrix::kMPersp1];
56394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
56494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Perspective matrix would be:
56594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // 1  0  0
56694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // 0  1  0
56794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // p0 p1 p2
56894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // But we need the inverse of persp.
56994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    perspectiveInverse->setAll(one,          zero,       zero,
57094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                               zero,         one,        zero,
57194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                               -p0/p2,     -p1/p2,     1/p2);
57294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
57394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    affine->setAll(sx - p0 * tx / p2,       kx - p1 * tx / p2,      tx / p2,
57494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                   ky - p0 * ty / p2,       sy - p1 * ty / p2,      ty / p2,
57594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                   zero,                    zero,                   one);
57694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
57794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return true;
57894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
57994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
58094fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic sk_sp<SkPDFArray> make_range_object() {
58194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto range = sk_make_sp<SkPDFArray>();
58294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    range->reserve(6);
58394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    range->appendInt(0);
58494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    range->appendInt(1);
58594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    range->appendInt(0);
58694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    range->appendInt(1);
58794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    range->appendInt(0);
58894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    range->appendInt(1);
58994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return range;
59094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
59194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
59294fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic sk_sp<SkPDFStream> make_ps_function(
59394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        std::unique_ptr<SkStreamAsset> psCode,
59494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        sk_sp<SkPDFArray> domain,
59594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        sk_sp<SkPDFObject> range) {
59694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto result = sk_make_sp<SkPDFStream>(std::move(psCode));
59794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->dict()->insertInt("FunctionType", 4);
59894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->dict()->insertObject("Domain", std::move(domain));
59994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    result->dict()->insertObject("Range", std::move(range));
60094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return result;
60194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
60294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
60394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
60494fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
60594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                             const SkPDFGradientShader::Key& state) {
60694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPoint transformPoints[2];
60794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    const SkShader::GradientInfo& info = state.fInfo;
60894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkMatrix finalMatrix = state.fCanvasTransform;
60994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    finalMatrix.preConcat(state.fShaderTransform);
61094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
61194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    bool doStitchFunctions = (state.fType == SkShader::kLinear_GradientType ||
61294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                              state.fType == SkShader::kRadial_GradientType ||
61394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                              state.fType == SkShader::kConical_GradientType) &&
61494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                             info.fTileMode == SkShader::kClamp_TileMode &&
61594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                             !finalMatrix.hasPerspective();
61694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
61794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto domain = sk_make_sp<SkPDFArray>();
61894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
61994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    int32_t shadingType = 1;
62094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto pdfShader = sk_make_sp<SkPDFDict>();
62194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // The two point radial gradient further references
62294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // state.fInfo
62394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // in translating from x, y coordinates to the t parameter. So, we have
62494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // to transform the points and radii according to the calculated matrix.
62594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (doStitchFunctions) {
62694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        pdfShader->insertObject("Function", gradientStitchCode(info));
62794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        shadingType = (state.fType == SkShader::kLinear_GradientType) ? 2 : 3;
62894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
62994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        auto extend = sk_make_sp<SkPDFArray>();
63094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        extend->reserve(2);
63194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        extend->appendBool(true);
63294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        extend->appendBool(true);
63394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        pdfShader->insertObject("Extend", std::move(extend));
63494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
63594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        auto coords = sk_make_sp<SkPDFArray>();
63694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (state.fType == SkShader::kConical_GradientType) {
63794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->reserve(6);
63894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            SkScalar r1 = info.fRadius[0];
63994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            SkScalar r2 = info.fRadius[1];
64094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            SkPoint pt1 = info.fPoint[0];
64194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            SkPoint pt2 = info.fPoint[1];
64294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            FixUpRadius(pt1, r1, pt2, r2);
64394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
64494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt1.fX);
64594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt1.fY);
64694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(r1);
64794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
64894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt2.fX);
64994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt2.fY);
65094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(r2);
65194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        } else if (state.fType == SkShader::kRadial_GradientType) {
65294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->reserve(6);
65394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            const SkPoint& pt1 = info.fPoint[0];
65494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
65594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt1.fX);
65694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt1.fY);
65794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(0);
65894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
65994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt1.fX);
66094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt1.fY);
66194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(info.fRadius[0]);
66294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        } else {
66394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->reserve(4);
66494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            const SkPoint& pt1 = info.fPoint[0];
66594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            const SkPoint& pt2 = info.fPoint[1];
66694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
66794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt1.fX);
66894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt1.fY);
66994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
67094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt2.fX);
67194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            coords->appendScalar(pt2.fY);
67294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
67394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
67494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        pdfShader->insertObject("Coords", std::move(coords));
67594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    } else {
67694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // Depending on the type of the gradient, we want to transform the
67794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // coordinate space in different ways.
67894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        transformPoints[0] = info.fPoint[0];
67994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        transformPoints[1] = info.fPoint[1];
68094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        switch (state.fType) {
68194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kLinear_GradientType:
68294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                break;
68394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kRadial_GradientType:
68494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                transformPoints[1] = transformPoints[0];
68594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                transformPoints[1].fX += info.fRadius[0];
68694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                break;
68794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kConical_GradientType: {
68894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                transformPoints[1] = transformPoints[0];
68994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                transformPoints[1].fX += SK_Scalar1;
69094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                break;
69194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            }
69294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kSweep_GradientType:
69394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                transformPoints[1] = transformPoints[0];
69494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                transformPoints[1].fX += SK_Scalar1;
69594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                break;
69694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kColor_GradientType:
69794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kNone_GradientType:
69894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            default:
69994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                return nullptr;
70094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
70194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
70294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // Move any scaling (assuming a unit gradient) or translation
70394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // (and rotation for linear gradient), of the final gradient from
70494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // info.fPoints to the matrix (updating bbox appropriately).  Now
70594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // the gradient can be drawn on on the unit segment.
70694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkMatrix mapperMatrix;
70794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        unit_to_points_matrix(transformPoints, &mapperMatrix);
70894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
70994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        finalMatrix.preConcat(mapperMatrix);
71094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
71194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // Preserves as much as posible in the final matrix, and only removes
71294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // the perspective. The inverse of the perspective is stored in
71394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // perspectiveInverseOnly matrix and has 3 useful numbers
71494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // (p0, p1, p2), while everything else is either 0 or 1.
71594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        // In this way the shader will handle it eficiently, with minimal code.
71694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkMatrix perspectiveInverseOnly = SkMatrix::I();
71794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (finalMatrix.hasPerspective()) {
71894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            if (!split_perspective(finalMatrix,
71994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                   &finalMatrix, &perspectiveInverseOnly)) {
72094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                return nullptr;
72194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            }
72294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
72394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
72494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkRect bbox;
72594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        bbox.set(state.fBBox);
72694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &bbox)) {
72794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            return nullptr;
72894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
72994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        domain->reserve(4);
73094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        domain->appendScalar(bbox.fLeft);
73194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        domain->appendScalar(bbox.fRight);
73294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        domain->appendScalar(bbox.fTop);
73394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        domain->appendScalar(bbox.fBottom);
73494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
73594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkDynamicMemoryWStream functionCode;
73694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
73794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkShader::GradientInfo infoCopy = info;
73894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
73994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (state.fType == SkShader::kConical_GradientType) {
74094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            SkMatrix inverseMapperMatrix;
74194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            if (!mapperMatrix.invert(&inverseMapperMatrix)) {
74294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                return nullptr;
74394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            }
74494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            inverseMapperMatrix.mapPoints(infoCopy.fPoint, 2);
74594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            infoCopy.fRadius[0] = inverseMapperMatrix.mapRadius(info.fRadius[0]);
74694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            infoCopy.fRadius[1] = inverseMapperMatrix.mapRadius(info.fRadius[1]);
74794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
74894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        switch (state.fType) {
74994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kLinear_GradientType:
75094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                linearCode(infoCopy, perspectiveInverseOnly, &functionCode);
75194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                break;
75294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kRadial_GradientType:
75394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                radialCode(infoCopy, perspectiveInverseOnly, &functionCode);
75494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                break;
75594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kConical_GradientType:
75694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                twoPointConicalCode(infoCopy, perspectiveInverseOnly, &functionCode);
75794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                break;
75894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            case SkShader::kSweep_GradientType:
75994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                sweepCode(infoCopy, perspectiveInverseOnly, &functionCode);
76094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                break;
76194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            default:
76294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                SkASSERT(false);
76394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
76494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        pdfShader->insertObject("Domain", domain);
76594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
76694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        sk_sp<SkPDFArray>& rangeObject = canon->fRangeObject;
76794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if (!rangeObject) {
76894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            rangeObject = make_range_object();
76994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
77094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        pdfShader->insertObjRef("Function",
77194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                make_ps_function(functionCode.detachAsStream(), std::move(domain),
77294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                                 rangeObject));
77394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
77494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
77594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    pdfShader->insertInt("ShadingType", shadingType);
77694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    pdfShader->insertName("ColorSpace", "DeviceRGB");
77794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
77894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto pdfFunctionShader = sk_make_sp<SkPDFDict>("Pattern");
77994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    pdfFunctionShader->insertInt("PatternType", 2);
78094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    pdfFunctionShader->insertObject("Matrix", SkPDFUtils::MatrixToArray(finalMatrix));
78194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    pdfFunctionShader->insertObject("Shading", std::move(pdfShader));
78294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
78394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return pdfFunctionShader;
78494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
78594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
7862d171397f863699eb7804b814994d4c2fcb00cb7Hal Canarystatic sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
7872d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary                                          SkPDFGradientShader::Key key,
7882d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary                                          bool keyHasAlpha);
78994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
79094fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic sk_sp<SkPDFDict> get_gradient_resource_dict(SkPDFObject* functionShader,
79194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                                   SkPDFObject* gState) {
79294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkTDArray<SkPDFObject*> patterns;
79394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (functionShader) {
79494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        patterns.push(functionShader);
79594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
79694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkTDArray<SkPDFObject*> graphicStates;
79794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (gState) {
79894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        graphicStates.push(gState);
79994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
80094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return SkPDFResourceDict::Make(&graphicStates, &patterns, nullptr, nullptr);
80194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
80294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
80394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary// Creates a content stream which fills the pattern P0 across bounds.
80494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary// @param gsIndex A graphics state resource index to apply, or <0 if no
80594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary// graphics state to apply.
80694fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex, SkRect& bounds) {
80794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkDynamicMemoryWStream content;
80894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (gsIndex >= 0) {
80994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkPDFUtils::ApplyGraphicState(gsIndex, &content);
81094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
81194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::ApplyPattern(0, &content);
81294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::AppendRectangle(bounds, &content);
81394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType, &content);
81494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return content.detachAsStream();
81594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
81694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
81794fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic bool gradient_has_alpha(const SkPDFGradientShader::Key& key) {
81894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(key.fType != SkShader::kNone_GradientType);
81994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 0; i < key.fInfo.fColorCount; i++) {
82094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        if ((SkAlpha)SkColorGetA(key.fInfo.fColors[i]) != SK_AlphaOPAQUE) {
82194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            return true;
82294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        }
82394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
82494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return false;
82594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
82694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
82794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary// warning: does not set fHash on new key.  (Both callers need to change fields.)
82894fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic SkPDFGradientShader::Key clone_key(const SkPDFGradientShader::Key& k) {
82994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFGradientShader::Key clone = {
83094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        k.fType,
83194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        k.fInfo,  // change pointers later.
83294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        std::unique_ptr<SkColor[]>(new SkColor[k.fInfo.fColorCount]),
83394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        std::unique_ptr<SkScalar[]>(new SkScalar[k.fInfo.fColorCount]),
83494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        k.fCanvasTransform,
83594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        k.fShaderTransform,
83694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        k.fBBox, 0};
83794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    clone.fInfo.fColors = clone.fColors.get();
83894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    clone.fInfo.fColorOffsets = clone.fStops.get();
83994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 0; i < clone.fInfo.fColorCount; i++) {
84094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        clone.fInfo.fColorOffsets[i] = k.fInfo.fColorOffsets[i];
84194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        clone.fInfo.fColors[i] = k.fInfo.fColors[i];
84294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
84394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return clone;
84494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
84594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
84694fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic sk_sp<SkPDFObject> create_smask_graphic_state(SkPDFDocument* doc,
84794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                                     const SkPDFGradientShader::Key& state) {
84894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(state.fType != SkShader::kNone_GradientType);
84994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFGradientShader::Key luminosityState = clone_key(state);
85094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 0; i < luminosityState.fInfo.fColorCount; i++) {
85194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        SkAlpha alpha = SkColorGetA(luminosityState.fInfo.fColors[i]);
85294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        luminosityState.fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha);
85394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
85494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    luminosityState.fHash = hash(luminosityState);
85594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
85694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(!gradient_has_alpha(luminosityState));
8572d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    sk_sp<SkPDFObject> luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
85894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    sk_sp<SkPDFDict> resources = get_gradient_resource_dict(luminosityShader.get(), nullptr);
85994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkRect bbox = SkRect::Make(state.fBBox);
86094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    sk_sp<SkPDFObject> alphaMask = SkPDFMakeFormXObject(create_pattern_fill_content(-1, bbox),
86194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                                        SkPDFUtils::RectToArray(bbox),
86294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                                        std::move(resources),
86394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                                        SkMatrix::I(),
86494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                                        "DeviceRGB");
86594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return SkPDFGraphicState::GetSMaskGraphicState(
86694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            std::move(alphaMask), false,
86794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            SkPDFGraphicState::kLuminosity_SMaskMode, doc->canon());
86894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
86994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
87094fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
87194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                                     const SkPDFGradientShader::Key& state) {
87294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(state.fType != SkShader::kNone_GradientType);
87394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFGradientShader::Key opaqueState = clone_key(state);
87494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    for (int i = 0; i < opaqueState.fInfo.fColorCount; i++) {
87594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        opaqueState.fInfo.fColors[i] = SkColorSetA(opaqueState.fInfo.fColors[i], SK_AlphaOPAQUE);
87694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
87794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    opaqueState.fHash = hash(opaqueState);
87894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
87994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(!gradient_has_alpha(opaqueState));
88094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkRect bbox = SkRect::Make(state.fBBox);
8812d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    sk_sp<SkPDFObject> colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
88294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    if (!colorShader) {
88394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary        return nullptr;
88494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    }
88594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
88694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // Create resource dict with alpha graphics state as G0 and
88794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    // pattern shader as P0, then write content stream.
88894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    sk_sp<SkPDFObject> alphaGs = create_smask_graphic_state(doc, state);
88994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
89094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    sk_sp<SkPDFDict> resourceDict =
89194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary            get_gradient_resource_dict(colorShader.get(), alphaGs.get());
89294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
89394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    std::unique_ptr<SkStreamAsset> colorStream(create_pattern_fill_content(0, bbox));
89494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    auto alphaFunctionShader = sk_make_sp<SkPDFStream>(std::move(colorStream));
89594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
89694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader->dict(), bbox,
89794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                 std::move(resourceDict), SkMatrix::I());
89894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return alphaFunctionShader;
89994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
90094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
90194fd66cc2502383628b2c5fb72a445460b752c35Hal Canarystatic SkPDFGradientShader::Key make_key(const SkShader* shader,
90294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                         const SkMatrix& canvasTransform,
90394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                         const SkIRect& bbox) {
90494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFGradientShader::Key key = {
90594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary         SkShader::kNone_GradientType,
90694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary         {0, nullptr, nullptr, {{0, 0}, {0, 0}}, {0, 0}, SkShader::kClamp_TileMode, 0},
90794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary         nullptr,
90894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary         nullptr,
90994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary         canvasTransform,
91094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary         SkPDFUtils::GetShaderLocalMatrix(shader),
91194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary         bbox, 0};
91294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    key.fType = shader->asAGradient(&key.fInfo);
91394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(SkShader::kNone_GradientType != key.fType);
91494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(key.fInfo.fColorCount > 0);
91594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    key.fColors.reset(new SkColor[key.fInfo.fColorCount]);
91694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    key.fStops.reset(new SkScalar[key.fInfo.fColorCount]);
91794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    key.fInfo.fColors = key.fColors.get();
91894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    key.fInfo.fColorOffsets = key.fStops.get();
91994fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    (void)shader->asAGradient(&key.fInfo);
92094fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    key.fHash = hash(key);
92194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    return key;
92294fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
92394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary
9242d171397f863699eb7804b814994d4c2fcb00cb7Hal Canarystatic sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
9252d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary                                          SkPDFGradientShader::Key key,
9262d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary                                          bool keyHasAlpha) {
9272d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    SkASSERT(gradient_has_alpha(key) == keyHasAlpha);
9282d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    SkPDFCanon* canon = doc->canon();
9292d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    if (sk_sp<SkPDFObject>* ptr = canon->fGradientPatternMap.find(key)) {
9302d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary        return *ptr;
9312d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    }
9322d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    sk_sp<SkPDFObject> pdfShader;
9332d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    if (keyHasAlpha) {
9342d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary        pdfShader = make_alpha_function_shader(doc, key);
9352d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    } else {
9362d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary        pdfShader = make_function_shader(canon, key);
9372d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    }
9382d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    canon->fGradientPatternMap.set(std::move(key), pdfShader);
9392d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    return pdfShader;
9402d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary}
9412d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary
94294fd66cc2502383628b2c5fb72a445460b752c35Hal Canarysk_sp<SkPDFObject> SkPDFGradientShader::Make(SkPDFDocument* doc,
94394fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                             SkShader* shader,
94494fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                             const SkMatrix& canvasTransform,
94594fd66cc2502383628b2c5fb72a445460b752c35Hal Canary                                             const SkIRect& bbox) {
94694fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(shader);
94794fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkASSERT(SkShader::kNone_GradientType != shader->asAGradient(nullptr));
94894fd66cc2502383628b2c5fb72a445460b752c35Hal Canary    SkPDFGradientShader::Key key = make_key(shader, canvasTransform, bbox);
9492d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    bool alpha = gradient_has_alpha(key);
9502d171397f863699eb7804b814994d4c2fcb00cb7Hal Canary    return find_pdf_shader(doc, std::move(key), alpha);
95194fd66cc2502383628b2c5fb72a445460b752c35Hal Canary}
952