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