1/* 2 * Copyright 2006 The Android Open Source Project 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 <algorithm> 9#include "include/core/SkMallocPixelRef.h" 10#include "include/private/SkFloatBits.h" 11#include "include/private/SkHalf.h" 12#include "include/private/SkTPin.h" 13#include "include/private/SkVx.h" 14#include "src/core/SkColorSpacePriv.h" 15#include "src/core/SkConvertPixels.h" 16#include "src/core/SkMatrixProvider.h" 17#include "src/core/SkReadBuffer.h" 18#include "src/core/SkVM.h" 19#include "src/core/SkWriteBuffer.h" 20#include "src/shaders/gradients/Sk4fLinearGradient.h" 21#include "src/shaders/gradients/SkGradientShaderPriv.h" 22#include "src/shaders/gradients/SkLinearGradient.h" 23#include "src/shaders/gradients/SkRadialGradient.h" 24#include "src/shaders/gradients/SkSweepGradient.h" 25#include "src/shaders/gradients/SkTwoPointConicalGradient.h" 26 27enum GradientSerializationFlags { 28 // Bits 29:31 used for various boolean flags 29 kHasPosition_GSF = 0x80000000, 30 kHasLocalMatrix_GSF = 0x40000000, 31 kHasColorSpace_GSF = 0x20000000, 32 33 // Bits 12:28 unused 34 35 // Bits 8:11 for fTileMode 36 kTileModeShift_GSF = 8, 37 kTileModeMask_GSF = 0xF, 38 39 // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80) 40 kGradFlagsShift_GSF = 0, 41 kGradFlagsMask_GSF = 0xFF, 42}; 43 44void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const { 45 uint32_t flags = 0; 46 if (fPos) { 47 flags |= kHasPosition_GSF; 48 } 49 if (fLocalMatrix) { 50 flags |= kHasLocalMatrix_GSF; 51 } 52 sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr; 53 if (colorSpaceData) { 54 flags |= kHasColorSpace_GSF; 55 } 56 SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF); 57 flags |= ((unsigned)fTileMode << kTileModeShift_GSF); 58 SkASSERT(fGradFlags <= kGradFlagsMask_GSF); 59 flags |= (fGradFlags << kGradFlagsShift_GSF); 60 61 buffer.writeUInt(flags); 62 63 buffer.writeColor4fArray(fColors, fCount); 64 if (colorSpaceData) { 65 buffer.writeDataAsByteArray(colorSpaceData.get()); 66 } 67 if (fPos) { 68 buffer.writeScalarArray(fPos, fCount); 69 } 70 if (fLocalMatrix) { 71 buffer.writeMatrix(*fLocalMatrix); 72 } 73} 74 75template <int N, typename T, bool MEM_MOVE> 76static bool validate_array(SkReadBuffer& buffer, size_t count, SkSTArray<N, T, MEM_MOVE>* array) { 77 if (!buffer.validateCanReadN<T>(count)) { 78 return false; 79 } 80 81 array->resize_back(count); 82 return true; 83} 84 85bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) { 86 // New gradient format. Includes floating point color, color space, densely packed flags 87 uint32_t flags = buffer.readUInt(); 88 89 fTileMode = (SkTileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF); 90 fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF; 91 92 fCount = buffer.getArrayCount(); 93 94 if (!(validate_array(buffer, fCount, &fColorStorage) && 95 buffer.readColor4fArray(fColorStorage.begin(), fCount))) { 96 return false; 97 } 98 fColors = fColorStorage.begin(); 99 100 if (SkToBool(flags & kHasColorSpace_GSF)) { 101 sk_sp<SkData> data = buffer.readByteArrayAsData(); 102 fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr; 103 } else { 104 fColorSpace = nullptr; 105 } 106 if (SkToBool(flags & kHasPosition_GSF)) { 107 if (!(validate_array(buffer, fCount, &fPosStorage) && 108 buffer.readScalarArray(fPosStorage.begin(), fCount))) { 109 return false; 110 } 111 fPos = fPosStorage.begin(); 112 } else { 113 fPos = nullptr; 114 } 115 if (SkToBool(flags & kHasLocalMatrix_GSF)) { 116 fLocalMatrix = &fLocalMatrixStorage; 117 buffer.readMatrix(&fLocalMatrixStorage); 118 } else { 119 fLocalMatrix = nullptr; 120 } 121 return buffer.isValid(); 122} 123 124//////////////////////////////////////////////////////////////////////////////////////////// 125 126SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit) 127 : INHERITED(desc.fLocalMatrix) 128 , fPtsToUnit(ptsToUnit) 129 , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB()) 130 , fColorsAreOpaque(true) 131{ 132 fPtsToUnit.getType(); // Precache so reads are threadsafe. 133 SkASSERT(desc.fCount > 1); 134 135 fGradFlags = static_cast<uint8_t>(desc.fGradFlags); 136 137 SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount); 138 fTileMode = desc.fTileMode; 139 140 /* Note: we let the caller skip the first and/or last position. 141 i.e. pos[0] = 0.3, pos[1] = 0.7 142 In these cases, we insert entries to ensure that the final data 143 will be bracketed by [0, 1]. 144 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1 145 146 Thus colorCount (the caller's value, and fColorCount (our value) may 147 differ by up to 2. In the above example: 148 colorCount = 2 149 fColorCount = 4 150 */ 151 fColorCount = desc.fCount; 152 // check if we need to add in start and/or end position/colors 153 bool needsFirst = false; 154 bool needsLast = false; 155 if (desc.fPos) { 156 needsFirst = desc.fPos[0] != 0; 157 needsLast = desc.fPos[desc.fCount - 1] != SK_Scalar1; 158 fColorCount += needsFirst + needsLast; 159 } 160 161 size_t storageSize = fColorCount * (sizeof(SkColor4f) + (desc.fPos ? sizeof(SkScalar) : 0)); 162 fOrigColors4f = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize)); 163 fOrigPos = desc.fPos ? reinterpret_cast<SkScalar*>(fOrigColors4f + fColorCount) 164 : nullptr; 165 166 // Now copy over the colors, adding the dummies as needed 167 SkColor4f* origColors = fOrigColors4f; 168 if (needsFirst) { 169 *origColors++ = desc.fColors[0]; 170 } 171 for (int i = 0; i < desc.fCount; ++i) { 172 origColors[i] = desc.fColors[i]; 173 fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1); 174 } 175 if (needsLast) { 176 origColors += desc.fCount; 177 *origColors = desc.fColors[desc.fCount - 1]; 178 } 179 180 if (desc.fPos) { 181 SkScalar prev = 0; 182 SkScalar* origPosPtr = fOrigPos; 183 *origPosPtr++ = prev; // force the first pos to 0 184 185 int startIndex = needsFirst ? 0 : 1; 186 int count = desc.fCount + needsLast; 187 188 bool uniformStops = true; 189 const SkScalar uniformStep = desc.fPos[startIndex] - prev; 190 for (int i = startIndex; i < count; i++) { 191 // Pin the last value to 1.0, and make sure pos is monotonic. 192 auto curr = (i == desc.fCount) ? 1 : SkTPin(desc.fPos[i], prev, 1.0f); 193 uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev); 194 195 *origPosPtr++ = prev = curr; 196 } 197 198 // If the stops are uniform, treat them as implicit. 199 if (uniformStops) { 200 fOrigPos = nullptr; 201 } 202 } 203} 204 205SkGradientShaderBase::~SkGradientShaderBase() {} 206 207void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const { 208 Descriptor desc; 209 desc.fColors = fOrigColors4f; 210 desc.fColorSpace = fColorSpace; 211 desc.fPos = fOrigPos; 212 desc.fCount = fColorCount; 213 desc.fTileMode = fTileMode; 214 desc.fGradFlags = fGradFlags; 215 216 const SkMatrix& m = this->getLocalMatrix(); 217 desc.fLocalMatrix = m.isIdentity() ? nullptr : &m; 218 desc.flatten(buffer); 219} 220 221static void add_stop_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f Fs, SkPMColor4f Bs) { 222 (ctx->fs[0])[stop] = Fs.fR; 223 (ctx->fs[1])[stop] = Fs.fG; 224 (ctx->fs[2])[stop] = Fs.fB; 225 (ctx->fs[3])[stop] = Fs.fA; 226 227 (ctx->bs[0])[stop] = Bs.fR; 228 (ctx->bs[1])[stop] = Bs.fG; 229 (ctx->bs[2])[stop] = Bs.fB; 230 (ctx->bs[3])[stop] = Bs.fA; 231} 232 233static void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) { 234 add_stop_color(ctx, stop, { 0, 0, 0, 0 }, color); 235} 236 237// Calculate a factor F and a bias B so that color = F*t + B when t is in range of 238// the stop. Assume that the distance between stops is 1/gapCount. 239static void init_stop_evenly( 240 SkRasterPipeline_GradientCtx* ctx, float gapCount, size_t stop, SkPMColor4f c_l, SkPMColor4f c_r) { 241 // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar... 242 SkPMColor4f Fs = { 243 (c_r.fR - c_l.fR) * gapCount, 244 (c_r.fG - c_l.fG) * gapCount, 245 (c_r.fB - c_l.fB) * gapCount, 246 (c_r.fA - c_l.fA) * gapCount, 247 }; 248 SkPMColor4f Bs = { 249 c_l.fR - Fs.fR*(stop/gapCount), 250 c_l.fG - Fs.fG*(stop/gapCount), 251 c_l.fB - Fs.fB*(stop/gapCount), 252 c_l.fA - Fs.fA*(stop/gapCount), 253 }; 254 add_stop_color(ctx, stop, Fs, Bs); 255} 256 257// For each stop we calculate a bias B and a scale factor F, such that 258// for any t between stops n and n+1, the color we want is B[n] + F[n]*t. 259static void init_stop_pos( 260 SkRasterPipeline_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPMColor4f c_l, SkPMColor4f c_r) { 261 // See note about Clankium's old compiler in init_stop_evenly(). 262 SkPMColor4f Fs = { 263 (c_r.fR - c_l.fR) / (t_r - t_l), 264 (c_r.fG - c_l.fG) / (t_r - t_l), 265 (c_r.fB - c_l.fB) / (t_r - t_l), 266 (c_r.fA - c_l.fA) / (t_r - t_l), 267 }; 268 SkPMColor4f Bs = { 269 c_l.fR - Fs.fR*t_l, 270 c_l.fG - Fs.fG*t_l, 271 c_l.fB - Fs.fB*t_l, 272 c_l.fA - Fs.fA*t_l, 273 }; 274 ctx->ts[stop] = t_l; 275 add_stop_color(ctx, stop, Fs, Bs); 276} 277 278bool SkGradientShaderBase::onAppendStages(const SkStageRec& rec) const { 279 SkRasterPipeline* p = rec.fPipeline; 280 SkArenaAlloc* alloc = rec.fAlloc; 281 SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr; 282 283 SkMatrix matrix; 284 if (!this->computeTotalInverse(rec.fMatrixProvider.localToDevice(), rec.fLocalM, &matrix)) { 285 return false; 286 } 287 matrix.postConcat(fPtsToUnit); 288 289 SkRasterPipeline_<256> postPipeline; 290 291 p->append(SkRasterPipeline::seed_shader); 292 p->append_matrix(alloc, matrix); 293 this->appendGradientStages(alloc, p, &postPipeline); 294 295 switch(fTileMode) { 296 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x_1); break; 297 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x_1); break; 298 case SkTileMode::kDecal: 299 decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>(); 300 decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1); 301 // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask 302 p->append(SkRasterPipeline::decal_x, decal_ctx); 303 [[fallthrough]]; 304 305 case SkTileMode::kClamp: 306 if (!fOrigPos) { 307 // We clamp only when the stops are evenly spaced. 308 // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1. 309 // In that case, we must make sure we're using the general "gradient" stage, 310 // which is the only stage that will correctly handle unclamped t. 311 p->append(SkRasterPipeline::clamp_x_1); 312 } 313 break; 314 } 315 316 const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag; 317 318 // Transform all of the colors to destination color space 319 SkColor4fXformer xformedColors(fOrigColors4f, fColorCount, fColorSpace.get(), rec.fDstCS); 320 321 auto prepareColor = [premulGrad, &xformedColors](int i) { 322 SkColor4f c = xformedColors.fColors[i]; 323 return premulGrad ? c.premul() 324 : SkPMColor4f{ c.fR, c.fG, c.fB, c.fA }; 325 }; 326 327 // The two-stop case with stops at 0 and 1. 328 if (fColorCount == 2 && fOrigPos == nullptr) { 329 const SkPMColor4f c_l = prepareColor(0), 330 c_r = prepareColor(1); 331 332 // See F and B below. 333 auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>(); 334 (Sk4f::Load(c_r.vec()) - Sk4f::Load(c_l.vec())).store(ctx->f); 335 ( Sk4f::Load(c_l.vec())).store(ctx->b); 336 ctx->interpolatedInPremul = premulGrad; 337 338 p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, ctx); 339 } else { 340 auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>(); 341 ctx->interpolatedInPremul = premulGrad; 342 343 // Note: In order to handle clamps in search, the search assumes a stop conceptully placed 344 // at -inf. Therefore, the max number of stops is fColorCount+1. 345 for (int i = 0; i < 4; i++) { 346 // Allocate at least at for the AVX2 gather from a YMM register. 347 ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8)); 348 ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8)); 349 } 350 351 if (fOrigPos == nullptr) { 352 // Handle evenly distributed stops. 353 354 size_t stopCount = fColorCount; 355 float gapCount = stopCount - 1; 356 357 SkPMColor4f c_l = prepareColor(0); 358 for (size_t i = 0; i < stopCount - 1; i++) { 359 SkPMColor4f c_r = prepareColor(i + 1); 360 init_stop_evenly(ctx, gapCount, i, c_l, c_r); 361 c_l = c_r; 362 } 363 add_const_color(ctx, stopCount - 1, c_l); 364 365 ctx->stopCount = stopCount; 366 p->append(SkRasterPipeline::evenly_spaced_gradient, ctx); 367 } else { 368 // Handle arbitrary stops. 369 370 ctx->ts = alloc->makeArray<float>(fColorCount+1); 371 372 // Remove the default stops inserted by SkGradientShaderBase::SkGradientShaderBase 373 // because they are naturally handled by the search method. 374 int firstStop; 375 int lastStop; 376 if (fColorCount > 2) { 377 firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1; 378 lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1] 379 ? fColorCount - 1 : fColorCount - 2; 380 } else { 381 firstStop = 0; 382 lastStop = 1; 383 } 384 385 size_t stopCount = 0; 386 float t_l = fOrigPos[firstStop]; 387 SkPMColor4f c_l = prepareColor(firstStop); 388 add_const_color(ctx, stopCount++, c_l); 389 // N.B. lastStop is the index of the last stop, not one after. 390 for (int i = firstStop; i < lastStop; i++) { 391 float t_r = fOrigPos[i + 1]; 392 SkPMColor4f c_r = prepareColor(i + 1); 393 SkASSERT(t_l <= t_r); 394 if (t_l < t_r) { 395 init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r); 396 stopCount += 1; 397 } 398 t_l = t_r; 399 c_l = c_r; 400 } 401 402 ctx->ts[stopCount] = t_l; 403 add_const_color(ctx, stopCount++, c_l); 404 405 ctx->stopCount = stopCount; 406 p->append(SkRasterPipeline::gradient, ctx); 407 } 408 } 409 410 if (decal_ctx) { 411 p->append(SkRasterPipeline::check_decal_mask, decal_ctx); 412 } 413 414 if (!premulGrad && !this->colorsAreOpaque()) { 415 p->append(SkRasterPipeline::premul); 416 } 417 418 p->extend(postPipeline); 419 420 return true; 421} 422 423skvm::Color SkGradientShaderBase::onProgram(skvm::Builder* p, 424 skvm::Coord device, skvm::Coord local, 425 skvm::Color /*paint*/, 426 const SkMatrixProvider& mats, const SkMatrix* localM, 427 const SkColorInfo& dstInfo, 428 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const { 429 SkMatrix inv; 430 if (!this->computeTotalInverse(mats.localToDevice(), localM, &inv)) { 431 return {}; 432 } 433 inv.postConcat(fPtsToUnit); 434 inv.normalizePerspective(); 435 436 local = SkShaderBase::ApplyMatrix(p, inv, local, uniforms); 437 438 skvm::I32 mask = p->splat(~0); 439 skvm::F32 t = this->transformT(p,uniforms, local, &mask); 440 441 // Perhaps unexpectedly, clamping is handled naturally by our search, so we 442 // don't explicitly clamp t to [0,1]. That clamp would break hard stops 443 // right at 0 or 1 boundaries in kClamp mode. (kRepeat and kMirror always 444 // produce values in [0,1].) 445 switch(fTileMode) { 446 case SkTileMode::kClamp: 447 break; 448 449 case SkTileMode::kDecal: 450 mask &= (t == clamp01(t)); 451 break; 452 453 case SkTileMode::kRepeat: 454 t = fract(t); 455 break; 456 457 case SkTileMode::kMirror: { 458 // t = | (t-1) - 2*(floor( (t-1)*0.5 )) - 1 | 459 // {-A-} {--------B-------} 460 skvm::F32 A = t - 1.0f, 461 B = floor(A * 0.5f); 462 t = abs(A - (B + B) - 1.0f); 463 } break; 464 } 465 466 // Transform our colors as we want them interpolated, in dst color space, possibly premul. 467 SkImageInfo common = SkImageInfo::Make(fColorCount,1, kRGBA_F32_SkColorType 468 , kUnpremul_SkAlphaType), 469 src = common.makeColorSpace(fColorSpace), 470 dst = common.makeColorSpace(dstInfo.refColorSpace()); 471 if (fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) { 472 dst = dst.makeAlphaType(kPremul_SkAlphaType); 473 } 474 475 std::vector<float> rgba(4*fColorCount); // TODO: SkSTArray? 476 SkAssertResult(SkConvertPixels(dst, rgba.data(), dst.minRowBytes(), 477 src, fOrigColors4f, src.minRowBytes())); 478 479 // Transform our colors into a scale factor f and bias b such that for 480 // any t between stops i and i+1, the color we want is mad(t, f[i], b[i]). 481 using F4 = skvx::Vec<4,float>; 482 struct FB { F4 f,b; }; 483 skvm::Color color; 484 485 auto uniformF = [&](float x) { return p->uniformF(uniforms->pushF(x)); }; 486 487 if (fColorCount == 2) { 488 // 2-stop gradients have colors at 0 and 1, and so must be evenly spaced. 489 SkASSERT(fOrigPos == nullptr); 490 491 // With 2 stops, we upload the single FB as uniforms and interpolate directly with t. 492 F4 lo = F4::Load(rgba.data() + 0), 493 hi = F4::Load(rgba.data() + 4); 494 F4 F = hi - lo, 495 B = lo; 496 497 auto T = clamp01(t); 498 color = { 499 T * uniformF(F[0]) + uniformF(B[0]), 500 T * uniformF(F[1]) + uniformF(B[1]), 501 T * uniformF(F[2]) + uniformF(B[2]), 502 T * uniformF(F[3]) + uniformF(B[3]), 503 }; 504 } else { 505 // To handle clamps in search we add a conceptual stop at t=-inf, so we 506 // may need up to fColorCount+1 FBs and fColorCount t stops between them: 507 // 508 // FBs: [color 0] [color 0->1] [color 1->2] [color 2->3] ... 509 // stops: (-inf) t0 t1 t2 ... 510 // 511 // Both these arrays could end up shorter if any hard stops share the same t. 512 FB* fb = alloc->makeArrayDefault<FB>(fColorCount+1); 513 std::vector<float> stops; // TODO: SkSTArray? 514 stops.reserve(fColorCount); 515 516 // Here's our conceptual stop at t=-inf covering all t<=0, clamping to our first color. 517 float t_lo = this->getPos(0); 518 F4 color_lo = F4::Load(rgba.data()); 519 fb[0] = { 0.0f, color_lo }; 520 // N.B. No stops[] entry for this implicit -inf. 521 522 // Now the non-edge cases, calculating scale and bias between adjacent normal stops. 523 for (int i = 1; i < fColorCount; i++) { 524 float t_hi = this->getPos(i); 525 F4 color_hi = F4::Load(rgba.data() + 4*i); 526 527 // If t_lo == t_hi, we're on a hard stop, and transition immediately to the next color. 528 SkASSERT(t_lo <= t_hi); 529 if (t_lo < t_hi) { 530 F4 f = (color_hi - color_lo) / (t_hi - t_lo), 531 b = color_lo - f*t_lo; 532 stops.push_back(t_lo); 533 fb[stops.size()] = {f,b}; 534 } 535 536 t_lo = t_hi; 537 color_lo = color_hi; 538 } 539 // Anything >= our final t clamps to our final color. 540 stops.push_back(t_lo); 541 fb[stops.size()] = { 0.0f, color_lo }; 542 543 // We'll gather FBs from that array we just created. 544 skvm::Uniform fbs = uniforms->pushPtr(fb); 545 546 // Find the two stops we need to interpolate. 547 skvm::I32 ix; 548 if (fOrigPos == nullptr) { 549 // Evenly spaced stops... we can calculate ix directly. 550 // Of note: we need to clamp t and skip over that conceptual -inf stop we made up. 551 ix = trunc(clamp01(t) * uniformF(stops.size() - 1) + 1.0f); 552 } else { 553 // Starting ix at 0 bakes in our conceptual first stop at -inf. 554 // TODO: good place to experiment with a loop in skvm.... stops.size() can be huge. 555 ix = p->splat(0); 556 for (float stop : stops) { 557 // ix += (t >= stop) ? +1 : 0 ~~> 558 // ix -= (t >= stop) ? -1 : 0 559 ix -= (t >= uniformF(stop)); 560 } 561 // TODO: we could skip any of the default stops GradientShaderBase's ctor added 562 // to ensure the full [0,1] span is covered. This linear search doesn't need 563 // them for correctness, and it'd be up to two fewer stops to check. 564 // N.B. we do still need those stops for the fOrigPos == nullptr direct math path. 565 } 566 567 // A scale factor and bias for each lane, 8 total. 568 // TODO: simpler, faster, tidier to push 8 uniform pointers, one for each struct lane? 569 ix = shl(ix, 3); 570 skvm::F32 Fr = gatherF(fbs, ix + 0); 571 skvm::F32 Fg = gatherF(fbs, ix + 1); 572 skvm::F32 Fb = gatherF(fbs, ix + 2); 573 skvm::F32 Fa = gatherF(fbs, ix + 3); 574 575 skvm::F32 Br = gatherF(fbs, ix + 4); 576 skvm::F32 Bg = gatherF(fbs, ix + 5); 577 skvm::F32 Bb = gatherF(fbs, ix + 6); 578 skvm::F32 Ba = gatherF(fbs, ix + 7); 579 580 // This is what we've been building towards! 581 color = { 582 t * Fr + Br, 583 t * Fg + Bg, 584 t * Fb + Bb, 585 t * Fa + Ba, 586 }; 587 } 588 589 // If we interpolated unpremul, premul now to match our output convention. 590 if (0 == (fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) 591 && !fColorsAreOpaque) { 592 color = premul(color); 593 } 594 595 return { 596 pun_to_F32(mask & pun_to_I32(color.r)), 597 pun_to_F32(mask & pun_to_I32(color.g)), 598 pun_to_F32(mask & pun_to_I32(color.b)), 599 pun_to_F32(mask & pun_to_I32(color.a)), 600 }; 601} 602 603 604bool SkGradientShaderBase::isOpaque() const { 605 return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal); 606} 607 608static unsigned rounded_divide(unsigned numer, unsigned denom) { 609 return (numer + (denom >> 1)) / denom; 610} 611 612bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const { 613 // we just compute an average color. 614 // possibly we could weight this based on the proportional width for each color 615 // assuming they are not evenly distributed in the fPos array. 616 int r = 0; 617 int g = 0; 618 int b = 0; 619 const int n = fColorCount; 620 // TODO: use linear colors? 621 for (int i = 0; i < n; ++i) { 622 SkColor c = this->getLegacyColor(i); 623 r += SkColorGetR(c); 624 g += SkColorGetG(c); 625 b += SkColorGetB(c); 626 } 627 *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n)); 628 return true; 629} 630 631SkColor4fXformer::SkColor4fXformer(const SkColor4f* colors, int colorCount, 632 SkColorSpace* src, SkColorSpace* dst) { 633 fColors = colors; 634 635 if (dst && !SkColorSpace::Equals(src, dst)) { 636 fStorage.reset(colorCount); 637 638 auto info = SkImageInfo::Make(colorCount,1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType); 639 640 auto dstInfo = info.makeColorSpace(sk_ref_sp(dst)); 641 auto srcInfo = info.makeColorSpace(sk_ref_sp(src)); 642 SkAssertResult(SkConvertPixels(dstInfo, fStorage.begin(), info.minRowBytes(), 643 srcInfo, fColors , info.minRowBytes())); 644 645 fColors = fStorage.begin(); 646 } 647} 648 649void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const { 650 if (info) { 651 if (info->fColorCount >= fColorCount) { 652 if (info->fColors) { 653 for (int i = 0; i < fColorCount; ++i) { 654 info->fColors[i] = this->getLegacyColor(i); 655 } 656 } 657 if (info->fColorOffsets) { 658 for (int i = 0; i < fColorCount; ++i) { 659 info->fColorOffsets[i] = this->getPos(i); 660 } 661 } 662 } 663 info->fColorCount = fColorCount; 664 info->fTileMode = fTileMode; 665 info->fGradientFlags = fGradFlags; 666 } 667} 668 669/////////////////////////////////////////////////////////////////////////////// 670/////////////////////////////////////////////////////////////////////////////// 671 672// Return true if these parameters are valid/legal/safe to construct a gradient 673// 674static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count, 675 SkTileMode tileMode) { 676 return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount; 677} 678 679static void desc_init(SkGradientShaderBase::Descriptor* desc, 680 const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace, 681 const SkScalar pos[], int colorCount, 682 SkTileMode mode, uint32_t flags, const SkMatrix* localMatrix) { 683 SkASSERT(colorCount > 1); 684 685 desc->fColors = colors; 686 desc->fColorSpace = std::move(colorSpace); 687 desc->fPos = pos; 688 desc->fCount = colorCount; 689 desc->fTileMode = mode; 690 desc->fGradFlags = flags; 691 desc->fLocalMatrix = localMatrix; 692} 693 694static SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[], 695 int colorCount) { 696 // The gradient is a piecewise linear interpolation between colors. For a given interval, 697 // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that 698 // intervals average color. The overall average color is thus the sum of each piece. The thing 699 // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1. 700 Sk4f blend(0.0f); 701 for (int i = 0; i < colorCount - 1; ++i) { 702 // Calculate the average color for the interval between pos(i) and pos(i+1) 703 Sk4f c0 = Sk4f::Load(&colors[i]); 704 Sk4f c1 = Sk4f::Load(&colors[i + 1]); 705 706 // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1, 707 // so pos[i + 1] - pos[i] = 1/(colorCount-1) 708 SkScalar w; 709 if (pos) { 710 // Match position fixing in SkGradientShader's constructor, clamping positions outside 711 // [0, 1] and forcing the sequence to be monotonic 712 SkScalar p0 = SkTPin(pos[i], 0.f, 1.f); 713 SkScalar p1 = SkTPin(pos[i + 1], p0, 1.f); 714 w = p1 - p0; 715 716 // And account for any implicit intervals at the start or end of the positions 717 if (i == 0) { 718 if (p0 > 0.0f) { 719 // The first color is fixed between p = 0 to pos[0], so 0.5*(ci + cj)*(pj - pi) 720 // becomes 0.5*(c + c)*(pj - 0) = c * pj 721 Sk4f c = Sk4f::Load(&colors[0]); 722 blend += p0 * c; 723 } 724 } 725 if (i == colorCount - 2) { 726 if (p1 < 1.f) { 727 // The last color is fixed between pos[n-1] to p = 1, so 0.5*(ci + cj)*(pj - pi) 728 // becomes 0.5*(c + c)*(1 - pi) = c * (1 - pi) 729 Sk4f c = Sk4f::Load(&colors[colorCount - 1]); 730 blend += (1.f - p1) * c; 731 } 732 } 733 } else { 734 w = 1.f / (colorCount - 1); 735 } 736 737 blend += 0.5f * w * (c1 + c0); 738 } 739 740 SkColor4f avg; 741 blend.store(&avg); 742 return avg; 743} 744 745// The default SkScalarNearlyZero threshold of .0024 is too big and causes regressions for svg 746// gradients defined in the wild. 747static constexpr SkScalar kDegenerateThreshold = SK_Scalar1 / (1 << 15); 748 749// Except for special circumstances of clamped gradients, every gradient shape--when degenerate-- 750// can be mapped to the same fallbacks. The specific shape factories must account for special 751// clamped conditions separately, this will always return the last color for clamped gradients. 752static sk_sp<SkShader> make_degenerate_gradient(const SkColor4f colors[], const SkScalar pos[], 753 int colorCount, sk_sp<SkColorSpace> colorSpace, 754 SkTileMode mode) { 755 switch(mode) { 756 case SkTileMode::kDecal: 757 // normally this would reject the area outside of the interpolation region, so since 758 // inside region is empty when the radii are equal, the entire draw region is empty 759 return SkShaders::Empty(); 760 case SkTileMode::kRepeat: 761 case SkTileMode::kMirror: 762 // repeat and mirror are treated the same: the border colors are never visible, 763 // but approximate the final color as infinite repetitions of the colors, so 764 // it can be represented as the average color of the gradient. 765 return SkShaders::Color( 766 average_gradient_color(colors, pos, colorCount), std::move(colorSpace)); 767 case SkTileMode::kClamp: 768 // Depending on how the gradient shape degenerates, there may be a more specialized 769 // fallback representation for the factories to use, but this is a reasonable default. 770 return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace)); 771 } 772 SkDEBUGFAIL("Should not be reached"); 773 return nullptr; 774} 775 776// assumes colors is SkColor4f* and pos is SkScalar* 777#define EXPAND_1_COLOR(count) \ 778 SkColor4f tmp[2]; \ 779 do { \ 780 if (1 == count) { \ 781 tmp[0] = tmp[1] = colors[0]; \ 782 colors = tmp; \ 783 pos = nullptr; \ 784 count = 2; \ 785 } \ 786 } while (0) 787 788struct ColorStopOptimizer { 789 ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos, int count, SkTileMode mode) 790 : fColors(colors) 791 , fPos(pos) 792 , fCount(count) { 793 794 if (!pos || count != 3) { 795 return; 796 } 797 798 if (SkScalarNearlyEqual(pos[0], 0.0f) && 799 SkScalarNearlyEqual(pos[1], 0.0f) && 800 SkScalarNearlyEqual(pos[2], 1.0f)) { 801 802 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode || 803 colors[0] == colors[1]) { 804 805 // Ignore the leftmost color/pos. 806 fColors += 1; 807 fPos += 1; 808 fCount = 2; 809 } 810 } else if (SkScalarNearlyEqual(pos[0], 0.0f) && 811 SkScalarNearlyEqual(pos[1], 1.0f) && 812 SkScalarNearlyEqual(pos[2], 1.0f)) { 813 814 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode || 815 colors[1] == colors[2]) { 816 817 // Ignore the rightmost color/pos. 818 fCount = 2; 819 } 820 } 821 } 822 823 const SkColor4f* fColors; 824 const SkScalar* fPos; 825 int fCount; 826}; 827 828struct ColorConverter { 829 ColorConverter(const SkColor* colors, int count) { 830 const float ONE_OVER_255 = 1.f / 255; 831 for (int i = 0; i < count; ++i) { 832 fColors4f.push_back({ 833 SkColorGetR(colors[i]) * ONE_OVER_255, 834 SkColorGetG(colors[i]) * ONE_OVER_255, 835 SkColorGetB(colors[i]) * ONE_OVER_255, 836 SkColorGetA(colors[i]) * ONE_OVER_255 }); 837 } 838 } 839 840 SkSTArray<2, SkColor4f, true> fColors4f; 841}; 842 843sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2], 844 const SkColor colors[], 845 const SkScalar pos[], int colorCount, 846 SkTileMode mode, 847 uint32_t flags, 848 const SkMatrix* localMatrix) { 849 ColorConverter converter(colors, colorCount); 850 return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags, 851 localMatrix); 852} 853 854sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2], 855 const SkColor4f colors[], 856 sk_sp<SkColorSpace> colorSpace, 857 const SkScalar pos[], int colorCount, 858 SkTileMode mode, 859 uint32_t flags, 860 const SkMatrix* localMatrix) { 861 if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) { 862 return nullptr; 863 } 864 if (!valid_grad(colors, pos, colorCount, mode)) { 865 return nullptr; 866 } 867 if (1 == colorCount) { 868 return SkShaders::Color(colors[0], std::move(colorSpace)); 869 } 870 if (localMatrix && !localMatrix->invert(nullptr)) { 871 return nullptr; 872 } 873 874 if (SkScalarNearlyZero((pts[1] - pts[0]).length(), kDegenerateThreshold)) { 875 // Degenerate gradient, the only tricky complication is when in clamp mode, the limit of 876 // the gradient approaches two half planes of solid color (first and last). However, they 877 // are divided by the line perpendicular to the start and end point, which becomes undefined 878 // once start and end are exactly the same, so just use the end color for a stable solution. 879 return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode); 880 } 881 882 ColorStopOptimizer opt(colors, pos, colorCount, mode); 883 884 SkGradientShaderBase::Descriptor desc; 885 desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, 886 localMatrix); 887 return sk_make_sp<SkLinearGradient>(pts, desc); 888} 889 890sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius, 891 const SkColor colors[], 892 const SkScalar pos[], int colorCount, 893 SkTileMode mode, 894 uint32_t flags, 895 const SkMatrix* localMatrix) { 896 ColorConverter converter(colors, colorCount); 897 return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, 898 flags, localMatrix); 899} 900 901sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius, 902 const SkColor4f colors[], 903 sk_sp<SkColorSpace> colorSpace, 904 const SkScalar pos[], int colorCount, 905 SkTileMode mode, 906 uint32_t flags, 907 const SkMatrix* localMatrix) { 908 if (radius < 0) { 909 return nullptr; 910 } 911 if (!valid_grad(colors, pos, colorCount, mode)) { 912 return nullptr; 913 } 914 if (1 == colorCount) { 915 return SkShaders::Color(colors[0], std::move(colorSpace)); 916 } 917 if (localMatrix && !localMatrix->invert(nullptr)) { 918 return nullptr; 919 } 920 921 if (SkScalarNearlyZero(radius, kDegenerateThreshold)) { 922 // Degenerate gradient optimization, and no special logic needed for clamped radial gradient 923 return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode); 924 } 925 926 ColorStopOptimizer opt(colors, pos, colorCount, mode); 927 928 SkGradientShaderBase::Descriptor desc; 929 desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, 930 localMatrix); 931 return sk_make_sp<SkRadialGradient>(center, radius, desc); 932} 933 934sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start, 935 SkScalar startRadius, 936 const SkPoint& end, 937 SkScalar endRadius, 938 const SkColor colors[], 939 const SkScalar pos[], 940 int colorCount, 941 SkTileMode mode, 942 uint32_t flags, 943 const SkMatrix* localMatrix) { 944 ColorConverter converter(colors, colorCount); 945 return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(), 946 nullptr, pos, colorCount, mode, flags, localMatrix); 947} 948 949sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start, 950 SkScalar startRadius, 951 const SkPoint& end, 952 SkScalar endRadius, 953 const SkColor4f colors[], 954 sk_sp<SkColorSpace> colorSpace, 955 const SkScalar pos[], 956 int colorCount, 957 SkTileMode mode, 958 uint32_t flags, 959 const SkMatrix* localMatrix) { 960 if (startRadius < 0 || endRadius < 0) { 961 return nullptr; 962 } 963 if (!valid_grad(colors, pos, colorCount, mode)) { 964 return nullptr; 965 } 966 if (SkScalarNearlyZero((start - end).length(), kDegenerateThreshold)) { 967 // If the center positions are the same, then the gradient is the radial variant of a 2 pt 968 // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate 969 // (startRadius == endRadius). 970 if (SkScalarNearlyEqual(startRadius, endRadius, kDegenerateThreshold)) { 971 // Degenerate case, where the interpolation region area approaches zero. The proper 972 // behavior depends on the tile mode, which is consistent with the default degenerate 973 // gradient behavior, except when mode = clamp and the radii > 0. 974 if (mode == SkTileMode::kClamp && endRadius > kDegenerateThreshold) { 975 // The interpolation region becomes an infinitely thin ring at the radius, so the 976 // final gradient will be the first color repeated from p=0 to 1, and then a hard 977 // stop switching to the last color at p=1. 978 static constexpr SkScalar circlePos[3] = {0, 1, 1}; 979 SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]}; 980 return MakeRadial(start, endRadius, reColors, std::move(colorSpace), 981 circlePos, 3, mode, flags, localMatrix); 982 } else { 983 // Otherwise use the default degenerate case 984 return make_degenerate_gradient( 985 colors, pos, colorCount, std::move(colorSpace), mode); 986 } 987 } else if (SkScalarNearlyZero(startRadius, kDegenerateThreshold)) { 988 // We can treat this gradient as radial, which is faster. If we got here, we know 989 // that endRadius is not equal to 0, so this produces a meaningful gradient 990 return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount, 991 mode, flags, localMatrix); 992 } 993 // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the 994 // regular 2pt constructor. 995 } 996 997 if (localMatrix && !localMatrix->invert(nullptr)) { 998 return nullptr; 999 } 1000 EXPAND_1_COLOR(colorCount); 1001 1002 ColorStopOptimizer opt(colors, pos, colorCount, mode); 1003 1004 SkGradientShaderBase::Descriptor desc; 1005 desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, 1006 localMatrix); 1007 return SkTwoPointConicalGradient::Create(start, startRadius, end, endRadius, desc); 1008} 1009 1010sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, 1011 const SkColor colors[], 1012 const SkScalar pos[], 1013 int colorCount, 1014 SkTileMode mode, 1015 SkScalar startAngle, 1016 SkScalar endAngle, 1017 uint32_t flags, 1018 const SkMatrix* localMatrix) { 1019 ColorConverter converter(colors, colorCount); 1020 return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount, 1021 mode, startAngle, endAngle, flags, localMatrix); 1022} 1023 1024sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, 1025 const SkColor4f colors[], 1026 sk_sp<SkColorSpace> colorSpace, 1027 const SkScalar pos[], 1028 int colorCount, 1029 SkTileMode mode, 1030 SkScalar startAngle, 1031 SkScalar endAngle, 1032 uint32_t flags, 1033 const SkMatrix* localMatrix) { 1034 if (!valid_grad(colors, pos, colorCount, mode)) { 1035 return nullptr; 1036 } 1037 if (1 == colorCount) { 1038 return SkShaders::Color(colors[0], std::move(colorSpace)); 1039 } 1040 if (!SkScalarIsFinite(startAngle) || !SkScalarIsFinite(endAngle) || startAngle > endAngle) { 1041 return nullptr; 1042 } 1043 if (localMatrix && !localMatrix->invert(nullptr)) { 1044 return nullptr; 1045 } 1046 1047 if (SkScalarNearlyEqual(startAngle, endAngle, kDegenerateThreshold)) { 1048 // Degenerate gradient, which should follow default degenerate behavior unless it is 1049 // clamped and the angle is greater than 0. 1050 if (mode == SkTileMode::kClamp && endAngle > kDegenerateThreshold) { 1051 // In this case, the first color is repeated from 0 to the angle, then a hardstop 1052 // switches to the last color (all other colors are compressed to the infinitely thin 1053 // interpolation region). 1054 static constexpr SkScalar clampPos[3] = {0, 1, 1}; 1055 SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]}; 1056 return MakeSweep(cx, cy, reColors, std::move(colorSpace), clampPos, 3, mode, 0, 1057 endAngle, flags, localMatrix); 1058 } else { 1059 return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode); 1060 } 1061 } 1062 1063 if (startAngle <= 0 && endAngle >= 360) { 1064 // If the t-range includes [0,1], then we can always use clamping (presumably faster). 1065 mode = SkTileMode::kClamp; 1066 } 1067 1068 ColorStopOptimizer opt(colors, pos, colorCount, mode); 1069 1070 SkGradientShaderBase::Descriptor desc; 1071 desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, 1072 localMatrix); 1073 1074 const SkScalar t0 = startAngle / 360, 1075 t1 = endAngle / 360; 1076 1077 return sk_make_sp<SkSweepGradient>(SkPoint::Make(cx, cy), t0, t1, desc); 1078} 1079 1080void SkGradientShader::RegisterFlattenables() { 1081 SK_REGISTER_FLATTENABLE(SkLinearGradient); 1082 SK_REGISTER_FLATTENABLE(SkRadialGradient); 1083 SK_REGISTER_FLATTENABLE(SkSweepGradient); 1084 SK_REGISTER_FLATTENABLE(SkTwoPointConicalGradient); 1085} 1086