Home | History | Annotate | Download | only in pdf
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkPDFShader.h"
     11 
     12 #include "SkCanvas.h"
     13 #include "SkData.h"
     14 #include "SkPDFCatalog.h"
     15 #include "SkPDFDevice.h"
     16 #include "SkPDFFormXObject.h"
     17 #include "SkPDFGraphicState.h"
     18 #include "SkPDFResourceDict.h"
     19 #include "SkPDFUtils.h"
     20 #include "SkScalar.h"
     21 #include "SkStream.h"
     22 #include "SkTemplates.h"
     23 #include "SkThread.h"
     24 #include "SkTSet.h"
     25 #include "SkTypes.h"
     26 
     27 static bool transformBBox(const SkMatrix& matrix, SkRect* bbox) {
     28     SkMatrix inverse;
     29     if (!matrix.invert(&inverse)) {
     30         return false;
     31     }
     32     inverse.mapRect(bbox);
     33     return true;
     34 }
     35 
     36 static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) {
     37     SkVector    vec = pts[1] - pts[0];
     38     SkScalar    mag = vec.length();
     39     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
     40 
     41     vec.scale(inv);
     42     matrix->setSinCos(vec.fY, vec.fX);
     43     matrix->preScale(mag, mag);
     44     matrix->postTranslate(pts[0].fX, pts[0].fY);
     45 }
     46 
     47 /* Assumes t + startOffset is on the stack and does a linear interpolation on t
     48    between startOffset and endOffset from prevColor to curColor (for each color
     49    component), leaving the result in component order on the stack. It assumes
     50    there are always 3 components per color.
     51    @param range                  endOffset - startOffset
     52    @param curColor[components]   The current color components.
     53    @param prevColor[components]  The previous color components.
     54    @param result                 The result ps function.
     55  */
     56 static void interpolateColorCode(SkScalar range, SkScalar* curColor,
     57                                  SkScalar* prevColor, SkString* result) {
     58     static const int kColorComponents = 3;
     59 
     60     // Figure out how to scale each color component.
     61     SkScalar multiplier[kColorComponents];
     62     for (int i = 0; i < kColorComponents; i++) {
     63         multiplier[i] = SkScalarDiv(curColor[i] - prevColor[i], range);
     64     }
     65 
     66     // Calculate when we no longer need to keep a copy of the input parameter t.
     67     // If the last component to use t is i, then dupInput[0..i - 1] = true
     68     // and dupInput[i .. components] = false.
     69     bool dupInput[kColorComponents];
     70     dupInput[kColorComponents - 1] = false;
     71     for (int i = kColorComponents - 2; i >= 0; i--) {
     72         dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
     73     }
     74 
     75     if (!dupInput[0] && multiplier[0] == 0) {
     76         result->append("pop ");
     77     }
     78 
     79     for (int i = 0; i < kColorComponents; i++) {
     80         // If the next components needs t and this component will consume a
     81         // copy, make another copy.
     82         if (dupInput[i] && multiplier[i] != 0) {
     83             result->append("dup ");
     84         }
     85 
     86         if (multiplier[i] == 0) {
     87             result->appendScalar(prevColor[i]);
     88             result->append(" ");
     89         } else {
     90             if (multiplier[i] != 1) {
     91                 result->appendScalar(multiplier[i]);
     92                 result->append(" mul ");
     93             }
     94             if (prevColor[i] != 0) {
     95                 result->appendScalar(prevColor[i]);
     96                 result->append(" add ");
     97             }
     98         }
     99 
    100         if (dupInput[i]) {
    101             result->append("exch\n");
    102         }
    103     }
    104 }
    105 
    106 /* Generate Type 4 function code to map t=[0,1) to the passed gradient,
    107    clamping at the edges of the range.  The generated code will be of the form:
    108        if (t < 0) {
    109            return colorData[0][r,g,b];
    110        } else {
    111            if (t < info.fColorOffsets[1]) {
    112                return linearinterpolation(colorData[0][r,g,b],
    113                                           colorData[1][r,g,b]);
    114            } else {
    115                if (t < info.fColorOffsets[2]) {
    116                    return linearinterpolation(colorData[1][r,g,b],
    117                                               colorData[2][r,g,b]);
    118                } else {
    119 
    120                 ...    } else {
    121                            return colorData[info.fColorCount - 1][r,g,b];
    122                        }
    123                 ...
    124            }
    125        }
    126  */
    127 static void gradientFunctionCode(const SkShader::GradientInfo& info,
    128                                  SkString* result) {
    129     /* We want to linearly interpolate from the previous color to the next.
    130        Scale the colors from 0..255 to 0..1 and determine the multipliers
    131        for interpolation.
    132        C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
    133      */
    134     static const int kColorComponents = 3;
    135     typedef SkScalar ColorTuple[kColorComponents];
    136     SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
    137     ColorTuple *colorData = colorDataAlloc.get();
    138     const SkScalar scale = SkScalarInvert(SkIntToScalar(255));
    139     for (int i = 0; i < info.fColorCount; i++) {
    140         colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale);
    141         colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale);
    142         colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale);
    143     }
    144 
    145     // Clamp the initial color.
    146     result->append("dup 0 le {pop ");
    147     result->appendScalar(colorData[0][0]);
    148     result->append(" ");
    149     result->appendScalar(colorData[0][1]);
    150     result->append(" ");
    151     result->appendScalar(colorData[0][2]);
    152     result->append(" }\n");
    153 
    154     // The gradient colors.
    155     for (int i = 1 ; i < info.fColorCount; i++) {
    156         result->append("{dup ");
    157         result->appendScalar(info.fColorOffsets[i]);
    158         result->append(" le {");
    159         if (info.fColorOffsets[i - 1] != 0) {
    160             result->appendScalar(info.fColorOffsets[i - 1]);
    161             result->append(" sub\n");
    162         }
    163 
    164         interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1],
    165                              colorData[i], colorData[i - 1], result);
    166         result->append("}\n");
    167     }
    168 
    169     // Clamp the final color.
    170     result->append("{pop ");
    171     result->appendScalar(colorData[info.fColorCount - 1][0]);
    172     result->append(" ");
    173     result->appendScalar(colorData[info.fColorCount - 1][1]);
    174     result->append(" ");
    175     result->appendScalar(colorData[info.fColorCount - 1][2]);
    176 
    177     for (int i = 0 ; i < info.fColorCount; i++) {
    178         result->append("} ifelse\n");
    179     }
    180 }
    181 
    182 /* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
    183 static void tileModeCode(SkShader::TileMode mode, SkString* result) {
    184     if (mode == SkShader::kRepeat_TileMode) {
    185         result->append("dup truncate sub\n");  // Get the fractional part.
    186         result->append("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
    187         return;
    188     }
    189 
    190     if (mode == SkShader::kMirror_TileMode) {
    191         // Map t mod 2 into [0, 1, 1, 0].
    192         //               Code                     Stack
    193         result->append("abs "                 // Map negative to positive.
    194                        "dup "                 // t.s t.s
    195                        "truncate "            // t.s t
    196                        "dup "                 // t.s t t
    197                        "cvi "                 // t.s t T
    198                        "2 mod "               // t.s t (i mod 2)
    199                        "1 eq "                // t.s t true|false
    200                        "3 1 roll "            // true|false t.s t
    201                        "sub "                 // true|false 0.s
    202                        "exch "                // 0.s true|false
    203                        "{1 exch sub} if\n");  // 1 - 0.s|0.s
    204     }
    205 }
    206 
    207 static SkString linearCode(const SkShader::GradientInfo& info) {
    208     SkString function("{pop\n");  // Just ditch the y value.
    209     tileModeCode(info.fTileMode, &function);
    210     gradientFunctionCode(info, &function);
    211     function.append("}");
    212     return function;
    213 }
    214 
    215 static SkString radialCode(const SkShader::GradientInfo& info) {
    216     SkString function("{");
    217     // Find the distance from the origin.
    218     function.append("dup "      // x y y
    219                     "mul "      // x y^2
    220                     "exch "     // y^2 x
    221                     "dup "      // y^2 x x
    222                     "mul "      // y^2 x^2
    223                     "add "      // y^2+x^2
    224                     "sqrt\n");  // sqrt(y^2+x^2)
    225 
    226     tileModeCode(info.fTileMode, &function);
    227     gradientFunctionCode(info, &function);
    228     function.append("}");
    229     return function;
    230 }
    231 
    232 /* The math here is all based on the description in Two_Point_Radial_Gradient,
    233    with one simplification, the coordinate space has been scaled so that
    234    Dr = 1.  This means we don't need to scale the entire equation by 1/Dr^2.
    235  */
    236 static SkString twoPointRadialCode(const SkShader::GradientInfo& info) {
    237     SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX;
    238     SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY;
    239     SkScalar sr = info.fRadius[0];
    240     SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1;
    241     bool posRoot = info.fRadius[1] > info.fRadius[0];
    242 
    243     // We start with a stack of (x y), copy it and then consume one copy in
    244     // order to calculate b and the other to calculate c.
    245     SkString function("{");
    246     function.append("2 copy ");
    247 
    248     // Calculate -b and b^2.
    249     function.appendScalar(dy);
    250     function.append(" mul exch ");
    251     function.appendScalar(dx);
    252     function.append(" mul add ");
    253     function.appendScalar(sr);
    254     function.append(" sub 2 mul neg dup dup mul\n");
    255 
    256     // Calculate c
    257     function.append("4 2 roll dup mul exch dup mul add ");
    258     function.appendScalar(SkScalarMul(sr, sr));
    259     function.append(" sub\n");
    260 
    261     // Calculate the determinate
    262     function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
    263     function.append(" mul sub abs sqrt\n");
    264 
    265     // And then the final value of t.
    266     if (posRoot) {
    267         function.append("sub ");
    268     } else {
    269         function.append("add ");
    270     }
    271     function.appendScalar(SkScalarMul(SkIntToScalar(2), a));
    272     function.append(" div\n");
    273 
    274     tileModeCode(info.fTileMode, &function);
    275     gradientFunctionCode(info, &function);
    276     function.append("}");
    277     return function;
    278 }
    279 
    280 /* Conical gradient shader, based on the Canvas spec for radial gradients
    281    See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
    282  */
    283 static SkString twoPointConicalCode(const SkShader::GradientInfo& info) {
    284     SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
    285     SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
    286     SkScalar r0 = info.fRadius[0];
    287     SkScalar dr = info.fRadius[1] - info.fRadius[0];
    288     SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) -
    289                  SkScalarMul(dr, dr);
    290 
    291     // First compute t, if the pixel falls outside the cone, then we'll end
    292     // with 'false' on the stack, otherwise we'll push 'true' with t below it
    293 
    294     // We start with a stack of (x y), copy it and then consume one copy in
    295     // order to calculate b and the other to calculate c.
    296     SkString function("{");
    297     function.append("2 copy ");
    298 
    299     // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
    300     function.appendScalar(dy);
    301     function.append(" mul exch ");
    302     function.appendScalar(dx);
    303     function.append(" mul add ");
    304     function.appendScalar(SkScalarMul(r0, dr));
    305     function.append(" add -2 mul dup dup mul\n");
    306 
    307     // c = x^2 + y^2 + radius0^2
    308     function.append("4 2 roll dup mul exch dup mul add ");
    309     function.appendScalar(SkScalarMul(r0, r0));
    310     function.append(" sub dup 4 1 roll\n");
    311 
    312     // Contents of the stack at this point: c, b, b^2, c
    313 
    314     // if a = 0, then we collapse to a simpler linear case
    315     if (a == 0) {
    316 
    317         // t = -c/b
    318         function.append("pop pop div neg dup ");
    319 
    320         // compute radius(t)
    321         function.appendScalar(dr);
    322         function.append(" mul ");
    323         function.appendScalar(r0);
    324         function.append(" add\n");
    325 
    326         // if r(t) < 0, then it's outside the cone
    327         function.append("0 lt {pop false} {true} ifelse\n");
    328 
    329     } else {
    330 
    331         // quadratic case: the Canvas spec wants the largest
    332         // root t for which radius(t) > 0
    333 
    334         // compute the discriminant (b^2 - 4ac)
    335         function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
    336         function.append(" mul sub dup\n");
    337 
    338         // if d >= 0, proceed
    339         function.append("0 ge {\n");
    340 
    341         // an intermediate value we'll use to compute the roots:
    342         // q = -0.5 * (b +/- sqrt(d))
    343         function.append("sqrt exch dup 0 lt {exch -1 mul} if");
    344         function.append(" add -0.5 mul dup\n");
    345 
    346         // first root = q / a
    347         function.appendScalar(a);
    348         function.append(" div\n");
    349 
    350         // second root = c / q
    351         function.append("3 1 roll div\n");
    352 
    353         // put the larger root on top of the stack
    354         function.append("2 copy gt {exch} if\n");
    355 
    356         // compute radius(t) for larger root
    357         function.append("dup ");
    358         function.appendScalar(dr);
    359         function.append(" mul ");
    360         function.appendScalar(r0);
    361         function.append(" add\n");
    362 
    363         // if r(t) > 0, we have our t, pop off the smaller root and we're done
    364         function.append(" 0 gt {exch pop true}\n");
    365 
    366         // otherwise, throw out the larger one and try the smaller root
    367         function.append("{pop dup\n");
    368         function.appendScalar(dr);
    369         function.append(" mul ");
    370         function.appendScalar(r0);
    371         function.append(" add\n");
    372 
    373         // if r(t) < 0, push false, otherwise the smaller root is our t
    374         function.append("0 le {pop false} {true} ifelse\n");
    375         function.append("} ifelse\n");
    376 
    377         // d < 0, clear the stack and push false
    378         function.append("} {pop pop pop false} ifelse\n");
    379     }
    380 
    381     // if the pixel is in the cone, proceed to compute a color
    382     function.append("{");
    383     tileModeCode(info.fTileMode, &function);
    384     gradientFunctionCode(info, &function);
    385 
    386     // otherwise, just write black
    387     function.append("} {0 0 0} ifelse }");
    388 
    389     return function;
    390 }
    391 
    392 static SkString sweepCode(const SkShader::GradientInfo& info) {
    393     SkString function("{exch atan 360 div\n");
    394     tileModeCode(info.fTileMode, &function);
    395     gradientFunctionCode(info, &function);
    396     function.append("}");
    397     return function;
    398 }
    399 
    400 class SkPDFShader::State {
    401 public:
    402     SkShader::GradientType fType;
    403     SkShader::GradientInfo fInfo;
    404     SkAutoFree fColorData;    // This provides storage for arrays in fInfo.
    405     SkMatrix fCanvasTransform;
    406     SkMatrix fShaderTransform;
    407     SkIRect fBBox;
    408 
    409     SkBitmap fImage;
    410     uint32_t fPixelGeneration;
    411     SkShader::TileMode fImageTileModes[2];
    412 
    413     State(const SkShader& shader, const SkMatrix& canvasTransform,
    414           const SkIRect& bbox);
    415 
    416     bool operator==(const State& b) const;
    417 
    418     SkPDFShader::State* CreateAlphaToLuminosityState() const;
    419     SkPDFShader::State* CreateOpaqueState() const;
    420 
    421     bool GradientHasAlpha() const;
    422 
    423 private:
    424     State(const State& other);
    425     State operator=(const State& rhs);
    426     void AllocateGradientInfoStorage();
    427 };
    428 
    429 class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader {
    430 public:
    431     explicit SkPDFFunctionShader(SkPDFShader::State* state);
    432     virtual ~SkPDFFunctionShader() {
    433         if (isValid()) {
    434             RemoveShader(this);
    435         }
    436         fResources.unrefAll();
    437     }
    438 
    439     virtual bool isValid() { return fResources.count() > 0; }
    440 
    441     void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
    442                       SkTSet<SkPDFObject*>* newResourceObjects) {
    443         GetResourcesHelper(&fResources,
    444                            knownResourceObjects,
    445                            newResourceObjects);
    446     }
    447 
    448 private:
    449     static SkPDFObject* RangeObject();
    450 
    451     SkTDArray<SkPDFObject*> fResources;
    452     SkAutoTDelete<const SkPDFShader::State> fState;
    453 
    454     SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain);
    455 };
    456 
    457 /**
    458  * A shader for PDF gradients. This encapsulates the function shader
    459  * inside a tiling pattern while providing a common pattern interface.
    460  * The encapsulation allows the use of a SMask for transparency gradients.
    461  */
    462 class SkPDFAlphaFunctionShader : public SkPDFStream, public SkPDFShader {
    463 public:
    464     explicit SkPDFAlphaFunctionShader(SkPDFShader::State* state);
    465     virtual ~SkPDFAlphaFunctionShader() {
    466         if (isValid()) {
    467             RemoveShader(this);
    468         }
    469     }
    470 
    471     virtual bool isValid() {
    472         return fColorShader.get() != NULL;
    473     }
    474 
    475 private:
    476     SkAutoTDelete<const SkPDFShader::State> fState;
    477 
    478     SkPDFGraphicState* CreateSMaskGraphicState();
    479 
    480     void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
    481                       SkTSet<SkPDFObject*>* newResourceObjects) {
    482         fResourceDict->getReferencedResources(knownResourceObjects,
    483                                               newResourceObjects,
    484                                               true);
    485     }
    486 
    487     SkAutoTUnref<SkPDFObject> fColorShader;
    488     SkAutoTUnref<SkPDFResourceDict> fResourceDict;
    489 };
    490 
    491 class SkPDFImageShader : public SkPDFStream, public SkPDFShader {
    492 public:
    493     explicit SkPDFImageShader(SkPDFShader::State* state);
    494     virtual ~SkPDFImageShader() {
    495         if (isValid()) {
    496             RemoveShader(this);
    497         }
    498         fResources.unrefAll();
    499     }
    500 
    501     virtual bool isValid() { return size() > 0; }
    502 
    503     void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
    504                       SkTSet<SkPDFObject*>* newResourceObjects) {
    505         GetResourcesHelper(&fResources.toArray(),
    506                            knownResourceObjects,
    507                            newResourceObjects);
    508     }
    509 
    510 private:
    511     SkTSet<SkPDFObject*> fResources;
    512     SkAutoTDelete<const SkPDFShader::State> fState;
    513 };
    514 
    515 SkPDFShader::SkPDFShader() {}
    516 
    517 // static
    518 SkPDFObject* SkPDFShader::GetPDFShaderByState(State* inState) {
    519     SkPDFObject* result;
    520 
    521     SkAutoTDelete<State> shaderState(inState);
    522     if (shaderState.get()->fType == SkShader::kNone_GradientType &&
    523             shaderState.get()->fImage.isNull()) {
    524         // TODO(vandebo) This drops SKComposeShader on the floor.  We could
    525         // handle compose shader by pulling things up to a layer, drawing with
    526         // the first shader, applying the xfer mode and drawing again with the
    527         // second shader, then applying the layer to the original drawing.
    528         return NULL;
    529     }
    530 
    531     ShaderCanonicalEntry entry(NULL, shaderState.get());
    532     int index = CanonicalShaders().find(entry);
    533     if (index >= 0) {
    534         result = CanonicalShaders()[index].fPDFShader;
    535         result->ref();
    536         return result;
    537     }
    538 
    539     bool valid = false;
    540     // The PDFShader takes ownership of the shaderSate.
    541     if (shaderState.get()->fType == SkShader::kNone_GradientType) {
    542         SkPDFImageShader* imageShader =
    543             new SkPDFImageShader(shaderState.detach());
    544         valid = imageShader->isValid();
    545         result = imageShader;
    546     } else {
    547         if (shaderState.get()->GradientHasAlpha()) {
    548             SkPDFAlphaFunctionShader* gradientShader =
    549                 SkNEW_ARGS(SkPDFAlphaFunctionShader, (shaderState.detach()));
    550             valid = gradientShader->isValid();
    551             result = gradientShader;
    552         } else {
    553             SkPDFFunctionShader* functionShader =
    554                 SkNEW_ARGS(SkPDFFunctionShader, (shaderState.detach()));
    555             valid = functionShader->isValid();
    556             result = functionShader;
    557         }
    558     }
    559     if (!valid) {
    560         delete result;
    561         return NULL;
    562     }
    563     entry.fPDFShader = result;
    564     CanonicalShaders().push(entry);
    565     return result;  // return the reference that came from new.
    566 }
    567 
    568 // static
    569 void SkPDFShader::RemoveShader(SkPDFObject* shader) {
    570     SkAutoMutexAcquire lock(CanonicalShadersMutex());
    571     ShaderCanonicalEntry entry(shader, NULL);
    572     int index = CanonicalShaders().find(entry);
    573     SkASSERT(index >= 0);
    574     CanonicalShaders().removeShuffle(index);
    575 }
    576 
    577 // static
    578 SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader,
    579                                        const SkMatrix& matrix,
    580                                        const SkIRect& surfaceBBox) {
    581     SkAutoMutexAcquire lock(CanonicalShadersMutex());
    582     return GetPDFShaderByState(
    583             SkNEW_ARGS(State, (shader, matrix, surfaceBBox)));
    584 }
    585 
    586 // static
    587 SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::CanonicalShaders() {
    588     // This initialization is only thread safe with gcc.
    589     static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders;
    590     return gCanonicalShaders;
    591 }
    592 
    593 // static
    594 SkBaseMutex& SkPDFShader::CanonicalShadersMutex() {
    595     // This initialization is only thread safe with gcc or when
    596     // POD-style mutex initialization is used.
    597     SK_DECLARE_STATIC_MUTEX(gCanonicalShadersMutex);
    598     return gCanonicalShadersMutex;
    599 }
    600 
    601 // static
    602 SkPDFObject* SkPDFFunctionShader::RangeObject() {
    603     // This initialization is only thread safe with gcc.
    604     static SkPDFArray* range = NULL;
    605     // This method is only used with CanonicalShadersMutex, so it's safe to
    606     // populate domain.
    607     if (range == NULL) {
    608         range = new SkPDFArray;
    609         range->reserve(6);
    610         range->appendInt(0);
    611         range->appendInt(1);
    612         range->appendInt(0);
    613         range->appendInt(1);
    614         range->appendInt(0);
    615         range->appendInt(1);
    616     }
    617     return range;
    618 }
    619 
    620 static SkPDFResourceDict* get_gradient_resource_dict(
    621         SkPDFObject* functionShader,
    622         SkPDFObject* gState) {
    623     SkPDFResourceDict* dict = new SkPDFResourceDict();
    624 
    625     if (functionShader != NULL) {
    626         dict->insertResourceAsReference(
    627                 SkPDFResourceDict::kPattern_ResourceType, 0, functionShader);
    628     }
    629     if (gState != NULL) {
    630         dict->insertResourceAsReference(
    631                 SkPDFResourceDict::kExtGState_ResourceType, 0, gState);
    632     }
    633 
    634     return dict;
    635 }
    636 
    637 static void populate_tiling_pattern_dict(SkPDFDict* pattern,
    638                                       SkRect& bbox, SkPDFDict* resources,
    639                                       const SkMatrix& matrix) {
    640     const int kTiling_PatternType = 1;
    641     const int kColoredTilingPattern_PaintType = 1;
    642     const int kConstantSpacing_TilingType = 1;
    643 
    644     pattern->insertName("Type", "Pattern");
    645     pattern->insertInt("PatternType", kTiling_PatternType);
    646     pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
    647     pattern->insertInt("TilingType", kConstantSpacing_TilingType);
    648     pattern->insert("BBox", SkPDFUtils::RectToArray(bbox))->unref();
    649     pattern->insertScalar("XStep", bbox.width());
    650     pattern->insertScalar("YStep", bbox.height());
    651     pattern->insert("Resources", resources);
    652     if (!matrix.isIdentity()) {
    653         pattern->insert("Matrix", SkPDFUtils::MatrixToArray(matrix))->unref();
    654     }
    655 }
    656 
    657 /**
    658  * Creates a content stream which fills the pattern P0 across bounds.
    659  * @param gsIndex A graphics state resource index to apply, or <0 if no
    660  * graphics state to apply.
    661  */
    662 static SkStream* create_pattern_fill_content(int gsIndex, SkRect& bounds) {
    663     SkDynamicMemoryWStream content;
    664     if (gsIndex >= 0) {
    665         SkPDFUtils::ApplyGraphicState(gsIndex, &content);
    666     }
    667     SkPDFUtils::ApplyPattern(0, &content);
    668     SkPDFUtils::AppendRectangle(bounds, &content);
    669     SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType,
    670                           &content);
    671 
    672     return content.detachAsStream();
    673 }
    674 
    675 /**
    676  * Creates a ExtGState with the SMask set to the luminosityShader in
    677  * luminosity mode. The shader pattern extends to the bbox.
    678  */
    679 SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState() {
    680     SkRect bbox;
    681     bbox.set(fState.get()->fBBox);
    682 
    683     SkAutoTUnref<SkPDFObject> luminosityShader(
    684             SkPDFShader::GetPDFShaderByState(
    685                  fState->CreateAlphaToLuminosityState()));
    686 
    687     SkAutoTUnref<SkStream> alphaStream(create_pattern_fill_content(-1, bbox));
    688 
    689     SkAutoTUnref<SkPDFResourceDict>
    690         resources(get_gradient_resource_dict(luminosityShader, NULL));
    691 
    692     SkAutoTUnref<SkPDFFormXObject> alphaMask(
    693             new SkPDFFormXObject(alphaStream.get(), bbox, resources.get()));
    694 
    695     return SkPDFGraphicState::GetSMaskGraphicState(
    696             alphaMask.get(), false,
    697             SkPDFGraphicState::kLuminosity_SMaskMode);
    698 }
    699 
    700 SkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state)
    701         : fState(state) {
    702     SkRect bbox;
    703     bbox.set(fState.get()->fBBox);
    704 
    705     fColorShader.reset(
    706             SkPDFShader::GetPDFShaderByState(state->CreateOpaqueState()));
    707 
    708     // Create resource dict with alpha graphics state as G0 and
    709     // pattern shader as P0, then write content stream.
    710     SkAutoTUnref<SkPDFGraphicState> alphaGs(CreateSMaskGraphicState());
    711     fResourceDict.reset(
    712             get_gradient_resource_dict(fColorShader.get(), alphaGs.get()));
    713 
    714     SkAutoTUnref<SkStream> colorStream(
    715             create_pattern_fill_content(0, bbox));
    716     setData(colorStream.get());
    717 
    718     populate_tiling_pattern_dict(this, bbox, fResourceDict.get(),
    719                                  SkMatrix::I());
    720 }
    721 
    722 SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
    723         : SkPDFDict("Pattern"),
    724           fState(state) {
    725     SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL;
    726     SkPoint transformPoints[2];
    727 
    728     // Depending on the type of the gradient, we want to transform the
    729     // coordinate space in different ways.
    730     const SkShader::GradientInfo* info = &fState.get()->fInfo;
    731     transformPoints[0] = info->fPoint[0];
    732     transformPoints[1] = info->fPoint[1];
    733     switch (fState.get()->fType) {
    734         case SkShader::kLinear_GradientType:
    735             codeFunction = &linearCode;
    736             break;
    737         case SkShader::kRadial_GradientType:
    738             transformPoints[1] = transformPoints[0];
    739             transformPoints[1].fX += info->fRadius[0];
    740             codeFunction = &radialCode;
    741             break;
    742         case SkShader::kRadial2_GradientType: {
    743             // Bail out if the radii are the same.  Empty fResources signals
    744             // an error and isValid will return false.
    745             if (info->fRadius[0] == info->fRadius[1]) {
    746                 return;
    747             }
    748             transformPoints[1] = transformPoints[0];
    749             SkScalar dr = info->fRadius[1] - info->fRadius[0];
    750             transformPoints[1].fX += dr;
    751             codeFunction = &twoPointRadialCode;
    752             break;
    753         }
    754         case SkShader::kConical_GradientType: {
    755             transformPoints[1] = transformPoints[0];
    756             transformPoints[1].fX += SK_Scalar1;
    757             codeFunction = &twoPointConicalCode;
    758             break;
    759         }
    760         case SkShader::kSweep_GradientType:
    761             transformPoints[1] = transformPoints[0];
    762             transformPoints[1].fX += SK_Scalar1;
    763             codeFunction = &sweepCode;
    764             break;
    765         case SkShader::kColor_GradientType:
    766         case SkShader::kNone_GradientType:
    767         default:
    768             return;
    769     }
    770 
    771     // Move any scaling (assuming a unit gradient) or translation
    772     // (and rotation for linear gradient), of the final gradient from
    773     // info->fPoints to the matrix (updating bbox appropriately).  Now
    774     // the gradient can be drawn on on the unit segment.
    775     SkMatrix mapperMatrix;
    776     unitToPointsMatrix(transformPoints, &mapperMatrix);
    777     SkMatrix finalMatrix = fState.get()->fCanvasTransform;
    778     finalMatrix.preConcat(fState.get()->fShaderTransform);
    779     finalMatrix.preConcat(mapperMatrix);
    780 
    781     SkRect bbox;
    782     bbox.set(fState.get()->fBBox);
    783     if (!transformBBox(finalMatrix, &bbox)) {
    784         return;
    785     }
    786 
    787     SkAutoTUnref<SkPDFArray> domain(new SkPDFArray);
    788     domain->reserve(4);
    789     domain->appendScalar(bbox.fLeft);
    790     domain->appendScalar(bbox.fRight);
    791     domain->appendScalar(bbox.fTop);
    792     domain->appendScalar(bbox.fBottom);
    793 
    794     SkString functionCode;
    795     // The two point radial gradient further references fState.get()->fInfo
    796     // in translating from x, y coordinates to the t parameter. So, we have
    797     // to transform the points and radii according to the calculated matrix.
    798     if (fState.get()->fType == SkShader::kRadial2_GradientType) {
    799         SkShader::GradientInfo twoPointRadialInfo = *info;
    800         SkMatrix inverseMapperMatrix;
    801         if (!mapperMatrix.invert(&inverseMapperMatrix)) {
    802             return;
    803         }
    804         inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
    805         twoPointRadialInfo.fRadius[0] =
    806             inverseMapperMatrix.mapRadius(info->fRadius[0]);
    807         twoPointRadialInfo.fRadius[1] =
    808             inverseMapperMatrix.mapRadius(info->fRadius[1]);
    809         functionCode = codeFunction(twoPointRadialInfo);
    810     } else {
    811         functionCode = codeFunction(*info);
    812     }
    813 
    814     SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict);
    815     pdfShader->insertInt("ShadingType", 1);
    816     pdfShader->insertName("ColorSpace", "DeviceRGB");
    817     pdfShader->insert("Domain", domain.get());
    818 
    819     SkPDFStream* function = makePSFunction(functionCode, domain.get());
    820     pdfShader->insert("Function", new SkPDFObjRef(function))->unref();
    821     fResources.push(function);  // Pass ownership to resource list.
    822 
    823     insertInt("PatternType", 2);
    824     insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
    825     insert("Shading", pdfShader.get());
    826 }
    827 
    828 SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) {
    829     fState.get()->fImage.lockPixels();
    830 
    831     SkMatrix finalMatrix = fState.get()->fCanvasTransform;
    832     finalMatrix.preConcat(fState.get()->fShaderTransform);
    833     SkRect surfaceBBox;
    834     surfaceBBox.set(fState.get()->fBBox);
    835     if (!transformBBox(finalMatrix, &surfaceBBox)) {
    836         return;
    837     }
    838 
    839     SkMatrix unflip;
    840     unflip.setTranslate(0, SkScalarRoundToScalar(surfaceBBox.height()));
    841     unflip.preScale(SK_Scalar1, -SK_Scalar1);
    842     SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()),
    843                                  SkScalarRound(surfaceBBox.height()));
    844     SkPDFDevice pattern(size, size, unflip);
    845     SkCanvas canvas(&pattern);
    846     canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop);
    847     finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop);
    848 
    849     const SkBitmap* image = &fState.get()->fImage;
    850     SkScalar width = SkIntToScalar(image->width());
    851     SkScalar height = SkIntToScalar(image->height());
    852     SkShader::TileMode tileModes[2];
    853     tileModes[0] = fState.get()->fImageTileModes[0];
    854     tileModes[1] = fState.get()->fImageTileModes[1];
    855 
    856     canvas.drawBitmap(*image, 0, 0);
    857     SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop,
    858                                           width, height);
    859 
    860     // Tiling is implied.  First we handle mirroring.
    861     if (tileModes[0] == SkShader::kMirror_TileMode) {
    862         SkMatrix xMirror;
    863         xMirror.setScale(-1, 1);
    864         xMirror.postTranslate(2 * width, 0);
    865         canvas.drawBitmapMatrix(*image, xMirror);
    866         patternBBox.fRight += width;
    867     }
    868     if (tileModes[1] == SkShader::kMirror_TileMode) {
    869         SkMatrix yMirror;
    870         yMirror.setScale(SK_Scalar1, -SK_Scalar1);
    871         yMirror.postTranslate(0, 2 * height);
    872         canvas.drawBitmapMatrix(*image, yMirror);
    873         patternBBox.fBottom += height;
    874     }
    875     if (tileModes[0] == SkShader::kMirror_TileMode &&
    876             tileModes[1] == SkShader::kMirror_TileMode) {
    877         SkMatrix mirror;
    878         mirror.setScale(-1, -1);
    879         mirror.postTranslate(2 * width, 2 * height);
    880         canvas.drawBitmapMatrix(*image, mirror);
    881     }
    882 
    883     // Then handle Clamping, which requires expanding the pattern canvas to
    884     // cover the entire surfaceBBox.
    885 
    886     // If both x and y are in clamp mode, we start by filling in the corners.
    887     // (Which are just a rectangles of the corner colors.)
    888     if (tileModes[0] == SkShader::kClamp_TileMode &&
    889             tileModes[1] == SkShader::kClamp_TileMode) {
    890         SkPaint paint;
    891         SkRect rect;
    892         rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0);
    893         if (!rect.isEmpty()) {
    894             paint.setColor(image->getColor(0, 0));
    895             canvas.drawRect(rect, paint);
    896         }
    897 
    898         rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0);
    899         if (!rect.isEmpty()) {
    900             paint.setColor(image->getColor(image->width() - 1, 0));
    901             canvas.drawRect(rect, paint);
    902         }
    903 
    904         rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight,
    905                                 surfaceBBox.fBottom);
    906         if (!rect.isEmpty()) {
    907             paint.setColor(image->getColor(image->width() - 1,
    908                                            image->height() - 1));
    909             canvas.drawRect(rect, paint);
    910         }
    911 
    912         rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0,
    913                                 surfaceBBox.fBottom);
    914         if (!rect.isEmpty()) {
    915             paint.setColor(image->getColor(0, image->height() - 1));
    916             canvas.drawRect(rect, paint);
    917         }
    918     }
    919 
    920     // Then expand the left, right, top, then bottom.
    921     if (tileModes[0] == SkShader::kClamp_TileMode) {
    922         SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height());
    923         if (surfaceBBox.fLeft < 0) {
    924             SkBitmap left;
    925             SkAssertResult(image->extractSubset(&left, subset));
    926 
    927             SkMatrix leftMatrix;
    928             leftMatrix.setScale(-surfaceBBox.fLeft, 1);
    929             leftMatrix.postTranslate(surfaceBBox.fLeft, 0);
    930             canvas.drawBitmapMatrix(left, leftMatrix);
    931 
    932             if (tileModes[1] == SkShader::kMirror_TileMode) {
    933                 leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
    934                 leftMatrix.postTranslate(0, 2 * height);
    935                 canvas.drawBitmapMatrix(left, leftMatrix);
    936             }
    937             patternBBox.fLeft = 0;
    938         }
    939 
    940         if (surfaceBBox.fRight > width) {
    941             SkBitmap right;
    942             subset.offset(image->width() - 1, 0);
    943             SkAssertResult(image->extractSubset(&right, subset));
    944 
    945             SkMatrix rightMatrix;
    946             rightMatrix.setScale(surfaceBBox.fRight - width, 1);
    947             rightMatrix.postTranslate(width, 0);
    948             canvas.drawBitmapMatrix(right, rightMatrix);
    949 
    950             if (tileModes[1] == SkShader::kMirror_TileMode) {
    951                 rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
    952                 rightMatrix.postTranslate(0, 2 * height);
    953                 canvas.drawBitmapMatrix(right, rightMatrix);
    954             }
    955             patternBBox.fRight = surfaceBBox.width();
    956         }
    957     }
    958 
    959     if (tileModes[1] == SkShader::kClamp_TileMode) {
    960         SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1);
    961         if (surfaceBBox.fTop < 0) {
    962             SkBitmap top;
    963             SkAssertResult(image->extractSubset(&top, subset));
    964 
    965             SkMatrix topMatrix;
    966             topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop);
    967             topMatrix.postTranslate(0, surfaceBBox.fTop);
    968             canvas.drawBitmapMatrix(top, topMatrix);
    969 
    970             if (tileModes[0] == SkShader::kMirror_TileMode) {
    971                 topMatrix.postScale(-1, 1);
    972                 topMatrix.postTranslate(2 * width, 0);
    973                 canvas.drawBitmapMatrix(top, topMatrix);
    974             }
    975             patternBBox.fTop = 0;
    976         }
    977 
    978         if (surfaceBBox.fBottom > height) {
    979             SkBitmap bottom;
    980             subset.offset(0, image->height() - 1);
    981             SkAssertResult(image->extractSubset(&bottom, subset));
    982 
    983             SkMatrix bottomMatrix;
    984             bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height);
    985             bottomMatrix.postTranslate(0, height);
    986             canvas.drawBitmapMatrix(bottom, bottomMatrix);
    987 
    988             if (tileModes[0] == SkShader::kMirror_TileMode) {
    989                 bottomMatrix.postScale(-1, 1);
    990                 bottomMatrix.postTranslate(2 * width, 0);
    991                 canvas.drawBitmapMatrix(bottom, bottomMatrix);
    992             }
    993             patternBBox.fBottom = surfaceBBox.height();
    994         }
    995     }
    996 
    997     // Put the canvas into the pattern stream (fContent).
    998     SkAutoTUnref<SkStream> content(pattern.content());
    999     setData(content.get());
   1000     SkPDFResourceDict* resourceDict = pattern.getResourceDict();
   1001     resourceDict->getReferencedResources(fResources, &fResources, false);
   1002 
   1003     populate_tiling_pattern_dict(this, patternBBox,
   1004                                  pattern.getResourceDict(), finalMatrix);
   1005 
   1006     fState.get()->fImage.unlockPixels();
   1007 }
   1008 
   1009 SkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode,
   1010                                                  SkPDFArray* domain) {
   1011     SkAutoDataUnref funcData(SkData::NewWithCopy(psCode.c_str(),
   1012                                                  psCode.size()));
   1013     SkPDFStream* result = new SkPDFStream(funcData.get());
   1014     result->insertInt("FunctionType", 4);
   1015     result->insert("Domain", domain);
   1016     result->insert("Range", RangeObject());
   1017     return result;
   1018 }
   1019 
   1020 SkPDFShader::ShaderCanonicalEntry::ShaderCanonicalEntry(SkPDFObject* pdfShader,
   1021                                                         const State* state)
   1022     : fPDFShader(pdfShader),
   1023       fState(state) {
   1024 }
   1025 
   1026 bool SkPDFShader::ShaderCanonicalEntry::operator==(
   1027         const ShaderCanonicalEntry& b) const {
   1028     return fPDFShader == b.fPDFShader ||
   1029            (fState != NULL && b.fState != NULL && *fState == *b.fState);
   1030 }
   1031 
   1032 bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const {
   1033     if (fType != b.fType ||
   1034             fCanvasTransform != b.fCanvasTransform ||
   1035             fShaderTransform != b.fShaderTransform ||
   1036             fBBox != b.fBBox) {
   1037         return false;
   1038     }
   1039 
   1040     if (fType == SkShader::kNone_GradientType) {
   1041         if (fPixelGeneration != b.fPixelGeneration ||
   1042                 fPixelGeneration == 0 ||
   1043                 fImageTileModes[0] != b.fImageTileModes[0] ||
   1044                 fImageTileModes[1] != b.fImageTileModes[1]) {
   1045             return false;
   1046         }
   1047     } else {
   1048         if (fInfo.fColorCount != b.fInfo.fColorCount ||
   1049                 memcmp(fInfo.fColors, b.fInfo.fColors,
   1050                        sizeof(SkColor) * fInfo.fColorCount) != 0 ||
   1051                 memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets,
   1052                        sizeof(SkScalar) * fInfo.fColorCount) != 0 ||
   1053                 fInfo.fPoint[0] != b.fInfo.fPoint[0] ||
   1054                 fInfo.fTileMode != b.fInfo.fTileMode) {
   1055             return false;
   1056         }
   1057 
   1058         switch (fType) {
   1059             case SkShader::kLinear_GradientType:
   1060                 if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) {
   1061                     return false;
   1062                 }
   1063                 break;
   1064             case SkShader::kRadial_GradientType:
   1065                 if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) {
   1066                     return false;
   1067                 }
   1068                 break;
   1069             case SkShader::kRadial2_GradientType:
   1070             case SkShader::kConical_GradientType:
   1071                 if (fInfo.fPoint[1] != b.fInfo.fPoint[1] ||
   1072                         fInfo.fRadius[0] != b.fInfo.fRadius[0] ||
   1073                         fInfo.fRadius[1] != b.fInfo.fRadius[1]) {
   1074                     return false;
   1075                 }
   1076                 break;
   1077             case SkShader::kSweep_GradientType:
   1078             case SkShader::kNone_GradientType:
   1079             case SkShader::kColor_GradientType:
   1080                 break;
   1081         }
   1082     }
   1083     return true;
   1084 }
   1085 
   1086 SkPDFShader::State::State(const SkShader& shader,
   1087                           const SkMatrix& canvasTransform, const SkIRect& bbox)
   1088         : fCanvasTransform(canvasTransform),
   1089           fBBox(bbox),
   1090           fPixelGeneration(0) {
   1091     fInfo.fColorCount = 0;
   1092     fInfo.fColors = NULL;
   1093     fInfo.fColorOffsets = NULL;
   1094     fShaderTransform = shader.getLocalMatrix();
   1095     fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode;
   1096 
   1097     fType = shader.asAGradient(&fInfo);
   1098 
   1099     if (fType == SkShader::kNone_GradientType) {
   1100         SkShader::BitmapType bitmapType;
   1101         SkMatrix matrix;
   1102         bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes);
   1103         if (bitmapType != SkShader::kDefault_BitmapType) {
   1104             fImage.reset();
   1105             return;
   1106         }
   1107         SkASSERT(matrix.isIdentity());
   1108         fPixelGeneration = fImage.getGenerationID();
   1109     } else {
   1110         AllocateGradientInfoStorage();
   1111         shader.asAGradient(&fInfo);
   1112     }
   1113 }
   1114 
   1115 SkPDFShader::State::State(const SkPDFShader::State& other)
   1116   : fType(other.fType),
   1117     fCanvasTransform(other.fCanvasTransform),
   1118     fShaderTransform(other.fShaderTransform),
   1119     fBBox(other.fBBox)
   1120 {
   1121     // Only gradients supported for now, since that is all that is used.
   1122     // If needed, image state copy constructor can be added here later.
   1123     SkASSERT(fType != SkShader::kNone_GradientType);
   1124 
   1125     if (fType != SkShader::kNone_GradientType) {
   1126         fInfo = other.fInfo;
   1127 
   1128         AllocateGradientInfoStorage();
   1129         for (int i = 0; i < fInfo.fColorCount; i++) {
   1130             fInfo.fColors[i] = other.fInfo.fColors[i];
   1131             fInfo.fColorOffsets[i] = other.fInfo.fColorOffsets[i];
   1132         }
   1133     }
   1134 }
   1135 
   1136 /**
   1137  * Create a copy of this gradient state with alpha assigned to RGB luminousity.
   1138  * Only valid for gradient states.
   1139  */
   1140 SkPDFShader::State* SkPDFShader::State::CreateAlphaToLuminosityState() const {
   1141     SkASSERT(fType != SkShader::kNone_GradientType);
   1142 
   1143     SkPDFShader::State* newState = new SkPDFShader::State(*this);
   1144 
   1145     for (int i = 0; i < fInfo.fColorCount; i++) {
   1146         SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
   1147         newState->fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha);
   1148     }
   1149 
   1150     return newState;
   1151 }
   1152 
   1153 /**
   1154  * Create a copy of this gradient state with alpha set to fully opaque
   1155  * Only valid for gradient states.
   1156  */
   1157 SkPDFShader::State* SkPDFShader::State::CreateOpaqueState() const {
   1158     SkASSERT(fType != SkShader::kNone_GradientType);
   1159 
   1160     SkPDFShader::State* newState = new SkPDFShader::State(*this);
   1161     for (int i = 0; i < fInfo.fColorCount; i++) {
   1162         newState->fInfo.fColors[i] = SkColorSetA(fInfo.fColors[i],
   1163                                                  SK_AlphaOPAQUE);
   1164     }
   1165 
   1166     return newState;
   1167 }
   1168 
   1169 /**
   1170  * Returns true if state is a gradient and the gradient has alpha.
   1171  */
   1172 bool SkPDFShader::State::GradientHasAlpha() const {
   1173     if (fType == SkShader::kNone_GradientType) {
   1174         return false;
   1175     }
   1176 
   1177     for (int i = 0; i < fInfo.fColorCount; i++) {
   1178         SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
   1179         if (alpha != SK_AlphaOPAQUE) {
   1180             return true;
   1181         }
   1182     }
   1183     return false;
   1184 }
   1185 
   1186 void SkPDFShader::State::AllocateGradientInfoStorage() {
   1187     fColorData.set(sk_malloc_throw(
   1188                fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar))));
   1189     fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get());
   1190     fInfo.fColorOffsets =
   1191             reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount);
   1192 }
   1193