#ifndef CC_TILES_TILING_SET_COVERAGE_ITERATOR_H_
#define CC_TILES_TILING_SET_COVERAGE_ITERATOR_H_
#include <algorithm>
#include <concepts>
#include <memory>
#include <optional>
#include <vector>
#include "base/check.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "cc/base/region.h"
#include "cc/tiles/tile_index.h"
#include "cc/tiles/tiling_coverage_iterator.h"
#include "cc/tiles/tiling_internal.h"
#include "ui/gfx/geometry/rect.h"
namespace cc {
namespace internal {
template <typename T>
concept TilingWithCover =
requires(const T t, const gfx::Rect& rect, float scale) {
{ t.Cover(rect, scale) } -> std::derived_from<TilingCoverageIterator<T>>;
};
}
template <typename T>
requires internal::TilingWithCover<T>
class TilingSetCoverageIterator {
public:
using Container = std::vector<std::unique_ptr<T>>;
using Tile = typename T::Tile;
TilingSetCoverageIterator(const Container& tilings,
const gfx::Rect& coverage_rect,
float coverage_scale,
float ideal_contents_scale)
: tilings_(tilings),
coverage_scale_(coverage_scale),
ideal_tiling_(FindIdealTiling(tilings_, ideal_contents_scale)),
missing_region_(coverage_rect) {
AdvanceUntilTileIsRelevant();
}
~TilingSetCoverageIterator() = default;
bool IsValid() const {
return (current_tiling_ && *current_tiling_ != tilings_.end()) ||
region_iter_ != current_region_.end();
}
explicit operator bool() const { return IsValid(); }
T* CurrentTiling() const {
if (!current_tiling_ || current_tiling_ == tilings_.end()) {
return nullptr;
}
return current_tiling_.value()->get();
}
Tile* operator*() const {
return tiling_iter_.IsValid() ? *tiling_iter_ : nullptr;
}
Tile* operator->() const { return **this; }
gfx::Rect geometry_rect() const {
if (tiling_iter_.IsValid()) {
return tiling_iter_.geometry_rect();
}
if (region_iter_ != current_region_.end()) {
return *region_iter_;
}
return gfx::Rect();
}
gfx::RectF texture_rect() const {
if (tiling_iter_.IsValid()) {
return tiling_iter_.texture_rect();
}
return gfx::RectF();
}
TileResolution resolution() const {
const T* tiling = CurrentTiling();
DCHECK(tiling);
return tiling->resolution();
}
TilingSetCoverageIterator& operator++() {
DCHECK(IsValid());
AdvanceUntilTileIsRelevant();
return *this;
}
private:
using TilingIterator = typename Container::const_iterator;
static TilingIterator FindIdealTiling(const Container& tilings,
float ideal_contents_scale) {
if (tilings.empty()) {
return tilings.end();
}
for (auto iter = tilings.begin(); iter != tilings.end(); ++iter) {
if ((*iter)->contents_scale_key() < ideal_contents_scale) {
return iter == tilings.begin() ? iter : iter - 1;
}
}
return tilings.end() - 1;
}
void AdvanceTiling() {
DCHECK(current_tiling_ != tilings_.end());
if (!current_tiling_) {
current_tiling_ = ideal_tiling_;
} else if (*current_tiling_ > ideal_tiling_) {
++*current_tiling_;
} else if (*current_tiling_ > tilings_.begin()) {
--*current_tiling_;
} else {
current_tiling_ = ideal_tiling_;
++*current_tiling_;
}
}
void AdvanceUntilTileIsRelevant() {
if (!IsValid() && current_tiling_) {
return;
}
if (tiling_iter_.IsValid()) {
++tiling_iter_;
}
while (true) {
for (; tiling_iter_.IsValid(); ++tiling_iter_) {
Tile* const tile = (**current_tiling_)->TileAt(tiling_iter_.index());
if (tile && tile->IsReadyToDraw()) {
return;
}
missing_region_.Union(tiling_iter_.geometry_rect());
}
if (region_iter_ == current_region_.end()) {
AdvanceTiling();
current_region_.Swap(&missing_region_);
missing_region_.Clear();
region_iter_ = current_region_.begin();
if (region_iter_ == current_region_.end()) {
current_tiling_ = tilings_.end();
return;
}
if (current_tiling_ == tilings_.end()) {
return;
}
}
gfx::Rect last_rect = *region_iter_;
++region_iter_;
if (current_tiling_ == tilings_.end()) {
return;
}
tiling_iter_ = (**current_tiling_)->Cover(last_rect, coverage_scale_);
}
}
RAW_PTR_EXCLUSION const Container& tilings_;
const float coverage_scale_;
const TilingIterator ideal_tiling_;
std::optional<TilingIterator> current_tiling_;
Region missing_region_;
Region current_region_;
Region::Iterator region_iter_;
TilingCoverageIterator<T> tiling_iter_;
};
}
#endif