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