GrTessellator: fix artifact with exactly-1-px-wide edges.
When path features are exactly a pixel wide, the extruded inner edges can become collinear and then be removed, since their winding is zero. We need these edges to be preserved through triangulation, otherwise opaque portions of the geometry can become transparent. Since the simplify() pass can handle zero-winding edges just fine, the the fix is to simply not remove them. In addition, this changes refactors out disconnect() from all the calls to remove_edge_above()/remove_edge_below(). It also renames the remaining function erase_edge() (since it's now unconditional). Add a new test to a new "thinconcavepaths" GM. BUG=680260 NOTRY=true Change-Id: I1d3a436c95a01c4d4ef5dc05503de4312677f65d Reviewed-on: https://skia-review.googlesource.com/6902 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Stephan White <senorblanco@chromium.org>
This commit is contained in:
parent
e4ca362259
commit
e7a364d435
63
gm/thinconcavepaths.cpp
Normal file
63
gm/thinconcavepaths.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gm.h"
|
||||||
|
#include "SkCanvas.h"
|
||||||
|
#include "SkPath.h"
|
||||||
|
|
||||||
|
#define WIDTH 400
|
||||||
|
#define HEIGHT 400
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Test thin stroked rect (stroked "by hand", not by stroking).
|
||||||
|
void draw_thin_stroked_rect(SkCanvas* canvas, const SkPaint& paint, SkScalar width) {
|
||||||
|
SkPath path;
|
||||||
|
path.moveTo(10 + width, 10 + width);
|
||||||
|
path.lineTo(40, 10 + width);
|
||||||
|
path.lineTo(40, 20);
|
||||||
|
path.lineTo(10 + width, 20);
|
||||||
|
path.moveTo(10, 10);
|
||||||
|
path.lineTo(10, 20 + width);
|
||||||
|
path.lineTo(40 + width, 20 + width);
|
||||||
|
path.lineTo(40 + width, 10);
|
||||||
|
canvas->drawPath(path, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThinConcavePathsGM : public skiagm::GM {
|
||||||
|
public:
|
||||||
|
ThinConcavePathsGM() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SkString onShortName() override {
|
||||||
|
return SkString("thinconcavepaths");
|
||||||
|
}
|
||||||
|
|
||||||
|
SkISize onISize() override {
|
||||||
|
return SkISize::Make(WIDTH, HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDraw(SkCanvas* canvas) override {
|
||||||
|
SkPaint paint;
|
||||||
|
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
paint.setStyle(SkPaint::kFill_Style);
|
||||||
|
|
||||||
|
canvas->save();
|
||||||
|
for (SkScalar width = 1.0; width < 2.05; width += 0.25) {
|
||||||
|
draw_thin_stroked_rect(canvas, paint, width);
|
||||||
|
canvas->translate(0, 25);
|
||||||
|
}
|
||||||
|
canvas->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef skiagm::GM INHERITED;
|
||||||
|
};
|
||||||
|
|
||||||
|
DEF_GM( return new ThinConcavePathsGM; )
|
@ -289,6 +289,7 @@ gm_sources = [
|
|||||||
"$_gm/textblobuseaftergpufree.cpp",
|
"$_gm/textblobuseaftergpufree.cpp",
|
||||||
"$_gm/texteffects.cpp",
|
"$_gm/texteffects.cpp",
|
||||||
"$_gm/texturedomaineffect.cpp",
|
"$_gm/texturedomaineffect.cpp",
|
||||||
|
"$_gm/thinconcavepaths.cpp",
|
||||||
"$_gm/thinrects.cpp",
|
"$_gm/thinrects.cpp",
|
||||||
"$_gm/thinstrokedrects.cpp",
|
"$_gm/thinstrokedrects.cpp",
|
||||||
"$_gm/tiledscaledbitmap.cpp",
|
"$_gm/tiledscaledbitmap.cpp",
|
||||||
|
@ -896,13 +896,15 @@ void remove_edge_below(Edge* edge) {
|
|||||||
edge, &edge->fTop->fFirstEdgeBelow, &edge->fTop->fLastEdgeBelow);
|
edge, &edge->fTop->fFirstEdgeBelow, &edge->fTop->fLastEdgeBelow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void erase_edge_if_zero_winding(Edge* edge, EdgeList* edges) {
|
void disconnect(Edge* edge)
|
||||||
if (edge->fWinding != 0) {
|
{
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOG("erasing edge (%g -> %g)\n", edge->fTop->fID, edge->fBottom->fID);
|
|
||||||
remove_edge_above(edge);
|
remove_edge_above(edge);
|
||||||
remove_edge_below(edge);
|
remove_edge_below(edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase_edge(Edge* edge, EdgeList* edges) {
|
||||||
|
LOG("erasing edge (%g -> %g)\n", edge->fTop->fID, edge->fBottom->fID);
|
||||||
|
disconnect(edge);
|
||||||
if (edges && edges->contains(edge)) {
|
if (edges && edges->contains(edge)) {
|
||||||
remove_edge(edge, edges);
|
remove_edge(edge, edges);
|
||||||
}
|
}
|
||||||
@ -934,16 +936,12 @@ void merge_edges_above(Edge* edge, Edge* other, EdgeList* activeEdges, Comparato
|
|||||||
edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
|
edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
|
||||||
edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
|
edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
|
||||||
other->fWinding += edge->fWinding;
|
other->fWinding += edge->fWinding;
|
||||||
erase_edge_if_zero_winding(other, activeEdges);
|
erase_edge(edge, activeEdges);
|
||||||
edge->fWinding = 0;
|
|
||||||
erase_edge_if_zero_winding(edge, activeEdges);
|
|
||||||
} else if (c.sweep_lt(edge->fTop->fPoint, other->fTop->fPoint)) {
|
} else if (c.sweep_lt(edge->fTop->fPoint, other->fTop->fPoint)) {
|
||||||
other->fWinding += edge->fWinding;
|
other->fWinding += edge->fWinding;
|
||||||
erase_edge_if_zero_winding(other, activeEdges);
|
|
||||||
set_bottom(edge, other->fTop, activeEdges, c);
|
set_bottom(edge, other->fTop, activeEdges, c);
|
||||||
} else {
|
} else {
|
||||||
edge->fWinding += other->fWinding;
|
edge->fWinding += other->fWinding;
|
||||||
erase_edge_if_zero_winding(edge, activeEdges);
|
|
||||||
set_bottom(other, edge->fTop, activeEdges, c);
|
set_bottom(other, edge->fTop, activeEdges, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -954,16 +952,12 @@ void merge_edges_below(Edge* edge, Edge* other, EdgeList* activeEdges, Comparato
|
|||||||
edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
|
edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
|
||||||
edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
|
edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
|
||||||
other->fWinding += edge->fWinding;
|
other->fWinding += edge->fWinding;
|
||||||
erase_edge_if_zero_winding(other, activeEdges);
|
erase_edge(edge, activeEdges);
|
||||||
edge->fWinding = 0;
|
|
||||||
erase_edge_if_zero_winding(edge, activeEdges);
|
|
||||||
} else if (c.sweep_lt(edge->fBottom->fPoint, other->fBottom->fPoint)) {
|
} else if (c.sweep_lt(edge->fBottom->fPoint, other->fBottom->fPoint)) {
|
||||||
edge->fWinding += other->fWinding;
|
edge->fWinding += other->fWinding;
|
||||||
erase_edge_if_zero_winding(edge, activeEdges);
|
|
||||||
set_top(other, edge->fBottom, activeEdges, c);
|
set_top(other, edge->fBottom, activeEdges, c);
|
||||||
} else {
|
} else {
|
||||||
other->fWinding += edge->fWinding;
|
other->fWinding += edge->fWinding;
|
||||||
erase_edge_if_zero_winding(other, activeEdges);
|
|
||||||
set_top(edge, other->fBottom, activeEdges, c);
|
set_top(edge, other->fBottom, activeEdges, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1455,8 +1449,7 @@ void remove_non_boundary_edges(const VertexList& mesh, SkPath::FillType fillType
|
|||||||
for (Edge* e = v->fFirstEdgeBelow; e != nullptr;) {
|
for (Edge* e = v->fFirstEdgeBelow; e != nullptr;) {
|
||||||
Edge* next = e->fNextEdgeBelow;
|
Edge* next = e->fNextEdgeBelow;
|
||||||
if (!is_boundary_edge(e, fillType)) {
|
if (!is_boundary_edge(e, fillType)) {
|
||||||
remove_edge_above(e);
|
disconnect(e);
|
||||||
remove_edge_below(e);
|
|
||||||
}
|
}
|
||||||
e = next;
|
e = next;
|
||||||
}
|
}
|
||||||
@ -1612,8 +1605,7 @@ void extract_boundary(EdgeList* boundary, Edge* e, SkPath::FillType fillType, Sk
|
|||||||
down = true;
|
down = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remove_edge_above(e);
|
disconnect(e);
|
||||||
remove_edge_below(e);
|
|
||||||
e = next;
|
e = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user