• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "Sk4fGradientBase.h"
9 
10 #include <functional>
11 
12 namespace {
13 
pack_color(const SkColor4f & c4f,bool premul,const Sk4f & component_scale)14 Sk4f pack_color(const SkColor4f& c4f, bool premul, const Sk4f& component_scale) {
15     const Sk4f pm4f = premul
16         ? c4f.premul().to4f()
17         : Sk4f{c4f.fR, c4f.fG, c4f.fB, c4f.fA};
18 
19     return pm4f * component_scale;
20 }
21 
22 class IntervalIterator {
23 public:
IntervalIterator(const SkGradientShaderBase & shader,SkColorSpace * dstCS,bool reverse)24     IntervalIterator(const SkGradientShaderBase& shader, SkColorSpace* dstCS, bool reverse)
25         : fShader(shader)
26         , fDstCS(dstCS)
27         , fFirstPos(reverse ? SK_Scalar1 : 0)
28         , fBegin(reverse ? shader.fColorCount - 1 : 0)
29         , fAdvance(reverse ? -1 : 1) {
30         SkASSERT(shader.fColorCount > 0);
31     }
32 
iterate(std::function<void (const SkColor4f &,const SkColor4f &,SkScalar,SkScalar)> func) const33     void iterate(std::function<void(const SkColor4f&, const SkColor4f&,
34                                     SkScalar, SkScalar)> func) const {
35         if (!fShader.fOrigPos) {
36             this->iterateImplicitPos(func);
37             return;
38         }
39 
40         const int end = fBegin + fAdvance * (fShader.fColorCount - 1);
41         const SkScalar lastPos = 1 - fFirstPos;
42         int prev = fBegin;
43         SkScalar prevPos = fFirstPos;
44 
45         do {
46             const int curr = prev + fAdvance;
47             SkASSERT(curr >= 0 && curr < fShader.fColorCount);
48 
49             // TODO: this sanitization should be done in SkGradientShaderBase
50             const SkScalar currPos = (fAdvance > 0)
51                 ? SkTPin(fShader.fOrigPos[curr], prevPos, lastPos)
52                 : SkTPin(fShader.fOrigPos[curr], lastPos, prevPos);
53 
54             if (currPos != prevPos) {
55                 SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
56                 func(fShader.getXformedColor(prev, fDstCS), fShader.getXformedColor(curr, fDstCS),
57                      prevPos, currPos);
58             }
59 
60             prev = curr;
61             prevPos = currPos;
62         } while (prev != end);
63     }
64 
65 private:
iterateImplicitPos(std::function<void (const SkColor4f &,const SkColor4f &,SkScalar,SkScalar)> func) const66     void iterateImplicitPos(std::function<void(const SkColor4f&, const SkColor4f&,
67                                                SkScalar, SkScalar)> func) const {
68         // When clients don't provide explicit color stop positions (fPos == nullptr),
69         // the color stops are distributed evenly across the unit interval
70         // (implicit positioning).
71         const SkScalar dt = fAdvance * SK_Scalar1 / (fShader.fColorCount - 1);
72         const int end = fBegin + fAdvance * (fShader.fColorCount - 2);
73         int prev = fBegin;
74         SkScalar prevPos = fFirstPos;
75 
76         while (prev != end) {
77             const int curr = prev + fAdvance;
78             SkASSERT(curr >= 0 && curr < fShader.fColorCount);
79 
80             const SkScalar currPos = prevPos + dt;
81             func(fShader.getXformedColor(prev, fDstCS),
82                  fShader.getXformedColor(curr, fDstCS),
83                  prevPos, currPos);
84             prev = curr;
85             prevPos = currPos;
86         }
87 
88         // emit the last interval with a pinned end position, to avoid precision issues
89         func(fShader.getXformedColor(prev, fDstCS),
90              fShader.getXformedColor(prev + fAdvance, fDstCS),
91              prevPos, 1 - fFirstPos);
92     }
93 
94     const SkGradientShaderBase& fShader;
95     SkColorSpace*               fDstCS;
96     const SkScalar              fFirstPos;
97     const int                   fBegin;
98     const int                   fAdvance;
99 };
100 
addMirrorIntervals(const SkGradientShaderBase & shader,SkColorSpace * dstCS,const Sk4f & componentScale,bool premulColors,bool reverse,Sk4fGradientIntervalBuffer::BufferType * buffer)101 void addMirrorIntervals(const SkGradientShaderBase& shader,
102                         SkColorSpace* dstCS,
103                         const Sk4f& componentScale,
104                         bool premulColors, bool reverse,
105                         Sk4fGradientIntervalBuffer::BufferType* buffer) {
106     const IntervalIterator iter(shader, dstCS, reverse);
107     iter.iterate([&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
108         SkASSERT(buffer->empty() || buffer->back().fT1 == 2 - t0);
109 
110         const auto mirror_t0 = 2 - t0;
111         const auto mirror_t1 = 2 - t1;
112         // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid
113         // triggering Interval asserts.
114         if (mirror_t0 != mirror_t1) {
115             buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_t0,
116                                  pack_color(c1, premulColors, componentScale), mirror_t1);
117         }
118     });
119 }
120 
121 } // anonymous namespace
122 
Sk4fGradientInterval(const Sk4f & c0,SkScalar t0,const Sk4f & c1,SkScalar t1)123 Sk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar t0,
124                                            const Sk4f& c1, SkScalar t1)
125     : fT0(t0)
126     , fT1(t1) {
127     SkASSERT(t0 != t1);
128     // Either p0 or p1 can be (-)inf for synthetic clamp edge intervals.
129     SkASSERT(SkScalarIsFinite(t0) || SkScalarIsFinite(t1));
130 
131     const auto dt = t1 - t0;
132 
133     // Clamp edge intervals are always zero-ramp.
134     SkASSERT(SkScalarIsFinite(dt) || (c0 == c1).allTrue());
135     SkASSERT(SkScalarIsFinite(t0) || (c0 == c1).allTrue());
136     const Sk4f   dc = SkScalarIsFinite(dt) ? (c1 - c0) / dt : 0;
137     const Sk4f bias = c0 - (SkScalarIsFinite(t0) ? t0 * dc : 0);
138 
139     bias.store(&fCb.fVec);
140     dc.store(&fCg.fVec);
141 }
142 
init(const SkGradientShaderBase & shader,SkColorSpace * dstCS,SkShader::TileMode tileMode,bool premulColors,SkScalar alpha,bool reverse)143 void Sk4fGradientIntervalBuffer::init(const SkGradientShaderBase& shader, SkColorSpace* dstCS,
144                                       SkShader::TileMode tileMode, bool premulColors,
145                                       SkScalar alpha, bool reverse) {
146     // The main job here is to build a specialized interval list: a different
147     // representation of the color stops data, optimized for efficient scan line
148     // access during shading.
149     //
150     //   [{P0,C0} , {P1,C1}) [{P1,C2} , {P2,c3}) ... [{Pn,C2n} , {Pn+1,C2n+1})
151     //
152     // The list may be inverted when requested (such that e.g. points are sorted
153     // in increasing x order when dx < 0).
154     //
155     // Note: the current representation duplicates pos data; we could refactor to
156     //       avoid this if interval storage size becomes a concern.
157     //
158     // Aside from reordering, we also perform two more pre-processing steps at
159     // this stage:
160     //
161     //   1) scale the color components depending on paint alpha and the requested
162     //      interpolation space (note: the interval color storage is SkPM4f, but
163     //      that doesn't necessarily mean the colors are premultiplied; that
164     //      property is tracked in fColorsArePremul)
165     //
166     //   2) inject synthetic intervals to support tiling.
167     //
168     //      * for kRepeat, no extra intervals are needed - the iterator just
169     //        wraps around at the end:
170     //
171     //          ->[P0,P1)->..[Pn-1,Pn)->
172     //
173     //      * for kClamp, we add two "infinite" intervals before/after:
174     //
175     //          [-/+inf , P0)->[P0 , P1)->..[Pn-1 , Pn)->[Pn , +/-inf)
176     //
177     //        (the iterator should never run off the end in this mode)
178     //
179     //      * for kMirror, we extend the range to [0..2] and add a flipped
180     //        interval series - then the iterator operates just as in the
181     //        kRepeat case:
182     //
183     //          ->[P0,P1)->..[Pn-1,Pn)->[2 - Pn,2 - Pn-1)->..[2 - P1,2 - P0)->
184     //
185     // TODO: investigate collapsing intervals << 1px.
186 
187     const auto count = shader.fColorCount;
188 
189     SkASSERT(count > 0);
190 
191     fIntervals.reset();
192 
193     const Sk4f componentScale = premulColors
194         ? Sk4f(alpha)
195         : Sk4f(1.0f, 1.0f, 1.0f, alpha);
196     const int first_index = reverse ? count - 1 : 0;
197     const int last_index = count - 1 - first_index;
198     const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
199     const SkScalar last_pos = SK_Scalar1 - first_pos;
200 
201     if (tileMode == SkShader::kClamp_TileMode) {
202         // synthetic edge interval: -/+inf .. P0
203         const Sk4f clamp_color = pack_color(shader.getXformedColor(first_index, dstCS),
204                                             premulColors, componentScale);
205         const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
206         fIntervals.emplace_back(clamp_color, clamp_pos,
207                                 clamp_color, first_pos);
208     } else if (tileMode == SkShader::kMirror_TileMode && reverse) {
209         // synthetic mirror intervals injected before main intervals: (2 .. 1]
210         addMirrorIntervals(shader, dstCS, componentScale, premulColors, false, &fIntervals);
211     }
212 
213     const IntervalIterator iter(shader, dstCS, reverse);
214     iter.iterate([&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
215         SkASSERT(fIntervals.empty() || fIntervals.back().fT1 == t0);
216 
217         fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), t0,
218                                 pack_color(c1, premulColors, componentScale), t1);
219     });
220 
221     if (tileMode == SkShader::kClamp_TileMode) {
222         // synthetic edge interval: Pn .. +/-inf
223         const Sk4f clamp_color = pack_color(shader.getXformedColor(last_index, dstCS),
224                                             premulColors, componentScale);
225         const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
226         fIntervals.emplace_back(clamp_color, last_pos,
227                                 clamp_color, clamp_pos);
228     } else if (tileMode == SkShader::kMirror_TileMode && !reverse) {
229         // synthetic mirror intervals injected after main intervals: [1 .. 2)
230         addMirrorIntervals(shader, dstCS, componentScale, premulColors, true, &fIntervals);
231     }
232 }
233 
find(SkScalar t) const234 const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const {
235     // Binary search.
236     const auto* i0 = fIntervals.begin();
237     const auto* i1 = fIntervals.end() - 1;
238 
239     while (i0 != i1) {
240         SkASSERT(i0 < i1);
241         SkASSERT(t >= i0->fT0 && t <= i1->fT1);
242 
243         const auto* i = i0 + ((i1 - i0) >> 1);
244 
245         if (t > i->fT1) {
246             i0 = i + 1;
247         } else {
248             i1 = i;
249         }
250     }
251 
252     SkASSERT(i0->contains(t));
253     return i0;
254 }
255 
findNext(SkScalar t,const Sk4fGradientInterval * prev,bool increasing) const256 const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext(
257     SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const {
258 
259     SkASSERT(!prev->contains(t));
260     SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end());
261     SkASSERT(t >= fIntervals.front().fT0 && t <= fIntervals.back().fT1);
262 
263     const auto* i = prev;
264 
265     // Use the |increasing| signal to figure which direction we should search for
266     // the next interval, then perform a linear search.
267     if (increasing) {
268         do {
269             i += 1;
270             if (i >= fIntervals.end()) {
271                 i = fIntervals.begin();
272             }
273         } while (!i->contains(t));
274     } else {
275         do {
276             i -= 1;
277             if (i < fIntervals.begin()) {
278                 i = fIntervals.end() - 1;
279             }
280         } while (!i->contains(t));
281     }
282 
283     return i;
284 }
285 
286 SkGradientShaderBase::
GradientShaderBase4fContext(const SkGradientShaderBase & shader,const ContextRec & rec)287 GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader,
288                                                          const ContextRec& rec)
289     : INHERITED(shader, rec)
290     , fFlags(this->INHERITED::getFlags())
291 {
292     SkASSERT(rec.fPreferredDstType == ContextRec::kPM4f_DstType);
293 
294     const SkMatrix& inverse = this->getTotalInverse();
295     fDstToPos.setConcat(shader.fPtsToUnit, inverse);
296     SkASSERT(!fDstToPos.hasPerspective());
297     fDstToPosProc = fDstToPos.getMapXYProc();
298 
299     if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) {
300         fFlags |= kOpaqueAlpha_Flag;
301     }
302 
303     fColorsArePremul =
304         (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
305         || shader.fColorsAreOpaque;
306 }
307 
308 bool SkGradientShaderBase::
isValid() const309 GradientShaderBase4fContext::isValid() const {
310     return fDstToPos.isFinite();
311 }
312 
313 void SkGradientShaderBase::
shadeSpan(int x,int y,SkPMColor dst[],int count)314 GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
315     // This impl only shades to 4f.
316     SkASSERT(false);
317 }
318