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 "SkTextBlob.h" 11 12 #include "Test.h" 13 14 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 22 // empty run set 23 RunBuilderTest(reporter, builder, NULL, 0, NULL, 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 SkAutoTUnref<const SkTextBlob> blob(builder.build()); 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 SkAutoTUnref<const SkTextBlob> blob(builder.build()); 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 SkAutoTUnref<const SkTextBlob> blob(builder.build()); 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 SkAutoTUnref<const SkTextBlob> blob(builder.build()); 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 SkAutoTUnref<const SkTextBlob> blob(builder.build()); 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 SkAutoTUnref<const SkTextBlob> blob(builder.build()); 148 REPORTER_ASSERT(reporter, blob->bounds().isEmpty()); 149 } 150 151 // Implicit bounds 152 // FIXME: not supported yet. 153 } 154 155 private: 156 struct RunDef { 157 unsigned count; 158 SkTextBlob::GlyphPositioning pos; 159 SkScalar x, y; 160 }; 161 162 static void RunBuilderTest(skiatest::Reporter* reporter, SkTextBlobBuilder& builder, 163 const RunDef in[], unsigned inCount, 164 const RunDef out[], unsigned outCount) { 165 SkPaint font; 166 font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 167 168 unsigned glyphCount = 0; 169 unsigned posCount = 0; 170 171 for (unsigned i = 0; i < inCount; ++i) { 172 AddRun(font, in[i].count, in[i].pos, SkPoint::Make(in[i].x, in[i].y), builder); 173 glyphCount += in[i].count; 174 posCount += in[i].count * in[i].pos; 175 } 176 177 SkAutoTUnref<const SkTextBlob> blob(builder.build()); 178 179 SkTextBlob::RunIterator it(blob); 180 for (unsigned i = 0; i < outCount; ++i) { 181 REPORTER_ASSERT(reporter, !it.done()); 182 REPORTER_ASSERT(reporter, out[i].pos == it.positioning()); 183 REPORTER_ASSERT(reporter, out[i].count == it.glyphCount()); 184 if (SkTextBlob::kDefault_Positioning == out[i].pos) { 185 REPORTER_ASSERT(reporter, out[i].x == it.offset().x()); 186 REPORTER_ASSERT(reporter, out[i].y == it.offset().y()); 187 } else if (SkTextBlob::kHorizontal_Positioning == out[i].pos) { 188 REPORTER_ASSERT(reporter, out[i].y == it.offset().y()); 189 } 190 191 for (unsigned k = 0; k < it.glyphCount(); ++k) { 192 REPORTER_ASSERT(reporter, k % 128 == it.glyphs()[k]); 193 if (SkTextBlob::kHorizontal_Positioning == it.positioning()) { 194 REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k]); 195 } else if (SkTextBlob::kFull_Positioning == it.positioning()) { 196 REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k * 2]); 197 REPORTER_ASSERT(reporter, -SkIntToScalar(k % 128) == it.pos()[k * 2 + 1]); 198 } 199 } 200 201 it.next(); 202 } 203 204 REPORTER_ASSERT(reporter, it.done()); 205 } 206 207 static void AddRun(const SkPaint& font, int count, SkTextBlob::GlyphPositioning pos, 208 const SkPoint& offset, SkTextBlobBuilder& builder, 209 const SkRect* bounds = NULL) { 210 switch (pos) { 211 case SkTextBlob::kDefault_Positioning: { 212 const SkTextBlobBuilder::RunBuffer& rb = builder.allocRun(font, count, offset.x(), 213 offset.y(), bounds); 214 for (int i = 0; i < count; ++i) { 215 rb.glyphs[i] = i; 216 } 217 } break; 218 case SkTextBlob::kHorizontal_Positioning: { 219 const SkTextBlobBuilder::RunBuffer& rb = builder.allocRunPosH(font, count, offset.y(), 220 bounds); 221 for (int i = 0; i < count; ++i) { 222 rb.glyphs[i] = i; 223 rb.pos[i] = SkIntToScalar(i); 224 } 225 } break; 226 case SkTextBlob::kFull_Positioning: { 227 const SkTextBlobBuilder::RunBuffer& rb = builder.allocRunPos(font, count, bounds); 228 for (int i = 0; i < count; ++i) { 229 rb.glyphs[i] = i; 230 rb.pos[i * 2] = SkIntToScalar(i); 231 rb.pos[i * 2 + 1] = -SkIntToScalar(i); 232 } 233 } break; 234 default: 235 SkFAIL("unhandled positioning value"); 236 } 237 } 238 }; 239 240 DEF_TEST(TextBlob_builder, reporter) { 241 TextBlobTester::TestBuilder(reporter); 242 TextBlobTester::TestBounds(reporter); 243 } 244