1 /*
2 * Copyright 2012 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 "Sk4fLinearGradient.h"
9 #include "SkColorSpaceXformer.h"
10 #include "SkLinearGradient.h"
11 #include "SkRefCnt.h"
12
13 static const float kInv255Float = 1.0f / 255;
14
repeat_8bits(int x)15 static inline int repeat_8bits(int x) {
16 return x & 0xFF;
17 }
18
mirror_8bits(int x)19 static inline int mirror_8bits(int x) {
20 if (x & 256) {
21 x = ~x;
22 }
23 return x & 255;
24 }
25
pts_to_unit_matrix(const SkPoint pts[2])26 static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
27 SkVector vec = pts[1] - pts[0];
28 SkScalar mag = vec.length();
29 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
30
31 vec.scale(inv);
32 SkMatrix matrix;
33 matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
34 matrix.postTranslate(-pts[0].fX, -pts[0].fY);
35 matrix.postScale(inv, inv);
36 return matrix;
37 }
38
39 ///////////////////////////////////////////////////////////////////////////////
40
SkLinearGradient(const SkPoint pts[2],const Descriptor & desc)41 SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
42 : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
43 , fStart(pts[0])
44 , fEnd(pts[1]) {
45 }
46
CreateProc(SkReadBuffer & buffer)47 sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
48 DescriptorScope desc;
49 if (!desc.unflatten(buffer)) {
50 return nullptr;
51 }
52 SkPoint pts[2];
53 pts[0] = buffer.readPoint();
54 pts[1] = buffer.readPoint();
55 return SkGradientShader::MakeLinear(pts, desc.fColors, std::move(desc.fColorSpace), desc.fPos,
56 desc.fCount, desc.fTileMode, desc.fGradFlags,
57 desc.fLocalMatrix);
58 }
59
flatten(SkWriteBuffer & buffer) const60 void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
61 this->INHERITED::flatten(buffer);
62 buffer.writePoint(fStart);
63 buffer.writePoint(fEnd);
64 }
65
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const66 SkShaderBase::Context* SkLinearGradient::onMakeContext(
67 const ContextRec& rec, SkArenaAlloc* alloc) const
68 {
69 return rec.fPreferredDstType == ContextRec::kPM4f_DstType
70 ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec)
71 : CheckedMakeContext< LinearGradientContext>(alloc, *this, rec);
72 }
73
onMakeBurstPipelineContext(const ContextRec & rec,SkArenaAlloc * alloc) const74 SkShaderBase::Context* SkLinearGradient::onMakeBurstPipelineContext(
75 const ContextRec& rec, SkArenaAlloc* alloc) const {
76
77 // Raster pipeline has a 2-stop specialization faster than our burst.
78 return fColorCount > 2 ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec)
79 : nullptr;
80 }
81
adjustMatrixAndAppendStages(SkArenaAlloc *,SkMatrix * matrix,SkRasterPipeline *,SkRasterPipeline *) const82 bool SkLinearGradient::adjustMatrixAndAppendStages(SkArenaAlloc*,
83 SkMatrix* matrix,
84 SkRasterPipeline*,
85 SkRasterPipeline*) const {
86 *matrix = SkMatrix::Concat(fPtsToUnit, *matrix);
87 return true;
88 }
89
onMakeColorSpace(SkColorSpaceXformer * xformer) const90 sk_sp<SkShader> SkLinearGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
91 SkPoint pts[2] = { fStart, fEnd };
92 SkSTArray<8, SkColor> xformedColors(fColorCount);
93 xformer->apply(xformedColors.begin(), fOrigColors, fColorCount);
94 return SkGradientShader::MakeLinear(pts, xformedColors.begin(), fOrigPos, fColorCount,
95 fTileMode, fGradFlags, &this->getLocalMatrix());
96 }
97
98 // This swizzles SkColor into the same component order as SkPMColor, but does not actually
99 // "pre" multiply the color components.
100 //
101 // This allows us to map directly to Sk4f, and eventually scale down to bytes to output a
102 // SkPMColor from the floats, without having to swizzle each time.
103 //
SkSwizzle_Color_to_PMColor(SkColor c)104 static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
105 return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
106 }
107
LinearGradientContext(const SkLinearGradient & shader,const ContextRec & ctx)108 SkLinearGradient::LinearGradientContext::LinearGradientContext(
109 const SkLinearGradient& shader, const ContextRec& ctx)
110 : INHERITED(shader, ctx)
111 {
112 // setup for Sk4f
113 const int count = shader.fColorCount;
114 SkASSERT(count > 1);
115
116 fRecs.setCount(count);
117 Rec* rec = fRecs.begin();
118 if (shader.fOrigPos) {
119 rec[0].fPos = 0;
120 SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;) // should never get used
121 for (int i = 1; i < count; ++i) {
122 rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f);
123 float diff = rec[i].fPos - rec[i - 1].fPos;
124 if (diff > 0) {
125 rec[i].fPosScale = 1.0f / diff;
126 } else {
127 rec[i].fPosScale = 0;
128 }
129 }
130 } else {
131 // no pos specified, so we compute evenly spaced values
132 const float scale = float(count - 1);
133 const float invScale = 1.0f / scale;
134 for (int i = 0; i < count; ++i) {
135 rec[i].fPos = i * invScale;
136 rec[i].fPosScale = scale;
137 }
138 }
139 rec[count - 1].fPos = 1; // overwrite the last value just to be sure we end at 1.0
140
141 fApplyAlphaAfterInterp = true;
142 if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) ||
143 shader.colorsAreOpaque())
144 {
145 fApplyAlphaAfterInterp = false;
146 }
147
148 if (fApplyAlphaAfterInterp) {
149 // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to
150 // interpolate in unpremultiplied space first, and then scale by alpha right before we
151 // convert to SkPMColor bytes.
152 const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
153 const Sk4f scale(1, 1, 1, paintAlpha);
154 for (int i = 0; i < count; ++i) {
155 uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
156 rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&c)) * scale;
157 if (i > 0) {
158 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
159 }
160 }
161 } else {
162 // Our fColor values are premultiplied, so converting to SkPMColor is just a matter
163 // of converting the floats down to bytes.
164 unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7);
165 for (int i = 0; i < count; ++i) {
166 SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
167 pmc = SkAlphaMulQ(pmc, alphaScale);
168 rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&pmc));
169 if (i > 0) {
170 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
171 }
172 }
173 }
174 }
175
176 #define NO_CHECK_ITER \
177 do { \
178 unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
179 SkASSERT(fi <= 0xFF); \
180 fx += dx; \
181 *dstC++ = cache[toggle + fi]; \
182 toggle = next_dither_toggle(toggle); \
183 } while (0)
184
185 namespace {
186
187 typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
188 SkPMColor* dstC, const SkPMColor* cache,
189 int toggle, int count);
190
191 // Linear interpolation (lerp) is unnecessary if there are no sharp
192 // discontinuities in the gradient - which must be true if there are
193 // only 2 colors - but it's cheap.
shadeSpan_linear_vertical_lerp(TileProc proc,SkGradFixed dx,SkGradFixed fx,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int toggle,int count)194 void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
195 SkPMColor* SK_RESTRICT dstC,
196 const SkPMColor* SK_RESTRICT cache,
197 int toggle, int count) {
198 // We're a vertical gradient, so no change in a span.
199 // If colors change sharply across the gradient, dithering is
200 // insufficient (it subsamples the color space) and we need to lerp.
201 unsigned fullIndex = proc(SkGradFixedToFixed(fx));
202 unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
203 unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
204
205 int index0 = fi + toggle;
206 int index1 = index0;
207 if (fi < SkGradientShaderBase::kCache32Count - 1) {
208 index1 += 1;
209 }
210 SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
211 index0 ^= SkGradientShaderBase::kDitherStride32;
212 index1 ^= SkGradientShaderBase::kDitherStride32;
213 SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
214 sk_memset32_dither(dstC, lerp, dlerp, count);
215 }
216
shadeSpan_linear_clamp(TileProc proc,SkGradFixed dx,SkGradFixed fx,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int toggle,int count)217 void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
218 SkPMColor* SK_RESTRICT dstC,
219 const SkPMColor* SK_RESTRICT cache,
220 int toggle, int count) {
221 SkClampRange range;
222 range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
223 range.validate(count);
224
225 if ((count = range.fCount0) > 0) {
226 sk_memset32_dither(dstC,
227 cache[toggle + range.fV0],
228 cache[next_dither_toggle(toggle) + range.fV0],
229 count);
230 dstC += count;
231 }
232 if ((count = range.fCount1) > 0) {
233 int unroll = count >> 3;
234 fx = range.fFx1;
235 for (int i = 0; i < unroll; i++) {
236 NO_CHECK_ITER; NO_CHECK_ITER;
237 NO_CHECK_ITER; NO_CHECK_ITER;
238 NO_CHECK_ITER; NO_CHECK_ITER;
239 NO_CHECK_ITER; NO_CHECK_ITER;
240 }
241 if ((count &= 7) > 0) {
242 do {
243 NO_CHECK_ITER;
244 } while (--count != 0);
245 }
246 }
247 if ((count = range.fCount2) > 0) {
248 sk_memset32_dither(dstC,
249 cache[toggle + range.fV1],
250 cache[next_dither_toggle(toggle) + range.fV1],
251 count);
252 }
253 }
254
shadeSpan_linear_mirror(TileProc proc,SkGradFixed dx,SkGradFixed fx,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int toggle,int count)255 void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
256 SkPMColor* SK_RESTRICT dstC,
257 const SkPMColor* SK_RESTRICT cache,
258 int toggle, int count) {
259 do {
260 unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8);
261 SkASSERT(fi <= 0xFF);
262 fx += dx;
263 *dstC++ = cache[toggle + fi];
264 toggle = next_dither_toggle(toggle);
265 } while (--count != 0);
266 }
267
shadeSpan_linear_repeat(TileProc proc,SkGradFixed dx,SkGradFixed fx,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int toggle,int count)268 void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
269 SkPMColor* SK_RESTRICT dstC,
270 const SkPMColor* SK_RESTRICT cache,
271 int toggle, int count) {
272 do {
273 unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8);
274 SkASSERT(fi <= 0xFF);
275 fx += dx;
276 *dstC++ = cache[toggle + fi];
277 toggle = next_dither_toggle(toggle);
278 } while (--count != 0);
279 }
280
281 }
282
shadeSpan(int x,int y,SkPMColor * SK_RESTRICT dstC,int count)283 void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
284 int count) {
285 SkASSERT(count > 0);
286 const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
287
288 if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
289 this->shade4_clamp(x, y, dstC, count);
290 return;
291 }
292
293 SkPoint srcPt;
294 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
295 TileProc proc = linearGradient.fTileProc;
296 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
297 int toggle = init_dither_toggle(x, y);
298
299 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
300 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
301 SkGradFixed fx = SkScalarPinToGradFixed(srcPt.fX),
302 dx = SkScalarPinToGradFixed(fDstToIndex.getScaleX());
303
304 LinearShadeProc shadeProc = shadeSpan_linear_repeat;
305 if (0 == dx) {
306 shadeProc = shadeSpan_linear_vertical_lerp;
307 } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
308 shadeProc = shadeSpan_linear_clamp;
309 } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
310 shadeProc = shadeSpan_linear_mirror;
311 } else {
312 SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
313 }
314 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
315 }
316
asAGradient(GradientInfo * info) const317 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
318 if (info) {
319 commonAsAGradient(info);
320 info->fPoint[0] = fStart;
321 info->fPoint[1] = fEnd;
322 }
323 return kLinear_GradientType;
324 }
325
326 #if SK_SUPPORT_GPU
327
328 #include "GrColorSpaceXform.h"
329 #include "GrShaderCaps.h"
330 #include "glsl/GrGLSLFragmentShaderBuilder.h"
331 #include "SkGr.h"
332
333 /////////////////////////////////////////////////////////////////////
334
335 class GrLinearGradient : public GrGradientEffect {
336 public:
337 class GLSLLinearProcessor;
338
Make(const CreateArgs & args)339 static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
340 auto processor = sk_sp<GrLinearGradient>(new GrLinearGradient(args));
341 return processor->isValid() ? std::move(processor) : nullptr;
342 }
343
~GrLinearGradient()344 ~GrLinearGradient() override {}
345
name() const346 const char* name() const override { return "Linear Gradient"; }
347
348 private:
GrLinearGradient(const CreateArgs & args)349 GrLinearGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
350 this->initClassID<GrLinearGradient>();
351 }
352
353 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
354
355 virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
356 GrProcessorKeyBuilder* b) const override;
357
358 GR_DECLARE_FRAGMENT_PROCESSOR_TEST
359
360 typedef GrGradientEffect INHERITED;
361 };
362
363 /////////////////////////////////////////////////////////////////////
364
365 class GrLinearGradient::GLSLLinearProcessor : public GrGradientEffect::GLSLProcessor {
366 public:
GLSLLinearProcessor(const GrProcessor &)367 GLSLLinearProcessor(const GrProcessor&) {}
368
~GLSLLinearProcessor()369 ~GLSLLinearProcessor() override {}
370
371 virtual void emitCode(EmitArgs&) override;
372
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)373 static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
374 b->add32(GenBaseGradientKey(processor));
375 }
376
377 private:
378 typedef GrGradientEffect::GLSLProcessor INHERITED;
379 };
380
381 /////////////////////////////////////////////////////////////////////
382
onCreateGLSLInstance() const383 GrGLSLFragmentProcessor* GrLinearGradient::onCreateGLSLInstance() const {
384 return new GrLinearGradient::GLSLLinearProcessor(*this);
385 }
386
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const387 void GrLinearGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
388 GrProcessorKeyBuilder* b) const {
389 GrLinearGradient::GLSLLinearProcessor::GenKey(*this, caps, b);
390 }
391
392 /////////////////////////////////////////////////////////////////////
393
394 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient);
395
396 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)397 sk_sp<GrFragmentProcessor> GrLinearGradient::TestCreate(GrProcessorTestData* d) {
398 SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()},
399 {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}};
400
401 RandomGradientParams params(d->fRandom);
402 auto shader = params.fUseColors4f ?
403 SkGradientShader::MakeLinear(points, params.fColors4f, params.fColorSpace, params.fStops,
404 params.fColorCount, params.fTileMode) :
405 SkGradientShader::MakeLinear(points, params.fColors, params.fStops,
406 params.fColorCount, params.fTileMode);
407 GrTest::TestAsFPArgs asFPArgs(d);
408 sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
409 GrAlwaysAssert(fp);
410 return fp;
411 }
412 #endif
413
414 /////////////////////////////////////////////////////////////////////
415
emitCode(EmitArgs & args)416 void GrLinearGradient::GLSLLinearProcessor::emitCode(EmitArgs& args) {
417 const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>();
418 this->emitUniforms(args.fUniformHandler, ge);
419 SkString t = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
420 t.append(".x");
421 this->emitColor(args.fFragBuilder,
422 args.fUniformHandler,
423 args.fShaderCaps,
424 ge,
425 t.c_str(),
426 args.fOutputColor,
427 args.fInputColor,
428 args.fTexSamplers);
429 }
430
431 /////////////////////////////////////////////////////////////////////
432
asFragmentProcessor(const AsFPArgs & args) const433 sk_sp<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(const AsFPArgs& args) const {
434 SkASSERT(args.fContext);
435
436 SkMatrix matrix;
437 if (!this->getLocalMatrix().invert(&matrix)) {
438 return nullptr;
439 }
440 if (args.fLocalMatrix) {
441 SkMatrix inv;
442 if (!args.fLocalMatrix->invert(&inv)) {
443 return nullptr;
444 }
445 matrix.postConcat(inv);
446 }
447 matrix.postConcat(fPtsToUnit);
448
449 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
450 args.fDstColorSpace);
451 sk_sp<GrFragmentProcessor> inner(GrLinearGradient::Make(
452 GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
453 std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
454 if (!inner) {
455 return nullptr;
456 }
457 return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
458 }
459
460
461 #endif
462
463 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const464 void SkLinearGradient::toString(SkString* str) const {
465 str->append("SkLinearGradient (");
466
467 str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
468 str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
469
470 this->INHERITED::toString(str);
471
472 str->append(")");
473 }
474 #endif
475
476 ///////////////////////////////////////////////////////////////////////////////////////////////////
477
478 #include "SkNx.h"
479
480 static const SkLinearGradient::LinearGradientContext::Rec*
find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[],float tiledX)481 find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
482 SkASSERT(tiledX >= 0 && tiledX <= 1);
483
484 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
485 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
486 SkASSERT(rec[0].fPos <= rec[1].fPos);
487 rec += 1;
488 while (rec->fPos < tiledX || rec->fPosScale == 0) {
489 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
490 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
491 SkASSERT(rec[0].fPos <= rec[1].fPos);
492 rec += 1;
493 }
494 return rec - 1;
495 }
496
497 static const SkLinearGradient::LinearGradientContext::Rec*
find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[],float tiledX)498 find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
499 SkASSERT(tiledX >= 0 && tiledX <= 1);
500
501 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
502 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
503 SkASSERT(rec[0].fPos <= rec[1].fPos);
504 while (tiledX < rec->fPos || rec[1].fPosScale == 0) {
505 rec -= 1;
506 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
507 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
508 SkASSERT(rec[0].fPos <= rec[1].fPos);
509 }
510 return rec;
511 }
512
513 // As an optimization, we can apply the dither bias before interpolation -- but only when
514 // operating in premul space (apply_alpha == false). When apply_alpha == true, we must
515 // defer the bias application until after premul.
516 //
517 // The following two helpers encapsulate this logic: pre_bias is called before interpolation,
518 // and effects the bias when apply_alpha == false, while post_bias is called after premul and
519 // effects the bias for the apply_alpha == true case.
520
521 template <bool apply_alpha>
pre_bias(const Sk4f & x,const Sk4f & bias)522 Sk4f pre_bias(const Sk4f& x, const Sk4f& bias) {
523 return apply_alpha ? x : x + bias;
524 }
525
526 template <bool apply_alpha>
post_bias(const Sk4f & x,const Sk4f & bias)527 Sk4f post_bias(const Sk4f& x, const Sk4f& bias) {
528 return apply_alpha ? x + bias : x;
529 }
530
trunc_from_255(const Sk4f & x,const Sk4f & bias)531 template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x, const Sk4f& bias) {
532 SkPMColor c;
533 Sk4f c4f255 = x;
534 if (apply_alpha) {
535 // Due to use of multiplication by the 1/255 reciprocal instead of division by 255,
536 // non-integer alpha values very close to their ceiling can push the color values
537 // above the alpha value, which will become an invalid premultiplied color. So nudge
538 // alpha up slightly by a compensating scale to keep it above the color values.
539 // To do this, multiply alpha by a number slightly greater than 1 to compensate
540 // for error in scaling from the 1/255 approximation. Since this error is then
541 // scaled by the alpha value, we need to scale the epsilon by 255 to get a safe
542 // upper bound on the error.
543 static constexpr float alphaScale = 1 + 255*std::numeric_limits<float>::epsilon();
544
545 const float scale = x[SkPM4f::A] * (1 / 255.f);
546 c4f255 *= Sk4f(scale, scale, scale, alphaScale);
547 }
548 SkNx_cast<uint8_t>(post_bias<apply_alpha>(c4f255, bias)).store(&c);
549
550 return c;
551 }
552
fill(SkPMColor dst[],int count,const Sk4f & c4,const Sk4f & bias0,const Sk4f & bias1)553 template <bool apply_alpha> void fill(SkPMColor dst[], int count,
554 const Sk4f& c4, const Sk4f& bias0, const Sk4f& bias1) {
555 const SkPMColor c0 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias0), bias0);
556 const SkPMColor c1 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias1), bias1);
557 sk_memset32_dither(dst, c0, c1, count);
558 }
559
fill(SkPMColor dst[],int count,const Sk4f & c4)560 template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) {
561 // Assumes that c4 does not need to be dithered.
562 sk_memset32(dst, trunc_from_255<apply_alpha>(c4, 0), count);
563 }
564
565 /*
566 * TODOs
567 *
568 * - tilemodes
569 * - interp before or after premul
570 * - perspective
571 * - optimizations
572 * - use fixed (32bit or 16bit) instead of floats?
573 */
574
lerp_color(float fx,const SkLinearGradient::LinearGradientContext::Rec * rec)575 static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) {
576 SkASSERT(fx >= rec[0].fPos);
577 SkASSERT(fx <= rec[1].fPos);
578
579 const float p0 = rec[0].fPos;
580 const Sk4f c0 = rec[0].fColor;
581 const Sk4f c1 = rec[1].fColor;
582 const Sk4f diffc = c1 - c0;
583 const float scale = rec[1].fPosScale;
584 const float t = (fx - p0) * scale;
585 return c0 + Sk4f(t) * diffc;
586 }
587
ramp(SkPMColor dstC[],int n,const Sk4f & c,const Sk4f & dc,const Sk4f & dither0,const Sk4f & dither1)588 template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc,
589 const Sk4f& dither0, const Sk4f& dither1) {
590 Sk4f dc2 = dc + dc;
591 Sk4f dc4 = dc2 + dc2;
592 Sk4f cd0 = pre_bias<apply_alpha>(c , dither0);
593 Sk4f cd1 = pre_bias<apply_alpha>(c + dc, dither1);
594 Sk4f cd2 = cd0 + dc2;
595 Sk4f cd3 = cd1 + dc2;
596 while (n >= 4) {
597 if (!apply_alpha) {
598 Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3);
599 dstC += 4;
600 } else {
601 *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
602 *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
603 *dstC++ = trunc_from_255<apply_alpha>(cd2, dither0);
604 *dstC++ = trunc_from_255<apply_alpha>(cd3, dither1);
605 }
606 cd0 = cd0 + dc4;
607 cd1 = cd1 + dc4;
608 cd2 = cd2 + dc4;
609 cd3 = cd3 + dc4;
610 n -= 4;
611 }
612 if (n & 2) {
613 *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
614 *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
615 cd0 = cd0 + dc2;
616 }
617 if (n & 1) {
618 *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
619 }
620 }
621
622 template <bool apply_alpha, bool dx_is_pos>
shade4_dx_clamp(SkPMColor dstC[],int count,float fx,float dx,float invDx,const float dither[2])623 void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count,
624 float fx, float dx, float invDx,
625 const float dither[2]) {
626 Sk4f dither0(dither[0]);
627 Sk4f dither1(dither[1]);
628 const Rec* rec = fRecs.begin();
629
630 const Sk4f dx4 = Sk4f(dx);
631 SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
632
633 if (dx_is_pos) {
634 if (fx < 0) {
635 // count is guaranteed to be positive, but the first arg may overflow int32 after
636 // increment => casting to uint32 ensures correct clamping.
637 int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor(-fx * invDx)) + 1,
638 count);
639 SkASSERT(n > 0);
640 fill<apply_alpha>(dstC, n, rec[0].fColor);
641 count -= n;
642 dstC += n;
643 fx += n * dx;
644 SkASSERT(0 == count || fx >= 0);
645 if (n & 1) {
646 SkTSwap(dither0, dither1);
647 }
648 }
649 } else { // dx < 0
650 if (fx > 1) {
651 // count is guaranteed to be positive, but the first arg may overflow int32 after
652 // increment => casting to uint32 ensures correct clamping.
653 int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor((1 - fx) * invDx)) + 1,
654 count);
655 SkASSERT(n > 0);
656 fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor);
657 count -= n;
658 dstC += n;
659 fx += n * dx;
660 SkASSERT(0 == count || fx <= 1);
661 if (n & 1) {
662 SkTSwap(dither0, dither1);
663 }
664 }
665 }
666 SkASSERT(count >= 0);
667
668 const Rec* r;
669 if (dx_is_pos) {
670 r = fRecs.begin(); // start at the beginning
671 } else {
672 r = fRecs.begin() + fRecs.count() - 2; // start at the end
673 }
674
675 while (count > 0) {
676 if (dx_is_pos) {
677 if (fx >= 1) {
678 fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor);
679 return;
680 }
681 } else { // dx < 0
682 if (fx <= 0) {
683 fill<apply_alpha>(dstC, count, rec[0].fColor);
684 return;
685 }
686 }
687
688 if (dx_is_pos) {
689 r = find_forward(r, fx);
690 } else {
691 r = find_backward(r, fx);
692 }
693 SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1);
694
695 const float p0 = r[0].fPos;
696 const Sk4f c0 = r[0].fColor;
697 const float p1 = r[1].fPos;
698 const Sk4f diffc = Sk4f(r[1].fColor) - c0;
699 const float scale = r[1].fPosScale;
700 const float t = (fx - p0) * scale;
701 const Sk4f c = c0 + Sk4f(t) * diffc;
702 const Sk4f dc = diffc * dx4 * Sk4f(scale);
703
704 int n;
705 if (dx_is_pos) {
706 n = SkTMin((int)((p1 - fx) * invDx) + 1, count);
707 } else {
708 n = SkTMin((int)((p0 - fx) * invDx) + 1, count);
709 }
710
711 fx += n * dx;
712 // fx should now outside of the p0..p1 interval. However, due to float precision loss,
713 // its possible that fx is slightly too small/large, so we clamp it.
714 if (dx_is_pos) {
715 fx = SkTMax(fx, p1);
716 } else {
717 fx = SkTMin(fx, p0);
718 }
719
720 ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
721 dstC += n;
722 SkASSERT(dstC <= endDstC);
723
724 if (n & 1) {
725 SkTSwap(dither0, dither1);
726 }
727
728 count -= n;
729 SkASSERT(count >= 0);
730 }
731 }
732
shade4_clamp(int x,int y,SkPMColor dstC[],int count)733 void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[],
734 int count) {
735 SkASSERT(count > 0);
736
737 SkPoint srcPt;
738 fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
739 float fx = srcPt.x();
740 const float dx = fDstToIndex.getScaleX();
741
742 // Default our dither bias values to 1/2, (rounding), which is no dithering
743 float dither0 = 0.5f;
744 float dither1 = 0.5f;
745 if (fDither) {
746 const float ditherCell[] = {
747 1/8.0f, 5/8.0f,
748 7/8.0f, 3/8.0f,
749 };
750 const int rowIndex = (y & 1) << 1;
751 dither0 = ditherCell[rowIndex];
752 dither1 = ditherCell[rowIndex + 1];
753 if (x & 1) {
754 SkTSwap(dither0, dither1);
755 }
756 }
757 const float dither[2] = { dither0, dither1 };
758
759 if (SkScalarNearlyZero(dx * count)) { // gradient is vertical
760 const float pinFx = SkTPin(fx, 0.0f, 1.0f);
761 Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx));
762 if (fApplyAlphaAfterInterp) {
763 fill<true>(dstC, count, c, dither0, dither1);
764 } else {
765 fill<false>(dstC, count, c, dither0, dither1);
766 }
767 return;
768 }
769
770 SkASSERT(0.f != dx);
771 const float invDx = 1 / dx;
772 if (dx > 0) {
773 if (fApplyAlphaAfterInterp) {
774 this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither);
775 } else {
776 this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither);
777 }
778 } else {
779 if (fApplyAlphaAfterInterp) {
780 this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither);
781 } else {
782 this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither);
783 }
784 }
785 }
786