blob: 982e77fe9b20f3c986a2d5a8b95ab60dbec3f9e6 [file] [log] [blame]
joshualittd0b5c332015-04-10 06:17:26 -07001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#include "include/core/SkBlendMode.h"
10#include "include/core/SkBlurTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkCanvas.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040012#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "include/core/SkMaskFilter.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040018#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050029#include "include/core/SkTextBlob.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040030#include "include/core/SkTileMode.h"
31#include "include/core/SkTypeface.h"
32#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "include/effects/Sk2DPathEffect.h"
34#include "include/effects/SkColorMatrixFilter.h"
35#include "include/effects/SkGradientShader.h"
36#include "include/effects/SkLayerDrawLooper.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040037#include "include/private/SkTArray.h"
38#include "include/private/SkTDArray.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050039#include "src/core/SkBlurMask.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040040#include "tools/ToolUtils.h"
41
42#include <string.h>
joshualittd0b5c332015-04-10 06:17:26 -070043
44namespace skiagm {
45
mtkleindbfd7ab2016-09-01 11:24:54 -070046constexpr int kWidth = 1250;
47constexpr int kHeight = 700;
joshualittd0b5c332015-04-10 06:17:26 -070048
Mike Kleinea3f0142019-03-20 11:12:10 -050049// Unlike the variant in ToolUtils, this version positions the glyphs on a diagonal
Mike Reed2ed78202018-11-21 15:10:08 -050050static void add_to_text_blob(SkTextBlobBuilder* builder, const char* text, const SkFont& font,
joshualittd0b5c332015-04-10 06:17:26 -070051 SkScalar x, SkScalar y) {
joshualittd0b5c332015-04-10 06:17:26 -070052 SkTDArray<uint16_t> glyphs;
53
54 size_t len = strlen(text);
Ben Wagner51e15a62019-05-07 15:38:46 -040055 glyphs.append(font.countText(text, len, SkTextEncoding::kUTF8));
56 font.textToGlyphs(text, len, SkTextEncoding::kUTF8, glyphs.begin(), glyphs.count());
joshualittd0b5c332015-04-10 06:17:26 -070057
Mike Reed2ed78202018-11-21 15:10:08 -050058 const SkScalar advanceX = font.getSize() * 0.85f;
59 const SkScalar advanceY = font.getSize() * 1.5f;
joshualittd0b5c332015-04-10 06:17:26 -070060
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 }
Mike Reed2ed78202018-11-21 15:10:08 -050066 const SkTextBlobBuilder::RunBuffer& run = builder->allocRunPos(font, glyphs.count());
joshualittd0b5c332015-04-10 06:17:26 -070067 memcpy(run.glyphs, glyphs.begin(), glyphs.count() * sizeof(uint16_t));
68 memcpy(run.pos, pos.begin(), len * sizeof(SkScalar) * 2);
69}
70
71typedef void (*LooperProc)(SkPaint*);
72
73struct LooperSettings {
reed374772b2016-10-05 17:33:02 -070074 SkBlendMode fMode;
joshualittd0b5c332015-04-10 06:17:26 -070075 SkColor fColor;
76 SkPaint::Style fStyle;
77 SkScalar fWidth;
78 SkScalar fOffset;
79 SkScalar fSkewX;
80 bool fEffect;
81};
82
83static void mask_filter(SkPaint* paint) {
Mike Reed1be1f8d2018-03-14 13:01:17 -040084 paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle,
reedefdfd512016-04-04 10:02:58 -070085 SkBlurMask::ConvertRadiusToSigma(3.f)));
joshualittd0b5c332015-04-10 06:17:26 -070086}
87
reeda4393342016-03-18 11:22:57 -070088static sk_sp<SkPathEffect> make_tile_effect() {
joshualittd0b5c332015-04-10 06:17:26 -070089 SkMatrix m;
90 m.setScale(1.f, 1.f);
91
92 SkPath path;
93 path.addCircle(0, 0, SkIntToScalar(5));
94
reeda4393342016-03-18 11:22:57 -070095 return SkPath2DPathEffect::Make(m, path);
joshualittd0b5c332015-04-10 06:17:26 -070096}
97
98static void path_effect(SkPaint* paint) {
reeda4393342016-03-18 11:22:57 -070099 paint->setPathEffect(make_tile_effect());
joshualittd0b5c332015-04-10 06:17:26 -0700100}
101
reed1a9b9642016-03-13 14:13:58 -0700102static sk_sp<SkShader> make_shader(const SkRect& bounds) {
joshualittd0b5c332015-04-10 06:17:26 -0700103 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 };
reed1a9b9642016-03-13 14:13:58 -0700111 return SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors),
Mike Reedfae8fce2019-04-03 10:27:45 -0400112 SkTileMode::kClamp);
joshualittd0b5c332015-04-10 06:17:26 -0700113}
114
115static void color_filter(SkPaint* paint) {
116 SkRect r;
117 r.setWH(SkIntToScalar(kWidth), 50);
reed1a9b9642016-03-13 14:13:58 -0700118 paint->setShader(make_shader(r));
reedd053ce92016-03-22 10:17:23 -0700119 paint->setColorFilter(SkColorMatrixFilter::MakeLightingFilter(0xF0F0F0, 0));
joshualittd0b5c332015-04-10 06:17:26 -0700120}
121
122static void kitchen_sink(SkPaint* paint) {
123 color_filter(paint);
124 path_effect(paint);
125 mask_filter(paint);
126
127}
128
reed7b380d02016-03-21 13:25:16 -0700129static sk_sp<SkDrawLooper> setupLooper(SkLayerDrawLooper::BitFlags bits,
130 LooperProc proc,
131 const LooperSettings settings[],
132 size_t size) {
joshualittd0b5c332015-04-10 06:17:26 -0700133 SkLayerDrawLooper::Builder looperBuilder;
134
135 SkLayerDrawLooper::LayerInfo info;
136 info.fPaintBits = bits;
137
Mike Reedfaba3712016-11-03 14:45:31 -0400138 info.fColorMode = SkBlendMode::kSrc;
joshualittd0b5c332015-04-10 06:17:26 -0700139
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);
reed374772b2016-10-05 17:33:02 -0700143 paint->setBlendMode(settings[i].fMode);
joshualittd0b5c332015-04-10 06:17:26 -0700144 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 }
reed7b380d02016-03-21 13:25:16 -0700151 return looperBuilder.detach();
joshualittd0b5c332015-04-10 06:17:26 -0700152}
153
154class TextBlobLooperGM : public GM {
155public:
156 TextBlobLooperGM() {}
157
158protected:
159 void onOnceBeforeDraw() override {
160 SkTextBlobBuilder builder;
161
162 // LCD
Mike Reed2ed78202018-11-21 15:10:08 -0500163 SkFont font;
164 font.setSize(32);
joshualittd0b5c332015-04-10 06:17:26 -0700165 const char* text = "The quick brown fox jumps over the lazy dog";
Mike Reed2ed78202018-11-21 15:10:08 -0500166 font.setSubpixel(true);
167 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
Mike Kleinea3f0142019-03-20 11:12:10 -0500168 font.setTypeface(ToolUtils::create_portable_typeface());
Mike Reed2ed78202018-11-21 15:10:08 -0500169 add_to_text_blob(&builder, text, font, 0, 0);
fmalita37283c22016-09-13 10:00:23 -0700170 fBlob = builder.make();
joshualittd0b5c332015-04-10 06:17:26 -0700171
172 // create a looper which sandwhiches an effect in two normal draws
173 LooperSettings looperSandwhich[] = {
reed374772b2016-10-05 17:33:02 -0700174 { 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 },
joshualittd0b5c332015-04-10 06:17:26 -0700177 };
178
179 LooperSettings compound[] = {
reed374772b2016-10-05 17:33:02 -0700180 { 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 }
joshualittd0b5c332015-04-10 06:17:26 -0700184 };
185
186 LooperSettings xfermode[] = {
reed374772b2016-10-05 17:33:02 -0700187 { 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 },
joshualittd0b5c332015-04-10 06:17:26 -0700190 };
191
192 // NOTE, this should be ignored by textblobs
193 LooperSettings skew[] = {
reed374772b2016-10-05 17:33:02 -0700194 { 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 },
joshualittd0b5c332015-04-10 06:17:26 -0700197 };
198
199 LooperSettings kitchenSink[] = {
reed374772b2016-10-05 17:33:02 -0700200 { 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 },
joshualittd0b5c332015-04-10 06:17:26 -0700205 };
206
reed7b380d02016-03-21 13:25:16 -0700207 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)));
joshualittd0b5c332015-04-10 06:17:26 -0700230
231 // Test we respect overrides
reed7b380d02016-03-21 13:25:16 -0700232 fLoopers.push_back(setupLooper(0, &kitchen_sink,
233 kitchenSink, SK_ARRAY_COUNT(kitchenSink)));
joshualittd0b5c332015-04-10 06:17:26 -0700234 }
235
236 SkString onShortName() override {
237 return SkString("textbloblooper");
238 }
239
240 SkISize onISize() override {
241 return SkISize::Make(kWidth, kHeight);
242 }
243
244 void onDraw(SkCanvas* canvas) override {
245
Mike Kleind46dce32018-08-16 10:17:03 -0400246 canvas->drawColor(SK_ColorGRAY);
joshualittd0b5c332015-04-10 06:17:26 -0700247
248 SkPaint paint;
249 canvas->translate(10, 40);
250
joshualittd0b5c332015-04-10 06:17:26 -0700251 SkRect bounds = fBlob->bounds();
252
253 int y = 0;
254 for (int looper = 0; looper < fLoopers.count(); looper++) {
Mike Reedc9f0c592019-07-20 20:14:56 -0400255 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();
joshualittd0b5c332015-04-10 06:17:26 -0700262 y += SkScalarFloorToInt(bounds.height());
263 }
264 }
265
266private:
fmalita37283c22016-09-13 10:00:23 -0700267 sk_sp<SkTextBlob> fBlob;
Brian Salomon343553a2018-09-05 15:41:23 -0400268 SkTArray<sk_sp<SkDrawLooper>> fLoopers;
joshualittd0b5c332015-04-10 06:17:26 -0700269
270 typedef GM INHERITED;
271};
272
273//////////////////////////////////////////////////////////////////////////////
274
halcanary385fe4d2015-08-26 13:07:48 -0700275DEF_GM(return new TextBlobLooperGM;)
joshualittd0b5c332015-04-10 06:17:26 -0700276}