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 bool rasterizer_init(RasterizerData *rst, int tile_order, 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;
62 rst->tile = ass_aligned_alloc(32, 1 << (2 * tile_order), false);
67 * \brief Ensure sufficient buffer size (allocate if necessary)
68 * \param index index (0 or 1) of the input segment buffer (rst->linebuf)
69 * \param delta requested size increase
70 * \return false on error
72 static inline bool check_capacity(RasterizerData *rst, int index, size_t delta)
74 delta += rst->size[index];
75 if (rst->capacity[index] >= delta)
78 size_t capacity = FFMAX(2 * rst->capacity[index], 64);
79 while (capacity < delta)
81 void *ptr = realloc(rst->linebuf[index], sizeof(struct segment) * capacity);
85 rst->linebuf[index] = (struct segment *) ptr;
86 rst->capacity[index] = capacity;
90 void rasterizer_done(RasterizerData *rst)
92 free(rst->linebuf[0]);
93 free(rst->linebuf[1]);
95 ass_aligned_free(rst->tile);
100 * Tiled Rasterization Algorithm
102 * 1) Convert splines into polylines using recursive subdivision.
104 * 2) Determine which segments of resulting polylines fall into each tile.
105 * That's done through recursive splitting of segment array with horizontal or vertical lines.
106 * Each individual segment can lie fully left(top) or right(bottom) from the splitting line or cross it.
107 * In the latter case copies of such segment go to both output arrays. Also winding count
108 * of the top-left corner of the second result rectangle gets calculated simultaneously with splitting.
109 * Winding count of the first result rectangle is the same as of the source rectangle.
111 * 3) When the splitting is done to the tile level, there are 3 possible outcome:
112 * - Tile doesn't have segments at all--fill it with solid color in accordance with winding count.
113 * - Tile have only 1 segment--use simple half-plane filling algorithm.
114 * - Generic case with 2 or more segments.
115 * In the latter case each segment gets rasterized as right trapezoid into buffer
116 * with additive or subtractive blending.
120 // Helper struct for spline split decision
126 static inline void segment_init(OutlineSegment *seg,
127 ASS_Vector beg, ASS_Vector end,
128 int32_t outline_error)
130 int32_t x = end.x - beg.x;
131 int32_t y = end.y - beg.y;
132 int32_t abs_x = x < 0 ? -x : x;
133 int32_t abs_y = y < 0 ? -y : y;
137 seg->r2 = x * (int64_t) x + y * (int64_t) y;
138 seg->er = outline_error * (int64_t) FFMAX(abs_x, abs_y);
141 static inline bool segment_subdivide(const OutlineSegment *seg,
142 ASS_Vector beg, ASS_Vector pt)
144 int32_t x = pt.x - beg.x;
145 int32_t y = pt.y - beg.y;
146 int64_t pdr = seg->r.x * (int64_t) x + seg->r.y * (int64_t) y;
147 int64_t pcr = seg->r.x * (int64_t) y - seg->r.y * (int64_t) x;
148 return pdr < -seg->er || pdr > seg->r2 + seg->er ||
149 (pcr < 0 ? -pcr : pcr) > seg->er;
153 * \brief Add new segment to polyline
155 static bool add_line(RasterizerData *rst, ASS_Vector pt0, ASS_Vector pt1)
157 int32_t x = pt1.x - pt0.x;
158 int32_t y = pt1.y - pt0.y;
162 if (!check_capacity(rst, 0, 1))
164 struct segment *line = rst->linebuf[0] + rst->size[0];
167 line->flags = SEGFLAG_EXACT_LEFT | SEGFLAG_EXACT_RIGHT |
168 SEGFLAG_EXACT_TOP | SEGFLAG_EXACT_BOTTOM;
170 line->flags ^= SEGFLAG_UL_DR;
172 line->flags ^= SEGFLAG_DN | SEGFLAG_UL_DR;
174 line->x_min = FFMIN(pt0.x, pt1.x);
175 line->x_max = FFMAX(pt0.x, pt1.x);
176 line->y_min = FFMIN(pt0.y, pt1.y);
177 line->y_max = FFMAX(pt0.y, pt1.y);
181 line->c = y * (int64_t) pt0.x - x * (int64_t) pt0.y;
183 // halfplane normalization
184 int32_t abs_x = x < 0 ? -x : x;
185 int32_t abs_y = y < 0 ? -y : y;
186 uint32_t max_ab = (abs_x > abs_y ? abs_x : abs_y);
187 int shift = 30 - ilog2(max_ab);
188 max_ab <<= shift + 1;
189 line->a *= 1 << shift;
190 line->b *= 1 << shift;
191 line->c *= 1 << shift;
192 line->scale = (uint64_t) 0x53333333 * (uint32_t) (max_ab * (uint64_t) max_ab >> 32) >> 32;
193 line->scale += 0x8810624D - (0xBBC6A7EF * (uint64_t) max_ab >> 32);
194 //line->scale = ((uint64_t) 1 << 61) / max_ab;
199 * \brief Add quadratic spline to polyline
200 * Performs recursive subdivision if necessary.
202 static bool add_quadratic(RasterizerData *rst, const ASS_Vector *pt)
205 segment_init(&seg, pt[0], pt[2], rst->outline_error);
206 if (!segment_subdivide(&seg, pt[0], pt[1]))
207 return add_line(rst, pt[0], pt[2]);
210 next[1].x = pt[0].x + pt[1].x;
211 next[1].y = pt[0].y + pt[1].y;
212 next[3].x = pt[1].x + pt[2].x;
213 next[3].y = pt[1].y + pt[2].y;
214 next[2].x = (next[1].x + next[3].x + 2) >> 2;
215 next[2].y = (next[1].y + next[3].y + 2) >> 2;
222 return add_quadratic(rst, next) && add_quadratic(rst, next + 2);
226 * \brief Add cubic spline to polyline
227 * Performs recursive subdivision if necessary.
229 static bool add_cubic(RasterizerData *rst, const ASS_Vector *pt)
232 segment_init(&seg, pt[0], pt[3], rst->outline_error);
233 if (!segment_subdivide(&seg, pt[0], pt[1]) && !segment_subdivide(&seg, pt[0], pt[2]))
234 return add_line(rst, pt[0], pt[3]);
236 ASS_Vector next[7], center;
237 next[1].x = pt[0].x + pt[1].x;
238 next[1].y = pt[0].y + pt[1].y;
239 center.x = pt[1].x + pt[2].x + 2;
240 center.y = pt[1].y + pt[2].y + 2;
241 next[5].x = pt[2].x + pt[3].x;
242 next[5].y = pt[2].y + pt[3].y;
243 next[2].x = next[1].x + center.x;
244 next[2].y = next[1].y + center.y;
245 next[4].x = center.x + next[5].x;
246 next[4].y = center.y + next[5].y;
247 next[3].x = (next[2].x + next[4].x - 1) >> 3;
248 next[3].y = (next[2].y + next[4].y - 1) >> 3;
259 return add_cubic(rst, next) && add_cubic(rst, next + 3);
263 bool rasterizer_set_outline(RasterizerData *rst,
264 const ASS_Outline *path, bool extra)
267 S_ON, S_Q, S_C1, S_C2
271 rst->x_min = rst->y_min = INT32_MAX;
272 rst->x_max = rst->y_max = INT32_MIN;
275 rst->size[0] = rst->n_first;
276 for (size_t i = 0, j = 0; i < path->n_contours; i++) {
277 ASS_Vector start, p[4];
281 int last = path->contours[i];
285 if (path->points[j].x < -(1 << 28) || path->points[j].x >= (1 << 28))
287 if (path->points[j].y <= -(1 << 28) || path->points[j].y > (1 << 28))
290 switch (FT_CURVE_TAG(path->tags[j])) {
291 case FT_CURVE_TAG_ON:
292 p[0].x = path->points[j].x;
293 p[0].y = -path->points[j].y;
298 case FT_CURVE_TAG_CONIC:
299 switch (FT_CURVE_TAG(path->tags[last])) {
300 case FT_CURVE_TAG_ON:
301 p[0].x = path->points[last].x;
302 p[0].y = -path->points[last].y;
303 p[1].x = path->points[j].x;
304 p[1].y = -path->points[j].y;
309 case FT_CURVE_TAG_CONIC:
310 p[1].x = path->points[j].x;
311 p[1].y = -path->points[j].y;
312 p[0].x = (p[1].x + path->points[last].x) >> 1;
313 p[0].y = (p[1].y - path->points[last].y) >> 1;
327 for (j++; j <= last; j++) {
328 if (path->points[j].x < -(1 << 28) || path->points[j].x >= (1 << 28))
330 if (path->points[j].y <= -(1 << 28) || path->points[j].y > (1 << 28))
333 switch (FT_CURVE_TAG(path->tags[j])) {
334 case FT_CURVE_TAG_ON:
337 p[1].x = path->points[j].x;
338 p[1].y = -path->points[j].y;
339 if (!add_line(rst, p[0], p[1]))
345 p[2].x = path->points[j].x;
346 p[2].y = -path->points[j].y;
347 if (!add_quadratic(rst, p))
354 p[3].x = path->points[j].x;
355 p[3].y = -path->points[j].y;
356 if (!add_cubic(rst, p))
367 case FT_CURVE_TAG_CONIC:
370 p[1].x = path->points[j].x;
371 p[1].y = -path->points[j].y;
376 p[3].x = path->points[j].x;
377 p[3].y = -path->points[j].y;
378 p[2].x = (p[1].x + p[3].x) >> 1;
379 p[2].y = (p[1].y + p[3].y) >> 1;
380 if (!add_quadratic(rst, p))
391 case FT_CURVE_TAG_CUBIC:
394 p[1].x = path->points[j].x;
395 p[1].y = -path->points[j].y;
400 p[2].x = path->points[j].x;
401 p[2].y = -path->points[j].y;
418 if (!add_line(rst, p[0], start))
424 if (!add_quadratic(rst, p))
430 if (!add_cubic(rst, p))
439 for (size_t k = rst->n_first; k < rst->size[0]; k++) {
440 rst->x_min = FFMIN(rst->x_min, rst->linebuf[0][k].x_min);
441 rst->x_max = FFMAX(rst->x_max, rst->linebuf[0][k].x_max);
442 rst->y_min = FFMIN(rst->y_min, rst->linebuf[0][k].y_min);
443 rst->y_max = FFMAX(rst->y_max, rst->linebuf[0][k].y_max);
446 rst->n_first = rst->size[0];
451 static void segment_move_x(struct segment *line, int32_t x)
455 line->x_min = FFMAX(line->x_min, 0);
456 line->c -= line->a * (int64_t) x;
458 static const int test = SEGFLAG_EXACT_LEFT | SEGFLAG_UL_DR;
459 if (!line->x_min && (line->flags & test) == test)
460 line->flags &= ~SEGFLAG_EXACT_TOP;
463 static void segment_move_y(struct segment *line, int32_t y)
467 line->y_min = FFMAX(line->y_min, 0);
468 line->c -= line->b * (int64_t) y;
470 static const int test = SEGFLAG_EXACT_TOP | SEGFLAG_UL_DR;
471 if (!line->y_min && (line->flags & test) == test)
472 line->flags &= ~SEGFLAG_EXACT_LEFT;
475 static void segment_split_horz(struct segment *line, struct segment *next, int32_t x)
477 assert(x > line->x_min && x < line->x_max);
480 next->c -= line->a * (int64_t) x;
485 line->flags &= ~SEGFLAG_EXACT_TOP;
486 next->flags &= ~SEGFLAG_EXACT_BOTTOM;
487 if (line->flags & SEGFLAG_UL_DR) {
488 int32_t tmp = line->flags;
489 line->flags = next->flags;
492 line->flags |= SEGFLAG_EXACT_RIGHT;
493 next->flags |= SEGFLAG_EXACT_LEFT;
496 static void segment_split_vert(struct segment *line, struct segment *next, int32_t y)
498 assert(y > line->y_min && y < line->y_max);
501 next->c -= line->b * (int64_t) y;
506 line->flags &= ~SEGFLAG_EXACT_LEFT;
507 next->flags &= ~SEGFLAG_EXACT_RIGHT;
508 if (line->flags & SEGFLAG_UL_DR) {
509 int32_t tmp = line->flags;
510 line->flags = next->flags;
513 line->flags |= SEGFLAG_EXACT_BOTTOM;
514 next->flags |= SEGFLAG_EXACT_TOP;
517 static inline int segment_check_left(const struct segment *line, int32_t x)
519 if (line->flags & SEGFLAG_EXACT_LEFT)
520 return line->x_min >= x;
521 int64_t cc = line->c - line->a * (int64_t) x -
522 line->b * (int64_t) (line->flags & SEGFLAG_UL_DR ? line->y_min : line->y_max);
528 static inline int segment_check_right(const struct segment *line, int32_t x)
530 if (line->flags & SEGFLAG_EXACT_RIGHT)
531 return line->x_max <= x;
532 int64_t cc = line->c - line->a * (int64_t) x -
533 line->b * (int64_t) (line->flags & SEGFLAG_UL_DR ? line->y_max : line->y_min);
539 static inline int segment_check_top(const struct segment *line, int32_t y)
541 if (line->flags & SEGFLAG_EXACT_TOP)
542 return line->y_min >= y;
543 int64_t cc = line->c - line->b * (int64_t) y -
544 line->a * (int64_t) (line->flags & SEGFLAG_UL_DR ? line->x_min : line->x_max);
550 static inline int segment_check_bottom(const struct segment *line, int32_t y)
552 if (line->flags & SEGFLAG_EXACT_BOTTOM)
553 return line->y_max <= y;
554 int64_t cc = line->c - line->b * (int64_t) y -
555 line->a * (int64_t) (line->flags & SEGFLAG_UL_DR ? line->x_max : line->x_min);
562 * \brief Split list of segments horizontally
563 * \param src in: input array, can coincide with *dst0 or *dst1
564 * \param n_src in: numbers of input segments for both groups
565 * \param dst0, dst1 out: output arrays of at least n_src[0] + n_src[1] size
566 * \param n_dst0, n_dst1 out: numbers of output segments for both groups
567 * \param winding out: resulting winding of bottom-split point
568 * \param x in: split coordinate
570 static void polyline_split_horz(const struct segment *src, const size_t n_src[2],
571 struct segment *dst0, size_t n_dst0[2],
572 struct segment *dst1, size_t n_dst1[2],
573 int winding[2], int32_t x)
575 const struct segment *cmp = src + n_src[0];
576 const struct segment *end = cmp + n_src[1];
577 n_dst0[0] = n_dst0[1] = 0;
578 n_dst1[0] = n_dst1[1] = 0;
579 for (; src != end; src++) {
580 int group = src < cmp ? 0 : 1;
583 if (!src->y_min && (src->flags & SEGFLAG_EXACT_TOP))
584 delta = src->a < 0 ? 1 : -1;
585 if (segment_check_right(src, x)) {
586 winding[group] += delta;
590 dst0->x_max = FFMIN(dst0->x_max, x);
595 if (segment_check_left(src, x)) {
597 segment_move_x(dst1, x);
602 if (src->flags & SEGFLAG_UL_DR)
603 winding[group] += delta;
605 segment_split_horz(dst0, dst1, x);
614 * \brief Split list of segments vertically
616 static void polyline_split_vert(const struct segment *src, const size_t n_src[2],
617 struct segment *dst0, size_t n_dst0[2],
618 struct segment *dst1, size_t n_dst1[2],
619 int winding[2], int32_t y)
621 const struct segment *cmp = src + n_src[0];
622 const struct segment *end = cmp + n_src[1];
623 n_dst0[0] = n_dst0[1] = 0;
624 n_dst1[0] = n_dst1[1] = 0;
625 for (; src != end; src++) {
626 int group = src < cmp ? 0 : 1;
629 if (!src->x_min && (src->flags & SEGFLAG_EXACT_LEFT))
630 delta = src->b < 0 ? 1 : -1;
631 if (segment_check_bottom(src, y)) {
632 winding[group] += delta;
636 dst0->y_max = dst0->y_max < y ? dst0->y_max : y;
641 if (segment_check_top(src, y)) {
643 segment_move_y(dst1, y);
648 if (src->flags & SEGFLAG_UL_DR)
649 winding[group] += delta;
651 segment_split_vert(dst0, dst1, y);
660 static inline void rasterizer_fill_solid(const BitmapEngine *engine,
661 uint8_t *buf, int width, int height, ptrdiff_t stride,
664 assert(!(width & ((1 << engine->tile_order) - 1)));
665 assert(!(height & ((1 << engine->tile_order) - 1)));
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 engine->fill_solid(buf + x * step, stride, set);
678 static inline void rasterizer_fill_halfplane(const BitmapEngine *engine,
679 uint8_t *buf, int width, int height, ptrdiff_t stride,
680 int32_t a, int32_t b, int64_t c, int32_t scale)
682 assert(!(width & ((1 << engine->tile_order) - 1)));
683 assert(!(height & ((1 << engine->tile_order) - 1)));
684 if (width == 1 << engine->tile_order && height == 1 << engine->tile_order) {
685 engine->fill_halfplane(buf, stride, a, b, c, scale);
689 uint32_t abs_a = a < 0 ? -a : a;
690 uint32_t abs_b = b < 0 ? -b : b;
691 int64_t size = (int64_t) (abs_a + abs_b) << (engine->tile_order + 5);
692 int64_t offs = ((int64_t) a + b) * (1 << (engine->tile_order + 5));
694 ptrdiff_t step = 1 << engine->tile_order;
695 ptrdiff_t tile_stride = stride * (1 << engine->tile_order);
696 width >>= engine->tile_order;
697 height >>= engine->tile_order;
698 for (int y = 0; y < height; y++) {
699 for (int x = 0; x < width; x++) {
700 int64_t cc = c - (a * (int64_t) x + b * (int64_t) y) * (1 << (engine->tile_order + 6));
701 int64_t offs_c = offs - cc;
702 int64_t abs_c = offs_c < 0 ? -offs_c : offs_c;
704 engine->fill_halfplane(buf + x * step, stride, a, b, cc, scale);
706 engine->fill_solid(buf + x * step, stride,
707 ((uint32_t) (offs_c >> 32) ^ scale) & 0x80000000);
720 static inline int get_fill_flags(struct segment *line, size_t n_lines, int winding)
723 return winding ? FLAG_SOLID : 0;
725 return FLAG_COMPLEX | FLAG_GENERIC;
727 static const int test = SEGFLAG_UL_DR | SEGFLAG_EXACT_LEFT;
728 if (((line->flags & test) != test) == !(line->flags & SEGFLAG_DN))
733 return FLAG_COMPLEX | FLAG_REVERSE;
742 * \brief Main quad-tree filling function
743 * \param index index (0 or 1) of the input segment buffer (rst->linebuf)
744 * \param offs current offset from the beginning of the buffer
745 * \param winding bottom-left winding value
746 * \return false on error
747 * Rasterizes (possibly recursive) one quad-tree level.
748 * Truncates used input buffer.
750 static bool rasterizer_fill_level(const BitmapEngine *engine, RasterizerData *rst,
751 uint8_t *buf, int width, int height, ptrdiff_t stride,
752 int index, const size_t n_lines[2], const int winding[2])
754 assert(width > 0 && height > 0);
755 assert((unsigned) index < 2u && n_lines[0] + n_lines[1] <= rst->size[index]);
756 assert(!(width & ((1 << engine->tile_order) - 1)));
757 assert(!(height & ((1 << engine->tile_order) - 1)));
759 size_t offs = rst->size[index] - n_lines[0] - n_lines[1];
760 struct segment *line = rst->linebuf[index] + offs, *line1 = line + n_lines[0];
761 int flags0 = get_fill_flags(line, n_lines[0], winding[0]);
762 int flags1 = get_fill_flags(line1, n_lines[1], winding[1]);
763 int flags = (flags0 | flags1) ^ FLAG_COMPLEX;
764 if (flags & (FLAG_SOLID | FLAG_COMPLEX)) {
765 rasterizer_fill_solid(engine, buf, width, height, stride, flags & FLAG_SOLID);
766 rst->size[index] = offs;
769 if (!(flags & FLAG_GENERIC) && ((flags0 ^ flags1) & FLAG_COMPLEX)) {
770 if (flags1 & FLAG_COMPLEX)
772 rasterizer_fill_halfplane(engine, buf, width, height, stride,
773 line->a, line->b, line->c,
774 flags & FLAG_REVERSE ? -line->scale : line->scale);
775 rst->size[index] = offs;
778 if (width == 1 << engine->tile_order && height == 1 << engine->tile_order) {
779 if (!(flags1 & FLAG_COMPLEX)) {
780 engine->fill_generic(buf, stride, line, n_lines[0], winding[0]);
781 rst->size[index] = offs;
784 if (!(flags0 & FLAG_COMPLEX)) {
785 engine->fill_generic(buf, stride, line1, n_lines[1], winding[1]);
786 rst->size[index] = offs;
789 if (flags0 & FLAG_GENERIC)
790 engine->fill_generic(buf, stride, line, n_lines[0], winding[0]);
792 engine->fill_halfplane(buf, stride, line->a, line->b, line->c,
793 flags0 & FLAG_REVERSE ? -line->scale : line->scale);
794 if (flags1 & FLAG_GENERIC)
795 engine->fill_generic(rst->tile, width, line1, n_lines[1], winding[1]);
797 engine->fill_halfplane(rst->tile, width, line1->a, line1->b, line1->c,
798 flags1 & FLAG_REVERSE ? -line1->scale : line1->scale);
799 // XXX: better to use max instead of add
800 engine->add_bitmaps(buf, stride, rst->tile, width, height, width);
801 rst->size[index] = offs;
805 size_t offs1 = rst->size[index ^ 1];
806 if (!check_capacity(rst, index ^ 1, n_lines[0] + n_lines[1]))
808 struct segment *dst0 = line;
809 struct segment *dst1 = rst->linebuf[index ^ 1] + offs1;
813 int height1 = height;
814 size_t n_next0[2], n_next1[2];
815 int winding1[2] = { winding[0], winding[1] };
816 if (width > height) {
817 width = 1 << ilog2(width - 1);
820 polyline_split_horz(line, n_lines,
821 dst0, n_next0, dst1, n_next1,
822 winding1, (int32_t) width << 6);
824 height = 1 << ilog2(height - 1);
826 buf1 += height * stride;
827 polyline_split_vert(line, n_lines,
828 dst0, n_next0, dst1, n_next1,
829 winding1, (int32_t) height << 6);
831 rst->size[index ^ 0] = offs + n_next0[0] + n_next0[1];
832 rst->size[index ^ 1] = offs1 + n_next1[0] + n_next1[1];
834 if (!rasterizer_fill_level(engine, rst, buf, width, height, stride, index ^ 0, n_next0, winding))
836 assert(rst->size[index ^ 0] == offs);
837 if (!rasterizer_fill_level(engine, rst, buf1, width1, height1, stride, index ^ 1, n_next1, winding1))
839 assert(rst->size[index ^ 1] == offs1);
843 bool rasterizer_fill(const BitmapEngine *engine, RasterizerData *rst,
844 uint8_t *buf, int x0, int y0,
845 int width, int height, ptrdiff_t stride)
847 assert(width > 0 && height > 0);
848 assert(!(width & ((1 << engine->tile_order) - 1)));
849 assert(!(height & ((1 << engine->tile_order) - 1)));
850 x0 *= 1 << 6; y0 *= 1 << 6;
852 struct segment *line = rst->linebuf[0];
853 struct segment *end = line + rst->size[0];
854 for (; line != end; line++) {
859 line->c -= line->a * (int64_t) x0 + line->b * (int64_t) y0;
866 if (!check_capacity(rst, 1, rst->size[0]))
870 size_t n_lines[2] = { rst->n_first, rst->size[0] - rst->n_first };
871 int winding[2] = { 0, 0 };
873 int32_t size_x = (int32_t) width << 6;
874 int32_t size_y = (int32_t) height << 6;
875 if (rst->x_max >= size_x) {
876 polyline_split_horz(rst->linebuf[0], n_lines,
877 rst->linebuf[0], n_lines,
878 rst->linebuf[1], n_unused,
880 winding[0] = winding[1] = 0;
882 if (rst->y_max >= size_y) {
883 polyline_split_vert(rst->linebuf[0], n_lines,
884 rst->linebuf[0], n_lines,
885 rst->linebuf[1], n_unused,
887 winding[0] = winding[1] = 0;
889 if (rst->x_min <= 0) {
890 polyline_split_horz(rst->linebuf[0], n_lines,
891 rst->linebuf[1], n_unused,
892 rst->linebuf[0], n_lines,
895 if (rst->y_min <= 0) {
896 polyline_split_vert(rst->linebuf[0], n_lines,
897 rst->linebuf[1], n_unused,
898 rst->linebuf[0], n_lines,
901 rst->size[0] = n_lines[0] + n_lines[1];
903 return rasterizer_fill_level(engine, rst,
904 buf, width, height, stride,
905 0, n_lines, winding);