1 /* 2 * Copyright 2012 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 "gm.h" 9 #include "sk_tool_utils.h" 10 #include "Resources.h" 11 #include "SkBlurTypes.h" 12 #include "SkCanvas.h" 13 #include "SkFontStyle.h" 14 #include "SkMaskFilter.h" 15 #include "SkString.h" 16 #include "SkSurfaceProps.h" 17 #include "SkTextBlob.h" 18 #include "SkTypeface.h" 19 #include "SkTypes.h" 20 21 static void getGlyphPositions(const SkFont& font, const uint16_t glyphs[], 22 int count, SkScalar x, SkScalar y, SkPoint pos[]) { 23 SkAutoSTMalloc<128, SkScalar> widthStorage(count); 24 SkScalar* widths = widthStorage.get(); 25 font.getWidths(glyphs, count, widths); 26 27 for (int i = 0; i < count; ++i) { 28 pos[i].set(x, y); 29 x += widths[i]; 30 } 31 } 32 33 static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count, 34 const SkFont& font) { 35 SkScalar scale = font.getSize() / font.getTypefaceOrDefault()->getUnitsPerEm(); 36 37 SkScalar globalAdj = 0; 38 for (int i = 0; i < count - 1; ++i) { 39 globalAdj += adjustments[i] * scale; 40 pos[i + 1].fX += globalAdj; 41 } 42 } 43 44 static void drawKernText(SkCanvas* canvas, const void* text, size_t len, 45 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) { 46 SkTypeface* face = font.getTypefaceOrDefault(); 47 if (!face) { 48 canvas->drawSimpleText(text, len, kUTF8_SkTextEncoding, x, y, font, paint); 49 return; 50 } 51 52 SkAutoSTMalloc<128, uint16_t> glyphStorage(len); 53 uint16_t* glyphs = glyphStorage.get(); 54 int glyphCount = font.textToGlyphs(text, len, kUTF8_SkTextEncoding, glyphs, len); 55 if (glyphCount < 1) { 56 return; 57 } 58 59 SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1); 60 int32_t* adjustments = adjustmentStorage.get(); 61 if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) { 62 canvas->drawSimpleText(text, len, kUTF8_SkTextEncoding, x, y, font, paint); 63 return; 64 } 65 66 67 SkTextBlobBuilder builder; 68 auto rec = builder.allocRunPos(font, glyphCount); 69 memcpy(rec.glyphs, glyphs, glyphCount * sizeof(SkGlyphID)); 70 getGlyphPositions(font, glyphs, glyphCount, x, y, rec.points()); 71 applyKerning(rec.points(), adjustments, glyphCount, font); 72 73 canvas->drawTextBlob(builder.make(), 0, 0, paint); 74 } 75 76 static constexpr SkFontStyle gStyles[] = { 77 SkFontStyle::Normal(), 78 SkFontStyle::Bold(), 79 SkFontStyle::Italic(), 80 SkFontStyle::BoldItalic(), 81 }; 82 83 constexpr int gStylesCount = SK_ARRAY_COUNT(gStyles); 84 85 class TypefaceStylesGM : public skiagm::GM { 86 sk_sp<SkTypeface> fFaces[gStylesCount]; 87 bool fApplyKerning; 88 89 public: 90 TypefaceStylesGM(bool applyKerning) : fApplyKerning(applyKerning) {} 91 92 protected: 93 void onOnceBeforeDraw() override { 94 for (int i = 0; i < gStylesCount; i++) { 95 fFaces[i] = SkTypeface::MakeFromName(nullptr, gStyles[i]); 96 } 97 } 98 99 SkString onShortName() override { 100 SkString name("typefacestyles"); 101 if (fApplyKerning) { 102 name.append("_kerning"); 103 } 104 name.append(sk_tool_utils::platform_font_manager()); 105 return name; 106 } 107 108 SkISize onISize() override { 109 return SkISize::Make(640, 480); 110 } 111 112 void onDraw(SkCanvas* canvas) override { 113 SkFont font; 114 font.setSize(30); 115 116 const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons"; 117 const size_t textLen = strlen(text); 118 119 SkScalar x = SkIntToScalar(10); 120 SkScalar dy = font.getMetrics(nullptr); 121 SkScalar y = dy; 122 123 if (fApplyKerning) { 124 font.setSubpixel(true); 125 } else { 126 font.setLinearMetrics(true); 127 } 128 129 SkPaint paint; 130 for (int i = 0; i < gStylesCount; i++) { 131 font.setTypeface(fFaces[i]); 132 canvas->drawSimpleText(text, textLen, kUTF8_SkTextEncoding, x, y, font, paint); 133 if (fApplyKerning) { 134 drawKernText(canvas, text, textLen, x + 240, y, font, paint); 135 } 136 y += dy; 137 } 138 } 139 140 private: 141 typedef skiagm::GM INHERITED; 142 }; 143 144 DEF_GM( return new TypefaceStylesGM(false); ) 145 DEF_GM( return new TypefaceStylesGM(true); ) 146 147 //////////////////////////////////////////////////////////////////////////////// 148 149 static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face, 150 char character = 'A') { 151 struct AliasType { 152 bool antiAlias; 153 bool subpixelAntitalias; 154 bool inLayer; 155 } constexpr aliasTypes[] { 156 #ifndef SK_BUILD_FOR_IOS 157 // This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering. 158 // The crash looks like 159 // libTrueTypeScaler.dylib`<redacted> + 80 160 // stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...) 161 // -> 0x330b19d0 <+80>: strd r2, r3, [r5, #36] 162 // 0x330b19d4 <+84>: movs r3, #0x0 163 // 0x330b19d6 <+86>: add r2, sp, #0x28 164 // 0x330b19d8 <+88>: ldr r0, [r4, #0x4] 165 // Disable testing embedded bitmaps on iOS for now. 166 // See https://bug.skia.org/5530 . 167 { false, false, false }, // aliased 168 #endif 169 { true, false, false }, // anti-aliased 170 { true, true , false }, // subpixel anti-aliased 171 { true, false, true }, // anti-aliased in layer (flat pixel geometry) 172 { true, true , true }, // subpixel anti-aliased in layer (flat pixel geometry) 173 }; 174 175 auto compute_edging = [](AliasType at) { 176 if (at.antiAlias) { 177 if (at.subpixelAntitalias) { 178 return SkFont::Edging::kSubpixelAntiAlias; 179 } else { 180 return SkFont::Edging::kAntiAlias; 181 } 182 } else { 183 return SkFont::Edging::kAlias; 184 } 185 }; 186 187 // The hintgasp.ttf is designed for the following sizes to be different. 188 // GASP_DOGRAY 0x0002 0<=ppem<=10 189 // GASP_SYMMETRIC_SMOOTHING 0x0008 0<=ppem<=10 190 // GASP_GRIDFIT 0x0001 11<=ppem<=12 191 // GASP_SYMMETRIC_GRIDFIT 0x0004 11<=ppem<=12 192 // GASP_DOGRAY|GASP_GRIDFIT 0x0003 13<=ppem<=14 193 // GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT 0x000C 13<=ppem<=14 194 // (neither) 0x0000 15<=ppem 195 // Odd sizes have embedded bitmaps. 196 constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 }; 197 198 constexpr SkFontHinting hintingTypes[] = { 199 kNo_SkFontHinting, 200 kSlight_SkFontHinting, 201 kNormal_SkFontHinting, 202 kFull_SkFontHinting 203 }; 204 205 struct SubpixelType { 206 bool requested; 207 SkVector offset; 208 } constexpr subpixelTypes[] = { 209 { false, { 0.00, 0.00 } }, 210 { true , { 0.00, 0.00 } }, 211 { true , { 0.25, 0.00 } }, 212 { true , { 0.25, 0.25 } }, 213 }; 214 215 constexpr bool rotateABitTypes[] = { false, true }; 216 217 SkScalar y = 0; // The baseline of the previous output 218 { 219 SkPaint paint; 220 221 SkFont font(face); 222 font.setEmbeddedBitmaps(true); 223 224 SkScalar x = 0; 225 SkScalar xMax = x; 226 SkScalar xBase = 0; 227 for (const SubpixelType subpixel : subpixelTypes) { 228 y = 0; 229 font.setSubpixel(subpixel.requested); 230 231 for (const AliasType& alias : aliasTypes) { 232 font.setEdging(compute_edging(alias)); 233 SkAutoCanvasRestore acr(canvas, false); 234 if (alias.inLayer) { 235 canvas->saveLayer(nullptr, &paint); 236 } 237 238 for (const SkScalar& textSize : textSizes) { 239 x = xBase + 5; 240 font.setSize(textSize); 241 242 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr)); 243 y += dy; 244 for (const SkFontHinting& hinting : hintingTypes) { 245 font.setHinting(hinting); 246 247 for (const bool& rotateABit : rotateABitTypes) { 248 SkAutoCanvasRestore acr(canvas, true); 249 if (rotateABit) { 250 canvas->rotate(2, x + subpixel.offset.x(), 251 y + subpixel.offset.y()); 252 } 253 canvas->drawSimpleText(&character, 1, kUTF8_SkTextEncoding, 254 x + subpixel.offset.x(), 255 y + subpixel.offset.y(), font, paint); 256 257 SkScalar dx = SkScalarCeilToScalar( 258 font.measureText(&character, 1, kUTF8_SkTextEncoding)) + 5; 259 x += dx; 260 xMax = SkTMax(x, xMax); 261 } 262 } 263 } 264 y += 10; 265 } 266 xBase = xMax; 267 } 268 } 269 270 constexpr struct StyleTests { 271 SkPaint::Style style; 272 SkScalar strokeWidth; 273 } styleTypes[] = { 274 { SkPaint::kFill_Style, 0.0f}, 275 { SkPaint::kStroke_Style, 0.0f}, 276 { SkPaint::kStroke_Style, 0.5f}, 277 { SkPaint::kStrokeAndFill_Style, 1.0f}, 278 }; 279 280 constexpr bool fakeBoldTypes[] = { false, true }; 281 282 { 283 SkPaint paint; 284 285 SkFont font(face, 16); 286 287 SkScalar x = 0; 288 for (const bool& fakeBold : fakeBoldTypes) { 289 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr)); 290 y += dy; 291 x = 5; 292 293 font.setEmbolden(fakeBold); 294 for (const AliasType& alias : aliasTypes) { 295 font.setEdging(compute_edging(alias)); 296 SkAutoCanvasRestore acr(canvas, false); 297 if (alias.inLayer) { 298 canvas->saveLayer(nullptr, &paint); 299 } 300 for (const StyleTests& style : styleTypes) { 301 paint.setStyle(style.style); 302 paint.setStrokeWidth(style.strokeWidth); 303 canvas->drawSimpleText(&character, 1, kUTF8_SkTextEncoding, x, y, font, paint); 304 305 SkScalar dx = SkScalarCeilToScalar(font.measureText(&character, 1, 306 kUTF8_SkTextEncoding)) + 5; 307 x += dx; 308 } 309 } 310 y += 10; 311 } 312 } 313 314 constexpr struct MaskTests { 315 SkBlurStyle style; 316 SkScalar sigma; 317 } maskTypes[] = { 318 { SkBlurStyle::kNormal_SkBlurStyle, 0.0f}, 319 { SkBlurStyle::kSolid_SkBlurStyle, 0.0f}, 320 { SkBlurStyle::kOuter_SkBlurStyle, 0.0f}, 321 { SkBlurStyle::kInner_SkBlurStyle, 0.0f}, 322 323 { SkBlurStyle::kNormal_SkBlurStyle, 0.5f}, 324 { SkBlurStyle::kSolid_SkBlurStyle, 0.5f}, 325 { SkBlurStyle::kOuter_SkBlurStyle, 0.5f}, 326 { SkBlurStyle::kInner_SkBlurStyle, 0.5f}, 327 328 { SkBlurStyle::kNormal_SkBlurStyle, 2.0f}, 329 { SkBlurStyle::kSolid_SkBlurStyle, 2.0f}, 330 { SkBlurStyle::kOuter_SkBlurStyle, 2.0f}, 331 { SkBlurStyle::kInner_SkBlurStyle, 2.0f}, 332 }; 333 334 { 335 SkPaint paint; 336 337 SkFont font(face, 16); 338 339 SkScalar x = 0; 340 { 341 for (const AliasType& alias : aliasTypes) { 342 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr)); 343 y += dy; 344 x = 5; 345 346 font.setEdging(compute_edging(alias)); 347 SkAutoCanvasRestore acr(canvas, false); 348 if (alias.inLayer) { 349 canvas->saveLayer(nullptr, &paint); 350 } 351 for (const MaskTests& mask : maskTypes) { 352 paint.setMaskFilter(SkMaskFilter::MakeBlur(mask.style, mask.sigma)); 353 canvas->drawSimpleText(&character, 1, kUTF8_SkTextEncoding, x, y, font, paint); 354 355 SkScalar dx = SkScalarCeilToScalar(font.measureText(&character, 1, 356 kUTF8_SkTextEncoding)) + 5; 357 x += dx; 358 } 359 paint.setMaskFilter(nullptr); 360 } 361 y += 10; 362 } 363 } 364 } 365 366 DEF_SIMPLE_GM_BG_NAME(typefacerendering, canvas, 640, 840, SK_ColorWHITE, 367 SkStringPrintf("typefacerendering%s", 368 sk_tool_utils::platform_font_manager())) { 369 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/hintgasp.ttf")) { 370 draw_typeface_rendering_gm(canvas, std::move(face)); 371 } 372 } 373 374 // Type1 fonts don't currently work in Skia on Windows. 375 #ifndef SK_BUILD_FOR_WIN 376 377 DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfa, canvas, 640, 840, SK_ColorWHITE, 378 SkStringPrintf("typefacerendering_pfa%s", 379 sk_tool_utils::platform_font_manager())) { 380 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfa")) { 381 // This subsetted typeface doesn't have the character 'A'. 382 draw_typeface_rendering_gm(canvas, std::move(face), 'O'); 383 } 384 } 385 386 DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfb, canvas, 640, 840, SK_ColorWHITE, 387 SkStringPrintf("typefacerendering_pfb%s", 388 sk_tool_utils::platform_font_manager())) { 389 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfb")) { 390 draw_typeface_rendering_gm(canvas, std::move(face), 'O'); 391 } 392 } 393 394 #endif 395