Upload upstream chromium 76.0.3809.146
[platform/framework/web/chromium-efl.git] / pdf / paint_aggregator.cc
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 <stddef.h>
8 #include <stdint.h>
9
10 #include <algorithm>
11
12 #include "base/logging.h"
13
14 namespace {
15
16 bool IsNegative(int32_t num) {
17   return num < 0;
18 }
19
20 }  // namespace
21
22 // ----------------------------------------------------------------------------
23 // ALGORITHM NOTES
24 //
25 // We attempt to maintain a scroll rect in the presence of invalidations that
26 // are contained within the scroll rect.  If an invalidation crosses a scroll
27 // rect, then we just treat the scroll rect as an invalidation rect.
28 //
29 // For invalidations performed prior to scrolling and contained within the
30 // scroll rect, we offset the invalidation rects to account for the fact that
31 // the consumer will perform scrolling before painting.
32 //
33 // We only support scrolling along one axis at a time.  A diagonal scroll will
34 // therefore be treated as an invalidation.
35 // ----------------------------------------------------------------------------
36
37 PaintAggregator::PaintUpdate::PaintUpdate() = default;
38
39 PaintAggregator::PaintUpdate::PaintUpdate(const PaintUpdate& that) = default;
40
41 PaintAggregator::PaintUpdate::~PaintUpdate() = default;
42
43 PaintAggregator::InternalPaintUpdate::InternalPaintUpdate()
44     : synthesized_scroll_damage_rect_(false) {}
45
46 PaintAggregator::InternalPaintUpdate::~InternalPaintUpdate() = default;
47
48 pp::Rect PaintAggregator::InternalPaintUpdate::GetScrollDamage() const {
49   // Should only be scrolling in one direction at a time.
50   DCHECK(!(scroll_delta.x() && scroll_delta.y()));
51
52   pp::Rect damaged_rect;
53
54   // Compute the region we will expose by scrolling, and paint that into a
55   // shared memory section.
56   if (scroll_delta.x()) {
57     int32_t dx = scroll_delta.x();
58     damaged_rect.set_y(scroll_rect.y());
59     damaged_rect.set_height(scroll_rect.height());
60     if (dx > 0) {
61       damaged_rect.set_x(scroll_rect.x());
62       damaged_rect.set_width(dx);
63     } else {
64       damaged_rect.set_x(scroll_rect.right() + dx);
65       damaged_rect.set_width(-dx);
66     }
67   } else {
68     int32_t dy = scroll_delta.y();
69     damaged_rect.set_x(scroll_rect.x());
70     damaged_rect.set_width(scroll_rect.width());
71     if (dy > 0) {
72       damaged_rect.set_y(scroll_rect.y());
73       damaged_rect.set_height(dy);
74     } else {
75       damaged_rect.set_y(scroll_rect.bottom() + dy);
76       damaged_rect.set_height(-dy);
77     }
78   }
79
80   // In case the scroll offset exceeds the width/height of the scroll rect
81   return scroll_rect.Intersect(damaged_rect);
82 }
83
84 PaintAggregator::PaintAggregator() = default;
85
86 bool PaintAggregator::HasPendingUpdate() const {
87   return !update_.scroll_rect.IsEmpty() || !update_.paint_rects.empty();
88 }
89
90 void PaintAggregator::ClearPendingUpdate() {
91   update_ = InternalPaintUpdate();
92 }
93
94 PaintAggregator::PaintUpdate PaintAggregator::GetPendingUpdate() {
95   // Convert the internal paint update to the external one, which includes a
96   // bit more precomputed info for the caller.
97   PaintUpdate ret;
98   ret.scroll_delta = update_.scroll_delta;
99   ret.scroll_rect = update_.scroll_rect;
100   ret.has_scroll = ret.scroll_delta.x() != 0 || ret.scroll_delta.y() != 0;
101
102   // Include the scroll damage (if any) in the paint rects.
103   // Code invalidates damaged rect here, it pick it up from the list of paint
104   // rects in the next block.
105   if (ret.has_scroll && !update_.synthesized_scroll_damage_rect_) {
106     update_.synthesized_scroll_damage_rect_ = true;
107     pp::Rect scroll_damage = update_.GetScrollDamage();
108     InvalidateRectInternal(scroll_damage, false);
109   }
110
111   ret.paint_rects.reserve(update_.paint_rects.size() + 1);
112   ret.paint_rects.insert(ret.paint_rects.end(), update_.paint_rects.begin(),
113                          update_.paint_rects.end());
114
115   return ret;
116 }
117
118 void PaintAggregator::SetIntermediateResults(
119     const std::vector<ReadyRect>& ready,
120     const std::vector<pp::Rect>& pending) {
121   update_.ready_rects.insert(update_.ready_rects.end(), ready.begin(),
122                              ready.end());
123   update_.paint_rects = pending;
124 }
125
126 std::vector<PaintAggregator::ReadyRect> PaintAggregator::GetReadyRects() const {
127   return update_.ready_rects;
128 }
129
130 void PaintAggregator::InvalidateRect(const pp::Rect& rect) {
131   InvalidateRectInternal(rect, true);
132 }
133
134 void PaintAggregator::ScrollRect(const pp::Rect& clip_rect,
135                                  const pp::Point& amount) {
136   // We only support scrolling along one axis at a time.
137   if (amount.x() != 0 && amount.y() != 0) {
138     InvalidateRect(clip_rect);
139     return;
140   }
141
142   // We can only scroll one rect at a time.
143   if (!update_.scroll_rect.IsEmpty() && update_.scroll_rect != clip_rect) {
144     InvalidateRect(clip_rect);
145     return;
146   }
147
148   // Again, we only support scrolling along one axis at a time.  Make sure this
149   // update doesn't scroll on a different axis than any existing one.
150   if ((amount.x() && update_.scroll_delta.y()) ||
151       (amount.y() && update_.scroll_delta.x())) {
152     InvalidateRect(clip_rect);
153     return;
154   }
155
156   // If we scroll in a reverse direction to the direction we originally scrolled
157   // and there were invalidations that happened in-between we may end up
158   // incorrectly clipping the invalidated rects (see crbug.com/488390). This bug
159   // doesn't exist in the original implementation
160   // (ppapi/utility/graphics/paint_aggregator.cc) which uses a different method
161   // of handling invalidations that occur after a scroll. The problem is that
162   // when we scroll the invalidated region, we clip it to the scroll rect. This
163   // can cause us to lose information about what the invalidated region was if
164   // it gets scrolled back into view. We either need to not do this clipping or
165   // disallow combining scrolls that occur in different directions with
166   // invalidations that happen in-between. This code really needs some tests...
167   if (!update_.paint_rects.empty()) {
168     if (IsNegative(amount.x()) != IsNegative(update_.scroll_delta.x()) ||
169         IsNegative(amount.y()) != IsNegative(update_.scroll_delta.y())) {
170       InvalidateRect(clip_rect);
171       return;
172     }
173   }
174
175   // The scroll rect is new or isn't changing (though the scroll amount may
176   // be changing).
177   update_.scroll_rect = clip_rect;
178   update_.scroll_delta += amount;
179
180   // We might have just wiped out a pre-existing scroll.
181   if (update_.scroll_delta == pp::Point()) {
182     update_.scroll_rect = pp::Rect();
183     return;
184   }
185
186   // Adjust any paint rects that intersect the scroll. For the portion of the
187   // paint that is inside the scroll area, move it by the scroll amount and
188   // replace the existing paint with it. For the portion (if any) that is
189   // outside the scroll, just invalidate it.
190   std::vector<pp::Rect> leftover_rects;
191   for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
192     if (!update_.scroll_rect.Intersects(update_.paint_rects[i]))
193       continue;
194
195     pp::Rect intersection =
196         update_.paint_rects[i].Intersect(update_.scroll_rect);
197     pp::Rect rect = update_.paint_rects[i];
198     while (!rect.IsEmpty()) {
199       pp::Rect leftover = rect.Subtract(intersection);
200       if (leftover.IsEmpty())
201         break;
202       // Don't want to call InvalidateRectInternal now since it'll modify
203       // update_.paint_rects, so keep track of this and do it below.
204       leftover_rects.push_back(leftover);
205       rect = rect.Subtract(leftover);
206     }
207
208     update_.paint_rects[i] = ScrollPaintRect(intersection, amount);
209
210     // The rect may have been scrolled out of view.
211     if (update_.paint_rects[i].IsEmpty()) {
212       update_.paint_rects.erase(update_.paint_rects.begin() + i);
213       i--;
214     }
215   }
216
217   for (const auto& leftover_rect : leftover_rects)
218     InvalidateRectInternal(leftover_rect, false);
219
220   for (auto& update_rect : update_.ready_rects) {
221     if (update_.scroll_rect.Contains(update_rect.rect))
222       update_rect.rect = ScrollPaintRect(update_rect.rect, amount);
223   }
224
225   if (update_.synthesized_scroll_damage_rect_) {
226     pp::Rect damage = update_.GetScrollDamage();
227     InvalidateRect(damage);
228   }
229 }
230
231 pp::Rect PaintAggregator::ScrollPaintRect(const pp::Rect& paint_rect,
232                                           const pp::Point& amount) const {
233   pp::Rect result = paint_rect;
234   result.Offset(amount);
235   result = update_.scroll_rect.Intersect(result);
236   return result;
237 }
238
239 void PaintAggregator::InvalidateScrollRect() {
240   pp::Rect scroll_rect = update_.scroll_rect;
241   update_.scroll_rect = pp::Rect();
242   update_.scroll_delta = pp::Point();
243   InvalidateRect(scroll_rect);
244 }
245
246 void PaintAggregator::InvalidateRectInternal(const pp::Rect& rect_old,
247                                              bool check_scroll) {
248   pp::Rect rect = rect_old;
249   // Check if any rects that are ready to be painted overlap.
250   for (size_t i = 0; i < update_.ready_rects.size(); ++i) {
251     const pp::Rect& existing_rect = update_.ready_rects[i].rect;
252     if (rect.Intersects(existing_rect)) {
253       // Re-invalidate in case the union intersects other paint rects.
254       rect = existing_rect.Union(rect);
255       update_.ready_rects.erase(update_.ready_rects.begin() + i);
256       break;
257     }
258   }
259
260   bool add_paint = true;
261
262   // Combine overlapping paints using smallest bounding box.
263   for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
264     const pp::Rect& existing_rect = update_.paint_rects[i];
265     if (existing_rect.Contains(rect))  // Optimize out redundancy.
266       add_paint = false;
267     if (rect.Intersects(existing_rect) || rect.SharesEdgeWith(existing_rect)) {
268       // Re-invalidate in case the union intersects other paint rects.
269       pp::Rect combined_rect = existing_rect.Union(rect);
270       update_.paint_rects.erase(update_.paint_rects.begin() + i);
271       InvalidateRectInternal(combined_rect, check_scroll);
272       add_paint = false;
273     }
274   }
275
276   if (add_paint) {
277     // Add a non-overlapping paint.
278     update_.paint_rects.push_back(rect);
279   }
280
281   // If the new paint overlaps with a scroll, then also invalidate the rect in
282   // its new position.
283   if (check_scroll && !update_.scroll_rect.IsEmpty() &&
284       update_.scroll_rect.Intersects(rect)) {
285     InvalidateRectInternal(ScrollPaintRect(rect, update_.scroll_delta), false);
286   }
287 }