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