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;
|
GEN_ID_INC;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if the path is flagged as being convex. This is not a
|
enum Convexity {
|
||||||
confirmed by any analysis, it is just the value set earlier.
|
kUnknown_Convexity,
|
||||||
*/
|
kConvex_Convexity,
|
||||||
bool isConvex() const { return fIsConvex != 0; }
|
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
|
* Return the path's convexity, as stored in the path.
|
||||||
not convex can give undefined results when drawn. Paths default to
|
*/
|
||||||
isConvex == false
|
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) {
|
void setIsConvex(bool isConvex) {
|
||||||
fIsConvex = (isConvex != 0);
|
this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
|
||||||
GEN_ID_INC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clear any lines and curves from the path, making it empty. This frees up
|
/** Clear any lines and curves from the path, making it empty. This frees up
|
||||||
@ -600,7 +635,7 @@ private:
|
|||||||
mutable SkRect fBounds;
|
mutable SkRect fBounds;
|
||||||
mutable uint8_t fBoundsIsDirty;
|
mutable uint8_t fBoundsIsDirty;
|
||||||
uint8_t fFillType;
|
uint8_t fFillType;
|
||||||
uint8_t fIsConvex;
|
uint8_t fConvexity;
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
uint32_t fGenerationID;
|
uint32_t fGenerationID;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,32 +1,60 @@
|
|||||||
#include "SampleCode.h"
|
#include "SampleCode.h"
|
||||||
#include "SkView.h"
|
#include "SkView.h"
|
||||||
#include "SkCanvas.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"
|
#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 {
|
class TestsView : public SkView {
|
||||||
public:
|
public:
|
||||||
skia::Test::Iter fIter;
|
|
||||||
|
|
||||||
TestsView() {}
|
TestsView() {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -46,29 +74,14 @@ protected:
|
|||||||
virtual void onDraw(SkCanvas* canvas) {
|
virtual void onDraw(SkCanvas* canvas) {
|
||||||
this->drawBG(canvas);
|
this->drawBG(canvas);
|
||||||
|
|
||||||
skia::Test* test = fIter.next();
|
skiatest::MyReporter reporter;
|
||||||
if (NULL == test) {
|
skiatest::Iter iter(&reporter);
|
||||||
fIter.reset();
|
skiatest::Test* test;
|
||||||
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) {
|
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) {
|
SkPath::SkPath() : fBoundsIsDirty(true), fFillType(kWinding_FillType) {
|
||||||
fIsConvex = false; // really should be kUnknown
|
fConvexity = kUnknown_Convexity;
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
fGenerationID = 0;
|
fGenerationID = 0;
|
||||||
#endif
|
#endif
|
||||||
@ -125,7 +125,7 @@ SkPath& SkPath::operator=(const SkPath& src) {
|
|||||||
fVerbs = src.fVerbs;
|
fVerbs = src.fVerbs;
|
||||||
fFillType = src.fFillType;
|
fFillType = src.fFillType;
|
||||||
fBoundsIsDirty = src.fBoundsIsDirty;
|
fBoundsIsDirty = src.fBoundsIsDirty;
|
||||||
fIsConvex = src.fIsConvex;
|
fConvexity = src.fConvexity;
|
||||||
GEN_ID_INC;
|
GEN_ID_INC;
|
||||||
}
|
}
|
||||||
SkDEBUGCODE(this->validate();)
|
SkDEBUGCODE(this->validate();)
|
||||||
@ -148,7 +148,7 @@ void SkPath::swap(SkPath& other) {
|
|||||||
fVerbs.swap(other.fVerbs);
|
fVerbs.swap(other.fVerbs);
|
||||||
SkTSwap<uint8_t>(fFillType, other.fFillType);
|
SkTSwap<uint8_t>(fFillType, other.fFillType);
|
||||||
SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
|
SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
|
||||||
SkTSwap<uint8_t>(fIsConvex, other.fIsConvex);
|
SkTSwap<uint8_t>(fConvexity, other.fConvexity);
|
||||||
GEN_ID_INC;
|
GEN_ID_INC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ void SkPath::reset() {
|
|||||||
fVerbs.reset();
|
fVerbs.reset();
|
||||||
GEN_ID_INC;
|
GEN_ID_INC;
|
||||||
fBoundsIsDirty = true;
|
fBoundsIsDirty = true;
|
||||||
fIsConvex = false; // really should be kUnknown
|
fConvexity = kUnknown_Convexity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkPath::rewind() {
|
void SkPath::rewind() {
|
||||||
@ -176,7 +176,7 @@ void SkPath::rewind() {
|
|||||||
fVerbs.rewind();
|
fVerbs.rewind();
|
||||||
GEN_ID_INC;
|
GEN_ID_INC;
|
||||||
fBoundsIsDirty = true;
|
fBoundsIsDirty = true;
|
||||||
fIsConvex = false; // really should be kUnknown
|
fConvexity = kUnknown_Convexity;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkPath::isEmpty() const {
|
bool SkPath::isEmpty() const {
|
||||||
@ -244,6 +244,13 @@ void SkPath::computeBounds() const {
|
|||||||
compute_pt_bounds(&fBounds, fPts);
|
compute_pt_bounds(&fBounds, fPts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkPath::setConvexity(Convexity c) {
|
||||||
|
if (fConvexity != c) {
|
||||||
|
fConvexity = c;
|
||||||
|
GEN_ID_INC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// Construction methods
|
// Construction methods
|
||||||
|
|
||||||
@ -1382,3 +1389,125 @@ void SkPath::validate() const {
|
|||||||
}
|
}
|
||||||
#endif
|
#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 "Test.h"
|
||||||
#include "SkPath.h"
|
#include "SkPath.h"
|
||||||
|
#include "SkParse.h"
|
||||||
#include "SkSize.h"
|
#include "SkSize.h"
|
||||||
|
|
||||||
static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
|
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);
|
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;
|
SkSize size;
|
||||||
size.fWidth = 3.4f;
|
size.fWidth = 3.4f;
|
||||||
@ -100,6 +161,8 @@ static void TestPath(skiatest::Reporter* reporter) {
|
|||||||
REPORTER_ASSERT(reporter, p.isConvex());
|
REPORTER_ASSERT(reporter, p.isConvex());
|
||||||
p.rewind();
|
p.rewind();
|
||||||
REPORTER_ASSERT(reporter, !p.isConvex());
|
REPORTER_ASSERT(reporter, !p.isConvex());
|
||||||
|
|
||||||
|
test_convexity(reporter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "TestClassDef.h"
|
#include "TestClassDef.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user