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