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