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