1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2006 The Android Open Source Project 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 "src/core/SkBlurMask.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/core/SkColorPriv.h" 11cb93a386Sopenharmony_ci#include "include/core/SkMath.h" 12cb93a386Sopenharmony_ci#include "include/private/SkTPin.h" 13cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h" 14cb93a386Sopenharmony_ci#include "include/private/SkTo.h" 15cb93a386Sopenharmony_ci#include "src/core/SkEndian.h" 16cb93a386Sopenharmony_ci#include "src/core/SkMaskBlurFilter.h" 17cb93a386Sopenharmony_ci#include "src/core/SkMathPriv.h" 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_ci// This constant approximates the scaling done in the software path's 20cb93a386Sopenharmony_ci// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)). 21cb93a386Sopenharmony_ci// IMHO, it actually should be 1: we blur "less" than we should do 22cb93a386Sopenharmony_ci// according to the CSS and canvas specs, simply because Safari does the same. 23cb93a386Sopenharmony_ci// Firefox used to do the same too, until 4.0 where they fixed it. So at some 24cb93a386Sopenharmony_ci// point we should probably get rid of these scaling constants and rebaseline 25cb93a386Sopenharmony_ci// all the blur tests. 26cb93a386Sopenharmony_cistatic const SkScalar kBLUR_SIGMA_SCALE = 0.57735f; 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_ciSkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) { 29cb93a386Sopenharmony_ci return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; 30cb93a386Sopenharmony_ci} 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ciSkScalar SkBlurMask::ConvertSigmaToRadius(SkScalar sigma) { 33cb93a386Sopenharmony_ci return sigma > 0.5f ? (sigma - 0.5f) / kBLUR_SIGMA_SCALE : 0.0f; 34cb93a386Sopenharmony_ci} 35cb93a386Sopenharmony_ci 36cb93a386Sopenharmony_ci 37cb93a386Sopenharmony_citemplate <typename AlphaIter> 38cb93a386Sopenharmony_cistatic void merge_src_with_blur(uint8_t dst[], int dstRB, 39cb93a386Sopenharmony_ci AlphaIter src, int srcRB, 40cb93a386Sopenharmony_ci const uint8_t blur[], int blurRB, 41cb93a386Sopenharmony_ci int sw, int sh) { 42cb93a386Sopenharmony_ci dstRB -= sw; 43cb93a386Sopenharmony_ci blurRB -= sw; 44cb93a386Sopenharmony_ci while (--sh >= 0) { 45cb93a386Sopenharmony_ci AlphaIter rowSrc(src); 46cb93a386Sopenharmony_ci for (int x = sw - 1; x >= 0; --x) { 47cb93a386Sopenharmony_ci *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*rowSrc))); 48cb93a386Sopenharmony_ci ++dst; 49cb93a386Sopenharmony_ci ++rowSrc; 50cb93a386Sopenharmony_ci ++blur; 51cb93a386Sopenharmony_ci } 52cb93a386Sopenharmony_ci dst += dstRB; 53cb93a386Sopenharmony_ci src >>= srcRB; 54cb93a386Sopenharmony_ci blur += blurRB; 55cb93a386Sopenharmony_ci } 56cb93a386Sopenharmony_ci} 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_citemplate <typename AlphaIter> 59cb93a386Sopenharmony_cistatic void clamp_solid_with_orig(uint8_t dst[], int dstRowBytes, 60cb93a386Sopenharmony_ci AlphaIter src, int srcRowBytes, 61cb93a386Sopenharmony_ci int sw, int sh) { 62cb93a386Sopenharmony_ci int x; 63cb93a386Sopenharmony_ci while (--sh >= 0) { 64cb93a386Sopenharmony_ci AlphaIter rowSrc(src); 65cb93a386Sopenharmony_ci for (x = sw - 1; x >= 0; --x) { 66cb93a386Sopenharmony_ci int s = *rowSrc; 67cb93a386Sopenharmony_ci int d = *dst; 68cb93a386Sopenharmony_ci *dst = SkToU8(s + d - SkMulDiv255Round(s, d)); 69cb93a386Sopenharmony_ci ++dst; 70cb93a386Sopenharmony_ci ++rowSrc; 71cb93a386Sopenharmony_ci } 72cb93a386Sopenharmony_ci dst += dstRowBytes - sw; 73cb93a386Sopenharmony_ci src >>= srcRowBytes; 74cb93a386Sopenharmony_ci } 75cb93a386Sopenharmony_ci} 76cb93a386Sopenharmony_ci 77cb93a386Sopenharmony_citemplate <typename AlphaIter> 78cb93a386Sopenharmony_cistatic void clamp_outer_with_orig(uint8_t dst[], int dstRowBytes, 79cb93a386Sopenharmony_ci AlphaIter src, int srcRowBytes, 80cb93a386Sopenharmony_ci int sw, int sh) { 81cb93a386Sopenharmony_ci int x; 82cb93a386Sopenharmony_ci while (--sh >= 0) { 83cb93a386Sopenharmony_ci AlphaIter rowSrc(src); 84cb93a386Sopenharmony_ci for (x = sw - 1; x >= 0; --x) { 85cb93a386Sopenharmony_ci int srcValue = *rowSrc; 86cb93a386Sopenharmony_ci if (srcValue) { 87cb93a386Sopenharmony_ci *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - srcValue))); 88cb93a386Sopenharmony_ci } 89cb93a386Sopenharmony_ci ++dst; 90cb93a386Sopenharmony_ci ++rowSrc; 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci dst += dstRowBytes - sw; 93cb93a386Sopenharmony_ci src >>= srcRowBytes; 94cb93a386Sopenharmony_ci } 95cb93a386Sopenharmony_ci} 96cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_ci// we use a local function to wrap the class static method to work around 99cb93a386Sopenharmony_ci// a bug in gcc98 100cb93a386Sopenharmony_civoid SkMask_FreeImage(uint8_t* image); 101cb93a386Sopenharmony_civoid SkMask_FreeImage(uint8_t* image) { 102cb93a386Sopenharmony_ci SkMask::FreeImage(image); 103cb93a386Sopenharmony_ci} 104cb93a386Sopenharmony_ci 105cb93a386Sopenharmony_cibool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style, 106cb93a386Sopenharmony_ci SkIPoint* margin) { 107cb93a386Sopenharmony_ci if (src.fFormat != SkMask::kBW_Format && 108cb93a386Sopenharmony_ci src.fFormat != SkMask::kA8_Format && 109cb93a386Sopenharmony_ci src.fFormat != SkMask::kARGB32_Format && 110cb93a386Sopenharmony_ci src.fFormat != SkMask::kLCD16_Format) 111cb93a386Sopenharmony_ci { 112cb93a386Sopenharmony_ci return false; 113cb93a386Sopenharmony_ci } 114cb93a386Sopenharmony_ci 115cb93a386Sopenharmony_ci SkMaskBlurFilter blurFilter{sigma, sigma}; 116cb93a386Sopenharmony_ci if (blurFilter.hasNoBlur()) { 117cb93a386Sopenharmony_ci // If there is no effective blur most styles will just produce the original mask. 118cb93a386Sopenharmony_ci // However, kOuter_SkBlurStyle will produce an empty mask. 119cb93a386Sopenharmony_ci if (style == kOuter_SkBlurStyle) { 120cb93a386Sopenharmony_ci dst->fImage = nullptr; 121cb93a386Sopenharmony_ci dst->fBounds = SkIRect::MakeEmpty(); 122cb93a386Sopenharmony_ci dst->fRowBytes = dst->fBounds.width(); 123cb93a386Sopenharmony_ci dst->fFormat = SkMask::kA8_Format; 124cb93a386Sopenharmony_ci if (margin != nullptr) { 125cb93a386Sopenharmony_ci // This filter will disregard the src.fImage completely. 126cb93a386Sopenharmony_ci // The margin is actually {-(src.fBounds.width() / 2), -(src.fBounds.height() / 2)} 127cb93a386Sopenharmony_ci // but it is not clear if callers will fall over with negative margins. 128cb93a386Sopenharmony_ci *margin = SkIPoint{0,0}; 129cb93a386Sopenharmony_ci } 130cb93a386Sopenharmony_ci return true; 131cb93a386Sopenharmony_ci } 132cb93a386Sopenharmony_ci return false; 133cb93a386Sopenharmony_ci } 134cb93a386Sopenharmony_ci const SkIPoint border = blurFilter.blur(src, dst); 135cb93a386Sopenharmony_ci // If src.fImage is null, then this call is only to calculate the border. 136cb93a386Sopenharmony_ci if (src.fImage != nullptr && dst->fImage == nullptr) { 137cb93a386Sopenharmony_ci return false; 138cb93a386Sopenharmony_ci } 139cb93a386Sopenharmony_ci 140cb93a386Sopenharmony_ci if (margin != nullptr) { 141cb93a386Sopenharmony_ci *margin = border; 142cb93a386Sopenharmony_ci } 143cb93a386Sopenharmony_ci 144cb93a386Sopenharmony_ci if (src.fImage == nullptr) { 145cb93a386Sopenharmony_ci if (style == kInner_SkBlurStyle) { 146cb93a386Sopenharmony_ci dst->fBounds = src.fBounds; // restore trimmed bounds 147cb93a386Sopenharmony_ci dst->fRowBytes = dst->fBounds.width(); 148cb93a386Sopenharmony_ci } 149cb93a386Sopenharmony_ci return true; 150cb93a386Sopenharmony_ci } 151cb93a386Sopenharmony_ci 152cb93a386Sopenharmony_ci switch (style) { 153cb93a386Sopenharmony_ci case kNormal_SkBlurStyle: 154cb93a386Sopenharmony_ci break; 155cb93a386Sopenharmony_ci case kSolid_SkBlurStyle: { 156cb93a386Sopenharmony_ci auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes]; 157cb93a386Sopenharmony_ci switch (src.fFormat) { 158cb93a386Sopenharmony_ci case SkMask::kBW_Format: 159cb93a386Sopenharmony_ci clamp_solid_with_orig( 160cb93a386Sopenharmony_ci dstStart, dst->fRowBytes, 161cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes, 162cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 163cb93a386Sopenharmony_ci break; 164cb93a386Sopenharmony_ci case SkMask::kA8_Format: 165cb93a386Sopenharmony_ci clamp_solid_with_orig( 166cb93a386Sopenharmony_ci dstStart, dst->fRowBytes, 167cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes, 168cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 169cb93a386Sopenharmony_ci break; 170cb93a386Sopenharmony_ci case SkMask::kARGB32_Format: { 171cb93a386Sopenharmony_ci uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage); 172cb93a386Sopenharmony_ci clamp_solid_with_orig( 173cb93a386Sopenharmony_ci dstStart, dst->fRowBytes, 174cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes, 175cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 176cb93a386Sopenharmony_ci } break; 177cb93a386Sopenharmony_ci case SkMask::kLCD16_Format: { 178cb93a386Sopenharmony_ci uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage); 179cb93a386Sopenharmony_ci clamp_solid_with_orig( 180cb93a386Sopenharmony_ci dstStart, dst->fRowBytes, 181cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes, 182cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 183cb93a386Sopenharmony_ci } break; 184cb93a386Sopenharmony_ci default: 185cb93a386Sopenharmony_ci SK_ABORT("Unhandled format."); 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci } break; 188cb93a386Sopenharmony_ci case kOuter_SkBlurStyle: { 189cb93a386Sopenharmony_ci auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes]; 190cb93a386Sopenharmony_ci switch (src.fFormat) { 191cb93a386Sopenharmony_ci case SkMask::kBW_Format: 192cb93a386Sopenharmony_ci clamp_outer_with_orig( 193cb93a386Sopenharmony_ci dstStart, dst->fRowBytes, 194cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes, 195cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 196cb93a386Sopenharmony_ci break; 197cb93a386Sopenharmony_ci case SkMask::kA8_Format: 198cb93a386Sopenharmony_ci clamp_outer_with_orig( 199cb93a386Sopenharmony_ci dstStart, dst->fRowBytes, 200cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes, 201cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 202cb93a386Sopenharmony_ci break; 203cb93a386Sopenharmony_ci case SkMask::kARGB32_Format: { 204cb93a386Sopenharmony_ci uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage); 205cb93a386Sopenharmony_ci clamp_outer_with_orig( 206cb93a386Sopenharmony_ci dstStart, dst->fRowBytes, 207cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes, 208cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 209cb93a386Sopenharmony_ci } break; 210cb93a386Sopenharmony_ci case SkMask::kLCD16_Format: { 211cb93a386Sopenharmony_ci uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage); 212cb93a386Sopenharmony_ci clamp_outer_with_orig( 213cb93a386Sopenharmony_ci dstStart, dst->fRowBytes, 214cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes, 215cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 216cb93a386Sopenharmony_ci } break; 217cb93a386Sopenharmony_ci default: 218cb93a386Sopenharmony_ci SK_ABORT("Unhandled format."); 219cb93a386Sopenharmony_ci } 220cb93a386Sopenharmony_ci } break; 221cb93a386Sopenharmony_ci case kInner_SkBlurStyle: { 222cb93a386Sopenharmony_ci // now we allocate the "real" dst, mirror the size of src 223cb93a386Sopenharmony_ci SkMask blur = *dst; 224cb93a386Sopenharmony_ci SkAutoMaskFreeImage autoFreeBlurMask(blur.fImage); 225cb93a386Sopenharmony_ci dst->fBounds = src.fBounds; 226cb93a386Sopenharmony_ci dst->fRowBytes = dst->fBounds.width(); 227cb93a386Sopenharmony_ci size_t dstSize = dst->computeImageSize(); 228cb93a386Sopenharmony_ci if (0 == dstSize) { 229cb93a386Sopenharmony_ci return false; // too big to allocate, abort 230cb93a386Sopenharmony_ci } 231cb93a386Sopenharmony_ci dst->fImage = SkMask::AllocImage(dstSize); 232cb93a386Sopenharmony_ci auto blurStart = &blur.fImage[border.x() + border.y() * blur.fRowBytes]; 233cb93a386Sopenharmony_ci switch (src.fFormat) { 234cb93a386Sopenharmony_ci case SkMask::kBW_Format: 235cb93a386Sopenharmony_ci merge_src_with_blur( 236cb93a386Sopenharmony_ci dst->fImage, dst->fRowBytes, 237cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes, 238cb93a386Sopenharmony_ci blurStart, blur.fRowBytes, 239cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 240cb93a386Sopenharmony_ci break; 241cb93a386Sopenharmony_ci case SkMask::kA8_Format: 242cb93a386Sopenharmony_ci merge_src_with_blur( 243cb93a386Sopenharmony_ci dst->fImage, dst->fRowBytes, 244cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes, 245cb93a386Sopenharmony_ci blurStart, blur.fRowBytes, 246cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 247cb93a386Sopenharmony_ci break; 248cb93a386Sopenharmony_ci case SkMask::kARGB32_Format: { 249cb93a386Sopenharmony_ci uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage); 250cb93a386Sopenharmony_ci merge_src_with_blur( 251cb93a386Sopenharmony_ci dst->fImage, dst->fRowBytes, 252cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes, 253cb93a386Sopenharmony_ci blurStart, blur.fRowBytes, 254cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 255cb93a386Sopenharmony_ci } break; 256cb93a386Sopenharmony_ci case SkMask::kLCD16_Format: { 257cb93a386Sopenharmony_ci uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage); 258cb93a386Sopenharmony_ci merge_src_with_blur( 259cb93a386Sopenharmony_ci dst->fImage, dst->fRowBytes, 260cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes, 261cb93a386Sopenharmony_ci blurStart, blur.fRowBytes, 262cb93a386Sopenharmony_ci src.fBounds.width(), src.fBounds.height()); 263cb93a386Sopenharmony_ci } break; 264cb93a386Sopenharmony_ci default: 265cb93a386Sopenharmony_ci SK_ABORT("Unhandled format."); 266cb93a386Sopenharmony_ci } 267cb93a386Sopenharmony_ci } break; 268cb93a386Sopenharmony_ci } 269cb93a386Sopenharmony_ci 270cb93a386Sopenharmony_ci return true; 271cb93a386Sopenharmony_ci} 272cb93a386Sopenharmony_ci 273cb93a386Sopenharmony_ci/* Convolving a box with itself three times results in a piecewise 274cb93a386Sopenharmony_ci quadratic function: 275cb93a386Sopenharmony_ci 276cb93a386Sopenharmony_ci 0 x <= -1.5 277cb93a386Sopenharmony_ci 9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= -.5 278cb93a386Sopenharmony_ci 3/4 - x^2 -.5 < x <= .5 279cb93a386Sopenharmony_ci 9/8 - 3/2 x + 1/2 x^2 0.5 < x <= 1.5 280cb93a386Sopenharmony_ci 0 1.5 < x 281cb93a386Sopenharmony_ci 282cb93a386Sopenharmony_ci Mathematica: 283cb93a386Sopenharmony_ci 284cb93a386Sopenharmony_ci g[x_] := Piecewise [ { 285cb93a386Sopenharmony_ci {9/8 + 3/2 x + 1/2 x^2 , -1.5 < x <= -.5}, 286cb93a386Sopenharmony_ci {3/4 - x^2 , -.5 < x <= .5}, 287cb93a386Sopenharmony_ci {9/8 - 3/2 x + 1/2 x^2 , 0.5 < x <= 1.5} 288cb93a386Sopenharmony_ci }, 0] 289cb93a386Sopenharmony_ci 290cb93a386Sopenharmony_ci To get the profile curve of the blurred step function at the rectangle 291cb93a386Sopenharmony_ci edge, we evaluate the indefinite integral, which is piecewise cubic: 292cb93a386Sopenharmony_ci 293cb93a386Sopenharmony_ci 0 x <= -1.5 294cb93a386Sopenharmony_ci 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5 295cb93a386Sopenharmony_ci 1/2 + 3/4 x - 1/3 x^3 -.5 < x <= .5 296cb93a386Sopenharmony_ci 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5 297cb93a386Sopenharmony_ci 1 1.5 < x 298cb93a386Sopenharmony_ci 299cb93a386Sopenharmony_ci in Mathematica code: 300cb93a386Sopenharmony_ci 301cb93a386Sopenharmony_ci gi[x_] := Piecewise[ { 302cb93a386Sopenharmony_ci { 0 , x <= -1.5 }, 303cb93a386Sopenharmony_ci { 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 }, 304cb93a386Sopenharmony_ci { 1/2 + 3/4 x - 1/3 x^3 , -.5 < x <= .5}, 305cb93a386Sopenharmony_ci { 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3, .5 < x <= 1.5} 306cb93a386Sopenharmony_ci },1] 307cb93a386Sopenharmony_ci*/ 308cb93a386Sopenharmony_ci 309cb93a386Sopenharmony_cistatic float gaussianIntegral(float x) { 310cb93a386Sopenharmony_ci if (x > 1.5f) { 311cb93a386Sopenharmony_ci return 0.0f; 312cb93a386Sopenharmony_ci } 313cb93a386Sopenharmony_ci if (x < -1.5f) { 314cb93a386Sopenharmony_ci return 1.0f; 315cb93a386Sopenharmony_ci } 316cb93a386Sopenharmony_ci 317cb93a386Sopenharmony_ci float x2 = x*x; 318cb93a386Sopenharmony_ci float x3 = x2*x; 319cb93a386Sopenharmony_ci 320cb93a386Sopenharmony_ci if ( x > 0.5f ) { 321cb93a386Sopenharmony_ci return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x); 322cb93a386Sopenharmony_ci } 323cb93a386Sopenharmony_ci if ( x > -0.5f ) { 324cb93a386Sopenharmony_ci return 0.5f - (0.75f * x - x3 / 3.0f); 325cb93a386Sopenharmony_ci } 326cb93a386Sopenharmony_ci return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x); 327cb93a386Sopenharmony_ci} 328cb93a386Sopenharmony_ci 329cb93a386Sopenharmony_ci/* ComputeBlurProfile fills in an array of floating 330cb93a386Sopenharmony_ci point values between 0 and 255 for the profile signature of 331cb93a386Sopenharmony_ci a blurred half-plane with the given blur radius. Since we're 332cb93a386Sopenharmony_ci going to be doing screened multiplications (i.e., 1 - (1-x)(1-y)) 333cb93a386Sopenharmony_ci all the time, we actually fill in the profile pre-inverted 334cb93a386Sopenharmony_ci (already done 255-x). 335cb93a386Sopenharmony_ci*/ 336cb93a386Sopenharmony_ci 337cb93a386Sopenharmony_civoid SkBlurMask::ComputeBlurProfile(uint8_t* profile, int size, SkScalar sigma) { 338cb93a386Sopenharmony_ci SkASSERT(SkScalarCeilToInt(6*sigma) == size); 339cb93a386Sopenharmony_ci 340cb93a386Sopenharmony_ci int center = size >> 1; 341cb93a386Sopenharmony_ci 342cb93a386Sopenharmony_ci float invr = 1.f/(2*sigma); 343cb93a386Sopenharmony_ci 344cb93a386Sopenharmony_ci profile[0] = 255; 345cb93a386Sopenharmony_ci for (int x = 1 ; x < size ; ++x) { 346cb93a386Sopenharmony_ci float scaled_x = (center - x - .5f) * invr; 347cb93a386Sopenharmony_ci float gi = gaussianIntegral(scaled_x); 348cb93a386Sopenharmony_ci profile[x] = 255 - (uint8_t) (255.f * gi); 349cb93a386Sopenharmony_ci } 350cb93a386Sopenharmony_ci} 351cb93a386Sopenharmony_ci 352cb93a386Sopenharmony_ci// TODO MAYBE: Maintain a profile cache to avoid recomputing this for 353cb93a386Sopenharmony_ci// commonly used radii. Consider baking some of the most common blur radii 354cb93a386Sopenharmony_ci// directly in as static data? 355cb93a386Sopenharmony_ci 356cb93a386Sopenharmony_ci// Implementation adapted from Michael Herf's approach: 357cb93a386Sopenharmony_ci// http://stereopsis.com/shadowrect/ 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_ciuint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc, 360cb93a386Sopenharmony_ci int blurredWidth, int sharpWidth) { 361cb93a386Sopenharmony_ci // how far are we from the original edge? 362cb93a386Sopenharmony_ci int dx = SkAbs32(((loc << 1) + 1) - blurredWidth) - sharpWidth; 363cb93a386Sopenharmony_ci int ox = dx >> 1; 364cb93a386Sopenharmony_ci if (ox < 0) { 365cb93a386Sopenharmony_ci ox = 0; 366cb93a386Sopenharmony_ci } 367cb93a386Sopenharmony_ci 368cb93a386Sopenharmony_ci return profile[ox]; 369cb93a386Sopenharmony_ci} 370cb93a386Sopenharmony_ci 371cb93a386Sopenharmony_civoid SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile, 372cb93a386Sopenharmony_ci unsigned int width, SkScalar sigma) { 373cb93a386Sopenharmony_ci 374cb93a386Sopenharmony_ci unsigned int profile_size = SkScalarCeilToInt(6*sigma); 375cb93a386Sopenharmony_ci SkAutoTMalloc<uint8_t> horizontalScanline(width); 376cb93a386Sopenharmony_ci 377cb93a386Sopenharmony_ci unsigned int sw = width - profile_size; 378cb93a386Sopenharmony_ci // nearest odd number less than the profile size represents the center 379cb93a386Sopenharmony_ci // of the (2x scaled) profile 380cb93a386Sopenharmony_ci int center = ( profile_size & ~1 ) - 1; 381cb93a386Sopenharmony_ci 382cb93a386Sopenharmony_ci int w = sw - center; 383cb93a386Sopenharmony_ci 384cb93a386Sopenharmony_ci for (unsigned int x = 0 ; x < width ; ++x) { 385cb93a386Sopenharmony_ci if (profile_size <= sw) { 386cb93a386Sopenharmony_ci pixels[x] = ProfileLookup(profile, x, width, w); 387cb93a386Sopenharmony_ci } else { 388cb93a386Sopenharmony_ci float span = float(sw)/(2*sigma); 389cb93a386Sopenharmony_ci float giX = 1.5f - (x+.5f)/(2*sigma); 390cb93a386Sopenharmony_ci pixels[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span))); 391cb93a386Sopenharmony_ci } 392cb93a386Sopenharmony_ci } 393cb93a386Sopenharmony_ci} 394cb93a386Sopenharmony_ci 395cb93a386Sopenharmony_cibool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst, 396cb93a386Sopenharmony_ci const SkRect &src, SkBlurStyle style, 397cb93a386Sopenharmony_ci SkIPoint *margin, SkMask::CreateMode createMode) { 398cb93a386Sopenharmony_ci int profileSize = SkScalarCeilToInt(6*sigma); 399cb93a386Sopenharmony_ci if (profileSize <= 0) { 400cb93a386Sopenharmony_ci return false; // no blur to compute 401cb93a386Sopenharmony_ci } 402cb93a386Sopenharmony_ci 403cb93a386Sopenharmony_ci int pad = profileSize/2; 404cb93a386Sopenharmony_ci if (margin) { 405cb93a386Sopenharmony_ci margin->set( pad, pad ); 406cb93a386Sopenharmony_ci } 407cb93a386Sopenharmony_ci 408cb93a386Sopenharmony_ci dst->fBounds.setLTRB(SkScalarRoundToInt(src.fLeft - pad), 409cb93a386Sopenharmony_ci SkScalarRoundToInt(src.fTop - pad), 410cb93a386Sopenharmony_ci SkScalarRoundToInt(src.fRight + pad), 411cb93a386Sopenharmony_ci SkScalarRoundToInt(src.fBottom + pad)); 412cb93a386Sopenharmony_ci 413cb93a386Sopenharmony_ci dst->fRowBytes = dst->fBounds.width(); 414cb93a386Sopenharmony_ci dst->fFormat = SkMask::kA8_Format; 415cb93a386Sopenharmony_ci dst->fImage = nullptr; 416cb93a386Sopenharmony_ci 417cb93a386Sopenharmony_ci int sw = SkScalarFloorToInt(src.width()); 418cb93a386Sopenharmony_ci int sh = SkScalarFloorToInt(src.height()); 419cb93a386Sopenharmony_ci 420cb93a386Sopenharmony_ci if (createMode == SkMask::kJustComputeBounds_CreateMode) { 421cb93a386Sopenharmony_ci if (style == kInner_SkBlurStyle) { 422cb93a386Sopenharmony_ci dst->fBounds = src.round(); // restore trimmed bounds 423cb93a386Sopenharmony_ci dst->fRowBytes = sw; 424cb93a386Sopenharmony_ci } 425cb93a386Sopenharmony_ci return true; 426cb93a386Sopenharmony_ci } 427cb93a386Sopenharmony_ci 428cb93a386Sopenharmony_ci SkAutoTMalloc<uint8_t> profile(profileSize); 429cb93a386Sopenharmony_ci 430cb93a386Sopenharmony_ci ComputeBlurProfile(profile, profileSize, sigma); 431cb93a386Sopenharmony_ci 432cb93a386Sopenharmony_ci size_t dstSize = dst->computeImageSize(); 433cb93a386Sopenharmony_ci if (0 == dstSize) { 434cb93a386Sopenharmony_ci return false; // too big to allocate, abort 435cb93a386Sopenharmony_ci } 436cb93a386Sopenharmony_ci 437cb93a386Sopenharmony_ci uint8_t* dp = SkMask::AllocImage(dstSize); 438cb93a386Sopenharmony_ci 439cb93a386Sopenharmony_ci dst->fImage = dp; 440cb93a386Sopenharmony_ci 441cb93a386Sopenharmony_ci int dstHeight = dst->fBounds.height(); 442cb93a386Sopenharmony_ci int dstWidth = dst->fBounds.width(); 443cb93a386Sopenharmony_ci 444cb93a386Sopenharmony_ci uint8_t *outptr = dp; 445cb93a386Sopenharmony_ci 446cb93a386Sopenharmony_ci SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth); 447cb93a386Sopenharmony_ci SkAutoTMalloc<uint8_t> verticalScanline(dstHeight); 448cb93a386Sopenharmony_ci 449cb93a386Sopenharmony_ci ComputeBlurredScanline(horizontalScanline, profile, dstWidth, sigma); 450cb93a386Sopenharmony_ci ComputeBlurredScanline(verticalScanline, profile, dstHeight, sigma); 451cb93a386Sopenharmony_ci 452cb93a386Sopenharmony_ci for (int y = 0 ; y < dstHeight ; ++y) { 453cb93a386Sopenharmony_ci for (int x = 0 ; x < dstWidth ; x++) { 454cb93a386Sopenharmony_ci unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]); 455cb93a386Sopenharmony_ci *(outptr++) = maskval; 456cb93a386Sopenharmony_ci } 457cb93a386Sopenharmony_ci } 458cb93a386Sopenharmony_ci 459cb93a386Sopenharmony_ci if (style == kInner_SkBlurStyle) { 460cb93a386Sopenharmony_ci // now we allocate the "real" dst, mirror the size of src 461cb93a386Sopenharmony_ci size_t srcSize = (size_t)(src.width() * src.height()); 462cb93a386Sopenharmony_ci if (0 == srcSize) { 463cb93a386Sopenharmony_ci return false; // too big to allocate, abort 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci dst->fImage = SkMask::AllocImage(srcSize); 466cb93a386Sopenharmony_ci for (int y = 0 ; y < sh ; y++) { 467cb93a386Sopenharmony_ci uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad; 468cb93a386Sopenharmony_ci uint8_t *inner_scanline = dst->fImage + y*sw; 469cb93a386Sopenharmony_ci memcpy(inner_scanline, blur_scanline, sw); 470cb93a386Sopenharmony_ci } 471cb93a386Sopenharmony_ci SkMask::FreeImage(dp); 472cb93a386Sopenharmony_ci 473cb93a386Sopenharmony_ci dst->fBounds = src.round(); // restore trimmed bounds 474cb93a386Sopenharmony_ci dst->fRowBytes = sw; 475cb93a386Sopenharmony_ci 476cb93a386Sopenharmony_ci } else if (style == kOuter_SkBlurStyle) { 477cb93a386Sopenharmony_ci for (int y = pad ; y < dstHeight-pad ; y++) { 478cb93a386Sopenharmony_ci uint8_t *dst_scanline = dp + y*dstWidth + pad; 479cb93a386Sopenharmony_ci memset(dst_scanline, 0, sw); 480cb93a386Sopenharmony_ci } 481cb93a386Sopenharmony_ci } else if (style == kSolid_SkBlurStyle) { 482cb93a386Sopenharmony_ci for (int y = pad ; y < dstHeight-pad ; y++) { 483cb93a386Sopenharmony_ci uint8_t *dst_scanline = dp + y*dstWidth + pad; 484cb93a386Sopenharmony_ci memset(dst_scanline, 0xff, sw); 485cb93a386Sopenharmony_ci } 486cb93a386Sopenharmony_ci } 487cb93a386Sopenharmony_ci // normal and solid styles are the same for analytic rect blurs, so don't 488cb93a386Sopenharmony_ci // need to handle solid specially. 489cb93a386Sopenharmony_ci 490cb93a386Sopenharmony_ci return true; 491cb93a386Sopenharmony_ci} 492cb93a386Sopenharmony_ci 493cb93a386Sopenharmony_cibool SkBlurMask::BlurRRect(SkScalar sigma, SkMask *dst, 494cb93a386Sopenharmony_ci const SkRRect &src, SkBlurStyle style, 495cb93a386Sopenharmony_ci SkIPoint *margin, SkMask::CreateMode createMode) { 496cb93a386Sopenharmony_ci // Temporary for now -- always fail, should cause caller to fall back 497cb93a386Sopenharmony_ci // to old path. Plumbing just to land API and parallelize effort. 498cb93a386Sopenharmony_ci 499cb93a386Sopenharmony_ci return false; 500cb93a386Sopenharmony_ci} 501cb93a386Sopenharmony_ci 502cb93a386Sopenharmony_ci// The "simple" blur is a direct implementation of separable convolution with a discrete 503cb93a386Sopenharmony_ci// gaussian kernel. It's "ground truth" in a sense; too slow to be used, but very 504cb93a386Sopenharmony_ci// useful for correctness comparisons. 505cb93a386Sopenharmony_ci 506cb93a386Sopenharmony_cibool SkBlurMask::BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src, 507cb93a386Sopenharmony_ci SkBlurStyle style, SkIPoint* margin) { 508cb93a386Sopenharmony_ci 509cb93a386Sopenharmony_ci if (src.fFormat != SkMask::kA8_Format) { 510cb93a386Sopenharmony_ci return false; 511cb93a386Sopenharmony_ci } 512cb93a386Sopenharmony_ci 513cb93a386Sopenharmony_ci float variance = sigma * sigma; 514cb93a386Sopenharmony_ci 515cb93a386Sopenharmony_ci int windowSize = SkScalarCeilToInt(sigma*6); 516cb93a386Sopenharmony_ci // round window size up to nearest odd number 517cb93a386Sopenharmony_ci windowSize |= 1; 518cb93a386Sopenharmony_ci 519cb93a386Sopenharmony_ci SkAutoTMalloc<float> gaussWindow(windowSize); 520cb93a386Sopenharmony_ci 521cb93a386Sopenharmony_ci int halfWindow = windowSize >> 1; 522cb93a386Sopenharmony_ci 523cb93a386Sopenharmony_ci gaussWindow[halfWindow] = 1; 524cb93a386Sopenharmony_ci 525cb93a386Sopenharmony_ci float windowSum = 1; 526cb93a386Sopenharmony_ci for (int x = 1 ; x <= halfWindow ; ++x) { 527cb93a386Sopenharmony_ci float gaussian = expf(-x*x / (2*variance)); 528cb93a386Sopenharmony_ci gaussWindow[halfWindow + x] = gaussWindow[halfWindow-x] = gaussian; 529cb93a386Sopenharmony_ci windowSum += 2*gaussian; 530cb93a386Sopenharmony_ci } 531cb93a386Sopenharmony_ci 532cb93a386Sopenharmony_ci // leave the filter un-normalized for now; we will divide by the normalization 533cb93a386Sopenharmony_ci // sum later; 534cb93a386Sopenharmony_ci 535cb93a386Sopenharmony_ci int pad = halfWindow; 536cb93a386Sopenharmony_ci if (margin) { 537cb93a386Sopenharmony_ci margin->set( pad, pad ); 538cb93a386Sopenharmony_ci } 539cb93a386Sopenharmony_ci 540cb93a386Sopenharmony_ci dst->fBounds = src.fBounds; 541cb93a386Sopenharmony_ci dst->fBounds.outset(pad, pad); 542cb93a386Sopenharmony_ci 543cb93a386Sopenharmony_ci dst->fRowBytes = dst->fBounds.width(); 544cb93a386Sopenharmony_ci dst->fFormat = SkMask::kA8_Format; 545cb93a386Sopenharmony_ci dst->fImage = nullptr; 546cb93a386Sopenharmony_ci 547cb93a386Sopenharmony_ci if (src.fImage) { 548cb93a386Sopenharmony_ci 549cb93a386Sopenharmony_ci size_t dstSize = dst->computeImageSize(); 550cb93a386Sopenharmony_ci if (0 == dstSize) { 551cb93a386Sopenharmony_ci return false; // too big to allocate, abort 552cb93a386Sopenharmony_ci } 553cb93a386Sopenharmony_ci 554cb93a386Sopenharmony_ci int srcWidth = src.fBounds.width(); 555cb93a386Sopenharmony_ci int srcHeight = src.fBounds.height(); 556cb93a386Sopenharmony_ci int dstWidth = dst->fBounds.width(); 557cb93a386Sopenharmony_ci 558cb93a386Sopenharmony_ci const uint8_t* srcPixels = src.fImage; 559cb93a386Sopenharmony_ci uint8_t* dstPixels = SkMask::AllocImage(dstSize); 560cb93a386Sopenharmony_ci SkAutoMaskFreeImage autoFreeDstPixels(dstPixels); 561cb93a386Sopenharmony_ci 562cb93a386Sopenharmony_ci // do the actual blur. First, make a padded copy of the source. 563cb93a386Sopenharmony_ci // use double pad so we never have to check if we're outside anything 564cb93a386Sopenharmony_ci 565cb93a386Sopenharmony_ci int padWidth = srcWidth + 4*pad; 566cb93a386Sopenharmony_ci int padHeight = srcHeight; 567cb93a386Sopenharmony_ci int padSize = padWidth * padHeight; 568cb93a386Sopenharmony_ci 569cb93a386Sopenharmony_ci SkAutoTMalloc<uint8_t> padPixels(padSize); 570cb93a386Sopenharmony_ci memset(padPixels, 0, padSize); 571cb93a386Sopenharmony_ci 572cb93a386Sopenharmony_ci for (int y = 0 ; y < srcHeight; ++y) { 573cb93a386Sopenharmony_ci uint8_t* padptr = padPixels + y * padWidth + 2*pad; 574cb93a386Sopenharmony_ci const uint8_t* srcptr = srcPixels + y * srcWidth; 575cb93a386Sopenharmony_ci memcpy(padptr, srcptr, srcWidth); 576cb93a386Sopenharmony_ci } 577cb93a386Sopenharmony_ci 578cb93a386Sopenharmony_ci // blur in X, transposing the result into a temporary floating point buffer. 579cb93a386Sopenharmony_ci // also double-pad the intermediate result so that the second blur doesn't 580cb93a386Sopenharmony_ci // have to do extra conditionals. 581cb93a386Sopenharmony_ci 582cb93a386Sopenharmony_ci int tmpWidth = padHeight + 4*pad; 583cb93a386Sopenharmony_ci int tmpHeight = padWidth - 2*pad; 584cb93a386Sopenharmony_ci int tmpSize = tmpWidth * tmpHeight; 585cb93a386Sopenharmony_ci 586cb93a386Sopenharmony_ci SkAutoTMalloc<float> tmpImage(tmpSize); 587cb93a386Sopenharmony_ci memset(tmpImage, 0, tmpSize*sizeof(tmpImage[0])); 588cb93a386Sopenharmony_ci 589cb93a386Sopenharmony_ci for (int y = 0 ; y < padHeight ; ++y) { 590cb93a386Sopenharmony_ci uint8_t *srcScanline = padPixels + y*padWidth; 591cb93a386Sopenharmony_ci for (int x = pad ; x < padWidth - pad ; ++x) { 592cb93a386Sopenharmony_ci float *outPixel = tmpImage + (x-pad)*tmpWidth + y + 2*pad; // transposed output 593cb93a386Sopenharmony_ci uint8_t *windowCenter = srcScanline + x; 594cb93a386Sopenharmony_ci for (int i = -pad ; i <= pad ; ++i) { 595cb93a386Sopenharmony_ci *outPixel += gaussWindow[pad+i]*windowCenter[i]; 596cb93a386Sopenharmony_ci } 597cb93a386Sopenharmony_ci *outPixel /= windowSum; 598cb93a386Sopenharmony_ci } 599cb93a386Sopenharmony_ci } 600cb93a386Sopenharmony_ci 601cb93a386Sopenharmony_ci // blur in Y; now filling in the actual desired destination. We have to do 602cb93a386Sopenharmony_ci // the transpose again; these transposes guarantee that we read memory in 603cb93a386Sopenharmony_ci // linear order. 604cb93a386Sopenharmony_ci 605cb93a386Sopenharmony_ci for (int y = 0 ; y < tmpHeight ; ++y) { 606cb93a386Sopenharmony_ci float *srcScanline = tmpImage + y*tmpWidth; 607cb93a386Sopenharmony_ci for (int x = pad ; x < tmpWidth - pad ; ++x) { 608cb93a386Sopenharmony_ci float *windowCenter = srcScanline + x; 609cb93a386Sopenharmony_ci float finalValue = 0; 610cb93a386Sopenharmony_ci for (int i = -pad ; i <= pad ; ++i) { 611cb93a386Sopenharmony_ci finalValue += gaussWindow[pad+i]*windowCenter[i]; 612cb93a386Sopenharmony_ci } 613cb93a386Sopenharmony_ci finalValue /= windowSum; 614cb93a386Sopenharmony_ci uint8_t *outPixel = dstPixels + (x-pad)*dstWidth + y; // transposed output 615cb93a386Sopenharmony_ci int integerPixel = int(finalValue + 0.5f); 616cb93a386Sopenharmony_ci *outPixel = SkTPin(SkClampPos(integerPixel), 0, 255); 617cb93a386Sopenharmony_ci } 618cb93a386Sopenharmony_ci } 619cb93a386Sopenharmony_ci 620cb93a386Sopenharmony_ci dst->fImage = dstPixels; 621cb93a386Sopenharmony_ci switch (style) { 622cb93a386Sopenharmony_ci case kNormal_SkBlurStyle: 623cb93a386Sopenharmony_ci break; 624cb93a386Sopenharmony_ci case kSolid_SkBlurStyle: { 625cb93a386Sopenharmony_ci clamp_solid_with_orig( 626cb93a386Sopenharmony_ci dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes, 627cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes, 628cb93a386Sopenharmony_ci srcWidth, srcHeight); 629cb93a386Sopenharmony_ci } break; 630cb93a386Sopenharmony_ci case kOuter_SkBlurStyle: { 631cb93a386Sopenharmony_ci clamp_outer_with_orig( 632cb93a386Sopenharmony_ci dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes, 633cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes, 634cb93a386Sopenharmony_ci srcWidth, srcHeight); 635cb93a386Sopenharmony_ci } break; 636cb93a386Sopenharmony_ci case kInner_SkBlurStyle: { 637cb93a386Sopenharmony_ci // now we allocate the "real" dst, mirror the size of src 638cb93a386Sopenharmony_ci size_t srcSize = src.computeImageSize(); 639cb93a386Sopenharmony_ci if (0 == srcSize) { 640cb93a386Sopenharmony_ci return false; // too big to allocate, abort 641cb93a386Sopenharmony_ci } 642cb93a386Sopenharmony_ci dst->fImage = SkMask::AllocImage(srcSize); 643cb93a386Sopenharmony_ci merge_src_with_blur(dst->fImage, src.fRowBytes, 644cb93a386Sopenharmony_ci SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes, 645cb93a386Sopenharmony_ci dstPixels + pad*dst->fRowBytes + pad, 646cb93a386Sopenharmony_ci dst->fRowBytes, srcWidth, srcHeight); 647cb93a386Sopenharmony_ci SkMask::FreeImage(dstPixels); 648cb93a386Sopenharmony_ci } break; 649cb93a386Sopenharmony_ci } 650cb93a386Sopenharmony_ci autoFreeDstPixels.release(); 651cb93a386Sopenharmony_ci } 652cb93a386Sopenharmony_ci 653cb93a386Sopenharmony_ci if (style == kInner_SkBlurStyle) { 654cb93a386Sopenharmony_ci dst->fBounds = src.fBounds; // restore trimmed bounds 655cb93a386Sopenharmony_ci dst->fRowBytes = src.fRowBytes; 656cb93a386Sopenharmony_ci } 657cb93a386Sopenharmony_ci 658cb93a386Sopenharmony_ci return true; 659cb93a386Sopenharmony_ci} 660