TextBlobTest.cpp revision bcfb8f639e516b673b6dbda41900efac69be2daf
1/* 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 "SkTextBlobRunIterator.h" 11#include "SkTypeface.h" 12 13#include "Test.h" 14 15class TextBlobTester { 16public: 17 // This unit test feeds an SkTextBlobBuilder various runs then checks to see if 18 // the result contains the provided data and merges runs when appropriate. 19 static void TestBuilder(skiatest::Reporter* reporter) { 20 SkTextBlobBuilder builder; 21 22 // empty run set 23 RunBuilderTest(reporter, builder, nullptr, 0, nullptr, 0); 24 25 RunDef set1[] = { 26 { 128, SkTextBlob::kDefault_Positioning, 100, 100 }, 27 }; 28 RunBuilderTest(reporter, builder, set1, SK_ARRAY_COUNT(set1), set1, SK_ARRAY_COUNT(set1)); 29 30 RunDef set2[] = { 31 { 128, SkTextBlob::kHorizontal_Positioning, 100, 100 }, 32 }; 33 RunBuilderTest(reporter, builder, set2, SK_ARRAY_COUNT(set2), set2, SK_ARRAY_COUNT(set2)); 34 35 RunDef set3[] = { 36 { 128, SkTextBlob::kFull_Positioning, 100, 100 }, 37 }; 38 RunBuilderTest(reporter, builder, set3, SK_ARRAY_COUNT(set3), set3, SK_ARRAY_COUNT(set3)); 39 40 RunDef set4[] = { 41 { 128, SkTextBlob::kDefault_Positioning, 100, 150 }, 42 { 128, SkTextBlob::kDefault_Positioning, 100, 150 }, 43 { 128, SkTextBlob::kDefault_Positioning, 100, 150 }, 44 }; 45 RunBuilderTest(reporter, builder, set4, SK_ARRAY_COUNT(set4), set4, SK_ARRAY_COUNT(set4)); 46 47 RunDef set5[] = { 48 { 128, SkTextBlob::kHorizontal_Positioning, 100, 150 }, 49 { 128, SkTextBlob::kHorizontal_Positioning, 200, 150 }, 50 { 128, SkTextBlob::kHorizontal_Positioning, 300, 250 }, 51 }; 52 RunDef mergedSet5[] = { 53 { 256, SkTextBlob::kHorizontal_Positioning, 0, 150 }, 54 { 128, SkTextBlob::kHorizontal_Positioning, 0, 250 }, 55 }; 56 RunBuilderTest(reporter, builder, set5, SK_ARRAY_COUNT(set5), mergedSet5, 57 SK_ARRAY_COUNT(mergedSet5)); 58 59 RunDef set6[] = { 60 { 128, SkTextBlob::kFull_Positioning, 100, 100 }, 61 { 128, SkTextBlob::kFull_Positioning, 200, 200 }, 62 { 128, SkTextBlob::kFull_Positioning, 300, 300 }, 63 }; 64 RunDef mergedSet6[] = { 65 { 384, SkTextBlob::kFull_Positioning, 0, 0 }, 66 }; 67 RunBuilderTest(reporter, builder, set6, SK_ARRAY_COUNT(set6), mergedSet6, 68 SK_ARRAY_COUNT(mergedSet6)); 69 70 RunDef set7[] = { 71 { 128, SkTextBlob::kDefault_Positioning, 100, 150 }, 72 { 128, SkTextBlob::kDefault_Positioning, 100, 150 }, 73 { 128, SkTextBlob::kHorizontal_Positioning, 100, 150 }, 74 { 128, SkTextBlob::kHorizontal_Positioning, 200, 150 }, 75 { 128, SkTextBlob::kFull_Positioning, 400, 350 }, 76 { 128, SkTextBlob::kFull_Positioning, 400, 350 }, 77 { 128, SkTextBlob::kDefault_Positioning, 100, 450 }, 78 { 128, SkTextBlob::kDefault_Positioning, 100, 450 }, 79 { 128, SkTextBlob::kHorizontal_Positioning, 100, 550 }, 80 { 128, SkTextBlob::kHorizontal_Positioning, 200, 650 }, 81 { 128, SkTextBlob::kFull_Positioning, 400, 750 }, 82 { 128, SkTextBlob::kFull_Positioning, 400, 850 }, 83 }; 84 RunDef mergedSet7[] = { 85 { 128, SkTextBlob::kDefault_Positioning, 100, 150 }, 86 { 128, SkTextBlob::kDefault_Positioning, 100, 150 }, 87 { 256, SkTextBlob::kHorizontal_Positioning, 0, 150 }, 88 { 256, SkTextBlob::kFull_Positioning, 0, 0 }, 89 { 128, SkTextBlob::kDefault_Positioning, 100, 450 }, 90 { 128, SkTextBlob::kDefault_Positioning, 100, 450 }, 91 { 128, SkTextBlob::kHorizontal_Positioning, 0, 550 }, 92 { 128, SkTextBlob::kHorizontal_Positioning, 0, 650 }, 93 { 256, SkTextBlob::kFull_Positioning, 0, 0 }, 94 }; 95 RunBuilderTest(reporter, builder, set7, SK_ARRAY_COUNT(set7), mergedSet7, 96 SK_ARRAY_COUNT(mergedSet7)); 97 } 98 99 // This unit test verifies blob bounds computation. 100 static void TestBounds(skiatest::Reporter* reporter) { 101 SkTextBlobBuilder builder; 102 SkPaint font; 103 font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 104 105 // Explicit bounds. 106 { 107 sk_sp<SkTextBlob> blob(builder.make()); 108 REPORTER_ASSERT(reporter, blob->bounds().isEmpty()); 109 } 110 111 { 112 SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); 113 builder.allocRun(font, 16, 0, 0, &r1); 114 sk_sp<SkTextBlob> blob(builder.make()); 115 REPORTER_ASSERT(reporter, blob->bounds() == r1); 116 } 117 118 { 119 SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); 120 builder.allocRunPosH(font, 16, 0, &r1); 121 sk_sp<SkTextBlob> blob(builder.make()); 122 REPORTER_ASSERT(reporter, blob->bounds() == r1); 123 } 124 125 { 126 SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); 127 builder.allocRunPos(font, 16, &r1); 128 sk_sp<SkTextBlob> blob(builder.make()); 129 REPORTER_ASSERT(reporter, blob->bounds() == r1); 130 } 131 132 { 133 SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); 134 SkRect r2 = SkRect::MakeXYWH(15, 20, 50, 50); 135 SkRect r3 = SkRect::MakeXYWH(0, 5, 10, 5); 136 137 builder.allocRun(font, 16, 0, 0, &r1); 138 builder.allocRunPosH(font, 16, 0, &r2); 139 builder.allocRunPos(font, 16, &r3); 140 141 sk_sp<SkTextBlob> blob(builder.make()); 142 REPORTER_ASSERT(reporter, blob->bounds() == SkRect::MakeXYWH(0, 5, 65, 65)); 143 } 144 145 { 146 // Verify empty blob bounds after building some non-empty blobs. 147 sk_sp<SkTextBlob> blob(builder.make()); 148 REPORTER_ASSERT(reporter, blob->bounds().isEmpty()); 149 } 150 151 // Implicit bounds 152 153 { 154 // Exercise the empty bounds path, and ensure that RunRecord-aligned pos buffers 155 // don't trigger asserts (http://crbug.com/542643). 156 SkPaint p; 157 p.setTextSize(0); 158 p.setTextEncoding(SkPaint::kUTF8_TextEncoding); 159 160 const char* txt = "BOOO"; 161 const size_t txtLen = strlen(txt); 162 const int glyphCount = p.textToGlyphs(txt, txtLen, nullptr); 163 164 p.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 165 const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(p, glyphCount); 166 167 p.setTextEncoding(SkPaint::kUTF8_TextEncoding); 168 p.textToGlyphs(txt, txtLen, buffer.glyphs); 169 170 memset(buffer.pos, 0, sizeof(SkScalar) * glyphCount * 2); 171 sk_sp<SkTextBlob> blob(builder.make()); 172 REPORTER_ASSERT(reporter, blob->bounds().isEmpty()); 173 } 174 } 175 176 // Verify that text-related properties are captured in run paints. 177 static void TestPaintProps(skiatest::Reporter* reporter) { 178 SkPaint font; 179 font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 180 181 // Kitchen sink font. 182 font.setTextSize(42); 183 font.setTextScaleX(4.2f); 184 font.setTypeface(SkTypeface::MakeDefault()); 185 font.setTextSkewX(0.42f); 186 font.setTextAlign(SkPaint::kCenter_Align); 187 font.setHinting(SkPaint::kFull_Hinting); 188 font.setAntiAlias(true); 189 font.setFakeBoldText(true); 190 font.setLinearText(true); 191 font.setSubpixelText(true); 192 font.setDevKernText(true); 193 font.setLCDRenderText(true); 194 font.setEmbeddedBitmapText(true); 195 font.setAutohinted(true); 196 font.setVerticalText(true); 197 font.setFlags(font.getFlags() | SkPaint::kGenA8FromLCD_Flag); 198 199 // Ensure we didn't pick default values by mistake. 200 SkPaint defaultPaint; 201 REPORTER_ASSERT(reporter, defaultPaint.getTextSize() != font.getTextSize()); 202 REPORTER_ASSERT(reporter, defaultPaint.getTextScaleX() != font.getTextScaleX()); 203 REPORTER_ASSERT(reporter, defaultPaint.getTypeface() != font.getTypeface()); 204 REPORTER_ASSERT(reporter, defaultPaint.getTextSkewX() != font.getTextSkewX()); 205 REPORTER_ASSERT(reporter, defaultPaint.getTextAlign() != font.getTextAlign()); 206 REPORTER_ASSERT(reporter, defaultPaint.getHinting() != font.getHinting()); 207 REPORTER_ASSERT(reporter, defaultPaint.isAntiAlias() != font.isAntiAlias()); 208 REPORTER_ASSERT(reporter, defaultPaint.isFakeBoldText() != font.isFakeBoldText()); 209 REPORTER_ASSERT(reporter, defaultPaint.isLinearText() != font.isLinearText()); 210 REPORTER_ASSERT(reporter, defaultPaint.isSubpixelText() != font.isSubpixelText()); 211 REPORTER_ASSERT(reporter, defaultPaint.isDevKernText() != font.isDevKernText()); 212 REPORTER_ASSERT(reporter, defaultPaint.isLCDRenderText() != font.isLCDRenderText()); 213 REPORTER_ASSERT(reporter, defaultPaint.isEmbeddedBitmapText() != font.isEmbeddedBitmapText()); 214 REPORTER_ASSERT(reporter, defaultPaint.isAutohinted() != font.isAutohinted()); 215 REPORTER_ASSERT(reporter, defaultPaint.isVerticalText() != font.isVerticalText()); 216 REPORTER_ASSERT(reporter, (defaultPaint.getFlags() & SkPaint::kGenA8FromLCD_Flag) != 217 (font.getFlags() & SkPaint::kGenA8FromLCD_Flag)); 218 219 SkTextBlobBuilder builder; 220 AddRun(font, 1, SkTextBlob::kDefault_Positioning, SkPoint::Make(0, 0), builder); 221 AddRun(font, 1, SkTextBlob::kHorizontal_Positioning, SkPoint::Make(0, 0), builder); 222 AddRun(font, 1, SkTextBlob::kFull_Positioning, SkPoint::Make(0, 0), builder); 223 sk_sp<SkTextBlob> blob(builder.make()); 224 225 SkTextBlobRunIterator it(blob.get()); 226 while (!it.done()) { 227 SkPaint paint; 228 it.applyFontToPaint(&paint); 229 230 REPORTER_ASSERT(reporter, paint.getTextSize() == font.getTextSize()); 231 REPORTER_ASSERT(reporter, paint.getTextScaleX() == font.getTextScaleX()); 232 REPORTER_ASSERT(reporter, paint.getTypeface() == font.getTypeface()); 233 REPORTER_ASSERT(reporter, paint.getTextSkewX() == font.getTextSkewX()); 234 REPORTER_ASSERT(reporter, paint.getTextAlign() == font.getTextAlign()); 235 REPORTER_ASSERT(reporter, paint.getHinting() == font.getHinting()); 236 REPORTER_ASSERT(reporter, paint.isAntiAlias() == font.isAntiAlias()); 237 REPORTER_ASSERT(reporter, paint.isFakeBoldText() == font.isFakeBoldText()); 238 REPORTER_ASSERT(reporter, paint.isLinearText() == font.isLinearText()); 239 REPORTER_ASSERT(reporter, paint.isSubpixelText() == font.isSubpixelText()); 240 REPORTER_ASSERT(reporter, paint.isDevKernText() == font.isDevKernText()); 241 REPORTER_ASSERT(reporter, paint.isLCDRenderText() == font.isLCDRenderText()); 242 REPORTER_ASSERT(reporter, paint.isEmbeddedBitmapText() == font.isEmbeddedBitmapText()); 243 REPORTER_ASSERT(reporter, paint.isAutohinted() == font.isAutohinted()); 244 REPORTER_ASSERT(reporter, paint.isVerticalText() == font.isVerticalText()); 245 REPORTER_ASSERT(reporter, (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) == 246 (font.getFlags() & SkPaint::kGenA8FromLCD_Flag)); 247 248 it.next(); 249 } 250 251 } 252 253private: 254 struct RunDef { 255 unsigned count; 256 SkTextBlob::GlyphPositioning pos; 257 SkScalar x, y; 258 }; 259 260 static void RunBuilderTest(skiatest::Reporter* reporter, SkTextBlobBuilder& builder, 261 const RunDef in[], unsigned inCount, 262 const RunDef out[], unsigned outCount) { 263 SkPaint font; 264 font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 265 266 unsigned glyphCount = 0; 267 unsigned posCount = 0; 268 269 for (unsigned i = 0; i < inCount; ++i) { 270 AddRun(font, in[i].count, in[i].pos, SkPoint::Make(in[i].x, in[i].y), builder); 271 glyphCount += in[i].count; 272 posCount += in[i].count * in[i].pos; 273 } 274 275 sk_sp<SkTextBlob> blob(builder.make()); 276 277 SkTextBlobRunIterator it(blob.get()); 278 for (unsigned i = 0; i < outCount; ++i) { 279 REPORTER_ASSERT(reporter, !it.done()); 280 REPORTER_ASSERT(reporter, out[i].pos == it.positioning()); 281 REPORTER_ASSERT(reporter, out[i].count == it.glyphCount()); 282 if (SkTextBlob::kDefault_Positioning == out[i].pos) { 283 REPORTER_ASSERT(reporter, out[i].x == it.offset().x()); 284 REPORTER_ASSERT(reporter, out[i].y == it.offset().y()); 285 } else if (SkTextBlob::kHorizontal_Positioning == out[i].pos) { 286 REPORTER_ASSERT(reporter, out[i].y == it.offset().y()); 287 } 288 289 for (unsigned k = 0; k < it.glyphCount(); ++k) { 290 REPORTER_ASSERT(reporter, k % 128 == it.glyphs()[k]); 291 if (SkTextBlob::kHorizontal_Positioning == it.positioning()) { 292 REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k]); 293 } else if (SkTextBlob::kFull_Positioning == it.positioning()) { 294 REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k * 2]); 295 REPORTER_ASSERT(reporter, -SkIntToScalar(k % 128) == it.pos()[k * 2 + 1]); 296 } 297 } 298 299 it.next(); 300 } 301 302 REPORTER_ASSERT(reporter, it.done()); 303 } 304 305 static void AddRun(const SkPaint& font, int count, SkTextBlob::GlyphPositioning pos, 306 const SkPoint& offset, SkTextBlobBuilder& builder, 307 const SkRect* bounds = nullptr) { 308 switch (pos) { 309 case SkTextBlob::kDefault_Positioning: { 310 const SkTextBlobBuilder::RunBuffer& rb = builder.allocRun(font, count, offset.x(), 311 offset.y(), bounds); 312 for (int i = 0; i < count; ++i) { 313 rb.glyphs[i] = i; 314 } 315 } break; 316 case SkTextBlob::kHorizontal_Positioning: { 317 const SkTextBlobBuilder::RunBuffer& rb = builder.allocRunPosH(font, count, offset.y(), 318 bounds); 319 for (int i = 0; i < count; ++i) { 320 rb.glyphs[i] = i; 321 rb.pos[i] = SkIntToScalar(i); 322 } 323 } break; 324 case SkTextBlob::kFull_Positioning: { 325 const SkTextBlobBuilder::RunBuffer& rb = builder.allocRunPos(font, count, bounds); 326 for (int i = 0; i < count; ++i) { 327 rb.glyphs[i] = i; 328 rb.pos[i * 2] = SkIntToScalar(i); 329 rb.pos[i * 2 + 1] = -SkIntToScalar(i); 330 } 331 } break; 332 default: 333 SkFAIL("unhandled positioning value"); 334 } 335 } 336}; 337 338DEF_TEST(TextBlob_builder, reporter) { 339 TextBlobTester::TestBuilder(reporter); 340 TextBlobTester::TestBounds(reporter); 341} 342 343DEF_TEST(TextBlob_paint, reporter) { 344 TextBlobTester::TestPaintProps(reporter); 345} 346 347DEF_TEST(TextBlob_extended, reporter) { 348 SkTextBlobBuilder textBlobBuilder; 349 SkPaint paint; 350 const char text1[] = "Foo"; 351 const char text2[] = "Bar"; 352 353 int glyphCount = paint.textToGlyphs(text1, strlen(text1), nullptr); 354 SkAutoTMalloc<uint16_t> glyphs(glyphCount); 355 (void)paint.textToGlyphs(text1, strlen(text1), glyphs.get()); 356 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 357 358 auto run = textBlobBuilder.allocRunText( 359 paint, glyphCount, 0, 0, SkToInt(strlen(text2)), SkString(), nullptr); 360 memcpy(run.glyphs, glyphs.get(), sizeof(uint16_t) * glyphCount); 361 memcpy(run.utf8text, text2, strlen(text2)); 362 for (int i = 0; i < glyphCount; ++i) { 363 run.clusters[i] = SkTMin(SkToU32(i), SkToU32(strlen(text2))); 364 } 365 sk_sp<SkTextBlob> blob(textBlobBuilder.make()); 366 REPORTER_ASSERT(reporter, blob); 367 368 for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) { 369 REPORTER_ASSERT(reporter, it.glyphCount() == (uint32_t)glyphCount); 370 for (uint32_t i = 0; i < it.glyphCount(); ++i) { 371 REPORTER_ASSERT(reporter, it.glyphs()[i] == glyphs[i]); 372 } 373 REPORTER_ASSERT(reporter, SkTextBlob::kDefault_Positioning == it.positioning()); 374 REPORTER_ASSERT(reporter, (SkPoint{0.0f, 0.0f}) == it.offset()); 375 REPORTER_ASSERT(reporter, it.textSize() > 0); 376 REPORTER_ASSERT(reporter, it.clusters()); 377 for (uint32_t i = 0; i < it.glyphCount(); ++i) { 378 REPORTER_ASSERT(reporter, i == it.clusters()[i]); 379 } 380 REPORTER_ASSERT(reporter, 0 == strncmp(text2, it.text(), it.textSize())); 381 } 382} 383