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
26 #include "tvgSwCommon.h"
28 /************************************************************************/
29 /* Internal Class Implementation */
30 /************************************************************************/
32 constexpr auto MAX_SPANS = 256;
33 constexpr auto PIXEL_BITS = 8; //must be at least 6 bits!
34 constexpr auto ONE_PIXEL = (1L << PIXEL_BITS);
70 SwPoint bezStack[32 * 3 + 1];
75 SwSpan spans[MAX_SPANS];
97 static inline SwPoint UPSCALE(const SwPoint& pt)
99 return {pt.x << (PIXEL_BITS - 6), pt.y << (PIXEL_BITS - 6)};
103 static inline SwPoint TRUNC(const SwPoint& pt)
105 return {pt.x >> PIXEL_BITS, pt.y >> PIXEL_BITS};
109 static inline SwCoord TRUNC(const SwCoord x)
111 return x >> PIXEL_BITS;
115 static inline SwPoint SUBPIXELS(const SwPoint& pt)
117 return {pt.x << PIXEL_BITS, pt.y << PIXEL_BITS};
121 static inline SwCoord SUBPIXELS(const SwCoord x)
123 return (x << PIXEL_BITS);
127 * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
128 * algorithm. We use alpha = 1, beta = 3/8, giving us results with a
129 * largest error less than 7% compared to the exact value.
131 static inline SwCoord HYPOT(SwPoint pt)
133 if (pt.x < 0) pt.x = -pt.x;
134 if (pt.y < 0) pt.y = -pt.y;
135 return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3)));
138 static void _genSpan(SwRleData* rle, const SwSpan* spans, uint32_t count)
140 auto newSize = rle->size + count;
142 /* allocate enough memory for new spans */
143 /* alloc is required to prevent free and reallocation */
144 /* when the rle needs to be regenerated because of attribute change. */
145 if (rle->alloc < newSize) {
146 rle->alloc = (newSize * 2);
147 rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
150 //copy the new spans to the allocated memory
151 SwSpan* lastSpan = rle->spans + rle->size;
152 memcpy(lastSpan, spans, count * sizeof(SwSpan));
158 static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount)
165 if (y >= rw.clip.h) return;
167 /* compute the coverage line's coverage, depending on the outline fill rule */
168 /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
169 auto coverage = static_cast<int>(area >> (PIXEL_BITS * 2 + 1 - 8)); //range 0 - 256
171 if (coverage < 0) coverage = -coverage;
173 if (rw.outline->fillRule == FillRule::EvenOdd) {
175 if (coverage > 256) coverage = 512 - coverage;
176 else if (coverage == 256) coverage = 255;
178 //normal non-zero winding rule
179 if (coverage >= 256) coverage = 255;
182 //span has ushort coordinates. check limit overflow
184 //LOG: x coordinate overflow!
188 //LOG: y coordinate overflow!
193 if (!rw.antiAlias) coverage = 255;
194 auto count = rw.spansCnt;
195 auto span = rw.spans + count - 1;
197 //see whether we can add this span to the current list
198 if ((count > 0) && (rw.ySpan == y) &&
199 (span->x + span->len == x) && (span->coverage == coverage)) {
203 if (x + acount >= rw.clip.w) xOver -= (x + acount - rw.clip.w);
204 if (x < 0) xOver += x;
206 //span->len += (acount + xOver) - 1;
207 span->len += (acount + xOver);
211 if (count >= MAX_SPANS) {
212 _genSpan(rw.rle, rw.spans, count);
222 if (x + acount >= rw.clip.w) xOver -= (x + acount - rw.clip.w);
229 if (acount + xOver <= 0) return;
231 //add a span to the current list
234 span->len = (acount + xOver);
235 span->coverage = coverage;
242 static void _sweep(RleWorker& rw)
244 if (rw.cellsCnt == 0) return;
249 for (int y = 0; y < rw.yCnt; ++y) {
252 auto cell = rw.yCells[y];
256 if (cell->x > x && cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), cell->x - x);
257 cover += cell->cover;
258 auto area = cover * (ONE_PIXEL * 2) - cell->area;
259 if (area != 0 && cell->x >= 0) _horizLine(rw, cell->x, y, area, 1);
265 if (cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), rw.cellXCnt - x);
268 if (rw.spansCnt > 0) _genSpan(rw.rle, rw.spans, rw.spansCnt);
272 static Cell* _findCell(RleWorker& rw)
274 auto x = rw.cellPos.x;
275 if (x > rw.cellXCnt) x = rw.cellXCnt;
277 auto pcell = &rw.yCells[rw.cellPos.y];
281 if (!cell || cell->x > x) break;
282 if (cell->x == x) return cell;
286 if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1);
288 auto cell = rw.cells + rw.cellsCnt++;
299 static void _recordCell(RleWorker& rw)
301 if (rw.area | rw.cover) {
302 auto cell = _findCell(rw);
303 cell->area += rw.area;
304 cell->cover += rw.cover;
309 static void _setCell(RleWorker& rw, SwPoint pos)
311 /* Move the cell pointer to a new position. We set the `invalid' */
312 /* flag to indicate that the cell isn't part of those we're interested */
313 /* in during the render phase. This means that: */
315 /* . the new vertical position must be within min_ey..max_ey-1. */
316 /* . the new horizontal position must be strictly less than max_ex */
318 /* Note that if a cell is to the left of the clipping region, it is */
319 /* actually set to the (min_ex-1) horizontal position. */
321 /* All cells that are on the left of the clipping region go to the
322 min_ex - 1 horizontal position. */
323 pos.y -= rw.cellMin.y;
325 if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
326 pos.x -= rw.cellMin.x;
327 if (pos.x < 0) pos.x = -1;
329 //Are we moving to a different cell?
330 if (pos != rw.cellPos) {
331 //Record the current one if it is valid
332 if (!rw.invalid) _recordCell(rw);
338 rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt);
342 static void _startCell(RleWorker& rw, SwPoint pos)
344 if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
345 if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x;
346 //if (pos.x < rw.cellMin.x) pos.x = (rw.cellMin.x - 1);
350 rw.cellPos = pos - rw.cellMin;
357 static void _moveTo(RleWorker& rw, const SwPoint& to)
359 //record current cell, if any */
360 if (!rw.invalid) _recordCell(rw);
362 //start to a new position
363 _startCell(rw, TRUNC(to));
369 static void _lineTo(RleWorker& rw, const SwPoint& to)
371 #define SW_UDIV(a, b) \
372 static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \
373 (sizeof(long) * CHAR_BIT - PIXEL_BITS))
375 auto e1 = TRUNC(rw.pos);
379 if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) ||
380 (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) {
385 auto diff = to - rw.pos;
386 auto f1 = rw.pos - SUBPIXELS(e1);
392 //any horizontal line
393 } else if (diff.y == 0) {
396 } else if (diff.x == 0) {
401 rw.cover += (f2.y - f1.y);
402 rw.area += (f2.y - f1.y) * f1.x * 2;
406 } while(e1.y != e2.y);
411 rw.cover += (f2.y - f1.y);
412 rw.area += (f2.y - f1.y) * f1.x * 2;
416 } while(e1.y != e2.y);
420 Area prod = diff.x * f1.y - diff.y * f1.x;
422 /* These macros speed up repetitive divisions by replacing them
423 with multiplications and right shifts. */
424 auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
425 auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
427 /* The fundamental value `prod' determines which side and the */
428 /* exact coordinate where the line exits current cell. It is */
429 /* also easily updated when moving from one cell to the next. */
431 auto px = diff.x * ONE_PIXEL;
432 auto py = diff.y * ONE_PIXEL;
435 if (prod <= 0 && prod - px > 0) {
436 f2 = {0, SW_UDIV(-prod, -dx_r)};
438 rw.cover += (f2.y - f1.y);
439 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
440 f1 = {ONE_PIXEL, f2.y};
443 } else if (prod - px <= 0 && prod - px + py > 0) {
445 f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
446 rw.cover += (f2.y - f1.y);
447 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
451 } else if (prod - px + py <= 0 && prod + py >= 0) {
453 f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
454 rw.cover += (f2.y - f1.y);
455 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
460 f2 = {SW_UDIV(prod, -dy_r), 0};
462 rw.cover += (f2.y - f1.y);
463 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
464 f1 = {f2.x, ONE_PIXEL};
473 f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)};
474 rw.cover += (f2.y - f1.y);
475 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
480 static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
482 auto arc = rw.bezStack;
488 //Short-cut the arc that crosses the current band
493 for (auto i = 1; i < 4; ++i) {
495 if (y < min) min = y;
496 if (y > max) max = y;
499 if (TRUNC(min) >= rw.cellMax.y || TRUNC(max) < rw.cellMin.y) goto draw;
501 /* Decide whether to split or draw. See `Rapid Termination */
502 /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */
504 /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */
507 //diff is the P0 - P3 chord vector
508 auto diff = arc[3] - arc[0];
509 auto L = HYPOT(diff);
511 //avoid possible arithmetic overflow below by splitting
512 if (L > SHRT_MAX) goto split;
514 //max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1)
515 auto sLimit = L * (ONE_PIXEL / 6);
517 auto diff1 = arc[1] - arc[0];
518 auto s = diff.y * diff1.x - diff.x * diff1.y;
520 if (s > sLimit) goto split;
522 //s is L * the perpendicular distance from P2 to the line P0 - P3
523 auto diff2 = arc[2] - arc[0];
524 s = diff.y * diff2.x - diff.x * diff2.y;
526 if (s > sLimit) goto split;
528 /* Split super curvy segments where the off points are so far
529 from the chord that the angles P0-P1-P3 or P0-P2-P3 become
530 acute as detected by appropriate dot products */
531 if (diff1.x * (diff1.x - diff.x) + diff1.y * (diff1.y - diff.y) > 0 ||
532 diff2.x * (diff2.x - diff.x) + diff2.y * (diff2.y - diff.y) > 0)
545 if (arc == rw.bezStack) return;
551 static bool _decomposeOutline(RleWorker& rw)
553 auto outline = rw.outline;
554 auto first = 0; //index of first point in contour
556 for (uint32_t n = 0; n < outline->cntrsCnt; ++n) {
557 auto last = outline->cntrs[n];
558 auto limit = outline->pts + last;
559 auto start = UPSCALE(outline->pts[first]);
560 auto pt = outline->pts + first;
561 auto types = outline->types + first;
563 /* A contour cannot start with a cubic control point! */
564 if (types[0] == SW_CURVE_TYPE_CUBIC) goto invalid_outline;
566 _moveTo(rw, UPSCALE(outline->pts[first]));
572 //emit a single line_to
573 if (types[0] == SW_CURVE_TYPE_POINT) {
574 _lineTo(rw, UPSCALE(*pt));
577 if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC)
578 goto invalid_outline;
584 _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
587 _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
599 //LOG: Invalid Outline!
604 static int _genRle(RleWorker& rw)
606 if (setjmp(rw.jmpBuf) == 0) {
607 auto ret = _decomposeOutline(rw);
608 if (!rw.invalid) _recordCell(rw);
609 if (ret) return 0; //success
610 else return 1; //fail
612 return -1; //lack of cell memory
616 SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
619 auto spans = targetRle->spans;
620 auto end = targetRle->spans + targetRle->size;
621 auto clipSpans = clip->spans;
622 auto clipEnd = clip->spans + clip->size;
624 while (spanCnt > 0 && spans < end ) {
625 if (clipSpans == clipEnd) {
629 if (clipSpans->y > spans->y) {
633 if (spans->y != clipSpans->y) {
638 auto sx2 = sx1 + spans->len;
639 auto cx1 = clipSpans->x;
640 auto cx2 = cx1 + clipSpans->len;
642 if (cx1 < sx1 && cx2 < sx1) {
646 else if (sx1 < cx1 && sx2 < cx1) {
650 auto x = sx1 > cx1 ? sx1 : cx1;
651 auto len = (sx2 < cx2 ? sx2 : cx2) - x;
653 auto spansCorverage = spans->coverage;
654 auto clipSpansCoverage = clipSpans->coverage;
655 out->x = sx1 > cx1 ? sx1 : cx1;
656 out->len = (sx2 < cx2 ? sx2 : cx2) - out->x;
658 out->coverage = (uint8_t)((spansCorverage * clipSpansCoverage) >> 8);
662 if (sx2 < cx2) ++spans;
668 SwSpan* _intersectMaskRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
672 auto spans = targetRle->spans;
673 auto end = targetRle->spans + targetRle->size;
674 auto clipSpans = clip->spans;
675 auto clipSpans1 = clip->spans;
676 auto clipEnd = clip->spans + clip->size;
678 auto maskClipMin = clipSpans1->y;
679 auto maskClipMax = clipSpans1->y;
681 while (clipSpans1->y) {
682 if (clipSpans1->y > maskClipMax)
683 maskClipMax = clipSpans1->y;
685 if (clipSpans1->y < maskClipMax)
686 maskClipMin = clipSpans1->y;
690 while (spanCnt && spans < end ) {
691 if (clipSpans > clipEnd) {
697 if (spans->y < maskClipMin || spans->y > maskClipMax) {
700 out->len = spans->len;
701 out->coverage = spans->coverage;
705 while (clipSpans->y) {
707 auto sx2 = sx1 + spans->len;
708 auto cx1 = clipSpans->x;
709 auto cx2 = cx1 + clipSpans->len;
710 auto x = sx1 > cx1 ? sx1 : cx1;
711 auto len = (sx2 < cx2 ? sx2 : cx2) - x;
715 out->y = clipSpans->y;
717 out->coverage = spans->coverage;
721 out->y = clipSpans->y;
722 out->len = sx2 - cx2;
723 out->coverage = spans->coverage;
737 SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
740 auto spans = targetRle->spans;
741 auto end = targetRle->spans + targetRle->size;
742 auto minx = static_cast<int16_t>(bbox->min.x);
743 auto miny = static_cast<int16_t>(bbox->min.y);
744 auto maxx = minx + static_cast<int16_t>(bbox->max.x - bbox->min.x) - 1;
745 auto maxy = miny + static_cast<int16_t>(bbox->max.y - bbox->min.y) - 1;
747 while (spanCnt && spans < end ) {
748 if (spans->y > maxy) {
752 if (spans->y < miny || spans->x > maxx || spans->x + spans->len <= minx) {
756 if (spans->x < minx) {
757 out->len = (spans->len - (minx - spans->x)) < (maxx - minx + 1) ? (spans->len - (minx - spans->x)) : (maxx - minx + 1);
762 out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1);
766 out->coverage = spans->coverage;
775 /************************************************************************/
776 /* External Class Implementation */
777 /************************************************************************/
779 SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& bbox, const SwSize& clip, bool antiAlias)
781 constexpr auto RENDER_POOL_SIZE = 16384L;
782 constexpr auto BAND_SIZE = 40;
784 //TODO: We can preserve several static workers in advance
786 Cell buffer[RENDER_POOL_SIZE / sizeof(Cell)];
790 rw.bufferSize = sizeof(buffer);
791 rw.yCells = reinterpret_cast<Cell**>(buffer);
798 rw.cellMin = bbox.min;
799 rw.cellMax = bbox.max;
800 rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
801 rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
803 rw.outline = const_cast<SwOutline*>(outline);
804 rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64
807 rw.antiAlias = antiAlias;
809 if (!rle) rw.rle = reinterpret_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
813 Band bands[BAND_SIZE];
816 /* set up vertical bands */
817 auto bandCnt = static_cast<int>((rw.cellMax.y - rw.cellMin.y) / rw.bandSize);
818 if (bandCnt == 0) bandCnt = 1;
819 else if (bandCnt >= BAND_SIZE) bandCnt = (BAND_SIZE - 1);
821 auto min = rw.cellMin.y;
822 auto yMax = rw.cellMax.y;
826 for (int n = 0; n < bandCnt; ++n, min = max) {
827 max = min + rw.bandSize;
828 if (n == bandCnt -1 || max > yMax) max = yMax;
834 while (band >= bands) {
835 rw.yCells = static_cast<Cell**>(rw.buffer);
836 rw.yCnt = band->max - band->min;
838 int cellStart = sizeof(Cell*) * (int)rw.yCnt;
839 int cellMod = cellStart % sizeof(Cell);
841 if (cellMod > 0) cellStart += sizeof(Cell) - cellMod;
843 auto cellEnd = rw.bufferSize;
844 cellEnd -= cellEnd % sizeof(Cell);
846 auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + cellEnd);
847 rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
849 if (rw.cells >= cellsMax) goto reduce_bands;
851 rw.maxCells = cellsMax - rw.cells;
852 if (rw.maxCells < 2) goto reduce_bands;
854 for (int y = 0; y < rw.yCnt; ++y)
855 rw.yCells[y] = nullptr;
859 rw.cellMin.y = band->min;
860 rw.cellMax.y = band->max;
861 rw.cellYCnt = band->max - band->min;
868 } else if (ret == 1) {
873 /* render pool overflow: we will reduce the render band by half */
874 auto bottom = band->min;
875 auto top = band->max;
876 auto middle = bottom + ((top - bottom) >> 1);
878 /* This is too complex for a single scanline; there must
880 if (middle == bottom) goto error;
882 if (bottom - top >= rw.bandSize) ++rw.bandShoot;
884 band[1].min = bottom;
885 band[1].max = middle;
886 band[0].min = middle;
892 if (rw.bandShoot > 8 && rw.bandSize > 16)
893 rw.bandSize = (rw.bandSize >> 1);
904 void rleReset(SwRleData* rle)
911 void rleFree(SwRleData* rle)
914 if (rle->spans) free(rle->spans);
918 void updateRleSpans(SwRleData *rle, const SwSpan* curSpans, uint32_t size)
925 if (!rle->spans || !curSpans) return;
927 rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->size * sizeof(SwSpan)));
929 if (!rle->spans) return;
931 for (int i = 0; i < (int)rle->size ; i++)
933 rle->spans[i].x = curSpans[i].x;
934 rle->spans[i].y = curSpans[i].y;
935 rle->spans[i].len = curSpans[i].len;
936 rle->spans[i].coverage = curSpans[i].coverage;
940 void rleClipPath(SwRleData *rle, const SwRleData *clip)
942 if (rle->size == 0 || clip->size == 0) return;
943 auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
944 auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
946 auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
949 updateRleSpans(rle, spans, spansEnd - spans);
951 if (spans) free(spans);
953 #ifdef THORVG_LOG_ENABLED
954 cout << "SW_ENGINE: Using ClipPath!" << endl;
958 void rleClipRect(SwRleData *rle, const SwBBox* clip)
960 if (rle->size == 0) return;
961 auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
963 auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
966 updateRleSpans(rle, spans, spansEnd - spans);
968 if (spans) free(spans);
970 #ifdef THORVG_LOG_ENABLED
971 cout <<"SW_ENGINE: Using ClipRect!" << endl;
976 void rleAlphaMask(SwRleData *rle, const SwRleData *clip)
978 if (rle->size == 0 || clip->size == 0) return;
979 auto spanCnt = rle->size + clip->size;
981 auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
984 auto spansEnd = _intersectMaskRegion(clip, rle, spans, spanCnt);
987 updateRleSpans(rle, spans, spansEnd - spans);
989 if (spans) free(spans);