diff options
author | senorblanco <senorblanco@chromium.org> | 2014-06-27 13:35:52 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-06-27 13:35:52 -0700 |
commit | ca6a7c29452e13cf63bb4e225972065cbfe6e265 (patch) | |
tree | b55b8a6d110762d62e4a97d24eadc58f0c1705eb | |
parent | d3e474e20c6f0f24ddb6b2643e92975d60190daa (diff) |
Make perlin noise do CTM-correct scaling.
When drawing perlin noise, generate noise at the resolution determined
by CTM, not by the resolution at construction time. This required moving
the generation of PaintingData to getContext() and asNewEffect() for the
raster and GPU paths, respectively.
It also required adjusting the matrices used during rendering
to be translate-only.
R=sugoi@chromium.org, bsalomon@google.com
Author: senorblanco@chromium.org
Review URL: https://codereview.chromium.org/358903002
-rw-r--r-- | expectations/gm/ignored-tests.txt | 6 | ||||
-rw-r--r-- | include/effects/SkPerlinNoiseShader.h | 11 | ||||
-rw-r--r-- | src/effects/SkPerlinNoiseShader.cpp | 148 |
3 files changed, 82 insertions, 83 deletions
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index c97a0b9de2..1adb12b57c 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -45,3 +45,9 @@ bleed blurquickreject blurrects bigblurs + +# senorblanco: +# Needs rebaselining after improved perlin noise scaling +perlinnoise +perlinnoise_localmatrix +imagefiltersscaled diff --git a/include/effects/SkPerlinNoiseShader.h b/include/effects/SkPerlinNoiseShader.h index 96cad4684d..2d9dfdd69a 100644 --- a/include/effects/SkPerlinNoiseShader.h +++ b/include/effects/SkPerlinNoiseShader.h @@ -23,9 +23,9 @@ http://www.w3.org/TR/SVG/filters.html#feTurbulenceElement */ class SK_API SkPerlinNoiseShader : public SkShader { - struct PaintingData; public: struct StitchData; + struct PaintingData; /** * About the noise types : the difference between the 2 is just minor tweaks to the algorithm, @@ -77,7 +77,7 @@ public: class PerlinNoiseShaderContext : public SkShader::Context { public: PerlinNoiseShaderContext(const SkPerlinNoiseShader& shader, const ContextRec&); - virtual ~PerlinNoiseShaderContext() {} + virtual ~PerlinNoiseShaderContext(); virtual void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE; virtual void shadeSpan16(int x, int y, uint16_t[], int count) SK_OVERRIDE; @@ -85,12 +85,13 @@ public: private: SkPMColor shade(const SkPoint& point, StitchData& stitchData) const; SkScalar calculateTurbulenceValueForPoint( - int channel, const PaintingData& paintingData, + int channel, StitchData& stitchData, const SkPoint& point) const; - SkScalar noise2D(int channel, const PaintingData& paintingData, + SkScalar noise2D(int channel, const StitchData& stitchData, const SkPoint& noiseVector) const; SkMatrix fMatrix; + PaintingData* fPaintingData; typedef SkShader::Context INHERITED; }; @@ -123,8 +124,6 @@ private: /*const*/ SkISize fTileSize; /*const*/ bool fStitchTiles; - PaintingData* fPaintingData; - typedef SkShader INHERITED; }; diff --git a/src/effects/SkPerlinNoiseShader.cpp b/src/effects/SkPerlinNoiseShader.cpp index 84026b6046..7f0127ea5a 100644 --- a/src/effects/SkPerlinNoiseShader.cpp +++ b/src/effects/SkPerlinNoiseShader.cpp @@ -79,10 +79,19 @@ struct SkPerlinNoiseShader::StitchData { struct SkPerlinNoiseShader::PaintingData { PaintingData(const SkISize& tileSize, SkScalar seed, - SkScalar baseFrequencyX, SkScalar baseFrequencyY) - : fTileSize(tileSize) - , fBaseFrequency(SkPoint::Make(baseFrequencyX, baseFrequencyY)) + SkScalar baseFrequencyX, SkScalar baseFrequencyY, + const SkMatrix& matrix) { + SkVector wavelength = SkVector::Make(SkScalarInvert(baseFrequencyX), + SkScalarInvert(baseFrequencyY)); + matrix.mapVectors(&wavelength, 1); + fBaseFrequency.fX = SkScalarInvert(wavelength.fX); + fBaseFrequency.fY = SkScalarInvert(wavelength.fY); + SkVector sizeVec = SkVector::Make(SkIntToScalar(tileSize.fWidth), + SkIntToScalar(tileSize.fHeight)); + matrix.mapVectors(&sizeVec, 1); + fTileSize.fWidth = SkScalarRoundToInt(sizeVec.fX); + fTileSize.fHeight = SkScalarRoundToInt(sizeVec.fY); this->init(seed); if (!fTileSize.isEmpty()) { this->stitch(); @@ -275,7 +284,6 @@ SkPerlinNoiseShader::SkPerlinNoiseShader(SkPerlinNoiseShader::Type type, , fStitchTiles(!fTileSize.isEmpty()) { SkASSERT(numOctaves >= 0 && numOctaves < 256); - fPaintingData = SkNEW_ARGS(PaintingData, (fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY)); } SkPerlinNoiseShader::SkPerlinNoiseShader(SkReadBuffer& buffer) @@ -289,15 +297,12 @@ SkPerlinNoiseShader::SkPerlinNoiseShader(SkReadBuffer& buffer) fStitchTiles = buffer.readBool(); fTileSize.fWidth = buffer.readInt(); fTileSize.fHeight = buffer.readInt(); - fPaintingData = SkNEW_ARGS(PaintingData, (fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY)); buffer.validate(perlin_noise_type_is_valid(fType) && (fNumOctaves >= 0) && (fNumOctaves <= 255) && (fStitchTiles != fTileSize.isEmpty())); } SkPerlinNoiseShader::~SkPerlinNoiseShader() { - // Safety, should have been done in endContext() - SkDELETE(fPaintingData); } void SkPerlinNoiseShader::flatten(SkWriteBuffer& buffer) const { @@ -313,8 +318,7 @@ void SkPerlinNoiseShader::flatten(SkWriteBuffer& buffer) const { } SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::noise2D( - int channel, const PaintingData& paintingData, - const StitchData& stitchData, const SkPoint& noiseVector) const { + int channel, const StitchData& stitchData, const SkPoint& noiseVector) const { struct Noise { int noisePositionIntegerValue; int nextNoisePositionIntegerValue; @@ -347,9 +351,9 @@ SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::noise2D( noiseX.nextNoisePositionIntegerValue &= kBlockMask; noiseY.nextNoisePositionIntegerValue &= kBlockMask; int i = - paintingData.fLatticeSelector[noiseX.noisePositionIntegerValue]; + fPaintingData->fLatticeSelector[noiseX.noisePositionIntegerValue]; int j = - paintingData.fLatticeSelector[noiseX.nextNoisePositionIntegerValue]; + fPaintingData->fLatticeSelector[noiseX.nextNoisePositionIntegerValue]; int b00 = (i + noiseY.noisePositionIntegerValue) & kBlockMask; int b10 = (j + noiseY.noisePositionIntegerValue) & kBlockMask; int b01 = (i + noiseY.nextNoisePositionIntegerValue) & kBlockMask; @@ -359,32 +363,31 @@ SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::noise2D( // 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][b00].dot(fractionValue); + u = fPaintingData->fGradient[channel][b00].dot(fractionValue); fractionValue.fX -= SK_Scalar1; // Offset (-1,0) - v = paintingData.fGradient[channel][b10].dot(fractionValue); + v = fPaintingData->fGradient[channel][b10].dot(fractionValue); SkScalar a = SkScalarInterp(u, v, sx); fractionValue.fY -= SK_Scalar1; // Offset (-1,-1) - v = paintingData.fGradient[channel][b11].dot(fractionValue); + v = fPaintingData->fGradient[channel][b11].dot(fractionValue); fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1) - u = paintingData.fGradient[channel][b01].dot(fractionValue); + u = fPaintingData->fGradient[channel][b01].dot(fractionValue); SkScalar b = SkScalarInterp(u, v, sx); return SkScalarInterp(a, b, sy); } SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint( - int channel, const PaintingData& paintingData, - StitchData& stitchData, const SkPoint& point) const { + int channel, StitchData& stitchData, const SkPoint& point) const { const SkPerlinNoiseShader& perlinNoiseShader = static_cast<const SkPerlinNoiseShader&>(fShader); if (perlinNoiseShader.fStitchTiles) { // Set up TurbulenceInitial stitch values. - stitchData = paintingData.fStitchDataInit; + stitchData = fPaintingData->fStitchDataInit; } SkScalar turbulenceFunctionResult = 0; - SkPoint noiseVector(SkPoint::Make(SkScalarMul(point.x(), paintingData.fBaseFrequency.fX), - SkScalarMul(point.y(), paintingData.fBaseFrequency.fY))); + SkPoint noiseVector(SkPoint::Make(SkScalarMul(point.x(), fPaintingData->fBaseFrequency.fX), + SkScalarMul(point.y(), fPaintingData->fBaseFrequency.fY))); SkScalar ratio = SK_Scalar1; for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) { - SkScalar noise = noise2D(channel, paintingData, stitchData, noiseVector); + SkScalar noise = noise2D(channel, stitchData, noiseVector); turbulenceFunctionResult += SkScalarDiv( (perlinNoiseShader.fType == kFractalNoise_Type) ? noise : SkScalarAbs(noise), ratio); noiseVector.fX *= 2; @@ -417,7 +420,6 @@ SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::calculateTurbulenceValue SkPMColor SkPerlinNoiseShader::PerlinNoiseShaderContext::shade( const SkPoint& point, StitchData& stitchData) const { - const SkPerlinNoiseShader& perlinNoiseShader = static_cast<const SkPerlinNoiseShader&>(fShader); SkPoint newPoint; fMatrix.mapPoints(&newPoint, &point, 1); newPoint.fX = SkScalarRoundToScalar(newPoint.fX); @@ -426,8 +428,7 @@ SkPMColor SkPerlinNoiseShader::PerlinNoiseShaderContext::shade( U8CPU rgba[4]; for (int channel = 3; channel >= 0; --channel) { rgba[channel] = SkScalarFloorToInt(255 * - calculateTurbulenceValueForPoint(channel, *perlinNoiseShader.fPaintingData, - stitchData, newPoint)); + calculateTurbulenceValueForPoint(channel, stitchData, newPoint)); } return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]); } @@ -450,16 +451,14 @@ SkPerlinNoiseShader::PerlinNoiseShaderContext::PerlinNoiseShaderContext( if (rec.fLocalMatrix) { newMatrix.preConcat(*rec.fLocalMatrix); } - SkMatrix invMatrix; - if (!newMatrix.invert(&invMatrix)) { - invMatrix.reset(); - } // 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. - newMatrix.postTranslate(SK_Scalar1, SK_Scalar1); - newMatrix.postConcat(invMatrix); - newMatrix.postConcat(invMatrix); - fMatrix = newMatrix; + fMatrix.setTranslate(-newMatrix.getTranslateX() + SK_Scalar1, -newMatrix.getTranslateY() + SK_Scalar1); + fPaintingData = SkNEW_ARGS(PaintingData, (shader.fTileSize, shader.fSeed, shader.fBaseFrequencyX, shader.fBaseFrequencyY, newMatrix)); +} + +SkPerlinNoiseShader::PerlinNoiseShaderContext::~PerlinNoiseShaderContext() { + SkDELETE(fPaintingData); } void SkPerlinNoiseShader::PerlinNoiseShaderContext::shadeSpan( @@ -517,7 +516,6 @@ private: int fNumOctaves; GrGLUniformManager::UniformHandle fBaseFrequencyUni; GrGLUniformManager::UniformHandle fAlphaUni; - GrGLUniformManager::UniformHandle fInvMatrixUni; private: typedef GrGLEffect INHERITED; @@ -527,27 +525,29 @@ private: class GrPerlinNoiseEffect : public GrEffect { public: - static GrEffectRef* Create(SkPerlinNoiseShader::Type type, const SkVector& baseFrequency, + static GrEffectRef* Create(SkPerlinNoiseShader::Type type, int numOctaves, bool stitchTiles, - const SkPerlinNoiseShader::StitchData& stitchData, + SkPerlinNoiseShader::PaintingData* paintingData, GrTexture* permutationsTexture, GrTexture* noiseTexture, const SkMatrix& matrix, uint8_t alpha) { - AutoEffectUnref effect(SkNEW_ARGS(GrPerlinNoiseEffect, (type, baseFrequency, numOctaves, - stitchTiles, stitchData, permutationsTexture, noiseTexture, matrix, alpha))); + AutoEffectUnref effect(SkNEW_ARGS(GrPerlinNoiseEffect, (type, numOctaves, + stitchTiles, paintingData, permutationsTexture, noiseTexture, matrix, alpha))); return CreateEffectRef(effect); } - virtual ~GrPerlinNoiseEffect() { } + virtual ~GrPerlinNoiseEffect() { + SkDELETE(fPaintingData); + } static const char* Name() { return "PerlinNoise"; } virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { return GrTBackendEffectFactory<GrPerlinNoiseEffect>::getInstance(); } - const SkPerlinNoiseShader::StitchData& stitchData() const { return fStitchData; } + const SkPerlinNoiseShader::StitchData& stitchData() const { return fPaintingData->fStitchDataInit; } SkPerlinNoiseShader::Type type() const { return fType; } bool stitchTiles() const { return fStitchTiles; } - const SkVector& baseFrequency() const { return fBaseFrequency; } + const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; } int numOctaves() const { return fNumOctaves; } const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); } uint8_t alpha() const { return fAlpha; } @@ -558,34 +558,31 @@ private: virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { const GrPerlinNoiseEffect& s = CastEffect<GrPerlinNoiseEffect>(sBase); return fType == s.fType && - fBaseFrequency == s.fBaseFrequency && + fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency && fNumOctaves == s.fNumOctaves && fStitchTiles == s.fStitchTiles && fCoordTransform.getMatrix() == s.fCoordTransform.getMatrix() && fAlpha == s.fAlpha && fPermutationsAccess.getTexture() == s.fPermutationsAccess.getTexture() && fNoiseAccess.getTexture() == s.fNoiseAccess.getTexture() && - fStitchData == s.fStitchData; + fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit; } - GrPerlinNoiseEffect(SkPerlinNoiseShader::Type type, const SkVector& baseFrequency, + GrPerlinNoiseEffect(SkPerlinNoiseShader::Type type, int numOctaves, bool stitchTiles, - const SkPerlinNoiseShader::StitchData& stitchData, + SkPerlinNoiseShader::PaintingData* paintingData, GrTexture* permutationsTexture, GrTexture* noiseTexture, const SkMatrix& matrix, uint8_t alpha) : fType(type) - , fBaseFrequency(baseFrequency) , fNumOctaves(numOctaves) , fStitchTiles(stitchTiles) , fAlpha(alpha) , fPermutationsAccess(permutationsTexture) , fNoiseAccess(noiseTexture) - , fStitchData(stitchData) { + , fPaintingData(paintingData) { this->addTextureAccess(&fPermutationsAccess); this->addTextureAccess(&fNoiseAccess); - SkMatrix m = matrix; - m.postTranslate(SK_Scalar1, SK_Scalar1); - fCoordTransform.reset(kLocal_GrCoordSet, m); + fCoordTransform.reset(kLocal_GrCoordSet, matrix); this->addCoordTransform(&fCoordTransform); this->setWillNotUseInputColor(); } @@ -594,13 +591,12 @@ private: SkPerlinNoiseShader::Type fType; GrCoordTransform fCoordTransform; - SkVector fBaseFrequency; int fNumOctaves; bool fStitchTiles; uint8_t fAlpha; GrTextureAccess fPermutationsAccess; GrTextureAccess fNoiseAccess; - SkPerlinNoiseShader::StitchData fStitchData; + SkPerlinNoiseShader::PaintingData *fPaintingData; void getConstantColorComponents(GrColor*, uint32_t* validFlags) const SK_OVERRIDE { *validFlags = 0; // This is noise. Nothing is constant. @@ -660,9 +656,6 @@ void GrGLPerlinNoise::emitCode(GrGLShaderBuilder* builder, SkString vCoords = builder->ensureFSCoords2D(coords, 0); - fInvMatrixUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, - kMat33f_GrSLType, "invMatrix"); - const char* invMatrixUni = builder->getUniformCStr(fInvMatrixUni); fBaseFrequencyUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, kVec2f_GrSLType, "baseFrequency"); const char* baseFrequencyUni = builder->getUniformCStr(fBaseFrequencyUni); @@ -841,8 +834,8 @@ void GrGLPerlinNoise::emitCode(GrGLShaderBuilder* builder, } // There are rounding errors if the floor operation is not performed here - builder->fsCodeAppendf("\n\t\tvec2 %s = floor((%s * vec3(%s, 1.0)).xy) * %s;", - noiseVec, invMatrixUni, vCoords.c_str(), baseFrequencyUni); + builder->fsCodeAppendf("\n\t\tvec2 %s = floor(%s.xy) * %s;", + noiseVec, vCoords.c_str(), baseFrequencyUni); // Clear the color accumulator builder->fsCodeAppendf("\n\t\t%s = vec4(0.0);", outputColor); @@ -942,16 +935,6 @@ void GrGLPerlinNoise::setData(const GrGLUniformManager& uman, const GrDrawEffect uman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY); uman.set1f(fAlphaUni, SkScalarDiv(SkIntToScalar(turbulence.alpha()), SkIntToScalar(255))); - SkMatrix m = turbulence.matrix(); - m.postTranslate(-SK_Scalar1, -SK_Scalar1); - SkMatrix invM; - if (!m.invert(&invM)) { - invM.reset(); - } else { - invM.postConcat(invM); // Square the matrix - } - uman.setSkMatrix(fInvMatrixUni, invM); - if (turbulence.stitchTiles()) { const SkPerlinNoiseShader::StitchData& stitchData = turbulence.stitchData(); uman.set2f(fStitchDataUni, SkIntToScalar(stitchData.fWidth), @@ -967,12 +950,15 @@ bool SkPerlinNoiseShader::asNewEffect(GrContext* context, const SkPaint& paint, SkASSERT(NULL != context); *grColor = SkColor2GrColorJustAlpha(paint.getColor()); - + SkMatrix localMatrix = this->getLocalMatrix(); if (externalLocalMatrix) { localMatrix.preConcat(*externalLocalMatrix); } + SkMatrix matrix = context->getMatrix(); + matrix.preConcat(localMatrix); + if (0 == fNumOctaves) { SkColor clearColor = 0; if (kFractalNoise_Type == fType) { @@ -987,18 +973,26 @@ bool SkPerlinNoiseShader::asNewEffect(GrContext* context, const SkPaint& paint, // Either we don't stitch tiles, either we have a valid tile size SkASSERT(!fStitchTiles || !fTileSize.isEmpty()); + SkPerlinNoiseShader::PaintingData* paintingData = SkNEW_ARGS(PaintingData, (fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY, matrix)); GrTexture* permutationsTexture = GrLockAndRefCachedBitmapTexture( - context, fPaintingData->getPermutationsBitmap(), NULL); + context, paintingData->getPermutationsBitmap(), NULL); GrTexture* noiseTexture = GrLockAndRefCachedBitmapTexture( - context, fPaintingData->getNoiseBitmap(), NULL); - - *grEffect = (NULL != permutationsTexture) && (NULL != noiseTexture) ? - GrPerlinNoiseEffect::Create(fType, fPaintingData->fBaseFrequency, - fNumOctaves, fStitchTiles, - fPaintingData->fStitchDataInit, - permutationsTexture, noiseTexture, - localMatrix, paint.getAlpha()) : - NULL; + context, paintingData->getNoiseBitmap(), NULL); + + SkMatrix m = context->getMatrix(); + m.setTranslateX(-localMatrix.getTranslateX() + SK_Scalar1); + m.setTranslateY(-localMatrix.getTranslateY() + SK_Scalar1); + if ((NULL != permutationsTexture) && (NULL != noiseTexture)) { + *grEffect = GrPerlinNoiseEffect::Create(fType, + fNumOctaves, + fStitchTiles, + paintingData, + permutationsTexture, noiseTexture, + m, paint.getAlpha()); + } else { + SkDELETE(paintingData); + *grEffect = 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 |