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