1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2006 The Android Open Source Project
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkBlurMask.h"
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkMaskBlurFilter.h"
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkMath.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTemplates.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkEndian.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// This constant approximates the scaling done in the software path's
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// IMHO, it actually should be 1:  we blur "less" than we should do
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// according to the CSS and canvas specs, simply because Safari does the same.
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Firefox used to do the same too, until 4.0 where they fixed it.  So at some
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// point we should probably get rid of these scaling constants and rebaseline
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// all the blur tests.
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic const SkScalar kBLUR_SIGMA_SCALE = 0.57735f;
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) {
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkScalar SkBlurMask::ConvertSigmaToRadius(SkScalar sigma) {
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return sigma > 0.5f ? (sigma - 0.5f) / kBLUR_SIGMA_SCALE : 0.0f;
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void merge_src_with_blur(uint8_t dst[], int dstRB,
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                const uint8_t src[], int srcRB,
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                const uint8_t blur[], int blurRB,
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                int sw, int sh) {
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dstRB -= sw;
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    srcRB -= sw;
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    blurRB -= sw;
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    while (--sh >= 0) {
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int x = sw - 1; x >= 0; --x) {
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            dst += 1;
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            src += 1;
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blur += 1;
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst += dstRB;
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        src += srcRB;
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        blur += blurRB;
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void clamp_with_orig(uint8_t dst[], int dstRowBytes,
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            const uint8_t src[], int srcRowBytes,
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            int sw, int sh,
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            SkBlurStyle style) {
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int x;
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    while (--sh >= 0) {
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (style) {
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case kSolid_SkBlurStyle:
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (x = sw - 1; x >= 0; --x) {
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                int s = *src;
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                int d = *dst;
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                *dst = SkToU8(s + d - SkMulDiv255Round(s, d));
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                dst += 1;
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                src += 1;
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case kOuter_SkBlurStyle:
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (x = sw - 1; x >= 0; --x) {
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (*src) {
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                dst += 1;
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                src += 1;
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        default:
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkDEBUGFAIL("Unexpected blur style here");
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst += dstRowBytes - sw;
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        src += srcRowBytes - sw;
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot///////////////////////////////////////////////////////////////////////////////
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// we use a local function to wrap the class static method to work around
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// a bug in gcc98
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkMask_FreeImage(uint8_t* image);
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkMask_FreeImage(uint8_t* image) {
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMask::FreeImage(image);
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src,
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                         SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                         SkIPoint* margin, bool force_quality) {
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (src.fFormat != SkMask::kA8_Format) {
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkIPoint border;
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMaskBlurFilter blurFilter{sigma, sigma};
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (blurFilter.hasNoBlur()) {
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    border = blurFilter.blur(src, dst);
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // If src.fImage is null, then this call is only to calculate the border.
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (src.fImage != nullptr && dst->fImage == nullptr) {
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (src.fImage != nullptr) {
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // if need be, alloc the "real" dst (same size as src) and copy/merge
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // the blur into it (applying the src)
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (style == kInner_SkBlurStyle) {
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // now we allocate the "real" dst, mirror the size of src
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            size_t srcSize = src.computeImageSize();
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (0 == srcSize) {
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return false;   // too big to allocate, abort
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            auto blur = dst->fImage;
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            dst->fImage = SkMask::AllocImage(srcSize);
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            auto blurStart = &blur[border.x() + border.y() * dst->fRowBytes];
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            merge_src_with_blur(dst->fImage, src.fRowBytes,
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                src.fImage, src.fRowBytes,
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                blurStart,
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                dst->fRowBytes,
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                src.fBounds.width(), src.fBounds.height());
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkMask::FreeImage(blur);
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else if (style != kNormal_SkBlurStyle) {
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes];
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            clamp_with_orig(dstStart,
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            dst->fRowBytes, src.fImage, src.fRowBytes,
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            src.fBounds.width(), src.fBounds.height(), style);
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (style == kInner_SkBlurStyle) {
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst->fBounds = src.fBounds; // restore trimmed bounds
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst->fRowBytes = src.fRowBytes;
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (margin != nullptr) {
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        *margin = border;
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/* Convolving a box with itself three times results in a piecewise
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   quadratic function:
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   0                              x <= -1.5
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   9/8 + 3/2 x + 1/2 x^2   -1.5 < x <= -.5
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   3/4 - x^2                -.5 < x <= .5
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   9/8 - 3/2 x + 1/2 x^2    0.5 < x <= 1.5
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   0                        1.5 < x
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   Mathematica:
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   g[x_] := Piecewise [ {
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     {9/8 + 3/2 x + 1/2 x^2 ,  -1.5 < x <= -.5},
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     {3/4 - x^2             ,   -.5 < x <= .5},
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     {9/8 - 3/2 x + 1/2 x^2 ,   0.5 < x <= 1.5}
170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   }, 0]
171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   To get the profile curve of the blurred step function at the rectangle
173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   edge, we evaluate the indefinite integral, which is piecewise cubic:
174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   0                                        x <= -1.5
176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3   -1.5 < x <= -0.5
177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   1/2 + 3/4 x - 1/3 x^3              -.5 < x <= .5
178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3     .5 < x <= 1.5
179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   1                                  1.5 < x
180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   in Mathematica code:
182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   gi[x_] := Piecewise[ {
184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     { 0 , x <= -1.5 },
185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     { 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 },
186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     { 1/2 + 3/4 x - 1/3 x^3          ,  -.5 < x <= .5},
187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     { 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3,   .5 < x <= 1.5}
188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   },1]
189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot*/
190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic float gaussianIntegral(float x) {
192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (x > 1.5f) {
193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return 0.0f;
194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (x < -1.5f) {
196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return 1.0f;
197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float x2 = x*x;
200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float x3 = x2*x;
201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if ( x > 0.5f ) {
203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if ( x > -0.5f ) {
206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return 0.5f - (0.75f * x - x3 / 3.0f);
207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*  ComputeBlurProfile allocates and fills in an array of floating
212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    point values between 0 and 255 for the profile signature of
213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    a blurred half-plane with the given blur radius.  Since we're
214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    all the time, we actually fill in the profile pre-inverted
216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (already done 255-x).
217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    It's the responsibility of the caller to delete the
219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    memory returned in profile_out.
220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot*/
221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotuint8_t* SkBlurMask::ComputeBlurProfile(SkScalar sigma) {
223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int size = SkScalarCeilToInt(6*sigma);
224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int center = size >> 1;
226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint8_t* profile = new uint8_t[size];
227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float invr = 1.f/(2*sigma);
229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    profile[0] = 255;
231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int x = 1 ; x < size ; ++x) {
232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        float scaled_x = (center - x - .5f) * invr;
233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        float gi = gaussianIntegral(scaled_x);
234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        profile[x] = 255 - (uint8_t) (255.f * gi);
235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return profile;
238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// TODO MAYBE: Maintain a profile cache to avoid recomputing this for
241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// commonly used radii.  Consider baking some of the most common blur radii
242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// directly in as static data?
243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Implementation adapted from Michael Herf's approach:
245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// http://stereopsis.com/shadowrect/
246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotuint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc, int blurred_width, int sharp_width) {
248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge?
249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int ox = dx >> 1;
250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (ox < 0) {
251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ox = 0;
252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return profile[ox];
255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile,
258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        unsigned int width, SkScalar sigma) {
259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    unsigned int profile_size = SkScalarCeilToInt(6*sigma);
261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTMalloc<uint8_t> horizontalScanline(width);
262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    unsigned int sw = width - profile_size;
264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // nearest odd number less than the profile size represents the center
265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // of the (2x scaled) profile
266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int center = ( profile_size & ~1 ) - 1;
267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int w = sw - center;
269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (unsigned int x = 0 ; x < width ; ++x) {
271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot       if (profile_size <= sw) {
272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           pixels[x] = ProfileLookup(profile, x, width, w);
273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot       } else {
274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           float span = float(sw)/(2*sigma);
275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           float giX = 1.5f - (x+.5f)/(2*sigma);
276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           pixels[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot       }
278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          const SkRect &src, SkBlurStyle style,
283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          SkIPoint *margin, SkMask::CreateMode createMode) {
284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int profile_size = SkScalarCeilToInt(6*sigma);
285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int pad = profile_size/2;
287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (margin) {
288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        margin->set( pad, pad );
289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad),
292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                     SkScalarRoundToInt(src.fTop - pad),
293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                     SkScalarRoundToInt(src.fRight + pad),
294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                     SkScalarRoundToInt(src.fBottom + pad));
295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fRowBytes = dst->fBounds.width();
297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fFormat = SkMask::kA8_Format;
298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fImage = nullptr;
299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int             sw = SkScalarFloorToInt(src.width());
301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int             sh = SkScalarFloorToInt(src.height());
302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (createMode == SkMask::kJustComputeBounds_CreateMode) {
304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (style == kInner_SkBlurStyle) {
305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             SkScalarRoundToInt(src.fTop),
307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             SkScalarRoundToInt(src.fRight),
308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            dst->fRowBytes = sw;
310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return true;
312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    std::unique_ptr<uint8_t[]> profile(ComputeBlurProfile(sigma));
315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    size_t dstSize = dst->computeImageSize();
317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (0 == dstSize) {
318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;   // too big to allocate, abort
319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint8_t*        dp = SkMask::AllocImage(dstSize);
322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fImage = dp;
324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int dstHeight = dst->fBounds.height();
326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int dstWidth = dst->fBounds.width();
327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint8_t *outptr = dp;
329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTMalloc<uint8_t> verticalScanline(dstHeight);
332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ComputeBlurredScanline(horizontalScanline, profile.get(), dstWidth, sigma);
334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ComputeBlurredScanline(verticalScanline, profile.get(), dstHeight, sigma);
335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int y = 0 ; y < dstHeight ; ++y) {
337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int x = 0 ; x < dstWidth ; x++) {
338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]);
339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            *(outptr++) = maskval;
340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (style == kInner_SkBlurStyle) {
344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // now we allocate the "real" dst, mirror the size of src
345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        size_t srcSize = (size_t)(src.width() * src.height());
346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (0 == srcSize) {
347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;   // too big to allocate, abort
348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst->fImage = SkMask::AllocImage(srcSize);
350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int y = 0 ; y < sh ; y++) {
351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            uint8_t *inner_scanline = dst->fImage + y*sw;
353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            memcpy(inner_scanline, blur_scanline, sw);
354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkMask::FreeImage(dp);
356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                         SkScalarRoundToInt(src.fTop),
359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                         SkScalarRoundToInt(src.fRight),
360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                         SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst->fRowBytes = sw;
362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else if (style == kOuter_SkBlurStyle) {
364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int y = pad ; y < dstHeight-pad ; y++) {
365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            uint8_t *dst_scanline = dp + y*dstWidth + pad;
366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            memset(dst_scanline, 0, sw);
367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else if (style == kSolid_SkBlurStyle) {
369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int y = pad ; y < dstHeight-pad ; y++) {
370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            uint8_t *dst_scanline = dp + y*dstWidth + pad;
371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            memset(dst_scanline, 0xff, sw);
372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // normal and solid styles are the same for analytic rect blurs, so don't
375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // need to handle solid specially.
376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkBlurMask::BlurRRect(SkScalar sigma, SkMask *dst,
381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           const SkRRect &src, SkBlurStyle style,
382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           SkIPoint *margin, SkMask::CreateMode createMode) {
383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Temporary for now -- always fail, should cause caller to fall back
384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // to old path.  Plumbing just to land API and parallelize effort.
385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return false;
387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// The "simple" blur is a direct implementation of separable convolution with a discrete
390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// gaussian kernel.  It's "ground truth" in a sense; too slow to be used, but very
391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// useful for correctness comparisons.
392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkBlurMask::BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src,
394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 SkBlurStyle style, SkIPoint* margin) {
395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (src.fFormat != SkMask::kA8_Format) {
397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float variance = sigma * sigma;
401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int windowSize = SkScalarCeilToInt(sigma*6);
403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // round window size up to nearest odd number
404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    windowSize |= 1;
405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTMalloc<float> gaussWindow(windowSize);
407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int halfWindow = windowSize >> 1;
409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    gaussWindow[halfWindow] = 1;
411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float windowSum = 1;
413fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int x = 1 ; x <= halfWindow ; ++x) {
414fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        float gaussian = expf(-x*x / (2*variance));
415fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        gaussWindow[halfWindow + x] = gaussWindow[halfWindow-x] = gaussian;
416fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        windowSum += 2*gaussian;
417fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
418fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
419fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // leave the filter un-normalized for now; we will divide by the normalization
420fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // sum later;
421fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
422fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int pad = halfWindow;
423fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (margin) {
424fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        margin->set( pad, pad );
425fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
426fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
427fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fBounds = src.fBounds;
428fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fBounds.outset(pad, pad);
429fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
430fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fRowBytes = dst->fBounds.width();
431fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fFormat = SkMask::kA8_Format;
432fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    dst->fImage = nullptr;
433fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
434fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (src.fImage) {
435fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
436fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        size_t dstSize = dst->computeImageSize();
437fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (0 == dstSize) {
438fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;   // too big to allocate, abort
439fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
440fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
441fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int             srcWidth = src.fBounds.width();
442fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int             srcHeight = src.fBounds.height();
443fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int             dstWidth = dst->fBounds.width();
444fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
445fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const uint8_t*  srcPixels = src.fImage;
446fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint8_t*        dstPixels = SkMask::AllocImage(dstSize);
447fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dstPixels);
448fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
449fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // do the actual blur.  First, make a padded copy of the source.
450fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // use double pad so we never have to check if we're outside anything
451fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
452fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int padWidth = srcWidth + 4*pad;
453fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int padHeight = srcHeight;
454fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int padSize = padWidth * padHeight;
455fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
456fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkAutoTMalloc<uint8_t> padPixels(padSize);
457fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        memset(padPixels, 0, padSize);
458fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
459fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int y = 0 ; y < srcHeight; ++y) {
460fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            uint8_t* padptr = padPixels + y * padWidth + 2*pad;
461fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const uint8_t* srcptr = srcPixels + y * srcWidth;
462fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            memcpy(padptr, srcptr, srcWidth);
463fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
464fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
465fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // blur in X, transposing the result into a temporary floating point buffer.
466fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // also double-pad the intermediate result so that the second blur doesn't
467fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // have to do extra conditionals.
468fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
469fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int tmpWidth = padHeight + 4*pad;
470fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int tmpHeight = padWidth - 2*pad;
471fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int tmpSize = tmpWidth * tmpHeight;
472fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
473fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkAutoTMalloc<float> tmpImage(tmpSize);
474fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        memset(tmpImage, 0, tmpSize*sizeof(tmpImage[0]));
475fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
476fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int y = 0 ; y < padHeight ; ++y) {
477fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            uint8_t *srcScanline = padPixels + y*padWidth;
478fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int x = pad ; x < padWidth - pad ; ++x) {
479fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                float *outPixel = tmpImage + (x-pad)*tmpWidth + y + 2*pad; // transposed output
480fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                uint8_t *windowCenter = srcScanline + x;
481fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                for (int i = -pad ; i <= pad ; ++i) {
482fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    *outPixel += gaussWindow[pad+i]*windowCenter[i];
483fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
484fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                *outPixel /= windowSum;
485fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
486fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
487fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
488fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // blur in Y; now filling in the actual desired destination.  We have to do
489fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // the transpose again; these transposes guarantee that we read memory in
490fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // linear order.
491fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
492fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int y = 0 ; y < tmpHeight ; ++y) {
493fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            float *srcScanline = tmpImage + y*tmpWidth;
494fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int x = pad ; x < tmpWidth - pad ; ++x) {
495fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                float *windowCenter = srcScanline + x;
496fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                float finalValue = 0;
497fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                for (int i = -pad ; i <= pad ; ++i) {
498fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    finalValue += gaussWindow[pad+i]*windowCenter[i];
499fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
500fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                finalValue /= windowSum;
501fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                uint8_t *outPixel = dstPixels + (x-pad)*dstWidth + y; // transposed output
502fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                int integerPixel = int(finalValue + 0.5f);
503fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                *outPixel = SkClampMax( SkClampPos(integerPixel), 255 );
504fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
505fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
506fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
507fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst->fImage = dstPixels;
508fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // if need be, alloc the "real" dst (same size as src) and copy/merge
509fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // the blur into it (applying the src)
510fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (style == kInner_SkBlurStyle) {
511fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // now we allocate the "real" dst, mirror the size of src
512fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            size_t srcSize = src.computeImageSize();
513fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (0 == srcSize) {
514fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return false;   // too big to allocate, abort
515fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
516fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            dst->fImage = SkMask::AllocImage(srcSize);
517fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            merge_src_with_blur(dst->fImage, src.fRowBytes,
518fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                srcPixels, src.fRowBytes,
519fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                dstPixels + pad*dst->fRowBytes + pad,
520fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                dst->fRowBytes, srcWidth, srcHeight);
521fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkMask::FreeImage(dstPixels);
522fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else if (style != kNormal_SkBlurStyle) {
523fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            clamp_with_orig(dstPixels + pad*dst->fRowBytes + pad,
524fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                dst->fRowBytes, srcPixels, src.fRowBytes, srcWidth, srcHeight, style);
525fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
526fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)autoCall.release();
527fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
528fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
529fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (style == kInner_SkBlurStyle) {
530fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst->fBounds = src.fBounds; // restore trimmed bounds
531fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dst->fRowBytes = src.fRowBytes;
532fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
533fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
534fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
535fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
536