2011-07-28 14:26:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright 2006 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
#include "SkBlurMask.h"
|
|
|
|
#include "SkTemplates.h"
|
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
/** The sum buffer is an array of u32 to hold the accumulated sum of all of the
|
|
|
|
src values at their position, plus all values above and to the left.
|
|
|
|
When we sample into this buffer, we need an initial row and column of 0s,
|
|
|
|
so we have an index correspondence as follows:
|
|
|
|
|
|
|
|
src[i, j] == sum[i+1, j+1]
|
|
|
|
sum[0, j] == sum[i, 0] == 0
|
|
|
|
|
|
|
|
We assume that the sum buffer's stride == its width
|
|
|
|
*/
|
2011-08-12 14:59:59 +00:00
|
|
|
static void build_sum_buffer(uint32_t sum[], int srcW, int srcH,
|
|
|
|
const uint8_t src[], int srcRB) {
|
2009-09-21 00:27:08 +00:00
|
|
|
int sumW = srcW + 1;
|
|
|
|
|
|
|
|
SkASSERT(srcRB >= srcW);
|
2008-12-17 15:59:43 +00:00
|
|
|
// mod srcRB so we can apply it after each row
|
2009-09-21 00:27:08 +00:00
|
|
|
srcRB -= srcW;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
int x, y;
|
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
// zero out the top row and column
|
|
|
|
memset(sum, 0, sumW * sizeof(sum[0]));
|
|
|
|
sum += sumW;
|
|
|
|
|
2008-12-17 15:59:43 +00:00
|
|
|
// special case first row
|
|
|
|
uint32_t X = 0;
|
2009-09-21 00:27:08 +00:00
|
|
|
*sum++ = 0; // initialze the first column to 0
|
2011-08-12 14:59:59 +00:00
|
|
|
for (x = srcW - 1; x >= 0; --x) {
|
2008-12-17 15:59:43 +00:00
|
|
|
X = *src++ + X;
|
2009-09-21 00:27:08 +00:00
|
|
|
*sum++ = X;
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
src += srcRB;
|
|
|
|
|
|
|
|
// now do the rest of the rows
|
2011-08-12 14:59:59 +00:00
|
|
|
for (y = srcH - 1; y > 0; --y) {
|
2008-12-17 15:59:43 +00:00
|
|
|
uint32_t L = 0;
|
|
|
|
uint32_t C = 0;
|
2009-09-21 00:27:08 +00:00
|
|
|
*sum++ = 0; // initialze the first column to 0
|
2011-08-12 14:59:59 +00:00
|
|
|
for (x = srcW - 1; x >= 0; --x) {
|
2009-09-21 00:27:08 +00:00
|
|
|
uint32_t T = sum[-sumW];
|
2008-12-17 15:59:43 +00:00
|
|
|
X = *src++ + L + T - C;
|
2009-09-21 00:27:08 +00:00
|
|
|
*sum++ = X;
|
2008-12-17 15:59:43 +00:00
|
|
|
L = X;
|
|
|
|
C = T;
|
|
|
|
}
|
|
|
|
src += srcRB;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-12 14:59:59 +00:00
|
|
|
/**
|
|
|
|
* sw and sh are the width and height of the src. Since the sum buffer
|
|
|
|
* matches that, but has an extra row and col at the beginning (with zeros),
|
|
|
|
* we can just use sw and sh as our "max" values for pinning coordinates
|
|
|
|
* when sampling into sum[][]
|
2009-09-21 00:27:08 +00:00
|
|
|
*/
|
|
|
|
static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
|
|
|
|
int sw, int sh) {
|
2008-12-17 15:59:43 +00:00
|
|
|
uint32_t scale = (1 << 24) / ((2*rx + 1)*(2*ry + 1));
|
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
int sumStride = sw + 1;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
int dw = sw + 2*rx;
|
|
|
|
int dh = sh + 2*ry;
|
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
int prev_y = -2*ry;
|
|
|
|
int next_y = 1;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
for (int y = 0; y < dh; y++) {
|
|
|
|
int py = SkClampPos(prev_y) * sumStride;
|
|
|
|
int ny = SkFastMin32(next_y, sh) * sumStride;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
int prev_x = -2*rx;
|
|
|
|
int next_x = 1;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
for (int x = 0; x < dw; x++) {
|
2008-12-17 15:59:43 +00:00
|
|
|
int px = SkClampPos(prev_x);
|
|
|
|
int nx = SkFastMin32(next_x, sw);
|
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
uint32_t tmp = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
|
|
|
|
*dst++ = SkToU8(tmp * scale >> 24);
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
prev_x += 1;
|
|
|
|
next_x += 1;
|
|
|
|
}
|
|
|
|
prev_y += 1;
|
|
|
|
next_y += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-12 14:59:59 +00:00
|
|
|
/**
|
|
|
|
* sw and sh are the width and height of the src. Since the sum buffer
|
|
|
|
* matches that, but has an extra row and col at the beginning (with zeros),
|
|
|
|
* we can just use sw and sh as our "max" values for pinning coordinates
|
|
|
|
* when sampling into sum[][]
|
2009-09-21 00:27:08 +00:00
|
|
|
*/
|
|
|
|
static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
|
|
|
|
const uint32_t sum[], int sw, int sh, U8CPU outer_weight) {
|
2008-12-17 15:59:43 +00:00
|
|
|
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));
|
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
int sumStride = sw + 1;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
int dw = sw + 2*rx;
|
|
|
|
int dh = sh + 2*ry;
|
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
int prev_y = -2*ry;
|
|
|
|
int next_y = 1;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
for (int y = 0; y < dh; y++) {
|
|
|
|
int py = SkClampPos(prev_y) * sumStride;
|
|
|
|
int ny = SkFastMin32(next_y, sh) * sumStride;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
int ipy = SkClampPos(prev_y + 1) * sumStride;
|
|
|
|
int iny = SkClampMax(next_y - 1, sh) * sumStride;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
int prev_x = -2*rx;
|
|
|
|
int next_x = 1;
|
2008-12-17 15:59:43 +00:00
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
for (int x = 0; x < dw; x++) {
|
2008-12-17 15:59:43 +00:00
|
|
|
int px = SkClampPos(prev_x);
|
|
|
|
int nx = SkFastMin32(next_x, sw);
|
|
|
|
|
|
|
|
int ipx = SkClampPos(prev_x + 1);
|
|
|
|
int inx = SkClampMax(next_x - 1, sw);
|
|
|
|
|
2009-09-21 00:27:08 +00:00
|
|
|
uint32_t outer_sum = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
|
|
|
|
uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny] - sum[inx+ipy] - sum[ipx+iny];
|
2008-12-17 15:59:43 +00:00
|
|
|
*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"
|
|
|
|
|
2009-09-18 13:41:56 +00:00
|
|
|
static void merge_src_with_blur(uint8_t dst[], int dstRB,
|
|
|
|
const uint8_t src[], int srcRB,
|
|
|
|
const uint8_t blur[], int blurRB,
|
|
|
|
int sw, int sh) {
|
|
|
|
dstRB -= sw;
|
|
|
|
srcRB -= sw;
|
|
|
|
blurRB -= sw;
|
|
|
|
while (--sh >= 0) {
|
|
|
|
for (int x = sw - 1; x >= 0; --x) {
|
2008-12-17 15:59:43 +00:00
|
|
|
*dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
|
|
|
|
dst += 1;
|
|
|
|
src += 1;
|
|
|
|
blur += 1;
|
|
|
|
}
|
2009-09-18 13:41:56 +00:00
|
|
|
dst += dstRB;
|
|
|
|
src += srcRB;
|
|
|
|
blur += blurRB;
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
|
2009-09-18 13:41:56 +00:00
|
|
|
const uint8_t src[], int srcRowBytes,
|
|
|
|
int sw, int sh,
|
2009-09-21 00:27:08 +00:00
|
|
|
SkBlurMask::Style style) {
|
2008-12-17 15:59:43 +00:00
|
|
|
int x;
|
2009-09-18 13:41:56 +00:00
|
|
|
while (--sh >= 0) {
|
2008-12-17 15:59:43 +00:00
|
|
|
switch (style) {
|
|
|
|
case SkBlurMask::kSolid_Style:
|
2009-09-18 13:41:56 +00:00
|
|
|
for (x = sw - 1; x >= 0; --x) {
|
|
|
|
int s = *src;
|
|
|
|
int d = *dst;
|
|
|
|
*dst = SkToU8(s + d - SkMulDiv255Round(s, d));
|
2008-12-17 15:59:43 +00:00
|
|
|
dst += 1;
|
|
|
|
src += 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SkBlurMask::kOuter_Style:
|
2009-09-18 13:41:56 +00:00
|
|
|
for (x = sw - 1; x >= 0; --x) {
|
|
|
|
if (*src) {
|
2008-12-17 15:59:43 +00:00
|
|
|
*dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
|
2009-09-18 13:41:56 +00:00
|
|
|
}
|
2008-12-17 15:59:43 +00:00
|
|
|
dst += 1;
|
|
|
|
src += 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SkASSERT(!"Unexpected blur style here");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dst += dstRowBytes - sw;
|
2009-09-18 13:41:56 +00:00
|
|
|
src += srcRowBytes - sw;
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-12 14:59:59 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
// we use a local funciton to wrap the class static method to work around
|
|
|
|
// a bug in gcc98
|
|
|
|
void SkMask_FreeImage(uint8_t* image);
|
2011-08-12 14:59:59 +00:00
|
|
|
void SkMask_FreeImage(uint8_t* image) {
|
2008-12-17 15:59:43 +00:00
|
|
|
SkMask::FreeImage(image);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
2011-09-02 15:06:44 +00:00
|
|
|
SkScalar radius, Style style, Quality quality,
|
|
|
|
SkIPoint* margin)
|
|
|
|
{
|
2011-08-12 14:59:59 +00:00
|
|
|
if (src.fFormat != SkMask::kA8_Format) {
|
2008-12-17 15:59:43 +00:00
|
|
|
return false;
|
2011-08-12 14:59:59 +00:00
|
|
|
}
|
2008-12-17 15:59:43 +00:00
|
|
|
|
2011-02-18 19:03:01 +00:00
|
|
|
// Force high quality off for small radii (performance)
|
|
|
|
if (radius < SkIntToScalar(3)) quality = kLow_Quality;
|
|
|
|
|
|
|
|
// highQuality: use three box blur passes as a cheap way to approximate a Gaussian blur
|
|
|
|
int passCount = (quality == kHigh_Quality) ? 3 : 1;
|
|
|
|
SkScalar passRadius = SkScalarDiv(radius, SkScalarSqrt(SkIntToScalar(passCount)));
|
|
|
|
|
|
|
|
int rx = SkScalarCeil(passRadius);
|
|
|
|
int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
SkASSERT(rx >= 0);
|
|
|
|
SkASSERT((unsigned)outer_weight <= 255);
|
2009-09-18 13:41:56 +00:00
|
|
|
if (rx <= 0) {
|
2008-12-17 15:59:43 +00:00
|
|
|
return false;
|
2009-09-18 13:41:56 +00:00
|
|
|
}
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
int ry = rx; // only do square blur for now
|
|
|
|
|
2011-02-18 19:03:01 +00:00
|
|
|
int padx = passCount * rx;
|
|
|
|
int pady = passCount * ry;
|
2011-09-02 15:06:44 +00:00
|
|
|
if (margin) {
|
|
|
|
margin->set(padx, pady);
|
|
|
|
}
|
2011-02-18 19:03:01 +00:00
|
|
|
dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady,
|
|
|
|
src.fBounds.fRight + padx, src.fBounds.fBottom + pady);
|
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;
|
|
|
|
|
2009-09-18 13:41:56 +00:00
|
|
|
if (src.fImage) {
|
2009-04-24 12:43:40 +00:00
|
|
|
size_t dstSize = dst->computeImageSize();
|
|
|
|
if (0 == dstSize) {
|
|
|
|
return false; // too big to allocate, abort
|
|
|
|
}
|
|
|
|
|
2008-12-17 15:59:43 +00:00
|
|
|
int sw = src.fBounds.width();
|
|
|
|
int sh = src.fBounds.height();
|
|
|
|
const uint8_t* sp = src.fImage;
|
2009-04-24 12:43:40 +00:00
|
|
|
uint8_t* dp = SkMask::AllocImage(dstSize);
|
2008-12-17 15:59:43 +00:00
|
|
|
|
|
|
|
SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
|
|
|
|
|
|
|
|
// build the blurry destination
|
|
|
|
{
|
2011-08-12 14:59:59 +00:00
|
|
|
const size_t storageW = sw + 2 * (passCount - 1) * rx + 1;
|
|
|
|
const size_t storageH = sh + 2 * (passCount - 1) * ry + 1;
|
|
|
|
SkAutoTMalloc<uint32_t> storage(storageW * storageH);
|
2008-12-17 15:59:43 +00:00
|
|
|
uint32_t* sumBuffer = storage.get();
|
|
|
|
|
2011-02-18 19:03:01 +00:00
|
|
|
//pass1: sp is source, dp is destination
|
2008-12-17 15:59:43 +00:00
|
|
|
build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
|
2011-08-12 14:59:59 +00:00
|
|
|
if (outer_weight == 255) {
|
2008-12-17 15:59:43 +00:00
|
|
|
apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
|
2011-08-12 14:59:59 +00:00
|
|
|
} else {
|
2008-12-17 15:59:43 +00:00
|
|
|
apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
|
2011-08-12 14:59:59 +00:00
|
|
|
}
|
2011-02-18 19:03:01 +00:00
|
|
|
|
2011-08-12 14:59:59 +00:00
|
|
|
if (quality == kHigh_Quality) {
|
2011-02-18 19:03:01 +00:00
|
|
|
//pass2: dp is source, tmpBuffer is destination
|
|
|
|
int tmp_sw = sw + 2 * rx;
|
|
|
|
int tmp_sh = sh + 2 * ry;
|
|
|
|
SkAutoTMalloc<uint8_t> tmpBuffer(dstSize);
|
|
|
|
build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, dp, tmp_sw);
|
|
|
|
if (outer_weight == 255)
|
|
|
|
apply_kernel(tmpBuffer.get(), rx, ry, sumBuffer, tmp_sw, tmp_sh);
|
|
|
|
else
|
2011-08-12 14:59:59 +00:00
|
|
|
apply_kernel_interp(tmpBuffer.get(), rx, ry, sumBuffer,
|
|
|
|
tmp_sw, tmp_sh, outer_weight);
|
2011-02-18 19:03:01 +00:00
|
|
|
|
|
|
|
//pass3: tmpBuffer is source, dp is destination
|
|
|
|
tmp_sw += 2 * rx;
|
|
|
|
tmp_sh += 2 * ry;
|
|
|
|
build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, tmpBuffer.get(), tmp_sw);
|
|
|
|
if (outer_weight == 255)
|
|
|
|
apply_kernel(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh);
|
|
|
|
else
|
2011-08-12 14:59:59 +00:00
|
|
|
apply_kernel_interp(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh,
|
|
|
|
outer_weight);
|
2011-02-18 19:03:01 +00:00
|
|
|
}
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dst->fImage = dp;
|
|
|
|
// if need be, alloc the "real" dst (same size as src) and copy/merge
|
|
|
|
// the blur into it (applying the src)
|
2009-09-18 13:41:56 +00:00
|
|
|
if (style == kInner_Style) {
|
|
|
|
// now we allocate the "real" dst, mirror the size of src
|
2009-04-24 12:43:40 +00:00
|
|
|
size_t srcSize = src.computeImageSize();
|
|
|
|
if (0 == srcSize) {
|
|
|
|
return false; // too big to allocate, abort
|
|
|
|
}
|
|
|
|
dst->fImage = SkMask::AllocImage(srcSize);
|
2009-09-18 13:41:56 +00:00
|
|
|
merge_src_with_blur(dst->fImage, src.fRowBytes,
|
|
|
|
sp, src.fRowBytes,
|
2011-08-12 14:59:59 +00:00
|
|
|
dp + passCount * (rx + ry * dst->fRowBytes),
|
|
|
|
dst->fRowBytes, sw, sh);
|
2008-12-17 15:59:43 +00:00
|
|
|
SkMask::FreeImage(dp);
|
2009-09-18 13:41:56 +00:00
|
|
|
} else if (style != kNormal_Style) {
|
2011-08-12 14:59:59 +00:00
|
|
|
clamp_with_orig(dp + passCount * (rx + ry * dst->fRowBytes),
|
|
|
|
dst->fRowBytes, sp, src.fRowBytes, sw, sh, style);
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
(void)autoCall.detach();
|
|
|
|
}
|
|
|
|
|
2009-09-18 13:41:56 +00:00
|
|
|
if (style == kInner_Style) {
|
2008-12-17 15:59:43 +00:00
|
|
|
dst->fBounds = src.fBounds; // restore trimmed bounds
|
2009-09-18 13:41:56 +00:00
|
|
|
dst->fRowBytes = src.fRowBytes;
|
2008-12-17 15:59:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|