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