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 "src/shaders/SkPerlinNoiseShaderImpl.h"
9
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkRefCnt.h"
13 #include "include/core/SkShader.h"
14 #include "include/effects/SkPerlinNoiseShader.h"
15 #include "src/base/SkArenaAlloc.h"
16 #include "src/core/SkEffectPriv.h"
17 #include "src/core/SkRasterPipeline.h"
18 #include "src/core/SkRasterPipelineOpContexts.h"
19 #include "src/core/SkRasterPipelineOpList.h"
20 #include "src/core/SkReadBuffer.h"
21 #include "src/core/SkWriteBuffer.h"
22 #include "src/shaders/SkPerlinNoiseShaderType.h"
23
24 #include <optional>
25
SkPerlinNoiseShader(SkPerlinNoiseShaderType type,SkScalar baseFrequencyX,SkScalar baseFrequencyY,int numOctaves,SkScalar seed,const SkISize * tileSize)26 SkPerlinNoiseShader::SkPerlinNoiseShader(SkPerlinNoiseShaderType type,
27 SkScalar baseFrequencyX,
28 SkScalar baseFrequencyY,
29 int numOctaves,
30 SkScalar seed,
31 const SkISize* tileSize)
32 : fType(type)
33 , fBaseFrequencyX(baseFrequencyX)
34 , fBaseFrequencyY(baseFrequencyY)
35 , fNumOctaves(numOctaves > kMaxOctaves ? kMaxOctaves
36 : numOctaves) // [0,255] octaves allowed
37 , fSeed(seed)
38 , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize)
39 , fStitchTiles(!fTileSize.isEmpty()) {
40 SkASSERT(numOctaves >= 0 && numOctaves <= kMaxOctaves);
41 SkASSERT(fBaseFrequencyX >= 0);
42 SkASSERT(fBaseFrequencyY >= 0);
43
44 // If kBlockSize changes then it must be changed in the SkSL noise_function
45 // implementation and the graphite backend
46 static_assert(SkPerlinNoiseShader::kBlockSize == 256);
47 }
48
CreateProc(SkReadBuffer & buffer)49 sk_sp<SkFlattenable> SkPerlinNoiseShader::CreateProc(SkReadBuffer& buffer) {
50 SkPerlinNoiseShaderType type = buffer.read32LE(SkPerlinNoiseShaderType::kLast);
51
52 SkScalar freqX = buffer.readScalar();
53 SkScalar freqY = buffer.readScalar();
54 int octaves = buffer.read32LE<int>(kMaxOctaves);
55
56 SkScalar seed = buffer.readScalar();
57 SkISize tileSize;
58 tileSize.fWidth = buffer.readInt();
59 tileSize.fHeight = buffer.readInt();
60
61 switch (type) {
62 case SkPerlinNoiseShaderType::kFractalNoise:
63 return SkShaders::MakeFractalNoise(freqX, freqY, octaves, seed, &tileSize);
64 case SkPerlinNoiseShaderType::kTurbulence:
65 return SkShaders::MakeTurbulence(freqX, freqY, octaves, seed, &tileSize);
66 default:
67 // Really shouldn't get here b.c. of earlier check on type
68 buffer.validate(false);
69 return nullptr;
70 }
71 }
72
flatten(SkWriteBuffer & buffer) const73 void SkPerlinNoiseShader::flatten(SkWriteBuffer& buffer) const {
74 buffer.writeInt((int)fType);
75 buffer.writeScalar(fBaseFrequencyX);
76 buffer.writeScalar(fBaseFrequencyY);
77 buffer.writeInt(fNumOctaves);
78 buffer.writeScalar(fSeed);
79 buffer.writeInt(fTileSize.fWidth);
80 buffer.writeInt(fTileSize.fHeight);
81 }
82
appendStages(const SkStageRec & rec,const SkShaders::MatrixRec & mRec) const83 bool SkPerlinNoiseShader::appendStages(const SkStageRec& rec,
84 const SkShaders::MatrixRec& mRec) const {
85 std::optional<SkShaders::MatrixRec> newMRec = mRec.apply(rec);
86 if (!newMRec.has_value()) {
87 return false;
88 }
89
90 fInitPaintingDataOnce([&] {
91 const_cast<SkPerlinNoiseShader*>(this)->fPaintingData = this->getPaintingData();
92 });
93
94 auto* ctx = rec.fAlloc->make<SkRasterPipeline_PerlinNoiseCtx>();
95 ctx->noiseType = fType;
96 ctx->baseFrequencyX = fPaintingData->fBaseFrequency.fX;
97 ctx->baseFrequencyY = fPaintingData->fBaseFrequency.fY;
98 ctx->stitchDataInX = fPaintingData->fStitchDataInit.fWidth;
99 ctx->stitchDataInY = fPaintingData->fStitchDataInit.fHeight;
100 ctx->stitching = fStitchTiles;
101 ctx->numOctaves = fNumOctaves;
102 ctx->latticeSelector = fPaintingData->fLatticeSelector;
103 ctx->noiseData = &fPaintingData->fNoise[0][0][0];
104
105 rec.fPipeline->append(SkRasterPipelineOp::perlin_noise, ctx);
106 return true;
107 }
108
109 ///////////////////////////////////////////////////////////////////////////////////////////////////
110
valid_input(SkScalar baseX,SkScalar baseY,int numOctaves,const SkISize * tileSize,SkScalar seed)111 static bool valid_input(
112 SkScalar baseX, SkScalar baseY, int numOctaves, const SkISize* tileSize, SkScalar seed) {
113 if (!(baseX >= 0 && baseY >= 0)) {
114 return false;
115 }
116 if (!(numOctaves >= 0 && numOctaves <= SkPerlinNoiseShader::kMaxOctaves)) {
117 return false;
118 }
119 if (tileSize && !(tileSize->width() >= 0 && tileSize->height() >= 0)) {
120 return false;
121 }
122 if (!SkIsFinite(seed)) {
123 return false;
124 }
125 return true;
126 }
127
SkRegisterPerlinNoiseShaderFlattenable()128 void SkRegisterPerlinNoiseShaderFlattenable() {
129 SK_REGISTER_FLATTENABLE(SkPerlinNoiseShader);
130 // Previous name
131 SkFlattenable::Register("SkPerlinNoiseShaderImpl", SkPerlinNoiseShader::CreateProc);
132 }
133
134 namespace SkShaders {
MakeFractalNoise(SkScalar baseFrequencyX,SkScalar baseFrequencyY,int numOctaves,SkScalar seed,const SkISize * tileSize)135 sk_sp<SkShader> MakeFractalNoise(SkScalar baseFrequencyX,
136 SkScalar baseFrequencyY,
137 int numOctaves,
138 SkScalar seed,
139 const SkISize* tileSize) {
140 if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) {
141 return nullptr;
142 }
143
144 if (0 == numOctaves) {
145 // For kFractalNoise, w/o any octaves, the entire shader collapses to:
146 // [0,0,0,0] * 0.5 + 0.5
147 constexpr SkColor4f kTransparentGray = {0.5f, 0.5f, 0.5f, 0.5f};
148
149 return SkShaders::Color(kTransparentGray, /* colorSpace= */ nullptr);
150 }
151
152 return sk_sp<SkShader>(new SkPerlinNoiseShader(SkPerlinNoiseShaderType::kFractalNoise,
153 baseFrequencyX,
154 baseFrequencyY,
155 numOctaves,
156 seed,
157 tileSize));
158 }
159
MakeTurbulence(SkScalar baseFrequencyX,SkScalar baseFrequencyY,int numOctaves,SkScalar seed,const SkISize * tileSize)160 sk_sp<SkShader> MakeTurbulence(SkScalar baseFrequencyX,
161 SkScalar baseFrequencyY,
162 int numOctaves,
163 SkScalar seed,
164 const SkISize* tileSize) {
165 if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) {
166 return nullptr;
167 }
168
169 if (0 == numOctaves) {
170 // For kTurbulence, w/o any octaves, the entire shader collapses to: [0,0,0,0]
171 return SkShaders::Color(SkColors::kTransparent, /* colorSpace= */ nullptr);
172 }
173
174 return sk_sp<SkShader>(new SkPerlinNoiseShader(SkPerlinNoiseShaderType::kTurbulence,
175 baseFrequencyX,
176 baseFrequencyY,
177 numOctaves,
178 seed,
179 tileSize));
180 }
181
182 } // namespace SkShaders
183