#include "pdf/paint_aggregator.h"
#include <stddef.h>
#include <stdint.h>
#include <utility>
#include "base/check.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"
namespace chrome_pdf {
namespace {
bool IsNegative(int32_t num) {
return num < 0;
}
}
PaintAggregator::PaintUpdate::PaintUpdate() = default;
PaintAggregator::PaintUpdate::PaintUpdate(
PaintAggregator::PaintUpdate&&) noexcept = default;
PaintAggregator::PaintUpdate& PaintAggregator::PaintUpdate::operator=(
PaintAggregator::PaintUpdate&&) noexcept = default;
PaintAggregator::PaintUpdate::~PaintUpdate() = default;
PaintAggregator::InternalPaintUpdate::InternalPaintUpdate() = default;
PaintAggregator::InternalPaintUpdate::InternalPaintUpdate(
PaintAggregator::InternalPaintUpdate&&) noexcept = default;
PaintAggregator::InternalPaintUpdate&
PaintAggregator::InternalPaintUpdate::operator=(
PaintAggregator::InternalPaintUpdate&&) noexcept = default;
PaintAggregator::InternalPaintUpdate::~InternalPaintUpdate() = default;
gfx::Rect PaintAggregator::GetScrollDamage() const {
DCHECK(!(update_.scroll_delta.x() && update_.scroll_delta.y()));
gfx::Rect damaged_rect;
if (update_.scroll_delta.x()) {
int32_t dx = update_.scroll_delta.x();
damaged_rect.set_y(update_.scroll_rect.y());
damaged_rect.set_height(update_.scroll_rect.height());
if (dx > 0) {
damaged_rect.set_x(update_.scroll_rect.x());
damaged_rect.set_width(dx);
} else {
damaged_rect.set_x(update_.scroll_rect.right() + dx);
damaged_rect.set_width(-dx);
}
} else {
int32_t dy = update_.scroll_delta.y();
damaged_rect.set_x(update_.scroll_rect.x());
damaged_rect.set_width(update_.scroll_rect.width());
if (dy > 0) {
damaged_rect.set_y(update_.scroll_rect.y());
damaged_rect.set_height(dy);
} else {
damaged_rect.set_y(update_.scroll_rect.bottom() + dy);
damaged_rect.set_height(-dy);
}
}
return gfx::IntersectRects(update_.scroll_rect, damaged_rect);
}
PaintAggregator::PaintAggregator() = default;
PaintAggregator::~PaintAggregator() = default;
bool PaintAggregator::HasPendingUpdate() const {
return !update_.scroll_rect.IsEmpty() || !update_.paint_rects.empty();
}
void PaintAggregator::ClearPendingUpdate() {
update_ = InternalPaintUpdate();
}
PaintAggregator::PaintUpdate PaintAggregator::GetPendingUpdate() {
PaintUpdate ret;
ret.scroll_delta = update_.scroll_delta;
ret.scroll_rect = update_.scroll_rect;
ret.has_scroll = ret.scroll_delta.x() != 0 || ret.scroll_delta.y() != 0;
if (ret.has_scroll && !update_.synthesized_scroll_damage_rect) {
update_.synthesized_scroll_damage_rect = true;
gfx::Rect scroll_damage = GetScrollDamage();
InvalidateRectInternal(scroll_damage, false);
}
ret.paint_rects.reserve(update_.paint_rects.size() + 1);
ret.paint_rects.insert(ret.paint_rects.end(), update_.paint_rects.begin(),
update_.paint_rects.end());
return ret;
}
void PaintAggregator::SetIntermediateResults(std::vector<PaintReadyRect> ready,
std::vector<gfx::Rect> pending) {
for (auto& rect : ready) {
update_.ready_rects.insert(update_.ready_rects.end(), std::move(rect));
}
update_.paint_rects = std::move(pending);
}
std::vector<PaintReadyRect> PaintAggregator::TakeReadyRects() {
return std::move(update_.ready_rects);
}
void PaintAggregator::InvalidateRect(const gfx::Rect& rect) {
InvalidateRectInternal(rect, true);
}
void PaintAggregator::ScrollRect(const gfx::Rect& clip_rect,
const gfx::Vector2d& amount) {
if (amount.x() != 0 && amount.y() != 0) {
InvalidateRect(clip_rect);
return;
}
if (!update_.scroll_rect.IsEmpty() && update_.scroll_rect != clip_rect) {
InvalidateRect(clip_rect);
return;
}
if ((amount.x() && update_.scroll_delta.y()) ||
(amount.y() && update_.scroll_delta.x())) {
InvalidateRect(clip_rect);
return;
}
if (!update_.paint_rects.empty()) {
if (IsNegative(amount.x()) != IsNegative(update_.scroll_delta.x()) ||
IsNegative(amount.y()) != IsNegative(update_.scroll_delta.y())) {
InvalidateRect(clip_rect);
return;
}
}
update_.scroll_rect = clip_rect;
update_.scroll_delta += amount;
if (update_.scroll_delta.IsZero()) {
update_.scroll_rect = gfx::Rect();
return;
}
std::vector<gfx::Rect> leftover_rects;
for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
if (!update_.scroll_rect.Intersects(update_.paint_rects[i])) {
continue;
}
gfx::Rect intersection =
gfx::IntersectRects(update_.paint_rects[i], update_.scroll_rect);
gfx::Rect rect = update_.paint_rects[i];
while (!rect.IsEmpty()) {
gfx::Rect leftover = gfx::SubtractRects(rect, intersection);
if (leftover.IsEmpty()) {
break;
}
leftover_rects.push_back(leftover);
rect.Subtract(leftover);
}
update_.paint_rects[i] = ScrollPaintRect(intersection, amount);
if (update_.paint_rects[i].IsEmpty()) {
update_.paint_rects.erase(update_.paint_rects.begin() + i);
i--;
}
}
for (const auto& leftover_rect : leftover_rects) {
InvalidateRectInternal(leftover_rect, false);
}
for (auto& update_rect : update_.ready_rects) {
if (update_.scroll_rect.Contains(update_rect.rect())) {
update_rect.set_rect(ScrollPaintRect(update_rect.rect(), amount));
}
}
if (update_.synthesized_scroll_damage_rect) {
InvalidateRect(GetScrollDamage());
}
}
gfx::Rect PaintAggregator::ScrollPaintRect(const gfx::Rect& paint_rect,
const gfx::Vector2d& amount) const {
gfx::Rect result = paint_rect + amount;
result.Intersect(update_.scroll_rect);
return result;
}
void PaintAggregator::InvalidateScrollRect() {
gfx::Rect scroll_rect = update_.scroll_rect;
update_.scroll_rect = gfx::Rect();
update_.scroll_delta = gfx::Vector2d();
InvalidateRect(scroll_rect);
}
void PaintAggregator::InvalidateRectInternal(const gfx::Rect& rect_old,
bool check_scroll) {
gfx::Rect rect = rect_old;
for (size_t i = 0; i < update_.ready_rects.size(); ++i) {
const gfx::Rect& existing_rect = update_.ready_rects[i].rect();
if (rect.Intersects(existing_rect)) {
rect.Union(existing_rect);
update_.ready_rects.erase(update_.ready_rects.begin() + i);
break;
}
}
bool add_paint = true;
for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
const gfx::Rect& existing_rect = update_.paint_rects[i];
if (existing_rect.Contains(rect)) {
add_paint = false;
}
if (rect.Intersects(existing_rect) || rect.SharesEdgeWith(existing_rect)) {
gfx::Rect combined_rect = gfx::UnionRects(rect, existing_rect);
update_.paint_rects.erase(update_.paint_rects.begin() + i);
InvalidateRectInternal(combined_rect, check_scroll);
add_paint = false;
}
}
if (add_paint) {
update_.paint_rects.push_back(rect);
}
if (check_scroll && !update_.scroll_rect.IsEmpty() &&
update_.scroll_rect.Intersects(rect)) {
InvalidateRectInternal(ScrollPaintRect(rect, update_.scroll_delta),
false);
}
}
}