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