xref: /aosp_15_r20/external/skia/src/pdf/SkPDFGradientShader.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2017 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFGradientShader.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTileMode.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkChecksum.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFDocumentPriv.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFFormXObject.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFGraphicState.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFResourceDict.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFTypes.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFUtils.h"
25*c8dee2aaSAndroid Build Coastguard Worker 
26*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
27*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
28*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
29*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
30*c8dee2aaSAndroid Build Coastguard Worker 
31*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
32*c8dee2aaSAndroid Build Coastguard Worker 
hash(const SkShaderBase::GradientInfo & v)33*c8dee2aaSAndroid Build Coastguard Worker static uint32_t hash(const SkShaderBase::GradientInfo& v) {
34*c8dee2aaSAndroid Build Coastguard Worker     uint32_t buffer[] = {
35*c8dee2aaSAndroid Build Coastguard Worker         (uint32_t)v.fColorCount,
36*c8dee2aaSAndroid Build Coastguard Worker         SkChecksum::Hash32(v.fColors, v.fColorCount * sizeof(SkColor)),
37*c8dee2aaSAndroid Build Coastguard Worker         SkChecksum::Hash32(v.fColorOffsets, v.fColorCount * sizeof(SkScalar)),
38*c8dee2aaSAndroid Build Coastguard Worker         SkChecksum::Hash32(v.fPoint, 2 * sizeof(SkPoint)),
39*c8dee2aaSAndroid Build Coastguard Worker         SkChecksum::Hash32(v.fRadius, 2 * sizeof(SkScalar)),
40*c8dee2aaSAndroid Build Coastguard Worker         (uint32_t)v.fTileMode,
41*c8dee2aaSAndroid Build Coastguard Worker         v.fGradientFlags,
42*c8dee2aaSAndroid Build Coastguard Worker     };
43*c8dee2aaSAndroid Build Coastguard Worker     return SkChecksum::Hash32(buffer, sizeof(buffer));
44*c8dee2aaSAndroid Build Coastguard Worker }
45*c8dee2aaSAndroid Build Coastguard Worker 
hash(const SkPDFGradientShader::Key & k)46*c8dee2aaSAndroid Build Coastguard Worker static uint32_t hash(const SkPDFGradientShader::Key& k) {
47*c8dee2aaSAndroid Build Coastguard Worker     uint32_t buffer[] = {
48*c8dee2aaSAndroid Build Coastguard Worker         (uint32_t)k.fType,
49*c8dee2aaSAndroid Build Coastguard Worker         hash(k.fInfo),
50*c8dee2aaSAndroid Build Coastguard Worker         SkChecksum::Hash32(&k.fCanvasTransform, sizeof(SkMatrix)),
51*c8dee2aaSAndroid Build Coastguard Worker         SkChecksum::Hash32(&k.fShaderTransform, sizeof(SkMatrix)),
52*c8dee2aaSAndroid Build Coastguard Worker         SkChecksum::Hash32(&k.fBBox, sizeof(SkIRect))
53*c8dee2aaSAndroid Build Coastguard Worker     };
54*c8dee2aaSAndroid Build Coastguard Worker     return SkChecksum::Hash32(buffer, sizeof(buffer));
55*c8dee2aaSAndroid Build Coastguard Worker }
56*c8dee2aaSAndroid Build Coastguard Worker 
unit_to_points_matrix(const SkPoint pts[2],SkMatrix * matrix)57*c8dee2aaSAndroid Build Coastguard Worker static void unit_to_points_matrix(const SkPoint pts[2], SkMatrix* matrix) {
58*c8dee2aaSAndroid Build Coastguard Worker     SkVector    vec = pts[1] - pts[0];
59*c8dee2aaSAndroid Build Coastguard Worker     SkScalar    mag = vec.length();
60*c8dee2aaSAndroid Build Coastguard Worker     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker     vec.scale(inv);
63*c8dee2aaSAndroid Build Coastguard Worker     matrix->setSinCos(vec.fY, vec.fX);
64*c8dee2aaSAndroid Build Coastguard Worker     matrix->preScale(mag, mag);
65*c8dee2aaSAndroid Build Coastguard Worker     matrix->postTranslate(pts[0].fX, pts[0].fY);
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker 
68*c8dee2aaSAndroid Build Coastguard Worker static const int kColorComponents = 3;
69*c8dee2aaSAndroid Build Coastguard Worker typedef uint8_t ColorTuple[kColorComponents];
70*c8dee2aaSAndroid Build Coastguard Worker 
71*c8dee2aaSAndroid Build Coastguard Worker /* Assumes t - startOffset is on the stack and does a linear interpolation on t
72*c8dee2aaSAndroid Build Coastguard Worker    between startOffset and endOffset from prevColor to curColor (for each color
73*c8dee2aaSAndroid Build Coastguard Worker    component), leaving the result in component order on the stack. It assumes
74*c8dee2aaSAndroid Build Coastguard Worker    there are always 3 components per color.
75*c8dee2aaSAndroid Build Coastguard Worker    @param range       endOffset - startOffset
76*c8dee2aaSAndroid Build Coastguard Worker    @param beginColor  The previous color.
77*c8dee2aaSAndroid Build Coastguard Worker    @param endColor    The current color.
78*c8dee2aaSAndroid Build Coastguard Worker    @param result      The result ps function.
79*c8dee2aaSAndroid Build Coastguard Worker  */
interpolate_color_code(SkScalar range,SkColor beginColor,SkColor endColor,SkDynamicMemoryWStream * result)80*c8dee2aaSAndroid Build Coastguard Worker static void interpolate_color_code(SkScalar range, SkColor beginColor, SkColor endColor,
81*c8dee2aaSAndroid Build Coastguard Worker                                    SkDynamicMemoryWStream* result) {
82*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(range != SkIntToScalar(0));
83*c8dee2aaSAndroid Build Coastguard Worker 
84*c8dee2aaSAndroid Build Coastguard Worker     /* Linearly interpolate from the previous color to the current.
85*c8dee2aaSAndroid Build Coastguard Worker        Scale the colors from 0..255 to 0..1 and determine the multipliers for interpolation.
86*c8dee2aaSAndroid Build Coastguard Worker        C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
87*c8dee2aaSAndroid Build Coastguard Worker      */
88*c8dee2aaSAndroid Build Coastguard Worker 
89*c8dee2aaSAndroid Build Coastguard Worker     ColorTuple curColor = { SkTo<uint8_t>(SkColorGetR(endColor)),
90*c8dee2aaSAndroid Build Coastguard Worker                             SkTo<uint8_t>(SkColorGetG(endColor)),
91*c8dee2aaSAndroid Build Coastguard Worker                             SkTo<uint8_t>(SkColorGetB(endColor)) };
92*c8dee2aaSAndroid Build Coastguard Worker 
93*c8dee2aaSAndroid Build Coastguard Worker     ColorTuple prevColor = { SkTo<uint8_t>(SkColorGetR(beginColor)),
94*c8dee2aaSAndroid Build Coastguard Worker                              SkTo<uint8_t>(SkColorGetG(beginColor)),
95*c8dee2aaSAndroid Build Coastguard Worker                              SkTo<uint8_t>(SkColorGetB(beginColor)) };
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker     // Figure out how to scale each color component.
98*c8dee2aaSAndroid Build Coastguard Worker     SkScalar multiplier[kColorComponents];
99*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < kColorComponents; i++) {
100*c8dee2aaSAndroid Build Coastguard Worker         static const SkScalar kColorScale = SkScalarInvert(255);
101*c8dee2aaSAndroid Build Coastguard Worker         multiplier[i] = kColorScale * (curColor[i] - prevColor[i]) / range;
102*c8dee2aaSAndroid Build Coastguard Worker     }
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker     // Calculate when we no longer need to keep a copy of the input parameter t.
105*c8dee2aaSAndroid Build Coastguard Worker     // If the last component to use t is i, then dupInput[0..i - 1] = true
106*c8dee2aaSAndroid Build Coastguard Worker     // and dupInput[i .. components] = false.
107*c8dee2aaSAndroid Build Coastguard Worker     bool dupInput[kColorComponents];
108*c8dee2aaSAndroid Build Coastguard Worker     dupInput[kColorComponents - 1] = false;
109*c8dee2aaSAndroid Build Coastguard Worker     for (int i = kColorComponents - 2; i >= 0; i--) {
110*c8dee2aaSAndroid Build Coastguard Worker         dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
111*c8dee2aaSAndroid Build Coastguard Worker     }
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker     if (!dupInput[0] && multiplier[0] == 0) {
114*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("pop ");
115*c8dee2aaSAndroid Build Coastguard Worker     }
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < kColorComponents; i++) {
118*c8dee2aaSAndroid Build Coastguard Worker         // If the next components needs t and this component will consume a
119*c8dee2aaSAndroid Build Coastguard Worker         // copy, make another copy.
120*c8dee2aaSAndroid Build Coastguard Worker         if (dupInput[i] && multiplier[i] != 0) {
121*c8dee2aaSAndroid Build Coastguard Worker             result->writeText("dup ");
122*c8dee2aaSAndroid Build Coastguard Worker         }
123*c8dee2aaSAndroid Build Coastguard Worker 
124*c8dee2aaSAndroid Build Coastguard Worker         if (multiplier[i] == 0) {
125*c8dee2aaSAndroid Build Coastguard Worker             SkPDFUtils::AppendColorComponent(prevColor[i], result);
126*c8dee2aaSAndroid Build Coastguard Worker             result->writeText(" ");
127*c8dee2aaSAndroid Build Coastguard Worker         } else {
128*c8dee2aaSAndroid Build Coastguard Worker             if (multiplier[i] != 1) {
129*c8dee2aaSAndroid Build Coastguard Worker                 SkPDFUtils::AppendScalar(multiplier[i], result);
130*c8dee2aaSAndroid Build Coastguard Worker                 result->writeText(" mul ");
131*c8dee2aaSAndroid Build Coastguard Worker             }
132*c8dee2aaSAndroid Build Coastguard Worker             if (prevColor[i] != 0) {
133*c8dee2aaSAndroid Build Coastguard Worker                 SkPDFUtils::AppendColorComponent(prevColor[i], result);
134*c8dee2aaSAndroid Build Coastguard Worker                 result->writeText(" add ");
135*c8dee2aaSAndroid Build Coastguard Worker             }
136*c8dee2aaSAndroid Build Coastguard Worker         }
137*c8dee2aaSAndroid Build Coastguard Worker 
138*c8dee2aaSAndroid Build Coastguard Worker         if (dupInput[i]) {
139*c8dee2aaSAndroid Build Coastguard Worker             result->writeText("exch ");
140*c8dee2aaSAndroid Build Coastguard Worker         }
141*c8dee2aaSAndroid Build Coastguard Worker     }
142*c8dee2aaSAndroid Build Coastguard Worker }
143*c8dee2aaSAndroid Build Coastguard Worker 
write_gradient_ranges(const SkShaderBase::GradientInfo & info,SkSpan<size_t> rangeEnds,bool top,bool first,SkDynamicMemoryWStream * result)144*c8dee2aaSAndroid Build Coastguard Worker static void write_gradient_ranges(const SkShaderBase::GradientInfo& info, SkSpan<size_t> rangeEnds,
145*c8dee2aaSAndroid Build Coastguard Worker                                   bool top, bool first, SkDynamicMemoryWStream* result) {
146*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!rangeEnds.empty());
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker     size_t rangeEndIndex = rangeEnds[rangeEnds.size() - 1];
149*c8dee2aaSAndroid Build Coastguard Worker     SkScalar rangeEnd = info.fColorOffsets[rangeEndIndex];
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker     // Each range check tests 0 < t <= end.
152*c8dee2aaSAndroid Build Coastguard Worker     if (top) {
153*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(first);
154*c8dee2aaSAndroid Build Coastguard Worker         // t may have been set to 0 to signal that the answer has already been found.
155*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("dup dup 0 gt exch ");  // In Preview 11.0 (1033.3) `0. 0 ne` is true.
156*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(rangeEnd, result);
157*c8dee2aaSAndroid Build Coastguard Worker         result->writeText(" le and {\n");
158*c8dee2aaSAndroid Build Coastguard Worker     } else if (first) {
159*c8dee2aaSAndroid Build Coastguard Worker         // After the top level check, only t <= end needs to be tested on if (lo) side.
160*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("dup ");
161*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(rangeEnd, result);
162*c8dee2aaSAndroid Build Coastguard Worker         result->writeText(" le {\n");
163*c8dee2aaSAndroid Build Coastguard Worker     } else {
164*c8dee2aaSAndroid Build Coastguard Worker         // The else (hi) side.
165*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("{\n");
166*c8dee2aaSAndroid Build Coastguard Worker     }
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker     if (rangeEnds.size() == 1) {
169*c8dee2aaSAndroid Build Coastguard Worker         // Set the stack to [r g b].
170*c8dee2aaSAndroid Build Coastguard Worker         size_t rangeBeginIndex = rangeEndIndex - 1;
171*c8dee2aaSAndroid Build Coastguard Worker         SkScalar rangeBegin = info.fColorOffsets[rangeBeginIndex];
172*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(rangeBegin, result);
173*c8dee2aaSAndroid Build Coastguard Worker         result->writeText(" sub ");  // consume t, put t - startOffset on the stack.
174*c8dee2aaSAndroid Build Coastguard Worker         interpolate_color_code(rangeEnd - rangeBegin,
175*c8dee2aaSAndroid Build Coastguard Worker                                info.fColors[rangeBeginIndex], info.fColors[rangeEndIndex], result);
176*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("\n");
177*c8dee2aaSAndroid Build Coastguard Worker     } else {
178*c8dee2aaSAndroid Build Coastguard Worker         size_t loCount = rangeEnds.size() / 2;
179*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<size_t> loSpan = rangeEnds.subspan(0, loCount);
180*c8dee2aaSAndroid Build Coastguard Worker         write_gradient_ranges(info, loSpan, false, true, result);
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<size_t> hiSpan = rangeEnds.subspan(loCount, rangeEnds.size() - loCount);
183*c8dee2aaSAndroid Build Coastguard Worker         write_gradient_ranges(info, hiSpan, false, false, result);
184*c8dee2aaSAndroid Build Coastguard Worker     }
185*c8dee2aaSAndroid Build Coastguard Worker 
186*c8dee2aaSAndroid Build Coastguard Worker     if (top) {
187*c8dee2aaSAndroid Build Coastguard Worker         // Put 0 on the stack for t once here instead of after every call to interpolate_color_code.
188*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("0} if\n");
189*c8dee2aaSAndroid Build Coastguard Worker     } else if (first) {
190*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("}");  // The else (hi) side will come next.
191*c8dee2aaSAndroid Build Coastguard Worker     } else {
192*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("} ifelse\n");
193*c8dee2aaSAndroid Build Coastguard Worker     }
194*c8dee2aaSAndroid Build Coastguard Worker }
195*c8dee2aaSAndroid Build Coastguard Worker 
196*c8dee2aaSAndroid Build Coastguard Worker /* Generate Type 4 function code to map t to the passed gradient, clamping at the ends.
197*c8dee2aaSAndroid Build Coastguard Worker    The types integer, real, and boolean are available.
198*c8dee2aaSAndroid Build Coastguard Worker    There are no string, array, procedure, variable, or name types available.
199*c8dee2aaSAndroid Build Coastguard Worker 
200*c8dee2aaSAndroid Build Coastguard Worker    The generated code will be of the following form with all values hard coded.
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker   if (t <= 0) {
203*c8dee2aaSAndroid Build Coastguard Worker     ret = color[0];
204*c8dee2aaSAndroid Build Coastguard Worker     t = 0;
205*c8dee2aaSAndroid Build Coastguard Worker   }
206*c8dee2aaSAndroid Build Coastguard Worker   if (t > 0 && t <= stop[4]) {
207*c8dee2aaSAndroid Build Coastguard Worker     if (t <= stop[2]) {
208*c8dee2aaSAndroid Build Coastguard Worker       if (t <= stop[1]) {
209*c8dee2aaSAndroid Build Coastguard Worker         ret = interp(t - stop[0], stop[1] - stop[0], color[0], color[1]);
210*c8dee2aaSAndroid Build Coastguard Worker       } else {
211*c8dee2aaSAndroid Build Coastguard Worker         ret = interp(t - stop[1], stop[2] - stop[1], color[1], color[2]);
212*c8dee2aaSAndroid Build Coastguard Worker       }
213*c8dee2aaSAndroid Build Coastguard Worker     } else {
214*c8dee2aaSAndroid Build Coastguard Worker       if (t <= stop[3] {
215*c8dee2aaSAndroid Build Coastguard Worker         ret = interp(t - stop[2], stop[3] - stop[2], color[2], color[3]);
216*c8dee2aaSAndroid Build Coastguard Worker       } else {
217*c8dee2aaSAndroid Build Coastguard Worker         ret = interp(t - stop[3], stop[4] - stop[3], color[3], color[4]);
218*c8dee2aaSAndroid Build Coastguard Worker       }
219*c8dee2aaSAndroid Build Coastguard Worker     }
220*c8dee2aaSAndroid Build Coastguard Worker     t = 0;
221*c8dee2aaSAndroid Build Coastguard Worker   }
222*c8dee2aaSAndroid Build Coastguard Worker   if (t > 0) {
223*c8dee2aaSAndroid Build Coastguard Worker     ret = color[4];
224*c8dee2aaSAndroid Build Coastguard Worker   }
225*c8dee2aaSAndroid Build Coastguard Worker 
226*c8dee2aaSAndroid Build Coastguard Worker    which in PDF will be represented like
227*c8dee2aaSAndroid Build Coastguard Worker 
228*c8dee2aaSAndroid Build Coastguard Worker   dup 0 le {pop 0 0 0 0} if
229*c8dee2aaSAndroid Build Coastguard Worker   dup dup 0 gt exch 1 le and {
230*c8dee2aaSAndroid Build Coastguard Worker     dup .5 le {
231*c8dee2aaSAndroid Build Coastguard Worker       dup .25 le {
232*c8dee2aaSAndroid Build Coastguard Worker         0 sub 2 mul 0 0
233*c8dee2aaSAndroid Build Coastguard Worker       }{
234*c8dee2aaSAndroid Build Coastguard Worker         .25 sub .5 exch 2 mul 0
235*c8dee2aaSAndroid Build Coastguard Worker       } ifelse
236*c8dee2aaSAndroid Build Coastguard Worker     }{
237*c8dee2aaSAndroid Build Coastguard Worker       dup .75 le {
238*c8dee2aaSAndroid Build Coastguard Worker         .5 sub .5 exch .5 exch 2 mul
239*c8dee2aaSAndroid Build Coastguard Worker       }{
240*c8dee2aaSAndroid Build Coastguard Worker         .75 sub dup 2 mul .5 add exch dup 2 mul .5 add exch 2 mul .5 add
241*c8dee2aaSAndroid Build Coastguard Worker       } ifelse
242*c8dee2aaSAndroid Build Coastguard Worker     } ifelse
243*c8dee2aaSAndroid Build Coastguard Worker   0} if
244*c8dee2aaSAndroid Build Coastguard Worker   0 gt {1 1 1} if
245*c8dee2aaSAndroid Build Coastguard Worker  */
gradient_function_code(const SkShaderBase::GradientInfo & info,SkDynamicMemoryWStream * result)246*c8dee2aaSAndroid Build Coastguard Worker static void gradient_function_code(const SkShaderBase::GradientInfo& info,
247*c8dee2aaSAndroid Build Coastguard Worker                                    SkDynamicMemoryWStream* result) {
248*c8dee2aaSAndroid Build Coastguard Worker     // While looking for a hit the stack is [t].
249*c8dee2aaSAndroid Build Coastguard Worker     // After finding a hit the stack is [r g b 0].
250*c8dee2aaSAndroid Build Coastguard Worker     // The 0 is consumed just before returning.
251*c8dee2aaSAndroid Build Coastguard Worker 
252*c8dee2aaSAndroid Build Coastguard Worker     // The initial range has no previous and contains a solid color.
253*c8dee2aaSAndroid Build Coastguard Worker     // Any t <= 0 will be handled by this initial range, so later t == 0 indicates a hit was found.
254*c8dee2aaSAndroid Build Coastguard Worker     result->writeText("dup 0 le {pop ");
255*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendColorComponent(SkColorGetR(info.fColors[0]), result);
256*c8dee2aaSAndroid Build Coastguard Worker     result->writeText(" ");
257*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendColorComponent(SkColorGetG(info.fColors[0]), result);
258*c8dee2aaSAndroid Build Coastguard Worker     result->writeText(" ");
259*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendColorComponent(SkColorGetB(info.fColors[0]), result);
260*c8dee2aaSAndroid Build Coastguard Worker     result->writeText(" 0} if\n");
261*c8dee2aaSAndroid Build Coastguard Worker 
262*c8dee2aaSAndroid Build Coastguard Worker     // Optimize out ranges which don't make any visual difference.
263*c8dee2aaSAndroid Build Coastguard Worker     AutoSTMalloc<4, size_t> rangeEnds(info.fColorCount);
264*c8dee2aaSAndroid Build Coastguard Worker     size_t rangeEndsCount = 0;
265*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 1; i < info.fColorCount; ++i) {
266*c8dee2aaSAndroid Build Coastguard Worker         // Ignoring the alpha, is this range the same solid color as the next range?
267*c8dee2aaSAndroid Build Coastguard Worker         // This optimizes gradients where sometimes only the color or only the alpha is changing.
268*c8dee2aaSAndroid Build Coastguard Worker         auto eqIgnoringAlpha = [](SkColor a, SkColor b) {
269*c8dee2aaSAndroid Build Coastguard Worker             return SkColorSetA(a, 0x00) == SkColorSetA(b, 0x00);
270*c8dee2aaSAndroid Build Coastguard Worker         };
271*c8dee2aaSAndroid Build Coastguard Worker         bool constantColorBothSides =
272*c8dee2aaSAndroid Build Coastguard Worker             eqIgnoringAlpha(info.fColors[i-1], info.fColors[i]) &&// This range is a solid color.
273*c8dee2aaSAndroid Build Coastguard Worker             i != info.fColorCount-1 &&                            // This is not the last range.
274*c8dee2aaSAndroid Build Coastguard Worker             eqIgnoringAlpha(info.fColors[i], info.fColors[i+1]);  // Next range is same solid color.
275*c8dee2aaSAndroid Build Coastguard Worker 
276*c8dee2aaSAndroid Build Coastguard Worker         // Does this range have zero size?
277*c8dee2aaSAndroid Build Coastguard Worker         bool degenerateRange = info.fColorOffsets[i-1] == info.fColorOffsets[i];
278*c8dee2aaSAndroid Build Coastguard Worker 
279*c8dee2aaSAndroid Build Coastguard Worker         if (!degenerateRange && !constantColorBothSides) {
280*c8dee2aaSAndroid Build Coastguard Worker             rangeEnds[rangeEndsCount] = i;
281*c8dee2aaSAndroid Build Coastguard Worker             ++rangeEndsCount;
282*c8dee2aaSAndroid Build Coastguard Worker         }
283*c8dee2aaSAndroid Build Coastguard Worker     }
284*c8dee2aaSAndroid Build Coastguard Worker 
285*c8dee2aaSAndroid Build Coastguard Worker     // If a cap on depth is needed, loop here.
286*c8dee2aaSAndroid Build Coastguard Worker     write_gradient_ranges(info, SkSpan(rangeEnds.get(), rangeEndsCount), true, true, result);
287*c8dee2aaSAndroid Build Coastguard Worker 
288*c8dee2aaSAndroid Build Coastguard Worker     // Clamp the final color.
289*c8dee2aaSAndroid Build Coastguard Worker     result->writeText("0 gt {");
290*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendColorComponent(SkColorGetR(info.fColors[info.fColorCount - 1]), result);
291*c8dee2aaSAndroid Build Coastguard Worker     result->writeText(" ");
292*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendColorComponent(SkColorGetG(info.fColors[info.fColorCount - 1]), result);
293*c8dee2aaSAndroid Build Coastguard Worker     result->writeText(" ");
294*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendColorComponent(SkColorGetB(info.fColors[info.fColorCount - 1]), result);
295*c8dee2aaSAndroid Build Coastguard Worker     result->writeText("} if\n");
296*c8dee2aaSAndroid Build Coastguard Worker }
297*c8dee2aaSAndroid Build Coastguard Worker 
createInterpolationFunction(const ColorTuple & color1,const ColorTuple & color2)298*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<SkPDFDict> createInterpolationFunction(const ColorTuple& color1,
299*c8dee2aaSAndroid Build Coastguard Worker                                                     const ColorTuple& color2) {
300*c8dee2aaSAndroid Build Coastguard Worker     auto retval = SkPDFMakeDict();
301*c8dee2aaSAndroid Build Coastguard Worker 
302*c8dee2aaSAndroid Build Coastguard Worker     auto c0 = SkPDFMakeArray();
303*c8dee2aaSAndroid Build Coastguard Worker     c0->appendColorComponent(color1[0]);
304*c8dee2aaSAndroid Build Coastguard Worker     c0->appendColorComponent(color1[1]);
305*c8dee2aaSAndroid Build Coastguard Worker     c0->appendColorComponent(color1[2]);
306*c8dee2aaSAndroid Build Coastguard Worker     retval->insertObject("C0", std::move(c0));
307*c8dee2aaSAndroid Build Coastguard Worker 
308*c8dee2aaSAndroid Build Coastguard Worker     auto c1 = SkPDFMakeArray();
309*c8dee2aaSAndroid Build Coastguard Worker     c1->appendColorComponent(color2[0]);
310*c8dee2aaSAndroid Build Coastguard Worker     c1->appendColorComponent(color2[1]);
311*c8dee2aaSAndroid Build Coastguard Worker     c1->appendColorComponent(color2[2]);
312*c8dee2aaSAndroid Build Coastguard Worker     retval->insertObject("C1", std::move(c1));
313*c8dee2aaSAndroid Build Coastguard Worker 
314*c8dee2aaSAndroid Build Coastguard Worker     retval->insertObject("Domain", SkPDFMakeArray(0, 1));
315*c8dee2aaSAndroid Build Coastguard Worker 
316*c8dee2aaSAndroid Build Coastguard Worker     retval->insertInt("FunctionType", 2);
317*c8dee2aaSAndroid Build Coastguard Worker     retval->insertScalar("N", 1.0f);
318*c8dee2aaSAndroid Build Coastguard Worker 
319*c8dee2aaSAndroid Build Coastguard Worker     return retval;
320*c8dee2aaSAndroid Build Coastguard Worker }
321*c8dee2aaSAndroid Build Coastguard Worker 
gradientStitchCode(const SkShaderBase::GradientInfo & info)322*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<SkPDFDict> gradientStitchCode(const SkShaderBase::GradientInfo& info) {
323*c8dee2aaSAndroid Build Coastguard Worker     auto retval = SkPDFMakeDict();
324*c8dee2aaSAndroid Build Coastguard Worker 
325*c8dee2aaSAndroid Build Coastguard Worker     // normalize color stops
326*c8dee2aaSAndroid Build Coastguard Worker     int colorCount = info.fColorCount;
327*c8dee2aaSAndroid Build Coastguard Worker     std::vector<SkColor>  colors(info.fColors, info.fColors + colorCount);
328*c8dee2aaSAndroid Build Coastguard Worker     std::vector<SkScalar> colorOffsets(info.fColorOffsets, info.fColorOffsets + colorCount);
329*c8dee2aaSAndroid Build Coastguard Worker 
330*c8dee2aaSAndroid Build Coastguard Worker     int i = 1;
331*c8dee2aaSAndroid Build Coastguard Worker     while (i < colorCount - 1) {
332*c8dee2aaSAndroid Build Coastguard Worker         // ensure stops are in order
333*c8dee2aaSAndroid Build Coastguard Worker         if (colorOffsets[i - 1] > colorOffsets[i]) {
334*c8dee2aaSAndroid Build Coastguard Worker             colorOffsets[i] = colorOffsets[i - 1];
335*c8dee2aaSAndroid Build Coastguard Worker         }
336*c8dee2aaSAndroid Build Coastguard Worker 
337*c8dee2aaSAndroid Build Coastguard Worker         // remove points that are between 2 coincident points
338*c8dee2aaSAndroid Build Coastguard Worker         if ((colorOffsets[i - 1] == colorOffsets[i]) && (colorOffsets[i] == colorOffsets[i + 1])) {
339*c8dee2aaSAndroid Build Coastguard Worker             colorCount -= 1;
340*c8dee2aaSAndroid Build Coastguard Worker             colors.erase(colors.begin() + i);
341*c8dee2aaSAndroid Build Coastguard Worker             colorOffsets.erase(colorOffsets.begin() + i);
342*c8dee2aaSAndroid Build Coastguard Worker         } else {
343*c8dee2aaSAndroid Build Coastguard Worker             i++;
344*c8dee2aaSAndroid Build Coastguard Worker         }
345*c8dee2aaSAndroid Build Coastguard Worker     }
346*c8dee2aaSAndroid Build Coastguard Worker     // find coincident points and slightly move them over
347*c8dee2aaSAndroid Build Coastguard Worker     for (i = 1; i < colorCount - 1; i++) {
348*c8dee2aaSAndroid Build Coastguard Worker         if (colorOffsets[i - 1] == colorOffsets[i]) {
349*c8dee2aaSAndroid Build Coastguard Worker             colorOffsets[i] += 0.00001f;
350*c8dee2aaSAndroid Build Coastguard Worker         }
351*c8dee2aaSAndroid Build Coastguard Worker     }
352*c8dee2aaSAndroid Build Coastguard Worker     // check if last 2 stops coincide
353*c8dee2aaSAndroid Build Coastguard Worker     if (colorOffsets[i - 1] == colorOffsets[i]) {
354*c8dee2aaSAndroid Build Coastguard Worker         colorOffsets[i - 1] -= 0.00001f;
355*c8dee2aaSAndroid Build Coastguard Worker     }
356*c8dee2aaSAndroid Build Coastguard Worker 
357*c8dee2aaSAndroid Build Coastguard Worker     AutoSTMalloc<4, ColorTuple> colorDataAlloc(colorCount);
358*c8dee2aaSAndroid Build Coastguard Worker     ColorTuple *colorData = colorDataAlloc.get();
359*c8dee2aaSAndroid Build Coastguard Worker     for (int idx = 0; idx < colorCount; idx++) {
360*c8dee2aaSAndroid Build Coastguard Worker         colorData[idx][0] = SkColorGetR(colors[idx]);
361*c8dee2aaSAndroid Build Coastguard Worker         colorData[idx][1] = SkColorGetG(colors[idx]);
362*c8dee2aaSAndroid Build Coastguard Worker         colorData[idx][2] = SkColorGetB(colors[idx]);
363*c8dee2aaSAndroid Build Coastguard Worker     }
364*c8dee2aaSAndroid Build Coastguard Worker 
365*c8dee2aaSAndroid Build Coastguard Worker     // no need for a stitch function if there are only 2 stops.
366*c8dee2aaSAndroid Build Coastguard Worker     if (colorCount == 2)
367*c8dee2aaSAndroid Build Coastguard Worker         return createInterpolationFunction(colorData[0], colorData[1]);
368*c8dee2aaSAndroid Build Coastguard Worker 
369*c8dee2aaSAndroid Build Coastguard Worker     auto encode = SkPDFMakeArray();
370*c8dee2aaSAndroid Build Coastguard Worker     auto bounds = SkPDFMakeArray();
371*c8dee2aaSAndroid Build Coastguard Worker     auto functions = SkPDFMakeArray();
372*c8dee2aaSAndroid Build Coastguard Worker 
373*c8dee2aaSAndroid Build Coastguard Worker     retval->insertObject("Domain", SkPDFMakeArray(0, 1));
374*c8dee2aaSAndroid Build Coastguard Worker     retval->insertInt("FunctionType", 3);
375*c8dee2aaSAndroid Build Coastguard Worker 
376*c8dee2aaSAndroid Build Coastguard Worker     for (int idx = 1; idx < colorCount; idx++) {
377*c8dee2aaSAndroid Build Coastguard Worker         if (idx > 1) {
378*c8dee2aaSAndroid Build Coastguard Worker             bounds->appendScalar(colorOffsets[idx-1]);
379*c8dee2aaSAndroid Build Coastguard Worker         }
380*c8dee2aaSAndroid Build Coastguard Worker 
381*c8dee2aaSAndroid Build Coastguard Worker         encode->appendScalar(0);
382*c8dee2aaSAndroid Build Coastguard Worker         encode->appendScalar(1.0f);
383*c8dee2aaSAndroid Build Coastguard Worker 
384*c8dee2aaSAndroid Build Coastguard Worker         functions->appendObject(createInterpolationFunction(colorData[idx-1], colorData[idx]));
385*c8dee2aaSAndroid Build Coastguard Worker     }
386*c8dee2aaSAndroid Build Coastguard Worker 
387*c8dee2aaSAndroid Build Coastguard Worker     retval->insertObject("Encode", std::move(encode));
388*c8dee2aaSAndroid Build Coastguard Worker     retval->insertObject("Bounds", std::move(bounds));
389*c8dee2aaSAndroid Build Coastguard Worker     retval->insertObject("Functions", std::move(functions));
390*c8dee2aaSAndroid Build Coastguard Worker 
391*c8dee2aaSAndroid Build Coastguard Worker     return retval;
392*c8dee2aaSAndroid Build Coastguard Worker }
393*c8dee2aaSAndroid Build Coastguard Worker 
394*c8dee2aaSAndroid Build Coastguard Worker /* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
tileModeCode(SkTileMode mode,SkDynamicMemoryWStream * result)395*c8dee2aaSAndroid Build Coastguard Worker static void tileModeCode(SkTileMode mode, SkDynamicMemoryWStream* result) {
396*c8dee2aaSAndroid Build Coastguard Worker     if (mode == SkTileMode::kRepeat) {
397*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("dup truncate sub\n");  // Get the fractional part.
398*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
399*c8dee2aaSAndroid Build Coastguard Worker         return;
400*c8dee2aaSAndroid Build Coastguard Worker     }
401*c8dee2aaSAndroid Build Coastguard Worker 
402*c8dee2aaSAndroid Build Coastguard Worker     if (mode == SkTileMode::kMirror) {
403*c8dee2aaSAndroid Build Coastguard Worker         // In Preview 11.0 (1033.3) `a n mod r eq` (with a and n both integers, r integer or real)
404*c8dee2aaSAndroid Build Coastguard Worker         // early aborts the function when false would be put on the stack.
405*c8dee2aaSAndroid Build Coastguard Worker         // Work around this by re-writing `t 2 mod 1 eq` as `t 2 mod 0 gt`.
406*c8dee2aaSAndroid Build Coastguard Worker 
407*c8dee2aaSAndroid Build Coastguard Worker         // Map t mod 2 into [0, 1, 1, 0].
408*c8dee2aaSAndroid Build Coastguard Worker         //                Code                 Stack t
409*c8dee2aaSAndroid Build Coastguard Worker         result->writeText("abs "                 // +t
410*c8dee2aaSAndroid Build Coastguard Worker                           "dup "                 // +t.s +t.s
411*c8dee2aaSAndroid Build Coastguard Worker                           "truncate "            // +t.s +t
412*c8dee2aaSAndroid Build Coastguard Worker                           "dup "                 // +t.s +t +t
413*c8dee2aaSAndroid Build Coastguard Worker                           "cvi "                 // +t.s +t +T
414*c8dee2aaSAndroid Build Coastguard Worker                           "2 mod "               // +t.s +t (+T mod 2)
415*c8dee2aaSAndroid Build Coastguard Worker               /*"1 eq "*/ "0 gt "                // +t.s +t true|false
416*c8dee2aaSAndroid Build Coastguard Worker                           "3 1 roll "            // true|false +t.s +t
417*c8dee2aaSAndroid Build Coastguard Worker                           "sub "                 // true|false 0.s
418*c8dee2aaSAndroid Build Coastguard Worker                           "exch "                // 0.s true|false
419*c8dee2aaSAndroid Build Coastguard Worker                           "{1 exch sub} if\n");  // 1 - 0.s|0.s
420*c8dee2aaSAndroid Build Coastguard Worker     }
421*c8dee2aaSAndroid Build Coastguard Worker }
422*c8dee2aaSAndroid Build Coastguard Worker 
423*c8dee2aaSAndroid Build Coastguard Worker /**
424*c8dee2aaSAndroid Build Coastguard Worker  *  Returns PS function code that applies inverse perspective
425*c8dee2aaSAndroid Build Coastguard Worker  *  to a x, y point.
426*c8dee2aaSAndroid Build Coastguard Worker  *  The function assumes that the stack has at least two elements,
427*c8dee2aaSAndroid Build Coastguard Worker  *  and that the top 2 elements are numeric values.
428*c8dee2aaSAndroid Build Coastguard Worker  *  After executing this code on a PS stack, the last 2 elements are updated
429*c8dee2aaSAndroid Build Coastguard Worker  *  while the rest of the stack is preserved intact.
430*c8dee2aaSAndroid Build Coastguard Worker  *  inversePerspectiveMatrix is the inverse perspective matrix.
431*c8dee2aaSAndroid Build Coastguard Worker  */
apply_perspective_to_coordinates(const SkMatrix & inversePerspectiveMatrix,SkDynamicMemoryWStream * code)432*c8dee2aaSAndroid Build Coastguard Worker static void apply_perspective_to_coordinates(const SkMatrix& inversePerspectiveMatrix,
433*c8dee2aaSAndroid Build Coastguard Worker                                              SkDynamicMemoryWStream* code) {
434*c8dee2aaSAndroid Build Coastguard Worker     if (!inversePerspectiveMatrix.hasPerspective()) {
435*c8dee2aaSAndroid Build Coastguard Worker         return;
436*c8dee2aaSAndroid Build Coastguard Worker     }
437*c8dee2aaSAndroid Build Coastguard Worker 
438*c8dee2aaSAndroid Build Coastguard Worker     // Perspective matrix should be:
439*c8dee2aaSAndroid Build Coastguard Worker     // 1   0  0
440*c8dee2aaSAndroid Build Coastguard Worker     // 0   1  0
441*c8dee2aaSAndroid Build Coastguard Worker     // p0 p1 p2
442*c8dee2aaSAndroid Build Coastguard Worker 
443*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0];
444*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1];
445*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2];
446*c8dee2aaSAndroid Build Coastguard Worker 
447*c8dee2aaSAndroid Build Coastguard Worker     // y = y / (p2 + p0 x + p1 y)
448*c8dee2aaSAndroid Build Coastguard Worker     // x = x / (p2 + p0 x + p1 y)
449*c8dee2aaSAndroid Build Coastguard Worker 
450*c8dee2aaSAndroid Build Coastguard Worker     // Input on stack: x y
451*c8dee2aaSAndroid Build Coastguard Worker     code->writeText(" dup ");             // x y y
452*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendScalar(p1, code);   // x y y p1
453*c8dee2aaSAndroid Build Coastguard Worker     code->writeText(" mul "               // x y y*p1
454*c8dee2aaSAndroid Build Coastguard Worker                     " 2 index ");         // x y y*p1 x
455*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendScalar(p0, code);   // x y y p1 x p0
456*c8dee2aaSAndroid Build Coastguard Worker     code->writeText(" mul ");             // x y y*p1 x*p0
457*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendScalar(p2, code);   // x y y p1 x*p0 p2
458*c8dee2aaSAndroid Build Coastguard Worker     code->writeText(" add "               // x y y*p1 x*p0+p2
459*c8dee2aaSAndroid Build Coastguard Worker                     "add "                // x y y*p1+x*p0+p2
460*c8dee2aaSAndroid Build Coastguard Worker                     "3 1 roll "           // y*p1+x*p0+p2 x y
461*c8dee2aaSAndroid Build Coastguard Worker                     "2 index "            // z x y y*p1+x*p0+p2
462*c8dee2aaSAndroid Build Coastguard Worker                     "div "                // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2)
463*c8dee2aaSAndroid Build Coastguard Worker                     "3 1 roll "           // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x
464*c8dee2aaSAndroid Build Coastguard Worker                     "exch "               // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2
465*c8dee2aaSAndroid Build Coastguard Worker                     "div "                // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2)
466*c8dee2aaSAndroid Build Coastguard Worker                     "exch\n");            // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2)
467*c8dee2aaSAndroid Build Coastguard Worker }
468*c8dee2aaSAndroid Build Coastguard Worker 
linearCode(const SkShaderBase::GradientInfo & info,const SkMatrix & perspectiveRemover,SkDynamicMemoryWStream * function)469*c8dee2aaSAndroid Build Coastguard Worker static void linearCode(const SkShaderBase::GradientInfo& info,
470*c8dee2aaSAndroid Build Coastguard Worker                        const SkMatrix& perspectiveRemover,
471*c8dee2aaSAndroid Build Coastguard Worker                        SkDynamicMemoryWStream* function) {
472*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("{");
473*c8dee2aaSAndroid Build Coastguard Worker 
474*c8dee2aaSAndroid Build Coastguard Worker     apply_perspective_to_coordinates(perspectiveRemover, function);
475*c8dee2aaSAndroid Build Coastguard Worker 
476*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("pop\n");  // Just ditch the y value.
477*c8dee2aaSAndroid Build Coastguard Worker     tileModeCode((SkTileMode)info.fTileMode, function);
478*c8dee2aaSAndroid Build Coastguard Worker     gradient_function_code(info, function);
479*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("}");
480*c8dee2aaSAndroid Build Coastguard Worker }
481*c8dee2aaSAndroid Build Coastguard Worker 
radialCode(const SkShaderBase::GradientInfo & info,const SkMatrix & perspectiveRemover,SkDynamicMemoryWStream * function)482*c8dee2aaSAndroid Build Coastguard Worker static void radialCode(const SkShaderBase::GradientInfo& info,
483*c8dee2aaSAndroid Build Coastguard Worker                        const SkMatrix& perspectiveRemover,
484*c8dee2aaSAndroid Build Coastguard Worker                        SkDynamicMemoryWStream* function) {
485*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("{");
486*c8dee2aaSAndroid Build Coastguard Worker 
487*c8dee2aaSAndroid Build Coastguard Worker     apply_perspective_to_coordinates(perspectiveRemover, function);
488*c8dee2aaSAndroid Build Coastguard Worker 
489*c8dee2aaSAndroid Build Coastguard Worker     // Find the distance from the origin.
490*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("dup "      // x y y
491*c8dee2aaSAndroid Build Coastguard Worker                     "mul "      // x y^2
492*c8dee2aaSAndroid Build Coastguard Worker                     "exch "     // y^2 x
493*c8dee2aaSAndroid Build Coastguard Worker                     "dup "      // y^2 x x
494*c8dee2aaSAndroid Build Coastguard Worker                     "mul "      // y^2 x^2
495*c8dee2aaSAndroid Build Coastguard Worker                     "add "      // y^2+x^2
496*c8dee2aaSAndroid Build Coastguard Worker                     "sqrt\n");  // sqrt(y^2+x^2)
497*c8dee2aaSAndroid Build Coastguard Worker 
498*c8dee2aaSAndroid Build Coastguard Worker     tileModeCode((SkTileMode)info.fTileMode, function);
499*c8dee2aaSAndroid Build Coastguard Worker     gradient_function_code(info, function);
500*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("}");
501*c8dee2aaSAndroid Build Coastguard Worker }
502*c8dee2aaSAndroid Build Coastguard Worker 
503*c8dee2aaSAndroid Build Coastguard Worker /* Conical gradient shader, based on the Canvas spec for radial gradients
504*c8dee2aaSAndroid Build Coastguard Worker    See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
505*c8dee2aaSAndroid Build Coastguard Worker  */
twoPointConicalCode(const SkShaderBase::GradientInfo & info,const SkMatrix & perspectiveRemover,SkDynamicMemoryWStream * function)506*c8dee2aaSAndroid Build Coastguard Worker static void twoPointConicalCode(const SkShaderBase::GradientInfo& info,
507*c8dee2aaSAndroid Build Coastguard Worker                                 const SkMatrix& perspectiveRemover,
508*c8dee2aaSAndroid Build Coastguard Worker                                 SkDynamicMemoryWStream* function) {
509*c8dee2aaSAndroid Build Coastguard Worker     SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
510*c8dee2aaSAndroid Build Coastguard Worker     SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
511*c8dee2aaSAndroid Build Coastguard Worker     SkScalar r0 = info.fRadius[0];
512*c8dee2aaSAndroid Build Coastguard Worker     SkScalar dr = info.fRadius[1] - info.fRadius[0];
513*c8dee2aaSAndroid Build Coastguard Worker     SkScalar a = dx * dx + dy * dy - dr * dr;
514*c8dee2aaSAndroid Build Coastguard Worker 
515*c8dee2aaSAndroid Build Coastguard Worker     // First compute t, if the pixel falls outside the cone, then we'll end
516*c8dee2aaSAndroid Build Coastguard Worker     // with 'false' on the stack, otherwise we'll push 'true' with t below it
517*c8dee2aaSAndroid Build Coastguard Worker 
518*c8dee2aaSAndroid Build Coastguard Worker     // We start with a stack of (x y), copy it and then consume one copy in
519*c8dee2aaSAndroid Build Coastguard Worker     // order to calculate b and the other to calculate c.
520*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("{");
521*c8dee2aaSAndroid Build Coastguard Worker 
522*c8dee2aaSAndroid Build Coastguard Worker     apply_perspective_to_coordinates(perspectiveRemover, function);
523*c8dee2aaSAndroid Build Coastguard Worker 
524*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("2 copy ");
525*c8dee2aaSAndroid Build Coastguard Worker 
526*c8dee2aaSAndroid Build Coastguard Worker     // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
527*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendScalar(dy, function);
528*c8dee2aaSAndroid Build Coastguard Worker     function->writeText(" mul exch ");
529*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendScalar(dx, function);
530*c8dee2aaSAndroid Build Coastguard Worker     function->writeText(" mul add ");
531*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendScalar(r0 * dr, function);
532*c8dee2aaSAndroid Build Coastguard Worker     function->writeText(" add -2 mul dup dup mul\n");
533*c8dee2aaSAndroid Build Coastguard Worker 
534*c8dee2aaSAndroid Build Coastguard Worker     // c = x^2 + y^2 + radius0^2
535*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("4 2 roll dup mul exch dup mul add ");
536*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendScalar(r0 * r0, function);
537*c8dee2aaSAndroid Build Coastguard Worker     function->writeText(" sub dup 4 1 roll\n");
538*c8dee2aaSAndroid Build Coastguard Worker 
539*c8dee2aaSAndroid Build Coastguard Worker     // Contents of the stack at this point: c, b, b^2, c
540*c8dee2aaSAndroid Build Coastguard Worker 
541*c8dee2aaSAndroid Build Coastguard Worker     // if a = 0, then we collapse to a simpler linear case
542*c8dee2aaSAndroid Build Coastguard Worker     if (a == 0) {
543*c8dee2aaSAndroid Build Coastguard Worker 
544*c8dee2aaSAndroid Build Coastguard Worker         // t = -c/b
545*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("pop pop div neg dup ");
546*c8dee2aaSAndroid Build Coastguard Worker 
547*c8dee2aaSAndroid Build Coastguard Worker         // compute radius(t)
548*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(dr, function);
549*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" mul ");
550*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(r0, function);
551*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" add\n");
552*c8dee2aaSAndroid Build Coastguard Worker 
553*c8dee2aaSAndroid Build Coastguard Worker         // if r(t) < 0, then it's outside the cone
554*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("0 lt {pop false} {true} ifelse\n");
555*c8dee2aaSAndroid Build Coastguard Worker 
556*c8dee2aaSAndroid Build Coastguard Worker     } else {
557*c8dee2aaSAndroid Build Coastguard Worker 
558*c8dee2aaSAndroid Build Coastguard Worker         // quadratic case: the Canvas spec wants the largest
559*c8dee2aaSAndroid Build Coastguard Worker         // root t for which radius(t) > 0
560*c8dee2aaSAndroid Build Coastguard Worker 
561*c8dee2aaSAndroid Build Coastguard Worker         // compute the discriminant (b^2 - 4ac)
562*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(a * 4, function);
563*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" mul sub dup\n");
564*c8dee2aaSAndroid Build Coastguard Worker 
565*c8dee2aaSAndroid Build Coastguard Worker         // if d >= 0, proceed
566*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("0 ge {\n");
567*c8dee2aaSAndroid Build Coastguard Worker 
568*c8dee2aaSAndroid Build Coastguard Worker         // an intermediate value we'll use to compute the roots:
569*c8dee2aaSAndroid Build Coastguard Worker         // q = -0.5 * (b +/- sqrt(d))
570*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("sqrt exch dup 0 lt {exch -1 mul} if");
571*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" add -0.5 mul dup\n");
572*c8dee2aaSAndroid Build Coastguard Worker 
573*c8dee2aaSAndroid Build Coastguard Worker         // first root = q / a
574*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(a, function);
575*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" div\n");
576*c8dee2aaSAndroid Build Coastguard Worker 
577*c8dee2aaSAndroid Build Coastguard Worker         // second root = c / q
578*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("3 1 roll div\n");
579*c8dee2aaSAndroid Build Coastguard Worker 
580*c8dee2aaSAndroid Build Coastguard Worker         // put the larger root on top of the stack
581*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("2 copy gt {exch} if\n");
582*c8dee2aaSAndroid Build Coastguard Worker 
583*c8dee2aaSAndroid Build Coastguard Worker         // compute radius(t) for larger root
584*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("dup ");
585*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(dr, function);
586*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" mul ");
587*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(r0, function);
588*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" add\n");
589*c8dee2aaSAndroid Build Coastguard Worker 
590*c8dee2aaSAndroid Build Coastguard Worker         // if r(t) > 0, we have our t, pop off the smaller root and we're done
591*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" 0 gt {exch pop true}\n");
592*c8dee2aaSAndroid Build Coastguard Worker 
593*c8dee2aaSAndroid Build Coastguard Worker         // otherwise, throw out the larger one and try the smaller root
594*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("{pop dup\n");
595*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(dr, function);
596*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" mul ");
597*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(r0, function);
598*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" add\n");
599*c8dee2aaSAndroid Build Coastguard Worker 
600*c8dee2aaSAndroid Build Coastguard Worker         // if r(t) < 0, push false, otherwise the smaller root is our t
601*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("0 le {pop false} {true} ifelse\n");
602*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("} ifelse\n");
603*c8dee2aaSAndroid Build Coastguard Worker 
604*c8dee2aaSAndroid Build Coastguard Worker         // d < 0, clear the stack and push false
605*c8dee2aaSAndroid Build Coastguard Worker         function->writeText("} {pop pop pop false} ifelse\n");
606*c8dee2aaSAndroid Build Coastguard Worker     }
607*c8dee2aaSAndroid Build Coastguard Worker 
608*c8dee2aaSAndroid Build Coastguard Worker     // if the pixel is in the cone, proceed to compute a color
609*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("{");
610*c8dee2aaSAndroid Build Coastguard Worker     tileModeCode((SkTileMode)info.fTileMode, function);
611*c8dee2aaSAndroid Build Coastguard Worker     gradient_function_code(info, function);
612*c8dee2aaSAndroid Build Coastguard Worker 
613*c8dee2aaSAndroid Build Coastguard Worker     // otherwise, just write black
614*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Correctly draw gradients_local_persepective, need to mask out this black
615*c8dee2aaSAndroid Build Coastguard Worker     // The "gradients" gm works as falls into the 8.7.4.5.4 "Type 3 (Radial) Shadings" case.
616*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("} {0 0 0} ifelse }");
617*c8dee2aaSAndroid Build Coastguard Worker }
618*c8dee2aaSAndroid Build Coastguard Worker 
sweepCode(const SkShaderBase::GradientInfo & info,const SkMatrix & perspectiveRemover,SkDynamicMemoryWStream * function)619*c8dee2aaSAndroid Build Coastguard Worker static void sweepCode(const SkShaderBase::GradientInfo& info,
620*c8dee2aaSAndroid Build Coastguard Worker                       const SkMatrix& perspectiveRemover,
621*c8dee2aaSAndroid Build Coastguard Worker                       SkDynamicMemoryWStream* function) {
622*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("{exch atan 360 div\n");
623*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar bias = info.fPoint[1].y();
624*c8dee2aaSAndroid Build Coastguard Worker     if (bias != 0.0f) {
625*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(bias, function);
626*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" add\n");
627*c8dee2aaSAndroid Build Coastguard Worker     }
628*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar scale = info.fPoint[1].x();
629*c8dee2aaSAndroid Build Coastguard Worker     if (scale != 1.0f) {
630*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::AppendScalar(scale, function);
631*c8dee2aaSAndroid Build Coastguard Worker         function->writeText(" mul\n");
632*c8dee2aaSAndroid Build Coastguard Worker     }
633*c8dee2aaSAndroid Build Coastguard Worker     tileModeCode((SkTileMode)info.fTileMode, function);
634*c8dee2aaSAndroid Build Coastguard Worker     gradient_function_code(info, function);
635*c8dee2aaSAndroid Build Coastguard Worker     function->writeText("}");
636*c8dee2aaSAndroid Build Coastguard Worker }
637*c8dee2aaSAndroid Build Coastguard Worker 
638*c8dee2aaSAndroid Build Coastguard Worker 
639*c8dee2aaSAndroid Build Coastguard Worker // catch cases where the inner just touches the outer circle
640*c8dee2aaSAndroid Build Coastguard Worker // and make the inner circle just inside the outer one to match raster
FixUpRadius(const SkPoint & p1,SkScalar & r1,const SkPoint & p2,SkScalar & r2)641*c8dee2aaSAndroid Build Coastguard Worker static void FixUpRadius(const SkPoint& p1, SkScalar& r1, const SkPoint& p2, SkScalar& r2) {
642*c8dee2aaSAndroid Build Coastguard Worker     // detect touching circles
643*c8dee2aaSAndroid Build Coastguard Worker     SkScalar distance = SkPoint::Distance(p1, p2);
644*c8dee2aaSAndroid Build Coastguard Worker     SkScalar subtractRadii = fabs(r1 - r2);
645*c8dee2aaSAndroid Build Coastguard Worker     if (fabs(distance - subtractRadii) < 0.002f) {
646*c8dee2aaSAndroid Build Coastguard Worker         if (r1 > r2) {
647*c8dee2aaSAndroid Build Coastguard Worker             r1 += 0.002f;
648*c8dee2aaSAndroid Build Coastguard Worker         } else {
649*c8dee2aaSAndroid Build Coastguard Worker             r2 += 0.002f;
650*c8dee2aaSAndroid Build Coastguard Worker         }
651*c8dee2aaSAndroid Build Coastguard Worker     }
652*c8dee2aaSAndroid Build Coastguard Worker }
653*c8dee2aaSAndroid Build Coastguard Worker 
654*c8dee2aaSAndroid Build Coastguard Worker // Finds affine and persp such that in = affine * persp.
655*c8dee2aaSAndroid Build Coastguard Worker // but it returns the inverse of perspective matrix.
split_perspective(const SkMatrix in,SkMatrix * affine,SkMatrix * perspectiveInverse)656*c8dee2aaSAndroid Build Coastguard Worker static bool split_perspective(const SkMatrix in, SkMatrix* affine,
657*c8dee2aaSAndroid Build Coastguard Worker                               SkMatrix* perspectiveInverse) {
658*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar p2 = in[SkMatrix::kMPersp2];
659*c8dee2aaSAndroid Build Coastguard Worker 
660*c8dee2aaSAndroid Build Coastguard Worker     if (SkScalarNearlyZero(p2)) {
661*c8dee2aaSAndroid Build Coastguard Worker         return false;
662*c8dee2aaSAndroid Build Coastguard Worker     }
663*c8dee2aaSAndroid Build Coastguard Worker 
664*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar zero = SkIntToScalar(0);
665*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar one = SkIntToScalar(1);
666*c8dee2aaSAndroid Build Coastguard Worker 
667*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar sx = in[SkMatrix::kMScaleX];
668*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar kx = in[SkMatrix::kMSkewX];
669*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar tx = in[SkMatrix::kMTransX];
670*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar ky = in[SkMatrix::kMSkewY];
671*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar sy = in[SkMatrix::kMScaleY];
672*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar ty = in[SkMatrix::kMTransY];
673*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar p0 = in[SkMatrix::kMPersp0];
674*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar p1 = in[SkMatrix::kMPersp1];
675*c8dee2aaSAndroid Build Coastguard Worker 
676*c8dee2aaSAndroid Build Coastguard Worker     // Perspective matrix would be:
677*c8dee2aaSAndroid Build Coastguard Worker     // 1  0  0
678*c8dee2aaSAndroid Build Coastguard Worker     // 0  1  0
679*c8dee2aaSAndroid Build Coastguard Worker     // p0 p1 p2
680*c8dee2aaSAndroid Build Coastguard Worker     // But we need the inverse of persp.
681*c8dee2aaSAndroid Build Coastguard Worker     perspectiveInverse->setAll(one,          zero,       zero,
682*c8dee2aaSAndroid Build Coastguard Worker                                zero,         one,        zero,
683*c8dee2aaSAndroid Build Coastguard Worker                                -p0/p2,     -p1/p2,     1/p2);
684*c8dee2aaSAndroid Build Coastguard Worker 
685*c8dee2aaSAndroid Build Coastguard Worker     affine->setAll(sx - p0 * tx / p2,       kx - p1 * tx / p2,      tx / p2,
686*c8dee2aaSAndroid Build Coastguard Worker                    ky - p0 * ty / p2,       sy - p1 * ty / p2,      ty / p2,
687*c8dee2aaSAndroid Build Coastguard Worker                    zero,                    zero,                   one);
688*c8dee2aaSAndroid Build Coastguard Worker 
689*c8dee2aaSAndroid Build Coastguard Worker     return true;
690*c8dee2aaSAndroid Build Coastguard Worker }
691*c8dee2aaSAndroid Build Coastguard Worker 
make_ps_function(std::unique_ptr<SkStreamAsset> psCode,std::unique_ptr<SkPDFArray> domain,std::unique_ptr<SkPDFObject> range,SkPDFDocument * doc)692*c8dee2aaSAndroid Build Coastguard Worker static SkPDFIndirectReference make_ps_function(std::unique_ptr<SkStreamAsset> psCode,
693*c8dee2aaSAndroid Build Coastguard Worker                                                std::unique_ptr<SkPDFArray> domain,
694*c8dee2aaSAndroid Build Coastguard Worker                                                std::unique_ptr<SkPDFObject> range,
695*c8dee2aaSAndroid Build Coastguard Worker                                                SkPDFDocument* doc) {
696*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict();
697*c8dee2aaSAndroid Build Coastguard Worker     dict->insertInt("FunctionType", 4);
698*c8dee2aaSAndroid Build Coastguard Worker     dict->insertObject("Domain", std::move(domain));
699*c8dee2aaSAndroid Build Coastguard Worker     dict->insertObject("Range", std::move(range));
700*c8dee2aaSAndroid Build Coastguard Worker     return SkPDFStreamOut(std::move(dict), std::move(psCode), doc);
701*c8dee2aaSAndroid Build Coastguard Worker }
702*c8dee2aaSAndroid Build Coastguard Worker 
make_function_shader(SkPDFDocument * doc,const SkPDFGradientShader::Key & state)703*c8dee2aaSAndroid Build Coastguard Worker static SkPDFIndirectReference make_function_shader(SkPDFDocument* doc,
704*c8dee2aaSAndroid Build Coastguard Worker                                                    const SkPDFGradientShader::Key& state) {
705*c8dee2aaSAndroid Build Coastguard Worker     SkPoint transformPoints[2];
706*c8dee2aaSAndroid Build Coastguard Worker     const SkShaderBase::GradientInfo& info = state.fInfo;
707*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix finalMatrix = state.fCanvasTransform;
708*c8dee2aaSAndroid Build Coastguard Worker     finalMatrix.preConcat(state.fShaderTransform);
709*c8dee2aaSAndroid Build Coastguard Worker 
710*c8dee2aaSAndroid Build Coastguard Worker     bool doStitchFunctions = (state.fType == SkShaderBase::GradientType::kLinear ||
711*c8dee2aaSAndroid Build Coastguard Worker                               state.fType == SkShaderBase::GradientType::kRadial ||
712*c8dee2aaSAndroid Build Coastguard Worker                               state.fType == SkShaderBase::GradientType::kConical) &&
713*c8dee2aaSAndroid Build Coastguard Worker                              (info.fTileMode == SkTileMode::kClamp ||
714*c8dee2aaSAndroid Build Coastguard Worker                               info.fTileMode == SkTileMode::kDecal) &&
715*c8dee2aaSAndroid Build Coastguard Worker                              !finalMatrix.hasPerspective();
716*c8dee2aaSAndroid Build Coastguard Worker 
717*c8dee2aaSAndroid Build Coastguard Worker     enum class ShadingType : int32_t {
718*c8dee2aaSAndroid Build Coastguard Worker         Function = 1,
719*c8dee2aaSAndroid Build Coastguard Worker         Axial = 2,
720*c8dee2aaSAndroid Build Coastguard Worker         Radial = 3,
721*c8dee2aaSAndroid Build Coastguard Worker         FreeFormGouraudTriangleMesh = 4,
722*c8dee2aaSAndroid Build Coastguard Worker         LatticeFormGouraudTriangleMesh = 5,
723*c8dee2aaSAndroid Build Coastguard Worker         CoonsPatchMesh = 6,
724*c8dee2aaSAndroid Build Coastguard Worker         TensorProductPatchMesh = 7,
725*c8dee2aaSAndroid Build Coastguard Worker     } shadingType;
726*c8dee2aaSAndroid Build Coastguard Worker 
727*c8dee2aaSAndroid Build Coastguard Worker     auto pdfShader = SkPDFMakeDict();
728*c8dee2aaSAndroid Build Coastguard Worker     if (doStitchFunctions) {
729*c8dee2aaSAndroid Build Coastguard Worker         pdfShader->insertObject("Function", gradientStitchCode(info));
730*c8dee2aaSAndroid Build Coastguard Worker 
731*c8dee2aaSAndroid Build Coastguard Worker         if (info.fTileMode == SkTileMode::kClamp) {
732*c8dee2aaSAndroid Build Coastguard Worker             auto extend = SkPDFMakeArray();
733*c8dee2aaSAndroid Build Coastguard Worker             extend->reserve(2);
734*c8dee2aaSAndroid Build Coastguard Worker             extend->appendBool(true);
735*c8dee2aaSAndroid Build Coastguard Worker             extend->appendBool(true);
736*c8dee2aaSAndroid Build Coastguard Worker             pdfShader->insertObject("Extend", std::move(extend));
737*c8dee2aaSAndroid Build Coastguard Worker         }
738*c8dee2aaSAndroid Build Coastguard Worker 
739*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkPDFArray> coords;
740*c8dee2aaSAndroid Build Coastguard Worker         switch (state.fType) {
741*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kLinear: {
742*c8dee2aaSAndroid Build Coastguard Worker                 shadingType = ShadingType::Axial;
743*c8dee2aaSAndroid Build Coastguard Worker                 const SkPoint& pt1 = info.fPoint[0];
744*c8dee2aaSAndroid Build Coastguard Worker                 const SkPoint& pt2 = info.fPoint[1];
745*c8dee2aaSAndroid Build Coastguard Worker                 coords = SkPDFMakeArray(pt1.x(), pt1.y(),
746*c8dee2aaSAndroid Build Coastguard Worker                                         pt2.x(), pt2.y());
747*c8dee2aaSAndroid Build Coastguard Worker             } break;
748*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kRadial: {
749*c8dee2aaSAndroid Build Coastguard Worker                 shadingType = ShadingType::Radial;
750*c8dee2aaSAndroid Build Coastguard Worker                 const SkPoint& pt1 = info.fPoint[0];
751*c8dee2aaSAndroid Build Coastguard Worker                 coords = SkPDFMakeArray(pt1.x(), pt1.y(), 0,
752*c8dee2aaSAndroid Build Coastguard Worker                                         pt1.x(), pt1.y(), info.fRadius[0]);
753*c8dee2aaSAndroid Build Coastguard Worker             } break;
754*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kConical: {
755*c8dee2aaSAndroid Build Coastguard Worker                 shadingType = ShadingType::Radial;
756*c8dee2aaSAndroid Build Coastguard Worker                 SkScalar r1 = info.fRadius[0];
757*c8dee2aaSAndroid Build Coastguard Worker                 SkScalar r2 = info.fRadius[1];
758*c8dee2aaSAndroid Build Coastguard Worker                 SkPoint pt1 = info.fPoint[0];
759*c8dee2aaSAndroid Build Coastguard Worker                 SkPoint pt2 = info.fPoint[1];
760*c8dee2aaSAndroid Build Coastguard Worker                 FixUpRadius(pt1, r1, pt2, r2);
761*c8dee2aaSAndroid Build Coastguard Worker 
762*c8dee2aaSAndroid Build Coastguard Worker                 coords = SkPDFMakeArray(pt1.x(), pt1.y(), r1,
763*c8dee2aaSAndroid Build Coastguard Worker                                         pt2.x(), pt2.y(), r2);
764*c8dee2aaSAndroid Build Coastguard Worker                 break;
765*c8dee2aaSAndroid Build Coastguard Worker             }
766*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kSweep:
767*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kNone:
768*c8dee2aaSAndroid Build Coastguard Worker             default:
769*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(false);
770*c8dee2aaSAndroid Build Coastguard Worker                 return SkPDFIndirectReference();
771*c8dee2aaSAndroid Build Coastguard Worker         }
772*c8dee2aaSAndroid Build Coastguard Worker         pdfShader->insertObject("Coords", std::move(coords));
773*c8dee2aaSAndroid Build Coastguard Worker     } else {
774*c8dee2aaSAndroid Build Coastguard Worker         shadingType = ShadingType::Function;
775*c8dee2aaSAndroid Build Coastguard Worker 
776*c8dee2aaSAndroid Build Coastguard Worker         // Transform the coordinate space for the type of gradient.
777*c8dee2aaSAndroid Build Coastguard Worker         transformPoints[0] = info.fPoint[0];
778*c8dee2aaSAndroid Build Coastguard Worker         transformPoints[1] = info.fPoint[1];
779*c8dee2aaSAndroid Build Coastguard Worker         switch (state.fType) {
780*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kLinear:
781*c8dee2aaSAndroid Build Coastguard Worker                 break;
782*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kRadial:
783*c8dee2aaSAndroid Build Coastguard Worker                 transformPoints[1] = transformPoints[0];
784*c8dee2aaSAndroid Build Coastguard Worker                 transformPoints[1].fX += info.fRadius[0];
785*c8dee2aaSAndroid Build Coastguard Worker                 break;
786*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kConical: {
787*c8dee2aaSAndroid Build Coastguard Worker                 transformPoints[1] = transformPoints[0];
788*c8dee2aaSAndroid Build Coastguard Worker                 transformPoints[1].fX += SK_Scalar1;
789*c8dee2aaSAndroid Build Coastguard Worker                 break;
790*c8dee2aaSAndroid Build Coastguard Worker             }
791*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kSweep:
792*c8dee2aaSAndroid Build Coastguard Worker                 transformPoints[1] = transformPoints[0];
793*c8dee2aaSAndroid Build Coastguard Worker                 transformPoints[1].fX += SK_Scalar1;
794*c8dee2aaSAndroid Build Coastguard Worker                 break;
795*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kNone:
796*c8dee2aaSAndroid Build Coastguard Worker             default:
797*c8dee2aaSAndroid Build Coastguard Worker                 return SkPDFIndirectReference();
798*c8dee2aaSAndroid Build Coastguard Worker         }
799*c8dee2aaSAndroid Build Coastguard Worker 
800*c8dee2aaSAndroid Build Coastguard Worker         // Move any scaling (assuming a unit gradient) or translation
801*c8dee2aaSAndroid Build Coastguard Worker         // (and rotation for linear gradient), of the final gradient from
802*c8dee2aaSAndroid Build Coastguard Worker         // info.fPoints to the matrix (updating bbox appropriately).  Now
803*c8dee2aaSAndroid Build Coastguard Worker         // the gradient can be drawn on on the unit segment.
804*c8dee2aaSAndroid Build Coastguard Worker         SkMatrix mapperMatrix;
805*c8dee2aaSAndroid Build Coastguard Worker         unit_to_points_matrix(transformPoints, &mapperMatrix);
806*c8dee2aaSAndroid Build Coastguard Worker 
807*c8dee2aaSAndroid Build Coastguard Worker         finalMatrix.preConcat(mapperMatrix);
808*c8dee2aaSAndroid Build Coastguard Worker 
809*c8dee2aaSAndroid Build Coastguard Worker         // Preserves as much as possible in the final matrix, and only removes
810*c8dee2aaSAndroid Build Coastguard Worker         // the perspective. The inverse of the perspective is stored in
811*c8dee2aaSAndroid Build Coastguard Worker         // perspectiveInverseOnly matrix and has 3 useful numbers
812*c8dee2aaSAndroid Build Coastguard Worker         // (p0, p1, p2), while everything else is either 0 or 1.
813*c8dee2aaSAndroid Build Coastguard Worker         // In this way the shader will handle it eficiently, with minimal code.
814*c8dee2aaSAndroid Build Coastguard Worker         SkMatrix perspectiveInverseOnly = SkMatrix::I();
815*c8dee2aaSAndroid Build Coastguard Worker         if (finalMatrix.hasPerspective()) {
816*c8dee2aaSAndroid Build Coastguard Worker             if (!split_perspective(finalMatrix,
817*c8dee2aaSAndroid Build Coastguard Worker                                    &finalMatrix, &perspectiveInverseOnly)) {
818*c8dee2aaSAndroid Build Coastguard Worker                 return SkPDFIndirectReference();
819*c8dee2aaSAndroid Build Coastguard Worker             }
820*c8dee2aaSAndroid Build Coastguard Worker         }
821*c8dee2aaSAndroid Build Coastguard Worker 
822*c8dee2aaSAndroid Build Coastguard Worker         SkRect bbox;
823*c8dee2aaSAndroid Build Coastguard Worker         bbox.set(state.fBBox);
824*c8dee2aaSAndroid Build Coastguard Worker         if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &bbox)) {
825*c8dee2aaSAndroid Build Coastguard Worker             return SkPDFIndirectReference();
826*c8dee2aaSAndroid Build Coastguard Worker         }
827*c8dee2aaSAndroid Build Coastguard Worker 
828*c8dee2aaSAndroid Build Coastguard Worker         SkDynamicMemoryWStream functionCode;
829*c8dee2aaSAndroid Build Coastguard Worker         switch (state.fType) {
830*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kLinear:
831*c8dee2aaSAndroid Build Coastguard Worker                 linearCode(info, perspectiveInverseOnly, &functionCode);
832*c8dee2aaSAndroid Build Coastguard Worker                 break;
833*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kRadial:
834*c8dee2aaSAndroid Build Coastguard Worker                 radialCode(info, perspectiveInverseOnly, &functionCode);
835*c8dee2aaSAndroid Build Coastguard Worker                 break;
836*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kConical: {
837*c8dee2aaSAndroid Build Coastguard Worker                 // The two point radial gradient further references state.fInfo
838*c8dee2aaSAndroid Build Coastguard Worker                 // in translating from x, y coordinates to the t parameter. So, we have
839*c8dee2aaSAndroid Build Coastguard Worker                 // to transform the points and radii according to the calculated matrix.
840*c8dee2aaSAndroid Build Coastguard Worker                 SkShaderBase::GradientInfo infoCopy = info;
841*c8dee2aaSAndroid Build Coastguard Worker                 SkMatrix inverseMapperMatrix;
842*c8dee2aaSAndroid Build Coastguard Worker                 if (!mapperMatrix.invert(&inverseMapperMatrix)) {
843*c8dee2aaSAndroid Build Coastguard Worker                     return SkPDFIndirectReference();
844*c8dee2aaSAndroid Build Coastguard Worker                 }
845*c8dee2aaSAndroid Build Coastguard Worker                 inverseMapperMatrix.mapPoints(infoCopy.fPoint, 2);
846*c8dee2aaSAndroid Build Coastguard Worker                 infoCopy.fRadius[0] = inverseMapperMatrix.mapRadius(info.fRadius[0]);
847*c8dee2aaSAndroid Build Coastguard Worker                 infoCopy.fRadius[1] = inverseMapperMatrix.mapRadius(info.fRadius[1]);
848*c8dee2aaSAndroid Build Coastguard Worker                 twoPointConicalCode(infoCopy, perspectiveInverseOnly, &functionCode);
849*c8dee2aaSAndroid Build Coastguard Worker             } break;
850*c8dee2aaSAndroid Build Coastguard Worker             case SkShaderBase::GradientType::kSweep:
851*c8dee2aaSAndroid Build Coastguard Worker                 sweepCode(info, perspectiveInverseOnly, &functionCode);
852*c8dee2aaSAndroid Build Coastguard Worker                 break;
853*c8dee2aaSAndroid Build Coastguard Worker             default:
854*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(false);
855*c8dee2aaSAndroid Build Coastguard Worker         }
856*c8dee2aaSAndroid Build Coastguard Worker         pdfShader->insertObject(
857*c8dee2aaSAndroid Build Coastguard Worker                 "Domain", SkPDFMakeArray(bbox.left(), bbox.right(), bbox.top(), bbox.bottom()));
858*c8dee2aaSAndroid Build Coastguard Worker 
859*c8dee2aaSAndroid Build Coastguard Worker         auto domain = SkPDFMakeArray(bbox.left(), bbox.right(), bbox.top(), bbox.bottom());
860*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkPDFArray> rangeObject = SkPDFMakeArray(0, 1, 0, 1, 0, 1);
861*c8dee2aaSAndroid Build Coastguard Worker         pdfShader->insertRef("Function",
862*c8dee2aaSAndroid Build Coastguard Worker                              make_ps_function(functionCode.detachAsStream(), std::move(domain),
863*c8dee2aaSAndroid Build Coastguard Worker                                               std::move(rangeObject), doc));
864*c8dee2aaSAndroid Build Coastguard Worker     }
865*c8dee2aaSAndroid Build Coastguard Worker 
866*c8dee2aaSAndroid Build Coastguard Worker     pdfShader->insertInt("ShadingType", SkToS32(shadingType));
867*c8dee2aaSAndroid Build Coastguard Worker     pdfShader->insertName("ColorSpace", "DeviceRGB");
868*c8dee2aaSAndroid Build Coastguard Worker 
869*c8dee2aaSAndroid Build Coastguard Worker     SkPDFDict pdfFunctionShader("Pattern");
870*c8dee2aaSAndroid Build Coastguard Worker     pdfFunctionShader.insertInt("PatternType", 2);
871*c8dee2aaSAndroid Build Coastguard Worker     pdfFunctionShader.insertObject("Matrix", SkPDFUtils::MatrixToArray(finalMatrix));
872*c8dee2aaSAndroid Build Coastguard Worker     pdfFunctionShader.insertObject("Shading", std::move(pdfShader));
873*c8dee2aaSAndroid Build Coastguard Worker     return doc->emit(pdfFunctionShader);
874*c8dee2aaSAndroid Build Coastguard Worker }
875*c8dee2aaSAndroid Build Coastguard Worker 
876*c8dee2aaSAndroid Build Coastguard Worker static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
877*c8dee2aaSAndroid Build Coastguard Worker                                               SkPDFGradientShader::Key key,
878*c8dee2aaSAndroid Build Coastguard Worker                                               bool keyHasAlpha);
879*c8dee2aaSAndroid Build Coastguard Worker 
get_gradient_resource_dict(SkPDFIndirectReference functionShader,SkPDFIndirectReference gState)880*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<SkPDFDict> get_gradient_resource_dict(SkPDFIndirectReference functionShader,
881*c8dee2aaSAndroid Build Coastguard Worker                                                    SkPDFIndirectReference gState) {
882*c8dee2aaSAndroid Build Coastguard Worker     std::vector<SkPDFIndirectReference> patternShaders;
883*c8dee2aaSAndroid Build Coastguard Worker     if (functionShader != SkPDFIndirectReference()) {
884*c8dee2aaSAndroid Build Coastguard Worker         patternShaders.push_back(functionShader);
885*c8dee2aaSAndroid Build Coastguard Worker     }
886*c8dee2aaSAndroid Build Coastguard Worker     std::vector<SkPDFIndirectReference> graphicStates;
887*c8dee2aaSAndroid Build Coastguard Worker     if (gState != SkPDFIndirectReference()) {
888*c8dee2aaSAndroid Build Coastguard Worker         graphicStates.push_back(gState);
889*c8dee2aaSAndroid Build Coastguard Worker     }
890*c8dee2aaSAndroid Build Coastguard Worker     return SkPDFMakeResourceDict(std::move(graphicStates),
891*c8dee2aaSAndroid Build Coastguard Worker                                  std::move(patternShaders),
892*c8dee2aaSAndroid Build Coastguard Worker                                  std::vector<SkPDFIndirectReference>(),
893*c8dee2aaSAndroid Build Coastguard Worker                                  std::vector<SkPDFIndirectReference>());
894*c8dee2aaSAndroid Build Coastguard Worker }
895*c8dee2aaSAndroid Build Coastguard Worker 
896*c8dee2aaSAndroid Build Coastguard Worker // Creates a content stream which fills the pattern P0 across bounds.
897*c8dee2aaSAndroid Build Coastguard Worker // @param gsIndex A graphics state resource index to apply, or <0 if no
898*c8dee2aaSAndroid Build Coastguard Worker // graphics state to apply.
create_pattern_fill_content(int gsIndex,int patternIndex,SkRect & bounds)899*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex,
900*c8dee2aaSAndroid Build Coastguard Worker                                                                   int patternIndex,
901*c8dee2aaSAndroid Build Coastguard Worker                                                                   SkRect& bounds) {
902*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream content;
903*c8dee2aaSAndroid Build Coastguard Worker     if (gsIndex >= 0) {
904*c8dee2aaSAndroid Build Coastguard Worker         SkPDFUtils::ApplyGraphicState(gsIndex, &content);
905*c8dee2aaSAndroid Build Coastguard Worker     }
906*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::ApplyPattern(patternIndex, &content);
907*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::AppendRectangle(bounds, &content);
908*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPathFillType::kEvenOdd, &content);
909*c8dee2aaSAndroid Build Coastguard Worker     return content.detachAsStream();
910*c8dee2aaSAndroid Build Coastguard Worker }
911*c8dee2aaSAndroid Build Coastguard Worker 
gradient_has_alpha(const SkPDFGradientShader::Key & key)912*c8dee2aaSAndroid Build Coastguard Worker static bool gradient_has_alpha(const SkPDFGradientShader::Key& key) {
913*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(key.fType != SkShaderBase::GradientType::kNone);
914*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < key.fInfo.fColorCount; i++) {
915*c8dee2aaSAndroid Build Coastguard Worker         if ((SkAlpha)SkColorGetA(key.fInfo.fColors[i]) != SK_AlphaOPAQUE) {
916*c8dee2aaSAndroid Build Coastguard Worker             return true;
917*c8dee2aaSAndroid Build Coastguard Worker         }
918*c8dee2aaSAndroid Build Coastguard Worker     }
919*c8dee2aaSAndroid Build Coastguard Worker     return false;
920*c8dee2aaSAndroid Build Coastguard Worker }
921*c8dee2aaSAndroid Build Coastguard Worker 
922*c8dee2aaSAndroid Build Coastguard Worker // warning: does not set fHash on new key.  (Both callers need to change fields.)
clone_key(const SkPDFGradientShader::Key & k)923*c8dee2aaSAndroid Build Coastguard Worker static SkPDFGradientShader::Key clone_key(const SkPDFGradientShader::Key& k) {
924*c8dee2aaSAndroid Build Coastguard Worker     SkPDFGradientShader::Key clone = {
925*c8dee2aaSAndroid Build Coastguard Worker         k.fType,
926*c8dee2aaSAndroid Build Coastguard Worker         k.fInfo,  // change pointers later.
927*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkColor[]>(new SkColor[k.fInfo.fColorCount]),
928*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkScalar[]>(new SkScalar[k.fInfo.fColorCount]),
929*c8dee2aaSAndroid Build Coastguard Worker         k.fCanvasTransform,
930*c8dee2aaSAndroid Build Coastguard Worker         k.fShaderTransform,
931*c8dee2aaSAndroid Build Coastguard Worker         k.fBBox, 0};
932*c8dee2aaSAndroid Build Coastguard Worker     clone.fInfo.fColors = clone.fColors.get();
933*c8dee2aaSAndroid Build Coastguard Worker     clone.fInfo.fColorOffsets = clone.fStops.get();
934*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < clone.fInfo.fColorCount; i++) {
935*c8dee2aaSAndroid Build Coastguard Worker         clone.fInfo.fColorOffsets[i] = k.fInfo.fColorOffsets[i];
936*c8dee2aaSAndroid Build Coastguard Worker         clone.fInfo.fColors[i] = k.fInfo.fColors[i];
937*c8dee2aaSAndroid Build Coastguard Worker     }
938*c8dee2aaSAndroid Build Coastguard Worker     return clone;
939*c8dee2aaSAndroid Build Coastguard Worker }
940*c8dee2aaSAndroid Build Coastguard Worker 
create_smask_graphic_state(SkPDFDocument * doc,const SkPDFGradientShader::Key & state)941*c8dee2aaSAndroid Build Coastguard Worker static SkPDFIndirectReference create_smask_graphic_state(SkPDFDocument* doc,
942*c8dee2aaSAndroid Build Coastguard Worker                                                      const SkPDFGradientShader::Key& state) {
943*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(state.fType != SkShaderBase::GradientType::kNone);
944*c8dee2aaSAndroid Build Coastguard Worker     SkPDFGradientShader::Key luminosityState = clone_key(state);
945*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < luminosityState.fInfo.fColorCount; i++) {
946*c8dee2aaSAndroid Build Coastguard Worker         SkAlpha alpha = SkColorGetA(luminosityState.fInfo.fColors[i]);
947*c8dee2aaSAndroid Build Coastguard Worker         luminosityState.fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha);
948*c8dee2aaSAndroid Build Coastguard Worker     }
949*c8dee2aaSAndroid Build Coastguard Worker     luminosityState.fHash = hash(luminosityState);
950*c8dee2aaSAndroid Build Coastguard Worker 
951*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!gradient_has_alpha(luminosityState));
952*c8dee2aaSAndroid Build Coastguard Worker     SkPDFIndirectReference luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
953*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkPDFDict> resources = get_gradient_resource_dict(luminosityShader,
954*c8dee2aaSAndroid Build Coastguard Worker                                                             SkPDFIndirectReference());
955*c8dee2aaSAndroid Build Coastguard Worker     SkRect bbox = SkRect::Make(state.fBBox);
956*c8dee2aaSAndroid Build Coastguard Worker     SkPDFIndirectReference alphaMask =
957*c8dee2aaSAndroid Build Coastguard Worker             SkPDFMakeFormXObject(doc,
958*c8dee2aaSAndroid Build Coastguard Worker                                  create_pattern_fill_content(-1, luminosityShader.fValue, bbox),
959*c8dee2aaSAndroid Build Coastguard Worker                                  SkPDFUtils::RectToArray(bbox),
960*c8dee2aaSAndroid Build Coastguard Worker                                  std::move(resources),
961*c8dee2aaSAndroid Build Coastguard Worker                                  SkMatrix::I(),
962*c8dee2aaSAndroid Build Coastguard Worker                                  "DeviceRGB");
963*c8dee2aaSAndroid Build Coastguard Worker     return SkPDFGraphicState::GetSMaskGraphicState(
964*c8dee2aaSAndroid Build Coastguard Worker             alphaMask, false, SkPDFGraphicState::kLuminosity_SMaskMode, doc);
965*c8dee2aaSAndroid Build Coastguard Worker }
966*c8dee2aaSAndroid Build Coastguard Worker 
make_alpha_function_shader(SkPDFDocument * doc,const SkPDFGradientShader::Key & state)967*c8dee2aaSAndroid Build Coastguard Worker static SkPDFIndirectReference make_alpha_function_shader(SkPDFDocument* doc,
968*c8dee2aaSAndroid Build Coastguard Worker                                                          const SkPDFGradientShader::Key& state) {
969*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(state.fType != SkShaderBase::GradientType::kNone);
970*c8dee2aaSAndroid Build Coastguard Worker     SkPDFGradientShader::Key opaqueState = clone_key(state);
971*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < opaqueState.fInfo.fColorCount; i++) {
972*c8dee2aaSAndroid Build Coastguard Worker         opaqueState.fInfo.fColors[i] = SkColorSetA(opaqueState.fInfo.fColors[i], SK_AlphaOPAQUE);
973*c8dee2aaSAndroid Build Coastguard Worker     }
974*c8dee2aaSAndroid Build Coastguard Worker     opaqueState.fHash = hash(opaqueState);
975*c8dee2aaSAndroid Build Coastguard Worker 
976*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!gradient_has_alpha(opaqueState));
977*c8dee2aaSAndroid Build Coastguard Worker     SkRect bbox = SkRect::Make(state.fBBox);
978*c8dee2aaSAndroid Build Coastguard Worker     SkPDFIndirectReference colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
979*c8dee2aaSAndroid Build Coastguard Worker     if (!colorShader) {
980*c8dee2aaSAndroid Build Coastguard Worker         return SkPDFIndirectReference();
981*c8dee2aaSAndroid Build Coastguard Worker     }
982*c8dee2aaSAndroid Build Coastguard Worker     // Create resource dict with alpha graphics state as G0 and
983*c8dee2aaSAndroid Build Coastguard Worker     // pattern shader as P0, then write content stream.
984*c8dee2aaSAndroid Build Coastguard Worker     SkPDFIndirectReference alphaGsRef = create_smask_graphic_state(doc, state);
985*c8dee2aaSAndroid Build Coastguard Worker 
986*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkPDFDict> resourceDict = get_gradient_resource_dict(colorShader, alphaGsRef);
987*c8dee2aaSAndroid Build Coastguard Worker 
988*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkStreamAsset> colorStream =
989*c8dee2aaSAndroid Build Coastguard Worker             create_pattern_fill_content(alphaGsRef.fValue, colorShader.fValue, bbox);
990*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkPDFDict> alphaFunctionShader = SkPDFMakeDict();
991*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader.get(), bbox,
992*c8dee2aaSAndroid Build Coastguard Worker                                  std::move(resourceDict), SkMatrix::I());
993*c8dee2aaSAndroid Build Coastguard Worker     return SkPDFStreamOut(std::move(alphaFunctionShader), std::move(colorStream), doc);
994*c8dee2aaSAndroid Build Coastguard Worker }
995*c8dee2aaSAndroid Build Coastguard Worker 
make_key(const SkShader * shader,const SkMatrix & canvasTransform,const SkIRect & bbox)996*c8dee2aaSAndroid Build Coastguard Worker static SkPDFGradientShader::Key make_key(const SkShader* shader,
997*c8dee2aaSAndroid Build Coastguard Worker                                          const SkMatrix& canvasTransform,
998*c8dee2aaSAndroid Build Coastguard Worker                                          const SkIRect& bbox) {
999*c8dee2aaSAndroid Build Coastguard Worker     SkPDFGradientShader::Key key = {
1000*c8dee2aaSAndroid Build Coastguard Worker          SkShaderBase::GradientType::kNone,
1001*c8dee2aaSAndroid Build Coastguard Worker          {0, nullptr, nullptr, {{0, 0}, {0, 0}}, {0, 0}, SkTileMode::kClamp, 0},
1002*c8dee2aaSAndroid Build Coastguard Worker          nullptr,
1003*c8dee2aaSAndroid Build Coastguard Worker          nullptr,
1004*c8dee2aaSAndroid Build Coastguard Worker          canvasTransform,
1005*c8dee2aaSAndroid Build Coastguard Worker          SkPDFUtils::GetShaderLocalMatrix(shader),
1006*c8dee2aaSAndroid Build Coastguard Worker          bbox, 0};
1007*c8dee2aaSAndroid Build Coastguard Worker     key.fType = as_SB(shader)->asGradient(&key.fInfo);
1008*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(SkShaderBase::GradientType::kNone != key.fType);
1009*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(key.fInfo.fColorCount > 0);
1010*c8dee2aaSAndroid Build Coastguard Worker     key.fColors.reset(new SkColor[key.fInfo.fColorCount]);
1011*c8dee2aaSAndroid Build Coastguard Worker     key.fStops.reset(new SkScalar[key.fInfo.fColorCount]);
1012*c8dee2aaSAndroid Build Coastguard Worker     key.fInfo.fColors = key.fColors.get();
1013*c8dee2aaSAndroid Build Coastguard Worker     key.fInfo.fColorOffsets = key.fStops.get();
1014*c8dee2aaSAndroid Build Coastguard Worker     as_SB(shader)->asGradient(&key.fInfo);
1015*c8dee2aaSAndroid Build Coastguard Worker     key.fHash = hash(key);
1016*c8dee2aaSAndroid Build Coastguard Worker     return key;
1017*c8dee2aaSAndroid Build Coastguard Worker }
1018*c8dee2aaSAndroid Build Coastguard Worker 
find_pdf_shader(SkPDFDocument * doc,SkPDFGradientShader::Key key,bool keyHasAlpha)1019*c8dee2aaSAndroid Build Coastguard Worker static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
1020*c8dee2aaSAndroid Build Coastguard Worker                                               SkPDFGradientShader::Key key,
1021*c8dee2aaSAndroid Build Coastguard Worker                                               bool keyHasAlpha) {
1022*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(gradient_has_alpha(key) == keyHasAlpha);
1023*c8dee2aaSAndroid Build Coastguard Worker     auto& gradientPatternMap = doc->fGradientPatternMap;
1024*c8dee2aaSAndroid Build Coastguard Worker     if (SkPDFIndirectReference* ptr = gradientPatternMap.find(key)) {
1025*c8dee2aaSAndroid Build Coastguard Worker         return *ptr;
1026*c8dee2aaSAndroid Build Coastguard Worker     }
1027*c8dee2aaSAndroid Build Coastguard Worker     SkPDFIndirectReference pdfShader;
1028*c8dee2aaSAndroid Build Coastguard Worker     if (keyHasAlpha) {
1029*c8dee2aaSAndroid Build Coastguard Worker         pdfShader = make_alpha_function_shader(doc, key);
1030*c8dee2aaSAndroid Build Coastguard Worker     } else {
1031*c8dee2aaSAndroid Build Coastguard Worker         pdfShader = make_function_shader(doc, key);
1032*c8dee2aaSAndroid Build Coastguard Worker     }
1033*c8dee2aaSAndroid Build Coastguard Worker     gradientPatternMap.set(std::move(key), pdfShader);
1034*c8dee2aaSAndroid Build Coastguard Worker     return pdfShader;
1035*c8dee2aaSAndroid Build Coastguard Worker }
1036*c8dee2aaSAndroid Build Coastguard Worker 
Make(SkPDFDocument * doc,SkShader * shader,const SkMatrix & canvasTransform,const SkIRect & bbox)1037*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference SkPDFGradientShader::Make(SkPDFDocument* doc,
1038*c8dee2aaSAndroid Build Coastguard Worker                                              SkShader* shader,
1039*c8dee2aaSAndroid Build Coastguard Worker                                              const SkMatrix& canvasTransform,
1040*c8dee2aaSAndroid Build Coastguard Worker                                              const SkIRect& bbox) {
1041*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(shader);
1042*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(as_SB(shader)->asGradient() != SkShaderBase::GradientType::kNone);
1043*c8dee2aaSAndroid Build Coastguard Worker     SkPDFGradientShader::Key key = make_key(shader, canvasTransform, bbox);
1044*c8dee2aaSAndroid Build Coastguard Worker     bool alpha = gradient_has_alpha(key);
1045*c8dee2aaSAndroid Build Coastguard Worker     return find_pdf_shader(doc, std::move(key), alpha);
1046*c8dee2aaSAndroid Build Coastguard Worker }
1047