path ops : add support for inverse fill
add inverse fill, reverse diff, and gm tests cleaned up some interfaces Review URL: https://codereview.chromium.org/14371011 git-svn-id: http://skia.googlecode.com/svn/trunk@8798 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
c4c9870953
commit
7dfbb0720a
116
gm/pathopsinverse.cpp
Normal file
116
gm/pathopsinverse.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2013 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 "SkBitmap.h"
|
||||
#include "SkPath.h"
|
||||
#include "SkPathOps.h"
|
||||
#include "SkRect.h"
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
class PathOpsInverseGM : public GM {
|
||||
public:
|
||||
PathOpsInverseGM() {
|
||||
this->makePaints();
|
||||
}
|
||||
|
||||
protected:
|
||||
void makePaints() {
|
||||
const unsigned oneColor = 0xFF8080FF;
|
||||
const unsigned twoColor = 0x807F1f1f;
|
||||
SkColor blendColor = blend(oneColor, twoColor);
|
||||
makePaint(&onePaint, oneColor);
|
||||
makePaint(&twoPaint, twoColor);
|
||||
makePaint(&opPaint[kDifference_PathOp], oneColor);
|
||||
makePaint(&opPaint[kIntersect_PathOp], blendColor);
|
||||
makePaint(&opPaint[kUnion_PathOp], 0xFFc0FFc0);
|
||||
makePaint(&opPaint[kReverseDifference_PathOp], twoColor);
|
||||
makePaint(&opPaint[kXOR_PathOp], 0xFFa0FFe0);
|
||||
makePaint(&outlinePaint, 0xFF000000);
|
||||
outlinePaint.setStyle(SkPaint::kStroke_Style);
|
||||
}
|
||||
|
||||
SkColor blend(SkColor one, SkColor two) {
|
||||
SkBitmap temp;
|
||||
temp.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
|
||||
temp.allocPixels();
|
||||
SkCanvas canvas(temp);
|
||||
canvas.drawColor(one);
|
||||
canvas.drawColor(two);
|
||||
void* pixels = temp.getPixels();
|
||||
return *(SkColor*) pixels;
|
||||
}
|
||||
|
||||
void makePaint(SkPaint* paint, SkColor color) {
|
||||
paint->setAntiAlias(true);
|
||||
paint->setStyle(SkPaint::kFill_Style);
|
||||
paint->setColor(color);
|
||||
}
|
||||
|
||||
virtual SkString onShortName() SK_OVERRIDE {
|
||||
return SkString("pathopsinverse");
|
||||
}
|
||||
|
||||
virtual SkISize onISize() SK_OVERRIDE {
|
||||
return make_isize(1200, 900);
|
||||
}
|
||||
|
||||
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
|
||||
SkPath one, two;
|
||||
int yPos = 0;
|
||||
for (int oneFill = 0; oneFill <= 1; ++oneFill) {
|
||||
SkPath::FillType oneF = oneFill ? SkPath::kInverseEvenOdd_FillType
|
||||
: SkPath::kEvenOdd_FillType;
|
||||
for (int twoFill = 0; twoFill <= 1; ++twoFill) {
|
||||
SkPath::FillType twoF = twoFill ? SkPath::kInverseEvenOdd_FillType
|
||||
: SkPath::kEvenOdd_FillType;
|
||||
one.reset();
|
||||
one.setFillType(oneF);
|
||||
one.addRect(10, 10, 70, 70);
|
||||
two.reset();
|
||||
two.setFillType(twoF);
|
||||
two.addRect(40, 40, 100, 100);
|
||||
canvas->save();
|
||||
canvas->translate(0, SkIntToScalar(yPos));
|
||||
canvas->clipRect(SkRect::MakeWH(110, 110), SkRegion::kIntersect_Op, true);
|
||||
canvas->drawPath(one, onePaint);
|
||||
canvas->drawPath(one, outlinePaint);
|
||||
canvas->drawPath(two, twoPaint);
|
||||
canvas->drawPath(two, outlinePaint);
|
||||
canvas->restore();
|
||||
int xPos = 150;
|
||||
for (int op = kDifference_PathOp; op <= kReverseDifference_PathOp; ++op) {
|
||||
SkPath result;
|
||||
Op(one, two, (SkPathOp) op, &result);
|
||||
canvas->save();
|
||||
canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos));
|
||||
canvas->clipRect(SkRect::MakeWH(110, 110), SkRegion::kIntersect_Op, true);
|
||||
canvas->drawPath(result, opPaint[op]);
|
||||
canvas->drawPath(result, outlinePaint);
|
||||
canvas->restore();
|
||||
xPos += 150;
|
||||
}
|
||||
yPos += 150;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SkPaint onePaint;
|
||||
SkPaint twoPaint;
|
||||
SkPaint outlinePaint;
|
||||
SkPaint opPaint[kReverseDifference_PathOp - kDifference_PathOp + 1];
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static GM* MyFactory(void*) { return new PathOpsInverseGM; }
|
||||
static GMRegistry reg(MyFactory);
|
||||
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
# include this gypi to include all the golden master slides.
|
||||
{
|
||||
'includes': [
|
||||
'pathops.gypi',
|
||||
],
|
||||
'sources': [
|
||||
'../gm/aaclip.cpp',
|
||||
'../gm/aarectmodes.cpp',
|
||||
@ -66,6 +69,7 @@
|
||||
'../gm/patheffects.cpp',
|
||||
'../gm/pathfill.cpp',
|
||||
'../gm/pathinterior.cpp',
|
||||
'../gm/pathopsinverse.cpp',
|
||||
'../gm/pathreverse.cpp',
|
||||
'../gm/perlinnoise.cpp',
|
||||
'../gm/points.cpp',
|
||||
|
@ -15,6 +15,7 @@
|
||||
'../tests/PathOpsDTriangleTest.cpp',
|
||||
'../tests/PathOpsDVectorTest.cpp',
|
||||
'../tests/PathOpsExtendedTest.cpp',
|
||||
'../tests/PathOpsInverseTest.cpp',
|
||||
'../tests/PathOpsLineIntersectionTest.cpp',
|
||||
'../tests/PathOpsLineParametetersTest.cpp',
|
||||
'../tests/PathOpsOpCubicThreadedTest.cpp',
|
||||
|
@ -9,18 +9,18 @@
|
||||
|
||||
class SkPath;
|
||||
|
||||
// FIXME: move this into SkPaths.h or just use the equivalent in SkRegion.h
|
||||
// FIXME: move everything below into the SkPath class
|
||||
/**
|
||||
* The logical operations that can be performed when combining two paths.
|
||||
*/
|
||||
enum SkPathOp {
|
||||
kDifference_PathOp, //!< subtract the op path from the first path
|
||||
kIntersect_PathOp, //!< intersect the two paths
|
||||
kUnion_PathOp, //!< union (inclusive-or) the two paths
|
||||
kXOR_PathOp, //!< exclusive-or the two paths
|
||||
/** subtract the first path from the op path */
|
||||
kReverseDifference_PathOp, // FIXME: unsupported
|
||||
kReplace_PathOp //!< replace the dst path with the op FIXME: unsupported: should it be?
|
||||
kDifference_PathOp, //!< subtract the op path from the first path
|
||||
kIntersect_PathOp, //!< intersect the two paths
|
||||
kUnion_PathOp, //!< union (inclusive-or) the two paths
|
||||
kXOR_PathOp, //!< exclusive-or the two paths
|
||||
kReverseDifference_PathOp, //!< subtract the first path from the op path
|
||||
};
|
||||
|
||||
// FIXME: these functions become members of SkPath
|
||||
/**
|
||||
* Set this path to the result of applying the Op to this path and the
|
||||
* specified path: this = (this op operand). The resulting path will be constructed
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "SkIntersections.h"
|
||||
#include "SkOpSegment.h"
|
||||
#include "SkPathWriter.h"
|
||||
#include "TSearch.h"
|
||||
#include "SkTSort.h"
|
||||
|
||||
#define F (false) // discard the edge
|
||||
#define T (true) // keep the edge
|
||||
@ -18,7 +18,6 @@ static const bool gUnaryActiveEdge[2][2] = {
|
||||
{F, T}, {T, F},
|
||||
};
|
||||
|
||||
// FIXME: add support for kReverseDifference_Op
|
||||
static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
|
||||
// miFrom=0 miFrom=1
|
||||
// miTo=0 miTo=1 miTo=0 miTo=1
|
||||
@ -2318,7 +2317,7 @@ bool SkOpSegment::SortAngles(const SkTDArray<SkOpAngle>& angles,
|
||||
sortable &= !angle.unsortable();
|
||||
}
|
||||
if (sortable) {
|
||||
QSort<SkOpAngle>(angleList->begin(), angleList->end() - 1);
|
||||
SkTQSort<SkOpAngle>(angleList->begin(), angleList->end() - 1);
|
||||
for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
|
||||
if (angles[angleIndex].unsortable()) {
|
||||
sortable = false;
|
||||
|
@ -206,18 +206,48 @@ static bool bridgeOp(SkTDArray<SkOpContour*>& contourList, const SkPathOp op,
|
||||
return simple->someAssemblyRequired();
|
||||
}
|
||||
|
||||
// pretty picture:
|
||||
// https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
|
||||
static const SkPathOp gOpInverse[kReverseDifference_PathOp + 1][2][2] = {
|
||||
// inside minuend outside minuend
|
||||
// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
|
||||
{{ kDifference_PathOp, kIntersect_PathOp }, { kUnion_PathOp, kReverseDifference_PathOp }},
|
||||
{{ kIntersect_PathOp, kDifference_PathOp }, { kReverseDifference_PathOp, kUnion_PathOp }},
|
||||
{{ kUnion_PathOp, kReverseDifference_PathOp }, { kDifference_PathOp, kIntersect_PathOp }},
|
||||
{{ kXOR_PathOp, kXOR_PathOp }, { kXOR_PathOp, kXOR_PathOp }},
|
||||
{{ kReverseDifference_PathOp, kUnion_PathOp }, { kIntersect_PathOp, kDifference_PathOp }},
|
||||
};
|
||||
|
||||
static const bool gOutInverse[kReverseDifference_PathOp + 1][2][2] = {
|
||||
{{ false, false }, { true, false }}, // diff
|
||||
{{ false, false }, { false, true }}, // sect
|
||||
{{ false, true }, { true, true }}, // union
|
||||
{{ false, true }, { true, false }}, // xor
|
||||
{{ false, true }, { false, false }}, // rev diff
|
||||
};
|
||||
|
||||
void Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
|
||||
op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
|
||||
result->reset();
|
||||
SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
|
||||
? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
|
||||
result->setFillType(fillType);
|
||||
const SkPath* minuend = &one;
|
||||
const SkPath* subtrahend = &two;
|
||||
if (op == kReverseDifference_PathOp) {
|
||||
minuend = &two;
|
||||
subtrahend = &one;
|
||||
op = kDifference_PathOp;
|
||||
}
|
||||
#if DEBUG_SORT || DEBUG_SWAP_TOP
|
||||
gDebugSortCount = gDebugSortCountDefault;
|
||||
#endif
|
||||
result->reset();
|
||||
result->setFillType(SkPath::kEvenOdd_FillType);
|
||||
// turn path into list of segments
|
||||
SkTArray<SkOpContour> contours;
|
||||
// FIXME: add self-intersecting cubics' T values to segment
|
||||
SkOpEdgeBuilder builder(one, contours);
|
||||
SkOpEdgeBuilder builder(*minuend, contours);
|
||||
const int xorMask = builder.xorMask();
|
||||
builder.addOperand(two);
|
||||
builder.addOperand(*subtrahend);
|
||||
builder.finish();
|
||||
const int xorOpMask = builder.xorMask();
|
||||
SkTDArray<SkOpContour*> contourList;
|
||||
@ -264,7 +294,7 @@ void Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
|
||||
bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
|
||||
{ // if some edges could not be resolved, assemble remaining fragments
|
||||
SkPath temp;
|
||||
temp.setFillType(SkPath::kEvenOdd_FillType);
|
||||
temp.setFillType(fillType);
|
||||
SkPathWriter assembled(temp);
|
||||
Assemble(wrapper, &assembled);
|
||||
*result = *assembled.nativePath();
|
||||
|
@ -149,7 +149,9 @@ void Simplify(const SkPath& path, SkPath* result) {
|
||||
#endif
|
||||
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
|
||||
result->reset();
|
||||
result->setFillType(SkPath::kEvenOdd_FillType);
|
||||
SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
|
||||
: SkPath::kEvenOdd_FillType;
|
||||
result->setFillType(fillType);
|
||||
SkPathWriter simple(*result);
|
||||
|
||||
// turn path into list of segments
|
||||
@ -187,7 +189,7 @@ void Simplify(const SkPath& path, SkPath* result) {
|
||||
: !bridgeXor(contourList, &simple))
|
||||
{ // if some edges could not be resolved, assemble remaining fragments
|
||||
SkPath temp;
|
||||
temp.setFillType(SkPath::kEvenOdd_FillType);
|
||||
temp.setFillType(fillType);
|
||||
SkPathWriter assembled(temp);
|
||||
Assemble(simple, &assembled);
|
||||
*result = *assembled.nativePath();
|
||||
|
34
tests/PathOpsInverseTest.cpp
Normal file
34
tests/PathOpsInverseTest.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "PathOpsExtendedTest.h"
|
||||
|
||||
static void PathOpsInverseTest(skiatest::Reporter* reporter) {
|
||||
SkPath one, two;
|
||||
for (int op = kDifference_PathOp; op <= kReverseDifference_PathOp; ++op) {
|
||||
for (int oneFill = SkPath::kWinding_FillType; oneFill <= SkPath::kInverseEvenOdd_FillType;
|
||||
++oneFill) {
|
||||
for (int oneDir = SkPath::kCW_Direction; oneDir != SkPath::kCCW_Direction; ++oneDir) {
|
||||
one.reset();
|
||||
one.setFillType((SkPath::FillType) oneFill);
|
||||
one.addRect(0, 0, 6, 6, (SkPath::Direction) oneDir);
|
||||
for (int twoFill = SkPath::kWinding_FillType;
|
||||
twoFill <= SkPath::kInverseEvenOdd_FillType; ++twoFill) {
|
||||
for (int twoDir = SkPath::kCW_Direction; twoDir != SkPath::kCCW_Direction;
|
||||
++twoDir) {
|
||||
two.reset();
|
||||
two.setFillType((SkPath::FillType) twoFill);
|
||||
two.addRect(3, 3, 9, 9, (SkPath::Direction) twoDir);
|
||||
testPathOp(reporter, one, two, (SkPathOp) op);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "TestClassDef.h"
|
||||
DEFINE_TESTCLASS_SHORT(PathOpsInverseTest)
|
Loading…
Reference in New Issue
Block a user