faster stroke rects

git-svn-id: http://skia.googlecode.com/svn/trunk@1042 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
mike@reedtribe.org 2011-04-04 14:38:12 +00:00
parent f687087633
commit 7ff678bc16
3 changed files with 234 additions and 32 deletions

View File

@ -1,6 +1,6 @@
/* libs/graphics/sgl/SkScan.h /* libs/graphics/sgl/SkScan.h
** **
** Copyright 2006, The Android Open Source Project ** Copyright 2011, The Android Open Source Project
** **
** Licensed under the Apache License, Version 2.0 (the "License"); ** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License. ** you may not use this file except in compliance with the License.
@ -42,10 +42,9 @@ public:
#else #else
static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*); static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
#endif #endif
static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*);
static void FillPath(const SkPath&, const SkRegion& clip, SkBlitter*); static void FillPath(const SkPath&, const SkRegion& clip, SkBlitter*);
static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*);
static void FillTriangle(const SkPoint& a, const SkPoint& b, static void FillTriangle(const SkPoint& a, const SkPoint& b,
const SkPoint& c, const SkRegion* clip, const SkPoint& c, const SkRegion* clip,
SkBlitter* blitter) { SkBlitter* blitter) {
@ -56,12 +55,11 @@ public:
FillTriangle(pts, clip, blitter); FillTriangle(pts, clip, blitter);
} }
static void HairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*); static void HairLine(const SkPoint&, const SkPoint&, const SkRegion*,
SkBlitter*);
static void HairRect(const SkRect&, const SkRegion* clip, SkBlitter*); static void HairRect(const SkRect&, const SkRegion* clip, SkBlitter*);
static void HairPath(const SkPath&, const SkRegion* clip, SkBlitter*); static void HairPath(const SkPath&, const SkRegion* clip, SkBlitter*);
static void FrameRect(const SkRect&, SkScalar width, const SkRegion* clip, SkBlitter*);
static void AntiFillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*); static void AntiFillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*);
#ifdef SK_SCALAR_IS_FIXED #ifdef SK_SCALAR_IS_FIXED
static void AntiFillRect(const SkRect& rect, const SkRegion* clip, static void AntiFillRect(const SkRect& rect, const SkRegion* clip,
@ -74,9 +72,16 @@ public:
static void AntiFillPath(const SkPath&, const SkRegion& clip, SkBlitter*); static void AntiFillPath(const SkPath&, const SkRegion& clip, SkBlitter*);
static void AntiHairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*); static void AntiHairLine(const SkPoint&, const SkPoint&, const SkRegion*,
SkBlitter*);
static void AntiHairRect(const SkRect&, const SkRegion* clip, SkBlitter*); static void AntiHairRect(const SkRect&, const SkRegion* clip, SkBlitter*);
static void AntiHairPath(const SkPath&, const SkRegion* clip, SkBlitter*); static void AntiHairPath(const SkPath&, const SkRegion* clip, SkBlitter*);
// draws with a miter-join
static void FrameRect(const SkRect&, SkScalar width, const SkRegion*,
SkBlitter*);
static void AntiFrameRect(const SkRect&, SkScalar width, const SkRegion*,
SkBlitter*);
}; };
/** Assign an SkXRect from a SkIRect, by promoting the src rect's coordinates /** Assign an SkXRect from a SkIRect, by promoting the src rect's coordinates

View File

@ -679,6 +679,18 @@ static inline SkPoint* as_rightbottom(SkRect* r) {
return ((SkPoint*)(void*)r) + 1; return ((SkPoint*)(void*)r) + 1;
} }
static bool easy_rect_join(const SkPaint& paint) {
return SkPaint::kMiter_Join == paint.getStrokeJoin() &&
paint.getStrokeMiter() >= SK_ScalarSqrt2;
}
enum RectType {
kHair_RectType,
kFill_RectType,
kStroke_RectType,
kPath_RectType
};
void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const { void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
@ -688,11 +700,30 @@ void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
return; return;
} }
// complex enough to draw as a path RectType rtype;
const SkScalar width = paint.getStrokeWidth();
bool zeroWidth = (0 == width);
SkPaint::Style style = paint.getStyle();
if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
style = SkPaint::kFill_Style;
}
if (paint.getPathEffect() || paint.getMaskFilter() || if (paint.getPathEffect() || paint.getMaskFilter() ||
paint.getRasterizer() || !fMatrix->rectStaysRect() || paint.getRasterizer() || !fMatrix->rectStaysRect() ||
(paint.getStyle() != SkPaint::kFill_Style && SkPaint::kStrokeAndFill_Style == style) {
SkScalarHalf(paint.getStrokeWidth()) > 0)) { rtype = kPath_RectType;
} else if (SkPaint::kFill_Style == paint.getStyle()) {
rtype = kFill_RectType;
} else if (zeroWidth) {
rtype = kHair_RectType;
} else if (easy_rect_join(paint)) {
rtype = kStroke_RectType;
} else {
rtype = kPath_RectType;
}
if (kPath_RectType == rtype) {
SkPath tmp; SkPath tmp;
tmp.addRect(rect); tmp.addRect(rect);
tmp.setFillType(SkPath::kWinding_FillType); tmp.setFillType(SkPath::kWinding_FillType);
@ -733,18 +764,30 @@ void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
// we want to "fill" if we are kFill or kStrokeAndFill, since in the latter // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
// case we are also hairline (if we've gotten to here), which devolves to // case we are also hairline (if we've gotten to here), which devolves to
// effectively just kFill // effectively just kFill
if (paint.getStyle() != SkPaint::kStroke_Style) { switch (rtype) {
if (paint.isAntiAlias()) { case kFill_RectType:
SkScan::AntiFillRect(devRect, clip, blitter); if (paint.isAntiAlias()) {
} else { SkScan::AntiFillRect(devRect, clip, blitter);
SkScan::FillRect(devRect, clip, blitter); } else {
} SkScan::FillRect(devRect, clip, blitter);
} else { }
if (paint.isAntiAlias()) { break;
SkScan::AntiHairRect(devRect, clip, blitter); case kStroke_RectType:
} else { if (paint.isAntiAlias()) {
SkScan::HairRect(devRect, clip, blitter); SkScan::AntiFrameRect(devRect, width, clip, blitter);
} } else {
SkScan::FrameRect(devRect, width, clip, blitter);
}
break;
case kHair_RectType:
if (paint.isAntiAlias()) {
SkScan::AntiHairRect(devRect, clip, blitter);
} else {
SkScan::HairRect(devRect, clip, blitter);
}
break;
default:
SkASSERT(!"bad rtype");
} }
} }

View File

@ -1,6 +1,6 @@
/* libs/graphics/sgl/SkScan_Antihair.cpp /* libs/graphics/sgl/SkScan_Antihair.cpp
** **
** Copyright 2006, The Android Open Source Project ** Copyright 2011, The Android Open Source Project
** **
** Licensed under the Apache License, Version 2.0 (the "License"); ** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License. ** you may not use this file except in compliance with the License.
@ -530,13 +530,8 @@ static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter* blitt
blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF)); blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
} }
static void antifillrect(const SkXRect& xr, SkBlitter* blitter) static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
{ bool fillInner) {
FDot8 L = SkFixedToFDot8(xr.fLeft);
FDot8 T = SkFixedToFDot8(xr.fTop);
FDot8 R = SkFixedToFDot8(xr.fRight);
FDot8 B = SkFixedToFDot8(xr.fBottom);
// check for empty now that we're in our reduced precision space // check for empty now that we're in our reduced precision space
if (L >= R || T >= B) if (L >= R || T >= B)
return; return;
@ -566,7 +561,7 @@ static void antifillrect(const SkXRect& xr, SkBlitter* blitter)
} }
int rite = R >> 8; int rite = R >> 8;
int width = rite - left; int width = rite - left;
if (width > 0) if (width > 0 && fillInner)
blitter->blitRect(left, top, width, height); blitter->blitRect(left, top, width, height);
if (R & 0xFF) if (R & 0xFF)
blitter->blitV(rite, top, height, R & 0xFF); blitter->blitV(rite, top, height, R & 0xFF);
@ -576,6 +571,12 @@ static void antifillrect(const SkXRect& xr, SkBlitter* blitter)
do_scanline(L, bot, R, B & 0xFF, blitter); do_scanline(L, bot, R, B & 0xFF, blitter);
} }
static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
blitter, true);
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip, void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
@ -667,6 +668,159 @@ void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
} }
} }
///////////////////////////////////////////////////////////////////////////////
// calls blitRect() if the rectangle is non-empty
static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
if (L < R && T < B) {
blitter->blitRect(L, T, R - L, B - T);
}
}
static inline FDot8 SkScalarToFDot8(SkScalar x) {
#ifdef SK_SCALAR_IS_FLOAT
return (int)(x * 256);
#else
return x >> 8;
#endif
}
static inline int FDot8Floor(FDot8 x) {
return x >> 8;
}
static inline int FDot8Ceil(FDot8 x) {
return (x + 0xFF) >> 8;
}
// 1 - (1 - a)*(1 - b)
static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
return SkToU8(a + b - SkAlphaMul(a, b));
}
static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
SkBlitter* blitter) {
SkASSERT(L < R);
if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
return;
}
int left = L >> 8;
if (L & 0xFF) {
blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
left += 1;
}
int rite = R >> 8;
int width = rite - left;
if (width > 0) {
call_hline_blitter(blitter, left, top, width, alpha);
}
if (R & 0xFF) {
blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
}
}
static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
SkBlitter* blitter) {
SkASSERT(L < R && T < B);
int top = T >> 8;
if (top == ((B - 1) >> 8)) { // just one scanline high
inner_scanline(L, top, R, B - T, blitter);
return;
}
if (T & 0xFF) {
inner_scanline(L, top, R, T & 0xFF, blitter);
top += 1;
}
int bot = B >> 8;
int height = bot - top;
if (height > 0) {
if (L & 0xFF) {
blitter->blitV(L >> 8, top, height, L & 0xFF);
}
if (R & 0xFF) {
blitter->blitV(R >> 8, top, height, ~R & 0xFF);
}
}
if (B & 0xFF) {
inner_scanline(L, bot, R, ~B & 0xFF, blitter);
}
}
void SkScan::AntiFrameRect(const SkRect& r, SkScalar diameter,
const SkRegion* clip, SkBlitter* blitter) {
SkASSERT(diameter > 0);
SkScalar radius = SkScalarHalf(diameter);
// outset by the radius
FDot8 L = SkScalarToFDot8(r.fLeft - radius);
FDot8 T = SkScalarToFDot8(r.fTop - radius);
FDot8 R = SkScalarToFDot8(r.fRight + radius);
FDot8 B = SkScalarToFDot8(r.fBottom + radius);
SkIRect outer;
// set outer to the outer rect of the outer section
outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
SkBlitterClipper clipper;
if (clip) {
if (clip->quickReject(outer)) {
return;
}
if (!clip->contains(outer)) {
blitter = clipper.apply(blitter, clip, &outer);
}
// now we can ignore clip for the rest of the function
}
// stroke the outer hull
antifilldot8(L, T, R, B, blitter, false);
// set outer to the outer rect of the middle section
outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
// in case we lost a bit with diameter/2
radius = diameter - radius;
// inset by the radius
L = SkScalarToFDot8(r.fLeft + radius);
T = SkScalarToFDot8(r.fTop + radius);
R = SkScalarToFDot8(r.fRight - radius);
B = SkScalarToFDot8(r.fBottom - radius);
if (L >= R || T >= B) {
fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
blitter);
} else {
SkIRect inner;
// set inner to the inner rect of the middle section
inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
// draw the frame in 4 pieces
fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
blitter);
fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
blitter);
fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
blitter);
fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
blitter);
// now stroke the inner rect, which is similar to antifilldot8() except that
// it treats the fractional coordinates with the inverse bias (since its
// inner).
innerstrokedot8(L, T, R, B, blitter);
}
}
#endif #endif