TextBlobTest.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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. #include "include/core/SkPaint.h"
  8. #include "include/core/SkPoint.h"
  9. #include "include/core/SkSerialProcs.h"
  10. #include "include/core/SkTypeface.h"
  11. #include "include/private/SkTo.h"
  12. #include "src/core/SkTextBlobPriv.h"
  13. #include "tests/Test.h"
  14. #include "tools/ToolUtils.h"
  15. class TextBlobTester {
  16. public:
  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. // empty run set
  22. RunBuilderTest(reporter, builder, nullptr, 0, nullptr, 0);
  23. RunDef set1[] = {
  24. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 100 },
  25. };
  26. RunBuilderTest(reporter, builder, set1, SK_ARRAY_COUNT(set1), set1, SK_ARRAY_COUNT(set1));
  27. RunDef set2[] = {
  28. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 100, 100 },
  29. };
  30. RunBuilderTest(reporter, builder, set2, SK_ARRAY_COUNT(set2), set2, SK_ARRAY_COUNT(set2));
  31. RunDef set3[] = {
  32. { 128, SkTextBlobRunIterator::kFull_Positioning, 100, 100 },
  33. };
  34. RunBuilderTest(reporter, builder, set3, SK_ARRAY_COUNT(set3), set3, SK_ARRAY_COUNT(set3));
  35. RunDef set4[] = {
  36. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 150 },
  37. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 150 },
  38. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 150 },
  39. };
  40. RunBuilderTest(reporter, builder, set4, SK_ARRAY_COUNT(set4), set4, SK_ARRAY_COUNT(set4));
  41. RunDef set5[] = {
  42. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 100, 150 },
  43. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 200, 150 },
  44. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 300, 250 },
  45. };
  46. RunDef mergedSet5[] = {
  47. { 256, SkTextBlobRunIterator::kHorizontal_Positioning, 0, 150 },
  48. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 0, 250 },
  49. };
  50. RunBuilderTest(reporter, builder, set5, SK_ARRAY_COUNT(set5), mergedSet5,
  51. SK_ARRAY_COUNT(mergedSet5));
  52. RunDef set6[] = {
  53. { 128, SkTextBlobRunIterator::kFull_Positioning, 100, 100 },
  54. { 128, SkTextBlobRunIterator::kFull_Positioning, 200, 200 },
  55. { 128, SkTextBlobRunIterator::kFull_Positioning, 300, 300 },
  56. };
  57. RunDef mergedSet6[] = {
  58. { 384, SkTextBlobRunIterator::kFull_Positioning, 0, 0 },
  59. };
  60. RunBuilderTest(reporter, builder, set6, SK_ARRAY_COUNT(set6), mergedSet6,
  61. SK_ARRAY_COUNT(mergedSet6));
  62. RunDef set7[] = {
  63. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 150 },
  64. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 150 },
  65. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 100, 150 },
  66. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 200, 150 },
  67. { 128, SkTextBlobRunIterator::kFull_Positioning, 400, 350 },
  68. { 128, SkTextBlobRunIterator::kFull_Positioning, 400, 350 },
  69. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 450 },
  70. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 450 },
  71. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 100, 550 },
  72. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 200, 650 },
  73. { 128, SkTextBlobRunIterator::kFull_Positioning, 400, 750 },
  74. { 128, SkTextBlobRunIterator::kFull_Positioning, 400, 850 },
  75. };
  76. RunDef mergedSet7[] = {
  77. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 150 },
  78. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 150 },
  79. { 256, SkTextBlobRunIterator::kHorizontal_Positioning, 0, 150 },
  80. { 256, SkTextBlobRunIterator::kFull_Positioning, 0, 0 },
  81. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 450 },
  82. { 128, SkTextBlobRunIterator::kDefault_Positioning, 100, 450 },
  83. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 0, 550 },
  84. { 128, SkTextBlobRunIterator::kHorizontal_Positioning, 0, 650 },
  85. { 256, SkTextBlobRunIterator::kFull_Positioning, 0, 0 },
  86. };
  87. RunBuilderTest(reporter, builder, set7, SK_ARRAY_COUNT(set7), mergedSet7,
  88. SK_ARRAY_COUNT(mergedSet7));
  89. }
  90. // This unit test verifies blob bounds computation.
  91. static void TestBounds(skiatest::Reporter* reporter) {
  92. SkTextBlobBuilder builder;
  93. SkFont font;
  94. // Explicit bounds.
  95. {
  96. sk_sp<SkTextBlob> blob(builder.make());
  97. REPORTER_ASSERT(reporter, !blob);
  98. }
  99. {
  100. SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
  101. builder.allocRun(font, 16, 0, 0, &r1);
  102. sk_sp<SkTextBlob> blob(builder.make());
  103. REPORTER_ASSERT(reporter, blob->bounds() == r1);
  104. }
  105. {
  106. SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
  107. builder.allocRunPosH(font, 16, 0, &r1);
  108. sk_sp<SkTextBlob> blob(builder.make());
  109. REPORTER_ASSERT(reporter, blob->bounds() == r1);
  110. }
  111. {
  112. SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
  113. builder.allocRunPos(font, 16, &r1);
  114. sk_sp<SkTextBlob> blob(builder.make());
  115. REPORTER_ASSERT(reporter, blob->bounds() == r1);
  116. }
  117. {
  118. SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
  119. SkRect r2 = SkRect::MakeXYWH(15, 20, 50, 50);
  120. SkRect r3 = SkRect::MakeXYWH(0, 5, 10, 5);
  121. builder.allocRun(font, 16, 0, 0, &r1);
  122. builder.allocRunPosH(font, 16, 0, &r2);
  123. builder.allocRunPos(font, 16, &r3);
  124. sk_sp<SkTextBlob> blob(builder.make());
  125. REPORTER_ASSERT(reporter, blob->bounds() == SkRect::MakeXYWH(0, 5, 65, 65));
  126. }
  127. {
  128. sk_sp<SkTextBlob> blob(builder.make());
  129. REPORTER_ASSERT(reporter, !blob);
  130. }
  131. // Implicit bounds
  132. {
  133. // Exercise the empty bounds path, and ensure that RunRecord-aligned pos buffers
  134. // don't trigger asserts (http://crbug.com/542643).
  135. SkFont font;
  136. font.setSize(0);
  137. const char* txt = "BOOO";
  138. const size_t txtLen = strlen(txt);
  139. const int glyphCount = font.countText(txt, txtLen, SkTextEncoding::kUTF8);
  140. const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(font, glyphCount);
  141. font.textToGlyphs(txt, txtLen, SkTextEncoding::kUTF8, buffer.glyphs, glyphCount);
  142. memset(buffer.pos, 0, sizeof(SkScalar) * glyphCount * 2);
  143. sk_sp<SkTextBlob> blob(builder.make());
  144. REPORTER_ASSERT(reporter, blob->bounds().isEmpty());
  145. }
  146. }
  147. // Verify that text-related properties are captured in run paints.
  148. static void TestPaintProps(skiatest::Reporter* reporter) {
  149. SkFont font;
  150. // Kitchen sink font.
  151. font.setSize(42);
  152. font.setScaleX(4.2f);
  153. font.setTypeface(ToolUtils::create_portable_typeface());
  154. font.setSkewX(0.42f);
  155. font.setHinting(SkFontHinting::kFull);
  156. font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
  157. font.setEmbolden(true);
  158. font.setLinearMetrics(true);
  159. font.setSubpixel(true);
  160. font.setEmbeddedBitmaps(true);
  161. font.setForceAutoHinting(true);
  162. // Ensure we didn't pick default values by mistake.
  163. SkFont defaultFont;
  164. REPORTER_ASSERT(reporter, defaultFont.getSize() != font.getSize());
  165. REPORTER_ASSERT(reporter, defaultFont.getScaleX() != font.getScaleX());
  166. REPORTER_ASSERT(reporter, defaultFont.getTypefaceOrDefault() != font.getTypefaceOrDefault());
  167. REPORTER_ASSERT(reporter, defaultFont.getSkewX() != font.getSkewX());
  168. REPORTER_ASSERT(reporter, defaultFont.getHinting() != font.getHinting());
  169. REPORTER_ASSERT(reporter, defaultFont.getEdging() != font.getEdging());
  170. REPORTER_ASSERT(reporter, defaultFont.isEmbolden() != font.isEmbolden());
  171. REPORTER_ASSERT(reporter, defaultFont.isLinearMetrics() != font.isLinearMetrics());
  172. REPORTER_ASSERT(reporter, defaultFont.isSubpixel() != font.isSubpixel());
  173. REPORTER_ASSERT(reporter,
  174. defaultFont.isEmbeddedBitmaps() != font.isEmbeddedBitmaps());
  175. REPORTER_ASSERT(reporter, defaultFont.isForceAutoHinting() != font.isForceAutoHinting());
  176. SkTextBlobBuilder builder;
  177. AddRun(font, 1, SkTextBlobRunIterator::kDefault_Positioning, SkPoint::Make(0, 0), builder);
  178. AddRun(font, 1, SkTextBlobRunIterator::kHorizontal_Positioning, SkPoint::Make(0, 0),
  179. builder);
  180. AddRun(font, 1, SkTextBlobRunIterator::kFull_Positioning, SkPoint::Make(0, 0), builder);
  181. sk_sp<SkTextBlob> blob(builder.make());
  182. SkTextBlobRunIterator it(blob.get());
  183. while (!it.done()) {
  184. REPORTER_ASSERT(reporter, it.font() == font);
  185. it.next();
  186. }
  187. }
  188. private:
  189. struct RunDef {
  190. unsigned count;
  191. SkTextBlobRunIterator::GlyphPositioning pos;
  192. SkScalar x, y;
  193. };
  194. static void RunBuilderTest(skiatest::Reporter* reporter, SkTextBlobBuilder& builder,
  195. const RunDef in[], unsigned inCount,
  196. const RunDef out[], unsigned outCount) {
  197. SkFont font;
  198. unsigned glyphCount = 0;
  199. unsigned posCount = 0;
  200. for (unsigned i = 0; i < inCount; ++i) {
  201. AddRun(font, in[i].count, in[i].pos, SkPoint::Make(in[i].x, in[i].y), builder);
  202. glyphCount += in[i].count;
  203. posCount += in[i].count * in[i].pos;
  204. }
  205. sk_sp<SkTextBlob> blob(builder.make());
  206. REPORTER_ASSERT(reporter, (inCount > 0) == SkToBool(blob));
  207. if (!blob) {
  208. return;
  209. }
  210. SkTextBlobRunIterator it(blob.get());
  211. for (unsigned i = 0; i < outCount; ++i) {
  212. REPORTER_ASSERT(reporter, !it.done());
  213. REPORTER_ASSERT(reporter, out[i].pos == it.positioning());
  214. REPORTER_ASSERT(reporter, out[i].count == it.glyphCount());
  215. if (SkTextBlobRunIterator::kDefault_Positioning == out[i].pos) {
  216. REPORTER_ASSERT(reporter, out[i].x == it.offset().x());
  217. REPORTER_ASSERT(reporter, out[i].y == it.offset().y());
  218. } else if (SkTextBlobRunIterator::kHorizontal_Positioning == out[i].pos) {
  219. REPORTER_ASSERT(reporter, out[i].y == it.offset().y());
  220. }
  221. for (unsigned k = 0; k < it.glyphCount(); ++k) {
  222. REPORTER_ASSERT(reporter, k % 128 == it.glyphs()[k]);
  223. if (SkTextBlobRunIterator::kHorizontal_Positioning == it.positioning()) {
  224. REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k]);
  225. } else if (SkTextBlobRunIterator::kFull_Positioning == it.positioning()) {
  226. REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k * 2]);
  227. REPORTER_ASSERT(reporter, -SkIntToScalar(k % 128) == it.pos()[k * 2 + 1]);
  228. }
  229. }
  230. it.next();
  231. }
  232. REPORTER_ASSERT(reporter, it.done());
  233. }
  234. static void AddRun(const SkFont& font, int count, SkTextBlobRunIterator::GlyphPositioning pos,
  235. const SkPoint& offset, SkTextBlobBuilder& builder,
  236. const SkRect* bounds = nullptr) {
  237. switch (pos) {
  238. case SkTextBlobRunIterator::kDefault_Positioning: {
  239. const SkTextBlobBuilder::RunBuffer& rb = builder.allocRun(font, count, offset.x(),
  240. offset.y(), bounds);
  241. for (int i = 0; i < count; ++i) {
  242. rb.glyphs[i] = i;
  243. }
  244. } break;
  245. case SkTextBlobRunIterator::kHorizontal_Positioning: {
  246. const SkTextBlobBuilder::RunBuffer& rb = builder.allocRunPosH(font, count, offset.y(),
  247. bounds);
  248. for (int i = 0; i < count; ++i) {
  249. rb.glyphs[i] = i;
  250. rb.pos[i] = SkIntToScalar(i);
  251. }
  252. } break;
  253. case SkTextBlobRunIterator::kFull_Positioning: {
  254. const SkTextBlobBuilder::RunBuffer& rb = builder.allocRunPos(font, count, bounds);
  255. for (int i = 0; i < count; ++i) {
  256. rb.glyphs[i] = i;
  257. rb.pos[i * 2] = SkIntToScalar(i);
  258. rb.pos[i * 2 + 1] = -SkIntToScalar(i);
  259. }
  260. } break;
  261. default:
  262. SK_ABORT("unhandled positioning value");
  263. }
  264. }
  265. };
  266. DEF_TEST(TextBlob_builder, reporter) {
  267. TextBlobTester::TestBuilder(reporter);
  268. TextBlobTester::TestBounds(reporter);
  269. }
  270. DEF_TEST(TextBlob_paint, reporter) {
  271. TextBlobTester::TestPaintProps(reporter);
  272. }
  273. DEF_TEST(TextBlob_extended, reporter) {
  274. SkTextBlobBuilder textBlobBuilder;
  275. SkFont font;
  276. const char text1[] = "Foo";
  277. const char text2[] = "Bar";
  278. int glyphCount = font.countText(text1, strlen(text1), SkTextEncoding::kUTF8);
  279. SkAutoTMalloc<uint16_t> glyphs(glyphCount);
  280. (void)font.textToGlyphs(text1, strlen(text1), SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
  281. auto run = SkTextBlobBuilderPriv::AllocRunText(&textBlobBuilder,
  282. font, glyphCount, 0, 0, SkToInt(strlen(text2)), SkString(), nullptr);
  283. memcpy(run.glyphs, glyphs.get(), sizeof(uint16_t) * glyphCount);
  284. memcpy(run.utf8text, text2, strlen(text2));
  285. for (int i = 0; i < glyphCount; ++i) {
  286. run.clusters[i] = SkTMin(SkToU32(i), SkToU32(strlen(text2)));
  287. }
  288. sk_sp<SkTextBlob> blob(textBlobBuilder.make());
  289. REPORTER_ASSERT(reporter, blob);
  290. for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) {
  291. REPORTER_ASSERT(reporter, it.glyphCount() == (uint32_t)glyphCount);
  292. for (uint32_t i = 0; i < it.glyphCount(); ++i) {
  293. REPORTER_ASSERT(reporter, it.glyphs()[i] == glyphs[i]);
  294. }
  295. REPORTER_ASSERT(reporter, SkTextBlobRunIterator::kDefault_Positioning == it.positioning());
  296. REPORTER_ASSERT(reporter, (SkPoint{0.0f, 0.0f}) == it.offset());
  297. REPORTER_ASSERT(reporter, it.textSize() > 0);
  298. REPORTER_ASSERT(reporter, it.clusters());
  299. for (uint32_t i = 0; i < it.glyphCount(); ++i) {
  300. REPORTER_ASSERT(reporter, i == it.clusters()[i]);
  301. }
  302. REPORTER_ASSERT(reporter, 0 == strncmp(text2, it.text(), it.textSize()));
  303. }
  304. }
  305. ///////////////////////////////////////////////////////////////////////////////////////////////////
  306. #include "include/core/SkCanvas.h"
  307. #include "include/core/SkSurface.h"
  308. #include "include/private/SkTArray.h"
  309. static void add_run(SkTextBlobBuilder* builder, const char text[], SkScalar x, SkScalar y,
  310. sk_sp<SkTypeface> tf) {
  311. SkFont font;
  312. font.setEdging(SkFont::Edging::kAntiAlias);
  313. font.setSubpixel(true);
  314. font.setSize(16);
  315. font.setTypeface(tf);
  316. int glyphCount = font.countText(text, strlen(text), SkTextEncoding::kUTF8);
  317. SkTextBlobBuilder::RunBuffer buffer = builder->allocRun(font, glyphCount, x, y);
  318. (void)font.textToGlyphs(text, strlen(text), SkTextEncoding::kUTF8, buffer.glyphs, glyphCount);
  319. }
  320. static sk_sp<SkImage> render(const SkTextBlob* blob) {
  321. auto surf = SkSurface::MakeRasterN32Premul(SkScalarRoundToInt(blob->bounds().width()),
  322. SkScalarRoundToInt(blob->bounds().height()));
  323. if (!surf) {
  324. return nullptr; // bounds are empty?
  325. }
  326. surf->getCanvas()->clear(SK_ColorWHITE);
  327. surf->getCanvas()->drawTextBlob(blob, -blob->bounds().left(), -blob->bounds().top(), SkPaint());
  328. return surf->makeImageSnapshot();
  329. }
  330. static sk_sp<SkData> SerializeTypeface(SkTypeface* tf, void* ctx) {
  331. auto array = (SkTArray<sk_sp<SkTypeface>>*)ctx;
  332. const size_t idx = array->size();
  333. array->emplace_back(sk_ref_sp(tf));
  334. // In this test, we are deserializing on the same machine, so we don't worry about endianness.
  335. return SkData::MakeWithCopy(&idx, sizeof(idx));
  336. }
  337. static sk_sp<SkTypeface> DeserializeTypeface(const void* data, size_t length, void* ctx) {
  338. auto array = (SkTArray<sk_sp<SkTypeface>>*)ctx;
  339. if (length != sizeof(size_t)) {
  340. SkASSERT(false);
  341. return nullptr;
  342. }
  343. size_t idx = *reinterpret_cast<const size_t*>(data);
  344. if (idx >= array->size()) {
  345. SkASSERT(false);
  346. return nullptr;
  347. }
  348. return (*array)[idx];
  349. }
  350. /*
  351. * Build a blob with more than one typeface.
  352. * Draw it into an offscreen,
  353. * then serialize and deserialize,
  354. * Then draw the new instance and assert it draws the same as the original.
  355. */
  356. DEF_TEST(TextBlob_serialize, reporter) {
  357. sk_sp<SkTextBlob> blob0 = []() {
  358. sk_sp<SkTypeface> tf = SkTypeface::MakeFromName(nullptr, SkFontStyle::BoldItalic());
  359. SkTextBlobBuilder builder;
  360. add_run(&builder, "Hello", 10, 20, nullptr); // don't flatten a typeface
  361. add_run(&builder, "World", 10, 40, tf); // do flatten this typeface
  362. return builder.make();
  363. }();
  364. SkTArray<sk_sp<SkTypeface>> array;
  365. SkSerialProcs serializeProcs;
  366. serializeProcs.fTypefaceProc = &SerializeTypeface;
  367. serializeProcs.fTypefaceCtx = (void*) &array;
  368. sk_sp<SkData> data = blob0->serialize(serializeProcs);
  369. REPORTER_ASSERT(reporter, array.count() == 1);
  370. SkDeserialProcs deserializeProcs;
  371. deserializeProcs.fTypefaceProc = &DeserializeTypeface;
  372. deserializeProcs.fTypefaceCtx = (void*) &array;
  373. sk_sp<SkTextBlob> blob1 = SkTextBlob::Deserialize(data->data(), data->size(), deserializeProcs);
  374. sk_sp<SkImage> img0 = render(blob0.get());
  375. sk_sp<SkImage> img1 = render(blob1.get());
  376. if (img0 && img1) {
  377. REPORTER_ASSERT(reporter, ToolUtils::equal_pixels(img0.get(), img1.get()));
  378. }
  379. }
  380. DEF_TEST(TextBlob_MakeAsDrawText, reporter) {
  381. const char text[] = "Hello";
  382. auto blob = SkTextBlob::MakeFromString(text, SkFont(), SkTextEncoding::kUTF8);
  383. int runs = 0;
  384. for(SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) {
  385. REPORTER_ASSERT(reporter, it.glyphCount() == strlen(text));
  386. REPORTER_ASSERT(reporter, it.positioning() == SkTextBlobRunIterator::kFull_Positioning);
  387. runs += 1;
  388. }
  389. REPORTER_ASSERT(reporter, runs == 1);
  390. }