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