16e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy/* 26e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * Copyright (C) 2013 The Android Open Source Project 36e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * 46e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * Licensed under the Apache License, Version 2.0 (the "License"); 56e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * you may not use this file except in compliance with the License. 66e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * You may obtain a copy of the License at 76e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * 86e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * http://www.apache.org/licenses/LICENSE-2.0 96e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * 106e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * Unless required by applicable law or agreed to in writing, software 116e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * distributed under the License is distributed on an "AS IS" BASIS, 126e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * See the License for the specific language governing permissions and 146e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy * limitations under the License. 156e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy */ 166e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 176e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy#include <math.h> 186e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 196e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy#include "Blur.h" 20e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger#include "MathUtils.h" 216e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 226e2004089305cf2cd958b52b234459a49a4e5c83Romain Guynamespace android { 236e2004089305cf2cd958b52b234459a49a4e5c83Romain Guynamespace uirenderer { 246e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 25c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger// This constant approximates the scaling done in the software path's 26c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)). 27c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerstatic const float BLUR_SIGMA_SCALE = 0.57735f; 28c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 29c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerfloat Blur::convertRadiusToSigma(float radius) { 30c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger return radius > 0 ? BLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; 31c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger} 32c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 33c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerfloat Blur::convertSigmaToRadius(float sigma) { 34c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f; 35c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger} 36c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 37e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger// if the original radius was on an integer boundary and the resulting radius 38e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger// is within the conversion error tolerance then we attempt to snap to the 39e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger// original integer boundary. 40e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenbergeruint32_t Blur::convertRadiusToInt(float radius) { 41e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger const float radiusCeil = ceilf(radius); 42e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger if (MathUtils::areEqual(radiusCeil, radius)) { 43e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger return radiusCeil; 44e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger } 45e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger return radius; 46e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger} 47e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger 48c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger/** 49c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * HWUI has used a slightly different equation than Skia to generate the value 50c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * for sigma and to preserve compatibility we have kept that logic. 51c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * 52c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * Based on some experimental radius and sigma values we approximate the 53c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * equation sigma = f(radius) as sigma = radius * 0.3 + 0.6. The larger the 54c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * radius gets, the more our gaussian blur will resemble a box blur since with 55c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * large sigma the gaussian curve begins to lose its shape. 56c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger */ 57c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerstatic float legacyConvertRadiusToSigma(float radius) { 58c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger return radius > 0 ? 0.3f * radius + 0.6f : 0.0f; 59c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger} 60c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 61a46ca5ec732a884c78d3e98f8b4eb8de03bc23f8huanhuan.x.wangvoid Blur::generateGaussianWeights(float* weights, float radius) { 62a46ca5ec732a884c78d3e98f8b4eb8de03bc23f8huanhuan.x.wang int32_t intRadius = convertRadiusToInt(radius); 63a46ca5ec732a884c78d3e98f8b4eb8de03bc23f8huanhuan.x.wang 646e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Compute gaussian weights for the blur 656e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // e is the euler's number 666e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy static float e = 2.718281828459045f; 676e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy static float pi = 3.1415926535897932f; 686e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 696e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // x is of the form [-radius .. 0 .. radius] 706e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // and sigma varies with radius. 71a46ca5ec732a884c78d3e98f8b4eb8de03bc23f8huanhuan.x.wang float sigma = legacyConvertRadiusToSigma(radius); 726e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 736e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Now compute the coefficints 746e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // We will store some redundant values to save some math during 756e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // the blur calculations 766e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // precompute some values 776e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float coeff1 = 1.0f / (sqrt(2.0f * pi) * sigma); 786e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float coeff2 = - 1.0f / (2.0f * sigma * sigma); 796e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 806e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float normalizeFactor = 0.0f; 81a46ca5ec732a884c78d3e98f8b4eb8de03bc23f8huanhuan.x.wang for (int32_t r = -intRadius; r <= intRadius; r ++) { 826e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float floatR = (float) r; 83a46ca5ec732a884c78d3e98f8b4eb8de03bc23f8huanhuan.x.wang weights[r + intRadius] = coeff1 * pow(e, floatR * floatR * coeff2); 84a46ca5ec732a884c78d3e98f8b4eb8de03bc23f8huanhuan.x.wang normalizeFactor += weights[r + intRadius]; 856e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 866e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 876e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy //Now we need to normalize the weights because all our coefficients need to add up to one 886e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy normalizeFactor = 1.0f / normalizeFactor; 89a46ca5ec732a884c78d3e98f8b4eb8de03bc23f8huanhuan.x.wang for (int32_t r = -intRadius; r <= intRadius; r ++) { 90a46ca5ec732a884c78d3e98f8b4eb8de03bc23f8huanhuan.x.wang weights[r + intRadius] *= normalizeFactor; 916e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 926e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy} 936e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 946e2004089305cf2cd958b52b234459a49a4e5c83Romain Guyvoid Blur::horizontal(float* weights, int32_t radius, 956e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 966e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float blurredPixel = 0.0f; 976e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float currentPixel = 0.0f; 986e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 996e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t y = 0; y < height; y ++) { 1006e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1016e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* input = source + y * width; 1026e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy uint8_t* output = dest + y * width; 1036e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1046e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t x = 0; x < width; x ++) { 1056e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel = 0.0f; 1066e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const float* gPtr = weights; 1076e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Optimization for non-border pixels 1086e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (x > radius && x < (width - radius)) { 1096e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t *i = input + (x - radius); 1106e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int r = -radius; r <= radius; r ++) { 1116e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) (*i); 1126e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1136e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1146e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy i++; 1156e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1166e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } else { 1176e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 1186e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Stepping left and right away from the pixel 1196e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy int validW = x + r; 1206e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validW < 0) { 1216e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validW = 0; 1226e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1236e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validW > width - 1) { 1246e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validW = width - 1; 1256e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1266e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1276e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) input[validW]; 1286e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1296e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1306e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1316e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1326e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy *output = (uint8_t)blurredPixel; 1336e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy output ++; 1346e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1356e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1366e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy} 1376e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1386e2004089305cf2cd958b52b234459a49a4e5c83Romain Guyvoid Blur::vertical(float* weights, int32_t radius, 1396e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1406e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float blurredPixel = 0.0f; 1416e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float currentPixel = 0.0f; 1426e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1436e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t y = 0; y < height; y ++) { 1446e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy uint8_t* output = dest + y * width; 1456e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1466e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t x = 0; x < width; x ++) { 1476e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel = 0.0f; 1486e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const float* gPtr = weights; 1496e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* input = source + x; 1506e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Optimization for non-border pixels 1516e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (y > radius && y < (height - radius)) { 1526e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t *i = input + ((y - radius) * width); 1536e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 1546e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) (*i); 1556e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1566e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1576e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy i += width; 1586e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1596e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } else { 1606e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 1616e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy int validH = y + r; 1626e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Clamp to zero and width 1636e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validH < 0) { 1646e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validH = 0; 1656e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1666e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validH > height - 1) { 1676e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validH = height - 1; 1686e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1696e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1706e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t *i = input + validH * width; 1716e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) (*i); 1726e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1736e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1746e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1756e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1766e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy *output = (uint8_t) blurredPixel; 1776e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy output++; 1786e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1796e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1806e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy} 1816e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1826e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy}; // namespace uirenderer 1836e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy}; // namespace android 184