2008-12-17 15:59:43 +00:00
|
|
|
/* libs/graphics/effects/SkBlurMask.cpp
|
|
|
|
**
|
|
|
|
** Copyright 2006, The Android Open Source Project
|
|
|
|
**
|
|
|
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
** you may not use this file except in compliance with the License.
|
|
|
|
** You may obtain a copy of the License at
|
|
|
|
**
|
|
|
|
** http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
**
|
|
|
|
** Unless required by applicable law or agreed to in writing, software
|
|
|
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
** See the License for the specific language governing permissions and
|
|
|
|
** limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "SkBlurMask.h"
|
|
|
|
#include "SkTemplates.h"
|
|
|
|
|
|
|
|
static void build_sum_buffer(uint32_t dst[], int w, int h, const uint8_t src[], int srcRB)
|
|
|
|
{
|
|
|
|
SkASSERT(srcRB >= w);
|
|
|
|
// mod srcRB so we can apply it after each row
|
|
|
|
srcRB -= w;
|
|
|
|
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
// special case first row
|
|
|
|
uint32_t X = 0;
|
|
|
|
for (x = w - 1; x >= 0; --x)
|
|
|
|
{
|
|
|
|
X = *src++ + X;
|
|
|
|
*dst++ = X;
|
|
|
|
}
|
|
|
|
src += srcRB;
|
|
|
|
|
|
|
|
// now do the rest of the rows
|
|
|
|
for (y = h - 1; y > 0; --y)
|
|
|
|
{
|
|
|
|
uint32_t L = 0;
|
|
|
|
uint32_t C = 0;
|
|
|
|
for (x = w - 1; x >= 0; --x)
|
|
|
|
{
|
|
|
|
uint32_t T = dst[-w];
|
|
|
|
X = *src++ + L + T - C;
|
|
|
|
*dst++ = X;
|
|
|
|
L = X;
|
|
|
|
C = T;
|
|
|
|
}
|
|
|
|
src += srcRB;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh)
|
|
|
|
{
|
|
|
|
uint32_t scale = (1 << 24) / ((2*rx + 1)*(2*ry + 1));
|
|
|
|
|
|
|
|
int rowBytes = sw;
|
|
|
|
|
|
|
|
int dw = sw + 2*rx;
|
|
|
|
int dh = sh + 2*ry;
|
|
|
|
|
|
|
|
sw -= 1; // now it is max_x
|
|
|
|
sh -= 1; // now it is max_y
|
|
|
|
|
|
|
|
int prev_y = -ry - 1 -ry;
|
|
|
|
int next_y = ry -ry;
|
|
|
|
|
|
|
|
for (int y = 0; y < dh; y++)
|
|
|
|
{
|
|
|
|
int py = SkClampPos(prev_y) * rowBytes;
|
|
|
|
int ny = SkFastMin32(next_y, sh) * rowBytes;
|
|
|
|
|
|
|
|
int prev_x = -rx - 1 -rx;
|
|
|
|
int next_x = rx -rx;
|
|
|
|
|
|
|
|
for (int x = 0; x < dw; x++)
|
|
|
|
{
|
|
|
|
int px = SkClampPos(prev_x);
|
|
|
|
int nx = SkFastMin32(next_x, sw);
|
|
|
|
|
|
|
|
uint32_t sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny];
|
|
|
|
*dst++ = SkToU8(sum * scale >> 24);
|
|
|
|
|
|
|
|
prev_x += 1;
|
|
|
|
next_x += 1;
|
|
|
|
}
|
|
|
|
prev_y += 1;
|
|
|
|
next_y += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void apply_kernel_interp(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh, U8CPU outer_weight)
|
|
|
|
{
|
|
|
|
SkASSERT(rx > 0 && ry > 0);
|
|
|
|
SkASSERT(outer_weight <= 255);
|
|
|
|
|
|
|
|
int inner_weight = 255 - outer_weight;
|
|
|
|
|
|
|
|
// round these guys up if they're bigger than 127
|
|
|
|
outer_weight += outer_weight >> 7;
|
|
|
|
inner_weight += inner_weight >> 7;
|
|
|
|
|
|
|
|
uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
|
|
|
|
uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));
|
|
|
|
|
|
|
|
int rowBytes = sw;
|
|
|
|
|
|
|
|
int dw = sw + 2*rx;
|
|
|
|
int dh = sh + 2*ry;
|
|
|
|
|
|
|
|
sw -= 1; // now it is max_x
|
|
|
|
sh -= 1; // now it is max_y
|
|
|
|
|
|
|
|
int prev_y = -ry - 1 -ry;
|
|
|
|
int next_y = ry -ry;
|
|
|
|
|
|
|
|
for (int y = 0; y < dh; y++)
|
|
|
|
{
|
|
|
|
int py = SkClampPos(prev_y) * rowBytes;
|
|
|
|
int ny = SkFastMin32(next_y, sh) * rowBytes;
|
|
|
|
|
|
|
|
int ipy = SkClampPos(prev_y + 1) * rowBytes;
|
|
|
|
int iny = SkClampMax(next_y - 1, sh) * rowBytes;
|
|
|
|
|
|
|
|
int prev_x = -rx - 1 -rx;
|
|
|
|
int next_x = rx -rx;
|
|
|
|
|
|
|
|
for (int x = 0; x < dw; x++)
|
|
|
|
{
|
|
|
|
int px = SkClampPos(prev_x);
|
|
|
|
int nx = SkFastMin32(next_x, sw);
|
|
|
|
|
|
|
|
int ipx = SkClampPos(prev_x + 1);
|
|
|
|
int inx = SkClampMax(next_x - 1, sw);
|
|
|
|
|
|
|
|
uint32_t outer_sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny];
|
|
|
|
uint32_t inner_sum = src[ipx+ipy] + src[inx+iny] - src[inx+ipy] - src[ipx+iny];
|
|
|
|
*dst++ = SkToU8((outer_sum * outer_scale + inner_sum * inner_scale) >> 24);
|
|
|
|
|
|
|
|
prev_x += 1;
|
|
|
|
next_x += 1;
|
|
|
|
}
|
|
|
|
prev_y += 1;
|
|
|
|
next_y += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "SkColorPriv.h"
|
|
|
|
|
|
|
|
static void merge_src_with_blur(uint8_t dst[],
|
|
|
|
const uint8_t src[], int sw, int sh,
|
|
|
|
const uint8_t blur[], int blurRowBytes)
|
|
|
|
{
|
|
|
|
while (--sh >= 0)
|
|
|
|
{
|
|
|
|
for (int x = sw - 1; x >= 0; --x)
|
|
|
|
{
|
|
|
|
*dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
|
|
|
|
dst += 1;
|
|
|
|
src += 1;
|
|
|
|
blur += 1;
|
|
|
|
}
|
|
|
|
blur += blurRowBytes - sw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
|
|
|
|
const uint8_t src[], int sw, int sh,
|
|
|
|
SkBlurMask::Style style)
|
|
|
|
{
|
|
|
|
int x;
|
|
|
|
while (--sh >= 0)
|
|
|
|
{
|
|
|
|
switch (style) {
|
|
|
|
case SkBlurMask::kSolid_Style:
|
|
|
|
for (x = sw - 1; x >= 0; --x)
|
|
|
|
{
|
|
|
|
*dst = SkToU8(*src + SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
|
|
|
|
dst += 1;
|
|
|
|
src += 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SkBlurMask::kOuter_Style:
|
|
|
|
for (x = sw - 1; x >= 0; --x)
|
|
|
|
{
|
|
|
|
if (*src)
|
|
|
|
*dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
|
|
|
|
dst += 1;
|
|
|
|
src += 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SkASSERT(!"Unexpected blur style here");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dst += dstRowBytes - sw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// we use a local funciton to wrap the class static method to work around
|
|
|
|
// a bug in gcc98
|
|
|
|
void SkMask_FreeImage(uint8_t* image);
|
|
|
|
void SkMask_FreeImage(uint8_t* image)
|
|
|
|
{
|
|
|
|
SkMask::FreeImage(image);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
|
|
|
SkScalar radius, Style style)
|
|
|
|
{
|
|
|
|
if (src.fFormat != SkMask::kA8_Format)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int rx = SkScalarCeil(radius);
|
|
|
|
int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - radius) * 255);
|
|
|
|
|
|
|
|
SkASSERT(rx >= 0);
|
|
|
|
SkASSERT((unsigned)outer_weight <= 255);
|
|
|
|
|
|
|
|
if (rx == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int ry = rx; // only do square blur for now
|
|
|
|
|
|
|
|
dst->fBounds.set(src.fBounds.fLeft - rx, src.fBounds.fTop - ry,
|
|
|
|
src.fBounds.fRight + rx, src.fBounds.fBottom + ry);
|
2009-03-19 21:52:42 +00:00
|
|
|
dst->fRowBytes = dst->fBounds.width();
|
2008-12-17 15:59:43 +00:00
|
|
|
dst->fFormat = SkMask::kA8_Format;
|
|
|
|
dst->fImage = NULL;
|
|
|
|
|
|
|
|
if (src.fImage)
|
|
|
|
{
|
|
|
|
int sw = src.fBounds.width();
|
|
|
|
int sh = src.fBounds.height();
|
|
|
|
const uint8_t* sp = src.fImage;
|
|
|
|
uint8_t* dp = SkMask::AllocImage(dst->computeImageSize());
|
|
|
|
|
|
|
|
SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
|
|
|
|
|
|
|
|
// build the blurry destination
|
|
|
|
{
|
|
|
|
SkAutoTMalloc<uint32_t> storage(sw * sh);
|
|
|
|
uint32_t* sumBuffer = storage.get();
|
|
|
|
|
|
|
|
build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
|
|
|
|
if (outer_weight == 255)
|
|
|
|
apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
|
|
|
|
else
|
|
|
|
apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
|
|
|
|
}
|
|
|
|
|
|
|
|
dst->fImage = dp;
|
|
|
|
// if need be, alloc the "real" dst (same size as src) and copy/merge
|
|
|
|
// the blur into it (applying the src)
|
|
|
|
if (style == kInner_Style)
|
|
|
|
{
|
|
|
|
dst->fImage = SkMask::AllocImage(src.computeImageSize());
|
|
|
|
merge_src_with_blur(dst->fImage, sp, sw, sh,
|
|
|
|
dp + rx + ry*dst->fBounds.width(),
|
|
|
|
dst->fBounds.width());
|
|
|
|
SkMask::FreeImage(dp);
|
|
|
|
}
|
|
|
|
else if (style != kNormal_Style)
|
|
|
|
{
|
|
|
|
clamp_with_orig(dp + rx + ry*dst->fBounds.width(),
|
|
|
|
dst->fBounds.width(),
|
|
|
|
sp, sw, sh,
|
|
|
|
style);
|
|
|
|
}
|
|
|
|
(void)autoCall.detach();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (style == kInner_Style)
|
|
|
|
{
|
|
|
|
dst->fBounds = src.fBounds; // restore trimmed bounds
|
2009-03-19 21:52:42 +00:00
|
|
|
dst->fRowBytes = dst->fBounds.width();
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (gamma && dst->fImage)
|
|
|
|
{
|
|
|
|
uint8_t* image = dst->fImage;
|
|
|
|
uint8_t* stop = image + dst->computeImageSize();
|
|
|
|
|
|
|
|
for (; image < stop; image += 1)
|
|
|
|
*image = gamma[*image];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
void SkBlurMask::BuildSqrtGamma(uint8_t gamma[256], SkScalar percent)
|
|
|
|
{
|
|
|
|
SkASSERT(gamma);
|
|
|
|
SkASSERT(percent >= 0 && percent <= SK_Scalar1);
|
|
|
|
|
|
|
|
int scale = SkScalarRound(percent * 256);
|
|
|
|
|
|
|
|
for (int i = 0; i < 256; i++)
|
|
|
|
{
|
|
|
|
SkFixed n = i * 257;
|
|
|
|
n += n >> 15;
|
|
|
|
SkASSERT(n >= 0 && n <= SK_Fixed1);
|
|
|
|
n = SkFixedSqrt(n);
|
|
|
|
n = n * 255 >> 16;
|
|
|
|
n = SkAlphaBlend(n, i, scale);
|
|
|
|
gamma[i] = SkToU8(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SkBlurMask::BuildSqrGamma(uint8_t gamma[256], SkScalar percent)
|
|
|
|
{
|
|
|
|
SkASSERT(gamma);
|
|
|
|
SkASSERT(percent >= 0 && percent <= SK_Scalar1);
|
|
|
|
|
|
|
|
int scale = SkScalarRound(percent * 256);
|
|
|
|
SkFixed div255 = SK_Fixed1 / 255;
|
|
|
|
|
|
|
|
for (int i = 0; i < 256; i++)
|
|
|
|
{
|
|
|
|
int square = i * i;
|
|
|
|
int linear = i * 255;
|
|
|
|
int n = SkAlphaBlend(square, linear, scale);
|
|
|
|
gamma[i] = SkToU8(n * div255 >> 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|