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