Blur.cpp revision 6e2004089305cf2cd958b52b234459a49a4e5c83
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 266e2004089305cf2cd958b52b234459a49a4e5c83Romain Guyvoid Blur::generateGaussianWeights(float* weights, int32_t radius) { 276e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Compute gaussian weights for the blur 286e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // e is the euler's number 296e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy static float e = 2.718281828459045f; 306e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy static float pi = 3.1415926535897932f; 316e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 326e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // x is of the form [-radius .. 0 .. radius] 336e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // and sigma varies with radius. 346e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Based on some experimental radius values and sigma's 356e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // we approximately fit sigma = f(radius) as 366e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // sigma = radius * 0.3 + 0.6 376e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // The larger the radius gets, the more our gaussian blur 386e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // will resemble a box blur since with large sigma 396e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // the gaussian curve begins to lose its shape 406e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float sigma = 0.3f * (float) radius + 0.6f; 416e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 426e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Now compute the coefficints 436e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // We will store some redundant values to save some math during 446e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // the blur calculations 456e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // precompute some values 466e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float coeff1 = 1.0f / (sqrt(2.0f * pi) * sigma); 476e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float coeff2 = - 1.0f / (2.0f * sigma * sigma); 486e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 496e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float normalizeFactor = 0.0f; 506e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 516e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float floatR = (float) r; 526e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 536e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy normalizeFactor += weights[r + radius]; 546e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 556e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 566e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy //Now we need to normalize the weights because all our coefficients need to add up to one 576e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy normalizeFactor = 1.0f / normalizeFactor; 586e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 596e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy weights[r + radius] *= normalizeFactor; 606e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 616e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy} 626e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 636e2004089305cf2cd958b52b234459a49a4e5c83Romain Guyvoid Blur::horizontal(float* weights, int32_t radius, 646e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 656e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float blurredPixel = 0.0f; 666e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float currentPixel = 0.0f; 676e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 686e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t y = 0; y < height; y ++) { 696e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 706e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* input = source + y * width; 716e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy uint8_t* output = dest + y * width; 726e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 736e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t x = 0; x < width; x ++) { 746e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel = 0.0f; 756e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const float* gPtr = weights; 766e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Optimization for non-border pixels 776e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (x > radius && x < (width - radius)) { 786e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t *i = input + (x - radius); 796e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int r = -radius; r <= radius; r ++) { 806e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) (*i); 816e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 826e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 836e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy i++; 846e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 856e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } else { 866e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 876e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Stepping left and right away from the pixel 886e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy int validW = x + r; 896e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validW < 0) { 906e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validW = 0; 916e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 926e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validW > width - 1) { 936e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validW = width - 1; 946e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 956e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 966e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) input[validW]; 976e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 986e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 996e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1006e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1016e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy *output = (uint8_t)blurredPixel; 1026e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy output ++; 1036e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1046e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1056e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy} 1066e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1076e2004089305cf2cd958b52b234459a49a4e5c83Romain Guyvoid Blur::vertical(float* weights, int32_t radius, 1086e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1096e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float blurredPixel = 0.0f; 1106e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy float currentPixel = 0.0f; 1116e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1126e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t y = 0; y < height; y ++) { 1136e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy uint8_t* output = dest + y * width; 1146e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1156e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t x = 0; x < width; x ++) { 1166e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel = 0.0f; 1176e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const float* gPtr = weights; 1186e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t* input = source + x; 1196e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Optimization for non-border pixels 1206e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (y > radius && y < (height - radius)) { 1216e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t *i = input + ((y - radius) * width); 1226e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 1236e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) (*i); 1246e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1256e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1266e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy i += width; 1276e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1286e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } else { 1296e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy for (int32_t r = -radius; r <= radius; r ++) { 1306e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy int validH = y + r; 1316e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy // Clamp to zero and width 1326e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validH < 0) { 1336e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validH = 0; 1346e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1356e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy if (validH > height - 1) { 1366e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy validH = height - 1; 1376e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1386e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1396e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy const uint8_t *i = input + validH * width; 1406e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy currentPixel = (float) (*i); 1416e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy blurredPixel += currentPixel * gPtr[0]; 1426e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy gPtr++; 1436e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1446e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1456e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy *output = (uint8_t) blurredPixel; 1466e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy output++; 1476e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1486e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy } 1496e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy} 1506e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy 1516e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy}; // namespace uirenderer 1526e2004089305cf2cd958b52b234459a49a4e5c83Romain Guy}; // namespace android 153