Blur.cpp revision c29a0a4664a4b9871fadd668b632469a0db240b9
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" 226e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 236e2004089305cf2cd958b52b234459a49a4e5c83Romain Guynamespace android { 246e2004089305cf2cd958b52b234459a49a4e5c83Romain Guynamespace uirenderer { 256e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 26c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger// This constant approximates the scaling done in the software path's 27c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)). 28c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerstatic const float BLUR_SIGMA_SCALE = 0.57735f; 29c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 30c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerfloat Blur::convertRadiusToSigma(float radius) { 31c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger return radius > 0 ? BLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; 32c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger} 33c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 34c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerfloat Blur::convertSigmaToRadius(float sigma) { 35c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f; 36c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger} 37c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 38c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger/** 39c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * HWUI has used a slightly different equation than Skia to generate the value 40c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * for sigma and to preserve compatibility we have kept that logic. 41c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * 42c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * Based on some experimental radius and sigma values we approximate the 43c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * equation sigma = f(radius) as sigma = radius * 0.3 + 0.6. The larger the 44c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * radius gets, the more our gaussian blur will resemble a box blur since with 45c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger * large sigma the gaussian curve begins to lose its shape. 46c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger */ 47c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenbergerstatic float legacyConvertRadiusToSigma(float radius) { 48c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger return radius > 0 ? 0.3f * radius + 0.6f : 0.0f; 49c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger} 50c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger 516e2004089305cf2cd958b52b234459a49a4e5c83Romain Guyvoid Blur::generateGaussianWeights(float* weights, int32_t radius) { 526e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Compute gaussian weights for the blur 536e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // e is the euler's number 546e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy static float e = 2.718281828459045f; 556e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy static float pi = 3.1415926535897932f; 566e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 576e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // x is of the form [-radius .. 0 .. radius] 586e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // and sigma varies with radius. 59c29a0a4664a4b9871fadd668b632469a0db240b9Derek Sollenberger float sigma = legacyConvertRadiusToSigma((float) radius); 606e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 616e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Now compute the coefficints 626e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // We will store some redundant values to save some math during 636e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // the blur calculations 646e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // precompute some values 656e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float coeff1 = 1.0f / (sqrt(2.0f * pi) * sigma); 666e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float coeff2 = - 1.0f / (2.0f * sigma * sigma); 676e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 686e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float normalizeFactor = 0.0f; 696e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 706e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float floatR = (float) r; 716e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 726e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy normalizeFactor += weights[r + radius]; 736e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 746e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 756e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy //Now we need to normalize the weights because all our coefficients need to add up to one 766e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy normalizeFactor = 1.0f / normalizeFactor; 776e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 786e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy weights[r + radius] *= normalizeFactor; 796e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 806e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy} 816e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 826e2004089305cf2cd958b52b234459a49a4e5c83Romain Guyvoid Blur::horizontal(float* weights, int32_t radius, 836e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 846e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float blurredPixel = 0.0f; 856e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float currentPixel = 0.0f; 866e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 876e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t y = 0; y < height; y ++) { 886e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 896e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* input = source + y * width; 906e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy uint8_t* output = dest + y * width; 916e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 926e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t x = 0; x < width; x ++) { 936e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel = 0.0f; 946e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const float* gPtr = weights; 956e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Optimization for non-border pixels 966e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (x > radius && x < (width - radius)) { 976e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t *i = input + (x - radius); 986e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int r = -radius; r <= radius; r ++) { 996e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) (*i); 1006e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1016e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1026e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy i++; 1036e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1046e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } else { 1056e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 1066e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Stepping left and right away from the pixel 1076e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy int validW = x + r; 1086e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validW < 0) { 1096e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validW = 0; 1106e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1116e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validW > width - 1) { 1126e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validW = width - 1; 1136e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1146e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1156e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) input[validW]; 1166e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1176e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1186e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1196e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1206e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy *output = (uint8_t)blurredPixel; 1216e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy output ++; 1226e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1236e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1246e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy} 1256e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1266e2004089305cf2cd958b52b234459a49a4e5c83Romain Guyvoid Blur::vertical(float* weights, int32_t radius, 1276e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1286e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float blurredPixel = 0.0f; 1296e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float currentPixel = 0.0f; 1306e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1316e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t y = 0; y < height; y ++) { 1326e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy uint8_t* output = dest + y * width; 1336e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1346e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t x = 0; x < width; x ++) { 1356e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel = 0.0f; 1366e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const float* gPtr = weights; 1376e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* input = source + x; 1386e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Optimization for non-border pixels 1396e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (y > radius && y < (height - radius)) { 1406e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t *i = input + ((y - radius) * width); 1416e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 1426e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) (*i); 1436e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1446e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1456e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy i += width; 1466e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1476e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } else { 1486e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 1496e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy int validH = y + r; 1506e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Clamp to zero and width 1516e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validH < 0) { 1526e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validH = 0; 1536e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1546e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validH > height - 1) { 1556e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validH = height - 1; 1566e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1576e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1586e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t *i = input + validH * width; 1596e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) (*i); 1606e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1616e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1626e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1636e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1646e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy *output = (uint8_t) blurredPixel; 1656e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy output++; 1666e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1676e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1686e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy} 1696e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1706e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy}; // namespace uirenderer 1716e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy}; // namespace android 172