Home | History | Annotate | Download | only in gm
      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