1 /*
2 * Copyright (c) 2020 - 2024 the ThorVG project. 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
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25
26 /*
27 * The FreeType Project LICENSE
28 * ----------------------------
29
30 * 2006-Jan-27
31
32 * Copyright 1996-2002, 2006 by
33 * David Turner, Robert Wilhelm, and Werner Lemberg
34
35
36
37 * Introduction
38 * ============
39
40 * The FreeType Project is distributed in several archive packages;
41 * some of them may contain, in addition to the FreeType font engine,
42 * various tools and contributions which rely on, or relate to, the
43 * FreeType Project.
44
45 * This license applies to all files found in such packages, and
46 * which do not fall under their own explicit license. The license
47 * affects thus the FreeType font engine, the test programs,
48 * documentation and makefiles, at the very least.
49
50 * This license was inspired by the BSD, Artistic, and IJG
51 * (Independent JPEG Group) licenses, which all encourage inclusion
52 * and use of free software in commercial and freeware products
53 * alike. As a consequence, its main points are that:
54
55 * o We don't promise that this software works. However, we will be
56 * interested in any kind of bug reports. (`as is' distribution)
57
58 * o You can use this software for whatever you want, in parts or
59 * full form, without having to pay us. (`royalty-free' usage)
60
61 * o You may not pretend that you wrote this software. If you use
62 * it, or only parts of it, in a program, you must acknowledge
63 * somewhere in your documentation that you have used the
64 * FreeType code. (`credits')
65
66 * We specifically permit and encourage the inclusion of this
67 * software, with or without modifications, in commercial products.
68 * We disclaim all warranties covering The FreeType Project and
69 * assume no liability related to The FreeType Project.
70
71
72 * Finally, many people asked us for a preferred form for a
73 * credit/disclaimer to use in compliance with this license. We thus
74 * encourage you to use the following text:
75
76 * """
77 * Portions of this software are copyright � <year> The FreeType
78 * Project (www.freetype.org). All rights reserved.
79 * """
80
81 * Please replace <year> with the value from the FreeType version you
82 * actually use.
83
84 * Legal Terms
85 * ===========
86
87 * 0. Definitions
88 * --------------
89
90 * Throughout this license, the terms `package', `FreeType Project',
91 * and `FreeType archive' refer to the set of files originally
92 * distributed by the authors (David Turner, Robert Wilhelm, and
93 * Werner Lemberg) as the `FreeType Project', be they named as alpha,
94 * beta or final release.
95
96 * `You' refers to the licensee, or person using the project, where
97 * `using' is a generic term including compiling the project's source
98 * code as well as linking it to form a `program' or `executable'.
99 * This program is referred to as `a program using the FreeType
100 * engine'.
101
102 * This license applies to all files distributed in the original
103 * FreeType Project, including all source code, binaries and
104 * documentation, unless otherwise stated in the file in its
105 * original, unmodified form as distributed in the original archive.
106 * If you are unsure whether or not a particular file is covered by
107 * this license, you must contact us to verify this.
108
109 * The FreeType Project is copyright (C) 1996-2000 by David Turner,
110 * Robert Wilhelm, and Werner Lemberg. All rights reserved except as
111 * specified below.
112
113 * 1. No Warranty
114 * --------------
115
116 * THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
117 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
118 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
119 * PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
120 * BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
121 * USE, OF THE FREETYPE PROJECT.
122
123 * 2. Redistribution
124 * -----------------
125
126 * This license grants a worldwide, royalty-free, perpetual and
127 * irrevocable right and license to use, execute, perform, compile,
128 * display, copy, create derivative works of, distribute and
129 * sublicense the FreeType Project (in both source and object code
130 * forms) and derivative works thereof for any purpose; and to
131 * authorize others to exercise some or all of the rights granted
132 * herein, subject to the following conditions:
133
134 * o Redistribution of source code must retain this license file
135 * (`FTL.TXT') unaltered; any additions, deletions or changes to
136 * the original files must be clearly indicated in accompanying
137 * documentation. The copyright notices of the unaltered,
138 * original files must be preserved in all copies of source
139 * files.
140
141 * o Redistribution in binary form must provide a disclaimer that
142 * states that the software is based in part of the work of the
143 * FreeType Team, in the distribution documentation. We also
144 * encourage you to put an URL to the FreeType web page in your
145 * documentation, though this isn't mandatory.
146
147 * These conditions apply to any software derived from or based on
148 * the FreeType Project, not just the unmodified files. If you use
149 * our work, you must acknowledge us. However, no fee need be paid
150 * to us.
151
152 * 3. Advertising
153 * --------------
154
155 * Neither the FreeType authors and contributors nor you shall use
156 * the name of the other for commercial, advertising, or promotional
157 * purposes without specific prior written permission.
158
159 * We suggest, but do not require, that you use one or more of the
160 * following phrases to refer to this software in your documentation
161 * or advertising materials: `FreeType Project', `FreeType Engine',
162 * `FreeType library', or `FreeType Distribution'.
163
164 * As you have not signed this license, you are not required to
165 * accept it. However, as the FreeType Project is copyrighted
166 * material, only this license, or another one contracted with the
167 * authors, grants you the right to use, distribute, and modify it.
168 * Therefore, by using, distributing, or modifying the FreeType
169 * Project, you indicate that you understand and accept all the terms
170 * of this license.
171
172 * 4. Contacts
173 * -----------
174
175 * There are two mailing lists related to FreeType:
176
177 * o freetype@nongnu.org
178
179 * Discusses general use and applications of FreeType, as well as
180 * future and wanted additions to the library and distribution.
181 * If you are looking for support, start in this list if you
182 * haven't found anything to help you in the documentation.
183
184 * o freetype-devel@nongnu.org
185
186 * Discusses bugs, as well as engine internals, design issues,
187 * specific licenses, porting, etc.
188
189 * Our home page can be found at
190
191 * http://www.freetype.org
192 */
193
194 #include <setjmp.h>
195 #include <limits.h>
196 #include <memory.h>
197 #include "tvgSwCommon.h"
198
199 /************************************************************************/
200 /* Internal Class Implementation */
201 /************************************************************************/
202
203 constexpr auto PIXEL_BITS = 8; //must be at least 6 bits!
204 constexpr auto ONE_PIXEL = (1L << PIXEL_BITS);
205
206 using Area = long;
207
208 struct Band
209 {
210 SwCoord min, max;
211 };
212
213 struct Cell
214 {
215 SwCoord x;
216 SwCoord cover;
217 Area area;
218 Cell *next;
219 };
220
221 struct RleWorker
222 {
223 SwRle* rle;
224
225 SwPoint cellPos;
226 SwPoint cellMin;
227 SwPoint cellMax;
228 SwCoord cellXCnt;
229 SwCoord cellYCnt;
230
231 Area area;
232 SwCoord cover;
233
234 Cell* cells;
235 ptrdiff_t maxCells;
236 ptrdiff_t cellsCnt;
237
238 SwPoint pos;
239
240 SwPoint bezStack[32 * 3 + 1];
241 SwPoint lineStack[32 + 1];
242 int levStack[32];
243
244 SwOutline* outline;
245
246 int bandSize;
247 int bandShoot;
248
249 jmp_buf jmpBuf;
250
251 void* buffer;
252 long bufferSize;
253
254 Cell** yCells;
255 SwCoord yCnt;
256
257 bool invalid;
258 bool antiAlias;
259 };
260
261
UPSCALE(const SwPoint & pt)262 static inline SwPoint UPSCALE(const SwPoint& pt)
263 {
264 return {SwCoord(((unsigned long) pt.x) << (PIXEL_BITS - 6)), SwCoord(((unsigned long) pt.y) << (PIXEL_BITS - 6))};
265 }
266
267
TRUNC(const SwPoint & pt)268 static inline SwPoint TRUNC(const SwPoint& pt)
269 {
270 return {pt.x >> PIXEL_BITS, pt.y >> PIXEL_BITS};
271 }
272
273
TRUNC(const SwCoord x)274 static inline SwCoord TRUNC(const SwCoord x)
275 {
276 return x >> PIXEL_BITS;
277 }
278
279
SUBPIXELS(const SwPoint & pt)280 static inline SwPoint SUBPIXELS(const SwPoint& pt)
281 {
282 return {SwCoord(((unsigned long) pt.x) << PIXEL_BITS), SwCoord(((unsigned long) pt.y) << PIXEL_BITS)};
283 }
284
285
SUBPIXELS(const SwCoord x)286 static inline SwCoord SUBPIXELS(const SwCoord x)
287 {
288 return SwCoord(((unsigned long) x) << PIXEL_BITS);
289 }
290
291 /*
292 * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
293 * algorithm. We use alpha = 1, beta = 3/8, giving us results with a
294 * largest error less than 7% compared to the exact value.
295 */
HYPOT(SwPoint pt)296 static inline SwCoord HYPOT(SwPoint pt)
297 {
298 if (pt.x < 0) pt.x = -pt.x;
299 if (pt.y < 0) pt.y = -pt.y;
300 return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3)));
301 }
302
303
_horizLine(RleWorker & rw,SwCoord x,SwCoord y,SwCoord area,SwCoord aCount)304 static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord aCount)
305 {
306 x += rw.cellMin.x;
307 y += rw.cellMin.y;
308
309 //Clip Y range
310 if (y < rw.cellMin.y || y >= rw.cellMax.y) return;
311
312 /* compute the coverage line's coverage, depending on the outline fill rule */
313 /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
314 auto coverage = static_cast<int>(area >> (PIXEL_BITS * 2 + 1 - 8)); //range 0 - 255
315
316 if (coverage < 0) coverage = -coverage;
317
318 if (rw.outline->fillRule == FillRule::EvenOdd) {
319 coverage &= 511;
320 if (coverage > 255) coverage = 511 - coverage;
321 } else {
322 //normal non-zero winding rule
323 if (coverage > 255) coverage = 255;
324 }
325
326 if (coverage == 0) return;
327
328 //span has ushort coordinates. check limit overflow
329 if (x >= SHRT_MAX) {
330 TVGERR("SW_ENGINE", "X-coordinate overflow!");
331 return;
332 }
333 if (y >= SHRT_MAX) {
334 TVGERR("SW_ENGINE", "Y-coordinate overflow!");
335 return;
336 }
337
338 auto rle = rw.rle;
339
340 if (!rw.antiAlias) coverage = 255;
341
342 //see whether we can add this span to the current list
343 if (rle->size > 0) {
344 auto span = rle->spans + rle->size - 1;
345 if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) {
346 //Clip x range
347 SwCoord xOver = 0;
348 if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
349 if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
350
351 //span->len += (aCount + xOver) - 1;
352 span->len += (aCount + xOver);
353 return;
354 }
355 }
356
357 //span pool is full, grow it.
358 if (rle->size >= rle->alloc) {
359 auto newSize = (rle->size > 0) ? (rle->size * 2) : 256;
360 if (rle->alloc < newSize) {
361 rle->alloc = newSize;
362 rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
363 }
364 }
365
366 //Clip x range
367 SwCoord xOver = 0;
368 if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
369 if (x < rw.cellMin.x) {
370 xOver -= (rw.cellMin.x - x);
371 x = rw.cellMin.x;
372 }
373
374 //Nothing to draw
375 if (aCount + xOver <= 0) return;
376
377 //add a span to the current list
378 auto span = rle->spans + rle->size;
379 span->x = x;
380 span->y = y;
381 span->len = (aCount + xOver);
382 span->coverage = coverage;
383 rle->size++;
384 }
385
386
_sweep(RleWorker & rw)387 static void _sweep(RleWorker& rw)
388 {
389 if (rw.cellsCnt == 0) return;
390
391 for (int y = 0; y < rw.yCnt; ++y) {
392 auto cover = 0;
393 auto x = 0;
394 auto cell = rw.yCells[y];
395
396 while (cell) {
397 if (cell->x > x && cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), cell->x - x);
398 cover += cell->cover;
399 auto area = cover * (ONE_PIXEL * 2) - cell->area;
400 if (area != 0 && cell->x >= 0) _horizLine(rw, cell->x, y, area, 1);
401 x = cell->x + 1;
402 cell = cell->next;
403 }
404
405 if (cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), rw.cellXCnt - x);
406 }
407 }
408
409
_findCell(RleWorker & rw)410 static Cell* _findCell(RleWorker& rw)
411 {
412 auto x = rw.cellPos.x;
413 if (x > rw.cellXCnt) x = rw.cellXCnt;
414
415 auto pcell = &rw.yCells[rw.cellPos.y];
416
417 while(true) {
418 Cell* cell = *pcell;
419 if (!cell || cell->x > x) break;
420 if (cell->x == x) return cell;
421 pcell = &cell->next;
422 }
423
424 if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1);
425
426 auto cell = rw.cells + rw.cellsCnt++;
427 cell->x = x;
428 cell->area = 0;
429 cell->cover = 0;
430 cell->next = *pcell;
431 *pcell = cell;
432
433 return cell;
434 }
435
436
_recordCell(RleWorker & rw)437 static void _recordCell(RleWorker& rw)
438 {
439 if (rw.area | rw.cover) {
440 auto cell = _findCell(rw);
441 cell->area += rw.area;
442 cell->cover += rw.cover;
443 }
444 }
445
446
_setCell(RleWorker & rw,SwPoint pos)447 static void _setCell(RleWorker& rw, SwPoint pos)
448 {
449 /* Move the cell pointer to a new position. We set the `invalid' */
450 /* flag to indicate that the cell isn't part of those we're interested */
451 /* in during the render phase. This means that: */
452 /* */
453 /* . the new vertical position must be within min_ey..max_ey-1. */
454 /* . the new horizontal position must be strictly less than max_ex */
455 /* */
456 /* Note that if a cell is to the left of the clipping region, it is */
457 /* actually set to the (min_ex-1) horizontal position. */
458
459 /* All cells that are on the left of the clipping region go to the
460 min_ex - 1 horizontal position. */
461 pos.x -= rw.cellMin.x;
462 pos.y -= rw.cellMin.y;
463
464 if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
465
466 //Are we moving to a different cell?
467 if (pos != rw.cellPos) {
468 //Record the current one if it is valid
469 if (!rw.invalid) _recordCell(rw);
470 }
471
472 rw.area = 0;
473 rw.cover = 0;
474 rw.cellPos = pos;
475 rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt);
476 }
477
478
_startCell(RleWorker & rw,SwPoint pos)479 static void _startCell(RleWorker& rw, SwPoint pos)
480 {
481 if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
482 if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x;
483
484 rw.area = 0;
485 rw.cover = 0;
486 rw.cellPos = pos - rw.cellMin;
487 rw.invalid = false;
488
489 _setCell(rw, pos);
490 }
491
492
_moveTo(RleWorker & rw,const SwPoint & to)493 static void _moveTo(RleWorker& rw, const SwPoint& to)
494 {
495 //record current cell, if any */
496 if (!rw.invalid) _recordCell(rw);
497
498 //start to a new position
499 _startCell(rw, TRUNC(to));
500
501 rw.pos = to;
502 }
503
504
_lineTo(RleWorker & rw,const SwPoint & to)505 static void _lineTo(RleWorker& rw, const SwPoint& to)
506 {
507 #define SW_UDIV(a, b) \
508 static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \
509 (sizeof(long) * CHAR_BIT - PIXEL_BITS))
510
511 auto e1 = TRUNC(rw.pos);
512 auto e2 = TRUNC(to);
513
514 //vertical clipping
515 if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) {
516 rw.pos = to;
517 return;
518 }
519
520 auto line = rw.lineStack;
521 line[0] = to;
522 line[1] = rw.pos;
523
524 while (true) {
525 auto diff = line[0] - line[1];
526 auto L = HYPOT(diff);
527
528 if (L > SHRT_MAX) {
529 mathSplitLine(line);
530 ++line;
531 continue;
532 }
533 e1 = TRUNC(line[1]);
534 e2 = TRUNC(line[0]);
535
536 auto f1 = line[1] - SUBPIXELS(e1);
537 SwPoint f2;
538
539 //inside one cell
540 if (e1 == e2) {
541 ;
542 //any horizontal line
543 } else if (diff.y == 0) {
544 e1.x = e2.x;
545 _setCell(rw, e1);
546 } else if (diff.x == 0) {
547 //vertical line up
548 if (diff.y > 0) {
549 do {
550 f2.y = ONE_PIXEL;
551 rw.cover += (f2.y - f1.y);
552 rw.area += (f2.y - f1.y) * f1.x * 2;
553 f1.y = 0;
554 ++e1.y;
555 _setCell(rw, e1);
556 } while(e1.y != e2.y);
557 //vertical line down
558 } else {
559 do {
560 f2.y = 0;
561 rw.cover += (f2.y - f1.y);
562 rw.area += (f2.y - f1.y) * f1.x * 2;
563 f1.y = ONE_PIXEL;
564 --e1.y;
565 _setCell(rw, e1);
566 } while(e1.y != e2.y);
567 }
568 //any other line
569 } else {
570 Area prod = diff.x * f1.y - diff.y * f1.x;
571
572 /* These macros speed up repetitive divisions by replacing them
573 with multiplications and right shifts. */
574 auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
575 auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
576
577 /* The fundamental value `prod' determines which side and the */
578 /* exact coordinate where the line exits current cell. It is */
579 /* also easily updated when moving from one cell to the next. */
580 do {
581 auto px = diff.x * ONE_PIXEL;
582 auto py = diff.y * ONE_PIXEL;
583
584 //left
585 if (prod <= 0 && prod - px > 0) {
586 f2 = {0, SW_UDIV(-prod, -dx_r)};
587 prod -= py;
588 rw.cover += (f2.y - f1.y);
589 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
590 f1 = {ONE_PIXEL, f2.y};
591 --e1.x;
592 //up
593 } else if (prod - px <= 0 && prod - px + py > 0) {
594 prod -= px;
595 f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
596 rw.cover += (f2.y - f1.y);
597 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
598 f1 = {f2.x, 0};
599 ++e1.y;
600 //right
601 } else if (prod - px + py <= 0 && prod + py >= 0) {
602 prod += py;
603 f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
604 rw.cover += (f2.y - f1.y);
605 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
606 f1 = {0, f2.y};
607 ++e1.x;
608 //down
609 } else {
610 f2 = {SW_UDIV(prod, -dy_r), 0};
611 prod += px;
612 rw.cover += (f2.y - f1.y);
613 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
614 f1 = {f2.x, ONE_PIXEL};
615 --e1.y;
616 }
617
618 _setCell(rw, e1);
619
620 } while(e1 != e2);
621 }
622
623 f2 = {line[0].x - SUBPIXELS(e2.x), line[0].y - SUBPIXELS(e2.y)};
624 rw.cover += (f2.y - f1.y);
625 rw.area += (f2.y - f1.y) * (f1.x + f2.x);
626 rw.pos = line[0];
627
628 if (line-- == rw.lineStack) return;
629 }
630 }
631
632
_cubicTo(RleWorker & rw,const SwPoint & ctrl1,const SwPoint & ctrl2,const SwPoint & to)633 static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
634 {
635 auto arc = rw.bezStack;
636 arc[0] = to;
637 arc[1] = ctrl2;
638 arc[2] = ctrl1;
639 arc[3] = rw.pos;
640
641 //Short-cut the arc that crosses the current band
642 auto min = arc[0].y;
643 auto max = arc[0].y;
644
645 SwCoord y;
646 for (auto i = 1; i < 4; ++i) {
647 y = arc[i].y;
648 if (y < min) min = y;
649 if (y > max) max = y;
650 }
651
652 if (TRUNC(min) >= rw.cellMax.y || TRUNC(max) < rw.cellMin.y) goto draw;
653
654 /* Decide whether to split or draw. See `Rapid Termination */
655 /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */
656 /* F. Hain, at */
657 /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */
658 while (true) {
659 {
660 //diff is the P0 - P3 chord vector
661 auto diff = arc[3] - arc[0];
662 auto L = HYPOT(diff);
663
664 //avoid possible arithmetic overflow below by splitting
665 if (L > SHRT_MAX) goto split;
666
667 //max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1)
668 auto sLimit = L * (ONE_PIXEL / 6);
669
670 auto diff1 = arc[1] - arc[0];
671 auto s = diff.y * diff1.x - diff.x * diff1.y;
672 if (s < 0) s = -s;
673 if (s > sLimit) goto split;
674
675 //s is L * the perpendicular distance from P2 to the line P0 - P3
676 auto diff2 = arc[2] - arc[0];
677 s = diff.y * diff2.x - diff.x * diff2.y;
678 if (s < 0) s = -s;
679 if (s > sLimit) goto split;
680
681 /* Split super curvy segments where the off points are so far
682 from the chord that the angles P0-P1-P3 or P0-P2-P3 become
683 acute as detected by appropriate dot products */
684 if (diff1.x * (diff1.x - diff.x) + diff1.y * (diff1.y - diff.y) > 0 ||
685 diff2.x * (diff2.x - diff.x) + diff2.y * (diff2.y - diff.y) > 0)
686 goto split;
687
688 //no reason to split
689 goto draw;
690 }
691 split:
692 mathSplitCubic(arc);
693 arc += 3;
694 continue;
695
696 draw:
697 _lineTo(rw, arc[0]);
698 if (arc == rw.bezStack) return;
699 arc -= 3;
700 }
701 }
702
703
_decomposeOutline(RleWorker & rw)704 static void _decomposeOutline(RleWorker& rw)
705 {
706 auto outline = rw.outline;
707 auto first = 0; //index of first point in contour
708
709 for (auto cntr = outline->cntrs.begin(); cntr < outline->cntrs.end(); ++cntr) {
710 auto last = *cntr;
711 auto limit = outline->pts.data + last;
712 auto start = UPSCALE(outline->pts[first]);
713 auto pt = outline->pts.data + first;
714 auto types = outline->types.data + first;
715 ++types;
716
717 _moveTo(rw, UPSCALE(outline->pts[first]));
718
719 while (pt < limit) {
720 //emit a single line_to
721 if (types[0] == SW_CURVE_TYPE_POINT) {
722 ++pt;
723 ++types;
724 _lineTo(rw, UPSCALE(*pt));
725 //types cubic
726 } else {
727 pt += 3;
728 types += 3;
729 if (pt <= limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
730 else if (pt - 1 == limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
731 else goto close;
732 }
733 }
734 close:
735 _lineTo(rw, start);
736 first = last + 1;
737 }
738 }
739
740
_genRle(RleWorker & rw)741 static int _genRle(RleWorker& rw)
742 {
743 if (setjmp(rw.jmpBuf) == 0) {
744 _decomposeOutline(rw);
745 if (!rw.invalid) _recordCell(rw);
746 return 0;
747 }
748 return -1; //lack of cell memory
749 }
750
751
_intersectSpansRegion(const SwRle * clip,const SwRle * target,SwSpan * outSpans,uint32_t outSpansCnt)752 static SwSpan* _intersectSpansRegion(const SwRle *clip, const SwRle *target, SwSpan *outSpans, uint32_t outSpansCnt)
753 {
754 auto out = outSpans;
755 auto spans = target->spans;
756 auto end = target->spans + target->size;
757 auto clipSpans = clip->spans;
758 auto clipEnd = clip->spans + clip->size;
759
760 while (spans < end && clipSpans < clipEnd) {
761 //align y-coordinates.
762 if (clipSpans->y > spans->y) {
763 ++spans;
764 continue;
765 }
766 if (spans->y > clipSpans->y) {
767 ++clipSpans;
768 continue;
769 }
770
771 //Try clipping with all clip spans which have a same y-coordinate.
772 auto temp = clipSpans;
773 while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) {
774 auto sx1 = spans->x;
775 auto sx2 = sx1 + spans->len;
776 auto cx1 = temp->x;
777 auto cx2 = cx1 + temp->len;
778
779 //The span must be left(x1) to right(x2) direction. Not intersected.
780 if (cx2 < sx1 || sx2 < cx1) {
781 ++temp;
782 continue;
783 }
784
785 //clip span region.
786 auto x = sx1 > cx1 ? sx1 : cx1;
787 auto len = (sx2 < cx2 ? sx2 : cx2) - x;
788 if (len > 0) {
789 out->x = x;
790 out->y = temp->y;
791 out->len = len;
792 out->coverage = (uint8_t)(((spans->coverage * temp->coverage) + 0xff) >> 8);
793 ++out;
794 --outSpansCnt;
795 }
796 ++temp;
797 }
798 ++spans;
799 }
800 return out;
801 }
802
803
_intersectSpansRect(const SwBBox * bbox,const SwRle * targetRle,SwSpan * outSpans,uint32_t outSpansCnt)804 static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRle *targetRle, SwSpan *outSpans, uint32_t outSpansCnt)
805 {
806 auto out = outSpans;
807 auto spans = targetRle->spans;
808 auto end = targetRle->spans + targetRle->size;
809 auto minx = static_cast<int16_t>(bbox->min.x);
810 auto miny = static_cast<int16_t>(bbox->min.y);
811 auto maxx = minx + static_cast<int16_t>(bbox->max.x - bbox->min.x) - 1;
812 auto maxy = miny + static_cast<int16_t>(bbox->max.y - bbox->min.y) - 1;
813
814 while (outSpansCnt > 0 && spans < end) {
815 if (spans->y > maxy) {
816 spans = end;
817 break;
818 }
819 if (spans->y < miny || spans->x > maxx || spans->x + spans->len <= minx) {
820 ++spans;
821 continue;
822 }
823 if (spans->x < minx) {
824 out->len = (spans->len - (minx - spans->x)) < (maxx - minx + 1) ? (spans->len - (minx - spans->x)) : (maxx - minx + 1);
825 out->x = minx;
826 }
827 else {
828 out->x = spans->x;
829 out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1);
830 }
831 if (out->len > 0) {
832 out->y = spans->y;
833 out->coverage = spans->coverage;
834 ++out;
835 --outSpansCnt;
836 }
837 ++spans;
838 }
839 return out;
840 }
841
842
_replaceClipSpan(SwRle * rle,SwSpan * clippedSpans,uint32_t size)843 void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size)
844 {
845 free(rle->spans);
846 rle->spans = clippedSpans;
847 rle->size = rle->alloc = size;
848 }
849
850
851 /************************************************************************/
852 /* External Class Implementation */
853 /************************************************************************/
854
rleRender(SwRle * rle,const SwOutline * outline,const SwBBox & renderRegion,bool antiAlias)855 SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
856 {
857 constexpr auto RENDER_POOL_SIZE = 16384L;
858 constexpr auto BAND_SIZE = 40;
859
860 //TODO: We can preserve several static workers in advance
861 RleWorker rw;
862 Cell buffer[RENDER_POOL_SIZE / sizeof(Cell)];
863
864 //Init Cells
865 rw.buffer = buffer;
866 rw.bufferSize = sizeof(buffer);
867 rw.yCells = reinterpret_cast<Cell**>(buffer);
868 rw.cells = nullptr;
869 rw.maxCells = 0;
870 rw.cellsCnt = 0;
871 rw.area = 0;
872 rw.cover = 0;
873 rw.invalid = true;
874 rw.cellMin = renderRegion.min;
875 rw.cellMax = renderRegion.max;
876 rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
877 rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
878 rw.outline = const_cast<SwOutline*>(outline);
879 rw.bandSize = rw.bufferSize / (sizeof(Cell) * 2); //bandSize: 256
880 rw.bandShoot = 0;
881 rw.antiAlias = antiAlias;
882
883 if (!rle) rw.rle = reinterpret_cast<SwRle*>(calloc(1, sizeof(SwRle)));
884 else rw.rle = rle;
885
886 //Generate RLE
887 Band bands[BAND_SIZE];
888 Band* band;
889
890 /* set up vertical bands */
891 auto bandCnt = static_cast<int>((rw.cellMax.y - rw.cellMin.y) / rw.bandSize);
892 if (bandCnt == 0) bandCnt = 1;
893 else if (bandCnt >= BAND_SIZE) bandCnt = (BAND_SIZE - 1);
894
895 auto min = rw.cellMin.y;
896 auto yMax = rw.cellMax.y;
897 SwCoord max;
898 int ret;
899
900 for (int n = 0; n < bandCnt; ++n, min = max) {
901 max = min + rw.bandSize;
902 if (n == bandCnt -1 || max > yMax) max = yMax;
903
904 bands[0].min = min;
905 bands[0].max = max;
906 band = bands;
907
908 while (band >= bands) {
909 rw.yCells = static_cast<Cell**>(rw.buffer);
910 rw.yCnt = band->max - band->min;
911
912 int cellStart = sizeof(Cell*) * (int)rw.yCnt;
913 int cellMod = cellStart % sizeof(Cell);
914
915 if (cellMod > 0) cellStart += sizeof(Cell) - cellMod;
916
917 auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + rw.bufferSize);
918 rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
919
920 if (rw.cells >= cellsMax) goto reduce_bands;
921
922 rw.maxCells = cellsMax - rw.cells;
923 if (rw.maxCells < 2) goto reduce_bands;
924
925 for (int y = 0; y < rw.yCnt; ++y)
926 rw.yCells[y] = nullptr;
927
928 rw.cellsCnt = 0;
929 rw.invalid = true;
930 rw.cellMin.y = band->min;
931 rw.cellMax.y = band->max;
932 rw.cellYCnt = band->max - band->min;
933
934 ret = _genRle(rw);
935 if (ret == 0) {
936 _sweep(rw);
937 --band;
938 continue;
939 } else if (ret == 1) {
940 goto error;
941 }
942
943 reduce_bands:
944 /* render pool overflow: we will reduce the render band by half */
945 auto bottom = band->min;
946 auto top = band->max;
947 auto middle = bottom + ((top - bottom) >> 1);
948
949 /* This is too complex for a single scanline; there must
950 be some problems */
951 if (middle == bottom) goto error;
952
953 if (bottom - top >= rw.bandSize) ++rw.bandShoot;
954
955 band[1].min = bottom;
956 band[1].max = middle;
957 band[0].min = middle;
958 band[0].max = top;
959 ++band;
960 }
961 }
962
963 if (rw.bandShoot > 8 && rw.bandSize > 16)
964 rw.bandSize = (rw.bandSize >> 1);
965
966 return rw.rle;
967
968 error:
969 free(rw.rle);
970 return nullptr;
971 }
972
973
rleRender(const SwBBox * bbox)974 SwRle* rleRender(const SwBBox* bbox)
975 {
976 auto width = static_cast<uint16_t>(bbox->max.x - bbox->min.x);
977 auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y);
978
979 auto rle = static_cast<SwRle*>(malloc(sizeof(SwRle)));
980 rle->spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * height));
981 rle->size = height;
982 rle->alloc = height;
983
984 auto span = rle->spans;
985 for (uint16_t i = 0; i < height; ++i, ++span) {
986 span->x = bbox->min.x;
987 span->y = bbox->min.y + i;
988 span->len = width;
989 span->coverage = 255;
990 }
991
992 return rle;
993 }
994
995
rleReset(SwRle * rle)996 void rleReset(SwRle* rle)
997 {
998 if (!rle) return;
999 rle->size = 0;
1000 }
1001
1002
rleFree(SwRle * rle)1003 void rleFree(SwRle* rle)
1004 {
1005 if (!rle) return;
1006 if (rle->spans) free(rle->spans);
1007 free(rle);
1008 }
1009
1010
rleClip(SwRle * rle,const SwRle * clip)1011 void rleClip(SwRle *rle, const SwRle *clip)
1012 {
1013 if (rle->size == 0 || clip->size == 0) return;
1014 auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
1015 auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
1016 auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
1017
1018 _replaceClipSpan(rle, spans, spansEnd - spans);
1019
1020 TVGLOG("SW_ENGINE", "Using Path Clipping!");
1021 }
1022
1023
rleClip(SwRle * rle,const SwBBox * clip)1024 void rleClip(SwRle *rle, const SwBBox* clip)
1025 {
1026 if (rle->size == 0) return;
1027 auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
1028 auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
1029
1030 _replaceClipSpan(rle, spans, spansEnd - spans);
1031
1032 TVGLOG("SW_ENGINE", "Using Box Clipping!");
1033 }
1034
1035 #endif /* LV_USE_THORVG_INTERNAL */
1036
1037