add Convexity enum to SkPath

git-svn-id: http://skia.googlecode.com/svn/trunk@1324 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2011-05-15 04:08:24 +00:00
parent a8fd79d407
commit 04863fa14a
4 changed files with 300 additions and 60 deletions

View File

@ -96,19 +96,54 @@ public:
GEN_ID_INC;
}
/** Returns true if the path is flagged as being convex. This is not a
confirmed by any analysis, it is just the value set earlier.
*/
bool isConvex() const { return fIsConvex != 0; }
enum Convexity {
kUnknown_Convexity,
kConvex_Convexity,
kConcave_Convexity
};
/** Set the isConvex flag to true or false. Convex paths may draw faster if
this flag is set, though setting this to true on a path that is in fact
not convex can give undefined results when drawn. Paths default to
isConvex == false
/**
* Return the path's convexity, as stored in the path.
*/
Convexity getConvexity() const { return (Convexity)fConvexity; }
/**
* Store a convexity setting in the path. There is no automatic check to
* see if this value actually agress with the return value from
* ComputeConvexity().
*/
void setConvexity(Convexity);
/**
* Compute the convexity of the specified path. This does not look at the
* value stored in the path, but computes it directly from the path's data.
*
* If there is more than one contour, this returns kConcave_Convexity.
* If the contour is degenerate (i.e. all segements are colinear,
* then this returns kUnknown_Convexity.
* The contour is treated as if it were closed, even if there is no kClose
* verb.
*/
static Convexity ComputeConvexity(const SkPath&);
/**
* DEPRECATED: use getConvexity()
* Returns true if the path is flagged as being convex. This is not a
* confirmed by any analysis, it is just the value set earlier.
*/
bool isConvex() const {
return kConvex_Convexity == this->getConvexity();
}
/**
* DEPRECATED: use setConvexity()
* Set the isConvex flag to true or false. Convex paths may draw faster if
* this flag is set, though setting this to true on a path that is in fact
* not convex can give undefined results when drawn. Paths default to
* isConvex == false
*/
void setIsConvex(bool isConvex) {
fIsConvex = (isConvex != 0);
GEN_ID_INC;
this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
}
/** Clear any lines and curves from the path, making it empty. This frees up
@ -600,7 +635,7 @@ private:
mutable SkRect fBounds;
mutable uint8_t fBoundsIsDirty;
uint8_t fFillType;
uint8_t fIsConvex;
uint8_t fConvexity;
#ifdef ANDROID
uint32_t fGenerationID;
#endif

View File

@ -1,32 +1,60 @@
#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkBlurMaskFilter.h"
#include "SkCamera.h"
#include "SkColorFilter.h"
#include "SkColorPriv.h"
#include "SkDevice.h"
#include "SkGradientShader.h"
#include "SkImageDecoder.h"
#include "SkInterpolator.h"
#include "SkMaskFilter.h"
#include "SkPath.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkShaderExtras.h"
#include "SkTime.h"
#include "SkTypeface.h"
#include "SkUtils.h"
#include "SkKey.h"
#include "SkXfermode.h"
#include "SkDrawFilter.h"
#include "test.h"
namespace skiatest {
class MyReporter : public Reporter {
protected:
virtual void onStart(Test* test) {}
virtual void onReport(const char desc[], Reporter::Result result) {
SkASSERT(Reporter::kPassed == result);
}
virtual void onEnd(Test* test) {}
};
class Iter {
public:
Iter(Reporter* r) : fReporter(r) {
r->ref();
fReg = TestRegistry::Head();
}
~Iter() {
fReporter->unref();
}
Test* next() {
if (fReg) {
TestRegistry::Factory fact = fReg->factory();
fReg = fReg->next();
Test* test = fact(NULL);
test->setReporter(fReporter);
return test;
}
return NULL;
}
static int Count() {
const TestRegistry* reg = TestRegistry::Head();
int count = 0;
while (reg) {
count += 1;
reg = reg->next();
}
return count;
}
private:
Reporter* fReporter;
const TestRegistry* fReg;
};
}
class TestsView : public SkView {
public:
skia::Test::Iter fIter;
TestsView() {}
protected:
@ -46,29 +74,14 @@ protected:
virtual void onDraw(SkCanvas* canvas) {
this->drawBG(canvas);
skia::Test* test = fIter.next();
if (NULL == test) {
fIter.reset();
test = fIter.next();
skiatest::MyReporter reporter;
skiatest::Iter iter(&reporter);
skiatest::Test* test;
while ((test = iter.next()) != NULL) {
test->run();
SkDELETE(test);
}
SkIPoint size;
test->getSize(&size);
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.fX, size.fY);
bitmap.allocPixels();
bitmap.eraseColor(0);
SkCanvas c(bitmap);
test->draw(&c);
canvas->drawBitmap(bitmap, SkIntToScalar(10), SkIntToScalar(10), NULL);
SkString str;
test->getString(skia::Test::kTitle, &str);
SkDebugf("--- %s\n", str.c_str());
delete test;
}
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {

View File

@ -97,7 +97,7 @@ static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
////////////////////////////////////////////////////////////////////////////
SkPath::SkPath() : fBoundsIsDirty(true), fFillType(kWinding_FillType) {
fIsConvex = false; // really should be kUnknown
fConvexity = kUnknown_Convexity;
#ifdef ANDROID
fGenerationID = 0;
#endif
@ -125,7 +125,7 @@ SkPath& SkPath::operator=(const SkPath& src) {
fVerbs = src.fVerbs;
fFillType = src.fFillType;
fBoundsIsDirty = src.fBoundsIsDirty;
fIsConvex = src.fIsConvex;
fConvexity = src.fConvexity;
GEN_ID_INC;
}
SkDEBUGCODE(this->validate();)
@ -148,7 +148,7 @@ void SkPath::swap(SkPath& other) {
fVerbs.swap(other.fVerbs);
SkTSwap<uint8_t>(fFillType, other.fFillType);
SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
SkTSwap<uint8_t>(fIsConvex, other.fIsConvex);
SkTSwap<uint8_t>(fConvexity, other.fConvexity);
GEN_ID_INC;
}
}
@ -166,7 +166,7 @@ void SkPath::reset() {
fVerbs.reset();
GEN_ID_INC;
fBoundsIsDirty = true;
fIsConvex = false; // really should be kUnknown
fConvexity = kUnknown_Convexity;
}
void SkPath::rewind() {
@ -176,7 +176,7 @@ void SkPath::rewind() {
fVerbs.rewind();
GEN_ID_INC;
fBoundsIsDirty = true;
fIsConvex = false; // really should be kUnknown
fConvexity = kUnknown_Convexity;
}
bool SkPath::isEmpty() const {
@ -244,6 +244,13 @@ void SkPath::computeBounds() const {
compute_pt_bounds(&fBounds, fPts);
}
void SkPath::setConvexity(Convexity c) {
if (fConvexity != c) {
fConvexity = c;
GEN_ID_INC;
}
}
//////////////////////////////////////////////////////////////////////////////
// Construction methods
@ -1382,3 +1389,125 @@ void SkPath::validate() const {
}
#endif
///////////////////////////////////////////////////////////////////////////////
/**
* Returns -1 || 0 || 1 depending on the sign of value:
* -1 if value < 0
* 0 if vlaue == 0
* 1 if value > 0
*/
static int SkScalarSign(SkScalar value) {
return value < 0 ? -1 : (value > 0);
}
static int CrossProductSign(const SkVector& a, const SkVector& b) {
return SkScalarSign(SkPoint::CrossProduct(a, b));
}
// only valid for a single contour
struct Convexicator {
Convexicator() : fPtCount(0), fConvexity(SkPath::kUnknown_Convexity) {
fSign = 0;
// warnings
fCurrPt.set(0, 0);
fVec0.set(0, 0);
fVec1.set(0, 0);
fFirstVec.set(0, 0);
}
SkPath::Convexity getConvexity() const { return fConvexity; }
void addPt(const SkPoint& pt) {
if (SkPath::kConcave_Convexity == fConvexity) {
return;
}
if (0 == fPtCount) {
fCurrPt = pt;
++fPtCount;
} else {
SkVector vec = pt - fCurrPt;
if (vec.fX || vec.fY) {
fCurrPt = pt;
if (++fPtCount == 2) {
fFirstVec = fVec1 = vec;
} else {
SkASSERT(fPtCount > 2);
this->addVec(vec);
}
}
}
}
void close() {
if (fPtCount > 2) {
this->addVec(fFirstVec);
}
}
private:
void addVec(const SkVector& vec) {
SkASSERT(vec.fX || vec.fY);
fVec0 = fVec1;
fVec1 = vec;
int sign = CrossProductSign(fVec0, fVec1);
if (0 == fSign) {
fSign = sign;
} else if (sign) {
if (fSign == sign) {
fConvexity = SkPath::kConvex_Convexity;
} else {
fConvexity = SkPath::kConcave_Convexity;
}
}
}
SkPoint fCurrPt;
SkVector fVec0, fVec1, fFirstVec;
int fPtCount; // non-degenerate points
int fSign;
SkPath::Convexity fConvexity;
};
SkPath::Convexity SkPath::ComputeConvexity(const SkPath& path) {
SkPoint pts[4];
SkPath::Verb verb;
SkPath::Iter iter(path, true);
int contourCount = 0;
int count;
Convexicator state;
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case kMove_Verb:
if (++contourCount > 1) {
return kConcave_Convexity;
}
pts[1] = pts[0];
count = 1;
break;
case kLine_Verb: count = 1; break;
case kQuad_Verb: count = 2; break;
case kCubic_Verb: count = 3; break;
case kClose_Verb:
state.close();
count = 0;
break;
default:
SkASSERT(!"bad verb");
return kConcave_Convexity;
}
for (int i = 1; i <= count; i++) {
state.addPt(pts[i]);
}
// early exit
if (kConcave_Convexity == state.getConvexity()) {
return kConcave_Convexity;
}
}
return state.getConvexity();
}

