• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "pdf/paint_aggregator.h"
6 
7 #include <algorithm>
8 
9 #include "base/logging.h"
10 
11 // ----------------------------------------------------------------------------
12 // ALGORITHM NOTES
13 //
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.
17 //
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.
21 //
22 // We only support scrolling along one axis at a time.  A diagonal scroll will
23 // therefore be treated as an invalidation.
24 // ----------------------------------------------------------------------------
25 
PaintUpdate()26 PaintAggregator::PaintUpdate::PaintUpdate() {
27 }
28 
~PaintUpdate()29 PaintAggregator::PaintUpdate::~PaintUpdate() {
30 }
31 
InternalPaintUpdate()32 PaintAggregator::InternalPaintUpdate::InternalPaintUpdate() :
33     synthesized_scroll_damage_rect_(false) {
34 }
35 
~InternalPaintUpdate()36 PaintAggregator::InternalPaintUpdate::~InternalPaintUpdate() {
37 }
38 
GetScrollDamage() const39 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()));
42 
43   pp::Rect damaged_rect;
44 
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());
51     if (dx > 0) {
52       damaged_rect.set_x(scroll_rect.x());
53       damaged_rect.set_width(dx);
54     } else {
55       damaged_rect.set_x(scroll_rect.right() + dx);
56       damaged_rect.set_width(-dx);
57     }
58   } else {
59     int32_t dy = scroll_delta.y();
60     damaged_rect.set_x(scroll_rect.x());
61     damaged_rect.set_width(scroll_rect.width());
62     if (dy > 0) {
63       damaged_rect.set_y(scroll_rect.y());
64       damaged_rect.set_height(dy);
65     } else {
66       damaged_rect.set_y(scroll_rect.bottom() + dy);
67       damaged_rect.set_height(-dy);
68     }
69   }
70 
71   // In case the scroll offset exceeds the width/height of the scroll rect
72   return scroll_rect.Intersect(damaged_rect);
73 }
74 
PaintAggregator()75 PaintAggregator::PaintAggregator() {
76 }
77 
HasPendingUpdate() const78 bool PaintAggregator::HasPendingUpdate() const {
79   return !update_.scroll_rect.IsEmpty() || !update_.paint_rects.empty();
80 }
81 
ClearPendingUpdate()82 void PaintAggregator::ClearPendingUpdate() {
83   update_ = InternalPaintUpdate();
84 }
85 
GetPendingUpdate()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.
89   PaintUpdate ret;
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;
93 
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);
101   }
102 
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]);
106 
107   return ret;
108 }
109 
SetIntermediateResults(const std::vector<ReadyRect> & ready,const std::vector<pp::Rect> & pending)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;
116 }
117 
GetReadyRects() const118 std::vector<PaintAggregator::ReadyRect> PaintAggregator::GetReadyRects() const {
119   return update_.ready_rects;
120 }
121 
InvalidateRect(const pp::Rect & rect)122 void PaintAggregator::InvalidateRect(const pp::Rect& rect) {
123   InvalidateRectInternal(rect, true);
124 }
125 
ScrollRect(const pp::Rect & clip_rect,const pp::Point & amount)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);
131     return;
132   }
133 
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);
137     return;
138   }
139 
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);
145     return;
146   }
147 
148   // The scroll rect is new or isn't changing (though the scroll amount may
149   // be changing).
150   update_.scroll_rect = clip_rect;
151   update_.scroll_delta += amount;
152 
153   // We might have just wiped out a pre-existing scroll.
154   if (update_.scroll_delta == pp::Point()) {
155     update_.scroll_rect = pp::Rect();
156     return;
157   }
158 
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]))
166       continue;
167 
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())
174         break;
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);
179     }
180 
181     update_.paint_rects[i] = ScrollPaintRect(intersection, amount);
182 
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);
186       i--;
187     }
188   }
189 
190   for (size_t i = 0; i < leftover_rects.size(); ++i)
191     InvalidateRectInternal(leftover_rects[i], false);
192 
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);
197     }
198   }
199 
200   if (update_.synthesized_scroll_damage_rect_) {
201     pp::Rect damage = update_.GetScrollDamage();
202     InvalidateRect(damage);
203   }
204 }
205 
ScrollPaintRect(const pp::Rect & paint_rect,const pp::Point & amount) const206 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);
211   return result;
212 }
213 
InvalidateScrollRect()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);
219 }
220 
InvalidateRectInternal(const pp::Rect & rect_old,bool check_scroll)221 void PaintAggregator::InvalidateRectInternal(const pp::Rect& rect_old,
222                                              bool check_scroll) {
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);
231       break;
232     }
233   }
234 
235   bool add_paint = true;
236 
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.
241       add_paint = false;
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);
247       add_paint = false;
248     }
249   }
250 
251   if (add_paint) {
252     // Add a non-overlapping paint.
253     update_.paint_rects.push_back(rect);
254   }
255 
256   // If the new paint overlaps with a scroll, then also invalidate the rect in
257   // its new position.
258   if (check_scroll &&
259       !update_.scroll_rect.IsEmpty() &&
260       update_.scroll_rect.Intersects(rect)) {
261     InvalidateRectInternal(ScrollPaintRect(rect, update_.scroll_delta), false);
262   }
263 }
264