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:
parent
a8fd79d407
commit
04863fa14a
@ -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
|
||||
|
@ -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:
|
||||
@ -45,30 +73,15 @@ protected:
|
||||
|
||||
virtual void onDraw(SkCanvas* canvas) {
|
||||
this->drawBG(canvas);
|
||||
|
||||
skiatest::MyReporter reporter;
|
||||
skiatest::Iter iter(&reporter);
|
||||
skiatest::Test* test;
|
||||
|
||||
skia::Test* test = fIter.next();
|
||||
if (NULL == test) {
|
||||
fIter.reset();
|
||||
test = fIter.next();
|
||||
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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user