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