]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/ShivaVG/src/shPath.c
Update for OpenSceneGraph 3.3.2 API changes.
[simgear.git] / simgear / canvas / ShivaVG / src / shPath.c
1 /*
2  * Copyright (c) 2007 Ivan Leben
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  * 
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  * 
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library in the file COPYING;
16  * if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #include <vg/openvg.h>
22 #include "shContext.h"
23 #include "shPath.h"
24 #include <string.h>
25 #include <stdio.h>
26
27 #define _ITEM_T SHVertex
28 #define _ARRAY_T SHVertexArray
29 #define _FUNC_T shVertexArray
30 #define _COMPARE_T(v1,v2) 0
31 #define _ARRAY_DEFINE
32 #include "shArrayBase.h"
33
34 #define _ITEM_T SHPath*
35 #define _ARRAY_T SHPathArray
36 #define _FUNC_T shPathArray
37 #define _ARRAY_DEFINE
38 #include "shArrayBase.h"
39
40
41 static const SHint shCoordsPerCommand[] = {
42   0, /* VG_CLOSE_PATH */
43   2, /* VG_MOTE_TO */
44   2, /* VG_LINE_TO */
45   1, /* VG_HLINE_TO */
46   1, /* VG_VLINE_TO */
47   4, /* VG_QUAD_TO */
48   6, /* VG_CUBIC_TO */
49   2, /* VG_SQUAD_TO */
50   4, /* VG_SCUBIC_TO */
51   5, /* VG_SCCWARC_TO */
52   5, /* VG_SCWARC_TO */
53   5, /* VG_LCCWARC_TO */
54   5  /* VG_LCWARC_TO */
55 };
56
57 #define SH_PATH_MAX_COORDS 6
58 #define SH_PATH_MAX_COORDS_PROCESSED 12
59
60 static const SHint shBytesPerDatatype[] = {
61   1, /* VG_PATH_DATATYPE_S_8 */
62   2, /* VG_PATH_DATATYPE_S_16 */
63   4, /* VG_PATH_DATATYPE_S_32 */
64   4  /* VG_PATH_DATATYPE_F */
65 };
66
67 #define SH_PATH_MAX_BYTES  4
68
69 /*-----------------------------------------------------
70  * Path constructor
71  *-----------------------------------------------------*/
72
73 void shClearSegCallbacks(SHPath *p);
74 void SHPath_ctor(SHPath *p)
75 {
76   p->format = 0;
77   p->scale = 0.0f;
78   p->bias = 0.0f;
79   p->caps = 0;
80   p->datatype = VG_PATH_DATATYPE_F;
81   
82   p->segs = NULL;
83   p->data = NULL;
84   p->segCount = 0;
85   p->dataCount = 0;
86   
87   SH_INITOBJ(SHVertexArray, p->vertices);
88   SH_INITOBJ(SHVector2Array, p->stroke);
89 }
90
91 /*-----------------------------------------------------
92  * Path destructor
93  *-----------------------------------------------------*/
94
95 void SHPath_dtor(SHPath *p)
96 {
97   if (p->segs) free(p->segs);
98   if (p->data) free(p->data);
99   
100   SH_DEINITOBJ(SHVertexArray, p->vertices);
101   SH_DEINITOBJ(SHVector2Array, p->stroke);
102 }
103
104 /*-----------------------------------------------------
105  * Returns true (1) if given path data type is valid
106  *-----------------------------------------------------*/
107
108 static SHint shIsValidDatatype(VGPathDatatype t)
109 {
110   return (t == VG_PATH_DATATYPE_S_8 ||
111           t == VG_PATH_DATATYPE_S_16 ||
112           t == VG_PATH_DATATYPE_S_32 ||
113           t == VG_PATH_DATATYPE_F);
114 }
115
116 static SHint shIsValidCommand(SHint c)
117 {
118   return (c >= (VG_CLOSE_PATH >> 1) &&
119           c <= (VG_LCWARC_TO >> 1));
120 }
121
122 static SHint shIsArcSegment(SHint s)
123 {
124   return (s == VG_SCWARC_TO || s == VG_SCCWARC_TO ||
125           s == VG_LCWARC_TO || s == VG_LCCWARC_TO);
126 }
127
128 /*-------------------------------------------------------
129  * Allocates a path resource in the current context and
130  * sets its capabilities.
131  *-------------------------------------------------------*/
132
133 VG_API_CALL VGPath vgCreatePath(VGint pathFormat,
134                                 VGPathDatatype datatype,
135                                 VGfloat scale, VGfloat bias,
136                                 VGint segmentCapacityHint,
137                                 VGint coordCapacityHint,
138                                 VGbitfield capabilities)
139 {
140   SHPath *p = NULL;
141   VG_GETCONTEXT(VG_INVALID_HANDLE);
142   
143   /* Only standard format supported */
144   VG_RETURN_ERR_IF(pathFormat != VG_PATH_FORMAT_STANDARD,
145                    VG_UNSUPPORTED_PATH_FORMAT_ERROR,
146                    VG_INVALID_HANDLE);
147   
148   /* Dataype must be valid, scale not zero */
149   VG_RETURN_ERR_IF(!shIsValidDatatype(datatype) || scale == 0.0f,
150                    VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);
151   
152   /* Allocate new resource */
153   SH_NEWOBJ(SHPath, p);
154   VG_RETURN_ERR_IF(!p, VG_OUT_OF_MEMORY_ERROR, VG_INVALID_HANDLE);
155   shPathArrayPushBack(&context->paths, p);
156   
157   /* Set parameters */
158   p->format = pathFormat;
159   p->scale = scale;
160   p->bias = bias;
161   p->segHint = segmentCapacityHint;
162   p->dataHint = coordCapacityHint;
163   p->datatype = datatype;
164   p->caps = capabilities & VG_PATH_CAPABILITY_ALL;
165
166   /* Init cache flags */
167   p->cacheDataValid = VG_TRUE;
168   p->cacheTransformInit = VG_FALSE;
169   p->cacheStrokeInit = VG_FALSE;
170   
171   VG_RETURN((VGPath)p);
172 }
173
174 /*-----------------------------------------------------
175  * Clears the specified path of all data and sets new
176  * capabilities to it.
177  *-----------------------------------------------------*/
178
179 VG_API_CALL void vgClearPath(VGPath path, VGbitfield capabilities)
180 {
181   SHPath *p = NULL;
182   VG_GETCONTEXT(VG_NO_RETVAL);
183   
184   VG_RETURN_ERR_IF(!shIsValidPath(context, path),
185                    VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
186   
187   /* Clear raw data */
188   p = (SHPath*)path;
189   free(p->segs);
190   free(p->data);
191   p->segs = NULL;
192   p->data = NULL;
193   p->segCount = 0;
194   p->dataCount = 0;
195
196   /* Mark change */
197   p->cacheDataValid = VG_FALSE;
198   
199   /* Downsize arrays to save memory */
200   shVertexArrayRealloc(&p->vertices, 1);
201   shVector2ArrayRealloc(&p->stroke, 1);
202   
203   /* Re-set capabilities */
204   p->caps = capabilities & VG_PATH_CAPABILITY_ALL;
205   
206   VG_RETURN(VG_NO_RETVAL);
207 }
208
209 /*---------------------------------------------------------
210  * Disposes specified path resource in the current context
211  *---------------------------------------------------------*/
212
213 VG_API_CALL void vgDestroyPath(VGPath path)
214 {
215   int index;
216   VG_GETCONTEXT(VG_NO_RETVAL);
217   
218   /* Check if handle valid */
219   index = shPathArrayFind(&context->paths, (SHPath*)path);
220   VG_RETURN_ERR_IF(index == -1, VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
221   
222   /* Delete object and remove resource */
223   SH_DELETEOBJ(SHPath, (SHPath*)path);
224   shPathArrayRemoveAt(&context->paths, index);
225   
226   VG_RETURN_ERR(VG_NO_ERROR, VG_NO_RETVAL);
227 }
228
229 /*-----------------------------------------------------
230  * Removes capabilities defined in the given bitfield
231  * from the specified path.
232  *-----------------------------------------------------*/
233
234 VG_API_CALL void vgRemovePathCapabilities(VGPath path, VGbitfield capabilities)
235 {
236   VG_GETCONTEXT(VG_NO_RETVAL);
237   
238   VG_RETURN_ERR_IF(!shIsValidPath(context, path),
239                    VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
240   
241   capabilities &= VG_PATH_CAPABILITY_ALL;
242   ((SHPath*)path)->caps &= ~capabilities;
243   
244   VG_RETURN(VG_NO_RETVAL);
245 }
246
247 /*-----------------------------------------------------
248  * Returns capabilities of a path resource
249  *-----------------------------------------------------*/
250
251 VG_API_CALL VGbitfield vgGetPathCapabilities(VGPath path)
252 {
253   VG_GETCONTEXT(0x0);
254   
255   VG_RETURN_ERR_IF(!shIsValidPath(context, path),
256                    VG_BAD_HANDLE_ERROR, 0x0);
257   
258   VG_RETURN( ((SHPath*)path)->caps );
259 }
260
261 /*-----------------------------------------------------
262  * Walks the given path segments and returns the total
263  * number of coordinates.
264  *-----------------------------------------------------*/
265
266 static SHint shCoordCountForData(VGint segcount, const SHuint8 *segs)
267 {
268   int s;
269   int command;
270   int count = 0;
271   
272   for (s=0; s<segcount; ++s) {
273     command = ((segs[s] & 0x1E) >> 1);
274     if (!shIsValidCommand(command)) return -1;
275     count += shCoordsPerCommand[command];
276   }
277   
278   return count;
279 }
280
281 /*-------------------------------------------------------
282  * Interpretes the path data array according to the
283  * path data type and returns the value at given index
284  * in final interpretation (including scale and bias)
285  *-------------------------------------------------------*/
286
287 static SHfloat shRealCoordFromData(VGPathDatatype type, SHfloat scale, SHfloat bias,
288                                    void *data, SHint index)
289 {
290   switch (type) {
291   case VG_PATH_DATATYPE_S_8:
292     return ( (SHfloat) ((SHint8*)data)[index] ) * scale + bias;
293   case VG_PATH_DATATYPE_S_16:
294     return ( (SHfloat) ((SHint16*)data)[index] ) * scale + bias;
295   case VG_PATH_DATATYPE_S_32:
296     return ( (SHfloat) ((SHint32*)data)[index] ) * scale + bias;;
297   default:
298     return ( (SHfloat) ((SHfloat32*)data)[index] ) * scale + bias;
299   }
300 }
301
302 /*-------------------------------------------------------
303  * Interpretes the path data array according to the
304  * path data type and sets the value at given index
305  * from final interpretation (including scale and bias)
306  *-------------------------------------------------------*/
307
308 static void shRealCoordToData(VGPathDatatype type, SHfloat scale, SHfloat bias,
309                               void *data,  SHint index, SHfloat c)
310 {
311   c -= bias;
312   c /= scale;
313   
314   switch (type) {
315   case VG_PATH_DATATYPE_S_8:
316     ((SHint8*)data) [index] = (SHint8)SH_FLOOR(c + 0.5f); break;
317   case VG_PATH_DATATYPE_S_16:
318     ((SHint16*)data) [index] = (SHint16)SH_FLOOR(c + 0.5f); break;
319   case VG_PATH_DATATYPE_S_32:
320     ((SHint32*)data) [index] = (SHint32)SH_FLOOR(c + 0.5f); break;
321   default:
322     ((SHfloat32*)data) [index] = (SHfloat32)c; break;
323   }
324 }
325
326 /*-------------------------------------------------
327  * Resizes storage for segment and coordinate data
328  * of the specified path by given number of items
329  *-------------------------------------------------*/
330
331 static int shResizePathData(SHPath *p, SHint newSegCount, SHint newDataCount,
332                             SHuint8 **newSegs, SHuint8 **newData)
333 {
334   SHint oldDataSize = 0;
335   SHint newDataSize = 0;
336   
337   /* Allocate memory for new segments */
338   (*newSegs) = (SHuint8*)malloc(p->segCount + newSegCount);
339   if ((*newSegs) == NULL) return 0;
340   
341   /* Allocate memory for new data */
342   oldDataSize = p->dataCount * shBytesPerDatatype[p->datatype];
343   newDataSize = newDataCount * shBytesPerDatatype[p->datatype];
344   (*newData) = (SHuint8*)malloc(oldDataSize + newDataSize);
345   if ((*newData) == NULL) {free(*newSegs); return 0;}
346   
347   /* Copy old segments */
348   memcpy(*newSegs, p->segs, p->segCount);
349   
350   /* Copy old data */
351   memcpy(*newData, p->data, oldDataSize);
352   
353   return 1;
354 }
355
356 /*-------------------------------------------------------------
357  * Appends path data from source to destination path resource
358  *-------------------------------------------------------------*/
359
360 VG_API_CALL void vgAppendPath(VGPath dstPath, VGPath srcPath)
361 {
362   int i;
363   SHPath *src, *dst;
364   SHuint8 *newSegs = NULL;
365   SHuint8 *newData = NULL;
366   VG_GETCONTEXT(VG_NO_RETVAL);
367   
368   VG_RETURN_ERR_IF(!shIsValidPath(context, srcPath) ||
369                    !shIsValidPath(context, dstPath),
370                    VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
371   
372   src = (SHPath*)srcPath; dst = (SHPath*)dstPath;
373   VG_RETURN_ERR_IF(!(src->caps & VG_PATH_CAPABILITY_APPEND_FROM) ||
374                    !(dst->caps & VG_PATH_CAPABILITY_APPEND_TO),
375                    VG_PATH_CAPABILITY_ERROR, VG_NO_RETVAL);
376   
377   /* Resize path storage */
378   shResizePathData(dst, src->segCount, src->dataCount, &newSegs, &newData);
379   VG_RETURN_ERR_IF(!newData, VG_OUT_OF_MEMORY_ERROR, VG_NO_RETVAL);
380   
381   /* Copy new segments */
382   memcpy(newSegs+dst->segCount, src->segs, src->segCount);
383   
384   /* Copy new coordinates */
385   for (i=0; i<src->dataCount; ++i) {
386     
387     SHfloat coord = shRealCoordFromData(src->datatype, src->scale,
388                                         src->bias, src->data, i);
389     
390     shRealCoordToData(dst->datatype, dst->scale, dst->bias,
391                       newData, dst->dataCount+i, coord);
392   }
393   
394   /* Free old arrays */
395   free(dst->segs);
396   free(dst->data);
397   
398   /* Adjust new properties */
399   dst->segs = newSegs;
400   dst->data = newData;
401   dst->segCount += src->segCount;
402   dst->dataCount += src->dataCount;
403
404   /* Mark change */
405   dst->cacheDataValid = VG_FALSE;
406   
407   VG_RETURN(VG_NO_RETVAL);
408 }
409
410 /*-----------------------------------------------------
411  * Appends data to destination path resource
412  *-----------------------------------------------------*/
413
414 SHfloat shValidInputFloat(VGfloat f);
415 VG_API_CALL void vgAppendPathData(VGPath dstPath, VGint newSegCount,
416                                   const VGubyte *segs, const void *data)
417 {
418   int i;
419   SHPath *dst = NULL;
420   SHint newDataCount = 0;
421   SHint oldDataSize = 0;
422   SHint newDataSize = 0;
423   SHuint8 *newSegs = NULL;
424   SHuint8 *newData = NULL;
425   VG_GETCONTEXT(VG_NO_RETVAL);
426   
427   VG_RETURN_ERR_IF(!shIsValidPath(context, dstPath),
428                    VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
429   
430   dst = (SHPath*)dstPath;
431   VG_RETURN_ERR_IF(!(dst->caps & VG_PATH_CAPABILITY_APPEND_TO),
432                    VG_PATH_CAPABILITY_ERROR, VG_NO_RETVAL);
433   
434   VG_RETURN_ERR_IF(!segs || !data || newSegCount <= 0,
435                    VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
436   
437   /* TODO: check segment + data array alignment */
438   
439   /* Count number of coordinatess in appended data */
440   newDataCount = shCoordCountForData(newSegCount, segs);
441   newDataSize = newDataCount * shBytesPerDatatype[dst->datatype];
442   oldDataSize = dst->dataCount * shBytesPerDatatype[dst->datatype];
443   VG_RETURN_ERR_IF(newDataCount == -1, VG_ILLEGAL_ARGUMENT_ERROR,
444                    VG_NO_RETVAL);
445   
446   /* Resize path storage */
447   shResizePathData(dst, newSegCount, newDataCount, &newSegs, &newData);
448   VG_RETURN_ERR_IF(!newData, VG_OUT_OF_MEMORY_ERROR, VG_NO_RETVAL);
449   
450   /* Copy new segments */
451   memcpy(newSegs+dst->segCount, segs, newSegCount);
452   
453   /* Copy new coordinates */
454   if (dst->datatype == VG_PATH_DATATYPE_F) {
455     for (i=0; i<newDataCount; ++i)
456       ((SHfloat32*)newData) [dst->dataCount+i] =
457         shValidInputFloat( ((VGfloat*)data) [i] );
458   }else{
459     memcpy(newData+oldDataSize, data, newDataSize);
460   }
461   
462   /* Free old arrays */
463   free(dst->segs);
464   free(dst->data);
465   
466   /* Adjust new properties */
467   dst->segs = newSegs;
468   dst->data = newData;
469   dst->segCount += newSegCount;
470   dst->dataCount += newDataCount;
471
472   /* Mark change */
473   dst->cacheDataValid = VG_FALSE;
474   
475   VG_RETURN(VG_NO_RETVAL);
476 }
477
478 /*--------------------------------------------------------
479  * Modifies the coordinates of the existing path segments
480  *--------------------------------------------------------*/
481
482 VG_API_CALL void vgModifyPathCoords(VGPath dstPath, VGint startIndex,
483                                     VGint numSegments,  const void * data)
484 {
485   int i;
486   SHPath *p;
487   SHint newDataCount;
488   SHint newDataSize;
489   SHint dataStartCount;
490   SHint dataStartSize;
491   VG_GETCONTEXT(VG_NO_RETVAL);
492   
493   VG_RETURN_ERR_IF(!shIsValidPath(context, dstPath),
494                    VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
495   
496   p = (SHPath*)dstPath;
497   VG_RETURN_ERR_IF(!(p->caps & VG_PATH_CAPABILITY_MODIFY),
498                    VG_PATH_CAPABILITY_ERROR, VG_NO_RETVAL);
499   
500   VG_RETURN_ERR_IF(!data || startIndex<0 || numSegments <=0 ||
501                    startIndex + numSegments > p->segCount,
502                    VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
503   
504   /* TODO: check data array alignment */
505   
506   /* Find start of the coordinates to be changed */
507   dataStartCount = shCoordCountForData(startIndex, p->segs);
508   dataStartSize = dataStartCount * shBytesPerDatatype[p->datatype];
509   
510   /* Find byte size of coordinates to be changed */
511   newDataCount = shCoordCountForData(numSegments, &p->segs[startIndex]);
512   newDataSize = newDataCount * shBytesPerDatatype[p->datatype];
513   
514   /* Copy new coordinates */
515   if (p->datatype == VG_PATH_DATATYPE_F) {
516     for (i=0; i<newDataCount; ++i)
517       ((SHfloat32*)p->data) [dataStartCount+i] =
518         shValidInputFloat( ((VGfloat*)data) [i] );
519   }else{
520     memcpy( ((SHuint8*)p->data) + dataStartSize, data, newDataSize);
521   }
522
523   /* Mark change */
524   p->cacheDataValid = VG_FALSE;
525   
526   VG_RETURN(VG_NO_RETVAL);
527 }
528
529 /*------------------------------------------------------------
530  * Converts standart endpoint arc parametrization into center
531  * arc parametrization for further internal processing
532  *------------------------------------------------------------*/
533
534 SHint shCentralizeArc(SHuint command, SHfloat *data)
535 {
536   SHfloat rx,ry,r,a;
537   SHVector2 p1, p2, pp1,pp2;
538   SHVector2 d, dt;
539   SHfloat dist, halfdist, q;
540   SHVector2 c1, c2, *c;
541   SHVector2 pc1, pc2;
542   SHfloat a1, ad, a2;
543   SHVector2 ux, uy;
544   SHMatrix3x3 user2arc;
545   SHMatrix3x3 arc2user;
546   
547   rx = data[2];
548   ry = data[3];
549   a = SH_DEG2RAD(data[4]);
550   SET2(p1, data[0], data[1]);
551   SET2(p2, data[5], data[6]);
552   
553   /* Check for invalid radius and return line */
554   if (SH_NEARZERO(rx) || SH_NEARZERO(ry)) {
555     data[2] = p2.x; data[3] = p2.y;
556     return 0;
557   }
558   
559   /* Rotate to arc coordinates.
560      Scale to correct eccentricity. */
561   IDMAT(user2arc);
562   ROTATEMATL(user2arc, -a);
563   SCALEMATL(user2arc, 1, rx/ry);
564   TRANSFORM2TO(p1, user2arc, pp1);
565   TRANSFORM2TO(p2, user2arc, pp2);
566   
567   /* Distance between points and
568      perpendicular vector */
569   SET2V(d, pp2); SUB2V(d, pp1);
570   dist = NORM2(d);
571   halfdist = dist * 0.5f;
572   SET2(dt, -d.y, d.x);
573   DIV2(dt, dist);
574   
575   /* Check if too close and return line */
576   if (halfdist == 0.0f) {
577     data[2] = p2.x; data[3] = p2.y;
578     return 0;
579   }
580   
581   /* Scale radius if too far away */
582   r = rx;
583   if (halfdist > rx)
584     r = halfdist;
585   
586   /* Intersections of circles centered to start and
587      end point. (i.e. centers of two possible arcs) */
588   q = SH_SIN(SH_ACOS(halfdist/r)) * r;
589   if (SH_ISNAN(q)) q = 0.0f;
590   c1.x = pp1.x + d.x/2 + dt.x * q;
591   c1.y = pp1.y + d.y/2 + dt.y * q;
592   c2.x = pp1.x + d.x/2 - dt.x * q;
593   c2.y = pp1.y + d.y/2 - dt.y * q;
594   
595   /* Pick the right arc center */
596   switch (command & 0x1E) {
597   case VG_SCWARC_TO: case VG_LCCWARC_TO:
598     c = &c2; break;
599   case VG_LCWARC_TO: case VG_SCCWARC_TO:
600     c = &c1; break;
601   default:
602     c = &c1;
603   }
604   
605   /* Find angles of p1,p2 towards the chosen center */
606   SET2V(pc1, pp1); SUB2V(pc1, (*c));
607   SET2V(pc2, pp2); SUB2V(pc2, (*c));
608   a1 = shVectorOrientation(&pc1);
609   
610   /* Correct precision error when ry very small
611      (q gets very large and points very far appart) */
612   if (SH_ISNAN(a1)) a1 = 0.0f;
613   
614   /* Small or large one? */
615   switch (command & 0x1E) {
616   case VG_SCWARC_TO: case VG_SCCWARC_TO:
617     ad = ANGLE2(pc1,pc2); break;
618   case VG_LCWARC_TO: case VG_LCCWARC_TO:
619     ad = 2*PI - ANGLE2(pc1,pc2); break;
620   default:
621     ad = 0.0f;
622   }
623   
624   /* Correct precision error when solution is single
625      (pc1 and pc2 are nearly collinear but not really) */
626   if (SH_ISNAN(ad)) ad = PI;
627   
628   /* CW or CCW? */
629   switch (command & 0x1E) {
630   case VG_SCWARC_TO: case VG_LCWARC_TO:
631     a2 = a1 - ad; break;
632   case VG_SCCWARC_TO: case VG_LCCWARC_TO:
633     a2 = a1 + ad; break;
634   default:
635     a2 = a1;
636   }
637   
638   /* Arc unit vectors */
639   SET2(ux, r, 0);
640   SET2(uy, 0, r);
641   
642   /* Transform back to user coordinates */
643   IDMAT(arc2user);
644   SCALEMATL(arc2user, 1, ry/rx);
645   ROTATEMATL(arc2user, a);
646   TRANSFORM2((*c), arc2user);
647   TRANSFORM2(ux, arc2user);
648   TRANSFORM2(uy, arc2user);
649   
650   /* Write out arc coords */
651   data[2]  = c->x;  data[3]  = c->y;
652   data[4]  = ux.x;  data[5]  = ux.y;
653   data[6]  = uy.x;  data[7]  = uy.y;
654   data[8]  = a1;    data[9]  = a2;
655   data[10] = p2.x;  data[11] = p2.y;
656   return 1;
657 }
658
659 /*-------------------------------------------------------
660  * Walks the raw (standard) path data and simplifies the
661  * complex and implicit segments according to given
662  * simplificatin request flags. Instead of storing the
663  * processed data into another array, the given callback
664  * function is called, so we don't need to walk the
665  * raw data twice just to find the neccessary memory
666  * size for processed data.
667  *-------------------------------------------------------*/
668
669 #define SH_PROCESS_SIMPLIFY_LINES    (1 << 0)
670 #define SH_PROCESS_SIMPLIFY_CURVES   (1 << 1)
671 #define SH_PROCESS_CENTRALIZE_ARCS   (1 << 2)
672 #define SH_PROCESS_REPAIR_ENDS       (1 << 3)
673
674 void shProcessPathData(SHPath *p,
675                        int flags,
676                        SegmentFunc callback,
677                        void *userData)
678 {
679   SHint i=0, s=0, d=0, c=0;
680   SHuint command;
681   SHuint segment;
682   SHint segindex;
683   VGPathAbsRel absrel;
684   SHint numcoords;
685   SHfloat data[SH_PATH_MAX_COORDS_PROCESSED];
686   SHVector2 start; /* start of the current contour */
687   SHVector2 pen; /* current pen position */
688   SHVector2 tan; /* backward tangent for smoothing */
689   SHint open = 0; /* contour-open flag */
690   
691   /* Reset points */
692   SET2(start, 0,0);
693   SET2(pen, 0,0);
694   SET2(tan, 0,0);
695   
696   for (s=0; s<p->segCount; ++s, d+=numcoords) {
697     
698     /* Extract command */
699     command = (p->segs[s]);
700     absrel = (command & 1);
701     segment = (command & 0x1E);
702     segindex = (segment >> 1);
703     numcoords = shCoordsPerCommand[segindex];
704     
705     /* Repair segment start / end */
706     if (flags & SH_PROCESS_REPAIR_ENDS) {
707       
708       /* Prevent double CLOSE_PATH */
709       if (!open && segment == VG_CLOSE_PATH)
710         continue;
711       
712       /* Implicit MOVE_TO if segment starts without */
713       if (!open && segment != VG_MOVE_TO) {
714         data[0] = pen.x; data[1] = pen.y;
715         data[2] = pen.x; data[3] = pen.y;
716         (*callback)(p,VG_MOVE_TO,command,data,userData);
717         open = 1;
718       }
719       
720       /* Avoid a MOVE_TO at the end of data */
721       if (segment == VG_MOVE_TO) {
722         if (s == p->segCount-1) break;
723         else {
724           /* Avoid a lone MOVE_TO  */
725           SHuint nextsegment = (p->segs[s+1] & 0x1E);
726           if (nextsegment == VG_MOVE_TO)
727             {open = 0; continue;}
728         }}
729     }
730     
731     /* Place pen into first two coords */
732     data[0] = pen.x;
733     data[1] = pen.y;
734     c = 2;
735     
736     /* Unpack coordinates from path data */
737     for (i=0; i<numcoords; ++i)
738       data[c++] = shRealCoordFromData(p->datatype, p->scale, p->bias,
739                                       p->data, d+i);
740     
741     /* Simplify complex segments */
742     switch (segment)
743     {
744     case VG_CLOSE_PATH:
745       
746       data[2] = start.x;
747       data[3] = start.y;
748       
749       SET2V(pen, start);
750       SET2V(tan, start);
751       open = 0;
752       
753       (*callback)(p,VG_CLOSE_PATH,command,data,userData);
754       
755       break;
756     case VG_MOVE_TO:
757       
758       if (absrel == VG_RELATIVE) {
759         data[2] += pen.x; data[3] += pen.y;
760       }
761       
762       SET2(pen, data[2], data[3]);
763       SET2V(start, pen);
764       SET2V(tan, pen);
765       open = 1;
766       
767       (*callback)(p,VG_MOVE_TO,command,data,userData);
768       
769       break;
770     case VG_LINE_TO:
771       
772       if (absrel == VG_RELATIVE) {
773         data[2] += pen.x; data[3] += pen.y;
774       }
775       
776       SET2(pen, data[2], data[3]);
777       SET2V(tan, pen);
778       
779       (*callback)(p,VG_LINE_TO,command,data,userData);
780       
781       break;
782     case VG_HLINE_TO:
783       
784       if (absrel == VG_RELATIVE)
785         data[2] += pen.x;
786       
787       SET2(pen, data[2], pen.y);
788       SET2V(tan, pen);
789       
790       if (flags & SH_PROCESS_SIMPLIFY_LINES) {
791         data[3] = pen.y;
792         (*callback)(p,VG_LINE_TO,command,data,userData);
793         break;
794       }
795       
796       (*callback)(p,VG_HLINE_TO,command,data,userData);
797       
798       break;
799     case VG_VLINE_TO:
800       
801       if (absrel == VG_RELATIVE)
802         data[2] += pen.y;
803       
804       SET2(pen, pen.x, data[2]);
805       SET2V(tan, pen);
806       
807       if (flags & SH_PROCESS_SIMPLIFY_LINES) {
808         data[2] = pen.x; data[3] = pen.y;
809         (*callback)(p,VG_LINE_TO,command,data,userData);
810         break;
811       }
812        
813       (*callback)(p,VG_VLINE_TO,command,data,userData);
814       
815       break;
816     case VG_QUAD_TO:
817       
818       if (absrel == VG_RELATIVE) {
819         data[2] += pen.x; data[3] += pen.y;
820         data[4] += pen.x; data[5] += pen.y;
821       }
822       
823       SET2(tan, data[2], data[3]);
824       SET2(pen, data[4], data[5]);
825       
826       (*callback)(p,VG_QUAD_TO,command,data,userData);
827       
828       break;
829     case VG_CUBIC_TO:
830       
831       if (absrel == VG_RELATIVE) {
832         data[2] += pen.x; data[3] += pen.y;
833         data[4] += pen.x; data[5] += pen.y;
834         data[6] += pen.x; data[7] += pen.y;
835       }
836       
837       SET2(tan, data[4], data[5]);
838       SET2(pen, data[6], data[7]);
839       
840       (*callback)(p,VG_CUBIC_TO,command,data,userData);
841       
842       break;
843     case VG_SQUAD_TO:
844       
845       if (absrel == VG_RELATIVE) {
846         data[2] += pen.x; data[3] += pen.y;
847       }
848       
849       SET2(tan, 2*pen.x - tan.x, 2*pen.y - tan.y);
850       SET2(pen, data[2], data[3]);
851       
852       if (flags & SH_PROCESS_SIMPLIFY_CURVES) {
853         data[2] = tan.x; data[3] = tan.y;
854         data[4] = pen.x; data[5] = pen.y;
855         (*callback)(p,VG_QUAD_TO,command,data,userData);
856         break;
857       }
858       
859       (*callback)(p,VG_SQUAD_TO,command,data,userData);
860       
861       break;
862     case VG_SCUBIC_TO:
863       
864       if (absrel == VG_RELATIVE) {
865         data[2] += pen.x; data[3] += pen.y;
866         data[4] += pen.x; data[5] += pen.y;
867       }
868       
869       SET2(tan, data[2], data[3]);
870       SET2(pen, data[4], data[5]);
871       
872       if (flags & SH_PROCESS_SIMPLIFY_CURVES) {
873         data[2] = 2*pen.x - tan.x;
874         data[3] = 2*pen.y - tan.y;
875         data[4] = tan.x; data[5] = tan.y;
876         data[6] = pen.x; data[7] = pen.y;
877         (*callback)(p,VG_CUBIC_TO,command,data,userData);
878         break;
879       }
880       
881       (*callback)(p,VG_SCUBIC_TO,command,data,userData);
882       
883       break;
884     case VG_SCWARC_TO: case VG_SCCWARC_TO:
885     case VG_LCWARC_TO: case VG_LCCWARC_TO:
886       
887       if (absrel == VG_RELATIVE) {
888         data[5] += pen.x; data[6] += pen.y;
889       }
890       
891       data[2] = SH_ABS(data[2]);
892       data[3] = SH_ABS(data[3]);
893       
894       SET2(tan, data[5], data[6]);
895       SET2V(pen, tan);
896       
897       if (flags & SH_PROCESS_CENTRALIZE_ARCS) {
898         if (shCentralizeArc(command, data))
899           (*callback)(p,segment,command,data,userData);
900         else
901           (*callback)(p,VG_LINE_TO,command,data,userData);
902         break;
903       }
904       
905       (*callback)(p,segment,command,data,userData);
906       break;
907       
908     } /* switch (command) */
909   } /* for each segment */
910 }
911
912 /*-------------------------------------------------------
913  * Walks raw path data and counts the resulting number
914  * of segments and coordinates if the simplifications
915  * specified in the given processing flags were applied.
916  *-------------------------------------------------------*/
917
918 static void shProcessedDataCount(SHPath *p, SHint flags,
919                                  SHint *segCount, SHint *dataCount)
920 {
921   SHint s, segment, segindex;
922   *segCount = 0; *dataCount = 0;
923   
924   for (s=0; s<p->segCount; ++s) {
925     
926     segment = (p->segs[s] & 0x1E);
927     segindex = (segment >> 1);
928     
929     switch (segment) {
930     case VG_HLINE_TO: case VG_VLINE_TO:
931       
932       if (flags & SH_PROCESS_SIMPLIFY_LINES)
933         *dataCount += shCoordsPerCommand[VG_LINE_TO >> 1];
934       else *dataCount += shCoordsPerCommand[segindex];
935       break;
936       
937     case VG_SQUAD_TO:
938       
939       if (flags & SH_PROCESS_SIMPLIFY_CURVES)
940         *dataCount += shCoordsPerCommand[VG_QUAD_TO >> 1];
941       else *dataCount += shCoordsPerCommand[segindex];
942       break;
943       
944     case VG_SCUBIC_TO:
945       
946       if (flags & SH_PROCESS_SIMPLIFY_CURVES)
947         *dataCount += shCoordsPerCommand[VG_CUBIC_TO >> 1];
948       else *dataCount += shCoordsPerCommand[segindex];
949       break;
950       
951     case VG_SCWARC_TO: case VG_SCCWARC_TO:
952     case VG_LCWARC_TO: case VG_LCCWARC_TO:
953       
954       if (flags & SH_PROCESS_CENTRALIZE_ARCS)
955         *dataCount += 10;
956       else *dataCount += shCoordsPerCommand[segindex];
957       break;
958       
959     default:
960       *dataCount += shCoordsPerCommand[segindex];
961     }
962     
963     *segCount += 1;
964   }
965 }
966
967 static void shTransformSegment(SHPath *p, VGPathSegment segment,
968                                VGPathCommand originalCommand,
969                                SHfloat *data, void *userData)
970 {
971   int i, numPoints; SHVector2 point;
972   SHuint8* newSegs   = (SHuint8*) ((void**)userData)[0];
973   SHint*   segCount  = (SHint*)   ((void**)userData)[1];
974   SHuint8* newData   = (SHuint8*) ((void**)userData)[2];
975   SHint*   dataCount = (SHint*)   ((void**)userData)[3];
976   SHPath*  dst       = (SHPath*)  ((void**)userData)[4];
977   SHMatrix3x3 *ctm;
978   
979   /* Get current transform matrix */
980   VG_GETCONTEXT(VG_NO_RETVAL);
981   ctm = &context->pathTransform;
982   
983   switch (segment) {
984   case VG_CLOSE_PATH:
985       
986     /* No cordinates for this segment */
987     
988     break;
989   case VG_MOVE_TO:
990   case VG_LINE_TO:
991   case VG_QUAD_TO:
992   case VG_CUBIC_TO:
993   case VG_SQUAD_TO:
994   case VG_SCUBIC_TO:
995     
996     /* Walk over control points */
997     numPoints = shCoordsPerCommand[segment >> 1] / 2;
998     for (i=0; i<numPoints; ++i) {
999       
1000       /* Transform point by user to surface matrix */
1001       SET2(point, data[2 + i*2], data[2 + i*2 + 1]);
1002       TRANSFORM2(point, (*ctm));
1003       
1004       /* Write coordinates back to path data */
1005       shRealCoordToData(dst->datatype, dst->scale, dst->bias,
1006                         newData, (*dataCount)++, point.x);
1007       
1008       shRealCoordToData(dst->datatype, dst->scale, dst->bias,
1009                         newData, (*dataCount)++, point.y);
1010     }
1011     
1012     break;
1013   default:{
1014       
1015       SHMatrix3x3 u, u2;
1016       SHfloat a, cosa, sina, rx, ry;
1017       SHfloat A,B,C,AC,K,A2,C2;
1018       SHint invertible;
1019       SHVector2 p;
1020       SHfloat out[5];
1021       SHint i;
1022       
1023       SH_ASSERT(segment==VG_SCWARC_TO || segment==VG_SCCWARC_TO ||
1024                 segment==VG_LCWARC_TO || segment==VG_LCCWARC_TO);
1025       
1026       rx = data[2]; ry = data[3];
1027       a = SH_DEG2RAD(data[4]);
1028       cosa = SH_COS(a); sina = SH_SIN(a);
1029       
1030       /* Center parametrization of the elliptical arc. */
1031       SETMAT(u, rx * cosa, -ry * sina, 0,
1032              rx * sina,  ry * cosa,    0,
1033              0,          0,            1);
1034       
1035       /* Add current transformation */
1036       MULMATMAT((*ctm), u, u2);
1037       
1038       /* Inverse (transforms ellipse back to unit circle) */
1039       invertible = shInvertMatrix(&u2, &u);
1040       if (!invertible) {
1041         
1042         /* Zero-out radii and rotation */
1043         out[0] = out[1] = out[2] = 0.0f;
1044         
1045       }else{
1046       
1047         /* Extract implicit ellipse equation */
1048         A =     u.m[0][0]*u.m[0][0] + u.m[1][0]*u.m[1][0];
1049         B = 2*( u.m[0][0]*u.m[0][1] + u.m[1][0]*u.m[1][1] );
1050         C =     u.m[0][1]*u.m[0][1] + u.m[1][1]*u.m[1][1];
1051         AC = A-C;
1052         
1053         /* Find rotation and new radii */
1054         if (B == 0.0f) {
1055           
1056           /* 0 or 90 degree */
1057           out[2] = 0.0f;
1058           A2 = A;
1059           C2 = C;
1060           
1061         }else if (AC == 0.0f) {
1062           
1063           /* 45 degree */
1064           out[2] = PI/4;
1065           out[2] = SH_RAD2DEG(out[2]);
1066           A2 = A + B * 0.5f;
1067           C2 = A - B * 0.5f;
1068           
1069         }else{
1070           
1071           /* Any other angle */
1072           out[2] = 0.5f * SH_ATAN(B / AC);
1073           out[2] = SH_RAD2DEG(out[2]);
1074           
1075           K = 1 + (B*B) / (AC*AC);
1076           if (K <= 0.0f) K = 0.0f;
1077           else K = SH_SQRT(K);
1078           
1079           A2 = 0.5f * (A + C + K*AC);
1080           C2 = 0.5f * (A + C - K*AC);
1081         }
1082         
1083         /* New radii */
1084         out[0] = (A2 <= 0.0f ? 0.0f : 1 / SH_SQRT(A2));
1085         out[1] = (C2 <= 0.0f ? 0.0f : 1 / SH_SQRT(C2));
1086         
1087         /* Check for a mirror component in the transform matrix */
1088         if (ctm->m[0][0] * ctm->m[1][1] - ctm->m[1][0] * ctm->m[0][1] < 0.0f) {
1089           switch (segment) {
1090           case VG_SCWARC_TO: segment = VG_SCCWARC_TO; break;
1091           case VG_LCWARC_TO: segment = VG_LCCWARC_TO; break;
1092           case VG_SCCWARC_TO: segment = VG_SCWARC_TO; break;
1093           case VG_LCCWARC_TO: segment = VG_LCWARC_TO; break;
1094           default: break; }
1095         }
1096         
1097       }/* if (invertible) */
1098       
1099       /* Transform target point */
1100       SET2(p, data[5], data[6]);
1101       TRANSFORM2(p, context->pathTransform);
1102       out[3] = p.x; out[4] = p.y;
1103       
1104       /* Write coordinates back to path data */
1105       for (i=0; i<5; ++i)
1106         shRealCoordToData(dst->datatype, dst->scale, dst->bias,
1107                           newData, (*dataCount)++, out[i]);
1108       
1109       break;}
1110   }
1111   
1112   /* Write segment to new dst path data */
1113   newSegs[(*segCount)++] = segment | VG_ABSOLUTE;
1114 }
1115
1116 VG_API_CALL void vgTransformPath(VGPath dstPath, VGPath srcPath)
1117 {
1118   SHint newSegCount=0;
1119   SHint newDataCount=0;
1120   SHPath *src, *dst;
1121   SHuint8 *newSegs = NULL;
1122   SHuint8 *newData = NULL;
1123   SHint segCount = 0;
1124   SHint dataCount = 0;
1125   void *userData[5];
1126   SHint processFlags =
1127     SH_PROCESS_SIMPLIFY_LINES;
1128   
1129   VG_GETCONTEXT(VG_NO_RETVAL);
1130   
1131   VG_RETURN_ERR_IF(!shIsValidPath(context, dstPath) ||
1132                    !shIsValidPath(context, srcPath),
1133                    VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
1134   
1135   src = (SHPath*)srcPath; dst = (SHPath*)dstPath;
1136   VG_RETURN_ERR_IF(!(src->caps & VG_PATH_CAPABILITY_TRANSFORM_FROM) ||
1137                    !(dst->caps & VG_PATH_CAPABILITY_TRANSFORM_TO),
1138                    VG_PATH_CAPABILITY_ERROR, VG_NO_RETVAL);
1139   
1140   /* Resize path storage */
1141   shProcessedDataCount(src, processFlags, &newSegCount, &newDataCount);
1142   shResizePathData(dst, newSegCount, newDataCount, &newSegs, &newData);
1143   VG_RETURN_ERR_IF(!newData, VG_OUT_OF_MEMORY_ERROR, VG_NO_RETVAL);
1144   
1145   /* Transform src path into new data */
1146   segCount = dst->segCount;
1147   dataCount = dst->dataCount;
1148   userData[0] = newSegs; userData[1] = &segCount;
1149   userData[2] = newData; userData[3] = &dataCount;
1150   userData[4] = dst;
1151   shProcessPathData(src, processFlags, shTransformSegment, userData);
1152   
1153   /* Free old arrays */
1154   free(dst->segs);
1155   free(dst->data);
1156   
1157   /* Adjust new properties */
1158   dst->segs = newSegs;
1159   dst->data = newData;
1160   dst->segCount = segCount;
1161   dst->dataCount = dataCount;
1162
1163   /* Mark change */
1164   dst->cacheDataValid = VG_FALSE;
1165   
1166   VG_RETURN_ERR(VG_NO_ERROR, VG_NO_RETVAL);
1167 }
1168
1169 static void shInterpolateSegment(SHPath *p, VGPathSegment segment,
1170                                  VGPathCommand originalCommand,
1171                                  SHfloat *data, void *userData)
1172 {
1173   SHuint8* procSegs      = (SHuint8*) ((void**)userData)[0];
1174   SHint*   procSegCount  = (SHint*)   ((void**)userData)[1];
1175   SHfloat* procData      = (SHfloat*) ((void**)userData)[2];
1176   SHint*   procDataCount = (SHint*)   ((void**)userData)[3];
1177   SHint segindex, i;
1178   
1179   /* Write the processed segment back */
1180   procSegs[(*procSegCount)++] = segment | VG_ABSOLUTE;
1181   
1182   /* Write the processed data back (exclude first 2 pen coords!) */
1183   segindex = (segment >> 1);
1184   for (i=2; i<2+shCoordsPerCommand[segindex]; ++i)
1185     procData[(*procDataCount)++] = data[i];
1186 }
1187
1188 VG_API_CALL VGboolean vgInterpolatePath(VGPath dstPath, VGPath startPath,
1189                                         VGPath endPath, VGfloat amount)
1190 {
1191   SHPath *dst, *start, *end;
1192   SHuint8 *procSegs1, *procSegs2;
1193   SHfloat *procData1, *procData2;
1194   SHint procSegCount1=0, procSegCount2=0;
1195   SHint procDataCount1=0, procDataCount2=0;
1196   SHuint8 *newSegs, *newData;
1197   void *userData[4];
1198   SHint segment1, segment2;
1199   SHint segindex, s,d,i;
1200   SHint processFlags =
1201     SH_PROCESS_SIMPLIFY_LINES |
1202     SH_PROCESS_SIMPLIFY_CURVES;
1203   
1204   VG_GETCONTEXT(VG_FALSE);
1205   
1206   VG_RETURN_ERR_IF(!shIsValidPath(context, dstPath) ||
1207                    !shIsValidPath(context, startPath) ||
1208                    !shIsValidPath(context, endPath),
1209                    VG_BAD_HANDLE_ERROR, VG_FALSE);
1210   
1211   dst = (SHPath*)dstPath; start = (SHPath*)startPath; end = (SHPath*)endPath;
1212   VG_RETURN_ERR_IF(!(start->caps & VG_PATH_CAPABILITY_INTERPOLATE_FROM) ||
1213                    !(end->caps & VG_PATH_CAPABILITY_INTERPOLATE_FROM) ||
1214                    !(dst->caps & VG_PATH_CAPABILITY_INTERPOLATE_TO),
1215                    VG_PATH_CAPABILITY_ERROR, VG_FALSE);
1216   
1217   /* Segment count must be equal */
1218   VG_RETURN_ERR_IF(start->segCount != end->segCount,
1219                    VG_NO_ERROR, VG_FALSE);
1220   
1221   /* Allocate storage for processed path data */
1222   shProcessedDataCount(start, processFlags, &procSegCount1, &procDataCount1);
1223   shProcessedDataCount(end, processFlags, &procSegCount2, &procDataCount2);
1224   procSegs1 = (SHuint8*)malloc(procSegCount1 * sizeof(SHuint8));
1225   procSegs2 = (SHuint8*)malloc(procSegCount2 * sizeof(SHuint8));
1226   procData1 = (SHfloat*)malloc(procDataCount1 * sizeof(SHfloat));
1227   procData2 = (SHfloat*)malloc(procDataCount2 * sizeof(SHfloat));
1228   if (!procSegs1 || !procSegs2 || !procData1 || !procData2) {
1229     free(procSegs1); free(procSegs2); free(procData1); free(procData2);
1230     VG_RETURN_ERR(VG_OUT_OF_MEMORY_ERROR, VG_FALSE);
1231   }
1232   
1233   /* Process data of start path */
1234   procSegCount1 = 0; procDataCount1 = 0;
1235   userData[0] = procSegs1; userData[1] = &procSegCount1;
1236   userData[2] = procData1; userData[3] = &procDataCount1;
1237   shProcessPathData(start, processFlags, shInterpolateSegment, userData);
1238   
1239   /* Process data of end path */
1240   procSegCount2 = 0; procDataCount2 = 0;
1241   userData[0] = procSegs2; userData[1] = &procSegCount2;
1242   userData[2] = procData2; userData[3] = &procDataCount2;
1243   shProcessPathData(end, processFlags, shInterpolateSegment, userData);
1244   SH_ASSERT(procSegCount1 == procSegCount2 &&
1245             procDataCount1 == procDataCount2);
1246   
1247   /* Resize dst path storage to include interpolated data */
1248   shResizePathData(dst, procSegCount1, procDataCount1, &newSegs, &newData);
1249   if (!newData) {
1250     free(procSegs1); free(procData1);
1251     free(procSegs2); free(procData2);
1252     VG_RETURN_ERR(VG_OUT_OF_MEMORY_ERROR, VG_FALSE);
1253   }
1254   
1255   /* Interpolate data between paths */
1256   for (s=0, d=0; s<procSegCount1; ++s) {
1257     
1258     segment1 = (procSegs1[s] & 0x1E);
1259     segment2 = (procSegs2[s] & 0x1E);
1260     
1261     /* Pick the right arc type */
1262     if (shIsArcSegment(segment1) &&
1263         shIsArcSegment(segment2)) {
1264       
1265       if (amount < 0.5f)
1266         segment2 = segment1;
1267       else
1268         segment1 = segment2;
1269     }
1270     
1271     /* Segment types must match */
1272     if (segment1 != segment2) {
1273       free(procSegs1); free(procData1);
1274       free(procSegs2); free(procData2);
1275       free(newSegs); free(newData);
1276       VG_RETURN_ERR(VG_NO_ERROR, VG_FALSE);
1277     }
1278     
1279     /* Interpolate values */
1280     segindex = (segment1 >> 1);
1281     newSegs[s] = segment1 | VG_ABSOLUTE;
1282     for (i=0; i<shCoordsPerCommand[segindex]; ++i, ++d) {
1283       SHfloat diff = procData2[d] - procData1[d];
1284       SHfloat value = procData1[d] + amount * diff;
1285       shRealCoordToData(dst->datatype, dst->scale, dst->bias,
1286                         newData, dst->dataCount + d, value);
1287     }
1288   }
1289   
1290   /* Free processed data */
1291   free(procSegs1); free(procData1);
1292   free(procSegs2); free(procData2);
1293   
1294   /* Assign interpolated data */
1295   dst->segs = newSegs;
1296   dst->data = newData;
1297   dst->segCount += procSegCount1;
1298   dst->dataCount += procDataCount1;
1299
1300   /* Mark change */
1301   dst->cacheDataValid = VG_FALSE;
1302   
1303   VG_RETURN_ERR(VG_NO_ERROR, VG_TRUE);
1304 }