1 /*
2  * Copyright 2013 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/SkBlendMode.h"
10 #include "include/core/SkBlurTypes.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorFilter.h"
14 #include "include/core/SkDrawLooper.h"
15 #include "include/core/SkFont.h"
16 #include "include/core/SkFontTypes.h"
17 #include "include/core/SkMaskFilter.h"
18 #include "include/core/SkMatrix.h"
19 #include "include/core/SkPaint.h"
20 #include "include/core/SkPath.h"
21 #include "include/core/SkPathEffect.h"
22 #include "include/core/SkPoint.h"
23 #include "include/core/SkRect.h"
24 #include "include/core/SkRefCnt.h"
25 #include "include/core/SkScalar.h"
26 #include "include/core/SkShader.h"
27 #include "include/core/SkSize.h"
28 #include "include/core/SkString.h"
29 #include "include/core/SkTextBlob.h"
30 #include "include/core/SkTileMode.h"
31 #include "include/core/SkTypeface.h"
32 #include "include/core/SkTypes.h"
33 #include "include/effects/Sk2DPathEffect.h"
34 #include "include/effects/SkColorMatrixFilter.h"
35 #include "include/effects/SkGradientShader.h"
36 #include "include/effects/SkLayerDrawLooper.h"
37 #include "include/private/SkTArray.h"
38 #include "include/private/SkTDArray.h"
39 #include "src/core/SkBlurMask.h"
40 #include "tools/ToolUtils.h"
41 
42 #include <string.h>
43 
44 namespace skiagm {
45 
46 constexpr int kWidth = 1250;
47 constexpr int kHeight = 700;
48 
49 // Unlike the variant in ToolUtils, this version positions the glyphs on a diagonal
add_to_text_blob(SkTextBlobBuilder * builder,const char * text,const SkFont & font,SkScalar x,SkScalar y)50 static void add_to_text_blob(SkTextBlobBuilder* builder, const char* text, const SkFont& font,
51                              SkScalar x, SkScalar y) {
52     SkTDArray<uint16_t> glyphs;
53 
54     size_t len = strlen(text);
55     glyphs.append(font.countText(text, len, SkTextEncoding::kUTF8));
56     font.textToGlyphs(text, len, SkTextEncoding::kUTF8, glyphs.begin(), glyphs.count());
57 
58     const SkScalar advanceX = font.getSize() * 0.85f;
59     const SkScalar advanceY = font.getSize() * 1.5f;
60 
61     SkTDArray<SkScalar> pos;
62     for (unsigned i = 0; i < len; ++i) {
63         *pos.append() = x + i * advanceX;
64         *pos.append() = y + i * (advanceY / len);
65     }
66     const SkTextBlobBuilder::RunBuffer& run = builder->allocRunPos(font, glyphs.count());
67     memcpy(run.glyphs, glyphs.begin(), glyphs.count() * sizeof(uint16_t));
68     memcpy(run.pos, pos.begin(), len * sizeof(SkScalar) * 2);
69 }
70 
71 typedef void (*LooperProc)(SkPaint*);
72 
73 struct LooperSettings {
74     SkBlendMode      fMode;
75     SkColor          fColor;
76     SkPaint::Style   fStyle;
77     SkScalar         fWidth;
78     SkScalar         fOffset;
79     SkScalar         fSkewX;
80     bool             fEffect;
81 };
82 
mask_filter(SkPaint * paint)83 static void mask_filter(SkPaint* paint) {
84     paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle,
85                                                 SkBlurMask::ConvertRadiusToSigma(3.f)));
86 }
87 
make_tile_effect()88 static sk_sp<SkPathEffect> make_tile_effect() {
89     SkMatrix m;
90     m.setScale(1.f, 1.f);
91 
92     SkPath path;
93     path.addCircle(0, 0, SkIntToScalar(5));
94 
95     return SkPath2DPathEffect::Make(m, path);
96 }
97 
path_effect(SkPaint * paint)98 static void path_effect(SkPaint* paint) {
99     paint->setPathEffect(make_tile_effect());
100 }
101 
make_shader(const SkRect & bounds)102 static sk_sp<SkShader> make_shader(const SkRect& bounds) {
103     const SkPoint pts[] = {
104         { bounds.left(), bounds.top() },
105         { bounds.right(), bounds.bottom() },
106     };
107     const SkColor colors[] = {
108         SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK,
109         SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorYELLOW,
110     };
111     return SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors),
112                                         SkTileMode::kClamp);
113 }
114 
color_filter(SkPaint * paint)115 static void color_filter(SkPaint* paint) {
116     SkRect r;
117     r.setWH(SkIntToScalar(kWidth), 50);
118     paint->setShader(make_shader(r));
119     paint->setColorFilter(SkColorMatrixFilter::MakeLightingFilter(0xF0F0F0, 0));
120 }
121 
kitchen_sink(SkPaint * paint)122 static void kitchen_sink(SkPaint* paint) {
123     color_filter(paint);
124     path_effect(paint);
125     mask_filter(paint);
126 
127 }
128 
setupLooper(SkLayerDrawLooper::BitFlags bits,LooperProc proc,const LooperSettings settings[],size_t size)129 static sk_sp<SkDrawLooper> setupLooper(SkLayerDrawLooper::BitFlags bits,
130                                        LooperProc proc,
131                                        const LooperSettings settings[],
132                                        size_t size) {
133     SkLayerDrawLooper::Builder looperBuilder;
134 
135     SkLayerDrawLooper::LayerInfo info;
136     info.fPaintBits = bits;
137 
138     info.fColorMode = SkBlendMode::kSrc;
139 
140     for (size_t i = 0; i < size; i++) {
141         info.fOffset.set(settings[i].fOffset, settings[i].fOffset);
142         SkPaint* paint = looperBuilder.addLayer(info);
143         paint->setBlendMode(settings[i].fMode);
144         paint->setColor(settings[i].fColor);
145         paint->setStyle(settings[i].fStyle);
146         paint->setStrokeWidth(settings[i].fWidth);
147         if (settings[i].fEffect) {
148             (*proc)(paint);
149         }
150     }
151     return looperBuilder.detach();
152 }
153 
154 class TextBlobLooperGM : public GM {
155 public:
TextBlobLooperGM()156     TextBlobLooperGM() {}
157 
158 protected:
onOnceBeforeDraw()159     void onOnceBeforeDraw() override {
160         SkTextBlobBuilder builder;
161 
162         // LCD
163         SkFont font;
164         font.setSize(32);
165         const char* text = "The quick brown fox jumps over the lazy dog";
166         font.setSubpixel(true);
167         font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
168         font.setTypeface(ToolUtils::create_portable_typeface());
169         add_to_text_blob(&builder, text, font, 0, 0);
170         fBlob = builder.make();
171 
172         // create a looper which sandwhiches an effect in two normal draws
173         LooperSettings looperSandwhich[] = {
174            { SkBlendMode::kSrc, SK_ColorMAGENTA, SkPaint::kFill_Style, 0, 0, 0, false },
175            { SkBlendMode::kSrcOver, 0x88000000, SkPaint::kFill_Style, 0, 10.f, 0, true },
176            { SkBlendMode::kSrcOver, 0x50FF00FF, SkPaint::kFill_Style, 0, 20.f, 0, false },
177         };
178 
179         LooperSettings compound[] = {
180             { SkBlendMode::kSrc, SK_ColorWHITE, SkPaint::kStroke_Style, 1.f * 3/4, 0, 0, false },
181             { SkBlendMode::kSrc, SK_ColorRED, SkPaint::kStroke_Style, 4.f, 0, 0, false },
182             { SkBlendMode::kSrc, SK_ColorBLUE, SkPaint::kFill_Style, 0, 0, 0, false },
183             { SkBlendMode::kSrcOver, 0x88000000, SkPaint::kFill_Style, 0, 10.f, 0, true }
184         };
185 
186         LooperSettings xfermode[] = {
187             { SkBlendMode::kDifference, SK_ColorWHITE, SkPaint::kFill_Style, 0, 0, 0, false },
188             { SkBlendMode::kSrcOver, 0xFF000000, SkPaint::kFill_Style, 0, 1.f, 0, true },
189             { SkBlendMode::kSrcOver, 0x50FF00FF, SkPaint::kFill_Style, 0, 2.f, 0, false },
190         };
191 
192         // NOTE, this should be ignored by textblobs
193         LooperSettings skew[] = {
194             { SkBlendMode::kSrc, SK_ColorRED, SkPaint::kFill_Style, 0, 0, -1.f, false },
195             { SkBlendMode::kSrc, SK_ColorGREEN, SkPaint::kFill_Style, 0, 10.f, -1.f, false },
196             { SkBlendMode::kSrc, SK_ColorBLUE, SkPaint::kFill_Style, 0, 20.f, -1.f, false },
197         };
198 
199         LooperSettings kitchenSink[] = {
200             { SkBlendMode::kSrc, SK_ColorWHITE, SkPaint::kStroke_Style, 1.f * 3/4, 0, 0, false },
201             { SkBlendMode::kSrc, SK_ColorBLACK, SkPaint::kFill_Style, 0, 0, 0, false },
202             { SkBlendMode::kDifference, SK_ColorWHITE, SkPaint::kFill_Style, 1.f, 10.f, 0, false },
203             { SkBlendMode::kSrc, SK_ColorWHITE, SkPaint::kFill_Style, 0, 10.f, 0, true },
204             { SkBlendMode::kSrcOver, 0x50FF00FF, SkPaint::kFill_Style, 0, 20.f, 0, false },
205         };
206 
207         fLoopers.push_back(setupLooper(SkLayerDrawLooper::kMaskFilter_Bit |
208                                        SkLayerDrawLooper::kXfermode_Bit |
209                                        SkLayerDrawLooper::kStyle_Bit, &mask_filter,
210                                        compound, SK_ARRAY_COUNT(compound)));
211         fLoopers.push_back(setupLooper(SkLayerDrawLooper::kPathEffect_Bit |
212                                        SkLayerDrawLooper::kXfermode_Bit, &path_effect,
213                                        looperSandwhich, SK_ARRAY_COUNT(looperSandwhich)));
214         fLoopers.push_back(setupLooper(SkLayerDrawLooper::kShader_Bit |
215                                        SkLayerDrawLooper::kColorFilter_Bit |
216                                        SkLayerDrawLooper::kXfermode_Bit, &color_filter,
217                                        looperSandwhich, SK_ARRAY_COUNT(looperSandwhich)));
218         fLoopers.push_back(setupLooper(SkLayerDrawLooper::kShader_Bit |
219                                        SkLayerDrawLooper::kColorFilter_Bit |
220                                        SkLayerDrawLooper::kXfermode_Bit, &color_filter,
221                                        xfermode, SK_ARRAY_COUNT(xfermode)));
222         fLoopers.push_back(setupLooper(0, nullptr, skew, SK_ARRAY_COUNT(skew)));
223         fLoopers.push_back(setupLooper(SkLayerDrawLooper::kMaskFilter_Bit |
224                                        SkLayerDrawLooper::kShader_Bit |
225                                        SkLayerDrawLooper::kColorFilter_Bit |
226                                        SkLayerDrawLooper::kPathEffect_Bit |
227                                        SkLayerDrawLooper::kStyle_Bit |
228                                        SkLayerDrawLooper::kXfermode_Bit, &kitchen_sink,
229                                        kitchenSink, SK_ARRAY_COUNT(kitchenSink)));
230 
231         // Test we respect overrides
232         fLoopers.push_back(setupLooper(0, &kitchen_sink,
233                                        kitchenSink, SK_ARRAY_COUNT(kitchenSink)));
234     }
235 
onShortName()236     SkString onShortName() override {
237         return SkString("textbloblooper");
238     }
239 
onISize()240     SkISize onISize() override {
241         return SkISize::Make(kWidth, kHeight);
242     }
243 
onDraw(SkCanvas * canvas)244     void onDraw(SkCanvas* canvas) override {
245 
246         canvas->drawColor(SK_ColorGRAY);
247 
248         SkPaint paint;
249         canvas->translate(10, 40);
250 
251         SkRect bounds = fBlob->bounds();
252 
253         int y = 0;
254         for (int looper = 0; looper < fLoopers.count(); looper++) {
255             SkTextBlob* b = fBlob.get();
256             canvas->save();
257             canvas->translate(0, SkIntToScalar(y));
258             fLoopers[looper]->apply(canvas, paint, [b](SkCanvas* c, const SkPaint& p) {
259                 c->drawTextBlob(b, 0, 0, p);
260             });
261             canvas->restore();
262             y += SkScalarFloorToInt(bounds.height());
263         }
264     }
265 
266 private:
267     sk_sp<SkTextBlob> fBlob;
268     SkTArray<sk_sp<SkDrawLooper>> fLoopers;
269 
270     typedef GM INHERITED;
271 };
272 
273 //////////////////////////////////////////////////////////////////////////////
274 
275 DEF_GM(return new TextBlobLooperGM;)
276 }
277