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#define LOG_TAG "OpenGLRenderer" 186e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 196e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy#include <math.h> 206e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 216e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy#include "Blur.h" 22e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger#include "MathUtils.h" 236e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 246e2004089305cf2cd958b52b234459a49a4e5c83Romain Guynamespace android { 256e2004089305cf2cd958b52b234459a49a4e5c83Romain Guynamespace uirenderer { 266e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 27c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger// This constant approximates the scaling done in the software path's 28c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)). 29c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerstatic const float BLUR_SIGMA_SCALE = 0.57735f; 30c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 31c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerfloat Blur::convertRadiusToSigma(float radius) { 32c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger return radius > 0 ? BLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; 33c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger} 34c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 35c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerfloat Blur::convertSigmaToRadius(float sigma) { 36c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f; 37c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger} 38c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 39e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger// if the original radius was on an integer boundary and the resulting radius 40e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger// is within the conversion error tolerance then we attempt to snap to the 41e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger// original integer boundary. 42e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenbergeruint32_t Blur::convertRadiusToInt(float radius) { 43e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger const float radiusCeil = ceilf(radius); 44e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger if (MathUtils::areEqual(radiusCeil, radius)) { 45e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger return radiusCeil; 46e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger } 47e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger return radius; 48e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger} 49e392c81f6b8f9ace0c0a48c9d4df117fda31fd13Derek Sollenberger 50c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger/** 51c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * HWUI has used a slightly different equation than Skia to generate the value 52c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * for sigma and to preserve compatibility we have kept that logic. 53c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * 54c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * Based on some experimental radius and sigma values we approximate the 55c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * equation sigma = f(radius) as sigma = radius * 0.3 + 0.6. The larger the 56c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * radius gets, the more our gaussian blur will resemble a box blur since with 57c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * large sigma the gaussian curve begins to lose its shape. 58c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger */ 59c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerstatic float legacyConvertRadiusToSigma(float radius) { 60c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger return radius > 0 ? 0.3f * radius + 0.6f : 0.0f; 61c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger} 62c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 636e2004089305cf2cd958b52b234459a49a4e5c83Romain Guyvoid Blur::generateGaussianWeights(float* weights, int32_t radius) { 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. 71c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger float sigma = legacyConvertRadiusToSigma((float) 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; 816e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 826e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float floatR = (float) r; 836e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 846e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy normalizeFactor += weights[r + radius]; 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; 896e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 906e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy weights[r + radius] *= 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