1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "pdf/paint_aggregator.h"
9 #include "base/logging.h"
11 // ----------------------------------------------------------------------------
14 // We attempt to maintain a scroll rect in the presence of invalidations that
15 // are contained within the scroll rect. If an invalidation crosses a scroll
16 // rect, then we just treat the scroll rect as an invalidation rect.
18 // For invalidations performed prior to scrolling and contained within the
19 // scroll rect, we offset the invalidation rects to account for the fact that
20 // the consumer will perform scrolling before painting.
22 // We only support scrolling along one axis at a time. A diagonal scroll will
23 // therefore be treated as an invalidation.
24 // ----------------------------------------------------------------------------
26 PaintAggregator::PaintUpdate::PaintUpdate() {
29 PaintAggregator::PaintUpdate::~PaintUpdate() {
32 PaintAggregator::InternalPaintUpdate::InternalPaintUpdate() :
33 synthesized_scroll_damage_rect_(false) {
36 PaintAggregator::InternalPaintUpdate::~InternalPaintUpdate() {
39 pp::Rect
PaintAggregator::InternalPaintUpdate::GetScrollDamage() const {
40 // Should only be scrolling in one direction at a time.
41 DCHECK(!(scroll_delta
.x() && scroll_delta
.y()));
43 pp::Rect damaged_rect
;
45 // Compute the region we will expose by scrolling, and paint that into a
46 // shared memory section.
47 if (scroll_delta
.x()) {
48 int32_t dx
= scroll_delta
.x();
49 damaged_rect
.set_y(scroll_rect
.y());
50 damaged_rect
.set_height(scroll_rect
.height());
52 damaged_rect
.set_x(scroll_rect
.x());
53 damaged_rect
.set_width(dx
);
55 damaged_rect
.set_x(scroll_rect
.right() + dx
);
56 damaged_rect
.set_width(-dx
);
59 int32_t dy
= scroll_delta
.y();
60 damaged_rect
.set_x(scroll_rect
.x());
61 damaged_rect
.set_width(scroll_rect
.width());
63 damaged_rect
.set_y(scroll_rect
.y());
64 damaged_rect
.set_height(dy
);
66 damaged_rect
.set_y(scroll_rect
.bottom() + dy
);
67 damaged_rect
.set_height(-dy
);
71 // In case the scroll offset exceeds the width/height of the scroll rect
72 return scroll_rect
.Intersect(damaged_rect
);
75 PaintAggregator::PaintAggregator() {
78 bool PaintAggregator::HasPendingUpdate() const {
79 return !update_
.scroll_rect
.IsEmpty() || !update_
.paint_rects
.empty();
82 void PaintAggregator::ClearPendingUpdate() {
83 update_
= InternalPaintUpdate();
86 PaintAggregator::PaintUpdate
PaintAggregator::GetPendingUpdate() {
87 // Convert the internal paint update to the external one, which includes a
88 // bit more precomputed info for the caller.
90 ret
.scroll_delta
= update_
.scroll_delta
;
91 ret
.scroll_rect
= update_
.scroll_rect
;
92 ret
.has_scroll
= ret
.scroll_delta
.x() != 0 || ret
.scroll_delta
.y() != 0;
94 // Include the scroll damage (if any) in the paint rects.
95 // Code invalidates damaged rect here, it pick it up from the list of paint
96 // rects in the next block.
97 if (ret
.has_scroll
&& !update_
.synthesized_scroll_damage_rect_
) {
98 update_
.synthesized_scroll_damage_rect_
= true;
99 pp::Rect scroll_damage
= update_
.GetScrollDamage();
100 InvalidateRectInternal(scroll_damage
, false);
103 ret
.paint_rects
.reserve(update_
.paint_rects
.size() + 1);
104 for (size_t i
= 0; i
< update_
.paint_rects
.size(); i
++)
105 ret
.paint_rects
.push_back(update_
.paint_rects
[i
]);
110 void PaintAggregator::SetIntermediateResults(
111 const std::vector
<ReadyRect
>& ready
,
112 const std::vector
<pp::Rect
>& pending
) {
113 update_
.ready_rects
.insert(
114 update_
.ready_rects
.end(), ready
.begin(), ready
.end());
115 update_
.paint_rects
= pending
;
118 std::vector
<PaintAggregator::ReadyRect
> PaintAggregator::GetReadyRects() const {
119 return update_
.ready_rects
;
122 void PaintAggregator::InvalidateRect(const pp::Rect
& rect
) {
123 InvalidateRectInternal(rect
, true);
126 void PaintAggregator::ScrollRect(const pp::Rect
& clip_rect
,
127 const pp::Point
& amount
) {
128 // We only support scrolling along one axis at a time.
129 if (amount
.x() != 0 && amount
.y() != 0) {
130 InvalidateRect(clip_rect
);
134 // We can only scroll one rect at a time.
135 if (!update_
.scroll_rect
.IsEmpty() && update_
.scroll_rect
!= clip_rect
) {
136 InvalidateRect(clip_rect
);
140 // Again, we only support scrolling along one axis at a time. Make sure this
141 // update doesn't scroll on a different axis than any existing one.
142 if ((amount
.x() && update_
.scroll_delta
.y()) ||
143 (amount
.y() && update_
.scroll_delta
.x())) {
144 InvalidateRect(clip_rect
);
148 // The scroll rect is new or isn't changing (though the scroll amount may
150 update_
.scroll_rect
= clip_rect
;
151 update_
.scroll_delta
+= amount
;
153 // We might have just wiped out a pre-existing scroll.
154 if (update_
.scroll_delta
== pp::Point()) {
155 update_
.scroll_rect
= pp::Rect();
159 // Adjust any paint rects that intersect the scroll. For the portion of the
160 // paint that is inside the scroll area, move it by the scroll amount and
161 // replace the existing paint with it. For the portion (if any) that is
162 // outside the scroll, just invalidate it.
163 std::vector
<pp::Rect
> leftover_rects
;
164 for (size_t i
= 0; i
< update_
.paint_rects
.size(); ++i
) {
165 if (!update_
.scroll_rect
.Intersects(update_
.paint_rects
[i
]))
168 pp::Rect intersection
=
169 update_
.paint_rects
[i
].Intersect(update_
.scroll_rect
);
170 pp::Rect rect
= update_
.paint_rects
[i
];
171 while (!rect
.IsEmpty()) {
172 pp::Rect leftover
= rect
.Subtract(intersection
);
173 if (leftover
.IsEmpty())
175 // Don't want to call InvalidateRectInternal now since it'll modify
176 // update_.paint_rects, so keep track of this and do it below.
177 leftover_rects
.push_back(leftover
);
178 rect
= rect
.Subtract(leftover
);
181 update_
.paint_rects
[i
] = ScrollPaintRect(intersection
, amount
);
183 // The rect may have been scrolled out of view.
184 if (update_
.paint_rects
[i
].IsEmpty()) {
185 update_
.paint_rects
.erase(update_
.paint_rects
.begin() + i
);
190 for (size_t i
= 0; i
< leftover_rects
.size(); ++i
)
191 InvalidateRectInternal(leftover_rects
[i
], false);
193 for (size_t i
= 0; i
< update_
.ready_rects
.size(); ++i
) {
194 if (update_
.scroll_rect
.Contains(update_
.ready_rects
[i
].rect
)) {
195 update_
.ready_rects
[i
].rect
=
196 ScrollPaintRect(update_
.ready_rects
[i
].rect
, amount
);
200 if (update_
.synthesized_scroll_damage_rect_
) {
201 pp::Rect damage
= update_
.GetScrollDamage();
202 InvalidateRect(damage
);
206 pp::Rect
PaintAggregator::ScrollPaintRect(const pp::Rect
& paint_rect
,
207 const pp::Point
& amount
) const {
208 pp::Rect result
= paint_rect
;
209 result
.Offset(amount
);
210 result
= update_
.scroll_rect
.Intersect(result
);
214 void PaintAggregator::InvalidateScrollRect() {
215 pp::Rect scroll_rect
= update_
.scroll_rect
;
216 update_
.scroll_rect
= pp::Rect();
217 update_
.scroll_delta
= pp::Point();
218 InvalidateRect(scroll_rect
);
221 void PaintAggregator::InvalidateRectInternal(const pp::Rect
& rect_old
,
223 pp::Rect rect
= rect_old
;
224 // Check if any rects that are ready to be painted overlap.
225 for (size_t i
= 0; i
< update_
.ready_rects
.size(); ++i
) {
226 const pp::Rect
& existing_rect
= update_
.ready_rects
[i
].rect
;
227 if (rect
.Intersects(existing_rect
)) {
228 // Re-invalidate in case the union intersects other paint rects.
229 rect
= existing_rect
.Union(rect
);
230 update_
.ready_rects
.erase(update_
.ready_rects
.begin() + i
);
235 bool add_paint
= true;
237 // Combine overlapping paints using smallest bounding box.
238 for (size_t i
= 0; i
< update_
.paint_rects
.size(); ++i
) {
239 const pp::Rect
& existing_rect
= update_
.paint_rects
[i
];
240 if (existing_rect
.Contains(rect
)) // Optimize out redundancy.
242 if (rect
.Intersects(existing_rect
) || rect
.SharesEdgeWith(existing_rect
)) {
243 // Re-invalidate in case the union intersects other paint rects.
244 pp::Rect combined_rect
= existing_rect
.Union(rect
);
245 update_
.paint_rects
.erase(update_
.paint_rects
.begin() + i
);
246 InvalidateRectInternal(combined_rect
, check_scroll
);
252 // Add a non-overlapping paint.
253 update_
.paint_rects
.push_back(rect
);
256 // If the new paint overlaps with a scroll, then also invalidate the rect in
259 !update_
.scroll_rect
.IsEmpty() &&
260 update_
.scroll_rect
.Intersects(rect
)) {
261 InvalidateRectInternal(ScrollPaintRect(rect
, update_
.scroll_delta
), false);