/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/shaders/SkPerlinNoiseShaderImpl.h" #include "include/core/SkColor.h" #include "include/core/SkColorSpace.h" #include "include/core/SkRefCnt.h" #include "include/core/SkShader.h" #include "include/effects/SkPerlinNoiseShader.h" #include "src/base/SkArenaAlloc.h" #include "src/core/SkEffectPriv.h" #include "src/core/SkRasterPipeline.h" #include "src/core/SkRasterPipelineOpContexts.h" #include "src/core/SkRasterPipelineOpList.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkWriteBuffer.h" #include "src/shaders/SkPerlinNoiseShaderType.h" #include SkPerlinNoiseShader::SkPerlinNoiseShader(SkPerlinNoiseShaderType type, SkScalar baseFrequencyX, SkScalar baseFrequencyY, int numOctaves, SkScalar seed, const SkISize* tileSize) : fType(type) , fBaseFrequencyX(baseFrequencyX) , fBaseFrequencyY(baseFrequencyY) , fNumOctaves(numOctaves > kMaxOctaves ? kMaxOctaves : numOctaves) // [0,255] octaves allowed , fSeed(seed) , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize) , fStitchTiles(!fTileSize.isEmpty()) { SkASSERT(numOctaves >= 0 && numOctaves <= kMaxOctaves); SkASSERT(fBaseFrequencyX >= 0); SkASSERT(fBaseFrequencyY >= 0); // If kBlockSize changes then it must be changed in the SkSL noise_function // implementation and the graphite backend static_assert(SkPerlinNoiseShader::kBlockSize == 256); } sk_sp SkPerlinNoiseShader::CreateProc(SkReadBuffer& buffer) { SkPerlinNoiseShaderType type = buffer.read32LE(SkPerlinNoiseShaderType::kLast); SkScalar freqX = buffer.readScalar(); SkScalar freqY = buffer.readScalar(); int octaves = buffer.read32LE(kMaxOctaves); SkScalar seed = buffer.readScalar(); SkISize tileSize; tileSize.fWidth = buffer.readInt(); tileSize.fHeight = buffer.readInt(); switch (type) { case SkPerlinNoiseShaderType::kFractalNoise: return SkShaders::MakeFractalNoise(freqX, freqY, octaves, seed, &tileSize); case SkPerlinNoiseShaderType::kTurbulence: return SkShaders::MakeTurbulence(freqX, freqY, octaves, seed, &tileSize); default: // Really shouldn't get here b.c. of earlier check on type buffer.validate(false); return nullptr; } } void SkPerlinNoiseShader::flatten(SkWriteBuffer& buffer) const { buffer.writeInt((int)fType); buffer.writeScalar(fBaseFrequencyX); buffer.writeScalar(fBaseFrequencyY); buffer.writeInt(fNumOctaves); buffer.writeScalar(fSeed); buffer.writeInt(fTileSize.fWidth); buffer.writeInt(fTileSize.fHeight); } bool SkPerlinNoiseShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const { std::optional newMRec = mRec.apply(rec); if (!newMRec.has_value()) { return false; } fInitPaintingDataOnce([&] { const_cast(this)->fPaintingData = this->getPaintingData(); }); auto* ctx = rec.fAlloc->make(); ctx->noiseType = fType; ctx->baseFrequencyX = fPaintingData->fBaseFrequency.fX; ctx->baseFrequencyY = fPaintingData->fBaseFrequency.fY; ctx->stitchDataInX = fPaintingData->fStitchDataInit.fWidth; ctx->stitchDataInY = fPaintingData->fStitchDataInit.fHeight; ctx->stitching = fStitchTiles; ctx->numOctaves = fNumOctaves; ctx->latticeSelector = fPaintingData->fLatticeSelector; ctx->noiseData = &fPaintingData->fNoise[0][0][0]; rec.fPipeline->append(SkRasterPipelineOp::perlin_noise, ctx); return true; } /////////////////////////////////////////////////////////////////////////////////////////////////// static bool valid_input( SkScalar baseX, SkScalar baseY, int numOctaves, const SkISize* tileSize, SkScalar seed) { if (!(baseX >= 0 && baseY >= 0)) { return false; } if (!(numOctaves >= 0 && numOctaves <= SkPerlinNoiseShader::kMaxOctaves)) { return false; } if (tileSize && !(tileSize->width() >= 0 && tileSize->height() >= 0)) { return false; } if (!SkIsFinite(seed)) { return false; } return true; } void SkRegisterPerlinNoiseShaderFlattenable() { SK_REGISTER_FLATTENABLE(SkPerlinNoiseShader); // Previous name SkFlattenable::Register("SkPerlinNoiseShaderImpl", SkPerlinNoiseShader::CreateProc); } namespace SkShaders { sk_sp MakeFractalNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY, int numOctaves, SkScalar seed, const SkISize* tileSize) { if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) { return nullptr; } if (0 == numOctaves) { // For kFractalNoise, w/o any octaves, the entire shader collapses to: // [0,0,0,0] * 0.5 + 0.5 constexpr SkColor4f kTransparentGray = {0.5f, 0.5f, 0.5f, 0.5f}; return SkShaders::Color(kTransparentGray, /* colorSpace= */ nullptr); } return sk_sp(new SkPerlinNoiseShader(SkPerlinNoiseShaderType::kFractalNoise, baseFrequencyX, baseFrequencyY, numOctaves, seed, tileSize)); } sk_sp MakeTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY, int numOctaves, SkScalar seed, const SkISize* tileSize) { if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) { return nullptr; } if (0 == numOctaves) { // For kTurbulence, w/o any octaves, the entire shader collapses to: [0,0,0,0] return SkShaders::Color(SkColors::kTransparent, /* colorSpace= */ nullptr); } return sk_sp(new SkPerlinNoiseShader(SkPerlinNoiseShaderType::kTurbulence, baseFrequencyX, baseFrequencyY, numOctaves, seed, tileSize)); } } // namespace SkShaders