Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(458)

Side by Side Diff: src/effects/gradients/Sk4fGradientBase.cpp

Issue 1783823002: Generic 4f gradient T sampler fallback (Closed) Base URL: https://chromium.googlesource.com/skia.git@master
Patch Set: review comments Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/effects/gradients/Sk4fGradientBase.h ('k') | src/effects/gradients/Sk4fGradientPriv.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2016 Google Inc. 2 * Copyright 2016 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "Sk4fGradientBase.h" 8 #include "Sk4fGradientBase.h"
9 #include "Sk4fGradientPriv.h"
10
11 #include <functional>
9 12
10 namespace { 13 namespace {
11 14
12 // true when x is in [k1,k2) 15 SkPMColor pack_color(SkColor c, bool premul) {
13 bool in_range(SkScalar x, SkScalar k1, SkScalar k2) { 16 return premul
14 SkASSERT(k1 != k2); 17 ? SkPreMultiplyColor(c)
15 return (k1 < k2) 18 : SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), Sk ColorGetB(c));
16 ? (x >= k1 && x < k2)
17 : (x >= k2 && x < k1);
18 } 19 }
19 20
21 template<SkShader::TileMode>
22 SkScalar tileProc(SkScalar t);
23
24 template<>
25 SkScalar tileProc<SkShader::kClamp_TileMode>(SkScalar t) {
26 // synthetic clamp-mode edge intervals allow for a free-floating t:
27 // [-inf..0)[0..1)[1..+inf)
28 return t;
29 }
30
31 template<>
32 SkScalar tileProc<SkShader::kRepeat_TileMode>(SkScalar t) {
33 // t % 1 (intervals range: [0..1))
34 return t - SkScalarFloorToScalar(t);
35 }
36
37 template<>
38 SkScalar tileProc<SkShader::kMirror_TileMode>(SkScalar t) {
39 // t % 2 (synthetic mirror intervals expand the range to [0..2)
40 return t - SkScalarFloorToScalar(t / 2) * 2;
41 }
42
43 class IntervalIterator {
44 public:
45 IntervalIterator(const SkColor* colors, const SkScalar* pos, int count, bool reverse)
46 : fColors(colors)
47 , fPos(pos)
48 , fCount(count)
49 , fFirstPos(reverse ? SK_Scalar1 : 0)
50 , fBegin(reverse ? count - 1 : 0)
51 , fAdvance(reverse ? -1 : 1) {
52 SkASSERT(colors);
53 SkASSERT(count > 0);
54 }
55
56 void iterate(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const {
57 if (!fPos) {
58 this->iterateImplicitPos(func);
59 return;
60 }
61
62 const int end = fBegin + fAdvance * (fCount - 1);
63 const SkScalar lastPos = 1 - fFirstPos;
64 int prev = fBegin;
65 SkScalar prevPos = fFirstPos;
66
67 do {
68 const int curr = prev + fAdvance;
69 SkASSERT(curr >= 0 && curr < fCount);
70
71 // TODO: this sanitization should be done in SkGradientShaderBase
72 const SkScalar currPos = (fAdvance > 0)
73 ? SkTPin(fPos[curr], prevPos, lastPos)
74 : SkTPin(fPos[curr], lastPos, prevPos);
75
76 if (currPos != prevPos) {
77 SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
78 func(fColors[prev], fColors[curr], prevPos, currPos);
79 }
80
81 prev = curr;
82 prevPos = currPos;
83 } while (prev != end);
84 }
85
86 private:
87 void iterateImplicitPos(std::function<void(SkColor, SkColor, SkScalar, SkSca lar)> func) const {
88 // When clients don't provide explicit color stop positions (fPos == nul lptr),
89 // the color stops are distributed evenly across the unit interval
90 // (implicit positioning).
91 const SkScalar dt = fAdvance * SK_Scalar1 / (fCount - 1);
92 const int end = fBegin + fAdvance * (fCount - 2);
93 int prev = fBegin;
94 SkScalar prevPos = fFirstPos;
95
96 while (prev != end) {
97 const int curr = prev + fAdvance;
98 SkASSERT(curr >= 0 && curr < fCount);
99
100 const SkScalar currPos = prevPos + dt;
101 func(fColors[prev], fColors[curr], prevPos, currPos);
102 prev = curr;
103 prevPos = currPos;
104 }
105
106 // emit the last interval with a pinned end position, to avoid precision issues
107 func(fColors[prev], fColors[prev + fAdvance], prevPos, 1 - fFirstPos);
108 }
109
110 const SkColor* fColors;
111 const SkScalar* fPos;
112 const int fCount;
113 const SkScalar fFirstPos;
114 const int fBegin;
115 const int fAdvance;
116 };
117
20 } // anonymous namespace 118 } // anonymous namespace
21 119
22 SkGradientShaderBase::GradientShaderBase4fContext:: 120 SkGradientShaderBase::GradientShaderBase4fContext::
23 Interval::Interval(SkPMColor c0, SkScalar p0, 121 Interval::Interval(SkPMColor c0, SkScalar p0,
24 SkPMColor c1, SkScalar p1, 122 SkPMColor c1, SkScalar p1,
25 const Sk4f& componentScale) 123 const Sk4f& componentScale)
26 : fP0(p0) 124 : fP0(p0)
27 , fP1(p1) 125 , fP1(p1)
28 , fZeroRamp(c0 == c1) { 126 , fZeroRamp(c0 == c1) {
29 SkASSERT(p0 != p1); 127 SkASSERT(p0 != p1);
30 128
31 const Sk4f c4f0 = SkPM4f::FromPMColor(c0).to4f() * componentScale; 129 const Sk4f c4f0 = SkPM4f::FromPMColor(c0).to4f() * componentScale;
32 const Sk4f c4f1 = SkPM4f::FromPMColor(c1).to4f() * componentScale; 130 const Sk4f c4f1 = SkPM4f::FromPMColor(c1).to4f() * componentScale;
33 const Sk4f dc4f = (c4f1 - c4f0) / (p1 - p0); 131 const Sk4f dc4f = (c4f1 - c4f0) / (p1 - p0);
34 132
35 c4f0.store(&fC0.fVec); 133 c4f0.store(&fC0.fVec);
36 dc4f.store(&fDc.fVec); 134 dc4f.store(&fDc.fVec);
37 } 135 }
38 136
39 bool SkGradientShaderBase::GradientShaderBase4fContext::
40 Interval::contains(SkScalar fx) const {
41 return in_range(fx, fP0, fP1);
42 }
43
44 SkGradientShaderBase:: 137 SkGradientShaderBase::
45 GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderB ase& shader, 138 GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderB ase& shader,
46 const ContextRec& rec) 139 const ContextRec& rec)
47 : INHERITED(shader, rec) 140 : INHERITED(shader, rec)
48 , fFlags(this->INHERITED::getFlags()) 141 , fFlags(this->INHERITED::getFlags())
49 #ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING 142 #ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
50 , fDither(true) 143 , fDither(true)
51 #else 144 #else
52 , fDither(rec.fPaint->isDither()) 145 , fDither(rec.fPaint->isDither())
53 #endif 146 #endif
54 { 147 {
55 const SkMatrix& inverse = this->getTotalInverse(); 148 const SkMatrix& inverse = this->getTotalInverse();
56 fDstToPos.setConcat(shader.fPtsToUnit, inverse); 149 fDstToPos.setConcat(shader.fPtsToUnit, inverse);
57 fDstToPosProc = fDstToPos.getMapXYProc(); 150 fDstToPosProc = fDstToPos.getMapXYProc();
58 fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPo s)); 151 fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPo s));
59 152
60 if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) { 153 if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) {
61 fFlags |= kOpaqueAlpha_Flag; 154 fFlags |= kOpaqueAlpha_Flag;
62 } 155 }
63 156
64 fColorsArePremul = 157 fColorsArePremul =
65 (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) 158 (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
66 || shader.fColorsAreOpaque; 159 || shader.fColorsAreOpaque;
67 } 160 }
161
162 void SkGradientShaderBase::
163 GradientShaderBase4fContext::buildIntervals(const SkGradientShaderBase& shader,
164 const ContextRec& rec, bool reverse) {
165 // The main job here is to build a specialized interval list: a different
166 // representation of the color stops data, optimized for efficient scan line
167 // access during shading.
168 //
169 // [{P0,C0} , {P1,C1}) [{P1,C2} , {P2,c3}) ... [{Pn,C2n} , {Pn+1,C2n+1})
170 //
171 // The list may be inverted when requested (such that e.g. points are sorted
172 // in increasing x order when dx < 0).
173 //
174 // Note: the current representation duplicates pos data; we could refactor t o
175 // avoid this if interval storage size becomes a concern.
176 //
177 // Aside from reordering, we also perform two more pre-processing steps at
178 // this stage:
179 //
180 // 1) scale the color components depending on paint alpha and the requeste d
181 // interpolation space (note: the interval color storage is SkPM4f, but
182 // that doesn't necessarily mean the colors are premultiplied; that
183 // property is tracked in fColorsArePremul)
184 //
185 // 2) inject synthetic intervals to support tiling.
186 //
187 // * for kRepeat, no extra intervals are needed - the iterator just
188 // wraps around at the end:
189 //
190 // ->[P0,P1)->..[Pn-1,Pn)->
191 //
192 // * for kClamp, we add two "infinite" intervals before/after:
193 //
194 // [-/+inf , P0)->[P0 , P1)->..[Pn-1 , Pn)->[Pn , +/-inf)
195 //
196 // (the iterator should never run off the end in this mode)
197 //
198 // * for kMirror, we extend the range to [0..2] and add a flipped
199 // interval series - then the iterator operates just as in the
200 // kRepeat case:
201 //
202 // ->[P0,P1)->..[Pn-1,Pn)->[2 - Pn,2 - Pn-1)->..[2 - P1,2 - P0)->
203 //
204 // TODO: investigate collapsing intervals << 1px.
205
206 SkASSERT(shader.fColorCount > 0);
207 SkASSERT(shader.fOrigColors);
208
209 const float paintAlpha = rec.fPaint->getAlpha() * (1.0f / 255);
210 const Sk4f componentScale = fColorsArePremul
211 ? Sk4f(paintAlpha)
212 : Sk4f(1.0f, 1.0f, 1.0f, paintAlpha);
213 const int first_index = reverse ? shader.fColorCount - 1 : 0;
214 const int last_index = shader.fColorCount - 1 - first_index;
215 const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
216 const SkScalar last_pos = SK_Scalar1 - first_pos;
217
218 if (shader.fTileMode == SkShader::kClamp_TileMode) {
219 // synthetic edge interval: -/+inf .. P0
220 const SkPMColor clamp_color = pack_color(shader.fOrigColors[first_index] ,
221 fColorsArePremul);
222 const SkScalar clamp_pos = reverse ? SK_ScalarMax : SK_ScalarMin;
223 fIntervals.emplace_back(clamp_color, clamp_pos,
224 clamp_color, first_pos,
225 componentScale);
226 } else if (shader.fTileMode == SkShader::kMirror_TileMode && reverse) {
227 // synthetic mirror intervals injected before main intervals: (2 .. 1]
228 addMirrorIntervals(shader, componentScale, false);
229 }
230
231 const IntervalIterator iter(shader.fOrigColors,
232 shader.fOrigPos,
233 shader.fColorCount,
234 reverse);
235 iter.iterate([this, &componentScale] (SkColor c0, SkColor c1, SkScalar p0, S kScalar p1) {
236 SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == p0);
237
238 fIntervals.emplace_back(pack_color(c0, fColorsArePremul),
239 p0,
240 pack_color(c1, fColorsArePremul),
241 p1,
242 componentScale);
243 });
244
245 if (shader.fTileMode == SkShader::kClamp_TileMode) {
246 // synthetic edge interval: Pn .. +/-inf
247 const SkPMColor clamp_color =
248 pack_color(shader.fOrigColors[last_index], fColorsArePremul);
249 const SkScalar clamp_pos = reverse ? SK_ScalarMin : SK_ScalarMax;
250 fIntervals.emplace_back(clamp_color, last_pos,
251 clamp_color, clamp_pos,
252 componentScale);
253 } else if (shader.fTileMode == SkShader::kMirror_TileMode && !reverse) {
254 // synthetic mirror intervals injected after main intervals: [1 .. 2)
255 addMirrorIntervals(shader, componentScale, true);
256 }
257 }
258
259 void SkGradientShaderBase::
260 GradientShaderBase4fContext::addMirrorIntervals(const SkGradientShaderBase& shad er,
261 const Sk4f& componentScale, bool rev erse) {
262 const IntervalIterator iter(shader.fOrigColors,
263 shader.fOrigPos,
264 shader.fColorCount,
265 reverse);
266 iter.iterate([this, &componentScale] (SkColor c0, SkColor c1, SkScalar p0, S kScalar p1) {
267 SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == 2 - p0);
268
269 fIntervals.emplace_back(pack_color(c0, fColorsArePremul),
270 2 - p0,
271 pack_color(c1, fColorsArePremul),
272 2 - p1,
273 componentScale);
274 });
275 }
276
277 void SkGradientShaderBase::
278 GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
279 if (fColorsArePremul) {
reed1 2016/03/10 19:01:12 This is clearer now, but it is slightly awkward th
f(malita) 2016/03/10 19:17:19 Good idea, I'll do that in a follow-up.
280 this->shadePremulSpan<SkPMColor, false>(x, y, dst, count);
281 } else {
282 this->shadePremulSpan<SkPMColor, true>(x, y, dst, count);
283 }
284 }
285
286 void SkGradientShaderBase::
287 GradientShaderBase4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
288 if (fColorsArePremul) {
289 this->shadePremulSpan<SkPM4f, false>(x, y, dst, count);
290 } else {
291 this->shadePremulSpan<SkPM4f, true>(x, y, dst, count);
292 }
293 }
294
295 template<typename DstType, bool do_premul>
296 void SkGradientShaderBase::
297 GradientShaderBase4fContext::shadePremulSpan(int x, int y,
298 DstType dst[],
299 int count) const {
300 const SkGradientShaderBase& shader =
301 static_cast<const SkGradientShaderBase&>(fShader);
302
303 switch (shader.fTileMode) {
304 case kClamp_TileMode:
305 this->shadeSpanInternal<DstType,
306 do_premul,
307 kClamp_TileMode>(x, y, dst, count);
308 break;
309 case kRepeat_TileMode:
310 this->shadeSpanInternal<DstType,
311 do_premul,
312 kRepeat_TileMode>(x, y, dst, count);
313 break;
314 case kMirror_TileMode:
315 this->shadeSpanInternal<DstType,
316 do_premul,
317 kMirror_TileMode>(x, y, dst, count);
318 break;
319 }
320 }
321
322 template<typename DstType, bool do_premul, SkShader::TileMode tileMode>
323 void SkGradientShaderBase::
324 GradientShaderBase4fContext::shadeSpanInternal(int x, int y,
325 DstType dst[],
326 int count) const {
327 static const int kBufSize = 128;
328 SkScalar ts[kBufSize];
329 TSampler<DstType, tileMode> sampler(*this);
330
331 SkASSERT(count > 0);
332 do {
333 const int n = SkTMin(kBufSize, count);
334 this->mapTs(x, y, ts, n);
335 for (int i = 0; i < n; ++i) {
336 const Sk4f c = sampler.sample(ts[i]);
337 store<DstType, do_premul>(c, dst++);
338 }
339 x += n;
340 count -= n;
341 } while (count > 0);
342 }
343
344 template<typename DstType, SkShader::TileMode tileMode>
345 class SkGradientShaderBase::GradientShaderBase4fContext::TSampler {
346 public:
347 TSampler(const GradientShaderBase4fContext& ctx)
348 : fFirstInterval(ctx.fIntervals.begin())
349 , fLastInterval(ctx.fIntervals.end() - 1)
350 , fInterval(nullptr) {
351 SkASSERT(fLastInterval >= fFirstInterval);
352 }
353
354 Sk4f sample(SkScalar t) {
355 const SkScalar tiled_t = tileProc<tileMode>(t);
356
357 if (!fInterval) {
358 // Very first sample => locate the initial interval.
359 // TODO: maybe do this in ctor to remove a branch?
360 fInterval = this->findFirstInterval(tiled_t);
361 this->loadIntervalData(fInterval);
362 } else if (tiled_t < fInterval->fP0 || tiled_t >= fInterval->fP1) {
363 fInterval = this->findNextInterval(t, tiled_t);
364 this->loadIntervalData(fInterval);
365 }
366
367 fPrevT = t;
368 return lerp(tiled_t);
369 }
370
371 private:
372 Sk4f lerp(SkScalar t) {
373 SkASSERT(t >= fInterval->fP0 && t < fInterval->fP1);
374 return fCc + fDc * (t - fInterval->fP0);
375 }
376
377 const Interval* findFirstInterval(SkScalar t) const {
378 // Binary search.
379 const Interval* i0 = fFirstInterval;
380 const Interval* i1 = fLastInterval;
381
382 while (i0 != i1) {
383 SkASSERT(i0 < i1);
384 SkASSERT(t >= i0->fP0 && t < i1->fP1);
385
386 const Interval* i = i0 + ((i1 - i0) >> 1);
387
388 if (t >= i->fP1) {
389 i0 = i + 1;
390 } else {
391 i1 = i;
392 }
393 }
394
395 SkASSERT(t >= i0->fP0 && t <= i0->fP1);
396 return i0;
397 }
398
399 const Interval* findNextInterval(SkScalar t, SkScalar tiled_t) const {
400 SkASSERT(tiled_t < fInterval->fP0 || tiled_t >= fInterval->fP1);
401 SkASSERT(tiled_t >= fFirstInterval->fP0 && tiled_t < fLastInterval->fP1) ;
402
403 const Interval* i = fInterval;
404
405 // Use the t vs. prev_t signal to figure which direction we should searc h for
406 // the next interval, then perform a linear search.
407 if (t >= fPrevT) {
408 do {
409 i += 1;
410 if (i > fLastInterval) {
411 i = fFirstInterval;
412 }
413 } while (tiled_t < i->fP0 || tiled_t >= i->fP1);
414 } else {
415 do {
416 i -= 1;
417 if (i < fFirstInterval) {
418 i = fLastInterval;
419 }
420 } while (tiled_t < i->fP0 || tiled_t >= i->fP1);
421 }
422
423 return i;
424 }
425
426 void loadIntervalData(const Interval* i) {
427 fCc = dst_swizzle<DstType>(i->fC0) * dst_component_scale<DstType>();
428 fDc = dst_swizzle<DstType>(i->fDc) * dst_component_scale<DstType>();
429 }
430
431 const Interval* fFirstInterval;
432 const Interval* fLastInterval;
433 const Interval* fInterval;
434 SkScalar fPrevT;
435 Sk4f fCc;
436 Sk4f fDc;
437 };
OLDNEW
« no previous file with comments | « src/effects/gradients/Sk4fGradientBase.h ('k') | src/effects/gradients/Sk4fGradientPriv.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698