• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkPoint.h"
9 #include "include/core/SkTypes.h"
10 #include "src/base/SkZip.h"
11 #include "src/core/SkEnumerate.h"
12 #include "src/core/SkGlyph.h"
13 #include "src/core/SkGlyphBuffer.h"
14 #include "tests/Test.h"
15 
16 #include <cstddef>
17 #include <cstdint>
18 #include <initializer_list>
19 #include <tuple>
20 
DEF_TEST(SkPackedGlyphIDCtor,reporter)21 DEF_TEST(SkPackedGlyphIDCtor, reporter) {
22     using PG = SkPackedGlyphID;
23     // x and y are in one quarter the sub-pixel sampling frequency.
24     // Number of steps on the interval [0, 1)
25     const int perUnit = 1u << (PG::kSubPixelPosLen + 2);
26     const float step = 1.f / perUnit;
27     const int testLimit = 2 * perUnit;
28     auto freqRound = [](uint32_t x) -> uint32_t {
29         return ((x + 2) >> 2) & PG::kSubPixelPosMask;
30     };
31 
32     {
33         // Normal sub-pixel with y-axis snapping.
34         auto roundingSpec = SkGlyphPositionRoundingSpec(true, SkAxisAlignment::kX);
35         SkIPoint mask = roundingSpec.ignorePositionFieldMask;
36         for (int x = -testLimit; x < testLimit; x++) {
37             float fx = x * step;
38             SkPoint roundedPos = SkPoint{fx, 0} + roundingSpec.halfAxisSampleFreq;
39             SkPackedGlyphID packedID{3, roundedPos, mask};
40             uint32_t subX = freqRound(x);
41             uint32_t subY = 0;
42             SkPackedGlyphID correctID(3, subX, subY);
43             SkASSERT(packedID == correctID);
44             REPORTER_ASSERT(reporter, packedID == correctID);
45         }
46     }
47 
48     {
49         // No subpixel positioning.
50         auto roundingSpec = SkGlyphPositionRoundingSpec(false, SkAxisAlignment::kNone);
51         SkIPoint mask = roundingSpec.ignorePositionFieldMask;
52         for (int y = -testLimit; y < testLimit; y++) {
53             for (int x = -testLimit; x < testLimit; x++) {
54                 float fx = x * step, fy = y * step;
55                 SkPoint roundedPos = SkPoint{fx, fy} + roundingSpec.halfAxisSampleFreq;
56                 SkPackedGlyphID packedID{3, roundedPos, mask};
57                 uint32_t subX = 0;
58                 uint32_t subY = 0;
59                 SkPackedGlyphID correctID(3, subX, subY);
60                 REPORTER_ASSERT(reporter, packedID == correctID);
61             }
62         }
63     }
64 
65     {
66         // Subpixel with no axis snapping.
67         auto roundingSpec = SkGlyphPositionRoundingSpec(true, SkAxisAlignment::kNone);
68         SkIPoint mask = roundingSpec.ignorePositionFieldMask;
69         for (int y = -testLimit; y < testLimit; y++) {
70             for (int x = -testLimit; x < testLimit; x++) {
71                 float fx = x * step, fy = y * step;
72                 SkPoint roundedPos = SkPoint{fx, fy} + roundingSpec.halfAxisSampleFreq;
73                 SkPackedGlyphID packedID{3, roundedPos, mask};
74                 uint32_t subX = freqRound(x);
75                 uint32_t subY = freqRound(y);
76                 SkPackedGlyphID correctID(3, subX, subY);
77                 REPORTER_ASSERT(reporter, packedID == correctID);
78             }
79         }
80     }
81 
82     {
83         // Test dynamic range by transposing a large distance.
84         // Floating point numbers have 24 bits of precision. The largest distance is 24 - 2 (for
85         // sub-pixel) - 1 (for truncation to floor trick in the code). This leaves 21 bits. Large
86         // Distance is 2^21 - 2 (because the test is on the interval [-2, 2).
87         const uint32_t kLogLargeDistance = 24 - PG::kSubPixelPosLen - 1;
88         const int64_t kLargeDistance = (1ull << kLogLargeDistance) - 2;
89         auto roundingSpec = SkGlyphPositionRoundingSpec(true, SkAxisAlignment::kNone);
90         SkIPoint mask = roundingSpec.ignorePositionFieldMask;
91         for (int y = -32; y < 33; y++) {
92             for (int x = -32; x < 33; x++) {
93                 float fx = x * step + kLargeDistance, fy = y * step + kLargeDistance;
94                 SkPoint roundedPos = SkPoint{fx, fy} + roundingSpec.halfAxisSampleFreq;
95                 SkPackedGlyphID packedID{3, roundedPos, mask};
96                 uint32_t subX = freqRound(x);
97                 uint32_t subY = freqRound(y);
98                 SkPackedGlyphID correctID(3, subX, subY);
99                 REPORTER_ASSERT(reporter, packedID == correctID);
100             }
101         }
102     }
103 }
104 
DEF_TEST(SkSourceGlyphBufferBasic,reporter)105 DEF_TEST(SkSourceGlyphBufferBasic, reporter) {
106     SkSourceGlyphBuffer rejects;
107     // Positions are picked to avoid precision problems.
108     const SkPoint positions[] = {{10.25,10.25}, {20.5,10.25}, {30.75,10.25}, {40,10.25}};
109     const SkGlyphID glyphIDs[] = {1, 2, 3, 4};
110     auto source = SkMakeZip(glyphIDs, positions);
111 
112     rejects.setSource(source);
113     for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
114         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[i]));
115         REPORTER_ASSERT(reporter, pos == std::get<1>(source[i]));
116     }
117     // Reject a couple of glyphs.
118     rejects.reject(1);
119     rejects.reject(2);
120     rejects.flipRejectsToSource();
121     for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
122         // This will index 1 and 2 from the original source.
123         size_t j = i + 1;
124         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
125         REPORTER_ASSERT(reporter, pos == std::get<1>(source[j]));
126     }
127 
128     // Reject an additional glyph
129     rejects.reject(0);
130     rejects.flipRejectsToSource();
131     for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
132         // This will index 1 from the original source.
133         size_t j = i + 1;
134         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
135         REPORTER_ASSERT(reporter, pos == std::get<1>(source[j]));
136     }
137 
138     // Start all over
139     rejects.setSource(source);
140     for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
141         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[i]));
142         REPORTER_ASSERT(reporter, pos == std::get<1>(source[i]));
143     }
144 
145     // Check that everything is working after calling setSource.
146     rejects.reject(1);
147     rejects.reject(2);
148     rejects.flipRejectsToSource();
149     for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
150         // This will index 1 and 2 from the original source.
151         size_t j = i + 1;
152         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
153         REPORTER_ASSERT(reporter, pos == std::get<1>(source[j]));
154     }
155 }
156 
DEF_TEST(SkDrawableGlyphBufferBasic,reporter)157 DEF_TEST(SkDrawableGlyphBufferBasic, reporter) {
158     // Positions are picked to avoid precision problems.
159     const SkPoint positions[] = {{10.25,10.25}, {20.5,10.25}, {30.75,10.25}, {40,10.25}};
160     const SkGlyphID glyphIDs[] = {1, 2, 3, 4};
161     SkGlyph glyphs[100];
162     auto source = SkMakeZip(glyphIDs, positions);
163 
164     {
165         SkDrawableGlyphBuffer accepted;
166         accepted.ensureSize(100);
167         accepted.startSource(source);
168         for (auto [i, packedID, pos] : SkMakeEnumerate(accepted.input())) {
169             REPORTER_ASSERT(reporter, packedID.glyphID() == glyphIDs[i]);
170             REPORTER_ASSERT(reporter, pos == positions[i]);
171         }
172     }
173 }
174