1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2011 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h" 9cb93a386Sopenharmony_ci#include "include/core/SkBlendMode.h" 10cb93a386Sopenharmony_ci#include "include/core/SkBlurTypes.h" 11cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h" 12cb93a386Sopenharmony_ci#include "include/core/SkColor.h" 13cb93a386Sopenharmony_ci#include "include/core/SkColorPriv.h" 14cb93a386Sopenharmony_ci#include "include/core/SkImageInfo.h" 15cb93a386Sopenharmony_ci#include "include/core/SkMaskFilter.h" 16cb93a386Sopenharmony_ci#include "include/core/SkMath.h" 17cb93a386Sopenharmony_ci#include "include/core/SkPaint.h" 18cb93a386Sopenharmony_ci#include "include/core/SkPath.h" 19cb93a386Sopenharmony_ci#include "include/core/SkPixmap.h" 20cb93a386Sopenharmony_ci#include "include/core/SkPoint.h" 21cb93a386Sopenharmony_ci#include "include/core/SkRRect.h" 22cb93a386Sopenharmony_ci#include "include/core/SkRect.h" 23cb93a386Sopenharmony_ci#include "include/core/SkRefCnt.h" 24cb93a386Sopenharmony_ci#include "include/core/SkScalar.h" 25cb93a386Sopenharmony_ci#include "include/core/SkShader.h" 26cb93a386Sopenharmony_ci#include "include/core/SkSize.h" 27cb93a386Sopenharmony_ci#include "include/core/SkSurface.h" 28cb93a386Sopenharmony_ci#include "include/core/SkTypes.h" 29cb93a386Sopenharmony_ci#include "include/effects/SkPerlinNoiseShader.h" 30cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h" 31cb93a386Sopenharmony_ci#include "include/private/SkFloatBits.h" 32cb93a386Sopenharmony_ci#include "include/private/SkTPin.h" 33cb93a386Sopenharmony_ci#include "src/core/SkBlurMask.h" 34cb93a386Sopenharmony_ci#include "src/core/SkGpuBlurUtils.h" 35cb93a386Sopenharmony_ci#include "src/core/SkMask.h" 36cb93a386Sopenharmony_ci#include "src/core/SkMaskFilterBase.h" 37cb93a386Sopenharmony_ci#include "src/core/SkMathPriv.h" 38cb93a386Sopenharmony_ci#include "src/effects/SkEmbossMaskFilter.h" 39cb93a386Sopenharmony_ci#include "tests/Test.h" 40cb93a386Sopenharmony_ci#include "tools/ToolUtils.h" 41cb93a386Sopenharmony_ci#include "tools/gpu/GrContextFactory.h" 42cb93a386Sopenharmony_ci 43cb93a386Sopenharmony_ci#include <math.h> 44cb93a386Sopenharmony_ci#include <string.h> 45cb93a386Sopenharmony_ci#include <initializer_list> 46cb93a386Sopenharmony_ci#include <utility> 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_ci#define WRITE_CSV 0 49cb93a386Sopenharmony_ci 50cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ci#define ILLEGAL_MODE ((SkXfermode::Mode)-1) 53cb93a386Sopenharmony_ci 54cb93a386Sopenharmony_cistatic const int outset = 100; 55cb93a386Sopenharmony_cistatic const SkColor bgColor = SK_ColorWHITE; 56cb93a386Sopenharmony_cistatic const int strokeWidth = 4; 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_cistatic void create(SkBitmap* bm, const SkIRect& bound) { 59cb93a386Sopenharmony_ci bm->allocN32Pixels(bound.width(), bound.height()); 60cb93a386Sopenharmony_ci} 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_cistatic void drawBG(SkCanvas* canvas) { 63cb93a386Sopenharmony_ci canvas->drawColor(bgColor); 64cb93a386Sopenharmony_ci} 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci 67cb93a386Sopenharmony_cistruct BlurTest { 68cb93a386Sopenharmony_ci void (*addPath)(SkPath*); 69cb93a386Sopenharmony_ci int viewLen; 70cb93a386Sopenharmony_ci SkIRect views[9]; 71cb93a386Sopenharmony_ci}; 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ci//Path Draw Procs 74cb93a386Sopenharmony_ci//Beware that paths themselves my draw differently depending on the clip. 75cb93a386Sopenharmony_cistatic void draw50x50Rect(SkPath* path) { 76cb93a386Sopenharmony_ci path->addRect(0, 0, SkIntToScalar(50), SkIntToScalar(50)); 77cb93a386Sopenharmony_ci} 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci//Tests 80cb93a386Sopenharmony_cistatic BlurTest tests[] = { 81cb93a386Sopenharmony_ci { draw50x50Rect, 3, { 82cb93a386Sopenharmony_ci //inner half of blur 83cb93a386Sopenharmony_ci { 0, 0, 50, 50 }, 84cb93a386Sopenharmony_ci //blur, but no path. 85cb93a386Sopenharmony_ci { 50 + strokeWidth/2, 50 + strokeWidth/2, 100, 100 }, 86cb93a386Sopenharmony_ci //just an edge 87cb93a386Sopenharmony_ci { 40, strokeWidth, 60, 50 - strokeWidth }, 88cb93a386Sopenharmony_ci }}, 89cb93a386Sopenharmony_ci}; 90cb93a386Sopenharmony_ci 91cb93a386Sopenharmony_ci/** Assumes that the ref draw was completely inside ref canvas -- 92cb93a386Sopenharmony_ci implies that everything outside is "bgColor". 93cb93a386Sopenharmony_ci Checks that all overlap is the same and that all non-overlap on the 94cb93a386Sopenharmony_ci ref is "bgColor". 95cb93a386Sopenharmony_ci */ 96cb93a386Sopenharmony_cistatic bool compare(const SkBitmap& ref, const SkIRect& iref, 97cb93a386Sopenharmony_ci const SkBitmap& test, const SkIRect& itest) 98cb93a386Sopenharmony_ci{ 99cb93a386Sopenharmony_ci const int xOff = itest.fLeft - iref.fLeft; 100cb93a386Sopenharmony_ci const int yOff = itest.fTop - iref.fTop; 101cb93a386Sopenharmony_ci 102cb93a386Sopenharmony_ci for (int y = 0; y < test.height(); ++y) { 103cb93a386Sopenharmony_ci for (int x = 0; x < test.width(); ++x) { 104cb93a386Sopenharmony_ci SkColor testColor = test.getColor(x, y); 105cb93a386Sopenharmony_ci int refX = x + xOff; 106cb93a386Sopenharmony_ci int refY = y + yOff; 107cb93a386Sopenharmony_ci SkColor refColor; 108cb93a386Sopenharmony_ci if (refX >= 0 && refX < ref.width() && 109cb93a386Sopenharmony_ci refY >= 0 && refY < ref.height()) 110cb93a386Sopenharmony_ci { 111cb93a386Sopenharmony_ci refColor = ref.getColor(refX, refY); 112cb93a386Sopenharmony_ci } else { 113cb93a386Sopenharmony_ci refColor = bgColor; 114cb93a386Sopenharmony_ci } 115cb93a386Sopenharmony_ci if (refColor != testColor) { 116cb93a386Sopenharmony_ci return false; 117cb93a386Sopenharmony_ci } 118cb93a386Sopenharmony_ci } 119cb93a386Sopenharmony_ci } 120cb93a386Sopenharmony_ci return true; 121cb93a386Sopenharmony_ci} 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ciDEF_TEST(BlurDrawing, reporter) { 124cb93a386Sopenharmony_ci SkPaint paint; 125cb93a386Sopenharmony_ci paint.setColor(SK_ColorGRAY); 126cb93a386Sopenharmony_ci paint.setStyle(SkPaint::kStroke_Style); 127cb93a386Sopenharmony_ci paint.setStrokeWidth(SkIntToScalar(strokeWidth)); 128cb93a386Sopenharmony_ci 129cb93a386Sopenharmony_ci SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)); 130cb93a386Sopenharmony_ci for (int style = 0; style <= kLastEnum_SkBlurStyle; ++style) { 131cb93a386Sopenharmony_ci SkBlurStyle blurStyle = static_cast<SkBlurStyle>(style); 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ci for (bool respectCTM : { false, true }) { 134cb93a386Sopenharmony_ci paint.setMaskFilter(SkMaskFilter::MakeBlur(blurStyle, sigma, respectCTM)); 135cb93a386Sopenharmony_ci 136cb93a386Sopenharmony_ci for (size_t test = 0; test < SK_ARRAY_COUNT(tests); ++test) { 137cb93a386Sopenharmony_ci SkPath path; 138cb93a386Sopenharmony_ci tests[test].addPath(&path); 139cb93a386Sopenharmony_ci SkPath strokedPath; 140cb93a386Sopenharmony_ci paint.getFillPath(path, &strokedPath); 141cb93a386Sopenharmony_ci SkRect refBound = strokedPath.getBounds(); 142cb93a386Sopenharmony_ci SkIRect iref; 143cb93a386Sopenharmony_ci refBound.roundOut(&iref); 144cb93a386Sopenharmony_ci iref.inset(-outset, -outset); 145cb93a386Sopenharmony_ci SkBitmap refBitmap; 146cb93a386Sopenharmony_ci create(&refBitmap, iref); 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci SkCanvas refCanvas(refBitmap); 149cb93a386Sopenharmony_ci refCanvas.translate(SkIntToScalar(-iref.fLeft), 150cb93a386Sopenharmony_ci SkIntToScalar(-iref.fTop)); 151cb93a386Sopenharmony_ci drawBG(&refCanvas); 152cb93a386Sopenharmony_ci refCanvas.drawPath(path, paint); 153cb93a386Sopenharmony_ci 154cb93a386Sopenharmony_ci for (int view = 0; view < tests[test].viewLen; ++view) { 155cb93a386Sopenharmony_ci SkIRect itest = tests[test].views[view]; 156cb93a386Sopenharmony_ci SkBitmap testBitmap; 157cb93a386Sopenharmony_ci create(&testBitmap, itest); 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_ci SkCanvas testCanvas(testBitmap); 160cb93a386Sopenharmony_ci testCanvas.translate(SkIntToScalar(-itest.fLeft), 161cb93a386Sopenharmony_ci SkIntToScalar(-itest.fTop)); 162cb93a386Sopenharmony_ci drawBG(&testCanvas); 163cb93a386Sopenharmony_ci testCanvas.drawPath(path, paint); 164cb93a386Sopenharmony_ci 165cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 166cb93a386Sopenharmony_ci compare(refBitmap, iref, testBitmap, itest)); 167cb93a386Sopenharmony_ci } 168cb93a386Sopenharmony_ci } 169cb93a386Sopenharmony_ci } 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci} 172cb93a386Sopenharmony_ci 173cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 174cb93a386Sopenharmony_ci 175cb93a386Sopenharmony_ci// Use SkBlurMask::BlurGroundTruth to blur a 'width' x 'height' solid 176cb93a386Sopenharmony_ci// white rect. Return the right half of the middle row in 'result'. 177cb93a386Sopenharmony_cistatic void ground_truth_2d(int width, int height, 178cb93a386Sopenharmony_ci SkScalar sigma, 179cb93a386Sopenharmony_ci int* result, int resultCount) { 180cb93a386Sopenharmony_ci SkMask src, dst; 181cb93a386Sopenharmony_ci 182cb93a386Sopenharmony_ci src.fBounds.setWH(width, height); 183cb93a386Sopenharmony_ci src.fFormat = SkMask::kA8_Format; 184cb93a386Sopenharmony_ci src.fRowBytes = src.fBounds.width(); 185cb93a386Sopenharmony_ci src.fImage = SkMask::AllocImage(src.computeTotalImageSize()); 186cb93a386Sopenharmony_ci 187cb93a386Sopenharmony_ci memset(src.fImage, 0xff, src.computeTotalImageSize()); 188cb93a386Sopenharmony_ci 189cb93a386Sopenharmony_ci if (!SkBlurMask::BlurGroundTruth(sigma, &dst, src, kNormal_SkBlurStyle)) { 190cb93a386Sopenharmony_ci return; 191cb93a386Sopenharmony_ci } 192cb93a386Sopenharmony_ci 193cb93a386Sopenharmony_ci int midX = dst.fBounds.x() + dst.fBounds.width()/2; 194cb93a386Sopenharmony_ci int midY = dst.fBounds.y() + dst.fBounds.height()/2; 195cb93a386Sopenharmony_ci uint8_t* bytes = dst.getAddr8(midX, midY); 196cb93a386Sopenharmony_ci int i; 197cb93a386Sopenharmony_ci for (i = 0; i < dst.fBounds.width()-(midX-dst.fBounds.fLeft); ++i) { 198cb93a386Sopenharmony_ci if (i < resultCount) { 199cb93a386Sopenharmony_ci result[i] = bytes[i]; 200cb93a386Sopenharmony_ci } 201cb93a386Sopenharmony_ci } 202cb93a386Sopenharmony_ci for ( ; i < resultCount; ++i) { 203cb93a386Sopenharmony_ci result[i] = 0; 204cb93a386Sopenharmony_ci } 205cb93a386Sopenharmony_ci 206cb93a386Sopenharmony_ci SkMask::FreeImage(src.fImage); 207cb93a386Sopenharmony_ci SkMask::FreeImage(dst.fImage); 208cb93a386Sopenharmony_ci} 209cb93a386Sopenharmony_ci 210cb93a386Sopenharmony_ci// Implement a step function that is 255 between min and max; 0 elsewhere. 211cb93a386Sopenharmony_cistatic int step(int x, SkScalar min, SkScalar max) { 212cb93a386Sopenharmony_ci if (min < x && x < max) { 213cb93a386Sopenharmony_ci return 255; 214cb93a386Sopenharmony_ci } 215cb93a386Sopenharmony_ci return 0; 216cb93a386Sopenharmony_ci} 217cb93a386Sopenharmony_ci 218cb93a386Sopenharmony_ci// Implement a Gaussian function with 0 mean and std.dev. of 'sigma'. 219cb93a386Sopenharmony_cistatic float gaussian(int x, SkScalar sigma) { 220cb93a386Sopenharmony_ci float k = SK_Scalar1/(sigma * sqrtf(2.0f*SK_ScalarPI)); 221cb93a386Sopenharmony_ci float exponent = -(x * x) / (2 * sigma * sigma); 222cb93a386Sopenharmony_ci return k * expf(exponent); 223cb93a386Sopenharmony_ci} 224cb93a386Sopenharmony_ci 225cb93a386Sopenharmony_ci// Perform a brute force convolution of a step function with a Gaussian. 226cb93a386Sopenharmony_ci// Return the right half in 'result' 227cb93a386Sopenharmony_cistatic void brute_force_1d(SkScalar stepMin, SkScalar stepMax, 228cb93a386Sopenharmony_ci SkScalar gaussianSigma, 229cb93a386Sopenharmony_ci int* result, int resultCount) { 230cb93a386Sopenharmony_ci 231cb93a386Sopenharmony_ci int gaussianRange = SkScalarCeilToInt(10 * gaussianSigma); 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_ci for (int i = 0; i < resultCount; ++i) { 234cb93a386Sopenharmony_ci SkScalar sum = 0.0f; 235cb93a386Sopenharmony_ci for (int j = -gaussianRange; j < gaussianRange; ++j) { 236cb93a386Sopenharmony_ci sum += gaussian(j, gaussianSigma) * step(i-j, stepMin, stepMax); 237cb93a386Sopenharmony_ci } 238cb93a386Sopenharmony_ci 239cb93a386Sopenharmony_ci result[i] = SkTPin(SkClampPos(int(sum + 0.5f)), 0, 255); 240cb93a386Sopenharmony_ci } 241cb93a386Sopenharmony_ci} 242cb93a386Sopenharmony_ci 243cb93a386Sopenharmony_cistatic void blur_path(SkCanvas* canvas, const SkPath& path, 244cb93a386Sopenharmony_ci SkScalar gaussianSigma) { 245cb93a386Sopenharmony_ci 246cb93a386Sopenharmony_ci SkScalar midX = path.getBounds().centerX(); 247cb93a386Sopenharmony_ci SkScalar midY = path.getBounds().centerY(); 248cb93a386Sopenharmony_ci 249cb93a386Sopenharmony_ci canvas->translate(-midX, -midY); 250cb93a386Sopenharmony_ci 251cb93a386Sopenharmony_ci SkPaint blurPaint; 252cb93a386Sopenharmony_ci blurPaint.setColor(SK_ColorWHITE); 253cb93a386Sopenharmony_ci blurPaint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, gaussianSigma)); 254cb93a386Sopenharmony_ci 255cb93a386Sopenharmony_ci canvas->drawColor(SK_ColorBLACK); 256cb93a386Sopenharmony_ci canvas->drawPath(path, blurPaint); 257cb93a386Sopenharmony_ci} 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ci// Readback the blurred draw results from the canvas 260cb93a386Sopenharmony_cistatic void readback(const SkBitmap& src, int* result, int resultCount) { 261cb93a386Sopenharmony_ci SkBitmap readback; 262cb93a386Sopenharmony_ci readback.allocN32Pixels(resultCount, 30); 263cb93a386Sopenharmony_ci SkPixmap pm; 264cb93a386Sopenharmony_ci readback.peekPixels(&pm); 265cb93a386Sopenharmony_ci src.readPixels(pm, 0, 0); 266cb93a386Sopenharmony_ci 267cb93a386Sopenharmony_ci const SkPMColor* pixels = pm.addr32(0, 15); 268cb93a386Sopenharmony_ci 269cb93a386Sopenharmony_ci for (int i = 0; i < resultCount; ++i) { 270cb93a386Sopenharmony_ci result[i] = SkColorGetR(pixels[i]); 271cb93a386Sopenharmony_ci } 272cb93a386Sopenharmony_ci} 273cb93a386Sopenharmony_ci 274cb93a386Sopenharmony_ci// Draw a blurred version of the provided path. 275cb93a386Sopenharmony_ci// Return the right half of the middle row in 'result'. 276cb93a386Sopenharmony_cistatic void cpu_blur_path(const SkPath& path, SkScalar gaussianSigma, 277cb93a386Sopenharmony_ci int* result, int resultCount) { 278cb93a386Sopenharmony_ci 279cb93a386Sopenharmony_ci SkBitmap bitmap; 280cb93a386Sopenharmony_ci bitmap.allocN32Pixels(resultCount, 30); 281cb93a386Sopenharmony_ci SkCanvas canvas(bitmap); 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_ci blur_path(&canvas, path, gaussianSigma); 284cb93a386Sopenharmony_ci readback(bitmap, result, resultCount); 285cb93a386Sopenharmony_ci} 286cb93a386Sopenharmony_ci 287cb93a386Sopenharmony_ci#if WRITE_CSV 288cb93a386Sopenharmony_cistatic void write_as_csv(const char* label, SkScalar scale, int* data, int count) { 289cb93a386Sopenharmony_ci SkDebugf("%s_%.2f,", label, scale); 290cb93a386Sopenharmony_ci for (int i = 0; i < count-1; ++i) { 291cb93a386Sopenharmony_ci SkDebugf("%d,", data[i]); 292cb93a386Sopenharmony_ci } 293cb93a386Sopenharmony_ci SkDebugf("%d\n", data[count-1]); 294cb93a386Sopenharmony_ci} 295cb93a386Sopenharmony_ci#endif 296cb93a386Sopenharmony_ci 297cb93a386Sopenharmony_cistatic bool match(int* first, int* second, int count, int tol) { 298cb93a386Sopenharmony_ci int delta; 299cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 300cb93a386Sopenharmony_ci delta = first[i] - second[i]; 301cb93a386Sopenharmony_ci if (delta > tol || delta < -tol) { 302cb93a386Sopenharmony_ci return false; 303cb93a386Sopenharmony_ci } 304cb93a386Sopenharmony_ci } 305cb93a386Sopenharmony_ci 306cb93a386Sopenharmony_ci return true; 307cb93a386Sopenharmony_ci} 308cb93a386Sopenharmony_ci 309cb93a386Sopenharmony_ci// Test out the normal blur style with a wide range of sigmas 310cb93a386Sopenharmony_ciDEF_TEST(BlurSigmaRange, reporter) { 311cb93a386Sopenharmony_ci static const int kSize = 100; 312cb93a386Sopenharmony_ci 313cb93a386Sopenharmony_ci // The geometry is offset a smidge to trigger: 314cb93a386Sopenharmony_ci // https://code.google.com/p/chromium/issues/detail?id=282418 315cb93a386Sopenharmony_ci SkPath rectPath; 316cb93a386Sopenharmony_ci rectPath.addRect(0.3f, 0.3f, 100.3f, 100.3f); 317cb93a386Sopenharmony_ci 318cb93a386Sopenharmony_ci SkPoint polyPts[] = { 319cb93a386Sopenharmony_ci { 0.3f, 0.3f }, 320cb93a386Sopenharmony_ci { 100.3f, 0.3f }, 321cb93a386Sopenharmony_ci { 100.3f, 100.3f }, 322cb93a386Sopenharmony_ci { 0.3f, 100.3f }, 323cb93a386Sopenharmony_ci { 2.3f, 50.3f } // a little divet to throw off the rect special case 324cb93a386Sopenharmony_ci }; 325cb93a386Sopenharmony_ci SkPath polyPath; 326cb93a386Sopenharmony_ci polyPath.addPoly(polyPts, SK_ARRAY_COUNT(polyPts), true); 327cb93a386Sopenharmony_ci 328cb93a386Sopenharmony_ci int rectSpecialCaseResult[kSize]; 329cb93a386Sopenharmony_ci int generalCaseResult[kSize]; 330cb93a386Sopenharmony_ci int groundTruthResult[kSize]; 331cb93a386Sopenharmony_ci int bruteForce1DResult[kSize]; 332cb93a386Sopenharmony_ci 333cb93a386Sopenharmony_ci SkScalar sigma = 10.0f; 334cb93a386Sopenharmony_ci 335cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i, sigma /= 10) { 336cb93a386Sopenharmony_ci 337cb93a386Sopenharmony_ci cpu_blur_path(rectPath, sigma, rectSpecialCaseResult, kSize); 338cb93a386Sopenharmony_ci cpu_blur_path(polyPath, sigma, generalCaseResult, kSize); 339cb93a386Sopenharmony_ci 340cb93a386Sopenharmony_ci ground_truth_2d(100, 100, sigma, groundTruthResult, kSize); 341cb93a386Sopenharmony_ci brute_force_1d(-50.0f, 50.0f, sigma, bruteForce1DResult, kSize); 342cb93a386Sopenharmony_ci 343cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, match(rectSpecialCaseResult, bruteForce1DResult, kSize, 5)); 344cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, match(generalCaseResult, bruteForce1DResult, kSize, 15)); 345cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, match(groundTruthResult, bruteForce1DResult, kSize, 1)); 346cb93a386Sopenharmony_ci 347cb93a386Sopenharmony_ci#if WRITE_CSV 348cb93a386Sopenharmony_ci write_as_csv("RectSpecialCase", sigma, rectSpecialCaseResult, kSize); 349cb93a386Sopenharmony_ci write_as_csv("GeneralCase", sigma, generalCaseResult, kSize); 350cb93a386Sopenharmony_ci write_as_csv("GPU", sigma, gpuResult, kSize); 351cb93a386Sopenharmony_ci write_as_csv("GroundTruth2D", sigma, groundTruthResult, kSize); 352cb93a386Sopenharmony_ci write_as_csv("BruteForce1D", sigma, bruteForce1DResult, kSize); 353cb93a386Sopenharmony_ci#endif 354cb93a386Sopenharmony_ci } 355cb93a386Sopenharmony_ci} 356cb93a386Sopenharmony_ci 357cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////// 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_ciDEF_TEST(BlurAsABlur, reporter) { 360cb93a386Sopenharmony_ci const SkBlurStyle styles[] = { 361cb93a386Sopenharmony_ci kNormal_SkBlurStyle, kSolid_SkBlurStyle, kOuter_SkBlurStyle, kInner_SkBlurStyle 362cb93a386Sopenharmony_ci }; 363cb93a386Sopenharmony_ci const SkScalar sigmas[] = { 364cb93a386Sopenharmony_ci // values <= 0 should not success for a blur 365cb93a386Sopenharmony_ci -1, 0, 0.5f, 2 366cb93a386Sopenharmony_ci }; 367cb93a386Sopenharmony_ci 368cb93a386Sopenharmony_ci // Test asABlur for SkBlurMaskFilter 369cb93a386Sopenharmony_ci // 370cb93a386Sopenharmony_ci for (size_t i = 0; i < SK_ARRAY_COUNT(styles); ++i) { 371cb93a386Sopenharmony_ci const SkBlurStyle style = styles[i]; 372cb93a386Sopenharmony_ci for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) { 373cb93a386Sopenharmony_ci const SkScalar sigma = sigmas[j]; 374cb93a386Sopenharmony_ci for (bool respectCTM : { false, true }) { 375cb93a386Sopenharmony_ci sk_sp<SkMaskFilter> mf(SkMaskFilter::MakeBlur(style, sigma, respectCTM)); 376cb93a386Sopenharmony_ci if (nullptr == mf.get()) { 377cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, sigma <= 0); 378cb93a386Sopenharmony_ci } else { 379cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, sigma > 0); 380cb93a386Sopenharmony_ci SkMaskFilterBase::BlurRec rec; 381cb93a386Sopenharmony_ci bool success = as_MFB(mf)->asABlur(&rec); 382cb93a386Sopenharmony_ci if (respectCTM) { 383cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, success); 384cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, rec.fSigma == sigma); 385cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, rec.fStyle == style); 386cb93a386Sopenharmony_ci } else { 387cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !success); 388cb93a386Sopenharmony_ci } 389cb93a386Sopenharmony_ci 390cb93a386Sopenharmony_ci const SkRect src = {0, 0, 100, 100}; 391cb93a386Sopenharmony_ci const auto dst = mf->approximateFilteredBounds(src); 392cb93a386Sopenharmony_ci 393cb93a386Sopenharmony_ci // This is a very conservative test. With more knowledge, we could 394cb93a386Sopenharmony_ci // consider more stringent tests. 395cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, dst.contains(src)); 396cb93a386Sopenharmony_ci } 397cb93a386Sopenharmony_ci } 398cb93a386Sopenharmony_ci } 399cb93a386Sopenharmony_ci } 400cb93a386Sopenharmony_ci 401cb93a386Sopenharmony_ci // Test asABlur for SkEmbossMaskFilter -- should never succeed 402cb93a386Sopenharmony_ci // 403cb93a386Sopenharmony_ci { 404cb93a386Sopenharmony_ci SkEmbossMaskFilter::Light light = { 405cb93a386Sopenharmony_ci { 1, 1, 1 }, 0, 127, 127 406cb93a386Sopenharmony_ci }; 407cb93a386Sopenharmony_ci for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) { 408cb93a386Sopenharmony_ci const SkScalar sigma = sigmas[j]; 409cb93a386Sopenharmony_ci auto mf(SkEmbossMaskFilter::Make(sigma, light)); 410cb93a386Sopenharmony_ci if (mf) { 411cb93a386Sopenharmony_ci SkMaskFilterBase::BlurRec rec; 412cb93a386Sopenharmony_ci bool success = as_MFB(mf)->asABlur(&rec); 413cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !success); 414cb93a386Sopenharmony_ci } 415cb93a386Sopenharmony_ci } 416cb93a386Sopenharmony_ci } 417cb93a386Sopenharmony_ci} 418cb93a386Sopenharmony_ci 419cb93a386Sopenharmony_ci// This exercises the problem discovered in crbug.com/570232. The return value from 420cb93a386Sopenharmony_ci// SkBlurMask::BoxBlur wasn't being checked in SkBlurMaskFilter.cpp::GrRRectBlurEffect::Create 421cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_RENDERING_CONTEXTS(SmallBoxBlurBug, reporter, ctxInfo) { 422cb93a386Sopenharmony_ci 423cb93a386Sopenharmony_ci SkImageInfo info = SkImageInfo::MakeN32Premul(128, 128); 424cb93a386Sopenharmony_ci auto surface(SkSurface::MakeRenderTarget(ctxInfo.directContext(), SkBudgeted::kNo, info)); 425cb93a386Sopenharmony_ci SkCanvas* canvas = surface->getCanvas(); 426cb93a386Sopenharmony_ci 427cb93a386Sopenharmony_ci SkRect r = SkRect::MakeXYWH(10, 10, 100, 100); 428cb93a386Sopenharmony_ci SkRRect rr = SkRRect::MakeRectXY(r, 10, 10); 429cb93a386Sopenharmony_ci 430cb93a386Sopenharmony_ci SkPaint p; 431cb93a386Sopenharmony_ci p.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 0.01f)); 432cb93a386Sopenharmony_ci 433cb93a386Sopenharmony_ci canvas->drawRRect(rr, p); 434cb93a386Sopenharmony_ci} 435cb93a386Sopenharmony_ci 436cb93a386Sopenharmony_ciDEF_TEST(BlurredRRectNinePatchComputation, reporter) { 437cb93a386Sopenharmony_ci const SkRect r = SkRect::MakeXYWH(10, 10, 100, 100); 438cb93a386Sopenharmony_ci static const SkScalar kBlurRad = 3.0f; 439cb93a386Sopenharmony_ci 440cb93a386Sopenharmony_ci bool ninePatchable; 441cb93a386Sopenharmony_ci SkRRect rrectToDraw; 442cb93a386Sopenharmony_ci SkISize size; 443cb93a386Sopenharmony_ci SkScalar rectXs[SkGpuBlurUtils::kBlurRRectMaxDivisions], 444cb93a386Sopenharmony_ci rectYs[SkGpuBlurUtils::kBlurRRectMaxDivisions]; 445cb93a386Sopenharmony_ci SkScalar texXs[SkGpuBlurUtils::kBlurRRectMaxDivisions], 446cb93a386Sopenharmony_ci texYs[SkGpuBlurUtils::kBlurRRectMaxDivisions]; 447cb93a386Sopenharmony_ci 448cb93a386Sopenharmony_ci // not nine-patchable 449cb93a386Sopenharmony_ci { 450cb93a386Sopenharmony_ci SkVector radii[4] = { { 100, 100 }, { 0, 0 }, { 100, 100 }, { 0, 0 } }; 451cb93a386Sopenharmony_ci 452cb93a386Sopenharmony_ci SkRRect rr; 453cb93a386Sopenharmony_ci rr.setRectRadii(r, radii); 454cb93a386Sopenharmony_ci 455cb93a386Sopenharmony_ci ninePatchable = SkGpuBlurUtils::ComputeBlurredRRectParams(rr, rr, kBlurRad, kBlurRad, 456cb93a386Sopenharmony_ci &rrectToDraw, &size, 457cb93a386Sopenharmony_ci rectXs, rectYs, texXs, texYs); 458cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, !ninePatchable); 459cb93a386Sopenharmony_ci } 460cb93a386Sopenharmony_ci 461cb93a386Sopenharmony_ci // simple circular 462cb93a386Sopenharmony_ci { 463cb93a386Sopenharmony_ci static const SkScalar kCornerRad = 10.0f; 464cb93a386Sopenharmony_ci SkRRect rr; 465cb93a386Sopenharmony_ci rr.setRectXY(r, kCornerRad, kCornerRad); 466cb93a386Sopenharmony_ci 467cb93a386Sopenharmony_ci ninePatchable = SkGpuBlurUtils::ComputeBlurredRRectParams(rr, rr, kBlurRad, kBlurRad, 468cb93a386Sopenharmony_ci &rrectToDraw, &size, 469cb93a386Sopenharmony_ci rectXs, rectYs, texXs, texYs); 470cb93a386Sopenharmony_ci 471cb93a386Sopenharmony_ci static const SkScalar kAns = 12.0f * kBlurRad + 2.0f * kCornerRad + 1.0f; 472cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, ninePatchable); 473cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kAns)); 474cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kAns)); 475cb93a386Sopenharmony_ci } 476cb93a386Sopenharmony_ci 477cb93a386Sopenharmony_ci // simple elliptical 478cb93a386Sopenharmony_ci { 479cb93a386Sopenharmony_ci static const SkScalar kXCornerRad = 2.0f; 480cb93a386Sopenharmony_ci static const SkScalar kYCornerRad = 10.0f; 481cb93a386Sopenharmony_ci SkRRect rr; 482cb93a386Sopenharmony_ci rr.setRectXY(r, kXCornerRad, kYCornerRad); 483cb93a386Sopenharmony_ci 484cb93a386Sopenharmony_ci ninePatchable = SkGpuBlurUtils::ComputeBlurredRRectParams(rr, rr, kBlurRad, kBlurRad, 485cb93a386Sopenharmony_ci &rrectToDraw, &size, 486cb93a386Sopenharmony_ci rectXs, rectYs, texXs, texYs); 487cb93a386Sopenharmony_ci 488cb93a386Sopenharmony_ci static const SkScalar kXAns = 12.0f * kBlurRad + 2.0f * kXCornerRad + 1.0f; 489cb93a386Sopenharmony_ci static const SkScalar kYAns = 12.0f * kBlurRad + 2.0f * kYCornerRad + 1.0f; 490cb93a386Sopenharmony_ci 491cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, ninePatchable); 492cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kXAns)); 493cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kYAns)); 494cb93a386Sopenharmony_ci } 495cb93a386Sopenharmony_ci} 496cb93a386Sopenharmony_ci 497cb93a386Sopenharmony_ci// https://crbugs.com/787712 498cb93a386Sopenharmony_ciDEF_TEST(EmbossPerlinCrash, reporter) { 499cb93a386Sopenharmony_ci SkPaint p; 500cb93a386Sopenharmony_ci 501cb93a386Sopenharmony_ci static constexpr SkEmbossMaskFilter::Light light = { 502cb93a386Sopenharmony_ci { 1, 1, 1 }, 0, 127, 127 503cb93a386Sopenharmony_ci }; 504cb93a386Sopenharmony_ci p.setMaskFilter(SkEmbossMaskFilter::Make(1, light)); 505cb93a386Sopenharmony_ci p.setShader(SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 2, 0.0f)); 506cb93a386Sopenharmony_ci 507cb93a386Sopenharmony_ci sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100); 508cb93a386Sopenharmony_ci surface->getCanvas()->drawPaint(p); 509cb93a386Sopenharmony_ci} 510cb93a386Sopenharmony_ci 511cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////// 512cb93a386Sopenharmony_ci 513cb93a386Sopenharmony_ciDEF_TEST(BlurZeroSigma, reporter) { 514cb93a386Sopenharmony_ci auto surf = SkSurface::MakeRasterN32Premul(20, 20); 515cb93a386Sopenharmony_ci SkPaint paint; 516cb93a386Sopenharmony_ci paint.setAntiAlias(true); 517cb93a386Sopenharmony_ci 518cb93a386Sopenharmony_ci const SkIRect ir = { 5, 5, 15, 15 }; 519cb93a386Sopenharmony_ci const SkRect r = SkRect::Make(ir); 520cb93a386Sopenharmony_ci 521cb93a386Sopenharmony_ci const SkScalar sigmas[] = { 0, SkBits2Float(1) }; 522cb93a386Sopenharmony_ci // if sigma is zero (or nearly so), we need to draw correctly (unblurred) and not crash 523cb93a386Sopenharmony_ci // or assert. 524cb93a386Sopenharmony_ci for (auto sigma : sigmas) { 525cb93a386Sopenharmony_ci paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma)); 526cb93a386Sopenharmony_ci surf->getCanvas()->drawRect(r, paint); 527cb93a386Sopenharmony_ci 528cb93a386Sopenharmony_ci ToolUtils::PixelIter iter(surf.get()); 529cb93a386Sopenharmony_ci SkIPoint loc; 530cb93a386Sopenharmony_ci while (const SkPMColor* p = (const SkPMColor*)iter.next(&loc)) { 531cb93a386Sopenharmony_ci if (ir.contains(loc.fX, loc.fY)) { 532cb93a386Sopenharmony_ci // inside the rect we draw (opaque black) 533cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, *p == SkPackARGB32(0xFF, 0, 0, 0)); 534cb93a386Sopenharmony_ci } else { 535cb93a386Sopenharmony_ci // outside the rect we didn't draw at all, no blurred edges 536cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, *p == 0); 537cb93a386Sopenharmony_ci } 538cb93a386Sopenharmony_ci } 539cb93a386Sopenharmony_ci } 540cb93a386Sopenharmony_ci} 541cb93a386Sopenharmony_ci 542cb93a386Sopenharmony_ci 543cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////// 544cb93a386Sopenharmony_ci 545cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_RENDERING_CONTEXTS(BlurMaskBiggerThanDest, reporter, ctxInfo) { 546cb93a386Sopenharmony_ci auto context = ctxInfo.directContext(); 547cb93a386Sopenharmony_ci 548cb93a386Sopenharmony_ci SkImageInfo ii = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType, kPremul_SkAlphaType); 549cb93a386Sopenharmony_ci 550cb93a386Sopenharmony_ci sk_sp<SkSurface> dst(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii)); 551cb93a386Sopenharmony_ci if (!dst) { 552cb93a386Sopenharmony_ci ERRORF(reporter, "Could not create surface for test."); 553cb93a386Sopenharmony_ci return; 554cb93a386Sopenharmony_ci } 555cb93a386Sopenharmony_ci 556cb93a386Sopenharmony_ci SkPaint p; 557cb93a386Sopenharmony_ci p.setColor(SK_ColorRED); 558cb93a386Sopenharmony_ci p.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 3)); 559cb93a386Sopenharmony_ci 560cb93a386Sopenharmony_ci SkCanvas* canvas = dst->getCanvas(); 561cb93a386Sopenharmony_ci 562cb93a386Sopenharmony_ci canvas->clear(SK_ColorBLACK); 563cb93a386Sopenharmony_ci canvas->drawCircle(SkPoint::Make(16, 16), 8, p); 564cb93a386Sopenharmony_ci 565cb93a386Sopenharmony_ci SkBitmap readback; 566cb93a386Sopenharmony_ci SkAssertResult(readback.tryAllocPixels(ii)); 567cb93a386Sopenharmony_ci 568cb93a386Sopenharmony_ci canvas->readPixels(readback, 0, 0); 569cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkColorGetR(readback.getColor(15, 15)) > 128); 570cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkColorGetG(readback.getColor(15, 15)) == 0); 571cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkColorGetB(readback.getColor(15, 15)) == 0); 572cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, readback.getColor(31, 31) == SK_ColorBLACK); 573cb93a386Sopenharmony_ci} 574cb93a386Sopenharmony_ci 575cb93a386Sopenharmony_ciDEF_TEST(zero_blur, reporter) { 576cb93a386Sopenharmony_ci SkBitmap alpha, bitmap; 577cb93a386Sopenharmony_ci 578cb93a386Sopenharmony_ci SkPaint paint; 579cb93a386Sopenharmony_ci paint.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, 3)); 580cb93a386Sopenharmony_ci SkIPoint offset; 581cb93a386Sopenharmony_ci bitmap.extractAlpha(&alpha, &paint, nullptr, &offset); 582cb93a386Sopenharmony_ci} 583