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