/* 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");
** you may not use this file except in compliance with the License.
#else
static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
#endif
-
- static void FillTriangle(const SkPoint pts[], const SkRegion*, 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,
const SkPoint& c, const SkRegion* clip,
SkBlitter* 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 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*);
#ifdef SK_SCALAR_IS_FIXED
static void AntiFillRect(const SkRect& rect, const SkRegion* clip,
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 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
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 {
SkDEBUGCODE(this->validate();)
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() ||
- paint.getRasterizer() || !fMatrix->rectStaysRect() ||
- (paint.getStyle() != SkPaint::kFill_Style &&
- SkScalarHalf(paint.getStrokeWidth()) > 0)) {
+ paint.getRasterizer() || !fMatrix->rectStaysRect() ||
+ SkPaint::kStrokeAndFill_Style == style) {
+ 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;
tmp.addRect(rect);
tmp.setFillType(SkPath::kWinding_FillType);
// 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
// effectively just kFill
- if (paint.getStyle() != SkPaint::kStroke_Style) {
- if (paint.isAntiAlias()) {
- SkScan::AntiFillRect(devRect, clip, blitter);
- } else {
- SkScan::FillRect(devRect, clip, blitter);
- }
- } else {
- if (paint.isAntiAlias()) {
- SkScan::AntiHairRect(devRect, clip, blitter);
- } else {
- SkScan::HairRect(devRect, clip, blitter);
- }
+ switch (rtype) {
+ case kFill_RectType:
+ if (paint.isAntiAlias()) {
+ SkScan::AntiFillRect(devRect, clip, blitter);
+ } else {
+ SkScan::FillRect(devRect, clip, blitter);
+ }
+ break;
+ case kStroke_RectType:
+ if (paint.isAntiAlias()) {
+ 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");
}
}
/* 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");
** you may not use this file except in compliance with the License.
blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
}
-static void antifillrect(const SkXRect& xr, SkBlitter* blitter)
-{
- FDot8 L = SkFixedToFDot8(xr.fLeft);
- FDot8 T = SkFixedToFDot8(xr.fTop);
- FDot8 R = SkFixedToFDot8(xr.fRight);
- FDot8 B = SkFixedToFDot8(xr.fBottom);
-
+static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
+ bool fillInner) {
// check for empty now that we're in our reduced precision space
if (L >= R || T >= B)
return;
}
int rite = R >> 8;
int width = rite - left;
- if (width > 0)
+ if (width > 0 && fillInner)
blitter->blitRect(left, top, width, height);
if (R & 0xFF)
blitter->blitV(rite, top, height, R & 0xFF);
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,
}
}
+///////////////////////////////////////////////////////////////////////////////
+
+// 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