2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
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:
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
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
22 #ifndef _TVG_SW_STROKER_H_
23 #define _TVG_SW_STROKER_H_
25 #include "tvgSwCommon.h"
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;
36 static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
38 return (SW_ANGLE_PI2 - static_cast<SwFixed>(s) * SW_ANGLE_PI);
42 static inline void SCALE(SwStroke& stroke, SwPoint& pt)
49 static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
53 auto maxOld = border->maxPts;
54 auto maxNew = border->ptsCnt + newPts;
56 if (maxNew <= maxOld) return;
60 while (maxCur < maxNew)
61 maxCur += (maxCur >> 1) + 16;
63 border->pts = static_cast<SwPoint*>(realloc(border->pts, maxCur * sizeof(SwPoint)));
66 border->tags = static_cast<uint8_t*>(realloc(border->tags, maxCur * sizeof(uint8_t)));
69 border->maxPts = maxCur;
73 static void _borderClose(SwStrokeBorder* border, bool reverse)
75 assert(border && border->start >= 0);
77 uint32_t start = border->start;
78 uint32_t count = border->ptsCnt;
80 //Don't record empty paths!
81 if (count <= start + 1U) {
82 border->ptsCnt = start;
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];
91 auto pt1 = border->pts + start + 1;
92 auto pt2 = border->pts + count - 1;
103 auto tag1 = border->tags + start + 1;
104 auto tag2 = border->tags + count - 1;
106 while (tag1 < tag2) {
115 border->tags[start] |= SW_STROKE_TAG_BEGIN;
116 border->tags[count - 1] |= SW_STROKE_TAG_END;
120 border->movable = false;
124 static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& to)
126 assert(border && border->start >= 0);
128 _growBorder(border, 3);
130 auto pt = border->pts + border->ptsCnt;
131 auto tag = border->tags + border->ptsCnt;
137 tag[0] = SW_STROKE_TAG_CUBIC;
138 tag[1] = SW_STROKE_TAG_CUBIC;
139 tag[2] = SW_STROKE_TAG_POINT;
143 border->movable = false;
147 static void _borderArcTo(SwStrokeBorder* border, SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff, SwStroke& stroke)
149 constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2;
150 SwPoint a = {static_cast<SwCoord>(radius), 0};
151 mathRotate(a, angleStart);
155 auto total = angleDiff;
156 auto angle = angleStart;
157 auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
161 if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE;
162 else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE;
164 auto next = angle + step;
166 if (theta < 0) theta = -theta;
171 SwPoint b = {static_cast<SwCoord>(radius), 0};
176 //compute first and second control points
177 auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3);
179 SwPoint a2 = {static_cast<SwCoord>(length), 0};
180 mathRotate(a2, angle + rotate);
184 SwPoint b2 = {static_cast<SwCoord>(length), 0};
185 mathRotate(b2, next - rotate);
190 _borderCubicTo(border, a2, b2, b);
192 //process the rest of the arc?
200 static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable)
202 assert(border && border->start >= 0);
204 if (border->movable) {
206 border->pts[border->ptsCnt - 1] = to;
209 //don't add zero-length line_to
210 if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
212 _growBorder(border, 1);
213 border->pts[border->ptsCnt] = to;
214 border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT;
218 border->movable = movable;
222 static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to)
226 //close current open path if any?
227 if (border->start >= 0) _borderClose(border, false);
229 border->start = border->ptsCnt;
230 border->movable = false;
232 _borderLineTo(border, to, false);
236 static void _arcTo(SwStroke& stroke, int32_t side)
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;
243 _borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total, stroke);
244 border->movable = false;
248 static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
250 constexpr SwFixed MITER_LIMIT = 4 * (1 << 16);
252 assert(MITER_LIMIT >= 0x10000);
254 auto border = stroke.borders + side;
257 if (stroke.join == StrokeJoin::Round) {
258 _arcTo(stroke, side);
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;
267 auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
268 if (theta == SW_ANGLE_PI) {
270 phi = stroke.angleIn;
273 phi = stroke.angleIn + theta + rotate;
276 thcos = mathCos(theta);
277 auto sigma = mathMultiply(MITER_LIMIT, thcos);
279 //is miter limit exceeded?
280 if (sigma < 0x10000L) bevel = true;
283 //this is a bevel (broken angle)
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)
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);
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);
314 static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
316 auto border = stroke.borders + side;
317 auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2;
319 bool intersect = false;
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;
329 auto rotate = SIDE_TO_ROTATE(side);
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;
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;
347 _borderLineTo(border, delta, false);
351 void _processCorner(SwStroke& stroke, SwFixed lineLength)
353 auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
355 //no specific corner processing is required if the turn is 0
356 if (turn == 0) return;
358 //when we turn to the right, the inside side is 0
361 //otherwise, the inside is 1
362 if (turn < 0) inside = 1;
365 _inside(stroke, inside, lineLength);
367 //process the outside
368 _outside(stroke, 1 - inside, lineLength);
372 void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
374 SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
375 mathRotate(delta, startAngle + SW_ANGLE_PI2);
376 SCALE(stroke, delta);
378 auto pt = stroke.center + delta;
379 auto border = stroke.borders;
380 _borderMoveTo(border, pt);
382 pt = stroke.center - delta;
384 _borderMoveTo(border, pt);
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;
394 static void _lineTo(SwStroke& stroke, const SwPoint& to)
396 auto delta = to - stroke.center;
398 //a zero-length lineto is a no-op; avoid creating a spurious corner
399 if (delta.zero()) return;
401 //compute length of line
402 auto lineLength = mathLength(delta);
403 auto angle = mathAtan(delta);
405 delta = {static_cast<SwCoord>(stroke.width), 0};
406 mathRotate(delta, angle + SW_ANGLE_PI2);
407 SCALE(stroke, delta);
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);
415 //process the current corner
416 stroke.angleOut = angle;
417 _processCorner(stroke, lineLength);
420 //now add a line segment to both the inside and outside paths
421 auto border = stroke.borders;
425 auto pt = to + delta;
427 //the ends of lineto borders are movable
428 _borderLineTo(border, pt, true);
437 stroke.angleIn = angle;
439 stroke.lineLength = lineLength;
443 static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
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()) {
452 SwPoint bezStack[37]; //TODO: static?
453 auto limit = bezStack + 32;
455 auto firstArc = true;
459 arc[3] = stroke.center;
461 while (arc >= bezStack) {
462 SwFixed angleIn, angleOut, angleMid;
464 //initialize with current direction
465 angleIn = angleOut = angleMid = stroke.angleIn;
467 if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
468 if (stroke.firstPt) stroke.angleIn = angleIn;
476 //process corner if necessary
477 if (stroke.firstPt) {
478 _firstSubPath(stroke, angleIn, 0);
480 stroke.angleOut = angleIn;
481 _processCorner(stroke, 0);
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;
489 _processCorner(stroke, 0);
491 //reinstate line join style
492 stroke.join = stroke.joinSaved;
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));
504 //compute direction of original arc
505 if (stroke.handleWideStrokes) {
506 alpha0 = mathAtan(arc[0] - arc[3]);
509 auto border = stroke.borders;
514 auto rotate = SIDE_TO_ROTATE(side);
516 //compute control points
517 SwPoint _ctrl1 = {static_cast<SwCoord>(length1), 0};
518 mathRotate(_ctrl1, phi1 + rotate);
519 SCALE(stroke, _ctrl1);
522 SwPoint _ctrl2 = {static_cast<SwCoord>(length2), 0};
523 mathRotate(_ctrl2, phi2 + rotate);
524 SCALE(stroke, _ctrl2);
528 SwPoint _end = {static_cast<SwCoord>(stroke.width), 0};
529 mathRotate(_end, angleOut + rotate);
533 if (stroke.handleWideStrokes) {
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);
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) {
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);
552 SwPoint delta = {static_cast<SwCoord>(alen), 0};
553 mathRotate(delta, beta);
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);
562 //and then move to the endpoint
563 _borderLineTo(border, _end, false);
572 _borderCubicTo(border, _ctrl1, _ctrl2, _end);
577 stroke.angleIn = angleOut;
583 static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
585 if (stroke.cap == StrokeCap::Square) {
586 auto rotate = SIDE_TO_ROTATE(side);
587 auto border = stroke.borders + side;
589 SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
590 mathRotate(delta, angle);
591 SCALE(stroke, delta);
593 SwPoint delta2 = {static_cast<SwCoord>(stroke.width), 0};
594 mathRotate(delta2, angle + rotate);
595 SCALE(stroke, delta2);
596 delta += stroke.center + delta2;
598 _borderLineTo(border, delta, false);
600 delta = {static_cast<SwCoord>(stroke.width), 0};
601 mathRotate(delta, angle);
602 SCALE(stroke, delta);
604 delta2 = {static_cast<SwCoord>(stroke.width), 0};
605 mathRotate(delta2, angle - rotate);
606 SCALE(stroke, delta2);
607 delta += delta2 + stroke.center;
609 _borderLineTo(border, delta, false);
611 } else if (stroke.cap == StrokeCap::Round) {
613 stroke.angleIn = angle;
614 stroke.angleOut = angle + SW_ANGLE_PI;
615 _arcTo(stroke, side);
619 auto rotate = SIDE_TO_ROTATE(side);
620 auto border = stroke.borders + side;
622 SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
623 mathRotate(delta, angle + rotate);
624 SCALE(stroke, delta);
625 delta += stroke.center;
627 _borderLineTo(border, delta, false);
629 delta = {static_cast<SwCoord>(stroke.width), 0};
630 mathRotate(delta, angle - rotate);
631 SCALE(stroke, delta);
632 delta += stroke.center;
634 _borderLineTo(border, delta, false);
639 static void _addReverseLeft(SwStroke& stroke, bool opened)
641 auto right = stroke.borders + 0;
642 auto left = stroke.borders + 1;
643 assert(left->start >= 0);
645 auto newPts = left->ptsCnt - left->start;
647 if (newPts <= 0) return;
649 _growBorder(right, newPts);
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;
656 while (srcPt >= left->pts + left->start) {
661 dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
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);
675 left->ptsCnt = left->start;
676 right->ptsCnt += newPts;
677 right->movable = false;
678 left->movable = false;
682 static void _beginSubPath(SwStroke& stroke, SwPoint& to, bool opened)
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() */
688 stroke.firstPt = true;
690 stroke.openSubPath = opened;
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;
700 stroke.handleWideStrokes = false;
702 stroke.ptStartSubPath = to;
707 static void _endSubPath(SwStroke& stroke)
709 if (stroke.openSubPath) {
710 auto right = stroke.borders;
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);
718 //add reversed points from 'left' to 'right'
719 _addReverseLeft(stroke, true);
721 //now add the final cap
722 stroke.center = stroke.ptStartSubPath;
723 _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
725 /* now end the right subpath accordingly. The left one is rewind
726 and deosn't need further processing */
727 _borderClose(right, false);
730 //close the path if needed
731 if (stroke.center != stroke.ptStartSubPath)
732 _lineTo(stroke, stroke.ptStartSubPath);
735 stroke.angleOut = stroke.subPathAngle;
736 auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
738 //No specific corner processing is required if the turn is 0
741 //when we turn to the right, the inside is 0
744 //otherwise, the inside is 1
745 if (turn < 0) inside = 1;
747 _inside(stroke, inside, stroke.subPathLineLength); //inside
748 _outside(stroke, 1 - inside, stroke.subPathLineLength); //outside
751 _borderClose(stroke.borders + 0, false);
752 _borderClose(stroke.borders + 1, true);
757 static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt)
761 auto count = border->ptsCnt;
762 auto tags = border->tags;
763 uint32_t _ptsCnt = 0;
764 uint32_t _cntrsCnt = 0;
769 if (tags[0] & SW_STROKE_TAG_BEGIN) {
770 if (inCntr) goto fail;
772 } else if (!inCntr) goto fail;
774 if (tags[0] & SW_STROKE_TAG_END) {
783 if (inCntr) goto fail;
784 border->valid = true;
786 cntrsCnt = _cntrsCnt;
796 static void _exportBorderOutline(SwStroke& stroke, SwOutline* outline, uint32_t side)
798 auto border = stroke.borders + side;
801 if (!border->valid) return;
803 memcpy(outline->pts + outline->ptsCnt, border->pts, border->ptsCnt * sizeof(SwPoint));
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;
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;
817 if (*src & SW_STROKE_TAG_END) {
827 outline->ptsCnt = outline->ptsCnt + border->ptsCnt;
831 /************************************************************************/
832 /* External Class Implementation */
833 /************************************************************************/
835 void strokeFree(SwStroke* stroke)
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);
849 void strokeReset(SwStroke& stroke, const Shape* sdata, const Matrix* 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));
857 stroke.sx = stroke.sy = 1.0f;
860 stroke.width = TO_SWCOORD(sdata->strokeWidth() * 0.5);
861 stroke.cap = sdata->strokeCap();
863 //Save line join: it can be temporarily changed when stroking curves...
864 stroke.joinSaved = stroke.join = sdata->strokeJoin();
866 stroke.borders[0].ptsCnt = 0;
867 stroke.borders[0].start = -1;
868 stroke.borders[0].valid = false;
870 stroke.borders[1].ptsCnt = 0;
871 stroke.borders[1].start = -1;
872 stroke.borders[1].valid = false;
876 bool strokeParseOutline(SwStroke& stroke, const SwOutline& outline)
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;
890 auto start = outline.pts[first];
891 auto pt = outline.pts + first;
892 auto types = outline.types + first;
893 auto type = types[0];
895 //A contour cannot start with a cubic control point
896 if (type == SW_CURVE_TYPE_CUBIC) return false;
898 _beginSubPath(stroke, start, outline.opened);
904 //emit a signel line_to
905 if (types[0] == SW_CURVE_TYPE_POINT) {
906 _lineTo(stroke, *pt);
909 if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
915 _cubicTo(stroke, pt[-2], pt[-1], pt[0]);
918 _cubicTo(stroke, pt[-2], pt[-1], start);
924 if (!stroke.firstPt) _endSubPath(stroke);
931 SwOutline* strokeExportOutline(SwStroke& stroke)
933 uint32_t count1, count2, count3, count4;
935 _getCounts(stroke.borders + 0, count1, count2);
936 _getCounts(stroke.borders + 1, count3, count4);
938 auto ptsCnt = count1 + count3;
939 auto cntrsCnt = count2 + count4;
941 auto outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
944 outline->pts = static_cast<SwPoint*>(malloc(sizeof(SwPoint) * ptsCnt));
945 assert(outline->pts);
947 outline->types = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * ptsCnt));
948 assert(outline->types);
950 outline->cntrs = static_cast<uint32_t*>(malloc(sizeof(uint32_t) * cntrsCnt));
951 assert(outline->cntrs);
953 _exportBorderOutline(stroke, outline, 0); //left
954 _exportBorderOutline(stroke, outline, 1); //right
959 #endif /* _TVG_SW_STROKER_H_ */