blob: 027fd2ac76c7aa514dda22c1043ba7d8413b92c1 [file] [log] [blame]
fmalitac6765d62014-08-21 15:03:04 -07001/*
2 * Copyright 2014 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 "SkPaint.h"
9#include "SkPoint.h"
10#include "SkTextBlob.h"
11
12#include "Test.h"
13
fmalitac6765d62014-08-21 15:03:04 -070014class TextBlobTester {
15public:
fmalita51bf9572014-08-22 07:50:45 -070016 // This unit test feeds an SkTextBlobBuilder various runs then checks to see if
17 // the result contains the provided data and merges runs when appropriate.
18 static void TestBuilder(skiatest::Reporter* reporter) {
fmalitac6765d62014-08-21 15:03:04 -070019 SkTextBlobBuilder builder;
20
21 // empty run set
halcanary96fcdcc2015-08-27 07:41:13 -070022 RunBuilderTest(reporter, builder, nullptr, 0, nullptr, 0);
fmalitac6765d62014-08-21 15:03:04 -070023
fmalita51bf9572014-08-22 07:50:45 -070024 RunDef set1[] = {
fmalitac6765d62014-08-21 15:03:04 -070025 { 128, SkTextBlob::kDefault_Positioning, 100, 100 },
26 };
fmalita51bf9572014-08-22 07:50:45 -070027 RunBuilderTest(reporter, builder, set1, SK_ARRAY_COUNT(set1), set1, SK_ARRAY_COUNT(set1));
fmalitac6765d62014-08-21 15:03:04 -070028
fmalita51bf9572014-08-22 07:50:45 -070029 RunDef set2[] = {
fmalitac6765d62014-08-21 15:03:04 -070030 { 128, SkTextBlob::kHorizontal_Positioning, 100, 100 },
31 };
fmalita51bf9572014-08-22 07:50:45 -070032 RunBuilderTest(reporter, builder, set2, SK_ARRAY_COUNT(set2), set2, SK_ARRAY_COUNT(set2));
fmalitac6765d62014-08-21 15:03:04 -070033
fmalita51bf9572014-08-22 07:50:45 -070034 RunDef set3[] = {
fmalitac6765d62014-08-21 15:03:04 -070035 { 128, SkTextBlob::kFull_Positioning, 100, 100 },
36 };
fmalita51bf9572014-08-22 07:50:45 -070037 RunBuilderTest(reporter, builder, set3, SK_ARRAY_COUNT(set3), set3, SK_ARRAY_COUNT(set3));
fmalitac6765d62014-08-21 15:03:04 -070038
fmalita51bf9572014-08-22 07:50:45 -070039 RunDef set4[] = {
fmalitac6765d62014-08-21 15:03:04 -070040 { 128, SkTextBlob::kDefault_Positioning, 100, 150 },
41 { 128, SkTextBlob::kDefault_Positioning, 100, 150 },
42 { 128, SkTextBlob::kDefault_Positioning, 100, 150 },
43 };
fmalita51bf9572014-08-22 07:50:45 -070044 RunBuilderTest(reporter, builder, set4, SK_ARRAY_COUNT(set4), set4, SK_ARRAY_COUNT(set4));
fmalitac6765d62014-08-21 15:03:04 -070045
fmalita51bf9572014-08-22 07:50:45 -070046 RunDef set5[] = {
fmalitac6765d62014-08-21 15:03:04 -070047 { 128, SkTextBlob::kHorizontal_Positioning, 100, 150 },
48 { 128, SkTextBlob::kHorizontal_Positioning, 200, 150 },
49 { 128, SkTextBlob::kHorizontal_Positioning, 300, 250 },
50 };
fmalita51bf9572014-08-22 07:50:45 -070051 RunDef mergedSet5[] = {
fmalitac6765d62014-08-21 15:03:04 -070052 { 256, SkTextBlob::kHorizontal_Positioning, 0, 150 },
53 { 128, SkTextBlob::kHorizontal_Positioning, 0, 250 },
54 };
fmalita51bf9572014-08-22 07:50:45 -070055 RunBuilderTest(reporter, builder, set5, SK_ARRAY_COUNT(set5), mergedSet5,
56 SK_ARRAY_COUNT(mergedSet5));
fmalitac6765d62014-08-21 15:03:04 -070057
fmalita51bf9572014-08-22 07:50:45 -070058 RunDef set6[] = {
fmalitac6765d62014-08-21 15:03:04 -070059 { 128, SkTextBlob::kFull_Positioning, 100, 100 },
60 { 128, SkTextBlob::kFull_Positioning, 200, 200 },
61 { 128, SkTextBlob::kFull_Positioning, 300, 300 },
62 };
fmalita51bf9572014-08-22 07:50:45 -070063 RunDef mergedSet6[] = {
fmalitac6765d62014-08-21 15:03:04 -070064 { 384, SkTextBlob::kFull_Positioning, 0, 0 },
65 };
fmalita51bf9572014-08-22 07:50:45 -070066 RunBuilderTest(reporter, builder, set6, SK_ARRAY_COUNT(set6), mergedSet6,
67 SK_ARRAY_COUNT(mergedSet6));
fmalitac6765d62014-08-21 15:03:04 -070068
fmalita51bf9572014-08-22 07:50:45 -070069 RunDef set7[] = {
fmalitac6765d62014-08-21 15:03:04 -070070 { 128, SkTextBlob::kDefault_Positioning, 100, 150 },
71 { 128, SkTextBlob::kDefault_Positioning, 100, 150 },
72 { 128, SkTextBlob::kHorizontal_Positioning, 100, 150 },
73 { 128, SkTextBlob::kHorizontal_Positioning, 200, 150 },
74 { 128, SkTextBlob::kFull_Positioning, 400, 350 },
75 { 128, SkTextBlob::kFull_Positioning, 400, 350 },
76 { 128, SkTextBlob::kDefault_Positioning, 100, 450 },
77 { 128, SkTextBlob::kDefault_Positioning, 100, 450 },
78 { 128, SkTextBlob::kHorizontal_Positioning, 100, 550 },
79 { 128, SkTextBlob::kHorizontal_Positioning, 200, 650 },
80 { 128, SkTextBlob::kFull_Positioning, 400, 750 },
81 { 128, SkTextBlob::kFull_Positioning, 400, 850 },
82 };
fmalita51bf9572014-08-22 07:50:45 -070083 RunDef mergedSet7[] = {
fmalitac6765d62014-08-21 15:03:04 -070084 { 128, SkTextBlob::kDefault_Positioning, 100, 150 },
85 { 128, SkTextBlob::kDefault_Positioning, 100, 150 },
86 { 256, SkTextBlob::kHorizontal_Positioning, 0, 150 },
87 { 256, SkTextBlob::kFull_Positioning, 0, 0 },
88 { 128, SkTextBlob::kDefault_Positioning, 100, 450 },
89 { 128, SkTextBlob::kDefault_Positioning, 100, 450 },
90 { 128, SkTextBlob::kHorizontal_Positioning, 0, 550 },
91 { 128, SkTextBlob::kHorizontal_Positioning, 0, 650 },
92 { 256, SkTextBlob::kFull_Positioning, 0, 0 },
93 };
fmalita51bf9572014-08-22 07:50:45 -070094 RunBuilderTest(reporter, builder, set7, SK_ARRAY_COUNT(set7), mergedSet7,
95 SK_ARRAY_COUNT(mergedSet7));
96 }
97
98 // This unit test verifies blob bounds computation.
99 static void TestBounds(skiatest::Reporter* reporter) {
100 SkTextBlobBuilder builder;
101 SkPaint font;
102 font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
103
104 // Explicit bounds.
105 {
106 SkAutoTUnref<const SkTextBlob> blob(builder.build());
107 REPORTER_ASSERT(reporter, blob->bounds().isEmpty());
108 }
109
110 {
111 SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
112 builder.allocRun(font, 16, 0, 0, &r1);
113 SkAutoTUnref<const SkTextBlob> blob(builder.build());
114 REPORTER_ASSERT(reporter, blob->bounds() == r1);
115 }
116
117 {
118 SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
119 builder.allocRunPosH(font, 16, 0, &r1);
120 SkAutoTUnref<const SkTextBlob> blob(builder.build());
121 REPORTER_ASSERT(reporter, blob->bounds() == r1);
122 }
123
124 {
125 SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
126 builder.allocRunPos(font, 16, &r1);
127 SkAutoTUnref<const SkTextBlob> blob(builder.build());
128 REPORTER_ASSERT(reporter, blob->bounds() == r1);
129 }
130
131 {
132 SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
133 SkRect r2 = SkRect::MakeXYWH(15, 20, 50, 50);
134 SkRect r3 = SkRect::MakeXYWH(0, 5, 10, 5);
135
136 builder.allocRun(font, 16, 0, 0, &r1);
137 builder.allocRunPosH(font, 16, 0, &r2);
138 builder.allocRunPos(font, 16, &r3);
139
140 SkAutoTUnref<const SkTextBlob> blob(builder.build());
141 REPORTER_ASSERT(reporter, blob->bounds() == SkRect::MakeXYWH(0, 5, 65, 65));
142 }
143
144 {
145 // Verify empty blob bounds after building some non-empty blobs.
146 SkAutoTUnref<const SkTextBlob> blob(builder.build());
147 REPORTER_ASSERT(reporter, blob->bounds().isEmpty());
148 }
149
150 // Implicit bounds
fmalita9ae8fe12015-10-13 08:59:23 -0700151
152 {
153 // Exercise the empty bounds path, and ensure that RunRecord-aligned pos buffers
154 // don't trigger asserts (http://crbug.com/542643).
155 SkPaint p;
156 p.setTextSize(0);
fmalita0b01da72015-10-14 08:11:40 -0700157 p.setTextEncoding(SkPaint::kUTF8_TextEncoding);
fmalita9ae8fe12015-10-13 08:59:23 -0700158
159 const char* txt = "BOOO";
fmalita0b01da72015-10-14 08:11:40 -0700160 const size_t txtLen = strlen(txt);
161 const int glyphCount = p.textToGlyphs(txt, txtLen, nullptr);
162
163 p.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
164 const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(p, glyphCount);
165
166 p.setTextEncoding(SkPaint::kUTF8_TextEncoding);
167 p.textToGlyphs(txt, txtLen, buffer.glyphs);
168
169 memset(buffer.pos, 0, sizeof(SkScalar) * glyphCount * 2);
fmalita9ae8fe12015-10-13 08:59:23 -0700170 SkAutoTUnref<const SkTextBlob> blob(builder.build());
171 REPORTER_ASSERT(reporter, blob->bounds().isEmpty());
172 }
fmalitac6765d62014-08-21 15:03:04 -0700173 }
174
175private:
176 struct RunDef {
177 unsigned count;
178 SkTextBlob::GlyphPositioning pos;
179 SkScalar x, y;
180 };
181
fmalita51bf9572014-08-22 07:50:45 -0700182 static void RunBuilderTest(skiatest::Reporter* reporter, SkTextBlobBuilder& builder,
fmalitac6765d62014-08-21 15:03:04 -0700183 const RunDef in[], unsigned inCount,
184 const RunDef out[], unsigned outCount) {
fmalita51bf9572014-08-22 07:50:45 -0700185 SkPaint font;
186 font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
187
fmalitac6765d62014-08-21 15:03:04 -0700188 unsigned glyphCount = 0;
189 unsigned posCount = 0;
190
191 for (unsigned i = 0; i < inCount; ++i) {
fmalita51bf9572014-08-22 07:50:45 -0700192 AddRun(font, in[i].count, in[i].pos, SkPoint::Make(in[i].x, in[i].y), builder);
fmalitac6765d62014-08-21 15:03:04 -0700193 glyphCount += in[i].count;
194 posCount += in[i].count * in[i].pos;
195 }
196
197 SkAutoTUnref<const SkTextBlob> blob(builder.build());
fmalitac6765d62014-08-21 15:03:04 -0700198
199 SkTextBlob::RunIterator it(blob);
200 for (unsigned i = 0; i < outCount; ++i) {
201 REPORTER_ASSERT(reporter, !it.done());
202 REPORTER_ASSERT(reporter, out[i].pos == it.positioning());
203 REPORTER_ASSERT(reporter, out[i].count == it.glyphCount());
204 if (SkTextBlob::kDefault_Positioning == out[i].pos) {
205 REPORTER_ASSERT(reporter, out[i].x == it.offset().x());
206 REPORTER_ASSERT(reporter, out[i].y == it.offset().y());
207 } else if (SkTextBlob::kHorizontal_Positioning == out[i].pos) {
208 REPORTER_ASSERT(reporter, out[i].y == it.offset().y());
209 }
210
211 for (unsigned k = 0; k < it.glyphCount(); ++k) {
212 REPORTER_ASSERT(reporter, k % 128 == it.glyphs()[k]);
213 if (SkTextBlob::kHorizontal_Positioning == it.positioning()) {
214 REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k]);
215 } else if (SkTextBlob::kFull_Positioning == it.positioning()) {
216 REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k * 2]);
217 REPORTER_ASSERT(reporter, -SkIntToScalar(k % 128) == it.pos()[k * 2 + 1]);
218 }
219 }
220
221 it.next();
222 }
223
224 REPORTER_ASSERT(reporter, it.done());
225 }
226
fmalita51bf9572014-08-22 07:50:45 -0700227 static void AddRun(const SkPaint& font, int count, SkTextBlob::GlyphPositioning pos,
fmalitac6765d62014-08-21 15:03:04 -0700228 const SkPoint& offset, SkTextBlobBuilder& builder,
halcanary96fcdcc2015-08-27 07:41:13 -0700229 const SkRect* bounds = nullptr) {
fmalitac6765d62014-08-21 15:03:04 -0700230 switch (pos) {
231 case SkTextBlob::kDefault_Positioning: {
232 const SkTextBlobBuilder::RunBuffer& rb = builder.allocRun(font, count, offset.x(),
233 offset.y(), bounds);
234 for (int i = 0; i < count; ++i) {
235 rb.glyphs[i] = i;
236 }
237 } break;
238 case SkTextBlob::kHorizontal_Positioning: {
239 const SkTextBlobBuilder::RunBuffer& rb = builder.allocRunPosH(font, count, offset.y(),
240 bounds);
241 for (int i = 0; i < count; ++i) {
242 rb.glyphs[i] = i;
243 rb.pos[i] = SkIntToScalar(i);
244 }
245 } break;
246 case SkTextBlob::kFull_Positioning: {
247 const SkTextBlobBuilder::RunBuffer& rb = builder.allocRunPos(font, count, bounds);
248 for (int i = 0; i < count; ++i) {
249 rb.glyphs[i] = i;
250 rb.pos[i * 2] = SkIntToScalar(i);
251 rb.pos[i * 2 + 1] = -SkIntToScalar(i);
252 }
253 } break;
254 default:
255 SkFAIL("unhandled positioning value");
256 }
257 }
258};
259
260DEF_TEST(TextBlob_builder, reporter) {
fmalita51bf9572014-08-22 07:50:45 -0700261 TextBlobTester::TestBuilder(reporter);
262 TextBlobTester::TestBounds(reporter);
fmalitac6765d62014-08-21 15:03:04 -0700263}