1/* 2 * Copyright 2013 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 "include/effects/SkPerlinNoiseShader.h" 9 10#include "include/core/SkBitmap.h" 11#include "include/core/SkColorFilter.h" 12#include "include/core/SkShader.h" 13#include "include/core/SkString.h" 14#include "include/core/SkUnPreMultiply.h" 15#include "include/private/SkTPin.h" 16#include "src/core/SkArenaAlloc.h" 17#include "src/core/SkMatrixProvider.h" 18#include "src/core/SkReadBuffer.h" 19#include "src/core/SkVM.h" 20#include "src/core/SkWriteBuffer.h" 21 22#if SK_SUPPORT_GPU 23#include "include/gpu/GrRecordingContext.h" 24#include "src/gpu/GrFragmentProcessor.h" 25#include "src/gpu/GrRecordingContextPriv.h" 26#include "src/gpu/SkGr.h" 27#include "src/gpu/effects/GrMatrixEffect.h" 28#include "src/gpu/effects/GrTextureEffect.h" 29#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" 30#include "src/gpu/glsl/GrGLSLProgramDataManager.h" 31#include "src/gpu/glsl/GrGLSLUniformHandler.h" 32#endif 33 34static const int kBlockSize = 256; 35static const int kBlockMask = kBlockSize - 1; 36static const int kPerlinNoise = 4096; 37static const int kRandMaximum = SK_MaxS32; // 2**31 - 1 38 39class SkPerlinNoiseShaderImpl : public SkShaderBase { 40public: 41 struct StitchData { 42 StitchData() 43 : fWidth(0) 44 , fWrapX(0) 45 , fHeight(0) 46 , fWrapY(0) 47 {} 48 49 StitchData(SkScalar w, SkScalar h) 50 : fWidth(std::min(SkScalarRoundToInt(w), SK_MaxS32 - kPerlinNoise)) 51 , fWrapX(kPerlinNoise + fWidth) 52 , fHeight(std::min(SkScalarRoundToInt(h), SK_MaxS32 - kPerlinNoise)) 53 , fWrapY(kPerlinNoise + fHeight) {} 54 55 bool operator==(const StitchData& other) const { 56 return fWidth == other.fWidth && 57 fWrapX == other.fWrapX && 58 fHeight == other.fHeight && 59 fWrapY == other.fWrapY; 60 } 61 62 int fWidth; // How much to subtract to wrap for stitching. 63 int fWrapX; // Minimum value to wrap. 64 int fHeight; 65 int fWrapY; 66 }; 67 68 struct PaintingData { 69 PaintingData(const SkISize& tileSize, SkScalar seed, 70 SkScalar baseFrequencyX, SkScalar baseFrequencyY, 71 const SkMatrix& matrix) 72 { 73 SkVector tileVec; 74 matrix.mapVector(SkIntToScalar(tileSize.fWidth), SkIntToScalar(tileSize.fHeight), 75 &tileVec); 76 77 SkSize scale; 78 if (!matrix.decomposeScale(&scale, nullptr)) { 79 scale.set(SK_ScalarNearlyZero, SK_ScalarNearlyZero); 80 } 81 fBaseFrequency.set(baseFrequencyX * SkScalarInvert(scale.width()), 82 baseFrequencyY * SkScalarInvert(scale.height())); 83 fTileSize.set(SkScalarRoundToInt(tileVec.fX), SkScalarRoundToInt(tileVec.fY)); 84 this->init(seed); 85 if (!fTileSize.isEmpty()) { 86 this->stitch(); 87 } 88 89 #if SK_SUPPORT_GPU 90 SkImageInfo info = SkImageInfo::MakeA8(kBlockSize, 1); 91 fPermutationsBitmap.installPixels(info, fLatticeSelector, info.minRowBytes()); 92 fPermutationsBitmap.setImmutable(); 93 94 info = SkImageInfo::Make(kBlockSize, 4, kRGBA_8888_SkColorType, kPremul_SkAlphaType); 95 fNoiseBitmap.installPixels(info, fNoise[0][0], info.minRowBytes()); 96 fNoiseBitmap.setImmutable(); 97 #endif 98 } 99 100 #if SK_SUPPORT_GPU 101 PaintingData(const PaintingData& that) 102 : fSeed(that.fSeed) 103 , fTileSize(that.fTileSize) 104 , fBaseFrequency(that.fBaseFrequency) 105 , fStitchDataInit(that.fStitchDataInit) 106 , fPermutationsBitmap(that.fPermutationsBitmap) 107 , fNoiseBitmap(that.fNoiseBitmap) { 108 memcpy(fLatticeSelector, that.fLatticeSelector, sizeof(fLatticeSelector)); 109 memcpy(fNoise, that.fNoise, sizeof(fNoise)); 110 memcpy(fGradient, that.fGradient, sizeof(fGradient)); 111 } 112 #endif 113 114 int fSeed; 115 uint8_t fLatticeSelector[kBlockSize]; 116 uint16_t fNoise[4][kBlockSize][2]; 117 SkPoint fGradient[4][kBlockSize]; 118 SkISize fTileSize; 119 SkVector fBaseFrequency; 120 StitchData fStitchDataInit; 121 122 private: 123 124 #if SK_SUPPORT_GPU 125 SkBitmap fPermutationsBitmap; 126 SkBitmap fNoiseBitmap; 127 #endif 128 129 inline int random() { 130 // See https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement 131 // m = kRandMaximum, 2**31 - 1 (2147483647) 132 static constexpr int kRandAmplitude = 16807; // 7**5; primitive root of m 133 static constexpr int kRandQ = 127773; // m / a 134 static constexpr int kRandR = 2836; // m % a 135 136 int result = kRandAmplitude * (fSeed % kRandQ) - kRandR * (fSeed / kRandQ); 137 if (result <= 0) { 138 result += kRandMaximum; 139 } 140 fSeed = result; 141 return result; 142 } 143 144 // Only called once. Could be part of the constructor. 145 void init(SkScalar seed) 146 { 147 // According to the SVG spec, we must truncate (not round) the seed value. 148 fSeed = SkScalarTruncToInt(seed); 149 // The seed value clamp to the range [1, kRandMaximum - 1]. 150 if (fSeed <= 0) { 151 fSeed = -(fSeed % (kRandMaximum - 1)) + 1; 152 } 153 if (fSeed > kRandMaximum - 1) { 154 fSeed = kRandMaximum - 1; 155 } 156 for (int channel = 0; channel < 4; ++channel) { 157 for (int i = 0; i < kBlockSize; ++i) { 158 fLatticeSelector[i] = i; 159 fNoise[channel][i][0] = (random() % (2 * kBlockSize)); 160 fNoise[channel][i][1] = (random() % (2 * kBlockSize)); 161 } 162 } 163 for (int i = kBlockSize - 1; i > 0; --i) { 164 int k = fLatticeSelector[i]; 165 int j = random() % kBlockSize; 166 SkASSERT(j >= 0); 167 SkASSERT(j < kBlockSize); 168 fLatticeSelector[i] = fLatticeSelector[j]; 169 fLatticeSelector[j] = k; 170 } 171 172 // Perform the permutations now 173 { 174 // Copy noise data 175 uint16_t noise[4][kBlockSize][2]; 176 for (int i = 0; i < kBlockSize; ++i) { 177 for (int channel = 0; channel < 4; ++channel) { 178 for (int j = 0; j < 2; ++j) { 179 noise[channel][i][j] = fNoise[channel][i][j]; 180 } 181 } 182 } 183 // Do permutations on noise data 184 for (int i = 0; i < kBlockSize; ++i) { 185 for (int channel = 0; channel < 4; ++channel) { 186 for (int j = 0; j < 2; ++j) { 187 fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j]; 188 } 189 } 190 } 191 } 192 193 // Half of the largest possible value for 16 bit unsigned int 194 static constexpr SkScalar kHalfMax16bits = 32767.5f; 195 196 // Compute gradients from permutated noise data 197 static constexpr SkScalar kInvBlockSizef = 1.0 / SkIntToScalar(kBlockSize); 198 for (int channel = 0; channel < 4; ++channel) { 199 for (int i = 0; i < kBlockSize; ++i) { 200 fGradient[channel][i] = SkPoint::Make( 201 (fNoise[channel][i][0] - kBlockSize) * kInvBlockSizef, 202 (fNoise[channel][i][1] - kBlockSize) * kInvBlockSizef); 203 fGradient[channel][i].normalize(); 204 // Put the normalized gradient back into the noise data 205 fNoise[channel][i][0] = 206 SkScalarRoundToInt((fGradient[channel][i].fX + 1) * kHalfMax16bits); 207 fNoise[channel][i][1] = 208 SkScalarRoundToInt((fGradient[channel][i].fY + 1) * kHalfMax16bits); 209 } 210 } 211 } 212 213 // Only called once. Could be part of the constructor. 214 void stitch() { 215 SkScalar tileWidth = SkIntToScalar(fTileSize.width()); 216 SkScalar tileHeight = SkIntToScalar(fTileSize.height()); 217 SkASSERT(tileWidth > 0 && tileHeight > 0); 218 // When stitching tiled turbulence, the frequencies must be adjusted 219 // so that the tile borders will be continuous. 220 if (fBaseFrequency.fX) { 221 SkScalar lowFrequencx = 222 SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; 223 SkScalar highFrequencx = 224 SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; 225 // BaseFrequency should be non-negative according to the standard. 226 // lowFrequencx can be 0 if fBaseFrequency.fX is very small. 227 if (sk_ieee_float_divide(fBaseFrequency.fX, lowFrequencx) < highFrequencx / fBaseFrequency.fX) { 228 fBaseFrequency.fX = lowFrequencx; 229 } else { 230 fBaseFrequency.fX = highFrequencx; 231 } 232 } 233 if (fBaseFrequency.fY) { 234 SkScalar lowFrequency = 235 SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; 236 SkScalar highFrequency = 237 SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; 238 // lowFrequency can be 0 if fBaseFrequency.fY is very small. 239 if (sk_ieee_float_divide(fBaseFrequency.fY, lowFrequency) < highFrequency / fBaseFrequency.fY) { 240 fBaseFrequency.fY = lowFrequency; 241 } else { 242 fBaseFrequency.fY = highFrequency; 243 } 244 } 245 // Set up TurbulenceInitial stitch values. 246 fStitchDataInit = StitchData(tileWidth * fBaseFrequency.fX, 247 tileHeight * fBaseFrequency.fY); 248 } 249 250 public: 251 252#if SK_SUPPORT_GPU 253 const SkBitmap& getPermutationsBitmap() const { return fPermutationsBitmap; } 254 255 const SkBitmap& getNoiseBitmap() const { return fNoiseBitmap; } 256#endif 257 }; 258 259 /** 260 * About the noise types : the difference between the first 2 is just minor tweaks to the 261 * algorithm, they're not 2 entirely different noises. The output looks different, but once the 262 * noise is generated in the [1, -1] range, the output is brought back in the [0, 1] range by 263 * doing : 264 * kFractalNoise_Type : noise * 0.5 + 0.5 265 * kTurbulence_Type : abs(noise) 266 * Very little differences between the 2 types, although you can tell the difference visually. 267 */ 268 enum Type { 269 kFractalNoise_Type, 270 kTurbulence_Type, 271 kLast_Type = kTurbulence_Type 272 }; 273 274 static const int kMaxOctaves = 255; // numOctaves must be <= 0 and <= kMaxOctaves 275 276 SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::Type type, SkScalar baseFrequencyX, 277 SkScalar baseFrequencyY, int numOctaves, SkScalar seed, 278 const SkISize* tileSize); 279 280 class PerlinNoiseShaderContext : public Context { 281 public: 282 PerlinNoiseShaderContext(const SkPerlinNoiseShaderImpl& shader, const ContextRec&); 283 284 void shadeSpan(int x, int y, SkPMColor[], int count) override; 285 286 private: 287 SkPMColor shade(const SkPoint& point, StitchData& stitchData) const; 288 SkScalar calculateTurbulenceValueForPoint( 289 int channel, 290 StitchData& stitchData, const SkPoint& point) const; 291 SkScalar noise2D(int channel, 292 const StitchData& stitchData, const SkPoint& noiseVector) const; 293 294 SkMatrix fMatrix; 295 PaintingData fPaintingData; 296 297 using INHERITED = Context; 298 }; 299 300#if SK_SUPPORT_GPU 301 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override; 302#endif 303 304 skvm::Color onProgram(skvm::Builder*, 305 skvm::Coord, skvm::Coord, skvm::Color, 306 const SkMatrixProvider&, const SkMatrix*, const SkColorInfo&, 307 skvm::Uniforms*, SkArenaAlloc*) const override { 308 // TODO? 309 return {}; 310 } 311 312protected: 313 void flatten(SkWriteBuffer&) const override; 314#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT 315 Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; 316#endif 317 318private: 319 SK_FLATTENABLE_HOOKS(SkPerlinNoiseShaderImpl) 320 321 const SkPerlinNoiseShaderImpl::Type fType; 322 const SkScalar fBaseFrequencyX; 323 const SkScalar fBaseFrequencyY; 324 const int fNumOctaves; 325 const SkScalar fSeed; 326 const SkISize fTileSize; 327 const bool fStitchTiles; 328 329 friend class ::SkPerlinNoiseShader; 330 331 using INHERITED = SkShaderBase; 332}; 333 334namespace { 335 336// noiseValue is the color component's value (or color) 337// limitValue is the maximum perlin noise array index value allowed 338// newValue is the current noise dimension (either width or height) 339inline int checkNoise(int noiseValue, int limitValue, int newValue) { 340 // If the noise value would bring us out of bounds of the current noise array while we are 341 // stiching noise tiles together, wrap the noise around the current dimension of the noise to 342 // stay within the array bounds in a continuous fashion (so that tiling lines are not visible) 343 if (noiseValue >= limitValue) { 344 noiseValue -= newValue; 345 } 346 return noiseValue; 347} 348 349inline SkScalar smoothCurve(SkScalar t) { 350 return t * t * (3 - 2 * t); 351} 352 353} // end namespace 354 355SkPerlinNoiseShaderImpl::SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::Type type, 356 SkScalar baseFrequencyX, 357 SkScalar baseFrequencyY, 358 int numOctaves, 359 SkScalar seed, 360 const SkISize* tileSize) 361 : fType(type) 362 , fBaseFrequencyX(baseFrequencyX) 363 , fBaseFrequencyY(baseFrequencyY) 364 , fNumOctaves(numOctaves > kMaxOctaves ? kMaxOctaves : numOctaves/*[0,255] octaves allowed*/) 365 , fSeed(seed) 366 , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize) 367 , fStitchTiles(!fTileSize.isEmpty()) 368{ 369 SkASSERT(numOctaves >= 0 && numOctaves <= kMaxOctaves); 370 SkASSERT(fBaseFrequencyX >= 0); 371 SkASSERT(fBaseFrequencyY >= 0); 372} 373 374sk_sp<SkFlattenable> SkPerlinNoiseShaderImpl::CreateProc(SkReadBuffer& buffer) { 375 Type type = buffer.read32LE(kLast_Type); 376 377 SkScalar freqX = buffer.readScalar(); 378 SkScalar freqY = buffer.readScalar(); 379 int octaves = buffer.read32LE<int>(kMaxOctaves); 380 381 SkScalar seed = buffer.readScalar(); 382 SkISize tileSize; 383 tileSize.fWidth = buffer.readInt(); 384 tileSize.fHeight = buffer.readInt(); 385 386 switch (type) { 387 case kFractalNoise_Type: 388 return SkPerlinNoiseShader::MakeFractalNoise(freqX, freqY, octaves, seed, &tileSize); 389 case kTurbulence_Type: 390 return SkPerlinNoiseShader::MakeTurbulence(freqX, freqY, octaves, seed, &tileSize); 391 default: 392 // Really shouldn't get here b.c. of earlier check on type 393 buffer.validate(false); 394 return nullptr; 395 } 396} 397 398void SkPerlinNoiseShaderImpl::flatten(SkWriteBuffer& buffer) const { 399 buffer.writeInt((int) fType); 400 buffer.writeScalar(fBaseFrequencyX); 401 buffer.writeScalar(fBaseFrequencyY); 402 buffer.writeInt(fNumOctaves); 403 buffer.writeScalar(fSeed); 404 buffer.writeInt(fTileSize.fWidth); 405 buffer.writeInt(fTileSize.fHeight); 406} 407 408SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::noise2D( 409 int channel, const StitchData& stitchData, const SkPoint& noiseVector) const { 410 struct Noise { 411 int noisePositionIntegerValue; 412 int nextNoisePositionIntegerValue; 413 SkScalar noisePositionFractionValue; 414 Noise(SkScalar component) 415 { 416 SkScalar position = component + kPerlinNoise; 417 noisePositionIntegerValue = SkScalarFloorToInt(position); 418 noisePositionFractionValue = position - SkIntToScalar(noisePositionIntegerValue); 419 nextNoisePositionIntegerValue = noisePositionIntegerValue + 1; 420 } 421 }; 422 Noise noiseX(noiseVector.x()); 423 Noise noiseY(noiseVector.y()); 424 SkScalar u, v; 425 const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader); 426 // If stitching, adjust lattice points accordingly. 427 if (perlinNoiseShader.fStitchTiles) { 428 noiseX.noisePositionIntegerValue = 429 checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth); 430 noiseY.noisePositionIntegerValue = 431 checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight); 432 noiseX.nextNoisePositionIntegerValue = 433 checkNoise(noiseX.nextNoisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth); 434 noiseY.nextNoisePositionIntegerValue = 435 checkNoise(noiseY.nextNoisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight); 436 } 437 noiseX.noisePositionIntegerValue &= kBlockMask; 438 noiseY.noisePositionIntegerValue &= kBlockMask; 439 noiseX.nextNoisePositionIntegerValue &= kBlockMask; 440 noiseY.nextNoisePositionIntegerValue &= kBlockMask; 441 int i = fPaintingData.fLatticeSelector[noiseX.noisePositionIntegerValue]; 442 int j = fPaintingData.fLatticeSelector[noiseX.nextNoisePositionIntegerValue]; 443 int b00 = (i + noiseY.noisePositionIntegerValue) & kBlockMask; 444 int b10 = (j + noiseY.noisePositionIntegerValue) & kBlockMask; 445 int b01 = (i + noiseY.nextNoisePositionIntegerValue) & kBlockMask; 446 int b11 = (j + noiseY.nextNoisePositionIntegerValue) & kBlockMask; 447 SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue); 448 SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue); 449 450 if (sx < 0 || sy < 0 || sx > 1 || sy > 1) { 451 return 0; // Check for pathological inputs. 452 } 453 454 // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement 455 SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue, 456 noiseY.noisePositionFractionValue); // Offset (0,0) 457 u = fPaintingData.fGradient[channel][b00].dot(fractionValue); 458 fractionValue.fX -= SK_Scalar1; // Offset (-1,0) 459 v = fPaintingData.fGradient[channel][b10].dot(fractionValue); 460 SkScalar a = SkScalarInterp(u, v, sx); 461 fractionValue.fY -= SK_Scalar1; // Offset (-1,-1) 462 v = fPaintingData.fGradient[channel][b11].dot(fractionValue); 463 fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1) 464 u = fPaintingData.fGradient[channel][b01].dot(fractionValue); 465 SkScalar b = SkScalarInterp(u, v, sx); 466 return SkScalarInterp(a, b, sy); 467} 468 469SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint( 470 int channel, StitchData& stitchData, const SkPoint& point) const { 471 const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader); 472 if (perlinNoiseShader.fStitchTiles) { 473 // Set up TurbulenceInitial stitch values. 474 stitchData = fPaintingData.fStitchDataInit; 475 } 476 SkScalar turbulenceFunctionResult = 0; 477 SkPoint noiseVector(SkPoint::Make(point.x() * fPaintingData.fBaseFrequency.fX, 478 point.y() * fPaintingData.fBaseFrequency.fY)); 479 SkScalar ratio = SK_Scalar1; 480 for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) { 481 SkScalar noise = noise2D(channel, stitchData, noiseVector); 482 SkScalar numer = (perlinNoiseShader.fType == kFractalNoise_Type) ? 483 noise : SkScalarAbs(noise); 484 turbulenceFunctionResult += numer / ratio; 485 noiseVector.fX *= 2; 486 noiseVector.fY *= 2; 487 ratio *= 2; 488 if (perlinNoiseShader.fStitchTiles) { 489 // Update stitch values 490 stitchData = StitchData(SkIntToScalar(stitchData.fWidth) * 2, 491 SkIntToScalar(stitchData.fHeight) * 2); 492 } 493 } 494 495 // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2 496 // by fractalNoise and (turbulenceFunctionResult) by turbulence. 497 if (perlinNoiseShader.fType == kFractalNoise_Type) { 498 turbulenceFunctionResult = SkScalarHalf(turbulenceFunctionResult + 1); 499 } 500 501 if (channel == 3) { // Scale alpha by paint value 502 turbulenceFunctionResult *= SkIntToScalar(getPaintAlpha()) / 255; 503 } 504 505 // Clamp result 506 return SkTPin(turbulenceFunctionResult, 0.0f, SK_Scalar1); 507} 508 509//////////////////////////////////////////////////////////////////////////////////////////////////// 510 511SkPMColor SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::shade( 512 const SkPoint& point, StitchData& stitchData) const { 513 SkPoint newPoint; 514 fMatrix.mapPoints(&newPoint, &point, 1); 515 newPoint.fX = SkScalarRoundToScalar(newPoint.fX); 516 newPoint.fY = SkScalarRoundToScalar(newPoint.fY); 517 518 U8CPU rgba[4]; 519 for (int channel = 3; channel >= 0; --channel) { 520 SkScalar value; 521 value = calculateTurbulenceValueForPoint(channel, stitchData, newPoint); 522 rgba[channel] = SkScalarFloorToInt(255 * value); 523 } 524 return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]); 525} 526 527#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT 528SkShaderBase::Context* SkPerlinNoiseShaderImpl::onMakeContext(const ContextRec& rec, 529 SkArenaAlloc* alloc) const { 530 // should we pay attention to rec's device-colorspace? 531 return alloc->make<PerlinNoiseShaderContext>(*this, rec); 532} 533#endif 534 535static inline SkMatrix total_matrix(const SkShaderBase::ContextRec& rec, 536 const SkShaderBase& shader) { 537 SkMatrix matrix = SkMatrix::Concat(*rec.fMatrix, shader.getLocalMatrix()); 538 if (rec.fLocalMatrix) { 539 matrix.preConcat(*rec.fLocalMatrix); 540 } 541 542 return matrix; 543} 544 545SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::PerlinNoiseShaderContext( 546 const SkPerlinNoiseShaderImpl& shader, const ContextRec& rec) 547 : INHERITED(shader, rec) 548 , fMatrix(total_matrix(rec, shader)) // used for temp storage, adjusted below 549 , fPaintingData(shader.fTileSize, shader.fSeed, shader.fBaseFrequencyX, 550 shader.fBaseFrequencyY, fMatrix) 551{ 552 // This (1,1) translation is due to WebKit's 1 based coordinates for the noise 553 // (as opposed to 0 based, usually). The same adjustment is in the setData() function. 554 fMatrix.setTranslate(-fMatrix.getTranslateX() + SK_Scalar1, 555 -fMatrix.getTranslateY() + SK_Scalar1); 556} 557 558void SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::shadeSpan( 559 int x, int y, SkPMColor result[], int count) { 560 SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y)); 561 StitchData stitchData; 562 for (int i = 0; i < count; ++i) { 563 result[i] = shade(point, stitchData); 564 point.fX += SK_Scalar1; 565 } 566} 567 568///////////////////////////////////////////////////////////////////// 569 570#if SK_SUPPORT_GPU 571 572class GrPerlinNoise2Effect : public GrFragmentProcessor { 573public: 574 static std::unique_ptr<GrFragmentProcessor> Make( 575 SkPerlinNoiseShaderImpl::Type type, 576 int numOctaves, 577 bool stitchTiles, 578 std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData, 579 GrSurfaceProxyView permutationsView, 580 GrSurfaceProxyView noiseView, 581 const SkMatrix& matrix, 582 const GrCaps& caps) { 583 static constexpr GrSamplerState kRepeatXSampler = {GrSamplerState::WrapMode::kRepeat, 584 GrSamplerState::WrapMode::kClamp, 585 GrSamplerState::Filter::kNearest}; 586 auto permutationsFP = 587 GrTextureEffect::Make(std::move(permutationsView), kPremul_SkAlphaType, 588 SkMatrix::I(), kRepeatXSampler, caps); 589 auto noiseFP = GrTextureEffect::Make(std::move(noiseView), kPremul_SkAlphaType, 590 SkMatrix::I(), kRepeatXSampler, caps); 591 592 return GrMatrixEffect::Make(matrix, std::unique_ptr<GrFragmentProcessor>( 593 new GrPerlinNoise2Effect(type, numOctaves, stitchTiles, std::move(paintingData), 594 std::move(permutationsFP), std::move(noiseFP)))); 595 } 596 597 const char* name() const override { return "PerlinNoise"; } 598 599 SkString getShaderDfxInfo() const override; 600 601 std::unique_ptr<GrFragmentProcessor> clone() const override { 602 return std::unique_ptr<GrFragmentProcessor>(new GrPerlinNoise2Effect(*this)); 603 } 604 605 const SkPerlinNoiseShaderImpl::StitchData& stitchData() const { return fPaintingData->fStitchDataInit; } 606 607 SkPerlinNoiseShaderImpl::Type type() const { return fType; } 608 bool stitchTiles() const { return fStitchTiles; } 609 const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; } 610 int numOctaves() const { return fNumOctaves; } 611 612private: 613 class Impl : public ProgramImpl { 614 public: 615 void emitCode(EmitArgs&) override; 616 617 private: 618 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; 619 620 GrGLSLProgramDataManager::UniformHandle fStitchDataUni; 621 GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni; 622 }; 623 624 std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override { 625 return std::make_unique<Impl>(); 626 } 627 628 void onAddToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override; 629 630 bool onIsEqual(const GrFragmentProcessor& sBase) const override { 631 const GrPerlinNoise2Effect& s = sBase.cast<GrPerlinNoise2Effect>(); 632 return fType == s.fType && 633 fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency && 634 fNumOctaves == s.fNumOctaves && 635 fStitchTiles == s.fStitchTiles && 636 fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit; 637 } 638 639 GrPerlinNoise2Effect(SkPerlinNoiseShaderImpl::Type type, 640 int numOctaves, 641 bool stitchTiles, 642 std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData, 643 std::unique_ptr<GrFragmentProcessor> permutationsFP, 644 std::unique_ptr<GrFragmentProcessor> noiseFP) 645 : INHERITED(kGrPerlinNoise2Effect_ClassID, kNone_OptimizationFlags) 646 , fType(type) 647 , fNumOctaves(numOctaves) 648 , fStitchTiles(stitchTiles) 649 , fPaintingData(std::move(paintingData)) { 650 this->registerChild(std::move(permutationsFP), SkSL::SampleUsage::Explicit()); 651 this->registerChild(std::move(noiseFP), SkSL::SampleUsage::Explicit()); 652 this->setUsesSampleCoordsDirectly(); 653 } 654 655 GrPerlinNoise2Effect(const GrPerlinNoise2Effect& that) 656 : INHERITED(that) 657 , fType(that.fType) 658 , fNumOctaves(that.fNumOctaves) 659 , fStitchTiles(that.fStitchTiles) 660 , fPaintingData(new SkPerlinNoiseShaderImpl::PaintingData(*that.fPaintingData)) {} 661 662 GR_DECLARE_FRAGMENT_PROCESSOR_TEST 663 664 SkPerlinNoiseShaderImpl::Type fType; 665 int fNumOctaves; 666 bool fStitchTiles; 667 668 std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> fPaintingData; 669 670 using INHERITED = GrFragmentProcessor; 671}; 672 673///////////////////////////////////////////////////////////////////// 674GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrPerlinNoise2Effect); 675 676#if GR_TEST_UTILS 677std::unique_ptr<GrFragmentProcessor> GrPerlinNoise2Effect::TestCreate(GrProcessorTestData* d) { 678 int numOctaves = d->fRandom->nextRangeU(2, 10); 679 bool stitchTiles = d->fRandom->nextBool(); 680 SkScalar seed = SkIntToScalar(d->fRandom->nextU()); 681 SkISize tileSize; 682 tileSize.fWidth = d->fRandom->nextRangeU(4, 4096); 683 tileSize.fHeight = d->fRandom->nextRangeU(4, 4096); 684 SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f, 0.99f); 685 SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f, 0.99f); 686 687 sk_sp<SkShader> shader(d->fRandom->nextBool() ? 688 SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed, 689 stitchTiles ? &tileSize : nullptr) : 690 SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed, 691 stitchTiles ? &tileSize : nullptr)); 692 693 GrTest::TestAsFPArgs asFPArgs(d); 694 return as_SB(shader)->asFragmentProcessor(asFPArgs.args()); 695} 696#endif 697 698void GrPerlinNoise2Effect::Impl::emitCode(EmitArgs& args) { 699 const GrPerlinNoise2Effect& pne = args.fFp.cast<GrPerlinNoise2Effect>(); 700 701 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 702 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 703 704 fBaseFrequencyUni = uniformHandler->addUniform(&pne, kFragment_GrShaderFlag, kHalf2_GrSLType, 705 "baseFrequency"); 706 const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni); 707 708 const char* stitchDataUni = nullptr; 709 if (pne.stitchTiles()) { 710 fStitchDataUni = uniformHandler->addUniform(&pne, kFragment_GrShaderFlag, kHalf2_GrSLType, 711 "stitchData"); 712 stitchDataUni = uniformHandler->getUniformCStr(fStitchDataUni); 713 } 714 715 // Add noise function 716 const GrShaderVar gPerlinNoiseArgs[] = {{"chanCoord", kHalf_GrSLType }, 717 {"noiseVec ", kHalf2_GrSLType}}; 718 719 const GrShaderVar gPerlinNoiseStitchArgs[] = {{"chanCoord" , kHalf_GrSLType }, 720 {"noiseVec" , kHalf2_GrSLType}, 721 {"stitchData", kHalf2_GrSLType}}; 722 723 SkString noiseCode; 724 725 noiseCode.append( 726 R"(half4 floorVal; 727 floorVal.xy = floor(noiseVec); 728 floorVal.zw = floorVal.xy + half2(1); 729 half2 fractVal = fract(noiseVec); 730 // smooth curve : t^2*(3 - 2*t) 731 half2 noiseSmooth = fractVal*fractVal*(half2(3) - 2*fractVal);)"); 732 733 // Adjust frequencies if we're stitching tiles 734 if (pne.stitchTiles()) { 735 noiseCode.append( 736 R"(if (floorVal.x >= stitchData.x) { floorVal.x -= stitchData.x; }; 737 if (floorVal.y >= stitchData.y) { floorVal.y -= stitchData.y; }; 738 if (floorVal.z >= stitchData.x) { floorVal.z -= stitchData.x; }; 739 if (floorVal.w >= stitchData.y) { floorVal.w -= stitchData.y; };)"); 740 } 741 742 // NOTE: We need to explicitly pass half4(1) as input color here, because the helper function 743 // can't see fInputColor (which is "_input" in the FP's outer function). skbug.com/10506 744 SkString sampleX = this->invokeChild(0, "half4(1)", args, "half2(floorVal.x, 0.5)"); 745 SkString sampleY = this->invokeChild(0, "half4(1)", args, "half2(floorVal.z, 0.5)"); 746 noiseCode.appendf("half2 latticeIdx = half2(%s.a, %s.a);", sampleX.c_str(), sampleY.c_str()); 747 748#if defined(SK_BUILD_FOR_ANDROID) 749 // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3). 750 // The issue is that colors aren't accurate enough on Tegra devices. For example, if an 8 bit 751 // value of 124 (or 0.486275 here) is entered, we can get a texture value of 123.513725 752 // (or 0.484368 here). The following rounding operation prevents these precision issues from 753 // affecting the result of the noise by making sure that we only have multiples of 1/255. 754 // (Note that 1/255 is about 0.003921569, which is the value used here). 755 noiseCode.append( 756 "latticeIdx = floor(latticeIdx * half2(255.0) + half2(0.5)) * half2(0.003921569);"); 757#endif 758 759 // Get (x,y) coordinates with the permutated x 760 noiseCode.append("half4 bcoords = 256*latticeIdx.xyxy + floorVal.yyww;"); 761 762 noiseCode.append("half2 uv;"); 763 764 // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a 765 // [-1,1] vector and perform a dot product between that vector and the provided vector. 766 // Save it as a string because we will repeat it 4x. 767 static constexpr const char* inc8bit = "0.00390625"; // 1.0 / 256.0 768 SkString dotLattice = 769 SkStringPrintf("dot((lattice.ga + lattice.rb*%s)*2 - half2(1), fractVal)", inc8bit); 770 771 SkString sampleA = this->invokeChild(1, "half4(1)", args, "half2(bcoords.x, chanCoord)"); 772 SkString sampleB = this->invokeChild(1, "half4(1)", args, "half2(bcoords.y, chanCoord)"); 773 SkString sampleC = this->invokeChild(1, "half4(1)", args, "half2(bcoords.w, chanCoord)"); 774 SkString sampleD = this->invokeChild(1, "half4(1)", args, "half2(bcoords.z, chanCoord)"); 775 776 // Compute u, at offset (0,0) 777 noiseCode.appendf("half4 lattice = %s;", sampleA.c_str()); 778 noiseCode.appendf("uv.x = %s;", dotLattice.c_str()); 779 780 // Compute v, at offset (-1,0) 781 noiseCode.append("fractVal.x -= 1.0;"); 782 noiseCode.appendf("lattice = %s;", sampleB.c_str()); 783 noiseCode.appendf("uv.y = %s;", dotLattice.c_str()); 784 785 // Compute 'a' as a linear interpolation of 'u' and 'v' 786 noiseCode.append("half2 ab;"); 787 noiseCode.append("ab.x = mix(uv.x, uv.y, noiseSmooth.x);"); 788 789 // Compute v, at offset (-1,-1) 790 noiseCode.append("fractVal.y -= 1.0;"); 791 noiseCode.appendf("lattice = %s;", sampleC.c_str()); 792 noiseCode.appendf("uv.y = %s;", dotLattice.c_str()); 793 794 // Compute u, at offset (0,-1) 795 noiseCode.append("fractVal.x += 1.0;"); 796 noiseCode.appendf("lattice = %s;", sampleD.c_str()); 797 noiseCode.appendf("uv.x = %s;", dotLattice.c_str()); 798 799 // Compute 'b' as a linear interpolation of 'u' and 'v' 800 noiseCode.append("ab.y = mix(uv.x, uv.y, noiseSmooth.x);"); 801 // Compute the noise as a linear interpolation of 'a' and 'b' 802 noiseCode.append("return mix(ab.x, ab.y, noiseSmooth.y);"); 803 804 SkString noiseFuncName = fragBuilder->getMangledFunctionName("noiseFuncName"); 805 if (pne.stitchTiles()) { 806 fragBuilder->emitFunction(kHalf_GrSLType, noiseFuncName.c_str(), 807 {gPerlinNoiseStitchArgs, SK_ARRAY_COUNT(gPerlinNoiseStitchArgs)}, 808 noiseCode.c_str()); 809 } else { 810 fragBuilder->emitFunction(kHalf_GrSLType, noiseFuncName.c_str(), 811 {gPerlinNoiseArgs, SK_ARRAY_COUNT(gPerlinNoiseArgs)}, 812 noiseCode.c_str()); 813 } 814 815 // There are rounding errors if the floor operation is not performed here 816 fragBuilder->codeAppendf("half2 noiseVec = half2(floor(%s.xy) * %s);", 817 args.fSampleCoord, baseFrequencyUni); 818 819 // Clear the color accumulator 820 fragBuilder->codeAppendf("half4 color = half4(0);"); 821 822 if (pne.stitchTiles()) { 823 // Set up TurbulenceInitial stitch values. 824 fragBuilder->codeAppendf("half2 stitchData = %s;", stitchDataUni); 825 } 826 827 fragBuilder->codeAppendf("half ratio = 1.0;"); 828 829 // Loop over all octaves 830 fragBuilder->codeAppendf("for (int octave = 0; octave < %d; ++octave) {", pne.numOctaves()); 831 fragBuilder->codeAppendf(" color += "); 832 if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) { 833 fragBuilder->codeAppend("abs("); 834 } 835 836 // There are 4 lines, put y coords at center of each. 837 static constexpr const char* chanCoordR = "0.5"; 838 static constexpr const char* chanCoordG = "1.5"; 839 static constexpr const char* chanCoordB = "2.5"; 840 static constexpr const char* chanCoordA = "3.5"; 841 if (pne.stitchTiles()) { 842 fragBuilder->codeAppendf(R"( 843 half4(%s(%s, noiseVec, stitchData), %s(%s, noiseVec, stitchData), 844 %s(%s, noiseVec, stitchData), %s(%s, noiseVec, stitchData)))", 845 noiseFuncName.c_str(), chanCoordR, 846 noiseFuncName.c_str(), chanCoordG, 847 noiseFuncName.c_str(), chanCoordB, 848 noiseFuncName.c_str(), chanCoordA); 849 } else { 850 fragBuilder->codeAppendf(R"( 851 half4(%s(%s, noiseVec), %s(%s, noiseVec), 852 %s(%s, noiseVec), %s(%s, noiseVec)))", 853 noiseFuncName.c_str(), chanCoordR, 854 noiseFuncName.c_str(), chanCoordG, 855 noiseFuncName.c_str(), chanCoordB, 856 noiseFuncName.c_str(), chanCoordA); 857 } 858 if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) { 859 fragBuilder->codeAppend(")"); // end of "abs(" 860 } 861 fragBuilder->codeAppend(" * ratio;"); 862 863 fragBuilder->codeAppend(R"(noiseVec *= half2(2.0); 864 ratio *= 0.5;)"); 865 866 if (pne.stitchTiles()) { 867 fragBuilder->codeAppend("stitchData *= half2(2.0);"); 868 } 869 fragBuilder->codeAppend("}"); // end of the for loop on octaves 870 871 if (pne.type() == SkPerlinNoiseShaderImpl::kFractalNoise_Type) { 872 // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2 873 // by fractalNoise and (turbulenceFunctionResult) by turbulence. 874 fragBuilder->codeAppendf("color = color * half4(0.5) + half4(0.5);"); 875 } 876 877 // Clamp values 878 fragBuilder->codeAppendf("color = saturate(color);"); 879 880 // Pre-multiply the result 881 fragBuilder->codeAppendf("return half4(color.rgb * color.aaa, color.a);"); 882} 883 884void GrPerlinNoise2Effect::Impl::onSetData(const GrGLSLProgramDataManager& pdman, 885 const GrFragmentProcessor& processor) { 886 const GrPerlinNoise2Effect& turbulence = processor.cast<GrPerlinNoise2Effect>(); 887 888 const SkVector& baseFrequency = turbulence.baseFrequency(); 889 pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY); 890 891 if (turbulence.stitchTiles()) { 892 const SkPerlinNoiseShaderImpl::StitchData& stitchData = turbulence.stitchData(); 893 pdman.set2f(fStitchDataUni, 894 SkIntToScalar(stitchData.fWidth), 895 SkIntToScalar(stitchData.fHeight)); 896 } 897} 898 899SkString GrPerlinNoise2Effect::getShaderDfxInfo() const 900{ 901 SkString format; 902 format.printf("ShaderDfx_GrPerlinNoise2Effect_%d_%d_%d", fNumOctaves, fType, fStitchTiles); 903 return format; 904} 905 906void GrPerlinNoise2Effect::onAddToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const { 907 uint32_t key = fNumOctaves; 908 key = key << 3; // Make room for next 3 bits 909 switch (fType) { 910 case SkPerlinNoiseShaderImpl::kFractalNoise_Type: 911 key |= 0x1; 912 break; 913 case SkPerlinNoiseShaderImpl::kTurbulence_Type: 914 key |= 0x2; 915 break; 916 default: 917 // leave key at 0 918 break; 919 } 920 if (fStitchTiles) { 921 key |= 0x4; // Flip the 3rd bit if tile stitching is on 922 } 923 b->add32(key); 924} 925 926///////////////////////////////////////////////////////////////////// 927 928std::unique_ptr<GrFragmentProcessor> SkPerlinNoiseShaderImpl::asFragmentProcessor( 929 const GrFPArgs& args) const { 930 SkASSERT(args.fContext); 931 932 const auto localMatrix = this->totalLocalMatrix(args.fPreLocalMatrix); 933 const auto paintMatrix = SkMatrix::Concat(args.fMatrixProvider.localToDevice(), *localMatrix); 934 935 // Either we don't stitch tiles, either we have a valid tile size 936 SkASSERT(!fStitchTiles || !fTileSize.isEmpty()); 937 938 std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData = 939 std::make_unique<SkPerlinNoiseShaderImpl::PaintingData>(fTileSize, 940 fSeed, 941 fBaseFrequencyX, 942 fBaseFrequencyY, 943 paintMatrix); 944 945 SkMatrix m = args.fMatrixProvider.localToDevice(); 946 m.setTranslateX(-localMatrix->getTranslateX() + SK_Scalar1); 947 m.setTranslateY(-localMatrix->getTranslateY() + SK_Scalar1); 948 949 auto context = args.fContext; 950 951 if (0 == fNumOctaves) { 952 if (kFractalNoise_Type == fType) { 953 // Incoming alpha is assumed to be 1. So emit rgba = (1/4, 1/4, 1/4, 1/2) 954 // TODO: Either treat the output of this shader as sRGB or allow client to specify a 955 // color space of the noise. Either way, this case (and the GLSL) need to convert to 956 // the destination. 957 return GrFragmentProcessor::MakeColor(SkPMColor4f::FromBytes_RGBA(0x80404040)); 958 } 959 // Emit zero. 960 return GrFragmentProcessor::MakeColor(SK_PMColor4fTRANSPARENT); 961 } 962 963 const SkBitmap& permutationsBitmap = paintingData->getPermutationsBitmap(); 964 const SkBitmap& noiseBitmap = paintingData->getNoiseBitmap(); 965 966 auto permutationsView = std::get<0>(GrMakeCachedBitmapProxyView(context, permutationsBitmap)); 967 auto noiseView = std::get<0>(GrMakeCachedBitmapProxyView(context, noiseBitmap)); 968 969 if (permutationsView && noiseView) { 970 return GrPerlinNoise2Effect::Make(fType, 971 fNumOctaves, 972 fStitchTiles, 973 std::move(paintingData), 974 std::move(permutationsView), 975 std::move(noiseView), 976 m, 977 *context->priv().caps()); 978 } 979 return nullptr; 980} 981 982#endif 983 984/////////////////////////////////////////////////////////////////////////////////////////////////// 985 986static bool valid_input(SkScalar baseX, SkScalar baseY, int numOctaves, const SkISize* tileSize, 987 SkScalar seed) { 988 if (!(baseX >= 0 && baseY >= 0)) { 989 return false; 990 } 991 if (!(numOctaves >= 0 && numOctaves <= SkPerlinNoiseShaderImpl::kMaxOctaves)) { 992 return false; 993 } 994 if (tileSize && !(tileSize->width() >= 0 && tileSize->height() >= 0)) { 995 return false; 996 } 997 if (!SkScalarIsFinite(seed)) { 998 return false; 999 } 1000 return true; 1001} 1002 1003sk_sp<SkShader> SkPerlinNoiseShader::MakeFractalNoise(SkScalar baseFrequencyX, 1004 SkScalar baseFrequencyY, 1005 int numOctaves, SkScalar seed, 1006 const SkISize* tileSize) { 1007 if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) { 1008 return nullptr; 1009 } 1010 return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kFractalNoise_Type, 1011 baseFrequencyX, baseFrequencyY, numOctaves, seed, 1012 tileSize)); 1013} 1014 1015sk_sp<SkShader> SkPerlinNoiseShader::MakeTurbulence(SkScalar baseFrequencyX, 1016 SkScalar baseFrequencyY, 1017 int numOctaves, SkScalar seed, 1018 const SkISize* tileSize) { 1019 if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) { 1020 return nullptr; 1021 } 1022 return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kTurbulence_Type, 1023 baseFrequencyX, baseFrequencyY, numOctaves, seed, 1024 tileSize)); 1025} 1026 1027void SkPerlinNoiseShader::RegisterFlattenables() { 1028 SK_REGISTER_FLATTENABLE(SkPerlinNoiseShaderImpl); 1029} 1030