Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkDither.h" | 8 #include "SkDither.h" |
| 9 #include "SkPerlinNoiseShader.h" | 9 #include "SkPerlinNoiseShader.h" |
| 10 #include "SkColorFilter.h" | 10 #include "SkColorFilter.h" |
| 11 #include "SkReadBuffer.h" | 11 #include "SkReadBuffer.h" |
| 12 #include "SkWriteBuffer.h" | 12 #include "SkWriteBuffer.h" |
| 13 #include "SkShader.h" | 13 #include "SkShader.h" |
| 14 #include "SkUnPreMultiply.h" | 14 #include "SkUnPreMultiply.h" |
| 15 #include "SkString.h" | 15 #include "SkString.h" |
| 16 | 16 |
| 17 #if SK_SUPPORT_GPU | 17 #if SK_SUPPORT_GPU |
| 18 #include "GrContext.h" | 18 #include "GrContext.h" |
| 19 #include "GrCoordTransform.h" | 19 #include "GrCoordTransform.h" |
| 20 #include "gl/GrGLEffect.h" | 20 #include "gl/GrGLEffect.h" |
| 21 #include "GrTBackendEffectFactory.h" | 21 #include "GrTBackendEffectFactory.h" |
| 22 #include "SkGr.h" | 22 #include "SkGr.h" |
| 23 #endif | 23 #endif |
| 24 | 24 |
| 25 static const int kBlockSize = 256; | 25 const int SkPerlinNoiseShader::kBlockSize; |
| 26 static const int kBlockMask = kBlockSize - 1; | 26 const int SkPerlinNoiseShader::kBlockMask; |
| 27 static const int kPerlinNoise = 4096; | 27 static const int kPerlinNoise = 4096; |
| 28 static const int kRandMaximum = SK_MaxS32; // 2**31 - 1 | 28 static const int kRandMaximum = SK_MaxS32; // 2**31 - 1 |
| 29 | 29 |
| 30 namespace { | 30 namespace { |
| 31 | 31 |
| 32 // noiseValue is the color component's value (or color) | 32 // noiseValue is the color component's value (or color) |
| 33 // limitValue is the maximum perlin noise array index value allowed | 33 // limitValue is the maximum perlin noise array index value allowed |
| 34 // newValue is the current noise dimension (either width or height) | 34 // newValue is the current noise dimension (either width or height) |
| 35 inline int checkNoise(int noiseValue, int limitValue, int newValue) { | 35 inline int checkNoise(int noiseValue, int limitValue, int newValue) { |
| 36 // If the noise value would bring us out of bounds of the current noise arra y while we are | 36 // If the noise value would bring us out of bounds of the current noise arra y while we are |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 52 return SkScalarMul(SkScalarSquare(t), SK_Scalar3 - 2 * t); | 52 return SkScalarMul(SkScalarSquare(t), SK_Scalar3 - 2 * t); |
| 53 } | 53 } |
| 54 | 54 |
| 55 bool perlin_noise_type_is_valid(SkPerlinNoiseShader::Type type) { | 55 bool perlin_noise_type_is_valid(SkPerlinNoiseShader::Type type) { |
| 56 return (SkPerlinNoiseShader::kFractalNoise_Type == type) || | 56 return (SkPerlinNoiseShader::kFractalNoise_Type == type) || |
| 57 (SkPerlinNoiseShader::kTurbulence_Type == type); | 57 (SkPerlinNoiseShader::kTurbulence_Type == type); |
| 58 } | 58 } |
| 59 | 59 |
| 60 } // end namespace | 60 } // end namespace |
| 61 | 61 |
| 62 struct SkPerlinNoiseShader::StitchData { | 62 inline int random(int* seed) { |
| 63 StitchData() | 63 static const int gRandAmplitude = 16807; // 7**5; primitive root of m |
| 64 : fWidth(0) | 64 static const int gRandQ = 127773; // m / a |
| 65 , fWrapX(0) | 65 static const int gRandR = 2836; // m % a |
| 66 , fHeight(0) | |
| 67 , fWrapY(0) | |
| 68 {} | |
| 69 | 66 |
| 70 bool operator==(const StitchData& other) const { | 67 int result = gRandAmplitude * (*seed % gRandQ) - gRandR * (*seed / gRandQ); |
| 71 return fWidth == other.fWidth && | 68 if (result <= 0) { |
| 72 fWrapX == other.fWrapX && | 69 result += kRandMaximum; |
| 73 fHeight == other.fHeight && | 70 } |
| 74 fWrapY == other.fWrapY; | 71 *seed = result; |
| 72 return result; | |
| 73 } | |
| 74 | |
| 75 // Only called once. Could be part of the constructor. | |
| 76 void SkPerlinNoiseShader::init() | |
| 77 { | |
| 78 static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlockSi ze)); | |
| 79 | |
| 80 // The seed value clamp to the range [1, kRandMaximum - 1]. | |
| 81 if (fSeed <= 0) { | |
| 82 fSeed = -(fSeed % (kRandMaximum - 1)) + 1; | |
| 83 } | |
| 84 if (fSeed > kRandMaximum - 1) { | |
| 85 fSeed = kRandMaximum - 1; | |
| 86 } | |
| 87 for (int channel = 0; channel < 4; ++channel) { | |
| 88 for (int i = 0; i < kBlockSize; ++i) { | |
| 89 fLatticeSelector[i] = i; | |
| 90 fNoise[channel][i][0] = (random(&fSeed) % (2 * kBlockSize)); | |
| 91 fNoise[channel][i][1] = (random(&fSeed) % (2 * kBlockSize)); | |
| 92 } | |
| 93 } | |
| 94 for (int i = kBlockSize - 1; i > 0; --i) { | |
| 95 int k = fLatticeSelector[i]; | |
| 96 int j = random(&fSeed) % kBlockSize; | |
| 97 SkASSERT(j >= 0); | |
| 98 SkASSERT(j < kBlockSize); | |
| 99 fLatticeSelector[i] = fLatticeSelector[j]; | |
| 100 fLatticeSelector[j] = k; | |
| 75 } | 101 } |
| 76 | 102 |
| 77 int fWidth; // How much to subtract to wrap for stitching. | 103 // Perform the permutations now |
| 78 int fWrapX; // Minimum value to wrap. | |
| 79 int fHeight; | |
| 80 int fWrapY; | |
| 81 }; | |
| 82 | |
| 83 struct SkPerlinNoiseShader::PaintingData { | |
| 84 PaintingData(const SkISize& tileSize) | |
| 85 : fSeed(0) | |
| 86 , fTileSize(tileSize) | |
| 87 , fPermutationsBitmap(NULL) | |
| 88 , fNoiseBitmap(NULL) | |
| 89 {} | |
| 90 | |
| 91 ~PaintingData() | |
| 92 { | 104 { |
| 93 SkDELETE(fPermutationsBitmap); | 105 // Copy noise data |
| 94 SkDELETE(fNoiseBitmap); | 106 uint16_t noise[4][kBlockSize][2]; |
| 95 } | 107 for (int i = 0; i < kBlockSize; ++i) { |
| 96 | 108 for (int channel = 0; channel < 4; ++channel) { |
| 97 int fSeed; | 109 for (int j = 0; j < 2; ++j) { |
| 98 uint8_t fLatticeSelector[kBlockSize]; | 110 noise[channel][i][j] = fNoise[channel][i][j]; |
| 99 uint16_t fNoise[4][kBlockSize][2]; | |
| 100 SkPoint fGradient[4][kBlockSize]; | |
| 101 SkISize fTileSize; | |
| 102 SkVector fBaseFrequency; | |
| 103 StitchData fStitchDataInit; | |
| 104 | |
| 105 private: | |
| 106 | |
| 107 SkBitmap* fPermutationsBitmap; | |
| 108 SkBitmap* fNoiseBitmap; | |
| 109 | |
| 110 public: | |
| 111 | |
| 112 inline int random() { | |
| 113 static const int gRandAmplitude = 16807; // 7**5; primitive root of m | |
| 114 static const int gRandQ = 127773; // m / a | |
| 115 static const int gRandR = 2836; // m % a | |
| 116 | |
| 117 int result = gRandAmplitude * (fSeed % gRandQ) - gRandR * (fSeed / gRand Q); | |
| 118 if (result <= 0) | |
| 119 result += kRandMaximum; | |
| 120 fSeed = result; | |
| 121 return result; | |
| 122 } | |
| 123 | |
| 124 void init(SkScalar seed) | |
| 125 { | |
| 126 static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlo ckSize)); | |
| 127 | |
| 128 // According to the SVG spec, we must truncate (not round) the seed valu e. | |
| 129 fSeed = SkScalarTruncToInt(seed); | |
| 130 // The seed value clamp to the range [1, kRandMaximum - 1]. | |
| 131 if (fSeed <= 0) { | |
| 132 fSeed = -(fSeed % (kRandMaximum - 1)) + 1; | |
| 133 } | |
| 134 if (fSeed > kRandMaximum - 1) { | |
| 135 fSeed = kRandMaximum - 1; | |
| 136 } | |
| 137 for (int channel = 0; channel < 4; ++channel) { | |
| 138 for (int i = 0; i < kBlockSize; ++i) { | |
| 139 fLatticeSelector[i] = i; | |
| 140 fNoise[channel][i][0] = (random() % (2 * kBlockSize)); | |
| 141 fNoise[channel][i][1] = (random() % (2 * kBlockSize)); | |
| 142 } | |
| 143 } | |
| 144 for (int i = kBlockSize - 1; i > 0; --i) { | |
| 145 int k = fLatticeSelector[i]; | |
| 146 int j = random() % kBlockSize; | |
| 147 SkASSERT(j >= 0); | |
| 148 SkASSERT(j < kBlockSize); | |
| 149 fLatticeSelector[i] = fLatticeSelector[j]; | |
| 150 fLatticeSelector[j] = k; | |
| 151 } | |
| 152 | |
| 153 // Perform the permutations now | |
| 154 { | |
| 155 // Copy noise data | |
| 156 uint16_t noise[4][kBlockSize][2]; | |
| 157 for (int i = 0; i < kBlockSize; ++i) { | |
| 158 for (int channel = 0; channel < 4; ++channel) { | |
| 159 for (int j = 0; j < 2; ++j) { | |
| 160 noise[channel][i][j] = fNoise[channel][i][j]; | |
| 161 } | |
| 162 } | |
| 163 } | |
| 164 // Do permutations on noise data | |
| 165 for (int i = 0; i < kBlockSize; ++i) { | |
| 166 for (int channel = 0; channel < 4; ++channel) { | |
| 167 for (int j = 0; j < 2; ++j) { | |
| 168 fNoise[channel][i][j] = noise[channel][fLatticeSelector[ i]][j]; | |
| 169 } | |
| 170 } | 111 } |
| 171 } | 112 } |
| 172 } | 113 } |
| 114 // Do permutations on noise data | |
| 115 for (int i = 0; i < kBlockSize; ++i) { | |
| 116 for (int channel = 0; channel < 4; ++channel) { | |
| 117 for (int j = 0; j < 2; ++j) { | |
| 118 fNoise[channel][i][j] = noise[channel][fLatticeSelector[ i]][j]; | |
| 119 } | |
| 120 } | |
| 121 } | |
| 122 } | |
| 173 | 123 |
| 174 // Half of the largest possible value for 16 bit unsigned int | 124 // Half of the largest possible value for 16 bit unsigned int |
| 175 static const SkScalar gHalfMax16bits = 32767.5f; | 125 static const SkScalar gHalfMax16bits = 32767.5f; |
| 176 | 126 |
| 177 // Compute gradients from permutated noise data | 127 // Compute gradients from permutated noise data |
| 178 for (int channel = 0; channel < 4; ++channel) { | 128 for (int channel = 0; channel < 4; ++channel) { |
| 179 for (int i = 0; i < kBlockSize; ++i) { | 129 for (int i = 0; i < kBlockSize; ++i) { |
| 180 fGradient[channel][i] = SkPoint::Make( | 130 fGradient[channel][i] = SkPoint::Make( |
| 181 SkScalarMul(SkIntToScalar(fNoise[channel][i][0] - kBlockSize ), | 131 SkScalarMul(SkIntToScalar(fNoise[channel][i][0] - kBlockSize ), |
| 182 gInvBlockSizef), | 132 gInvBlockSizef), |
| 183 SkScalarMul(SkIntToScalar(fNoise[channel][i][1] - kBlockSize ), | 133 SkScalarMul(SkIntToScalar(fNoise[channel][i][1] - kBlockSize ), |
| 184 gInvBlockSizef)); | 134 gInvBlockSizef)); |
| 185 fGradient[channel][i].normalize(); | 135 fGradient[channel][i].normalize(); |
| 186 // Put the normalized gradient back into the noise data | 136 // Put the normalized gradient back into the noise data |
| 187 fNoise[channel][i][0] = SkScalarRoundToInt(SkScalarMul( | 137 fNoise[channel][i][0] = SkScalarRoundToInt(SkScalarMul( |
| 188 fGradient[channel][i].fX + SK_Scalar1, gHalfMax16bits)); | 138 fGradient[channel][i].fX + SK_Scalar1, gHalfMax16bits)); |
| 189 fNoise[channel][i][1] = SkScalarRoundToInt(SkScalarMul( | 139 fNoise[channel][i][1] = SkScalarRoundToInt(SkScalarMul( |
| 190 fGradient[channel][i].fY + SK_Scalar1, gHalfMax16bits)); | 140 fGradient[channel][i].fY + SK_Scalar1, gHalfMax16bits)); |
| 191 } | |
| 192 } | 141 } |
| 193 | |
| 194 // Invalidate bitmaps | |
| 195 SkDELETE(fPermutationsBitmap); | |
| 196 fPermutationsBitmap = NULL; | |
| 197 SkDELETE(fNoiseBitmap); | |
| 198 fNoiseBitmap = NULL; | |
| 199 } | 142 } |
| 200 | 143 |
| 201 void stitch() { | 144 if (fTileSize.isEmpty()) { |
|
scroggo
2014/03/04 22:26:34
stitch() is now part of init.
| |
| 202 SkScalar tileWidth = SkIntToScalar(fTileSize.width()); | 145 return; |
| 203 SkScalar tileHeight = SkIntToScalar(fTileSize.height()); | |
| 204 SkASSERT(tileWidth > 0 && tileHeight > 0); | |
| 205 // When stitching tiled turbulence, the frequencies must be adjusted | |
| 206 // so that the tile borders will be continuous. | |
| 207 if (fBaseFrequency.fX) { | |
| 208 SkScalar lowFrequencx = | |
| 209 SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth ; | |
| 210 SkScalar highFrequencx = | |
| 211 SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; | |
| 212 // BaseFrequency should be non-negative according to the standard. | |
| 213 if (SkScalarDiv(fBaseFrequency.fX, lowFrequencx) < | |
| 214 SkScalarDiv(highFrequencx, fBaseFrequency.fX)) { | |
| 215 fBaseFrequency.fX = lowFrequencx; | |
| 216 } else { | |
| 217 fBaseFrequency.fX = highFrequencx; | |
| 218 } | |
| 219 } | |
| 220 if (fBaseFrequency.fY) { | |
| 221 SkScalar lowFrequency = | |
| 222 SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeig ht; | |
| 223 SkScalar highFrequency = | |
| 224 SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeigh t; | |
| 225 if (SkScalarDiv(fBaseFrequency.fY, lowFrequency) < | |
| 226 SkScalarDiv(highFrequency, fBaseFrequency.fY)) { | |
| 227 fBaseFrequency.fY = lowFrequency; | |
| 228 } else { | |
| 229 fBaseFrequency.fY = highFrequency; | |
| 230 } | |
| 231 } | |
| 232 // Set up TurbulenceInitial stitch values. | |
| 233 fStitchDataInit.fWidth = | |
| 234 SkScalarRoundToInt(tileWidth * fBaseFrequency.fX); | |
| 235 fStitchDataInit.fWrapX = kPerlinNoise + fStitchDataInit.fWidth; | |
| 236 fStitchDataInit.fHeight = | |
| 237 SkScalarRoundToInt(tileHeight * fBaseFrequency.fY); | |
| 238 fStitchDataInit.fWrapY = kPerlinNoise + fStitchDataInit.fHeight; | |
| 239 } | 146 } |
| 240 | 147 |
| 241 SkBitmap* getPermutationsBitmap() | 148 SkScalar tileWidth = SkIntToScalar(fTileSize.width()); |
| 242 { | 149 SkScalar tileHeight = SkIntToScalar(fTileSize.height()); |
| 243 if (!fPermutationsBitmap) { | 150 SkASSERT(tileWidth > 0 && tileHeight > 0); |
| 244 fPermutationsBitmap = SkNEW(SkBitmap); | 151 // When stitching tiled turbulence, the frequencies must be adjusted |
| 245 fPermutationsBitmap->allocPixels(SkImageInfo::MakeA8(kBlockSize, 1)) ; | 152 // so that the tile borders will be continuous. |
| 246 uint8_t* bitmapPixels = fPermutationsBitmap->getAddr8(0, 0); | 153 if (fBaseFrequency.fX) { |
| 247 memcpy(bitmapPixels, fLatticeSelector, sizeof(uint8_t) * kBlockSize) ; | 154 SkScalar lowFrequencx = |
| 155 SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth ; | |
| 156 SkScalar highFrequencx = | |
| 157 SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; | |
| 158 // BaseFrequency should be non-negative according to the standard. | |
| 159 if (SkScalarDiv(fBaseFrequency.fX, lowFrequencx) < | |
| 160 SkScalarDiv(highFrequencx, fBaseFrequency.fX)) { | |
| 161 fBaseFrequency.fX = lowFrequencx; | |
| 162 } else { | |
| 163 fBaseFrequency.fX = highFrequencx; | |
| 248 } | 164 } |
| 249 return fPermutationsBitmap; | |
| 250 } | 165 } |
| 166 if (fBaseFrequency.fY) { | |
| 167 SkScalar lowFrequency = | |
| 168 SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeig ht; | |
| 169 SkScalar highFrequency = | |
| 170 SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeigh t; | |
| 171 if (SkScalarDiv(fBaseFrequency.fY, lowFrequency) < | |
| 172 SkScalarDiv(highFrequency, fBaseFrequency.fY)) { | |
| 173 fBaseFrequency.fY = lowFrequency; | |
| 174 } else { | |
| 175 fBaseFrequency.fY = highFrequency; | |
| 176 } | |
| 177 } | |
| 178 // Set up TurbulenceInitial stitch values. | |
| 179 fStitchDataInit.fWidth = | |
| 180 SkScalarRoundToInt(tileWidth * fBaseFrequency.fX); | |
| 181 fStitchDataInit.fWrapX = kPerlinNoise + fStitchDataInit.fWidth; | |
| 182 fStitchDataInit.fHeight = | |
| 183 SkScalarRoundToInt(tileHeight * fBaseFrequency.fY); | |
| 184 fStitchDataInit.fWrapY = kPerlinNoise + fStitchDataInit.fHeight; | |
| 185 } | |
| 251 | 186 |
| 252 SkBitmap* getNoiseBitmap() | 187 const SkBitmap& SkPerlinNoiseShader::getPermutationsBitmap() const { |
| 253 { | 188 if (kUnknown_SkColorType == fPermutationsBitmap.colorType()) { |
| 254 if (!fNoiseBitmap) { | 189 // initialize the cache. |
| 255 fNoiseBitmap = SkNEW(SkBitmap); | 190 fPermutationsBitmap.allocPixels(SkImageInfo::MakeA8(kBlockSize, 1)); |
| 256 fNoiseBitmap->allocPixels(SkImageInfo::MakeN32Premul(kBlockSize, 4)) ; | 191 uint8_t* bitmapPixels = fPermutationsBitmap.getAddr8(0, 0); |
| 257 uint32_t* bitmapPixels = fNoiseBitmap->getAddr32(0, 0); | 192 memcpy(bitmapPixels, fLatticeSelector, sizeof(uint8_t) * kBlockSize); |
| 258 memcpy(bitmapPixels, fNoise[0][0], sizeof(uint16_t) * kBlockSize * 4 * 2); | |
| 259 } | |
| 260 return fNoiseBitmap; | |
| 261 } | 193 } |
| 262 }; | 194 return fPermutationsBitmap; |
| 195 } | |
| 196 | |
| 197 const SkBitmap& SkPerlinNoiseShader::getNoiseBitmap() const { | |
| 198 if (kUnknown_SkColorType == fNoiseBitmap.colorType()) { | |
| 199 // initialize the cache. | |
| 200 fNoiseBitmap.allocPixels(SkImageInfo::MakeN32Premul(kBlockSize, 4)); | |
| 201 uint32_t* bitmapPixels = fNoiseBitmap.getAddr32(0, 0); | |
| 202 memcpy(bitmapPixels, fNoise[0][0], sizeof(uint16_t) * kBlockSize * 4 * 2 ); | |
| 203 } | |
| 204 return fNoiseBitmap; | |
| 205 } | |
| 263 | 206 |
| 264 SkShader* SkPerlinNoiseShader::CreateFractalNoise(SkScalar baseFrequencyX, SkSca lar baseFrequencyY, | 207 SkShader* SkPerlinNoiseShader::CreateFractalNoise(SkScalar baseFrequencyX, SkSca lar baseFrequencyY, |
| 265 int numOctaves, SkScalar seed, | 208 int numOctaves, SkScalar seed, |
| 266 const SkISize* tileSize) { | 209 const SkISize* tileSize) { |
| 267 return SkNEW_ARGS(SkPerlinNoiseShader, (kFractalNoise_Type, baseFrequencyX, baseFrequencyY, | 210 return SkNEW_ARGS(SkPerlinNoiseShader, (kFractalNoise_Type, baseFrequencyX, baseFrequencyY, |
| 268 numOctaves, seed, tileSize)); | 211 numOctaves, seed, tileSize)); |
| 269 } | 212 } |
| 270 | 213 |
| 271 SkShader* SkPerlinNoiseShader::CreateTubulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY, | 214 SkShader* SkPerlinNoiseShader::CreateTubulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY, |
| 272 int numOctaves, SkScalar seed, | 215 int numOctaves, SkScalar seed, |
| 273 const SkISize* tileSize) { | 216 const SkISize* tileSize) { |
| 274 return SkNEW_ARGS(SkPerlinNoiseShader, (kTurbulence_Type, baseFrequencyX, ba seFrequencyY, | 217 return SkNEW_ARGS(SkPerlinNoiseShader, (kTurbulence_Type, baseFrequencyX, ba seFrequencyY, |
| 275 numOctaves, seed, tileSize)); | 218 numOctaves, seed, tileSize)); |
| 276 } | 219 } |
| 277 | 220 |
| 278 SkPerlinNoiseShader::SkPerlinNoiseShader(SkPerlinNoiseShader::Type type, | 221 SkPerlinNoiseShader::SkPerlinNoiseShader(SkPerlinNoiseShader::Type type, |
| 279 SkScalar baseFrequencyX, | 222 SkScalar baseFrequencyX, |
| 280 SkScalar baseFrequencyY, | 223 SkScalar baseFrequencyY, |
| 281 int numOctaves, | 224 int numOctaves, |
| 282 SkScalar seed, | 225 SkScalar seed, |
| 283 const SkISize* tileSize) | 226 const SkISize* tileSize) |
| 284 : fType(type) | 227 : fType(type) |
| 285 , fBaseFrequencyX(baseFrequencyX) | 228 , fBaseFrequency(SkPoint::Make(baseFrequencyX, baseFrequencyY)) |
| 286 , fBaseFrequencyY(baseFrequencyY) | |
| 287 , fNumOctaves(numOctaves > 255 ? 255 : numOctaves/*[0,255] octaves allowed*/) | 229 , fNumOctaves(numOctaves > 255 ? 255 : numOctaves/*[0,255] octaves allowed*/) |
| 288 , fSeed(seed) | 230 // According to the SVG spec, we must truncate (not round) the seed value. |
| 289 , fStitchTiles((tileSize != NULL) && !tileSize->isEmpty()) | 231 , fSeed(SkScalarTruncToInt(seed)) |
| 290 , fPaintingData(NULL) | 232 , fTileSize(NULL == tileSize ? SkISize::Make(0, 0) : *tileSize) |
| 233 , fStitchTiles(!fTileSize.isEmpty()) | |
| 291 { | 234 { |
| 292 SkASSERT(numOctaves >= 0 && numOctaves < 256); | 235 SkASSERT(numOctaves >= 0 && numOctaves < 256); |
| 293 setTileSize(fStitchTiles ? *tileSize : SkISize::Make(0,0)); | |
| 294 fMatrix.reset(); | 236 fMatrix.reset(); |
| 237 this->init(); | |
| 295 } | 238 } |
| 296 | 239 |
| 297 SkPerlinNoiseShader::SkPerlinNoiseShader(SkReadBuffer& buffer) : | 240 SkPerlinNoiseShader::SkPerlinNoiseShader(SkReadBuffer& buffer) |
| 298 INHERITED(buffer), fPaintingData(NULL) { | 241 : INHERITED(buffer) |
| 242 { | |
| 299 fType = (SkPerlinNoiseShader::Type) buffer.readInt(); | 243 fType = (SkPerlinNoiseShader::Type) buffer.readInt(); |
| 300 fBaseFrequencyX = buffer.readScalar(); | 244 buffer.readPoint(&fBaseFrequency); |
| 301 fBaseFrequencyY = buffer.readScalar(); | |
| 302 fNumOctaves = buffer.readInt(); | 245 fNumOctaves = buffer.readInt(); |
| 303 fSeed = buffer.readScalar(); | 246 fSeed = buffer.readInt(); |
|
Stephen White
2014/03/05 14:32:58
Hmmm. Isn't this going to require an SkPicture ver
sugoi
2014/03/05 16:50:33
Hmmm... In this const shader world, is there no wa
scroggo
2014/03/05 18:24:05
What do you mean by "later on during processing?"
scroggo
2014/03/05 18:24:05
Yes, this would require a picture version bump. In
| |
| 304 fStitchTiles = buffer.readBool(); | 247 fStitchTiles = buffer.readBool(); |
| 305 fTileSize.fWidth = buffer.readInt(); | 248 fTileSize.fWidth = buffer.readInt(); |
| 306 fTileSize.fHeight = buffer.readInt(); | 249 fTileSize.fHeight = buffer.readInt(); |
| 307 setTileSize(fTileSize); | |
| 308 fMatrix.reset(); | 250 fMatrix.reset(); |
| 251 this->init(); | |
| 309 buffer.validate(perlin_noise_type_is_valid(fType) && | 252 buffer.validate(perlin_noise_type_is_valid(fType) && |
| 310 (fNumOctaves >= 0) && (fNumOctaves <= 255)); | 253 (fNumOctaves >= 0) && (fNumOctaves <= 255) && |
| 311 } | 254 (fStitchTiles != fTileSize.isEmpty())); |
| 312 | |
| 313 SkPerlinNoiseShader::~SkPerlinNoiseShader() { | |
| 314 // Safety, should have been done in endContext() | |
| 315 SkDELETE(fPaintingData); | |
| 316 } | 255 } |
| 317 | 256 |
| 318 void SkPerlinNoiseShader::flatten(SkWriteBuffer& buffer) const { | 257 void SkPerlinNoiseShader::flatten(SkWriteBuffer& buffer) const { |
| 319 this->INHERITED::flatten(buffer); | 258 this->INHERITED::flatten(buffer); |
| 320 buffer.writeInt((int) fType); | 259 buffer.writeInt((int) fType); |
| 321 buffer.writeScalar(fBaseFrequencyX); | 260 buffer.writePoint(fBaseFrequency); |
| 322 buffer.writeScalar(fBaseFrequencyY); | |
| 323 buffer.writeInt(fNumOctaves); | 261 buffer.writeInt(fNumOctaves); |
| 324 buffer.writeScalar(fSeed); | 262 buffer.writeInt(fSeed); |
| 325 buffer.writeBool(fStitchTiles); | 263 buffer.writeBool(fStitchTiles); |
| 326 buffer.writeInt(fTileSize.fWidth); | 264 buffer.writeInt(fTileSize.fWidth); |
| 327 buffer.writeInt(fTileSize.fHeight); | 265 buffer.writeInt(fTileSize.fHeight); |
| 328 } | 266 } |
| 329 | 267 |
| 330 void SkPerlinNoiseShader::initPaint(PaintingData& paintingData) | 268 SkScalar SkPerlinNoiseShader::noise2D(int channel, const StitchData& stitchData, |
| 331 { | 269 const SkPoint& noiseVector) const { |
| 332 paintingData.init(fSeed); | |
| 333 | |
| 334 // Set frequencies to original values | |
| 335 paintingData.fBaseFrequency.set(fBaseFrequencyX, fBaseFrequencyY); | |
| 336 // Adjust frequecies based on size if stitching is enabled | |
| 337 if (fStitchTiles) { | |
| 338 paintingData.stitch(); | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 void SkPerlinNoiseShader::setTileSize(const SkISize& tileSize) { | |
| 343 fTileSize = tileSize; | |
| 344 | |
| 345 if (NULL == fPaintingData) { | |
| 346 fPaintingData = SkNEW_ARGS(PaintingData, (fTileSize)); | |
| 347 initPaint(*fPaintingData); | |
| 348 } else { | |
| 349 // Set Size | |
| 350 fPaintingData->fTileSize = fTileSize; | |
| 351 // Set frequencies to original values | |
| 352 fPaintingData->fBaseFrequency.set(fBaseFrequencyX, fBaseFrequencyY); | |
| 353 // Adjust frequecies based on size if stitching is enabled | |
| 354 if (fStitchTiles) { | |
| 355 fPaintingData->stitch(); | |
| 356 } | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 SkScalar SkPerlinNoiseShader::noise2D(int channel, const PaintingData& paintingD ata, | |
| 361 const StitchData& stitchData, const SkPoint & noiseVector) | |
| 362 { | |
| 363 struct Noise { | 270 struct Noise { |
| 364 int noisePositionIntegerValue; | 271 int noisePositionIntegerValue; |
| 365 SkScalar noisePositionFractionValue; | 272 SkScalar noisePositionFractionValue; |
| 366 Noise(SkScalar component) | 273 Noise(SkScalar component) |
| 367 { | 274 { |
| 368 SkScalar position = component + kPerlinNoise; | 275 SkScalar position = component + kPerlinNoise; |
| 369 noisePositionIntegerValue = SkScalarFloorToInt(position); | 276 noisePositionIntegerValue = SkScalarFloorToInt(position); |
| 370 noisePositionFractionValue = position - SkIntToScalar(noisePositionI ntegerValue); | 277 noisePositionFractionValue = position - SkIntToScalar(noisePositionI ntegerValue); |
| 371 } | 278 } |
| 372 }; | 279 }; |
| 373 Noise noiseX(noiseVector.x()); | 280 Noise noiseX(noiseVector.x()); |
| 374 Noise noiseY(noiseVector.y()); | 281 Noise noiseY(noiseVector.y()); |
| 375 SkScalar u, v; | 282 SkScalar u, v; |
| 376 // If stitching, adjust lattice points accordingly. | 283 // If stitching, adjust lattice points accordingly. |
| 377 if (fStitchTiles) { | 284 if (fStitchTiles) { |
| 378 noiseX.noisePositionIntegerValue = | 285 noiseX.noisePositionIntegerValue = |
| 379 checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stit chData.fWidth); | 286 checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stit chData.fWidth); |
| 380 noiseY.noisePositionIntegerValue = | 287 noiseY.noisePositionIntegerValue = |
| 381 checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stit chData.fHeight); | 288 checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stit chData.fHeight); |
| 382 } | 289 } |
| 383 noiseX.noisePositionIntegerValue &= kBlockMask; | 290 noiseX.noisePositionIntegerValue &= kBlockMask; |
| 384 noiseY.noisePositionIntegerValue &= kBlockMask; | 291 noiseY.noisePositionIntegerValue &= kBlockMask; |
| 385 int latticeIndex = | 292 int latticeIndex = fLatticeSelector[noiseX.noisePositionIntegerValue] + |
| 386 paintingData.fLatticeSelector[noiseX.noisePositionIntegerValue] + | 293 noiseY.noisePositionIntegerValue; |
| 387 noiseY.noisePositionIntegerValue; | 294 int nextLatticeIndex = fLatticeSelector[(noiseX.noisePositionIntegerValue + 1) & kBlockMask] + |
| 388 int nextLatticeIndex = | 295 noiseY.noisePositionIntegerValue; |
| 389 paintingData.fLatticeSelector[(noiseX.noisePositionIntegerValue + 1) & k BlockMask] + | |
| 390 noiseY.noisePositionIntegerValue; | |
| 391 SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue); | 296 SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue); |
| 392 SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue); | 297 SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue); |
| 393 // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html# feTurbulenceElement | 298 // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html# feTurbulenceElement |
| 394 SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue, | 299 SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue, |
| 395 noiseY.noisePositionFractionValue); // Offset (0,0) | 300 noiseY.noisePositionFractionValue); // Offset (0,0) |
| 396 u = paintingData.fGradient[channel][latticeIndex & kBlockMask].dot(fractionV alue); | 301 u = fGradient[channel][latticeIndex & kBlockMask].dot(fractionValue); |
| 397 fractionValue.fX -= SK_Scalar1; // Offset (-1,0) | 302 fractionValue.fX -= SK_Scalar1; // Offset (-1,0) |
| 398 v = paintingData.fGradient[channel][nextLatticeIndex & kBlockMask].dot(fract ionValue); | 303 v = fGradient[channel][nextLatticeIndex & kBlockMask].dot(fractionValue); |
| 399 SkScalar a = SkScalarInterp(u, v, sx); | 304 SkScalar a = SkScalarInterp(u, v, sx); |
| 400 fractionValue.fY -= SK_Scalar1; // Offset (-1,-1) | 305 fractionValue.fY -= SK_Scalar1; // Offset (-1,-1) |
| 401 v = paintingData.fGradient[channel][(nextLatticeIndex + 1) & kBlockMask].dot (fractionValue); | 306 v = fGradient[channel][(nextLatticeIndex + 1) & kBlockMask].dot(fractionValu e); |
| 402 fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1) | 307 fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1) |
| 403 u = paintingData.fGradient[channel][(latticeIndex + 1) & kBlockMask].dot(fra ctionValue); | 308 u = fGradient[channel][(latticeIndex + 1) & kBlockMask].dot(fractionValue); |
| 404 SkScalar b = SkScalarInterp(u, v, sx); | 309 SkScalar b = SkScalarInterp(u, v, sx); |
| 405 return SkScalarInterp(a, b, sy); | 310 return SkScalarInterp(a, b, sy); |
| 406 } | 311 } |
| 407 | 312 |
| 408 SkScalar SkPerlinNoiseShader::calculateTurbulenceValueForPoint( | 313 SkScalar SkPerlinNoiseShader::calculateTurbulenceValueForPoint(int channel, Stit chData& stitchData, |
| 409 int channel, const PaintingData& paintingData, StitchData& stitchData, const SkPoint& point) | 314 const SkPoint& po int) const { |
| 410 { | |
| 411 if (fStitchTiles) { | 315 if (fStitchTiles) { |
| 412 // Set up TurbulenceInitial stitch values. | 316 // Set up TurbulenceInitial stitch values. |
| 413 stitchData = paintingData.fStitchDataInit; | 317 stitchData = fStitchDataInit; |
| 414 } | 318 } |
| 415 SkScalar turbulenceFunctionResult = 0; | 319 SkScalar turbulenceFunctionResult = 0; |
| 416 SkPoint noiseVector(SkPoint::Make(SkScalarMul(point.x(), paintingData.fBaseF requency.fX), | 320 SkPoint noiseVector(SkPoint::Make(SkScalarMul(point.x(), fBaseFrequency.fX), |
| 417 SkScalarMul(point.y(), paintingData.fBaseF requency.fY))); | 321 SkScalarMul(point.y(), fBaseFrequency.fY)) ); |
| 418 SkScalar ratio = SK_Scalar1; | 322 SkScalar ratio = SK_Scalar1; |
| 419 for (int octave = 0; octave < fNumOctaves; ++octave) { | 323 for (int octave = 0; octave < fNumOctaves; ++octave) { |
| 420 SkScalar noise = noise2D(channel, paintingData, stitchData, noiseVector) ; | 324 SkScalar noise = noise2D(channel, stitchData, noiseVector); |
| 421 turbulenceFunctionResult += SkScalarDiv( | 325 turbulenceFunctionResult += SkScalarDiv( |
| 422 (fType == kFractalNoise_Type) ? noise : SkScalarAbs(noise), ratio); | 326 (fType == kFractalNoise_Type) ? noise : SkScalarAbs(noise), ratio); |
| 423 noiseVector.fX *= 2; | 327 noiseVector.fX *= 2; |
| 424 noiseVector.fY *= 2; | 328 noiseVector.fY *= 2; |
| 425 ratio *= 2; | 329 ratio *= 2; |
| 426 if (fStitchTiles) { | 330 if (fStitchTiles) { |
| 427 // Update stitch values | 331 // Update stitch values |
| 428 stitchData.fWidth *= 2; | 332 stitchData.fWidth *= 2; |
| 429 stitchData.fWrapX = stitchData.fWidth + kPerlinNoise; | 333 stitchData.fWrapX = stitchData.fWidth + kPerlinNoise; |
| 430 stitchData.fHeight *= 2; | 334 stitchData.fHeight *= 2; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 441 | 345 |
| 442 if (channel == 3) { // Scale alpha by paint value | 346 if (channel == 3) { // Scale alpha by paint value |
| 443 turbulenceFunctionResult = SkScalarMul(turbulenceFunctionResult, | 347 turbulenceFunctionResult = SkScalarMul(turbulenceFunctionResult, |
| 444 SkScalarDiv(SkIntToScalar(getPaintAlpha()), SkIntToScalar(255))); | 348 SkScalarDiv(SkIntToScalar(getPaintAlpha()), SkIntToScalar(255))); |
| 445 } | 349 } |
| 446 | 350 |
| 447 // Clamp result | 351 // Clamp result |
| 448 return SkScalarPin(turbulenceFunctionResult, 0, SK_Scalar1); | 352 return SkScalarPin(turbulenceFunctionResult, 0, SK_Scalar1); |
| 449 } | 353 } |
| 450 | 354 |
| 451 SkPMColor SkPerlinNoiseShader::shade(const SkPoint& point, StitchData& stitchDat a) { | 355 SkPMColor SkPerlinNoiseShader::shade(const SkPoint& point, StitchData& stitchDat a) const { |
| 452 SkMatrix matrix = fMatrix; | 356 SkMatrix matrix = fMatrix; |
| 453 matrix.postConcat(getLocalMatrix()); | 357 matrix.postConcat(getLocalMatrix()); |
| 454 SkMatrix invMatrix; | 358 SkMatrix invMatrix; |
| 455 if (!matrix.invert(&invMatrix)) { | 359 if (!matrix.invert(&invMatrix)) { |
| 456 invMatrix.reset(); | 360 invMatrix.reset(); |
| 457 } else { | 361 } else { |
| 458 invMatrix.postConcat(invMatrix); // Square the matrix | 362 invMatrix.postConcat(invMatrix); // Square the matrix |
| 459 } | 363 } |
| 460 // This (1,1) translation is due to WebKit's 1 based coordinates for the noi se | 364 // This (1,1) translation is due to WebKit's 1 based coordinates for the noi se |
| 461 // (as opposed to 0 based, usually). The same adjustment is in the setData() function. | 365 // (as opposed to 0 based, usually). The same adjustment is in the setData() function. |
| 462 matrix.postTranslate(SK_Scalar1, SK_Scalar1); | 366 matrix.postTranslate(SK_Scalar1, SK_Scalar1); |
| 463 SkPoint newPoint; | 367 SkPoint newPoint; |
| 464 matrix.mapPoints(&newPoint, &point, 1); | 368 matrix.mapPoints(&newPoint, &point, 1); |
| 465 invMatrix.mapPoints(&newPoint, &newPoint, 1); | 369 invMatrix.mapPoints(&newPoint, &newPoint, 1); |
| 466 newPoint.fX = SkScalarRoundToScalar(newPoint.fX); | 370 newPoint.fX = SkScalarRoundToScalar(newPoint.fX); |
| 467 newPoint.fY = SkScalarRoundToScalar(newPoint.fY); | 371 newPoint.fY = SkScalarRoundToScalar(newPoint.fY); |
| 468 | 372 |
| 469 U8CPU rgba[4]; | 373 U8CPU rgba[4]; |
| 470 for (int channel = 3; channel >= 0; --channel) { | 374 for (int channel = 3; channel >= 0; --channel) { |
| 471 rgba[channel] = SkScalarFloorToInt(255 * | 375 rgba[channel] = SkScalarFloorToInt(255 * |
| 472 calculateTurbulenceValueForPoint(channel, *fPaintingData, stitchData , newPoint)); | 376 calculateTurbulenceValueForPoint(channel, stitchData, newPoint)); |
| 473 } | 377 } |
| 474 return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]); | 378 return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]); |
| 475 } | 379 } |
| 476 | 380 |
| 477 bool SkPerlinNoiseShader::setContext(const SkBitmap& device, const SkPaint& pain t, | 381 bool SkPerlinNoiseShader::setContext(const SkBitmap& device, const SkPaint& pain t, |
| 478 const SkMatrix& matrix) { | 382 const SkMatrix& matrix) { |
| 479 fMatrix = matrix; | 383 fMatrix = matrix; |
| 480 return INHERITED::setContext(device, paint, matrix); | 384 return INHERITED::setContext(device, paint, matrix); |
| 481 } | 385 } |
| 482 | 386 |
| (...skipping 831 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1314 return cf->asNewEffect(context); | 1218 return cf->asNewEffect(context); |
| 1315 } | 1219 } |
| 1316 | 1220 |
| 1317 // Either we don't stitch tiles, either we have a valid tile size | 1221 // Either we don't stitch tiles, either we have a valid tile size |
| 1318 SkASSERT(!fStitchTiles || !fTileSize.isEmpty()); | 1222 SkASSERT(!fStitchTiles || !fTileSize.isEmpty()); |
| 1319 | 1223 |
| 1320 #ifdef SK_USE_SIMPLEX_NOISE | 1224 #ifdef SK_USE_SIMPLEX_NOISE |
| 1321 // Simplex noise is currently disabled but can be enabled by defining SK_USE _SIMPLEX_NOISE | 1225 // Simplex noise is currently disabled but can be enabled by defining SK_USE _SIMPLEX_NOISE |
| 1322 sk_ignore_unused_variable(context); | 1226 sk_ignore_unused_variable(context); |
| 1323 GrEffectRef* effect = | 1227 GrEffectRef* effect = |
| 1324 GrSimplexNoiseEffect::Create(fType, fPaintingData->fBaseFrequency, | 1228 GrSimplexNoiseEffect::Create(fType, fBaseFrequency, |
| 1325 fNumOctaves, fStitchTiles, fSeed, | 1229 fNumOctaves, fStitchTiles, fSeed, |
| 1326 this->getLocalMatrix(), paint.getAlpha()); | 1230 this->getLocalMatrix(), paint.getAlpha()); |
| 1327 #else | 1231 #else |
| 1328 GrTexture* permutationsTexture = GrLockAndRefCachedBitmapTexture( | 1232 GrTexture* permutationsTexture = GrLockAndRefCachedBitmapTexture( |
| 1329 context, *fPaintingData->getPermutationsBitmap(), NULL); | 1233 context, this->getPermutationsBitmap(), NULL); |
| 1330 GrTexture* noiseTexture = GrLockAndRefCachedBitmapTexture( | 1234 GrTexture* noiseTexture = GrLockAndRefCachedBitmapTexture( |
| 1331 context, *fPaintingData->getNoiseBitmap(), NULL); | 1235 context, this->getNoiseBitmap(), NULL); |
| 1332 | 1236 |
| 1333 GrEffectRef* effect = (NULL != permutationsTexture) && (NULL != noiseTexture ) ? | 1237 GrEffectRef* effect = (NULL != permutationsTexture) && (NULL != noiseTexture ) ? |
| 1334 GrPerlinNoiseEffect::Create(fType, fPaintingData->fBaseFrequency, | 1238 GrPerlinNoiseEffect::Create(fType, fBaseFrequency, |
| 1335 fNumOctaves, fStitchTiles, | 1239 fNumOctaves, fStitchTiles, |
| 1336 fPaintingData->fStitchDataInit, | 1240 fStitchDataInit, |
| 1337 permutationsTexture, noiseTexture, | 1241 permutationsTexture, noiseTexture, |
| 1338 this->getLocalMatrix(), paint.getAlpha()) : | 1242 this->getLocalMatrix(), paint.getAlpha()) : |
| 1339 NULL; | 1243 NULL; |
| 1340 | 1244 |
| 1341 // Unlock immediately, this is not great, but we don't have a way of | 1245 // Unlock immediately, this is not great, but we don't have a way of |
| 1342 // knowing when else to unlock it currently. TODO: Remove this when | 1246 // knowing when else to unlock it currently. TODO: Remove this when |
| 1343 // unref becomes the unlock replacement for all types of textures. | 1247 // unref becomes the unlock replacement for all types of textures. |
| 1344 if (NULL != permutationsTexture) { | 1248 if (NULL != permutationsTexture) { |
| 1345 GrUnlockAndUnrefCachedBitmapTexture(permutationsTexture); | 1249 GrUnlockAndUnrefCachedBitmapTexture(permutationsTexture); |
| 1346 } | 1250 } |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 1371 str->append("\"fractal noise\""); | 1275 str->append("\"fractal noise\""); |
| 1372 break; | 1276 break; |
| 1373 case kTurbulence_Type: | 1277 case kTurbulence_Type: |
| 1374 str->append("\"turbulence\""); | 1278 str->append("\"turbulence\""); |
| 1375 break; | 1279 break; |
| 1376 default: | 1280 default: |
| 1377 str->append("\"unknown\""); | 1281 str->append("\"unknown\""); |
| 1378 break; | 1282 break; |
| 1379 } | 1283 } |
| 1380 str->append(" base frequency: ("); | 1284 str->append(" base frequency: ("); |
| 1381 str->appendScalar(fBaseFrequencyX); | 1285 str->appendScalar(fBaseFrequency.fX); |
| 1382 str->append(", "); | 1286 str->append(", "); |
| 1383 str->appendScalar(fBaseFrequencyY); | 1287 str->appendScalar(fBaseFrequency.fY); |
| 1384 str->append(") number of octaves: "); | 1288 str->append(") number of octaves: "); |
| 1385 str->appendS32(fNumOctaves); | 1289 str->appendS32(fNumOctaves); |
| 1386 str->append(" seed: "); | 1290 str->append(" seed: "); |
| 1387 str->appendScalar(fSeed); | 1291 str->appendS32(fSeed); |
| 1388 str->append(" stitch tiles: "); | 1292 str->append(" stitch tiles: "); |
| 1389 str->append(fStitchTiles ? "true " : "false "); | 1293 str->append(fStitchTiles ? "true " : "false "); |
| 1390 | 1294 |
| 1391 this->INHERITED::toString(str); | 1295 this->INHERITED::toString(str); |
| 1392 | 1296 |
| 1393 str->append(")"); | 1297 str->append(")"); |
| 1394 } | 1298 } |
| 1395 #endif | 1299 #endif |
| OLD | NEW |