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