diff options
Diffstat (limited to 'src/effects')
22 files changed, 0 insertions, 9229 deletions
diff --git a/src/effects/SkPerlinNoiseShader.cpp b/src/effects/SkPerlinNoiseShader.cpp deleted file mode 100644 index 9d8d030002..0000000000 --- a/src/effects/SkPerlinNoiseShader.cpp +++ /dev/null @@ -1,1496 +0,0 @@ -/* - * 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 "SkPerlinNoiseShader.h" - -#include "SkArenaAlloc.h" -#include "SkDither.h" -#include "SkColorFilter.h" -#include "SkReadBuffer.h" -#include "SkWriteBuffer.h" -#include "SkShader.h" -#include "SkUnPreMultiply.h" -#include "SkString.h" - -#if SK_SUPPORT_GPU -#include "GrContext.h" -#include "GrCoordTransform.h" -#include "SkGr.h" -#include "effects/GrConstColorProcessor.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.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 - -static uint8_t improved_noise_permutations[] = { - 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, - 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, - 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, - 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, - 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, - 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, - 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, - 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, - 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, - 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, - 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, - 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, - 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, - 141, 128, 195, 78, 66, 215, 61, 156, 180, - 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, - 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, - 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, - 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, - 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, - 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, - 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, - 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, - 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, - 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, - 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, - 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, - 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, - 141, 128, 195, 78, 66, 215, 61, 156, 180 -}; - -class SkPerlinNoiseShaderImpl : public SkShaderBase { -public: - struct StitchData; - struct PaintingData; - - /** - * About the noise types : the difference between the first 2 is just minor tweaks to the - * algorithm, they're not 2 entirely different noises. The output looks different, but once the - * noise is generated in the [1, -1] range, the output is brought back in the [0, 1] range by - * doing : - * kFractalNoise_Type : noise * 0.5 + 0.5 - * kTurbulence_Type : abs(noise) - * Very little differences between the 2 types, although you can tell the difference visually. - * "Improved" is based on the Improved Perlin Noise algorithm described at - * http://mrl.nyu.edu/~perlin/noise/. It is quite distinct from the other two, and the noise is - * a 2D slice of a 3D noise texture. Minor changes to the Z coordinate will result in minor - * changes to the noise, making it suitable for animated noise. - */ - enum Type { - kFractalNoise_Type, - kTurbulence_Type, - kImprovedNoise_Type, - kFirstType = kFractalNoise_Type, - kLastType = kImprovedNoise_Type - }; - - SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::Type type, SkScalar baseFrequencyX, - SkScalar baseFrequencyY, int numOctaves, SkScalar seed, - const SkISize* tileSize); - ~SkPerlinNoiseShaderImpl() override; - - - class PerlinNoiseShaderContext : public Context { - public: - PerlinNoiseShaderContext(const SkPerlinNoiseShaderImpl& shader, const ContextRec&); - ~PerlinNoiseShaderContext() override; - - void shadeSpan(int x, int y, SkPMColor[], int count) override; - - private: - SkPMColor shade(const SkPoint& point, StitchData& stitchData) const; - SkScalar calculateTurbulenceValueForPoint( - int channel, - StitchData& stitchData, const SkPoint& point) const; - SkScalar calculateImprovedNoiseValueForPoint(int channel, const SkPoint& point) const; - SkScalar noise2D(int channel, - const StitchData& stitchData, const SkPoint& noiseVector) const; - - SkMatrix fMatrix; - PaintingData* fPaintingData; - - typedef Context INHERITED; - }; - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPerlinNoiseShaderImpl) - -protected: - void flatten(SkWriteBuffer&) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - -private: - const SkPerlinNoiseShaderImpl::Type fType; - const SkScalar fBaseFrequencyX; - const SkScalar fBaseFrequencyY; - const int fNumOctaves; - const SkScalar fSeed; - const SkISize fTileSize; - const bool fStitchTiles; - - friend class ::SkPerlinNoiseShader; - - typedef SkShaderBase INHERITED; -}; - -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; - } - return noiseValue; -} - -inline SkScalar smoothCurve(SkScalar t) { - return t * t * (3 - 2 * t); -} - -} // end namespace - -struct SkPerlinNoiseShaderImpl::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 SkPerlinNoiseShaderImpl::PaintingData { - PaintingData(const SkISize& tileSize, SkScalar seed, - SkScalar baseFrequencyX, SkScalar baseFrequencyY, - const SkMatrix& matrix) - { - SkVector vec[2] = { - { SkScalarInvert(baseFrequencyX), SkScalarInvert(baseFrequencyY) }, - { SkIntToScalar(tileSize.fWidth), SkIntToScalar(tileSize.fHeight) }, - }; - matrix.mapVectors(vec, 2); - - fBaseFrequency.set(SkScalarInvert(vec[0].fX), SkScalarInvert(vec[0].fY)); - fTileSize.set(SkScalarRoundToInt(vec[1].fX), SkScalarRoundToInt(vec[1].fY)); - this->init(seed); - if (!fTileSize.isEmpty()) { - this->stitch(); - } - -#if SK_SUPPORT_GPU - fPermutationsBitmap.setInfo(SkImageInfo::MakeA8(kBlockSize, 1)); - fPermutationsBitmap.setPixels(fLatticeSelector); - - fNoiseBitmap.setInfo(SkImageInfo::MakeN32Premul(kBlockSize, 4)); - fNoiseBitmap.setPixels(fNoise[0][0]); - - fImprovedPermutationsBitmap.setInfo(SkImageInfo::MakeA8(256, 1)); - fImprovedPermutationsBitmap.setPixels(improved_noise_permutations); - - fGradientBitmap.setInfo(SkImageInfo::MakeN32Premul(16, 1)); - static uint8_t gradients[] = { 2, 2, 1, 0, - 0, 2, 1, 0, - 2, 0, 1, 0, - 0, 0, 1, 0, - 2, 1, 2, 0, - 0, 1, 2, 0, - 2, 1, 0, 0, - 0, 1, 0, 0, - 1, 2, 2, 0, - 1, 0, 2, 0, - 1, 2, 0, 0, - 1, 0, 0, 0, - 2, 2, 1, 0, - 1, 0, 2, 0, - 0, 2, 1, 0, - 1, 0, 0, 0 }; - fGradientBitmap.setPixels(gradients); -#endif - } - - int fSeed; - uint8_t fLatticeSelector[kBlockSize]; - uint16_t fNoise[4][kBlockSize][2]; - SkPoint fGradient[4][kBlockSize]; - SkISize fTileSize; - SkVector fBaseFrequency; - StitchData fStitchDataInit; - -private: - -#if SK_SUPPORT_GPU - SkBitmap fPermutationsBitmap; - SkBitmap fNoiseBitmap; - SkBitmap fImprovedPermutationsBitmap; - SkBitmap fGradientBitmap; -#endif - - 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; - } - - // Only called once. Could be part of the constructor. - void init(SkScalar seed) - { - static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlockSize)); - - // According to the SVG spec, we must truncate (not round) the seed value. - fSeed = SkScalarTruncToInt(seed); - // The seed value clamp to the range [1, kRandMaximum - 1]. - 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 gHalfMax16bits = 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( - (fNoise[channel][i][0] - kBlockSize) * gInvBlockSizef, - (fNoise[channel][i][1] - kBlockSize) * gInvBlockSizef); - fGradient[channel][i].normalize(); - // Put the normalized gradient back into the noise data - fNoise[channel][i][0] = SkScalarRoundToInt( - (fGradient[channel][i].fX + 1) * gHalfMax16bits); - fNoise[channel][i][1] = SkScalarRoundToInt( - (fGradient[channel][i].fY + 1) * gHalfMax16bits); - } - } - } - - // Only called once. Could be part of the constructor. - 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 = - SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; - SkScalar highFrequencx = - SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; - // BaseFrequency should be non-negative according to the standard. - if (fBaseFrequency.fX / lowFrequencx < highFrequencx / fBaseFrequency.fX) { - fBaseFrequency.fX = lowFrequencx; - } else { - fBaseFrequency.fX = highFrequencx; - } - } - if (fBaseFrequency.fY) { - SkScalar lowFrequency = - SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; - SkScalar highFrequency = - SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; - if (fBaseFrequency.fY / lowFrequency < highFrequency / fBaseFrequency.fY) { - fBaseFrequency.fY = lowFrequency; - } else { - fBaseFrequency.fY = highFrequency; - } - } - // Set up TurbulenceInitial stitch values. - fStitchDataInit.fWidth = - SkScalarRoundToInt(tileWidth * fBaseFrequency.fX); - fStitchDataInit.fWrapX = kPerlinNoise + fStitchDataInit.fWidth; - fStitchDataInit.fHeight = - SkScalarRoundToInt(tileHeight * fBaseFrequency.fY); - fStitchDataInit.fWrapY = kPerlinNoise + fStitchDataInit.fHeight; - } - -public: - -#if SK_SUPPORT_GPU - const SkBitmap& getPermutationsBitmap() const { return fPermutationsBitmap; } - - const SkBitmap& getNoiseBitmap() const { return fNoiseBitmap; } - - const SkBitmap& getImprovedPermutationsBitmap() const { return fImprovedPermutationsBitmap; } - - const SkBitmap& getGradientBitmap() const { return fGradientBitmap; } -#endif -}; - -SkPerlinNoiseShaderImpl::SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::Type type, - SkScalar baseFrequencyX, - SkScalar baseFrequencyY, - int numOctaves, - SkScalar seed, - const SkISize* tileSize) - : fType(type) - , fBaseFrequencyX(baseFrequencyX) - , fBaseFrequencyY(baseFrequencyY) - , fNumOctaves(numOctaves > 255 ? 255 : numOctaves/*[0,255] octaves allowed*/) - , fSeed(seed) - , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize) - , fStitchTiles(!fTileSize.isEmpty()) -{ - SkASSERT(numOctaves >= 0 && numOctaves < 256); -} - -SkPerlinNoiseShaderImpl::~SkPerlinNoiseShaderImpl() { -} - -sk_sp<SkFlattenable> SkPerlinNoiseShaderImpl::CreateProc(SkReadBuffer& buffer) { - Type type = (Type)buffer.readInt(); - SkScalar freqX = buffer.readScalar(); - SkScalar freqY = buffer.readScalar(); - int octaves = buffer.readInt(); - SkScalar seed = buffer.readScalar(); - SkISize tileSize; - tileSize.fWidth = buffer.readInt(); - tileSize.fHeight = buffer.readInt(); - - switch (type) { - case kFractalNoise_Type: - return SkPerlinNoiseShader::MakeFractalNoise(freqX, freqY, octaves, seed, &tileSize); - case kTurbulence_Type: - return SkPerlinNoiseShader::MakeTurbulence(freqX, freqY, octaves, seed, &tileSize); - case kImprovedNoise_Type: - return SkPerlinNoiseShader::MakeImprovedNoise(freqX, freqY, octaves, seed); - default: - return nullptr; - } -} - -void SkPerlinNoiseShaderImpl::flatten(SkWriteBuffer& buffer) const { - buffer.writeInt((int) fType); - buffer.writeScalar(fBaseFrequencyX); - buffer.writeScalar(fBaseFrequencyY); - buffer.writeInt(fNumOctaves); - buffer.writeScalar(fSeed); - buffer.writeInt(fTileSize.fWidth); - buffer.writeInt(fTileSize.fHeight); -} - -SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::noise2D( - int channel, const StitchData& stitchData, const SkPoint& noiseVector) const { - struct Noise { - int noisePositionIntegerValue; - int nextNoisePositionIntegerValue; - SkScalar noisePositionFractionValue; - Noise(SkScalar component) - { - SkScalar position = component + kPerlinNoise; - noisePositionIntegerValue = SkScalarFloorToInt(position); - noisePositionFractionValue = position - SkIntToScalar(noisePositionIntegerValue); - nextNoisePositionIntegerValue = noisePositionIntegerValue + 1; - } - }; - Noise noiseX(noiseVector.x()); - Noise noiseY(noiseVector.y()); - SkScalar u, v; - const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader); - // If stitching, adjust lattice points accordingly. - if (perlinNoiseShader.fStitchTiles) { - noiseX.noisePositionIntegerValue = - checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth); - noiseY.noisePositionIntegerValue = - checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight); - noiseX.nextNoisePositionIntegerValue = - checkNoise(noiseX.nextNoisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth); - noiseY.nextNoisePositionIntegerValue = - checkNoise(noiseY.nextNoisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight); - } - noiseX.noisePositionIntegerValue &= kBlockMask; - noiseY.noisePositionIntegerValue &= kBlockMask; - noiseX.nextNoisePositionIntegerValue &= kBlockMask; - noiseY.nextNoisePositionIntegerValue &= kBlockMask; - int i = - fPaintingData->fLatticeSelector[noiseX.noisePositionIntegerValue]; - int j = - fPaintingData->fLatticeSelector[noiseX.nextNoisePositionIntegerValue]; - int b00 = (i + noiseY.noisePositionIntegerValue) & kBlockMask; - int b10 = (j + noiseY.noisePositionIntegerValue) & kBlockMask; - int b01 = (i + noiseY.nextNoisePositionIntegerValue) & kBlockMask; - int b11 = (j + noiseY.nextNoisePositionIntegerValue) & kBlockMask; - SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue); - SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue); - - if (sx < 0 || sy < 0 || sx > 1 || sy > 1) { - return 0; // Check for pathological inputs. - } - - // 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 = fPaintingData->fGradient[channel][b00].dot(fractionValue); - fractionValue.fX -= SK_Scalar1; // Offset (-1,0) - v = fPaintingData->fGradient[channel][b10].dot(fractionValue); - SkScalar a = SkScalarInterp(u, v, sx); - fractionValue.fY -= SK_Scalar1; // Offset (-1,-1) - v = fPaintingData->fGradient[channel][b11].dot(fractionValue); - fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1) - u = fPaintingData->fGradient[channel][b01].dot(fractionValue); - SkScalar b = SkScalarInterp(u, v, sx); - return SkScalarInterp(a, b, sy); -} - -SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint( - int channel, StitchData& stitchData, const SkPoint& point) const { - const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader); - if (perlinNoiseShader.fStitchTiles) { - // Set up TurbulenceInitial stitch values. - stitchData = fPaintingData->fStitchDataInit; - } - SkScalar turbulenceFunctionResult = 0; - SkPoint noiseVector(SkPoint::Make(point.x() * fPaintingData->fBaseFrequency.fX, - point.y() * fPaintingData->fBaseFrequency.fY)); - SkScalar ratio = SK_Scalar1; - for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) { - SkScalar noise = noise2D(channel, stitchData, noiseVector); - SkScalar numer = (perlinNoiseShader.fType == kFractalNoise_Type) ? - noise : SkScalarAbs(noise); - turbulenceFunctionResult += numer / ratio; - noiseVector.fX *= 2; - noiseVector.fY *= 2; - ratio *= 2; - if (perlinNoiseShader.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 (perlinNoiseShader.fType == kFractalNoise_Type) { - turbulenceFunctionResult = SkScalarHalf(turbulenceFunctionResult + 1); - } - - if (channel == 3) { // Scale alpha by paint value - turbulenceFunctionResult *= SkIntToScalar(getPaintAlpha()) / 255; - } - - // Clamp result - return SkScalarPin(turbulenceFunctionResult, 0, SK_Scalar1); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Improved Perlin Noise based on Java implementation found at http://mrl.nyu.edu/~perlin/noise/ -static SkScalar fade(SkScalar t) { - return t * t * t * (t * (t * 6 - 15) + 10); -} - -static SkScalar lerp(SkScalar t, SkScalar a, SkScalar b) { - return a + t * (b - a); -} - -static SkScalar grad(int hash, SkScalar x, SkScalar y, SkScalar z) { - int h = hash & 15; - SkScalar u = h < 8 ? x : y; - SkScalar v = h < 4 ? y : h == 12 || h == 14 ? x : z; - return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); -} - -SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::calculateImprovedNoiseValueForPoint( - int channel, const SkPoint& point) const { - const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader); - SkScalar x = point.fX * perlinNoiseShader.fBaseFrequencyX; - SkScalar y = point.fY * perlinNoiseShader.fBaseFrequencyY; - // z offset between different channels, chosen arbitrarily - static const SkScalar CHANNEL_DELTA = 1000.0f; - SkScalar z = channel * CHANNEL_DELTA + perlinNoiseShader.fSeed; - SkScalar result = 0; - SkScalar ratio = SK_Scalar1; - for (int i = 0; i < perlinNoiseShader.fNumOctaves; i++) { - int X = SkScalarFloorToInt(x) & 255; - int Y = SkScalarFloorToInt(y) & 255; - int Z = SkScalarFloorToInt(z) & 255; - SkScalar px = x - SkScalarFloorToScalar(x); - SkScalar py = y - SkScalarFloorToScalar(y); - SkScalar pz = z - SkScalarFloorToScalar(z); - SkScalar u = fade(px); - SkScalar v = fade(py); - SkScalar w = fade(pz); - uint8_t* permutations = improved_noise_permutations; - int A = permutations[X] + Y; - int AA = permutations[A] + Z; - int AB = permutations[A + 1] + Z; - int B = permutations[X + 1] + Y; - int BA = permutations[B] + Z; - int BB = permutations[B + 1] + Z; - result += lerp(w, lerp(v, lerp(u, grad(permutations[AA ], px , py , pz ), - grad(permutations[BA ], px - 1, py , pz )), - lerp(u, grad(permutations[AB ], px , py - 1, pz ), - grad(permutations[BB ], px - 1, py - 1, pz ))), - lerp(v, lerp(u, grad(permutations[AA + 1], px , py , pz - 1), - grad(permutations[BA + 1], px - 1, py , pz - 1)), - lerp(u, grad(permutations[AB + 1], px , py - 1, pz - 1), - grad(permutations[BB + 1], px - 1, py - 1, pz - 1)))) / - ratio; - x *= 2; - y *= 2; - ratio *= 2; - } - result = SkScalarClampMax((result + 1.0f) / 2.0f, 1.0f); - return result; -} -//////////////////////////////////////////////////////////////////////////////////////////////////// - -SkPMColor SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::shade( - const SkPoint& point, StitchData& stitchData) const { - const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader); - SkPoint newPoint; - fMatrix.mapPoints(&newPoint, &point, 1); - newPoint.fX = SkScalarRoundToScalar(newPoint.fX); - newPoint.fY = SkScalarRoundToScalar(newPoint.fY); - - U8CPU rgba[4]; - for (int channel = 3; channel >= 0; --channel) { - SkScalar value; - if (perlinNoiseShader.fType == kImprovedNoise_Type) { - value = calculateImprovedNoiseValueForPoint(channel, newPoint); - } - else { - value = calculateTurbulenceValueForPoint(channel, stitchData, newPoint); - } - rgba[channel] = SkScalarFloorToInt(255 * value); - } - return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]); -} - -SkShaderBase::Context* SkPerlinNoiseShaderImpl::onMakeContext(const ContextRec& rec, - SkArenaAlloc* alloc) const { - return alloc->make<PerlinNoiseShaderContext>(*this, rec); -} - -SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::PerlinNoiseShaderContext( - const SkPerlinNoiseShaderImpl& shader, const ContextRec& rec) - : INHERITED(shader, rec) -{ - SkMatrix newMatrix = *rec.fMatrix; - newMatrix.preConcat(shader.getLocalMatrix()); - if (rec.fLocalMatrix) { - newMatrix.preConcat(*rec.fLocalMatrix); - } - // 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. - fMatrix.setTranslate(-newMatrix.getTranslateX() + SK_Scalar1, -newMatrix.getTranslateY() + SK_Scalar1); - fPaintingData = new PaintingData(shader.fTileSize, shader.fSeed, shader.fBaseFrequencyX, - shader.fBaseFrequencyY, newMatrix); -} - -SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::~PerlinNoiseShaderContext() { delete fPaintingData; } - -void SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::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; - } -} - -///////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -class GrGLPerlinNoise : public GrGLSLFragmentProcessor { -public: - void emitCode(EmitArgs&) override; - - static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - -private: - GrGLSLProgramDataManager::UniformHandle fStitchDataUni; - GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni; - - typedef GrGLSLFragmentProcessor INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -class GrPerlinNoise2Effect : public GrFragmentProcessor { -public: - static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider, - SkPerlinNoiseShaderImpl::Type type, - int numOctaves, bool stitchTiles, - SkPerlinNoiseShaderImpl::PaintingData* paintingData, - sk_sp<GrTextureProxy> permutationsProxy, - sk_sp<GrTextureProxy> noiseProxy, - const SkMatrix& matrix) { - return sk_sp<GrFragmentProcessor>( - new GrPerlinNoise2Effect(resourceProvider, type, numOctaves, stitchTiles, paintingData, - std::move(permutationsProxy), std::move(noiseProxy), matrix)); - } - - ~GrPerlinNoise2Effect() override { delete fPaintingData; } - - const char* name() const override { return "PerlinNoise"; } - - const SkPerlinNoiseShaderImpl::StitchData& stitchData() const { return fPaintingData->fStitchDataInit; } - - SkPerlinNoiseShaderImpl::Type type() const { return fType; } - bool stitchTiles() const { return fStitchTiles; } - const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; } - int numOctaves() const { return fNumOctaves; } - const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { - return new GrGLPerlinNoise; - } - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override { - GrGLPerlinNoise::GenKey(*this, caps, b); - } - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const GrPerlinNoise2Effect& s = sBase.cast<GrPerlinNoise2Effect>(); - return fType == s.fType && - fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency && - fNumOctaves == s.fNumOctaves && - fStitchTiles == s.fStitchTiles && - fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit; - } - - GrPerlinNoise2Effect(GrResourceProvider* resourceProvider, - SkPerlinNoiseShaderImpl::Type type, int numOctaves, bool stitchTiles, - SkPerlinNoiseShaderImpl::PaintingData* paintingData, - sk_sp<GrTextureProxy> permutationsProxy, - sk_sp<GrTextureProxy> noiseProxy, - const SkMatrix& matrix) - : INHERITED(kNone_OptimizationFlags) - , fType(type) - , fNumOctaves(numOctaves) - , fStitchTiles(stitchTiles) - , fPermutationsSampler(resourceProvider, std::move(permutationsProxy)) - , fNoiseSampler(resourceProvider, std::move(noiseProxy)) - , fPaintingData(paintingData) { - this->initClassID<GrPerlinNoise2Effect>(); - this->addTextureSampler(&fPermutationsSampler); - this->addTextureSampler(&fNoiseSampler); - fCoordTransform.reset(matrix); - this->addCoordTransform(&fCoordTransform); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - SkPerlinNoiseShaderImpl::Type fType; - GrCoordTransform fCoordTransform; - int fNumOctaves; - bool fStitchTiles; - TextureSampler fPermutationsSampler; - TextureSampler fNoiseSampler; - SkPerlinNoiseShaderImpl::PaintingData* fPaintingData; - - typedef GrFragmentProcessor INHERITED; -}; - -///////////////////////////////////////////////////////////////////// -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrPerlinNoise2Effect); - -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> GrPerlinNoise2Effect::TestCreate(GrProcessorTestData* d) { - int numOctaves = d->fRandom->nextRangeU(2, 10); - bool stitchTiles = d->fRandom->nextBool(); - SkScalar seed = SkIntToScalar(d->fRandom->nextU()); - SkISize tileSize = SkISize::Make(d->fRandom->nextRangeU(4, 4096), - d->fRandom->nextRangeU(4, 4096)); - SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f, - 0.99f); - SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f, - 0.99f); - - sk_sp<SkShader> shader(d->fRandom->nextBool() ? - SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed, - stitchTiles ? &tileSize : nullptr) : - SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed, - stitchTiles ? &tileSize : nullptr)); - - GrTest::TestAsFPArgs asFPArgs(d); - return as_SB(shader)->asFragmentProcessor(asFPArgs.args()); -} -#endif - -void GrGLPerlinNoise::emitCode(EmitArgs& args) { - const GrPerlinNoise2Effect& pne = args.fFp.cast<GrPerlinNoise2Effect>(); - - GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - SkString vCoords = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - - fBaseFrequencyUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "baseFrequency"); - const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni); - - const char* stitchDataUni = nullptr; - if (pne.stitchTiles()) { - fStitchDataUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "stitchData"); - stitchDataUni = uniformHandler->getUniformCStr(fStitchDataUni); - } - - // There are 4 lines, so the center of each line is 1/8, 3/8, 5/8 and 7/8 - const char* chanCoordR = "0.125"; - const char* chanCoordG = "0.375"; - const char* chanCoordB = "0.625"; - const char* chanCoordA = "0.875"; - const char* chanCoord = "chanCoord"; - const char* stitchData = "stitchData"; - const char* ratio = "ratio"; - const char* noiseVec = "noiseVec"; - const char* noiseSmooth = "noiseSmooth"; - const char* floorVal = "floorVal"; - const char* fractVal = "fractVal"; - const char* uv = "uv"; - const char* ab = "ab"; - const char* latticeIdx = "latticeIdx"; - const char* bcoords = "bcoords"; - const char* lattice = "lattice"; - 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);"; - - // Add noise function - static const GrShaderVar gPerlinNoiseArgs[] = { - GrShaderVar(chanCoord, kFloat_GrSLType), - GrShaderVar(noiseVec, kVec2f_GrSLType) - }; - - static const GrShaderVar gPerlinNoiseStitchArgs[] = { - GrShaderVar(chanCoord, kFloat_GrSLType), - GrShaderVar(noiseVec, kVec2f_GrSLType), - GrShaderVar(stitchData, kVec2f_GrSLType) - }; - - SkString noiseCode; - - noiseCode.appendf("\tvec4 %s;\n", floorVal); - noiseCode.appendf("\t%s.xy = floor(%s);\n", floorVal, noiseVec); - noiseCode.appendf("\t%s.zw = %s.xy + vec2(1.0);\n", floorVal, floorVal); - noiseCode.appendf("\tvec2 %s = fract(%s);\n", fractVal, noiseVec); - - // smooth curve : t * t * (3 - 2 * t) - noiseCode.appendf("\n\tvec2 %s = %s * %s * (vec2(3.0) - vec2(2.0) * %s);", - noiseSmooth, fractVal, fractVal, fractVal); - - // Adjust frequencies if we're stitching tiles - if (pne.stitchTiles()) { - noiseCode.appendf("\n\tif(%s.x >= %s.x) { %s.x -= %s.x; }", - floorVal, stitchData, floorVal, stitchData); - noiseCode.appendf("\n\tif(%s.y >= %s.y) { %s.y -= %s.y; }", - floorVal, stitchData, floorVal, stitchData); - noiseCode.appendf("\n\tif(%s.z >= %s.x) { %s.z -= %s.x; }", - floorVal, stitchData, floorVal, stitchData); - noiseCode.appendf("\n\tif(%s.w >= %s.y) { %s.w -= %s.y; }", - floorVal, stitchData, floorVal, stitchData); - } - - // Get texture coordinates and normalize - noiseCode.appendf("\n\t%s = fract(floor(mod(%s, 256.0)) / vec4(256.0));\n", - floorVal, floorVal); - - // Get permutation for x - { - SkString xCoords(""); - xCoords.appendf("vec2(%s.x, 0.5)", floorVal); - - noiseCode.appendf("\n\tvec2 %s;\n\t%s.x = ", latticeIdx, latticeIdx); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(), - kVec2f_GrSLType); - noiseCode.append(".r;"); - } - - // Get permutation for x + 1 - { - SkString xCoords(""); - xCoords.appendf("vec2(%s.z, 0.5)", floorVal); - - noiseCode.appendf("\n\t%s.y = ", latticeIdx); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(), - kVec2f_GrSLType); - noiseCode.append(".r;"); - } - -#if defined(SK_BUILD_FOR_ANDROID) - // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3). - // The issue is that colors aren't accurate enough on Tegra devices. For example, if an 8 bit - // value of 124 (or 0.486275 here) is entered, we can get a texture value of 123.513725 - // (or 0.484368 here). The following rounding operation prevents these precision issues from - // affecting the result of the noise by making sure that we only have multiples of 1/255. - // (Note that 1/255 is about 0.003921569, which is the value used here). - noiseCode.appendf("\n\t%s = floor(%s * vec2(255.0) + vec2(0.5)) * vec2(0.003921569);", - latticeIdx, latticeIdx); -#endif - - // Get (x,y) coordinates with the permutated x - noiseCode.appendf("\n\tvec4 %s = fract(%s.xyxy + %s.yyww);", bcoords, latticeIdx, floorVal); - - noiseCode.appendf("\n\n\tvec2 %s;", uv); - // Compute u, at offset (0,0) - { - SkString latticeCoords(""); - latticeCoords.appendf("vec2(%s.x, %s)", bcoords, chanCoord); - noiseCode.appendf("\n\tvec4 %s = ", lattice); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(), - kVec2f_GrSLType); - noiseCode.appendf(".bgra;\n\t%s.x = ", uv); - noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); - } - - noiseCode.appendf("\n\t%s.x -= 1.0;", fractVal); - // Compute v, at offset (-1,0) - { - SkString latticeCoords(""); - latticeCoords.appendf("vec2(%s.y, %s)", bcoords, chanCoord); - noiseCode.append("\n\tlattice = "); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(), - kVec2f_GrSLType); - noiseCode.appendf(".bgra;\n\t%s.y = ", uv); - noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); - } - - // Compute 'a' as a linear interpolation of 'u' and 'v' - noiseCode.appendf("\n\tvec2 %s;", ab); - noiseCode.appendf("\n\t%s.x = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth); - - noiseCode.appendf("\n\t%s.y -= 1.0;", fractVal); - // Compute v, at offset (-1,-1) - { - SkString latticeCoords(""); - latticeCoords.appendf("vec2(%s.w, %s)", bcoords, chanCoord); - noiseCode.append("\n\tlattice = "); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(), - kVec2f_GrSLType); - noiseCode.appendf(".bgra;\n\t%s.y = ", uv); - noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); - } - - noiseCode.appendf("\n\t%s.x += 1.0;", fractVal); - // Compute u, at offset (0,-1) - { - SkString latticeCoords(""); - latticeCoords.appendf("vec2(%s.z, %s)", bcoords, chanCoord); - noiseCode.append("\n\tlattice = "); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(), - kVec2f_GrSLType); - noiseCode.appendf(".bgra;\n\t%s.x = ", uv); - noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); - } - - // Compute 'b' as a linear interpolation of 'u' and 'v' - noiseCode.appendf("\n\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' - noiseCode.appendf("\n\treturn mix(%s.x, %s.y, %s.y);\n", ab, ab, noiseSmooth); - - SkString noiseFuncName; - if (pne.stitchTiles()) { - fragBuilder->emitFunction(kFloat_GrSLType, - "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseStitchArgs), - gPerlinNoiseStitchArgs, noiseCode.c_str(), &noiseFuncName); - } else { - fragBuilder->emitFunction(kFloat_GrSLType, - "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseArgs), - gPerlinNoiseArgs, noiseCode.c_str(), &noiseFuncName); - } - - // There are rounding errors if the floor operation is not performed here - fragBuilder->codeAppendf("\n\t\tvec2 %s = floor(%s.xy) * %s;", - noiseVec, vCoords.c_str(), baseFrequencyUni); - - // Clear the color accumulator - fragBuilder->codeAppendf("\n\t\t%s = vec4(0.0);", args.fOutputColor); - - if (pne.stitchTiles()) { - // Set up TurbulenceInitial stitch values. - fragBuilder->codeAppendf("\n\t\tvec2 %s = %s;", stitchData, stitchDataUni); - } - - fragBuilder->codeAppendf("\n\t\tfloat %s = 1.0;", ratio); - - // Loop over all octaves - fragBuilder->codeAppendf("for (int octave = 0; octave < %d; ++octave) {", pne.numOctaves()); - - fragBuilder->codeAppendf("\n\t\t\t%s += ", args.fOutputColor); - if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) { - fragBuilder->codeAppend("abs("); - } - if (pne.stitchTiles()) { - fragBuilder->codeAppendf( - "vec4(\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s)," - "\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s))", - noiseFuncName.c_str(), chanCoordR, noiseVec, stitchData, - noiseFuncName.c_str(), chanCoordG, noiseVec, stitchData, - noiseFuncName.c_str(), chanCoordB, noiseVec, stitchData, - noiseFuncName.c_str(), chanCoordA, noiseVec, stitchData); - } else { - fragBuilder->codeAppendf( - "vec4(\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s)," - "\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s))", - noiseFuncName.c_str(), chanCoordR, noiseVec, - noiseFuncName.c_str(), chanCoordG, noiseVec, - noiseFuncName.c_str(), chanCoordB, noiseVec, - noiseFuncName.c_str(), chanCoordA, noiseVec); - } - if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) { - fragBuilder->codeAppendf(")"); // end of "abs(" - } - fragBuilder->codeAppendf(" * %s;", ratio); - - fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", noiseVec); - fragBuilder->codeAppendf("\n\t\t\t%s *= 0.5;", ratio); - - if (pne.stitchTiles()) { - fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", stitchData); - } - fragBuilder->codeAppend("\n\t\t}"); // end of the for loop on octaves - - if (pne.type() == SkPerlinNoiseShaderImpl::kFractalNoise_Type) { - // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2 - // by fractalNoise and (turbulenceFunctionResult) by turbulence. - fragBuilder->codeAppendf("\n\t\t%s = %s * vec4(0.5) + vec4(0.5);", - args.fOutputColor,args.fOutputColor); - } - - // Clamp values - fragBuilder->codeAppendf("\n\t\t%s = clamp(%s, 0.0, 1.0);", args.fOutputColor, args.fOutputColor); - - // Pre-multiply the result - fragBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n", - args.fOutputColor, args.fOutputColor, - args.fOutputColor, args.fOutputColor); -} - -void GrGLPerlinNoise::GenKey(const GrProcessor& processor, const GrShaderCaps&, - GrProcessorKeyBuilder* b) { - const GrPerlinNoise2Effect& turbulence = processor.cast<GrPerlinNoise2Effect>(); - - uint32_t key = turbulence.numOctaves(); - - key = key << 3; // Make room for next 3 bits - - switch (turbulence.type()) { - case SkPerlinNoiseShaderImpl::kFractalNoise_Type: - key |= 0x1; - break; - case SkPerlinNoiseShaderImpl::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 - } - - b->add32(key); -} - -void GrGLPerlinNoise::onSetData(const GrGLSLProgramDataManager& pdman, - const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - - const GrPerlinNoise2Effect& turbulence = processor.cast<GrPerlinNoise2Effect>(); - - const SkVector& baseFrequency = turbulence.baseFrequency(); - pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY); - - if (turbulence.stitchTiles()) { - const SkPerlinNoiseShaderImpl::StitchData& stitchData = turbulence.stitchData(); - pdman.set2f(fStitchDataUni, SkIntToScalar(stitchData.fWidth), - SkIntToScalar(stitchData.fHeight)); - } -} - -///////////////////////////////////////////////////////////////////// - -class GrGLImprovedPerlinNoise : public GrGLSLFragmentProcessor { -public: - void emitCode(EmitArgs&) override; - - static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - -private: - GrGLSLProgramDataManager::UniformHandle fZUni; - GrGLSLProgramDataManager::UniformHandle fOctavesUni; - GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni; - - typedef GrGLSLFragmentProcessor INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -class GrImprovedPerlinNoiseEffect : public GrFragmentProcessor { -public: - static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider, - int octaves, SkScalar z, - SkPerlinNoiseShaderImpl::PaintingData* paintingData, - sk_sp<GrTextureProxy> permutationsProxy, - sk_sp<GrTextureProxy> gradientProxy, - const SkMatrix& matrix) { - return sk_sp<GrFragmentProcessor>( - new GrImprovedPerlinNoiseEffect(resourceProvider, octaves, z, paintingData, - std::move(permutationsProxy), - std::move(gradientProxy), matrix)); - } - - ~GrImprovedPerlinNoiseEffect() override { delete fPaintingData; } - - const char* name() const override { return "ImprovedPerlinNoise"; } - - const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; } - SkScalar z() const { return fZ; } - int octaves() const { return fOctaves; } - const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { - return new GrGLImprovedPerlinNoise; - } - - void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { - GrGLImprovedPerlinNoise::GenKey(*this, caps, b); - } - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const GrImprovedPerlinNoiseEffect& s = sBase.cast<GrImprovedPerlinNoiseEffect>(); - return fZ == fZ && - fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency; - } - - GrImprovedPerlinNoiseEffect(GrResourceProvider* resourceProvider, - int octaves, SkScalar z, - SkPerlinNoiseShaderImpl::PaintingData* paintingData, - sk_sp<GrTextureProxy> permutationsProxy, - sk_sp<GrTextureProxy> gradientProxy, - const SkMatrix& matrix) - : INHERITED(kNone_OptimizationFlags) - , fOctaves(octaves) - , fZ(z) - , fPermutationsSampler(resourceProvider, std::move(permutationsProxy)) - , fGradientSampler(resourceProvider, std::move(gradientProxy)) - , fPaintingData(paintingData) { - this->initClassID<GrImprovedPerlinNoiseEffect>(); - this->addTextureSampler(&fPermutationsSampler); - this->addTextureSampler(&fGradientSampler); - fCoordTransform.reset(matrix); - this->addCoordTransform(&fCoordTransform); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - GrCoordTransform fCoordTransform; - int fOctaves; - SkScalar fZ; - TextureSampler fPermutationsSampler; - TextureSampler fGradientSampler; - SkPerlinNoiseShaderImpl::PaintingData* fPaintingData; - - typedef GrFragmentProcessor INHERITED; -}; - -///////////////////////////////////////////////////////////////////// -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrImprovedPerlinNoiseEffect); - -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> GrImprovedPerlinNoiseEffect::TestCreate(GrProcessorTestData* d) { - SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f, - 0.99f); - SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f, - 0.99f); - int numOctaves = d->fRandom->nextRangeU(2, 10); - SkScalar z = SkIntToScalar(d->fRandom->nextU()); - - sk_sp<SkShader> shader(SkPerlinNoiseShader::MakeImprovedNoise(baseFrequencyX, - baseFrequencyY, - numOctaves, - z)); - - GrTest::TestAsFPArgs asFPArgs(d); - return as_SB(shader)->asFragmentProcessor(asFPArgs.args()); -} -#endif - -void GrGLImprovedPerlinNoise::emitCode(EmitArgs& args) { - GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - SkString vCoords = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - - fBaseFrequencyUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "baseFrequency"); - const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni); - - fOctavesUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kFloat_GrSLType, kDefault_GrSLPrecision, - "octaves"); - const char* octavesUni = uniformHandler->getUniformCStr(fOctavesUni); - - fZUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kFloat_GrSLType, kDefault_GrSLPrecision, - "z"); - const char* zUni = uniformHandler->getUniformCStr(fZUni); - - // fade function - static const GrShaderVar fadeArgs[] = { - GrShaderVar("t", kVec3f_GrSLType) - }; - SkString fadeFuncName; - fragBuilder->emitFunction(kVec3f_GrSLType, "fade", SK_ARRAY_COUNT(fadeArgs), - fadeArgs, - "return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);", - &fadeFuncName); - - // perm function - static const GrShaderVar permArgs[] = { - GrShaderVar("x", kFloat_GrSLType) - }; - SkString permFuncName; - SkString permCode("return "); - // FIXME even though I'm creating these textures with kRepeat_TileMode, they're clamped. Not - // sure why. Using fract() (here and the next texture lookup) as a workaround. - fragBuilder->appendTextureLookup(&permCode, args.fTexSamplers[0], "vec2(fract(x / 256.0), 0.0)", - kVec2f_GrSLType); - permCode.append(".r * 255.0;"); - fragBuilder->emitFunction(kFloat_GrSLType, "perm", SK_ARRAY_COUNT(permArgs), permArgs, - permCode.c_str(), &permFuncName); - - // grad function - static const GrShaderVar gradArgs[] = { - GrShaderVar("x", kFloat_GrSLType), - GrShaderVar("p", kVec3f_GrSLType) - }; - SkString gradFuncName; - SkString gradCode("return dot("); - fragBuilder->appendTextureLookup(&gradCode, args.fTexSamplers[1], "vec2(fract(x / 16.0), 0.0)", - kVec2f_GrSLType); - gradCode.append(".rgb * 255.0 - vec3(1.0), p);"); - fragBuilder->emitFunction(kFloat_GrSLType, "grad", SK_ARRAY_COUNT(gradArgs), gradArgs, - gradCode.c_str(), &gradFuncName); - - // lerp function - static const GrShaderVar lerpArgs[] = { - GrShaderVar("a", kFloat_GrSLType), - GrShaderVar("b", kFloat_GrSLType), - GrShaderVar("w", kFloat_GrSLType) - }; - SkString lerpFuncName; - fragBuilder->emitFunction(kFloat_GrSLType, "lerp", SK_ARRAY_COUNT(lerpArgs), lerpArgs, - "return a + w * (b - a);", &lerpFuncName); - - // noise function - static const GrShaderVar noiseArgs[] = { - GrShaderVar("p", kVec3f_GrSLType), - }; - SkString noiseFuncName; - SkString noiseCode; - noiseCode.append("vec3 P = mod(floor(p), 256.0);"); - noiseCode.append("p -= floor(p);"); - noiseCode.appendf("vec3 f = %s(p);", fadeFuncName.c_str()); - noiseCode.appendf("float A = %s(P.x) + P.y;", permFuncName.c_str()); - noiseCode.appendf("float AA = %s(A) + P.z;", permFuncName.c_str()); - noiseCode.appendf("float AB = %s(A + 1.0) + P.z;", permFuncName.c_str()); - noiseCode.appendf("float B = %s(P.x + 1.0) + P.y;", permFuncName.c_str()); - noiseCode.appendf("float BA = %s(B) + P.z;", permFuncName.c_str()); - noiseCode.appendf("float BB = %s(B + 1.0) + P.z;", permFuncName.c_str()); - noiseCode.appendf("float result = %s(", lerpFuncName.c_str()); - noiseCode.appendf("%s(%s(%s(%s(AA), p),", lerpFuncName.c_str(), lerpFuncName.c_str(), - gradFuncName.c_str(), permFuncName.c_str()); - noiseCode.appendf("%s(%s(BA), p + vec3(-1.0, 0.0, 0.0)), f.x),", gradFuncName.c_str(), - permFuncName.c_str()); - noiseCode.appendf("%s(%s(%s(AB), p + vec3(0.0, -1.0, 0.0)),", lerpFuncName.c_str(), - gradFuncName.c_str(), permFuncName.c_str()); - noiseCode.appendf("%s(%s(BB), p + vec3(-1.0, -1.0, 0.0)), f.x), f.y),", - gradFuncName.c_str(), permFuncName.c_str()); - noiseCode.appendf("%s(%s(%s(%s(AA + 1.0), p + vec3(0.0, 0.0, -1.0)),", - lerpFuncName.c_str(), lerpFuncName.c_str(), gradFuncName.c_str(), - permFuncName.c_str()); - noiseCode.appendf("%s(%s(BA + 1.0), p + vec3(-1.0, 0.0, -1.0)), f.x),", - gradFuncName.c_str(), permFuncName.c_str()); - noiseCode.appendf("%s(%s(%s(AB + 1.0), p + vec3(0.0, -1.0, -1.0)),", - lerpFuncName.c_str(), gradFuncName.c_str(), permFuncName.c_str()); - noiseCode.appendf("%s(%s(BB + 1.0), p + vec3(-1.0, -1.0, -1.0)), f.x), f.y), f.z);", - gradFuncName.c_str(), permFuncName.c_str()); - noiseCode.append("return result;"); - fragBuilder->emitFunction(kFloat_GrSLType, "noise", SK_ARRAY_COUNT(noiseArgs), noiseArgs, - noiseCode.c_str(), &noiseFuncName); - - // noiseOctaves function - static const GrShaderVar noiseOctavesArgs[] = { - GrShaderVar("p", kVec3f_GrSLType), - GrShaderVar("octaves", kFloat_GrSLType), - }; - SkString noiseOctavesFuncName; - SkString noiseOctavesCode; - noiseOctavesCode.append("float result = 0.0;"); - noiseOctavesCode.append("float ratio = 1.0;"); - noiseOctavesCode.append("for (float i = 0.0; i < octaves; i++) {"); - noiseOctavesCode.appendf("result += %s(p) / ratio;", noiseFuncName.c_str()); - noiseOctavesCode.append("p *= 2.0;"); - noiseOctavesCode.append("ratio *= 2.0;"); - noiseOctavesCode.append("}"); - noiseOctavesCode.append("return (result + 1.0) / 2.0;"); - fragBuilder->emitFunction(kFloat_GrSLType, "noiseOctaves", SK_ARRAY_COUNT(noiseOctavesArgs), - noiseOctavesArgs, noiseOctavesCode.c_str(), &noiseOctavesFuncName); - - fragBuilder->codeAppendf("vec2 coords = %s * %s;", vCoords.c_str(), baseFrequencyUni); - fragBuilder->codeAppendf("float r = %s(vec3(coords, %s), %s);", noiseOctavesFuncName.c_str(), - zUni, octavesUni); - fragBuilder->codeAppendf("float g = %s(vec3(coords, %s + 0000.0), %s);", - noiseOctavesFuncName.c_str(), zUni, octavesUni); - fragBuilder->codeAppendf("float b = %s(vec3(coords, %s + 0000.0), %s);", - noiseOctavesFuncName.c_str(), zUni, octavesUni); - fragBuilder->codeAppendf("float a = %s(vec3(coords, %s + 0000.0), %s);", - noiseOctavesFuncName.c_str(), zUni, octavesUni); - fragBuilder->codeAppendf("%s = vec4(r, g, b, a);", args.fOutputColor); - - // Clamp values - fragBuilder->codeAppendf("%s = clamp(%s, 0.0, 1.0);", args.fOutputColor, args.fOutputColor); - - // Pre-multiply the result - fragBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n", - args.fOutputColor, args.fOutputColor, - args.fOutputColor, args.fOutputColor); -} - -void GrGLImprovedPerlinNoise::GenKey(const GrProcessor& processor, const GrShaderCaps&, - GrProcessorKeyBuilder* b) { -} - -void GrGLImprovedPerlinNoise::onSetData(const GrGLSLProgramDataManager& pdman, - const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - - const GrImprovedPerlinNoiseEffect& noise = processor.cast<GrImprovedPerlinNoiseEffect>(); - - const SkVector& baseFrequency = noise.baseFrequency(); - pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY); - - pdman.set1f(fOctavesUni, SkIntToScalar(noise.octaves())); - - pdman.set1f(fZUni, noise.z()); -} - -///////////////////////////////////////////////////////////////////// -sk_sp<GrFragmentProcessor> SkPerlinNoiseShaderImpl::asFragmentProcessor(const AsFPArgs& args) const { - SkASSERT(args.fContext); - - SkMatrix localMatrix = this->getLocalMatrix(); - if (args.fLocalMatrix) { - localMatrix.preConcat(*args.fLocalMatrix); - } - - SkMatrix matrix = *args.fViewMatrix; - matrix.preConcat(localMatrix); - - // Either we don't stitch tiles, either we have a valid tile size - SkASSERT(!fStitchTiles || !fTileSize.isEmpty()); - - SkPerlinNoiseShaderImpl::PaintingData* paintingData = - new PaintingData(fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY, matrix); - - SkMatrix m = *args.fViewMatrix; - m.setTranslateX(-localMatrix.getTranslateX() + SK_Scalar1); - m.setTranslateY(-localMatrix.getTranslateY() + SK_Scalar1); - - if (fType == kImprovedNoise_Type) { - GrSamplerParams textureParams(SkShader::TileMode::kRepeat_TileMode, - GrSamplerParams::FilterMode::kNone_FilterMode); - sk_sp<GrTextureProxy> permutationsTexture( - GrRefCachedBitmapTextureProxy(args.fContext, - paintingData->getImprovedPermutationsBitmap(), - textureParams, nullptr)); - sk_sp<GrTextureProxy> gradientTexture( - GrRefCachedBitmapTextureProxy(args.fContext, - paintingData->getGradientBitmap(), - textureParams, nullptr)); - return GrImprovedPerlinNoiseEffect::Make(args.fContext->resourceProvider(), - fNumOctaves, fSeed, paintingData, - std::move(permutationsTexture), - std::move(gradientTexture), m); - } - - if (0 == fNumOctaves) { - if (kFractalNoise_Type == fType) { - // Extract the incoming alpha and emit rgba = (a/4, a/4, a/4, a/2) - // TODO: Either treat the output of this shader as sRGB or allow client to specify a - // color space of the noise. Either way, this case (and the GLSL) need to convert to - // the destination. - sk_sp<GrFragmentProcessor> inner( - GrConstColorProcessor::Make(GrColor4f::FromGrColor(0x80404040), - GrConstColorProcessor::kModulateRGBA_InputMode)); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); - } - // Emit zero. - return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(), - GrConstColorProcessor::kIgnore_InputMode); - } - - sk_sp<GrTextureProxy> permutationsProxy = GrMakeCachedBitmapProxy( - args.fContext->resourceProvider(), - paintingData->getPermutationsBitmap()); - sk_sp<GrTextureProxy> noiseProxy = GrMakeCachedBitmapProxy(args.fContext->resourceProvider(), - paintingData->getNoiseBitmap()); - - if (permutationsProxy && noiseProxy) { - sk_sp<GrFragmentProcessor> inner( - GrPerlinNoise2Effect::Make(args.fContext->resourceProvider(), - fType, - fNumOctaves, - fStitchTiles, - paintingData, - std::move(permutationsProxy), - std::move(noiseProxy), - m)); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); - } - delete paintingData; - return nullptr; -} - -#endif - -#ifndef SK_IGNORE_TO_STRING -void SkPerlinNoiseShaderImpl::toString(SkString* str) const { - str->append("SkPerlinNoiseShaderImpl: ("); - - 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 - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -sk_sp<SkShader> SkPerlinNoiseShader::MakeFractalNoise(SkScalar baseFrequencyX, - SkScalar baseFrequencyY, - int numOctaves, SkScalar seed, - const SkISize* tileSize) { - return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kFractalNoise_Type, - baseFrequencyX, baseFrequencyY, numOctaves, seed, - tileSize)); -} - -sk_sp<SkShader> SkPerlinNoiseShader::MakeTurbulence(SkScalar baseFrequencyX, - SkScalar baseFrequencyY, - int numOctaves, SkScalar seed, - const SkISize* tileSize) { - return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kTurbulence_Type, - baseFrequencyX, baseFrequencyY, numOctaves, seed, - tileSize)); -} - -sk_sp<SkShader> SkPerlinNoiseShader::MakeImprovedNoise(SkScalar baseFrequencyX, - SkScalar baseFrequencyY, - int numOctaves, SkScalar z) { - return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kImprovedNoise_Type, - baseFrequencyX, baseFrequencyY, numOctaves, z, - nullptr)); -} - -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkPerlinNoiseShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShaderImpl) -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END diff --git a/src/effects/gradients/Sk4fGradientBase.cpp b/src/effects/gradients/Sk4fGradientBase.cpp deleted file mode 100644 index e20f5f4702..0000000000 --- a/src/effects/gradients/Sk4fGradientBase.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Sk4fGradientBase.h" - -#include <functional> - -namespace { - -Sk4f pack_color(SkColor c, bool premul, const Sk4f& component_scale) { - const SkColor4f c4f = SkColor4f::FromColor(c); - const Sk4f pm4f = premul - ? c4f.premul().to4f() - : Sk4f{c4f.fR, c4f.fG, c4f.fB, c4f.fA}; - - return pm4f * component_scale; -} - -class IntervalIterator { -public: - IntervalIterator(const SkColor* colors, const SkScalar* pos, int count, bool reverse) - : fColors(colors) - , fPos(pos) - , fCount(count) - , fFirstPos(reverse ? SK_Scalar1 : 0) - , fBegin(reverse ? count - 1 : 0) - , fAdvance(reverse ? -1 : 1) { - SkASSERT(colors); - SkASSERT(count > 0); - } - - void iterate(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const { - if (!fPos) { - this->iterateImplicitPos(func); - return; - } - - const int end = fBegin + fAdvance * (fCount - 1); - const SkScalar lastPos = 1 - fFirstPos; - int prev = fBegin; - SkScalar prevPos = fFirstPos; - - do { - const int curr = prev + fAdvance; - SkASSERT(curr >= 0 && curr < fCount); - - // TODO: this sanitization should be done in SkGradientShaderBase - const SkScalar currPos = (fAdvance > 0) - ? SkTPin(fPos[curr], prevPos, lastPos) - : SkTPin(fPos[curr], lastPos, prevPos); - - if (currPos != prevPos) { - SkASSERT((currPos - prevPos > 0) == (fAdvance > 0)); - func(fColors[prev], fColors[curr], prevPos, currPos); - } - - prev = curr; - prevPos = currPos; - } while (prev != end); - } - -private: - void iterateImplicitPos(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const { - // When clients don't provide explicit color stop positions (fPos == nullptr), - // the color stops are distributed evenly across the unit interval - // (implicit positioning). - const SkScalar dt = fAdvance * SK_Scalar1 / (fCount - 1); - const int end = fBegin + fAdvance * (fCount - 2); - int prev = fBegin; - SkScalar prevPos = fFirstPos; - - while (prev != end) { - const int curr = prev + fAdvance; - SkASSERT(curr >= 0 && curr < fCount); - - const SkScalar currPos = prevPos + dt; - func(fColors[prev], fColors[curr], prevPos, currPos); - prev = curr; - prevPos = currPos; - } - - // emit the last interval with a pinned end position, to avoid precision issues - func(fColors[prev], fColors[prev + fAdvance], prevPos, 1 - fFirstPos); - } - - const SkColor* fColors; - const SkScalar* fPos; - const int fCount; - const SkScalar fFirstPos; - const int fBegin; - const int fAdvance; -}; - -void addMirrorIntervals(const SkColor colors[], - const SkScalar pos[], int count, - const Sk4f& componentScale, - bool premulColors, bool reverse, - Sk4fGradientIntervalBuffer::BufferType* buffer) { - const IntervalIterator iter(colors, pos, count, reverse); - iter.iterate([&] (SkColor c0, SkColor c1, SkScalar t0, SkScalar t1) { - SkASSERT(buffer->empty() || buffer->back().fT1 == 2 - t0); - - const auto mirror_t0 = 2 - t0; - const auto mirror_t1 = 2 - t1; - // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid - // triggering Interval asserts. - if (mirror_t0 != mirror_t1) { - buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_t0, - pack_color(c1, premulColors, componentScale), mirror_t1); - } - }); -} - -} // anonymous namespace - -Sk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar t0, - const Sk4f& c1, SkScalar t1) - : fT0(t0) - , fT1(t1) { - SkASSERT(t0 != t1); - // Either p0 or p1 can be (-)inf for synthetic clamp edge intervals. - SkASSERT(SkScalarIsFinite(t0) || SkScalarIsFinite(t1)); - - const auto dt = t1 - t0; - - // Clamp edge intervals are always zero-ramp. - SkASSERT(SkScalarIsFinite(dt) || (c0 == c1).allTrue()); - SkASSERT(SkScalarIsFinite(t0) || (c0 == c1).allTrue()); - const Sk4f dc = SkScalarIsFinite(dt) ? (c1 - c0) / dt : 0; - const Sk4f bias = c0 - (SkScalarIsFinite(t0) ? t0 * dc : 0); - - bias.store(&fCb.fVec); - dc.store(&fCg.fVec); -} - -void Sk4fGradientIntervalBuffer::init(const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode tileMode, bool premulColors, - SkScalar alpha, bool reverse) { - // The main job here is to build a specialized interval list: a different - // representation of the color stops data, optimized for efficient scan line - // access during shading. - // - // [{P0,C0} , {P1,C1}) [{P1,C2} , {P2,c3}) ... [{Pn,C2n} , {Pn+1,C2n+1}) - // - // The list may be inverted when requested (such that e.g. points are sorted - // in increasing x order when dx < 0). - // - // Note: the current representation duplicates pos data; we could refactor to - // avoid this if interval storage size becomes a concern. - // - // Aside from reordering, we also perform two more pre-processing steps at - // this stage: - // - // 1) scale the color components depending on paint alpha and the requested - // interpolation space (note: the interval color storage is SkPM4f, but - // that doesn't necessarily mean the colors are premultiplied; that - // property is tracked in fColorsArePremul) - // - // 2) inject synthetic intervals to support tiling. - // - // * for kRepeat, no extra intervals are needed - the iterator just - // wraps around at the end: - // - // ->[P0,P1)->..[Pn-1,Pn)-> - // - // * for kClamp, we add two "infinite" intervals before/after: - // - // [-/+inf , P0)->[P0 , P1)->..[Pn-1 , Pn)->[Pn , +/-inf) - // - // (the iterator should never run off the end in this mode) - // - // * for kMirror, we extend the range to [0..2] and add a flipped - // interval series - then the iterator operates just as in the - // kRepeat case: - // - // ->[P0,P1)->..[Pn-1,Pn)->[2 - Pn,2 - Pn-1)->..[2 - P1,2 - P0)-> - // - // TODO: investigate collapsing intervals << 1px. - - SkASSERT(count > 0); - SkASSERT(colors); - - fIntervals.reset(); - - const Sk4f componentScale = premulColors - ? Sk4f(alpha) - : Sk4f(1.0f, 1.0f, 1.0f, alpha); - const int first_index = reverse ? count - 1 : 0; - const int last_index = count - 1 - first_index; - const SkScalar first_pos = reverse ? SK_Scalar1 : 0; - const SkScalar last_pos = SK_Scalar1 - first_pos; - - if (tileMode == SkShader::kClamp_TileMode) { - // synthetic edge interval: -/+inf .. P0 - const Sk4f clamp_color = pack_color(colors[first_index], - premulColors, componentScale); - const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity; - fIntervals.emplace_back(clamp_color, clamp_pos, - clamp_color, first_pos); - } else if (tileMode == SkShader::kMirror_TileMode && reverse) { - // synthetic mirror intervals injected before main intervals: (2 .. 1] - addMirrorIntervals(colors, pos, count, componentScale, premulColors, false, &fIntervals); - } - - const IntervalIterator iter(colors, pos, count, reverse); - iter.iterate([&] (SkColor c0, SkColor c1, SkScalar t0, SkScalar t1) { - SkASSERT(fIntervals.empty() || fIntervals.back().fT1 == t0); - - fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), t0, - pack_color(c1, premulColors, componentScale), t1); - }); - - if (tileMode == SkShader::kClamp_TileMode) { - // synthetic edge interval: Pn .. +/-inf - const Sk4f clamp_color = pack_color(colors[last_index], premulColors, componentScale); - const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity; - fIntervals.emplace_back(clamp_color, last_pos, - clamp_color, clamp_pos); - } else if (tileMode == SkShader::kMirror_TileMode && !reverse) { - // synthetic mirror intervals injected after main intervals: [1 .. 2) - addMirrorIntervals(colors, pos, count, componentScale, premulColors, true, &fIntervals); - } -} - -const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const { - // Binary search. - const auto* i0 = fIntervals.begin(); - const auto* i1 = fIntervals.end() - 1; - - while (i0 != i1) { - SkASSERT(i0 < i1); - SkASSERT(t >= i0->fT0 && t <= i1->fT1); - - const auto* i = i0 + ((i1 - i0) >> 1); - - if (t > i->fT1) { - i0 = i + 1; - } else { - i1 = i; - } - } - - SkASSERT(i0->contains(t)); - return i0; -} - -const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext( - SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const { - - SkASSERT(!prev->contains(t)); - SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end()); - SkASSERT(t >= fIntervals.front().fT0 && t <= fIntervals.back().fT1); - - const auto* i = prev; - - // Use the |increasing| signal to figure which direction we should search for - // the next interval, then perform a linear search. - if (increasing) { - do { - i += 1; - if (i >= fIntervals.end()) { - i = fIntervals.begin(); - } - } while (!i->contains(t)); - } else { - do { - i -= 1; - if (i < fIntervals.begin()) { - i = fIntervals.end() - 1; - } - } while (!i->contains(t)); - } - - return i; -} - -SkGradientShaderBase:: -GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader, - const ContextRec& rec) - : INHERITED(shader, rec) - , fFlags(this->INHERITED::getFlags()) -#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING - , fDither(true) -#else - , fDither(rec.fPaint->isDither()) -#endif -{ - const SkMatrix& inverse = this->getTotalInverse(); - fDstToPos.setConcat(shader.fPtsToUnit, inverse); - fDstToPosProc = fDstToPos.getMapXYProc(); - fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos)); - - if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) { - fFlags |= kOpaqueAlpha_Flag; - } - - fColorsArePremul = - (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) - || shader.fColorsAreOpaque; -} - -bool SkGradientShaderBase:: -GradientShaderBase4fContext::isValid() const { - return fDstToPos.isFinite(); -} - -void SkGradientShaderBase:: -GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) { - if (fColorsArePremul) { - this->shadePremulSpan<DstType::L32, ApplyPremul::False>(x, y, dst, count); - } else { - this->shadePremulSpan<DstType::L32, ApplyPremul::True>(x, y, dst, count); - } -} - -void SkGradientShaderBase:: -GradientShaderBase4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) { - if (fColorsArePremul) { - this->shadePremulSpan<DstType::F32, ApplyPremul::False>(x, y, dst, count); - } else { - this->shadePremulSpan<DstType::F32, ApplyPremul::True>(x, y, dst, count); - } -} - -template<DstType dstType, ApplyPremul premul> -void SkGradientShaderBase:: -GradientShaderBase4fContext::shadePremulSpan(int x, int y, - typename DstTraits<dstType, premul>::Type dst[], - int count) const { - const SkGradientShaderBase& shader = - static_cast<const SkGradientShaderBase&>(fShader); - - switch (shader.fTileMode) { - case kClamp_TileMode: - this->shadeSpanInternal<dstType, - premul, - kClamp_TileMode>(x, y, dst, count); - break; - case kRepeat_TileMode: - this->shadeSpanInternal<dstType, - premul, - kRepeat_TileMode>(x, y, dst, count); - break; - case kMirror_TileMode: - this->shadeSpanInternal<dstType, - premul, - kMirror_TileMode>(x, y, dst, count); - break; - } -} - -template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> -void SkGradientShaderBase:: -GradientShaderBase4fContext::shadeSpanInternal(int x, int y, - typename DstTraits<dstType, premul>::Type dst[], - int count) const { - static const int kBufSize = 128; - SkScalar ts[kBufSize]; - TSampler<dstType, premul, tileMode> sampler(*this); - - SkASSERT(count > 0); - do { - const int n = SkTMin(kBufSize, count); - this->mapTs(x, y, ts, n); - for (int i = 0; i < n; ++i) { - const Sk4f c = sampler.sample(ts[i]); - DstTraits<dstType, premul>::store(c, dst++); - } - x += n; - count -= n; - } while (count > 0); -} - -template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> -class SkGradientShaderBase::GradientShaderBase4fContext::TSampler { -public: - TSampler(const GradientShaderBase4fContext& ctx) - : fCtx(ctx) - , fInterval(nullptr) { - switch (tileMode) { - case kClamp_TileMode: - fLargestIntervalValue = SK_ScalarInfinity; - break; - case kRepeat_TileMode: - fLargestIntervalValue = nextafterf(1, 0); - break; - case kMirror_TileMode: - fLargestIntervalValue = nextafterf(2.0f, 0); - break; - } - } - - Sk4f sample(SkScalar t) { - const auto tiled_t = tileProc(t); - - if (!fInterval) { - // Very first sample => locate the initial interval. - // TODO: maybe do this in ctor to remove a branch? - fInterval = fCtx.fIntervals.find(tiled_t); - this->loadIntervalData(fInterval); - } else if (!fInterval->contains(tiled_t)) { - fInterval = fCtx.fIntervals.findNext(tiled_t, fInterval, t >= fPrevT); - this->loadIntervalData(fInterval); - } - - fPrevT = t; - return lerp(tiled_t); - } - -private: - SkScalar tileProc(SkScalar t) const { - switch (tileMode) { - case kClamp_TileMode: - // synthetic clamp-mode edge intervals allow for a free-floating t: - // [-inf..0)[0..1)[1..+inf) - return t; - case kRepeat_TileMode: - // t % 1 (intervals range: [0..1)) - // Due to the extra arithmetic, we must clamp to ensure the value remains less than 1. - return SkTMin(t - SkScalarFloorToScalar(t), fLargestIntervalValue); - case kMirror_TileMode: - // t % 2 (synthetic mirror intervals expand the range to [0..2) - // Due to the extra arithmetic, we must clamp to ensure the value remains less than 2. - return SkTMin(t - SkScalarFloorToScalar(t / 2) * 2, fLargestIntervalValue); - } - - SK_ABORT("Unhandled tile mode."); - return 0; - } - - Sk4f lerp(SkScalar t) { - SkASSERT(fInterval->contains(t)); - return fCb + fCg * t; - } - - void loadIntervalData(const Sk4fGradientInterval* i) { - fCb = DstTraits<dstType, premul>::load(i->fCb); - fCg = DstTraits<dstType, premul>::load(i->fCg); - } - - const GradientShaderBase4fContext& fCtx; - const Sk4fGradientInterval* fInterval; - SkScalar fPrevT; - SkScalar fLargestIntervalValue; - Sk4f fCb; - Sk4f fCg; -}; diff --git a/src/effects/gradients/Sk4fGradientBase.h b/src/effects/gradients/Sk4fGradientBase.h deleted file mode 100644 index a660d6bde5..0000000000 --- a/src/effects/gradients/Sk4fGradientBase.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef Sk4fGradientBase_DEFINED -#define Sk4fGradientBase_DEFINED - -#include "Sk4fGradientPriv.h" -#include "SkColor.h" -#include "SkGradientShaderPriv.h" -#include "SkMatrix.h" -#include "SkNx.h" -#include "SkPM4f.h" -#include "SkShaderBase.h" -#include "SkTArray.h" - -struct Sk4fGradientInterval { - Sk4fGradientInterval(const Sk4f& c0, SkScalar t0, - const Sk4f& c1, SkScalar t1); - - bool contains(SkScalar t) const { - // True if t is in [p0,p1]. Note: this helper assumes a - // natural/increasing interval - so it's not usable in Sk4fLinearGradient. - SkASSERT(fT0 < fT1); - return t >= fT0 && t <= fT1; - } - - // Color bias and color gradient, such that for a t in this interval - // - // C = fCb + t * fCg; - SkPM4f fCb, fCg; - SkScalar fT0, fT1; -}; - -class Sk4fGradientIntervalBuffer { -public: - void init(const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode tileMode, bool premulColors, SkScalar alpha, bool reverse); - - const Sk4fGradientInterval* find(SkScalar t) const; - const Sk4fGradientInterval* findNext(SkScalar t, const Sk4fGradientInterval* prev, - bool increasing) const; - - using BufferType = SkSTArray<8, Sk4fGradientInterval, true>; - - const BufferType* operator->() const { return &fIntervals; } - -private: - BufferType fIntervals; -}; - -class SkGradientShaderBase:: -GradientShaderBase4fContext : public Context { -public: - GradientShaderBase4fContext(const SkGradientShaderBase&, - const ContextRec&); - - uint32_t getFlags() const override { return fFlags; } - - void shadeSpan(int x, int y, SkPMColor dst[], int count) override; - void shadeSpan4f(int x, int y, SkPM4f dst[], int count) override; - - bool isValid() const; - -protected: - virtual void mapTs(int x, int y, SkScalar ts[], int count) const = 0; - - Sk4fGradientIntervalBuffer fIntervals; - SkMatrix fDstToPos; - SkMatrix::MapXYProc fDstToPosProc; - uint8_t fDstToPosClass; - uint8_t fFlags; - bool fDither; - bool fColorsArePremul; - -private: - using INHERITED = Context; - - void addMirrorIntervals(const SkGradientShaderBase&, - const Sk4f& componentScale, bool reverse); - - template<DstType, ApplyPremul, SkShader::TileMode tileMode> - class TSampler; - - template <DstType dstType, ApplyPremul premul> - void shadePremulSpan(int x, int y, typename DstTraits<dstType, premul>::Type[], - int count) const; - - template <DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> - void shadeSpanInternal(int x, int y, typename DstTraits<dstType, premul>::Type[], - int count) const; -}; - -#endif // Sk4fGradientBase_DEFINED diff --git a/src/effects/gradients/Sk4fGradientPriv.h b/src/effects/gradients/Sk4fGradientPriv.h deleted file mode 100644 index f18d6ced7b..0000000000 --- a/src/effects/gradients/Sk4fGradientPriv.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef Sk4fGradientPriv_DEFINED -#define Sk4fGradientPriv_DEFINED - -#include "SkColor.h" -#include "SkHalf.h" -#include "SkImageInfo.h" -#include "SkNx.h" -#include "SkPM4f.h" -#include "SkPM4fPriv.h" -#include "SkUtils.h" - -// Templates shared by various 4f gradient flavors. - -namespace { - -enum class ApplyPremul { True, False }; - -enum class DstType { - L32, // Linear 32bit. - F32, // Linear float. -}; - -template <ApplyPremul> -struct PremulTraits; - -template <> -struct PremulTraits<ApplyPremul::False> { - static Sk4f apply(const Sk4f& c) { return c; } -}; - -template <> -struct PremulTraits<ApplyPremul::True> { - static Sk4f apply(const Sk4f& c) { - const float alpha = c[SkPM4f::A]; - // FIXME: portable swizzle? - return c * Sk4f(alpha, alpha, alpha, 1); - } -}; - -// Struct encapsulating various dest-dependent ops: -// -// - load() Load a SkPM4f value into Sk4f. Normally called once per interval -// advance. Also applies a scale and swizzle suitable for DstType. -// -// - store() Store one Sk4f to dest. Optionally handles premul, color space -// conversion, etc. -// -// - store(count) Store the Sk4f value repeatedly to dest, count times. -// -// - store4x() Store 4 Sk4f values to dest (opportunistic optimization). -// -template <DstType, ApplyPremul premul> -struct DstTraits; - -template <ApplyPremul premul> -struct DstTraits<DstType::L32, premul> { - using PM = PremulTraits<premul>; - using Type = SkPMColor; - - // For L32, prescaling by 255 saves a per-pixel multiplication when premul is not needed. - static Sk4f load(const SkPM4f& c) { - return premul == ApplyPremul::False - ? c.to4f_pmorder() * Sk4f(255) - : c.to4f_pmorder(); - } - - static void store(const Sk4f& c, Type* dst) { - if (premul == ApplyPremul::False) { - // c is prescaled by 255, just store. - SkNx_cast<uint8_t>(c).store(dst); - } else { - *dst = Sk4f_toL32(PM::apply(c)); - } - } - - static void store(const Sk4f& c, Type* dst, int n) { - Type pmc; - store(c, &pmc); - sk_memset32(dst, pmc, n); - } - - static void store4x(const Sk4f& c0, const Sk4f& c1, - const Sk4f& c2, const Sk4f& c3, - Type* dst) { - if (premul == ApplyPremul::False) { - Sk4f_ToBytes((uint8_t*)dst, c0, c1, c2, c3); - } else { - store(c0, dst + 0); - store(c1, dst + 1); - store(c2, dst + 2); - store(c3, dst + 3); - } - } -}; - -template <ApplyPremul premul> -struct DstTraits<DstType::F32, premul> { - using PM = PremulTraits<premul>; - using Type = SkPM4f; - - static Sk4f load(const SkPM4f& c) { - return c.to4f(); - } - - static void store(const Sk4f& c, Type* dst) { - PM::apply(c).store(dst->fVec); - } - - static void store(const Sk4f& c, Type* dst, int n) { - const Sk4f pmc = PM::apply(c); - for (int i = 0; i < n; ++i) { - pmc.store(dst[i].fVec); - } - } - - static void store4x(const Sk4f& c0, const Sk4f& c1, - const Sk4f& c2, const Sk4f& c3, - Type* dst) { - store(c0, dst + 0); - store(c1, dst + 1); - store(c2, dst + 2); - store(c3, dst + 3); - } -}; - -} // anonymous namespace - -#endif // Sk4fGradientPriv_DEFINED diff --git a/src/effects/gradients/Sk4fLinearGradient.cpp b/src/effects/gradients/Sk4fLinearGradient.cpp deleted file mode 100644 index 7b7498eaef..0000000000 --- a/src/effects/gradients/Sk4fLinearGradient.cpp +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Sk4fLinearGradient.h" -#include "Sk4x4f.h" - -#include <cmath> - -namespace { - -template<DstType dstType, ApplyPremul premul> -void ramp(const Sk4f& c, const Sk4f& dc, typename DstTraits<dstType, premul>::Type dst[], int n) { - SkASSERT(n > 0); - - const Sk4f dc2 = dc + dc; - const Sk4f dc4 = dc2 + dc2; - - Sk4f c0 = c ; - Sk4f c1 = c + dc; - Sk4f c2 = c0 + dc2; - Sk4f c3 = c1 + dc2; - - while (n >= 4) { - DstTraits<dstType, premul>::store4x(c0, c1, c2, c3, dst); - dst += 4; - - c0 = c0 + dc4; - c1 = c1 + dc4; - c2 = c2 + dc4; - c3 = c3 + dc4; - n -= 4; - } - if (n & 2) { - DstTraits<dstType, premul>::store(c0, dst++); - DstTraits<dstType, premul>::store(c1, dst++); - c0 = c0 + dc2; - } - if (n & 1) { - DstTraits<dstType, premul>::store(c0, dst); - } -} - -template<SkShader::TileMode> -SkScalar pinFx(SkScalar); - -template<> -SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) { - return fx; -} - -template<> -SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) { - SkScalar f = SkScalarFraction(fx); - if (f < 0) { - f = SkTMin(f + 1, nextafterf(1, 0)); - } - SkASSERT(f >= 0); - SkASSERT(f < 1.0f); - return f; -} - -template<> -SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) { - SkScalar f = SkScalarMod(fx, 2.0f); - if (f < 0) { - f = SkTMin(f + 2, nextafterf(2, 0)); - } - SkASSERT(f >= 0); - SkASSERT(f < 2.0f); - return f; -} - -// true when x is in [k1,k2], or [k2, k1] when the interval is reversed. -// TODO(fmalita): hoist the reversed interval check out of this helper. -bool in_range(SkScalar x, SkScalar k1, SkScalar k2) { - SkASSERT(k1 != k2); - return (k1 < k2) - ? (x >= k1 && x <= k2) - : (x >= k2 && x <= k1); -} - -} // anonymous namespace - -SkLinearGradient:: -LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader, - const ContextRec& rec) - : INHERITED(shader, rec) { - - // Our fast path expects interval points to be monotonically increasing in x. - const bool reverseIntervals = this->isFast() && std::signbit(fDstToPos.getScaleX()); - fIntervals.init(shader.fOrigColors, shader.fOrigPos, shader.fColorCount, shader.fTileMode, - fColorsArePremul, rec.fPaint->getAlpha() * (1.0f / 255), reverseIntervals); - - SkASSERT(fIntervals->count() > 0); - fCachedInterval = fIntervals->begin(); -} - -const Sk4fGradientInterval* -SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const { - SkASSERT(in_range(fx, fIntervals->front().fT0, fIntervals->back().fT1)); - - if (1) { - // Linear search, using the last scanline interval as a starting point. - SkASSERT(fCachedInterval >= fIntervals->begin()); - SkASSERT(fCachedInterval < fIntervals->end()); - const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1; - while (!in_range(fx, fCachedInterval->fT0, fCachedInterval->fT1)) { - fCachedInterval += search_dir; - if (fCachedInterval >= fIntervals->end()) { - fCachedInterval = fIntervals->begin(); - } else if (fCachedInterval < fIntervals->begin()) { - fCachedInterval = fIntervals->end() - 1; - } - } - return fCachedInterval; - } else { - // Binary search. Seems less effective than linear + caching. - const auto* i0 = fIntervals->begin(); - const auto* i1 = fIntervals->end() - 1; - - while (i0 != i1) { - SkASSERT(i0 < i1); - SkASSERT(in_range(fx, i0->fT0, i1->fT1)); - - const auto* i = i0 + ((i1 - i0) >> 1); - - if (in_range(fx, i0->fT0, i->fT1)) { - i1 = i; - } else { - SkASSERT(in_range(fx, i->fT1, i1->fT1)); - i0 = i + 1; - } - } - - SkASSERT(in_range(fx, i0->fT0, i0->fT1)); - return i0; - } -} - -void SkLinearGradient:: -LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) { - if (!this->isFast()) { - this->INHERITED::shadeSpan(x, y, dst, count); - return; - } - - // TODO: plumb dithering - SkASSERT(count > 0); - if (fColorsArePremul) { - this->shadePremulSpan<DstType::L32, - ApplyPremul::False>(x, y, dst, count); - } else { - this->shadePremulSpan<DstType::L32, - ApplyPremul::True>(x, y, dst, count); - } -} - -void SkLinearGradient:: -LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) { - if (!this->isFast()) { - this->INHERITED::shadeSpan4f(x, y, dst, count); - return; - } - - // TONOTDO: plumb dithering - SkASSERT(count > 0); - if (fColorsArePremul) { - this->shadePremulSpan<DstType::F32, - ApplyPremul::False>(x, y, dst, count); - } else { - this->shadePremulSpan<DstType::F32, - ApplyPremul::True>(x, y, dst, count); - } -} - -template<DstType dstType, ApplyPremul premul> -void SkLinearGradient:: -LinearGradient4fContext::shadePremulSpan(int x, int y, - typename DstTraits<dstType, premul>::Type dst[], - int count) const { - const SkLinearGradient& shader = - static_cast<const SkLinearGradient&>(fShader); - switch (shader.fTileMode) { - case kClamp_TileMode: - this->shadeSpanInternal<dstType, - premul, - kClamp_TileMode>(x, y, dst, count); - break; - case kRepeat_TileMode: - this->shadeSpanInternal<dstType, - premul, - kRepeat_TileMode>(x, y, dst, count); - break; - case kMirror_TileMode: - this->shadeSpanInternal<dstType, - premul, - kMirror_TileMode>(x, y, dst, count); - break; - } -} - -template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> -void SkLinearGradient:: -LinearGradient4fContext::shadeSpanInternal(int x, int y, - typename DstTraits<dstType, premul>::Type dst[], - int count) const { - SkPoint pt; - fDstToPosProc(fDstToPos, - x + SK_ScalarHalf, - y + SK_ScalarHalf, - &pt); - const SkScalar fx = pinFx<tileMode>(pt.x()); - const SkScalar dx = fDstToPos.getScaleX(); - LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals->begin(), - fIntervals->end() - 1, - this->findInterval(fx), - fx, - dx, - SkScalarNearlyZero(dx * count)); - while (count > 0) { - // What we really want here is SkTPin(advance, 1, count) - // but that's a significant perf hit for >> stops; investigate. - const int n = SkScalarTruncToInt( - SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count))); - - // The current interval advance can be +inf (e.g. when reaching - // the clamp mode end intervals) - when that happens, we expect to - // a) consume all remaining count in one swoop - // b) return a zero color gradient - SkASSERT(SkScalarIsFinite(proc.currentAdvance()) - || (n == count && proc.currentRampIsZero())); - - if (proc.currentRampIsZero()) { - DstTraits<dstType, premul>::store(proc.currentColor(), - dst, n); - } else { - ramp<dstType, premul>(proc.currentColor(), - proc.currentColorGrad(), - dst, n); - } - - proc.advance(SkIntToScalar(n)); - count -= n; - dst += n; - } -} - -template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> -class SkLinearGradient:: -LinearGradient4fContext::LinearIntervalProcessor { -public: - LinearIntervalProcessor(const Sk4fGradientInterval* firstInterval, - const Sk4fGradientInterval* lastInterval, - const Sk4fGradientInterval* i, - SkScalar fx, - SkScalar dx, - bool is_vertical) - : fAdvX(is_vertical ? SK_ScalarInfinity : (i->fT1 - fx) / dx) - , fFirstInterval(firstInterval) - , fLastInterval(lastInterval) - , fInterval(i) - , fDx(dx) - , fIsVertical(is_vertical) - { - SkASSERT(fAdvX >= 0); - SkASSERT(firstInterval <= lastInterval); - - if (tileMode != kClamp_TileMode && !is_vertical) { - const auto spanX = (lastInterval->fT1 - firstInterval->fT0) / dx; - SkASSERT(spanX >= 0); - - // If we're in a repeating tile mode and the whole gradient is compressed into a - // fraction of a pixel, we just use the average color in zero-ramp mode. - // This also avoids cases where we make no progress due to interval advances being - // close to zero. - static constexpr SkScalar kMinSpanX = .25f; - if (spanX < kMinSpanX) { - this->init_average_props(); - return; - } - } - - this->compute_interval_props(fx); - } - - SkScalar currentAdvance() const { - SkASSERT(fAdvX >= 0); - SkASSERT(fAdvX <= (fInterval->fT1 - fInterval->fT0) / fDx || !std::isfinite(fAdvX)); - return fAdvX; - } - - bool currentRampIsZero() const { return fZeroRamp; } - const Sk4f& currentColor() const { return fCc; } - const Sk4f& currentColorGrad() const { return fDcDx; } - - void advance(SkScalar advX) { - SkASSERT(advX > 0); - SkASSERT(fAdvX >= 0); - - if (advX >= fAdvX) { - advX = this->advance_interval(advX); - } - SkASSERT(advX < fAdvX); - - fCc = fCc + fDcDx * Sk4f(advX); - fAdvX -= advX; - } - -private: - void compute_interval_props(SkScalar t) { - SkASSERT(in_range(t, fInterval->fT0, fInterval->fT1)); - - const Sk4f dc = DstTraits<dstType, premul>::load(fInterval->fCg); - fCc = DstTraits<dstType, premul>::load(fInterval->fCb) + dc * Sk4f(t); - fDcDx = dc * fDx; - fZeroRamp = fIsVertical || (dc == 0).allTrue(); - } - - void init_average_props() { - fAdvX = SK_ScalarInfinity; - fZeroRamp = true; - fDcDx = 0; - fCc = Sk4f(0); - - // TODO: precompute the average at interval setup time? - for (const auto* i = fFirstInterval; i <= fLastInterval; ++i) { - // Each interval contributes its average color to the total/weighted average: - // - // C = (c0 + c1) / 2 = (Cb + Cg * t0 + Cb + Cg * t1) / 2 = Cb + Cg *(t0 + t1) / 2 - // - // Avg += C * (t1 - t0) - // - const auto c = DstTraits<dstType, premul>::load(i->fCb) - + DstTraits<dstType, premul>::load(i->fCg) * (i->fT0 + i->fT1) * 0.5f; - fCc = fCc + c * (i->fT1 - i->fT0); - } - } - - const Sk4fGradientInterval* next_interval(const Sk4fGradientInterval* i) const { - SkASSERT(i >= fFirstInterval); - SkASSERT(i <= fLastInterval); - i++; - - if (tileMode == kClamp_TileMode) { - SkASSERT(i <= fLastInterval); - return i; - } - - return (i <= fLastInterval) ? i : fFirstInterval; - } - - SkScalar advance_interval(SkScalar advX) { - SkASSERT(advX >= fAdvX); - - do { - advX -= fAdvX; - fInterval = this->next_interval(fInterval); - fAdvX = (fInterval->fT1 - fInterval->fT0) / fDx; - SkASSERT(fAdvX > 0); - } while (advX >= fAdvX); - - compute_interval_props(fInterval->fT0); - - SkASSERT(advX >= 0); - return advX; - } - - // Current interval properties. - Sk4f fDcDx; // dst color gradient (dc/dx) - Sk4f fCc; // current color, interpolated in dst - SkScalar fAdvX; // remaining interval advance in dst - bool fZeroRamp; // current interval color grad is 0 - - const Sk4fGradientInterval* fFirstInterval; - const Sk4fGradientInterval* fLastInterval; - const Sk4fGradientInterval* fInterval; // current interval - const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx - const bool fIsVertical; -}; - -void SkLinearGradient:: -LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const { - SkASSERT(count > 0); - SkASSERT(fDstToPosClass != kLinear_MatrixClass); - - SkScalar sx = x + SK_ScalarHalf; - const SkScalar sy = y + SK_ScalarHalf; - SkPoint pt; - - if (fDstToPosClass != kPerspective_MatrixClass) { - // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline - const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x(); - fDstToPosProc(fDstToPos, sx, sy, &pt); - - const Sk4f dtdx4 = Sk4f(4 * dtdx); - Sk4f t4 = Sk4f(pt.x() + 0 * dtdx, - pt.x() + 1 * dtdx, - pt.x() + 2 * dtdx, - pt.x() + 3 * dtdx); - - while (count >= 4) { - t4.store(ts); - t4 = t4 + dtdx4; - ts += 4; - count -= 4; - } - - if (count & 2) { - *ts++ = t4[0]; - *ts++ = t4[1]; - t4 = SkNx_shuffle<2, 0, 1, 3>(t4); - } - - if (count & 1) { - *ts++ = t4[0]; - } - } else { - for (int i = 0; i < count; ++i) { - fDstToPosProc(fDstToPos, sx, sy, &pt); - // Perspective may yield NaN values. - // Short of a better idea, drop to 0. - ts[i] = SkScalarIsNaN(pt.x()) ? 0 : pt.x(); - sx += SK_Scalar1; - } - } -} diff --git a/src/effects/gradients/Sk4fLinearGradient.h b/src/effects/gradients/Sk4fLinearGradient.h deleted file mode 100644 index f1c0bb5a30..0000000000 --- a/src/effects/gradients/Sk4fLinearGradient.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef Sk4fLinearGradient_DEFINED -#define Sk4fLinearGradient_DEFINED - -#include "Sk4fGradientBase.h" -#include "SkLinearGradient.h" - -class SkLinearGradient:: -LinearGradient4fContext final : public GradientShaderBase4fContext { -public: - LinearGradient4fContext(const SkLinearGradient&, const ContextRec&); - - void shadeSpan(int x, int y, SkPMColor dst[], int count) override; - void shadeSpan4f(int x, int y, SkPM4f dst[], int count) override; - -protected: - void mapTs(int x, int y, SkScalar ts[], int count) const override; - -private: - using INHERITED = GradientShaderBase4fContext; - - template<DstType, ApplyPremul, TileMode> - class LinearIntervalProcessor; - - template <DstType dstType, ApplyPremul premul> - void shadePremulSpan(int x, int y, typename DstTraits<dstType, premul>::Type[], - int count) const; - - template <DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> - void shadeSpanInternal(int x, int y, typename DstTraits<dstType, premul>::Type[], - int count) const; - - const Sk4fGradientInterval* findInterval(SkScalar fx) const; - - bool isFast() const { return fDstToPosClass == kLinear_MatrixClass; } - - mutable const Sk4fGradientInterval* fCachedInterval; -}; - -#endif // Sk4fLinearGradient_DEFINED diff --git a/src/effects/gradients/SkClampRange.cpp b/src/effects/gradients/SkClampRange.cpp deleted file mode 100644 index efc93959d1..0000000000 --- a/src/effects/gradients/SkClampRange.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkClampRange.h" -#include "SkMathPriv.h" - -static int SkCLZ64(uint64_t value) { - int count = 0; - if (value >> 32) { - value >>= 32; - } else { - count += 32; - } - return count + SkCLZ(SkToU32(value)); -} - -static bool sk_64_smul_check(int64_t count, int64_t dx, int64_t* result) { - // Do it the slow way until we have some assembly. - if (dx == std::numeric_limits<int64_t>::min()) { - return false; // SkTAbs overflow - } - - SkASSERT(count >= 0); - uint64_t ucount = static_cast<uint64_t>(count); - uint64_t udx = static_cast<uint64_t>(SkTAbs(dx)); - int zeros = SkCLZ64(ucount) + SkCLZ64(udx); - // this is a conservative check: it may return false when in fact it would not have overflowed. - // Hackers Delight uses 34 as its convervative check, but that is for 32x32 multiplies. - // Since we are looking at 64x64 muls, we add 32 to the check. - if (zeros < (32 + 34)) { - return false; - } - *result = count * dx; - return true; -} - -static bool sk_64_sadd_check(int64_t a, int64_t b, int64_t* result) { - if (a > 0) { - if (b > std::numeric_limits<int64_t>::max() - a) { - return false; - } - } else { - if (b < std::numeric_limits<int64_t>::min() - a) { - return false; - } - } - - *result = a + b; - return true; -} - - -/* - * returns [0..count] for the number of steps (<= count) for which x0 <= edge - * given each step is followed by x0 += dx - */ -static int chop(int64_t x0, SkGradFixed edge, int64_t x1, int64_t dx, int count) { - SkASSERT(dx > 0); - SkASSERT(count >= 0); - - if (x0 >= edge) { - return 0; - } - if (x1 <= edge) { - return count; - } - int64_t n = (edge - x0 + dx - 1) / dx; - SkASSERT(n >= 0); - SkASSERT(n <= count); - return (int)n; -} - -void SkClampRange::initFor1(SkGradFixed fx) { - fCount0 = fCount1 = fCount2 = 0; - if (fx <= 0) { - fCount0 = 1; - } else if (fx < kFracMax_SkGradFixed) { - fCount1 = 1; - fFx1 = fx; - } else { - fCount2 = 1; - } -} - -void SkClampRange::init(SkGradFixed fx0, SkGradFixed dx0, int count, int v0, int v1) { - SkASSERT(count > 0); - - fV0 = v0; - fV1 = v1; - - // special case 1 == count, as it is slightly common for skia - // and avoids us ever calling divide or 64bit multiply - if (1 == count) { - this->initFor1(fx0); - return; - } - - int64_t fx = fx0; - int64_t dx = dx0; - - // start with ex equal to the last computed value - int64_t count_times_dx, ex; - if (!sk_64_smul_check(count - 1, dx, &count_times_dx) || - !sk_64_sadd_check(fx, count_times_dx, &ex)) { - // we can't represent the computed end in 32.32, so just draw something (first color) - fCount1 = fCount2 = 0; - fCount0 = count; - return; - } - - if ((uint64_t)(fx | ex) <= kFracMax_SkGradFixed) { - fCount0 = fCount2 = 0; - fCount1 = count; - fFx1 = fx0; - return; - } - if (fx <= 0 && ex <= 0) { - fCount1 = fCount2 = 0; - fCount0 = count; - return; - } - if (fx >= kFracMax_SkGradFixed && ex >= kFracMax_SkGradFixed) { - fCount0 = fCount1 = 0; - fCount2 = count; - return; - } - - // now make ex be 1 past the last computed value - ex += dx; - - bool doSwap = dx < 0; - - if (doSwap) { - ex -= dx; - fx -= dx; - SkTSwap(fx, ex); - dx = -dx; - } - - - fCount0 = chop(fx, 0, ex, dx, count); - SkASSERT(fCount0 >= 0); - SkASSERT(fCount0 <= count); - count -= fCount0; - fx += fCount0 * dx; - SkASSERT(fx >= 0); - SkASSERT(fCount0 == 0 || (fx - dx) < 0); - fCount1 = chop(fx, kFracMax_SkGradFixed, ex, dx, count); - SkASSERT(fCount1 >= 0); - SkASSERT(fCount1 <= count); - count -= fCount1; - fCount2 = count; - -#ifdef SK_DEBUG - fx += fCount1 * dx; - SkASSERT(fx <= ex); - if (fCount2 > 0) { - SkASSERT(fx >= kFracMax_SkGradFixed); - if (fCount1 > 0) { - SkASSERT(fx - dx < kFracMax_SkGradFixed); - } - } -#endif - - if (doSwap) { - SkTSwap(fCount0, fCount2); - SkTSwap(fV0, fV1); - dx = -dx; - } - - if (fCount1 > 0) { - fFx1 = fx0 + fCount0 * dx; - } -} diff --git a/src/effects/gradients/SkClampRange.h b/src/effects/gradients/SkClampRange.h deleted file mode 100644 index 8a22e72d38..0000000000 --- a/src/effects/gradients/SkClampRange.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkClampRange_DEFINED -#define SkClampRange_DEFINED - -#include "SkFixed.h" -#include "SkScalar.h" - -#define SkGradFixed SkFixed3232 - -// We want the largest 32.32 value representable as a float. (float)0x7FFFFFFF -// becomes too big, due to limited mantissa on the float and its rounding rules, so -// we have to manually compute the next smaller value (aka nextafter). - -// #define SkGradFixedMaxScalar nextafterf(SkFixed3232ToFloat(SkFixed3232Max), 0) -// #define SkGradFixedMinScalar nextafterf(SkFixed3232ToFloat(SkFixed3232Min), 0) -#define SkGradFixedMaxScalar ( 2147483520.0f) -#define SkGradFixedMinScalar (-2147483520.0f) -#define SkScalarPinToGradFixed(x) SkScalarToFixed3232(SkTPin(x, \ - SkGradFixedMinScalar,\ - SkGradFixedMaxScalar)) -#define SkFixedToGradFixed(x) SkFixedToFixed3232(x) -#define SkGradFixedToFixed(x) (SkFixed)((x) >> 16) -#define kFracMax_SkGradFixed 0xFFFFFFFFLL - -/** - * Iteration fixed fx by dx, clamping as you go to [0..kFracMax_SkGradFixed], this class - * computes the (up to) 3 spans there are: - * - * range0: use constant value V0 - * range1: iterate as usual fx += dx - * range2: use constant value V1 - */ -struct SkClampRange { - int fCount0; // count for fV0 - int fCount1; // count for interpolating (fV0...fV1) - int fCount2; // count for fV1 - SkGradFixed fFx1; // initial fx value for the fCount1 range. - // only valid if fCount1 > 0 - int fV0, fV1; - - void init(SkGradFixed fx, SkGradFixed dx, int count, int v0, int v1); - - void validate(int count) const { -#ifdef SK_DEBUG - SkASSERT(fCount0 >= 0); - SkASSERT(fCount1 >= 0); - SkASSERT(fCount2 >= 0); - SkASSERT(fCount0 + fCount1 + fCount2 == count); -#endif - } - -private: - void initFor1(SkGradFixed fx); -}; - -#endif diff --git a/src/effects/gradients/SkGradientBitmapCache.cpp b/src/effects/gradients/SkGradientBitmapCache.cpp deleted file mode 100644 index 06b2d8c3fe..0000000000 --- a/src/effects/gradients/SkGradientBitmapCache.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkGradientBitmapCache.h" - -#include "SkMalloc.h" - -struct SkGradientBitmapCache::Entry { - Entry* fPrev; - Entry* fNext; - - void* fBuffer; - size_t fSize; - SkBitmap fBitmap; - - Entry(const void* buffer, size_t size, const SkBitmap& bm) - : fPrev(nullptr), - fNext(nullptr), - fBitmap(bm) { - fBuffer = sk_malloc_throw(size); - fSize = size; - memcpy(fBuffer, buffer, size); - } - - ~Entry() { sk_free(fBuffer); } - - bool equals(const void* buffer, size_t size) const { - return (fSize == size) && !memcmp(fBuffer, buffer, size); - } -}; - -SkGradientBitmapCache::SkGradientBitmapCache(int max) : fMaxEntries(max) { - fEntryCount = 0; - fHead = fTail = nullptr; - - this->validate(); -} - -SkGradientBitmapCache::~SkGradientBitmapCache() { - this->validate(); - - Entry* entry = fHead; - while (entry) { - Entry* next = entry->fNext; - delete entry; - entry = next; - } -} - -SkGradientBitmapCache::Entry* SkGradientBitmapCache::release(Entry* entry) const { - if (entry->fPrev) { - SkASSERT(fHead != entry); - entry->fPrev->fNext = entry->fNext; - } else { - SkASSERT(fHead == entry); - fHead = entry->fNext; - } - if (entry->fNext) { - SkASSERT(fTail != entry); - entry->fNext->fPrev = entry->fPrev; - } else { - SkASSERT(fTail == entry); - fTail = entry->fPrev; - } - return entry; -} - -void SkGradientBitmapCache::attachToHead(Entry* entry) const { - entry->fPrev = nullptr; - entry->fNext = fHead; - if (fHead) { - fHead->fPrev = entry; - } else { - fTail = entry; - } - fHead = entry; -} - -bool SkGradientBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const { - AutoValidate av(this); - - Entry* entry = fHead; - while (entry) { - if (entry->equals(buffer, size)) { - if (bm) { - *bm = entry->fBitmap; - } - // move to the head of our list, so we purge it last - this->release(entry); - this->attachToHead(entry); - return true; - } - entry = entry->fNext; - } - return false; -} - -void SkGradientBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) { - AutoValidate av(this); - - if (fEntryCount == fMaxEntries) { - SkASSERT(fTail); - delete this->release(fTail); - fEntryCount -= 1; - } - - Entry* entry = new Entry(buffer, len, bm); - this->attachToHead(entry); - fEntryCount += 1; -} - -/////////////////////////////////////////////////////////////////////////////// - -#ifdef SK_DEBUG - -void SkGradientBitmapCache::validate() const { - SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries); - - if (fEntryCount > 0) { - SkASSERT(nullptr == fHead->fPrev); - SkASSERT(nullptr == fTail->fNext); - - if (fEntryCount == 1) { - SkASSERT(fHead == fTail); - } else { - SkASSERT(fHead != fTail); - } - - Entry* entry = fHead; - int count = 0; - while (entry) { - count += 1; - entry = entry->fNext; - } - SkASSERT(count == fEntryCount); - - entry = fTail; - while (entry) { - count -= 1; - entry = entry->fPrev; - } - SkASSERT(0 == count); - } else { - SkASSERT(nullptr == fHead); - SkASSERT(nullptr == fTail); - } -} - -#endif diff --git a/src/effects/gradients/SkGradientBitmapCache.h b/src/effects/gradients/SkGradientBitmapCache.h deleted file mode 100644 index 0dcd32272e..0000000000 --- a/src/effects/gradients/SkGradientBitmapCache.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkGradientBitmapCache_DEFINED -#define SkGradientBitmapCache_DEFINED - -#include "SkBitmap.h" - -class SkGradientBitmapCache : SkNoncopyable { -public: - SkGradientBitmapCache(int maxEntries); - ~SkGradientBitmapCache(); - - bool find(const void* buffer, size_t len, SkBitmap*) const; - void add(const void* buffer, size_t len, const SkBitmap&); - -private: - int fEntryCount; - const int fMaxEntries; - - struct Entry; - mutable Entry* fHead; - mutable Entry* fTail; - - inline Entry* release(Entry*) const; - inline void attachToHead(Entry*) const; - -#ifdef SK_DEBUG - void validate() const; -#else - void validate() const {} -#endif - - class AutoValidate : SkNoncopyable { - public: - AutoValidate(const SkGradientBitmapCache* bc) : fBC(bc) { bc->validate(); } - ~AutoValidate() { fBC->validate(); } - private: - const SkGradientBitmapCache* fBC; - }; -}; - -#endif diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp deleted file mode 100644 index 137da84d0c..0000000000 --- a/src/effects/gradients/SkGradientShader.cpp +++ /dev/null @@ -1,2004 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include <algorithm> -#include "Sk4fLinearGradient.h" -#include "SkColorSpace_XYZ.h" -#include "SkGradientShaderPriv.h" -#include "SkHalf.h" -#include "SkLinearGradient.h" -#include "SkMallocPixelRef.h" -#include "SkRadialGradient.h" -#include "SkSweepGradient.h" -#include "SkTwoPointConicalGradient.h" -#include "../../jumper/SkJumper.h" - - -enum GradientSerializationFlags { - // Bits 29:31 used for various boolean flags - kHasPosition_GSF = 0x80000000, - kHasLocalMatrix_GSF = 0x40000000, - kHasColorSpace_GSF = 0x20000000, - - // Bits 12:28 unused - - // Bits 8:11 for fTileMode - kTileModeShift_GSF = 8, - kTileModeMask_GSF = 0xF, - - // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80) - kGradFlagsShift_GSF = 0, - kGradFlagsMask_GSF = 0xFF, -}; - -void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const { - uint32_t flags = 0; - if (fPos) { - flags |= kHasPosition_GSF; - } - if (fLocalMatrix) { - flags |= kHasLocalMatrix_GSF; - } - sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr; - if (colorSpaceData) { - flags |= kHasColorSpace_GSF; - } - SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF); - flags |= (fTileMode << kTileModeShift_GSF); - SkASSERT(fGradFlags <= kGradFlagsMask_GSF); - flags |= (fGradFlags << kGradFlagsShift_GSF); - - buffer.writeUInt(flags); - - buffer.writeColor4fArray(fColors, fCount); - if (colorSpaceData) { - buffer.writeDataAsByteArray(colorSpaceData.get()); - } - if (fPos) { - buffer.writeScalarArray(fPos, fCount); - } - if (fLocalMatrix) { - buffer.writeMatrix(*fLocalMatrix); - } -} - -bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) { - if (buffer.isVersionLT(SkReadBuffer::kGradientShaderFloatColor_Version)) { - fCount = buffer.getArrayCount(); - if (fCount > kStorageCount) { - size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount; - fDynamicStorage.reset(allocSize); - fColors = (SkColor4f*)fDynamicStorage.get(); - fPos = (SkScalar*)(fColors + fCount); - } else { - fColors = fColorStorage; - fPos = fPosStorage; - } - - // Old gradients serialized SkColor. Read that to a temporary location, then convert. - SkSTArray<2, SkColor, true> colors; - colors.resize_back(fCount); - if (!buffer.readColorArray(colors.begin(), fCount)) { - return false; - } - for (int i = 0; i < fCount; ++i) { - mutableColors()[i] = SkColor4f::FromColor(colors[i]); - } - - if (buffer.readBool()) { - if (!buffer.readScalarArray(const_cast<SkScalar*>(fPos), fCount)) { - return false; - } - } else { - fPos = nullptr; - } - - fColorSpace = nullptr; - fTileMode = (SkShader::TileMode)buffer.read32(); - fGradFlags = buffer.read32(); - - if (buffer.readBool()) { - fLocalMatrix = &fLocalMatrixStorage; - buffer.readMatrix(&fLocalMatrixStorage); - } else { - fLocalMatrix = nullptr; - } - } else { - // New gradient format. Includes floating point color, color space, densely packed flags - uint32_t flags = buffer.readUInt(); - - fTileMode = (SkShader::TileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF); - fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF; - - fCount = buffer.getArrayCount(); - if (fCount > kStorageCount) { - size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount; - fDynamicStorage.reset(allocSize); - fColors = (SkColor4f*)fDynamicStorage.get(); - fPos = (SkScalar*)(fColors + fCount); - } else { - fColors = fColorStorage; - fPos = fPosStorage; - } - if (!buffer.readColor4fArray(mutableColors(), fCount)) { - return false; - } - if (SkToBool(flags & kHasColorSpace_GSF)) { - sk_sp<SkData> data = buffer.readByteArrayAsData(); - fColorSpace = SkColorSpace::Deserialize(data->data(), data->size()); - } else { - fColorSpace = nullptr; - } - if (SkToBool(flags & kHasPosition_GSF)) { - if (!buffer.readScalarArray(mutablePos(), fCount)) { - return false; - } - } else { - fPos = nullptr; - } - if (SkToBool(flags & kHasLocalMatrix_GSF)) { - fLocalMatrix = &fLocalMatrixStorage; - buffer.readMatrix(&fLocalMatrixStorage); - } else { - fLocalMatrix = nullptr; - } - } - return buffer.isValid(); -} - -//////////////////////////////////////////////////////////////////////////////////////////// - -SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit) - : INHERITED(desc.fLocalMatrix) - , fPtsToUnit(ptsToUnit) -{ - fPtsToUnit.getType(); // Precache so reads are threadsafe. - SkASSERT(desc.fCount > 1); - - fGradFlags = static_cast<uint8_t>(desc.fGradFlags); - - SkASSERT((unsigned)desc.fTileMode < SkShader::kTileModeCount); - SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs)); - fTileMode = desc.fTileMode; - fTileProc = gTileProcs[desc.fTileMode]; - - /* Note: we let the caller skip the first and/or last position. - i.e. pos[0] = 0.3, pos[1] = 0.7 - In these cases, we insert dummy entries to ensure that the final data - will be bracketed by [0, 1]. - i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1 - - Thus colorCount (the caller's value, and fColorCount (our value) may - differ by up to 2. In the above example: - colorCount = 2 - fColorCount = 4 - */ - fColorCount = desc.fCount; - // check if we need to add in dummy start and/or end position/colors - bool dummyFirst = false; - bool dummyLast = false; - if (desc.fPos) { - dummyFirst = desc.fPos[0] != 0; - dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1; - fColorCount += dummyFirst + dummyLast; - } - - if (fColorCount > kColorStorageCount) { - size_t size = sizeof(SkColor) + sizeof(SkColor4f) + sizeof(Rec); - if (desc.fPos) { - size += sizeof(SkScalar); - } - fOrigColors = reinterpret_cast<SkColor*>(sk_malloc_throw(size * fColorCount)); - } - else { - fOrigColors = fStorage; - } - - fOrigColors4f = (SkColor4f*)(fOrigColors + fColorCount); - - // Now copy over the colors, adding the dummies as needed - SkColor4f* origColors = fOrigColors4f; - if (dummyFirst) { - *origColors++ = desc.fColors[0]; - } - memcpy(origColors, desc.fColors, desc.fCount * sizeof(SkColor4f)); - if (dummyLast) { - origColors += desc.fCount; - *origColors = desc.fColors[desc.fCount - 1]; - } - - // Convert our SkColor4f colors to SkColor as well. Note that this is incorrect if the - // source colors are not in sRGB gamut. We would need to do a gamut transformation, but - // SkColorSpaceXform can't do that (yet). GrColorSpaceXform can, but we may not have GPU - // support compiled in here. For the common case (sRGB colors), this does the right thing. - for (int i = 0; i < fColorCount; ++i) { - fOrigColors[i] = fOrigColors4f[i].toSkColor(); - } - - if (!desc.fColorSpace) { - // This happens if we were constructed from SkColors, so our colors are really sRGB - fColorSpace = SkColorSpace::MakeSRGBLinear(); - } else { - // The color space refers to the float colors, so it must be linear gamma - SkASSERT(desc.fColorSpace->gammaIsLinear()); - fColorSpace = desc.fColorSpace; - } - - if (desc.fPos && fColorCount) { - fOrigPos = (SkScalar*)(fOrigColors4f + fColorCount); - fRecs = (Rec*)(fOrigPos + fColorCount); - } else { - fOrigPos = nullptr; - fRecs = (Rec*)(fOrigColors4f + fColorCount); - } - - if (fColorCount > 2) { - Rec* recs = fRecs; - recs->fPos = 0; - // recs->fScale = 0; // unused; - recs += 1; - if (desc.fPos) { - SkScalar* origPosPtr = fOrigPos; - *origPosPtr++ = 0; - - /* We need to convert the user's array of relative positions into - fixed-point positions and scale factors. We need these results - to be strictly monotonic (no two values equal or out of order). - Hence this complex loop that just jams a zero for the scale - value if it sees a segment out of order, and it assures that - we start at 0 and end at 1.0 - */ - SkScalar prev = 0; - int startIndex = dummyFirst ? 0 : 1; - int count = desc.fCount + dummyLast; - for (int i = startIndex; i < count; i++) { - // force the last value to be 1.0 - SkScalar curr; - if (i == desc.fCount) { // we're really at the dummyLast - curr = 1; - } else { - curr = SkScalarPin(desc.fPos[i], 0, 1); - } - *origPosPtr++ = curr; - - recs->fPos = SkScalarToFixed(curr); - SkFixed diff = SkScalarToFixed(curr - prev); - if (diff > 0) { - recs->fScale = (1 << 24) / diff; - } else { - recs->fScale = 0; // ignore this segment - } - // get ready for the next value - prev = curr; - recs += 1; - } - } else { // assume even distribution - fOrigPos = nullptr; - - SkFixed dp = SK_Fixed1 / (desc.fCount - 1); - SkFixed p = dp; - SkFixed scale = (desc.fCount - 1) << 8; // (1 << 24) / dp - for (int i = 1; i < desc.fCount - 1; i++) { - recs->fPos = p; - recs->fScale = scale; - recs += 1; - p += dp; - } - recs->fPos = SK_Fixed1; - recs->fScale = scale; - } - } else if (desc.fPos) { - SkASSERT(2 == fColorCount); - fOrigPos[0] = SkScalarPin(desc.fPos[0], 0, 1); - fOrigPos[1] = SkScalarPin(desc.fPos[1], fOrigPos[0], 1); - if (0 == fOrigPos[0] && 1 == fOrigPos[1]) { - fOrigPos = nullptr; - } - } - this->initCommon(); -} - -SkGradientShaderBase::~SkGradientShaderBase() { - if (fOrigColors != fStorage) { - sk_free(fOrigColors); - } -} - -void SkGradientShaderBase::initCommon() { - unsigned colorAlpha = 0xFF; - for (int i = 0; i < fColorCount; i++) { - colorAlpha &= SkColorGetA(fOrigColors[i]); - } - fColorsAreOpaque = colorAlpha == 0xFF; -} - -void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const { - Descriptor desc; - desc.fColors = fOrigColors4f; - desc.fColorSpace = fColorSpace; - desc.fPos = fOrigPos; - desc.fCount = fColorCount; - desc.fTileMode = fTileMode; - desc.fGradFlags = fGradFlags; - - const SkMatrix& m = this->getLocalMatrix(); - desc.fLocalMatrix = m.isIdentity() ? nullptr : &m; - desc.flatten(buffer); -} - -void SkGradientShaderBase::FlipGradientColors(SkColor* colorDst, Rec* recDst, - SkColor* colorSrc, Rec* recSrc, - int count) { - SkAutoSTArray<8, SkColor> colorsTemp(count); - for (int i = 0; i < count; ++i) { - int offset = count - i - 1; - colorsTemp[i] = colorSrc[offset]; - } - if (count > 2) { - SkAutoSTArray<8, Rec> recsTemp(count); - for (int i = 0; i < count; ++i) { - int offset = count - i - 1; - recsTemp[i].fPos = SK_Fixed1 - recSrc[offset].fPos; - recsTemp[i].fScale = recSrc[offset].fScale; - } - memcpy(recDst, recsTemp.get(), count * sizeof(Rec)); - } - memcpy(colorDst, colorsTemp.get(), count * sizeof(SkColor)); -} - -static void add_stop_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f Fs, SkPM4f Bs) { - (ctx->fs[0])[stop] = Fs.r(); - (ctx->fs[1])[stop] = Fs.g(); - (ctx->fs[2])[stop] = Fs.b(); - (ctx->fs[3])[stop] = Fs.a(); - (ctx->bs[0])[stop] = Bs.r(); - (ctx->bs[1])[stop] = Bs.g(); - (ctx->bs[2])[stop] = Bs.b(); - (ctx->bs[3])[stop] = Bs.a(); -} - -static void add_const_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f color) { - add_stop_color(ctx, stop, SkPM4f::FromPremulRGBA(0,0,0,0), color); -} - -// Calculate a factor F and a bias B so that color = F*t + B when t is in range of -// the stop. Assume that the distance between stops is 1/gapCount. -static void init_stop_evenly( - SkJumper_GradientCtx* ctx, float gapCount, size_t stop, SkPM4f c_l, SkPM4f c_r) { - // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar... - SkPM4f Fs = {{ - (c_r.r() - c_l.r()) * gapCount, - (c_r.g() - c_l.g()) * gapCount, - (c_r.b() - c_l.b()) * gapCount, - (c_r.a() - c_l.a()) * gapCount, - }}; - SkPM4f Bs = {{ - c_l.r() - Fs.r()*(stop/gapCount), - c_l.g() - Fs.g()*(stop/gapCount), - c_l.b() - Fs.b()*(stop/gapCount), - c_l.a() - Fs.a()*(stop/gapCount), - }}; - add_stop_color(ctx, stop, Fs, Bs); -} - -// For each stop we calculate a bias B and a scale factor F, such that -// for any t between stops n and n+1, the color we want is B[n] + F[n]*t. -static void init_stop_pos( - SkJumper_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPM4f c_l, SkPM4f c_r) { - // See note about Clankium's old compiler in init_stop_evenly(). - SkPM4f Fs = {{ - (c_r.r() - c_l.r()) / (t_r - t_l), - (c_r.g() - c_l.g()) / (t_r - t_l), - (c_r.b() - c_l.b()) / (t_r - t_l), - (c_r.a() - c_l.a()) / (t_r - t_l), - }}; - SkPM4f Bs = {{ - c_l.r() - Fs.r()*t_l, - c_l.g() - Fs.g()*t_l, - c_l.b() - Fs.b()*t_l, - c_l.a() - Fs.a()*t_l, - }}; - ctx->ts[stop] = t_l; - add_stop_color(ctx, stop, Fs, Bs); -} - -bool SkGradientShaderBase::onAppendStages(SkRasterPipeline* p, - SkColorSpace* dstCS, - SkArenaAlloc* alloc, - const SkMatrix& ctm, - const SkPaint& paint, - const SkMatrix* localM) const { - SkMatrix matrix; - if (!this->computeTotalInverse(ctm, localM, &matrix)) { - return false; - } - - SkRasterPipeline_<256> subclass; - if (!this->adjustMatrixAndAppendStages(alloc, &matrix, &subclass)) { - return false; - } - - auto* m = alloc->makeArrayDefault<float>(9); - if (matrix.asAffine(m)) { - p->append(SkRasterPipeline::matrix_2x3, m); - } else { - matrix.get9(m); - p->append(SkRasterPipeline::matrix_perspective, m); - } - - p->extend(subclass); - - switch(fTileMode) { - case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x_1); break; - case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x_1); break; - case kClamp_TileMode: - if (!fOrigPos) { - // We clamp only when the stops are evenly spaced. - // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1. - // In that case, we must make sure we're using the general "gradient" stage, - // which is the only stage that will correctly handle unclamped t. - p->append(SkRasterPipeline::clamp_x_1); - } - } - - const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag; - auto prepareColor = [premulGrad, dstCS, this](int i) { - SkColor4f c = dstCS ? to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS) - : SkColor4f_from_SkColor(fOrigColors[i], nullptr); - return premulGrad ? c.premul() - : SkPM4f::From4f(Sk4f::Load(&c)); - }; - - // The two-stop case with stops at 0 and 1. - if (fColorCount == 2 && fOrigPos == nullptr) { - const SkPM4f c_l = prepareColor(0), - c_r = prepareColor(1); - - // See F and B below. - auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2); - f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f()); - f_and_b[1] = c_l; - - p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, f_and_b); - } else { - auto* ctx = alloc->make<SkJumper_GradientCtx>(); - - // Note: In order to handle clamps in search, the search assumes a stop conceptully placed - // at -inf. Therefore, the max number of stops is fColorCount+1. - for (int i = 0; i < 4; i++) { - // Allocate at least at for the AVX2 gather from a YMM register. - ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8)); - ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8)); - } - - if (fOrigPos == nullptr) { - // Handle evenly distributed stops. - - size_t stopCount = fColorCount; - float gapCount = stopCount - 1; - - SkPM4f c_l = prepareColor(0); - for (size_t i = 0; i < stopCount - 1; i++) { - SkPM4f c_r = prepareColor(i + 1); - init_stop_evenly(ctx, gapCount, i, c_l, c_r); - c_l = c_r; - } - add_const_color(ctx, stopCount - 1, c_l); - - ctx->stopCount = stopCount; - p->append(SkRasterPipeline::evenly_spaced_gradient, ctx); - } else { - // Handle arbitrary stops. - - ctx->ts = alloc->makeArray<float>(fColorCount+1); - - // Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase - // because they are naturally handled by the search method. - int firstStop; - int lastStop; - if (fColorCount > 2) { - firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1; - lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1] - ? fColorCount - 1 : fColorCount - 2; - } else { - firstStop = 0; - lastStop = 1; - } - - size_t stopCount = 0; - float t_l = fOrigPos[firstStop]; - SkPM4f c_l = prepareColor(firstStop); - add_const_color(ctx, stopCount++, c_l); - // N.B. lastStop is the index of the last stop, not one after. - for (int i = firstStop; i < lastStop; i++) { - float t_r = fOrigPos[i + 1]; - SkPM4f c_r = prepareColor(i + 1); - if (t_l < t_r) { - init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r); - stopCount += 1; - } - t_l = t_r; - c_l = c_r; - } - - ctx->ts[stopCount] = t_l; - add_const_color(ctx, stopCount++, c_l); - - ctx->stopCount = stopCount; - p->append(SkRasterPipeline::gradient, ctx); - } - } - - if (!premulGrad && !this->colorsAreOpaque()) { - p->append(SkRasterPipeline::premul); - } - - return true; -} - - -bool SkGradientShaderBase::isOpaque() const { - return fColorsAreOpaque; -} - -static unsigned rounded_divide(unsigned numer, unsigned denom) { - return (numer + (denom >> 1)) / denom; -} - -bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const { - // we just compute an average color. - // possibly we could weight this based on the proportional width for each color - // assuming they are not evenly distributed in the fPos array. - int r = 0; - int g = 0; - int b = 0; - const int n = fColorCount; - for (int i = 0; i < n; ++i) { - SkColor c = fOrigColors[i]; - r += SkColorGetR(c); - g += SkColorGetG(c); - b += SkColorGetB(c); - } - *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n)); - return true; -} - -SkGradientShaderBase::GradientShaderBaseContext::GradientShaderBaseContext( - const SkGradientShaderBase& shader, const ContextRec& rec) - : INHERITED(shader, rec) -#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING - , fDither(true) -#else - , fDither(rec.fPaint->isDither()) -#endif - , fCache(shader.refCache(getPaintAlpha(), fDither)) -{ - const SkMatrix& inverse = this->getTotalInverse(); - - fDstToIndex.setConcat(shader.fPtsToUnit, inverse); - - fDstToIndexProc = fDstToIndex.getMapXYProc(); - fDstToIndexClass = (uint8_t)SkShaderBase::Context::ComputeMatrixClass(fDstToIndex); - - // now convert our colors in to PMColors - unsigned paintAlpha = this->getPaintAlpha(); - - fFlags = this->INHERITED::getFlags(); - if (shader.fColorsAreOpaque && paintAlpha == 0xFF) { - fFlags |= kOpaqueAlpha_Flag; - } -} - -bool SkGradientShaderBase::GradientShaderBaseContext::isValid() const { - return fDstToIndex.isFinite(); -} - -SkGradientShaderBase::GradientShaderCache::GradientShaderCache( - U8CPU alpha, bool dither, const SkGradientShaderBase& shader) - : fCacheAlpha(alpha) - , fCacheDither(dither) - , fShader(shader) -{ - // Only initialize the cache in getCache32. - fCache32 = nullptr; -} - -SkGradientShaderBase::GradientShaderCache::~GradientShaderCache() {} - -/* - * r,g,b used to be SkFixed, but on gcc (4.2.1 mac and 4.6.3 goobuntu) in - * release builds, we saw a compiler error where the 0xFF parameter in - * SkPackARGB32() was being totally ignored whenever it was called with - * a non-zero add (e.g. 0x8000). - * - * We found two work-arounds: - * 1. change r,g,b to unsigned (or just one of them) - * 2. change SkPackARGB32 to + its (a << SK_A32_SHIFT) value instead - * of using | - * - * We chose #1 just because it was more localized. - * See http://code.google.com/p/skia/issues/detail?id=1113 - * - * The type SkUFixed encapsulate this need for unsigned, but logically Fixed. - */ -typedef uint32_t SkUFixed; - -void SkGradientShaderBase::GradientShaderCache::Build32bitCache( - SkPMColor cache[], SkColor c0, SkColor c1, - int count, U8CPU paintAlpha, uint32_t gradFlags, bool dither) { - SkASSERT(count > 1); - - // need to apply paintAlpha to our two endpoints - uint32_t a0 = SkMulDiv255Round(SkColorGetA(c0), paintAlpha); - uint32_t a1 = SkMulDiv255Round(SkColorGetA(c1), paintAlpha); - - - const bool interpInPremul = SkToBool(gradFlags & - SkGradientShader::kInterpolateColorsInPremul_Flag); - - uint32_t r0 = SkColorGetR(c0); - uint32_t g0 = SkColorGetG(c0); - uint32_t b0 = SkColorGetB(c0); - - uint32_t r1 = SkColorGetR(c1); - uint32_t g1 = SkColorGetG(c1); - uint32_t b1 = SkColorGetB(c1); - - if (interpInPremul) { - r0 = SkMulDiv255Round(r0, a0); - g0 = SkMulDiv255Round(g0, a0); - b0 = SkMulDiv255Round(b0, a0); - - r1 = SkMulDiv255Round(r1, a1); - g1 = SkMulDiv255Round(g1, a1); - b1 = SkMulDiv255Round(b1, a1); - } - - SkFixed da = SkIntToFixed(a1 - a0) / (count - 1); - SkFixed dr = SkIntToFixed(r1 - r0) / (count - 1); - SkFixed dg = SkIntToFixed(g1 - g0) / (count - 1); - SkFixed db = SkIntToFixed(b1 - b0) / (count - 1); - - /* We pre-add 1/8 to avoid having to add this to our [0] value each time - in the loop. Without this, the bias for each would be - 0x2000 0xA000 0xE000 0x6000 - With this trick, we can add 0 for the first (no-op) and just adjust the - others. - */ - const SkUFixed bias0 = dither ? 0x2000 : 0x8000; - const SkUFixed bias1 = dither ? 0x8000 : 0; - const SkUFixed bias2 = dither ? 0xC000 : 0; - const SkUFixed bias3 = dither ? 0x4000 : 0; - - SkUFixed a = SkIntToFixed(a0) + bias0; - SkUFixed r = SkIntToFixed(r0) + bias0; - SkUFixed g = SkIntToFixed(g0) + bias0; - SkUFixed b = SkIntToFixed(b0) + bias0; - - /* - * Our dither-cell (spatially) is - * 0 2 - * 3 1 - * Where - * [0] -> [-1/8 ... 1/8 ) values near 0 - * [1] -> [ 1/8 ... 3/8 ) values near 1/4 - * [2] -> [ 3/8 ... 5/8 ) values near 1/2 - * [3] -> [ 5/8 ... 7/8 ) values near 3/4 - */ - - if (0xFF == a0 && 0 == da) { - do { - cache[kCache32Count*0] = SkPackARGB32(0xFF, (r + 0 ) >> 16, - (g + 0 ) >> 16, - (b + 0 ) >> 16); - cache[kCache32Count*1] = SkPackARGB32(0xFF, (r + bias1) >> 16, - (g + bias1) >> 16, - (b + bias1) >> 16); - cache[kCache32Count*2] = SkPackARGB32(0xFF, (r + bias2) >> 16, - (g + bias2) >> 16, - (b + bias2) >> 16); - cache[kCache32Count*3] = SkPackARGB32(0xFF, (r + bias3) >> 16, - (g + bias3) >> 16, - (b + bias3) >> 16); - cache += 1; - r += dr; - g += dg; - b += db; - } while (--count != 0); - } else if (interpInPremul) { - do { - cache[kCache32Count*0] = SkPackARGB32((a + 0 ) >> 16, - (r + 0 ) >> 16, - (g + 0 ) >> 16, - (b + 0 ) >> 16); - cache[kCache32Count*1] = SkPackARGB32((a + bias1) >> 16, - (r + bias1) >> 16, - (g + bias1) >> 16, - (b + bias1) >> 16); - cache[kCache32Count*2] = SkPackARGB32((a + bias2) >> 16, - (r + bias2) >> 16, - (g + bias2) >> 16, - (b + bias2) >> 16); - cache[kCache32Count*3] = SkPackARGB32((a + bias3) >> 16, - (r + bias3) >> 16, - (g + bias3) >> 16, - (b + bias3) >> 16); - cache += 1; - a += da; - r += dr; - g += dg; - b += db; - } while (--count != 0); - } else { // interpolate in unpreml space - do { - cache[kCache32Count*0] = SkPremultiplyARGBInline((a + 0 ) >> 16, - (r + 0 ) >> 16, - (g + 0 ) >> 16, - (b + 0 ) >> 16); - cache[kCache32Count*1] = SkPremultiplyARGBInline((a + bias1) >> 16, - (r + bias1) >> 16, - (g + bias1) >> 16, - (b + bias1) >> 16); - cache[kCache32Count*2] = SkPremultiplyARGBInline((a + bias2) >> 16, - (r + bias2) >> 16, - (g + bias2) >> 16, - (b + bias2) >> 16); - cache[kCache32Count*3] = SkPremultiplyARGBInline((a + bias3) >> 16, - (r + bias3) >> 16, - (g + bias3) >> 16, - (b + bias3) >> 16); - cache += 1; - a += da; - r += dr; - g += dg; - b += db; - } while (--count != 0); - } -} - -static inline int SkFixedToFFFF(SkFixed x) { - SkASSERT((unsigned)x <= SK_Fixed1); - return x - (x >> 16); -} - -const SkPMColor* SkGradientShaderBase::GradientShaderCache::getCache32() { - fCache32InitOnce(SkGradientShaderBase::GradientShaderCache::initCache32, this); - SkASSERT(fCache32); - return fCache32; -} - -void SkGradientShaderBase::GradientShaderCache::initCache32(GradientShaderCache* cache) { - const int kNumberOfDitherRows = 4; - const SkImageInfo info = SkImageInfo::MakeN32Premul(kCache32Count, kNumberOfDitherRows); - - SkASSERT(nullptr == cache->fCache32PixelRef); - cache->fCache32PixelRef = SkMallocPixelRef::MakeAllocate(info, 0, nullptr); - cache->fCache32 = (SkPMColor*)cache->fCache32PixelRef->pixels(); - if (cache->fShader.fColorCount == 2) { - Build32bitCache(cache->fCache32, cache->fShader.fOrigColors[0], - cache->fShader.fOrigColors[1], kCache32Count, cache->fCacheAlpha, - cache->fShader.fGradFlags, cache->fCacheDither); - } else { - Rec* rec = cache->fShader.fRecs; - int prevIndex = 0; - for (int i = 1; i < cache->fShader.fColorCount; i++) { - int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift; - SkASSERT(nextIndex < kCache32Count); - - if (nextIndex > prevIndex) - Build32bitCache(cache->fCache32 + prevIndex, cache->fShader.fOrigColors[i-1], - cache->fShader.fOrigColors[i], nextIndex - prevIndex + 1, - cache->fCacheAlpha, cache->fShader.fGradFlags, cache->fCacheDither); - prevIndex = nextIndex; - } - } -} - -void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap) const { - const bool interpInPremul = SkToBool(fGradFlags & - SkGradientShader::kInterpolateColorsInPremul_Flag); - SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels()); - uint32_t* pixelsS32 = reinterpret_cast<uint32_t*>(bitmap->getPixels()); - - typedef std::function<void(const Sk4f&, int)> pixelWriteFn_t; - - pixelWriteFn_t writeF16Pixel = [&](const Sk4f& x, int index) { - Sk4h c = SkFloatToHalf_finite_ftz(x); - pixelsF16[4*index+0] = c[0]; - pixelsF16[4*index+1] = c[1]; - pixelsF16[4*index+2] = c[2]; - pixelsF16[4*index+3] = c[3]; - }; - pixelWriteFn_t writeS32Pixel = [&](const Sk4f& c, int index) { - pixelsS32[index] = Sk4f_toS32(c); - }; - - pixelWriteFn_t writeSizedPixel = - (kRGBA_F16_SkColorType == bitmap->colorType()) ? writeF16Pixel : writeS32Pixel; - pixelWriteFn_t writeUnpremulPixel = [&](const Sk4f& c, int index) { - writeSizedPixel(c * Sk4f(c[3], c[3], c[3], 1.0f), index); - }; - - pixelWriteFn_t writePixel = interpInPremul ? writeSizedPixel : writeUnpremulPixel; - - int prevIndex = 0; - for (int i = 1; i < fColorCount; i++) { - int nextIndex = (fColorCount == 2) ? (kCache32Count - 1) - : SkFixedToFFFF(fRecs[i].fPos) >> kCache32Shift; - SkASSERT(nextIndex < kCache32Count); - - if (nextIndex > prevIndex) { - Sk4f c0 = Sk4f::Load(fOrigColors4f[i - 1].vec()); - Sk4f c1 = Sk4f::Load(fOrigColors4f[i].vec()); - if (interpInPremul) { - c0 = c0 * Sk4f(c0[3], c0[3], c0[3], 1.0f); - c1 = c1 * Sk4f(c1[3], c1[3], c1[3], 1.0f); - } - - Sk4f step = Sk4f(1.0f / static_cast<float>(nextIndex - prevIndex)); - Sk4f delta = (c1 - c0) * step; - - for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) { - writePixel(c0, curIndex); - c0 += delta; - } - } - prevIndex = nextIndex; - } - SkASSERT(prevIndex == kCache32Count - 1); -} - -/* - * The gradient holds a cache for the most recent value of alpha. Successive - * callers with the same alpha value will share the same cache. - */ -sk_sp<SkGradientShaderBase::GradientShaderCache> SkGradientShaderBase::refCache(U8CPU alpha, - bool dither) const { - SkAutoMutexAcquire ama(fCacheMutex); - if (!fCache || fCache->getAlpha() != alpha || fCache->getDither() != dither) { - fCache.reset(new GradientShaderCache(alpha, dither, *this)); - } - // Increment the ref counter inside the mutex to ensure the returned pointer is still valid. - // Otherwise, the pointer may have been overwritten on a different thread before the object's - // ref count was incremented. - return fCache; -} - -SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex); -/* - * Because our caller might rebuild the same (logically the same) gradient - * over and over, we'd like to return exactly the same "bitmap" if possible, - * allowing the client to utilize a cache of our bitmap (e.g. with a GPU). - * To do that, we maintain a private cache of built-bitmaps, based on our - * colors and positions. Note: we don't try to flatten the fMapper, so if one - * is present, we skip the cache for now. - */ -void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap, - GradientBitmapType bitmapType) const { - // our caller assumes no external alpha, so we ensure that our cache is built with 0xFF - sk_sp<GradientShaderCache> cache(this->refCache(0xFF, true)); - - // build our key: [numColors + colors[] + {positions[]} + flags + colorType ] - int count = 1 + fColorCount + 1 + 1; - if (fColorCount > 2) { - count += fColorCount - 1; // fRecs[].fPos - } - - SkAutoSTMalloc<16, int32_t> storage(count); - int32_t* buffer = storage.get(); - - *buffer++ = fColorCount; - memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor)); - buffer += fColorCount; - if (fColorCount > 2) { - for (int i = 1; i < fColorCount; i++) { - *buffer++ = fRecs[i].fPos; - } - } - *buffer++ = fGradFlags; - *buffer++ = static_cast<int32_t>(bitmapType); - SkASSERT(buffer - storage.get() == count); - - /////////////////////////////////// - - static SkGradientBitmapCache* gCache; - // each cache cost 1K or 2K of RAM, since each bitmap will be 1x256 at either 32bpp or 64bpp - static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32; - SkAutoMutexAcquire ama(gGradientCacheMutex); - - if (nullptr == gCache) { - gCache = new SkGradientBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS); - } - size_t size = count * sizeof(int32_t); - - if (!gCache->find(storage.get(), size, bitmap)) { - if (GradientBitmapType::kLegacy == bitmapType) { - // force our cache32pixelref to be built - (void)cache->getCache32(); - bitmap->setInfo(SkImageInfo::MakeN32Premul(kCache32Count, 1)); - bitmap->setPixelRef(sk_ref_sp(cache->getCache32PixelRef()), 0, 0); - } else { - // For these cases we use the bitmap cache, but not the GradientShaderCache. So just - // allocate and populate the bitmap's data directly. - - SkImageInfo info; - switch (bitmapType) { - case GradientBitmapType::kSRGB: - info = SkImageInfo::Make(kCache32Count, 1, kRGBA_8888_SkColorType, - kPremul_SkAlphaType, - SkColorSpace::MakeSRGB()); - break; - case GradientBitmapType::kHalfFloat: - info = SkImageInfo::Make( - kCache32Count, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType, - SkColorSpace::MakeSRGBLinear()); - break; - default: - SkFAIL("Unexpected bitmap type"); - return; - } - bitmap->allocPixels(info); - this->initLinearBitmap(bitmap); - } - gCache->add(storage.get(), size, *bitmap); - } -} - -void SkGradientShaderBase::commonAsAGradient(GradientInfo* info, bool flipGrad) const { - if (info) { - if (info->fColorCount >= fColorCount) { - SkColor* colorLoc; - Rec* recLoc; - SkAutoSTArray<8, SkColor> colorStorage; - SkAutoSTArray<8, Rec> recStorage; - if (flipGrad && (info->fColors || info->fColorOffsets)) { - colorStorage.reset(fColorCount); - recStorage.reset(fColorCount); - colorLoc = colorStorage.get(); - recLoc = recStorage.get(); - FlipGradientColors(colorLoc, recLoc, fOrigColors, fRecs, fColorCount); - } else { - colorLoc = fOrigColors; - recLoc = fRecs; - } - if (info->fColors) { - memcpy(info->fColors, colorLoc, fColorCount * sizeof(SkColor)); - } - if (info->fColorOffsets) { - if (fColorCount == 2) { - info->fColorOffsets[0] = 0; - info->fColorOffsets[1] = SK_Scalar1; - } else if (fColorCount > 2) { - for (int i = 0; i < fColorCount; ++i) { - info->fColorOffsets[i] = SkFixedToScalar(recLoc[i].fPos); - } - } - } - } - info->fColorCount = fColorCount; - info->fTileMode = fTileMode; - info->fGradientFlags = fGradFlags; - } -} - -#ifndef SK_IGNORE_TO_STRING -void SkGradientShaderBase::toString(SkString* str) const { - - str->appendf("%d colors: ", fColorCount); - - for (int i = 0; i < fColorCount; ++i) { - str->appendHex(fOrigColors[i], 8); - if (i < fColorCount-1) { - str->append(", "); - } - } - - if (fColorCount > 2) { - str->append(" points: ("); - for (int i = 0; i < fColorCount; ++i) { - str->appendScalar(SkFixedToScalar(fRecs[i].fPos)); - if (i < fColorCount-1) { - str->append(", "); - } - } - str->append(")"); - } - - static const char* gTileModeName[SkShader::kTileModeCount] = { - "clamp", "repeat", "mirror" - }; - - str->append(" "); - str->append(gTileModeName[fTileMode]); - - this->INHERITED::toString(str); -} -#endif - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -// Return true if these parameters are valid/legal/safe to construct a gradient -// -static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count, - unsigned tileMode) { - return nullptr != colors && count >= 1 && tileMode < (unsigned)SkShader::kTileModeCount; -} - -static void desc_init(SkGradientShaderBase::Descriptor* desc, - const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, uint32_t flags, const SkMatrix* localMatrix) { - SkASSERT(colorCount > 1); - - desc->fColors = colors; - desc->fColorSpace = std::move(colorSpace); - desc->fPos = pos; - desc->fCount = colorCount; - desc->fTileMode = mode; - desc->fGradFlags = flags; - desc->fLocalMatrix = localMatrix; -} - -// assumes colors is SkColor4f* and pos is SkScalar* -#define EXPAND_1_COLOR(count) \ - SkColor4f tmp[2]; \ - do { \ - if (1 == count) { \ - tmp[0] = tmp[1] = colors[0]; \ - colors = tmp; \ - pos = nullptr; \ - count = 2; \ - } \ - } while (0) - -struct ColorStopOptimizer { - ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos, - int count, SkShader::TileMode mode) - : fColors(colors) - , fPos(pos) - , fCount(count) { - - if (!pos || count != 3) { - return; - } - - if (SkScalarNearlyEqual(pos[0], 0.0f) && - SkScalarNearlyEqual(pos[1], 0.0f) && - SkScalarNearlyEqual(pos[2], 1.0f)) { - - if (SkShader::kRepeat_TileMode == mode || - SkShader::kMirror_TileMode == mode || - colors[0] == colors[1]) { - - // Ignore the leftmost color/pos. - fColors += 1; - fPos += 1; - fCount = 2; - } - } else if (SkScalarNearlyEqual(pos[0], 0.0f) && - SkScalarNearlyEqual(pos[1], 1.0f) && - SkScalarNearlyEqual(pos[2], 1.0f)) { - - if (SkShader::kRepeat_TileMode == mode || - SkShader::kMirror_TileMode == mode || - colors[1] == colors[2]) { - - // Ignore the rightmost color/pos. - fCount = 2; - } - } - } - - const SkColor4f* fColors; - const SkScalar* fPos; - int fCount; -}; - -struct ColorConverter { - ColorConverter(const SkColor* colors, int count) { - for (int i = 0; i < count; ++i) { - fColors4f.push_back(SkColor4f::FromColor(colors[i])); - } - } - - SkSTArray<2, SkColor4f, true> fColors4f; -}; - -sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2], - const SkColor colors[], - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - ColorConverter converter(colors, colorCount); - return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags, - localMatrix); -} - -sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2], - const SkColor4f colors[], - sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) { - return nullptr; - } - if (!valid_grad(colors, pos, colorCount, mode)) { - return nullptr; - } - if (1 == colorCount) { - return SkShader::MakeColorShader(colors[0], std::move(colorSpace)); - } - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - - ColorStopOptimizer opt(colors, pos, colorCount, mode); - - SkGradientShaderBase::Descriptor desc; - desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, - localMatrix); - return sk_make_sp<SkLinearGradient>(pts, desc); -} - -sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius, - const SkColor colors[], - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - ColorConverter converter(colors, colorCount); - return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, - flags, localMatrix); -} - -sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius, - const SkColor4f colors[], - sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - if (radius <= 0) { - return nullptr; - } - if (!valid_grad(colors, pos, colorCount, mode)) { - return nullptr; - } - if (1 == colorCount) { - return SkShader::MakeColorShader(colors[0], std::move(colorSpace)); - } - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - - ColorStopOptimizer opt(colors, pos, colorCount, mode); - - SkGradientShaderBase::Descriptor desc; - desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, - localMatrix); - return sk_make_sp<SkRadialGradient>(center, radius, desc); -} - -sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start, - SkScalar startRadius, - const SkPoint& end, - SkScalar endRadius, - const SkColor colors[], - const SkScalar pos[], - int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - ColorConverter converter(colors, colorCount); - return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(), - nullptr, pos, colorCount, mode, flags, localMatrix); -} - -sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start, - SkScalar startRadius, - const SkPoint& end, - SkScalar endRadius, - const SkColor4f colors[], - sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], - int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - if (startRadius < 0 || endRadius < 0) { - return nullptr; - } - if (!valid_grad(colors, pos, colorCount, mode)) { - return nullptr; - } - if (startRadius == endRadius) { - if (start == end || startRadius == 0) { - return SkShader::MakeEmptyShader(); - } - } - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - EXPAND_1_COLOR(colorCount); - - ColorStopOptimizer opt(colors, pos, colorCount, mode); - - bool flipGradient = startRadius > endRadius; - - SkGradientShaderBase::Descriptor desc; - - if (!flipGradient) { - desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, - localMatrix); - return sk_make_sp<SkTwoPointConicalGradient>(start, startRadius, end, endRadius, - flipGradient, desc); - } else { - SkAutoSTArray<8, SkColor4f> colorsNew(opt.fCount); - SkAutoSTArray<8, SkScalar> posNew(opt.fCount); - for (int i = 0; i < opt.fCount; ++i) { - colorsNew[i] = opt.fColors[opt.fCount - i - 1]; - } - - if (pos) { - for (int i = 0; i < opt.fCount; ++i) { - posNew[i] = 1 - opt.fPos[opt.fCount - i - 1]; - } - desc_init(&desc, colorsNew.get(), std::move(colorSpace), posNew.get(), opt.fCount, mode, - flags, localMatrix); - } else { - desc_init(&desc, colorsNew.get(), std::move(colorSpace), nullptr, opt.fCount, mode, - flags, localMatrix); - } - - return sk_make_sp<SkTwoPointConicalGradient>(end, endRadius, start, startRadius, - flipGradient, desc); - } -} - -sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, - const SkColor colors[], - const SkScalar pos[], - int colorCount, - uint32_t flags, - const SkMatrix* localMatrix) { - ColorConverter converter(colors, colorCount); - return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount, flags, - localMatrix); -} - -sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, - const SkColor4f colors[], - sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], - int colorCount, - uint32_t flags, - const SkMatrix* localMatrix) { - if (!valid_grad(colors, pos, colorCount, SkShader::kClamp_TileMode)) { - return nullptr; - } - if (1 == colorCount) { - return SkShader::MakeColorShader(colors[0], std::move(colorSpace)); - } - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - - auto mode = SkShader::kClamp_TileMode; - - ColorStopOptimizer opt(colors, pos, colorCount, mode); - - SkGradientShaderBase::Descriptor desc; - desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, - localMatrix); - return sk_make_sp<SkSweepGradient>(cx, cy, desc); -} - -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLinearGradient) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialGradient) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSweepGradient) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTwoPointConicalGradient) -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END - -/////////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "GrContext.h" -#include "GrShaderCaps.h" -#include "GrTextureStripAtlas.h" -#include "gl/GrGLContext.h" -#include "glsl/GrGLSLColorSpaceXformHelper.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" -#include "SkGr.h" - -static inline bool close_to_one_half(const SkFixed& val) { - return SkScalarNearlyEqual(SkFixedToScalar(val), SK_ScalarHalf); -} - -static inline int color_type_to_color_count(GrGradientEffect::ColorType colorType) { - switch (colorType) { -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - case GrGradientEffect::kSingleHardStop_ColorType: - return 4; - case GrGradientEffect::kHardStopLeftEdged_ColorType: - case GrGradientEffect::kHardStopRightEdged_ColorType: - return 3; -#endif - case GrGradientEffect::kTwo_ColorType: - return 2; - case GrGradientEffect::kThree_ColorType: - return 3; - case GrGradientEffect::kTexture_ColorType: - return 0; - } - - SkDEBUGFAIL("Unhandled ColorType in color_type_to_color_count()"); - return -1; -} - -GrGradientEffect::ColorType GrGradientEffect::determineColorType( - const SkGradientShaderBase& shader) { -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - if (shader.fOrigPos) { - if (4 == shader.fColorCount) { - if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) && - SkScalarNearlyEqual(shader.fOrigPos[1], shader.fOrigPos[2]) && - SkScalarNearlyEqual(shader.fOrigPos[3], 1.0f)) { - - return kSingleHardStop_ColorType; - } - } else if (3 == shader.fColorCount) { - if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) && - SkScalarNearlyEqual(shader.fOrigPos[1], 0.0f) && - SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) { - - return kHardStopLeftEdged_ColorType; - } else if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) && - SkScalarNearlyEqual(shader.fOrigPos[1], 1.0f) && - SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) { - - return kHardStopRightEdged_ColorType; - } - } - } -#endif - - if (SkShader::kClamp_TileMode == shader.getTileMode()) { - if (2 == shader.fColorCount) { - return kTwo_ColorType; - } else if (3 == shader.fColorCount && - close_to_one_half(shader.getRecs()[1].fPos)) { - return kThree_ColorType; - } - } - - return kTexture_ColorType; -} - -void GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniformHandler, - const GrGradientEffect& ge) { - if (int colorCount = color_type_to_color_count(ge.getColorType())) { - fColorsUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, - kVec4f_GrSLType, - kDefault_GrSLPrecision, - "Colors", - colorCount); - if (ge.fColorType == kSingleHardStop_ColorType) { - fHardStopT = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, - kDefault_GrSLPrecision, "HardStopT"); - } - } else { - fFSYUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kFloat_GrSLType, kDefault_GrSLPrecision, - "GradientYCoordFS"); - } -} - -static inline void set_after_interp_color_uni_array( - const GrGLSLProgramDataManager& pdman, - const GrGLSLProgramDataManager::UniformHandle uni, - const SkTDArray<SkColor4f>& colors, - const GrColorSpaceXform* colorSpaceXform) { - int count = colors.count(); - if (colorSpaceXform) { - constexpr int kSmallCount = 10; - SkAutoSTArray<4 * kSmallCount, float> vals(4 * count); - - for (int i = 0; i < count; i++) { - colorSpaceXform->srcToDst().mapScalars(colors[i].vec(), &vals[4 * i]); - } - - pdman.set4fv(uni, count, vals.get()); - } else { - pdman.set4fv(uni, count, (float*)&colors[0]); - } -} - -static inline void set_before_interp_color_uni_array( - const GrGLSLProgramDataManager& pdman, - const GrGLSLProgramDataManager::UniformHandle uni, - const SkTDArray<SkColor4f>& colors, - const GrColorSpaceXform* colorSpaceXform) { - int count = colors.count(); - constexpr int kSmallCount = 10; - SkAutoSTArray<4 * kSmallCount, float> vals(4 * count); - - for (int i = 0; i < count; i++) { - float a = colors[i].fA; - vals[4 * i + 0] = colors[i].fR * a; - vals[4 * i + 1] = colors[i].fG * a; - vals[4 * i + 2] = colors[i].fB * a; - vals[4 * i + 3] = a; - } - - if (colorSpaceXform) { - for (int i = 0; i < count; i++) { - colorSpaceXform->srcToDst().mapScalars(&vals[4 * i]); - } - } - - pdman.set4fv(uni, count, vals.get()); -} - -static inline void set_after_interp_color_uni_array(const GrGLSLProgramDataManager& pdman, - const GrGLSLProgramDataManager::UniformHandle uni, - const SkTDArray<SkColor>& colors) { - int count = colors.count(); - constexpr int kSmallCount = 10; - - SkAutoSTArray<4*kSmallCount, float> vals(4*count); - - for (int i = 0; i < colors.count(); i++) { - // RGBA - vals[4*i + 0] = SkColorGetR(colors[i]) / 255.f; - vals[4*i + 1] = SkColorGetG(colors[i]) / 255.f; - vals[4*i + 2] = SkColorGetB(colors[i]) / 255.f; - vals[4*i + 3] = SkColorGetA(colors[i]) / 255.f; - } - - pdman.set4fv(uni, colors.count(), vals.get()); -} - -static inline void set_before_interp_color_uni_array(const GrGLSLProgramDataManager& pdman, - const GrGLSLProgramDataManager::UniformHandle uni, - const SkTDArray<SkColor>& colors) { - int count = colors.count(); - constexpr int kSmallCount = 10; - - SkAutoSTArray<4*kSmallCount, float> vals(4*count); - - for (int i = 0; i < count; i++) { - float a = SkColorGetA(colors[i]) / 255.f; - float aDiv255 = a / 255.f; - - // RGBA - vals[4*i + 0] = SkColorGetR(colors[i]) * aDiv255; - vals[4*i + 1] = SkColorGetG(colors[i]) * aDiv255; - vals[4*i + 2] = SkColorGetB(colors[i]) * aDiv255; - vals[4*i + 3] = a; - } - - pdman.set4fv(uni, count, vals.get()); -} - -void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman, - const GrFragmentProcessor& processor) { - const GrGradientEffect& e = processor.cast<GrGradientEffect>(); - - switch (e.getColorType()) { -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - case GrGradientEffect::kSingleHardStop_ColorType: - pdman.set1f(fHardStopT, e.fPositions[1]); - // fall through - case GrGradientEffect::kHardStopLeftEdged_ColorType: - case GrGradientEffect::kHardStopRightEdged_ColorType: -#endif - case GrGradientEffect::kTwo_ColorType: - case GrGradientEffect::kThree_ColorType: { - if (e.fColors4f.count() > 0) { - // Gamma-correct / color-space aware - if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) { - set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors4f, - e.fColorSpaceXform.get()); - } else { - set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors4f, - e.fColorSpaceXform.get()); - } - } else { - // Legacy mode. Would be nice if we had converted the 8-bit colors to float earlier - if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) { - set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors); - } else { - set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors); - } - } - - break; - } - - case GrGradientEffect::kTexture_ColorType: { - SkScalar yCoord = e.getYCoord(); - if (yCoord != fCachedYCoord) { - pdman.set1f(fFSYUni, yCoord); - fCachedYCoord = yCoord; - } - if (SkToBool(e.fColorSpaceXform)) { - fColorSpaceHelper.setData(pdman, e.fColorSpaceXform.get()); - } - break; - } - } -} - -uint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& processor) { - const GrGradientEffect& e = processor.cast<GrGradientEffect>(); - - uint32_t key = 0; - - if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) { - key |= kPremulBeforeInterpKey; - } - - if (GrGradientEffect::kTwo_ColorType == e.getColorType()) { - key |= kTwoColorKey; - } else if (GrGradientEffect::kThree_ColorType == e.getColorType()) { - key |= kThreeColorKey; - } -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - else if (GrGradientEffect::kSingleHardStop_ColorType == e.getColorType()) { - key |= kHardStopCenteredKey; - } else if (GrGradientEffect::kHardStopLeftEdged_ColorType == e.getColorType()) { - key |= kHardStopZeroZeroOneKey; - } else if (GrGradientEffect::kHardStopRightEdged_ColorType == e.getColorType()) { - key |= kHardStopZeroOneOneKey; - } - - if (SkShader::TileMode::kClamp_TileMode == e.fTileMode) { - key |= kClampTileMode; - } else if (SkShader::TileMode::kRepeat_TileMode == e.fTileMode) { - key |= kRepeatTileMode; - } else { - key |= kMirrorTileMode; - } -#endif - - key |= GrColorSpaceXform::XformKey(e.fColorSpaceXform.get()) << kReservedBits; - - return key; -} - -void GrGradientEffect::GLSLProcessor::emitColor(GrGLSLFPFragmentBuilder* fragBuilder, - GrGLSLUniformHandler* uniformHandler, - const GrShaderCaps* shaderCaps, - const GrGradientEffect& ge, - const char* gradientTValue, - const char* outputColor, - const char* inputColor, - const TextureSamplers& texSamplers) { - switch (ge.getColorType()) { -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - case kSingleHardStop_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - const char* stopT = uniformHandler->getUniformCStr(fHardStopT); - - fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t); - - // Account for tile mode - if (SkShader::kRepeat_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("clamp_t = fract(%s);", t); - } else if (SkShader::kMirror_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t); - fragBuilder->codeAppendf(" if (mod(floor(%s), 2.0) == 0.0) {", t); - fragBuilder->codeAppendf(" clamp_t = fract(%s);", t); - fragBuilder->codeAppendf(" } else {"); - fragBuilder->codeAppendf(" clamp_t = 1.0 - fract(%s);", t); - fragBuilder->codeAppendf(" }"); - fragBuilder->codeAppendf("}"); - } - - // Calculate color - fragBuilder->codeAppend ("vec4 start, end;"); - fragBuilder->codeAppend ("float relative_t;"); - fragBuilder->codeAppendf("if (clamp_t < %s) {", stopT); - fragBuilder->codeAppendf(" start = %s[0];", colors); - fragBuilder->codeAppendf(" end = %s[1];", colors); - fragBuilder->codeAppendf(" relative_t = clamp_t / %s;", stopT); - fragBuilder->codeAppend ("} else {"); - fragBuilder->codeAppendf(" start = %s[2];", colors); - fragBuilder->codeAppendf(" end = %s[3];", colors); - fragBuilder->codeAppendf(" relative_t = (clamp_t - %s) / (1 - %s);", stopT, stopT); - fragBuilder->codeAppend ("}"); - fragBuilder->codeAppend ("vec4 colorTemp = mix(start, end, relative_t);"); - - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } - - case kHardStopLeftEdged_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - - fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t); - - // Account for tile mode - if (SkShader::kRepeat_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("clamp_t = fract(%s);", t); - } else if (SkShader::kMirror_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t); - fragBuilder->codeAppendf(" if (mod(floor(%s), 2.0) == 0.0) {", t); - fragBuilder->codeAppendf(" clamp_t = fract(%s);", t); - fragBuilder->codeAppendf(" } else {"); - fragBuilder->codeAppendf(" clamp_t = 1.0 - fract(%s);", t); - fragBuilder->codeAppendf(" }"); - fragBuilder->codeAppendf("}"); - } - - fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[1], %s[2], clamp_t);", colors, - colors); - if (SkShader::kClamp_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s < 0.0) {", t); - fragBuilder->codeAppendf(" colorTemp = %s[0];", colors); - fragBuilder->codeAppendf("}"); - } - - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } - - case kHardStopRightEdged_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - - fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t); - - // Account for tile mode - if (SkShader::kRepeat_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("clamp_t = fract(%s);", t); - } else if (SkShader::kMirror_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t); - fragBuilder->codeAppendf(" if (mod(floor(%s), 2.0) == 0.0) {", t); - fragBuilder->codeAppendf(" clamp_t = fract(%s);", t); - fragBuilder->codeAppendf(" } else {"); - fragBuilder->codeAppendf(" clamp_t = 1.0 - fract(%s);", t); - fragBuilder->codeAppendf(" }"); - fragBuilder->codeAppendf("}"); - } - - fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp_t);", colors, - colors); - if (SkShader::kClamp_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s > 1.0) {", t); - fragBuilder->codeAppendf(" colorTemp = %s[2];", colors); - fragBuilder->codeAppendf("}"); - } - - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } -#endif - - case kTwo_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - - fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp(%s, 0.0, 1.0));", - colors, colors, t); - - // We could skip this step if both colors are known to be opaque. Two - // considerations: - // The gradient SkShader reporting opaque is more restrictive than necessary in the two - // pt case. Make sure the key reflects this optimization (and note that it can use the - // same shader as thekBeforeIterp case). This same optimization applies to the 3 color - // case below. - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } - - case kThree_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - - fragBuilder->codeAppendf("float oneMinus2t = 1.0 - (2.0 * %s);", t); - fragBuilder->codeAppendf("vec4 colorTemp = clamp(oneMinus2t, 0.0, 1.0) * %s[0];", - colors); - if (!shaderCaps->canUseMinAndAbsTogether()) { - // The Tegra3 compiler will sometimes never return if we have - // min(abs(oneMinus2t), 1.0), or do the abs first in a separate expression. - fragBuilder->codeAppendf("float minAbs = abs(oneMinus2t);"); - fragBuilder->codeAppendf("minAbs = minAbs > 1.0 ? 1.0 : minAbs;"); - fragBuilder->codeAppendf("colorTemp += (1.0 - minAbs) * %s[1];", colors); - } else { - fragBuilder->codeAppendf("colorTemp += (1.0 - min(abs(oneMinus2t), 1.0)) * %s[1];", - colors); - } - fragBuilder->codeAppendf("colorTemp += clamp(-oneMinus2t, 0.0, 1.0) * %s[2];", colors); - - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } - - case kTexture_ColorType: { - fColorSpaceHelper.emitCode(uniformHandler, ge.fColorSpaceXform.get()); - - const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni); - - fragBuilder->codeAppendf("vec2 coord = vec2(%s, %s);", gradientTValue, fsyuni); - fragBuilder->codeAppendf("%s = ", outputColor); - fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord", - kVec2f_GrSLType, &fColorSpaceHelper); - fragBuilder->codeAppend(";"); - - break; - } - } -} - -///////////////////////////////////////////////////////////////////// - -inline GrFragmentProcessor::OptimizationFlags GrGradientEffect::OptFlags(bool isOpaque) { - return isOpaque - ? kPreservesOpaqueInput_OptimizationFlag | - kCompatibleWithCoverageAsAlpha_OptimizationFlag - : kCompatibleWithCoverageAsAlpha_OptimizationFlag; -} - -GrGradientEffect::GrGradientEffect(const CreateArgs& args, bool isOpaque) - : INHERITED(OptFlags(isOpaque)) { - const SkGradientShaderBase& shader(*args.fShader); - - fIsOpaque = shader.isOpaque(); - - fColorType = this->determineColorType(shader); - fColorSpaceXform = std::move(args.fColorSpaceXform); - - if (kTexture_ColorType != fColorType) { - SkASSERT(shader.fOrigColors && shader.fOrigColors4f); - if (args.fGammaCorrect) { - fColors4f = SkTDArray<SkColor4f>(shader.fOrigColors4f, shader.fColorCount); - } else { - fColors = SkTDArray<SkColor>(shader.fOrigColors, shader.fColorCount); - } - -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - if (shader.fOrigPos) { - fPositions = SkTDArray<SkScalar>(shader.fOrigPos, shader.fColorCount); - } -#endif - } - -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - fTileMode = args.fTileMode; -#endif - - switch (fColorType) { - // The two and three color specializations do not currently support tiling. - case kTwo_ColorType: - case kThree_ColorType: -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - case kHardStopLeftEdged_ColorType: - case kHardStopRightEdged_ColorType: - case kSingleHardStop_ColorType: -#endif - fRow = -1; - - if (SkGradientShader::kInterpolateColorsInPremul_Flag & shader.getGradFlags()) { - fPremulType = kBeforeInterp_PremulType; - } else { - fPremulType = kAfterInterp_PremulType; - } - - fCoordTransform.reset(*args.fMatrix); - - break; - case kTexture_ColorType: - // doesn't matter how this is set, just be consistent because it is part of the - // effect key. - fPremulType = kBeforeInterp_PremulType; - - SkGradientShaderBase::GradientBitmapType bitmapType = - SkGradientShaderBase::GradientBitmapType::kLegacy; - if (args.fGammaCorrect) { - // Try to use F16 if we can - if (args.fContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) { - bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat; - } else if (args.fContext->caps()->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) { - bitmapType = SkGradientShaderBase::GradientBitmapType::kSRGB; - } else { - // This can happen, but only if someone explicitly creates an unsupported - // (eg sRGB) surface. Just fall back to legacy behavior. - } - } - - SkBitmap bitmap; - shader.getGradientTableBitmap(&bitmap, bitmapType); - SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width())); - - - GrTextureStripAtlas::Desc desc; - desc.fWidth = bitmap.width(); - desc.fHeight = 32; - desc.fRowHeight = bitmap.height(); - desc.fContext = args.fContext; - desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *args.fContext->caps()); - fAtlas = GrTextureStripAtlas::GetAtlas(desc); - SkASSERT(fAtlas); - - // We always filter the gradient table. Each table is one row of a texture, always - // y-clamp. - GrSamplerParams params; - params.setFilterMode(GrSamplerParams::kBilerp_FilterMode); - params.setTileModeX(args.fTileMode); - - fRow = fAtlas->lockRow(bitmap); - if (-1 != fRow) { - fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight(); - // This is 1/2 places where auto-normalization is disabled - fCoordTransform.reset(args.fContext->resourceProvider(), *args.fMatrix, - fAtlas->asTextureProxyRef().get(), false); - fTextureSampler.reset(args.fContext->resourceProvider(), - fAtlas->asTextureProxyRef(), params); - } else { - // In this instance we know the params are: - // clampY, bilerp - // and the proxy is: - // exact fit, power of two in both dimensions - // Only the x-tileMode is unknown. However, given all the other knowns we know - // that GrMakeCachedBitmapProxy is sufficient (i.e., it won't need to be - // extracted to a subset or mipmapped). - sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy( - args.fContext->resourceProvider(), - bitmap); - if (!proxy) { - return; - } - // This is 2/2 places where auto-normalization is disabled - fCoordTransform.reset(args.fContext->resourceProvider(), *args.fMatrix, - proxy.get(), false); - fTextureSampler.reset(args.fContext->resourceProvider(), - std::move(proxy), params); - fYCoord = SK_ScalarHalf; - } - - this->addTextureSampler(&fTextureSampler); - - break; - } - - this->addCoordTransform(&fCoordTransform); -} - -GrGradientEffect::~GrGradientEffect() { - if (this->useAtlas()) { - fAtlas->unlockRow(fRow); - } -} - -bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const { - const GrGradientEffect& ge = processor.cast<GrGradientEffect>(); - - if (this->fColorType != ge.getColorType()) { - return false; - } - SkASSERT(this->useAtlas() == ge.useAtlas()); - if (kTexture_ColorType == fColorType) { - if (fYCoord != ge.getYCoord()) { - return false; - } - } else { - if (kSingleHardStop_ColorType == fColorType) { - if (!SkScalarNearlyEqual(ge.fPositions[1], fPositions[1])) { - return false; - } - } - if (this->getPremulType() != ge.getPremulType() || - this->fColors.count() != ge.fColors.count() || - this->fColors4f.count() != ge.fColors4f.count()) { - return false; - } - - for (int i = 0; i < this->fColors.count(); i++) { - if (*this->getColors(i) != *ge.getColors(i)) { - return false; - } - } - for (int i = 0; i < this->fColors4f.count(); i++) { - if (*this->getColors4f(i) != *ge.getColors4f(i)) { - return false; - } - } - } - return GrColorSpaceXform::Equals(this->fColorSpaceXform.get(), ge.fColorSpaceXform.get()); -} - -#if GR_TEST_UTILS -GrGradientEffect::RandomGradientParams::RandomGradientParams(SkRandom* random) { - // Set color count to min of 2 so that we don't trigger the const color optimization and make - // a non-gradient processor. - fColorCount = random->nextRangeU(2, kMaxRandomGradientColors); - fUseColors4f = random->nextBool(); - - // if one color, omit stops, otherwise randomly decide whether or not to - if (fColorCount == 1 || (fColorCount >= 2 && random->nextBool())) { - fStops = nullptr; - } else { - fStops = fStopStorage; - } - - // if using SkColor4f, attach a random (possibly null) color space (with linear gamma) - if (fUseColors4f) { - fColorSpace = GrTest::TestColorSpace(random); - if (fColorSpace) { - SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(fColorSpace)->type()); - fColorSpace = static_cast<SkColorSpace_XYZ*>(fColorSpace.get())->makeLinearGamma(); - } - } - - SkScalar stop = 0.f; - for (int i = 0; i < fColorCount; ++i) { - if (fUseColors4f) { - fColors4f[i].fR = random->nextUScalar1(); - fColors4f[i].fG = random->nextUScalar1(); - fColors4f[i].fB = random->nextUScalar1(); - fColors4f[i].fA = random->nextUScalar1(); - } else { - fColors[i] = random->nextU(); - } - if (fStops) { - fStops[i] = stop; - stop = i < fColorCount - 1 ? stop + random->nextUScalar1() * (1.f - stop) : 1.f; - } - } - fTileMode = static_cast<SkShader::TileMode>(random->nextULessThan(SkShader::kTileModeCount)); -} -#endif - -#endif diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h deleted file mode 100644 index 7a66edaffc..0000000000 --- a/src/effects/gradients/SkGradientShaderPriv.h +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkGradientShaderPriv_DEFINED -#define SkGradientShaderPriv_DEFINED - -#include "SkGradientBitmapCache.h" -#include "SkGradientShader.h" - -#include "SkArenaAlloc.h" -#include "SkAutoMalloc.h" -#include "SkClampRange.h" -#include "SkColorPriv.h" -#include "SkColorSpace.h" -#include "SkOnce.h" -#include "SkPM4fPriv.h" -#include "SkRasterPipeline.h" -#include "SkReadBuffer.h" -#include "SkShaderBase.h" -#include "SkUtils.h" -#include "SkWriteBuffer.h" - -#if SK_SUPPORT_GPU - #define GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS 1 -#endif - -static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1, - int count) { - if (count > 0) { - if (v0 == v1) { - sk_memset32(dst, v0, count); - } else { - int pairs = count >> 1; - for (int i = 0; i < pairs; i++) { - *dst++ = v0; - *dst++ = v1; - } - if (count & 1) { - *dst = v0; - } - } - } -} - -// Clamp - -static inline SkFixed clamp_tileproc(SkFixed x) { - return SkClampMax(x, 0xFFFF); -} - -// Repeat - -static inline SkFixed repeat_tileproc(SkFixed x) { - return x & 0xFFFF; -} - -// Mirror - -static inline SkFixed mirror_tileproc(SkFixed x) { - int s = SkLeftShift(x, 15) >> 31; - return (x ^ s) & 0xFFFF; -} - -/////////////////////////////////////////////////////////////////////////////// - -typedef SkFixed (*TileProc)(SkFixed); - -/////////////////////////////////////////////////////////////////////////////// - -static const TileProc gTileProcs[] = { - clamp_tileproc, - repeat_tileproc, - mirror_tileproc -}; - -/////////////////////////////////////////////////////////////////////////////// - -class SkGradientShaderBase : public SkShaderBase { -public: - struct Descriptor { - Descriptor() { - sk_bzero(this, sizeof(*this)); - fTileMode = SkShader::kClamp_TileMode; - } - - const SkMatrix* fLocalMatrix; - const SkColor4f* fColors; - sk_sp<SkColorSpace> fColorSpace; - const SkScalar* fPos; - int fCount; - SkShader::TileMode fTileMode; - uint32_t fGradFlags; - - void flatten(SkWriteBuffer&) const; - }; - - class DescriptorScope : public Descriptor { - public: - DescriptorScope() {} - - bool unflatten(SkReadBuffer&); - - // fColors and fPos always point into local memory, so they can be safely mutated - // - SkColor4f* mutableColors() { return const_cast<SkColor4f*>(fColors); } - SkScalar* mutablePos() { return const_cast<SkScalar*>(fPos); } - - private: - enum { - kStorageCount = 16 - }; - SkColor4f fColorStorage[kStorageCount]; - SkScalar fPosStorage[kStorageCount]; - SkMatrix fLocalMatrixStorage; - SkAutoMalloc fDynamicStorage; - }; - - SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit); - ~SkGradientShaderBase() override; - - // The cache is initialized on-demand when getCache32 is called. - class GradientShaderCache : public SkRefCnt { - public: - GradientShaderCache(U8CPU alpha, bool dither, const SkGradientShaderBase& shader); - ~GradientShaderCache(); - - const SkPMColor* getCache32(); - - SkPixelRef* getCache32PixelRef() const { return fCache32PixelRef.get(); } - - unsigned getAlpha() const { return fCacheAlpha; } - bool getDither() const { return fCacheDither; } - - private: - // Working pointer. If it's nullptr, we need to recompute the cache values. - SkPMColor* fCache32; - - sk_sp<SkPixelRef> fCache32PixelRef; - const unsigned fCacheAlpha; // The alpha value we used when we computed the cache. - // Larger than 8bits so we can store uninitialized - // value. - const bool fCacheDither; // The dither flag used when we computed the cache. - - const SkGradientShaderBase& fShader; - - // Make sure we only initialize the cache once. - SkOnce fCache32InitOnce; - - static void initCache32(GradientShaderCache* cache); - - static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count, - U8CPU alpha, uint32_t gradFlags, bool dither); - }; - - class GradientShaderBaseContext : public Context { - public: - GradientShaderBaseContext(const SkGradientShaderBase& shader, const ContextRec&); - - uint32_t getFlags() const override { return fFlags; } - - bool isValid() const; - - protected: - SkMatrix fDstToIndex; - SkMatrix::MapXYProc fDstToIndexProc; - uint8_t fDstToIndexClass; - uint8_t fFlags; - bool fDither; - - sk_sp<GradientShaderCache> fCache; - - private: - typedef Context INHERITED; - }; - - bool isOpaque() const override; - - enum class GradientBitmapType : uint8_t { - kLegacy, - kSRGB, - kHalfFloat, - }; - - void getGradientTableBitmap(SkBitmap*, GradientBitmapType bitmapType) const; - - enum { - /// Seems like enough for visual accuracy. TODO: if pos[] deserves - /// it, use a larger cache. - kCache32Bits = 8, - kCache32Count = (1 << kCache32Bits), - kCache32Shift = 16 - kCache32Bits, - kSqrt32Shift = 8 - kCache32Bits, - - /// This value is used to *read* the dither cache; it may be 0 - /// if dithering is disabled. - kDitherStride32 = kCache32Count, - }; - - uint32_t getGradFlags() const { return fGradFlags; } - -protected: - struct Rec { - SkFixed fPos; // 0...1 - uint32_t fScale; // (1 << 24) / range - }; - - class GradientShaderBase4fContext; - - SkGradientShaderBase(SkReadBuffer& ); - void flatten(SkWriteBuffer&) const override; - SK_TO_STRING_OVERRIDE() - - void commonAsAGradient(GradientInfo*, bool flipGrad = false) const; - - bool onAsLuminanceColor(SkColor*) const override; - - void initLinearBitmap(SkBitmap* bitmap) const; - - /* - * Takes in pointers to gradient color and Rec info as colorSrc and recSrc respectively. - * Count is the number of colors in the gradient - * It will then flip all the color and rec information and return in their respective Dst - * pointers. It is assumed that space has already been allocated for the Dst pointers. - * The rec src and dst are only assumed to be valid if count > 2 - */ - static void FlipGradientColors(SkColor* colorDst, Rec* recDst, - SkColor* colorSrc, Rec* recSrc, - int count); - - bool onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, SkArenaAlloc* alloc, - const SkMatrix& ctm, const SkPaint& paint, - const SkMatrix* localM) const override; - - virtual bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const { return false; } - - template <typename T, typename... Args> - static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) { - auto* ctx = alloc->make<T>(std::forward<Args>(args)...); - if (!ctx->isValid()) { - return nullptr; - } - return ctx; - } - - const SkMatrix fPtsToUnit; - TileMode fTileMode; - TileProc fTileProc; - uint8_t fGradFlags; - Rec* fRecs; - -private: - enum { - kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space - - kStorageSize = kColorStorageCount * - (sizeof(SkColor) + sizeof(SkScalar) + sizeof(Rec) + sizeof(SkColor4f)) - }; - SkColor fStorage[(kStorageSize + 3) >> 2]; -public: - SkColor* fOrigColors; // original colors, before modulation by paint in context. - SkColor4f* fOrigColors4f; // original colors, as linear floats - SkScalar* fOrigPos; // original positions - int fColorCount; - sk_sp<SkColorSpace> fColorSpace; // color space of gradient stops - - bool colorsAreOpaque() const { return fColorsAreOpaque; } - - TileMode getTileMode() const { return fTileMode; } - Rec* getRecs() const { return fRecs; } - -private: - bool fColorsAreOpaque; - - sk_sp<GradientShaderCache> refCache(U8CPU alpha, bool dither) const; - mutable SkMutex fCacheMutex; - mutable sk_sp<GradientShaderCache> fCache; - - void initCommon(); - - typedef SkShaderBase INHERITED; -}; - - -static inline int init_dither_toggle(int x, int y) { - x &= 1; - y = (y & 1) << 1; - return (x | y) * SkGradientShaderBase::kDitherStride32; -} - -static inline int next_dither_toggle(int toggle) { - return toggle ^ SkGradientShaderBase::kDitherStride32; -} - -/////////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "GrColorSpaceXform.h" -#include "GrCoordTransform.h" -#include "GrFragmentProcessor.h" -#include "glsl/GrGLSLColorSpaceXformHelper.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLProgramDataManager.h" - -class GrInvariantOutput; - -/* - * The interpretation of the texture matrix depends on the sample mode. The - * texture matrix is applied both when the texture coordinates are explicit - * and when vertex positions are used as texture coordinates. In the latter - * case the texture matrix is applied to the pre-view-matrix position - * values. - * - * Normal SampleMode - * The post-matrix texture coordinates are in normalize space with (0,0) at - * the top-left and (1,1) at the bottom right. - * RadialGradient - * The matrix specifies the radial gradient parameters. - * (0,0) in the post-matrix space is center of the radial gradient. - * Radial2Gradient - * Matrix transforms to space where first circle is centered at the - * origin. The second circle will be centered (x, 0) where x may be - * 0 and is provided by setRadial2Params. The post-matrix space is - * normalized such that 1 is the second radius - first radius. - * SweepGradient - * The angle from the origin of texture coordinates in post-matrix space - * determines the gradient value. - */ - - class GrTextureStripAtlas; - -// Base class for Gr gradient effects -class GrGradientEffect : public GrFragmentProcessor { -public: - struct CreateArgs { - CreateArgs(GrContext* context, - const SkGradientShaderBase* shader, - const SkMatrix* matrix, - SkShader::TileMode tileMode, - sk_sp<GrColorSpaceXform> colorSpaceXform, - bool gammaCorrect) - : fContext(context) - , fShader(shader) - , fMatrix(matrix) - , fTileMode(tileMode) - , fColorSpaceXform(std::move(colorSpaceXform)) - , fGammaCorrect(gammaCorrect) {} - - GrContext* fContext; - const SkGradientShaderBase* fShader; - const SkMatrix* fMatrix; - SkShader::TileMode fTileMode; - sk_sp<GrColorSpaceXform> fColorSpaceXform; - bool fGammaCorrect; - }; - - class GLSLProcessor; - - ~GrGradientEffect() override; - - bool useAtlas() const { return SkToBool(-1 != fRow); } - SkScalar getYCoord() const { return fYCoord; } - - enum ColorType { - kTwo_ColorType, - kThree_ColorType, // Symmetric three color - kTexture_ColorType, - -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - kSingleHardStop_ColorType, // 0, t, t, 1 - kHardStopLeftEdged_ColorType, // 0, 0, 1 - kHardStopRightEdged_ColorType, // 0, 1, 1 -#endif - }; - - ColorType getColorType() const { return fColorType; } - - // Determines the type of gradient, one of: - // - Two-color - // - Symmetric three-color - // - Texture - // - Centered hard stop - // - Left-edged hard stop - // - Right-edged hard stop - ColorType determineColorType(const SkGradientShaderBase& shader); - - enum PremulType { - kBeforeInterp_PremulType, - kAfterInterp_PremulType, - }; - - PremulType getPremulType() const { return fPremulType; } - - const SkColor* getColors(int pos) const { - SkASSERT(fColorType != kTexture_ColorType); - SkASSERT(pos < fColors.count()); - return &fColors[pos]; - } - - const SkColor4f* getColors4f(int pos) const { - SkASSERT(fColorType != kTexture_ColorType); - SkASSERT(pos < fColors4f.count()); - return &fColors4f[pos]; - } - -protected: - GrGradientEffect(const CreateArgs&, bool isOpaque); - - #if GR_TEST_UTILS - /** Helper struct that stores (and populates) parameters to construct a random gradient. - If fUseColors4f is true, then the SkColor4f factory should be called, with fColors4f and - fColorSpace. Otherwise, the SkColor factory should be called, with fColors. fColorCount - will be the number of color stops in either case, and fColors and fStops can be passed to - the gradient factory. (The constructor may decide not to use stops, in which case fStops - will be nullptr). */ - struct RandomGradientParams { - static const int kMaxRandomGradientColors = 5; - - RandomGradientParams(SkRandom* r); - - bool fUseColors4f; - SkColor fColors[kMaxRandomGradientColors]; - SkColor4f fColors4f[kMaxRandomGradientColors]; - sk_sp<SkColorSpace> fColorSpace; - SkScalar fStopStorage[kMaxRandomGradientColors]; - SkShader::TileMode fTileMode; - int fColorCount; - SkScalar* fStops; - }; - #endif - - bool onIsEqual(const GrFragmentProcessor&) const override; - - const GrCoordTransform& getCoordTransform() const { return fCoordTransform; } - -private: - static OptimizationFlags OptFlags(bool isOpaque); - - // If we're in legacy mode, then fColors will be populated. If we're gamma-correct, then - // fColors4f and fColorSpaceXform will be populated. - SkTDArray<SkColor> fColors; - - SkTDArray<SkColor4f> fColors4f; - sk_sp<GrColorSpaceXform> fColorSpaceXform; - - SkTDArray<SkScalar> fPositions; - SkShader::TileMode fTileMode; - - GrCoordTransform fCoordTransform; - TextureSampler fTextureSampler; - SkScalar fYCoord; - GrTextureStripAtlas* fAtlas; - int fRow; - bool fIsOpaque; - ColorType fColorType; - PremulType fPremulType; // This is already baked into the table for texture gradients, and - // only changes behavior for gradients that don't use a texture. - typedef GrFragmentProcessor INHERITED; - -}; - -/////////////////////////////////////////////////////////////////////////////// - -// Base class for GL gradient effects -class GrGradientEffect::GLSLProcessor : public GrGLSLFragmentProcessor { -public: - GLSLProcessor() { - fCachedYCoord = SK_ScalarMax; - } - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - -protected: - /** - * Subclasses must call this. It will return a key for the part of the shader code controlled - * by the base class. The subclasses must stick it in their key and then pass it to the below - * emit* functions from their emitCode function. - */ - static uint32_t GenBaseGradientKey(const GrProcessor&); - - // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses - // should call this method from their emitCode(). - void emitUniforms(GrGLSLUniformHandler*, const GrGradientEffect&); - - // Emit code that gets a fragment's color from an expression for t; has branches for - // several control flows inside -- 2-color gradients, 3-color symmetric gradients, 4+ - // color gradients that use the traditional texture lookup, as well as several varieties - // of hard stop gradients - void emitColor(GrGLSLFPFragmentBuilder* fragBuilder, - GrGLSLUniformHandler* uniformHandler, - const GrShaderCaps* shaderCaps, - const GrGradientEffect&, - const char* gradientTValue, - const char* outputColor, - const char* inputColor, - const TextureSamplers&); - -private: - enum { - // First bit for premul before/after interp - kPremulBeforeInterpKey = 1, - - // Next three bits for 2/3 color type or different special - // hard stop cases (neither means using texture atlas) - kTwoColorKey = 2, - kThreeColorKey = 4, -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - kHardStopCenteredKey = 6, - kHardStopZeroZeroOneKey = 8, - kHardStopZeroOneOneKey = 10, - - // Next two bits for tile mode - kClampTileMode = 16, - kRepeatTileMode = 32, - kMirrorTileMode = 48, - - // Lower six bits for premul, 2/3 color type, and tile mode - kReservedBits = 6, -#endif - }; - - SkScalar fCachedYCoord; - GrGLSLProgramDataManager::UniformHandle fColorsUni; - GrGLSLProgramDataManager::UniformHandle fHardStopT; - GrGLSLProgramDataManager::UniformHandle fFSYUni; - GrGLSLColorSpaceXformHelper fColorSpaceHelper; - - typedef GrGLSLFragmentProcessor INHERITED; -}; - -#endif - -#endif diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp deleted file mode 100644 index 17c4fd36a4..0000000000 --- a/src/effects/gradients/SkLinearGradient.cpp +++ /dev/null @@ -1,804 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Sk4fLinearGradient.h" -#include "SkColorSpaceXformer.h" -#include "SkLinearGradient.h" -#include "SkRefCnt.h" - -// define to test the 4f gradient path -// #define FORCE_4F_CONTEXT - -static const float kInv255Float = 1.0f / 255; - -static inline int repeat_8bits(int x) { - return x & 0xFF; -} - -static inline int mirror_8bits(int x) { - if (x & 256) { - x = ~x; - } - return x & 255; -} - -static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) { - SkVector vec = pts[1] - pts[0]; - SkScalar mag = vec.length(); - SkScalar inv = mag ? SkScalarInvert(mag) : 0; - - vec.scale(inv); - SkMatrix matrix; - matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); - matrix.postTranslate(-pts[0].fX, -pts[0].fY); - matrix.postScale(inv, inv); - return matrix; -} - -static bool use_4f_context(const SkShaderBase::ContextRec& rec, uint32_t flags) { -#ifdef FORCE_4F_CONTEXT - return true; -#else - return rec.fPreferredDstType == SkShaderBase::ContextRec::kPM4f_DstType - || SkToBool(flags & SkLinearGradient::kForce4fContext_PrivateFlag); -#endif -} - -/////////////////////////////////////////////////////////////////////////////// - -SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc) - : SkGradientShaderBase(desc, pts_to_unit_matrix(pts)) - , fStart(pts[0]) - , fEnd(pts[1]) { -} - -sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) { - DescriptorScope desc; - if (!desc.unflatten(buffer)) { - return nullptr; - } - SkPoint pts[2]; - pts[0] = buffer.readPoint(); - pts[1] = buffer.readPoint(); - return SkGradientShader::MakeLinear(pts, desc.fColors, std::move(desc.fColorSpace), desc.fPos, - desc.fCount, desc.fTileMode, desc.fGradFlags, - desc.fLocalMatrix); -} - -void SkLinearGradient::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writePoint(fStart); - buffer.writePoint(fEnd); -} - -SkShaderBase::Context* SkLinearGradient::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const -{ - return use_4f_context(rec, fGradFlags) - ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec) - : CheckedMakeContext< LinearGradientContext>(alloc, *this, rec); -} - -bool SkLinearGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const { - *matrix = SkMatrix::Concat(fPtsToUnit, *matrix); - // If the gradient is less than a quarter of a pixel, this falls into the - // subpixel gradient code handled on a different path. - SkVector dx = matrix->mapVector(1, 0); - if (dx.fX >= 4) { - return false; - } - return true; -} - -sk_sp<SkShader> SkLinearGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkPoint pts[2] = { fStart, fEnd }; - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); - return SkGradientShader::MakeLinear(pts, xformedColors.begin(), fOrigPos, fColorCount, - fTileMode, fGradFlags, &this->getLocalMatrix()); -} - -// This swizzles SkColor into the same component order as SkPMColor, but does not actually -// "pre" multiply the color components. -// -// This allows us to map directly to Sk4f, and eventually scale down to bytes to output a -// SkPMColor from the floats, without having to swizzle each time. -// -static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) { - return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); -} - -SkLinearGradient::LinearGradientContext::LinearGradientContext( - const SkLinearGradient& shader, const ContextRec& ctx) - : INHERITED(shader, ctx) -{ - // setup for Sk4f - const int count = shader.fColorCount; - SkASSERT(count > 1); - - fRecs.setCount(count); - Rec* rec = fRecs.begin(); - if (shader.fOrigPos) { - rec[0].fPos = 0; - SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;) // should never get used - for (int i = 1; i < count; ++i) { - rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f); - float diff = rec[i].fPos - rec[i - 1].fPos; - if (diff > 0) { - rec[i].fPosScale = 1.0f / diff; - } else { - rec[i].fPosScale = 0; - } - } - } else { - // no pos specified, so we compute evenly spaced values - const float scale = float(count - 1); - const float invScale = 1.0f / scale; - for (int i = 0; i < count; ++i) { - rec[i].fPos = i * invScale; - rec[i].fPosScale = scale; - } - } - rec[count - 1].fPos = 1; // overwrite the last value just to be sure we end at 1.0 - - fApplyAlphaAfterInterp = true; - if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) || - shader.colorsAreOpaque()) - { - fApplyAlphaAfterInterp = false; - } - - if (fApplyAlphaAfterInterp) { - // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to - // interpolate in unpremultiplied space first, and then scale by alpha right before we - // convert to SkPMColor bytes. - const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float; - const Sk4f scale(1, 1, 1, paintAlpha); - for (int i = 0; i < count; ++i) { - uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]); - rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&c)) * scale; - if (i > 0) { - SkASSERT(rec[i - 1].fPos <= rec[i].fPos); - } - } - } else { - // Our fColor values are premultiplied, so converting to SkPMColor is just a matter - // of converting the floats down to bytes. - unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7); - for (int i = 0; i < count; ++i) { - SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]); - pmc = SkAlphaMulQ(pmc, alphaScale); - rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&pmc)); - if (i > 0) { - SkASSERT(rec[i - 1].fPos <= rec[i].fPos); - } - } - } -} - -#define NO_CHECK_ITER \ - do { \ - unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \ - SkASSERT(fi <= 0xFF); \ - fx += dx; \ - *dstC++ = cache[toggle + fi]; \ - toggle = next_dither_toggle(toggle); \ - } while (0) - -namespace { - -typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* dstC, const SkPMColor* cache, - int toggle, int count); - -// Linear interpolation (lerp) is unnecessary if there are no sharp -// discontinuities in the gradient - which must be true if there are -// only 2 colors - but it's cheap. -void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, - int toggle, int count) { - // We're a vertical gradient, so no change in a span. - // If colors change sharply across the gradient, dithering is - // insufficient (it subsamples the color space) and we need to lerp. - unsigned fullIndex = proc(SkGradFixedToFixed(fx)); - unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift; - unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1); - - int index0 = fi + toggle; - int index1 = index0; - if (fi < SkGradientShaderBase::kCache32Count - 1) { - index1 += 1; - } - SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); - index0 ^= SkGradientShaderBase::kDitherStride32; - index1 ^= SkGradientShaderBase::kDitherStride32; - SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); - sk_memset32_dither(dstC, lerp, dlerp, count); -} - -void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, - int toggle, int count) { - SkClampRange range; - range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); - range.validate(count); - - if ((count = range.fCount0) > 0) { - sk_memset32_dither(dstC, - cache[toggle + range.fV0], - cache[next_dither_toggle(toggle) + range.fV0], - count); - dstC += count; - } - if ((count = range.fCount1) > 0) { - int unroll = count >> 3; - fx = range.fFx1; - for (int i = 0; i < unroll; i++) { - NO_CHECK_ITER; NO_CHECK_ITER; - NO_CHECK_ITER; NO_CHECK_ITER; - NO_CHECK_ITER; NO_CHECK_ITER; - NO_CHECK_ITER; NO_CHECK_ITER; - } - if ((count &= 7) > 0) { - do { - NO_CHECK_ITER; - } while (--count != 0); - } - } - if ((count = range.fCount2) > 0) { - sk_memset32_dither(dstC, - cache[toggle + range.fV1], - cache[next_dither_toggle(toggle) + range.fV1], - count); - } -} - -void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, - int toggle, int count) { - do { - unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8); - SkASSERT(fi <= 0xFF); - fx += dx; - *dstC++ = cache[toggle + fi]; - toggle = next_dither_toggle(toggle); - } while (--count != 0); -} - -void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, - int toggle, int count) { - do { - unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8); - SkASSERT(fi <= 0xFF); - fx += dx; - *dstC++ = cache[toggle + fi]; - toggle = next_dither_toggle(toggle); - } while (--count != 0); -} - -} - -void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, - int count) { - SkASSERT(count > 0); - const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader); - - if (SkShader::kClamp_TileMode == linearGradient.fTileMode && - kLinear_MatrixClass == fDstToIndexClass) - { - this->shade4_clamp(x, y, dstC, count); - return; - } - - SkPoint srcPt; - SkMatrix::MapXYProc dstProc = fDstToIndexProc; - TileProc proc = linearGradient.fTileProc; - const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); - int toggle = init_dither_toggle(x, y); - - if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkGradFixed dx, fx = SkScalarPinToGradFixed(srcPt.fX); - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y)); - // todo: do we need a real/high-precision value for dx here? - dx = SkScalarPinToGradFixed(step.fX); - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - dx = SkScalarPinToGradFixed(fDstToIndex.getScaleX()); - } - - LinearShadeProc shadeProc = shadeSpan_linear_repeat; - if (0 == dx) { - shadeProc = shadeSpan_linear_vertical_lerp; - } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) { - shadeProc = shadeSpan_linear_clamp; - } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) { - shadeProc = shadeSpan_linear_mirror; - } else { - SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode); - } - (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count); - } else { - SkScalar dstX = SkIntToScalar(x); - SkScalar dstY = SkIntToScalar(y); - do { - dstProc(fDstToIndex, dstX, dstY, &srcPt); - unsigned fi = proc(SkScalarToFixed(srcPt.fX)); - SkASSERT(fi <= 0xFFFF); - *dstC++ = cache[toggle + (fi >> kCache32Shift)]; - toggle = next_dither_toggle(toggle); - dstX += SK_Scalar1; - } while (--count != 0); - } -} - -SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const { - if (info) { - commonAsAGradient(info); - info->fPoint[0] = fStart; - info->fPoint[1] = fEnd; - } - return kLinear_GradientType; -} - -#if SK_SUPPORT_GPU - -#include "GrColorSpaceXform.h" -#include "GrShaderCaps.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "SkGr.h" - -///////////////////////////////////////////////////////////////////// - -class GrLinearGradient : public GrGradientEffect { -public: - class GLSLLinearProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) { - return sk_sp<GrFragmentProcessor>(new GrLinearGradient(args)); - } - - ~GrLinearGradient() override {} - - const char* name() const override { return "Linear Gradient"; } - -private: - GrLinearGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) { - this->initClassID<GrLinearGradient>(); - } - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override; - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - typedef GrGradientEffect INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -class GrLinearGradient::GLSLLinearProcessor : public GrGradientEffect::GLSLProcessor { -public: - GLSLLinearProcessor(const GrProcessor&) {} - - ~GLSLLinearProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); - } - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -GrGLSLFragmentProcessor* GrLinearGradient::onCreateGLSLInstance() const { - return new GrLinearGradient::GLSLLinearProcessor(*this); -} - -void GrLinearGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - GrLinearGradient::GLSLLinearProcessor::GenKey(*this, caps, b); -} - -///////////////////////////////////////////////////////////////////// - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient); - -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> GrLinearGradient::TestCreate(GrProcessorTestData* d) { - SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}, - {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}}; - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeLinear(points, params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeLinear(points, params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -///////////////////////////////////////////////////////////////////// - -void GrLinearGradient::GLSLLinearProcessor::emitCode(EmitArgs& args) { - const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>(); - this->emitUniforms(args.fUniformHandler, ge); - SkString t = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - t.append(".x"); - this->emitColor(args.fFragBuilder, - args.fUniformHandler, - args.fShaderCaps, - ge, - t.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -///////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(const AsFPArgs& args) const { - SkASSERT(args.fContext); - - SkMatrix matrix; - if (!this->getLocalMatrix().invert(&matrix)) { - return nullptr; - } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } - matrix.postConcat(fPtsToUnit); - - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), - args.fDstColorSpace); - sk_sp<GrFragmentProcessor> inner(GrLinearGradient::Make( - GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode, - std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)))); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); -} - - -#endif - -#ifndef SK_IGNORE_TO_STRING -void SkLinearGradient::toString(SkString* str) const { - str->append("SkLinearGradient ("); - - str->appendf("start: (%f, %f)", fStart.fX, fStart.fY); - str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#include "SkNx.h" - -static const SkLinearGradient::LinearGradientContext::Rec* -find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) { - SkASSERT(tiledX >= 0 && tiledX <= 1); - - SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); - SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); - SkASSERT(rec[0].fPos <= rec[1].fPos); - rec += 1; - while (rec->fPos < tiledX || rec->fPosScale == 0) { - SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); - SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); - SkASSERT(rec[0].fPos <= rec[1].fPos); - rec += 1; - } - return rec - 1; -} - -static const SkLinearGradient::LinearGradientContext::Rec* -find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) { - SkASSERT(tiledX >= 0 && tiledX <= 1); - - SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); - SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); - SkASSERT(rec[0].fPos <= rec[1].fPos); - while (tiledX < rec->fPos || rec[1].fPosScale == 0) { - rec -= 1; - SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); - SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); - SkASSERT(rec[0].fPos <= rec[1].fPos); - } - return rec; -} - -// As an optimization, we can apply the dither bias before interpolation -- but only when -// operating in premul space (apply_alpha == false). When apply_alpha == true, we must -// defer the bias application until after premul. -// -// The following two helpers encapsulate this logic: pre_bias is called before interpolation, -// and effects the bias when apply_alpha == false, while post_bias is called after premul and -// effects the bias for the apply_alpha == true case. - -template <bool apply_alpha> -Sk4f pre_bias(const Sk4f& x, const Sk4f& bias) { - return apply_alpha ? x : x + bias; -} - -template <bool apply_alpha> -Sk4f post_bias(const Sk4f& x, const Sk4f& bias) { - return apply_alpha ? x + bias : x; -} - -template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x, const Sk4f& bias) { - SkPMColor c; - Sk4f c4f255 = x; - if (apply_alpha) { - const float scale = x[SkPM4f::A] * (1 / 255.f); - c4f255 *= Sk4f(scale, scale, scale, 1); - } - SkNx_cast<uint8_t>(post_bias<apply_alpha>(c4f255, bias)).store(&c); - - return c; -} - -template <bool apply_alpha> void fill(SkPMColor dst[], int count, - const Sk4f& c4, const Sk4f& bias0, const Sk4f& bias1) { - const SkPMColor c0 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias0), bias0); - const SkPMColor c1 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias1), bias1); - sk_memset32_dither(dst, c0, c1, count); -} - -template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) { - // Assumes that c4 does not need to be dithered. - sk_memset32(dst, trunc_from_255<apply_alpha>(c4, 0), count); -} - -/* - * TODOs - * - * - tilemodes - * - interp before or after premul - * - perspective - * - optimizations - * - use fixed (32bit or 16bit) instead of floats? - */ - -static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) { - SkASSERT(fx >= rec[0].fPos); - SkASSERT(fx <= rec[1].fPos); - - const float p0 = rec[0].fPos; - const Sk4f c0 = rec[0].fColor; - const Sk4f c1 = rec[1].fColor; - const Sk4f diffc = c1 - c0; - const float scale = rec[1].fPosScale; - const float t = (fx - p0) * scale; - return c0 + Sk4f(t) * diffc; -} - -template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc, - const Sk4f& dither0, const Sk4f& dither1) { - Sk4f dc2 = dc + dc; - Sk4f dc4 = dc2 + dc2; - Sk4f cd0 = pre_bias<apply_alpha>(c , dither0); - Sk4f cd1 = pre_bias<apply_alpha>(c + dc, dither1); - Sk4f cd2 = cd0 + dc2; - Sk4f cd3 = cd1 + dc2; - while (n >= 4) { - if (!apply_alpha) { - Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3); - dstC += 4; - } else { - *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0); - *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1); - *dstC++ = trunc_from_255<apply_alpha>(cd2, dither0); - *dstC++ = trunc_from_255<apply_alpha>(cd3, dither1); - } - cd0 = cd0 + dc4; - cd1 = cd1 + dc4; - cd2 = cd2 + dc4; - cd3 = cd3 + dc4; - n -= 4; - } - if (n & 2) { - *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0); - *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1); - cd0 = cd0 + dc2; - } - if (n & 1) { - *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0); - } -} - -template <bool apply_alpha, bool dx_is_pos> -void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count, - float fx, float dx, float invDx, - const float dither[2]) { - Sk4f dither0(dither[0]); - Sk4f dither1(dither[1]); - const Rec* rec = fRecs.begin(); - - const Sk4f dx4 = Sk4f(dx); - SkDEBUGCODE(SkPMColor* endDstC = dstC + count;) - - if (dx_is_pos) { - if (fx < 0) { - // count is guaranteed to be positive, but the first arg may overflow int32 after - // increment => casting to uint32 ensures correct clamping. - int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor(-fx * invDx)) + 1, - count); - SkASSERT(n > 0); - fill<apply_alpha>(dstC, n, rec[0].fColor); - count -= n; - dstC += n; - fx += n * dx; - SkASSERT(0 == count || fx >= 0); - if (n & 1) { - SkTSwap(dither0, dither1); - } - } - } else { // dx < 0 - if (fx > 1) { - // count is guaranteed to be positive, but the first arg may overflow int32 after - // increment => casting to uint32 ensures correct clamping. - int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor((1 - fx) * invDx)) + 1, - count); - SkASSERT(n > 0); - fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor); - count -= n; - dstC += n; - fx += n * dx; - SkASSERT(0 == count || fx <= 1); - if (n & 1) { - SkTSwap(dither0, dither1); - } - } - } - SkASSERT(count >= 0); - - const Rec* r; - if (dx_is_pos) { - r = fRecs.begin(); // start at the beginning - } else { - r = fRecs.begin() + fRecs.count() - 2; // start at the end - } - - while (count > 0) { - if (dx_is_pos) { - if (fx >= 1) { - fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor); - return; - } - } else { // dx < 0 - if (fx <= 0) { - fill<apply_alpha>(dstC, count, rec[0].fColor); - return; - } - } - - if (dx_is_pos) { - r = find_forward(r, fx); - } else { - r = find_backward(r, fx); - } - SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1); - - const float p0 = r[0].fPos; - const Sk4f c0 = r[0].fColor; - const float p1 = r[1].fPos; - const Sk4f diffc = Sk4f(r[1].fColor) - c0; - const float scale = r[1].fPosScale; - const float t = (fx - p0) * scale; - const Sk4f c = c0 + Sk4f(t) * diffc; - const Sk4f dc = diffc * dx4 * Sk4f(scale); - - int n; - if (dx_is_pos) { - n = SkTMin((int)((p1 - fx) * invDx) + 1, count); - } else { - n = SkTMin((int)((p0 - fx) * invDx) + 1, count); - } - - fx += n * dx; - // fx should now outside of the p0..p1 interval. However, due to float precision loss, - // its possible that fx is slightly too small/large, so we clamp it. - if (dx_is_pos) { - fx = SkTMax(fx, p1); - } else { - fx = SkTMin(fx, p0); - } - - ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1); - dstC += n; - SkASSERT(dstC <= endDstC); - - if (n & 1) { - SkTSwap(dither0, dither1); - } - - count -= n; - SkASSERT(count >= 0); - } -} - -void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[], - int count) { - SkASSERT(count > 0); - SkASSERT(kLinear_MatrixClass == fDstToIndexClass); - - SkPoint srcPt; - fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt); - float fx = srcPt.x(); - const float dx = fDstToIndex.getScaleX(); - - // Default our dither bias values to 1/2, (rounding), which is no dithering - float dither0 = 0.5f; - float dither1 = 0.5f; - if (fDither) { - const float ditherCell[] = { - 1/8.0f, 5/8.0f, - 7/8.0f, 3/8.0f, - }; - const int rowIndex = (y & 1) << 1; - dither0 = ditherCell[rowIndex]; - dither1 = ditherCell[rowIndex + 1]; - if (x & 1) { - SkTSwap(dither0, dither1); - } - } - const float dither[2] = { dither0, dither1 }; - - if (SkScalarNearlyZero(dx * count)) { // gradient is vertical - const float pinFx = SkTPin(fx, 0.0f, 1.0f); - Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx)); - if (fApplyAlphaAfterInterp) { - fill<true>(dstC, count, c, dither0, dither1); - } else { - fill<false>(dstC, count, c, dither0, dither1); - } - return; - } - - SkASSERT(0.f != dx); - const float invDx = 1 / dx; - if (dx > 0) { - if (fApplyAlphaAfterInterp) { - this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither); - } else { - this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither); - } - } else { - if (fApplyAlphaAfterInterp) { - this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither); - } else { - this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither); - } - } -} diff --git a/src/effects/gradients/SkLinearGradient.h b/src/effects/gradients/SkLinearGradient.h deleted file mode 100644 index 19a965c7bb..0000000000 --- a/src/effects/gradients/SkLinearGradient.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkLinearGradient_DEFINED -#define SkLinearGradient_DEFINED - -#include "SkGradientShaderPriv.h" -#include "SkNx.h" - -struct Sk4fStorage { - float fArray[4]; - - operator Sk4f() const { - return Sk4f::Load(fArray); - } - - Sk4fStorage& operator=(const Sk4f& src) { - src.store(fArray); - return *this; - } -}; - -class SkLinearGradient : public SkGradientShaderBase { -public: - enum { - // Temp flag for testing the 4f impl. - kForce4fContext_PrivateFlag = 1 << 7, - }; - - SkLinearGradient(const SkPoint pts[2], const Descriptor&); - - class LinearGradientContext : public SkGradientShaderBase::GradientShaderBaseContext { - public: - LinearGradientContext(const SkLinearGradient&, const ContextRec&); - - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - - struct Rec { - Sk4fStorage fColor; - float fPos; - float fPosScale; - }; - private: - SkTDArray<Rec> fRecs; - bool fApplyAlphaAfterInterp; - - void shade4_clamp(int x, int y, SkPMColor dstC[], int count); - template <bool, bool> void shade4_dx_clamp(SkPMColor dstC[], int count, float fx, float dx, - float invDx, const float dither[2]); - - typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED; - }; - - GradientType asAGradient(GradientInfo* info) const override; -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLinearGradient) - -protected: - SkLinearGradient(SkReadBuffer& buffer); - void flatten(SkWriteBuffer& buffer) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - - bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const final; - - - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - -private: - class LinearGradient4fContext; - - friend class SkGradientShader; - typedef SkGradientShaderBase INHERITED; - const SkPoint fStart; - const SkPoint fEnd; -}; - -#endif diff --git a/src/effects/gradients/SkRadialGradient.cpp b/src/effects/gradients/SkRadialGradient.cpp deleted file mode 100644 index d49b3dd8e1..0000000000 --- a/src/effects/gradients/SkRadialGradient.cpp +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkColorSpaceXformer.h" -#include "SkRadialGradient.h" -#include "SkNx.h" - -namespace { - -// GCC doesn't like using static functions as template arguments. So force these to be non-static. -inline SkFixed mirror_tileproc_nonstatic(SkFixed x) { - return mirror_tileproc(x); -} - -inline SkFixed repeat_tileproc_nonstatic(SkFixed x) { - return repeat_tileproc(x); -} - -SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) { - SkScalar inv = SkScalarInvert(radius); - - SkMatrix matrix; - matrix.setTranslate(-center.fX, -center.fY); - matrix.postScale(inv, inv); - return matrix; -} - - -} // namespace - -///////////////////////////////////////////////////////////////////// - -SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc) - : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius)) - , fCenter(center) - , fRadius(radius) { -} - -SkShaderBase::Context* SkRadialGradient::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const -{ - return CheckedMakeContext<RadialGradientContext>(alloc, *this, rec); -} - -SkRadialGradient::RadialGradientContext::RadialGradientContext( - const SkRadialGradient& shader, const ContextRec& rec) - : INHERITED(shader, rec) {} - -SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const { - if (info) { - commonAsAGradient(info); - info->fPoint[0] = fCenter; - info->fRadius[0] = fRadius; - } - return kRadial_GradientType; -} - -sk_sp<SkFlattenable> SkRadialGradient::CreateProc(SkReadBuffer& buffer) { - DescriptorScope desc; - if (!desc.unflatten(buffer)) { - return nullptr; - } - const SkPoint center = buffer.readPoint(); - const SkScalar radius = buffer.readScalar(); - return SkGradientShader::MakeRadial(center, radius, desc.fColors, std::move(desc.fColorSpace), - desc.fPos, desc.fCount, desc.fTileMode, desc.fGradFlags, - desc.fLocalMatrix); -} - -void SkRadialGradient::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writePoint(fCenter); - buffer.writeScalar(fRadius); -} - -namespace { - -inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) { - // fast, overly-conservative test: checks unit square instead of unit circle - bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0); - bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0); - return xClamped || yClamped; -} - -typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx, - SkScalar sfy, SkScalar sdy, - SkPMColor* dstC, const SkPMColor* cache, - int count, int toggle); - -static inline Sk4f fast_sqrt(const Sk4f& R) { - return R * R.rsqrt(); -} - -static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) { - return a * a + b * b; -} - -void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy, - SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, - int count, int toggle) { - if (radial_completely_pinned(sfx, sdx, sfy, sdy)) { - unsigned fi = SkGradientShaderBase::kCache32Count - 1; - sk_memset32_dither(dstC, - cache[toggle + fi], - cache[next_dither_toggle(toggle) + fi], - count); - } else { - const Sk4f min(SK_ScalarNearlyZero); - const Sk4f max(255); - const float scale = 255; - sfx *= scale; - sfy *= scale; - sdx *= scale; - sdy *= scale; - const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx); - const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy); - const Sk4f dx4(sdx * 4); - const Sk4f dy4(sdy * 4); - - Sk4f tmpxy = fx4 * dx4 + fy4 * dy4; - Sk4f tmpdxdy = sum_squares(dx4, dy4); - Sk4f R = Sk4f::Max(sum_squares(fx4, fy4), min); - Sk4f dR = tmpxy + tmpxy + tmpdxdy; - const Sk4f ddR = tmpdxdy + tmpdxdy; - - for (int i = 0; i < (count >> 2); ++i) { - Sk4f dist = Sk4f::Min(fast_sqrt(R), max); - R = Sk4f::Max(R + dR, min); - dR = dR + ddR; - - uint8_t fi[4]; - SkNx_cast<uint8_t>(dist).store(fi); - - for (int i = 0; i < 4; i++) { - *dstC++ = cache[toggle + fi[i]]; - toggle = next_dither_toggle(toggle); - } - } - count &= 3; - if (count) { - Sk4f dist = Sk4f::Min(fast_sqrt(R), max); - - uint8_t fi[4]; - SkNx_cast<uint8_t>(dist).store(fi); - for (int i = 0; i < count; i++) { - *dstC++ = cache[toggle + fi[i]]; - toggle = next_dither_toggle(toggle); - } - } - } -} - -// Unrolling this loop doesn't seem to help (when float); we're stalling to -// get the results of the sqrt (?), and don't have enough extra registers to -// have many in flight. -template <SkFixed (*TileProc)(SkFixed)> -void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, - SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, - int count, int toggle) { - do { - const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy)); - const unsigned fi = TileProc(dist); - SkASSERT(fi <= 0xFFFF); - *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)]; - toggle = next_dither_toggle(toggle); - fx += dx; - fy += dy; - } while (--count != 0); -} - -void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, - SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, - int count, int toggle) { - shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); -} - -void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, - SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, - int count, int toggle) { - shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); -} - -} // namespace - -void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y, - SkPMColor* SK_RESTRICT dstC, int count) { - SkASSERT(count > 0); - - const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader); - - SkPoint srcPt; - SkMatrix::MapXYProc dstProc = fDstToIndexProc; - TileProc proc = radialGradient.fTileProc; - const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); - int toggle = init_dither_toggle(x, y); - - if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkScalar sdx = fDstToIndex.getScaleX(); - SkScalar sdy = fDstToIndex.getSkewY(); - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y)); - sdx = step.fX; - sdy = step.fY; - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - } - - RadialShadeProc shadeProc = shadeSpan_radial_repeat; - if (SkShader::kClamp_TileMode == radialGradient.fTileMode) { - shadeProc = shadeSpan_radial_clamp2; - } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) { - shadeProc = shadeSpan_radial_mirror; - } else { - SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode); - } - (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle); - } else { // perspective case - SkScalar dstX = SkIntToScalar(x); - SkScalar dstY = SkIntToScalar(y); - do { - dstProc(fDstToIndex, dstX, dstY, &srcPt); - unsigned fi = proc(SkScalarToFixed(srcPt.length())); - SkASSERT(fi <= 0xFFFF); - *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift]; - dstX += SK_Scalar1; - } while (--count != 0); - } -} - -///////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "SkGr.h" -#include "GrShaderCaps.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" - -class GrRadialGradient : public GrGradientEffect { -public: - class GLSLRadialProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) { - return sk_sp<GrFragmentProcessor>(new GrRadialGradient(args)); - } - - ~GrRadialGradient() override {} - - const char* name() const override { return "Radial Gradient"; } - -private: - GrRadialGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) { - this->initClassID<GrRadialGradient>(); - } - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override; - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - typedef GrGradientEffect INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -class GrRadialGradient::GLSLRadialProcessor : public GrGradientEffect::GLSLProcessor { -public: - GLSLRadialProcessor(const GrProcessor&) {} - ~GLSLRadialProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); - } - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -///////////////////////////////////////////////////////////////////// - -GrGLSLFragmentProcessor* GrRadialGradient::onCreateGLSLInstance() const { - return new GrRadialGradient::GLSLRadialProcessor(*this); -} - -void GrRadialGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - GrRadialGradient::GLSLRadialProcessor::GenKey(*this, caps, b); -} - -///////////////////////////////////////////////////////////////////// - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient); - -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> GrRadialGradient::TestCreate(GrProcessorTestData* d) { - sk_sp<SkShader> shader; - do { - RandomGradientParams params(d->fRandom); - SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius = d->fRandom->nextUScalar1(); - shader = params.fUseColors4f - ? SkGradientShader::MakeRadial(center, radius, params.fColors4f, - params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) - : SkGradientShader::MakeRadial(center, radius, params.fColors, - params.fStops, params.fColorCount, - params.fTileMode); - } while (!shader); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -///////////////////////////////////////////////////////////////////// - -void GrRadialGradient::GLSLRadialProcessor::emitCode(EmitArgs& args) { - const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>(); - this->emitUniforms(args.fUniformHandler, ge); - SkString t("length("); - t.append(args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0])); - t.append(")"); - this->emitColor(args.fFragBuilder, - args.fUniformHandler, - args.fShaderCaps, - ge, t.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -///////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(const AsFPArgs& args) const { - SkASSERT(args.fContext); - - SkMatrix matrix; - if (!this->getLocalMatrix().invert(&matrix)) { - return nullptr; - } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } - matrix.postConcat(fPtsToUnit); - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), - args.fDstColorSpace); - sk_sp<GrFragmentProcessor> inner(GrRadialGradient::Make( - GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode, - std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)))); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); -} - -#endif - -sk_sp<SkShader> SkRadialGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); - return SkGradientShader::MakeRadial(fCenter, fRadius, xformedColors.begin(), fOrigPos, - fColorCount, fTileMode, fGradFlags, - &this->getLocalMatrix()); -} - -bool SkRadialGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const { - matrix->postTranslate(-fCenter.fX, -fCenter.fY); - matrix->postScale(1/fRadius, 1/fRadius); - - p->append(SkRasterPipeline::xy_to_radius); - return true; -} - -#ifndef SK_IGNORE_TO_STRING -void SkRadialGradient::toString(SkString* str) const { - str->append("SkRadialGradient: ("); - - str->append("center: ("); - str->appendScalar(fCenter.fX); - str->append(", "); - str->appendScalar(fCenter.fY); - str->append(") radius: "); - str->appendScalar(fRadius); - str->append(" "); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif diff --git a/src/effects/gradients/SkRadialGradient.h b/src/effects/gradients/SkRadialGradient.h deleted file mode 100644 index 69ec4b1285..0000000000 --- a/src/effects/gradients/SkRadialGradient.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkRadialGradient_DEFINED -#define SkRadialGradient_DEFINED - -#include "SkGradientShaderPriv.h" - -class SkRadialGradient : public SkGradientShaderBase { -public: - SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor&); - - class RadialGradientContext : public SkGradientShaderBase::GradientShaderBaseContext { - public: - RadialGradientContext(const SkRadialGradient&, const ContextRec&); - - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - - private: - typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED; - }; - - GradientType asAGradient(GradientInfo* info) const override; -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialGradient) - -protected: - SkRadialGradient(SkReadBuffer& buffer); - void flatten(SkWriteBuffer& buffer) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - - bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const final; - -private: - const SkPoint fCenter; - const SkScalar fRadius; - - friend class SkGradientShader; - typedef SkGradientShaderBase INHERITED; -}; - -#endif diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp deleted file mode 100644 index 1e583c2ee0..0000000000 --- a/src/effects/gradients/SkSweepGradient.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkColorSpaceXformer.h" -#include "SkSweepGradient.h" - -#include <algorithm> -#include <cmath> - -#include "SkPM4fPriv.h" -#include "SkRasterPipeline.h" - -static SkMatrix translate(SkScalar dx, SkScalar dy) { - SkMatrix matrix; - matrix.setTranslate(dx, dy); - return matrix; -} - -SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor& desc) - : SkGradientShaderBase(desc, translate(-cx, -cy)) - , fCenter(SkPoint::Make(cx, cy)) -{ - // overwrite the tilemode to a canonical value (since sweep ignores it) - fTileMode = SkShader::kClamp_TileMode; -} - -SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const { - if (info) { - commonAsAGradient(info); - info->fPoint[0] = fCenter; - } - return kSweep_GradientType; -} - -sk_sp<SkFlattenable> SkSweepGradient::CreateProc(SkReadBuffer& buffer) { - DescriptorScope desc; - if (!desc.unflatten(buffer)) { - return nullptr; - } - const SkPoint center = buffer.readPoint(); - return SkGradientShader::MakeSweep(center.x(), center.y(), desc.fColors, - std::move(desc.fColorSpace), desc.fPos, desc.fCount, - desc.fGradFlags, desc.fLocalMatrix); -} - -void SkSweepGradient::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writePoint(fCenter); -} - -SkShaderBase::Context* SkSweepGradient::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const -{ - return CheckedMakeContext<SweepGradientContext>(alloc, *this, rec); -} - -SkSweepGradient::SweepGradientContext::SweepGradientContext( - const SkSweepGradient& shader, const ContextRec& rec) - : INHERITED(shader, rec) {} - -bool SkSweepGradient::isRasterPipelineOnly() const { -#ifdef SK_LEGACY_SWEEP_GRADIENT - return false; -#else - return true; -#endif -} - -// returns angle in a circle [0..2PI) -> [0..255] -static unsigned SkATan2_255(float y, float x) { - // static const float g255Over2PI = 255 / (2 * SK_ScalarPI); - static const float g255Over2PI = 40.584510488433314f; - - float result = sk_float_atan2(y, x); - if (!SkScalarIsFinite(result)) { - return 0; - } - if (result < 0) { - result += 2 * SK_ScalarPI; - } - SkASSERT(result >= 0); - // since our value is always >= 0, we can cast to int, which is faster than - // calling floorf() - int ir = (int)(result * g255Over2PI); - SkASSERT(ir >= 0 && ir <= 255); - return ir; -} - -void SkSweepGradient::SweepGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, - int count) { - SkMatrix::MapXYProc proc = fDstToIndexProc; - const SkMatrix& matrix = fDstToIndex; - const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); - int toggle = init_dither_toggle(x, y); - SkPoint srcPt; - - if (fDstToIndexClass != kPerspective_MatrixClass) { - proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkScalar dx, fx = srcPt.fX; - SkScalar dy, fy = srcPt.fY; - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - const auto step = matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf); - dx = step.fX; - dy = step.fY; - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - dx = matrix.getScaleX(); - dy = matrix.getSkewY(); - } - - for (; count > 0; --count) { - *dstC++ = cache[toggle + SkATan2_255(fy, fx)]; - fx += dx; - fy += dy; - toggle = next_dither_toggle(toggle); - } - } else { // perspective case - for (int stop = x + count; x < stop; x++) { - proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - *dstC++ = cache[toggle + SkATan2_255(srcPt.fY, srcPt.fX)]; - toggle = next_dither_toggle(toggle); - } - } -} - -///////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "SkGr.h" -#include "GrShaderCaps.h" -#include "gl/GrGLContext.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" - -class GrSweepGradient : public GrGradientEffect { -public: - class GLSLSweepProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) { - return sk_sp<GrFragmentProcessor>(new GrSweepGradient(args)); - } - ~GrSweepGradient() override {} - - const char* name() const override { return "Sweep Gradient"; } - -private: - GrSweepGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) { - this->initClassID<GrSweepGradient>(); - } - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override; - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - typedef GrGradientEffect INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -class GrSweepGradient::GLSLSweepProcessor : public GrGradientEffect::GLSLProcessor { -public: - GLSLSweepProcessor(const GrProcessor&) {} - ~GLSLSweepProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); - } - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -///////////////////////////////////////////////////////////////////// - -GrGLSLFragmentProcessor* GrSweepGradient::onCreateGLSLInstance() const { - return new GrSweepGradient::GLSLSweepProcessor(*this); -} - -void GrSweepGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - GrSweepGradient::GLSLSweepProcessor::GenKey(*this, caps, b); -} - - -///////////////////////////////////////////////////////////////////// - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSweepGradient); - -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> GrSweepGradient::TestCreate(GrProcessorTestData* d) { - SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeSweep(center.fX, center.fY, params.fColors4f, params.fColorSpace, - params.fStops, params.fColorCount) : - SkGradientShader::MakeSweep(center.fX, center.fY, params.fColors, - params.fStops, params.fColorCount); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -///////////////////////////////////////////////////////////////////// - -void GrSweepGradient::GLSLSweepProcessor::emitCode(EmitArgs& args) { - const GrSweepGradient& ge = args.fFp.cast<GrSweepGradient>(); - this->emitUniforms(args.fUniformHandler, ge); - SkString coords2D = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - SkString t; - // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi] - if (args.fShaderCaps->atan2ImplementedAsAtanYOverX()) { - // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is - // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in - // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device - // handle the undefined behavior of the second paramenter being 0 instead of doing the - // divide ourselves and using atan instead. - t.printf("(2.0 * atan(- %s.y, length(%s) - %s.x) * 0.1591549430918 + 0.5)", - coords2D.c_str(), coords2D.c_str(), coords2D.c_str()); - } else { - t.printf("(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5)", - coords2D.c_str(), coords2D.c_str()); - } - this->emitColor(args.fFragBuilder, - args.fUniformHandler, - args.fShaderCaps, - ge, t.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -///////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(const AsFPArgs& args) const { - - SkMatrix matrix; - if (!this->getLocalMatrix().invert(&matrix)) { - return nullptr; - } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } - matrix.postConcat(fPtsToUnit); - - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), - args.fDstColorSpace); - sk_sp<GrFragmentProcessor> inner(GrSweepGradient::Make( - GrGradientEffect::CreateArgs(args.fContext, this, &matrix, SkShader::kClamp_TileMode, - std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)))); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); -} - -#endif - -sk_sp<SkShader> SkSweepGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); - return SkGradientShader::MakeSweep(fCenter.fX, fCenter.fY, xformedColors.begin(), fOrigPos, - fColorCount, fGradFlags, &this->getLocalMatrix()); -} - -#ifndef SK_IGNORE_TO_STRING -void SkSweepGradient::toString(SkString* str) const { - str->append("SkSweepGradient: ("); - - str->append("center: ("); - str->appendScalar(fCenter.fX); - str->append(", "); - str->appendScalar(fCenter.fY); - str->append(") "); - - this->INHERITED::toString(str); - - str->append(")"); -} - -bool SkSweepGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const { - matrix->postTranslate(-fCenter.fX, -fCenter.fY); - p->append(SkRasterPipeline::xy_to_unit_angle); - - return true; -} - -#endif diff --git a/src/effects/gradients/SkSweepGradient.h b/src/effects/gradients/SkSweepGradient.h deleted file mode 100644 index b7ed7e5bb9..0000000000 --- a/src/effects/gradients/SkSweepGradient.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkSweepGradient_DEFINED -#define SkSweepGradient_DEFINED - -#include "SkGradientShaderPriv.h" - -class SkSweepGradient : public SkGradientShaderBase { -public: - SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor&); - - class SweepGradientContext : public SkGradientShaderBase::GradientShaderBaseContext { - public: - SweepGradientContext(const SkSweepGradient& shader, const ContextRec&); - - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - - private: - typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED; - }; - - GradientType asAGradient(GradientInfo* info) const override; - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSweepGradient) - -protected: - void flatten(SkWriteBuffer& buffer) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - - bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const final; - - bool isRasterPipelineOnly() const final; - -private: - const SkPoint fCenter; - - friend class SkGradientShader; - typedef SkGradientShaderBase INHERITED; -}; - -#endif diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp deleted file mode 100644 index 4549527d51..0000000000 --- a/src/effects/gradients/SkTwoPointConicalGradient.cpp +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkTwoPointConicalGradient.h" - -struct TwoPtRadialContext { - const TwoPtRadial& fRec; - float fRelX, fRelY; - const float fIncX, fIncY; - float fB; - const float fDB; - - TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy, - SkScalar dfx, SkScalar dfy); - SkFixed nextT(); -}; - -static int valid_divide(float numer, float denom, float* ratio) { - SkASSERT(ratio); - if (0 == denom) { - return 0; - } - *ratio = numer / denom; - return 1; -} - -// Return the number of distinct real roots, and write them into roots[] in -// ascending order -static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) { - SkASSERT(roots); - - if (A == 0) { - return valid_divide(-C, B, roots); - } - - float R = B*B - 4*A*C; - if (R < 0) { - return 0; - } - R = sk_float_sqrt(R); - -#if 1 - float Q = B; - if (Q < 0) { - Q -= R; - } else { - Q += R; - } -#else - // on 10.6 this was much slower than the above branch :( - float Q = B + copysignf(R, B); -#endif - Q *= -0.5f; - if (0 == Q) { - roots[0] = 0; - return 1; - } - - float r0 = Q / A; - float r1 = C / Q; - roots[0] = r0 < r1 ? r0 : r1; - roots[1] = r0 > r1 ? r0 : r1; - if (descendingOrder) { - SkTSwap(roots[0], roots[1]); - } - return 2; -} - -static float lerp(float x, float dx, float t) { - return x + t * dx; -} - -static float sqr(float x) { return x * x; } - -void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0, - const SkPoint& center1, SkScalar rad1, - bool flipped) { - fCenterX = SkScalarToFloat(center0.fX); - fCenterY = SkScalarToFloat(center0.fY); - fDCenterX = SkScalarToFloat(center1.fX) - fCenterX; - fDCenterY = SkScalarToFloat(center1.fY) - fCenterY; - fRadius = SkScalarToFloat(rad0); - fDRadius = SkScalarToFloat(rad1) - fRadius; - - fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius); - fRadius2 = sqr(fRadius); - fRDR = fRadius * fDRadius; - - fFlipped = flipped; -} - -TwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy, - SkScalar dfx, SkScalar dfy) - : fRec(rec) - , fRelX(SkScalarToFloat(fx) - rec.fCenterX) - , fRelY(SkScalarToFloat(fy) - rec.fCenterY) - , fIncX(SkScalarToFloat(dfx)) - , fIncY(SkScalarToFloat(dfy)) - , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR)) - , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {} - -SkFixed TwoPtRadialContext::nextT() { - float roots[2]; - - float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2; - int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped); - - fRelX += fIncX; - fRelY += fIncY; - fB += fDB; - - if (0 == countRoots) { - return TwoPtRadial::kDontDrawT; - } - - // Prefer the bigger t value if both give a radius(t) > 0 - // find_quad_roots returns the values sorted, so we start with the last - float t = roots[countRoots - 1]; - float r = lerp(fRec.fRadius, fRec.fDRadius, t); - if (r < 0) { - t = roots[0]; // might be the same as roots[countRoots-1] - r = lerp(fRec.fRadius, fRec.fDRadius, t); - if (r < 0) { - return TwoPtRadial::kDontDrawT; - } - } - return SkFloatToFixed(t); -} - -typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC, - const SkPMColor* cache, int toggle, int count); - -static void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, int toggle, - int count) { - for (; count > 0; --count) { - SkFixed t = rec->nextT(); - if (TwoPtRadial::DontDrawT(t)) { - *dstC++ = 0; - } else { - SkFixed index = SkClampMax(t, 0xFFFF); - SkASSERT(index <= 0xFFFF); - *dstC++ = cache[toggle + - (index >> SkGradientShaderBase::kCache32Shift)]; - } - toggle = next_dither_toggle(toggle); - } -} - -static void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, int toggle, - int count) { - for (; count > 0; --count) { - SkFixed t = rec->nextT(); - if (TwoPtRadial::DontDrawT(t)) { - *dstC++ = 0; - } else { - SkFixed index = repeat_tileproc(t); - SkASSERT(index <= 0xFFFF); - *dstC++ = cache[toggle + - (index >> SkGradientShaderBase::kCache32Shift)]; - } - toggle = next_dither_toggle(toggle); - } -} - -static void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, int toggle, - int count) { - for (; count > 0; --count) { - SkFixed t = rec->nextT(); - if (TwoPtRadial::DontDrawT(t)) { - *dstC++ = 0; - } else { - SkFixed index = mirror_tileproc(t); - SkASSERT(index <= 0xFFFF); - *dstC++ = cache[toggle + - (index >> SkGradientShaderBase::kCache32Shift)]; - } - toggle = next_dither_toggle(toggle); - } -} - -///////////////////////////////////////////////////////////////////// - -SkTwoPointConicalGradient::SkTwoPointConicalGradient( - const SkPoint& start, SkScalar startRadius, - const SkPoint& end, SkScalar endRadius, - bool flippedGrad, const Descriptor& desc) - : SkGradientShaderBase(desc, SkMatrix::I()) - , fCenter1(start) - , fCenter2(end) - , fRadius1(startRadius) - , fRadius2(endRadius) - , fFlippedGrad(flippedGrad) -{ - // this is degenerate, and should be caught by our caller - SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2); - fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad); -} - -bool SkTwoPointConicalGradient::isOpaque() const { - // Because areas outside the cone are left untouched, we cannot treat the - // shader as opaque even if the gradient itself is opaque. - // TODO(junov): Compute whether the cone fills the plane crbug.com/222380 - return false; -} - -SkShaderBase::Context* SkTwoPointConicalGradient::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const { - return CheckedMakeContext<TwoPointConicalGradientContext>(alloc, *this, rec); -} - -SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext( - const SkTwoPointConicalGradient& shader, const ContextRec& rec) - : INHERITED(shader, rec) -{ - // in general, we might discard based on computed-radius, so clear - // this flag (todo: sometimes we can detect that we never discard...) - fFlags &= ~kOpaqueAlpha_Flag; -} - -void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan( - int x, int y, SkPMColor* dstCParam, int count) { - const SkTwoPointConicalGradient& twoPointConicalGradient = - static_cast<const SkTwoPointConicalGradient&>(fShader); - - int toggle = init_dither_toggle(x, y); - - SkASSERT(count > 0); - - SkPMColor* SK_RESTRICT dstC = dstCParam; - - SkMatrix::MapXYProc dstProc = fDstToIndexProc; - - const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); - - TwoPointConicalProc shadeProc = twopoint_repeat; - if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) { - shadeProc = twopoint_clamp; - } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) { - shadeProc = twopoint_mirror; - } else { - SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode); - } - - if (fDstToIndexClass != kPerspective_MatrixClass) { - SkPoint srcPt; - dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkScalar dx, fx = srcPt.fX; - SkScalar dy, fy = srcPt.fY; - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y)); - dx = step.fX; - dy = step.fY; - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - dx = fDstToIndex.getScaleX(); - dy = fDstToIndex.getSkewY(); - } - - TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy); - (*shadeProc)(&rec, dstC, cache, toggle, count); - } else { // perspective case - SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf; - SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf; - for (; count > 0; --count) { - SkPoint srcPt; - dstProc(fDstToIndex, dstX, dstY, &srcPt); - TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0); - (*shadeProc)(&rec, dstC, cache, toggle, 1); - - dstX += SK_Scalar1; - toggle = next_dither_toggle(toggle); - dstC += 1; - } - } -} - -// Returns the original non-sorted version of the gradient -SkShader::GradientType SkTwoPointConicalGradient::asAGradient( - GradientInfo* info) const { - if (info) { - commonAsAGradient(info, fFlippedGrad); - info->fPoint[0] = fCenter1; - info->fPoint[1] = fCenter2; - info->fRadius[0] = fRadius1; - info->fRadius[1] = fRadius2; - if (fFlippedGrad) { - SkTSwap(info->fPoint[0], info->fPoint[1]); - SkTSwap(info->fRadius[0], info->fRadius[1]); - } - } - return kConical_GradientType; -} - -sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) { - DescriptorScope desc; - if (!desc.unflatten(buffer)) { - return nullptr; - } - SkPoint c1 = buffer.readPoint(); - SkPoint c2 = buffer.readPoint(); - SkScalar r1 = buffer.readScalar(); - SkScalar r2 = buffer.readScalar(); - - if (buffer.readBool()) { // flipped - SkTSwap(c1, c2); - SkTSwap(r1, r2); - - SkColor4f* colors = desc.mutableColors(); - SkScalar* pos = desc.mutablePos(); - const int last = desc.fCount - 1; - const int half = desc.fCount >> 1; - for (int i = 0; i < half; ++i) { - SkTSwap(colors[i], colors[last - i]); - if (pos) { - SkScalar tmp = pos[i]; - pos[i] = SK_Scalar1 - pos[last - i]; - pos[last - i] = SK_Scalar1 - tmp; - } - } - if (pos) { - if (desc.fCount & 1) { - pos[half] = SK_Scalar1 - pos[half]; - } - } - } - - return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors, - std::move(desc.fColorSpace), desc.fPos, - desc.fCount, desc.fTileMode, desc.fGradFlags, - desc.fLocalMatrix); -} - -void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writePoint(fCenter1); - buffer.writePoint(fCenter2); - buffer.writeScalar(fRadius1); - buffer.writeScalar(fRadius2); - buffer.writeBool(fFlippedGrad); -} - -#if SK_SUPPORT_GPU - -#include "SkGr.h" -#include "SkTwoPointConicalGradient_gpu.h" - -sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor( - const AsFPArgs& args) const { - SkASSERT(args.fContext); - SkASSERT(fPtsToUnit.isIdentity()); - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), - args.fDstColorSpace); - sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make( - GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode, - std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)))); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); -} - -#endif - -sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkSTArray<8, SkColor> origColorsStorage(fColorCount); - SkSTArray<8, SkScalar> origPosStorage(fColorCount); - SkSTArray<8, SkColor> xformedColorsStorage(fColorCount); - SkColor* origColors = origColorsStorage.begin(); - SkScalar* origPos = fOrigPos ? origPosStorage.begin() : nullptr; - SkColor* xformedColors = xformedColorsStorage.begin(); - - // Flip if necessary - SkPoint center1 = fFlippedGrad ? fCenter2 : fCenter1; - SkPoint center2 = fFlippedGrad ? fCenter1 : fCenter2; - SkScalar radius1 = fFlippedGrad ? fRadius2 : fRadius1; - SkScalar radius2 = fFlippedGrad ? fRadius1 : fRadius2; - for (int i = 0; i < fColorCount; i++) { - origColors[i] = fFlippedGrad ? fOrigColors[fColorCount - i - 1] : fOrigColors[i]; - if (origPos) { - origPos[i] = fFlippedGrad ? 1.0f - fOrigPos[fColorCount - i - 1] : fOrigPos[i]; - } - } - - xformer->apply(xformedColors, origColors, fColorCount); - return SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, xformedColors, - origPos, fColorCount, fTileMode, fGradFlags, - &this->getLocalMatrix()); -} - - -#ifndef SK_IGNORE_TO_STRING -void SkTwoPointConicalGradient::toString(SkString* str) const { - str->append("SkTwoPointConicalGradient: ("); - - str->append("center1: ("); - str->appendScalar(fCenter1.fX); - str->append(", "); - str->appendScalar(fCenter1.fY); - str->append(") radius1: "); - str->appendScalar(fRadius1); - str->append(" "); - - str->append("center2: ("); - str->appendScalar(fCenter2.fX); - str->append(", "); - str->appendScalar(fCenter2.fY); - str->append(") radius2: "); - str->appendScalar(fRadius2); - str->append(" "); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif diff --git a/src/effects/gradients/SkTwoPointConicalGradient.h b/src/effects/gradients/SkTwoPointConicalGradient.h deleted file mode 100644 index b32f52c1e0..0000000000 --- a/src/effects/gradients/SkTwoPointConicalGradient.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkTwoPointConicalGradient_DEFINED -#define SkTwoPointConicalGradient_DEFINED - -#include "SkColorSpaceXformer.h" -#include "SkGradientShaderPriv.h" - -// TODO(dominikg): Worth making it truly immutable (i.e. set values in constructor)? -// Should only be initialized once via init(). Immutable afterwards. -struct TwoPtRadial { - enum { - // This value is outside the range SK_FixedMin to SK_FixedMax. - kDontDrawT = 0x80000000 - }; - - float fCenterX, fCenterY; - float fDCenterX, fDCenterY; - float fRadius; - float fDRadius; - float fA; - float fRadius2; - float fRDR; - bool fFlipped; - - void init(const SkPoint& center0, SkScalar rad0, - const SkPoint& center1, SkScalar rad1, - bool flipped); - - static bool DontDrawT(SkFixed t) { - return kDontDrawT == (uint32_t)t; - } -}; - - -class SkTwoPointConicalGradient : public SkGradientShaderBase { - TwoPtRadial fRec; -public: - SkTwoPointConicalGradient(const SkPoint& start, SkScalar startRadius, - const SkPoint& end, SkScalar endRadius, - bool flippedGrad, const Descriptor&); - - class TwoPointConicalGradientContext : public SkGradientShaderBase::GradientShaderBaseContext { - public: - TwoPointConicalGradientContext(const SkTwoPointConicalGradient&, const ContextRec&); - ~TwoPointConicalGradientContext() override {} - - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - - private: - typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED; - }; - - SkShader::GradientType asAGradient(GradientInfo* info) const override; -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - bool isOpaque() const override; - - SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); } - SkScalar getStartRadius() const { return fRadius1; } - SkScalar getDiffRadius() const { return fRadius2 - fRadius1; } - const SkPoint& getStartCenter() const { return fCenter1; } - const SkPoint& getEndCenter() const { return fCenter2; } - SkScalar getEndRadius() const { return fRadius2; } - bool isFlippedGrad() const { return fFlippedGrad; } - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointConicalGradient) - -protected: - SkTwoPointConicalGradient(SkReadBuffer& buffer); - void flatten(SkWriteBuffer& buffer) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - -private: - SkPoint fCenter1; - SkPoint fCenter2; - SkScalar fRadius1; - SkScalar fRadius2; - bool fFlippedGrad; - - friend class SkGradientShader; - typedef SkGradientShaderBase INHERITED; -}; - -#endif diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp deleted file mode 100644 index 8402199362..0000000000 --- a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp +++ /dev/null @@ -1,1341 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkTwoPointConicalGradient.h" - -#if SK_SUPPORT_GPU -#include "GrCoordTransform.h" -#include "GrPaint.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" -#include "SkTwoPointConicalGradient_gpu.h" - -// For brevity -typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; - -static const SkScalar kErrorTol = 0.00001f; -static const SkScalar kEdgeErrorTol = 5.f * kErrorTol; - -/** - * We have three general cases for 2pt conical gradients. First we always assume that - * the start radius <= end radius. Our first case (kInside_) is when the start circle - * is completely enclosed by the end circle. The second case (kOutside_) is the case - * when the start circle is either completely outside the end circle or the circles - * overlap. The final case (kEdge_) is when the start circle is inside the end one, - * but the two are just barely touching at 1 point along their edges. - */ -enum ConicalType { - kInside_ConicalType, - kOutside_ConicalType, - kEdge_ConicalType, -}; - -////////////////////////////////////////////////////////////////////////////// - -static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader, - SkMatrix* invLMatrix) { - // Inverse of the current local matrix is passed in then, - // translate to center1, rotate so center2 is on x axis. - const SkPoint& center1 = shader.getStartCenter(); - const SkPoint& center2 = shader.getEndCenter(); - - invLMatrix->postTranslate(-center1.fX, -center1.fY); - - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - if (0 != diffLen) { - SkScalar invDiffLen = SkScalarInvert(diffLen); - SkMatrix rot; - rot.setSinCos(-invDiffLen * diff.fY, invDiffLen * diff.fX); - invLMatrix->postConcat(rot); - } -} - -class Edge2PtConicalEffect : public GrGradientEffect { -public: - class GLSLEdge2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) { - return sk_sp<GrFragmentProcessor>(new Edge2PtConicalEffect(args)); - } - - ~Edge2PtConicalEffect() override {} - - const char* name() const override { - return "Two-Point Conical Gradient Edge Touching"; - } - - // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. - SkScalar center() const { return fCenterX1; } - SkScalar diffRadius() const { return fDiffRadius; } - SkScalar radius() const { return fRadius0; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fCenterX1 == s.fCenterX1 && - this->fRadius0 == s.fRadius0 && - this->fDiffRadius == s.fDiffRadius); - } - - Edge2PtConicalEffect(const CreateArgs& args) - : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) { - const SkTwoPointConicalGradient& shader = - *static_cast<const SkTwoPointConicalGradient*>(args.fShader); - fCenterX1 = shader.getCenterX1(); - fRadius0 = shader.getStartRadius(); - fDiffRadius = shader.getDiffRadius(); - this->initClassID<Edge2PtConicalEffect>(); - // We should only be calling this shader if we are degenerate case with touching circles - // When deciding if we are in edge case, we scaled by the end radius for cases when the - // start radius was close to zero, otherwise we scaled by the start radius. In addition - // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we - // need the sqrt value below - SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) < - (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol : - fRadius0 * sqrt(kEdgeErrorTol))); - - // We pass the linear part of the quadratic as a varying. - // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) - fBTransform = this->getCoordTransform(); - SkMatrix& bMatrix = *fBTransform.accessMatrix(); - SkScalar r0dr = fRadius0 * fDiffRadius; - bMatrix[SkMatrix::kMScaleX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMScaleX] + - r0dr * bMatrix[SkMatrix::kMPersp0]); - bMatrix[SkMatrix::kMSkewX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMSkewX] + - r0dr * bMatrix[SkMatrix::kMPersp1]); - bMatrix[SkMatrix::kMTransX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMTransX] + - r0dr * bMatrix[SkMatrix::kMPersp2]); - this->addCoordTransform(&fBTransform); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - // @{ - // Cache of values - these can change arbitrarily, EXCEPT - // we shouldn't change between degenerate and non-degenerate?! - - GrCoordTransform fBTransform; - SkScalar fCenterX1; - SkScalar fRadius0; - SkScalar fDiffRadius; - - // @} - - typedef GrGradientEffect INHERITED; -}; - -class Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor : public GrGradientEffect::GLSLProcessor { -public: - GLSLEdge2PtConicalProcessor(const GrProcessor&); - ~GLSLEdge2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fParamUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedRadius; - SkScalar fCachedDiffRadius; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void Edge2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const { - return new Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = d->fRandom->nextUScalar1(); - SkPoint center2; - SkScalar radius2; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // If the circles are identical the factory will give us an empty shader. - // This will happen if we pick identical centers - } while (center1 == center2); - - // Below makes sure that circle one is contained within circle two - // and both circles are touching on an edge - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - radius2 = radius1 + diffLen; - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GLSLEdge2PtConicalProcessor(const GrProcessor&) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedRadius(-SK_ScalarMax) - , fCachedDiffRadius(-SK_ScalarMax) {} - -void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::emitCode(EmitArgs& args) { - const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - - SkString cName("c"); - SkString tName("t"); - SkString p0; // start radius - SkString p1; // start radius squared - SkString p2; // difference in radii (r1 - r0) - - - p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - - // We interpolate the linear component in coords[1]. - SkASSERT(args.fTransformedCoords[0].getType() == args.fTransformedCoords[1].getType()); - const char* coords2D; - SkString bVar; - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - if (kVec3f_GrSLType == args.fTransformedCoords[0].getType()) { - fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n", - args.fTransformedCoords[0].c_str(), - args.fTransformedCoords[0].c_str(), - args.fTransformedCoords[1].c_str(), - args.fTransformedCoords[1].c_str()); - coords2D = "interpolants.xy"; - bVar = "interpolants.z"; - } else { - coords2D = args.fTransformedCoords[0].c_str(); - bVar.printf("%s.x", args.fTransformedCoords[1].c_str()); - } - - // output will default to transparent black (we simply won't write anything - // else to it if invalid, instead of discarding or returning prematurely) - fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor); - - // c = (x^2)+(y^2) - params[1] - fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", - cName.c_str(), coords2D, coords2D, p1.c_str()); - - // linear case: t = -c/b - fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(), - cName.c_str(), bVar.c_str()); - - // if r(t) > 0, then t will be the x coordinate - fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), - p2.c_str(), p0.c_str()); - fragBuilder->codeAppend("\t"); - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); - fragBuilder->codeAppend("\t}\n"); -} - -void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>(); - SkScalar radius0 = data.radius(); - SkScalar diffRadius = data.diffRadius(); - - if (fCachedRadius != radius0 || - fCachedDiffRadius != diffRadius) { - - pdman.set3f(fParamUni, radius0, radius0 * radius0, diffRadius); - fCachedRadius = radius0; - fCachedDiffRadius = diffRadius; - } -} - -void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); -} - -////////////////////////////////////////////////////////////////////////////// -// Focal Conical Gradients -////////////////////////////////////////////////////////////////////////////// - -static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader, - SkMatrix* invLMatrix, SkScalar* focalX) { - // Inverse of the current local matrix is passed in then, - // translate, scale, and rotate such that endCircle is unit circle on x-axis, - // and focal point is at the origin. - ConicalType conicalType; - const SkPoint& focal = shader.getStartCenter(); - const SkPoint& centerEnd = shader.getEndCenter(); - SkScalar radius = shader.getEndRadius(); - SkScalar invRadius = 1.f / radius; - - SkMatrix matrix; - - matrix.setTranslate(-centerEnd.fX, -centerEnd.fY); - matrix.postScale(invRadius, invRadius); - - SkPoint focalTrans; - matrix.mapPoints(&focalTrans, &focal, 1); - *focalX = focalTrans.length(); - - if (0.f != *focalX) { - SkScalar invFocalX = SkScalarInvert(*focalX); - SkMatrix rot; - rot.setSinCos(-invFocalX * focalTrans.fY, invFocalX * focalTrans.fX); - matrix.postConcat(rot); - } - - matrix.postTranslate(-(*focalX), 0.f); - - // If the focal point is touching the edge of the circle it will - // cause a degenerate case that must be handled separately - // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the - // stability trade off versus the linear approx used in the Edge Shader - if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) { - return kEdge_ConicalType; - } - - // Scale factor 1 / (1 - focalX * focalX) - SkScalar oneMinusF2 = 1.f - *focalX * *focalX; - SkScalar s = SkScalarInvert(oneMinusF2); - - - if (s >= 0.f) { - conicalType = kInside_ConicalType; - matrix.postScale(s, s * SkScalarSqrt(oneMinusF2)); - } else { - conicalType = kOutside_ConicalType; - matrix.postScale(s, s); - } - - invLMatrix->postConcat(matrix); - - return conicalType; -} - -////////////////////////////////////////////////////////////////////////////// - -class FocalOutside2PtConicalEffect : public GrGradientEffect { -public: - class GLSLFocalOutside2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) { - return sk_sp<GrFragmentProcessor>( - new FocalOutside2PtConicalEffect(args, focalX)); - } - - ~FocalOutside2PtConicalEffect() override {} - - const char* name() const override { - return "Two-Point Conical Gradient Focal Outside"; - } - - bool isFlipped() const { return fIsFlipped; } - SkScalar focal() const { return fFocalX; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fFocalX == s.fFocalX && - this->fIsFlipped == s.fIsFlipped); - } - - static bool IsFlipped(const CreateArgs& args) { - // eww. - return static_cast<const SkTwoPointConicalGradient*>(args.fShader)->isFlippedGrad(); - } - - FocalOutside2PtConicalEffect(const CreateArgs& args, SkScalar focalX) - : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) - , fFocalX(focalX) - , fIsFlipped(IsFlipped(args)) { - this->initClassID<FocalOutside2PtConicalEffect>(); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - SkScalar fFocalX; - bool fIsFlipped; - - typedef GrGradientEffect INHERITED; -}; - -class FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor - : public GrGradientEffect::GLSLProcessor { -public: - GLSLFocalOutside2PtConicalProcessor(const GrProcessor&); - ~GLSLFocalOutside2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fParamUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - bool fIsFlipped; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedFocal; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const { - return new FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = 0.f; - SkPoint center2; - SkScalar radius2; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // Need to make sure the centers are not the same or else focal point will be inside - } while (center1 == center2); - - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - // Below makes sure that the focal point is not contained within circle two - radius2 = d->fRandom->nextRangeF(0.f, diffLen); - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor - ::GLSLFocalOutside2PtConicalProcessor(const GrProcessor& processor) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedFocal(SK_ScalarMax) { - const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>(); - fIsFlipped = data.isFlipped(); -} - -void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::emitCode(EmitArgs& args) { - const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - SkString tName("t"); - SkString p0; // focalX - SkString p1; // 1 - focalX * focalX - - p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - - // if we have a vec3 from being in perspective, convert it to a vec2 first - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - const char* coords2D = coords2DString.c_str(); - - // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2) - - // output will default to transparent black (we simply won't write anything - // else to it if invalid, instead of discarding or returning prematurely) - fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor); - - fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D); - fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D); - fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str()); - - // Must check to see if we flipped the circle order (to make sure start radius < end radius) - // If so we must also flip sign on sqrt - if (!fIsFlipped) { - fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(), - coords2D, p0.c_str()); - } else { - fragBuilder->codeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(), - coords2D, p0.c_str()); - } - - fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str()); - fragBuilder->codeAppend("\t\t"); - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); - fragBuilder->codeAppend("\t}\n"); -} - -void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>(); - SkASSERT(data.isFlipped() == fIsFlipped); - SkScalar focal = data.focal(); - - if (fCachedFocal != focal) { - SkScalar oneMinus2F = 1.f - focal * focal; - - pdman.set2f(fParamUni, SkScalarToFloat(focal), SkScalarToFloat(oneMinus2F)); - fCachedFocal = focal; - } -} - -void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey( - const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - uint32_t* key = b->add32n(2); - key[0] = GenBaseGradientKey(processor); - key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped(); -} - -////////////////////////////////////////////////////////////////////////////// - -class FocalInside2PtConicalEffect : public GrGradientEffect { -public: - class GLSLFocalInside2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) { - return sk_sp<GrFragmentProcessor>( - new FocalInside2PtConicalEffect(args, focalX)); - } - - ~FocalInside2PtConicalEffect() override {} - - const char* name() const override { - return "Two-Point Conical Gradient Focal Inside"; - } - - SkScalar focal() const { return fFocalX; } - - typedef FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor GLSLProcessor; - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fFocalX == s.fFocalX); - } - - FocalInside2PtConicalEffect(const CreateArgs& args, SkScalar focalX) - : INHERITED(args, args.fShader->colorsAreOpaque()), fFocalX(focalX) { - this->initClassID<FocalInside2PtConicalEffect>(); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - SkScalar fFocalX; - - typedef GrGradientEffect INHERITED; -}; - -class FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor - : public GrGradientEffect::GLSLProcessor { -public: - GLSLFocalInside2PtConicalProcessor(const GrProcessor&); - ~GLSLFocalInside2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fFocalUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedFocal; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const { - return new FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = 0.f; - SkPoint center2; - SkScalar radius2; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // Below makes sure radius2 is larger enouch such that the focal point - // is inside the end circle - SkScalar increase = d->fRandom->nextUScalar1(); - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - radius2 = diffLen + increase; - // If the circles are identical the factory will give us an empty shader. - } while (radius1 == radius2 && center1 == center2); - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor - ::GLSLFocalInside2PtConicalProcessor(const GrProcessor&) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedFocal(SK_ScalarMax) {} - -void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::emitCode(EmitArgs& args) { - const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kFloat_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - SkString tName("t"); - - // this is the distance along x-axis from the end center to focal point in - // transformed coordinates - GrShaderVar focal = uniformHandler->getUniformVariable(fFocalUni); - - // if we have a vec3 from being in perspective, convert it to a vec2 first - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - const char* coords2D = coords2DString.c_str(); - - // t = p.x * focalX + length(p) - fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(), - coords2D, focal.c_str(), coords2D); - - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>(); - SkScalar focal = data.focal(); - - if (fCachedFocal != focal) { - pdman.set1f(fFocalUni, SkScalarToFloat(focal)); - fCachedFocal = focal; - } -} - -void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey( - const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); -} - -////////////////////////////////////////////////////////////////////////////// -// Circle Conical Gradients -////////////////////////////////////////////////////////////////////////////// - -struct CircleConicalInfo { - SkPoint fCenterEnd; - SkScalar fA; - SkScalar fB; - SkScalar fC; -}; - -// Returns focal distance along x-axis in transformed coords -static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader, - SkMatrix* invLMatrix, CircleConicalInfo* info) { - // Inverse of the current local matrix is passed in then, - // translate and scale such that start circle is on the origin and has radius 1 - const SkPoint& centerStart = shader.getStartCenter(); - const SkPoint& centerEnd = shader.getEndCenter(); - SkScalar radiusStart = shader.getStartRadius(); - SkScalar radiusEnd = shader.getEndRadius(); - - SkMatrix matrix; - - matrix.setTranslate(-centerStart.fX, -centerStart.fY); - - SkScalar invStartRad = 1.f / radiusStart; - matrix.postScale(invStartRad, invStartRad); - - radiusEnd /= radiusStart; - - SkPoint centerEndTrans; - matrix.mapPoints(¢erEndTrans, ¢erEnd, 1); - - SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY - - radiusEnd * radiusEnd + 2 * radiusEnd - 1; - - // Check to see if start circle is inside end circle with edges touching. - // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting - // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing - // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is - // still accurate. - if (SkScalarAbs(A) < kEdgeErrorTol) { - return kEdge_ConicalType; - } - - SkScalar C = 1.f / A; - SkScalar B = (radiusEnd - 1.f) * C; - - matrix.postScale(C, C); - - invLMatrix->postConcat(matrix); - - info->fCenterEnd = centerEndTrans; - info->fA = A; - info->fB = B; - info->fC = C; - - // if A ends up being negative, the start circle is contained completely inside the end cirlce - if (A < 0.f) { - return kInside_ConicalType; - } - return kOutside_ConicalType; -} - -class CircleInside2PtConicalEffect : public GrGradientEffect { -public: - class GLSLCircleInside2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) { - return sk_sp<GrFragmentProcessor>( - new CircleInside2PtConicalEffect(args, info)); - } - - ~CircleInside2PtConicalEffect() override {} - - const char* name() const override { return "Two-Point Conical Gradient Inside"; } - - SkScalar centerX() const { return fInfo.fCenterEnd.fX; } - SkScalar centerY() const { return fInfo.fCenterEnd.fY; } - SkScalar A() const { return fInfo.fA; } - SkScalar B() const { return fInfo.fB; } - SkScalar C() const { return fInfo.fC; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fInfo.fCenterEnd == s.fInfo.fCenterEnd && - this->fInfo.fA == s.fInfo.fA && - this->fInfo.fB == s.fInfo.fB && - this->fInfo.fC == s.fInfo.fC); - } - - CircleInside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info) - : INHERITED(args, args.fShader->colorsAreOpaque()), fInfo(info) { - this->initClassID<CircleInside2PtConicalEffect>(); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - const CircleConicalInfo fInfo; - - typedef GrGradientEffect INHERITED; -}; - -class CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor - : public GrGradientEffect::GLSLProcessor { -public: - GLSLCircleInside2PtConicalProcessor(const GrProcessor&); - ~GLSLCircleInside2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fCenterUni; - UniformHandle fParamUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedCenterX; - SkScalar fCachedCenterY; - SkScalar fCachedA; - SkScalar fCachedB; - SkScalar fCachedC; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const { - return new CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0 - SkPoint center2; - SkScalar radius2; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // Below makes sure that circle one is contained within circle two - SkScalar increase = d->fRandom->nextUScalar1(); - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - radius2 = radius1 + diffLen + increase; - // If the circles are identical the factory will give us an empty shader. - } while (radius1 == radius2 && center1 == center2); - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor - ::GLSLCircleInside2PtConicalProcessor(const GrProcessor& processor) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedCenterX(SK_ScalarMax) - , fCachedCenterY(SK_ScalarMax) - , fCachedA(SK_ScalarMax) - , fCachedB(SK_ScalarMax) - , fCachedC(SK_ScalarMax) {} - -void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::emitCode(EmitArgs& args) { - const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSCenter"); - fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - SkString tName("t"); - - GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni); - // params.x = A - // params.y = B - // params.z = C - GrShaderVar params = uniformHandler->getUniformVariable(fParamUni); - - // if we have a vec3 from being in perspective, convert it to a vec2 first - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - const char* coords2D = coords2DString.c_str(); - - // p = coords2D - // e = center end - // r = radius end - // A = dot(e, e) - r^2 + 2 * r - 1 - // B = (r -1) / A - // C = 1 / A - // d = dot(e, p) + B - // t = d +/- sqrt(d^2 - A * dot(p, p) + C) - fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D); - fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), - params.c_str()); - fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n", - tName.c_str(), params.c_str(), params.c_str()); - - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>(); - SkScalar centerX = data.centerX(); - SkScalar centerY = data.centerY(); - SkScalar A = data.A(); - SkScalar B = data.B(); - SkScalar C = data.C(); - - if (fCachedCenterX != centerX || fCachedCenterY != centerY || - fCachedA != A || fCachedB != B || fCachedC != C) { - - pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY)); - pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C)); - - fCachedCenterX = centerX; - fCachedCenterY = centerY; - fCachedA = A; - fCachedB = B; - fCachedC = C; - } -} - -void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey( - const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); -} - -////////////////////////////////////////////////////////////////////////////// - -class CircleOutside2PtConicalEffect : public GrGradientEffect { -public: - class GLSLCircleOutside2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) { - return sk_sp<GrFragmentProcessor>( - new CircleOutside2PtConicalEffect(args, info)); - } - - ~CircleOutside2PtConicalEffect() override {} - - const char* name() const override { return "Two-Point Conical Gradient Outside"; } - - SkScalar centerX() const { return fInfo.fCenterEnd.fX; } - SkScalar centerY() const { return fInfo.fCenterEnd.fY; } - SkScalar A() const { return fInfo.fA; } - SkScalar B() const { return fInfo.fB; } - SkScalar C() const { return fInfo.fC; } - SkScalar tLimit() const { return fTLimit; } - bool isFlipped() const { return fIsFlipped; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fInfo.fCenterEnd == s.fInfo.fCenterEnd && - this->fInfo.fA == s.fInfo.fA && - this->fInfo.fB == s.fInfo.fB && - this->fInfo.fC == s.fInfo.fC && - this->fTLimit == s.fTLimit && - this->fIsFlipped == s.fIsFlipped); - } - - CircleOutside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info) - : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) - , fInfo(info) { - this->initClassID<CircleOutside2PtConicalEffect>(); - const SkTwoPointConicalGradient& shader = - *static_cast<const SkTwoPointConicalGradient*>(args.fShader); - if (shader.getStartRadius() != shader.getEndRadius()) { - fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius()); - } else { - fTLimit = SK_ScalarMin; - } - - fIsFlipped = shader.isFlippedGrad(); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - const CircleConicalInfo fInfo; - SkScalar fTLimit; - bool fIsFlipped; - - typedef GrGradientEffect INHERITED; -}; - -class CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor - : public GrGradientEffect::GLSLProcessor { -public: - GLSLCircleOutside2PtConicalProcessor(const GrProcessor&); - ~GLSLCircleOutside2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fCenterUni; - UniformHandle fParamUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - bool fIsFlipped; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedCenterX; - SkScalar fCachedCenterY; - SkScalar fCachedA; - SkScalar fCachedB; - SkScalar fCachedC; - SkScalar fCachedTLimit; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const { - return new CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0 - SkPoint center2; - SkScalar radius2; - SkScalar diffLen; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // If the circles share a center than we can't be in the outside case - } while (center1 == center2); - SkPoint diff = center2 - center1; - diffLen = diff.length(); - // Below makes sure that circle one is not contained within circle two - // and have radius2 >= radius to match sorting on cpu side - radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen); - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor - ::GLSLCircleOutside2PtConicalProcessor(const GrProcessor& processor) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedCenterX(SK_ScalarMax) - , fCachedCenterY(SK_ScalarMax) - , fCachedA(SK_ScalarMax) - , fCachedB(SK_ScalarMax) - , fCachedC(SK_ScalarMax) - , fCachedTLimit(SK_ScalarMax) { - const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>(); - fIsFlipped = data.isFlipped(); - } - -void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::emitCode(EmitArgs& args) { - const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSCenter"); - fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - SkString tName("t"); - - GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni); - // params.x = A - // params.y = B - // params.z = C - GrShaderVar params = uniformHandler->getUniformVariable(fParamUni); - - // if we have a vec3 from being in perspective, convert it to a vec2 first - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - const char* coords2D = coords2DString.c_str(); - - // output will default to transparent black (we simply won't write anything - // else to it if invalid, instead of discarding or returning prematurely) - fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor); - - // p = coords2D - // e = center end - // r = radius end - // A = dot(e, e) - r^2 + 2 * r - 1 - // B = (r -1) / A - // C = 1 / A - // d = dot(e, p) + B - // t = d +/- sqrt(d^2 - A * dot(p, p) + C) - - fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D); - fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), - params.c_str()); - fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(), - params.c_str()); - - // Must check to see if we flipped the circle order (to make sure start radius < end radius) - // If so we must also flip sign on sqrt - if (!fIsFlipped) { - fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str()); - } else { - fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str()); - } - - fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", - tName.c_str(), params.c_str()); - fragBuilder->codeAppend("\t\t"); - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); - fragBuilder->codeAppend("\t}\n"); -} - -void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>(); - SkASSERT(data.isFlipped() == fIsFlipped); - SkScalar centerX = data.centerX(); - SkScalar centerY = data.centerY(); - SkScalar A = data.A(); - SkScalar B = data.B(); - SkScalar C = data.C(); - SkScalar tLimit = data.tLimit(); - - if (fCachedCenterX != centerX || fCachedCenterY != centerY || - fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) { - - pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY)); - pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C), - SkScalarToFloat(tLimit)); - - fCachedCenterX = centerX; - fCachedCenterY = centerY; - fCachedA = A; - fCachedB = B; - fCachedC = C; - fCachedTLimit = tLimit; - } -} - -void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey( - const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - uint32_t* key = b->add32n(2); - key[0] = GenBaseGradientKey(processor); - key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped(); -} - -////////////////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make( - const GrGradientEffect::CreateArgs& args) { - const SkTwoPointConicalGradient& shader = - *static_cast<const SkTwoPointConicalGradient*>(args.fShader); - - SkMatrix matrix; - if (!shader.getLocalMatrix().invert(&matrix)) { - return nullptr; - } - if (args.fMatrix) { - SkMatrix inv; - if (!args.fMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } - - GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fTileMode, - std::move(args.fColorSpaceXform), args.fGammaCorrect); - - if (shader.getStartRadius() < kErrorTol) { - SkScalar focalX; - ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX); - if (type == kInside_ConicalType) { - return FocalInside2PtConicalEffect::Make(newArgs, focalX); - } else if(type == kEdge_ConicalType) { - set_matrix_edge_conical(shader, &matrix); - return Edge2PtConicalEffect::Make(newArgs); - } else { - return FocalOutside2PtConicalEffect::Make(newArgs, focalX); - } - } - - CircleConicalInfo info; - ConicalType type = set_matrix_circle_conical(shader, &matrix, &info); - - if (type == kInside_ConicalType) { - return CircleInside2PtConicalEffect::Make(newArgs, info); - } else if (type == kEdge_ConicalType) { - set_matrix_edge_conical(shader, &matrix); - return Edge2PtConicalEffect::Make(newArgs); - } else { - return CircleOutside2PtConicalEffect::Make(newArgs, info); - } -} - -#endif diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.h b/src/effects/gradients/SkTwoPointConicalGradient_gpu.h deleted file mode 100644 index 46edb1f7d1..0000000000 --- a/src/effects/gradients/SkTwoPointConicalGradient_gpu.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkTwoPointConicalGradient_gpu_DEFINED -#define SkTwoPointConicalGradient_gpu_DEFINED - -#include "SkGradientShaderPriv.h" - -class GrProcessor; -class SkTwoPointConicalGradient; - -namespace Gr2PtConicalGradientEffect { - /** - * Creates an effect that produces a two point conical gradient based on the - * shader passed in. - */ - sk_sp<GrFragmentProcessor> Make(const GrGradientEffect::CreateArgs& args); -}; - -#endif |