Line data Source code
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 "SkLinearGradient.h"
10 : #include "SkRefCnt.h"
11 :
12 : // define to test the 4f gradient path
13 : // #define FORCE_4F_CONTEXT
14 :
15 : static const float kInv255Float = 1.0f / 255;
16 :
17 0 : static inline int repeat_8bits(int x) {
18 0 : return x & 0xFF;
19 : }
20 :
21 0 : static inline int mirror_8bits(int x) {
22 0 : if (x & 256) {
23 0 : x = ~x;
24 : }
25 0 : return x & 255;
26 : }
27 :
28 25 : static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
29 25 : SkVector vec = pts[1] - pts[0];
30 25 : SkScalar mag = vec.length();
31 25 : SkScalar inv = mag ? SkScalarInvert(mag) : 0;
32 :
33 25 : vec.scale(inv);
34 : SkMatrix matrix;
35 25 : matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
36 25 : matrix.postTranslate(-pts[0].fX, -pts[0].fY);
37 25 : matrix.postScale(inv, inv);
38 25 : return matrix;
39 : }
40 :
41 25 : static bool use_4f_context(const SkShader::ContextRec& rec, uint32_t flags) {
42 : #ifdef FORCE_4F_CONTEXT
43 : return true;
44 : #else
45 25 : return rec.fPreferredDstType == SkShader::ContextRec::kPM4f_DstType
46 25 : || SkToBool(flags & SkLinearGradient::kForce4fContext_PrivateFlag);
47 : #endif
48 : }
49 :
50 : ///////////////////////////////////////////////////////////////////////////////
51 :
52 25 : SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
53 50 : : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
54 : , fStart(pts[0])
55 50 : , fEnd(pts[1]) {
56 25 : }
57 :
58 0 : sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
59 0 : DescriptorScope desc;
60 0 : if (!desc.unflatten(buffer)) {
61 0 : return nullptr;
62 : }
63 : SkPoint pts[2];
64 0 : pts[0] = buffer.readPoint();
65 0 : pts[1] = buffer.readPoint();
66 0 : return SkGradientShader::MakeLinear(pts, desc.fColors, std::move(desc.fColorSpace), desc.fPos,
67 : desc.fCount, desc.fTileMode, desc.fGradFlags,
68 0 : desc.fLocalMatrix);
69 : }
70 :
71 0 : void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
72 0 : this->INHERITED::flatten(buffer);
73 0 : buffer.writePoint(fStart);
74 0 : buffer.writePoint(fEnd);
75 0 : }
76 :
77 25 : SkShader::Context* SkLinearGradient::onMakeContext(
78 : const ContextRec& rec, SkArenaAlloc* alloc) const
79 : {
80 25 : return use_4f_context(rec, fGradFlags)
81 25 : ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec)
82 25 : : CheckedMakeContext< LinearGradientContext>(alloc, *this, rec);
83 : }
84 :
85 : //
86 : // Stages:
87 : //
88 : // * matrix (map dst -> grad space)
89 : // * clamp/repeat/mirror (tiling)
90 : // * linear_gradient_2stops (lerp c0/c1)
91 : // * optional premul
92 : //
93 2 : bool SkLinearGradient::onAppendStages(SkRasterPipeline* p,
94 : SkColorSpace* dstCS,
95 : SkArenaAlloc* alloc,
96 : const SkMatrix& ctm,
97 : const SkPaint& paint,
98 : const SkMatrix* localM) const {
99 : // Local matrix not supported currently. Remove once we have a generic RP wrapper.
100 2 : if (localM || !getLocalMatrix().isIdentity()) {
101 2 : return false;
102 : }
103 :
104 : SkMatrix dstToPts;
105 0 : if (!ctm.invert(&dstToPts)) {
106 0 : return false;
107 : }
108 :
109 0 : const auto dstToUnit = SkMatrix::Concat(fPtsToUnit, dstToPts);
110 :
111 : // If the gradient is less than a quarter of a pixel, this falls into the subpixel gradient code
112 : // handled on a different path.
113 0 : SkVector dx = dstToUnit.mapVector(1, 0);
114 0 : if (dx.fX >= 4) {
115 0 : return false;
116 : }
117 :
118 0 : auto* m = alloc->makeArrayDefault<float>(9);
119 0 : if (dstToUnit.asAffine(m)) {
120 : // TODO: mapping y is not needed; split the matrix stages to save some math?
121 0 : p->append(SkRasterPipeline::matrix_2x3, m);
122 : } else {
123 0 : dstToUnit.get9(m);
124 0 : p->append(SkRasterPipeline::matrix_perspective, m);
125 : }
126 :
127 : // TODO: clamp/repeat/mirror const 1f stages?
128 0 : auto* limit = alloc->make<float>(1.0f);
129 :
130 0 : const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
131 0 : auto prepareColor = [premulGrad, dstCS, this](int i) {
132 0 : SkColor4f c = dstCS ? to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS)
133 0 : : SkColor4f_from_SkColor(fOrigColors[i], nullptr);
134 : return premulGrad ? c.premul()
135 0 : : SkPM4f::From4f(Sk4f::Load(&c));
136 0 : };
137 :
138 : // The two-stop case with stops at 0 and 1.
139 0 : if (fColorCount == 2 && fOrigPos == nullptr) {
140 0 : switch (fTileMode) {
141 0 : case kClamp_TileMode: p->append(SkRasterPipeline:: clamp_x, limit); break;
142 0 : case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit); break;
143 0 : case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit); break;
144 : }
145 :
146 0 : const SkPM4f c_l = prepareColor(0),
147 0 : c_r = prepareColor(1);
148 :
149 : // See F and B below.
150 0 : auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2);
151 0 : f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f());
152 0 : f_and_b[1] = c_l;
153 :
154 0 : p->append(SkRasterPipeline::linear_gradient_2stops, f_and_b);
155 : } else {
156 0 : switch (fTileMode) {
157 : // The search strategy does not need clamping. It has implicit hard stops at the
158 : // first and last stop.
159 0 : case kClamp_TileMode: break;
160 0 : case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit); break;
161 0 : case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit); break;
162 : }
163 :
164 : struct Stop { float t; SkPM4f f, b; };
165 : struct Ctx { size_t n; Stop* stops; SkPM4f start; };
166 :
167 0 : auto* ctx = alloc->make<Ctx>();
168 0 : ctx->start = prepareColor(0);
169 :
170 : // For each stop we calculate a bias B and a scale factor F, such that
171 : // for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
172 0 : auto init_stop = [](float t_l, float t_r, SkPM4f c_l, SkPM4f c_r, Stop *stop) {
173 0 : auto F = SkPM4f::From4f((c_r.to4f() - c_l.to4f()) / (t_r - t_l));
174 0 : auto B = SkPM4f::From4f(c_l.to4f() - (F.to4f() * t_l));
175 0 : *stop = {t_l, F, B};
176 0 : };
177 :
178 0 : if (fOrigPos == nullptr) {
179 : // Handle evenly distributed stops.
180 :
181 0 : float dt = 1.0f / (fColorCount - 1);
182 : // In the evenly distributed case, fColorCount is the number of stops. There are no
183 : // dummy entries.
184 0 : auto* stopsArray = alloc->makeArrayDefault<Stop>(fColorCount);
185 :
186 0 : float t_l = 0;
187 0 : SkPM4f c_l = ctx->start;
188 0 : for (int i = 0; i < fColorCount - 1; i++) {
189 : // Use multiply instead of accumulating error using repeated addition.
190 0 : float t_r = (i + 1) * dt;
191 0 : SkPM4f c_r = prepareColor(i + 1);
192 0 : init_stop(t_l, t_r, c_l, c_r, &stopsArray[i]);
193 :
194 0 : t_l = t_r;
195 0 : c_l = c_r;
196 : }
197 :
198 : // Force the last stop.
199 0 : stopsArray[fColorCount - 1].t = 1;
200 0 : stopsArray[fColorCount - 1].f = SkPM4f::From4f(Sk4f{0});
201 0 : stopsArray[fColorCount - 1].b = prepareColor(fColorCount - 1);
202 :
203 0 : ctx->n = fColorCount;
204 0 : ctx->stops = stopsArray;
205 : } else {
206 : // Handle arbitrary stops.
207 :
208 : // Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
209 : // because they are naturally handled by the search method.
210 : int firstStop;
211 : int lastStop;
212 0 : if (fColorCount > 2) {
213 0 : firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
214 0 : lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
215 0 : ? fColorCount - 1 : fColorCount - 2;
216 : } else {
217 0 : firstStop = 0;
218 0 : lastStop = 1;
219 : }
220 0 : int realCount = lastStop - firstStop + 1;
221 :
222 : // This is the maximum number of stops. There may be fewer stops because the duplicate
223 : // points of hard stops are removed.
224 0 : auto* stopsArray = alloc->makeArrayDefault<Stop>(realCount);
225 :
226 0 : size_t stopCount = 0;
227 0 : float t_l = fOrigPos[firstStop];
228 0 : SkPM4f c_l = prepareColor(firstStop);
229 : // N.B. lastStop is the index of the last stop, not one after.
230 0 : for (int i = firstStop; i < lastStop; i++) {
231 0 : float t_r = fOrigPos[i + 1];
232 0 : SkPM4f c_r = prepareColor(i + 1);
233 0 : if (t_l < t_r) {
234 0 : init_stop(t_l, t_r, c_l, c_r, &stopsArray[stopCount]);
235 0 : stopCount += 1;
236 : }
237 0 : t_l = t_r;
238 0 : c_l = c_r;
239 : }
240 :
241 0 : stopsArray[stopCount].t = fOrigPos[lastStop];
242 0 : stopsArray[stopCount].f = SkPM4f::From4f(Sk4f{0});
243 0 : stopsArray[stopCount].b = prepareColor(lastStop);
244 0 : stopCount += 1;
245 :
246 0 : ctx->n = stopCount;
247 0 : ctx->stops = stopsArray;
248 : }
249 :
250 0 : p->append(SkRasterPipeline::linear_gradient, ctx);
251 : }
252 :
253 0 : if (!premulGrad && !this->colorsAreOpaque()) {
254 0 : p->append(SkRasterPipeline::premul);
255 : }
256 :
257 0 : return true;
258 : }
259 :
260 : // This swizzles SkColor into the same component order as SkPMColor, but does not actually
261 : // "pre" multiply the color components.
262 : //
263 : // This allows us to map directly to Sk4f, and eventually scale down to bytes to output a
264 : // SkPMColor from the floats, without having to swizzle each time.
265 : //
266 81 : static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
267 81 : return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
268 : }
269 :
270 25 : SkLinearGradient::LinearGradientContext::LinearGradientContext(
271 25 : const SkLinearGradient& shader, const ContextRec& ctx)
272 25 : : INHERITED(shader, ctx)
273 : {
274 : // setup for Sk4f
275 25 : const int count = shader.fColorCount;
276 25 : SkASSERT(count > 1);
277 :
278 25 : fRecs.setCount(count);
279 25 : Rec* rec = fRecs.begin();
280 25 : if (shader.fOrigPos) {
281 15 : rec[0].fPos = 0;
282 15 : SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;) // should never get used
283 61 : for (int i = 1; i < count; ++i) {
284 46 : rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f);
285 46 : float diff = rec[i].fPos - rec[i - 1].fPos;
286 46 : if (diff > 0) {
287 33 : rec[i].fPosScale = 1.0f / diff;
288 : } else {
289 13 : rec[i].fPosScale = 0;
290 : }
291 : }
292 : } else {
293 : // no pos specified, so we compute evenly spaced values
294 10 : const float scale = float(count - 1);
295 10 : const float invScale = 1.0f / scale;
296 30 : for (int i = 0; i < count; ++i) {
297 20 : rec[i].fPos = i * invScale;
298 20 : rec[i].fPosScale = scale;
299 : }
300 : }
301 25 : rec[count - 1].fPos = 1; // overwrite the last value just to be sure we end at 1.0
302 :
303 25 : fApplyAlphaAfterInterp = true;
304 50 : if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) ||
305 25 : shader.colorsAreOpaque())
306 : {
307 0 : fApplyAlphaAfterInterp = false;
308 : }
309 :
310 25 : if (fApplyAlphaAfterInterp) {
311 : // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to
312 : // interpolate in unpremultiplied space first, and then scale by alpha right before we
313 : // convert to SkPMColor bytes.
314 25 : const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
315 : const Sk4f scale(1, 1, 1, paintAlpha);
316 106 : for (int i = 0; i < count; ++i) {
317 81 : uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
318 243 : rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&c)) * scale;
319 81 : if (i > 0) {
320 56 : SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
321 : }
322 : }
323 : } else {
324 : // Our fColor values are premultiplied, so converting to SkPMColor is just a matter
325 : // of converting the floats down to bytes.
326 0 : unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7);
327 0 : for (int i = 0; i < count; ++i) {
328 0 : SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
329 0 : pmc = SkAlphaMulQ(pmc, alphaScale);
330 0 : rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&pmc));
331 0 : if (i > 0) {
332 0 : SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
333 : }
334 : }
335 : }
336 25 : }
337 :
338 : #define NO_CHECK_ITER \
339 : do { \
340 : unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
341 : SkASSERT(fi <= 0xFF); \
342 : fx += dx; \
343 : *dstC++ = cache[toggle + fi]; \
344 : toggle = next_dither_toggle(toggle); \
345 : } while (0)
346 :
347 : namespace {
348 :
349 : typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
350 : SkPMColor* dstC, const SkPMColor* cache,
351 : int toggle, int count);
352 :
353 : // Linear interpolation (lerp) is unnecessary if there are no sharp
354 : // discontinuities in the gradient - which must be true if there are
355 : // only 2 colors - but it's cheap.
356 2816 : void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
357 : SkPMColor* SK_RESTRICT dstC,
358 : const SkPMColor* SK_RESTRICT cache,
359 : int toggle, int count) {
360 : // We're a vertical gradient, so no change in a span.
361 : // If colors change sharply across the gradient, dithering is
362 : // insufficient (it subsamples the color space) and we need to lerp.
363 2816 : unsigned fullIndex = proc(SkGradFixedToFixed(fx));
364 2816 : if (fullIndex >= (SK_FixedHalf >> SkGradientShaderBase::kCache32Bits)) {
365 2816 : fullIndex -= SK_FixedHalf >> SkGradientShaderBase::kCache32Bits;
366 : } else {
367 0 : fullIndex = 0;
368 : }
369 2816 : unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
370 2816 : unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
371 :
372 2816 : int index0 = fi + toggle;
373 2816 : int index1 = index0;
374 2816 : if (fi < SkGradientShaderBase::kCache32Count - 1) {
375 2816 : index1 += 1;
376 : }
377 2816 : SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
378 2816 : index0 ^= SkGradientShaderBase::kDitherStride32;
379 2816 : index1 ^= SkGradientShaderBase::kDitherStride32;
380 2816 : SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
381 2816 : sk_memset32_dither(dstC, lerp, dlerp, count);
382 2816 : }
383 :
384 0 : void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
385 : SkPMColor* SK_RESTRICT dstC,
386 : const SkPMColor* SK_RESTRICT cache,
387 : int toggle, int count) {
388 : SkClampRange range;
389 0 : range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
390 0 : range.validate(count);
391 :
392 0 : if ((count = range.fCount0) > 0) {
393 0 : sk_memset32_dither(dstC,
394 0 : cache[toggle + range.fV0],
395 0 : cache[next_dither_toggle(toggle) + range.fV0],
396 0 : count);
397 0 : dstC += count;
398 : }
399 0 : if ((count = range.fCount1) > 0) {
400 0 : int unroll = count >> 3;
401 0 : fx = range.fFx1;
402 0 : for (int i = 0; i < unroll; i++) {
403 0 : NO_CHECK_ITER; NO_CHECK_ITER;
404 0 : NO_CHECK_ITER; NO_CHECK_ITER;
405 0 : NO_CHECK_ITER; NO_CHECK_ITER;
406 0 : NO_CHECK_ITER; NO_CHECK_ITER;
407 : }
408 0 : if ((count &= 7) > 0) {
409 0 : do {
410 0 : NO_CHECK_ITER;
411 : } while (--count != 0);
412 : }
413 : }
414 0 : if ((count = range.fCount2) > 0) {
415 0 : sk_memset32_dither(dstC,
416 0 : cache[toggle + range.fV1],
417 0 : cache[next_dither_toggle(toggle) + range.fV1],
418 0 : count);
419 : }
420 0 : }
421 :
422 0 : void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
423 : SkPMColor* SK_RESTRICT dstC,
424 : const SkPMColor* SK_RESTRICT cache,
425 : int toggle, int count) {
426 0 : do {
427 0 : unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8);
428 0 : SkASSERT(fi <= 0xFF);
429 0 : fx += dx;
430 0 : *dstC++ = cache[toggle + fi];
431 0 : toggle = next_dither_toggle(toggle);
432 : } while (--count != 0);
433 0 : }
434 :
435 0 : void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
436 : SkPMColor* SK_RESTRICT dstC,
437 : const SkPMColor* SK_RESTRICT cache,
438 : int toggle, int count) {
439 0 : do {
440 0 : unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8);
441 0 : SkASSERT(fi <= 0xFF);
442 0 : fx += dx;
443 0 : *dstC++ = cache[toggle + fi];
444 0 : toggle = next_dither_toggle(toggle);
445 : } while (--count != 0);
446 0 : }
447 :
448 : }
449 :
450 3393 : void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
451 : int count) {
452 3393 : SkASSERT(count > 0);
453 3393 : const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
454 :
455 3970 : if (SkShader::kClamp_TileMode == linearGradient.fTileMode &&
456 577 : kLinear_MatrixClass == fDstToIndexClass)
457 : {
458 577 : this->shade4_clamp(x, y, dstC, count);
459 577 : return;
460 : }
461 :
462 : SkPoint srcPt;
463 2816 : SkMatrix::MapXYProc dstProc = fDstToIndexProc;
464 2816 : TileProc proc = linearGradient.fTileProc;
465 2816 : const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
466 2816 : int toggle = init_dither_toggle(x, y);
467 :
468 2816 : if (fDstToIndexClass != kPerspective_MatrixClass) {
469 2816 : dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
470 5632 : SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
471 2816 : SkGradFixed dx, fx = SkScalarPinToGradFixed(srcPt.fX);
472 :
473 2816 : if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
474 0 : const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
475 : // todo: do we need a real/high-precision value for dx here?
476 0 : dx = SkScalarPinToGradFixed(step.fX);
477 : } else {
478 2816 : SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
479 2816 : dx = SkScalarPinToGradFixed(fDstToIndex.getScaleX());
480 : }
481 :
482 2816 : LinearShadeProc shadeProc = shadeSpan_linear_repeat;
483 2816 : if (0 == dx) {
484 2816 : shadeProc = shadeSpan_linear_vertical_lerp;
485 0 : } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
486 0 : shadeProc = shadeSpan_linear_clamp;
487 0 : } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
488 0 : shadeProc = shadeSpan_linear_mirror;
489 : } else {
490 0 : SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
491 : }
492 2816 : (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
493 : } else {
494 0 : SkScalar dstX = SkIntToScalar(x);
495 0 : SkScalar dstY = SkIntToScalar(y);
496 0 : do {
497 0 : dstProc(fDstToIndex, dstX, dstY, &srcPt);
498 0 : unsigned fi = proc(SkScalarToFixed(srcPt.fX));
499 0 : SkASSERT(fi <= 0xFFFF);
500 0 : *dstC++ = cache[toggle + (fi >> kCache32Shift)];
501 0 : toggle = next_dither_toggle(toggle);
502 0 : dstX += SK_Scalar1;
503 : } while (--count != 0);
504 : }
505 : }
506 :
507 0 : SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
508 0 : if (info) {
509 0 : commonAsAGradient(info);
510 0 : info->fPoint[0] = fStart;
511 0 : info->fPoint[1] = fEnd;
512 : }
513 0 : return kLinear_GradientType;
514 : }
515 :
516 : #if SK_SUPPORT_GPU
517 :
518 : #include "GrColorSpaceXform.h"
519 : #include "GrShaderCaps.h"
520 : #include "glsl/GrGLSLFragmentShaderBuilder.h"
521 : #include "SkGr.h"
522 :
523 : /////////////////////////////////////////////////////////////////////
524 :
525 : class GrLinearGradient : public GrGradientEffect {
526 : public:
527 : class GLSLLinearProcessor;
528 :
529 0 : static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
530 0 : return sk_sp<GrFragmentProcessor>(new GrLinearGradient(args));
531 : }
532 :
533 0 : ~GrLinearGradient() override {}
534 :
535 0 : const char* name() const override { return "Linear Gradient"; }
536 :
537 : private:
538 0 : GrLinearGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
539 0 : this->initClassID<GrLinearGradient>();
540 0 : }
541 :
542 : GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
543 :
544 : virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
545 : GrProcessorKeyBuilder* b) const override;
546 :
547 : GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
548 :
549 : typedef GrGradientEffect INHERITED;
550 : };
551 :
552 : /////////////////////////////////////////////////////////////////////
553 :
554 : class GrLinearGradient::GLSLLinearProcessor : public GrGradientEffect::GLSLProcessor {
555 : public:
556 0 : GLSLLinearProcessor(const GrProcessor&) {}
557 :
558 0 : ~GLSLLinearProcessor() override {}
559 :
560 : virtual void emitCode(EmitArgs&) override;
561 :
562 0 : static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
563 0 : b->add32(GenBaseGradientKey(processor));
564 0 : }
565 :
566 : private:
567 : typedef GrGradientEffect::GLSLProcessor INHERITED;
568 : };
569 :
570 : /////////////////////////////////////////////////////////////////////
571 :
572 0 : GrGLSLFragmentProcessor* GrLinearGradient::onCreateGLSLInstance() const {
573 0 : return new GrLinearGradient::GLSLLinearProcessor(*this);
574 : }
575 :
576 0 : void GrLinearGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
577 : GrProcessorKeyBuilder* b) const {
578 0 : GrLinearGradient::GLSLLinearProcessor::GenKey(*this, caps, b);
579 0 : }
580 :
581 : /////////////////////////////////////////////////////////////////////
582 :
583 : GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient);
584 :
585 : #if GR_TEST_UTILS
586 0 : sk_sp<GrFragmentProcessor> GrLinearGradient::TestCreate(GrProcessorTestData* d) {
587 0 : SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()},
588 0 : {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}};
589 :
590 0 : RandomGradientParams params(d->fRandom);
591 0 : auto shader = params.fUseColors4f ?
592 0 : SkGradientShader::MakeLinear(points, params.fColors4f, params.fColorSpace, params.fStops,
593 : params.fColorCount, params.fTileMode) :
594 0 : SkGradientShader::MakeLinear(points, params.fColors, params.fStops,
595 0 : params.fColorCount, params.fTileMode);
596 0 : GrTest::TestAsFPArgs asFPArgs(d);
597 0 : sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
598 0 : GrAlwaysAssert(fp);
599 0 : return fp;
600 : }
601 : #endif
602 :
603 : /////////////////////////////////////////////////////////////////////
604 :
605 0 : void GrLinearGradient::GLSLLinearProcessor::emitCode(EmitArgs& args) {
606 0 : const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>();
607 0 : this->emitUniforms(args.fUniformHandler, ge);
608 0 : SkString t = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
609 0 : t.append(".x");
610 0 : this->emitColor(args.fFragBuilder,
611 : args.fUniformHandler,
612 : args.fShaderCaps,
613 : ge,
614 : t.c_str(),
615 : args.fOutputColor,
616 : args.fInputColor,
617 0 : args.fTexSamplers);
618 0 : }
619 :
620 : /////////////////////////////////////////////////////////////////////
621 :
622 0 : sk_sp<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(const AsFPArgs& args) const {
623 0 : SkASSERT(args.fContext);
624 :
625 : SkMatrix matrix;
626 0 : if (!this->getLocalMatrix().invert(&matrix)) {
627 0 : return nullptr;
628 : }
629 0 : if (args.fLocalMatrix) {
630 : SkMatrix inv;
631 0 : if (!args.fLocalMatrix->invert(&inv)) {
632 0 : return nullptr;
633 : }
634 0 : matrix.postConcat(inv);
635 : }
636 0 : matrix.postConcat(fPtsToUnit);
637 :
638 0 : sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
639 0 : args.fDstColorSpace);
640 : sk_sp<GrFragmentProcessor> inner(GrLinearGradient::Make(
641 0 : GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
642 0 : std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
643 0 : return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
644 : }
645 :
646 :
647 : #endif
648 :
649 : #ifndef SK_IGNORE_TO_STRING
650 0 : void SkLinearGradient::toString(SkString* str) const {
651 0 : str->append("SkLinearGradient (");
652 :
653 0 : str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
654 0 : str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
655 :
656 0 : this->INHERITED::toString(str);
657 :
658 0 : str->append(")");
659 0 : }
660 : #endif
661 :
662 : ///////////////////////////////////////////////////////////////////////////////////////////////////
663 :
664 : #include "SkNx.h"
665 :
666 : static const SkLinearGradient::LinearGradientContext::Rec*
667 519 : find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
668 519 : SkASSERT(tiledX >= 0 && tiledX <= 1);
669 :
670 519 : SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
671 519 : SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
672 519 : SkASSERT(rec[0].fPos <= rec[1].fPos);
673 519 : rec += 1;
674 2179 : while (rec->fPos < tiledX || rec->fPosScale == 0) {
675 830 : SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
676 830 : SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
677 830 : SkASSERT(rec[0].fPos <= rec[1].fPos);
678 830 : rec += 1;
679 : }
680 519 : return rec - 1;
681 : }
682 :
683 : static const SkLinearGradient::LinearGradientContext::Rec*
684 116 : find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
685 116 : SkASSERT(tiledX >= 0 && tiledX <= 1);
686 :
687 116 : SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
688 116 : SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
689 116 : SkASSERT(rec[0].fPos <= rec[1].fPos);
690 232 : while (tiledX < rec->fPos || rec[1].fPosScale == 0) {
691 58 : rec -= 1;
692 58 : SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
693 58 : SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
694 58 : SkASSERT(rec[0].fPos <= rec[1].fPos);
695 : }
696 116 : return rec;
697 : }
698 :
699 : // As an optimization, we can apply the dither bias before interpolation -- but only when
700 : // operating in premul space (apply_alpha == false). When apply_alpha == true, we must
701 : // defer the bias application until after premul.
702 : //
703 : // The following two helpers encapsulate this logic: pre_bias is called before interpolation,
704 : // and effects the bias when apply_alpha == false, while post_bias is called after premul and
705 : // effects the bias for the apply_alpha == true case.
706 :
707 : template <bool apply_alpha>
708 1270 : Sk4f pre_bias(const Sk4f& x, const Sk4f& bias) {
709 1270 : return apply_alpha ? x : x + bias;
710 : }
711 :
712 : template <bool apply_alpha>
713 20294 : Sk4f post_bias(const Sk4f& x, const Sk4f& bias) {
714 20294 : return apply_alpha ? x + bias : x;
715 : }
716 :
717 20294 : template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x, const Sk4f& bias) {
718 : SkPMColor c;
719 20294 : Sk4f c4f255 = x;
720 : if (apply_alpha) {
721 20294 : const float scale = x[SkPM4f::A] * (1 / 255.f);
722 : // Multiply alpha by a number slightly greater than 1 to compensate for error
723 : // in scaling from the 1/255 approximation. This error is less than 1e-6 for
724 : // all alpha values. Non-integer alpha values very close to their ceiling can
725 : // push the color values above the alpha value, which will become an invalid
726 : // premultiplied color. So nudge alpha up slightly by this compensating scale
727 : // to keep it above the color values.
728 20294 : c4f255 *= Sk4f(scale, scale, scale, 1.000001f);
729 : }
730 60882 : SkNx_cast<uint8_t>(post_bias<apply_alpha>(c4f255, bias)).store(&c);
731 :
732 20294 : return c;
733 : }
734 :
735 519 : template <bool apply_alpha> void fill(SkPMColor dst[], int count,
736 : const Sk4f& c4, const Sk4f& bias0, const Sk4f& bias1) {
737 519 : const SkPMColor c0 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias0), bias0);
738 519 : const SkPMColor c1 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias1), bias1);
739 519 : sk_memset32_dither(dst, c0, c1, count);
740 519 : }
741 :
742 0 : template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) {
743 : // Assumes that c4 does not need to be dithered.
744 0 : sk_memset32(dst, trunc_from_255<apply_alpha>(c4, 0), count);
745 0 : }
746 :
747 : /*
748 : * TODOs
749 : *
750 : * - tilemodes
751 : * - interp before or after premul
752 : * - perspective
753 : * - optimizations
754 : * - use fixed (32bit or 16bit) instead of floats?
755 : */
756 :
757 519 : static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) {
758 519 : SkASSERT(fx >= rec[0].fPos);
759 519 : SkASSERT(fx <= rec[1].fPos);
760 :
761 519 : const float p0 = rec[0].fPos;
762 519 : const Sk4f c0 = rec[0].fColor;
763 519 : const Sk4f c1 = rec[1].fColor;
764 519 : const Sk4f diffc = c1 - c0;
765 519 : const float scale = rec[1].fPosScale;
766 519 : const float t = (fx - p0) * scale;
767 1038 : return c0 + Sk4f(t) * diffc;
768 : }
769 :
770 116 : template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc,
771 : const Sk4f& dither0, const Sk4f& dither1) {
772 116 : Sk4f dc2 = dc + dc;
773 116 : Sk4f dc4 = dc2 + dc2;
774 116 : Sk4f cd0 = pre_bias<apply_alpha>(c , dither0);
775 116 : Sk4f cd1 = pre_bias<apply_alpha>(c + dc, dither1);
776 116 : Sk4f cd2 = cd0 + dc2;
777 116 : Sk4f cd3 = cd1 + dc2;
778 9570 : while (n >= 4) {
779 : if (!apply_alpha) {
780 : Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3);
781 0 : dstC += 4;
782 : } else {
783 4727 : *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
784 4727 : *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
785 4727 : *dstC++ = trunc_from_255<apply_alpha>(cd2, dither0);
786 4727 : *dstC++ = trunc_from_255<apply_alpha>(cd3, dither1);
787 : }
788 4727 : cd0 = cd0 + dc4;
789 4727 : cd1 = cd1 + dc4;
790 4727 : cd2 = cd2 + dc4;
791 4727 : cd3 = cd3 + dc4;
792 4727 : n -= 4;
793 : }
794 116 : if (n & 2) {
795 116 : *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
796 116 : *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
797 116 : cd0 = cd0 + dc2;
798 : }
799 116 : if (n & 1) {
800 116 : *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
801 : }
802 116 : }
803 :
804 : template <bool apply_alpha, bool dx_is_pos>
805 58 : void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count,
806 : float fx, float dx, float invDx,
807 : const float dither[2]) {
808 58 : Sk4f dither0(dither[0]);
809 58 : Sk4f dither1(dither[1]);
810 58 : const Rec* rec = fRecs.begin();
811 :
812 : const Sk4f dx4 = Sk4f(dx);
813 58 : SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
814 :
815 : if (dx_is_pos) {
816 0 : if (fx < 0) {
817 : // count is guaranteed to be positive, but the first arg may overflow int32 after
818 : // increment => casting to uint32 ensures correct clamping.
819 0 : int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor(-fx * invDx)) + 1,
820 0 : count);
821 0 : SkASSERT(n > 0);
822 0 : fill<apply_alpha>(dstC, n, rec[0].fColor);
823 0 : count -= n;
824 0 : dstC += n;
825 0 : fx += n * dx;
826 0 : SkASSERT(0 == count || fx >= 0);
827 0 : if (n & 1) {
828 0 : SkTSwap(dither0, dither1);
829 : }
830 : }
831 : } else { // dx < 0
832 58 : if (fx > 1) {
833 : // count is guaranteed to be positive, but the first arg may overflow int32 after
834 : // increment => casting to uint32 ensures correct clamping.
835 0 : int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor((1 - fx) * invDx)) + 1,
836 0 : count);
837 0 : SkASSERT(n > 0);
838 0 : fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor);
839 0 : count -= n;
840 0 : dstC += n;
841 0 : fx += n * dx;
842 0 : SkASSERT(0 == count || fx <= 1);
843 0 : if (n & 1) {
844 0 : SkTSwap(dither0, dither1);
845 : }
846 : }
847 : }
848 58 : SkASSERT(count >= 0);
849 :
850 : const Rec* r;
851 : if (dx_is_pos) {
852 0 : r = fRecs.begin(); // start at the beginning
853 : } else {
854 58 : r = fRecs.begin() + fRecs.count() - 2; // start at the end
855 : }
856 :
857 290 : while (count > 0) {
858 : if (dx_is_pos) {
859 0 : if (fx >= 1) {
860 0 : fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor);
861 0 : return;
862 : }
863 : } else { // dx < 0
864 116 : if (fx <= 0) {
865 0 : fill<apply_alpha>(dstC, count, rec[0].fColor);
866 0 : return;
867 : }
868 : }
869 :
870 : if (dx_is_pos) {
871 0 : r = find_forward(r, fx);
872 : } else {
873 116 : r = find_backward(r, fx);
874 : }
875 116 : SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1);
876 :
877 116 : const float p0 = r[0].fPos;
878 116 : const Sk4f c0 = r[0].fColor;
879 116 : const float p1 = r[1].fPos;
880 232 : const Sk4f diffc = Sk4f(r[1].fColor) - c0;
881 116 : const float scale = r[1].fPosScale;
882 116 : const float t = (fx - p0) * scale;
883 232 : const Sk4f c = c0 + Sk4f(t) * diffc;
884 232 : const Sk4f dc = diffc * dx4 * Sk4f(scale);
885 :
886 : int n;
887 : if (dx_is_pos) {
888 0 : n = SkTMin((int)((p1 - fx) * invDx) + 1, count);
889 : } else {
890 116 : n = SkTMin((int)((p0 - fx) * invDx) + 1, count);
891 : }
892 :
893 116 : fx += n * dx;
894 : // fx should now outside of the p0..p1 interval. However, due to float precision loss,
895 : // its possible that fx is slightly too small/large, so we clamp it.
896 : if (dx_is_pos) {
897 0 : fx = SkTMax(fx, p1);
898 : } else {
899 116 : fx = SkTMin(fx, p0);
900 : }
901 :
902 116 : ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
903 116 : dstC += n;
904 116 : SkASSERT(dstC <= endDstC);
905 :
906 116 : if (n & 1) {
907 116 : SkTSwap(dither0, dither1);
908 : }
909 :
910 116 : count -= n;
911 116 : SkASSERT(count >= 0);
912 : }
913 : }
914 :
915 577 : void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[],
916 : int count) {
917 577 : SkASSERT(count > 0);
918 577 : SkASSERT(kLinear_MatrixClass == fDstToIndexClass);
919 :
920 : SkPoint srcPt;
921 577 : fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
922 577 : float fx = srcPt.x();
923 577 : const float dx = fDstToIndex.getScaleX();
924 :
925 : // Default our dither bias values to 1/2, (rounding), which is no dithering
926 577 : float dither0 = 0.5f;
927 577 : float dither1 = 0.5f;
928 577 : if (fDither) {
929 : const float ditherCell[] = {
930 : 1/8.0f, 5/8.0f,
931 : 7/8.0f, 3/8.0f,
932 0 : };
933 0 : const int rowIndex = (y & 1) << 1;
934 0 : dither0 = ditherCell[rowIndex];
935 0 : dither1 = ditherCell[rowIndex + 1];
936 0 : if (x & 1) {
937 0 : SkTSwap(dither0, dither1);
938 : }
939 : }
940 577 : const float dither[2] = { dither0, dither1 };
941 :
942 577 : if (SkScalarNearlyZero(dx * count)) { // gradient is vertical
943 519 : const float pinFx = SkTPin(fx, 0.0f, 1.0f);
944 519 : Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx));
945 519 : if (fApplyAlphaAfterInterp) {
946 1557 : fill<true>(dstC, count, c, dither0, dither1);
947 : } else {
948 0 : fill<false>(dstC, count, c, dither0, dither1);
949 : }
950 519 : return;
951 : }
952 :
953 58 : SkASSERT(0.f != dx);
954 58 : const float invDx = 1 / dx;
955 58 : if (dx > 0) {
956 0 : if (fApplyAlphaAfterInterp) {
957 0 : this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither);
958 : } else {
959 0 : this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither);
960 : }
961 : } else {
962 58 : if (fApplyAlphaAfterInterp) {
963 58 : this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither);
964 : } else {
965 0 : this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither);
966 : }
967 : }
968 : }
|