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