1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2013 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #include <math.h>
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include "Blur.h"
20*d57664e9SAndroid Build Coastguard Worker #include "MathUtils.h"
21*d57664e9SAndroid Build Coastguard Worker
22*d57664e9SAndroid Build Coastguard Worker namespace android {
23*d57664e9SAndroid Build Coastguard Worker namespace uirenderer {
24*d57664e9SAndroid Build Coastguard Worker
25*d57664e9SAndroid Build Coastguard Worker // This constant approximates the scaling done in the software path's
26*d57664e9SAndroid Build Coastguard Worker // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
27*d57664e9SAndroid Build Coastguard Worker static const float BLUR_SIGMA_SCALE = 0.57735f;
28*d57664e9SAndroid Build Coastguard Worker
convertRadiusToSigma(float radius)29*d57664e9SAndroid Build Coastguard Worker float Blur::convertRadiusToSigma(float radius) {
30*d57664e9SAndroid Build Coastguard Worker return radius > 0 ? BLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
31*d57664e9SAndroid Build Coastguard Worker }
32*d57664e9SAndroid Build Coastguard Worker
convertSigmaToRadius(float sigma)33*d57664e9SAndroid Build Coastguard Worker float Blur::convertSigmaToRadius(float sigma) {
34*d57664e9SAndroid Build Coastguard Worker return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f;
35*d57664e9SAndroid Build Coastguard Worker }
36*d57664e9SAndroid Build Coastguard Worker
37*d57664e9SAndroid Build Coastguard Worker // if the original radius was on an integer boundary and the resulting radius
38*d57664e9SAndroid Build Coastguard Worker // is within the conversion error tolerance then we attempt to snap to the
39*d57664e9SAndroid Build Coastguard Worker // original integer boundary.
convertRadiusToInt(float radius)40*d57664e9SAndroid Build Coastguard Worker uint32_t Blur::convertRadiusToInt(float radius) {
41*d57664e9SAndroid Build Coastguard Worker const float radiusCeil = ceilf(radius);
42*d57664e9SAndroid Build Coastguard Worker if (MathUtils::areEqual(radiusCeil, radius)) {
43*d57664e9SAndroid Build Coastguard Worker return radiusCeil;
44*d57664e9SAndroid Build Coastguard Worker }
45*d57664e9SAndroid Build Coastguard Worker return radius;
46*d57664e9SAndroid Build Coastguard Worker }
47*d57664e9SAndroid Build Coastguard Worker
48*d57664e9SAndroid Build Coastguard Worker /**
49*d57664e9SAndroid Build Coastguard Worker * HWUI has used a slightly different equation than Skia to generate the value
50*d57664e9SAndroid Build Coastguard Worker * for sigma and to preserve compatibility we have kept that logic.
51*d57664e9SAndroid Build Coastguard Worker *
52*d57664e9SAndroid Build Coastguard Worker * Based on some experimental radius and sigma values we approximate the
53*d57664e9SAndroid Build Coastguard Worker * equation sigma = f(radius) as sigma = radius * 0.3 + 0.6. The larger the
54*d57664e9SAndroid Build Coastguard Worker * radius gets, the more our gaussian blur will resemble a box blur since with
55*d57664e9SAndroid Build Coastguard Worker * large sigma the gaussian curve begins to lose its shape.
56*d57664e9SAndroid Build Coastguard Worker */
legacyConvertRadiusToSigma(float radius)57*d57664e9SAndroid Build Coastguard Worker static float legacyConvertRadiusToSigma(float radius) {
58*d57664e9SAndroid Build Coastguard Worker return radius > 0 ? 0.3f * radius + 0.6f : 0.0f;
59*d57664e9SAndroid Build Coastguard Worker }
60*d57664e9SAndroid Build Coastguard Worker
generateGaussianWeights(float * weights,float radius)61*d57664e9SAndroid Build Coastguard Worker void Blur::generateGaussianWeights(float* weights, float radius) {
62*d57664e9SAndroid Build Coastguard Worker int32_t intRadius = convertRadiusToInt(radius);
63*d57664e9SAndroid Build Coastguard Worker
64*d57664e9SAndroid Build Coastguard Worker // Compute gaussian weights for the blur
65*d57664e9SAndroid Build Coastguard Worker // e is the euler's number
66*d57664e9SAndroid Build Coastguard Worker static float e = 2.718281828459045f;
67*d57664e9SAndroid Build Coastguard Worker static float pi = 3.1415926535897932f;
68*d57664e9SAndroid Build Coastguard Worker // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
69*d57664e9SAndroid Build Coastguard Worker // x is of the form [-radius .. 0 .. radius]
70*d57664e9SAndroid Build Coastguard Worker // and sigma varies with radius.
71*d57664e9SAndroid Build Coastguard Worker float sigma = legacyConvertRadiusToSigma(radius);
72*d57664e9SAndroid Build Coastguard Worker
73*d57664e9SAndroid Build Coastguard Worker // Now compute the coefficints
74*d57664e9SAndroid Build Coastguard Worker // We will store some redundant values to save some math during
75*d57664e9SAndroid Build Coastguard Worker // the blur calculations
76*d57664e9SAndroid Build Coastguard Worker // precompute some values
77*d57664e9SAndroid Build Coastguard Worker float coeff1 = 1.0f / (sqrt(2.0f * pi) * sigma);
78*d57664e9SAndroid Build Coastguard Worker float coeff2 = -1.0f / (2.0f * sigma * sigma);
79*d57664e9SAndroid Build Coastguard Worker
80*d57664e9SAndroid Build Coastguard Worker float normalizeFactor = 0.0f;
81*d57664e9SAndroid Build Coastguard Worker for (int32_t r = -intRadius; r <= intRadius; r++) {
82*d57664e9SAndroid Build Coastguard Worker float floatR = (float)r;
83*d57664e9SAndroid Build Coastguard Worker weights[r + intRadius] = coeff1 * pow(e, floatR * floatR * coeff2);
84*d57664e9SAndroid Build Coastguard Worker normalizeFactor += weights[r + intRadius];
85*d57664e9SAndroid Build Coastguard Worker }
86*d57664e9SAndroid Build Coastguard Worker
87*d57664e9SAndroid Build Coastguard Worker // Now we need to normalize the weights because all our coefficients need to add up to one
88*d57664e9SAndroid Build Coastguard Worker normalizeFactor = 1.0f / normalizeFactor;
89*d57664e9SAndroid Build Coastguard Worker for (int32_t r = -intRadius; r <= intRadius; r++) {
90*d57664e9SAndroid Build Coastguard Worker weights[r + intRadius] *= normalizeFactor;
91*d57664e9SAndroid Build Coastguard Worker }
92*d57664e9SAndroid Build Coastguard Worker }
93*d57664e9SAndroid Build Coastguard Worker
horizontal(float * weights,int32_t radius,const uint8_t * source,uint8_t * dest,int32_t width,int32_t height)94*d57664e9SAndroid Build Coastguard Worker void Blur::horizontal(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest,
95*d57664e9SAndroid Build Coastguard Worker int32_t width, int32_t height) {
96*d57664e9SAndroid Build Coastguard Worker float blurredPixel = 0.0f;
97*d57664e9SAndroid Build Coastguard Worker float currentPixel = 0.0f;
98*d57664e9SAndroid Build Coastguard Worker
99*d57664e9SAndroid Build Coastguard Worker for (int32_t y = 0; y < height; y++) {
100*d57664e9SAndroid Build Coastguard Worker const uint8_t* input = source + y * width;
101*d57664e9SAndroid Build Coastguard Worker uint8_t* output = dest + y * width;
102*d57664e9SAndroid Build Coastguard Worker
103*d57664e9SAndroid Build Coastguard Worker for (int32_t x = 0; x < width; x++) {
104*d57664e9SAndroid Build Coastguard Worker blurredPixel = 0.0f;
105*d57664e9SAndroid Build Coastguard Worker const float* gPtr = weights;
106*d57664e9SAndroid Build Coastguard Worker // Optimization for non-border pixels
107*d57664e9SAndroid Build Coastguard Worker if (x > radius && x < (width - radius)) {
108*d57664e9SAndroid Build Coastguard Worker const uint8_t* i = input + (x - radius);
109*d57664e9SAndroid Build Coastguard Worker for (int r = -radius; r <= radius; r++) {
110*d57664e9SAndroid Build Coastguard Worker currentPixel = (float)(*i);
111*d57664e9SAndroid Build Coastguard Worker blurredPixel += currentPixel * gPtr[0];
112*d57664e9SAndroid Build Coastguard Worker gPtr++;
113*d57664e9SAndroid Build Coastguard Worker i++;
114*d57664e9SAndroid Build Coastguard Worker }
115*d57664e9SAndroid Build Coastguard Worker } else {
116*d57664e9SAndroid Build Coastguard Worker for (int32_t r = -radius; r <= radius; r++) {
117*d57664e9SAndroid Build Coastguard Worker // Stepping left and right away from the pixel
118*d57664e9SAndroid Build Coastguard Worker int validW = x + r;
119*d57664e9SAndroid Build Coastguard Worker if (validW < 0) {
120*d57664e9SAndroid Build Coastguard Worker validW = 0;
121*d57664e9SAndroid Build Coastguard Worker }
122*d57664e9SAndroid Build Coastguard Worker if (validW > width - 1) {
123*d57664e9SAndroid Build Coastguard Worker validW = width - 1;
124*d57664e9SAndroid Build Coastguard Worker }
125*d57664e9SAndroid Build Coastguard Worker
126*d57664e9SAndroid Build Coastguard Worker currentPixel = (float)input[validW];
127*d57664e9SAndroid Build Coastguard Worker blurredPixel += currentPixel * gPtr[0];
128*d57664e9SAndroid Build Coastguard Worker gPtr++;
129*d57664e9SAndroid Build Coastguard Worker }
130*d57664e9SAndroid Build Coastguard Worker }
131*d57664e9SAndroid Build Coastguard Worker *output = (uint8_t)blurredPixel;
132*d57664e9SAndroid Build Coastguard Worker output++;
133*d57664e9SAndroid Build Coastguard Worker }
134*d57664e9SAndroid Build Coastguard Worker }
135*d57664e9SAndroid Build Coastguard Worker }
136*d57664e9SAndroid Build Coastguard Worker
vertical(float * weights,int32_t radius,const uint8_t * source,uint8_t * dest,int32_t width,int32_t height)137*d57664e9SAndroid Build Coastguard Worker void Blur::vertical(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest,
138*d57664e9SAndroid Build Coastguard Worker int32_t width, int32_t height) {
139*d57664e9SAndroid Build Coastguard Worker float blurredPixel = 0.0f;
140*d57664e9SAndroid Build Coastguard Worker float currentPixel = 0.0f;
141*d57664e9SAndroid Build Coastguard Worker
142*d57664e9SAndroid Build Coastguard Worker for (int32_t y = 0; y < height; y++) {
143*d57664e9SAndroid Build Coastguard Worker uint8_t* output = dest + y * width;
144*d57664e9SAndroid Build Coastguard Worker
145*d57664e9SAndroid Build Coastguard Worker for (int32_t x = 0; x < width; x++) {
146*d57664e9SAndroid Build Coastguard Worker blurredPixel = 0.0f;
147*d57664e9SAndroid Build Coastguard Worker const float* gPtr = weights;
148*d57664e9SAndroid Build Coastguard Worker const uint8_t* input = source + x;
149*d57664e9SAndroid Build Coastguard Worker // Optimization for non-border pixels
150*d57664e9SAndroid Build Coastguard Worker if (y > radius && y < (height - radius)) {
151*d57664e9SAndroid Build Coastguard Worker const uint8_t* i = input + ((y - radius) * width);
152*d57664e9SAndroid Build Coastguard Worker for (int32_t r = -radius; r <= radius; r++) {
153*d57664e9SAndroid Build Coastguard Worker currentPixel = (float)(*i);
154*d57664e9SAndroid Build Coastguard Worker blurredPixel += currentPixel * gPtr[0];
155*d57664e9SAndroid Build Coastguard Worker gPtr++;
156*d57664e9SAndroid Build Coastguard Worker i += width;
157*d57664e9SAndroid Build Coastguard Worker }
158*d57664e9SAndroid Build Coastguard Worker } else {
159*d57664e9SAndroid Build Coastguard Worker for (int32_t r = -radius; r <= radius; r++) {
160*d57664e9SAndroid Build Coastguard Worker int validH = y + r;
161*d57664e9SAndroid Build Coastguard Worker // Clamp to zero and width
162*d57664e9SAndroid Build Coastguard Worker if (validH < 0) {
163*d57664e9SAndroid Build Coastguard Worker validH = 0;
164*d57664e9SAndroid Build Coastguard Worker }
165*d57664e9SAndroid Build Coastguard Worker if (validH > height - 1) {
166*d57664e9SAndroid Build Coastguard Worker validH = height - 1;
167*d57664e9SAndroid Build Coastguard Worker }
168*d57664e9SAndroid Build Coastguard Worker
169*d57664e9SAndroid Build Coastguard Worker const uint8_t* i = input + validH * width;
170*d57664e9SAndroid Build Coastguard Worker currentPixel = (float)(*i);
171*d57664e9SAndroid Build Coastguard Worker blurredPixel += currentPixel * gPtr[0];
172*d57664e9SAndroid Build Coastguard Worker gPtr++;
173*d57664e9SAndroid Build Coastguard Worker }
174*d57664e9SAndroid Build Coastguard Worker }
175*d57664e9SAndroid Build Coastguard Worker *output = (uint8_t)blurredPixel;
176*d57664e9SAndroid Build Coastguard Worker output++;
177*d57664e9SAndroid Build Coastguard Worker }
178*d57664e9SAndroid Build Coastguard Worker }
179*d57664e9SAndroid Build Coastguard Worker }
180*d57664e9SAndroid Build Coastguard Worker
181*d57664e9SAndroid Build Coastguard Worker } // namespace uirenderer
182*d57664e9SAndroid Build Coastguard Worker } // namespace android
183