aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/effects
diff options
context:
space:
mode:
authorGravatar Ethan Nicholas <ethannicholas@google.com>2017-09-07 15:44:01 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-09-11 16:17:00 +0000
commitc576e93d174f3106e072a2f506bca3990b541265 (patch)
treed4a410200aa71183c95643535b440bec919f2e18 /src/effects
parenta2bdf005f3c706065d1aa93f319f4b73932721d4 (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.cpp2
-rw-r--r--src/effects/GrCircleBlurFragmentProcessor.cpp55
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));