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(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: 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 33 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: 66 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 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 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 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 234 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 256 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:: 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:: 309 GradientShaderBase4fContext::isValid() const { 310 return fDstToPos.isFinite(); 311 } 312 313 void SkGradientShaderBase:: 314 GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) { 315 // This impl only shades to 4f. 316 SkASSERT(false); 317 } 318