1/*
2 * Copyright 2014 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 "SkCanvas.h"
11#include "SkPath.h"
12#include "SkTypeface.h"
13#include "SkRandom.h"
14
15/**
16 * Draws text with random parameters. The text draws each get their own clip rect. It is also
17 * used as a bench to measure how well the GPU backend combines draw ops for text draws.
18 */
19
20class VariedTextGM : public skiagm::GM {
21public:
22    VariedTextGM(bool effectiveClip, bool lcd)
23        : fEffectiveClip(effectiveClip)
24        , fLCD(lcd) {
25    }
26
27protected:
28    SkString onShortName() override {
29        SkString name("varied_text");
30        if (fEffectiveClip) {
31            name.append("_clipped");
32        } else {
33            name.append("_ignorable_clip");
34        }
35        if (fLCD) {
36            name.append("_lcd");
37        } else {
38            name.append("_no_lcd");
39        }
40        return name;
41    }
42
43    SkISize onISize() override {
44        return SkISize::Make(640, 480);
45    }
46
47    void onOnceBeforeDraw() override {
48        fPaint.setAntiAlias(true);
49        fPaint.setLCDRenderText(fLCD);
50
51        SkISize size = this->getISize();
52        SkScalar w = SkIntToScalar(size.fWidth);
53        SkScalar h = SkIntToScalar(size.fHeight);
54
55        static_assert(4 == SK_ARRAY_COUNT(fTypefaces), "typeface_cnt");
56        fTypefaces[0] = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle());
57        fTypefaces[1] = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Bold());
58        fTypefaces[2] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle());
59        fTypefaces[3] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Bold());
60
61        SkRandom random;
62        for (int i = 0; i < kCnt; ++i) {
63            int length = random.nextRangeU(kMinLength, kMaxLength);
64            char text[kMaxLength];
65            for (int j = 0; j < length; ++j) {
66                text[j] = (char)random.nextRangeU('!', 'z');
67            }
68            fStrings[i].set(text, length);
69
70            fColors[i] = random.nextU();
71            fColors[i] |= 0xFF000000;
72            fColors[i] = sk_tool_utils::color_to_565(fColors[i]);
73
74            constexpr SkScalar kMinPtSize = 8.f;
75            constexpr SkScalar kMaxPtSize = 32.f;
76
77            fPtSizes[i] = random.nextRangeScalar(kMinPtSize, kMaxPtSize);
78
79            fTypefaceIndices[i] = random.nextULessThan(SK_ARRAY_COUNT(fTypefaces));
80
81            SkRect r;
82            fPaint.setColor(fColors[i]);
83            fPaint.setTypeface(fTypefaces[fTypefaceIndices[i]]);
84            fPaint.setTextSize(fPtSizes[i]);
85
86            fPaint.measureText(fStrings[i].c_str(), fStrings[i].size(), &r);
87            // safeRect is set of x,y positions where we can draw the string without hitting
88            // the GM's border.
89            SkRect safeRect = SkRect::MakeLTRB(-r.fLeft, -r.fTop, w - r.fRight, h - r.fBottom);
90            if (safeRect.isEmpty()) {
91                // If we don't fit then just don't worry about how we get cliped to the device
92                // border.
93                safeRect = SkRect::MakeWH(w, h);
94            }
95            fPositions[i].fX = random.nextRangeScalar(safeRect.fLeft, safeRect.fRight);
96            fPositions[i].fY = random.nextRangeScalar(safeRect.fTop, safeRect.fBottom);
97
98            fClipRects[i] = r;
99            fClipRects[i].offset(fPositions[i].fX, fPositions[i].fY);
100            fClipRects[i].outset(2.f, 2.f);
101
102            if (fEffectiveClip) {
103                fClipRects[i].fRight -= 0.25f * fClipRects[i].width();
104            }
105        }
106    }
107
108    void onDraw(SkCanvas* canvas) override {
109        for (int i = 0; i < kCnt; ++i) {
110            fPaint.setColor(fColors[i]);
111            fPaint.setTextSize(fPtSizes[i]);
112            fPaint.setTypeface(fTypefaces[fTypefaceIndices[i]]);
113
114            canvas->save();
115                canvas->clipRect(fClipRects[i]);
116                canvas->translate(fPositions[i].fX, fPositions[i].fY);
117                canvas->drawString(fStrings[i], 0, 0, fPaint);
118            canvas->restore();
119        }
120
121        // Visualize the clips, but not in bench mode.
122        if (kBench_Mode != this->getMode()) {
123            SkPaint wirePaint;
124            wirePaint.setAntiAlias(true);
125            wirePaint.setStrokeWidth(0);
126            wirePaint.setStyle(SkPaint::kStroke_Style);
127            for (int i = 0; i < kCnt; ++i) {
128                canvas->drawRect(fClipRects[i], wirePaint);
129            }
130        }
131    }
132
133    bool runAsBench() const override { return true; }
134
135private:
136    static constexpr int kCnt = 30;
137    static constexpr int kMinLength = 15;
138    static constexpr int kMaxLength = 40;
139
140    bool        fEffectiveClip;
141    bool        fLCD;
142    sk_sp<SkTypeface> fTypefaces[4];
143    SkPaint     fPaint;
144
145    // precomputed for each text draw
146    SkString        fStrings[kCnt];
147    SkColor         fColors[kCnt];
148    SkScalar        fPtSizes[kCnt];
149    int             fTypefaceIndices[kCnt];
150    SkPoint         fPositions[kCnt];
151    SkRect          fClipRects[kCnt];
152
153    typedef skiagm::GM INHERITED;
154};
155
156DEF_GM(return new VariedTextGM(false, false);)
157DEF_GM(return new VariedTextGM(true, false);)
158DEF_GM(return new VariedTextGM(false, true);)
159DEF_GM(return new VariedTextGM(true, true);)
160