diff options
author | Ethan Nicholas <ethannicholas@google.com> | 2017-09-07 15:44:01 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-09-11 16:17:00 +0000 |
commit | c576e93d174f3106e072a2f506bca3990b541265 (patch) | |
tree | d4a410200aa71183c95643535b440bec919f2e18 /src/effects | |
parent | a2bdf005f3c706065d1aa93f319f4b73932721d4 (diff) |
Switch to the new SkSL lexer.
This completely replaces flex with a new in-house lexical analyzer generator,
which we have done for performance and memory usage reasons. Flex requires us
to copy strings every time we need the text of a token, whereas this new lexer
allows us to handle strings as a (non-null-terminated) pointer and length
everywhere, eliminating most string copies.
Bug: skia:
Change-Id: I2add26efc9e20cb699520e82abcf713af3968aca
Reviewed-on: https://skia-review.googlesource.com/39780
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Diffstat (limited to 'src/effects')
-rw-r--r-- | src/effects/GrAlphaThresholdFragmentProcessor.cpp | 2 | ||||
-rw-r--r-- | src/effects/GrCircleBlurFragmentProcessor.cpp | 55 |
2 files changed, 44 insertions, 13 deletions
diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.cpp b/src/effects/GrAlphaThresholdFragmentProcessor.cpp index 96961cf6e5..aebb7d35e8 100644 --- a/src/effects/GrAlphaThresholdFragmentProcessor.cpp +++ b/src/effects/GrAlphaThresholdFragmentProcessor.cpp @@ -130,7 +130,7 @@ std::unique_ptr<GrFragmentProcessor> GrAlphaThresholdFragmentProcessor::TestCrea GrProcessorTestData* testData) { sk_sp<GrTextureProxy> bmpProxy = testData->textureProxy(GrProcessorUnitTest::kSkiaPMTextureIdx); sk_sp<GrTextureProxy> maskProxy = testData->textureProxy(GrProcessorUnitTest::kAlphaTextureIdx); - + // Make the inner and outer thresholds be in (0, 1) exclusive and be sorted correctly. float innerThresh = testData->fRandom->nextUScalar1() * .99f + 0.005f; float outerThresh = testData->fRandom->nextUScalar1() * .99f + 0.005f; const int kMaxWidth = 1000; diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp index 7cec3cc431..75d73bdbd0 100644 --- a/src/effects/GrCircleBlurFragmentProcessor.cpp +++ b/src/effects/GrCircleBlurFragmentProcessor.cpp @@ -13,11 +13,13 @@ #include "GrResourceProvider.h" +// Computes an unnormalized half kernel (right side). Returns the summation of all the half +// kernel values. static float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize, float sigma) { const float invSigma = 1.f / sigma; const float b = -0.5f * invSigma * invSigma; float tot = 0.0f; - + // Compute half kernel values at half pixel steps out from the center. float t = 0.5f; for (int i = 0; i < halfKernelSize; ++i) { float value = expf(t * t * b); @@ -28,8 +30,11 @@ static float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize return tot; } +// Create a Gaussian half-kernel (right side) and a summed area table given a sigma and number +// of discrete steps. The half kernel is normalized to sum to 0.5. static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHalfKernel, int halfKernelSize, float sigma) { + // The half kernel should sum to 0.5 not 1.0. const float tot = 2.f * make_unnormalized_half_kernel(halfKernel, halfKernelSize, sigma); float sum = 0.f; for (int i = 0; i < halfKernelSize; ++i) { @@ -39,6 +44,8 @@ static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHa } } +// Applies the 1D half kernel vertically at points along the x axis to a circle centered at the +// origin with radius circleR. void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR, int halfKernelSize, const float* summedHalfKernelTable) { float x = firstX; @@ -48,7 +55,8 @@ void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR continue; } float y = sqrtf(circleR * circleR - x * x); - + // In the column at x we exit the circle at +y and -y + // The summed table entry j is actually reflects an offset of j + 0.5. y -= 0.5f; int yInt = SkScalarFloorToInt(y); SkASSERT(yInt >= -1); @@ -64,6 +72,10 @@ void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR } } +// Apply a Gaussian at point (evalX, 0) to a circle centered at the origin with radius circleR. +// This relies on having a half kernel computed for the Gaussian and a table of applications of +// the half kernel in y to columns at (evalX - halfKernel, evalX - halfKernel + 1, ..., evalX + +// halfKernel) passed in as yKernelEvaluations. static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize, const float* yKernelEvaluations) { float acc = 0; @@ -83,18 +95,28 @@ static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int float verticalEval = yKernelEvaluations[i + halfKernelSize]; acc += verticalEval * halfKernel[i]; } - + // Since we applied a half kernel in y we multiply acc by 2 (the circle is symmetric about + // the x axis). return SkUnitScalarClampToByte(2.f * acc); } +// This function creates a profile of a blurred circle. It does this by computing a kernel for +// half the Gaussian and a matching summed area table. The summed area table is used to compute +// an array of vertical applications of the half kernel to the circle along the x axis. The +// table of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is +// the size of the profile being computed. Then for each of the n profile entries we walk out k +// steps in each horizontal direction multiplying the corresponding y evaluation by the half +// kernel entry and sum these values to compute the profile entry. static uint8_t* create_circle_profile(float sigma, float circleR, int profileTextureWidth) { const int numSteps = profileTextureWidth; uint8_t* weights = new uint8_t[numSteps]; + // The full kernel is 6 sigmas wide. int halfKernelSize = SkScalarCeilToInt(6.0f * sigma); - + // round up to next multiple of 2 and then divide by 2 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; + // Number of x steps at which to apply kernel in y to cover all the profile samples in x. int numYSteps = numSteps + 2 * halfKernelSize; SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); @@ -110,34 +132,36 @@ static uint8_t* create_circle_profile(float sigma, float circleR, int profileTex float evalX = i + 0.5f; weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); } - + // Ensure the tail of the Gaussian goes to zero. weights[numSteps - 1] = 0; return weights; } static uint8_t* create_half_plane_profile(int profileWidth) { SkASSERT(!(profileWidth & 0x1)); - + // The full kernel is 6 sigmas wide. float sigma = profileWidth / 6.f; int halfKernelSize = profileWidth / 2; SkAutoTArray<float> halfKernel(halfKernelSize); uint8_t* profile = new uint8_t[profileWidth]; + // The half kernel should sum to 0.5. const float tot = 2.f * make_unnormalized_half_kernel(halfKernel.get(), halfKernelSize, sigma); float sum = 0.f; - + // Populate the profile from the right edge to the middle. for (int i = 0; i < halfKernelSize; ++i) { halfKernel[halfKernelSize - i - 1] /= tot; sum += halfKernel[halfKernelSize - i - 1]; profile[profileWidth - i - 1] = SkUnitScalarClampToByte(sum); } - + // Populate the profile from the middle to the left edge (by flipping the half kernel and + // continuing the summation). for (int i = 0; i < halfKernelSize; ++i) { sum += halfKernel[i]; profile[halfKernelSize - i - 1] = SkUnitScalarClampToByte(sum); } - + // Ensure tail goes to 0. profile[profileWidth - 1] = 0; return profile; } @@ -146,9 +170,13 @@ static sk_sp<GrTextureProxy> create_profile_texture(GrResourceProvider* resource const SkRect& circle, float sigma, float* solidRadius, float* textureRadius) { float circleR = circle.width() / 2.0f; - + // Profile textures are cached by the ratio of sigma to circle radius and by the size of the + // profile texture (binned by powers of 2). SkScalar sigmaToCircleRRatio = sigma / circleR; - + // When sigma is really small this becomes a equivalent to convolving a Gaussian with a + // half-plane. Similarly, in the extreme high ratio cases circle becomes a point WRT to the + // Guassian and the profile texture is a just a Gaussian evaluation. However, we haven't yet + // implemented this latter optimization. sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f); SkFixed sigmaToCircleRRatioFixed; static const SkScalar kHalfPlaneThreshold = 0.1f; @@ -159,8 +187,10 @@ static sk_sp<GrTextureProxy> create_profile_texture(GrResourceProvider* resource *solidRadius = circleR - 3 * sigma; *textureRadius = 6 * sigma; } else { + // Convert to fixed point for the key. sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio); - + // We shave off some bits to reduce the number of unique entries. We could probably + // shave off more than we do. sigmaToCircleRRatioFixed &= ~0xff; sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed); sigma = circleR * sigmaToCircleRRatio; @@ -188,6 +218,7 @@ static sk_sp<GrTextureProxy> create_profile_texture(GrResourceProvider* resource if (useHalfPlaneApprox) { profile.reset(create_half_plane_profile(kProfileTextureWidth)); } else { + // Rescale params to the size of the texture we're creating. SkScalar scale = kProfileTextureWidth / *textureRadius; profile.reset( create_circle_profile(sigma * scale, circleR * scale, kProfileTextureWidth)); |