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