Home | History | Annotate | Download | only in pdf
      1 /*
      2  * Copyright 2011 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 
      9 #include "SkPDFShader.h"
     10 
     11 #include "SkData.h"
     12 #include "SkPDFCanon.h"
     13 #include "SkPDFDevice.h"
     14 #include "SkPDFDocument.h"
     15 #include "SkPDFFormXObject.h"
     16 #include "SkPDFGraphicState.h"
     17 #include "SkPDFResourceDict.h"
     18 #include "SkPDFUtils.h"
     19 #include "SkScalar.h"
     20 #include "SkStream.h"
     21 #include "SkTemplates.h"
     22 
     23 static bool inverse_transform_bbox(const SkMatrix& matrix, SkRect* bbox) {
     24     SkMatrix inverse;
     25     if (!matrix.invert(&inverse)) {
     26         return false;
     27     }
     28     inverse.mapRect(bbox);
     29     return true;
     30 }
     31 
     32 static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) {
     33     SkVector    vec = pts[1] - pts[0];
     34     SkScalar    mag = vec.length();
     35     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
     36 
     37     vec.scale(inv);
     38     matrix->setSinCos(vec.fY, vec.fX);
     39     matrix->preScale(mag, mag);
     40     matrix->postTranslate(pts[0].fX, pts[0].fY);
     41 }
     42 
     43 static const int kColorComponents = 3;
     44 typedef uint8_t ColorTuple[kColorComponents];
     45 
     46 /* Assumes t + startOffset is on the stack and does a linear interpolation on t
     47    between startOffset and endOffset from prevColor to curColor (for each color
     48    component), leaving the result in component order on the stack. It assumes
     49    there are always 3 components per color.
     50    @param range                  endOffset - startOffset
     51    @param curColor[components]   The current color components.
     52    @param prevColor[components]  The previous color components.
     53    @param result                 The result ps function.
     54  */
     55 static void interpolateColorCode(SkScalar range, const ColorTuple& curColor,
     56                                  const ColorTuple& prevColor,
     57                                  SkDynamicMemoryWStream* result) {
     58     SkASSERT(range != SkIntToScalar(0));
     59 
     60     // Figure out how to scale each color component.
     61     SkScalar multiplier[kColorComponents];
     62     for (int i = 0; i < kColorComponents; i++) {
     63         static const SkScalar kColorScale = SkScalarInvert(255);
     64         multiplier[i] = kColorScale * (curColor[i] - prevColor[i]) / range;
     65     }
     66 
     67     // Calculate when we no longer need to keep a copy of the input parameter t.
     68     // If the last component to use t is i, then dupInput[0..i - 1] = true
     69     // and dupInput[i .. components] = false.
     70     bool dupInput[kColorComponents];
     71     dupInput[kColorComponents - 1] = false;
     72     for (int i = kColorComponents - 2; i >= 0; i--) {
     73         dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
     74     }
     75 
     76     if (!dupInput[0] && multiplier[0] == 0) {
     77         result->writeText("pop ");
     78     }
     79 
     80     for (int i = 0; i < kColorComponents; i++) {
     81         // If the next components needs t and this component will consume a
     82         // copy, make another copy.
     83         if (dupInput[i] && multiplier[i] != 0) {
     84             result->writeText("dup ");
     85         }
     86 
     87         if (multiplier[i] == 0) {
     88             SkPDFUtils::AppendColorComponent(prevColor[i], result);
     89             result->writeText(" ");
     90         } else {
     91             if (multiplier[i] != 1) {
     92                 SkPDFUtils::AppendScalar(multiplier[i], result);
     93                 result->writeText(" mul ");
     94             }
     95             if (prevColor[i] != 0) {
     96                 SkPDFUtils::AppendColorComponent(prevColor[i], result);
     97                 result->writeText(" add ");
     98             }
     99         }
    100 
    101         if (dupInput[i]) {
    102             result->writeText("exch\n");
    103         }
    104     }
    105 }
    106 
    107 /* Generate Type 4 function code to map t=[0,1) to the passed gradient,
    108    clamping at the edges of the range.  The generated code will be of the form:
    109        if (t < 0) {
    110            return colorData[0][r,g,b];
    111        } else {
    112            if (t < info.fColorOffsets[1]) {
    113                return linearinterpolation(colorData[0][r,g,b],
    114                                           colorData[1][r,g,b]);
    115            } else {
    116                if (t < info.fColorOffsets[2]) {
    117                    return linearinterpolation(colorData[1][r,g,b],
    118                                               colorData[2][r,g,b]);
    119                } else {
    120 
    121                 ...    } else {
    122                            return colorData[info.fColorCount - 1][r,g,b];
    123                        }
    124                 ...
    125            }
    126        }
    127  */
    128 static void gradientFunctionCode(const SkShader::GradientInfo& info,
    129                                  SkDynamicMemoryWStream* result) {
    130     /* We want to linearly interpolate from the previous color to the next.
    131        Scale the colors from 0..255 to 0..1 and determine the multipliers
    132        for interpolation.
    133        C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
    134      */
    135 
    136     SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
    137     ColorTuple *colorData = colorDataAlloc.get();
    138     for (int i = 0; i < info.fColorCount; i++) {
    139         colorData[i][0] = SkColorGetR(info.fColors[i]);
    140         colorData[i][1] = SkColorGetG(info.fColors[i]);
    141         colorData[i][2] = SkColorGetB(info.fColors[i]);
    142     }
    143 
    144     // Clamp the initial color.
    145     result->writeText("dup 0 le {pop ");
    146     SkPDFUtils::AppendColorComponent(colorData[0][0], result);
    147     result->writeText(" ");
    148     SkPDFUtils::AppendColorComponent(colorData[0][1], result);
    149     result->writeText(" ");
    150     SkPDFUtils::AppendColorComponent(colorData[0][2], result);
    151     result->writeText(" }\n");
    152 
    153     // The gradient colors.
    154     int gradients = 0;
    155     for (int i = 1 ; i < info.fColorCount; i++) {
    156         if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) {
    157             continue;
    158         }
    159         gradients++;
    160 
    161         result->writeText("{dup ");
    162         SkPDFUtils::AppendScalar(info.fColorOffsets[i], result);
    163         result->writeText(" le {");
    164         if (info.fColorOffsets[i - 1] != 0) {
    165             SkPDFUtils::AppendScalar(info.fColorOffsets[i - 1], result);
    166             result->writeText(" sub\n");
    167         }
    168 
    169         interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1],
    170                              colorData[i], colorData[i - 1], result);
    171         result->writeText("}\n");
    172     }
    173 
    174     // Clamp the final color.
    175     result->writeText("{pop ");
    176     SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][0], result);
    177     result->writeText(" ");
    178     SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][1], result);
    179     result->writeText(" ");
    180     SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][2], result);
    181 
    182     for (int i = 0 ; i < gradients + 1; i++) {
    183         result->writeText("} ifelse\n");
    184     }
    185 }
    186 
    187 static sk_sp<SkPDFDict> createInterpolationFunction(const ColorTuple& color1,
    188                                                     const ColorTuple& color2) {
    189     auto retval = sk_make_sp<SkPDFDict>();
    190 
    191     auto c0 = sk_make_sp<SkPDFArray>();
    192     c0->appendColorComponent(color1[0]);
    193     c0->appendColorComponent(color1[1]);
    194     c0->appendColorComponent(color1[2]);
    195     retval->insertObject("C0", std::move(c0));
    196 
    197     auto c1 = sk_make_sp<SkPDFArray>();
    198     c1->appendColorComponent(color2[0]);
    199     c1->appendColorComponent(color2[1]);
    200     c1->appendColorComponent(color2[2]);
    201     retval->insertObject("C1", std::move(c1));
    202 
    203     auto domain = sk_make_sp<SkPDFArray>();
    204     domain->appendScalar(0);
    205     domain->appendScalar(1.0f);
    206     retval->insertObject("Domain", std::move(domain));
    207 
    208     retval->insertInt("FunctionType", 2);
    209     retval->insertScalar("N", 1.0f);
    210 
    211     return retval;
    212 }
    213 
    214 static sk_sp<SkPDFDict> gradientStitchCode(const SkShader::GradientInfo& info) {
    215     auto retval = sk_make_sp<SkPDFDict>();
    216 
    217     // normalize color stops
    218     int colorCount = info.fColorCount;
    219     SkTDArray<SkColor>    colors(info.fColors, colorCount);
    220     SkTDArray<SkScalar>   colorOffsets(info.fColorOffsets, colorCount);
    221 
    222     int i = 1;
    223     while (i < colorCount - 1) {
    224         // ensure stops are in order
    225         if (colorOffsets[i - 1] > colorOffsets[i]) {
    226             colorOffsets[i] = colorOffsets[i - 1];
    227         }
    228 
    229         // remove points that are between 2 coincident points
    230         if ((colorOffsets[i - 1] == colorOffsets[i]) && (colorOffsets[i] == colorOffsets[i + 1])) {
    231             colorCount -= 1;
    232             colors.remove(i);
    233             colorOffsets.remove(i);
    234         } else {
    235             i++;
    236         }
    237     }
    238     // find coincident points and slightly move them over
    239     for (i = 1; i < colorCount - 1; i++) {
    240         if (colorOffsets[i - 1] == colorOffsets[i]) {
    241             colorOffsets[i] += 0.00001f;
    242         }
    243     }
    244     // check if last 2 stops coincide
    245     if (colorOffsets[i - 1] == colorOffsets[i]) {
    246         colorOffsets[i - 1] -= 0.00001f;
    247     }
    248 
    249     SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(colorCount);
    250     ColorTuple *colorData = colorDataAlloc.get();
    251     for (int i = 0; i < colorCount; i++) {
    252         colorData[i][0] = SkColorGetR(colors[i]);
    253         colorData[i][1] = SkColorGetG(colors[i]);
    254         colorData[i][2] = SkColorGetB(colors[i]);
    255     }
    256 
    257     // no need for a stitch function if there are only 2 stops.
    258     if (colorCount == 2)
    259         return createInterpolationFunction(colorData[0], colorData[1]);
    260 
    261     auto encode = sk_make_sp<SkPDFArray>();
    262     auto bounds = sk_make_sp<SkPDFArray>();
    263     auto functions = sk_make_sp<SkPDFArray>();
    264 
    265     auto domain = sk_make_sp<SkPDFArray>();
    266     domain->appendScalar(0);
    267     domain->appendScalar(1.0f);
    268     retval->insertObject("Domain", std::move(domain));
    269     retval->insertInt("FunctionType", 3);
    270 
    271     for (int i = 1; i < colorCount; i++) {
    272         if (i > 1) {
    273             bounds->appendScalar(colorOffsets[i-1]);
    274         }
    275 
    276         encode->appendScalar(0);
    277         encode->appendScalar(1.0f);
    278 
    279         functions->appendObject(createInterpolationFunction(colorData[i-1], colorData[i]));
    280     }
    281 
    282     retval->insertObject("Encode", std::move(encode));
    283     retval->insertObject("Bounds", std::move(bounds));
    284     retval->insertObject("Functions", std::move(functions));
    285 
    286     return retval;
    287 }
    288 
    289 /* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
    290 static void tileModeCode(SkShader::TileMode mode,
    291                          SkDynamicMemoryWStream* result) {
    292     if (mode == SkShader::kRepeat_TileMode) {
    293         result->writeText("dup truncate sub\n");  // Get the fractional part.
    294         result->writeText("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
    295         return;
    296     }
    297 
    298     if (mode == SkShader::kMirror_TileMode) {
    299         // Map t mod 2 into [0, 1, 1, 0].
    300         //               Code                     Stack
    301         result->writeText("abs "                 // Map negative to positive.
    302                           "dup "                 // t.s t.s
    303                           "truncate "            // t.s t
    304                           "dup "                 // t.s t t
    305                           "cvi "                 // t.s t T
    306                           "2 mod "               // t.s t (i mod 2)
    307                           "1 eq "                // t.s t true|false
    308                           "3 1 roll "            // true|false t.s t
    309                           "sub "                 // true|false 0.s
    310                           "exch "                // 0.s true|false
    311                           "{1 exch sub} if\n");  // 1 - 0.s|0.s
    312     }
    313 }
    314 
    315 /**
    316  *  Returns PS function code that applies inverse perspective
    317  *  to a x, y point.
    318  *  The function assumes that the stack has at least two elements,
    319  *  and that the top 2 elements are numeric values.
    320  *  After executing this code on a PS stack, the last 2 elements are updated
    321  *  while the rest of the stack is preserved intact.
    322  *  inversePerspectiveMatrix is the inverse perspective matrix.
    323  */
    324 static void apply_perspective_to_coordinates(
    325         const SkMatrix& inversePerspectiveMatrix,
    326         SkDynamicMemoryWStream* code) {
    327     if (!inversePerspectiveMatrix.hasPerspective()) {
    328         return;
    329     }
    330 
    331     // Perspective matrix should be:
    332     // 1   0  0
    333     // 0   1  0
    334     // p0 p1 p2
    335 
    336     const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0];
    337     const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1];
    338     const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2];
    339 
    340     // y = y / (p2 + p0 x + p1 y)
    341     // x = x / (p2 + p0 x + p1 y)
    342 
    343     // Input on stack: x y
    344     code->writeText(" dup ");             // x y y
    345     SkPDFUtils::AppendScalar(p1, code);   // x y y p1
    346     code->writeText(" mul "               // x y y*p1
    347                     " 2 index ");         // x y y*p1 x
    348     SkPDFUtils::AppendScalar(p0, code);   // x y y p1 x p0
    349     code->writeText(" mul ");             // x y y*p1 x*p0
    350     SkPDFUtils::AppendScalar(p2, code);   // x y y p1 x*p0 p2
    351     code->writeText(" add "               // x y y*p1 x*p0+p2
    352                     "add "                // x y y*p1+x*p0+p2
    353                     "3 1 roll "           // y*p1+x*p0+p2 x y
    354                     "2 index "            // z x y y*p1+x*p0+p2
    355                     "div "                // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2)
    356                     "3 1 roll "           // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x
    357                     "exch "               // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2
    358                     "div "                // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2)
    359                     "exch\n");            // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2)
    360 }
    361 
    362 static void linearCode(const SkShader::GradientInfo& info,
    363                        const SkMatrix& perspectiveRemover,
    364                        SkDynamicMemoryWStream* function) {
    365     function->writeText("{");
    366 
    367     apply_perspective_to_coordinates(perspectiveRemover, function);
    368 
    369     function->writeText("pop\n");  // Just ditch the y value.
    370     tileModeCode(info.fTileMode, function);
    371     gradientFunctionCode(info, function);
    372     function->writeText("}");
    373 }
    374 
    375 static void radialCode(const SkShader::GradientInfo& info,
    376                        const SkMatrix& perspectiveRemover,
    377                        SkDynamicMemoryWStream* function) {
    378     function->writeText("{");
    379 
    380     apply_perspective_to_coordinates(perspectiveRemover, function);
    381 
    382     // Find the distance from the origin.
    383     function->writeText("dup "      // x y y
    384                     "mul "      // x y^2
    385                     "exch "     // y^2 x
    386                     "dup "      // y^2 x x
    387                     "mul "      // y^2 x^2
    388                     "add "      // y^2+x^2
    389                     "sqrt\n");  // sqrt(y^2+x^2)
    390 
    391     tileModeCode(info.fTileMode, function);
    392     gradientFunctionCode(info, function);
    393     function->writeText("}");
    394 }
    395 
    396 /* Conical gradient shader, based on the Canvas spec for radial gradients
    397    See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
    398  */
    399 static void twoPointConicalCode(const SkShader::GradientInfo& info,
    400                                 const SkMatrix& perspectiveRemover,
    401                                 SkDynamicMemoryWStream* function) {
    402     SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
    403     SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
    404     SkScalar r0 = info.fRadius[0];
    405     SkScalar dr = info.fRadius[1] - info.fRadius[0];
    406     SkScalar a = dx * dx + dy * dy - dr * dr;
    407 
    408     // First compute t, if the pixel falls outside the cone, then we'll end
    409     // with 'false' on the stack, otherwise we'll push 'true' with t below it
    410 
    411     // We start with a stack of (x y), copy it and then consume one copy in
    412     // order to calculate b and the other to calculate c.
    413     function->writeText("{");
    414 
    415     apply_perspective_to_coordinates(perspectiveRemover, function);
    416 
    417     function->writeText("2 copy ");
    418 
    419     // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
    420     SkPDFUtils::AppendScalar(dy, function);
    421     function->writeText(" mul exch ");
    422     SkPDFUtils::AppendScalar(dx, function);
    423     function->writeText(" mul add ");
    424     SkPDFUtils::AppendScalar(r0 * dr, function);
    425     function->writeText(" add -2 mul dup dup mul\n");
    426 
    427     // c = x^2 + y^2 + radius0^2
    428     function->writeText("4 2 roll dup mul exch dup mul add ");
    429     SkPDFUtils::AppendScalar(r0 * r0, function);
    430     function->writeText(" sub dup 4 1 roll\n");
    431 
    432     // Contents of the stack at this point: c, b, b^2, c
    433 
    434     // if a = 0, then we collapse to a simpler linear case
    435     if (a == 0) {
    436 
    437         // t = -c/b
    438         function->writeText("pop pop div neg dup ");
    439 
    440         // compute radius(t)
    441         SkPDFUtils::AppendScalar(dr, function);
    442         function->writeText(" mul ");
    443         SkPDFUtils::AppendScalar(r0, function);
    444         function->writeText(" add\n");
    445 
    446         // if r(t) < 0, then it's outside the cone
    447         function->writeText("0 lt {pop false} {true} ifelse\n");
    448 
    449     } else {
    450 
    451         // quadratic case: the Canvas spec wants the largest
    452         // root t for which radius(t) > 0
    453 
    454         // compute the discriminant (b^2 - 4ac)
    455         SkPDFUtils::AppendScalar(a * 4, function);
    456         function->writeText(" mul sub dup\n");
    457 
    458         // if d >= 0, proceed
    459         function->writeText("0 ge {\n");
    460 
    461         // an intermediate value we'll use to compute the roots:
    462         // q = -0.5 * (b +/- sqrt(d))
    463         function->writeText("sqrt exch dup 0 lt {exch -1 mul} if");
    464         function->writeText(" add -0.5 mul dup\n");
    465 
    466         // first root = q / a
    467         SkPDFUtils::AppendScalar(a, function);
    468         function->writeText(" div\n");
    469 
    470         // second root = c / q
    471         function->writeText("3 1 roll div\n");
    472 
    473         // put the larger root on top of the stack
    474         function->writeText("2 copy gt {exch} if\n");
    475 
    476         // compute radius(t) for larger root
    477         function->writeText("dup ");
    478         SkPDFUtils::AppendScalar(dr, function);
    479         function->writeText(" mul ");
    480         SkPDFUtils::AppendScalar(r0, function);
    481         function->writeText(" add\n");
    482 
    483         // if r(t) > 0, we have our t, pop off the smaller root and we're done
    484         function->writeText(" 0 gt {exch pop true}\n");
    485 
    486         // otherwise, throw out the larger one and try the smaller root
    487         function->writeText("{pop dup\n");
    488         SkPDFUtils::AppendScalar(dr, function);
    489         function->writeText(" mul ");
    490         SkPDFUtils::AppendScalar(r0, function);
    491         function->writeText(" add\n");
    492 
    493         // if r(t) < 0, push false, otherwise the smaller root is our t
    494         function->writeText("0 le {pop false} {true} ifelse\n");
    495         function->writeText("} ifelse\n");
    496 
    497         // d < 0, clear the stack and push false
    498         function->writeText("} {pop pop pop false} ifelse\n");
    499     }
    500 
    501     // if the pixel is in the cone, proceed to compute a color
    502     function->writeText("{");
    503     tileModeCode(info.fTileMode, function);
    504     gradientFunctionCode(info, function);
    505 
    506     // otherwise, just write black
    507     function->writeText("} {0 0 0} ifelse }");
    508 }
    509 
    510 static void sweepCode(const SkShader::GradientInfo& info,
    511                           const SkMatrix& perspectiveRemover,
    512                           SkDynamicMemoryWStream* function) {
    513     function->writeText("{exch atan 360 div\n");
    514     tileModeCode(info.fTileMode, function);
    515     gradientFunctionCode(info, function);
    516     function->writeText("}");
    517 }
    518 
    519 static void drawBitmapMatrix(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& matrix) {
    520     SkAutoCanvasRestore acr(canvas, true);
    521     canvas->concat(matrix);
    522     canvas->drawBitmap(bm, 0, 0);
    523 }
    524 
    525 ////////////////////////////////////////////////////////////////////////////////
    526 
    527 static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
    528                                                      SkScalar dpi,
    529                                                      const SkPDFShader::State& state);
    530 static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
    531                                              const SkPDFShader::State& state);
    532 
    533 static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
    534                                             SkScalar dpi,
    535                                             const SkPDFShader::State& state,
    536                                             SkBitmap image);
    537 
    538 static sk_sp<SkPDFObject> get_pdf_shader_by_state(
    539         SkPDFDocument* doc,
    540         SkScalar dpi,
    541         SkPDFShader::State state,
    542         SkBitmap image) {
    543     SkPDFCanon* canon = doc->canon();
    544     if (state.fType == SkShader::kNone_GradientType && image.isNull()) {
    545         // TODO(vandebo) This drops SKComposeShader on the floor.  We could
    546         // handle compose shader by pulling things up to a layer, drawing with
    547         // the first shader, applying the xfer mode and drawing again with the
    548         // second shader, then applying the layer to the original drawing.
    549         return nullptr;
    550     } else if (state.fType == SkShader::kNone_GradientType) {
    551         sk_sp<SkPDFObject> shader = canon->findImageShader(state);
    552         if (!shader) {
    553             shader = make_image_shader(doc, dpi, state, std::move(image));
    554             canon->addImageShader(shader, std::move(state));
    555         }
    556         return shader;
    557     } else if (state.GradientHasAlpha()) {
    558         sk_sp<SkPDFObject> shader = canon->findAlphaShader(state);
    559         if (!shader) {
    560             shader = make_alpha_function_shader(doc, dpi, state);
    561             canon->addAlphaShader(shader, std::move(state));
    562         }
    563         return shader;
    564     } else {
    565         sk_sp<SkPDFObject> shader = canon->findFunctionShader(state);
    566         if (!shader) {
    567             shader = make_function_shader(canon, state);
    568             canon->addFunctionShader(shader, std::move(state));
    569         }
    570         return shader;
    571     }
    572 }
    573 
    574 sk_sp<SkPDFObject> SkPDFShader::GetPDFShader(SkPDFDocument* doc,
    575                                              SkScalar dpi,
    576                                              SkShader* shader,
    577                                              const SkMatrix& matrix,
    578                                              const SkIRect& surfaceBBox,
    579                                              SkScalar rasterScale) {
    580     if (surfaceBBox.isEmpty()) {
    581         return nullptr;
    582     }
    583     SkBitmap image;
    584     State state(shader, matrix, surfaceBBox, rasterScale, &image);
    585     return get_pdf_shader_by_state(
    586             doc, dpi, std::move(state), std::move(image));
    587 }
    588 
    589 static sk_sp<SkPDFDict> get_gradient_resource_dict(
    590         SkPDFObject* functionShader,
    591         SkPDFObject* gState) {
    592     SkTDArray<SkPDFObject*> patterns;
    593     if (functionShader) {
    594         patterns.push(functionShader);
    595     }
    596     SkTDArray<SkPDFObject*> graphicStates;
    597     if (gState) {
    598         graphicStates.push(gState);
    599     }
    600     return SkPDFResourceDict::Make(&graphicStates, &patterns, nullptr, nullptr);
    601 }
    602 
    603 static void populate_tiling_pattern_dict(SkPDFDict* pattern,
    604                                          SkRect& bbox,
    605                                          sk_sp<SkPDFDict> resources,
    606                                          const SkMatrix& matrix) {
    607     const int kTiling_PatternType = 1;
    608     const int kColoredTilingPattern_PaintType = 1;
    609     const int kConstantSpacing_TilingType = 1;
    610 
    611     pattern->insertName("Type", "Pattern");
    612     pattern->insertInt("PatternType", kTiling_PatternType);
    613     pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
    614     pattern->insertInt("TilingType", kConstantSpacing_TilingType);
    615     pattern->insertObject("BBox", SkPDFUtils::RectToArray(bbox));
    616     pattern->insertScalar("XStep", bbox.width());
    617     pattern->insertScalar("YStep", bbox.height());
    618     pattern->insertObject("Resources", std::move(resources));
    619     if (!matrix.isIdentity()) {
    620         pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix));
    621     }
    622 }
    623 
    624 /**
    625  * Creates a content stream which fills the pattern P0 across bounds.
    626  * @param gsIndex A graphics state resource index to apply, or <0 if no
    627  * graphics state to apply.
    628  */
    629 static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(
    630         int gsIndex, SkRect& bounds) {
    631     SkDynamicMemoryWStream content;
    632     if (gsIndex >= 0) {
    633         SkPDFUtils::ApplyGraphicState(gsIndex, &content);
    634     }
    635     SkPDFUtils::ApplyPattern(0, &content);
    636     SkPDFUtils::AppendRectangle(bounds, &content);
    637     SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType,
    638                           &content);
    639 
    640     return std::unique_ptr<SkStreamAsset>(content.detachAsStream());
    641 }
    642 
    643 /**
    644  * Creates a ExtGState with the SMask set to the luminosityShader in
    645  * luminosity mode. The shader pattern extends to the bbox.
    646  */
    647 static sk_sp<SkPDFObject> create_smask_graphic_state(
    648         SkPDFDocument* doc, SkScalar dpi, const SkPDFShader::State& state) {
    649     SkRect bbox;
    650     bbox.set(state.fBBox);
    651 
    652     sk_sp<SkPDFObject> luminosityShader(
    653             get_pdf_shader_by_state(doc, dpi, state.MakeAlphaToLuminosityState(),
    654                                     SkBitmap()));
    655 
    656     std::unique_ptr<SkStreamAsset> alphaStream(create_pattern_fill_content(-1, bbox));
    657 
    658     sk_sp<SkPDFDict> resources =
    659         get_gradient_resource_dict(luminosityShader.get(), nullptr);
    660 
    661     sk_sp<SkPDFObject> alphaMask =
    662         SkPDFMakeFormXObject(std::move(alphaStream),
    663                              SkPDFUtils::RectToArray(bbox),
    664                              std::move(resources),
    665                              SkMatrix::I(),
    666                              "DeviceRGB");
    667     return SkPDFGraphicState::GetSMaskGraphicState(
    668             std::move(alphaMask), false,
    669             SkPDFGraphicState::kLuminosity_SMaskMode, doc->canon());
    670 }
    671 
    672 static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
    673                                                      SkScalar dpi,
    674                                                      const SkPDFShader::State& state) {
    675     SkRect bbox;
    676     bbox.set(state.fBBox);
    677 
    678     SkPDFShader::State opaqueState(state.MakeOpaqueState());
    679 
    680     sk_sp<SkPDFObject> colorShader(
    681             get_pdf_shader_by_state(doc, dpi, std::move(opaqueState), SkBitmap()));
    682     if (!colorShader) {
    683         return nullptr;
    684     }
    685 
    686     // Create resource dict with alpha graphics state as G0 and
    687     // pattern shader as P0, then write content stream.
    688     sk_sp<SkPDFObject> alphaGs = create_smask_graphic_state(doc, dpi, state);
    689 
    690     sk_sp<SkPDFDict> resourceDict =
    691             get_gradient_resource_dict(colorShader.get(), alphaGs.get());
    692 
    693     std::unique_ptr<SkStreamAsset> colorStream(
    694             create_pattern_fill_content(0, bbox));
    695     auto alphaFunctionShader = sk_make_sp<SkPDFStream>(std::move(colorStream));
    696 
    697     populate_tiling_pattern_dict(alphaFunctionShader->dict(), bbox,
    698                                  std::move(resourceDict), SkMatrix::I());
    699     return alphaFunctionShader;
    700 }
    701 
    702 // Finds affine and persp such that in = affine * persp.
    703 // but it returns the inverse of perspective matrix.
    704 static bool split_perspective(const SkMatrix in, SkMatrix* affine,
    705                               SkMatrix* perspectiveInverse) {
    706     const SkScalar p2 = in[SkMatrix::kMPersp2];
    707 
    708     if (SkScalarNearlyZero(p2)) {
    709         return false;
    710     }
    711 
    712     const SkScalar zero = SkIntToScalar(0);
    713     const SkScalar one = SkIntToScalar(1);
    714 
    715     const SkScalar sx = in[SkMatrix::kMScaleX];
    716     const SkScalar kx = in[SkMatrix::kMSkewX];
    717     const SkScalar tx = in[SkMatrix::kMTransX];
    718     const SkScalar ky = in[SkMatrix::kMSkewY];
    719     const SkScalar sy = in[SkMatrix::kMScaleY];
    720     const SkScalar ty = in[SkMatrix::kMTransY];
    721     const SkScalar p0 = in[SkMatrix::kMPersp0];
    722     const SkScalar p1 = in[SkMatrix::kMPersp1];
    723 
    724     // Perspective matrix would be:
    725     // 1  0  0
    726     // 0  1  0
    727     // p0 p1 p2
    728     // But we need the inverse of persp.
    729     perspectiveInverse->setAll(one,          zero,       zero,
    730                                zero,         one,        zero,
    731                                -p0/p2,     -p1/p2,     1/p2);
    732 
    733     affine->setAll(sx - p0 * tx / p2,       kx - p1 * tx / p2,      tx / p2,
    734                    ky - p0 * ty / p2,       sy - p1 * ty / p2,      ty / p2,
    735                    zero,                    zero,                   one);
    736 
    737     return true;
    738 }
    739 
    740 sk_sp<SkPDFArray> SkPDFShader::MakeRangeObject() {
    741     auto range = sk_make_sp<SkPDFArray>();
    742     range->reserve(6);
    743     range->appendInt(0);
    744     range->appendInt(1);
    745     range->appendInt(0);
    746     range->appendInt(1);
    747     range->appendInt(0);
    748     range->appendInt(1);
    749     return range;
    750 }
    751 
    752 static sk_sp<SkPDFStream> make_ps_function(
    753         std::unique_ptr<SkStreamAsset> psCode,
    754         sk_sp<SkPDFArray> domain,
    755         sk_sp<SkPDFObject> range) {
    756     auto result = sk_make_sp<SkPDFStream>(std::move(psCode));
    757     result->dict()->insertInt("FunctionType", 4);
    758     result->dict()->insertObject("Domain", std::move(domain));
    759     result->dict()->insertObject("Range", std::move(range));
    760     return result;
    761 }
    762 
    763 // catch cases where the inner just touches the outer circle
    764 // and make the inner circle just inside the outer one to match raster
    765 static void FixUpRadius(const SkPoint& p1, SkScalar& r1, const SkPoint& p2, SkScalar& r2) {
    766     // detect touching circles
    767     SkScalar distance = SkPoint::Distance(p1, p2);
    768     SkScalar subtractRadii = fabs(r1 - r2);
    769     if (fabs(distance - subtractRadii) < 0.002f) {
    770         if (r1 > r2) {
    771             r1 += 0.002f;
    772         } else {
    773             r2 += 0.002f;
    774         }
    775     }
    776 }
    777 
    778 static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
    779                                              const SkPDFShader::State& state) {
    780     void (*codeFunction)(const SkShader::GradientInfo& info,
    781                          const SkMatrix& perspectiveRemover,
    782                          SkDynamicMemoryWStream* function) = nullptr;
    783     SkPoint transformPoints[2];
    784     const SkShader::GradientInfo* info = &state.fInfo;
    785     SkMatrix finalMatrix = state.fCanvasTransform;
    786     finalMatrix.preConcat(state.fShaderTransform);
    787 
    788     bool doStitchFunctions = (state.fType == SkShader::kLinear_GradientType ||
    789                               state.fType == SkShader::kRadial_GradientType ||
    790                               state.fType == SkShader::kConical_GradientType) &&
    791                              info->fTileMode == SkShader::kClamp_TileMode &&
    792                              !finalMatrix.hasPerspective();
    793 
    794     auto domain = sk_make_sp<SkPDFArray>();
    795 
    796     int32_t shadingType = 1;
    797     auto pdfShader = sk_make_sp<SkPDFDict>();
    798     // The two point radial gradient further references
    799     // state.fInfo
    800     // in translating from x, y coordinates to the t parameter. So, we have
    801     // to transform the points and radii according to the calculated matrix.
    802     if (doStitchFunctions) {
    803         pdfShader->insertObject("Function", gradientStitchCode(*info));
    804         shadingType = (state.fType == SkShader::kLinear_GradientType) ? 2 : 3;
    805 
    806         auto extend = sk_make_sp<SkPDFArray>();
    807         extend->reserve(2);
    808         extend->appendBool(true);
    809         extend->appendBool(true);
    810         pdfShader->insertObject("Extend", std::move(extend));
    811 
    812         auto coords = sk_make_sp<SkPDFArray>();
    813         if (state.fType == SkShader::kConical_GradientType) {
    814             coords->reserve(6);
    815             SkScalar r1 = info->fRadius[0];
    816             SkScalar r2 = info->fRadius[1];
    817             SkPoint pt1 = info->fPoint[0];
    818             SkPoint pt2 = info->fPoint[1];
    819             FixUpRadius(pt1, r1, pt2, r2);
    820 
    821             coords->appendScalar(pt1.fX);
    822             coords->appendScalar(pt1.fY);
    823             coords->appendScalar(r1);
    824 
    825             coords->appendScalar(pt2.fX);
    826             coords->appendScalar(pt2.fY);
    827             coords->appendScalar(r2);
    828         } else if (state.fType == SkShader::kRadial_GradientType) {
    829             coords->reserve(6);
    830             const SkPoint& pt1 = info->fPoint[0];
    831 
    832             coords->appendScalar(pt1.fX);
    833             coords->appendScalar(pt1.fY);
    834             coords->appendScalar(0);
    835 
    836             coords->appendScalar(pt1.fX);
    837             coords->appendScalar(pt1.fY);
    838             coords->appendScalar(info->fRadius[0]);
    839         } else {
    840             coords->reserve(4);
    841             const SkPoint& pt1 = info->fPoint[0];
    842             const SkPoint& pt2 = info->fPoint[1];
    843 
    844             coords->appendScalar(pt1.fX);
    845             coords->appendScalar(pt1.fY);
    846 
    847             coords->appendScalar(pt2.fX);
    848             coords->appendScalar(pt2.fY);
    849         }
    850 
    851         pdfShader->insertObject("Coords", std::move(coords));
    852     } else {
    853         // Depending on the type of the gradient, we want to transform the
    854         // coordinate space in different ways.
    855         transformPoints[0] = info->fPoint[0];
    856         transformPoints[1] = info->fPoint[1];
    857         switch (state.fType) {
    858             case SkShader::kLinear_GradientType:
    859                 codeFunction = &linearCode;
    860                 break;
    861             case SkShader::kRadial_GradientType:
    862                 transformPoints[1] = transformPoints[0];
    863                 transformPoints[1].fX += info->fRadius[0];
    864                 codeFunction = &radialCode;
    865                 break;
    866             case SkShader::kConical_GradientType: {
    867                 transformPoints[1] = transformPoints[0];
    868                 transformPoints[1].fX += SK_Scalar1;
    869                 codeFunction = &twoPointConicalCode;
    870                 break;
    871             }
    872             case SkShader::kSweep_GradientType:
    873                 transformPoints[1] = transformPoints[0];
    874                 transformPoints[1].fX += SK_Scalar1;
    875                 codeFunction = &sweepCode;
    876                 break;
    877             case SkShader::kColor_GradientType:
    878             case SkShader::kNone_GradientType:
    879             default:
    880                 return nullptr;
    881         }
    882 
    883         // Move any scaling (assuming a unit gradient) or translation
    884         // (and rotation for linear gradient), of the final gradient from
    885         // info->fPoints to the matrix (updating bbox appropriately).  Now
    886         // the gradient can be drawn on on the unit segment.
    887         SkMatrix mapperMatrix;
    888         unitToPointsMatrix(transformPoints, &mapperMatrix);
    889 
    890         finalMatrix.preConcat(mapperMatrix);
    891 
    892         // Preserves as much as posible in the final matrix, and only removes
    893         // the perspective. The inverse of the perspective is stored in
    894         // perspectiveInverseOnly matrix and has 3 useful numbers
    895         // (p0, p1, p2), while everything else is either 0 or 1.
    896         // In this way the shader will handle it eficiently, with minimal code.
    897         SkMatrix perspectiveInverseOnly = SkMatrix::I();
    898         if (finalMatrix.hasPerspective()) {
    899             if (!split_perspective(finalMatrix,
    900                                    &finalMatrix, &perspectiveInverseOnly)) {
    901                 return nullptr;
    902             }
    903         }
    904 
    905         SkRect bbox;
    906         bbox.set(state.fBBox);
    907         if (!inverse_transform_bbox(finalMatrix, &bbox)) {
    908             return nullptr;
    909         }
    910         domain->reserve(4);
    911         domain->appendScalar(bbox.fLeft);
    912         domain->appendScalar(bbox.fRight);
    913         domain->appendScalar(bbox.fTop);
    914         domain->appendScalar(bbox.fBottom);
    915 
    916         SkDynamicMemoryWStream functionCode;
    917 
    918         if (state.fType == SkShader::kConical_GradientType) {
    919             SkShader::GradientInfo twoPointRadialInfo = *info;
    920             SkMatrix inverseMapperMatrix;
    921             if (!mapperMatrix.invert(&inverseMapperMatrix)) {
    922                 return nullptr;
    923             }
    924             inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
    925             twoPointRadialInfo.fRadius[0] =
    926                 inverseMapperMatrix.mapRadius(info->fRadius[0]);
    927             twoPointRadialInfo.fRadius[1] =
    928                 inverseMapperMatrix.mapRadius(info->fRadius[1]);
    929             codeFunction(twoPointRadialInfo, perspectiveInverseOnly, &functionCode);
    930         } else {
    931             codeFunction(*info, perspectiveInverseOnly, &functionCode);
    932         }
    933 
    934         pdfShader->insertObject("Domain", domain);
    935 
    936         // Call canon->makeRangeObject() instead of
    937         // SkPDFShader::MakeRangeObject() so that the canon can
    938         // deduplicate.
    939         std::unique_ptr<SkStreamAsset> functionStream(
    940                 functionCode.detachAsStream());
    941         sk_sp<SkPDFStream> function = make_ps_function(std::move(functionStream),
    942                                                        std::move(domain),
    943                                                        canon->makeRangeObject());
    944         pdfShader->insertObjRef("Function", std::move(function));
    945     }
    946 
    947     pdfShader->insertInt("ShadingType", shadingType);
    948     pdfShader->insertName("ColorSpace", "DeviceRGB");
    949 
    950     auto pdfFunctionShader = sk_make_sp<SkPDFDict>("Pattern");
    951     pdfFunctionShader->insertInt("PatternType", 2);
    952     pdfFunctionShader->insertObject("Matrix",
    953                                     SkPDFUtils::MatrixToArray(finalMatrix));
    954     pdfFunctionShader->insertObject("Shading", std::move(pdfShader));
    955 
    956     return pdfFunctionShader;
    957 }
    958 
    959 static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
    960                                             SkScalar dpi,
    961                                             const SkPDFShader::State& state,
    962                                             SkBitmap image) {
    963     SkASSERT(state.fBitmapKey ==
    964              (SkBitmapKey{image.getSubset(), image.getGenerationID()}));
    965     SkAutoLockPixels SkAutoLockPixels(image);
    966 
    967     // The image shader pattern cell will be drawn into a separate device
    968     // in pattern cell space (no scaling on the bitmap, though there may be
    969     // translations so that all content is in the device, coordinates > 0).
    970 
    971     // Map clip bounds to shader space to ensure the device is large enough
    972     // to handle fake clamping.
    973     SkMatrix finalMatrix = state.fCanvasTransform;
    974     finalMatrix.preConcat(state.fShaderTransform);
    975     SkRect deviceBounds;
    976     deviceBounds.set(state.fBBox);
    977     if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) {
    978         return nullptr;
    979     }
    980 
    981     SkRect bitmapBounds;
    982     image.getBounds(&bitmapBounds);
    983 
    984     // For tiling modes, the bounds should be extended to include the bitmap,
    985     // otherwise the bitmap gets clipped out and the shader is empty and awful.
    986     // For clamp modes, we're only interested in the clip region, whether
    987     // or not the main bitmap is in it.
    988     SkShader::TileMode tileModes[2];
    989     tileModes[0] = state.fImageTileModes[0];
    990     tileModes[1] = state.fImageTileModes[1];
    991     if (tileModes[0] != SkShader::kClamp_TileMode ||
    992             tileModes[1] != SkShader::kClamp_TileMode) {
    993         deviceBounds.join(bitmapBounds);
    994     }
    995 
    996     SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()),
    997                                  SkScalarRoundToInt(deviceBounds.height()));
    998     sk_sp<SkPDFDevice> patternDevice(
    999             SkPDFDevice::CreateUnflipped(size, dpi, doc));
   1000     SkCanvas canvas(patternDevice.get());
   1001 
   1002     SkRect patternBBox;
   1003     image.getBounds(&patternBBox);
   1004 
   1005     // Translate the canvas so that the bitmap origin is at (0, 0).
   1006     canvas.translate(-deviceBounds.left(), -deviceBounds.top());
   1007     patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
   1008     // Undo the translation in the final matrix
   1009     finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());
   1010 
   1011     // If the bitmap is out of bounds (i.e. clamp mode where we only see the
   1012     // stretched sides), canvas will clip this out and the extraneous data
   1013     // won't be saved to the PDF.
   1014     canvas.drawBitmap(image, 0, 0);
   1015 
   1016     SkScalar width = SkIntToScalar(image.width());
   1017     SkScalar height = SkIntToScalar(image.height());
   1018 
   1019     // Tiling is implied.  First we handle mirroring.
   1020     if (tileModes[0] == SkShader::kMirror_TileMode) {
   1021         SkMatrix xMirror;
   1022         xMirror.setScale(-1, 1);
   1023         xMirror.postTranslate(2 * width, 0);
   1024         drawBitmapMatrix(&canvas, image, xMirror);
   1025         patternBBox.fRight += width;
   1026     }
   1027     if (tileModes[1] == SkShader::kMirror_TileMode) {
   1028         SkMatrix yMirror;
   1029         yMirror.setScale(SK_Scalar1, -SK_Scalar1);
   1030         yMirror.postTranslate(0, 2 * height);
   1031         drawBitmapMatrix(&canvas, image, yMirror);
   1032         patternBBox.fBottom += height;
   1033     }
   1034     if (tileModes[0] == SkShader::kMirror_TileMode &&
   1035             tileModes[1] == SkShader::kMirror_TileMode) {
   1036         SkMatrix mirror;
   1037         mirror.setScale(-1, -1);
   1038         mirror.postTranslate(2 * width, 2 * height);
   1039         drawBitmapMatrix(&canvas, image, mirror);
   1040     }
   1041 
   1042     // Then handle Clamping, which requires expanding the pattern canvas to
   1043     // cover the entire surfaceBBox.
   1044 
   1045     // If both x and y are in clamp mode, we start by filling in the corners.
   1046     // (Which are just a rectangles of the corner colors.)
   1047     if (tileModes[0] == SkShader::kClamp_TileMode &&
   1048             tileModes[1] == SkShader::kClamp_TileMode) {
   1049         SkPaint paint;
   1050         SkRect rect;
   1051         rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0);
   1052         if (!rect.isEmpty()) {
   1053             paint.setColor(image.getColor(0, 0));
   1054             canvas.drawRect(rect, paint);
   1055         }
   1056 
   1057         rect = SkRect::MakeLTRB(width, deviceBounds.top(),
   1058                                 deviceBounds.right(), 0);
   1059         if (!rect.isEmpty()) {
   1060             paint.setColor(image.getColor(image.width() - 1, 0));
   1061             canvas.drawRect(rect, paint);
   1062         }
   1063 
   1064         rect = SkRect::MakeLTRB(width, height,
   1065                                 deviceBounds.right(), deviceBounds.bottom());
   1066         if (!rect.isEmpty()) {
   1067             paint.setColor(image.getColor(image.width() - 1,
   1068                                            image.height() - 1));
   1069             canvas.drawRect(rect, paint);
   1070         }
   1071 
   1072         rect = SkRect::MakeLTRB(deviceBounds.left(), height,
   1073                                 0, deviceBounds.bottom());
   1074         if (!rect.isEmpty()) {
   1075             paint.setColor(image.getColor(0, image.height() - 1));
   1076             canvas.drawRect(rect, paint);
   1077         }
   1078     }
   1079 
   1080     // Then expand the left, right, top, then bottom.
   1081     if (tileModes[0] == SkShader::kClamp_TileMode) {
   1082         SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image.height());
   1083         if (deviceBounds.left() < 0) {
   1084             SkBitmap left;
   1085             SkAssertResult(image.extractSubset(&left, subset));
   1086 
   1087             SkMatrix leftMatrix;
   1088             leftMatrix.setScale(-deviceBounds.left(), 1);
   1089             leftMatrix.postTranslate(deviceBounds.left(), 0);
   1090             drawBitmapMatrix(&canvas, left, leftMatrix);
   1091 
   1092             if (tileModes[1] == SkShader::kMirror_TileMode) {
   1093                 leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
   1094                 leftMatrix.postTranslate(0, 2 * height);
   1095                 drawBitmapMatrix(&canvas, left, leftMatrix);
   1096             }
   1097             patternBBox.fLeft = 0;
   1098         }
   1099 
   1100         if (deviceBounds.right() > width) {
   1101             SkBitmap right;
   1102             subset.offset(image.width() - 1, 0);
   1103             SkAssertResult(image.extractSubset(&right, subset));
   1104 
   1105             SkMatrix rightMatrix;
   1106             rightMatrix.setScale(deviceBounds.right() - width, 1);
   1107             rightMatrix.postTranslate(width, 0);
   1108             drawBitmapMatrix(&canvas, right, rightMatrix);
   1109 
   1110             if (tileModes[1] == SkShader::kMirror_TileMode) {
   1111                 rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
   1112                 rightMatrix.postTranslate(0, 2 * height);
   1113                 drawBitmapMatrix(&canvas, right, rightMatrix);
   1114             }
   1115             patternBBox.fRight = deviceBounds.width();
   1116         }
   1117     }
   1118 
   1119     if (tileModes[1] == SkShader::kClamp_TileMode) {
   1120         SkIRect subset = SkIRect::MakeXYWH(0, 0, image.width(), 1);
   1121         if (deviceBounds.top() < 0) {
   1122             SkBitmap top;
   1123             SkAssertResult(image.extractSubset(&top, subset));
   1124 
   1125             SkMatrix topMatrix;
   1126             topMatrix.setScale(SK_Scalar1, -deviceBounds.top());
   1127             topMatrix.postTranslate(0, deviceBounds.top());
   1128             drawBitmapMatrix(&canvas, top, topMatrix);
   1129 
   1130             if (tileModes[0] == SkShader::kMirror_TileMode) {
   1131                 topMatrix.postScale(-1, 1);
   1132                 topMatrix.postTranslate(2 * width, 0);
   1133                 drawBitmapMatrix(&canvas, top, topMatrix);
   1134             }
   1135             patternBBox.fTop = 0;
   1136         }
   1137 
   1138         if (deviceBounds.bottom() > height) {
   1139             SkBitmap bottom;
   1140             subset.offset(0, image.height() - 1);
   1141             SkAssertResult(image.extractSubset(&bottom, subset));
   1142 
   1143             SkMatrix bottomMatrix;
   1144             bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height);
   1145             bottomMatrix.postTranslate(0, height);
   1146             drawBitmapMatrix(&canvas, bottom, bottomMatrix);
   1147 
   1148             if (tileModes[0] == SkShader::kMirror_TileMode) {
   1149                 bottomMatrix.postScale(-1, 1);
   1150                 bottomMatrix.postTranslate(2 * width, 0);
   1151                 drawBitmapMatrix(&canvas, bottom, bottomMatrix);
   1152             }
   1153             patternBBox.fBottom = deviceBounds.height();
   1154         }
   1155     }
   1156 
   1157     auto imageShader = sk_make_sp<SkPDFStream>(patternDevice->content());
   1158     populate_tiling_pattern_dict(imageShader->dict(), patternBBox,
   1159                                  patternDevice->makeResourceDict(), finalMatrix);
   1160     return imageShader;
   1161 }
   1162 
   1163 bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const {
   1164     if (fType != b.fType ||
   1165             fCanvasTransform != b.fCanvasTransform ||
   1166             fShaderTransform != b.fShaderTransform ||
   1167             fBBox != b.fBBox) {
   1168         return false;
   1169     }
   1170 
   1171     if (fType == SkShader::kNone_GradientType) {
   1172         if (fBitmapKey != b.fBitmapKey ||
   1173                 fBitmapKey.fID == 0 ||
   1174                 fImageTileModes[0] != b.fImageTileModes[0] ||
   1175                 fImageTileModes[1] != b.fImageTileModes[1]) {
   1176             return false;
   1177         }
   1178     } else {
   1179         if (fInfo.fColorCount != b.fInfo.fColorCount ||
   1180                 memcmp(fInfo.fColors, b.fInfo.fColors,
   1181                        sizeof(SkColor) * fInfo.fColorCount) != 0 ||
   1182                 memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets,
   1183                        sizeof(SkScalar) * fInfo.fColorCount) != 0 ||
   1184                 fInfo.fPoint[0] != b.fInfo.fPoint[0] ||
   1185                 fInfo.fTileMode != b.fInfo.fTileMode) {
   1186             return false;
   1187         }
   1188 
   1189         switch (fType) {
   1190             case SkShader::kLinear_GradientType:
   1191                 if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) {
   1192                     return false;
   1193                 }
   1194                 break;
   1195             case SkShader::kRadial_GradientType:
   1196                 if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) {
   1197                     return false;
   1198                 }
   1199                 break;
   1200             case SkShader::kConical_GradientType:
   1201                 if (fInfo.fPoint[1] != b.fInfo.fPoint[1] ||
   1202                         fInfo.fRadius[0] != b.fInfo.fRadius[0] ||
   1203                         fInfo.fRadius[1] != b.fInfo.fRadius[1]) {
   1204                     return false;
   1205                 }
   1206                 break;
   1207             case SkShader::kSweep_GradientType:
   1208             case SkShader::kNone_GradientType:
   1209             case SkShader::kColor_GradientType:
   1210                 break;
   1211         }
   1212     }
   1213     return true;
   1214 }
   1215 
   1216 SkPDFShader::State::State(SkShader* shader, const SkMatrix& canvasTransform,
   1217                           const SkIRect& bbox, SkScalar rasterScale,
   1218                           SkBitmap* imageDst)
   1219         : fType(SkShader::kNone_GradientType)
   1220         , fInfo{0, nullptr, nullptr, {{0.0f, 0.0f}, {0.0f, 0.0f}},
   1221                 {0.0f, 0.0f}, SkShader::kClamp_TileMode, 0}
   1222         , fCanvasTransform(canvasTransform)
   1223         , fShaderTransform{SkMatrix::I()}
   1224         , fBBox(bbox)
   1225         , fBitmapKey{{0, 0, 0, 0}, 0}
   1226         , fImageTileModes{SkShader::kClamp_TileMode,
   1227                           SkShader::kClamp_TileMode} {
   1228     SkASSERT(imageDst);
   1229     fInfo.fColorCount = 0;
   1230     fInfo.fColors = nullptr;
   1231     fInfo.fColorOffsets = nullptr;
   1232     fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode;
   1233     fType = shader->asAGradient(&fInfo);
   1234 
   1235     if (fType != SkShader::kNone_GradientType) {
   1236         fBitmapKey = SkBitmapKey{{0, 0, 0, 0}, 0};
   1237         fShaderTransform = shader->getLocalMatrix();
   1238         this->allocateGradientInfoStorage();
   1239         shader->asAGradient(&fInfo);
   1240         return;
   1241     }
   1242     if (SkImage* skimg = shader->isAImage(&fShaderTransform, fImageTileModes)) {
   1243         // TODO(halcanary): delay converting to bitmap.
   1244         if (skimg->asLegacyBitmap(imageDst, SkImage::kRO_LegacyBitmapMode)) {
   1245             fBitmapKey = SkBitmapKey{imageDst->getSubset(), imageDst->getGenerationID()};
   1246             return;
   1247         }
   1248     }
   1249     fShaderTransform = shader->getLocalMatrix();
   1250     // Generic fallback for unsupported shaders:
   1251     //  * allocate a bbox-sized bitmap
   1252     //  * shade the whole area
   1253     //  * use the result as a bitmap shader
   1254 
   1255     // bbox is in device space. While that's exactly what we
   1256     // want for sizing our bitmap, we need to map it into
   1257     // shader space for adjustments (to match
   1258     // MakeImageShader's behavior).
   1259     SkRect shaderRect = SkRect::Make(bbox);
   1260     if (!inverse_transform_bbox(canvasTransform, &shaderRect)) {
   1261         imageDst->reset();
   1262         return;
   1263     }
   1264 
   1265     // Clamp the bitmap size to about 1M pixels
   1266     static const SkScalar kMaxBitmapArea = 1024 * 1024;
   1267     SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height();
   1268     if (bitmapArea > kMaxBitmapArea) {
   1269         rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea);
   1270     }
   1271 
   1272     SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()),
   1273                                  SkScalarRoundToInt(rasterScale * bbox.height()));
   1274     SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(),
   1275                                 SkIntToScalar(size.height()) / shaderRect.height());
   1276 
   1277     imageDst->allocN32Pixels(size.width(), size.height());
   1278     imageDst->eraseColor(SK_ColorTRANSPARENT);
   1279 
   1280     SkPaint p;
   1281     p.setShader(sk_ref_sp(shader));
   1282 
   1283     SkCanvas canvas(*imageDst);
   1284     canvas.scale(scale.width(), scale.height());
   1285     canvas.translate(-shaderRect.x(), -shaderRect.y());
   1286     canvas.drawPaint(p);
   1287 
   1288     fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y());
   1289     fShaderTransform.preScale(1 / scale.width(), 1 / scale.height());
   1290     fBitmapKey = SkBitmapKey{imageDst->getSubset(), imageDst->getGenerationID()};
   1291 }
   1292 
   1293 SkPDFShader::State::State(const SkPDFShader::State& other)
   1294   : fType(other.fType),
   1295     fCanvasTransform(other.fCanvasTransform),
   1296     fShaderTransform(other.fShaderTransform),
   1297     fBBox(other.fBBox)
   1298 {
   1299     // Only gradients supported for now, since that is all that is used.
   1300     // If needed, image state copy constructor can be added here later.
   1301     SkASSERT(fType != SkShader::kNone_GradientType);
   1302 
   1303     if (fType != SkShader::kNone_GradientType) {
   1304         fInfo = other.fInfo;
   1305 
   1306         this->allocateGradientInfoStorage();
   1307         for (int i = 0; i < fInfo.fColorCount; i++) {
   1308             fInfo.fColors[i] = other.fInfo.fColors[i];
   1309             fInfo.fColorOffsets[i] = other.fInfo.fColorOffsets[i];
   1310         }
   1311     }
   1312 }
   1313 
   1314 /**
   1315  * Create a copy of this gradient state with alpha assigned to RGB luminousity.
   1316  * Only valid for gradient states.
   1317  */
   1318 SkPDFShader::State SkPDFShader::State::MakeAlphaToLuminosityState() const {
   1319     SkASSERT(fBitmapKey == (SkBitmapKey{{0, 0, 0, 0}, 0}));
   1320     SkASSERT(fType != SkShader::kNone_GradientType);
   1321 
   1322     SkPDFShader::State newState(*this);
   1323 
   1324     for (int i = 0; i < fInfo.fColorCount; i++) {
   1325         SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
   1326         newState.fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha);
   1327     }
   1328 
   1329     return newState;
   1330 }
   1331 
   1332 /**
   1333  * Create a copy of this gradient state with alpha set to fully opaque
   1334  * Only valid for gradient states.
   1335  */
   1336 SkPDFShader::State SkPDFShader::State::MakeOpaqueState() const {
   1337     SkASSERT(fBitmapKey == (SkBitmapKey{{0, 0, 0, 0}, 0}));
   1338     SkASSERT(fType != SkShader::kNone_GradientType);
   1339 
   1340     SkPDFShader::State newState(*this);
   1341     for (int i = 0; i < fInfo.fColorCount; i++) {
   1342         newState.fInfo.fColors[i] = SkColorSetA(fInfo.fColors[i],
   1343                                                  SK_AlphaOPAQUE);
   1344     }
   1345 
   1346     return newState;
   1347 }
   1348 
   1349 /**
   1350  * Returns true if state is a gradient and the gradient has alpha.
   1351  */
   1352 bool SkPDFShader::State::GradientHasAlpha() const {
   1353     if (fType == SkShader::kNone_GradientType) {
   1354         return false;
   1355     }
   1356 
   1357     for (int i = 0; i < fInfo.fColorCount; i++) {
   1358         SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
   1359         if (alpha != SK_AlphaOPAQUE) {
   1360             return true;
   1361         }
   1362     }
   1363     return false;
   1364 }
   1365 
   1366 void SkPDFShader::State::allocateGradientInfoStorage() {
   1367     fColors.reset(new SkColor[fInfo.fColorCount]);
   1368     fStops.reset(new SkScalar[fInfo.fColorCount]);
   1369     fInfo.fColors = fColors.get();
   1370     fInfo.fColorOffsets = fStops.get();
   1371 }
   1372