1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
// Unit tests for src/core/SkPoint.cpp and its header
#include "SkPoint.h"
#include "SkRect.h"
#include "Test.h"
static void test_casts(skiatest::Reporter* reporter) {
SkPoint p = { 0, 0 };
SkRect r = { 0, 0, 0, 0 };
const SkScalar* pPtr = SkTCast<const SkScalar*>(&p);
const SkScalar* rPtr = SkTCast<const SkScalar*>(&r);
REPORTER_ASSERT(reporter, p.asScalars() == pPtr);
REPORTER_ASSERT(reporter, r.asScalars() == rPtr);
}
// Tests SkPoint::Normalize() for this (x,y)
static void test_Normalize(skiatest::Reporter* reporter,
SkScalar x, SkScalar y) {
SkPoint point;
point.set(x, y);
SkScalar oldLength = point.length();
SkScalar returned = SkPoint::Normalize(&point);
SkScalar newLength = point.length();
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(returned, oldLength));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, SK_Scalar1));
}
// Tests that SkPoint::length() and SkPoint::Length() both return
// approximately expectedLength for this (x,y).
static void test_length(skiatest::Reporter* reporter, SkScalar x, SkScalar y,
SkScalar expectedLength) {
SkPoint point;
point.set(x, y);
SkScalar s1 = point.length();
SkScalar s2 = SkPoint::Length(x, y);
//The following should be exactly the same, but need not be.
//See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, s2));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, expectedLength));
test_Normalize(reporter, x, y);
}
// Ugh. Windows compiler can dive into other .cpp files, and sometimes
// notices that I will generate an overflow... which is exactly the point
// of this test!
//
// To avoid this warning, I need to convince the compiler that I might not
// use that big value, hence this hacky helper function: reporter is
// ALWAYS non-null. (shhhhhh, don't tell the compiler that).
template <typename T> T get_value(skiatest::Reporter* reporter, T value) {
return reporter ? value : 0;
}
// On linux gcc, 32bit, we are seeing the compiler propagate up the value
// of SkPoint::length() as a double (which we use sometimes to avoid overflow
// during the computation), even though the signature says float (SkScalar).
//
// force_as_float is meant to capture our latest technique (horrible as
// it is) to force the value to be a float, so we can test whether it was
// finite or not.
static float force_as_float(skiatest::Reporter* reporter, float value) {
uint32_t storage;
memcpy(&storage, &value, 4);
// even the pair of memcpy calls are not sufficient, since those seem to
// be no-op'd, so we add a runtime tests (just like get_value) to force
// the compiler to give us an actual float.
if (nullptr == reporter) {
storage = ~storage;
}
memcpy(&value, &storage, 4);
return value;
}
// test that we handle very large values correctly. i.e. that we can
// successfully normalize something whose mag overflows a float.
static void test_overflow(skiatest::Reporter* reporter) {
SkScalar bigFloat = get_value(reporter, 3.4e38f);
SkPoint pt = { bigFloat, bigFloat };
SkScalar length = pt.length();
length = force_as_float(reporter, length);
// expect this to be non-finite, but dump the results if not.
if (SkScalarIsFinite(length)) {
SkDebugf("length(%g, %g) == %g\n", pt.fX, pt.fY, length);
REPORTER_ASSERT(reporter, !SkScalarIsFinite(length));
}
// this should succeed, even though we can't represent length
REPORTER_ASSERT(reporter, pt.setLength(SK_Scalar1));
// now that pt is normalized, we check its length
length = pt.length();
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(length, SK_Scalar1));
}
// test that we handle very small values correctly. i.e. that we can
// report failure if we try to normalize them.
static void test_underflow(skiatest::Reporter* reporter) {
SkPoint pt = { 1.0e-37f, 1.0e-37f };
const SkPoint empty = { 0, 0 };
REPORTER_ASSERT(reporter, 0 == SkPoint::Normalize(&pt));
REPORTER_ASSERT(reporter, pt == empty);
REPORTER_ASSERT(reporter, !pt.setLength(SK_Scalar1));
REPORTER_ASSERT(reporter, pt == empty);
}
DEF_TEST(Point, reporter) {
test_casts(reporter);
static const struct {
SkScalar fX;
SkScalar fY;
SkScalar fLength;
} gRec[] = {
{ SkIntToScalar(3), SkIntToScalar(4), SkIntToScalar(5) },
{ 0.6f, 0.8f, SK_Scalar1 },
};
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
test_length(reporter, gRec[i].fX, gRec[i].fY, gRec[i].fLength);
}
test_underflow(reporter);
test_overflow(reporter);
}
DEF_TEST(Point_setLengthFast, reporter) {
// Scale a (1,1) point to a bunch of different lengths,
// making sure the slow and fast paths are within 0.1%.
const float tests[] = { 1.0f, 0.0f, 1.0e-37f, 3.4e38f, 42.0f, 0.00012f };
const SkPoint kOne = {1.0f, 1.0f};
for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); i++) {
SkPoint slow = kOne, fast = kOne;
slow.setLength(tests[i]);
fast.setLengthFast(tests[i]);
if (slow.length() < FLT_MIN && fast.length() < FLT_MIN) continue;
SkScalar ratio = slow.length() / fast.length();
REPORTER_ASSERT(reporter, ratio > 0.999f);
REPORTER_ASSERT(reporter, ratio < 1.001f);
}
}
|