| /* |
| * 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 |