15DlRegion::SpanBuffer::SpanBuffer(DlRegion::SpanBuffer&& m)
16 : capacity_(m.capacity_), size_(m.size_), spans_(m.spans_) {
22DlRegion::SpanBuffer::SpanBuffer(
const DlRegion::SpanBuffer& m)
23 : capacity_(m.capacity_), size_(m.size_) {
24 if (m.spans_ ==
nullptr) {
27 spans_ =
static_cast<Span*
>(std::malloc(capacity_ *
sizeof(Span)));
28 memcpy(spans_, m.spans_, size_ *
sizeof(Span));
32DlRegion::SpanBuffer& DlRegion::SpanBuffer::operator=(
33 const DlRegion::SpanBuffer& buffer) {
34 SpanBuffer copy(buffer);
35 std::swap(*
this, copy);
39DlRegion::SpanBuffer& DlRegion::SpanBuffer::operator=(
40 DlRegion::SpanBuffer&& buffer) {
41 std::swap(capacity_,
buffer.capacity_);
42 std::swap(size_,
buffer.size_);
43 std::swap(spans_,
buffer.spans_);
47DlRegion::SpanBuffer::~SpanBuffer() {
51void DlRegion::SpanBuffer::reserve(
size_t capacity) {
52 if (capacity_ < capacity) {
53 spans_ =
static_cast<Span*
>(std::realloc(spans_, capacity *
sizeof(Span)));
58DlRegion::SpanChunkHandle DlRegion::SpanBuffer::storeChunk(
const Span* begin,
60 size_t chunk_size =
end - begin;
61 size_t min_capacity = size_ + chunk_size + 1;
62 if (capacity_ < min_capacity) {
63 size_t new_capacity = std::max(min_capacity, capacity_ * 2);
64 new_capacity = std::max(new_capacity,
static_cast<size_t>(512));
65 reserve(new_capacity);
67 SpanChunkHandle res = size_;
68 size_ += chunk_size + 1;
69 setChunkSize(res, chunk_size);
71 auto* dst = spans_ + res + 1;
72 memmove(dst, begin, chunk_size *
sizeof(Span));
77size_t DlRegion::SpanBuffer::getChunkSize(SpanChunkHandle handle)
const {
79 return spans_[handle].left;
82void DlRegion::SpanBuffer::setChunkSize(SpanChunkHandle handle,
size_t size) {
86 spans_[handle].left =
size;
89void DlRegion::SpanBuffer::getSpans(SpanChunkHandle handle,
90 const DlRegion::Span*& begin,
91 const DlRegion::Span*&
end)
const {
93 begin = spans_ + handle + 1;
94 end = begin + getChunkSize(handle);
97DlRegion::DlRegion(
const std::vector<DlIRect>& rects) {
101DlRegion::DlRegion(
const DlIRect& rect) : bounds_(rect) {
103 lines_.push_back(makeLine(rect.
GetTop(), rect.
GetBottom(), &span, &span + 1));
106bool DlRegion::spansEqual(SpanLine& line,
108 const Span*
end)
const {
109 const Span *our_begin, *our_end;
110 span_buffer_.getSpans(line.chunk_handle, our_begin, our_end);
111 size_t our_size = our_end - our_begin;
112 size_t their_size =
end - begin;
113 if (our_size != their_size) {
117 return memcmp(our_begin, begin, our_size *
sizeof(Span)) == 0;
120DlRegion::SpanLine DlRegion::makeLine(int32_t top,
123 return makeLine(top, bottom, v.data(), v.data() + v.size());
126DlRegion::SpanLine DlRegion::makeLine(int32_t top,
130 auto handle = span_buffer_.storeChunk(begin,
end);
131 return {top, bottom, handle};
136size_t DlRegion::unionLineSpans(std::vector<Span>& res,
137 const SpanBuffer& a_buffer,
138 SpanChunkHandle a_handle,
139 const SpanBuffer& b_buffer,
140 SpanChunkHandle b_handle) {
141 class OrderedSpanAccumulator {
143 explicit OrderedSpanAccumulator(std::vector<Span>& res) : res(res) {}
145 void accumulate(
const Span& span) {
146 if (span.left > last_ || len == 0) {
149 }
else if (span.right > last_) {
151 res[len - 1].right = span.right;
157 std::vector<Span>& res;
160 int32_t last_ = std::numeric_limits<int32_t>::min();
163 const Span *begin1, *end1;
164 a_buffer.getSpans(a_handle, begin1, end1);
166 const Span *begin2, *end2;
167 b_buffer.getSpans(b_handle, begin2, end2);
169 size_t min_size = (end1 - begin1) + (end2 - begin2);
170 if (res.size() < min_size) {
171 res.resize(min_size);
174 OrderedSpanAccumulator accumulator(res);
177 if (begin1->left < begin2->left) {
178 accumulator.accumulate(*begin1++);
179 if (begin1 == end1) {
185 accumulator.accumulate(*begin2++);
186 if (begin2 == end2) {
194 while (begin1 < end1) {
195 accumulator.accumulate(*begin1++);
197 while (begin2 < end2) {
198 accumulator.accumulate(*begin2++);
203 return accumulator.len;
206size_t DlRegion::intersectLineSpans(std::vector<Span>& res,
207 const SpanBuffer& a_buffer,
208 SpanChunkHandle a_handle,
209 const SpanBuffer& b_buffer,
210 SpanChunkHandle b_handle) {
211 const Span *begin1, *end1;
212 a_buffer.getSpans(a_handle, begin1, end1);
214 const Span *begin2, *end2;
215 b_buffer.getSpans(b_handle, begin2, end2);
220 size_t min_size = (end1 - begin1) + (end2 - begin2) - 1;
221 if (res.size() < min_size) {
222 res.resize(min_size);
226 Span* new_span = res.data();
228 while (begin1 != end1 && begin2 != end2) {
229 if (begin1->right <= begin2->left) {
231 }
else if (begin2->right <= begin1->left) {
234 int32_t
left = std::max(begin1->left, begin2->left);
235 int32_t
right = std::min(begin1->right, begin2->right);
237 FML_DCHECK(new_span < res.data() + res.size());
239 if (begin1->right == right) {
242 if (begin2->right == right) {
248 return new_span - res.data();
251void DlRegion::setRects(
const std::vector<DlIRect>& unsorted_rects) {
255 size_t count = unsorted_rects.size();
256 std::vector<const DlIRect*> rects(count);
257 for (
size_t i = 0;
i < count;
i++) {
258 rects[
i] = &unsorted_rects[
i];
259 bounds_ = bounds_.
Union(unsorted_rects[
i]);
261 std::sort(rects.begin(), rects.end(), [](
const DlIRect* a,
const DlIRect* b) {
262 if (a->GetTop() < b->GetTop()) {
265 if (a->GetTop() >
b->GetTop()) {
268 return a->GetLeft() <
b->GetLeft();
271 size_t active_end = 0;
272 size_t next_rect = 0;
273 int32_t cur_y = std::numeric_limits<int32_t>::min();
274 SpanVec working_spans;
276#ifdef DlRegion_DO_STATS
277 size_t active_rect_count = 0;
278 size_t span_count = 0;
283 while (next_rect < count || active_end > 0) {
285 size_t preserve_end = 0;
286 for (
size_t i = 0;
i < active_end;
i++) {
288 if (r->GetBottom() > cur_y) {
289 rects[preserve_end++] = r;
292 active_end = preserve_end;
296 if (active_end == 0) {
297 if (next_rect >= count) {
301 cur_y = rects[next_rect]->
GetTop();
305 while (next_rect < count) {
306 const DlIRect* r = rects[next_rect];
310 if (r->GetTop() > cur_y) {
315 size_t insert_at = active_end++;
316 while (insert_at > 0) {
317 const DlIRect* ir = rects[insert_at - 1];
318 if (ir->GetLeft() <= r->GetLeft()) {
321 rects[insert_at--] = ir;
323 rects[insert_at] = r;
329 working_spans.clear();
332#ifdef DlRegion_DO_STATS
333 active_rect_count += active_end;
339 int32_t start_x = rects[0]->GetLeft();
340 int32_t end_x = rects[0]->GetRight();
341 int32_t end_y = rects[0]->GetBottom();
342 for (
size_t i = 1;
i < active_end;
i++) {
344 if (r->GetLeft() > end_x) {
345 working_spans.emplace_back(start_x, end_x);
347 end_x = r->GetRight();
348 }
else if (end_x < r->GetRight()) {
349 end_x = r->GetRight();
351 if (end_y > r->GetBottom()) {
352 end_y = r->GetBottom();
355 working_spans.emplace_back(start_x, end_x);
358 if (next_rect < count && end_y > rects[next_rect]->GetTop()) {
359 end_y = rects[next_rect]->GetTop();
366 if (!lines_.empty() && lines_.back().bottom == cur_y &&
367 spansEqual(lines_.back(), working_spans.data(),
368 working_spans.data() + working_spans.size())) {
369 lines_.back().bottom = end_y;
371#ifdef DlRegion_DO_STATS
372 span_count += working_spans.size();
375 lines_.push_back(makeLine(cur_y, end_y, working_spans));
380#ifdef DlRegion_DO_STATS
381 double span_avg = ((double)span_count) / line_count;
382 double active_avg = ((double)active_rect_count) / pass_count;
383 FML_LOG(ERROR) << lines_.size() <<
" lines for " << count
384 <<
" input rects, avg " << span_avg
385 <<
" spans per line and avg " << active_avg
386 <<
" active rects per loop";
390void DlRegion::appendLine(int32_t top,
394 if (lines_.empty()) {
395 lines_.push_back(makeLine(top, bottom, begin,
end));
397 if (lines_.back().bottom == top && spansEqual(lines_.back(), begin,
end)) {
398 lines_.back().bottom = bottom;
400 lines_.push_back(makeLine(top, bottom, begin,
end));
408 }
else if (b.isEmpty()) {
412 }
else if (b.isSimple() && b.bounds_.Contains(a.bounds_)) {
417 res.bounds_ = a.bounds_.
Union(b.bounds_);
418 res.span_buffer_.reserve(a.span_buffer_.capacity() +
419 b.span_buffer_.capacity());
421 auto& lines = res.lines_;
422 lines.reserve(a.lines_.size() + b.lines_.size());
424 auto a_it = a.lines_.begin();
425 auto b_it = b.lines_.begin();
426 auto a_end = a.lines_.end();
427 auto b_end = b.lines_.end();
431 auto& a_buffer = a.span_buffer_;
432 auto& b_buffer = b.span_buffer_;
434 std::vector<Span> tmp;
436 int32_t cur_top = std::numeric_limits<int32_t>::min();
438 while (a_it != a_end && b_it != b_end) {
439 auto a_top = std::max(cur_top, a_it->top);
440 auto b_top = std::max(cur_top, b_it->top);
441 if (a_it->bottom <= b_top) {
442 res.appendLine(a_top, a_it->bottom, a_buffer, a_it->chunk_handle);
444 }
else if (b_it->bottom <= a_top) {
445 res.appendLine(b_top, b_it->bottom, b_buffer, b_it->chunk_handle);
449 res.appendLine(a_top, b_top, a_buffer, a_it->chunk_handle);
451 if (cur_top == a_it->bottom) {
454 }
else if (b_top < a_top) {
455 res.appendLine(b_top, a_top, b_buffer, b_it->chunk_handle);
457 if (cur_top == b_it->bottom) {
461 auto new_bottom = std::min(a_it->bottom, b_it->bottom);
465 auto size = unionLineSpans(tmp, a_buffer, a_it->chunk_handle, b_buffer,
467 res.appendLine(a_top, new_bottom, tmp.data(), tmp.data() +
size);
468 cur_top = new_bottom;
469 if (cur_top == a_it->bottom) {
472 if (cur_top == b_it->bottom) {
481 while (a_it != a_end) {
482 auto a_top = std::max(cur_top, a_it->top);
483 res.appendLine(a_top, a_it->bottom, a_buffer, a_it->chunk_handle);
487 while (b_it != b_end) {
488 auto b_top = std::max(cur_top, b_it->top);
489 res.appendLine(b_top, b_it->bottom, b_buffer, b_it->chunk_handle);
499 }
else if (a.
isSimple() && b.isSimple()) {
505 }
else if (b.isSimple() && b.bounds_.Contains(a.bounds_)) {
510 res.span_buffer_.reserve(
511 std::max(a.span_buffer_.capacity(), b.span_buffer_.capacity()));
513 auto& lines = res.lines_;
514 lines.reserve(std::min(a.lines_.size(), b.lines_.size()));
516 std::vector<SpanLine>::const_iterator a_it, b_it;
517 getIntersectionIterators(a.lines_, b.lines_, a_it, b_it);
519 auto a_end = a.lines_.end();
520 auto b_end = b.lines_.end();
522 auto& a_buffer = a.span_buffer_;
523 auto& b_buffer = b.span_buffer_;
525 std::vector<Span> tmp;
527 int32_t cur_top = std::numeric_limits<int32_t>::min();
529 while (a_it != a_end && b_it != b_end) {
530 auto a_top = std::max(cur_top, a_it->top);
531 auto b_top = std::max(cur_top, b_it->top);
532 if (a_it->bottom <= b_top) {
534 }
else if (b_it->bottom <= a_top) {
537 auto top = std::max(a_top, b_top);
538 auto bottom = std::min(a_it->bottom, b_it->bottom);
540 auto size = intersectLineSpans(tmp, a_buffer, a_it->chunk_handle,
541 b_buffer, b_it->chunk_handle);
543 res.appendLine(top, bottom, tmp.data(), tmp.data() +
size);
544 res.bounds_ = res.bounds_.
Union(DlIRect::MakeLTRB(
545 tmp.data()->left, top, (tmp.data() +
size - 1)->right, bottom));
548 if (cur_top == a_it->bottom) {
551 if (cur_top == b_it->bottom) {
560std::vector<DlIRect> DlRegion::getRects(
bool deband)
const {
561 std::vector<DlIRect> rects;
564 }
else if (isSimple()) {
565 rects.push_back(bounds_);
569 size_t rect_count = 0;
570 size_t previous_span_end = 0;
571 for (
const auto& line : lines_) {
572 rect_count += span_buffer_.getChunkSize(line.chunk_handle);
574 rects.reserve(rect_count);
576 for (
const auto& line : lines_) {
577 const Span *span_begin, *span_end;
578 span_buffer_.getSpans(line.chunk_handle, span_begin, span_end);
579 for (
const auto* span = span_begin; span < span_end; ++span) {
580 int32_t top = line.top;
582 auto iter = rects.begin() + previous_span_end;
586 while (iter != rects.begin()) {
588 if (iter->GetBottom() < top) {
591 }
else if (iter->GetLeft() == span->left &&
592 iter->GetRight() == span->right) {
594 top = iter->GetTop();
602 DlIRect::MakeLTRB(span->left, top, span->right, line.bottom));
604 previous_span_end = rects.size();
609bool DlRegion::isComplex()
const {
610 return lines_.size() > 1 ||
611 (lines_.size() == 1 &&
612 span_buffer_.getChunkSize(lines_.front().chunk_handle) > 1);
615bool DlRegion::intersects(
const DlIRect& rect)
const {
620 auto bounds_intersect = bounds_.IntersectsWithRect(rect);
623 return bounds_intersect;
626 if (!bounds_intersect) {
630 auto it = lines_.begin();
631 auto end = lines_.end();
634 it = std::lower_bound(
637 [](
const SpanLine& line, int32_t top) { return line.bottom <= top; });
639 while (it !=
end && it->bottom <= rect.
GetTop()) {
646 const Span *begin, *
end;
647 span_buffer_.getSpans(it->chunk_handle, begin,
end);
648 while (begin !=
end && begin->left < rect.
GetRight()) {
649 if (begin->right > rect.
GetLeft()) {
660bool DlRegion::spansIntersect(
const Span* begin1,
664 while (begin1 != end1 && begin2 != end2) {
665 if (begin1->right <= begin2->left) {
667 }
else if (begin2->right <= begin1->left) {
676void DlRegion::getIntersectionIterators(
677 const std::vector<SpanLine>& a_lines,
678 const std::vector<SpanLine>& b_lines,
679 std::vector<SpanLine>::const_iterator& a_it,
680 std::vector<SpanLine>::const_iterator& b_it) {
681 a_it = a_lines.begin();
682 auto a_end = a_lines.end();
683 b_it = b_lines.begin();
684 auto b_end = b_lines.end();
688 auto a_len = a_end - a_it;
689 auto b_len = b_end - b_it;
691 if (a_len > kBinarySearchThreshold &&
692 a_it[kBinarySearchThreshold].bottom <= b_it->top) {
693 a_it = std::lower_bound(
694 a_lines.begin() + kBinarySearchThreshold + 1, a_lines.end(), b_it->top,
695 [](
const SpanLine& line, int32_t top) { return line.bottom <= top; });
696 }
else if (b_len > kBinarySearchThreshold &&
697 b_it[kBinarySearchThreshold].bottom <= a_it->top) {
698 b_it = std::lower_bound(
699 b_lines.begin() + kBinarySearchThreshold + 1, b_lines.end(), a_it->top,
700 [](
const SpanLine& line, int32_t top) { return line.bottom <= top; });
704bool DlRegion::intersects(
const DlRegion& region)
const {
705 if (isEmpty() || region.
isEmpty()) {
709 auto our_complex = isComplex();
711 auto bounds_intersect = bounds_.IntersectsWithRect(region.bounds_);
713 if (!our_complex && !their_complex) {
714 return bounds_intersect;
717 if (!bounds_intersect) {
725 if (!their_complex) {
726 return intersects(region.bounds_);
729 std::vector<SpanLine>::const_iterator ours, theirs;
730 getIntersectionIterators(lines_, region.lines_, ours, theirs);
731 auto ours_end = lines_.end();
732 auto theirs_end = region.lines_.end();
734 while (ours != ours_end && theirs != theirs_end) {
735 if (ours->bottom <= theirs->top) {
737 }
else if (theirs->bottom <= ours->top) {
740 FML_DCHECK(ours->top < theirs->bottom && theirs->top < ours->bottom);
741 const Span *ours_begin, *ours_end;
742 span_buffer_.getSpans(ours->chunk_handle, ours_begin, ours_end);
743 const Span *theirs_begin, *theirs_end;
744 region.span_buffer_.getSpans(theirs->chunk_handle, theirs_begin,
746 if (spansIntersect(ours_begin, ours_end, theirs_begin, theirs_end)) {
749 if (ours->bottom < theirs->bottom) {
bool isEmpty() const
Returns true if region is empty (contains no rectangles).
bool isComplex() const
Returns true if region is not empty and contains more than one rectangle.
bool intersects(const DlIRect &rect) const
Returns whether this region intersects with a rectangle.
#define FML_LOG(severity)
#define FML_DCHECK(condition)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
impeller::IRect32 DlIRect
const int kBinarySearchThreshold
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
constexpr auto GetBottom() const
constexpr auto GetTop() const
constexpr std::optional< TRect > Intersection(const TRect &o) const
constexpr bool Contains(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the half-open interior of this rectangle.
constexpr TRect Union(const TRect &o) const
constexpr bool IntersectsWithRect(const TRect &o) const
constexpr auto GetLeft() const
constexpr auto GetRight() const