blob: 281fd142b393f1b3727edbe846329345d8c28f39 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/display_list/geometry/dl_region.h"
#include "flutter/fml/logging.h"
namespace flutter {
DlRegion::DlRegion(std::vector<SkIRect>&& rects) {
// If SpanLines can not be memmoved `addRect` would be signifantly slower
// due to cost of inserting and removing elements from the `lines_` vector.
static_assert(std::is_trivially_constructible<SpanLine>::value,
"SpanLine must be trivially constructible.");
addRects(std::move(rects));
for (auto& spanvec : spanvec_pool_) {
delete spanvec;
}
spanvec_pool_.clear();
}
DlRegion::~DlRegion() {
for (auto& line : lines_) {
delete line.spans;
}
}
std::vector<SkIRect> DlRegion::getRects(bool deband) const {
std::vector<SkIRect> rects;
size_t previous_span_end = 0;
for (const auto& line : lines_) {
for (const Span& span : *line.spans) {
SkIRect rect{span.left, line.top, span.right, line.bottom};
if (deband) {
auto iter = rects.begin() + previous_span_end;
// If there is rectangle previously in rects on which this one is a
// vertical continuation, remove the previous rectangle and expand
// this one vertically to cover the area.
while (iter != rects.begin()) {
--iter;
if (iter->bottom() < rect.top()) {
// Went all the way to previous span line.
break;
} else if (iter->left() == rect.left() &&
iter->right() == rect.right()) {
FML_DCHECK(iter->bottom() == rect.top());
rect.fTop = iter->fTop;
rects.erase(iter);
--previous_span_end;
break;
}
}
}
rects.push_back(rect);
}
previous_span_end = rects.size();
}
return rects;
}
void DlRegion::SpanLine::insertSpan(int32_t left, int32_t right) {
auto& spans = *this->spans;
auto size = spans.size();
for (size_t i = 0; i < size; ++i) {
Span& span = spans[i];
if (right < span.left) {
spans.insert(spans.begin() + i, {left, right});
return;
}
if (left > span.right) {
continue;
}
size_t last_index = i;
while (last_index + 1 < size && right >= spans[last_index + 1].left) {
++last_index;
}
span.left = std::min(span.left, left);
span.right = std::max(spans[last_index].right, right);
if (last_index > i) {
spans.erase(spans.begin() + i + 1, spans.begin() + last_index + 1);
}
return;
}
spans.push_back({left, right});
}
bool DlRegion::SpanLine::spansEqual(const SpanLine& l2) const {
SpanVec& spans = *this->spans;
SpanVec& otherSpans = *l2.spans;
FML_DCHECK(this != &l2);
if (spans.size() != otherSpans.size()) {
return false;
}
return memcmp(spans.data(), otherSpans.data(), spans.size() * sizeof(Span)) ==
0;
}
void DlRegion::insertLine(size_t position, SpanLine line) {
lines_.insert(lines_.begin() + position, line);
}
DlRegion::LineVec::iterator DlRegion::removeLine(
DlRegion::LineVec::iterator line) {
spanvec_pool_.push_back(line->spans);
return lines_.erase(line);
}
DlRegion::SpanLine DlRegion::makeLine(int32_t top,
int32_t bottom,
int32_t spanLeft,
int32_t spanRight) {
SpanVec* span_vec;
if (!spanvec_pool_.empty()) {
span_vec = spanvec_pool_.back();
spanvec_pool_.pop_back();
span_vec->clear();
} else {
span_vec = new SpanVec();
}
span_vec->push_back({spanLeft, spanRight});
return {top, bottom, span_vec};
}
DlRegion::SpanLine DlRegion::makeLine(int32_t top,
int32_t bottom,
const SpanVec& spans) {
SpanVec* span_vec;
if (!spanvec_pool_.empty()) {
span_vec = spanvec_pool_.back();
spanvec_pool_.pop_back();
} else {
span_vec = new SpanVec();
}
*span_vec = spans;
return {top, bottom, span_vec};
}
void DlRegion::addRects(std::vector<SkIRect>&& rects) {
std::sort(rects.begin(), rects.end(), [](const SkIRect& a, const SkIRect& b) {
// Sort the rectangles by Y axis. Because the rectangles have varying
// height, they are added to span lines in non-deterministic order and thus
// it makes no difference if they are also sorted by the X axis.
return a.top() < b.top();
});
size_t start_index = 0;
size_t dirty_start = std::numeric_limits<size_t>::max();
size_t dirty_end = 0;
// Marks line as dirty. Dirty lines will be checked for equality
// later and merged as needed.
auto mark_dirty = [&](size_t line) {
dirty_start = std::min(dirty_start, line);
dirty_end = std::max(dirty_end, line);
};
for (const SkIRect& rect : rects) {
if (rect.isEmpty()) {
continue;
}
int32_t y1 = rect.fTop;
int32_t y2 = rect.fBottom;
for (size_t i = start_index; i < lines_.size() && y1 < y2; ++i) {
SpanLine& line = lines_[i];
if (rect.fTop >= line.bottom) {
start_index = i;
continue;
}
if (y2 <= line.top) {
insertLine(i, makeLine(y1, y2, rect.fLeft, rect.fRight));
mark_dirty(i);
y1 = y2;
break;
}
if (y1 < line.top) {
auto prevLineStart = line.top;
insertLine(i, makeLine(y1, prevLineStart, rect.fLeft, rect.fRight));
mark_dirty(i);
y1 = prevLineStart;
continue;
}
if (y1 > line.top) {
// duplicate line
auto prevLineEnd = line.bottom;
line.bottom = y1;
mark_dirty(i);
insertLine(i + 1, makeLine(y1, prevLineEnd, *line.spans));
continue;
}
FML_DCHECK(y1 == line.top);
if (y2 < line.bottom) {
// duplicate line
auto newLine = makeLine(y2, line.bottom, *line.spans);
line.bottom = y2;
line.insertSpan(rect.fLeft, rect.fRight);
insertLine(i + 1, newLine);
y1 = y2;
mark_dirty(i);
break;
}
FML_DCHECK(y2 >= line.bottom);
line.insertSpan(rect.fLeft, rect.fRight);
mark_dirty(i);
y1 = line.bottom;
}
if (y1 < y2) {
lines_.push_back(makeLine(y1, y2, rect.fLeft, rect.fRight));
mark_dirty(lines_.size() - 1);
}
// Check for duplicate lines and merge them.
if (dirty_start <= dirty_end) {
// Expand the region by one if possible.
if (dirty_start > 0) {
--dirty_start;
}
if (dirty_end + 1 < lines_.size()) {
++dirty_end;
}
for (auto i = lines_.begin() + dirty_start;
i < lines_.begin() + dirty_end;) {
auto& line = *i;
auto& next = *(i + 1);
if (line.bottom == next.top && line.spansEqual(next)) {
--dirty_end;
next.top = line.top;
i = removeLine(i);
} else {
++i;
}
}
}
dirty_start = std::numeric_limits<size_t>::max();
dirty_end = 0;
}
}
} // namespace flutter