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