egl/sfopenvg/riPath.cpp
branchEGL_MERGE
changeset 180 f767bd5f4cfc
parent 119 5f371025658c
child 181 c1509651cd2b
equal deleted inserted replaced
119:5f371025658c 180:f767bd5f4cfc
     1 /*------------------------------------------------------------------------
       
     2  *
       
     3  * OpenVG 1.1 Reference Implementation
       
     4  * -----------------------------------
       
     5  *
       
     6  * Copyright (c) 2007 The Khronos Group Inc.
       
     7  *
       
     8  * Permission is hereby granted, free of charge, to any person obtaining a
       
     9  * copy of this software and /or associated documentation files
       
    10  * (the "Materials "), to deal in the Materials without restriction,
       
    11  * including without limitation the rights to use, copy, modify, merge,
       
    12  * publish, distribute, sublicense, and/or sell copies of the Materials,
       
    13  * and to permit persons to whom the Materials are furnished to do so,
       
    14  * subject to the following conditions: 
       
    15  *
       
    16  * The above copyright notice and this permission notice shall be included 
       
    17  * in all copies or substantial portions of the Materials. 
       
    18  *
       
    19  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       
    20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       
    21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
       
    22  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
       
    23  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
       
    24  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR
       
    25  * THE USE OR OTHER DEALINGS IN THE MATERIALS.
       
    26  *
       
    27  *//**
       
    28  * \file
       
    29  * \brief	Implementation of Path functions.
       
    30  * \note	
       
    31  *//*-------------------------------------------------------------------*/
       
    32 
       
    33 #include "riPath.h"
       
    34 
       
    35 using namespace OpenVGRI;
       
    36 //==============================================================================================
       
    37 
       
    38 
       
    39 //==============================================================================================
       
    40 
       
    41 namespace OpenVGRI
       
    42 {
       
    43 
       
    44 RIfloat inputFloat(VGfloat f);	//defined in riApi.cpp
       
    45 
       
    46 /*-------------------------------------------------------------------*//*!
       
    47 * \brief	Form a reliable normalized average of the two unit input vectors.
       
    48 *           The average always lies to the given direction from the first
       
    49 *			vector.
       
    50 * \param	u0, u1 Unit input vectors.
       
    51 * \param	cw True if the average should be clockwise from u0, false if
       
    52 *              counterclockwise.
       
    53 * \return	Average of the two input vectors.
       
    54 * \note		
       
    55 *//*-------------------------------------------------------------------*/
       
    56 
       
    57 static const Vector2 unitAverage(const Vector2& u0, const Vector2& u1, bool cw)
       
    58 {
       
    59 	Vector2 u = 0.5f * (u0 + u1);
       
    60 	Vector2 n0 = perpendicularCCW(u0);
       
    61 
       
    62 	if( dot(u, u) > 0.25f )
       
    63 	{	//the average is long enough and thus reliable
       
    64 		if( dot(n0, u1) < 0.0f )
       
    65 			u = -u;	//choose the larger angle
       
    66 	}
       
    67 	else
       
    68 	{	// the average is too short, use the average of the normals to the vectors instead
       
    69 		Vector2 n1 = perpendicularCW(u1);
       
    70 		u = 0.5f * (n0 + n1);
       
    71 	}
       
    72 	if( cw )
       
    73 		u = -u;
       
    74 
       
    75 	return normalize(u);
       
    76 }
       
    77 
       
    78 /*-------------------------------------------------------------------*//*!
       
    79 * \brief	Form a reliable normalized average of the two unit input vectors.
       
    80 *			The average lies on the side where the angle between the input
       
    81 *			vectors is less than 180 degrees.
       
    82 * \param	u0, u1 Unit input vectors.
       
    83 * \return	Average of the two input vectors.
       
    84 * \note		
       
    85 *//*-------------------------------------------------------------------*/
       
    86 
       
    87 static const Vector2 unitAverage(const Vector2& u0, const Vector2& u1)
       
    88 {
       
    89 	Vector2 u = 0.5f * (u0 + u1);
       
    90 
       
    91 	if( dot(u, u) < 0.25f )
       
    92 	{	// the average is unreliable, use the average of the normals to the vectors instead
       
    93 		Vector2 n0 = perpendicularCCW(u0);
       
    94 		Vector2 n1 = perpendicularCW(u1);
       
    95 		u = 0.5f * (n0 + n1);
       
    96 		if( dot(n1, u0) < 0.0f )
       
    97 			u = -u;
       
    98 	}
       
    99 
       
   100 	return normalize(u);
       
   101 }
       
   102 
       
   103 /*-------------------------------------------------------------------*//*!
       
   104 * \brief	Interpolate the given unit tangent vectors to the given
       
   105 *			direction on a unit circle.
       
   106 * \param	
       
   107 * \return	
       
   108 * \note		
       
   109 *//*-------------------------------------------------------------------*/
       
   110 
       
   111 static const Vector2 circularLerp(const Vector2& t0, const Vector2& t1, RIfloat ratio, bool cw)
       
   112 {
       
   113 	Vector2 u0 = t0, u1 = t1;
       
   114 	RIfloat l0 = 0.0f, l1 = 1.0f;
       
   115 	for(int i=0;i<18;i++)
       
   116 	{
       
   117 		Vector2 n = unitAverage(u0, u1, cw);
       
   118 		RIfloat l = 0.5f * (l0 + l1);
       
   119 		if( ratio < l )
       
   120 		{
       
   121 			u1 = n;
       
   122 			l1 = l;
       
   123 		}
       
   124 		else
       
   125 		{
       
   126 			u0 = n;
       
   127 			l0 = l;
       
   128 		}
       
   129 	}
       
   130 	return u0;
       
   131 }
       
   132 
       
   133 /*-------------------------------------------------------------------*//*!
       
   134 * \brief	Interpolate the given unit tangent vectors on a unit circle.
       
   135 *			Smaller angle between the vectors is used.
       
   136 * \param	
       
   137 * \return	
       
   138 * \note		
       
   139 *//*-------------------------------------------------------------------*/
       
   140 
       
   141 static const Vector2 circularLerp(const Vector2& t0, const Vector2& t1, RIfloat ratio)
       
   142 {
       
   143 	Vector2 u0 = t0, u1 = t1;
       
   144 	RIfloat l0 = 0.0f, l1 = 1.0f;
       
   145 	for(int i=0;i<18;i++)
       
   146 	{
       
   147 		Vector2 n = unitAverage(u0, u1);
       
   148 		RIfloat l = 0.5f * (l0 + l1);
       
   149 		if( ratio < l )
       
   150 		{
       
   151 			u1 = n;
       
   152 			l1 = l;
       
   153 		}
       
   154 		else
       
   155 		{
       
   156 			u0 = n;
       
   157 			l0 = l;
       
   158 		}
       
   159 	}
       
   160 	return u0;
       
   161 }
       
   162 
       
   163 /*-------------------------------------------------------------------*//*!
       
   164 * \brief	Path constructor.
       
   165 * \param	
       
   166 * \return	
       
   167 * \note		
       
   168 *//*-------------------------------------------------------------------*/
       
   169 
       
   170 Path::Path(VGint format, VGPathDatatype datatype, RIfloat scale, RIfloat bias, int segmentCapacityHint, int coordCapacityHint, VGbitfield caps) :
       
   171 	m_format(format),
       
   172 	m_datatype(datatype),
       
   173 	m_scale(scale),
       
   174 	m_bias(bias),
       
   175 	m_capabilities(caps),
       
   176 	m_referenceCount(0),
       
   177 	m_segments(),
       
   178 	m_data(),
       
   179 	m_vertices(),
       
   180 	m_segmentToVertex(),
       
   181 	m_userMinx(0.0f),
       
   182 	m_userMiny(0.0f),
       
   183 	m_userMaxx(0.0f),
       
   184 	m_userMaxy(0.0f)
       
   185 {
       
   186 	RI_ASSERT(format == VG_PATH_FORMAT_STANDARD);
       
   187 	RI_ASSERT(datatype >= VG_PATH_DATATYPE_S_8 && datatype <= VG_PATH_DATATYPE_F);
       
   188 	if(segmentCapacityHint > 0)
       
   189 		m_segments.reserve(RI_INT_MIN(segmentCapacityHint, 65536));
       
   190 	if(coordCapacityHint > 0)
       
   191 		m_data.reserve(RI_INT_MIN(coordCapacityHint, 65536) * getBytesPerCoordinate(datatype));
       
   192 }
       
   193 
       
   194 /*-------------------------------------------------------------------*//*!
       
   195 * \brief	Path destructor.
       
   196 * \param	
       
   197 * \return	
       
   198 * \note		
       
   199 *//*-------------------------------------------------------------------*/
       
   200 
       
   201 Path::~Path()
       
   202 {
       
   203 	RI_ASSERT(m_referenceCount == 0);
       
   204 }
       
   205 
       
   206 /*-------------------------------------------------------------------*//*!
       
   207 * \brief	Reads a coordinate and applies scale and bias.
       
   208 * \param	
       
   209 * \return	
       
   210 *//*-------------------------------------------------------------------*/
       
   211 
       
   212 RIfloat Path::getCoordinate(int i) const
       
   213 {
       
   214 	RI_ASSERT(i >= 0 && i < m_data.size() / getBytesPerCoordinate(m_datatype));
       
   215 	RI_ASSERT(m_scale != 0.0f);
       
   216 
       
   217 	const RIuint8* ptr = &m_data[0];
       
   218 	switch(m_datatype)
       
   219 	{
       
   220 	case VG_PATH_DATATYPE_S_8:
       
   221 		return (RIfloat)(((const RIint8*)ptr)[i]) * m_scale + m_bias;
       
   222 
       
   223 	case VG_PATH_DATATYPE_S_16:
       
   224 		return (RIfloat)(((const RIint16*)ptr)[i]) * m_scale + m_bias;
       
   225 
       
   226 	case VG_PATH_DATATYPE_S_32:
       
   227 		return (RIfloat)(((const RIint32*)ptr)[i]) * m_scale + m_bias;
       
   228 
       
   229 	default:
       
   230 		RI_ASSERT(m_datatype == VG_PATH_DATATYPE_F);
       
   231 		return (RIfloat)(((const RIfloat32*)ptr)[i]) * m_scale + m_bias;
       
   232 	}
       
   233 }
       
   234 
       
   235 /*-------------------------------------------------------------------*//*!
       
   236 * \brief	Writes a coordinate, subtracting bias and dividing out scale.
       
   237 * \param	
       
   238 * \return	
       
   239 * \note		If the coordinates do not fit into path datatype range, they
       
   240 *			will overflow silently.
       
   241 *//*-------------------------------------------------------------------*/
       
   242 
       
   243 void Path::setCoordinate(Array<RIuint8>& data, VGPathDatatype datatype, RIfloat scale, RIfloat bias, int i, RIfloat c)
       
   244 {
       
   245 	RI_ASSERT(i >= 0 && i < data.size()/getBytesPerCoordinate(datatype));
       
   246 	RI_ASSERT(scale != 0.0f);
       
   247 
       
   248 	c -= bias;
       
   249 	c /= scale;
       
   250 
       
   251 	RIuint8* ptr = &data[0];
       
   252 	switch(datatype)
       
   253 	{
       
   254 	case VG_PATH_DATATYPE_S_8:
       
   255 		((RIint8*)ptr)[i] = (RIint8)floor(c + 0.5f);	//add 0.5 for correct rounding
       
   256 		break;
       
   257 
       
   258 	case VG_PATH_DATATYPE_S_16:
       
   259 		((RIint16*)ptr)[i] = (RIint16)floor(c + 0.5f);	//add 0.5 for correct rounding
       
   260 		break;
       
   261 
       
   262 	case VG_PATH_DATATYPE_S_32:
       
   263 		((RIint32*)ptr)[i] = (RIint32)floor(c + 0.5f);	//add 0.5 for correct rounding
       
   264 		break;
       
   265 
       
   266 	default:
       
   267 		RI_ASSERT(datatype == VG_PATH_DATATYPE_F);
       
   268 		((RIfloat32*)ptr)[i] = (RIfloat32)c;
       
   269 		break;
       
   270 	}
       
   271 }
       
   272 
       
   273 /*-------------------------------------------------------------------*//*!
       
   274 * \brief	Given a datatype, returns the number of bytes per coordinate.
       
   275 * \param	
       
   276 * \return	
       
   277 * \note		
       
   278 *//*-------------------------------------------------------------------*/
       
   279 
       
   280 int Path::getBytesPerCoordinate(VGPathDatatype datatype)
       
   281 {
       
   282 	if(datatype == VG_PATH_DATATYPE_S_8)
       
   283 		return 1;
       
   284 	if(datatype == VG_PATH_DATATYPE_S_16)
       
   285 		return 2;
       
   286 	RI_ASSERT(datatype == VG_PATH_DATATYPE_S_32 || datatype == VG_PATH_DATATYPE_F);
       
   287 	return 4;
       
   288 }
       
   289 
       
   290 /*-------------------------------------------------------------------*//*!
       
   291 * \brief	Given a path segment type, returns the number of coordinates
       
   292 *			it uses.
       
   293 * \param	
       
   294 * \return	
       
   295 * \note		
       
   296 *//*-------------------------------------------------------------------*/
       
   297 
       
   298 int Path::segmentToNumCoordinates(VGPathSegment segment)
       
   299 {
       
   300 	RI_ASSERT(((int)segment >> 1) >= 0 && ((int)segment >> 1) <= 12);
       
   301 	static const int coords[13] = {0,2,2,1,1,4,6,2,4,5,5,5,5};
       
   302 	return coords[(int)segment >> 1];
       
   303 }
       
   304 
       
   305 /*-------------------------------------------------------------------*//*!
       
   306 * \brief	Computes the number of coordinates a segment sequence uses.
       
   307 * \param	
       
   308 * \return	
       
   309 * \note		
       
   310 *//*-------------------------------------------------------------------*/
       
   311 
       
   312 int Path::countNumCoordinates(const RIuint8* segments, int numSegments)
       
   313 {
       
   314 	RI_ASSERT(segments);
       
   315 	RI_ASSERT(numSegments >= 0);
       
   316 
       
   317 	int coordinates = 0;
       
   318 	for(int i=0;i<numSegments;i++)
       
   319 		coordinates += segmentToNumCoordinates(getPathSegment(segments[i]));
       
   320 	return coordinates;
       
   321 }
       
   322 
       
   323 /*-------------------------------------------------------------------*//*!
       
   324 * \brief	Clears path segments and data, and resets capabilities.
       
   325 * \param	
       
   326 * \return	
       
   327 * \note		
       
   328 *//*-------------------------------------------------------------------*/
       
   329 
       
   330 void Path::clear(VGbitfield capabilities)
       
   331 {
       
   332 	m_segments.clear();
       
   333 	m_data.clear();
       
   334 	m_capabilities = capabilities;
       
   335 }
       
   336 
       
   337 /*-------------------------------------------------------------------*//*!
       
   338 * \brief	Appends user segments and data.
       
   339 * \param	
       
   340 * \return	
       
   341 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
   342 *//*-------------------------------------------------------------------*/
       
   343 
       
   344 void Path::appendData(const RIuint8* segments, int numSegments, const RIuint8* data)
       
   345 {
       
   346 	RI_ASSERT(numSegments > 0);
       
   347 	RI_ASSERT(segments && data);
       
   348 	RI_ASSERT(m_referenceCount > 0);
       
   349 
       
   350 	//allocate new arrays
       
   351 	int oldSegmentsSize = m_segments.size();
       
   352 	int newSegmentsSize = oldSegmentsSize + numSegments;
       
   353 	Array<RIuint8> newSegments;
       
   354 	newSegments.resize(newSegmentsSize);	//throws bad_alloc
       
   355 
       
   356 	int newCoords = countNumCoordinates(segments, numSegments);
       
   357 	int bytesPerCoordinate = getBytesPerCoordinate(m_datatype);
       
   358 	int newDataSize = m_data.size() + newCoords * bytesPerCoordinate;
       
   359 	Array<RIuint8> newData;
       
   360 	newData.resize(newDataSize);	//throws bad_alloc
       
   361 	//if we get here, the memory allocations have succeeded
       
   362 
       
   363 	//copy old segments and append new ones
       
   364 	if(m_segments.size())
       
   365 		memcpy(&newSegments[0], &m_segments[0], m_segments.size());
       
   366 	memcpy(&newSegments[0] + m_segments.size(), segments, numSegments);
       
   367 
       
   368 	//copy old data and append new ones
       
   369 	if(newData.size())
       
   370 	{
       
   371 		if(m_data.size())
       
   372 			memcpy(&newData[0], &m_data[0], m_data.size());
       
   373 		if(m_datatype == VG_PATH_DATATYPE_F)
       
   374 		{
       
   375 			RIfloat32* d = (RIfloat32*)(&newData[0] + m_data.size());
       
   376 			const RIfloat32* s = (const RIfloat32*)data;
       
   377 			for(int i=0;i<newCoords;i++)
       
   378 				*d++ = (RIfloat32)inputFloat(*s++);
       
   379 		}
       
   380 		else
       
   381 		{
       
   382 			memcpy(&newData[0] + m_data.size(), data, newCoords * bytesPerCoordinate);
       
   383 		}
       
   384 	}
       
   385 
       
   386 	RI_ASSERT(newData.size() == countNumCoordinates(&newSegments[0],newSegments.size()) * getBytesPerCoordinate(m_datatype));
       
   387 
       
   388 	//replace old arrays
       
   389 	m_segments.swap(newSegments);
       
   390 	m_data.swap(newData);
       
   391 
       
   392 	int c = 0;
       
   393 	for(int i=0;i<m_segments.size();i++)
       
   394 	{
       
   395 		VGPathSegment segment = getPathSegment(m_segments[i]);
       
   396 		int coords = segmentToNumCoordinates(segment);
       
   397 		c += coords;
       
   398 	}
       
   399 }
       
   400 
       
   401 /*-------------------------------------------------------------------*//*!
       
   402 * \brief	Appends a path.
       
   403 * \param	
       
   404 * \return	
       
   405 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
   406 *//*-------------------------------------------------------------------*/
       
   407 
       
   408 void Path::append(const Path* srcPath)
       
   409 {
       
   410 	RI_ASSERT(srcPath);
       
   411 	RI_ASSERT(m_referenceCount > 0 && srcPath->m_referenceCount > 0);
       
   412 
       
   413 	if(srcPath->m_segments.size())
       
   414 	{
       
   415 		//allocate new arrays
       
   416 		int newSegmentsSize = m_segments.size() + srcPath->m_segments.size();
       
   417 		Array<RIuint8> newSegments;
       
   418 		newSegments.resize(newSegmentsSize);	//throws bad_alloc
       
   419 
       
   420 		int newDataSize = m_data.size() + srcPath->getNumCoordinates() * getBytesPerCoordinate(m_datatype);
       
   421 		Array<RIuint8> newData;
       
   422 		newData.resize(newDataSize);	//throws bad_alloc
       
   423 		//if we get here, the memory allocations have succeeded
       
   424 
       
   425 		//copy old segments and append new ones
       
   426 		if(m_segments.size())
       
   427 			memcpy(&newSegments[0], &m_segments[0], m_segments.size());
       
   428 		if(srcPath->m_segments.size())
       
   429 			memcpy(&newSegments[0] + m_segments.size(), &srcPath->m_segments[0], srcPath->m_segments.size());
       
   430 
       
   431 		//copy old data and append new ones
       
   432 		if(m_data.size())
       
   433 			memcpy(&newData[0], &m_data[0], m_data.size());
       
   434 		for(int i=0;i<srcPath->getNumCoordinates();i++)
       
   435 			setCoordinate(newData, m_datatype, m_scale, m_bias, i + getNumCoordinates(), srcPath->getCoordinate(i));
       
   436 
       
   437 		RI_ASSERT(newData.size() == countNumCoordinates(&newSegments[0],newSegments.size()) * getBytesPerCoordinate(m_datatype));
       
   438 
       
   439 		//replace old arrays
       
   440 		m_segments.swap(newSegments);
       
   441 		m_data.swap(newData);
       
   442 	}
       
   443 }
       
   444 
       
   445 /*-------------------------------------------------------------------*//*!
       
   446 * \brief	Modifies existing coordinate data.
       
   447 * \param	
       
   448 * \return	
       
   449 * \note		
       
   450 *//*-------------------------------------------------------------------*/
       
   451 
       
   452 void Path::modifyCoords(int startIndex, int numSegments, const RIuint8* data)
       
   453 {
       
   454 	RI_ASSERT(numSegments > 0);
       
   455 	RI_ASSERT(startIndex >= 0 && startIndex + numSegments <= m_segments.size());
       
   456 	RI_ASSERT(data);
       
   457 	RI_ASSERT(m_referenceCount > 0);
       
   458 
       
   459 	int startCoord = countNumCoordinates(&m_segments[0], startIndex);
       
   460 	int numCoords = countNumCoordinates(&m_segments[startIndex], numSegments);
       
   461 	if(!numCoords)
       
   462 		return;
       
   463 	int bytesPerCoordinate = getBytesPerCoordinate(m_datatype);
       
   464 	RIuint8* dst = &m_data[startCoord * bytesPerCoordinate];
       
   465 	if(m_datatype == VG_PATH_DATATYPE_F)
       
   466 	{
       
   467 		RIfloat32* d = (RIfloat32*)dst;
       
   468 		const RIfloat32* s = (const RIfloat32*)data;
       
   469 		for(int i=0;i<numCoords;i++)
       
   470 			*d++ = (RIfloat32)inputFloat(*s++);
       
   471 	}
       
   472 	else
       
   473 	{
       
   474 		memcpy(dst, data, numCoords*bytesPerCoordinate);
       
   475 	}
       
   476 }
       
   477 
       
   478 /*-------------------------------------------------------------------*//*!
       
   479 * \brief	Appends a transformed copy of the source path.
       
   480 * \param	
       
   481 * \return	
       
   482 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
   483 *//*-------------------------------------------------------------------*/
       
   484 
       
   485 void Path::transform(const Path* srcPath, const Matrix3x3& matrix)
       
   486 {
       
   487 	RI_ASSERT(srcPath);
       
   488 	RI_ASSERT(m_referenceCount > 0 && srcPath->m_referenceCount > 0);
       
   489 	RI_ASSERT(matrix.isAffine());
       
   490 
       
   491 	if(!srcPath->m_segments.size())
       
   492 		return;
       
   493 
       
   494 	//count the number of resulting coordinates
       
   495 	int numSrcCoords = 0;
       
   496 	int numDstCoords = 0;
       
   497 	for(int i=0;i<srcPath->m_segments.size();i++)
       
   498 	{
       
   499 		VGPathSegment segment = getPathSegment(srcPath->m_segments[i]);
       
   500 		int coords = segmentToNumCoordinates(segment);
       
   501 		numSrcCoords += coords;
       
   502 		if(segment == VG_HLINE_TO || segment == VG_VLINE_TO)
       
   503 			coords = 2;	//convert hline and vline to lines
       
   504 		numDstCoords += coords;
       
   505 	}
       
   506 
       
   507 	//allocate new arrays
       
   508 	Array<RIuint8> newSegments;
       
   509 	newSegments.resize(m_segments.size() + srcPath->m_segments.size());	//throws bad_alloc
       
   510 	Array<RIuint8> newData;
       
   511 	newData.resize(m_data.size() + numDstCoords * getBytesPerCoordinate(m_datatype));	//throws bad_alloc
       
   512 	//if we get here, the memory allocations have succeeded
       
   513 
       
   514 	//copy old segments
       
   515 	if(m_segments.size())
       
   516 		memcpy(&newSegments[0], &m_segments[0], m_segments.size());
       
   517 
       
   518 	//copy old data
       
   519 	if(m_data.size())
       
   520 		memcpy(&newData[0], &m_data[0], m_data.size());
       
   521 
       
   522 	int srcCoord = 0;
       
   523 	int dstCoord = getNumCoordinates();
       
   524 	Vector2 s(0,0);		//the beginning of the current subpath
       
   525 	Vector2 o(0,0);		//the last point of the previous segment
       
   526 	for(int i=0;i<srcPath->m_segments.size();i++)
       
   527 	{
       
   528 		VGPathSegment segment = getPathSegment(srcPath->m_segments[i]);
       
   529 		VGPathAbsRel absRel = getPathAbsRel(srcPath->m_segments[i]);
       
   530 		int coords = segmentToNumCoordinates(segment);
       
   531 
       
   532 		switch(segment)
       
   533 		{
       
   534 		case VG_CLOSE_PATH:
       
   535 		{
       
   536 			RI_ASSERT(coords == 0);
       
   537 			o = s;
       
   538 			break;
       
   539 		}
       
   540 
       
   541 		case VG_MOVE_TO:
       
   542 		{
       
   543 			RI_ASSERT(coords == 2);
       
   544 			Vector2 c(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   545             Vector2 tc;
       
   546 
       
   547 			if (absRel == VG_ABSOLUTE)
       
   548                 tc = affineTransform(matrix, c);
       
   549             else
       
   550             {
       
   551                 tc = affineTangentTransform(matrix, c);
       
   552 				c += o;
       
   553             }
       
   554 			
       
   555 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.x);
       
   556 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.y);
       
   557 			s = c;
       
   558 			o = c;
       
   559 			break;
       
   560 		}
       
   561 
       
   562 		case VG_LINE_TO:
       
   563 		{
       
   564 			RI_ASSERT(coords == 2);
       
   565 			Vector2 c(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   566             Vector2 tc;
       
   567 
       
   568 			if (absRel == VG_ABSOLUTE)
       
   569                 tc = affineTransform(matrix, c);
       
   570             else
       
   571             {
       
   572                 tc = affineTangentTransform(matrix, c);
       
   573 				c += o;
       
   574             }
       
   575 
       
   576 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.x);
       
   577 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.y);
       
   578 			o = c;
       
   579 			break;
       
   580 		}
       
   581 
       
   582 		case VG_HLINE_TO:
       
   583 		{
       
   584 			RI_ASSERT(coords == 1);
       
   585 			Vector2 c(srcPath->getCoordinate(srcCoord+0), 0);
       
   586             Vector2 tc;
       
   587 
       
   588 			if (absRel == VG_ABSOLUTE)
       
   589             {
       
   590                 c.y = o.y;
       
   591                 tc = affineTransform(matrix, c);
       
   592             }
       
   593             else
       
   594             {
       
   595                 tc = affineTangentTransform(matrix, c);
       
   596 				c += o;
       
   597             }
       
   598 
       
   599 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.x);
       
   600 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.y);
       
   601 			o = c;
       
   602 			segment = VG_LINE_TO;
       
   603 			break;
       
   604 		}
       
   605 
       
   606 		case VG_VLINE_TO:
       
   607 		{
       
   608 			RI_ASSERT(coords == 1);
       
   609 			Vector2 c(0, srcPath->getCoordinate(srcCoord+0));
       
   610             Vector2 tc;
       
   611 
       
   612 			if (absRel == VG_ABSOLUTE)
       
   613             {
       
   614                 c.x = o.x;
       
   615                 tc = affineTransform(matrix, c);
       
   616             }
       
   617             else
       
   618             {
       
   619                 tc = affineTangentTransform(matrix, c);
       
   620 				c += o;
       
   621             }
       
   622 
       
   623 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.x);
       
   624 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.y);
       
   625 			o = c;
       
   626 			segment = VG_LINE_TO;
       
   627 			break;
       
   628 		}
       
   629 
       
   630 		case VG_QUAD_TO:
       
   631 		{
       
   632 			RI_ASSERT(coords == 4);
       
   633 			Vector2 c0(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   634 			Vector2 c1(srcPath->getCoordinate(srcCoord+2), srcPath->getCoordinate(srcCoord+3));
       
   635             Vector2 tc0, tc1;
       
   636 
       
   637 			if (absRel == VG_ABSOLUTE)
       
   638             {
       
   639                 tc0 = affineTransform(matrix, c0);
       
   640                 tc1 = affineTransform(matrix, c1);
       
   641             }
       
   642             else
       
   643             {
       
   644                 tc0 = affineTangentTransform(matrix, c0);
       
   645                 tc1 = affineTangentTransform(matrix, c1);
       
   646                 c1 += o;
       
   647             }
       
   648 
       
   649 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc0.x);
       
   650 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc0.y);
       
   651 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc1.x);
       
   652 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc1.y);
       
   653 			o = c1;
       
   654 			break;
       
   655 		}
       
   656 
       
   657 		case VG_CUBIC_TO:
       
   658 		{
       
   659 			RI_ASSERT(coords == 6);
       
   660 			Vector2 c0(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   661 			Vector2 c1(srcPath->getCoordinate(srcCoord+2), srcPath->getCoordinate(srcCoord+3));
       
   662 			Vector2 c2(srcPath->getCoordinate(srcCoord+4), srcPath->getCoordinate(srcCoord+5));
       
   663             Vector2 tc0, tc1, tc2;
       
   664 
       
   665 			if (absRel == VG_ABSOLUTE)
       
   666             {
       
   667                 tc0 = affineTransform(matrix, c0);
       
   668                 tc1 = affineTransform(matrix, c1);
       
   669                 tc2 = affineTransform(matrix, c2);
       
   670             }
       
   671             else
       
   672             {
       
   673                 tc0 = affineTangentTransform(matrix, c0);
       
   674                 tc1 = affineTangentTransform(matrix, c1);
       
   675                 tc2 = affineTangentTransform(matrix, c2);
       
   676                 c2 += o;
       
   677             }
       
   678 
       
   679 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc0.x);
       
   680 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc0.y);
       
   681 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc1.x);
       
   682 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc1.y);
       
   683 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc2.x);
       
   684 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc2.y);
       
   685 			o = c2;
       
   686 			break;
       
   687 		}
       
   688 
       
   689 		case VG_SQUAD_TO:
       
   690 		{
       
   691 			RI_ASSERT(coords == 2);
       
   692 			Vector2 c1(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   693             Vector2 tc1;
       
   694 
       
   695 			if (absRel == VG_ABSOLUTE)
       
   696                 tc1 = affineTransform(matrix, c1);
       
   697             else
       
   698             {
       
   699                 tc1 = affineTangentTransform(matrix, c1);
       
   700                 c1 += o;
       
   701             }
       
   702 
       
   703 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc1.x);
       
   704 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc1.y);
       
   705 			o = c1;
       
   706 			break;
       
   707 		}
       
   708 
       
   709 		case VG_SCUBIC_TO:
       
   710 		{
       
   711 			RI_ASSERT(coords == 4);
       
   712 			Vector2 c1(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   713 			Vector2 c2(srcPath->getCoordinate(srcCoord+2), srcPath->getCoordinate(srcCoord+3));
       
   714             Vector2 tc1, tc2;
       
   715 
       
   716 			if (absRel == VG_ABSOLUTE)
       
   717             {
       
   718                 tc1 = affineTransform(matrix, c1);
       
   719                 tc2 = affineTransform(matrix, c2);
       
   720             }
       
   721             else
       
   722             {
       
   723                 tc1 = affineTangentTransform(matrix, c1);
       
   724                 tc2 = affineTangentTransform(matrix, c2);
       
   725                 c2 += o;
       
   726             }
       
   727 
       
   728 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc1.x);
       
   729 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc1.y);
       
   730 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc2.x);
       
   731 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc2.y);
       
   732 			o = c2;
       
   733 			break;
       
   734 		}
       
   735 
       
   736 		default:
       
   737 		{
       
   738 			RI_ASSERT(segment == VG_SCCWARC_TO || segment == VG_SCWARC_TO ||
       
   739 					  segment == VG_LCCWARC_TO || segment == VG_LCWARC_TO);
       
   740 			RI_ASSERT(coords == 5);
       
   741 			RIfloat rh = srcPath->getCoordinate(srcCoord+0);
       
   742 			RIfloat rv = srcPath->getCoordinate(srcCoord+1);
       
   743 			RIfloat rot = srcPath->getCoordinate(srcCoord+2);
       
   744 			Vector2 c(srcPath->getCoordinate(srcCoord+3), srcPath->getCoordinate(srcCoord+4));
       
   745 
       
   746 			rot = RI_DEG_TO_RAD(rot);
       
   747 			Matrix3x3 u((RIfloat)cos(rot)*rh, -(RIfloat)sin(rot)*rv,  0,
       
   748 						(RIfloat)sin(rot)*rh,  (RIfloat)cos(rot)*rv,  0,
       
   749 						0,                   0,                   1);
       
   750 			u = matrix * u;
       
   751 			u[2].set(0,0,1);		//force affinity
       
   752 			//u maps from the unit circle to transformed ellipse
       
   753 
       
   754 			//compute new rh, rv and rot
       
   755 			Vector2	p(u[0][0], u[1][0]);
       
   756 			Vector2	q(u[1][1], -u[0][1]);
       
   757 			bool swapped = false;
       
   758 			if(dot(p,p) < dot(q,q))
       
   759 			{
       
   760 				RI_SWAP(p.x,q.x);
       
   761 				RI_SWAP(p.y,q.y);
       
   762 				swapped = true;
       
   763 			}
       
   764 			Vector2 h = (p+q) * 0.5f;
       
   765 			Vector2 hp = (p-q) * 0.5f;
       
   766 			RIfloat hlen = h.length();
       
   767 			RIfloat hplen = hp.length();
       
   768 			rh = hlen + hplen;
       
   769 			rv = hlen - hplen;
       
   770 			h = hplen * h + hlen * hp;
       
   771 			hlen = dot(h,h);
       
   772 			if(hlen == 0.0f)
       
   773 				rot = 0.0f;
       
   774 			else
       
   775 			{
       
   776 				h.normalize();
       
   777 				rot = (RIfloat)acos(h.x);
       
   778 				if(h.y < 0.0f)
       
   779 					rot = 2.0f*PI - rot;
       
   780 			}
       
   781 			if(swapped)
       
   782 				rot += PI*0.5f;
       
   783 
       
   784             Vector2 tc;
       
   785 			if (absRel == VG_ABSOLUTE)
       
   786                 tc = affineTransform(matrix, c);
       
   787             else
       
   788             {
       
   789                 tc = affineTangentTransform(matrix, c);
       
   790                 c += o;
       
   791             }
       
   792 
       
   793 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, rh);
       
   794 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, rv);
       
   795 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, RI_RAD_TO_DEG(rot));
       
   796 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.x);
       
   797 			setCoordinate(newData, m_datatype, m_scale, m_bias, dstCoord++, tc.y);
       
   798 			o = c;
       
   799 
       
   800             //flip winding if the determinant is negative
       
   801             if (matrix.det() < 0)
       
   802             {
       
   803                 switch (segment)
       
   804                 {
       
   805                 case VG_SCCWARC_TO: segment = VG_SCWARC_TO;     break;
       
   806                 case VG_SCWARC_TO:  segment = VG_SCCWARC_TO;    break;
       
   807                 case VG_LCCWARC_TO: segment = VG_LCWARC_TO;     break;
       
   808                 case VG_LCWARC_TO:  segment = VG_LCCWARC_TO;    break;
       
   809                 default:                                        break;
       
   810                 }
       
   811             }
       
   812             break;
       
   813 		}
       
   814 		}
       
   815 
       
   816 		newSegments[m_segments.size() + i] = (RIuint8)(segment | absRel);
       
   817 		srcCoord += coords;
       
   818 	}
       
   819 	RI_ASSERT(srcCoord == numSrcCoords);
       
   820 	RI_ASSERT(dstCoord == getNumCoordinates() + numDstCoords);
       
   821 
       
   822 	RI_ASSERT(newData.size() == countNumCoordinates(&newSegments[0],newSegments.size()) * getBytesPerCoordinate(m_datatype));
       
   823 
       
   824 	//replace old arrays
       
   825 	m_segments.swap(newSegments);
       
   826 	m_data.swap(newData);
       
   827 }
       
   828 
       
   829 /*-------------------------------------------------------------------*//*!
       
   830 * \brief	Normalizes a path for interpolation.
       
   831 * \param	
       
   832 * \return	
       
   833 * \note		
       
   834 *//*-------------------------------------------------------------------*/
       
   835 
       
   836 void Path::normalizeForInterpolation(const Path* srcPath)
       
   837 {
       
   838 	RI_ASSERT(srcPath);
       
   839 	RI_ASSERT(srcPath != this);
       
   840 	RI_ASSERT(srcPath->m_referenceCount > 0);
       
   841 
       
   842 	//count the number of resulting coordinates
       
   843 	int numSrcCoords = 0;
       
   844 	int numDstCoords = 0;
       
   845 	for(int i=0;i<srcPath->m_segments.size();i++)
       
   846 	{
       
   847 		VGPathSegment segment = getPathSegment(srcPath->m_segments[i]);
       
   848 		int coords = segmentToNumCoordinates(segment);
       
   849 		numSrcCoords += coords;
       
   850 		switch(segment)
       
   851 		{
       
   852 		case VG_CLOSE_PATH:
       
   853 		case VG_MOVE_TO:
       
   854 		case VG_LINE_TO:
       
   855 			break;
       
   856 
       
   857 		case VG_HLINE_TO:
       
   858 		case VG_VLINE_TO:
       
   859 			coords = 2;
       
   860 			break;
       
   861 
       
   862 		case VG_QUAD_TO:
       
   863 		case VG_CUBIC_TO:
       
   864 		case VG_SQUAD_TO:
       
   865 		case VG_SCUBIC_TO:
       
   866 			coords = 6;
       
   867 			break;
       
   868 
       
   869 		default:
       
   870 			RI_ASSERT(segment == VG_SCCWARC_TO || segment == VG_SCWARC_TO ||
       
   871 					  segment == VG_LCCWARC_TO || segment == VG_LCWARC_TO);
       
   872 			break;
       
   873 		}
       
   874 		numDstCoords += coords;
       
   875 	}
       
   876 
       
   877 	m_segments.resize(srcPath->m_segments.size());	//throws bad_alloc
       
   878 	m_data.resize(numDstCoords * getBytesPerCoordinate(VG_PATH_DATATYPE_F));	//throws bad_alloc
       
   879 
       
   880 	int srcCoord = 0;
       
   881 	int dstCoord = 0;
       
   882 	Vector2 s(0,0);		//the beginning of the current subpath
       
   883 	Vector2 o(0,0);		//the last point of the previous segment
       
   884 
       
   885 	// the last internal control point of the previous segment, if the
       
   886 	//segment was a (regular or smooth) quadratic or cubic
       
   887 	//Bezier, or else the last point of the previous segment
       
   888 	Vector2 p(0,0);		
       
   889 	for(int i=0;i<srcPath->m_segments.size();i++)
       
   890 	{
       
   891 		VGPathSegment segment = getPathSegment(srcPath->m_segments[i]);
       
   892 		VGPathAbsRel absRel = getPathAbsRel(srcPath->m_segments[i]);
       
   893 		int coords = segmentToNumCoordinates(segment);
       
   894 
       
   895 		switch(segment)
       
   896 		{
       
   897 		case VG_CLOSE_PATH:
       
   898 		{
       
   899 			RI_ASSERT(coords == 0);
       
   900 			p = s;
       
   901 			o = s;
       
   902 			break;
       
   903 		}
       
   904 
       
   905 		case VG_MOVE_TO:
       
   906 		{
       
   907 			RI_ASSERT(coords == 2);
       
   908 			Vector2 c(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   909 			if(absRel == VG_RELATIVE)
       
   910 				c += o;
       
   911 			setCoordinate(dstCoord++, c.x);
       
   912 			setCoordinate(dstCoord++, c.y);
       
   913 			s = c;
       
   914 			p = c;
       
   915 			o = c;
       
   916 			break;
       
   917 		}
       
   918 
       
   919 		case VG_LINE_TO:
       
   920 		{
       
   921 			RI_ASSERT(coords == 2);
       
   922 			Vector2 c(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   923 			if(absRel == VG_RELATIVE)
       
   924 				c += o;
       
   925 			setCoordinate(dstCoord++, c.x);
       
   926 			setCoordinate(dstCoord++, c.y);
       
   927 			p = c;
       
   928 			o = c;
       
   929 			break;
       
   930 		}
       
   931 
       
   932 		case VG_HLINE_TO:
       
   933 		{
       
   934 			RI_ASSERT(coords == 1);
       
   935 			Vector2 c(srcPath->getCoordinate(srcCoord+0), o.y);
       
   936 			if(absRel == VG_RELATIVE)
       
   937 				c.x += o.x;
       
   938 			setCoordinate(dstCoord++, c.x);
       
   939 			setCoordinate(dstCoord++, c.y);
       
   940 			p = c;
       
   941 			o = c;
       
   942 			segment = VG_LINE_TO;
       
   943 			break;
       
   944 		}
       
   945 
       
   946 		case VG_VLINE_TO:
       
   947 		{
       
   948 			RI_ASSERT(coords == 1);
       
   949 			Vector2 c(o.x, srcPath->getCoordinate(srcCoord+0));
       
   950 			if(absRel == VG_RELATIVE)
       
   951 				c.y += o.y;
       
   952 			setCoordinate(dstCoord++, c.x);
       
   953 			setCoordinate(dstCoord++, c.y);
       
   954 			p = c;
       
   955 			o = c;
       
   956 			segment = VG_LINE_TO;
       
   957 			break;
       
   958 		}
       
   959 
       
   960 		case VG_QUAD_TO:
       
   961 		{
       
   962 			RI_ASSERT(coords == 4);
       
   963 			Vector2 c0(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   964 			Vector2 c1(srcPath->getCoordinate(srcCoord+2), srcPath->getCoordinate(srcCoord+3));
       
   965 			if(absRel == VG_RELATIVE)
       
   966 			{
       
   967 				c0 += o;
       
   968 				c1 += o;
       
   969 			}
       
   970 			Vector2 d0 = (1.0f/3.0f) * (o + 2.0f * c0);
       
   971 			Vector2 d1 = (1.0f/3.0f) * (c1 + 2.0f * c0);
       
   972 			setCoordinate(dstCoord++, d0.x);
       
   973 			setCoordinate(dstCoord++, d0.y);
       
   974 			setCoordinate(dstCoord++, d1.x);
       
   975 			setCoordinate(dstCoord++, d1.y);
       
   976 			setCoordinate(dstCoord++, c1.x);
       
   977 			setCoordinate(dstCoord++, c1.y);
       
   978 			p = c0;
       
   979 			o = c1;
       
   980 			segment = VG_CUBIC_TO;
       
   981 			break;
       
   982 		}
       
   983 
       
   984 		case VG_CUBIC_TO:
       
   985 		{
       
   986 			RI_ASSERT(coords == 6);
       
   987 			Vector2 c0(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
   988 			Vector2 c1(srcPath->getCoordinate(srcCoord+2), srcPath->getCoordinate(srcCoord+3));
       
   989 			Vector2 c2(srcPath->getCoordinate(srcCoord+4), srcPath->getCoordinate(srcCoord+5));
       
   990 			if(absRel == VG_RELATIVE)
       
   991 			{
       
   992 				c0 += o;
       
   993 				c1 += o;
       
   994 				c2 += o;
       
   995 			}
       
   996 			setCoordinate(dstCoord++, c0.x);
       
   997 			setCoordinate(dstCoord++, c0.y);
       
   998 			setCoordinate(dstCoord++, c1.x);
       
   999 			setCoordinate(dstCoord++, c1.y);
       
  1000 			setCoordinate(dstCoord++, c2.x);
       
  1001 			setCoordinate(dstCoord++, c2.y);
       
  1002 			p = c1;
       
  1003 			o = c2;
       
  1004 			break;
       
  1005 		}
       
  1006 
       
  1007 		case VG_SQUAD_TO:
       
  1008 		{
       
  1009 			RI_ASSERT(coords == 2);
       
  1010 			Vector2 c0 = 2.0f * o - p;
       
  1011 			Vector2 c1(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
  1012 			if(absRel == VG_RELATIVE)
       
  1013 				c1 += o;
       
  1014 			Vector2 d0 = (1.0f/3.0f) * (o + 2.0f * c0);
       
  1015 			Vector2 d1 = (1.0f/3.0f) * (c1 + 2.0f * c0);
       
  1016 			setCoordinate(dstCoord++, d0.x);
       
  1017 			setCoordinate(dstCoord++, d0.y);
       
  1018 			setCoordinate(dstCoord++, d1.x);
       
  1019 			setCoordinate(dstCoord++, d1.y);
       
  1020 			setCoordinate(dstCoord++, c1.x);
       
  1021 			setCoordinate(dstCoord++, c1.y);
       
  1022 			p = c0;
       
  1023 			o = c1;
       
  1024 			segment = VG_CUBIC_TO;
       
  1025 			break;
       
  1026 		}
       
  1027 
       
  1028 		case VG_SCUBIC_TO:
       
  1029 		{
       
  1030 			RI_ASSERT(coords == 4);
       
  1031 			Vector2 c0 = 2.0f * o - p;
       
  1032 			Vector2 c1(srcPath->getCoordinate(srcCoord+0), srcPath->getCoordinate(srcCoord+1));
       
  1033 			Vector2 c2(srcPath->getCoordinate(srcCoord+2), srcPath->getCoordinate(srcCoord+3));
       
  1034 			if(absRel == VG_RELATIVE)
       
  1035 			{
       
  1036 				c1 += o;
       
  1037 				c2 += o;
       
  1038 			}
       
  1039 			setCoordinate(dstCoord++, c0.x);
       
  1040 			setCoordinate(dstCoord++, c0.y);
       
  1041 			setCoordinate(dstCoord++, c1.x);
       
  1042 			setCoordinate(dstCoord++, c1.y);
       
  1043 			setCoordinate(dstCoord++, c2.x);
       
  1044 			setCoordinate(dstCoord++, c2.y);
       
  1045 			p = c1;
       
  1046 			o = c2;
       
  1047 			segment = VG_CUBIC_TO;
       
  1048 			break;
       
  1049 		}
       
  1050 
       
  1051 		default:
       
  1052 		{
       
  1053 			RI_ASSERT(segment == VG_SCCWARC_TO || segment == VG_SCWARC_TO ||
       
  1054 					  segment == VG_LCCWARC_TO || segment == VG_LCWARC_TO);
       
  1055 			RI_ASSERT(coords == 5);
       
  1056 			RIfloat rh = srcPath->getCoordinate(srcCoord+0);
       
  1057 			RIfloat rv = srcPath->getCoordinate(srcCoord+1);
       
  1058 			RIfloat rot = srcPath->getCoordinate(srcCoord+2);
       
  1059 			Vector2 c(srcPath->getCoordinate(srcCoord+3), srcPath->getCoordinate(srcCoord+4));
       
  1060 			if(absRel == VG_RELATIVE)
       
  1061 				c += o;
       
  1062 			setCoordinate(dstCoord++, rh);
       
  1063 			setCoordinate(dstCoord++, rv);
       
  1064 			setCoordinate(dstCoord++, rot);
       
  1065 			setCoordinate(dstCoord++, c.x);
       
  1066 			setCoordinate(dstCoord++, c.y);
       
  1067 			p = c;
       
  1068 			o = c;
       
  1069 			break;
       
  1070 		}
       
  1071 		}
       
  1072 
       
  1073 		m_segments[i] = (RIuint8)(segment | VG_ABSOLUTE);
       
  1074 		srcCoord += coords;
       
  1075 	}
       
  1076 	RI_ASSERT(srcCoord == numSrcCoords);
       
  1077 	RI_ASSERT(dstCoord == numDstCoords);
       
  1078 }
       
  1079 
       
  1080 /*-------------------------------------------------------------------*//*!
       
  1081 * \brief	Appends a linearly interpolated copy of the two source paths.
       
  1082 * \param	
       
  1083 * \return	
       
  1084 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
  1085 *//*-------------------------------------------------------------------*/
       
  1086 
       
  1087 bool Path::interpolate(const Path* startPath, const Path* endPath, RIfloat amount)
       
  1088 {
       
  1089 	RI_ASSERT(startPath && endPath);
       
  1090 	RI_ASSERT(m_referenceCount > 0 && startPath->m_referenceCount > 0 && endPath->m_referenceCount > 0);
       
  1091 
       
  1092 	if(!startPath->m_segments.size() || startPath->m_segments.size() != endPath->m_segments.size())
       
  1093 		return false;	//start and end paths are incompatible or zero length
       
  1094 
       
  1095 	Path start(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, 0);
       
  1096 	start.normalizeForInterpolation(startPath);	//throws bad_alloc
       
  1097 
       
  1098 	Path end(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, 0);
       
  1099 	end.normalizeForInterpolation(endPath);	//throws bad_alloc
       
  1100 
       
  1101 	//check that start and end paths are compatible
       
  1102 	if(start.m_data.size() != end.m_data.size() || start.m_segments.size() != end.m_segments.size())
       
  1103 		return false;	//start and end paths are incompatible
       
  1104 
       
  1105 	//allocate new arrays
       
  1106 	Array<RIuint8> newSegments;
       
  1107 	newSegments.resize(m_segments.size() + start.m_segments.size());	//throws bad_alloc
       
  1108 	Array<RIuint8> newData;
       
  1109 	newData.resize(m_data.size() + start.m_data.size() * getBytesPerCoordinate(m_datatype) / getBytesPerCoordinate(start.m_datatype));	//throws bad_alloc
       
  1110 	//if we get here, the memory allocations have succeeded
       
  1111 
       
  1112 	//copy old segments
       
  1113 	if(m_segments.size())
       
  1114 		memcpy(&newSegments[0], &m_segments[0], m_segments.size());
       
  1115 
       
  1116 	//copy old data
       
  1117 	if(m_data.size())
       
  1118 		memcpy(&newData[0], &m_data[0], m_data.size());
       
  1119 
       
  1120 	//copy segments
       
  1121 	for(int i=0;i<start.m_segments.size();i++)
       
  1122 	{
       
  1123 		VGPathSegment s = getPathSegment(start.m_segments[i]);
       
  1124 		VGPathSegment e = getPathSegment(end.m_segments[i]);
       
  1125 
       
  1126 		if(s == VG_SCCWARC_TO || s == VG_SCWARC_TO || s == VG_LCCWARC_TO || s == VG_LCWARC_TO)
       
  1127 		{
       
  1128 			if(e != VG_SCCWARC_TO && e != VG_SCWARC_TO && e != VG_LCCWARC_TO && e != VG_LCWARC_TO)
       
  1129 				return false;	//start and end paths are incompatible
       
  1130 			if(amount < 0.5f)
       
  1131 				newSegments[m_segments.size() + i] = start.m_segments[i];
       
  1132 			else
       
  1133 				newSegments[m_segments.size() + i] = end.m_segments[i];
       
  1134 		}
       
  1135 		else
       
  1136 		{
       
  1137 			if(s != e)
       
  1138 				return false;	//start and end paths are incompatible
       
  1139 			newSegments[m_segments.size() + i] = start.m_segments[i];
       
  1140 		}
       
  1141 	}
       
  1142 
       
  1143 	//interpolate data
       
  1144 	int oldNumCoords = getNumCoordinates();
       
  1145 	for(int i=0;i<start.getNumCoordinates();i++)
       
  1146 		setCoordinate(newData, m_datatype, m_scale, m_bias, oldNumCoords + i, start.getCoordinate(i) * (1.0f - amount) + end.getCoordinate(i) * amount);
       
  1147 
       
  1148 	RI_ASSERT(newData.size() == countNumCoordinates(&newSegments[0],newSegments.size()) * getBytesPerCoordinate(m_datatype));
       
  1149 
       
  1150 	//replace old arrays
       
  1151 	m_segments.swap(newSegments);
       
  1152 	m_data.swap(newData);
       
  1153 
       
  1154 	return true;
       
  1155 }
       
  1156 
       
  1157 /*-------------------------------------------------------------------*//*!
       
  1158 * \brief	Tessellates a path for filling and appends resulting edges
       
  1159 *			to a rasterizer.
       
  1160 * \param	
       
  1161 * \return	
       
  1162 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
  1163 *//*-------------------------------------------------------------------*/
       
  1164 
       
  1165 void Path::fill(const Matrix3x3& pathToSurface, Rasterizer& rasterizer)
       
  1166 {
       
  1167 	RI_ASSERT(m_referenceCount > 0);
       
  1168 	RI_ASSERT(pathToSurface.isAffine());
       
  1169 
       
  1170 	tessellate(pathToSurface, 0.0f);	//throws bad_alloc
       
  1171 
       
  1172 	try
       
  1173 	{
       
  1174 		Vector2 p0(0,0), p1(0,0);
       
  1175 		for(int i=0;i<m_vertices.size();i++)
       
  1176 		{
       
  1177 			p1 = affineTransform(pathToSurface, m_vertices[i].userPosition);
       
  1178 
       
  1179 			if(!(m_vertices[i].flags & START_SEGMENT))
       
  1180 			{	//in the middle of a segment
       
  1181 				rasterizer.addEdge(p0, p1);	//throws bad_alloc
       
  1182 			}
       
  1183 
       
  1184 			p0 = p1;
       
  1185 		}
       
  1186 	}
       
  1187 	catch(std::bad_alloc)
       
  1188 	{
       
  1189 		rasterizer.clear();	//remove the unfinished path
       
  1190 		throw;
       
  1191 	}
       
  1192 }
       
  1193 
       
  1194 /*-------------------------------------------------------------------*//*!
       
  1195 * \brief	Smoothly interpolates between two StrokeVertices. Positions
       
  1196 *			are interpolated linearly, while tangents are interpolated
       
  1197 *			on a unit circle. Stroking is implemented so that overlapping
       
  1198 *			geometry doesnt cancel itself when filled with nonzero rule.
       
  1199 *			The resulting polygons are closed.
       
  1200 * \param	
       
  1201 * \return	
       
  1202 * \note		
       
  1203 *//*-------------------------------------------------------------------*/
       
  1204 
       
  1205 void Path::interpolateStroke(const Matrix3x3& pathToSurface, Rasterizer& rasterizer, const StrokeVertex& v0, const StrokeVertex& v1, RIfloat strokeWidth) const
       
  1206 {
       
  1207 	Vector2 pccw = affineTransform(pathToSurface, v0.ccw);
       
  1208 	Vector2 pcw = affineTransform(pathToSurface, v0.cw);
       
  1209 	Vector2 p = affineTransform(pathToSurface, v0.p);
       
  1210 	Vector2 endccw = affineTransform(pathToSurface, v1.ccw);
       
  1211 	Vector2 endcw = affineTransform(pathToSurface, v1.cw);
       
  1212 	Vector2 endp = affineTransform(pathToSurface, v1.p);
       
  1213 
       
  1214 	const RIfloat tessellationAngle = 5.0f;
       
  1215 
       
  1216 	RIfloat angle = RI_RAD_TO_DEG((RIfloat)acos(RI_CLAMP(dot(v0.t, v1.t), -1.0f, 1.0f))) / tessellationAngle;
       
  1217 	int samples = RI_INT_MAX((int)ceil(angle), 1);
       
  1218 
       
  1219 	for(int j=0;j<samples-1;j++)
       
  1220 	{
       
  1221 		RIfloat t = (RIfloat)(j+1) / (RIfloat)samples;
       
  1222 		Vector2 position = v0.p * (1.0f - t) + v1.p * t;
       
  1223 		Vector2 tangent = circularLerp(v0.t, v1.t, t);
       
  1224 		Vector2 normal = normalize(perpendicularCCW(tangent)) * strokeWidth * 0.5f;
       
  1225 
       
  1226 		Vector2 nccw = affineTransform(pathToSurface, position + normal);
       
  1227 		Vector2 ncw = affineTransform(pathToSurface, position - normal);
       
  1228 		Vector2 n = affineTransform(pathToSurface, position);
       
  1229 
       
  1230         rasterizer.clear();
       
  1231 		rasterizer.addEdge(p, pccw);	//throws bad_alloc
       
  1232 		rasterizer.addEdge(pccw, nccw);	//throws bad_alloc
       
  1233 		rasterizer.addEdge(nccw, n);	//throws bad_alloc
       
  1234 		rasterizer.addEdge(n, ncw);     //throws bad_alloc
       
  1235 		rasterizer.addEdge(ncw, pcw);	//throws bad_alloc
       
  1236 		rasterizer.addEdge(pcw, p);	    //throws bad_alloc
       
  1237         rasterizer.fill();
       
  1238 
       
  1239 		pccw = nccw;
       
  1240 		pcw = ncw;
       
  1241         p = n;
       
  1242 	}
       
  1243 
       
  1244 	//connect the last segment to the end coordinates
       
  1245     rasterizer.clear();
       
  1246 	rasterizer.addEdge(p, pccw);	    //throws bad_alloc
       
  1247 	rasterizer.addEdge(pccw, endccw);	//throws bad_alloc
       
  1248 	rasterizer.addEdge(endccw, endp);	//throws bad_alloc
       
  1249 	rasterizer.addEdge(endp, endcw);	//throws bad_alloc
       
  1250 	rasterizer.addEdge(endcw, pcw);     //throws bad_alloc
       
  1251 	rasterizer.addEdge(pcw, p);         //throws bad_alloc
       
  1252     rasterizer.fill();
       
  1253 }
       
  1254 
       
  1255 /*-------------------------------------------------------------------*//*!
       
  1256 * \brief	Generate edges for stroke caps. Resulting polygons are closed.
       
  1257 * \param	
       
  1258 * \return	
       
  1259 * \note		
       
  1260 *//*-------------------------------------------------------------------*/
       
  1261 
       
  1262 void Path::doCap(const Matrix3x3& pathToSurface, Rasterizer& rasterizer, const StrokeVertex& v, RIfloat strokeWidth, VGCapStyle capStyle) const
       
  1263 {
       
  1264 	Vector2 ccwt = affineTransform(pathToSurface, v.ccw);
       
  1265 	Vector2 cwt = affineTransform(pathToSurface, v.cw);
       
  1266 	Vector2 p = affineTransform(pathToSurface, v.p);
       
  1267 
       
  1268     rasterizer.clear();
       
  1269 	switch(capStyle)
       
  1270 	{
       
  1271 	case VG_CAP_BUTT:
       
  1272 		break;
       
  1273 
       
  1274 	case VG_CAP_ROUND:
       
  1275 	{
       
  1276         const RIfloat tessellationAngle = 5.0f;
       
  1277 
       
  1278 		RIfloat angle = 180.0f / tessellationAngle;
       
  1279 
       
  1280 		int samples = (int)ceil(angle);
       
  1281 		RIfloat step = 1.0f / samples;
       
  1282 		RIfloat t = step;
       
  1283 		Vector2 u0 = normalize(v.ccw - v.p);
       
  1284 		Vector2 u1 = normalize(v.cw - v.p);
       
  1285 		Vector2 prev = ccwt;
       
  1286 		rasterizer.addEdge(p, ccwt);	//throws bad_alloc
       
  1287 		for(int j=1;j<samples;j++)
       
  1288 		{
       
  1289 			Vector2 next = v.p + circularLerp(u0, u1, t, true) * strokeWidth * 0.5f;
       
  1290 			next = affineTransform(pathToSurface, next);
       
  1291 
       
  1292 			rasterizer.addEdge(prev, next);	//throws bad_alloc
       
  1293 			prev = next;
       
  1294 			t += step;
       
  1295 		}
       
  1296 		rasterizer.addEdge(prev, cwt);	//throws bad_alloc
       
  1297 		rasterizer.addEdge(cwt, p);     //throws bad_alloc
       
  1298 		break;
       
  1299 	}
       
  1300 
       
  1301 	default:
       
  1302 	{
       
  1303 		RI_ASSERT(capStyle == VG_CAP_SQUARE);
       
  1304 		Vector2 t = v.t;
       
  1305 		t.normalize();
       
  1306 		Vector2 ccws = affineTransform(pathToSurface, v.ccw + t * strokeWidth * 0.5f);
       
  1307 		Vector2 cws = affineTransform(pathToSurface, v.cw + t * strokeWidth * 0.5f);
       
  1308 		rasterizer.addEdge(p, ccwt);	//throws bad_alloc
       
  1309 		rasterizer.addEdge(ccwt, ccws);	//throws bad_alloc
       
  1310 		rasterizer.addEdge(ccws, cws);	//throws bad_alloc
       
  1311 		rasterizer.addEdge(cws, cwt);	//throws bad_alloc
       
  1312 		rasterizer.addEdge(cwt, p);     //throws bad_alloc
       
  1313 		break;
       
  1314 	}
       
  1315 	}
       
  1316     rasterizer.fill();
       
  1317 }
       
  1318 
       
  1319 /*-------------------------------------------------------------------*//*!
       
  1320 * \brief	Generate edges for stroke joins. Resulting polygons are closed.
       
  1321 * \param	
       
  1322 * \return	
       
  1323 * \note		
       
  1324 *//*-------------------------------------------------------------------*/
       
  1325 
       
  1326 void Path::doJoin(const Matrix3x3& pathToSurface, Rasterizer& rasterizer, const StrokeVertex& v0, const StrokeVertex& v1, RIfloat strokeWidth, VGJoinStyle joinStyle, RIfloat miterLimit) const
       
  1327 {
       
  1328 	Vector2 ccw0t = affineTransform(pathToSurface, v0.ccw);
       
  1329 	Vector2 cw0t = affineTransform(pathToSurface, v0.cw);
       
  1330 	Vector2 m0t = affineTransform(pathToSurface, v0.p);
       
  1331 	Vector2 ccw1t = affineTransform(pathToSurface, v1.ccw);
       
  1332 	Vector2 cw1t = affineTransform(pathToSurface, v1.cw);
       
  1333 	Vector2 m1t = affineTransform(pathToSurface, v1.p);
       
  1334 
       
  1335 	Vector2 tccw = v1.ccw - v0.ccw;
       
  1336 	Vector2 s, e, m, st, et;
       
  1337 	bool cw;
       
  1338 
       
  1339     rasterizer.clear();
       
  1340 
       
  1341 	if( dot(tccw, v0.t) > 0.0f )
       
  1342 	{	//draw ccw miter (draw from point 0 to 1)
       
  1343 		s = ccw0t;
       
  1344 		e = ccw1t;
       
  1345 		st = v0.t;
       
  1346 		et = v1.t;
       
  1347 		m = v0.ccw;
       
  1348 		cw = false;
       
  1349 		rasterizer.addEdge(m0t, ccw0t);	//throws bad_alloc
       
  1350 		rasterizer.addEdge(ccw1t, m1t);	//throws bad_alloc
       
  1351 		rasterizer.addEdge(m1t, m0t);	//throws bad_alloc
       
  1352 	}
       
  1353 	else
       
  1354 	{	//draw cw miter (draw from point 1 to 0)
       
  1355 		s = cw1t;
       
  1356 		e = cw0t;
       
  1357 		st = v1.t;
       
  1358 		et = v0.t;
       
  1359 		m = v0.cw;
       
  1360 		cw = true;
       
  1361 		rasterizer.addEdge(cw0t, m0t);	//throws bad_alloc
       
  1362 		rasterizer.addEdge(m1t, cw1t);	//throws bad_alloc
       
  1363 		rasterizer.addEdge(m0t, m1t);	//throws bad_alloc
       
  1364 	}
       
  1365 
       
  1366 	switch(joinStyle)
       
  1367 	{
       
  1368 	case VG_JOIN_MITER:
       
  1369 	{
       
  1370 		RIfloat theta = (RIfloat)acos(RI_CLAMP(dot(v0.t, -v1.t), -1.0f, 1.0f));
       
  1371 		RIfloat miterLengthPerStrokeWidth = 1.0f / (RIfloat)sin(theta*0.5f);
       
  1372 		if( miterLengthPerStrokeWidth < miterLimit )
       
  1373 		{	//miter
       
  1374 			RIfloat l = (RIfloat)cos(theta*0.5f) * miterLengthPerStrokeWidth * (strokeWidth * 0.5f);
       
  1375 			l = RI_MIN(l, RI_FLOAT_MAX);	//force finite
       
  1376 			Vector2 c = m + v0.t * l;
       
  1377 			c = affineTransform(pathToSurface, c);
       
  1378 			rasterizer.addEdge(s, c);	//throws bad_alloc
       
  1379 			rasterizer.addEdge(c, e);	//throws bad_alloc
       
  1380 		}
       
  1381 		else
       
  1382 		{	//bevel
       
  1383 			rasterizer.addEdge(s, e);	//throws bad_alloc
       
  1384 		}
       
  1385 		break;
       
  1386 	}
       
  1387 
       
  1388 	case VG_JOIN_ROUND:
       
  1389 	{
       
  1390 		const RIfloat tessellationAngle = 5.0f;
       
  1391 
       
  1392 		Vector2 prev = s;
       
  1393 		RIfloat angle = RI_RAD_TO_DEG((RIfloat)acos(RI_CLAMP(dot(st, et), -1.0f, 1.0f))) / tessellationAngle;
       
  1394 		int samples = (int)ceil(angle);
       
  1395 		if( samples )
       
  1396 		{
       
  1397 			RIfloat step = 1.0f / samples;
       
  1398 			RIfloat t = step;
       
  1399 			for(int j=1;j<samples;j++)
       
  1400 			{
       
  1401 				Vector2 position = v0.p * (1.0f - t) + v1.p * t;
       
  1402 				Vector2 tangent = circularLerp(st, et, t, true);
       
  1403 
       
  1404 				Vector2 next = position + normalize(perpendicular(tangent, cw)) * strokeWidth * 0.5f;
       
  1405 				next = affineTransform(pathToSurface, next);
       
  1406 
       
  1407 				rasterizer.addEdge(prev, next);	//throws bad_alloc
       
  1408 				prev = next;
       
  1409 				t += step;
       
  1410 			}
       
  1411 		}
       
  1412 		rasterizer.addEdge(prev, e);	//throws bad_alloc
       
  1413 		break;
       
  1414 	}
       
  1415 
       
  1416 	default:
       
  1417 		RI_ASSERT(joinStyle == VG_JOIN_BEVEL);
       
  1418 		if(!cw)
       
  1419 			rasterizer.addEdge(ccw0t, ccw1t);	//throws bad_alloc
       
  1420 		else
       
  1421 			rasterizer.addEdge(cw1t, cw0t);		//throws bad_alloc
       
  1422 		break;
       
  1423 	}
       
  1424     rasterizer.fill();
       
  1425 }
       
  1426 
       
  1427 /*-------------------------------------------------------------------*//*!
       
  1428 * \brief	Tessellate a path, apply stroking, dashing, caps and joins, and
       
  1429 *			append resulting edges to a rasterizer.
       
  1430 * \param	
       
  1431 * \return	
       
  1432 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
  1433 *//*-------------------------------------------------------------------*/
       
  1434 
       
  1435 void Path::stroke(const Matrix3x3& pathToSurface, Rasterizer& rasterizer, const Array<RIfloat>& dashPattern, RIfloat dashPhase, bool dashPhaseReset, RIfloat strokeWidth, VGCapStyle capStyle, VGJoinStyle joinStyle, RIfloat miterLimit)
       
  1436 {
       
  1437 	RI_ASSERT(pathToSurface.isAffine());
       
  1438 	RI_ASSERT(m_referenceCount > 0);
       
  1439 	RI_ASSERT(strokeWidth >= 0.0f);
       
  1440 	RI_ASSERT(miterLimit >= 1.0f);
       
  1441 
       
  1442 	tessellate(pathToSurface, strokeWidth);	//throws bad_alloc
       
  1443 
       
  1444 	if(!m_vertices.size())
       
  1445 		return;
       
  1446 
       
  1447 	bool dashing = true;
       
  1448 	int dashPatternSize = dashPattern.size();
       
  1449 	if( dashPattern.size() & 1 )
       
  1450 		dashPatternSize--;	//odd number of dash pattern entries, discard the last one
       
  1451 	RIfloat dashPatternLength = 0.0f;
       
  1452 	for(int i=0;i<dashPatternSize;i++)
       
  1453 		dashPatternLength += RI_MAX(dashPattern[i], 0.0f);
       
  1454 	if(!dashPatternSize || dashPatternLength == 0.0f )
       
  1455 		dashing = false;
       
  1456 	dashPatternLength = RI_MIN(dashPatternLength, RI_FLOAT_MAX);
       
  1457 
       
  1458 	//walk along the path
       
  1459 	//stop at the next event which is either:
       
  1460 	//-path vertex
       
  1461 	//-dash stop
       
  1462 	//for robustness, decisions based on geometry are done only once.
       
  1463 	//inDash keeps track whether the last point was in dash or not
       
  1464 
       
  1465 	//loop vertex events
       
  1466 	try
       
  1467 	{
       
  1468 		RIfloat nextDash = 0.0f;
       
  1469 		int d = 0;
       
  1470 		bool inDash = true;
       
  1471 		StrokeVertex v0, v1, vs;
       
  1472 		for(int i=0;i<m_vertices.size();i++)
       
  1473 		{
       
  1474 			//read the next vertex
       
  1475 			Vertex& v = m_vertices[i];
       
  1476 			v1.p = v.userPosition;
       
  1477 			v1.t = v.userTangent;
       
  1478 			RI_ASSERT(!isZero(v1.t));	//don't allow zero tangents
       
  1479 			v1.ccw = v1.p + normalize(perpendicularCCW(v1.t)) * strokeWidth * 0.5f;
       
  1480 			v1.cw = v1.p + normalize(perpendicularCW(v1.t)) * strokeWidth * 0.5f;
       
  1481 			v1.pathLength = v.pathLength;
       
  1482 			v1.flags = v.flags;
       
  1483 			v1.inDash = dashing ? inDash : true;	//NOTE: for other than START_SEGMENT vertices inDash will be updated after dashing
       
  1484 
       
  1485 			//process the vertex event
       
  1486 			if(v.flags & START_SEGMENT)
       
  1487 			{
       
  1488 				if(v.flags & START_SUBPATH)
       
  1489 				{
       
  1490 					if( dashing )
       
  1491 					{	//initialize dashing by finding which dash or gap the first point of the path lies in
       
  1492 						if(dashPhaseReset || i == 0)
       
  1493 						{
       
  1494 							d = 0;
       
  1495 							inDash = true;
       
  1496 							nextDash = v1.pathLength - RI_MOD(dashPhase, dashPatternLength);
       
  1497 							for(;;)
       
  1498 							{
       
  1499 								RIfloat prevDash = nextDash;
       
  1500 								nextDash = prevDash + RI_MAX(dashPattern[d], 0.0f);
       
  1501 								if(nextDash >= v1.pathLength)
       
  1502 									break;
       
  1503 
       
  1504 								if( d & 1 )
       
  1505 									inDash = true;
       
  1506 								else
       
  1507 									inDash = false;
       
  1508 								d = (d+1) % dashPatternSize;
       
  1509 							}
       
  1510 							v1.inDash = inDash;
       
  1511 							//the first point of the path lies between prevDash and nextDash
       
  1512 							//d in the index of the next dash stop
       
  1513 							//inDash is true if the first point is in a dash
       
  1514 						}
       
  1515 					}
       
  1516 					vs = v1;	//save the subpath start point
       
  1517 				}
       
  1518 				else
       
  1519 				{
       
  1520 					if( v.flags & IMPLICIT_CLOSE_SUBPATH )
       
  1521 					{	//do caps for the start and end of the current subpath
       
  1522 						if( v0.inDash )
       
  1523 							doCap(pathToSurface, rasterizer, v0, strokeWidth, capStyle);	//end cap	//throws bad_alloc
       
  1524 						if( vs.inDash )
       
  1525 						{
       
  1526 							StrokeVertex vi = vs;
       
  1527 							vi.t = -vi.t;
       
  1528 							RI_SWAP(vi.ccw.x, vi.cw.x);
       
  1529 							RI_SWAP(vi.ccw.y, vi.cw.y);
       
  1530 							doCap(pathToSurface, rasterizer, vi, strokeWidth, capStyle);	//start cap	//throws bad_alloc
       
  1531 						}
       
  1532 					}
       
  1533 					else
       
  1534 					{	//join two segments
       
  1535 						RI_ASSERT(v0.inDash == v1.inDash);
       
  1536 						if( v0.inDash )
       
  1537 							doJoin(pathToSurface, rasterizer, v0, v1, strokeWidth, joinStyle, miterLimit);	//throws bad_alloc
       
  1538 					}
       
  1539 				}
       
  1540 			}
       
  1541 			else
       
  1542 			{	//in the middle of a segment
       
  1543 				if( !(v.flags & IMPLICIT_CLOSE_SUBPATH) )
       
  1544 				{	//normal segment, do stroking
       
  1545 					if( dashing )
       
  1546 					{
       
  1547 						StrokeVertex prevDashVertex = v0;	//dashing of the segment starts from the previous vertex
       
  1548 
       
  1549 						if(nextDash + 10000.0f * dashPatternLength < v1.pathLength)
       
  1550 							throw std::bad_alloc();		//too many dashes, throw bad_alloc
       
  1551 
       
  1552 						//loop dash events until the next vertex event
       
  1553 						//zero length dashes are handled as a special case since if they hit the vertex,
       
  1554 						//we want to include their starting point to this segment already in order to generate a join
       
  1555 						int numDashStops = 0;
       
  1556 						while(nextDash < v1.pathLength || (nextDash <= v1.pathLength && dashPattern[(d+1) % dashPatternSize] == 0.0f))
       
  1557 						{
       
  1558 							RIfloat edgeLength = v1.pathLength - v0.pathLength;
       
  1559 							RIfloat ratio = 0.0f;
       
  1560 							if(edgeLength > 0.0f)
       
  1561 								ratio = (nextDash - v0.pathLength) / edgeLength;
       
  1562 							StrokeVertex nextDashVertex;
       
  1563 							nextDashVertex.p = v0.p * (1.0f - ratio) + v1.p * ratio;
       
  1564 							nextDashVertex.t = circularLerp(v0.t, v1.t, ratio);
       
  1565 							nextDashVertex.ccw = nextDashVertex.p + normalize(perpendicularCCW(nextDashVertex.t)) * strokeWidth * 0.5f;
       
  1566 							nextDashVertex.cw = nextDashVertex.p + normalize(perpendicularCW(nextDashVertex.t)) * strokeWidth * 0.5f;
       
  1567 
       
  1568 							if( inDash )
       
  1569 							{	//stroke from prevDashVertex -> nextDashVertex
       
  1570 								if( numDashStops )
       
  1571 								{	//prevDashVertex is not the start vertex of the segment, cap it (start vertex has already been joined or capped)
       
  1572 									StrokeVertex vi = prevDashVertex;
       
  1573 									vi.t = -vi.t;
       
  1574 									RI_SWAP(vi.ccw.x, vi.cw.x);
       
  1575 									RI_SWAP(vi.ccw.y, vi.cw.y);
       
  1576 									doCap(pathToSurface, rasterizer, vi, strokeWidth, capStyle);	//throws bad_alloc
       
  1577 								}
       
  1578 								interpolateStroke(pathToSurface, rasterizer, prevDashVertex, nextDashVertex, strokeWidth);	//throws bad_alloc
       
  1579 								doCap(pathToSurface, rasterizer, nextDashVertex, strokeWidth, capStyle);	//end cap	//throws bad_alloc
       
  1580 							}
       
  1581 							prevDashVertex = nextDashVertex;
       
  1582 
       
  1583 							if( d & 1 )
       
  1584 							{	//dash starts
       
  1585 								RI_ASSERT(!inDash);
       
  1586 								inDash = true;
       
  1587 							}
       
  1588 							else
       
  1589 							{	//dash ends
       
  1590 								RI_ASSERT(inDash);
       
  1591 								inDash = false;
       
  1592 							}
       
  1593 							d = (d+1) % dashPatternSize;
       
  1594 							nextDash += RI_MAX(dashPattern[d], 0.0f);
       
  1595 							numDashStops++;
       
  1596 						}
       
  1597 						
       
  1598 						if( inDash )
       
  1599 						{	//stroke prevDashVertex -> v1
       
  1600 							if( numDashStops )
       
  1601 							{	//prevDashVertex is not the start vertex of the segment, cap it (start vertex has already been joined or capped)
       
  1602 								StrokeVertex vi = prevDashVertex;
       
  1603 								vi.t = -vi.t;
       
  1604 								RI_SWAP(vi.ccw.x, vi.cw.x);
       
  1605 								RI_SWAP(vi.ccw.y, vi.cw.y);
       
  1606 								doCap(pathToSurface, rasterizer, vi, strokeWidth, capStyle);	//throws bad_alloc
       
  1607 							}
       
  1608 							interpolateStroke(pathToSurface, rasterizer, prevDashVertex, v1, strokeWidth);	//throws bad_alloc
       
  1609 							//no cap, leave path open
       
  1610 						}
       
  1611 
       
  1612 						v1.inDash = inDash;	//update inDash status of the segment end point
       
  1613 					}
       
  1614 					else	//no dashing, just interpolate segment end points
       
  1615 						interpolateStroke(pathToSurface, rasterizer, v0, v1, strokeWidth);	//throws bad_alloc
       
  1616 				}
       
  1617 			}
       
  1618 
       
  1619 			if((v.flags & END_SEGMENT) && (v.flags & CLOSE_SUBPATH))
       
  1620 			{	//join start and end of the current subpath
       
  1621 				if( v1.inDash && vs.inDash )
       
  1622 					doJoin(pathToSurface, rasterizer, v1, vs, strokeWidth, joinStyle, miterLimit);	//throws bad_alloc
       
  1623 				else
       
  1624 				{	//both start and end are not in dash, cap them
       
  1625 					if( v1.inDash )
       
  1626 						doCap(pathToSurface, rasterizer, v1, strokeWidth, capStyle);	//end cap	//throws bad_alloc
       
  1627 					if( vs.inDash )
       
  1628 					{
       
  1629 						StrokeVertex vi = vs;
       
  1630 						vi.t = -vi.t;
       
  1631 						RI_SWAP(vi.ccw.x, vi.cw.x);
       
  1632 						RI_SWAP(vi.ccw.y, vi.cw.y);
       
  1633 						doCap(pathToSurface, rasterizer, vi, strokeWidth, capStyle);	//start cap	//throws bad_alloc
       
  1634 					}
       
  1635 				}
       
  1636 			}
       
  1637 
       
  1638 			v0 = v1;
       
  1639 		}
       
  1640 	}
       
  1641 	catch(std::bad_alloc)
       
  1642 	{
       
  1643 		rasterizer.clear();	//remove the unfinished path
       
  1644 		throw;
       
  1645 	}
       
  1646 }
       
  1647 
       
  1648 /*-------------------------------------------------------------------*//*!
       
  1649 * \brief	Tessellates a path, and returns a position and a tangent on the path
       
  1650 *			given a distance along the path.
       
  1651 * \param	
       
  1652 * \return	
       
  1653 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
  1654 *//*-------------------------------------------------------------------*/
       
  1655 
       
  1656 void Path::getPointAlong(int startIndex, int numSegments, RIfloat distance, Vector2& p, Vector2& t)
       
  1657 {
       
  1658 	RI_ASSERT(m_referenceCount > 0);
       
  1659 	RI_ASSERT(startIndex >= 0 && startIndex + numSegments <= m_segments.size() && numSegments > 0);
       
  1660 
       
  1661 	Matrix3x3 identity;
       
  1662 	identity.identity();
       
  1663 	tessellate(identity, 0.0f);	//throws bad_alloc
       
  1664 
       
  1665 	RI_ASSERT(startIndex >= 0 && startIndex < m_segmentToVertex.size());
       
  1666 	RI_ASSERT(startIndex + numSegments >= 0 && startIndex + numSegments <= m_segmentToVertex.size());
       
  1667 
       
  1668     // ignore move segments at the start of the path
       
  1669     while (numSegments && (m_segments[startIndex] & ~VG_RELATIVE) == VG_MOVE_TO)
       
  1670     {
       
  1671         startIndex++;
       
  1672         numSegments--;
       
  1673     }
       
  1674 
       
  1675     // ignore move segments at the end of the path
       
  1676     while (numSegments && (m_segments[startIndex + numSegments - 1] & ~VG_RELATIVE) == VG_MOVE_TO)
       
  1677         numSegments--;
       
  1678 
       
  1679     // empty path?
       
  1680     if (!m_vertices.size() || !numSegments)
       
  1681     {
       
  1682 		p.set(0,0);
       
  1683 		t.set(1,0);
       
  1684 		return;
       
  1685     }
       
  1686 
       
  1687 	int startVertex = m_segmentToVertex[startIndex].start;
       
  1688 	int endVertex = m_segmentToVertex[startIndex + numSegments - 1].end;
       
  1689 
       
  1690 	if(startVertex == -1)
       
  1691 		startVertex = 0;
       
  1692 
       
  1693     // zero length?
       
  1694     if (startVertex >= endVertex)
       
  1695     {
       
  1696 		p = m_vertices[startVertex].userPosition;
       
  1697 		t.set(1,0);
       
  1698 		return;
       
  1699     }
       
  1700 
       
  1701 	RI_ASSERT(startVertex >= 0 && startVertex < m_vertices.size());
       
  1702 	RI_ASSERT(endVertex >= 0 && endVertex < m_vertices.size());
       
  1703 
       
  1704 	distance += m_vertices[startVertex].pathLength;	//map distance to the range of the whole path
       
  1705 
       
  1706 	if(distance <= m_vertices[startVertex].pathLength)
       
  1707 	{	//return the first point of the path
       
  1708 		p = m_vertices[startVertex].userPosition;
       
  1709 		t = m_vertices[startVertex].userTangent;
       
  1710 		return;
       
  1711 	}
       
  1712 
       
  1713 	if(distance >= m_vertices[endVertex].pathLength)
       
  1714 	{	//return the last point of the path
       
  1715 		p = m_vertices[endVertex].userPosition;
       
  1716 		t = m_vertices[endVertex].userTangent;
       
  1717 		return;
       
  1718 	}
       
  1719 
       
  1720 	//search for the segment containing the distance
       
  1721 	for(int s=startIndex;s<startIndex+numSegments;s++)
       
  1722 	{
       
  1723 		int start = m_segmentToVertex[s].start;
       
  1724 		int end = m_segmentToVertex[s].end;
       
  1725 		if(start < 0)
       
  1726 			start = 0;
       
  1727 		if(end < 0)
       
  1728 			end = 0;
       
  1729 		RI_ASSERT(start >= 0 && start < m_vertices.size());
       
  1730 		RI_ASSERT(end >= 0 && end < m_vertices.size());
       
  1731 
       
  1732 		if(distance >= m_vertices[start].pathLength && distance < m_vertices[end].pathLength)
       
  1733 		{	//segment contains the queried distance
       
  1734 			for(int i=start;i<end;i++)
       
  1735 			{
       
  1736 				const Vertex& v0 = m_vertices[i];
       
  1737 				const Vertex& v1 = m_vertices[i+1];
       
  1738 				if(distance >= v0.pathLength && distance < v1.pathLength)
       
  1739 				{	//segment found, interpolate linearly between its end points
       
  1740 					RIfloat edgeLength = v1.pathLength - v0.pathLength;
       
  1741 					RI_ASSERT(edgeLength > 0.0f);
       
  1742 					RIfloat r = (distance - v0.pathLength) / edgeLength;
       
  1743 					p = (1.0f - r) * v0.userPosition + r * v1.userPosition;
       
  1744 					t = (1.0f - r) * v0.userTangent + r * v1.userTangent;
       
  1745 					return;
       
  1746 				}
       
  1747 			}
       
  1748 		}
       
  1749 	}
       
  1750 
       
  1751 	RI_ASSERT(0);	//point not found (should never get here)
       
  1752 }
       
  1753 
       
  1754 /*-------------------------------------------------------------------*//*!
       
  1755 * \brief	Tessellates a path, and computes its length.
       
  1756 * \param	
       
  1757 * \return	
       
  1758 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
  1759 *//*-------------------------------------------------------------------*/
       
  1760 
       
  1761 RIfloat Path::getPathLength(int startIndex, int numSegments)
       
  1762 {
       
  1763 	RI_ASSERT(m_referenceCount > 0);
       
  1764 	RI_ASSERT(startIndex >= 0 && startIndex + numSegments <= m_segments.size() && numSegments > 0);
       
  1765 
       
  1766 	Matrix3x3 identity;
       
  1767 	identity.identity();
       
  1768 	tessellate(identity, 0.0f);	//throws bad_alloc
       
  1769 
       
  1770 	RI_ASSERT(startIndex >= 0 && startIndex < m_segmentToVertex.size());
       
  1771 	RI_ASSERT(startIndex + numSegments >= 0 && startIndex + numSegments <= m_segmentToVertex.size());
       
  1772 
       
  1773 	int startVertex = m_segmentToVertex[startIndex].start;
       
  1774 	int endVertex = m_segmentToVertex[startIndex + numSegments - 1].end;
       
  1775 
       
  1776 	if(!m_vertices.size())
       
  1777 		return 0.0f;
       
  1778 
       
  1779 	RIfloat startPathLength = 0.0f;
       
  1780 	if(startVertex >= 0)
       
  1781 	{
       
  1782 		RI_ASSERT(startVertex >= 0 && startVertex < m_vertices.size());
       
  1783 		startPathLength = m_vertices[startVertex].pathLength;
       
  1784 	}
       
  1785 	RIfloat endPathLength = 0.0f;
       
  1786 	if(endVertex >= 0)
       
  1787 	{
       
  1788 		RI_ASSERT(endVertex >= 0 && endVertex < m_vertices.size());
       
  1789 		endPathLength = m_vertices[endVertex].pathLength;
       
  1790 	}
       
  1791 
       
  1792 	return endPathLength - startPathLength;
       
  1793 }
       
  1794 
       
  1795 /*-------------------------------------------------------------------*//*!
       
  1796 * \brief	Tessellates a path, and computes its bounding box in user space.
       
  1797 * \param	
       
  1798 * \return	
       
  1799 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
  1800 *//*-------------------------------------------------------------------*/
       
  1801 
       
  1802 void Path::getPathBounds(RIfloat& minx, RIfloat& miny, RIfloat& maxx, RIfloat& maxy)
       
  1803 {
       
  1804 	RI_ASSERT(m_referenceCount > 0);
       
  1805 
       
  1806 	Matrix3x3 identity;
       
  1807 	identity.identity();
       
  1808 	tessellate(identity, 0.0f);	//throws bad_alloc
       
  1809 
       
  1810 	if(m_vertices.size())
       
  1811 	{
       
  1812 		minx = m_userMinx;
       
  1813 		miny = m_userMiny;
       
  1814 		maxx = m_userMaxx;
       
  1815 		maxy = m_userMaxy;
       
  1816 	}
       
  1817 	else
       
  1818 	{
       
  1819 		minx = miny = 0;
       
  1820 		maxx = maxy = -1;
       
  1821 	}
       
  1822 }
       
  1823 
       
  1824 /*-------------------------------------------------------------------*//*!
       
  1825 * \brief	Tessellates a path, and computes its bounding box in surface space.
       
  1826 * \param	
       
  1827 * \return	
       
  1828 * \note		if runs out of memory, throws bad_alloc and leaves the path as it was
       
  1829 *//*-------------------------------------------------------------------*/
       
  1830 
       
  1831 void Path::getPathTransformedBounds(const Matrix3x3& pathToSurface, RIfloat& minx, RIfloat& miny, RIfloat& maxx, RIfloat& maxy)
       
  1832 {
       
  1833 	RI_ASSERT(m_referenceCount > 0);
       
  1834 	RI_ASSERT(pathToSurface.isAffine());
       
  1835 
       
  1836 	Matrix3x3 identity;
       
  1837 	identity.identity();
       
  1838 	tessellate(identity, 0.0f);	//throws bad_alloc
       
  1839 
       
  1840 	if(m_vertices.size())
       
  1841 	{
       
  1842 		Vector3 p0(m_userMinx, m_userMiny, 1.0f);
       
  1843 		Vector3 p1(m_userMinx, m_userMaxy, 1.0f);
       
  1844 		Vector3 p2(m_userMaxx, m_userMaxy, 1.0f);
       
  1845 		Vector3 p3(m_userMaxx, m_userMiny, 1.0f);
       
  1846 		p0 = pathToSurface * p0;
       
  1847 		p1 = pathToSurface * p1;
       
  1848 		p2 = pathToSurface * p2;
       
  1849 		p3 = pathToSurface * p3;
       
  1850 
       
  1851 		minx = RI_MIN(RI_MIN(RI_MIN(p0.x, p1.x), p2.x), p3.x);
       
  1852 		miny = RI_MIN(RI_MIN(RI_MIN(p0.y, p1.y), p2.y), p3.y);
       
  1853 		maxx = RI_MAX(RI_MAX(RI_MAX(p0.x, p1.x), p2.x), p3.x);
       
  1854 		maxy = RI_MAX(RI_MAX(RI_MAX(p0.y, p1.y), p2.y), p3.y);
       
  1855 	}
       
  1856 	else
       
  1857 	{
       
  1858 		minx = miny = 0;
       
  1859 		maxx = maxy = -1;
       
  1860 	}
       
  1861 }
       
  1862 
       
  1863 /*-------------------------------------------------------------------*//*!
       
  1864 * \brief	Adds a vertex to a tessellated path.
       
  1865 * \param	
       
  1866 * \return	
       
  1867 * \note		
       
  1868 *//*-------------------------------------------------------------------*/
       
  1869 
       
  1870 void Path::addVertex(const Vector2& p, const Vector2& t, RIfloat pathLength, unsigned int flags)
       
  1871 {
       
  1872 	RI_ASSERT(!isZero(t));
       
  1873 
       
  1874 	Vertex v;
       
  1875 	v.pathLength = pathLength;
       
  1876 	v.userPosition = p;
       
  1877 	v.userTangent = t;
       
  1878 	v.flags = flags;
       
  1879 	m_vertices.push_back(v);	//throws bad_alloc
       
  1880     m_numTessVertices++;
       
  1881 
       
  1882 	m_userMinx = RI_MIN(m_userMinx, v.userPosition.x);
       
  1883 	m_userMiny = RI_MIN(m_userMiny, v.userPosition.y);
       
  1884 	m_userMaxx = RI_MAX(m_userMaxx, v.userPosition.x);
       
  1885 	m_userMaxy = RI_MAX(m_userMaxy, v.userPosition.y);
       
  1886 }
       
  1887 
       
  1888 /*-------------------------------------------------------------------*//*!
       
  1889 * \brief	Adds an edge to a tessellated path.
       
  1890 * \param	
       
  1891 * \return	
       
  1892 * \note		
       
  1893 *//*-------------------------------------------------------------------*/
       
  1894 
       
  1895 void Path::addEdge(const Vector2& p0, const Vector2& p1, const Vector2& t0, const Vector2& t1, unsigned int startFlags, unsigned int endFlags)
       
  1896 {
       
  1897 	Vertex v;
       
  1898 	RIfloat pathLength = 0.0f;
       
  1899 
       
  1900 	RI_ASSERT(!isZero(t0) && !isZero(t1));
       
  1901 
       
  1902 	//segment midpoints are shared between edges
       
  1903 	if(!m_numTessVertices)
       
  1904 	{
       
  1905 		if(m_vertices.size() > 0)
       
  1906 			pathLength = m_vertices[m_vertices.size()-1].pathLength;
       
  1907 
       
  1908 		addVertex(p0, t0, pathLength, startFlags);	//throws bad_alloc
       
  1909 	}
       
  1910 
       
  1911 	//other than implicit close paths (caused by a MOVE_TO) add to path length
       
  1912 	if( !(endFlags & IMPLICIT_CLOSE_SUBPATH) )
       
  1913 	{
       
  1914 		//NOTE: with extremely large coordinates the floating point path length is infinite
       
  1915 		RIfloat l = (p1 - p0).length();
       
  1916 		pathLength = m_vertices[m_vertices.size()-1].pathLength + l;
       
  1917 		pathLength = RI_MIN(pathLength, RI_FLOAT_MAX);
       
  1918 	}
       
  1919 
       
  1920 	addVertex(p1, t1, pathLength, endFlags);	//throws bad_alloc
       
  1921 }
       
  1922 
       
  1923 /*-------------------------------------------------------------------*//*!
       
  1924 * \brief	Tessellates a close-path segment.
       
  1925 * \param	
       
  1926 * \return	
       
  1927 * \note		
       
  1928 *//*-------------------------------------------------------------------*/
       
  1929 
       
  1930 void Path::addEndPath(const Matrix3x3& pathToSurface, const Vector2& p0, const Vector2& p1, bool subpathHasGeometry, unsigned int flags)
       
  1931 {
       
  1932 	RI_UNREF(pathToSurface);
       
  1933     m_numTessVertices = 0;
       
  1934 	if(!subpathHasGeometry)
       
  1935 	{	//single vertex
       
  1936 		Vector2 t(1.0f,0.0f);
       
  1937 		addEdge(p0, p1, t, t, START_SEGMENT | START_SUBPATH, END_SEGMENT | END_SUBPATH);	//throws bad_alloc
       
  1938         m_numTessVertices = 0;
       
  1939 		addEdge(p0, p1, -t, -t, IMPLICIT_CLOSE_SUBPATH | START_SEGMENT, IMPLICIT_CLOSE_SUBPATH | END_SEGMENT);	//throws bad_alloc
       
  1940 		return;
       
  1941 	}
       
  1942 	//the subpath contains segment commands that have generated geometry
       
  1943 
       
  1944 	//add a close path segment to the start point of the subpath
       
  1945 	RI_ASSERT(m_vertices.size() > 0);
       
  1946 	m_vertices[m_vertices.size()-1].flags |= END_SUBPATH;
       
  1947 
       
  1948 	Vector2 t = normalize(p1 - p0);
       
  1949 	if(isZero(t))
       
  1950 		t = m_vertices[m_vertices.size()-1].userTangent;	//if the segment is zero-length, use the tangent of the last segment end point so that proper join will be generated
       
  1951 	RI_ASSERT(!isZero(t));
       
  1952 
       
  1953 	addEdge(p0, p1, t, t, flags | START_SEGMENT, flags | END_SEGMENT);	//throws bad_alloc
       
  1954 }
       
  1955 
       
  1956 /*-------------------------------------------------------------------*//*!
       
  1957 * \brief	Tessellates a line-to segment.
       
  1958 * \param	
       
  1959 * \return	
       
  1960 * \note		
       
  1961 *//*-------------------------------------------------------------------*/
       
  1962 
       
  1963 bool Path::addLineTo(const Matrix3x3& pathToSurface, const Vector2& p0, const Vector2& p1, bool subpathHasGeometry)
       
  1964 {
       
  1965 	RI_UNREF(pathToSurface);
       
  1966 	if(p0 == p1)
       
  1967 		return false;	//discard zero-length segments
       
  1968 
       
  1969 	//compute end point tangents
       
  1970 	Vector2 t = normalize(p1 - p0);
       
  1971 	RI_ASSERT(!isZero(t));
       
  1972 
       
  1973     m_numTessVertices = 0;
       
  1974 	unsigned int startFlags = START_SEGMENT;
       
  1975 	if(!subpathHasGeometry)
       
  1976 		startFlags |= START_SUBPATH;
       
  1977 	addEdge(p0, p1, t, t, startFlags, END_SEGMENT);	//throws bad_alloc
       
  1978 	return true;
       
  1979 }
       
  1980 
       
  1981 /*-------------------------------------------------------------------*//*!
       
  1982 * \brief	Tessellates a quad-to segment.
       
  1983 * \param	
       
  1984 * \return	
       
  1985 * \note		
       
  1986 *//*-------------------------------------------------------------------*/
       
  1987 
       
  1988 bool Path::addQuadTo(const Matrix3x3& pathToSurface, const Vector2& p0, const Vector2& p1, const Vector2& p2, bool subpathHasGeometry, float strokeWidth)
       
  1989 {
       
  1990 	RI_UNREF(pathToSurface);
       
  1991 	RI_UNREF(strokeWidth);
       
  1992 	if(p0 == p1 && p0 == p2)
       
  1993 	{
       
  1994 		RI_ASSERT(p1 == p2);
       
  1995 		return false;	//discard zero-length segments
       
  1996 	}
       
  1997 
       
  1998 	//compute end point tangents
       
  1999 
       
  2000 	Vector2 incomingTangent = normalize(p1 - p0);
       
  2001 	Vector2 outgoingTangent = normalize(p2 - p1);
       
  2002 	if(p0 == p1)
       
  2003 		incomingTangent = normalize(p2 - p0);
       
  2004 	if(p1 == p2)
       
  2005 		outgoingTangent = normalize(p2 - p0);
       
  2006 	RI_ASSERT(!isZero(incomingTangent) && !isZero(outgoingTangent));
       
  2007 
       
  2008     m_numTessVertices = 0;
       
  2009 	unsigned int startFlags = START_SEGMENT;
       
  2010 	if(!subpathHasGeometry)
       
  2011 		startFlags |= START_SUBPATH;
       
  2012 
       
  2013 	const int segments = RI_NUM_TESSELLATED_SEGMENTS;
       
  2014 	Vector2 pp = p0;
       
  2015 	Vector2 tp = incomingTangent;
       
  2016 	unsigned int prevFlags = startFlags;
       
  2017 	for(int i=1;i<segments;i++)
       
  2018 	{
       
  2019 		RIfloat t = (RIfloat)i / (RIfloat)segments;
       
  2020 		RIfloat u = 1.0f-t;
       
  2021 		Vector2 pn = u*u * p0 + 2.0f*t*u * p1 + t*t * p2;
       
  2022 		Vector2 tn = (-1.0f+t) * p0 + (1.0f-2.0f*t) * p1 + t * p2;
       
  2023 		tn = normalize(tn);
       
  2024 		if(isZero(tn))
       
  2025 			tn = tp;
       
  2026 
       
  2027 		addEdge(pp, pn, tp, tn, prevFlags, 0);	//throws bad_alloc
       
  2028 
       
  2029 		pp = pn;
       
  2030 		tp = tn;
       
  2031 		prevFlags = 0;
       
  2032 	}
       
  2033 	addEdge(pp, p2, tp, outgoingTangent, prevFlags, END_SEGMENT);	//throws bad_alloc
       
  2034 	return true;
       
  2035 }
       
  2036 
       
  2037 /*-------------------------------------------------------------------*//*!
       
  2038 * \brief	Tessellates a cubic-to segment.
       
  2039 * \param	
       
  2040 * \return	
       
  2041 * \note		
       
  2042 *//*-------------------------------------------------------------------*/
       
  2043 
       
  2044 bool Path::addCubicTo(const Matrix3x3& pathToSurface, const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3, bool subpathHasGeometry, float strokeWidth)
       
  2045 {
       
  2046 	RI_UNREF(pathToSurface);
       
  2047 	RI_UNREF(strokeWidth);
       
  2048 
       
  2049 	if(p0 == p1 && p0 == p2 && p0 == p3)
       
  2050 	{
       
  2051 		RI_ASSERT(p1 == p2 && p1 == p3 && p2 == p3);
       
  2052 		return false;	//discard zero-length segments
       
  2053 	}
       
  2054 
       
  2055 	//compute end point tangents
       
  2056 	Vector2 incomingTangent = normalize(p1 - p0);
       
  2057 	Vector2 outgoingTangent = normalize(p3 - p2);
       
  2058 	if(p0 == p1)
       
  2059 	{
       
  2060 		incomingTangent = normalize(p2 - p0);
       
  2061 		if(p1 == p2)
       
  2062 			incomingTangent = normalize(p3 - p0);
       
  2063 	}
       
  2064 	if(p2 == p3)
       
  2065 	{
       
  2066 		outgoingTangent = normalize(p3 - p1);
       
  2067 		if(p1 == p2)
       
  2068 			outgoingTangent = normalize(p3 - p0);
       
  2069 	}
       
  2070 	RI_ASSERT(!isZero(incomingTangent) && !isZero(outgoingTangent));
       
  2071 
       
  2072     m_numTessVertices = 0;
       
  2073 	unsigned int startFlags = START_SEGMENT;
       
  2074 	if(!subpathHasGeometry)
       
  2075 		startFlags |= START_SUBPATH;
       
  2076 
       
  2077 	const int segments = RI_NUM_TESSELLATED_SEGMENTS;
       
  2078 	Vector2 pp = p0;
       
  2079 	Vector2 tp = incomingTangent;
       
  2080 	unsigned int prevFlags = startFlags;
       
  2081 	for(int i=1;i<segments;i++)
       
  2082 	{
       
  2083 		RIfloat t = (RIfloat)i / (RIfloat)segments;
       
  2084 		Vector2 pn = (1.0f - 3.0f*t + 3.0f*t*t - t*t*t) * p0 + (3.0f*t - 6.0f*t*t + 3.0f*t*t*t) * p1 + (3.0f*t*t - 3.0f*t*t*t) * p2 + t*t*t * p3;
       
  2085 		Vector2 tn = (-3.0f + 6.0f*t - 3.0f*t*t) * p0 + (3.0f - 12.0f*t + 9.0f*t*t) * p1 + (6.0f*t - 9.0f*t*t) * p2 + 3.0f*t*t * p3;
       
  2086 
       
  2087 		tn = normalize(tn);
       
  2088 		if(isZero(tn))
       
  2089 			tn = tp;
       
  2090 
       
  2091 		addEdge(pp, pn, tp, tn, prevFlags, 0);	//throws bad_alloc
       
  2092 
       
  2093 		pp = pn;
       
  2094 		tp = tn;
       
  2095 		prevFlags = 0;
       
  2096 	}
       
  2097 	addEdge(pp, p3, tp, outgoingTangent, prevFlags, END_SEGMENT);	//throws bad_alloc
       
  2098 	return true;
       
  2099 }
       
  2100 
       
  2101 /*-------------------------------------------------------------------*//*!
       
  2102 * \brief	Finds an ellipse center and transformation from the unit circle to
       
  2103 *			that ellipse.
       
  2104 * \param	rh Length of the horizontal axis
       
  2105 *			rv Length of the vertical axis
       
  2106 *			rot Rotation angle
       
  2107 *			p0,p1 User space end points of the arc
       
  2108 *			c0,c1 (Return value) Unit circle space center points of the two ellipses
       
  2109 *			u0,u1 (Return value) Unit circle space end points of the arc
       
  2110 *			unitCircleToEllipse (Return value) A matrix mapping from unit circle space to user space
       
  2111 * \return	true if ellipse exists, false if doesn't
       
  2112 * \note		
       
  2113 *//*-------------------------------------------------------------------*/
       
  2114 
       
  2115 static bool findEllipses(RIfloat rh, RIfloat rv, RIfloat rot, const Vector2& p0, const Vector2& p1, VGPathSegment segment, Vector2& c0, Vector2& c1, Vector2& u0, Vector2& u1, Matrix3x3& unitCircleToEllipse, bool& cw)
       
  2116 {
       
  2117 	rh = RI_ABS(rh);
       
  2118 	rv = RI_ABS(rv);
       
  2119 	if(rh == 0.0f || rv == 0.0f || p0 == p1)
       
  2120 		return false;	//degenerate ellipse
       
  2121 
       
  2122 	rot = RI_DEG_TO_RAD(rot);
       
  2123 	unitCircleToEllipse.set((RIfloat)cos(rot)*rh, -(RIfloat)sin(rot)*rv,  0,
       
  2124 							(RIfloat)sin(rot)*rh,  (RIfloat)cos(rot)*rv,  0,
       
  2125 							0,                   0,                   1);
       
  2126 	Matrix3x3 ellipseToUnitCircle = invert(unitCircleToEllipse);
       
  2127 	//force affinity
       
  2128 	ellipseToUnitCircle[2][0] = 0.0f;
       
  2129 	ellipseToUnitCircle[2][1] = 0.0f;
       
  2130 	ellipseToUnitCircle[2][2] = 1.0f;
       
  2131 
       
  2132 	// Transform p0 and p1 into unit space
       
  2133 	u0 = affineTransform(ellipseToUnitCircle, p0);
       
  2134 	u1 = affineTransform(ellipseToUnitCircle, p1);
       
  2135 
       
  2136 	Vector2 m = 0.5f * (u0 + u1);
       
  2137 	Vector2 d = u0 - u1;
       
  2138 
       
  2139 	RIfloat lsq = (RIfloat)dot(d,d);
       
  2140 	if(lsq <= 0.0f)
       
  2141 		return false;	//the points are coincident
       
  2142 
       
  2143 	RIfloat disc = (1.0f / lsq) - 0.25f;
       
  2144 	if(disc < 0.0f)
       
  2145 	{	//the points are too far apart for a solution to exist, scale the axes so that there is a solution
       
  2146 		RIfloat l = (RIfloat)sqrt(lsq);
       
  2147 		rh *= 0.5f * l;
       
  2148 		rv *= 0.5f * l;
       
  2149 
       
  2150 		//redo the computation with scaled axes
       
  2151 		unitCircleToEllipse.set((RIfloat)cos(rot)*rh, -(RIfloat)sin(rot)*rv,  0,
       
  2152 								(RIfloat)sin(rot)*rh,  (RIfloat)cos(rot)*rv,  0,
       
  2153 								0,                   0,                   1);
       
  2154 		ellipseToUnitCircle = invert(unitCircleToEllipse);
       
  2155 		//force affinity
       
  2156 		ellipseToUnitCircle[2][0] = 0.0f;
       
  2157 		ellipseToUnitCircle[2][1] = 0.0f;
       
  2158 		ellipseToUnitCircle[2][2] = 1.0f;
       
  2159 
       
  2160 		// Transform p0 and p1 into unit space
       
  2161 		u0 = affineTransform(ellipseToUnitCircle, p0);
       
  2162 		u1 = affineTransform(ellipseToUnitCircle, p1);
       
  2163 
       
  2164 		// Solve for intersecting unit circles
       
  2165 		d = u0 - u1;
       
  2166 		m = 0.5f * (u0 + u1);
       
  2167 
       
  2168 		lsq = dot(d,d);
       
  2169 		if(lsq <= 0.0f)
       
  2170 			return false;	//the points are coincident
       
  2171 
       
  2172 		disc = RI_MAX(0.0f, 1.0f / lsq - 0.25f);
       
  2173 	}
       
  2174 
       
  2175 	if(u0 == u1)
       
  2176 		return false;
       
  2177 
       
  2178 	Vector2 sd = d * (RIfloat)sqrt(disc);
       
  2179 	Vector2 sp = perpendicularCW(sd);
       
  2180 	c0 = m + sp;
       
  2181 	c1 = m - sp;
       
  2182 
       
  2183 	//choose the center point and direction
       
  2184 	Vector2 cp = c0;
       
  2185 	if(segment == VG_SCWARC_TO || segment == VG_LCCWARC_TO)
       
  2186 		cp = c1;
       
  2187 	cw = false;
       
  2188 	if(segment == VG_SCWARC_TO || segment == VG_LCWARC_TO)
       
  2189 		cw = true;
       
  2190 
       
  2191 	//move the unit circle origin to the chosen center point
       
  2192 	u0 -= cp;
       
  2193 	u1 -= cp;
       
  2194 
       
  2195 	if(u0 == u1 || isZero(u0) || isZero(u1))
       
  2196 		return false;
       
  2197 
       
  2198 	//transform back to the original coordinate space
       
  2199 	cp = affineTransform(unitCircleToEllipse, cp);
       
  2200 	unitCircleToEllipse[0][2] = cp.x;
       
  2201 	unitCircleToEllipse[1][2] = cp.y;
       
  2202 	return true;
       
  2203 }
       
  2204 
       
  2205 /*-------------------------------------------------------------------*//*!
       
  2206 * \brief	Tessellates an arc-to segment.
       
  2207 * \param	
       
  2208 * \return	
       
  2209 * \note		
       
  2210 *//*-------------------------------------------------------------------*/
       
  2211 
       
  2212 bool Path::addArcTo(const Matrix3x3& pathToSurface, const Vector2& p0, RIfloat rh, RIfloat rv, RIfloat rot, const Vector2& p1, const Vector2& p1r, VGPathSegment segment, bool subpathHasGeometry, float strokeWidth)
       
  2213 {
       
  2214 	RI_UNREF(pathToSurface);
       
  2215 	RI_UNREF(strokeWidth);
       
  2216 	if(p0 == p1)
       
  2217 		return false;	//discard zero-length segments
       
  2218 
       
  2219 	Vector2 c0, c1, u0, u1;
       
  2220 	Matrix3x3 unitCircleToEllipse;
       
  2221 	bool cw;
       
  2222 
       
  2223     m_numTessVertices = 0;
       
  2224 	unsigned int startFlags = START_SEGMENT;
       
  2225 	if(!subpathHasGeometry)
       
  2226 		startFlags |= START_SUBPATH;
       
  2227 
       
  2228 	if(!findEllipses(rh, rv, rot, Vector2(), p1r, segment, c0, c1, u0, u1, unitCircleToEllipse, cw))
       
  2229 	{	//ellipses don't exist, add line instead
       
  2230 		Vector2 t = normalize(p1r);
       
  2231 		RI_ASSERT(!isZero(t));
       
  2232 		addEdge(p0, p1, t, t, startFlags, END_SEGMENT);	//throws bad_alloc
       
  2233 		return true;
       
  2234 	}
       
  2235 
       
  2236 	//compute end point tangents
       
  2237 	Vector2 incomingTangent = perpendicular(u0, cw);
       
  2238 	incomingTangent = affineTangentTransform(unitCircleToEllipse, incomingTangent);
       
  2239 	incomingTangent = normalize(incomingTangent);
       
  2240 	Vector2 outgoingTangent = perpendicular(u1, cw);
       
  2241 	outgoingTangent = affineTangentTransform(unitCircleToEllipse, outgoingTangent);
       
  2242 	outgoingTangent = normalize(outgoingTangent);
       
  2243 	RI_ASSERT(!isZero(incomingTangent) && !isZero(outgoingTangent));
       
  2244 
       
  2245 	const int segments = RI_NUM_TESSELLATED_SEGMENTS;
       
  2246 	Vector2 pp = p0;
       
  2247 	Vector2 tp = incomingTangent;
       
  2248 	unsigned int prevFlags = startFlags;
       
  2249 	for(int i=1;i<segments;i++)
       
  2250 	{
       
  2251 		RIfloat t = (RIfloat)i / (RIfloat)segments;
       
  2252 		Vector2 pn = circularLerp(u0, u1, t, cw);
       
  2253 		Vector2 tn = perpendicular(pn, cw);
       
  2254 		tn = affineTangentTransform(unitCircleToEllipse, tn);
       
  2255 		pn = affineTransform(unitCircleToEllipse, pn) + p0;
       
  2256 		tn = normalize(tn);
       
  2257 		if(isZero(tn))
       
  2258 			tn = tp;
       
  2259 
       
  2260 		addEdge(pp, pn, tp, tn, prevFlags, 0);	//throws bad_alloc
       
  2261 
       
  2262 		pp = pn;
       
  2263 		tp = tn;
       
  2264 		prevFlags = 0;
       
  2265 	}
       
  2266 	addEdge(pp, p1, tp, outgoingTangent, prevFlags, END_SEGMENT);	//throws bad_alloc
       
  2267 	return true;
       
  2268 }
       
  2269 
       
  2270 /*-------------------------------------------------------------------*//*!
       
  2271 * \brief	Tessellates a path.
       
  2272 * \param	
       
  2273 * \return	
       
  2274 * \note		tessellation output format: A list of vertices describing the
       
  2275 *			path tessellated into line segments and relevant aspects of the
       
  2276 *			input data. Each path segment has a start vertex, a number of
       
  2277 *			internal vertices (possibly zero), and an end vertex. The start
       
  2278 *			and end of segments and subpaths have been flagged, as well as
       
  2279 *			implicit and explicit close subpath segments.
       
  2280 *//*-------------------------------------------------------------------*/
       
  2281 
       
  2282 void Path::tessellate(const Matrix3x3& pathToSurface, float strokeWidth)
       
  2283 {
       
  2284 	m_vertices.clear();
       
  2285 
       
  2286 	m_userMinx = RI_FLOAT_MAX;
       
  2287 	m_userMiny = RI_FLOAT_MAX;
       
  2288 	m_userMaxx = -RI_FLOAT_MAX;
       
  2289 	m_userMaxy = -RI_FLOAT_MAX;
       
  2290 
       
  2291 	try
       
  2292 	{
       
  2293 		m_segmentToVertex.resize(m_segments.size());
       
  2294 
       
  2295 		int coordIndex = 0;
       
  2296 		Vector2 s(0,0);		//the beginning of the current subpath
       
  2297 		Vector2 o(0,0);		//the last point of the previous segment
       
  2298 		Vector2 p(0,0);		//the last internal control point of the previous segment, if the segment was a (regular or smooth) quadratic or cubic Bezier, or else the last point of the previous segment
       
  2299 
       
  2300 		//tessellate the path segments
       
  2301 		coordIndex = 0;
       
  2302 		s.set(0,0);
       
  2303 		o.set(0,0);
       
  2304 		p.set(0,0);
       
  2305 		bool subpathHasGeometry = false;
       
  2306 		VGPathSegment prevSegment = VG_MOVE_TO;
       
  2307 		for(int i=0;i<m_segments.size();i++)
       
  2308 		{
       
  2309 			VGPathSegment segment = getPathSegment(m_segments[i]);
       
  2310 			VGPathAbsRel absRel = getPathAbsRel(m_segments[i]);
       
  2311 			int coords = segmentToNumCoordinates(segment);
       
  2312 			m_segmentToVertex[i].start = m_vertices.size();
       
  2313 
       
  2314 			switch(segment)
       
  2315 			{
       
  2316 			case VG_CLOSE_PATH:
       
  2317 			{
       
  2318 				RI_ASSERT(coords == 0);
       
  2319 				addEndPath(pathToSurface, o, s, subpathHasGeometry, CLOSE_SUBPATH);
       
  2320 				p = s;
       
  2321 				o = s;
       
  2322 				subpathHasGeometry = false;
       
  2323 				break;
       
  2324 			}
       
  2325 
       
  2326 			case VG_MOVE_TO:
       
  2327 			{
       
  2328 				RI_ASSERT(coords == 2);
       
  2329 				Vector2 c(getCoordinate(coordIndex+0), getCoordinate(coordIndex+1));
       
  2330 				if(absRel == VG_RELATIVE)
       
  2331 					c += o;
       
  2332 				if(prevSegment != VG_MOVE_TO && prevSegment != VG_CLOSE_PATH)
       
  2333 					addEndPath(pathToSurface, o, s, subpathHasGeometry, IMPLICIT_CLOSE_SUBPATH);
       
  2334 				s = c;
       
  2335 				p = c;
       
  2336 				o = c;
       
  2337 				subpathHasGeometry = false;
       
  2338 				break;
       
  2339 			}
       
  2340 
       
  2341 			case VG_LINE_TO:
       
  2342 			{
       
  2343 				RI_ASSERT(coords == 2);
       
  2344 				Vector2 c(getCoordinate(coordIndex+0), getCoordinate(coordIndex+1));
       
  2345 				if(absRel == VG_RELATIVE)
       
  2346 					c += o;
       
  2347 				if(addLineTo(pathToSurface, o, c, subpathHasGeometry))
       
  2348 					subpathHasGeometry = true;
       
  2349 				p = c;
       
  2350 				o = c;
       
  2351 				break;
       
  2352 			}
       
  2353 
       
  2354 			case VG_HLINE_TO:
       
  2355 			{
       
  2356 				RI_ASSERT(coords == 1);
       
  2357 				Vector2 c(getCoordinate(coordIndex+0), o.y);
       
  2358 				if(absRel == VG_RELATIVE)
       
  2359 					c.x += o.x;
       
  2360 				if(addLineTo(pathToSurface, o, c, subpathHasGeometry))
       
  2361 					subpathHasGeometry = true;
       
  2362 				p = c;
       
  2363 				o = c;
       
  2364 				break;
       
  2365 			}
       
  2366 
       
  2367 			case VG_VLINE_TO:
       
  2368 			{
       
  2369 				RI_ASSERT(coords == 1);
       
  2370 				Vector2 c(o.x, getCoordinate(coordIndex+0));
       
  2371 				if(absRel == VG_RELATIVE)
       
  2372 					c.y += o.y;
       
  2373 				if(addLineTo(pathToSurface, o, c, subpathHasGeometry))
       
  2374 					subpathHasGeometry = true;
       
  2375 				p = c;
       
  2376 				o = c;
       
  2377 				break;
       
  2378 			}
       
  2379 
       
  2380 			case VG_QUAD_TO:
       
  2381 			{
       
  2382 				RI_ASSERT(coords == 4);
       
  2383 				Vector2 c0(getCoordinate(coordIndex+0), getCoordinate(coordIndex+1));
       
  2384 				Vector2 c1(getCoordinate(coordIndex+2), getCoordinate(coordIndex+3));
       
  2385 				if(absRel == VG_RELATIVE)
       
  2386 				{
       
  2387 					c0 += o;
       
  2388 					c1 += o;
       
  2389 				}
       
  2390 				if(addQuadTo(pathToSurface, o, c0, c1, subpathHasGeometry, strokeWidth))
       
  2391 					subpathHasGeometry = true;
       
  2392 				p = c0;
       
  2393 				o = c1;
       
  2394 				break;
       
  2395 			}
       
  2396 
       
  2397 			case VG_SQUAD_TO:
       
  2398 			{
       
  2399 				RI_ASSERT(coords == 2);
       
  2400 				Vector2 c0 = 2.0f * o - p;
       
  2401 				Vector2 c1(getCoordinate(coordIndex+0), getCoordinate(coordIndex+1));
       
  2402 				if(absRel == VG_RELATIVE)
       
  2403 					c1 += o;
       
  2404 				if(addQuadTo(pathToSurface, o, c0, c1, subpathHasGeometry, strokeWidth))
       
  2405 					subpathHasGeometry = true;
       
  2406 				p = c0;
       
  2407 				o = c1;
       
  2408 				break;
       
  2409 			}
       
  2410 
       
  2411 			case VG_CUBIC_TO:
       
  2412 			{
       
  2413 				RI_ASSERT(coords == 6);
       
  2414 				Vector2 c0(getCoordinate(coordIndex+0), getCoordinate(coordIndex+1));
       
  2415 				Vector2 c1(getCoordinate(coordIndex+2), getCoordinate(coordIndex+3));
       
  2416 				Vector2 c2(getCoordinate(coordIndex+4), getCoordinate(coordIndex+5));
       
  2417 				if(absRel == VG_RELATIVE)
       
  2418 				{
       
  2419 					c0 += o;
       
  2420 					c1 += o;
       
  2421 					c2 += o;
       
  2422 				}
       
  2423 				if(addCubicTo(pathToSurface, o, c0, c1, c2, subpathHasGeometry, strokeWidth))
       
  2424 					subpathHasGeometry = true;
       
  2425 				p = c1;
       
  2426 				o = c2;
       
  2427 				break;
       
  2428 			}
       
  2429 
       
  2430 			case VG_SCUBIC_TO:
       
  2431 			{
       
  2432 				RI_ASSERT(coords == 4);
       
  2433 				Vector2 c0 = 2.0f * o - p;
       
  2434 				Vector2 c1(getCoordinate(coordIndex+0), getCoordinate(coordIndex+1));
       
  2435 				Vector2 c2(getCoordinate(coordIndex+2), getCoordinate(coordIndex+3));
       
  2436 				if(absRel == VG_RELATIVE)
       
  2437 				{
       
  2438 					c1 += o;
       
  2439 					c2 += o;
       
  2440 				}
       
  2441 				if(addCubicTo(pathToSurface, o, c0, c1, c2, subpathHasGeometry, strokeWidth))
       
  2442 					subpathHasGeometry = true;
       
  2443 				p = c1;
       
  2444 				o = c2;
       
  2445 				break;
       
  2446 			}
       
  2447 
       
  2448 			default:
       
  2449 			{
       
  2450 				RI_ASSERT(segment == VG_SCCWARC_TO || segment == VG_SCWARC_TO ||
       
  2451 						  segment == VG_LCCWARC_TO || segment == VG_LCWARC_TO);
       
  2452 				RI_ASSERT(coords == 5);
       
  2453 				RIfloat rh = getCoordinate(coordIndex+0);
       
  2454 				RIfloat rv = getCoordinate(coordIndex+1);
       
  2455 				RIfloat rot = getCoordinate(coordIndex+2);
       
  2456 				Vector2 c(getCoordinate(coordIndex+3), getCoordinate(coordIndex+4));
       
  2457 
       
  2458                 Vector2 cr = c;
       
  2459 				if(absRel == VG_ABSOLUTE)
       
  2460                     cr -= o;
       
  2461                 else
       
  2462 					c += o;
       
  2463 
       
  2464 				if(addArcTo(pathToSurface, o, rh, rv, rot, c, cr, segment, subpathHasGeometry, strokeWidth))
       
  2465 					subpathHasGeometry = true;
       
  2466 				p = c;
       
  2467 				o = c;
       
  2468 				break;
       
  2469 			}
       
  2470 			}
       
  2471 
       
  2472 			if(m_vertices.size() > m_segmentToVertex[i].start)
       
  2473 			{	//segment produced vertices
       
  2474 				m_segmentToVertex[i].end = m_vertices.size() - 1;
       
  2475 			}
       
  2476 			else
       
  2477 			{	//segment didn't produce vertices (zero-length segment). Ignore it.
       
  2478 				m_segmentToVertex[i].start = m_segmentToVertex[i].end = m_vertices.size()-1;
       
  2479 			}
       
  2480 			prevSegment = segment;
       
  2481 			coordIndex += coords;
       
  2482 		}
       
  2483 
       
  2484 		//add an implicit MOVE_TO to the end to close the last subpath.
       
  2485 		//if the subpath contained only zero-length segments, this produces the necessary geometry to get it stroked
       
  2486 		// and included in path bounds. The geometry won't be included in the pointAlongPath query.
       
  2487 		if(prevSegment != VG_MOVE_TO && prevSegment != VG_CLOSE_PATH)
       
  2488 			addEndPath(pathToSurface, o, s, subpathHasGeometry, IMPLICIT_CLOSE_SUBPATH);
       
  2489 
       
  2490 		//check that the flags are correct
       
  2491 #ifdef RI_DEBUG
       
  2492 		int prev = -1;
       
  2493 		bool subpathStarted = false;
       
  2494 		bool segmentStarted = false;
       
  2495 		for(int i=0;i<m_vertices.size();i++)
       
  2496 		{
       
  2497 			Vertex& v = m_vertices[i];
       
  2498 
       
  2499 			if(v.flags & START_SUBPATH)
       
  2500 			{
       
  2501 				RI_ASSERT(!subpathStarted);
       
  2502 				RI_ASSERT(v.flags & START_SEGMENT);
       
  2503 				RI_ASSERT(!(v.flags & END_SUBPATH));
       
  2504 				RI_ASSERT(!(v.flags & END_SEGMENT));
       
  2505 				RI_ASSERT(!(v.flags & CLOSE_SUBPATH));
       
  2506 				RI_ASSERT(!(v.flags & IMPLICIT_CLOSE_SUBPATH));
       
  2507 				subpathStarted = true;
       
  2508 			}
       
  2509 			
       
  2510 			if(v.flags & START_SEGMENT)
       
  2511 			{
       
  2512 				RI_ASSERT(subpathStarted || (v.flags & CLOSE_SUBPATH) || (v.flags & IMPLICIT_CLOSE_SUBPATH));
       
  2513 				RI_ASSERT(!segmentStarted);
       
  2514 				RI_ASSERT(!(v.flags & END_SUBPATH));
       
  2515 				RI_ASSERT(!(v.flags & END_SEGMENT));
       
  2516 				segmentStarted = true;
       
  2517 			}
       
  2518 			
       
  2519 			if( v.flags & CLOSE_SUBPATH )
       
  2520 			{
       
  2521 				RI_ASSERT(segmentStarted);
       
  2522 				RI_ASSERT(!subpathStarted);
       
  2523 				RI_ASSERT((v.flags & START_SEGMENT) || (v.flags & END_SEGMENT));
       
  2524 				RI_ASSERT(!(v.flags & IMPLICIT_CLOSE_SUBPATH));
       
  2525 				RI_ASSERT(!(v.flags & START_SUBPATH));
       
  2526 				RI_ASSERT(!(v.flags & END_SUBPATH));
       
  2527 			}
       
  2528 			if( v.flags & IMPLICIT_CLOSE_SUBPATH )
       
  2529 			{
       
  2530 				RI_ASSERT(segmentStarted);
       
  2531 				RI_ASSERT(!subpathStarted);
       
  2532 				RI_ASSERT((v.flags & START_SEGMENT) || (v.flags & END_SEGMENT));
       
  2533 				RI_ASSERT(!(v.flags & CLOSE_SUBPATH));
       
  2534 				RI_ASSERT(!(v.flags & START_SUBPATH));
       
  2535 				RI_ASSERT(!(v.flags & END_SUBPATH));
       
  2536 			}
       
  2537 			
       
  2538 			if( prev >= 0 )
       
  2539 			{
       
  2540 				RI_ASSERT(segmentStarted);
       
  2541 				RI_ASSERT(subpathStarted || ((m_vertices[prev].flags & CLOSE_SUBPATH) && (m_vertices[i].flags & CLOSE_SUBPATH)) ||
       
  2542 						  ((m_vertices[prev].flags & IMPLICIT_CLOSE_SUBPATH) && (m_vertices[i].flags & IMPLICIT_CLOSE_SUBPATH)));
       
  2543 			}
       
  2544 
       
  2545 			prev = i;
       
  2546 			if(v.flags & END_SEGMENT)
       
  2547 			{
       
  2548 				RI_ASSERT(subpathStarted || (v.flags & CLOSE_SUBPATH) || (v.flags & IMPLICIT_CLOSE_SUBPATH));
       
  2549 				RI_ASSERT(segmentStarted);
       
  2550 				RI_ASSERT(!(v.flags & START_SUBPATH));
       
  2551 				RI_ASSERT(!(v.flags & START_SEGMENT));
       
  2552 				segmentStarted = false;
       
  2553 				prev = -1;
       
  2554 			}
       
  2555 			
       
  2556 			if(v.flags & END_SUBPATH)
       
  2557 			{
       
  2558 				RI_ASSERT(subpathStarted);
       
  2559 				RI_ASSERT(v.flags & END_SEGMENT);
       
  2560 				RI_ASSERT(!(v.flags & START_SUBPATH));
       
  2561 				RI_ASSERT(!(v.flags & START_SEGMENT));
       
  2562 				RI_ASSERT(!(v.flags & CLOSE_SUBPATH));
       
  2563 				RI_ASSERT(!(v.flags & IMPLICIT_CLOSE_SUBPATH));
       
  2564 				subpathStarted = false;
       
  2565 			}
       
  2566 		}
       
  2567 #endif	//RI_DEBUG
       
  2568 	}
       
  2569 	catch(std::bad_alloc)
       
  2570 	{
       
  2571 		m_vertices.clear();
       
  2572 		throw;
       
  2573 	}
       
  2574 }
       
  2575 
       
  2576 //==============================================================================================
       
  2577 
       
  2578 }		//namespace OpenVGRI
       
  2579 
       
  2580 //==============================================================================================