New 'blitAntiRect' entry point for blitters, specialized in supersampling

code to avoid quarter-scanline-at-a-time building of large rectangular
clips.

Speeds up aa_clip_build_path_AA benchmark 2x, aa_clip_build_rect_AA benchmark
10x or more. This is a sufficient performance gain to let Chromium reenable
WebKit's soft clipping code. Rolling into Chromium will require ~18
rebaselines.



git-svn-id: http://skia.googlecode.com/svn/trunk@2924 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
tomhudson@google.com 2011-12-27 13:59:20 +00:00
parent b0af6dad94
commit 49eac192fa
4 changed files with 183 additions and 36 deletions

View File

@ -34,6 +34,12 @@ public:
virtual void blitV(int x, int y, int height, SkAlpha alpha);
/// Blit a solid rectangle.
virtual void blitRect(int x, int y, int width, int height);
/** Blit a rectangle with one alpha-blended column on the left,
width opaque pixels, and one alpha-blended column on the right.
Note that the result will always be at least two pixels wide.
*/
virtual void blitAntiRect(int x, int y, int width, int height,
SkAlpha leftAlpha, SkAlpha rightAlpha);
/// Blit a pattern of pixels defined by a rectangle-clipped mask;
/// typically used for text.
virtual void blitMask(const SkMask&, const SkIRect& clip);
@ -104,6 +110,8 @@ public:
const int16_t runs[]) SK_OVERRIDE;
virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
virtual void blitAntiRect(int x, int y, int width, int height,
SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
@ -129,6 +137,8 @@ public:
const int16_t runs[]) SK_OVERRIDE;
virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
virtual void blitAntiRect(int x, int y, int width, int height,
SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;

View File

@ -926,6 +926,16 @@ public:
SkASSERT(row->fWidth <= fBounds.width());
}
void addColumn(int x, int y, U8CPU alpha, int height) {
SkASSERT(fBounds.contains(x, y + height - 1));
this->addRun(x, y, alpha, 1);
this->flushRowH(fCurrRow);
y -= fBounds.fTop;
SkASSERT(y == fCurrRow->fY);
fCurrRow->fY = y + height - 1;
}
void addRectRun(int x, int y, int width, int height) {
SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
this->addRun(x, y, 0xFF, width);
@ -939,6 +949,39 @@ public:
fCurrRow->fY = y + height - 1;
}
void addAntiRectRun(int x, int y, int width, int height,
SkAlpha leftAlpha, SkAlpha rightAlpha) {
SkASSERT(fBounds.contains(x + width - 1 +
(leftAlpha > 0 ? 1 : 0) + (rightAlpha > 0 ? 1 : 0),
y + height - 1));
SkASSERT(width >= 0);
// Conceptually we're always adding 3 runs, but we should
// merge or omit them if possible.
if (leftAlpha == 0xFF) {
width++;
} else if (leftAlpha > 0) {
this->addRun(x++, y, leftAlpha, 1);
}
if (rightAlpha == 0xFF) {
width++;
}
if (width > 0) {
this->addRun(x, y, 0xFF, width);
}
if (rightAlpha > 0 && rightAlpha < 255) {
this->addRun(x + width, y, rightAlpha, 1);
}
// we assume the rect must be all we'll see for these scanlines
// so we ensure our row goes all the way to our right
this->flushRowH(fCurrRow);
y -= fBounds.fTop;
SkASSERT(y == fCurrRow->fY);
fCurrRow->fY = y + height - 1;
}
bool finish(SkAAClip* target) {
this->flushRow(false);
@ -1113,14 +1156,29 @@ public:
}
}
virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE
{ unexpected(); }
/**
Must evaluate clips in scan-line order, so don't want to allow blitV(),
but an AAClip can be clipped down to a single pixel wide, so we
must support it (given AntiRect semantics: minimum width is 2).
Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
any failure cases that misses may have minor artifacts.
*/
virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
this->recordMinY(y);
fBuilder->addColumn(x, y, alpha, height);
}
virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
this->recordMinY(y);
fBuilder->addRectRun(x, y, width, height);
}
virtual void blitAntiRect(int x, int y, int width, int height,
SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE {
this->recordMinY(y);
fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
}
virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE
{ unexpected(); }

View File

@ -52,6 +52,16 @@ void SkBlitter::blitRect(int x, int y, int width, int height) {
}
}
/// Default implementation doesn't check for any easy optimizations
/// such as alpha == 0 or 255; also uses blitV(), which some subclasses
/// may not support.
void SkBlitter::blitAntiRect(int x, int y, int width, int height,
SkAlpha leftAlpha, SkAlpha rightAlpha) {
this->blitV(x, y, height, leftAlpha);
this->blitRect(x + 1, y, width, height);
this->blitV(x + width + 1, y, height, rightAlpha);
}
//////////////////////////////////////////////////////////////////////////////
static inline void bits_to_runs(SkBlitter* blitter, int x, int y,
@ -335,6 +345,37 @@ void SkRectClipBlitter::blitRect(int left, int y, int width, int height) {
}
}
void SkRectClipBlitter::blitAntiRect(int left, int y, int width, int height,
SkAlpha leftAlpha, SkAlpha rightAlpha) {
SkIRect r;
// The *true* width of the rectangle blitted is width+2:
r.set(left, y, left + width + 2, y + height);
if (r.intersect(fClipRect)) {
if (r.fLeft != left) {
SkASSERT(r.fLeft > left);
leftAlpha = 255;
}
if (r.fRight != left + width + 2) {
SkASSERT(r.fRight < left + width + 2);
rightAlpha = 255;
}
if (255 == leftAlpha && 255 == rightAlpha) {
fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
} else if (1 == r.width()) {
if (r.fLeft == left) {
fBlitter->blitV(r.fLeft, r.fTop, r.height(), leftAlpha);
} else {
SkASSERT(r.fLeft == left + width + 1);
fBlitter->blitV(r.fLeft, r.fTop, r.height(), rightAlpha);
}
} else {
fBlitter->blitAntiRect(r.fLeft, r.fTop, r.width() - 2, r.height(),
leftAlpha, rightAlpha);
}
}
}
void SkRectClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
SkASSERT(mask.fBounds.contains(clip));
@ -430,6 +471,44 @@ void SkRgnClipBlitter::blitRect(int x, int y, int width, int height) {
}
}
void SkRgnClipBlitter::blitAntiRect(int x, int y, int width, int height,
SkAlpha leftAlpha, SkAlpha rightAlpha) {
// The *true* width of the rectangle to blit is width + 2
SkIRect bounds;
bounds.set(x, y, x + width + 2, y + height);
SkRegion::Cliperator iter(*fRgn, bounds);
while (!iter.done()) {
const SkIRect& r = iter.rect();
SkASSERT(bounds.contains(r));
SkASSERT(r.fLeft >= x);
SkASSERT(r.fRight < x + width + 2);
SkAlpha effectiveLeftAlpha = (r.fLeft == x) ? leftAlpha : 255;
SkAlpha effectiveRightAlpha = (r.fRight == x + width + 2) ?
rightAlpha : 255;
if (255 == effectiveLeftAlpha && 255 == effectiveRightAlpha) {
fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
} else if (1 == r.width()) {
if (r.fLeft == x) {
fBlitter->blitV(r.fLeft, r.fTop, r.height(),
effectiveLeftAlpha);
} else {
SkASSERT(r.fLeft == x + width + 1);
fBlitter->blitV(r.fLeft, r.fTop, r.height(),
effectiveRightAlpha);
}
} else {
fBlitter->blitAntiRect(r.fLeft, r.fTop, r.width() - 2, r.height(),
effectiveLeftAlpha, effectiveRightAlpha);
}
iter.next();
}
}
void SkRgnClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
SkASSERT(mask.fBounds.contains(clip));

