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 "SkCanvas.h" 12 #include "SkFontStyle.h" 13 #include "SkString.h" 14 #include "SkSurfaceProps.h" 15 #include "SkTypeface.h" 16 #include "SkTypes.h" 17 18 static void getGlyphPositions(const SkPaint& paint, const uint16_t glyphs[], 19 int count, SkScalar x, SkScalar y, SkPoint pos[]) { 20 SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()); 21 22 SkAutoSTMalloc<128, SkScalar> widthStorage(count); 23 SkScalar* widths = widthStorage.get(); 24 paint.getTextWidths(glyphs, count * sizeof(uint16_t), widths); 25 26 for (int i = 0; i < count; ++i) { 27 pos[i].set(x, y); 28 x += widths[i]; 29 } 30 } 31 32 static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count, 33 const SkPaint& paint) { 34 SkScalar scale = paint.getTextSize() / paint.getTypeface()->getUnitsPerEm(); 35 36 SkScalar globalAdj = 0; 37 for (int i = 0; i < count - 1; ++i) { 38 globalAdj += adjustments[i] * scale; 39 pos[i + 1].fX += globalAdj; 40 } 41 } 42 43 static void drawKernText(SkCanvas* canvas, const void* text, size_t len, 44 SkScalar x, SkScalar y, const SkPaint& paint) { 45 SkTypeface* face = paint.getTypeface(); 46 if (!face) { 47 canvas->drawText(text, len, x, y, paint); 48 return; 49 } 50 51 SkAutoSTMalloc<128, uint16_t> glyphStorage(len); 52 uint16_t* glyphs = glyphStorage.get(); 53 int glyphCount = paint.textToGlyphs(text, len, glyphs); 54 if (glyphCount < 1) { 55 return; 56 } 57 58 SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1); 59 int32_t* adjustments = adjustmentStorage.get(); 60 if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) { 61 canvas->drawText(text, len, x, y, paint); 62 return; 63 } 64 65 SkPaint glyphPaint(paint); 66 glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 67 68 SkAutoSTMalloc<128, SkPoint> posStorage(glyphCount); 69 SkPoint* pos = posStorage.get(); 70 getGlyphPositions(glyphPaint, glyphs, glyphCount, x, y, pos); 71 72 applyKerning(pos, adjustments, glyphCount, glyphPaint); 73 canvas->drawPosText(glyphs, glyphCount * sizeof(uint16_t), pos, glyphPaint); 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) 91 : fApplyKerning(applyKerning) { 92 memset(fFaces, 0, sizeof(fFaces)); 93 } 94 95 protected: 96 void onOnceBeforeDraw() override { 97 for (int i = 0; i < gStylesCount; i++) { 98 fFaces[i] = SkTypeface::MakeFromName(nullptr, gStyles[i]); 99 } 100 } 101 102 SkString onShortName() override { 103 SkString name("typefacestyles"); 104 if (fApplyKerning) { 105 name.append("_kerning"); 106 } 107 name.append(sk_tool_utils::platform_font_manager()); 108 return name; 109 } 110 111 SkISize onISize() override { 112 return SkISize::Make(640, 480); 113 } 114 115 void onDraw(SkCanvas* canvas) override { 116 SkPaint paint; 117 paint.setAntiAlias(true); 118 paint.setTextSize(SkIntToScalar(30)); 119 120 const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons"; 121 const size_t textLen = strlen(text); 122 123 SkScalar x = SkIntToScalar(10); 124 SkScalar dy = paint.getFontMetrics(nullptr); 125 SkScalar y = dy; 126 127 if (fApplyKerning) { 128 paint.setSubpixelText(true); 129 } else { 130 paint.setLinearText(true); 131 } 132 for (int i = 0; i < gStylesCount; i++) { 133 paint.setTypeface(fFaces[i]); 134 canvas->drawText(text, textLen, x, y, paint); 135 if (fApplyKerning) { 136 drawKernText(canvas, text, textLen, x + 240, y, paint); 137 } 138 y += dy; 139 } 140 } 141 142 private: 143 typedef skiagm::GM INHERITED; 144 }; 145 146 DEF_GM( return new TypefaceStylesGM(false); ) 147 DEF_GM( return new TypefaceStylesGM(true); ) 148 149 //////////////////////////////////////////////////////////////////////////////// 150 151 static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face, 152 char character = 'A') { 153 struct AliasType { 154 bool antiAlias; 155 bool subpixelAntitalias; 156 bool inLayer; 157 } constexpr aliasTypes[] { 158 #ifndef SK_BUILD_FOR_IOS 159 // This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering. 160 // The crash looks like 161 // libTrueTypeScaler.dylib`<redacted> + 80 162 // stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...) 163 // -> 0x330b19d0 <+80>: strd r2, r3, [r5, #36] 164 // 0x330b19d4 <+84>: movs r3, #0x0 165 // 0x330b19d6 <+86>: add r2, sp, #0x28 166 // 0x330b19d8 <+88>: ldr r0, [r4, #0x4] 167 // Disable testing embedded bitmaps on iOS for now. 168 // See https://bug.skia.org/5530 . 169 { false, false, false }, // aliased 170 #endif 171 { true, false, false }, // anti-aliased 172 { true, true , false }, // subpixel anti-aliased 173 { true, false, true }, // anti-aliased in layer (flat pixel geometry) 174 { true, true , true }, // subpixel anti-aliased in layer (flat pixel geometry) 175 }; 176 177 // The hintgasp.ttf is designed for the following sizes to be different. 178 // GASP_DOGRAY 0x0002 0<=ppem<=10 179 // GASP_SYMMETRIC_SMOOTHING 0x0008 0<=ppem<=10 180 // GASP_GRIDFIT 0x0001 11<=ppem<=12 181 // GASP_SYMMETRIC_GRIDFIT 0x0004 11<=ppem<=12 182 // GASP_DOGRAY|GASP_GRIDFIT 0x0003 13<=ppem<=14 183 // GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT 0x000C 13<=ppem<=14 184 // (neither) 0x0000 15<=ppem 185 // Odd sizes have embedded bitmaps. 186 constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 }; 187 188 constexpr SkPaint::Hinting hintingTypes[] = { SkPaint::kNo_Hinting, 189 SkPaint::kSlight_Hinting, 190 SkPaint::kNormal_Hinting, 191 SkPaint::kFull_Hinting }; 192 193 struct SubpixelType { 194 bool requested; 195 SkVector offset; 196 } constexpr subpixelTypes[] = { 197 { false, { 0.00, 0.00 } }, 198 { true , { 0.00, 0.00 } }, 199 { true , { 0.25, 0.00 } }, 200 { true , { 0.25, 0.25 } }, 201 }; 202 203 constexpr bool rotateABitTypes[] = { false, true }; 204 205 SkPaint paint; 206 paint.setTypeface(face); 207 paint.setEmbeddedBitmapText(true); 208 209 SkScalar x = 0; 210 SkScalar xMax = x; 211 SkScalar xBase = 0; 212 SkScalar y = 0; // The baseline of the previous output 213 for (const SubpixelType subpixel : subpixelTypes) { 214 y = 0; 215 paint.setSubpixelText(subpixel.requested); 216 217 for (const AliasType& alias : aliasTypes) { 218 paint.setAntiAlias(alias.antiAlias); 219 paint.setLCDRenderText(alias.subpixelAntitalias); 220 SkAutoCanvasRestore acr(canvas, false); 221 if (alias.inLayer) { 222 canvas->saveLayer(nullptr, &paint); 223 } 224 225 for (const SkScalar& textSize : textSizes) { 226 x = xBase + 5; 227 paint.setTextSize(textSize); 228 229 SkScalar dy = SkScalarCeilToScalar(paint.getFontMetrics(nullptr)); 230 y += dy; 231 for (const SkPaint::Hinting& hinting : hintingTypes) { 232 paint.setHinting(hinting); 233 234 for (const bool& rotateABit : rotateABitTypes) { 235 SkAutoCanvasRestore acr(canvas, true); 236 if (rotateABit) { 237 canvas->rotate(2, x + subpixel.offset.x(), 238 y + subpixel.offset.y()); 239 } 240 canvas->drawText(&character, 1, 241 x + subpixel.offset.x(), 242 y + subpixel.offset.y(), paint); 243 244 SkScalar dx = SkScalarCeilToScalar( 245 paint.measureText(&character, 1)) + 5; 246 x += dx; 247 xMax = SkTMax(x, xMax); 248 } 249 } 250 } 251 y += 10; 252 } 253 xBase = xMax; 254 } 255 } 256 257 DEF_SIMPLE_GM_BG_NAME(typefacerendering, canvas, 640, 680, SK_ColorWHITE, 258 SkStringPrintf("typefacerendering%s", 259 sk_tool_utils::platform_font_manager())) { 260 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/hintgasp.ttf")) { 261 draw_typeface_rendering_gm(canvas, std::move(face)); 262 } 263 } 264 265 // Type1 fonts don't currently work in Skia on Windows. 266 #ifndef SK_BUILD_FOR_WIN 267 268 DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfa, canvas, 640, 680, SK_ColorWHITE, 269 SkStringPrintf("typefacerendering_pfa%s", 270 sk_tool_utils::platform_font_manager())) { 271 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfa")) { 272 // This subsetted typeface doesn't have the character 'A'. 273 draw_typeface_rendering_gm(canvas, std::move(face), 'O'); 274 } 275 } 276 277 DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfb, canvas, 640, 680, SK_ColorWHITE, 278 SkStringPrintf("typefacerendering_pfb%s", 279 sk_tool_utils::platform_font_manager())) { 280 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfb")) { 281 draw_typeface_rendering_gm(canvas, std::move(face), 'O'); 282 } 283 } 284 285 #endif 286