From 812515b623be9179e67d97b84b10df587244d38c Mon Sep 17 00:00:00 2001 From: Tamito Kajiyama Date: Wed, 4 Jun 2014 14:51:39 +0900 Subject: Freestyle: Fix for a potential infinite loop in stroke resampling by vertex count. Changes were made in Stroke::Resample(int) in C++ to prevent a potential infinite loop caused by an inconsistency between Stroke::_Length and the stroke length computed based on stroke vertices. Such a stroke length inconsistency is usually caused by missing calls of Stroke::UpdateLength() (i.e., API implementation bugs), but also may occur due to scripting errors in user-defined style modules. This commit is meant to help script writters to identify the latter error cases. Now Stroke.resample(int) may raise a runtime error to signal an error condition. --- .../intern/python/Interface1D/BPy_Stroke.cpp | 10 ++++++-- source/blender/freestyle/intern/stroke/Stroke.cpp | 30 ++++++++++++++-------- source/blender/freestyle/intern/stroke/Stroke.h | 4 +-- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp index 7110eef9036..aa8c90cfd24 100644 --- a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp @@ -151,10 +151,16 @@ static PyObject *Stroke_resample(BPy_Stroke *self, PyObject *args, PyObject *kwd float f; if (PyArg_ParseTupleAndKeywords(args, kwds, "i", (char **)kwlist_1, &i)) { - self->s->Resample(i); + if (self->s->Resample(i) < 0) { + PyErr_SetString(PyExc_RuntimeError, "Stroke resampling (by vertex count) failed"); + return NULL; + } } else if (PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f)) { - self->s->Resample(f); + if (self->s->Resample(f) < 0) { + PyErr_SetString(PyExc_RuntimeError, "Stroke resampling (by vertex interval) failed"); + return NULL; + } } else { PyErr_SetString(PyExc_TypeError, "invalid argument"); diff --git a/source/blender/freestyle/intern/stroke/Stroke.cpp b/source/blender/freestyle/intern/stroke/Stroke.cpp index 6629de0fa04..c85295e72bf 100644 --- a/source/blender/freestyle/intern/stroke/Stroke.cpp +++ b/source/blender/freestyle/intern/stroke/Stroke.cpp @@ -508,11 +508,11 @@ public: } }; -void Stroke::Resample(int iNPoints) +int Stroke::Resample(int iNPoints) { - int vertsize = strokeVerticesSize(); - if (iNPoints <= vertsize) - return; + int NPointsToAdd = iNPoints - strokeVerticesSize(); + if (NPointsToAdd <= 0) + return 0; StrokeInternal::StrokeVertexIterator it = strokeVerticesBegin(); StrokeInternal::StrokeVertexIterator next = it; @@ -531,7 +531,7 @@ void Stroke::Resample(int iNPoints) Vec2r b((next)->getPoint()); Vec2r vec_tmp(b - a); real norm_var = vec_tmp.norm(); - int numberOfPointsToAdd = (int)floor((iNPoints - strokeVerticesSize()) * norm_var / _Length); + int numberOfPointsToAdd = (int)floor(NPointsToAdd * norm_var / _Length); float csampling = norm_var / (float)(numberOfPointsToAdd + 1); strokeSegments.push_back(StrokeSegment(it, next, norm_var, numberOfPointsToAdd, csampling)); N += numberOfPointsToAdd; @@ -543,9 +543,10 @@ void Stroke::Resample(int iNPoints) meanlength /= (float)nsegments; // if we don't have enough points let's resample finer some segments - int NPointsToAdd = iNPoints - vertsize; bool checkEveryone = false; + bool resampled; while (N < NPointsToAdd) { + resampled = false; for (vector::iterator s = strokeSegments.begin(), send = strokeSegments.end(); s != send; ++s) { if (s->_sampling == 0.0f) continue; @@ -556,14 +557,20 @@ void Stroke::Resample(int iNPoints) //resample s->_n = s->_n + 1; s->_sampling = s->_length / (float)(s->_n + 1); - s->_resampled = true; + s->_resampled = resampled = true; N++; if (N == NPointsToAdd) break; } } + if (checkEveryone && !resampled) + break; checkEveryone = true; } + if (N < NPointsToAdd) { + // fatal error, likely because _Length is inconsistent with the stroke length computed with the vertices + return -1; + } //actually resample: for (vector::iterator s = strokeSegments.begin(), send = strokeSegments.end(); s != send; ++s) { newVertices.push_back(&(*(s->_begin))); @@ -598,15 +605,17 @@ void Stroke::Resample(int iNPoints) delete _rep; _rep = new StrokeRep(this); } + + return 0; } -void Stroke::Resample(float iSampling) +int Stroke::Resample(float iSampling) { //cerr << "old size :" << strokeVerticesSize() << endl; if (iSampling == 0) - return; + return 0; if (iSampling >= _sampling) - return; + return 0; _sampling = iSampling; // Resample... @@ -655,6 +664,7 @@ void Stroke::Resample(float iSampling) delete _rep; _rep = new StrokeRep(this); } + return 0; } void Stroke::RemoveAllVertices() diff --git a/source/blender/freestyle/intern/stroke/Stroke.h b/source/blender/freestyle/intern/stroke/Stroke.h index 5e6c2fcf6cb..abbbcc32750 100644 --- a/source/blender/freestyle/intern/stroke/Stroke.h +++ b/source/blender/freestyle/intern/stroke/Stroke.h @@ -588,7 +588,7 @@ public: * \param iNPoints * The number of vertices we eventually want in our stroke. */ - void Resample(int iNPoints); + int Resample(int iNPoints); /*! Resampling method. * Resamples the curve with a given sampling. @@ -596,7 +596,7 @@ public: * \param iSampling * The new sampling value. */ - void Resample(float iSampling); + int Resample(float iSampling); /*! Removes all vertices from the Stroke. */ -- cgit v1.2.3