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