View File

@ -156,6 +156,12 @@ static inline int coverage_to_alpha(int aa) {
return aa;
}
static inline int coverage_to_exact_alpha(int aa) {
static int map [] = { 0, 64, 128, 192, 255 };
SkASSERT(SHIFT == 2);
return map[aa];
}
void SuperBlitter::blitH(int x, int y, int width) {
SkASSERT(width > 0);
@ -183,10 +189,6 @@ void SuperBlitter::blitH(int x, int y, int width) {
fCurrIY = iy;
}
// we sub 1 from maxValue 1 time for each block, so that we don't
// hit 256 as a summed max, but 255.
// int maxValue = (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT);
int start = x;
int stop = x + width;
@ -204,10 +206,11 @@ void SuperBlitter::blitH(int x, int y, int width) {
if (fb == 0) {
n += 1;
} else {
fb = (1 << SHIFT) - fb;
fb = SCALE - fb;
}
}
// TODO - should this be using coverage_to_exact_alpha?
fOffsetX = fRuns.add(x >> SHIFT, coverage_to_alpha(fb),
n, coverage_to_alpha(fe),
(1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT),
@ -288,24 +291,35 @@ void SuperBlitter::blitRect(int x, int y, int width, int height) {
x = 0;
}
// There is always a left column, a middle, and a right column.
// ileft is the destination x of the first pixel of the entire rect.
// xleft is (SCALE - # of covered supersampled pixels) in that
// destination pixel.
int ileft = x >> SHIFT;
int xleft = x & MASK;
// irite is the destination x of the last pixel of the OPAQUE section.
// xrite is the number of supersampled pixels extending beyond irite;
// xrite/SCALE should give us alpha.
int irite = (x + width) >> SHIFT;
int xrite = (x + width) & MASK;
if (!xrite) {
xrite = SCALE;
irite--;
}
int n = irite - ileft - 1;
if (n < 0) {
// only one pixel, call blitV()?
xleft = xrite - xleft;
n = 0;
SkASSERT(xleft <= SCALE);
SkASSERT(xleft > 0);
xrite = 0;
fRealBlitter->blitV(ileft + fLeft, start_y, count,
coverage_to_exact_alpha(xleft));
} else {
if (0 == xleft) {
n += 1;
} else {
xleft = (1 << SHIFT) - xleft;
}
xleft = SCALE - xleft;
}
// here we go
SkASSERT(start_y > fCurrIY);
this->flush();
@ -314,23 +328,13 @@ void SuperBlitter::blitRect(int x, int y, int width, int height) {
// values up. If we didn't care about that, we could be more precise
// and compute these exactly (e.g. 2->128 instead of 2->124)
//
const int coverageL = coverage_to_alpha(xleft) << SHIFT;
const int coverageR = coverage_to_alpha(xrite) << SHIFT;
const int coverageL = coverage_to_exact_alpha(xleft);
const int coverageR = coverage_to_exact_alpha(xrite);
SkASSERT(n + (coverageR != 0) <= fWidth);
for (int i = start_y; i < stop_y; ++i) {
// note: we should only need to call set_left_rite once, but
// our clipping blitters sometimes modify runs/alpha in-place,
// so for now we reset fRuns each time :(
//
// TODO:
// - don't modify in-place, or at least tell us when you're going to
// - pass height down to blitAntiH (blitAntiHV) so that aaclip and
// other can take advantage of the vertical-repeat explicitly
//
set_left_rite_runs(fRuns, ileft, coverageL, n, coverageR);
fRealBlitter->blitAntiH(fLeft, i, fRuns.fAlpha, fRuns.fRuns);
}
SkASSERT(coverageL > 0 || n > 0 || coverageR > 0);
fRealBlitter->blitAntiRect(ileft + fLeft, start_y, n, count,
coverageL, coverageR);
// preamble for our next call to blitH()
fCurrIY = stop_y - 1;
@ -340,7 +344,7 @@ void SuperBlitter::blitRect(int x, int y, int width, int height) {
x = origX;
}
// catch any remaining few
// catch any remaining few rows
SkASSERT(height <= MASK);
while (--height >= 0) {
this->blitH(x, y++, width);
@ -507,10 +511,6 @@ void MaskSuperBlitter::blitH(int x, int y, int width) {
x = 0;
}
// we sub 1 from maxValue 1 time for each block, so that we don't
// hit 256 as a summed max, but 255.
// int maxValue = (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT);
uint8_t* row = fMask.fImage + iy * fMask.fRowBytes + (x >> SHIFT);
int start = x;
@ -531,10 +531,10 @@ void MaskSuperBlitter::blitH(int x, int y, int width) {
if (0 == fb) {
n += 1;
} else {
fb = (1 << SHIFT) - fb;
fb = SCALE - fb;
}
#else
fb = (1 << SHIFT) - fb;
fb = SCALE - fb;
#endif
SkASSERT(row >= fMask.fImage);
SkASSERT(row + n + 1 < fMask.fImage + kMAX_STORAGE + 1);