const int fAdvance;
};
+void addMirrorIntervals(const SkColor colors[],
+ const SkScalar pos[], int count,
+ const Sk4f& componentScale,
+ bool premulColors, bool reverse,
+ Sk4fGradientIntervalBuffer::BufferType* buffer) {
+ const IntervalIterator iter(colors, pos, count, reverse);
+ iter.iterate([&] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
+ SkASSERT(buffer->empty() || buffer->back().fP1 == 2 - p0);
+
+ const auto mirror_p0 = 2 - p0;
+ const auto mirror_p1 = 2 - p1;
+ // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid
+ // triggering Interval asserts.
+ if (mirror_p0 != mirror_p1) {
+ buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_p0,
+ pack_color(c1, premulColors, componentScale), mirror_p1);
+ }
+ });
+}
+
} // anonymous namespace
-SkGradientShaderBase::GradientShaderBase4fContext::
-Interval::Interval(const Sk4f& c0, SkScalar p0,
- const Sk4f& c1, SkScalar p1)
+Sk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar p0,
+ const Sk4f& c1, SkScalar p1)
: fP0(p0)
, fP1(p1)
, fZeroRamp((c0 == c1).allTrue()) {
dc.store(&fDc.fVec);
}
-SkGradientShaderBase::
-GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader,
- const ContextRec& rec)
- : INHERITED(shader, rec)
- , fFlags(this->INHERITED::getFlags())
-#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
- , fDither(true)
-#else
- , fDither(rec.fPaint->isDither())
-#endif
-{
- const SkMatrix& inverse = this->getTotalInverse();
- fDstToPos.setConcat(shader.fPtsToUnit, inverse);
- fDstToPosProc = fDstToPos.getMapXYProc();
- fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos));
-
- if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) {
- fFlags |= kOpaqueAlpha_Flag;
- }
-
- fColorsArePremul =
- (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
- || shader.fColorsAreOpaque;
-}
-
-bool SkGradientShaderBase::
-GradientShaderBase4fContext::isValid() const {
- return fDstToPos.isFinite();
-}
-
-void SkGradientShaderBase::
-GradientShaderBase4fContext::buildIntervals(const SkGradientShaderBase& shader,
- const ContextRec& rec, bool reverse) {
+void Sk4fGradientIntervalBuffer::init(const SkColor colors[], const SkScalar pos[], int count,
+ SkShader::TileMode tileMode, bool premulColors,
+ SkScalar alpha, bool reverse) {
// The main job here is to build a specialized interval list: a different
// representation of the color stops data, optimized for efficient scan line
// access during shading.
//
// TODO: investigate collapsing intervals << 1px.
- SkASSERT(shader.fColorCount > 0);
- SkASSERT(shader.fOrigColors);
+ SkASSERT(count > 0);
+ SkASSERT(colors);
- const float paintAlpha = rec.fPaint->getAlpha() * (1.0f / 255);
- const Sk4f componentScale = fColorsArePremul
- ? Sk4f(paintAlpha)
- : Sk4f(1.0f, 1.0f, 1.0f, paintAlpha);
- const int first_index = reverse ? shader.fColorCount - 1 : 0;
- const int last_index = shader.fColorCount - 1 - first_index;
+ fIntervals.reset();
+
+ const Sk4f componentScale = premulColors
+ ? Sk4f(alpha)
+ : Sk4f(1.0f, 1.0f, 1.0f, alpha);
+ const int first_index = reverse ? count - 1 : 0;
+ const int last_index = count - 1 - first_index;
const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
const SkScalar last_pos = SK_Scalar1 - first_pos;
- if (shader.fTileMode == SkShader::kClamp_TileMode) {
+ if (tileMode == SkShader::kClamp_TileMode) {
// synthetic edge interval: -/+inf .. P0
- const Sk4f clamp_color = pack_color(shader.fOrigColors[first_index],
- fColorsArePremul, componentScale);
+ const Sk4f clamp_color = pack_color(colors[first_index],
+ premulColors, componentScale);
const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
fIntervals.emplace_back(clamp_color, clamp_pos,
clamp_color, first_pos);
- } else if (shader.fTileMode == SkShader::kMirror_TileMode && reverse) {
+ } else if (tileMode == SkShader::kMirror_TileMode && reverse) {
// synthetic mirror intervals injected before main intervals: (2 .. 1]
- addMirrorIntervals(shader, componentScale, false);
+ addMirrorIntervals(colors, pos, count, componentScale, premulColors, false, &fIntervals);
}
- const IntervalIterator iter(shader.fOrigColors,
- shader.fOrigPos,
- shader.fColorCount,
- reverse);
- iter.iterate([this, &componentScale] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
+ const IntervalIterator iter(colors, pos, count, reverse);
+ iter.iterate([&] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == p0);
- fIntervals.emplace_back(pack_color(c0, fColorsArePremul, componentScale),
- p0,
- pack_color(c1, fColorsArePremul, componentScale),
- p1);
+ fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), p0,
+ pack_color(c1, premulColors, componentScale), p1);
});
- if (shader.fTileMode == SkShader::kClamp_TileMode) {
+ if (tileMode == SkShader::kClamp_TileMode) {
// synthetic edge interval: Pn .. +/-inf
- const Sk4f clamp_color = pack_color(shader.fOrigColors[last_index],
- fColorsArePremul, componentScale);
+ const Sk4f clamp_color = pack_color(colors[last_index], premulColors, componentScale);
const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
fIntervals.emplace_back(clamp_color, last_pos,
clamp_color, clamp_pos);
- } else if (shader.fTileMode == SkShader::kMirror_TileMode && !reverse) {
+ } else if (tileMode == SkShader::kMirror_TileMode && !reverse) {
// synthetic mirror intervals injected after main intervals: [1 .. 2)
- addMirrorIntervals(shader, componentScale, true);
+ addMirrorIntervals(colors, pos, count, componentScale, premulColors, true, &fIntervals);
}
}
-void SkGradientShaderBase::
-GradientShaderBase4fContext::addMirrorIntervals(const SkGradientShaderBase& shader,
- const Sk4f& componentScale, bool reverse) {
- const IntervalIterator iter(shader.fOrigColors,
- shader.fOrigPos,
- shader.fColorCount,
- reverse);
- iter.iterate([this, &componentScale] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
- SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == 2 - p0);
+const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const {
+ // Binary search.
+ const auto* i0 = fIntervals.begin();
+ const auto* i1 = fIntervals.end() - 1;
- const auto mirror_p0 = 2 - p0;
- const auto mirror_p1 = 2 - p1;
- // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid
- // triggering Interval asserts.
- if (mirror_p0 != mirror_p1) {
- fIntervals.emplace_back(pack_color(c0, fColorsArePremul, componentScale),
- mirror_p0,
- pack_color(c1, fColorsArePremul, componentScale),
- mirror_p1);
+ while (i0 != i1) {
+ SkASSERT(i0 < i1);
+ SkASSERT(t >= i0->fP0 && t <= i1->fP1);
+
+ const auto* i = i0 + ((i1 - i0) >> 1);
+
+ if (t > i->fP1) {
+ i0 = i + 1;
+ } else {
+ i1 = i;
}
- });
+ }
+
+ SkASSERT(i0->contains(t));
+ return i0;
+}
+
+const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext(
+ SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const {
+
+ SkASSERT(!prev->contains(t));
+ SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end());
+ SkASSERT(t >= fIntervals.front().fP0 && t <= fIntervals.back().fP1);
+
+ const auto* i = prev;
+
+ // Use the |increasing| signal to figure which direction we should search for
+ // the next interval, then perform a linear search.
+ if (increasing) {
+ do {
+ i += 1;
+ if (i >= fIntervals.end()) {
+ i = fIntervals.begin();
+ }
+ } while (!i->contains(t));
+ } else {
+ do {
+ i -= 1;
+ if (i < fIntervals.begin()) {
+ i = fIntervals.end() - 1;
+ }
+ } while (!i->contains(t));
+ }
+
+ return i;
+}
+
+SkGradientShaderBase::
+GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader,
+ const ContextRec& rec)
+ : INHERITED(shader, rec)
+ , fFlags(this->INHERITED::getFlags())
+#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
+ , fDither(true)
+#else
+ , fDither(rec.fPaint->isDither())
+#endif
+{
+ const SkMatrix& inverse = this->getTotalInverse();
+ fDstToPos.setConcat(shader.fPtsToUnit, inverse);
+ fDstToPosProc = fDstToPos.getMapXYProc();
+ fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos));
+
+ if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) {
+ fFlags |= kOpaqueAlpha_Flag;
+ }
+
+ fColorsArePremul =
+ (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
+ || shader.fColorsAreOpaque;
+}
+
+bool SkGradientShaderBase::
+GradientShaderBase4fContext::isValid() const {
+ return fDstToPos.isFinite();
}
void SkGradientShaderBase::
class SkGradientShaderBase::GradientShaderBase4fContext::TSampler {
public:
TSampler(const GradientShaderBase4fContext& ctx)
- : fFirstInterval(ctx.fIntervals.begin())
- , fLastInterval(ctx.fIntervals.end() - 1)
+ : fCtx(ctx)
, fInterval(nullptr) {
- SkASSERT(fLastInterval >= fFirstInterval);
switch (tileMode) {
case kClamp_TileMode:
fLargestIntervalValue = SK_ScalarInfinity;
if (!fInterval) {
// Very first sample => locate the initial interval.
// TODO: maybe do this in ctor to remove a branch?
- fInterval = this->findFirstInterval(tiled_t);
+ fInterval = fCtx.fIntervals.find(tiled_t);
this->loadIntervalData(fInterval);
} else if (!fInterval->contains(tiled_t)) {
- fInterval = this->findNextInterval(t, tiled_t);
+ fInterval = fCtx.fIntervals.findNext(tiled_t, fInterval, t >= fPrevT);
this->loadIntervalData(fInterval);
}
return fCc + fDc * (t - fInterval->fP0);
}
- const Interval* findFirstInterval(SkScalar t) const {
- // Binary search.
- const Interval* i0 = fFirstInterval;
- const Interval* i1 = fLastInterval;
-
- while (i0 != i1) {
- SkASSERT(i0 < i1);
- SkASSERT(t >= i0->fP0 && t <= i1->fP1);
-
- const Interval* i = i0 + ((i1 - i0) >> 1);
-
- if (t > i->fP1) {
- i0 = i + 1;
- } else {
- i1 = i;
- }
- }
-
- SkASSERT(i0->contains(t));
- return i0;
- }
-
- const Interval* findNextInterval(SkScalar t, SkScalar tiled_t) const {
- SkASSERT(!fInterval->contains(tiled_t));
- SkASSERT(tiled_t >= fFirstInterval->fP0 && tiled_t <= fLastInterval->fP1);
-
- const Interval* i = fInterval;
-
- // Use the t vs. prev_t signal to figure which direction we should search for
- // the next interval, then perform a linear search.
- if (t >= fPrevT) {
- do {
- i += 1;
- if (i > fLastInterval) {
- i = fFirstInterval;
- }
- } while (!i->contains(tiled_t));
- } else {
- do {
- i -= 1;
- if (i < fFirstInterval) {
- i = fLastInterval;
- }
- } while (!i->contains(tiled_t));
- }
-
- return i;
- }
-
- void loadIntervalData(const Interval* i) {
+ void loadIntervalData(const Sk4fGradientInterval* i) {
fCc = DstTraits<dstType, premul>::load(i->fC0);
fDc = DstTraits<dstType, premul>::load(i->fDc);
}
- const Interval* fFirstInterval;
- const Interval* fLastInterval;
- const Interval* fInterval;
- SkScalar fPrevT;
- SkScalar fLargestIntervalValue;
- Sk4f fCc;
- Sk4f fDc;
+ const GradientShaderBase4fContext& fCtx;
+ const Sk4fGradientInterval* fInterval;
+ SkScalar fPrevT;
+ SkScalar fLargestIntervalValue;
+ Sk4f fCc;
+ Sk4f fDc;
};
// Our fast path expects interval points to be monotonically increasing in x.
const bool reverseIntervals = this->isFast() && std::signbit(fDstToPos.getScaleX());
- this->buildIntervals(shader, rec, reverseIntervals);
+ fIntervals.init(shader.fOrigColors, shader.fOrigPos, shader.fColorCount, shader.fTileMode,
+ fColorsArePremul, rec.fPaint->getAlpha() * (1.0f / 255), reverseIntervals);
- SkASSERT(fIntervals.count() > 0);
- fCachedInterval = fIntervals.begin();
+ SkASSERT(fIntervals->count() > 0);
+ fCachedInterval = fIntervals->begin();
}
-const SkGradientShaderBase::GradientShaderBase4fContext::Interval*
+const Sk4fGradientInterval*
SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const {
- SkASSERT(in_range(fx, fIntervals.front().fP0, fIntervals.back().fP1));
+ SkASSERT(in_range(fx, fIntervals->front().fP0, fIntervals->back().fP1));
if (1) {
// Linear search, using the last scanline interval as a starting point.
- SkASSERT(fCachedInterval >= fIntervals.begin());
- SkASSERT(fCachedInterval < fIntervals.end());
+ SkASSERT(fCachedInterval >= fIntervals->begin());
+ SkASSERT(fCachedInterval < fIntervals->end());
const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1;
while (!in_range(fx, fCachedInterval->fP0, fCachedInterval->fP1)) {
fCachedInterval += search_dir;
- if (fCachedInterval >= fIntervals.end()) {
- fCachedInterval = fIntervals.begin();
- } else if (fCachedInterval < fIntervals.begin()) {
- fCachedInterval = fIntervals.end() - 1;
+ if (fCachedInterval >= fIntervals->end()) {
+ fCachedInterval = fIntervals->begin();
+ } else if (fCachedInterval < fIntervals->begin()) {
+ fCachedInterval = fIntervals->end() - 1;
}
}
return fCachedInterval;
} else {
// Binary search. Seems less effective than linear + caching.
- const Interval* i0 = fIntervals.begin();
- const Interval* i1 = fIntervals.end() - 1;
+ const auto* i0 = fIntervals->begin();
+ const auto* i1 = fIntervals->end() - 1;
while (i0 != i1) {
SkASSERT(i0 < i1);
SkASSERT(in_range(fx, i0->fP0, i1->fP1));
- const Interval* i = i0 + ((i1 - i0) >> 1);
+ const auto* i = i0 + ((i1 - i0) >> 1);
if (in_range(fx, i0->fP0, i->fP1)) {
i1 = i;
&pt);
const SkScalar fx = pinFx<tileMode>(pt.x());
const SkScalar dx = fDstToPos.getScaleX();
- LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals.begin(),
- fIntervals.end() - 1,
+ LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals->begin(),
+ fIntervals->end() - 1,
this->findInterval(fx),
fx,
dx,
class SkLinearGradient::
LinearGradient4fContext::LinearIntervalProcessor {
public:
- LinearIntervalProcessor(const Interval* firstInterval,
- const Interval* lastInterval,
- const Interval* i,
+ LinearIntervalProcessor(const Sk4fGradientInterval* firstInterval,
+ const Sk4fGradientInterval* lastInterval,
+ const Sk4fGradientInterval* i,
SkScalar fx,
SkScalar dx,
bool is_vertical)
private:
void compute_interval_props(SkScalar t) {
- fZeroRamp = fIsVertical || fInterval->isZeroRamp();
+ fZeroRamp = fIsVertical || fInterval->fZeroRamp;
fCc = DstTraits<dstType, premul>::load(fInterval->fC0);
- if (fInterval->isZeroRamp()) {
+ if (fInterval->fZeroRamp) {
fDcDx = 0;
} else {
const Sk4f dC = DstTraits<dstType, premul>::load(fInterval->fDc);
}
}
- const Interval* next_interval(const Interval* i) const {
+ const Sk4fGradientInterval* next_interval(const Sk4fGradientInterval* i) const {
SkASSERT(i >= fFirstInterval);
SkASSERT(i <= fLastInterval);
i++;
SkScalar fAdvX; // remaining interval advance in dst
bool fZeroRamp; // current interval color grad is 0
- const Interval* fFirstInterval;
- const Interval* fLastInterval;
- const Interval* fInterval; // current interval
- const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx
- const bool fIsVertical;
+ const Sk4fGradientInterval* fFirstInterval;
+ const Sk4fGradientInterval* fLastInterval;
+ const Sk4fGradientInterval* fInterval; // current interval
+ const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx
+ const bool fIsVertical;
};
void SkLinearGradient::