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