blurrect.cpp revision 72c9faab45124e08c85f70ca38536914862d947c
1/* 2* Copyright 2012 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 "SkBlurMask.h" 10#include "SkBlurMaskFilter.h" 11#include "SkCanvas.h" 12#include "SkPath.h" 13 14#define STROKE_WIDTH SkIntToScalar(10) 15 16typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&); 17 18static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { 19 canvas->drawRect(r, p); 20} 21 22static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { 23 SkRect rect; 24 SkPath path; 25 26 rect = r; 27 rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2); 28 path.addRect(rect); 29 rect = r; 30 rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2); 31 32 path.addRect(rect); 33 path.setFillType(SkPath::kEvenOdd_FillType); 34 35 canvas->drawPath(path, p); 36} 37 38static void draw_donut_skewed(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { 39 SkRect rect; 40 SkPath path; 41 42 rect = r; 43 rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2); 44 path.addRect(rect); 45 rect = r; 46 rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2); 47 48 rect.offset(7, -7); 49 50 path.addRect(rect); 51 path.setFillType(SkPath::kEvenOdd_FillType); 52 53 canvas->drawPath(path, p); 54} 55 56#include "SkGradientShader.h" 57 58/* 59 * Spits out a dummy gradient to test blur with shader on paint 60 */ 61static SkShader* MakeRadial() { 62 SkPoint pts[2] = { 63 { 0, 0 }, 64 { SkIntToScalar(100), SkIntToScalar(100) } 65 }; 66 SkShader::TileMode tm = SkShader::kClamp_TileMode; 67 const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, }; 68 const SkScalar pos[] = { SK_Scalar1/4, SK_Scalar1*3/4 }; 69 SkMatrix scale; 70 scale.setScale(0.5f, 0.5f); 71 scale.postTranslate(25.f, 25.f); 72 SkPoint center0, center1; 73 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 74 SkScalarAve(pts[0].fY, pts[1].fY)); 75 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 76 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 77 return SkGradientShader::CreateTwoPointRadial(center1, (pts[1].fX - pts[0].fX) / 7, 78 center0, (pts[1].fX - pts[0].fX) / 2, 79 colors, pos, SK_ARRAY_COUNT(colors), tm, 80 0, &scale); 81} 82 83typedef void (*PaintProc)(SkPaint*, SkScalar width); 84 85class BlurRectGM : public skiagm::GM { 86 SkAutoTUnref<SkMaskFilter> fMaskFilters[kLastEnum_SkBlurStyle + 1]; 87 SkString fName; 88 SkAlpha fAlpha; 89public: 90 BlurRectGM(const char name[], U8CPU alpha) 91 : fName(name) 92 , fAlpha(SkToU8(alpha)) { 93 } 94 95protected: 96 void onOnceBeforeDraw() SK_OVERRIDE { 97 for (int i = 0; i <= kLastEnum_SkBlurStyle; ++i) { 98 fMaskFilters[i].reset(SkBlurMaskFilter::Create((SkBlurStyle)i, 99 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2)), 100 SkBlurMaskFilter::kHighQuality_BlurFlag)); 101 } 102 } 103 104 SkString onShortName() SK_OVERRIDE { 105 return fName; 106 } 107 108 SkISize onISize() SK_OVERRIDE { 109 return SkISize::Make(860, 820); 110 } 111 112 void onDraw(SkCanvas* canvas) SK_OVERRIDE { 113 canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2); 114 115 SkRect r = { 0, 0, 100, 50 }; 116 SkScalar scales[] = { SK_Scalar1, 0.6f }; 117 118 for (size_t s = 0; s < SK_ARRAY_COUNT(scales); ++s) { 119 canvas->save(); 120 for (size_t f = 0; f < SK_ARRAY_COUNT(fMaskFilters); ++f) { 121 SkPaint paint; 122 paint.setMaskFilter(fMaskFilters[f]); 123 paint.setAlpha(fAlpha); 124 125 SkPaint paintWithRadial = paint; 126 paintWithRadial.setShader(MakeRadial())->unref(); 127 128 static const Proc procs[] = { 129 fill_rect, draw_donut, draw_donut_skewed 130 }; 131 132 canvas->save(); 133 canvas->scale(scales[s], scales[s]); 134 this->drawProcs(canvas, r, paint, false, procs, SK_ARRAY_COUNT(procs)); 135 canvas->translate(r.width() * 4/3, 0); 136 this->drawProcs(canvas, r, paintWithRadial, false, procs, SK_ARRAY_COUNT(procs)); 137 canvas->translate(r.width() * 4/3, 0); 138 this->drawProcs(canvas, r, paint, true, procs, SK_ARRAY_COUNT(procs)); 139 canvas->translate(r.width() * 4/3, 0); 140 this->drawProcs(canvas, r, paintWithRadial, true, procs, SK_ARRAY_COUNT(procs)); 141 canvas->restore(); 142 143 canvas->translate(0, SK_ARRAY_COUNT(procs) * r.height() * 4/3 * scales[s]); 144 } 145 canvas->restore(); 146 canvas->translate(4 * r.width() * 4/3 * scales[s], 0); 147 } 148 } 149 150 uint32_t onGetFlags() const SK_OVERRIDE { return kSkipPipe_Flag | kSkipTiled_Flag; } 151 152private: 153 void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint, 154 bool doClip, const Proc procs[], size_t procsCount) { 155 SkAutoCanvasRestore acr(canvas, true); 156 for (size_t i = 0; i < procsCount; ++i) { 157 if (doClip) { 158 SkRect clipRect(r); 159 clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2); 160 canvas->save(); 161 canvas->clipRect(r); 162 } 163 procs[i](canvas, r, paint); 164 if (doClip) { 165 canvas->restore(); 166 } 167 canvas->translate(0, r.height() * 4/3); 168 } 169 } 170private: 171 typedef GM INHERITED; 172}; 173 174 175class BlurRectDirectGM : public skiagm::GM { 176 SkString fName; 177 int fGMWidth, fGMHeight; 178 int fPadding, fMargin; 179public: 180 BlurRectDirectGM(const char name[]) 181 : fName(name), 182 fGMWidth(1200), 183 fGMHeight(1024), 184 fPadding(10), 185 fMargin(100) 186 { 187 } 188 189protected: 190 virtual SkString onShortName() { 191 return fName; 192 } 193 194 virtual SkISize onISize() { 195 return SkISize::Make(fGMWidth, fGMHeight); 196 } 197 198 virtual void onDraw(SkCanvas* canvas) { 199 const int widths[] = {25, 5, 5, 100, 150, 25}; 200 const int heights[] = {100, 100, 5, 25, 150, 25}; 201 const SkBlurStyle styles[] = {kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle}; 202 const float radii[] = {20, 5, 10}; 203 204 canvas->translate(50,20); 205 206 int cur_x = 0; 207 int cur_y = 0; 208 209 int max_height = 0; 210 211 for (size_t i = 0 ; i < SK_ARRAY_COUNT(widths) ; i++) { 212 int width = widths[i]; 213 int height = heights[i]; 214 SkRect r; 215 r.setWH(SkIntToScalar(width), SkIntToScalar(height)); 216 SkAutoCanvasRestore autoRestore(canvas, true); 217 218 for (size_t j = 0 ; j < SK_ARRAY_COUNT(radii) ; j++) { 219 float radius = radii[j]; 220 for (size_t k = 0 ; k < SK_ARRAY_COUNT(styles) ; k++) { 221 SkBlurStyle style = styles[k]; 222 223 SkMask mask; 224 SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(radius), &mask, r, style); 225 226 SkAutoMaskFreeImage amfi(mask.fImage); 227 228 SkBitmap bm; 229 bm.installMaskPixels(mask); 230 231 if (cur_x + bm.width() >= fGMWidth - fMargin) { 232 cur_x = 0; 233 cur_y += max_height + fPadding; 234 max_height = 0; 235 } 236 237 canvas->save(); 238 canvas->translate((SkScalar)cur_x, (SkScalar)cur_y); 239 canvas->translate(-(bm.width() - r.width())/2, -(bm.height()-r.height())/2); 240 canvas->drawBitmap(bm, 0.f, 0.f, NULL); 241 canvas->restore(); 242 243 cur_x += bm.width() + fPadding; 244 if (bm.height() > max_height) 245 max_height = bm.height(); 246 } 247 } 248 } 249 } 250 251 virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; } 252 253private: 254 typedef GM INHERITED; 255}; 256 257class BlurRectCompareGM : public skiagm::GM { 258 SkString fName; 259 unsigned int fRectWidth, fRectHeight; 260 SkScalar fRadius; 261 SkBlurStyle fStyle; 262public: 263 BlurRectCompareGM(const char name[], unsigned int rectWidth, 264 unsigned int rectHeight, float radius, 265 SkBlurStyle style) 266 : fName(name) 267 , fRectWidth(rectWidth) 268 , fRectHeight(rectHeight) 269 , fRadius(radius) 270 , fStyle(style) { 271 } 272 int width() const { 273 return fRectWidth; 274 } 275 int height() const { 276 return fRectHeight; 277 } 278 SkScalar radius() const { 279 return fRadius; 280 } 281 SkBlurStyle style() const { 282 return fStyle; 283 } 284 285protected: 286 virtual SkString onShortName() { 287 return fName; 288 } 289 290 virtual SkISize onISize() { 291 return SkISize::Make(640, 480); 292 } 293 294 virtual bool makeMask(SkMask *m, const SkRect&) = 0; 295 296 virtual void onDraw(SkCanvas* canvas) { 297 SkRect r; 298 r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight)); 299 300 SkISize canvas_size = canvas->getDeviceSize(); 301 int center_x = (canvas_size.fWidth - (int)(r.width()))/2; 302 int center_y = (canvas_size.fHeight - (int)(r.height()))/2; 303 304 SkMask mask; 305 306 if (!this->makeMask(&mask, r)) { 307 SkPaint paint; 308 r.offset( SkIntToScalar(center_x), SkIntToScalar(center_y) ); 309 canvas->drawRect(r,paint); 310 return; 311 } 312 SkAutoMaskFreeImage amfi(mask.fImage); 313 314 SkBitmap bm; 315 bm.installMaskPixels(mask); 316 317 center_x = (canvas_size.fWidth - mask.fBounds.width())/2; 318 center_y = (canvas_size.fHeight - mask.fBounds.height())/2; 319 320 canvas->drawBitmap(bm, SkIntToScalar(center_x), SkIntToScalar(center_y), NULL); 321 } 322 323 virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; } 324 325private: 326 typedef GM INHERITED; 327}; 328 329class BlurRectFastGM: public BlurRectCompareGM { 330public: 331 BlurRectFastGM(const char name[], unsigned int rectWidth, 332 unsigned int rectHeight, float blurRadius, 333 SkBlurStyle style) : 334 INHERITED(name, rectWidth, rectHeight, blurRadius, style) { 335 } 336 337protected: 338 bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE { 339 return SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(this->radius()), 340 m, r, this->style()); 341 } 342private: 343 typedef BlurRectCompareGM INHERITED; 344}; 345 346class BlurRectSlowGM: public BlurRectCompareGM { 347public: 348 BlurRectSlowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, 349 float blurRadius, SkBlurStyle style) 350 : INHERITED(name, rectWidth, rectHeight, blurRadius, style) { 351 } 352 353protected: 354 bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE { 355 SkMask src; 356 r.roundOut(&src.fBounds); 357 src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin 358 src.fFormat = SkMask::kA8_Format; 359 src.fRowBytes = src.fBounds.width(); 360 src.fImage = SkMask::AllocImage(src.computeTotalImageSize()); 361 SkAutoMaskFreeImage amfi(src.fImage); 362 363 memset(src.fImage, 0xff, src.computeTotalImageSize()); 364 365 return SkBlurMask::BoxBlur(m, src, 366 SkBlurMask::ConvertRadiusToSigma(this->radius()), 367 this->style(), this->getQuality()); 368 } 369 370 virtual SkBlurQuality getQuality() { 371 return kHigh_SkBlurQuality; 372 } 373private: 374 typedef BlurRectCompareGM INHERITED; 375}; 376 377class BlurRectSlowLowGM: public BlurRectSlowGM { 378public: 379 BlurRectSlowLowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, 380 float blurRadius, SkBlurStyle style) 381 : INHERITED(name, rectWidth, rectHeight, blurRadius, style) { 382 } 383 384protected: 385 SkBlurQuality getQuality() SK_OVERRIDE { 386 return kLow_SkBlurQuality; 387 } 388private: 389 typedef BlurRectSlowGM INHERITED; 390}; 391 392class BlurRectGroundTruthGM: public BlurRectCompareGM { 393public: 394 BlurRectGroundTruthGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, 395 float blurRadius, SkBlurStyle style) 396 : INHERITED(name, rectWidth, rectHeight, blurRadius, style) { 397 } 398 399protected: 400 bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE { 401 SkMask src; 402 r.roundOut(&src.fBounds); 403 src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin 404 src.fFormat = SkMask::kA8_Format; 405 src.fRowBytes = src.fBounds.width(); 406 src.fImage = SkMask::AllocImage(src.computeTotalImageSize()); 407 SkAutoMaskFreeImage amfi(src.fImage); 408 409 memset(src.fImage, 0xff, src.computeTotalImageSize()); 410 411 return SkBlurMask::BlurGroundTruth(SkBlurMask::ConvertRadiusToSigma(this->radius()), 412 m, src, this->style()); 413 } 414 415 virtual SkBlurQuality getQuality() { 416 return kHigh_SkBlurQuality; 417 } 418private: 419 typedef BlurRectCompareGM INHERITED; 420}; 421 422 423////////////////////////////////////////////////////////////////////////////// 424 425DEF_GM(return new BlurRectGM("blurrects", 0xFF);) 426DEF_GM(return new BlurRectDirectGM("blurrect_gallery");) 427