| /* | 
 |  * 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 <algorithm> | 
 | #include <cstdint> | 
 | #include <cstring> | 
 | #include <memory> | 
 |  | 
 | 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<PaintingData> getPaintingData() const { | 
 |         return std::make_unique<PaintingData>(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<PaintingData> fPaintingData; | 
 |  | 
 |     friend void SkRegisterPerlinNoiseShaderFlattenable(); | 
 | }; | 
 |  | 
 | #endif |