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