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