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