+++ /dev/null
-/*
- * 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 "SkDither.h"
-#include "SkPerlinNoiseShader.h"
-#include "SkFlattenableBuffers.h"
-#include "SkShader.h"
-#include "SkUnPreMultiply.h"
-#include "SkString.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
-#include "GrTBackendEffectFactory.h"
-#include "SkGr.h"
-#endif
-
-static const int kBlockSize = 256;
-static const int kBlockMask = kBlockSize - 1;
-static const int kPerlinNoise = 4096;
-static const int kRandMaximum = SK_MaxS32; // 2**31 - 1
-
-namespace {
-
-// noiseValue is the color component's value (or color)
-// limitValue is the maximum perlin noise array index value allowed
-// newValue is the current noise dimension (either width or height)
-inline int checkNoise(int noiseValue, int limitValue, int newValue) {
- // If the noise value would bring us out of bounds of the current noise array while we are
- // stiching noise tiles together, wrap the noise around the current dimension of the noise to
- // stay within the array bounds in a continuous fashion (so that tiling lines are not visible)
- if (noiseValue >= limitValue) {
- noiseValue -= newValue;
- }
- if (noiseValue >= limitValue - 1) {
- noiseValue -= newValue - 1;
- }
- return noiseValue;
-}
-
-inline SkScalar smoothCurve(SkScalar t) {
- static const SkScalar SK_Scalar3 = SkFloatToScalar(3.0f);
-
- // returns t * t * (3 - 2 * t)
- return SkScalarMul(SkScalarSquare(t), SK_Scalar3 - 2 * t);
-}
-
-} // end namespace
-
-struct SkPerlinNoiseShader::StitchData {
- StitchData()
- : fWidth(0)
- , fWrapX(0)
- , fHeight(0)
- , fWrapY(0)
- {}
-
- bool operator==(const StitchData& other) const {
- return fWidth == other.fWidth &&
- fWrapX == other.fWrapX &&
- fHeight == other.fHeight &&
- fWrapY == other.fWrapY;
- }
-
- int fWidth; // How much to subtract to wrap for stitching.
- int fWrapX; // Minimum value to wrap.
- int fHeight;
- int fWrapY;
-};
-
-struct SkPerlinNoiseShader::PaintingData {
- PaintingData(const SkISize& tileSize)
- : fSeed(0)
- , fTileSize(tileSize)
- , fPermutationsBitmap(NULL)
- , fNoiseBitmap(NULL)
- {}
-
- ~PaintingData()
- {
- SkDELETE(fPermutationsBitmap);
- SkDELETE(fNoiseBitmap);
- }
-
- int fSeed;
- uint8_t fLatticeSelector[kBlockSize];
- uint16_t fNoise[4][kBlockSize][2];
- SkPoint fGradient[4][kBlockSize];
- SkISize fTileSize;
- SkVector fBaseFrequency;
- StitchData fStitchDataInit;
-
-private:
-
- SkBitmap* fPermutationsBitmap;
- SkBitmap* fNoiseBitmap;
-
-public:
-
- inline int random() {
- static const int gRandAmplitude = 16807; // 7**5; primitive root of m
- static const int gRandQ = 127773; // m / a
- static const int gRandR = 2836; // m % a
-
- int result = gRandAmplitude * (fSeed % gRandQ) - gRandR * (fSeed / gRandQ);
- if (result <= 0)
- result += kRandMaximum;
- fSeed = result;
- return result;
- }
-
- void init(SkScalar seed)
- {
- static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlockSize));
-
- // The seed value clamp to the range [1, kRandMaximum - 1].
- fSeed = SkScalarRoundToInt(seed);
- if (fSeed <= 0) {
- fSeed = -(fSeed % (kRandMaximum - 1)) + 1;
- }
- if (fSeed > kRandMaximum - 1) {
- fSeed = kRandMaximum - 1;
- }
- for (int channel = 0; channel < 4; ++channel) {
- for (int i = 0; i < kBlockSize; ++i) {
- fLatticeSelector[i] = i;
- fNoise[channel][i][0] = (random() % (2 * kBlockSize));
- fNoise[channel][i][1] = (random() % (2 * kBlockSize));
- }
- }
- for (int i = kBlockSize - 1; i > 0; --i) {
- int k = fLatticeSelector[i];
- int j = random() % kBlockSize;
- SkASSERT(j >= 0);
- SkASSERT(j < kBlockSize);
- fLatticeSelector[i] = fLatticeSelector[j];
- fLatticeSelector[j] = k;
- }
-
- // Perform the permutations now
- {
- // Copy noise data
- uint16_t noise[4][kBlockSize][2];
- for (int i = 0; i < kBlockSize; ++i) {
- for (int channel = 0; channel < 4; ++channel) {
- for (int j = 0; j < 2; ++j) {
- noise[channel][i][j] = fNoise[channel][i][j];
- }
- }
- }
- // Do permutations on noise data
- for (int i = 0; i < kBlockSize; ++i) {
- for (int channel = 0; channel < 4; ++channel) {
- for (int j = 0; j < 2; ++j) {
- fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j];
- }
- }
- }
- }
-
- // Half of the largest possible value for 16 bit unsigned int
- static const SkScalar halfMax16bits = SkFloatToScalar(32767.5f);
-
- // Compute gradients from permutated noise data
- for (int channel = 0; channel < 4; ++channel) {
- for (int i = 0; i < kBlockSize; ++i) {
- fGradient[channel][i] = SkPoint::Make(
- SkScalarMul(SkIntToScalar(fNoise[channel][i][0] - kBlockSize),
- gInvBlockSizef),
- SkScalarMul(SkIntToScalar(fNoise[channel][i][1] - kBlockSize),
- gInvBlockSizef));
- fGradient[channel][i].normalize();
- // Put the normalized gradient back into the noise data
- fNoise[channel][i][0] = SkScalarRoundToInt(SkScalarMul(
- fGradient[channel][i].fX + SK_Scalar1, halfMax16bits));
- fNoise[channel][i][1] = SkScalarRoundToInt(SkScalarMul(
- fGradient[channel][i].fY + SK_Scalar1, halfMax16bits));
- }
- }
-
- // Invalidate bitmaps
- SkDELETE(fPermutationsBitmap);
- fPermutationsBitmap = NULL;
- SkDELETE(fNoiseBitmap);
- fNoiseBitmap = NULL;
- }
-
- void stitch() {
- SkScalar tileWidth = SkIntToScalar(fTileSize.width());
- SkScalar tileHeight = SkIntToScalar(fTileSize.height());
- SkASSERT(tileWidth > 0 && tileHeight > 0);
- // When stitching tiled turbulence, the frequencies must be adjusted
- // so that the tile borders will be continuous.
- if (fBaseFrequency.fX) {
- SkScalar lowFrequencx = SkScalarDiv(
- SkScalarMulFloor(tileWidth, fBaseFrequency.fX), tileWidth);
- SkScalar highFrequencx = SkScalarDiv(
- SkScalarMulCeil(tileWidth, fBaseFrequency.fX), tileWidth);
- // BaseFrequency should be non-negative according to the standard.
- if (SkScalarDiv(fBaseFrequency.fX, lowFrequencx) <
- SkScalarDiv(highFrequencx, fBaseFrequency.fX)) {
- fBaseFrequency.fX = lowFrequencx;
- } else {
- fBaseFrequency.fX = highFrequencx;
- }
- }
- if (fBaseFrequency.fY) {
- SkScalar lowFrequency = SkScalarDiv(
- SkScalarMulFloor(tileHeight, fBaseFrequency.fY), tileHeight);
- SkScalar highFrequency = SkScalarDiv(
- SkScalarMulCeil(tileHeight, fBaseFrequency.fY), tileHeight);
- if (SkScalarDiv(fBaseFrequency.fY, lowFrequency) <
- SkScalarDiv(highFrequency, fBaseFrequency.fY)) {
- fBaseFrequency.fY = lowFrequency;
- } else {
- fBaseFrequency.fY = highFrequency;
- }
- }
- // Set up TurbulenceInitial stitch values.
- fStitchDataInit.fWidth =
- SkScalarMulRound(tileWidth, fBaseFrequency.fX);
- fStitchDataInit.fWrapX = kPerlinNoise + fStitchDataInit.fWidth;
- fStitchDataInit.fHeight =
- SkScalarMulRound(tileHeight, fBaseFrequency.fY);
- fStitchDataInit.fWrapY = kPerlinNoise + fStitchDataInit.fHeight;
- }
-
- SkBitmap* getPermutationsBitmap()
- {
- if (!fPermutationsBitmap) {
- fPermutationsBitmap = SkNEW(SkBitmap);
- fPermutationsBitmap->setConfig(SkBitmap::kA8_Config, kBlockSize, 1);
- fPermutationsBitmap->allocPixels();
- uint8_t* bitmapPixels = fPermutationsBitmap->getAddr8(0, 0);
- memcpy(bitmapPixels, fLatticeSelector, sizeof(uint8_t) * kBlockSize);
- }
- return fPermutationsBitmap;
- }
-
- SkBitmap* getNoiseBitmap()
- {
- if (!fNoiseBitmap) {
- fNoiseBitmap = SkNEW(SkBitmap);
- fNoiseBitmap->setConfig(SkBitmap::kARGB_8888_Config, kBlockSize, 4);
- fNoiseBitmap->allocPixels();
- uint32_t* bitmapPixels = fNoiseBitmap->getAddr32(0, 0);
- memcpy(bitmapPixels, fNoise[0][0], sizeof(uint16_t) * kBlockSize * 4 * 2);
- }
- return fNoiseBitmap;
- }
-};
-
-SkShader* SkPerlinNoiseShader::CreateFractalNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
- int numOctaves, SkScalar seed,
- const SkISize* tileSize) {
- return SkNEW_ARGS(SkPerlinNoiseShader, (kFractalNoise_Type, baseFrequencyX, baseFrequencyY,
- numOctaves, seed, tileSize));
-}
-
-SkShader* SkPerlinNoiseShader::CreateTubulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
- int numOctaves, SkScalar seed,
- const SkISize* tileSize) {
- return SkNEW_ARGS(SkPerlinNoiseShader, (kTurbulence_Type, baseFrequencyX, baseFrequencyY,
- numOctaves, seed, tileSize));
-}
-
-SkPerlinNoiseShader::SkPerlinNoiseShader(SkPerlinNoiseShader::Type type,
- SkScalar baseFrequencyX,
- SkScalar baseFrequencyY,
- int numOctaves,
- SkScalar seed,
- const SkISize* tileSize)
- : fType(type)
- , fBaseFrequencyX(baseFrequencyX)
- , fBaseFrequencyY(baseFrequencyY)
- , fNumOctaves(numOctaves)
- , fSeed(seed)
- , fStitchTiles((tileSize != NULL) && !tileSize->isEmpty())
- , fPaintingData(NULL)
-{
- setTileSize(fStitchTiles ? *tileSize : SkISize::Make(0,0));
- fMatrix.reset();
-}
-
-SkPerlinNoiseShader::SkPerlinNoiseShader(SkFlattenableReadBuffer& buffer) :
- INHERITED(buffer), fPaintingData(NULL) {
- fType = (SkPerlinNoiseShader::Type) buffer.readInt();
- fBaseFrequencyX = buffer.readScalar();
- fBaseFrequencyY = buffer.readScalar();
- fNumOctaves = buffer.readInt();
- fSeed = buffer.readScalar();
- fStitchTiles = buffer.readBool();
- fTileSize.fWidth = buffer.readInt();
- fTileSize.fHeight = buffer.readInt();
- setTileSize(fTileSize);
- fMatrix.reset();
-}
-
-SkPerlinNoiseShader::~SkPerlinNoiseShader() {
- // Safety, should have been done in endContext()
- SkDELETE(fPaintingData);
-}
-
-void SkPerlinNoiseShader::flatten(SkFlattenableWriteBuffer& buffer) const {
- this->INHERITED::flatten(buffer);
- buffer.writeInt((int) fType);
- buffer.writeScalar(fBaseFrequencyX);
- buffer.writeScalar(fBaseFrequencyY);
- buffer.writeInt(fNumOctaves);
- buffer.writeScalar(fSeed);
- buffer.writeBool(fStitchTiles);
- buffer.writeInt(fTileSize.fWidth);
- buffer.writeInt(fTileSize.fHeight);
-}
-
-void SkPerlinNoiseShader::initPaint(PaintingData& paintingData)
-{
- paintingData.init(fSeed);
-
- // Set frequencies to original values
- paintingData.fBaseFrequency.set(fBaseFrequencyX, fBaseFrequencyY);
- // Adjust frequecies based on size if stitching is enabled
- if (fStitchTiles) {
- paintingData.stitch();
- }
-}
-
-void SkPerlinNoiseShader::setTileSize(const SkISize& tileSize) {
- fTileSize = tileSize;
-
- if (NULL == fPaintingData) {
- fPaintingData = SkNEW_ARGS(PaintingData, (fTileSize));
- initPaint(*fPaintingData);
- } else {
- // Set Size
- fPaintingData->fTileSize = fTileSize;
- // Set frequencies to original values
- fPaintingData->fBaseFrequency.set(fBaseFrequencyX, fBaseFrequencyY);
- // Adjust frequecies based on size if stitching is enabled
- if (fStitchTiles) {
- fPaintingData->stitch();
- }
- }
-}
-
-SkScalar SkPerlinNoiseShader::noise2D(int channel, const PaintingData& paintingData,
- const StitchData& stitchData, const SkPoint& noiseVector)
-{
- struct Noise {
- int noisePositionIntegerValue;
- SkScalar noisePositionFractionValue;
- Noise(SkScalar component)
- {
- SkScalar position = component + kPerlinNoise;
- noisePositionIntegerValue = SkScalarFloorToInt(position);
- noisePositionFractionValue = position - SkIntToScalar(noisePositionIntegerValue);
- }
- };
- Noise noiseX(noiseVector.x());
- Noise noiseY(noiseVector.y());
- SkScalar u, v;
- // If stitching, adjust lattice points accordingly.
- if (fStitchTiles) {
- noiseX.noisePositionIntegerValue =
- checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
- noiseY.noisePositionIntegerValue =
- checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
- }
- noiseX.noisePositionIntegerValue &= kBlockMask;
- noiseY.noisePositionIntegerValue &= kBlockMask;
- int latticeIndex =
- paintingData.fLatticeSelector[noiseX.noisePositionIntegerValue] +
- noiseY.noisePositionIntegerValue;
- int nextLatticeIndex =
- paintingData.fLatticeSelector[(noiseX.noisePositionIntegerValue + 1) & kBlockMask] +
- noiseY.noisePositionIntegerValue;
- SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue);
- SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue);
- // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement
- SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue,
- noiseY.noisePositionFractionValue); // Offset (0,0)
- u = paintingData.fGradient[channel][latticeIndex & kBlockMask].dot(fractionValue);
- fractionValue.fX -= SK_Scalar1; // Offset (-1,0)
- v = paintingData.fGradient[channel][nextLatticeIndex & kBlockMask].dot(fractionValue);
- SkScalar a = SkScalarInterp(u, v, sx);
- fractionValue.fY -= SK_Scalar1; // Offset (-1,-1)
- v = paintingData.fGradient[channel][(nextLatticeIndex + 1) & kBlockMask].dot(fractionValue);
- fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1)
- u = paintingData.fGradient[channel][(latticeIndex + 1) & kBlockMask].dot(fractionValue);
- SkScalar b = SkScalarInterp(u, v, sx);
- return SkScalarInterp(a, b, sy);
-}
-
-SkScalar SkPerlinNoiseShader::calculateTurbulenceValueForPoint(
- int channel, const PaintingData& paintingData, StitchData& stitchData, const SkPoint& point)
-{
- if (fStitchTiles) {
- // Set up TurbulenceInitial stitch values.
- stitchData = paintingData.fStitchDataInit;
- }
- SkScalar turbulenceFunctionResult = 0;
- SkPoint noiseVector(SkPoint::Make(SkScalarMul(point.x(), paintingData.fBaseFrequency.fX),
- SkScalarMul(point.y(), paintingData.fBaseFrequency.fY)));
- SkScalar ratio = SK_Scalar1;
- for (int octave = 0; octave < fNumOctaves; ++octave) {
- SkScalar noise = noise2D(channel, paintingData, stitchData, noiseVector);
- turbulenceFunctionResult += SkScalarDiv(
- (fType == kFractalNoise_Type) ? noise : SkScalarAbs(noise), ratio);
- noiseVector.fX *= 2;
- noiseVector.fY *= 2;
- ratio *= 2;
- if (fStitchTiles) {
- // Update stitch values
- stitchData.fWidth *= 2;
- stitchData.fWrapX = stitchData.fWidth + kPerlinNoise;
- stitchData.fHeight *= 2;
- stitchData.fWrapY = stitchData.fHeight + kPerlinNoise;
- }
- }
-
- // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
- // by fractalNoise and (turbulenceFunctionResult) by turbulence.
- if (fType == kFractalNoise_Type) {
- turbulenceFunctionResult =
- SkScalarMul(turbulenceFunctionResult, SK_ScalarHalf) + SK_ScalarHalf;
- }
-
- if (channel == 3) { // Scale alpha by paint value
- turbulenceFunctionResult = SkScalarMul(turbulenceFunctionResult,
- SkScalarDiv(SkIntToScalar(getPaintAlpha()), SkIntToScalar(255)));
- }
-
- // Clamp result
- return SkScalarPin(turbulenceFunctionResult, 0, SK_Scalar1);
-}
-
-SkPMColor SkPerlinNoiseShader::shade(const SkPoint& point, StitchData& stitchData) {
- SkMatrix matrix = fMatrix;
- SkMatrix invMatrix;
- if (!matrix.invert(&invMatrix)) {
- invMatrix.reset();
- } else {
- invMatrix.postConcat(invMatrix); // Square the matrix
- }
- // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
- // (as opposed to 0 based, usually). The same adjustment is in the setData() function.
- matrix.postTranslate(SK_Scalar1, SK_Scalar1);
- SkPoint newPoint;
- matrix.mapPoints(&newPoint, &point, 1);
- invMatrix.mapPoints(&newPoint, &newPoint, 1);
- newPoint.fX = SkScalarRoundToScalar(newPoint.fX);
- newPoint.fY = SkScalarRoundToScalar(newPoint.fY);
-
- U8CPU rgba[4];
- for (int channel = 3; channel >= 0; --channel) {
- rgba[channel] = SkScalarFloorToInt(255 *
- calculateTurbulenceValueForPoint(channel, *fPaintingData, stitchData, newPoint));
- }
- return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]);
-}
-
-bool SkPerlinNoiseShader::setContext(const SkBitmap& device, const SkPaint& paint,
- const SkMatrix& matrix) {
- fMatrix = matrix;
- return INHERITED::setContext(device, paint, matrix);
-}
-
-void SkPerlinNoiseShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
- SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
- StitchData stitchData;
- for (int i = 0; i < count; ++i) {
- result[i] = shade(point, stitchData);
- point.fX += SK_Scalar1;
- }
-}
-
-void SkPerlinNoiseShader::shadeSpan16(int x, int y, uint16_t result[], int count) {
- SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
- StitchData stitchData;
- DITHER_565_SCAN(y);
- for (int i = 0; i < count; ++i) {
- unsigned dither = DITHER_VALUE(x);
- result[i] = SkDitherRGB32To565(shade(point, stitchData), dither);
- DITHER_INC_X(x);
- point.fX += SK_Scalar1;
- }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "GrTBackendEffectFactory.h"
-
-class GrGLPerlinNoise : public GrGLEffect {
-public:
-
- GrGLPerlinNoise(const GrBackendEffectFactory& factory,
- const GrDrawEffect& drawEffect);
- virtual ~GrGLPerlinNoise() { }
-
- virtual void emitCode(GrGLShaderBuilder*,
- const GrDrawEffect&,
- EffectKey,
- const char* outputColor,
- const char* inputColor,
- const TextureSamplerArray&) SK_OVERRIDE;
-
- static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
-
- virtual void setData(const GrGLUniformManager&, const GrDrawEffect&);
-
-private:
- SkPerlinNoiseShader::Type fType;
- bool fStitchTiles;
- GrGLUniformManager::UniformHandle fBaseFrequencyUni;
- GrGLUniformManager::UniformHandle fNumOctavesUni;
- GrGLUniformManager::UniformHandle fStitchDataUni;
- GrGLUniformManager::UniformHandle fAlphaUni;
- GrGLUniformManager::UniformHandle fInvMatrixUni;
- GrGLEffectMatrix fEffectMatrix;
-
- typedef GrGLEffect INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-class GrPerlinNoiseEffect : public GrEffect {
-public:
- static GrEffectRef* Create(SkPerlinNoiseShader::Type type, const SkVector& baseFrequency,
- SkScalar numOctaves, bool stitchTiles,
- const SkPerlinNoiseShader::StitchData& stitchData,
- GrTexture* permutationsTexture, GrTexture* noiseTexture,
- const SkMatrix& matrix, uint8_t alpha) {
- AutoEffectUnref effect(SkNEW_ARGS(GrPerlinNoiseEffect, (type, baseFrequency, numOctaves,
- stitchTiles, stitchData, permutationsTexture, noiseTexture, matrix, alpha)));
- return CreateEffectRef(effect);
- }
-
- virtual ~GrPerlinNoiseEffect() { }
-
- static const char* Name() { return "PerlinNoise"; }
- virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
- return GrTBackendEffectFactory<GrPerlinNoiseEffect>::getInstance();
- }
- SkPerlinNoiseShader::Type type() const { return fType; }
- bool stitchTiles() const { return fStitchTiles; }
- const SkVector& baseFrequency() const { return fBaseFrequency; }
- SkScalar numOctaves() const { return fNumOctaves; }
- const SkPerlinNoiseShader::StitchData& stitchData() const { return fStitchData; }
- const SkMatrix& matrix() const { return fMatrix; }
- uint8_t alpha() const { return fAlpha; }
- GrGLEffectMatrix::CoordsType coordsType() const { return GrEffect::kLocal_CoordsType; }
-
- typedef GrGLPerlinNoise GLEffect;
-
- void getConstantColorComponents(GrColor*, uint32_t* validFlags) const SK_OVERRIDE {
- *validFlags = 0; // This is noise. Nothing is constant.
- }
-
-private:
- virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
- const GrPerlinNoiseEffect& s = CastEffect<GrPerlinNoiseEffect>(sBase);
- return fPermutationsAccess.getTexture() == s.fPermutationsAccess.getTexture() &&
- fNoiseAccess.getTexture() == s.fNoiseAccess.getTexture() &&
- fType == s.fType &&
- fBaseFrequency == s.fBaseFrequency &&
- fStitchTiles == s.fStitchTiles &&
- fStitchData == s.fStitchData &&
- fMatrix == s.fMatrix &&
- fAlpha == s.fAlpha;
- }
-
- GrPerlinNoiseEffect(SkPerlinNoiseShader::Type type, const SkVector& baseFrequency,
- SkScalar numOctaves, bool stitchTiles,
- const SkPerlinNoiseShader::StitchData& stitchData,
- GrTexture* permutationsTexture, GrTexture* noiseTexture,
- const SkMatrix& matrix, uint8_t alpha)
- : fPermutationsAccess(permutationsTexture)
- , fNoiseAccess(noiseTexture)
- , fType(type)
- , fBaseFrequency(baseFrequency)
- , fNumOctaves(numOctaves)
- , fStitchTiles(stitchTiles)
- , fStitchData(stitchData)
- , fMatrix(matrix)
- , fAlpha(alpha)
- {
- this->addTextureAccess(&fPermutationsAccess);
- this->addTextureAccess(&fNoiseAccess);
- }
-
- GR_DECLARE_EFFECT_TEST;
-
- GrTextureAccess fPermutationsAccess;
- GrTextureAccess fNoiseAccess;
- SkPerlinNoiseShader::Type fType;
- SkVector fBaseFrequency;
- SkScalar fNumOctaves;
- bool fStitchTiles;
- SkPerlinNoiseShader::StitchData fStitchData;
- SkMatrix fMatrix;
- uint8_t fAlpha;
-
- typedef GrEffect INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_EFFECT_TEST(GrPerlinNoiseEffect);
-
-GrEffectRef* GrPerlinNoiseEffect::TestCreate(SkMWCRandom* random,
- GrContext* context,
- const GrDrawTargetCaps&,
- GrTexture**) {
- int numOctaves = random->nextRangeU(2, 10);
- bool stitchTiles = random->nextBool();
- SkScalar seed = SkIntToScalar(random->nextU());
- SkISize tileSize = SkISize::Make(random->nextRangeU(4, 4096), random->nextRangeU(4, 4096));
- SkScalar baseFrequencyX = random->nextRangeScalar(SkFloatToScalar(0.01f),
- SkFloatToScalar(0.99f));
- SkScalar baseFrequencyY = random->nextRangeScalar(SkFloatToScalar(0.01f),
- SkFloatToScalar(0.99f));
-
- SkShader* shader = random->nextBool() ?
- SkPerlinNoiseShader::CreateFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed,
- stitchTiles ? &tileSize : NULL) :
- SkPerlinNoiseShader::CreateTubulence(baseFrequencyX, baseFrequencyY, numOctaves, seed,
- stitchTiles ? &tileSize : NULL);
-
- SkPaint paint;
- GrEffectRef* effect = shader->asNewEffect(context, paint);
-
- SkDELETE(shader);
-
- return effect;
-}
-
-/////////////////////////////////////////////////////////////////////
-
-void GrGLPerlinNoise::emitCode(GrGLShaderBuilder* builder,
- const GrDrawEffect&,
- EffectKey key,
- const char* outputColor,
- const char* inputColor,
- const TextureSamplerArray& samplers) {
- sk_ignore_unused_variable(inputColor);
-
- const char* vCoords;
- fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &vCoords);
-
- fInvMatrixUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
- kMat33f_GrSLType, "invMatrix");
- const char* invMatrixUni = builder->getUniformCStr(fInvMatrixUni);
- fBaseFrequencyUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
- kVec2f_GrSLType, "baseFrequency");
- const char* baseFrequencyUni = builder->getUniformCStr(fBaseFrequencyUni);
- fNumOctavesUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
- kFloat_GrSLType, "numOctaves");
- const char* numOctavesUni = builder->getUniformCStr(fNumOctavesUni);
- fAlphaUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
- kFloat_GrSLType, "alpha");
- const char* alphaUni = builder->getUniformCStr(fAlphaUni);
-
- const char* stitchDataUni = NULL;
- if (fStitchTiles) {
- fStitchDataUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
- kVec4f_GrSLType, "stitchData");
- stitchDataUni = builder->getUniformCStr(fStitchDataUni);
- }
-
- const char* chanCoords = "chanCoords";
- const char* stitchData = "stitchData";
- const char* ratio = "ratio";
- const char* noise = "noise";
- const char* noiseXY = "noiseXY";
- const char* noiseVec = "noiseVec";
- const char* noiseVecIni = "noiseVecIni";
- const char* noiseSmooth = "noiseSmooth";
- const char* fractVal = "fractVal";
- const char* uv = "uv";
- const char* ab = "ab";
- const char* latticeIdx = "latticeIdx";
- const char* lattice = "lattice";
- const char* perlinNoise = "4096.0";
- const char* inc8bit = "0.00390625"; // 1.0 / 256.0
- // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a
- // [-1,1] vector and perform a dot product between that vector and the provided vector.
- const char* dotLattice = "dot(((%s.ga + %s.rb * vec2(%s)) * vec2(2.0) - vec2(1.0)), %s);";
-
- // There are 4 lines, so the center of each line is 1/8, 3/8, 5/8 and 7/8
- builder->fsCodeAppendf("\t\tconst vec4 %s = vec4(0.125, 0.375, 0.625, 0.875);", chanCoords);
-
- // There are rounding errors if the floor operation is not performed here
- builder->fsCodeAppendf("\t\tvec2 %s = floor((%s*vec3(%s, 1.0)).xy) * %s;",
- noiseVecIni, invMatrixUni, vCoords, baseFrequencyUni);
-
- // Loop over the 4 channels
- builder->fsCodeAppend("\t\tfor (int channel = 3; channel >= 0; --channel) {");
-
- if (fStitchTiles) {
- // Set up TurbulenceInitial stitch values.
- builder->fsCodeAppendf("\t\tvec4 %s = %s;", stitchData, stitchDataUni);
- }
-
- builder->fsCodeAppendf("\t\t%s[channel] = 0.0;", outputColor);
-
- builder->fsCodeAppendf("\t\tfloat %s = 1.0;", ratio);
- builder->fsCodeAppendf("\t\tvec2 %s = %s;", noiseVec, noiseVecIni);
-
- // Loop over all octaves
- builder->fsCodeAppendf("\t\tfor (int octave = 0; octave < %s; ++octave) {", numOctavesUni);
-
- builder->fsCodeAppendf("\t\tvec4 %s = vec4(floor(%s) + vec2(%s), fract(%s));",
- noiseXY, noiseVec, perlinNoise, noiseVec);
-
- // smooth curve : t * t * (3 - 2 * t)
- builder->fsCodeAppendf("\t\tvec2 %s = %s.zw * %s.zw * (vec2(3.0) - vec2(2.0) * %s.zw);",
- noiseSmooth, noiseXY, noiseXY, noiseXY);
-
- // Adjust frequencies if we're stitching tiles
- if (fStitchTiles) {
- builder->fsCodeAppendf("\t\tif(%s.x >= %s.y) { %s.x -= %s.x; }",
- noiseXY, stitchData, noiseXY, stitchData);
- builder->fsCodeAppendf("\t\tif(%s.x >= (%s.y - 1.0)) { %s.x -= (%s.x - 1.0); }",
- noiseXY, stitchData, noiseXY, stitchData);
- builder->fsCodeAppendf("\t\tif(%s.y >= %s.w) { %s.y -= %s.z; }",
- noiseXY, stitchData, noiseXY, stitchData);
- builder->fsCodeAppendf("\t\tif(%s.y >= (%s.w - 1.0)) { %s.y -= (%s.z - 1.0); }",
- noiseXY, stitchData, noiseXY, stitchData);
- }
-
- // Get texture coordinates and normalize
- builder->fsCodeAppendf("\t\t%s.xy = fract(floor(mod(%s.xy, 256.0)) / vec2(256.0));",
- noiseXY, noiseXY);
-
- // Get permutation for x
- {
- SkString xCoords("");
- xCoords.appendf("vec2(%s.x, 0.5)", noiseXY);
-
- builder->fsCodeAppendf("\t\tvec2 %s;\t\t%s.x = ", latticeIdx, latticeIdx);
- builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType,
- samplers[0], xCoords.c_str(), kVec2f_GrSLType);
- builder->fsCodeAppend(".r;\n");
- }
-
- // Get permutation for x + 1
- {
- SkString xCoords("");
- xCoords.appendf("vec2(fract(%s.x + %s), 0.5)", noiseXY, inc8bit);
-
- builder->fsCodeAppendf("\t\t%s.y = ", latticeIdx);
- builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType,
- samplers[0], xCoords.c_str(), kVec2f_GrSLType);
- builder->fsCodeAppend(".r;\n");
- }
-
- // Get (x,y) coordinates with the permutated x
- builder->fsCodeAppendf("\t\t%s = fract(%s + %s.yy);", latticeIdx, latticeIdx, noiseXY);
-
- builder->fsCodeAppendf("\t\tvec2 %s = %s.zw;", fractVal, noiseXY);
-
- builder->fsCodeAppendf("\t\tvec2 %s;", uv);
- // Compute u, at offset (0,0)
- {
- SkString latticeCoords("");
- latticeCoords.appendf("vec2(%s.x, %s[channel])", latticeIdx, chanCoords);
- builder->fsCodeAppendf("vec4 %s = ", lattice);
- builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType,
- samplers[1], latticeCoords.c_str(), kVec2f_GrSLType);
- builder->fsCodeAppendf(".bgra;\n\t\t%s.x = ", uv);
- builder->fsCodeAppendf(dotLattice, lattice, lattice, inc8bit, fractVal);
- }
-
- builder->fsCodeAppendf("\t\t%s.x -= 1.0;", fractVal);
- // Compute v, at offset (-1,0)
- {
- SkString latticeCoords("");
- latticeCoords.appendf("vec2(%s.y, %s[channel])", latticeIdx, chanCoords);
- builder->fsCodeAppend("lattice = ");
- builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType,
- samplers[1], latticeCoords.c_str(), kVec2f_GrSLType);
- builder->fsCodeAppendf(".bgra;\n\t\t%s.y = ", uv);
- builder->fsCodeAppendf(dotLattice, lattice, lattice, inc8bit, fractVal);
- }
-
- // Compute 'a' as a linear interpolation of 'u' and 'v'
- builder->fsCodeAppendf("\t\tvec2 %s;", ab);
- builder->fsCodeAppendf("\t\t%s.x = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);
-
- builder->fsCodeAppendf("\t\t%s.y -= 1.0;", fractVal);
- // Compute v, at offset (-1,-1)
- {
- SkString latticeCoords("");
- latticeCoords.appendf("vec2(fract(%s.y + %s), %s[channel])",
- latticeIdx, inc8bit, chanCoords);
- builder->fsCodeAppend("lattice = ");
- builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType,
- samplers[1], latticeCoords.c_str(), kVec2f_GrSLType);
- builder->fsCodeAppendf(".bgra;\n\t\t%s.y = ", uv);
- builder->fsCodeAppendf(dotLattice, lattice, lattice, inc8bit, fractVal);
- }
-
- builder->fsCodeAppendf("\t\t%s.x += 1.0;", fractVal);
- // Compute u, at offset (0,-1)
- {
- SkString latticeCoords("");
- latticeCoords.appendf("vec2(fract(%s.x + %s), %s[channel])",
- latticeIdx, inc8bit, chanCoords);
- builder->fsCodeAppend("lattice = ");
- builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType,
- samplers[1], latticeCoords.c_str(), kVec2f_GrSLType);
- builder->fsCodeAppendf(".bgra;\n\t\t%s.x = ", uv);
- builder->fsCodeAppendf(dotLattice, lattice, lattice, inc8bit, fractVal);
- }
-
- // Compute 'b' as a linear interpolation of 'u' and 'v'
- builder->fsCodeAppendf("\t\t%s.y = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);
- // Compute the noise as a linear interpolation of 'a' and 'b'
- builder->fsCodeAppendf("\t\tfloat %s = mix(%s.x, %s.y, %s.y);", noise, ab, ab, noiseSmooth);
-
- builder->fsCodeAppendf("\t\t%s[channel] += ", outputColor);
- builder->fsCodeAppendf((fType == SkPerlinNoiseShader::kFractalNoise_Type) ?
- "%s / %s;" : "abs(%s) / %s;", noise, ratio);
-
- builder->fsCodeAppendf("\t\t%s *= vec2(2.0);", noiseVec);
- builder->fsCodeAppendf("\t\t%s *= 2.0;", ratio);
-
- if (fStitchTiles) {
- builder->fsCodeAppendf("\t\t%s.xz *= vec2(2.0);", stitchData);
- builder->fsCodeAppendf("\t\t%s.yw = %s.xz + vec2(%s);", stitchData, stitchData, perlinNoise);
- }
- builder->fsCodeAppend("\t\t}"); // end of the for loop on octaves
-
- builder->fsCodeAppend("\t\t}"); // end of the for loop on channels
-
- if (fType == SkPerlinNoiseShader::kFractalNoise_Type) {
- // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
- // by fractalNoise and (turbulenceFunctionResult) by turbulence.
- builder->fsCodeAppendf("\t\t%s = %s * vec4(0.5) + vec4(0.5);", outputColor, outputColor);
- }
-
- builder->fsCodeAppendf("\t\t%s.a *= %s;", outputColor, alphaUni);
-
- // Clamp values
- builder->fsCodeAppendf("\t\t%s = clamp(%s, 0.0, 1.0);", outputColor, outputColor);
-
- // Pre-multiply the result
- builder->fsCodeAppendf("\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n",
- outputColor, outputColor, outputColor, outputColor);
-}
-
-GrGLPerlinNoise::GrGLPerlinNoise(const GrBackendEffectFactory& factory,
- const GrDrawEffect& drawEffect)
- : INHERITED (factory)
- , fType(drawEffect.castEffect<GrPerlinNoiseEffect>().type())
- , fStitchTiles(drawEffect.castEffect<GrPerlinNoiseEffect>().stitchTiles())
- , fEffectMatrix(drawEffect.castEffect<GrPerlinNoiseEffect>().coordsType()) {
-}
-
-GrGLEffect::EffectKey GrGLPerlinNoise::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
- const GrPerlinNoiseEffect& turbulence = drawEffect.castEffect<GrPerlinNoiseEffect>();
-
- EffectKey key = 0;
-
- switch (turbulence.type()) {
- case SkPerlinNoiseShader::kFractalNoise_Type:
- key = 0x1;
- break;
- case SkPerlinNoiseShader::kTurbulence_Type:
- key = 0x2;
- break;
- default:
- // leave key at 0
- break;
- }
-
- if (turbulence.stitchTiles()) {
- key |= 0x4; // Flip the 3rd bit if tile stitching is on
- }
-
- key = key << GrGLEffectMatrix::kKeyBits;
-
- SkMatrix m = turbulence.matrix();
- m.postTranslate(SK_Scalar1, SK_Scalar1);
- return key | GrGLEffectMatrix::GenKey(m, drawEffect,
- drawEffect.castEffect<GrPerlinNoiseEffect>().coordsType(), NULL);
-}
-
-void GrGLPerlinNoise::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
- const GrPerlinNoiseEffect& turbulence = drawEffect.castEffect<GrPerlinNoiseEffect>();
-
- const SkVector& baseFrequency = turbulence.baseFrequency();
- uman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY);
- uman.set1f(fNumOctavesUni, turbulence.numOctaves());
- if (turbulence.stitchTiles()) {
- const SkPerlinNoiseShader::StitchData& stitchData = turbulence.stitchData();
- uman.set4f(fStitchDataUni, SkIntToScalar(stitchData.fWidth),
- SkIntToScalar(stitchData.fWrapX),
- SkIntToScalar(stitchData.fHeight),
- SkIntToScalar(stitchData.fWrapY));
- }
-
- uman.set1f(fAlphaUni, SkScalarDiv(SkIntToScalar(turbulence.alpha()), SkIntToScalar(255)));
-
- SkMatrix m = turbulence.matrix();
- SkMatrix invM;
- if (!m.invert(&invM)) {
- invM.reset();
- } else {
- invM.postConcat(invM); // Square the matrix
- }
- uman.setSkMatrix(fInvMatrixUni, invM);
-
- // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
- // (as opposed to 0 based, usually). The same adjustment is in the shadeSpan() functions.
- m.postTranslate(SK_Scalar1, SK_Scalar1);
- fEffectMatrix.setData(uman, m, drawEffect, NULL);
-}
-
-/////////////////////////////////////////////////////////////////////
-
-GrEffectRef* SkPerlinNoiseShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
- SkASSERT(NULL != context);
-
- // Either we don't stitch tiles, either we have a valid tile size
- SkASSERT(!fStitchTiles || !fTileSize.isEmpty());
-
- GrTexture* permutationsTexture = GrLockAndRefCachedBitmapTexture(
- context, *fPaintingData->getPermutationsBitmap(), NULL);
- GrTexture* noiseTexture = GrLockAndRefCachedBitmapTexture(
- context, *fPaintingData->getNoiseBitmap(), NULL);
-
- GrEffectRef* effect = (NULL != permutationsTexture) && (NULL != noiseTexture) ?
- GrPerlinNoiseEffect::Create(fType, fPaintingData->fBaseFrequency,
- SkIntToScalar(fNumOctaves), fStitchTiles,
- fPaintingData->fStitchDataInit,
- permutationsTexture, noiseTexture,
- this->getLocalMatrix(), paint.getAlpha()) :
- NULL;
-
- // Unlock immediately, this is not great, but we don't have a way of
- // knowing when else to unlock it currently. TODO: Remove this when
- // unref becomes the unlock replacement for all types of textures.
- if (NULL != permutationsTexture) {
- GrUnlockAndUnrefCachedBitmapTexture(permutationsTexture);
- }
- if (NULL != noiseTexture) {
- GrUnlockAndUnrefCachedBitmapTexture(noiseTexture);
- }
-
- return effect;
-}
-
-#else
-
-GrEffectRef* SkPerlinNoiseShader::asNewEffect(GrContext*, const SkPaint&) const {
- SkDEBUGFAIL("Should not call in GPU-less build");
- return NULL;
-}
-
-#endif
-
-#ifdef SK_DEVELOPER
-void SkPerlinNoiseShader::toString(SkString* str) const {
- str->append("SkPerlinNoiseShader: (");
-
- str->append("type: ");
- switch (fType) {
- case kFractalNoise_Type:
- str->append("\"fractal noise\"");
- break;
- case kTurbulence_Type:
- str->append("\"turbulence\"");
- break;
- default:
- str->append("\"unknown\"");
- break;
- }
- str->append(" base frequency: (");
- str->appendScalar(fBaseFrequencyX);
- str->append(", ");
- str->appendScalar(fBaseFrequencyY);
- str->append(") number of octaves: ");
- str->appendS32(fNumOctaves);
- str->append(" seed: ");
- str->appendScalar(fSeed);
- str->append(" stitch tiles: ");
- str->append(fStitchTiles ? "true " : "false ");
-
- this->INHERITED::toString(str);
-
- str->append(")");
-}
-#endif