Home | History | Annotate | Download | only in pdf
      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 "SkPDFGradientShader.h"
      9 
     10 #include "SkOpts.h"
     11 #include "SkPDFDocument.h"
     12 #include "SkPDFFormXObject.h"
     13 #include "SkPDFResourceDict.h"
     14 #include "SkPDFUtils.h"
     15 
     16 static uint32_t hash(const SkShader::GradientInfo& v) {
     17     uint32_t buffer[] = {
     18         (uint32_t)v.fColorCount,
     19         SkOpts::hash(v.fColors, v.fColorCount * sizeof(SkColor)),
     20         SkOpts::hash(v.fColorOffsets, v.fColorCount * sizeof(SkScalar)),
     21         SkOpts::hash(v.fPoint, 2 * sizeof(SkPoint)),
     22         SkOpts::hash(v.fRadius, 2 * sizeof(SkScalar)),
     23         (uint32_t)v.fTileMode,
     24         v.fGradientFlags,
     25     };
     26     return SkOpts::hash(buffer, sizeof(buffer));
     27 }
     28 
     29 static uint32_t hash(const SkPDFGradientShader::Key& k) {
     30     uint32_t buffer[] = {
     31         (uint32_t)k.fType,
     32         hash(k.fInfo),
     33         SkOpts::hash(&k.fCanvasTransform, sizeof(SkMatrix)),
     34         SkOpts::hash(&k.fShaderTransform, sizeof(SkMatrix)),
     35         SkOpts::hash(&k.fBBox, sizeof(SkIRect))
     36     };
     37     return SkOpts::hash(buffer, sizeof(buffer));
     38 }
     39 
     40 static void unit_to_points_matrix(const SkPoint pts[2], SkMatrix* matrix) {
     41     SkVector    vec = pts[1] - pts[0];
     42     SkScalar    mag = vec.length();
     43     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
     44 
     45     vec.scale(inv);
     46     matrix->setSinCos(vec.fY, vec.fX);
     47     matrix->preScale(mag, mag);
     48     matrix->postTranslate(pts[0].fX, pts[0].fY);
     49 }
     50 
     51 static const int kColorComponents = 3;
     52 typedef uint8_t ColorTuple[kColorComponents];
     53 
     54 /* Assumes t + startOffset is on the stack and does a linear interpolation on t
     55    between startOffset and endOffset from prevColor to curColor (for each color
     56    component), leaving the result in component order on the stack. It assumes
     57    there are always 3 components per color.
     58    @param range                  endOffset - startOffset
     59    @param curColor[components]   The current color components.
     60    @param prevColor[components]  The previous color components.
     61    @param result                 The result ps function.
     62  */
     63 static void interpolate_color_code(SkScalar range, const ColorTuple& curColor,
     64                                    const ColorTuple& prevColor,
     65                                    SkDynamicMemoryWStream* result) {
     66     SkASSERT(range != SkIntToScalar(0));
     67 
     68     // Figure out how to scale each color component.
     69     SkScalar multiplier[kColorComponents];
     70     for (int i = 0; i < kColorComponents; i++) {
     71         static const SkScalar kColorScale = SkScalarInvert(255);
     72         multiplier[i] = kColorScale * (curColor[i] - prevColor[i]) / range;
     73     }
     74 
     75     // Calculate when we no longer need to keep a copy of the input parameter t.
     76     // If the last component to use t is i, then dupInput[0..i - 1] = true
     77     // and dupInput[i .. components] = false.
     78     bool dupInput[kColorComponents];
     79     dupInput[kColorComponents - 1] = false;
     80     for (int i = kColorComponents - 2; i >= 0; i--) {
     81         dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
     82     }
     83 
     84     if (!dupInput[0] && multiplier[0] == 0) {
     85         result->writeText("pop ");
     86     }
     87 
     88     for (int i = 0; i < kColorComponents; i++) {
     89         // If the next components needs t and this component will consume a
     90         // copy, make another copy.
     91         if (dupInput[i] && multiplier[i] != 0) {
     92             result->writeText("dup ");
     93         }
     94 
     95         if (multiplier[i] == 0) {
     96             SkPDFUtils::AppendColorComponent(prevColor[i], result);
     97             result->writeText(" ");
     98         } else {
     99             if (multiplier[i] != 1) {
    100                 SkPDFUtils::AppendScalar(multiplier[i], result);
    101                 result->writeText(" mul ");
    102             }
    103             if (prevColor[i] != 0) {
    104                 SkPDFUtils::AppendColorComponent(prevColor[i], result);
    105                 result->writeText(" add ");
    106             }
    107         }
    108 
    109         if (dupInput[i]) {
    110             result->writeText("exch\n");
    111         }
    112     }
    113 }
    114 
    115 /* Generate Type 4 function code to map t=[0,1) to the passed gradient,
    116    clamping at the edges of the range.  The generated code will be of the form:
    117        if (t < 0) {
    118            return colorData[0][r,g,b];
    119        } else {
    120            if (t < info.fColorOffsets[1]) {
    121                return linearinterpolation(colorData[0][r,g,b],
    122                                           colorData[1][r,g,b]);
    123            } else {
    124                if (t < info.fColorOffsets[2]) {
    125                    return linearinterpolation(colorData[1][r,g,b],
    126                                               colorData[2][r,g,b]);
    127                } else {
    128 
    129                 ...    } else {
    130                            return colorData[info.fColorCount - 1][r,g,b];
    131                        }
    132                 ...
    133            }
    134        }
    135  */
    136 static void gradient_function_code(const SkShader::GradientInfo& info,
    137                                  SkDynamicMemoryWStream* result) {
    138     /* We want to linearly interpolate from the previous color to the next.
    139        Scale the colors from 0..255 to 0..1 and determine the multipliers
    140        for interpolation.
    141        C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
    142      */
    143 
    144     SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
    145     ColorTuple *colorData = colorDataAlloc.get();
    146     for (int i = 0; i < info.fColorCount; i++) {
    147         colorData[i][0] = SkColorGetR(info.fColors[i]);
    148         colorData[i][1] = SkColorGetG(info.fColors[i]);
    149         colorData[i][2] = SkColorGetB(info.fColors[i]);
    150     }
    151 
    152     // Clamp the initial color.
    153     result->writeText("dup 0 le {pop ");
    154     SkPDFUtils::AppendColorComponent(colorData[0][0], result);
    155     result->writeText(" ");
    156     SkPDFUtils::AppendColorComponent(colorData[0][1], result);
    157     result->writeText(" ");
    158     SkPDFUtils::AppendColorComponent(colorData[0][2], result);
    159     result->writeText(" }\n");
    160 
    161     // The gradient colors.
    162     int gradients = 0;
    163     for (int i = 1 ; i < info.fColorCount; i++) {
    164         if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) {
    165             continue;
    166         }
    167         gradients++;
    168 
    169         result->writeText("{dup ");
    170         SkPDFUtils::AppendScalar(info.fColorOffsets[i], result);
    171         result->writeText(" le {");
    172         if (info.fColorOffsets[i - 1] != 0) {
    173             SkPDFUtils::AppendScalar(info.fColorOffsets[i - 1], result);
    174             result->writeText(" sub\n");
    175         }
    176 
    177         interpolate_color_code(info.fColorOffsets[i] - info.fColorOffsets[i - 1],
    178                              colorData[i], colorData[i - 1], result);
    179         result->writeText("}\n");
    180     }
    181 
    182     // Clamp the final color.
    183     result->writeText("{pop ");
    184     SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][0], result);
    185     result->writeText(" ");
    186     SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][1], result);
    187     result->writeText(" ");
    188     SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][2], result);
    189 
    190     for (int i = 0 ; i < gradients + 1; i++) {
    191         result->writeText("} ifelse\n");
    192     }
    193 }
    194 
    195 static sk_sp<SkPDFDict> createInterpolationFunction(const ColorTuple& color1,
    196                                                     const ColorTuple& color2) {
    197     auto retval = sk_make_sp<SkPDFDict>();
    198 
    199     auto c0 = sk_make_sp<SkPDFArray>();
    200     c0->appendColorComponent(color1[0]);
    201     c0->appendColorComponent(color1[1]);
    202     c0->appendColorComponent(color1[2]);
    203     retval->insertObject("C0", std::move(c0));
    204 
    205     auto c1 = sk_make_sp<SkPDFArray>();
    206     c1->appendColorComponent(color2[0]);
    207     c1->appendColorComponent(color2[1]);
    208     c1->appendColorComponent(color2[2]);
    209     retval->insertObject("C1", std::move(c1));
    210 
    211     auto domain = sk_make_sp<SkPDFArray>();
    212     domain->appendScalar(0);
    213     domain->appendScalar(1.0f);
    214     retval->insertObject("Domain", std::move(domain));
    215 
    216     retval->insertInt("FunctionType", 2);
    217     retval->insertScalar("N", 1.0f);
    218 
    219     return retval;
    220 }
    221 
    222 static sk_sp<SkPDFDict> gradientStitchCode(const SkShader::GradientInfo& info) {
    223     auto retval = sk_make_sp<SkPDFDict>();
    224 
    225     // normalize color stops
    226     int colorCount = info.fColorCount;
    227     SkTDArray<SkColor>    colors(info.fColors, colorCount);
    228     SkTDArray<SkScalar>   colorOffsets(info.fColorOffsets, colorCount);
    229 
    230     int i = 1;
    231     while (i < colorCount - 1) {
    232         // ensure stops are in order
    233         if (colorOffsets[i - 1] > colorOffsets[i]) {
    234             colorOffsets[i] = colorOffsets[i - 1];
    235         }
    236 
    237         // remove points that are between 2 coincident points
    238         if ((colorOffsets[i - 1] == colorOffsets[i]) && (colorOffsets[i] == colorOffsets[i + 1])) {
    239             colorCount -= 1;
    240             colors.remove(i);
    241             colorOffsets.remove(i);
    242         } else {
    243             i++;
    244         }
    245     }
    246     // find coincident points and slightly move them over
    247     for (i = 1; i < colorCount - 1; i++) {
    248         if (colorOffsets[i - 1] == colorOffsets[i]) {
    249             colorOffsets[i] += 0.00001f;
    250         }
    251     }
    252     // check if last 2 stops coincide
    253     if (colorOffsets[i - 1] == colorOffsets[i]) {
    254         colorOffsets[i - 1] -= 0.00001f;
    255     }
    256 
    257     SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(colorCount);
    258     ColorTuple *colorData = colorDataAlloc.get();
    259     for (int i = 0; i < colorCount; i++) {
    260         colorData[i][0] = SkColorGetR(colors[i]);
    261         colorData[i][1] = SkColorGetG(colors[i]);
    262         colorData[i][2] = SkColorGetB(colors[i]);
    263     }
    264 
    265     // no need for a stitch function if there are only 2 stops.
    266     if (colorCount == 2)
    267         return createInterpolationFunction(colorData[0], colorData[1]);
    268 
    269     auto encode = sk_make_sp<SkPDFArray>();
    270     auto bounds = sk_make_sp<SkPDFArray>();
    271     auto functions = sk_make_sp<SkPDFArray>();
    272 
    273     auto domain = sk_make_sp<SkPDFArray>();
    274     domain->appendScalar(0);
    275     domain->appendScalar(1.0f);
    276     retval->insertObject("Domain", std::move(domain));
    277     retval->insertInt("FunctionType", 3);
    278 
    279     for (int i = 1; i < colorCount; i++) {
    280         if (i > 1) {
    281             bounds->appendScalar(colorOffsets[i-1]);
    282         }
    283 
    284         encode->appendScalar(0);
    285         encode->appendScalar(1.0f);
    286 
    287         functions->appendObject(createInterpolationFunction(colorData[i-1], colorData[i]));
    288     }
    289 
    290     retval->insertObject("Encode", std::move(encode));
    291     retval->insertObject("Bounds", std::move(bounds));
    292     retval->insertObject("Functions", std::move(functions));
    293 
    294     return retval;
    295 }
    296 
    297 /* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
    298 static void tileModeCode(SkShader::TileMode mode,
    299                          SkDynamicMemoryWStream* result) {
    300     if (mode == SkShader::kRepeat_TileMode) {
    301         result->writeText("dup truncate sub\n");  // Get the fractional part.
    302         result->writeText("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
    303         return;
    304     }
    305 
    306     if (mode == SkShader::kMirror_TileMode) {
    307         // Map t mod 2 into [0, 1, 1, 0].
    308         //               Code                     Stack
    309         result->writeText("abs "                 // Map negative to positive.
    310                           "dup "                 // t.s t.s
    311                           "truncate "            // t.s t
    312                           "dup "                 // t.s t t
    313                           "cvi "                 // t.s t T
    314                           "2 mod "               // t.s t (i mod 2)
    315                           "1 eq "                // t.s t true|false
    316                           "3 1 roll "            // true|false t.s t
    317                           "sub "                 // true|false 0.s
    318                           "exch "                // 0.s true|false
    319                           "{1 exch sub} if\n");  // 1 - 0.s|0.s
    320     }
    321 }
    322 
    323 /**
    324  *  Returns PS function code that applies inverse perspective
    325  *  to a x, y point.
    326  *  The function assumes that the stack has at least two elements,
    327  *  and that the top 2 elements are numeric values.
    328  *  After executing this code on a PS stack, the last 2 elements are updated
    329  *  while the rest of the stack is preserved intact.
    330  *  inversePerspectiveMatrix is the inverse perspective matrix.
    331  */
    332 static void apply_perspective_to_coordinates(const SkMatrix& inversePerspectiveMatrix,
    333                                              SkDynamicMemoryWStream* code) {
    334     if (!inversePerspectiveMatrix.hasPerspective()) {
    335         return;
    336     }
    337 
    338     // Perspective matrix should be:
    339     // 1   0  0
    340     // 0   1  0
    341     // p0 p1 p2
    342 
    343     const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0];
    344     const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1];
    345     const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2];
    346 
    347     // y = y / (p2 + p0 x + p1 y)
    348     // x = x / (p2 + p0 x + p1 y)
    349 
    350     // Input on stack: x y
    351     code->writeText(" dup ");             // x y y
    352     SkPDFUtils::AppendScalar(p1, code);   // x y y p1
    353     code->writeText(" mul "               // x y y*p1
    354                     " 2 index ");         // x y y*p1 x
    355     SkPDFUtils::AppendScalar(p0, code);   // x y y p1 x p0
    356     code->writeText(" mul ");             // x y y*p1 x*p0
    357     SkPDFUtils::AppendScalar(p2, code);   // x y y p1 x*p0 p2
    358     code->writeText(" add "               // x y y*p1 x*p0+p2
    359                     "add "                // x y y*p1+x*p0+p2
    360                     "3 1 roll "           // y*p1+x*p0+p2 x y
    361                     "2 index "            // z x y y*p1+x*p0+p2
    362                     "div "                // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2)
    363                     "3 1 roll "           // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x
    364                     "exch "               // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2
    365                     "div "                // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2)
    366                     "exch\n");            // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2)
    367 }
    368 
    369 static void linearCode(const SkShader::GradientInfo& info,
    370                        const SkMatrix& perspectiveRemover,
    371                        SkDynamicMemoryWStream* function) {
    372     function->writeText("{");
    373 
    374     apply_perspective_to_coordinates(perspectiveRemover, function);
    375 
    376     function->writeText("pop\n");  // Just ditch the y value.
    377     tileModeCode(info.fTileMode, function);
    378     gradient_function_code(info, function);
    379     function->writeText("}");
    380 }
    381 
    382 static void radialCode(const SkShader::GradientInfo& info,
    383                        const SkMatrix& perspectiveRemover,
    384                        SkDynamicMemoryWStream* function) {
    385     function->writeText("{");
    386 
    387     apply_perspective_to_coordinates(perspectiveRemover, function);
    388 
    389     // Find the distance from the origin.
    390     function->writeText("dup "      // x y y
    391                     "mul "      // x y^2
    392                     "exch "     // y^2 x
    393                     "dup "      // y^2 x x
    394                     "mul "      // y^2 x^2
    395                     "add "      // y^2+x^2
    396                     "sqrt\n");  // sqrt(y^2+x^2)
    397 
    398     tileModeCode(info.fTileMode, function);
    399     gradient_function_code(info, function);
    400     function->writeText("}");
    401 }
    402 
    403 /* Conical gradient shader, based on the Canvas spec for radial gradients
    404    See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
    405  */
    406 static void twoPointConicalCode(const SkShader::GradientInfo& info,
    407                                 const SkMatrix& perspectiveRemover,
    408                                 SkDynamicMemoryWStream* function) {
    409     SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
    410     SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
    411     SkScalar r0 = info.fRadius[0];
    412     SkScalar dr = info.fRadius[1] - info.fRadius[0];
    413     SkScalar a = dx * dx + dy * dy - dr * dr;
    414 
    415     // First compute t, if the pixel falls outside the cone, then we'll end
    416     // with 'false' on the stack, otherwise we'll push 'true' with t below it
    417 
    418     // We start with a stack of (x y), copy it and then consume one copy in
    419     // order to calculate b and the other to calculate c.
    420     function->writeText("{");
    421 
    422     apply_perspective_to_coordinates(perspectiveRemover, function);
    423 
    424     function->writeText("2 copy ");
    425 
    426     // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
    427     SkPDFUtils::AppendScalar(dy, function);
    428     function->writeText(" mul exch ");
    429     SkPDFUtils::AppendScalar(dx, function);
    430     function->writeText(" mul add ");
    431     SkPDFUtils::AppendScalar(r0 * dr, function);
    432     function->writeText(" add -2 mul dup dup mul\n");
    433 
    434     // c = x^2 + y^2 + radius0^2
    435     function->writeText("4 2 roll dup mul exch dup mul add ");
    436     SkPDFUtils::AppendScalar(r0 * r0, function);
    437     function->writeText(" sub dup 4 1 roll\n");
    438 
    439     // Contents of the stack at this point: c, b, b^2, c
    440 
    441     // if a = 0, then we collapse to a simpler linear case
    442     if (a == 0) {
    443 
    444         // t = -c/b
    445         function->writeText("pop pop div neg dup ");
    446 
    447         // compute radius(t)
    448         SkPDFUtils::AppendScalar(dr, function);
    449         function->writeText(" mul ");
    450         SkPDFUtils::AppendScalar(r0, function);
    451         function->writeText(" add\n");
    452 
    453         // if r(t) < 0, then it's outside the cone
    454         function->writeText("0 lt {pop false} {true} ifelse\n");
    455 
    456     } else {
    457 
    458         // quadratic case: the Canvas spec wants the largest
    459         // root t for which radius(t) > 0
    460 
    461         // compute the discriminant (b^2 - 4ac)
    462         SkPDFUtils::AppendScalar(a * 4, function);
    463         function->writeText(" mul sub dup\n");
    464 
    465         // if d >= 0, proceed
    466         function->writeText("0 ge {\n");
    467 
    468         // an intermediate value we'll use to compute the roots:
    469         // q = -0.5 * (b +/- sqrt(d))
    470         function->writeText("sqrt exch dup 0 lt {exch -1 mul} if");
    471         function->writeText(" add -0.5 mul dup\n");
    472 
    473         // first root = q / a
    474         SkPDFUtils::AppendScalar(a, function);
    475         function->writeText(" div\n");
    476 
    477         // second root = c / q
    478         function->writeText("3 1 roll div\n");
    479 
    480         // put the larger root on top of the stack
    481         function->writeText("2 copy gt {exch} if\n");
    482 
    483         // compute radius(t) for larger root
    484         function->writeText("dup ");
    485         SkPDFUtils::AppendScalar(dr, function);
    486         function->writeText(" mul ");
    487         SkPDFUtils::AppendScalar(r0, function);
    488         function->writeText(" add\n");
    489 
    490         // if r(t) > 0, we have our t, pop off the smaller root and we're done
    491         function->writeText(" 0 gt {exch pop true}\n");
    492 
    493         // otherwise, throw out the larger one and try the smaller root
    494         function->writeText("{pop dup\n");
    495         SkPDFUtils::AppendScalar(dr, function);
    496         function->writeText(" mul ");
    497         SkPDFUtils::AppendScalar(r0, function);
    498         function->writeText(" add\n");
    499 
    500         // if r(t) < 0, push false, otherwise the smaller root is our t
    501         function->writeText("0 le {pop false} {true} ifelse\n");
    502         function->writeText("} ifelse\n");
    503 
    504         // d < 0, clear the stack and push false
    505         function->writeText("} {pop pop pop false} ifelse\n");
    506     }
    507 
    508     // if the pixel is in the cone, proceed to compute a color
    509     function->writeText("{");
    510     tileModeCode(info.fTileMode, function);
    511     gradient_function_code(info, function);
    512 
    513     // otherwise, just write black
    514     function->writeText("} {0 0 0} ifelse }");
    515 }
    516 
    517 static void sweepCode(const SkShader::GradientInfo& info,
    518                           const SkMatrix& perspectiveRemover,
    519                           SkDynamicMemoryWStream* function) {
    520     function->writeText("{exch atan 360 div\n");
    521     tileModeCode(info.fTileMode, function);
    522     gradient_function_code(info, function);
    523     function->writeText("}");
    524 }
    525 
    526 
    527 // catch cases where the inner just touches the outer circle
    528 // and make the inner circle just inside the outer one to match raster
    529 static void FixUpRadius(const SkPoint& p1, SkScalar& r1, const SkPoint& p2, SkScalar& r2) {
    530     // detect touching circles
    531     SkScalar distance = SkPoint::Distance(p1, p2);
    532     SkScalar subtractRadii = fabs(r1 - r2);
    533     if (fabs(distance - subtractRadii) < 0.002f) {
    534         if (r1 > r2) {
    535             r1 += 0.002f;
    536         } else {
    537             r2 += 0.002f;
    538         }
    539     }
    540 }
    541 
    542 // Finds affine and persp such that in = affine * persp.
    543 // but it returns the inverse of perspective matrix.
    544 static bool split_perspective(const SkMatrix in, SkMatrix* affine,
    545                               SkMatrix* perspectiveInverse) {
    546     const SkScalar p2 = in[SkMatrix::kMPersp2];
    547 
    548     if (SkScalarNearlyZero(p2)) {
    549         return false;
    550     }
    551 
    552     const SkScalar zero = SkIntToScalar(0);
    553     const SkScalar one = SkIntToScalar(1);
    554 
    555     const SkScalar sx = in[SkMatrix::kMScaleX];
    556     const SkScalar kx = in[SkMatrix::kMSkewX];
    557     const SkScalar tx = in[SkMatrix::kMTransX];
    558     const SkScalar ky = in[SkMatrix::kMSkewY];
    559     const SkScalar sy = in[SkMatrix::kMScaleY];
    560     const SkScalar ty = in[SkMatrix::kMTransY];
    561     const SkScalar p0 = in[SkMatrix::kMPersp0];
    562     const SkScalar p1 = in[SkMatrix::kMPersp1];
    563 
    564     // Perspective matrix would be:
    565     // 1  0  0
    566     // 0  1  0
    567     // p0 p1 p2
    568     // But we need the inverse of persp.
    569     perspectiveInverse->setAll(one,          zero,       zero,
    570                                zero,         one,        zero,
    571                                -p0/p2,     -p1/p2,     1/p2);
    572 
    573     affine->setAll(sx - p0 * tx / p2,       kx - p1 * tx / p2,      tx / p2,
    574                    ky - p0 * ty / p2,       sy - p1 * ty / p2,      ty / p2,
    575                    zero,                    zero,                   one);
    576 
    577     return true;
    578 }
    579 
    580 static sk_sp<SkPDFArray> make_range_object() {
    581     auto range = sk_make_sp<SkPDFArray>();
    582     range->reserve(6);
    583     range->appendInt(0);
    584     range->appendInt(1);
    585     range->appendInt(0);
    586     range->appendInt(1);
    587     range->appendInt(0);
    588     range->appendInt(1);
    589     return range;
    590 }
    591 
    592 static sk_sp<SkPDFStream> make_ps_function(
    593         std::unique_ptr<SkStreamAsset> psCode,
    594         sk_sp<SkPDFArray> domain,
    595         sk_sp<SkPDFObject> range) {
    596     auto result = sk_make_sp<SkPDFStream>(std::move(psCode));
    597     result->dict()->insertInt("FunctionType", 4);
    598     result->dict()->insertObject("Domain", std::move(domain));
    599     result->dict()->insertObject("Range", std::move(range));
    600     return result;
    601 }
    602 
    603 
    604 static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
    605                                              const SkPDFGradientShader::Key& state) {
    606     SkPoint transformPoints[2];
    607     const SkShader::GradientInfo& info = state.fInfo;
    608     SkMatrix finalMatrix = state.fCanvasTransform;
    609     finalMatrix.preConcat(state.fShaderTransform);
    610 
    611     bool doStitchFunctions = (state.fType == SkShader::kLinear_GradientType ||
    612                               state.fType == SkShader::kRadial_GradientType ||
    613                               state.fType == SkShader::kConical_GradientType) &&
    614                              info.fTileMode == SkShader::kClamp_TileMode &&
    615                              !finalMatrix.hasPerspective();
    616 
    617     auto domain = sk_make_sp<SkPDFArray>();
    618 
    619     int32_t shadingType = 1;
    620     auto pdfShader = sk_make_sp<SkPDFDict>();
    621     // The two point radial gradient further references
    622     // state.fInfo
    623     // in translating from x, y coordinates to the t parameter. So, we have
    624     // to transform the points and radii according to the calculated matrix.
    625     if (doStitchFunctions) {
    626         pdfShader->insertObject("Function", gradientStitchCode(info));
    627         shadingType = (state.fType == SkShader::kLinear_GradientType) ? 2 : 3;
    628 
    629         auto extend = sk_make_sp<SkPDFArray>();
    630         extend->reserve(2);
    631         extend->appendBool(true);
    632         extend->appendBool(true);
    633         pdfShader->insertObject("Extend", std::move(extend));
    634 
    635         auto coords = sk_make_sp<SkPDFArray>();
    636         if (state.fType == SkShader::kConical_GradientType) {
    637             coords->reserve(6);
    638             SkScalar r1 = info.fRadius[0];
    639             SkScalar r2 = info.fRadius[1];
    640             SkPoint pt1 = info.fPoint[0];
    641             SkPoint pt2 = info.fPoint[1];
    642             FixUpRadius(pt1, r1, pt2, r2);
    643 
    644             coords->appendScalar(pt1.fX);
    645             coords->appendScalar(pt1.fY);
    646             coords->appendScalar(r1);
    647 
    648             coords->appendScalar(pt2.fX);
    649             coords->appendScalar(pt2.fY);
    650             coords->appendScalar(r2);
    651         } else if (state.fType == SkShader::kRadial_GradientType) {
    652             coords->reserve(6);
    653             const SkPoint& pt1 = info.fPoint[0];
    654 
    655             coords->appendScalar(pt1.fX);
    656             coords->appendScalar(pt1.fY);
    657             coords->appendScalar(0);
    658 
    659             coords->appendScalar(pt1.fX);
    660             coords->appendScalar(pt1.fY);
    661             coords->appendScalar(info.fRadius[0]);
    662         } else {
    663             coords->reserve(4);
    664             const SkPoint& pt1 = info.fPoint[0];
    665             const SkPoint& pt2 = info.fPoint[1];
    666 
    667             coords->appendScalar(pt1.fX);
    668             coords->appendScalar(pt1.fY);
    669 
    670             coords->appendScalar(pt2.fX);
    671             coords->appendScalar(pt2.fY);
    672         }
    673 
    674         pdfShader->insertObject("Coords", std::move(coords));
    675     } else {
    676         // Depending on the type of the gradient, we want to transform the
    677         // coordinate space in different ways.
    678         transformPoints[0] = info.fPoint[0];
    679         transformPoints[1] = info.fPoint[1];
    680         switch (state.fType) {
    681             case SkShader::kLinear_GradientType:
    682                 break;
    683             case SkShader::kRadial_GradientType:
    684                 transformPoints[1] = transformPoints[0];
    685                 transformPoints[1].fX += info.fRadius[0];
    686                 break;
    687             case SkShader::kConical_GradientType: {
    688                 transformPoints[1] = transformPoints[0];
    689                 transformPoints[1].fX += SK_Scalar1;
    690                 break;
    691             }
    692             case SkShader::kSweep_GradientType:
    693                 transformPoints[1] = transformPoints[0];
    694                 transformPoints[1].fX += SK_Scalar1;
    695                 break;
    696             case SkShader::kColor_GradientType:
    697             case SkShader::kNone_GradientType:
    698             default:
    699                 return nullptr;
    700         }
    701 
    702         // Move any scaling (assuming a unit gradient) or translation
    703         // (and rotation for linear gradient), of the final gradient from
    704         // info.fPoints to the matrix (updating bbox appropriately).  Now
    705         // the gradient can be drawn on on the unit segment.
    706         SkMatrix mapperMatrix;
    707         unit_to_points_matrix(transformPoints, &mapperMatrix);
    708 
    709         finalMatrix.preConcat(mapperMatrix);
    710 
    711         // Preserves as much as posible in the final matrix, and only removes
    712         // the perspective. The inverse of the perspective is stored in
    713         // perspectiveInverseOnly matrix and has 3 useful numbers
    714         // (p0, p1, p2), while everything else is either 0 or 1.
    715         // In this way the shader will handle it eficiently, with minimal code.
    716         SkMatrix perspectiveInverseOnly = SkMatrix::I();
    717         if (finalMatrix.hasPerspective()) {
    718             if (!split_perspective(finalMatrix,
    719                                    &finalMatrix, &perspectiveInverseOnly)) {
    720                 return nullptr;
    721             }
    722         }
    723 
    724         SkRect bbox;
    725         bbox.set(state.fBBox);
    726         if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &bbox)) {
    727             return nullptr;
    728         }
    729         domain->reserve(4);
    730         domain->appendScalar(bbox.fLeft);
    731         domain->appendScalar(bbox.fRight);
    732         domain->appendScalar(bbox.fTop);
    733         domain->appendScalar(bbox.fBottom);
    734 
    735         SkDynamicMemoryWStream functionCode;
    736 
    737         SkShader::GradientInfo infoCopy = info;
    738 
    739         if (state.fType == SkShader::kConical_GradientType) {
    740             SkMatrix inverseMapperMatrix;
    741             if (!mapperMatrix.invert(&inverseMapperMatrix)) {
    742                 return nullptr;
    743             }
    744             inverseMapperMatrix.mapPoints(infoCopy.fPoint, 2);
    745             infoCopy.fRadius[0] = inverseMapperMatrix.mapRadius(info.fRadius[0]);
    746             infoCopy.fRadius[1] = inverseMapperMatrix.mapRadius(info.fRadius[1]);
    747         }
    748         switch (state.fType) {
    749             case SkShader::kLinear_GradientType:
    750                 linearCode(infoCopy, perspectiveInverseOnly, &functionCode);
    751                 break;
    752             case SkShader::kRadial_GradientType:
    753                 radialCode(infoCopy, perspectiveInverseOnly, &functionCode);
    754                 break;
    755             case SkShader::kConical_GradientType:
    756                 twoPointConicalCode(infoCopy, perspectiveInverseOnly, &functionCode);
    757                 break;
    758             case SkShader::kSweep_GradientType:
    759                 sweepCode(infoCopy, perspectiveInverseOnly, &functionCode);
    760                 break;
    761             default:
    762                 SkASSERT(false);
    763         }
    764         pdfShader->insertObject("Domain", domain);
    765 
    766         sk_sp<SkPDFArray>& rangeObject = canon->fRangeObject;
    767         if (!rangeObject) {
    768             rangeObject = make_range_object();
    769         }
    770         pdfShader->insertObjRef("Function",
    771                                 make_ps_function(functionCode.detachAsStream(), std::move(domain),
    772                                                  rangeObject));
    773     }
    774 
    775     pdfShader->insertInt("ShadingType", shadingType);
    776     pdfShader->insertName("ColorSpace", "DeviceRGB");
    777 
    778     auto pdfFunctionShader = sk_make_sp<SkPDFDict>("Pattern");
    779     pdfFunctionShader->insertInt("PatternType", 2);
    780     pdfFunctionShader->insertObject("Matrix", SkPDFUtils::MatrixToArray(finalMatrix));
    781     pdfFunctionShader->insertObject("Shading", std::move(pdfShader));
    782 
    783     return pdfFunctionShader;
    784 }
    785 
    786 static sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
    787                                           SkPDFGradientShader::Key key,
    788                                           bool keyHasAlpha);
    789 
    790 static sk_sp<SkPDFDict> get_gradient_resource_dict(SkPDFObject* functionShader,
    791                                                    SkPDFObject* gState) {
    792     SkTDArray<SkPDFObject*> patterns;
    793     if (functionShader) {
    794         patterns.push(functionShader);
    795     }
    796     SkTDArray<SkPDFObject*> graphicStates;
    797     if (gState) {
    798         graphicStates.push(gState);
    799     }
    800     return SkPDFResourceDict::Make(&graphicStates, &patterns, nullptr, nullptr);
    801 }
    802 
    803 // Creates a content stream which fills the pattern P0 across bounds.
    804 // @param gsIndex A graphics state resource index to apply, or <0 if no
    805 // graphics state to apply.
    806 static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex, SkRect& bounds) {
    807     SkDynamicMemoryWStream content;
    808     if (gsIndex >= 0) {
    809         SkPDFUtils::ApplyGraphicState(gsIndex, &content);
    810     }
    811     SkPDFUtils::ApplyPattern(0, &content);
    812     SkPDFUtils::AppendRectangle(bounds, &content);
    813     SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType, &content);
    814     return content.detachAsStream();
    815 }
    816 
    817 static bool gradient_has_alpha(const SkPDFGradientShader::Key& key) {
    818     SkASSERT(key.fType != SkShader::kNone_GradientType);
    819     for (int i = 0; i < key.fInfo.fColorCount; i++) {
    820         if ((SkAlpha)SkColorGetA(key.fInfo.fColors[i]) != SK_AlphaOPAQUE) {
    821             return true;
    822         }
    823     }
    824     return false;
    825 }
    826 
    827 // warning: does not set fHash on new key.  (Both callers need to change fields.)
    828 static SkPDFGradientShader::Key clone_key(const SkPDFGradientShader::Key& k) {
    829     SkPDFGradientShader::Key clone = {
    830         k.fType,
    831         k.fInfo,  // change pointers later.
    832         std::unique_ptr<SkColor[]>(new SkColor[k.fInfo.fColorCount]),
    833         std::unique_ptr<SkScalar[]>(new SkScalar[k.fInfo.fColorCount]),
    834         k.fCanvasTransform,
    835         k.fShaderTransform,
    836         k.fBBox, 0};
    837     clone.fInfo.fColors = clone.fColors.get();
    838     clone.fInfo.fColorOffsets = clone.fStops.get();
    839     for (int i = 0; i < clone.fInfo.fColorCount; i++) {
    840         clone.fInfo.fColorOffsets[i] = k.fInfo.fColorOffsets[i];
    841         clone.fInfo.fColors[i] = k.fInfo.fColors[i];
    842     }
    843     return clone;
    844 }
    845 
    846 static sk_sp<SkPDFObject> create_smask_graphic_state(SkPDFDocument* doc,
    847                                                      const SkPDFGradientShader::Key& state) {
    848     SkASSERT(state.fType != SkShader::kNone_GradientType);
    849     SkPDFGradientShader::Key luminosityState = clone_key(state);
    850     for (int i = 0; i < luminosityState.fInfo.fColorCount; i++) {
    851         SkAlpha alpha = SkColorGetA(luminosityState.fInfo.fColors[i]);
    852         luminosityState.fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha);
    853     }
    854     luminosityState.fHash = hash(luminosityState);
    855 
    856     SkASSERT(!gradient_has_alpha(luminosityState));
    857     sk_sp<SkPDFObject> luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
    858     sk_sp<SkPDFDict> resources = get_gradient_resource_dict(luminosityShader.get(), nullptr);
    859     SkRect bbox = SkRect::Make(state.fBBox);
    860     sk_sp<SkPDFObject> alphaMask = SkPDFMakeFormXObject(create_pattern_fill_content(-1, bbox),
    861                                                         SkPDFUtils::RectToArray(bbox),
    862                                                         std::move(resources),
    863                                                         SkMatrix::I(),
    864                                                         "DeviceRGB");
    865     return SkPDFGraphicState::GetSMaskGraphicState(
    866             std::move(alphaMask), false,
    867             SkPDFGraphicState::kLuminosity_SMaskMode, doc->canon());
    868 }
    869 
    870 static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
    871                                                      const SkPDFGradientShader::Key& state) {
    872     SkASSERT(state.fType != SkShader::kNone_GradientType);
    873     SkPDFGradientShader::Key opaqueState = clone_key(state);
    874     for (int i = 0; i < opaqueState.fInfo.fColorCount; i++) {
    875         opaqueState.fInfo.fColors[i] = SkColorSetA(opaqueState.fInfo.fColors[i], SK_AlphaOPAQUE);
    876     }
    877     opaqueState.fHash = hash(opaqueState);
    878 
    879     SkASSERT(!gradient_has_alpha(opaqueState));
    880     SkRect bbox = SkRect::Make(state.fBBox);
    881     sk_sp<SkPDFObject> colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
    882     if (!colorShader) {
    883         return nullptr;
    884     }
    885 
    886     // Create resource dict with alpha graphics state as G0 and
    887     // pattern shader as P0, then write content stream.
    888     sk_sp<SkPDFObject> alphaGs = create_smask_graphic_state(doc, state);
    889 
    890     sk_sp<SkPDFDict> resourceDict =
    891             get_gradient_resource_dict(colorShader.get(), alphaGs.get());
    892 
    893     std::unique_ptr<SkStreamAsset> colorStream(create_pattern_fill_content(0, bbox));
    894     auto alphaFunctionShader = sk_make_sp<SkPDFStream>(std::move(colorStream));
    895 
    896     SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader->dict(), bbox,
    897                                  std::move(resourceDict), SkMatrix::I());
    898     return alphaFunctionShader;
    899 }
    900 
    901 static SkPDFGradientShader::Key make_key(const SkShader* shader,
    902                                          const SkMatrix& canvasTransform,
    903                                          const SkIRect& bbox) {
    904     SkPDFGradientShader::Key key = {
    905          SkShader::kNone_GradientType,
    906          {0, nullptr, nullptr, {{0, 0}, {0, 0}}, {0, 0}, SkShader::kClamp_TileMode, 0},
    907          nullptr,
    908          nullptr,
    909          canvasTransform,
    910          SkPDFUtils::GetShaderLocalMatrix(shader),
    911          bbox, 0};
    912     key.fType = shader->asAGradient(&key.fInfo);
    913     SkASSERT(SkShader::kNone_GradientType != key.fType);
    914     SkASSERT(key.fInfo.fColorCount > 0);
    915     key.fColors.reset(new SkColor[key.fInfo.fColorCount]);
    916     key.fStops.reset(new SkScalar[key.fInfo.fColorCount]);
    917     key.fInfo.fColors = key.fColors.get();
    918     key.fInfo.fColorOffsets = key.fStops.get();
    919     (void)shader->asAGradient(&key.fInfo);
    920     key.fHash = hash(key);
    921     return key;
    922 }
    923 
    924 static sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
    925                                           SkPDFGradientShader::Key key,
    926                                           bool keyHasAlpha) {
    927     SkASSERT(gradient_has_alpha(key) == keyHasAlpha);
    928     SkPDFCanon* canon = doc->canon();
    929     if (sk_sp<SkPDFObject>* ptr = canon->fGradientPatternMap.find(key)) {
    930         return *ptr;
    931     }
    932     sk_sp<SkPDFObject> pdfShader;
    933     if (keyHasAlpha) {
    934         pdfShader = make_alpha_function_shader(doc, key);
    935     } else {
    936         pdfShader = make_function_shader(canon, key);
    937     }
    938     canon->fGradientPatternMap.set(std::move(key), pdfShader);
    939     return pdfShader;
    940 }
    941 
    942 sk_sp<SkPDFObject> SkPDFGradientShader::Make(SkPDFDocument* doc,
    943                                              SkShader* shader,
    944                                              const SkMatrix& canvasTransform,
    945                                              const SkIRect& bbox) {
    946     SkASSERT(shader);
    947     SkASSERT(SkShader::kNone_GradientType != shader->asAGradient(nullptr));
    948     SkPDFGradientShader::Key key = make_key(shader, canvasTransform, bbox);
    949     bool alpha = gradient_has_alpha(key);
    950     return find_pdf_shader(doc, std::move(key), alpha);
    951 }
    952