2 * Copyright (c) 2007 Ivan Leben
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.
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.
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
21 #include <vg/openvg.h>
22 #include "shContext.h"
27 #define _ITEM_T SHVertex
28 #define _ARRAY_T SHVertexArray
29 #define _FUNC_T shVertexArray
30 #define _COMPARE_T(v1,v2) 0
32 #include "shArrayBase.h"
34 #define _ITEM_T SHPath*
35 #define _ARRAY_T SHPathArray
36 #define _FUNC_T shPathArray
38 #include "shArrayBase.h"
41 static const SHint shCoordsPerCommand[] = {
42 0, /* VG_CLOSE_PATH */
51 5, /* VG_SCCWARC_TO */
53 5, /* VG_LCCWARC_TO */
57 #define SH_PATH_MAX_COORDS 6
58 #define SH_PATH_MAX_COORDS_PROCESSED 12
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 */
67 #define SH_PATH_MAX_BYTES 4
69 /*-----------------------------------------------------
71 *-----------------------------------------------------*/
73 void shClearSegCallbacks(SHPath *p);
74 void SHPath_ctor(SHPath *p)
80 p->datatype = VG_PATH_DATATYPE_F;
87 SH_INITOBJ(SHVertexArray, p->vertices);
88 SH_INITOBJ(SHVector2Array, p->stroke);
91 /*-----------------------------------------------------
93 *-----------------------------------------------------*/
95 void SHPath_dtor(SHPath *p)
97 if (p->segs) free(p->segs);
98 if (p->data) free(p->data);
100 SH_DEINITOBJ(SHVertexArray, p->vertices);
101 SH_DEINITOBJ(SHVector2Array, p->stroke);
104 /*-----------------------------------------------------
105 * Returns true (1) if given path data type is valid
106 *-----------------------------------------------------*/
108 static SHint shIsValidDatatype(VGPathDatatype t)
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);
116 static SHint shIsValidCommand(SHint c)
118 return (c >= (VG_CLOSE_PATH >> 1) &&
119 c <= (VG_LCWARC_TO >> 1));
122 static SHint shIsArcSegment(SHint s)
124 return (s == VG_SCWARC_TO || s == VG_SCCWARC_TO ||
125 s == VG_LCWARC_TO || s == VG_LCCWARC_TO);
128 /*-------------------------------------------------------
129 * Allocates a path resource in the current context and
130 * sets its capabilities.
131 *-------------------------------------------------------*/
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)
141 VG_GETCONTEXT(VG_INVALID_HANDLE);
143 /* Only standard format supported */
144 VG_RETURN_ERR_IF(pathFormat != VG_PATH_FORMAT_STANDARD,
145 VG_UNSUPPORTED_PATH_FORMAT_ERROR,
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);
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);
158 p->format = pathFormat;
161 p->segHint = segmentCapacityHint;
162 p->dataHint = coordCapacityHint;
163 p->datatype = datatype;
164 p->caps = capabilities & VG_PATH_CAPABILITY_ALL;
166 /* Init cache flags */
167 p->cacheDataValid = VG_TRUE;
168 p->cacheTransformInit = VG_FALSE;
169 p->cacheStrokeInit = VG_FALSE;
171 VG_RETURN((VGPath)p);
174 /*-----------------------------------------------------
175 * Clears the specified path of all data and sets new
176 * capabilities to it.
177 *-----------------------------------------------------*/
179 VG_API_CALL void vgClearPath(VGPath path, VGbitfield capabilities)
182 VG_GETCONTEXT(VG_NO_RETVAL);
184 VG_RETURN_ERR_IF(!shIsValidPath(context, path),
185 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
197 p->cacheDataValid = VG_FALSE;
199 /* Downsize arrays to save memory */
200 shVertexArrayRealloc(&p->vertices, 1);
201 shVector2ArrayRealloc(&p->stroke, 1);
203 /* Re-set capabilities */
204 p->caps = capabilities & VG_PATH_CAPABILITY_ALL;
206 VG_RETURN(VG_NO_RETVAL);
209 /*---------------------------------------------------------
210 * Disposes specified path resource in the current context
211 *---------------------------------------------------------*/
213 VG_API_CALL void vgDestroyPath(VGPath path)
216 VG_GETCONTEXT(VG_NO_RETVAL);
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);
222 /* Delete object and remove resource */
223 SH_DELETEOBJ(SHPath, (SHPath*)path);
224 shPathArrayRemoveAt(&context->paths, index);
226 VG_RETURN_ERR(VG_NO_ERROR, VG_NO_RETVAL);
229 /*-----------------------------------------------------
230 * Removes capabilities defined in the given bitfield
231 * from the specified path.
232 *-----------------------------------------------------*/
234 VG_API_CALL void vgRemovePathCapabilities(VGPath path, VGbitfield capabilities)
236 VG_GETCONTEXT(VG_NO_RETVAL);
238 VG_RETURN_ERR_IF(!shIsValidPath(context, path),
239 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
241 capabilities &= VG_PATH_CAPABILITY_ALL;
242 ((SHPath*)path)->caps &= ~capabilities;
244 VG_RETURN(VG_NO_RETVAL);
247 /*-----------------------------------------------------
248 * Returns capabilities of a path resource
249 *-----------------------------------------------------*/
251 VG_API_CALL VGbitfield vgGetPathCapabilities(VGPath path)
255 VG_RETURN_ERR_IF(!shIsValidPath(context, path),
256 VG_BAD_HANDLE_ERROR, 0x0);
258 VG_RETURN( ((SHPath*)path)->caps );
261 /*-----------------------------------------------------
262 * Walks the given path segments and returns the total
263 * number of coordinates.
264 *-----------------------------------------------------*/
266 static SHint shCoordCountForData(VGint segcount, const SHuint8 *segs)
272 for (s=0; s<segcount; ++s) {
273 command = ((segs[s] & 0x1E) >> 1);
274 if (!shIsValidCommand(command)) return -1;
275 count += shCoordsPerCommand[command];
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 *-------------------------------------------------------*/
287 static SHfloat shRealCoordFromData(VGPathDatatype type, SHfloat scale, SHfloat bias,
288 void *data, SHint index)
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;;
298 return ( (SHfloat) ((SHfloat32*)data)[index] ) * scale + bias;
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 *-------------------------------------------------------*/
308 static void shRealCoordToData(VGPathDatatype type, SHfloat scale, SHfloat bias,
309 void *data, SHint index, SHfloat c)
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;
322 ((SHfloat32*)data) [index] = (SHfloat32)c; break;
326 /*-------------------------------------------------
327 * Resizes storage for segment and coordinate data
328 * of the specified path by given number of items
329 *-------------------------------------------------*/
331 static int shResizePathData(SHPath *p, SHint newSegCount, SHint newDataCount,
332 SHuint8 **newSegs, SHuint8 **newData)
334 SHint oldDataSize = 0;
335 SHint newDataSize = 0;
337 /* Allocate memory for new segments */
338 (*newSegs) = (SHuint8*)malloc(p->segCount + newSegCount);
339 if ((*newSegs) == NULL) return 0;
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;}
347 /* Copy old segments */
348 memcpy(*newSegs, p->segs, p->segCount);
351 memcpy(*newData, p->data, oldDataSize);
356 /*-------------------------------------------------------------
357 * Appends path data from source to destination path resource
358 *-------------------------------------------------------------*/
360 VG_API_CALL void vgAppendPath(VGPath dstPath, VGPath srcPath)
364 SHuint8 *newSegs = NULL;
365 SHuint8 *newData = NULL;
366 VG_GETCONTEXT(VG_NO_RETVAL);
368 VG_RETURN_ERR_IF(!shIsValidPath(context, srcPath) ||
369 !shIsValidPath(context, dstPath),
370 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
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);
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);
381 /* Copy new segments */
382 memcpy(newSegs+dst->segCount, src->segs, src->segCount);
384 /* Copy new coordinates */
385 for (i=0; i<src->dataCount; ++i) {
387 SHfloat coord = shRealCoordFromData(src->datatype, src->scale,
388 src->bias, src->data, i);
390 shRealCoordToData(dst->datatype, dst->scale, dst->bias,
391 newData, dst->dataCount+i, coord);
394 /* Free old arrays */
398 /* Adjust new properties */
401 dst->segCount += src->segCount;
402 dst->dataCount += src->dataCount;
405 dst->cacheDataValid = VG_FALSE;
407 VG_RETURN(VG_NO_RETVAL);
410 /*-----------------------------------------------------
411 * Appends data to destination path resource
412 *-----------------------------------------------------*/
414 SHfloat shValidInputFloat(VGfloat f);
415 VG_API_CALL void vgAppendPathData(VGPath dstPath, VGint newSegCount,
416 const VGubyte *segs, const void *data)
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);
427 VG_RETURN_ERR_IF(!shIsValidPath(context, dstPath),
428 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
430 dst = (SHPath*)dstPath;
431 VG_RETURN_ERR_IF(!(dst->caps & VG_PATH_CAPABILITY_APPEND_TO),
432 VG_PATH_CAPABILITY_ERROR, VG_NO_RETVAL);
434 VG_RETURN_ERR_IF(!segs || !data || newSegCount <= 0,
435 VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
437 /* TODO: check segment + data array alignment */
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,
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);
450 /* Copy new segments */
451 memcpy(newSegs+dst->segCount, segs, newSegCount);
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] );
459 memcpy(newData+oldDataSize, data, newDataSize);
462 /* Free old arrays */
466 /* Adjust new properties */
469 dst->segCount += newSegCount;
470 dst->dataCount += newDataCount;
473 dst->cacheDataValid = VG_FALSE;
475 VG_RETURN(VG_NO_RETVAL);
478 /*--------------------------------------------------------
479 * Modifies the coordinates of the existing path segments
480 *--------------------------------------------------------*/
482 VG_API_CALL void vgModifyPathCoords(VGPath dstPath, VGint startIndex,
483 VGint numSegments, const void * data)
489 SHint dataStartCount;
491 VG_GETCONTEXT(VG_NO_RETVAL);
493 VG_RETURN_ERR_IF(!shIsValidPath(context, dstPath),
494 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
496 p = (SHPath*)dstPath;
497 VG_RETURN_ERR_IF(!(p->caps & VG_PATH_CAPABILITY_MODIFY),
498 VG_PATH_CAPABILITY_ERROR, VG_NO_RETVAL);
500 VG_RETURN_ERR_IF(!data || startIndex<0 || numSegments <=0 ||
501 startIndex + numSegments > p->segCount,
502 VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
504 /* TODO: check data array alignment */
506 /* Find start of the coordinates to be changed */
507 dataStartCount = shCoordCountForData(startIndex, p->segs);
508 dataStartSize = dataStartCount * shBytesPerDatatype[p->datatype];
510 /* Find byte size of coordinates to be changed */
511 newDataCount = shCoordCountForData(numSegments, &p->segs[startIndex]);
512 newDataSize = newDataCount * shBytesPerDatatype[p->datatype];
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] );
520 memcpy( ((SHuint8*)p->data) + dataStartSize, data, newDataSize);
524 p->cacheDataValid = VG_FALSE;
526 VG_RETURN(VG_NO_RETVAL);
529 /*------------------------------------------------------------
530 * Converts standart endpoint arc parametrization into center
531 * arc parametrization for further internal processing
532 *------------------------------------------------------------*/
534 SHint shCentralizeArc(SHuint command, SHfloat *data)
537 SHVector2 p1, p2, pp1,pp2;
539 SHfloat dist, halfdist, q;
540 SHVector2 c1, c2, *c;
544 SHMatrix3x3 user2arc;
545 SHMatrix3x3 arc2user;
549 a = SH_DEG2RAD(data[4]);
550 SET2(p1, data[0], data[1]);
551 SET2(p2, data[5], data[6]);
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;
559 /* Rotate to arc coordinates.
560 Scale to correct eccentricity. */
562 ROTATEMATL(user2arc, -a);
563 SCALEMATL(user2arc, 1, rx/ry);
564 TRANSFORM2TO(p1, user2arc, pp1);
565 TRANSFORM2TO(p2, user2arc, pp2);
567 /* Distance between points and
568 perpendicular vector */
569 SET2V(d, pp2); SUB2V(d, pp1);
571 halfdist = dist * 0.5f;
575 /* Check if too close and return line */
576 if (halfdist == 0.0f) {
577 data[2] = p2.x; data[3] = p2.y;
581 /* Scale radius if too far away */
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;
595 /* Pick the right arc center */
596 switch (command & 0x1E) {
597 case VG_SCWARC_TO: case VG_LCCWARC_TO:
599 case VG_LCWARC_TO: case VG_SCCWARC_TO:
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);
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;
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;
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;
629 switch (command & 0x1E) {
630 case VG_SCWARC_TO: case VG_LCWARC_TO:
632 case VG_SCCWARC_TO: case VG_LCCWARC_TO:
638 /* Arc unit vectors */
642 /* Transform back to user coordinates */
644 SCALEMATL(arc2user, 1, ry/rx);
645 ROTATEMATL(arc2user, a);
646 TRANSFORM2((*c), arc2user);
647 TRANSFORM2(ux, arc2user);
648 TRANSFORM2(uy, arc2user);
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;
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 *-------------------------------------------------------*/
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)
674 void shProcessPathData(SHPath *p,
676 SegmentFunc callback,
679 SHint i=0, s=0, d=0, c=0;
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 */
696 for (s=0; s<p->segCount; ++s, d+=numcoords) {
698 /* Extract command */
699 command = (p->segs[s]);
700 absrel = (command & 1);
701 segment = (command & 0x1E);
702 segindex = (segment >> 1);
703 numcoords = shCoordsPerCommand[segindex];
705 /* Repair segment start / end */
706 if (flags & SH_PROCESS_REPAIR_ENDS) {
708 /* Prevent double CLOSE_PATH */
709 if (!open && segment == VG_CLOSE_PATH)
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);
720 /* Avoid a MOVE_TO at the end of data */
721 if (segment == VG_MOVE_TO) {
722 if (s == p->segCount-1) break;
724 /* Avoid a lone MOVE_TO */
725 SHuint nextsegment = (p->segs[s+1] & 0x1E);
726 if (nextsegment == VG_MOVE_TO)
727 {open = 0; continue;}
731 /* Place pen into first two coords */
736 /* Unpack coordinates from path data */
737 for (i=0; i<numcoords; ++i)
738 data[c++] = shRealCoordFromData(p->datatype, p->scale, p->bias,
741 /* Simplify complex segments */
753 (*callback)(p,VG_CLOSE_PATH,command,data,userData);
758 if (absrel == VG_RELATIVE) {
759 data[2] += pen.x; data[3] += pen.y;
762 SET2(pen, data[2], data[3]);
767 (*callback)(p,VG_MOVE_TO,command,data,userData);
772 if (absrel == VG_RELATIVE) {
773 data[2] += pen.x; data[3] += pen.y;
776 SET2(pen, data[2], data[3]);
779 (*callback)(p,VG_LINE_TO,command,data,userData);
784 if (absrel == VG_RELATIVE)
787 SET2(pen, data[2], pen.y);
790 if (flags & SH_PROCESS_SIMPLIFY_LINES) {
792 (*callback)(p,VG_LINE_TO,command,data,userData);
796 (*callback)(p,VG_HLINE_TO,command,data,userData);
801 if (absrel == VG_RELATIVE)
804 SET2(pen, pen.x, data[2]);
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);
813 (*callback)(p,VG_VLINE_TO,command,data,userData);
818 if (absrel == VG_RELATIVE) {
819 data[2] += pen.x; data[3] += pen.y;
820 data[4] += pen.x; data[5] += pen.y;
823 SET2(tan, data[2], data[3]);
824 SET2(pen, data[4], data[5]);
826 (*callback)(p,VG_QUAD_TO,command,data,userData);
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;
837 SET2(tan, data[4], data[5]);
838 SET2(pen, data[6], data[7]);
840 (*callback)(p,VG_CUBIC_TO,command,data,userData);
845 if (absrel == VG_RELATIVE) {
846 data[2] += pen.x; data[3] += pen.y;
849 SET2(tan, 2*pen.x - tan.x, 2*pen.y - tan.y);
850 SET2(pen, data[2], data[3]);
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);
859 (*callback)(p,VG_SQUAD_TO,command,data,userData);
864 if (absrel == VG_RELATIVE) {
865 data[2] += pen.x; data[3] += pen.y;
866 data[4] += pen.x; data[5] += pen.y;
869 SET2(tan, data[2], data[3]);
870 SET2(pen, data[4], data[5]);
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);
881 (*callback)(p,VG_SCUBIC_TO,command,data,userData);
884 case VG_SCWARC_TO: case VG_SCCWARC_TO:
885 case VG_LCWARC_TO: case VG_LCCWARC_TO:
887 if (absrel == VG_RELATIVE) {
888 data[5] += pen.x; data[6] += pen.y;
891 data[2] = SH_ABS(data[2]);
892 data[3] = SH_ABS(data[3]);
894 SET2(tan, data[5], data[6]);
897 if (flags & SH_PROCESS_CENTRALIZE_ARCS) {
898 if (shCentralizeArc(command, data))
899 (*callback)(p,segment,command,data,userData);
901 (*callback)(p,VG_LINE_TO,command,data,userData);
905 (*callback)(p,segment,command,data,userData);
908 } /* switch (command) */
909 } /* for each segment */
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 *-------------------------------------------------------*/
918 static void shProcessedDataCount(SHPath *p, SHint flags,
919 SHint *segCount, SHint *dataCount)
921 SHint s, segment, segindex;
922 *segCount = 0; *dataCount = 0;
924 for (s=0; s<p->segCount; ++s) {
926 segment = (p->segs[s] & 0x1E);
927 segindex = (segment >> 1);
930 case VG_HLINE_TO: case VG_VLINE_TO:
932 if (flags & SH_PROCESS_SIMPLIFY_LINES)
933 *dataCount += shCoordsPerCommand[VG_LINE_TO >> 1];
934 else *dataCount += shCoordsPerCommand[segindex];
939 if (flags & SH_PROCESS_SIMPLIFY_CURVES)
940 *dataCount += shCoordsPerCommand[VG_QUAD_TO >> 1];
941 else *dataCount += shCoordsPerCommand[segindex];
946 if (flags & SH_PROCESS_SIMPLIFY_CURVES)
947 *dataCount += shCoordsPerCommand[VG_CUBIC_TO >> 1];
948 else *dataCount += shCoordsPerCommand[segindex];
951 case VG_SCWARC_TO: case VG_SCCWARC_TO:
952 case VG_LCWARC_TO: case VG_LCCWARC_TO:
954 if (flags & SH_PROCESS_CENTRALIZE_ARCS)
956 else *dataCount += shCoordsPerCommand[segindex];
960 *dataCount += shCoordsPerCommand[segindex];
967 static void shTransformSegment(SHPath *p, VGPathSegment segment,
968 VGPathCommand originalCommand,
969 SHfloat *data, void *userData)
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];
979 /* Get current transform matrix */
980 VG_GETCONTEXT(VG_NO_RETVAL);
981 ctm = &context->pathTransform;
986 /* No cordinates for this segment */
996 /* Walk over control points */
997 numPoints = shCoordsPerCommand[segment >> 1] / 2;
998 for (i=0; i<numPoints; ++i) {
1000 /* Transform point by user to surface matrix */
1001 SET2(point, data[2 + i*2], data[2 + i*2 + 1]);
1002 TRANSFORM2(point, (*ctm));
1004 /* Write coordinates back to path data */
1005 shRealCoordToData(dst->datatype, dst->scale, dst->bias,
1006 newData, (*dataCount)++, point.x);
1008 shRealCoordToData(dst->datatype, dst->scale, dst->bias,
1009 newData, (*dataCount)++, point.y);
1016 SHfloat a, cosa, sina, rx, ry;
1017 SHfloat A,B,C,AC,K,A2,C2;
1023 SH_ASSERT(segment==VG_SCWARC_TO || segment==VG_SCCWARC_TO ||
1024 segment==VG_LCWARC_TO || segment==VG_LCCWARC_TO);
1026 rx = data[2]; ry = data[3];
1027 a = SH_DEG2RAD(data[4]);
1028 cosa = SH_COS(a); sina = SH_SIN(a);
1030 /* Center parametrization of the elliptical arc. */
1031 SETMAT(u, rx * cosa, -ry * sina, 0,
1032 rx * sina, ry * cosa, 0,
1035 /* Add current transformation */
1036 MULMATMAT((*ctm), u, u2);
1038 /* Inverse (transforms ellipse back to unit circle) */
1039 invertible = shInvertMatrix(&u2, &u);
1042 /* Zero-out radii and rotation */
1043 out[0] = out[1] = out[2] = 0.0f;
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];
1053 /* Find rotation and new radii */
1056 /* 0 or 90 degree */
1061 }else if (AC == 0.0f) {
1065 out[2] = SH_RAD2DEG(out[2]);
1071 /* Any other angle */
1072 out[2] = 0.5f * SH_ATAN(B / AC);
1073 out[2] = SH_RAD2DEG(out[2]);
1075 K = 1 + (B*B) / (AC*AC);
1076 if (K <= 0.0f) K = 0.0f;
1077 else K = SH_SQRT(K);
1079 A2 = 0.5f * (A + C + K*AC);
1080 C2 = 0.5f * (A + C - K*AC);
1084 out[0] = (A2 <= 0.0f ? 0.0f : 1 / SH_SQRT(A2));
1085 out[1] = (C2 <= 0.0f ? 0.0f : 1 / SH_SQRT(C2));
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) {
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;
1097 }/* if (invertible) */
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;
1104 /* Write coordinates back to path data */
1106 shRealCoordToData(dst->datatype, dst->scale, dst->bias,
1107 newData, (*dataCount)++, out[i]);
1112 /* Write segment to new dst path data */
1113 newSegs[(*segCount)++] = segment | VG_ABSOLUTE;
1116 VG_API_CALL void vgTransformPath(VGPath dstPath, VGPath srcPath)
1118 SHint newSegCount=0;
1119 SHint newDataCount=0;
1121 SHuint8 *newSegs = NULL;
1122 SHuint8 *newData = NULL;
1124 SHint dataCount = 0;
1126 SHint processFlags =
1127 SH_PROCESS_SIMPLIFY_LINES;
1129 VG_GETCONTEXT(VG_NO_RETVAL);
1131 VG_RETURN_ERR_IF(!shIsValidPath(context, dstPath) ||
1132 !shIsValidPath(context, srcPath),
1133 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
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);
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);
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;
1151 shProcessPathData(src, processFlags, shTransformSegment, userData);
1153 /* Free old arrays */
1157 /* Adjust new properties */
1158 dst->segs = newSegs;
1159 dst->data = newData;
1160 dst->segCount = segCount;
1161 dst->dataCount = dataCount;
1164 dst->cacheDataValid = VG_FALSE;
1166 VG_RETURN_ERR(VG_NO_ERROR, VG_NO_RETVAL);
1169 static void shInterpolateSegment(SHPath *p, VGPathSegment segment,
1170 VGPathCommand originalCommand,
1171 SHfloat *data, void *userData)
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];
1179 /* Write the processed segment back */
1180 procSegs[(*procSegCount)++] = segment | VG_ABSOLUTE;
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];
1188 VG_API_CALL VGboolean vgInterpolatePath(VGPath dstPath, VGPath startPath,
1189 VGPath endPath, VGfloat amount)
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;
1198 SHint segment1, segment2;
1199 SHint segindex, s,d,i;
1200 SHint processFlags =
1201 SH_PROCESS_SIMPLIFY_LINES |
1202 SH_PROCESS_SIMPLIFY_CURVES;
1204 VG_GETCONTEXT(VG_FALSE);
1206 VG_RETURN_ERR_IF(!shIsValidPath(context, dstPath) ||
1207 !shIsValidPath(context, startPath) ||
1208 !shIsValidPath(context, endPath),
1209 VG_BAD_HANDLE_ERROR, VG_FALSE);
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);
1217 /* Segment count must be equal */
1218 VG_RETURN_ERR_IF(start->segCount != end->segCount,
1219 VG_NO_ERROR, VG_FALSE);
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);
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);
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);
1247 /* Resize dst path storage to include interpolated data */
1248 shResizePathData(dst, procSegCount1, procDataCount1, &newSegs, &newData);
1250 free(procSegs1); free(procData1);
1251 free(procSegs2); free(procData2);
1252 VG_RETURN_ERR(VG_OUT_OF_MEMORY_ERROR, VG_FALSE);
1255 /* Interpolate data between paths */
1256 for (s=0, d=0; s<procSegCount1; ++s) {
1258 segment1 = (procSegs1[s] & 0x1E);
1259 segment2 = (procSegs2[s] & 0x1E);
1261 /* Pick the right arc type */
1262 if (shIsArcSegment(segment1) &&
1263 shIsArcSegment(segment2)) {
1266 segment2 = segment1;
1268 segment1 = segment2;
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);
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);
1290 /* Free processed data */
1291 free(procSegs1); free(procData1);
1292 free(procSegs2); free(procData2);
1294 /* Assign interpolated data */
1295 dst->segs = newSegs;
1296 dst->data = newData;
1297 dst->segCount += procSegCount1;
1298 dst->dataCount += procDataCount1;
1301 dst->cacheDataValid = VG_FALSE;
1303 VG_RETURN_ERR(VG_NO_ERROR, VG_TRUE);