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 "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