2 * Copyright (C) 2014 Vabishchevich Nikolay <vabnick@gmail.com>
4 * This file is part of libass.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include "ass_compat.h"
25 #pragma intrinsic(_BitScanReverse)
28 #include "ass_utils.h"
29 #include "ass_outline.h"
30 #include "ass_rasterizer.h"
34 static inline int ilog2(uint32_t n)
37 return __builtin_clz(n) ^ 31;
38 #elif defined(_MSC_VER)
40 _BitScanReverse(&res, n);
44 for (int ord = 16; ord; ord /= 2)
45 if (n >= ((uint32_t)1 << ord)) {
54 void rasterizer_init(RasterizerData *rst, int outline_error)
56 rst->outline_error = outline_error;
57 rst->linebuf[0] = rst->linebuf[1] = NULL;
58 rst->size[0] = rst->capacity[0] = 0;
59 rst->size[1] = rst->capacity[1] = 0;
63 * \brief Ensure sufficient buffer size (allocate if necessary)
64 * \param index index (0 or 1) of the input segment buffer (rst->linebuf)
65 * \param delta requested size increase
66 * \return false on error
68 static inline bool check_capacity(RasterizerData *rst, int index, size_t delta)
70 delta += rst->size[index];
71 if (rst->capacity[index] >= delta)
74 size_t capacity = FFMAX(2 * rst->capacity[index], 64);
75 while (capacity < delta)
77 void *ptr = realloc(rst->linebuf[index], sizeof(struct segment) * capacity);
81 rst->linebuf[index] = (struct segment *)ptr;
82 rst->capacity[index] = capacity;
86 void rasterizer_done(RasterizerData *rst)
88 free(rst->linebuf[0]);
89 free(rst->linebuf[1]);
94 * Tiled Rasterization Algorithm
96 * 1) Convert splines into polylines using recursive subdivision.
98 * 2) Determine which segments of resulting polylines fall into each tile.
99 * That's done through recursive splitting of segment array with horizontal or vertical lines.
100 * Each individual segment can lie fully left(top) or right(bottom) from the splitting line or cross it.
101 * In the latter case copies of such segment go to both output arrays. Also winding count
102 * of the top-left corner of the second result rectangle gets calculated simultaneously with splitting.
103 * Winding count of the first result rectangle is the same as of the source rectangle.
105 * 3) When the splitting is done to the tile level, there are 3 possible outcome:
106 * - Tile doesn't have segments at all--fill it with solid color in accordance with winding count.
107 * - Tile have only 1 segment--use simple half-plane filling algorithm.
108 * - Generic case with 2 or more segments.
109 * In the latter case each segment gets rasterized as right trapezoid into buffer
110 * with additive or subtractive blending.
118 // Helper struct for spline split decision
124 static inline void segment_init(OutlineSegment *seg,
125 OutlinePoint beg, OutlinePoint end,
126 int32_t outline_error)
128 int32_t x = end.x - beg.x;
129 int32_t y = end.y - beg.y;
130 int32_t abs_x = x < 0 ? -x : x;
131 int32_t abs_y = y < 0 ? -y : y;
135 seg->r2 = x * (int64_t)x + y * (int64_t)y;
136 seg->er = outline_error * (int64_t)FFMAX(abs_x, abs_y);
139 static inline bool segment_subdivide(const OutlineSegment *seg,
140 OutlinePoint beg, OutlinePoint pt)
142 int32_t x = pt.x - beg.x;
143 int32_t y = pt.y - beg.y;
144 int64_t pdr = seg->r.x * (int64_t)x + seg->r.y * (int64_t)y;
145 int64_t pcr = seg->r.x * (int64_t)y - seg->r.y * (int64_t)x;
146 return pdr < -seg->er || pdr > seg->r2 + seg->er ||
147 (pcr < 0 ? -pcr : pcr) > seg->er;
151 * \brief Add new segment to polyline
153 static bool add_line(RasterizerData *rst, OutlinePoint pt0, OutlinePoint pt1)
155 int32_t x = pt1.x - pt0.x;
156 int32_t y = pt1.y - pt0.y;
160 if (!check_capacity(rst, 0, 1))
162 struct segment *line = rst->linebuf[0] + rst->size[0];
165 line->flags = SEGFLAG_EXACT_LEFT | SEGFLAG_EXACT_RIGHT |
166 SEGFLAG_EXACT_TOP | SEGFLAG_EXACT_BOTTOM;
168 line->flags ^= SEGFLAG_UL_DR;
170 line->flags ^= SEGFLAG_DN | SEGFLAG_UL_DR;
172 line->x_min = FFMIN(pt0.x, pt1.x);
173 line->x_max = FFMAX(pt0.x, pt1.x);
174 line->y_min = FFMIN(pt0.y, pt1.y);
175 line->y_max = FFMAX(pt0.y, pt1.y);
179 line->c = y * (int64_t)pt0.x - x * (int64_t)pt0.y;
181 // halfplane normalization
182 int32_t abs_x = x < 0 ? -x : x;
183 int32_t abs_y = y < 0 ? -y : y;
184 uint32_t max_ab = (abs_x > abs_y ? abs_x : abs_y);
185 int shift = 30 - ilog2(max_ab);
186 max_ab <<= shift + 1;
187 line->a *= 1 << shift;
188 line->b *= 1 << shift;
189 line->c *= 1 << shift;
190 line->scale = (uint64_t)0x53333333 * (uint32_t)(max_ab * (uint64_t)max_ab >> 32) >> 32;
191 line->scale += 0x8810624D - (0xBBC6A7EF * (uint64_t)max_ab >> 32);
192 //line->scale = ((uint64_t)1 << 61) / max_ab;
197 * \brief Add quadratic spline to polyline
198 * Performs recursive subdivision if necessary.
200 static bool add_quadratic(RasterizerData *rst, const OutlinePoint *pt)
203 segment_init(&seg, pt[0], pt[2], rst->outline_error);
204 if (!segment_subdivide(&seg, pt[0], pt[1]))
205 return add_line(rst, pt[0], pt[2]);
207 OutlinePoint next[5];
208 next[1].x = pt[0].x + pt[1].x;
209 next[1].y = pt[0].y + pt[1].y;
210 next[3].x = pt[1].x + pt[2].x;
211 next[3].y = pt[1].y + pt[2].y;
212 next[2].x = (next[1].x + next[3].x + 2) >> 2;
213 next[2].y = (next[1].y + next[3].y + 2) >> 2;
220 return add_quadratic(rst, next) && add_quadratic(rst, next + 2);
224 * \brief Add cubic spline to polyline
225 * Performs recursive subdivision if necessary.
227 static bool add_cubic(RasterizerData *rst, const OutlinePoint *pt)
230 segment_init(&seg, pt[0], pt[3], rst->outline_error);
231 if (!segment_subdivide(&seg, pt[0], pt[1]) && !segment_subdivide(&seg, pt[0], pt[2]))
232 return add_line(rst, pt[0], pt[3]);
234 OutlinePoint next[7], center;
235 next[1].x = pt[0].x + pt[1].x;
236 next[1].y = pt[0].y + pt[1].y;
237 center.x = pt[1].x + pt[2].x + 2;
238 center.y = pt[1].y + pt[2].y + 2;
239 next[5].x = pt[2].x + pt[3].x;
240 next[5].y = pt[2].y + pt[3].y;
241 next[2].x = next[1].x + center.x;
242 next[2].y = next[1].y + center.y;
243 next[4].x = center.x + next[5].x;
244 next[4].y = center.y + next[5].y;
245 next[3].x = (next[2].x + next[4].x - 1) >> 3;
246 next[3].y = (next[2].y + next[4].y - 1) >> 3;
257 return add_cubic(rst, next) && add_cubic(rst, next + 3);
261 bool rasterizer_set_outline(RasterizerData *rst, const ASS_Outline *path)
264 S_ON, S_Q, S_C1, S_C2
268 for (size_t i = 0, j = 0; i < path->n_contours; i++) {
269 OutlinePoint start, p[4];
273 int last = path->contours[i];
277 if (path->points[j].x < -(1 << 28) || path->points[j].x >= (1 << 28))
279 if (path->points[j].y <= -(1 << 28) || path->points[j].y > (1 << 28))
282 switch (FT_CURVE_TAG(path->tags[j])) {
283 case FT_CURVE_TAG_ON:
284 p[0].x = path->points[j].x;
285 p[0].y = -path->points[j].y;
290 case FT_CURVE_TAG_CONIC:
291 switch (FT_CURVE_TAG(path->tags[last])) {
292 case FT_CURVE_TAG_ON:
293 p[0].x = path->points[last].x;
294 p[0].y = -path->points[last].y;
295 p[1].x = path->points[j].x;
296 p[1].y = -path->points[j].y;
301 case FT_CURVE_TAG_CONIC:
302 p[1].x = path->points[j].x;
303 p[1].y = -path->points[j].y;
304 p[0].x = (p[1].x + path->points[last].x) >> 1;
305 p[0].y = (p[1].y - path->points[last].y) >> 1;
319 for (j++; j <= last; j++) {
320 if (path->points[j].x < -(1 << 28) || path->points[j].x >= (1 << 28))
322 if (path->points[j].y <= -(1 << 28) || path->points[j].y > (1 << 28))
325 switch (FT_CURVE_TAG(path->tags[j])) {
326 case FT_CURVE_TAG_ON:
329 p[1].x = path->points[j].x;
330 p[1].y = -path->points[j].y;
331 if (!add_line(rst, p[0], p[1]))
337 p[2].x = path->points[j].x;
338 p[2].y = -path->points[j].y;
339 if (!add_quadratic(rst, p))
346 p[3].x = path->points[j].x;
347 p[3].y = -path->points[j].y;
348 if (!add_cubic(rst, p))
359 case FT_CURVE_TAG_CONIC:
362 p[1].x = path->points[j].x;
363 p[1].y = -path->points[j].y;
368 p[3].x = path->points[j].x;
369 p[3].y = -path->points[j].y;
370 p[2].x = (p[1].x + p[3].x) >> 1;
371 p[2].y = (p[1].y + p[3].y) >> 1;
372 if (!add_quadratic(rst, p))
383 case FT_CURVE_TAG_CUBIC:
386 p[1].x = path->points[j].x;
387 p[1].y = -path->points[j].y;
392 p[2].x = path->points[j].x;
393 p[2].y = -path->points[j].y;
410 if (!add_line(rst, p[0], start))
416 if (!add_quadratic(rst, p))
422 if (!add_cubic(rst, p))
431 rst->x_min = rst->y_min = 0x7FFFFFFF;
432 rst->x_max = rst->y_max = 0x80000000;
433 for (size_t k = 0; k < rst->size[0]; k++) {
434 rst->x_min = FFMIN(rst->x_min, rst->linebuf[0][k].x_min);
435 rst->x_max = FFMAX(rst->x_max, rst->linebuf[0][k].x_max);
436 rst->y_min = FFMIN(rst->y_min, rst->linebuf[0][k].y_min);
437 rst->y_max = FFMAX(rst->y_max, rst->linebuf[0][k].y_max);
443 static void segment_move_x(struct segment *line, int32_t x)
447 line->x_min = FFMAX(line->x_min, 0);
448 line->c -= line->a * (int64_t)x;
450 static const int test = SEGFLAG_EXACT_LEFT | SEGFLAG_UL_DR;
451 if (!line->x_min && (line->flags & test) == test)
452 line->flags &= ~SEGFLAG_EXACT_TOP;
455 static void segment_move_y(struct segment *line, int32_t y)
459 line->y_min = FFMAX(line->y_min, 0);
460 line->c -= line->b * (int64_t)y;
462 static const int test = SEGFLAG_EXACT_TOP | SEGFLAG_UL_DR;
463 if (!line->y_min && (line->flags & test) == test)
464 line->flags &= ~SEGFLAG_EXACT_LEFT;
467 static void segment_split_horz(struct segment *line, struct segment *next, int32_t x)
469 assert(x > line->x_min && x < line->x_max);
472 next->c -= line->a * (int64_t)x;
477 line->flags &= ~SEGFLAG_EXACT_TOP;
478 next->flags &= ~SEGFLAG_EXACT_BOTTOM;
479 if (line->flags & SEGFLAG_UL_DR) {
480 int32_t tmp = line->flags;
481 line->flags = next->flags;
484 line->flags |= SEGFLAG_EXACT_RIGHT;
485 next->flags |= SEGFLAG_EXACT_LEFT;
488 static void segment_split_vert(struct segment *line, struct segment *next, int32_t y)
490 assert(y > line->y_min && y < line->y_max);
493 next->c -= line->b * (int64_t)y;
498 line->flags &= ~SEGFLAG_EXACT_LEFT;
499 next->flags &= ~SEGFLAG_EXACT_RIGHT;
500 if (line->flags & SEGFLAG_UL_DR) {
501 int32_t tmp = line->flags;
502 line->flags = next->flags;
505 line->flags |= SEGFLAG_EXACT_BOTTOM;
506 next->flags |= SEGFLAG_EXACT_TOP;
509 static inline int segment_check_left(const struct segment *line, int32_t x)
511 if (line->flags & SEGFLAG_EXACT_LEFT)
512 return line->x_min >= x;
513 int64_t cc = line->c - line->a * (int64_t)x -
514 line->b * (int64_t)(line->flags & SEGFLAG_UL_DR ? line->y_min : line->y_max);
520 static inline int segment_check_right(const struct segment *line, int32_t x)
522 if (line->flags & SEGFLAG_EXACT_RIGHT)
523 return line->x_max <= x;
524 int64_t cc = line->c - line->a * (int64_t)x -
525 line->b * (int64_t)(line->flags & SEGFLAG_UL_DR ? line->y_max : line->y_min);
531 static inline int segment_check_top(const struct segment *line, int32_t y)
533 if (line->flags & SEGFLAG_EXACT_TOP)
534 return line->y_min >= y;
535 int64_t cc = line->c - line->b * (int64_t)y -
536 line->a * (int64_t)(line->flags & SEGFLAG_UL_DR ? line->x_min : line->x_max);
542 static inline int segment_check_bottom(const struct segment *line, int32_t y)
544 if (line->flags & SEGFLAG_EXACT_BOTTOM)
545 return line->y_max <= y;
546 int64_t cc = line->c - line->b * (int64_t)y -
547 line->a * (int64_t)(line->flags & SEGFLAG_UL_DR ? line->x_max : line->x_min);
554 * \brief Split list of segments horizontally
555 * \param src in: input array, can coincide with *dst0 or *dst1
556 * \param n_src in: input array size
557 * \param dst0, dst1 out: pointers to output arrays of at least n_src size
558 * \param x in: split coordinate
559 * \return winding difference between bottom-split and bottom-left points
561 static int polyline_split_horz(const struct segment *src, size_t n_src,
562 struct segment **dst0, struct segment **dst1, int32_t x)
565 const struct segment *end = src + n_src;
566 for (; src != end; src++) {
568 if (!src->y_min && (src->flags & SEGFLAG_EXACT_TOP))
569 delta = src->a < 0 ? 1 : -1;
570 if (segment_check_right(src, x)) {
575 (*dst0)->x_max = FFMIN((*dst0)->x_max, x);
579 if (segment_check_left(src, x)) {
581 segment_move_x(*dst1, x);
585 if (src->flags & SEGFLAG_UL_DR)
588 segment_split_horz(*dst0, *dst1, x);
596 * \brief Split list of segments vertically
598 static int polyline_split_vert(const struct segment *src, size_t n_src,
599 struct segment **dst0, struct segment **dst1, int32_t y)
602 const struct segment *end = src + n_src;
603 for (; src != end; src++) {
605 if (!src->x_min && (src->flags & SEGFLAG_EXACT_LEFT))
606 delta = src->b < 0 ? 1 : -1;
607 if (segment_check_bottom(src, y)) {
612 (*dst0)->y_max = (*dst0)->y_max < y ? (*dst0)->y_max : y;
616 if (segment_check_top(src, y)) {
618 segment_move_y(*dst1, y);
622 if (src->flags & SEGFLAG_UL_DR)
625 segment_split_vert(*dst0, *dst1, y);
633 static inline void rasterizer_fill_solid(const BitmapEngine *engine,
634 uint8_t *buf, int width, int height, ptrdiff_t stride,
637 assert(!(width & ((1 << engine->tile_order) - 1)));
638 assert(!(height & ((1 << engine->tile_order) - 1)));
640 ptrdiff_t step = 1 << engine->tile_order;
641 ptrdiff_t tile_stride = stride * (1 << engine->tile_order);
642 width >>= engine->tile_order;
643 height >>= engine->tile_order;
644 for (int y = 0; y < height; y++) {
645 for (int x = 0; x < width; x++)
646 engine->fill_solid(buf + x * step, stride, set);
651 static inline void rasterizer_fill_halfplane(const BitmapEngine *engine,
652 uint8_t *buf, int width, int height, ptrdiff_t stride,
653 int32_t a, int32_t b, int64_t c, int32_t scale)
655 assert(!(width & ((1 << engine->tile_order) - 1)));
656 assert(!(height & ((1 << engine->tile_order) - 1)));
657 if (width == 1 << engine->tile_order && height == 1 << engine->tile_order) {
658 engine->fill_halfplane(buf, stride, a, b, c, scale);
662 uint32_t abs_a = a < 0 ? -a : a;
663 uint32_t abs_b = b < 0 ? -b : b;
664 int64_t size = (int64_t)(abs_a + abs_b) << (engine->tile_order + 5);
665 int64_t offs = ((int64_t)a + b) * (1 << (engine->tile_order + 5));
667 ptrdiff_t step = 1 << engine->tile_order;
668 ptrdiff_t tile_stride = stride * (1 << engine->tile_order);
669 width >>= engine->tile_order;
670 height >>= engine->tile_order;
671 for (int y = 0; y < height; y++) {
672 for (int x = 0; x < width; x++) {
673 int64_t cc = c - (a * (int64_t)x + b * (int64_t)y) * (1 << (engine->tile_order + 6));
674 int64_t offs_c = offs - cc;
675 int64_t abs_c = offs_c < 0 ? -offs_c : offs_c;
677 engine->fill_halfplane(buf + x * step, stride, a, b, cc, scale);
679 engine->fill_solid(buf + x * step, stride,
680 ((uint32_t)(offs_c >> 32) ^ scale) & 0x80000000);
687 * \brief Main quad-tree filling function
688 * \param index index (0 or 1) of the input segment buffer (rst->linebuf)
689 * \param offs current offset from the beginning of the buffer
690 * \param winding bottom-left winding value
691 * \return false on error
692 * Rasterizes (possibly recursive) one quad-tree level.
693 * Truncates used input buffer.
695 static bool rasterizer_fill_level(const BitmapEngine *engine, RasterizerData *rst,
696 uint8_t *buf, int width, int height, ptrdiff_t stride,
697 int index, size_t offs, int winding)
699 assert(width > 0 && height > 0);
700 assert((unsigned)index < 2u && offs <= rst->size[index]);
701 assert(!(width & ((1 << engine->tile_order) - 1)));
702 assert(!(height & ((1 << engine->tile_order) - 1)));
704 size_t n = rst->size[index] - offs;
705 struct segment *line = rst->linebuf[index] + offs;
707 rasterizer_fill_solid(engine, buf, width, height, stride, winding);
711 static const int test = SEGFLAG_UL_DR | SEGFLAG_EXACT_LEFT;
712 if (((line->flags & test) != test) == !(line->flags & SEGFLAG_DN))
721 rasterizer_fill_halfplane(engine, buf, width, height, stride,
722 line->a, line->b, line->c,
723 flag & 2 ? -line->scale : line->scale);
725 rasterizer_fill_solid(engine, buf, width, height, stride, flag & 2);
726 rst->size[index] = offs;
729 if (width == 1 << engine->tile_order && height == 1 << engine->tile_order) {
730 engine->fill_generic(buf, stride, line, rst->size[index] - offs, winding);
731 rst->size[index] = offs;
735 size_t offs1 = rst->size[index ^ 1];
736 if (!check_capacity(rst, index ^ 1, n))
738 struct segment *dst0 = line;
739 struct segment *dst1 = rst->linebuf[index ^ 1] + offs1;
741 int winding1 = winding;
744 int height1 = height;
745 if (width > height) {
746 width = 1 << ilog2(width - 1);
749 winding1 += polyline_split_horz(line, n, &dst0, &dst1, (int32_t)width << 6);
751 height = 1 << ilog2(height - 1);
753 buf1 += height * stride;
754 winding1 += polyline_split_vert(line, n, &dst0, &dst1, (int32_t)height << 6);
756 rst->size[index ^ 0] = dst0 - rst->linebuf[index ^ 0];
757 rst->size[index ^ 1] = dst1 - rst->linebuf[index ^ 1];
759 if (!rasterizer_fill_level(engine, rst, buf, width, height, stride, index ^ 0, offs, winding))
761 assert(rst->size[index ^ 0] == offs);
762 if (!rasterizer_fill_level(engine, rst, buf1, width1, height1, stride, index ^ 1, offs1, winding1))
764 assert(rst->size[index ^ 1] == offs1);
768 bool rasterizer_fill(const BitmapEngine *engine, RasterizerData *rst,
769 uint8_t *buf, int x0, int y0,
770 int width, int height, ptrdiff_t stride)
772 assert(width > 0 && height > 0);
773 assert(!(width & ((1 << engine->tile_order) - 1)));
774 assert(!(height & ((1 << engine->tile_order) - 1)));
775 x0 *= 1 << 6; y0 *= 1 << 6;
777 size_t n = rst->size[0];
778 struct segment *line = rst->linebuf[0];
779 struct segment *end = line + n;
780 for (; line != end; line++) {
785 line->c -= line->a * (int64_t)x0 + line->b * (int64_t)y0;
794 if (!check_capacity(rst, 1, rst->size[0]))
796 int32_t size_x = (int32_t)width << 6;
797 int32_t size_y = (int32_t)height << 6;
798 if (rst->x_max >= size_x) {
799 struct segment *dst0 = rst->linebuf[index];
800 struct segment *dst1 = rst->linebuf[index ^ 1];
801 polyline_split_horz(rst->linebuf[index], n, &dst0, &dst1, size_x);
802 n = dst0 - rst->linebuf[index];
804 if (rst->y_max >= size_y) {
805 struct segment *dst0 = rst->linebuf[index];
806 struct segment *dst1 = rst->linebuf[index ^ 1];
807 polyline_split_vert(rst->linebuf[index], n, &dst0, &dst1, size_y);
808 n = dst0 - rst->linebuf[index];
810 if (rst->x_min <= 0) {
811 struct segment *dst0 = rst->linebuf[index];
812 struct segment *dst1 = rst->linebuf[index ^ 1];
813 polyline_split_horz(rst->linebuf[index], n, &dst0, &dst1, 0);
815 n = dst1 - rst->linebuf[index];
817 if (rst->y_min <= 0) {
818 struct segment *dst0 = rst->linebuf[index];
819 struct segment *dst1 = rst->linebuf[index ^ 1];
820 winding = polyline_split_vert(rst->linebuf[index], n, &dst0, &dst1, 0);
822 n = dst1 - rst->linebuf[index];
824 rst->size[index] = n;
825 rst->size[index ^ 1] = 0;
826 return rasterizer_fill_level(engine, rst, buf, width, height, stride,