/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkPerlinNoiseShaderImpl_DEFINED #define SkPerlinNoiseShaderImpl_DEFINED #include "include/core/SkAlphaType.h" #include "include/core/SkBitmap.h" #include "include/core/SkColorType.h" #include "include/core/SkFlattenable.h" #include "include/core/SkImageInfo.h" #include "include/core/SkPoint.h" #include "include/core/SkScalar.h" #include "include/core/SkSize.h" #include "include/core/SkTypes.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkMath.h" #include "include/private/base/SkOnce.h" #include "src/shaders/SkShaderBase.h" #include #include #include #include class SkReadBuffer; enum class SkPerlinNoiseShaderType; struct SkStageRec; class SkWriteBuffer; class SkPerlinNoiseShader : public SkShaderBase { private: static constexpr int kBlockSize = 256; static constexpr int kBlockMask = kBlockSize - 1; static constexpr int kPerlinNoise = 4096; static constexpr int kRandMaximum = SK_MaxS32; // 2**31 - 1 public: struct StitchData { StitchData() = default; StitchData(SkScalar w, SkScalar h) : fWidth(std::min(SkScalarRoundToInt(w), SK_MaxS32 - kPerlinNoise)) , fWrapX(kPerlinNoise + fWidth) , fHeight(std::min(SkScalarRoundToInt(h), SK_MaxS32 - kPerlinNoise)) , fWrapY(kPerlinNoise + fHeight) {} bool operator==(const StitchData& other) const { return fWidth == other.fWidth && fWrapX == other.fWrapX && fHeight == other.fHeight && fWrapY == other.fWrapY; } int fWidth = 0; // How much to subtract to wrap for stitching. int fWrapX = 0; // Minimum value to wrap. int fHeight = 0; int fWrapY = 0; }; struct PaintingData { PaintingData(const SkISize& tileSize, SkScalar seed, SkScalar baseFrequencyX, SkScalar baseFrequencyY) { fBaseFrequency.set(baseFrequencyX, baseFrequencyY); fTileSize.set(SkScalarRoundToInt(tileSize.fWidth), SkScalarRoundToInt(tileSize.fHeight)); this->init(seed); if (!fTileSize.isEmpty()) { this->stitch(); } } void generateBitmaps() { SkImageInfo info = SkImageInfo::MakeA8(kBlockSize, 1); fPermutationsBitmap.installPixels(info, fLatticeSelector, info.minRowBytes()); fPermutationsBitmap.setImmutable(); info = SkImageInfo::Make(kBlockSize, 4, kRGBA_8888_SkColorType, kPremul_SkAlphaType); fNoiseBitmap.installPixels(info, fNoise[0][0], info.minRowBytes()); fNoiseBitmap.setImmutable(); } PaintingData(const PaintingData& that) : fSeed(that.fSeed) , fTileSize(that.fTileSize) , fBaseFrequency(that.fBaseFrequency) , fStitchDataInit(that.fStitchDataInit) , fPermutationsBitmap(that.fPermutationsBitmap) , fNoiseBitmap(that.fNoiseBitmap) { memcpy(fLatticeSelector, that.fLatticeSelector, sizeof(fLatticeSelector)); memcpy(fNoise, that.fNoise, sizeof(fNoise)); } int fSeed; uint8_t fLatticeSelector[kBlockSize]; uint16_t fNoise[4][kBlockSize][2]; SkISize fTileSize; SkVector fBaseFrequency; StitchData fStitchDataInit; private: SkBitmap fPermutationsBitmap; SkBitmap fNoiseBitmap; int random() { // See https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement // m = kRandMaximum, 2**31 - 1 (2147483647) static constexpr int kRandAmplitude = 16807; // 7**5; primitive root of m static constexpr int kRandQ = 127773; // m / a static constexpr int kRandR = 2836; // m % a int result = kRandAmplitude * (fSeed % kRandQ) - kRandR * (fSeed / kRandQ); if (result <= 0) { result += kRandMaximum; } fSeed = result; return result; } // Only called once. Could be part of the constructor. void init(SkScalar seed) { // According to the SVG spec, we must truncate (not round) the seed value. fSeed = SkScalarTruncToInt(seed); // The seed value clamp to the range [1, kRandMaximum - 1]. if (fSeed <= 0) { fSeed = -(fSeed % (kRandMaximum - 1)) + 1; } if (fSeed > kRandMaximum - 1) { fSeed = kRandMaximum - 1; } for (int channel = 0; channel < 4; ++channel) { for (int i = 0; i < kBlockSize; ++i) { fLatticeSelector[i] = i; fNoise[channel][i][0] = (random() % (2 * kBlockSize)); fNoise[channel][i][1] = (random() % (2 * kBlockSize)); } } for (int i = kBlockSize - 1; i > 0; --i) { int k = fLatticeSelector[i]; int j = random() % kBlockSize; SkASSERT(j >= 0); SkASSERT(j < kBlockSize); fLatticeSelector[i] = fLatticeSelector[j]; fLatticeSelector[j] = k; } // Perform the permutations now { // Copy noise data uint16_t noise[4][kBlockSize][2]; for (int i = 0; i < kBlockSize; ++i) { for (int channel = 0; channel < 4; ++channel) { for (int j = 0; j < 2; ++j) { noise[channel][i][j] = fNoise[channel][i][j]; } } } // Do permutations on noise data for (int i = 0; i < kBlockSize; ++i) { for (int channel = 0; channel < 4; ++channel) { for (int j = 0; j < 2; ++j) { fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j]; } } } } // Half of the largest possible value for 16 bit unsigned int static constexpr SkScalar kHalfMax16bits = 32767.5f; // Compute gradients from permuted noise data static constexpr SkScalar kInvBlockSizef = 1.0 / SkIntToScalar(kBlockSize); for (int channel = 0; channel < 4; ++channel) { for (int i = 0; i < kBlockSize; ++i) { SkPoint gradient = SkPoint::Make((fNoise[channel][i][0] - kBlockSize) * kInvBlockSizef, (fNoise[channel][i][1] - kBlockSize) * kInvBlockSizef); gradient.normalize(); // Put the normalized gradient back into the noise data fNoise[channel][i][0] = SkScalarRoundToInt((gradient.fX + 1) * kHalfMax16bits); fNoise[channel][i][1] = SkScalarRoundToInt((gradient.fY + 1) * kHalfMax16bits); } } } // Only called once. Could be part of the constructor. void stitch() { SkScalar tileWidth = SkIntToScalar(fTileSize.width()); SkScalar tileHeight = SkIntToScalar(fTileSize.height()); SkASSERT(tileWidth > 0 && tileHeight > 0); // When stitching tiled turbulence, the frequencies must be adjusted // so that the tile borders will be continuous. if (fBaseFrequency.fX) { SkScalar lowFrequencx = SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; SkScalar highFrequencx = SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; // BaseFrequency should be non-negative according to the standard. // lowFrequencx can be 0 if fBaseFrequency.fX is very small. if (sk_ieee_float_divide(fBaseFrequency.fX, lowFrequencx) < highFrequencx / fBaseFrequency.fX) { fBaseFrequency.fX = lowFrequencx; } else { fBaseFrequency.fX = highFrequencx; } } if (fBaseFrequency.fY) { SkScalar lowFrequency = SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; SkScalar highFrequency = SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; // lowFrequency can be 0 if fBaseFrequency.fY is very small. if (sk_ieee_float_divide(fBaseFrequency.fY, lowFrequency) < highFrequency / fBaseFrequency.fY) { fBaseFrequency.fY = lowFrequency; } else { fBaseFrequency.fY = highFrequency; } } fStitchDataInit = StitchData(tileWidth * fBaseFrequency.fX, tileHeight * fBaseFrequency.fY); } public: const SkBitmap& getPermutationsBitmap() const { SkASSERT(!fPermutationsBitmap.drawsNothing()); return fPermutationsBitmap; } const SkBitmap& getNoiseBitmap() const { SkASSERT(!fNoiseBitmap.drawsNothing()); return fNoiseBitmap; } }; // struct PaintingData static const int kMaxOctaves = 255; // numOctaves must be <= 0 and <= kMaxOctaves SkPerlinNoiseShader(SkPerlinNoiseShaderType type, SkScalar baseFrequencyX, SkScalar baseFrequencyY, int numOctaves, SkScalar seed, const SkISize* tileSize); ShaderType type() const override { return ShaderType::kPerlinNoise; } SkPerlinNoiseShaderType noiseType() const { return fType; } int numOctaves() const { return fNumOctaves; } bool stitchTiles() const { return fStitchTiles; } SkISize tileSize() const { return fTileSize; } std::unique_ptr getPaintingData() const { return std::make_unique(fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY); } bool appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const override; protected: void flatten(SkWriteBuffer&) const override; private: SK_FLATTENABLE_HOOKS(SkPerlinNoiseShader) const SkPerlinNoiseShaderType fType; const SkScalar fBaseFrequencyX; const SkScalar fBaseFrequencyY; const int fNumOctaves; const SkScalar fSeed; const SkISize fTileSize; const bool fStitchTiles; mutable SkOnce fInitPaintingDataOnce; std::unique_ptr fPaintingData; friend void SkRegisterPerlinNoiseShaderFlattenable(); }; #endif