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 
19 class VariedTextGM : public skiagm::GM {
20 public:
VariedTextGM(bool effectiveClip,bool lcd)21     VariedTextGM(bool effectiveClip, bool lcd)
22         : fEffectiveClip(effectiveClip)
23         , fLCD(lcd) {
24         memset(fTypefacesToUnref, 0, sizeof(fTypefacesToUnref));
25     }
26 
~VariedTextGM()27     ~VariedTextGM() {
28         for (size_t i = 0; i < SK_ARRAY_COUNT(fTypefacesToUnref); ++i) {
29             SkSafeUnref(fTypefacesToUnref[i]);
30         }
31     }
32 
33 protected:
onShortName()34     SkString onShortName() 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 
onISize()49     SkISize onISize() override {
50         return SkISize::Make(640, 480);
51     }
52 
onOnceBeforeDraw()53     void onOnceBeforeDraw() 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] = sk_tool_utils::create_portable_typeface("sans-serif", SkTypeface::kNormal);
63         fTypefacesToUnref[1] = sk_tool_utils::create_portable_typeface("sans-serif", SkTypeface::kBold);
64         fTypefacesToUnref[2] = sk_tool_utils::create_portable_typeface("serif", SkTypeface::kNormal);
65         fTypefacesToUnref[3] = sk_tool_utils::create_portable_typeface("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 
onDraw(SkCanvas * canvas)114     void onDraw(SkCanvas* canvas) 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 
runAsBench() const139     bool runAsBench() const override { return true; }
140 
141 private:
142     static const int kCnt = 30;
143     static const int kMinLength = 15;
144     static const int kMaxLength = 40;
145 
146     bool        fEffectiveClip;
147     bool        fLCD;
148     SkTypeface* fTypefacesToUnref[4];
149     SkPaint     fPaint;
150 
151     // precomputed for each text draw
152     SkString        fStrings[kCnt];
153     SkColor         fColors[kCnt];
154     SkScalar        fPtSizes[kCnt];
155     SkTypeface*     fTypefaces[kCnt];
156     SkPoint         fPositions[kCnt];
157     SkRect          fClipRects[kCnt];
158 
159     typedef skiagm::GM INHERITED;
160 };
161 
162 DEF_GM( return SkNEW(VariedTextGM(false, false)); )
163 DEF_GM( return SkNEW(VariedTextGM(true, false)); )
164 DEF_GM( return SkNEW(VariedTextGM(false, true)); )
165 DEF_GM( return SkNEW(VariedTextGM(true, true)); )
166