Index: src/core/SkScan_Path.cpp |
diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp |
index fc79fc8537d08fdbff306ee801a2fa66a5a9a6a2..dbe61e13d207e55376140c69fae7e300125dcea3 100644 |
--- a/src/core/SkScan_Path.cpp |
+++ b/src/core/SkScan_Path.cpp |
@@ -14,9 +14,14 @@ |
#include "SkQuadClipper.h" |
#include "SkRasterClip.h" |
#include "SkRegion.h" |
+#include "SkStroke.h" |
#include "SkTemplates.h" |
#include "SkTSort.h" |
+#include <stdio.h> // FIXME |
+ |
+static bool dumping; |
+ |
#define kEDGE_HEAD_Y SK_MinS32 |
#define kEDGE_TAIL_Y SK_MaxS32 |
@@ -48,6 +53,105 @@ static inline void insert_edge_after(SkEdge* edge, SkEdge* afterMe) { |
afterMe->fNext = edge; |
} |
+static void emit_wireframe_triangle(SkPoint t[3], SkTDArray<SkPoint>* points) { |
+ SkPoint *p = points->append(6); |
+ p[0] = t[0]; |
+ p[1] = t[1]; |
+ p[2] = t[1]; |
+ p[3] = t[2]; |
+ p[4] = t[2]; |
+ p[5] = t[0]; |
+} |
+ |
+static void emit_trapezoid(SkEdge* left, SkEdge* right, float top, float bottom, SkTDArray<SkPoint>* points) |
+{ |
+ // If this edge was already neutered by its partner, don't emit. |
+ if (top > bottom) { |
+ return; |
+ } |
+ |
+ if (!left || !right) { |
+ return; |
+ } |
+ |
+ float top_left = left->fFirstXf; |
+ float top_right = right->fFirstXf; |
+ float bottom_left = left->fFirstXf + SkFixedToFloat(left->fDX * (bottom - top)); |
+ float bottom_right = right->fFirstXf + SkFixedToFloat(right->fDX * (bottom - top)); |
+ if (dumping) { |
+ printf("top %g bot %g tl %g tr %g bl %g br %g\n", |
+ top, bottom, left->fFirstXf, right->fFirstXf, bottom_left, bottom_right); |
+ } |
+ if (SkScan::gWireframe) { |
+ if (top_left == top_right) { |
+ SkPoint p[3]; |
+ p[0] = SkPoint::Make(top_right, top); |
+ p[1] = SkPoint::Make(bottom_right, bottom); |
+ p[2] = SkPoint::Make(bottom_left , bottom); |
+ emit_wireframe_triangle(p, points); |
+ } else if (bottom_left == bottom_right) { |
+ SkPoint p[3]; |
+ p[0] = SkPoint::Make(top_left, top); |
+ p[1] = SkPoint::Make(top_right, top); |
+ p[2] = SkPoint::Make(bottom_left, bottom); |
+ emit_wireframe_triangle(p, points); |
+ } else { |
+ SkPoint p[3]; |
+ p[0] = SkPoint::Make(top_left, top); |
+ p[1] = SkPoint::Make(top_right, top); |
+ p[2] = SkPoint::Make(bottom_left , bottom); |
+ emit_wireframe_triangle(p, points); |
+ p[0] = SkPoint::Make(top_right, top); |
+ p[1] = SkPoint::Make(bottom_right, bottom); |
+ p[2] = SkPoint::Make(bottom_left , bottom); |
+ emit_wireframe_triangle(p, points); |
+ } |
+ } else { |
+ if (top_left == top_right) { |
+ SkPoint *p = points->append(3); |
+ p[0] = SkPoint::Make(top_right, top); |
+ p[1] = SkPoint::Make(bottom_right, bottom); |
+ p[2] = SkPoint::Make(bottom_left , bottom); |
+ } else if (bottom_left == bottom_right) { |
+ SkPoint *p = points->append(3); |
+ p[0] = SkPoint::Make(top_left, top); |
+ p[1] = SkPoint::Make(top_right, top); |
+ p[2] = SkPoint::Make(bottom_left, bottom); |
+ } else { |
+ SkPoint *p = points->append(6); |
+ p[0] = SkPoint::Make(top_left, top); |
+ p[1] = SkPoint::Make(top_right, top); |
+ p[2] = SkPoint::Make(bottom_left , bottom); |
+ p[3] = SkPoint::Make(top_right, top); |
+ p[4] = SkPoint::Make(bottom_right, bottom); |
+ p[5] = SkPoint::Make(bottom_left , bottom); |
+ } |
+ } |
+ left->fFirstXf = bottom_left; |
+ left->fFirstYf = bottom; |
+ right->fFirstXf = bottom_right; |
+ right->fFirstYf = bottom; |
+} |
+ |
+static void emit_all_trapezoids(SkEdge* start, int windingMask, int curr_y, SkTDArray<SkPoint>* points) |
+{ |
+ int w = 0; |
+ SkEdge* left = NULL; |
+ for (SkEdge* edge = start; edge != NULL && edge->fFirstY < curr_y && edge->fFirstYf < curr_y; edge = edge->fNext) { |
+ w += edge->fWinding; |
+ if ((w & windingMask) == 0) { |
+ SkASSERT(left); |
+ emit_trapezoid(left, edge, left->fFirstYf, curr_y, points); |
+ left = NULL; |
+ } else if (!left) { |
+ left = edge; |
+ } else { |
+ edge->fFirstXf += SkFixedToFloat(edge->fDX * (curr_y - edge->fFirstYf)); |
+ edge->fFirstYf = (float) curr_y; |
+ } |
+ } |
+} |
+ |
static void backward_insert_edge_based_on_x(SkEdge* edge SkDECLAREPARAM(int, curr_y)) { |
SkFixed x = edge->fX; |
@@ -97,8 +201,8 @@ typedef void (*PrePostProc)(SkBlitter* blitter, int y, bool isStartOfScanline); |
#define PREPOST_END false |
static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, |
- SkBlitter* blitter, int start_y, int stop_y, |
- PrePostProc proc, int rightClip) { |
+ SkBlitter* blitter, SkTDArray<SkPoint>* points, |
+ int start_y, int stop_y, PrePostProc proc, int rightClip) { |
validate_sort(prevHead->fNext); |
int curr_y = start_y; |
@@ -114,7 +218,7 @@ static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, |
validate_edges_for_y(currE, curr_y); |
- if (proc) { |
+ if (proc && blitter) { |
proc(blitter, curr_y, PREPOST_START); // pre-proc |
} |
@@ -127,8 +231,11 @@ static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, |
SkASSERT(in_interval); |
int width = x - left; |
SkASSERT(width >= 0); |
- if (width) |
- blitter->blitH(left, curr_y, width); |
+ if (width) { |
+ if (blitter) { |
+ blitter->blitH(left, curr_y, width); |
+ } |
+ } |
in_interval = false; |
} else if (!in_interval) { |
left = x; |
@@ -139,6 +246,7 @@ static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, |
SkFixed newX; |
if (currE->fLastY == curr_y) { // are we done with this edge? |
+ if (points) emit_all_trapezoids(prevHead->fNext, windingMask, curr_y + 1, points); |
if (currE->fCurveCount < 0) { |
if (((SkCubicEdge*)currE)->updateCubic()) { |
SkASSERT(currE->fFirstY == curr_y + 1); |
@@ -159,6 +267,7 @@ static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, |
currE->fX = newX; |
NEXT_X: |
if (newX < prevX) { // ripple currE backwards until it is x-sorted |
+ if (points) emit_all_trapezoids(prevHead->fNext, windingMask, curr_y + 1, points); |
backward_insert_edge_based_on_x(currE SkPARAM(curr_y)); |
} else { |
prevX = newX; |
@@ -169,14 +278,14 @@ static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, |
} |
// was our right-edge culled away? |
- if (in_interval) { |
+ if (in_interval && blitter) { |
int width = rightClip - left; |
if (width > 0) { |
blitter->blitH(left, curr_y, width); |
} |
} |
- if (proc) { |
+ if (proc && blitter) { |
proc(blitter, curr_y, PREPOST_END); // post-proc |
} |
@@ -184,6 +293,9 @@ static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, |
if (curr_y >= stop_y) { |
break; |
} |
+ if (points && currE->fFirstY == curr_y) { |
+ emit_all_trapezoids(prevHead->fNext, windingMask, curr_y, points); |
+ } |
// now currE points to the first edge with a Yint larger than curr_y |
insert_new_edges(currE, curr_y); |
} |
@@ -210,7 +322,8 @@ static bool update_edge(SkEdge* edge, int last_y) { |
} |
static void walk_convex_edges(SkEdge* prevHead, SkPath::FillType, |
- SkBlitter* blitter, int start_y, int stop_y, |
+ SkBlitter* blitter, SkTDArray<SkPoint>* points, |
+ int start_y, int stop_y, |
PrePostProc proc) { |
validate_sort(prevHead->fNext); |
@@ -247,12 +360,22 @@ static void walk_convex_edges(SkEdge* prevHead, SkPath::FillType, |
SkFixed dRite = riteE->fDX; |
int count = local_bot - local_top; |
SkASSERT(count >= 0); |
- if (0 == (dLeft | dRite)) { |
+ if (points) { |
+ emit_trapezoid(leftE, riteE, (float) local_top, (float) (local_bot + 1), points); |
+ if (left < rite) { |
+ count += 1; |
+ left += count * dLeft; |
+ rite += count * dRite; |
+ } |
+ local_top = local_bot + 1; |
+ } else if (0 == (dLeft | dRite)) { |
int L = SkFixedRoundToInt(left); |
int R = SkFixedRoundToInt(rite); |
if (L < R) { |
count += 1; |
blitter->blitRect(L, local_top, R - L, count); |
+ left += count * dLeft; |
+ rite += count * dRite; |
} |
local_top = local_bot + 1; |
} else { |
@@ -394,14 +517,16 @@ static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last) { |
// |
// clipRect (if no null) has already been shifted up |
// |
-void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitter, |
- int start_y, int stop_y, int shiftEdgesUp, const SkRegion& clipRgn) { |
- SkASSERT(blitter); |
+void sk_fill_path(const SkPath& path, const SkIRect* clipRect, |
+ SkBlitter* blitter, SkTDArray<SkPoint>* points, |
+ int start_y, int stop_y, int shiftEdgesUp, |
+ const SkRegion& clipRgn) { |
+ SkASSERT(blitter || points); |
SkEdgeBuilder builder; |
// If we're convex, then we need both edges, even the right edge is past the clip |
- const bool canCullToTheRight = !path.isConvex(); |
+ const bool canCullToTheRight = !path.isConvex() && !points; |
int count = builder.build(path, clipRect, shiftEdgesUp, canCullToTheRight); |
SkASSERT(count >= 0); |
@@ -424,18 +549,32 @@ void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitte |
rect.fBottom = stop_y; |
} |
if (!rect.isEmpty()) { |
- blitter->blitRect(rect.fLeft << shiftEdgesUp, |
- rect.fTop << shiftEdgesUp, |
- rect.width() << shiftEdgesUp, |
- rect.height() << shiftEdgesUp); |
+ if (blitter) { |
+ blitter->blitRect(rect.fLeft << shiftEdgesUp, |
+ rect.fTop << shiftEdgesUp, |
+ rect.width() << shiftEdgesUp, |
+ rect.height() << shiftEdgesUp); |
+ } else { |
+ SkRect r = SkRect::Make(rect); |
+ r.toQuad(points->append(4)); |
+ } |
} |
} |
+ |
return; |
} |
SkEdge headEdge, tailEdge, *last; |
// this returns the first and last edge after they're sorted into a dlink list |
SkEdge* edge = sort_edges(list, count, &last); |
+ if (dumping) { |
+ printf("sorted into %d edges:\n", count); |
+ SkEdge* e = edge; |
+ for (int i = 0; i < count; i++) { |
+ printf("edge %p fFirstXf %g fFirstYf %g fLastY %d fDX %g\n", e, e->fFirstXf, e->fFirstYf, e->fLastY, e->fDX / 65536.0); |
+ e = e->fNext; |
+ } |
+ } |
headEdge.fPrev = nullptr; |
headEdge.fNext = edge; |
@@ -462,15 +601,15 @@ void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitte |
InverseBlitter ib; |
PrePostProc proc = nullptr; |
- if (path.isInverseFillType()) { |
+ if (path.isInverseFillType() && blitter) { |
ib.setBlitter(blitter, clipRgn.getBounds(), shiftEdgesUp); |
blitter = &ib; |
proc = PrePostInverseBlitterProc; |
} |
- if (path.isConvex() && (nullptr == proc)) { |
+ if (path.isConvex() && (nullptr == proc) && blitter) { |
SkASSERT(count >= 2); // convex walker does not handle missing right edges |
- walk_convex_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, nullptr); |
+ walk_convex_edges(&headEdge, path.getFillType(), blitter, points, start_y, stop_y, NULL); |
} else { |
int rightEdge; |
if (clipRect) { |
@@ -478,8 +617,8 @@ void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitte |
} else { |
rightEdge = SkScalarRoundToInt(path.getBounds().right()) << shiftEdgesUp; |
} |
- |
- walk_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, proc, rightEdge); |
+ |
+ walk_edges(&headEdge, path.getFillType(), blitter, points, start_y, stop_y, proc, rightEdge); |
} |
} |
@@ -599,7 +738,7 @@ void SkScan::FillPath(const SkPath& path, const SkRegion& origClip, |
if (path.isInverseFillType()) { |
sk_blit_above(blitter, ir, *clipPtr); |
} |
- sk_fill_path(path, clipper.getClipRect(), blitter, ir.fTop, ir.fBottom, |
+ sk_fill_path(path, clipper.getClipRect(), blitter, NULL, ir.fTop, ir.fBottom, |
0, *clipPtr); |
if (path.isInverseFillType()) { |
sk_blit_below(blitter, ir, *clipPtr); |
@@ -673,8 +812,8 @@ static void sk_fill_triangle(const SkPoint pts[], const SkIRect* clipRect, |
if (clipRect && start_y < clipRect->fTop) { |
start_y = clipRect->fTop; |
} |
- walk_convex_edges(&headEdge, SkPath::kEvenOdd_FillType, blitter, start_y, stop_y, nullptr); |
-// walk_edges(&headEdge, SkPath::kEvenOdd_FillType, blitter, start_y, stop_y, nullptr); |
+ walk_convex_edges(&headEdge, SkPath::kEvenOdd_FillType, blitter, nullptr, start_y, stop_y, nullptr); |
+// walk_edges(&headEdge, SkPath::kEvenOdd_FillType, blitter, start_y, stop_y, NULL); |
} |
void SkScan::FillTriangle(const SkPoint pts[], const SkRasterClip& clip, |
@@ -707,3 +846,39 @@ void SkScan::FillTriangle(const SkPoint pts[], const SkRasterClip& clip, |
sk_fill_triangle(pts, clipper.getClipRect(), blitter, ir); |
} |
} |
+ |
+void SkScan::PathToTriangles(const SkPath& path, const SkIRect& clipBounds, |
+ bool antiAlias, SkTDArray<SkPoint>* points) { |
+// dumping = true; |
+// printf("*** SkScan::PathToTriangles start\n"); |
+ points->setReserve(path.countPoints()); |
+ SkPath localPath = path; |
+ SkIRect localClipBounds = clipBounds; |
+ if (antiAlias) { |
+ // Stroke 1-pix wide around path. |
+ SkStroke stroker; |
+ stroker.setJoin(SkPaint::kMiter_Join); |
+ stroker.setWidth(5); |
+ SkPath strokedPath; |
+ // We want even open paths to become closed, so we can fill them. |
+ stroker.setAutoClose(true); |
+ // Don't reverse the inner path direction, since want the interior to be filled. |
+ stroker.setReverseInner(false); |
+ stroker.strokePath(localPath, &strokedPath); |
+ localPath = strokedPath; |
+ } |
+ SkRect pathBounds = localPath.getBounds(); |
+ SkRegion clipRgn(localClipBounds); |
+ if (localPath.isInverseFillType()) { |
+ localPath.addRect(SkRect::Make(localClipBounds), SkPath::kCCW_Direction); |
+ sk_fill_path(localPath, &localClipBounds, NULL, points, |
+ localClipBounds.top(), localClipBounds.bottom(), 0, |
+ clipRgn); |
+ } else { |
+ sk_fill_path(localPath, &localClipBounds, NULL, points, |
+ pathBounds.top() - 1, pathBounds.bottom() + 1, 0, |
+ clipRgn); |
+ } |
+// printf("*** SkScan::PathToTriangles end\n"); |
+// dumping = false; |
+} |