edba638d216ca3a8a0d8b1929bd98ca969cc0b21
[platform/core/graphics/tizenvg.git] / src / lib / sw_engine / tvgSwStroke.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
3
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 #ifndef _TVG_SW_STROKER_H_
23 #define _TVG_SW_STROKER_H_
24
25 #include "tvgSwCommon.h"
26
27
28 /************************************************************************/
29 /* Internal Class Implementation                                        */
30 /************************************************************************/
31 static constexpr auto SW_STROKE_TAG_POINT = 1;
32 static constexpr auto SW_STROKE_TAG_CUBIC = 2;
33 static constexpr auto SW_STROKE_TAG_BEGIN = 4;
34 static constexpr auto SW_STROKE_TAG_END = 8;
35
36 static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
37 {
38     return (SW_ANGLE_PI2 - (s) * SW_ANGLE_PI);
39 }
40
41
42 static inline void SCALE(SwStroke& stroke, SwPoint& pt)
43 {
44     pt.x *= stroke.sx;
45     pt.y *= stroke.sy;
46 }
47
48
49 static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
50 {
51     assert(border);
52
53     auto maxOld = border->maxPts;
54     auto maxNew = border->ptsCnt + newPts;
55
56     if (maxNew <= maxOld) return;
57
58     auto maxCur = maxOld;
59
60     while (maxCur < maxNew)
61         maxCur += (maxCur >> 1) + 16;
62
63     border->pts = static_cast<SwPoint*>(realloc(border->pts, maxCur * sizeof(SwPoint)));
64     assert(border->pts);
65
66     border->tags = static_cast<uint8_t*>(realloc(border->tags, maxCur * sizeof(uint8_t)));
67     assert(border->tags);
68
69     border->maxPts = maxCur;
70 }
71
72
73 static void _borderClose(SwStrokeBorder* border, bool reverse)
74 {
75     assert(border && border->start >= 0);
76
77     uint32_t start = border->start;
78     uint32_t count = border->ptsCnt;
79
80     //Don't record empty paths!
81     if (count <= start + 1U) {
82         border->ptsCnt = start;
83     } else {
84         /* Copy the last point to the start of this sub-path,
85            since it contains the adjusted starting coordinates */
86         border->ptsCnt = --count;
87         border->pts[start] = border->pts[count];
88
89         if (reverse) {
90             //reverse the points
91             auto pt1 = border->pts + start + 1;
92             auto pt2 = border->pts + count - 1;
93
94             while (pt1 < pt2) {
95                 auto tmp = *pt1;
96                 *pt1 = *pt2;
97                 *pt2 = tmp;
98                 ++pt1;
99                 --pt2;
100             }
101
102             //reverse the tags
103             auto tag1 = border->tags + start + 1;
104             auto tag2 = border->tags + count - 1;
105
106             while (tag1 < tag2) {
107                 auto tmp = *tag1;
108                 *tag1 = *tag2;
109                 *tag2 = tmp;
110                 ++tag1;
111                 --tag2;
112             }
113         }
114
115         border->tags[start] |= SW_STROKE_TAG_BEGIN;
116         border->tags[count - 1] |=  SW_STROKE_TAG_END;
117     }
118
119     border->start = -1;
120     border->movable = false;
121 }
122
123
124 static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& to)
125 {
126     assert(border && border->start >= 0);
127
128     _growBorder(border, 3);
129
130     auto pt = border->pts + border->ptsCnt;
131     auto tag = border->tags + border->ptsCnt;
132
133     pt[0] = ctrl1;
134     pt[1] = ctrl2;
135     pt[2] = to;
136
137     tag[0] = SW_STROKE_TAG_CUBIC;
138     tag[1] = SW_STROKE_TAG_CUBIC;
139     tag[2] = SW_STROKE_TAG_POINT;
140
141     border->ptsCnt += 3;
142
143     border->movable = false;
144 }
145
146
147 static void _borderArcTo(SwStrokeBorder* border, SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff, SwStroke& stroke)
148 {
149     constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2;
150     SwPoint a = {static_cast<SwCoord>(radius), 0};
151     mathRotate(a, angleStart);
152     SCALE(stroke, a);
153     a += center;
154
155     auto total = angleDiff;
156     auto angle = angleStart;
157     auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
158
159     while (total != 0) {
160         auto step = total;
161         if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE;
162         else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE;
163
164         auto next = angle + step;
165         auto theta = step;
166         if (theta < 0) theta = -theta;
167
168         theta >>= 1;
169
170         //compute end point
171         SwPoint b = {static_cast<SwCoord>(radius), 0};
172         mathRotate(b, next);
173         SCALE(stroke, b);
174         b += center;
175
176         //compute first and second control points
177         auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3);
178
179         SwPoint a2 = {static_cast<SwCoord>(length), 0};
180         mathRotate(a2, angle + rotate);
181         SCALE(stroke, a2);
182         a2 += a;
183
184         SwPoint b2 = {static_cast<SwCoord>(length), 0};
185         mathRotate(b2, next - rotate);
186         SCALE(stroke, b2);
187         b2 += b;
188
189         //add cubic arc
190         _borderCubicTo(border, a2, b2, b);
191
192         //process the rest of the arc?
193         a = b;
194         total -= step;
195         angle = next;
196     }
197 }
198
199
200 static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable)
201 {
202     assert(border && border->start >= 0);
203
204     if (border->movable) {
205         //move last point
206         border->pts[border->ptsCnt - 1] = to;
207     } else {
208
209         //don't add zero-length line_to
210         if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
211
212         _growBorder(border, 1);
213         border->pts[border->ptsCnt] = to;
214         border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT;
215         border->ptsCnt += 1;
216     }
217
218     border->movable = movable;
219 }
220
221
222 static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to)
223 {
224     assert(border);
225
226     //close current open path if any?
227     if (border->start >= 0) _borderClose(border, false);
228
229     border->start = border->ptsCnt;
230     border->movable = false;
231
232     _borderLineTo(border, to, false);
233 }
234
235
236 static void _arcTo(SwStroke& stroke, int32_t side)
237 {
238     auto border = stroke.borders + side;
239     auto rotate = SIDE_TO_ROTATE(side);
240     auto total = mathDiff(stroke.angleIn, stroke.angleOut);
241     if (total == SW_ANGLE_PI) total = -rotate * 2;
242
243     _borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total, stroke);
244     border->movable = false;
245 }
246
247
248 static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
249 {
250     constexpr SwFixed MITER_LIMIT = 4 * (1 << 16);
251
252     assert(MITER_LIMIT >= 0x10000);
253
254     auto border = stroke.borders + side;
255     assert(border);
256
257     if (stroke.join == StrokeJoin::Round) {
258         _arcTo(stroke, side);
259     } else {
260         //this is a mitered (pointed) or beveled (truncated) corner
261         auto rotate = SIDE_TO_ROTATE(side);
262         auto bevel = (stroke.join == StrokeJoin::Bevel) ? true : false;
263         SwFixed phi = 0;
264         SwFixed thcos = 0;
265
266         if (!bevel) {
267             auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
268             if (theta == SW_ANGLE_PI) {
269                 theta = rotate;
270                 phi = stroke.angleIn;
271             } else {
272                 theta /= 2;
273                 phi = stroke.angleIn + theta + rotate;
274             }
275
276             thcos = mathCos(theta);
277             auto sigma = mathMultiply(MITER_LIMIT, thcos);
278
279             //is miter limit exceeded?
280             if (sigma < 0x10000L) bevel = true;
281         }
282
283         //this is a bevel (broken angle)
284         if (bevel) {
285             SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
286             mathRotate(delta, stroke.angleOut + rotate);
287             SCALE(stroke, delta);
288             delta += stroke.center;
289             border->movable = false;
290             _borderLineTo(border, delta, false);
291         //this is a miter (intersection)
292         } else {
293             auto length = mathDivide(stroke.width, thcos);
294             SwPoint delta = {static_cast<SwCoord>(length), 0};
295             mathRotate(delta, phi);
296             SCALE(stroke, delta);
297             delta += stroke.center;
298             _borderLineTo(border, delta, false);
299
300             /* Now add and end point
301                Only needed if not lineto (lineLength is zero for curves) */
302             if (lineLength == 0) {
303                 delta = {static_cast<SwCoord>(stroke.width), 0};
304                 mathRotate(delta, stroke.angleOut + rotate);
305                 SCALE(stroke, delta);
306                 delta += stroke.center;
307                 _borderLineTo(border, delta, false);
308             }
309         }
310     }
311 }
312
313
314 static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
315 {
316     auto border = stroke.borders + side;
317     auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2;
318     SwPoint delta;
319     bool intersect = false;
320
321     /* Only intersect borders if between two line_to's and both
322        lines are long enough (line length is zero fur curves). */
323     if (border->movable && lineLength > 0) {
324         //compute minimum required length of lines
325         SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta)));
326         if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true;
327     }
328
329     auto rotate = SIDE_TO_ROTATE(side);
330
331     if (!intersect) {
332         delta = {static_cast<SwCoord>(stroke.width), 0};
333         mathRotate(delta, stroke.angleOut + rotate);
334         SCALE(stroke, delta);
335         delta += stroke.center;
336         border->movable = false;
337     } else {
338         //compute median angle
339         auto phi = stroke.angleIn + theta;
340         auto thcos = mathCos(theta);
341         delta = {static_cast<SwCoord>(mathDivide(stroke.width, thcos)), 0};
342         mathRotate(delta, phi + rotate);
343         SCALE(stroke, delta);
344         delta += stroke.center;
345     }
346
347     _borderLineTo(border, delta, false);
348 }
349
350
351 void _processCorner(SwStroke& stroke, SwFixed lineLength)
352 {
353     auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
354
355     //no specific corner processing is required if the turn is 0
356     if (turn == 0) return;
357
358     //when we turn to the right, the inside side is 0
359     int32_t inside = 0;
360
361     //otherwise, the inside is 1
362     if (turn < 0) inside = 1;
363
364     //process the inside
365     _inside(stroke, inside, lineLength);
366
367     //process the outside
368     _outside(stroke, 1 - inside, lineLength);
369 }
370
371
372 void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
373 {
374     SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
375     mathRotate(delta, startAngle + SW_ANGLE_PI2);
376     SCALE(stroke, delta);
377
378     auto pt = stroke.center + delta;
379     auto border = stroke.borders;
380     _borderMoveTo(border, pt);
381
382     pt = stroke.center - delta;
383     ++border;
384     _borderMoveTo(border, pt);
385
386     /* Save angle, position and line length for last join
387        lineLength is zero for curves */
388     stroke.subPathAngle = startAngle;
389     stroke.firstPt = false;
390     stroke.subPathLineLength = lineLength;
391 }
392
393
394 static void _lineTo(SwStroke& stroke, const SwPoint& to)
395 {
396     auto delta = to - stroke.center;
397
398     //a zero-length lineto is a no-op; avoid creating a spurious corner
399     if (delta.zero()) return;
400
401     //compute length of line
402     auto lineLength =  mathLength(delta);
403     auto angle = mathAtan(delta);
404
405     delta = {static_cast<SwCoord>(stroke.width), 0};
406     mathRotate(delta, angle + SW_ANGLE_PI2);
407     SCALE(stroke, delta);
408
409     //process corner if necessary
410     if (stroke.firstPt) {
411         /* This is the first segment of a subpath. We need to add a point to each border
412         at their respective starting point locations. */
413         _firstSubPath(stroke, angle, lineLength);
414     } else {
415         //process the current corner
416         stroke.angleOut = angle;
417         _processCorner(stroke, lineLength);
418     }
419
420     //now add a line segment to both the inside and outside paths
421     auto border = stroke.borders;
422     auto side = 1;
423
424     while (side >= 0) {
425         auto pt = to + delta;
426
427         //the ends of lineto borders are movable
428         _borderLineTo(border, pt, true);
429
430         delta.x = -delta.x;
431         delta.y = -delta.y;
432
433         --side;
434         ++border;
435     }
436
437     stroke.angleIn = angle;
438     stroke.center = to;
439     stroke.lineLength = lineLength;
440 }
441
442
443 static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
444 {
445     /* if all control points are coincident, this is a no-op;
446        avoid creating a spurious corner */
447     if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
448         stroke.center = to;
449         return;
450     }
451
452     SwPoint bezStack[37];   //TODO: static?
453     auto limit = bezStack + 32;
454     auto arc = bezStack;
455     auto firstArc = true;
456     arc[0] = to;
457     arc[1] = ctrl2;
458     arc[2] = ctrl1;
459     arc[3] = stroke.center;
460
461     while (arc >= bezStack) {
462         SwFixed angleIn, angleOut, angleMid;
463
464         //initialize with current direction
465         angleIn = angleOut = angleMid = stroke.angleIn;
466
467         if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
468             if (stroke.firstPt) stroke.angleIn = angleIn;
469             mathSplitCubic(arc);
470             arc += 3;
471             continue;
472         }
473
474         if (firstArc) {
475             firstArc = false;
476             //process corner if necessary
477             if (stroke.firstPt) {
478                 _firstSubPath(stroke, angleIn, 0);
479             } else {
480                 stroke.angleOut = angleIn;
481                 _processCorner(stroke, 0);
482             }
483         } else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8) / 4) {
484             //if the deviation from one arc to the next is too great add a round corner
485             stroke.center = arc[3];
486             stroke.angleOut = angleIn;
487             stroke.join = StrokeJoin::Round;
488
489             _processCorner(stroke, 0);
490
491             //reinstate line join style
492             stroke.join = stroke.joinSaved;
493         }
494
495         //the arc's angle is small enough; we can add it directly to each border
496         auto theta1 = mathDiff(angleIn, angleMid) / 2;
497         auto theta2 = mathDiff(angleMid, angleOut) / 2;
498         auto phi1 = mathMean(angleIn, angleMid);
499         auto phi2 = mathMean(angleMid, angleOut);
500         auto length1 = mathDivide(stroke.width, mathCos(theta1));
501         auto length2 = mathDivide(stroke.width, mathCos(theta2));
502         SwFixed alpha0 = 0;
503
504         //compute direction of original arc
505         if (stroke.handleWideStrokes) {
506             alpha0 = mathAtan(arc[0] - arc[3]);
507         }
508
509         auto border = stroke.borders;
510         int32_t side = 0;
511
512         while (side <= 1)
513         {
514             auto rotate = SIDE_TO_ROTATE(side);
515
516             //compute control points
517             SwPoint _ctrl1 = {static_cast<SwCoord>(length1), 0};
518             mathRotate(_ctrl1, phi1 + rotate);
519             SCALE(stroke, _ctrl1);
520             _ctrl1 += arc[2];
521
522             SwPoint _ctrl2 = {static_cast<SwCoord>(length2), 0};
523             mathRotate(_ctrl2, phi2 + rotate);
524             SCALE(stroke, _ctrl2);
525             _ctrl2 += arc[1];
526
527             //compute end point
528             SwPoint _end = {static_cast<SwCoord>(stroke.width), 0};
529             mathRotate(_end, angleOut + rotate);
530             SCALE(stroke, _end);
531             _end += arc[0];
532
533             if (stroke.handleWideStrokes) {
534
535                 /* determine whether the border radius is greater than the radius of
536                    curvature of the original arc */
537                 auto _start = border->pts[border->ptsCnt - 1];
538                 auto alpha1 = mathAtan(_end - _start);
539
540                 //is the direction of the border arc opposite to that of the original arc?
541                 if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) {
542
543                     //use the sine rule to find the intersection point
544                     auto beta = mathAtan(arc[3] - _start);
545                     auto gamma = mathAtan(arc[0] - _end);
546                     auto bvec = _end - _start;
547                     auto blen = mathLength(bvec);
548                     auto sinA = abs(mathSin(alpha1 - gamma));
549                     auto sinB = abs(mathSin(beta - gamma));
550                     auto alen = mathMulDiv(blen, sinA, sinB);
551
552                     SwPoint delta = {static_cast<SwCoord>(alen), 0};
553                     mathRotate(delta, beta);
554                     delta += _start;
555
556                     //circumnavigate the negative sector backwards
557                     border->movable = false;
558                     _borderLineTo(border, delta, false);
559                     _borderLineTo(border, _end, false);
560                     _borderCubicTo(border, _ctrl2, _ctrl1, _start);
561
562                     //and then move to the endpoint
563                     _borderLineTo(border, _end, false);
564
565                     ++side;
566                     ++border;
567                     continue;
568                 }
569
570             //else fall through
571             }
572             _borderCubicTo(border, _ctrl1, _ctrl2, _end);
573             ++side;
574             ++border;
575         }
576         arc -= 3;
577         stroke.angleIn = angleOut;
578     }
579     stroke.center = to;
580 }
581
582
583 static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
584 {
585     if (stroke.cap == StrokeCap::Square) {
586         auto rotate = SIDE_TO_ROTATE(side);
587         auto border = stroke.borders + side;
588
589         SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
590         mathRotate(delta, angle);
591         SCALE(stroke, delta);
592
593         SwPoint delta2 = {static_cast<SwCoord>(stroke.width), 0};
594         mathRotate(delta2, angle + rotate);
595         SCALE(stroke, delta2);
596         delta += stroke.center + delta2;
597
598         _borderLineTo(border, delta, false);
599
600         delta = {static_cast<SwCoord>(stroke.width), 0};
601         mathRotate(delta, angle);
602         SCALE(stroke, delta);
603
604         delta2 = {static_cast<SwCoord>(stroke.width), 0};
605         mathRotate(delta2, angle - rotate);
606         SCALE(stroke, delta2);
607         delta += delta2 + stroke.center;
608
609         _borderLineTo(border, delta, false);
610
611     } else if (stroke.cap == StrokeCap::Round) {
612
613         stroke.angleIn = angle;
614         stroke.angleOut = angle + SW_ANGLE_PI;
615         _arcTo(stroke, side);
616         return;
617
618     } else {  //Butt
619         auto rotate = SIDE_TO_ROTATE(side);
620         auto border = stroke.borders + side;
621
622         SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
623         mathRotate(delta, angle + rotate);
624         SCALE(stroke, delta);
625         delta += stroke.center;
626
627         _borderLineTo(border, delta, false);
628
629         delta = {static_cast<SwCoord>(stroke.width), 0};
630         mathRotate(delta, angle - rotate);
631         SCALE(stroke, delta);
632         delta += stroke.center;
633
634         _borderLineTo(border, delta, false);
635     }
636 }
637
638
639 static void _addReverseLeft(SwStroke& stroke, bool opened)
640 {
641     auto right = stroke.borders + 0;
642     auto left = stroke.borders + 1;
643     assert(left->start >= 0);
644
645     auto newPts = left->ptsCnt - left->start;
646
647     if (newPts <= 0) return;
648
649     _growBorder(right, newPts);
650
651     auto dstPt = right->pts + right->ptsCnt;
652     auto dstTag = right->tags + right->ptsCnt;
653     auto srcPt = left->pts + left->ptsCnt - 1;
654     auto srcTag = left->tags + left->ptsCnt - 1;
655
656     while (srcPt >= left->pts + left->start) {
657         *dstPt = *srcPt;
658         *dstTag = *srcTag;
659
660         if (opened) {
661              dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
662         } else {
663             //switch begin/end tags if necessary
664             auto ttag = dstTag[0] & (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
665             if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END)
666               dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
667         }
668
669         --srcPt;
670         --srcTag;
671         ++dstPt;
672         ++dstTag;
673     }
674
675     left->ptsCnt = left->start;
676     right->ptsCnt += newPts;
677     right->movable = false;
678     left->movable = false;
679 }
680
681
682 static void _beginSubPath(SwStroke& stroke, SwPoint& to, bool opened)
683 {
684     /* We cannot process the first point because there is not enough
685        information regarding its corner/cap. Later, it will be processed
686        in the _endSubPath() */
687
688     stroke.firstPt = true;
689     stroke.center = to;
690     stroke.openSubPath = opened;
691
692     /* Determine if we need to check whether the border radius is greater
693        than the radius of curvature of a curve, to handle this case specially.
694        This is only required if bevel joins or butt caps may be created because
695        round & miter joins and round & square caps cover the nagative sector
696        created with wide strokes. */
697     if ((stroke.join != StrokeJoin::Round) || (stroke.openSubPath && stroke.cap == StrokeCap::Butt))
698         stroke.handleWideStrokes = true;
699     else
700         stroke.handleWideStrokes = false;
701
702     stroke.ptStartSubPath = to;
703     stroke.angleIn = 0;
704 }
705
706
707 static void _endSubPath(SwStroke& stroke)
708 {
709     if (stroke.openSubPath) {
710         auto right = stroke.borders;
711         assert(right);
712
713         /* all right, this is an opened path, we need to add a cap between
714            right & left, add the reverse of left, then add a final cap
715            between left & right */
716         _addCap(stroke, stroke.angleIn, 0);
717
718         //add reversed points from 'left' to 'right'
719         _addReverseLeft(stroke, true);
720
721         //now add the final cap
722         stroke.center = stroke.ptStartSubPath;
723         _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
724
725         /* now end the right subpath accordingly. The left one is rewind
726            and deosn't need further processing */
727         _borderClose(right, false);
728     } else {
729
730         //close the path if needed
731         if (stroke.center != stroke.ptStartSubPath)
732             _lineTo(stroke, stroke.ptStartSubPath);
733
734         //process the corner
735         stroke.angleOut = stroke.subPathAngle;
736         auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
737
738         //No specific corner processing is required if the turn is 0
739         if (turn != 0) {
740
741             //when we turn to the right, the inside is 0
742             int32_t inside = 0;
743
744             //otherwise, the inside is 1
745             if (turn < 0) inside = 1;
746
747             _inside(stroke, inside, stroke.subPathLineLength);        //inside
748             _outside(stroke, 1 - inside, stroke.subPathLineLength);   //outside
749         }
750
751         _borderClose(stroke.borders + 0, false);
752         _borderClose(stroke.borders + 1, true);
753     }
754 }
755
756
757 static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt)
758 {
759     assert(border);
760
761     auto count = border->ptsCnt;
762     auto tags = border->tags;
763     uint32_t _ptsCnt = 0;
764     uint32_t _cntrsCnt = 0;
765     bool inCntr = false;
766
767     while (count > 0) {
768
769         if (tags[0] & SW_STROKE_TAG_BEGIN) {
770             if (inCntr) goto fail;
771             inCntr = true;
772         } else if (!inCntr) goto fail;
773
774         if (tags[0] & SW_STROKE_TAG_END) {
775             inCntr = false;
776             ++_cntrsCnt;
777         }
778         --count;
779         ++_ptsCnt;
780         ++tags;
781     }
782
783     if (inCntr) goto fail;
784     border->valid = true;
785     ptsCnt = _ptsCnt;
786     cntrsCnt = _cntrsCnt;
787
788     return;
789
790 fail:
791     ptsCnt = 0;
792     cntrsCnt = 0;
793 }
794
795
796 static void _exportBorderOutline(SwStroke& stroke, SwOutline* outline, uint32_t side)
797 {
798     auto border = stroke.borders + side;
799     assert(border);
800
801     if (!border->valid) return;
802
803     memcpy(outline->pts + outline->ptsCnt, border->pts, border->ptsCnt * sizeof(SwPoint));
804
805     auto cnt = border->ptsCnt;
806     auto src = border->tags;
807     auto tags = outline->types + outline->ptsCnt;
808     auto cntrs = outline->cntrs + outline->cntrsCnt;
809     uint16_t idx = outline->ptsCnt;
810
811     while (cnt > 0) {
812
813         if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT;
814         else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC;
815         else cout << "what type of stroke outline??" << endl;
816
817         if (*src & SW_STROKE_TAG_END) {
818             *cntrs = idx;
819             ++cntrs;
820             ++outline->cntrsCnt;
821         }
822         ++src;
823         ++tags;
824         ++idx;
825         --cnt;
826     }
827     outline->ptsCnt = outline->ptsCnt + border->ptsCnt;
828 }
829
830
831 /************************************************************************/
832 /* External Class Implementation                                        */
833 /************************************************************************/
834
835 void strokeFree(SwStroke* stroke)
836 {
837     if (!stroke) return;
838
839     //free borders
840     if (stroke->borders[0].pts) free(stroke->borders[0].pts);
841     if (stroke->borders[0].tags) free(stroke->borders[0].tags);
842     if (stroke->borders[1].pts) free(stroke->borders[1].pts);
843     if (stroke->borders[1].tags) free(stroke->borders[1].tags);
844
845     free(stroke);
846 }
847
848
849 void strokeReset(SwStroke& stroke, const Shape* sdata, const Matrix* transform)
850 {
851     assert(sdata);
852
853     if (transform) {
854         stroke.sx = sqrt(pow(transform->e11, 2) + pow(transform->e21, 2));
855         stroke.sy = sqrt(pow(transform->e12, 2) + pow(transform->e22, 2));
856     } else {
857         stroke.sx = stroke.sy = 1.0f;
858     }
859
860     stroke.width = TO_SWCOORD(sdata->strokeWidth() * 0.5);
861     stroke.cap = sdata->strokeCap();
862
863     //Save line join: it can be temporarily changed when stroking curves...
864     stroke.joinSaved = stroke.join = sdata->strokeJoin();
865
866     stroke.borders[0].ptsCnt = 0;
867     stroke.borders[0].start = -1;
868     stroke.borders[0].valid = false;
869
870     stroke.borders[1].ptsCnt = 0;
871     stroke.borders[1].start = -1;
872     stroke.borders[1].valid = false;
873 }
874
875
876 bool strokeParseOutline(SwStroke& stroke, const SwOutline& outline)
877 {
878     uint32_t first = 0;
879
880     for (uint32_t i = 0; i < outline.cntrsCnt; ++i) {
881         auto last = outline.cntrs[i];  //index of last point in contour
882         auto limit = outline.pts + last;
883
884         //Skip empty points
885         if (last <= first) {
886             first = last + 1;
887             continue;
888         }
889
890         auto start = outline.pts[first];
891         auto pt = outline.pts + first;
892         auto types = outline.types + first;
893         auto type = types[0];
894
895         //A contour cannot start with a cubic control point
896         if (type == SW_CURVE_TYPE_CUBIC) return false;
897
898         _beginSubPath(stroke, start, outline.opened);
899
900         while (pt < limit) {
901             ++pt;
902             ++types;
903
904             //emit a signel line_to
905             if (types[0] == SW_CURVE_TYPE_POINT) {
906                 _lineTo(stroke, *pt);
907             //types cubic
908             } else {
909                 if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
910
911                 pt += 2;
912                 types += 2;
913
914                 if (pt <= limit) {
915                     _cubicTo(stroke, pt[-2], pt[-1], pt[0]);
916                     continue;
917                 }
918                 _cubicTo(stroke, pt[-2], pt[-1], start);
919                 goto close;
920             }
921         }
922
923     close:
924         if (!stroke.firstPt) _endSubPath(stroke);
925         first = last + 1;
926     }
927     return true;
928 }
929
930
931 SwOutline* strokeExportOutline(SwStroke& stroke)
932 {
933     uint32_t count1, count2, count3, count4;
934
935     _getCounts(stroke.borders + 0, count1, count2);
936     _getCounts(stroke.borders + 1, count3, count4);
937
938     auto ptsCnt = count1 + count3;
939     auto cntrsCnt = count2 + count4;
940
941     auto outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
942     assert(outline);
943
944     outline->pts = static_cast<SwPoint*>(malloc(sizeof(SwPoint) * ptsCnt));
945     assert(outline->pts);
946
947     outline->types = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * ptsCnt));
948     assert(outline->types);
949
950     outline->cntrs = static_cast<uint32_t*>(malloc(sizeof(uint32_t) * cntrsCnt));
951     assert(outline->cntrs);
952
953     _exportBorderOutline(stroke, outline, 0);  //left
954     _exportBorderOutline(stroke, outline, 1);  //right
955
956     return outline;
957 }
958
959 #endif /* _TVG_SW_STROKER_H_ */