View File

@ -1,5 +1,6 @@
#include "Test.h"
#include "SkPath.h"
#include "SkParse.h"
#include "SkSize.h"
static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
@ -17,7 +18,67 @@ static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
REPORTER_ASSERT(reporter, other.getBounds() == bounds);
}
static void TestPath(skiatest::Reporter* reporter) {
static void setFromString(SkPath* path, const char str[]) {
bool first = true;
while (str) {
SkScalar x, y;
str = SkParse::FindScalar(str, &x);
if (NULL == str) {
break;
}
str = SkParse::FindScalar(str, &y);
SkASSERT(str);
if (first) {
path->moveTo(x, y);
first = false;
} else {
path->lineTo(x, y);
}
}
}
static void test_convexity(skiatest::Reporter* reporter) {
static const SkPath::Convexity U = SkPath::kUnknown_Convexity;
static const SkPath::Convexity C = SkPath::kConcave_Convexity;
static const SkPath::Convexity V = SkPath::kConvex_Convexity;
SkPath path;
REPORTER_ASSERT(reporter, U == SkPath::ComputeConvexity(path));
path.addCircle(0, 0, 10);
REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
path.addCircle(0, 0, 10); // 2nd circle
REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
path.reset();
path.addRect(0, 0, 10, 10, SkPath::kCCW_Direction);
REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
path.reset();
path.addRect(0, 0, 10, 10, SkPath::kCW_Direction);
REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
static const struct {
const char* fPathStr;
SkPath::Convexity fExpectedConvexity;
} gRec[] = {
{ "0 0", SkPath::kUnknown_Convexity },
{ "0 0 10 10", SkPath::kUnknown_Convexity },
{ "0 0 10 10 20 20 0 0 10 10", SkPath::kUnknown_Convexity },
{ "0 0 10 10 10 20", SkPath::kConvex_Convexity },
{ "0 0 10 10 10 0", SkPath::kConvex_Convexity },
{ "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
{ "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
};
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
SkPath path;
setFromString(&path, gRec[i].fPathStr);
SkPath::Convexity c = SkPath::ComputeConvexity(path);
REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
}
}
void TestPath(skiatest::Reporter* reporter);
void TestPath(skiatest::Reporter* reporter) {
{
SkSize size;
size.fWidth = 3.4f;
@ -100,6 +161,8 @@ static void TestPath(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, p.isConvex());
p.rewind();
REPORTER_ASSERT(reporter, !p.isConvex());
test_convexity(reporter);
}
#include "TestClassDef.h"