Line data Source code
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 :
14 0 : Sk4f pack_color(SkColor c, bool premul, const Sk4f& component_scale) {
15 0 : const SkColor4f c4f = SkColor4f::FromColor(c);
16 : const Sk4f pm4f = premul
17 0 : ? c4f.premul().to4f()
18 0 : : Sk4f{c4f.fR, c4f.fG, c4f.fB, c4f.fA};
19 :
20 0 : return pm4f * component_scale;
21 : }
22 :
23 : class IntervalIterator {
24 : public:
25 0 : IntervalIterator(const SkColor* colors, const SkScalar* pos, int count, bool reverse)
26 0 : : fColors(colors)
27 : , fPos(pos)
28 : , fCount(count)
29 0 : , fFirstPos(reverse ? SK_Scalar1 : 0)
30 0 : , fBegin(reverse ? count - 1 : 0)
31 0 : , fAdvance(reverse ? -1 : 1) {
32 0 : SkASSERT(colors);
33 0 : SkASSERT(count > 0);
34 0 : }
35 :
36 0 : void iterate(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const {
37 0 : if (!fPos) {
38 0 : this->iterateImplicitPos(func);
39 0 : return;
40 : }
41 :
42 0 : const int end = fBegin + fAdvance * (fCount - 1);
43 0 : const SkScalar lastPos = 1 - fFirstPos;
44 0 : int prev = fBegin;
45 0 : SkScalar prevPos = fFirstPos;
46 :
47 0 : do {
48 0 : const int curr = prev + fAdvance;
49 0 : SkASSERT(curr >= 0 && curr < fCount);
50 :
51 : // TODO: this sanitization should be done in SkGradientShaderBase
52 0 : const SkScalar currPos = (fAdvance > 0)
53 0 : ? SkTPin(fPos[curr], prevPos, lastPos)
54 0 : : SkTPin(fPos[curr], lastPos, prevPos);
55 :
56 0 : if (currPos != prevPos) {
57 0 : SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
58 0 : func(fColors[prev], fColors[curr], prevPos, currPos);
59 : }
60 :
61 0 : prev = curr;
62 0 : prevPos = currPos;
63 0 : } while (prev != end);
64 : }
65 :
66 : private:
67 0 : void iterateImplicitPos(std::function<void(SkColor, SkColor, 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 0 : const SkScalar dt = fAdvance * SK_Scalar1 / (fCount - 1);
72 0 : const int end = fBegin + fAdvance * (fCount - 2);
73 0 : int prev = fBegin;
74 0 : SkScalar prevPos = fFirstPos;
75 :
76 0 : while (prev != end) {
77 0 : const int curr = prev + fAdvance;
78 0 : SkASSERT(curr >= 0 && curr < fCount);
79 :
80 0 : const SkScalar currPos = prevPos + dt;
81 0 : func(fColors[prev], fColors[curr], prevPos, currPos);
82 0 : prev = curr;
83 0 : prevPos = currPos;
84 : }
85 :
86 : // emit the last interval with a pinned end position, to avoid precision issues
87 0 : func(fColors[prev], fColors[prev + fAdvance], prevPos, 1 - fFirstPos);
88 0 : }
89 :
90 : const SkColor* fColors;
91 : const SkScalar* fPos;
92 : const int fCount;
93 : const SkScalar fFirstPos;
94 : const int fBegin;
95 : const int fAdvance;
96 : };
97 :
98 0 : void addMirrorIntervals(const SkColor colors[],
99 : const SkScalar pos[], int count,
100 : const Sk4f& componentScale,
101 : bool premulColors, bool reverse,
102 : Sk4fGradientIntervalBuffer::BufferType* buffer) {
103 0 : const IntervalIterator iter(colors, pos, count, reverse);
104 0 : iter.iterate([&] (SkColor c0, SkColor c1, SkScalar t0, SkScalar t1) {
105 0 : SkASSERT(buffer->empty() || buffer->back().fT1 == 2 - t0);
106 :
107 0 : const auto mirror_t0 = 2 - t0;
108 0 : const auto mirror_t1 = 2 - t1;
109 : // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid
110 : // triggering Interval asserts.
111 0 : if (mirror_t0 != mirror_t1) {
112 0 : buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_t0,
113 0 : pack_color(c1, premulColors, componentScale), mirror_t1);
114 : }
115 0 : });
116 0 : }
117 :
118 : } // anonymous namespace
119 :
120 0 : Sk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar t0,
121 0 : const Sk4f& c1, SkScalar t1)
122 : : fT0(t0)
123 : , fT1(t1)
124 0 : , fZeroRamp((c0 == c1).allTrue()) {
125 0 : SkASSERT(t0 != t1);
126 : // Either p0 or p1 can be (-)inf for synthetic clamp edge intervals.
127 0 : SkASSERT(SkScalarIsFinite(t0) || SkScalarIsFinite(t1));
128 :
129 0 : const auto dt = t1 - t0;
130 :
131 : // Clamp edge intervals are always zero-ramp.
132 0 : SkASSERT(SkScalarIsFinite(dt) || fZeroRamp);
133 0 : SkASSERT(SkScalarIsFinite(t0) || fZeroRamp);
134 0 : const Sk4f dc = SkScalarIsFinite(dt) ? (c1 - c0) / dt : 0;
135 0 : const Sk4f bias = c0 - (SkScalarIsFinite(t0) ? t0 * dc : 0);
136 :
137 0 : bias.store(&fCb.fVec);
138 0 : dc.store(&fCg.fVec);
139 0 : }
140 :
141 0 : void Sk4fGradientIntervalBuffer::init(const SkColor colors[], const SkScalar pos[], int count,
142 : SkShader::TileMode tileMode, bool premulColors,
143 : SkScalar alpha, bool reverse) {
144 : // The main job here is to build a specialized interval list: a different
145 : // representation of the color stops data, optimized for efficient scan line
146 : // access during shading.
147 : //
148 : // [{P0,C0} , {P1,C1}) [{P1,C2} , {P2,c3}) ... [{Pn,C2n} , {Pn+1,C2n+1})
149 : //
150 : // The list may be inverted when requested (such that e.g. points are sorted
151 : // in increasing x order when dx < 0).
152 : //
153 : // Note: the current representation duplicates pos data; we could refactor to
154 : // avoid this if interval storage size becomes a concern.
155 : //
156 : // Aside from reordering, we also perform two more pre-processing steps at
157 : // this stage:
158 : //
159 : // 1) scale the color components depending on paint alpha and the requested
160 : // interpolation space (note: the interval color storage is SkPM4f, but
161 : // that doesn't necessarily mean the colors are premultiplied; that
162 : // property is tracked in fColorsArePremul)
163 : //
164 : // 2) inject synthetic intervals to support tiling.
165 : //
166 : // * for kRepeat, no extra intervals are needed - the iterator just
167 : // wraps around at the end:
168 : //
169 : // ->[P0,P1)->..[Pn-1,Pn)->
170 : //
171 : // * for kClamp, we add two "infinite" intervals before/after:
172 : //
173 : // [-/+inf , P0)->[P0 , P1)->..[Pn-1 , Pn)->[Pn , +/-inf)
174 : //
175 : // (the iterator should never run off the end in this mode)
176 : //
177 : // * for kMirror, we extend the range to [0..2] and add a flipped
178 : // interval series - then the iterator operates just as in the
179 : // kRepeat case:
180 : //
181 : // ->[P0,P1)->..[Pn-1,Pn)->[2 - Pn,2 - Pn-1)->..[2 - P1,2 - P0)->
182 : //
183 : // TODO: investigate collapsing intervals << 1px.
184 :
185 0 : SkASSERT(count > 0);
186 0 : SkASSERT(colors);
187 :
188 0 : fIntervals.reset();
189 :
190 : const Sk4f componentScale = premulColors
191 : ? Sk4f(alpha)
192 0 : : Sk4f(1.0f, 1.0f, 1.0f, alpha);
193 0 : const int first_index = reverse ? count - 1 : 0;
194 0 : const int last_index = count - 1 - first_index;
195 0 : const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
196 0 : const SkScalar last_pos = SK_Scalar1 - first_pos;
197 :
198 0 : if (tileMode == SkShader::kClamp_TileMode) {
199 : // synthetic edge interval: -/+inf .. P0
200 0 : const Sk4f clamp_color = pack_color(colors[first_index],
201 0 : premulColors, componentScale);
202 0 : const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
203 : fIntervals.emplace_back(clamp_color, clamp_pos,
204 0 : clamp_color, first_pos);
205 0 : } else if (tileMode == SkShader::kMirror_TileMode && reverse) {
206 : // synthetic mirror intervals injected before main intervals: (2 .. 1]
207 0 : addMirrorIntervals(colors, pos, count, componentScale, premulColors, false, &fIntervals);
208 : }
209 :
210 0 : const IntervalIterator iter(colors, pos, count, reverse);
211 0 : iter.iterate([&] (SkColor c0, SkColor c1, SkScalar t0, SkScalar t1) {
212 0 : SkASSERT(fIntervals.empty() || fIntervals.back().fT1 == t0);
213 :
214 0 : fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), t0,
215 0 : pack_color(c1, premulColors, componentScale), t1);
216 0 : });
217 :
218 0 : if (tileMode == SkShader::kClamp_TileMode) {
219 : // synthetic edge interval: Pn .. +/-inf
220 0 : const Sk4f clamp_color = pack_color(colors[last_index], premulColors, componentScale);
221 0 : const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
222 : fIntervals.emplace_back(clamp_color, last_pos,
223 0 : clamp_color, clamp_pos);
224 0 : } else if (tileMode == SkShader::kMirror_TileMode && !reverse) {
225 : // synthetic mirror intervals injected after main intervals: [1 .. 2)
226 0 : addMirrorIntervals(colors, pos, count, componentScale, premulColors, true, &fIntervals);
227 : }
228 0 : }
229 :
230 0 : const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const {
231 : // Binary search.
232 0 : const auto* i0 = fIntervals.begin();
233 0 : const auto* i1 = fIntervals.end() - 1;
234 :
235 0 : while (i0 != i1) {
236 0 : SkASSERT(i0 < i1);
237 0 : SkASSERT(t >= i0->fT0 && t <= i1->fT1);
238 :
239 0 : const auto* i = i0 + ((i1 - i0) >> 1);
240 :
241 0 : if (t > i->fT1) {
242 0 : i0 = i + 1;
243 : } else {
244 0 : i1 = i;
245 : }
246 : }
247 :
248 0 : SkASSERT(i0->contains(t));
249 0 : return i0;
250 : }
251 :
252 0 : const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext(
253 : SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const {
254 :
255 0 : SkASSERT(!prev->contains(t));
256 0 : SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end());
257 0 : SkASSERT(t >= fIntervals.front().fT0 && t <= fIntervals.back().fT1);
258 :
259 0 : const auto* i = prev;
260 :
261 : // Use the |increasing| signal to figure which direction we should search for
262 : // the next interval, then perform a linear search.
263 0 : if (increasing) {
264 0 : do {
265 0 : i += 1;
266 0 : if (i >= fIntervals.end()) {
267 0 : i = fIntervals.begin();
268 : }
269 0 : } while (!i->contains(t));
270 : } else {
271 0 : do {
272 0 : i -= 1;
273 0 : if (i < fIntervals.begin()) {
274 0 : i = fIntervals.end() - 1;
275 : }
276 0 : } while (!i->contains(t));
277 : }
278 :
279 0 : return i;
280 : }
281 :
282 0 : SkGradientShaderBase::
283 : GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader,
284 0 : const ContextRec& rec)
285 : : INHERITED(shader, rec)
286 0 : , fFlags(this->INHERITED::getFlags())
287 : #ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
288 : , fDither(true)
289 : #else
290 0 : , fDither(rec.fPaint->isDither())
291 : #endif
292 : {
293 0 : const SkMatrix& inverse = this->getTotalInverse();
294 0 : fDstToPos.setConcat(shader.fPtsToUnit, inverse);
295 0 : fDstToPosProc = fDstToPos.getMapXYProc();
296 0 : fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos));
297 :
298 0 : if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) {
299 0 : fFlags |= kOpaqueAlpha_Flag;
300 : }
301 :
302 0 : fColorsArePremul =
303 0 : (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
304 0 : || shader.fColorsAreOpaque;
305 0 : }
306 :
307 0 : bool SkGradientShaderBase::
308 : GradientShaderBase4fContext::isValid() const {
309 0 : return fDstToPos.isFinite();
310 : }
311 :
312 0 : void SkGradientShaderBase::
313 : GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
314 0 : if (fColorsArePremul) {
315 0 : this->shadePremulSpan<DstType::L32, ApplyPremul::False>(x, y, dst, count);
316 : } else {
317 0 : this->shadePremulSpan<DstType::L32, ApplyPremul::True>(x, y, dst, count);
318 : }
319 0 : }
320 :
321 0 : void SkGradientShaderBase::
322 : GradientShaderBase4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
323 0 : if (fColorsArePremul) {
324 0 : this->shadePremulSpan<DstType::F32, ApplyPremul::False>(x, y, dst, count);
325 : } else {
326 0 : this->shadePremulSpan<DstType::F32, ApplyPremul::True>(x, y, dst, count);
327 : }
328 0 : }
329 :
330 : template<DstType dstType, ApplyPremul premul>
331 0 : void SkGradientShaderBase::
332 : GradientShaderBase4fContext::shadePremulSpan(int x, int y,
333 : typename DstTraits<dstType, premul>::Type dst[],
334 : int count) const {
335 : const SkGradientShaderBase& shader =
336 0 : static_cast<const SkGradientShaderBase&>(fShader);
337 :
338 0 : switch (shader.fTileMode) {
339 : case kClamp_TileMode:
340 0 : this->shadeSpanInternal<dstType,
341 : premul,
342 : kClamp_TileMode>(x, y, dst, count);
343 0 : break;
344 : case kRepeat_TileMode:
345 0 : this->shadeSpanInternal<dstType,
346 : premul,
347 : kRepeat_TileMode>(x, y, dst, count);
348 0 : break;
349 : case kMirror_TileMode:
350 0 : this->shadeSpanInternal<dstType,
351 : premul,
352 : kMirror_TileMode>(x, y, dst, count);
353 0 : break;
354 : }
355 0 : }
356 :
357 : template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
358 0 : void SkGradientShaderBase::
359 : GradientShaderBase4fContext::shadeSpanInternal(int x, int y,
360 : typename DstTraits<dstType, premul>::Type dst[],
361 : int count) const {
362 : static const int kBufSize = 128;
363 : SkScalar ts[kBufSize];
364 0 : TSampler<dstType, premul, tileMode> sampler(*this);
365 :
366 0 : SkASSERT(count > 0);
367 0 : do {
368 0 : const int n = SkTMin(kBufSize, count);
369 0 : this->mapTs(x, y, ts, n);
370 0 : for (int i = 0; i < n; ++i) {
371 0 : const Sk4f c = sampler.sample(ts[i]);
372 0 : DstTraits<dstType, premul>::store(c, dst++);
373 : }
374 0 : x += n;
375 0 : count -= n;
376 0 : } while (count > 0);
377 0 : }
378 :
379 : template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
380 : class SkGradientShaderBase::GradientShaderBase4fContext::TSampler {
381 : public:
382 0 : TSampler(const GradientShaderBase4fContext& ctx)
383 : : fCtx(ctx)
384 0 : , fInterval(nullptr) {
385 : switch (tileMode) {
386 : case kClamp_TileMode:
387 0 : fLargestIntervalValue = SK_ScalarInfinity;
388 0 : break;
389 : case kRepeat_TileMode:
390 0 : fLargestIntervalValue = nextafterf(1, 0);
391 0 : break;
392 : case kMirror_TileMode:
393 0 : fLargestIntervalValue = nextafterf(2.0f, 0);
394 0 : break;
395 : }
396 0 : }
397 :
398 0 : Sk4f sample(SkScalar t) {
399 0 : const auto tiled_t = tileProc(t);
400 :
401 0 : if (!fInterval) {
402 : // Very first sample => locate the initial interval.
403 : // TODO: maybe do this in ctor to remove a branch?
404 0 : fInterval = fCtx.fIntervals.find(tiled_t);
405 0 : this->loadIntervalData(fInterval);
406 0 : } else if (!fInterval->contains(tiled_t)) {
407 0 : fInterval = fCtx.fIntervals.findNext(tiled_t, fInterval, t >= fPrevT);
408 0 : this->loadIntervalData(fInterval);
409 : }
410 :
411 0 : fPrevT = t;
412 0 : return lerp(tiled_t);
413 : }
414 :
415 : private:
416 0 : SkScalar tileProc(SkScalar t) const {
417 : switch (tileMode) {
418 : case kClamp_TileMode:
419 : // synthetic clamp-mode edge intervals allow for a free-floating t:
420 : // [-inf..0)[0..1)[1..+inf)
421 0 : return t;
422 : case kRepeat_TileMode:
423 : // t % 1 (intervals range: [0..1))
424 : // Due to the extra arithmetic, we must clamp to ensure the value remains less than 1.
425 0 : return SkTMin(t - SkScalarFloorToScalar(t), fLargestIntervalValue);
426 : case kMirror_TileMode:
427 : // t % 2 (synthetic mirror intervals expand the range to [0..2)
428 : // Due to the extra arithmetic, we must clamp to ensure the value remains less than 2.
429 0 : return SkTMin(t - SkScalarFloorToScalar(t / 2) * 2, fLargestIntervalValue);
430 : }
431 :
432 : SK_ABORT("Unhandled tile mode.");
433 : return 0;
434 : }
435 :
436 0 : Sk4f lerp(SkScalar t) {
437 0 : SkASSERT(fInterval->contains(t));
438 0 : return fCb + fCg * t;
439 : }
440 :
441 0 : void loadIntervalData(const Sk4fGradientInterval* i) {
442 0 : fCb = DstTraits<dstType, premul>::load(i->fCb);
443 0 : fCg = DstTraits<dstType, premul>::load(i->fCg);
444 0 : }
445 :
446 : const GradientShaderBase4fContext& fCtx;
447 : const Sk4fGradientInterval* fInterval;
448 : SkScalar fPrevT;
449 : SkScalar fLargestIntervalValue;
450 : Sk4f fCb;
451 : Sk4f fCg;
452 : };
|