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