2011-07-28 14:26:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright 2011 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
2011-09-27 17:38:17 +00:00
|
|
|
#include "SkMath.h"
|
2011-06-08 14:46:28 +00:00
|
|
|
#include "SkPoint.h"
|
|
|
|
#include "SkScalar.h"
|
|
|
|
#include "Test.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
Duplicates lots of code from gpu/src/GrPathUtils.cpp
|
2011-06-24 15:43:24 +00:00
|
|
|
It'd be nice not to do so, but that code's set up currently to only have
|
|
|
|
a single implementation.
|
2011-06-08 14:46:28 +00:00
|
|
|
*/
|
|
|
|
|
2011-06-24 15:43:24 +00:00
|
|
|
// Sk uses 6, Gr (implicitly) used 10, both apparently arbitrarily.
|
2011-06-08 14:46:28 +00:00
|
|
|
#define MAX_COEFF_SHIFT 6
|
|
|
|
static const uint32_t MAX_POINTS_PER_CURVE = 1 << MAX_COEFF_SHIFT;
|
|
|
|
|
2011-06-24 15:43:24 +00:00
|
|
|
// max + 0.5 min has error [0.0, 0.12]
|
|
|
|
// max + 0.375 min has error [-.03, 0.07]
|
|
|
|
// 0.96043387 max + 0.397824735 min has error [-.06, +.05]
|
|
|
|
// For determining the maximum possible number of points to use in
|
|
|
|
// drawing a quadratic, we want to err on the high side.
|
2011-06-08 14:46:28 +00:00
|
|
|
static inline int cheap_distance(SkScalar dx, SkScalar dy) {
|
|
|
|
int idx = SkAbs32(SkScalarRound(dx));
|
|
|
|
int idy = SkAbs32(SkScalarRound(dy));
|
|
|
|
if (idx > idy) {
|
|
|
|
idx += idy >> 1;
|
|
|
|
} else {
|
|
|
|
idx = idy + (idx >> 1);
|
|
|
|
}
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2011-06-24 15:43:24 +00:00
|
|
|
static inline int estimate_distance(const SkPoint points[]) {
|
|
|
|
return cheap_distance(points[1].fX * 2 - points[2].fX - points[0].fX,
|
|
|
|
points[1].fY * 2 - points[2].fY - points[0].fY);
|
2011-06-08 14:46:28 +00:00
|
|
|
}
|
|
|
|
|
2011-06-24 15:43:24 +00:00
|
|
|
static inline SkScalar compute_distance(const SkPoint points[]) {
|
|
|
|
return points[1].distanceToLineSegmentBetween(points[0], points[2]);
|
|
|
|
}
|
2011-06-08 14:46:28 +00:00
|
|
|
|
2011-06-24 15:43:24 +00:00
|
|
|
static inline uint32_t estimate_pointCount(int distance) {
|
|
|
|
// Includes -2 bias because this estimator runs 4x high?
|
|
|
|
int shift = 30 - SkCLZ(distance);
|
|
|
|
// Clamp to zero if above subtraction went negative.
|
|
|
|
shift &= ~(shift>>31);
|
2011-06-08 14:46:28 +00:00
|
|
|
if (shift > MAX_COEFF_SHIFT) {
|
|
|
|
shift = MAX_COEFF_SHIFT;
|
|
|
|
}
|
2011-06-24 15:43:24 +00:00
|
|
|
return 1 << shift;
|
2011-06-08 14:46:28 +00:00
|
|
|
}
|
|
|
|
|
2011-06-24 15:43:24 +00:00
|
|
|
static inline uint32_t compute_pointCount(SkScalar d, SkScalar tol) {
|
2011-06-08 14:46:28 +00:00
|
|
|
if (d < tol) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
2012-04-16 15:50:18 +00:00
|
|
|
int temp = SkScalarCeilToInt(SkScalarSqrt(SkScalarDiv(d, tol)));
|
|
|
|
uint32_t count = SkMin32(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
|
2011-06-08 14:46:28 +00:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-24 15:43:24 +00:00
|
|
|
uint32_t quadraticPointCount_EE(const SkPoint points[], SkScalar tol) {
|
|
|
|
int distance = estimate_distance(points);
|
|
|
|
return estimate_pointCount(distance);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t quadraticPointCount_EC(const SkPoint points[], SkScalar tol) {
|
|
|
|
int distance = estimate_distance(points);
|
|
|
|
return compute_pointCount(SkIntToScalar(distance), tol);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t quadraticPointCount_CE(const SkPoint points[], SkScalar tol) {
|
|
|
|
SkScalar distance = compute_distance(points);
|
|
|
|
return estimate_pointCount(SkScalarRound(distance));
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t quadraticPointCount_CC(const SkPoint points[], SkScalar tol) {
|
|
|
|
SkScalar distance = compute_distance(points);
|
|
|
|
return compute_pointCount(distance, tol);
|
|
|
|
}
|
|
|
|
|
2011-06-08 14:46:28 +00:00
|
|
|
// Curve from samplecode/SampleSlides.cpp
|
|
|
|
static const int gXY[] = {
|
|
|
|
4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int gSawtooth[] = {
|
|
|
|
0, 0, 10, 10, 20, 20, 30, 10, 40, 0, 50, -10, 60, -20, 70, -10, 80, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int gOvalish[] = {
|
|
|
|
0, 0, 5, 15, 20, 20, 35, 15, 40, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int gSharpSawtooth[] = {
|
|
|
|
0, 0, 1, 10, 2, 0, 3, -10, 4, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
// Curve crosses back over itself around 0,10
|
|
|
|
static const int gRibbon[] = {
|
|
|
|
-4, 0, 4, 20, 0, 25, -4, 20, 4, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool one_d_pe(const int* array, const unsigned int count,
|
|
|
|
skiatest::Reporter* reporter) {
|
|
|
|
SkPoint path [3];
|
|
|
|
path[1] = SkPoint::Make(SkIntToScalar(array[0]), SkIntToScalar(array[1]));
|
|
|
|
path[2] = SkPoint::Make(SkIntToScalar(array[2]), SkIntToScalar(array[3]));
|
|
|
|
int numErrors = 0;
|
2011-06-24 15:43:24 +00:00
|
|
|
for (unsigned i = 4; i < count; i += 2) {
|
2011-06-08 14:46:28 +00:00
|
|
|
path[0] = path[1];
|
|
|
|
path[1] = path[2];
|
|
|
|
path[2] = SkPoint::Make(SkIntToScalar(array[i]),
|
|
|
|
SkIntToScalar(array[i+1]));
|
|
|
|
uint32_t computedCount =
|
2011-06-24 15:43:24 +00:00
|
|
|
quadraticPointCount_CC(path, SkIntToScalar(1));
|
2011-06-08 14:46:28 +00:00
|
|
|
uint32_t estimatedCount =
|
2011-06-24 15:43:24 +00:00
|
|
|
quadraticPointCount_EE(path, SkIntToScalar(1));
|
|
|
|
// Allow estimated to be high by a factor of two, but no less than
|
|
|
|
// the computed value.
|
|
|
|
bool isAccurate = (estimatedCount >= computedCount) &&
|
|
|
|
(estimatedCount <= 2 * computedCount);
|
|
|
|
|
|
|
|
if (!isAccurate) {
|
2011-06-08 14:46:28 +00:00
|
|
|
SkString errorDescription;
|
|
|
|
errorDescription.printf(
|
|
|
|
"Curve from %.2f %.2f through %.2f %.2f to %.2f %.2f "
|
|
|
|
"computes %d, estimates %d\n",
|
|
|
|
path[0].fX, path[0].fY, path[1].fX, path[1].fY,
|
|
|
|
path[2].fX, path[2].fY, computedCount, estimatedCount);
|
|
|
|
numErrors++;
|
|
|
|
reporter->reportFailed(errorDescription);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (numErrors == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void TestQuadPointCount(skiatest::Reporter* reporter) {
|
|
|
|
one_d_pe(gXY, SK_ARRAY_COUNT(gXY), reporter);
|
|
|
|
one_d_pe(gSawtooth, SK_ARRAY_COUNT(gSawtooth), reporter);
|
|
|
|
one_d_pe(gOvalish, SK_ARRAY_COUNT(gOvalish), reporter);
|
|
|
|
one_d_pe(gSharpSawtooth, SK_ARRAY_COUNT(gSharpSawtooth), reporter);
|
|
|
|
one_d_pe(gRibbon, SK_ARRAY_COUNT(gRibbon), reporter);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestPathCoverage(skiatest::Reporter* reporter) {
|
|
|
|
TestQuadPointCount(reporter);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "TestClassDef.h"
|
|
|
|
DEFINE_TESTCLASS("PathCoverage", PathCoverageTestClass, TestPathCoverage)
|