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_manager.h"
9 #include "base/logging.h"
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/cpp/instance.h"
12 #include "ppapi/cpp/module.h"
14 PaintManager::PaintManager(pp::Instance
* instance
,
16 bool is_always_opaque
)
17 : instance_(instance
),
19 is_always_opaque_(is_always_opaque
),
20 callback_factory_(NULL
),
21 manual_callback_pending_(false),
22 flush_pending_(false),
23 has_pending_resize_(false),
24 graphics_need_to_be_bound_(false),
25 pending_device_scale_(1.0),
29 view_size_changed_waiting_for_paint_(false) {
30 // Set the callback object outside of the initializer list to avoid a
31 // compiler warning about using "this" in an initializer list.
32 callback_factory_
.Initialize(this);
34 // You can not use a NULL client pointer.
38 PaintManager::~PaintManager() {
42 pp::Size
PaintManager::GetNewContextSize(const pp::Size
& current_context_size
,
43 const pp::Size
& plugin_size
) {
44 // The amount of additional space in pixels to allocate to the right/bottom of
46 const int kBufferSize
= 50;
48 // Default to returning the same size.
49 pp::Size result
= current_context_size
;
51 // The minimum size of the plugin before resizing the context to ensure we
52 // aren't wasting too much memory. We deduct twice the kBufferSize from the
53 // current context size which gives a threshhold that is kBufferSize below
54 // the plugin size when the context size was last computed.
56 std::max(current_context_size
.width() - 2 * kBufferSize
, 0),
57 std::max(current_context_size
.height() - 2 * kBufferSize
, 0));
59 // If the plugin size is bigger than the current context size, we need to
60 // resize the context. If the plugin size is smaller than the current
61 // context size by a given threshhold then resize the context so that we
62 // aren't wasting too much memory.
63 if (plugin_size
.width() > current_context_size
.width() ||
64 plugin_size
.height() > current_context_size
.height() ||
65 plugin_size
.width() < min_size
.width() ||
66 plugin_size
.height() < min_size
.height()) {
67 // Create a larger context than needed so that if we only resize by a
68 // small margin, we don't need a new context.
69 result
= pp::Size(plugin_size
.width() + kBufferSize
,
70 plugin_size
.height() + kBufferSize
);
76 void PaintManager::Initialize(pp::Instance
* instance
,
78 bool is_always_opaque
) {
79 DCHECK(!instance_
&& !client_
); // Can't initialize twice.
82 is_always_opaque_
= is_always_opaque
;
85 void PaintManager::SetSize(const pp::Size
& new_size
, float device_scale
) {
86 if (GetEffectiveSize() == new_size
&&
87 GetEffectiveDeviceScale() == device_scale
)
90 has_pending_resize_
= true;
91 pending_size_
= new_size
;
92 pending_device_scale_
= device_scale
;
94 view_size_changed_waiting_for_paint_
= true;
99 void PaintManager::Invalidate() {
100 if (graphics_
.is_null() && !has_pending_resize_
)
103 EnsureCallbackPending();
104 aggregator_
.InvalidateRect(pp::Rect(GetEffectiveSize()));
107 void PaintManager::InvalidateRect(const pp::Rect
& rect
) {
110 if (graphics_
.is_null() && !has_pending_resize_
)
113 // Clip the rect to the device area.
114 pp::Rect clipped_rect
= rect
.Intersect(pp::Rect(GetEffectiveSize()));
115 if (clipped_rect
.IsEmpty())
116 return; // Nothing to do.
118 EnsureCallbackPending();
119 aggregator_
.InvalidateRect(clipped_rect
);
122 void PaintManager::ScrollRect(const pp::Rect
& clip_rect
,
123 const pp::Point
& amount
) {
126 if (graphics_
.is_null() && !has_pending_resize_
)
129 EnsureCallbackPending();
131 aggregator_
.ScrollRect(clip_rect
, amount
);
134 pp::Size
PaintManager::GetEffectiveSize() const {
135 return has_pending_resize_
? pending_size_
: plugin_size_
;
138 float PaintManager::GetEffectiveDeviceScale() const {
139 return has_pending_resize_
? pending_device_scale_
: device_scale_
;
142 void PaintManager::EnsureCallbackPending() {
143 // The best way for us to do the next update is to get a notification that
144 // a previous one has completed. So if we're already waiting for one, we
145 // don't have to do anything differently now.
149 // If no flush is pending, we need to do a manual call to get back to the
150 // main thread. We may have one already pending, or we may need to schedule.
151 if (manual_callback_pending_
)
154 pp::Module::Get()->core()->CallOnMainThread(
156 callback_factory_
.NewCallback(&PaintManager::OnManualCallbackComplete
),
158 manual_callback_pending_
= true;
161 void PaintManager::DoPaint() {
164 std::vector
<ReadyRect
> ready
;
165 std::vector
<pp::Rect
> pending
;
167 DCHECK(aggregator_
.HasPendingUpdate());
169 // Apply any pending resize. Setting the graphics to this class must happen
170 // before asking the plugin to paint in case it requests invalides or resizes.
171 // However, the bind must not happen until afterward since we don't want to
172 // have an unpainted device bound. The needs_binding flag tells us whether to
174 if (has_pending_resize_
) {
175 plugin_size_
= pending_size_
;
176 // Only create a new graphics context if the current context isn't big
177 // enough or if it is far too big. This avoids creating a new context if
178 // we only resize by a small amount.
179 pp::Size new_size
= GetNewContextSize(graphics_
.size(), pending_size_
);
180 if (graphics_
.size() != new_size
) {
181 graphics_
= pp::Graphics2D(instance_
, new_size
, is_always_opaque_
);
182 graphics_need_to_be_bound_
= true;
184 // Since we're binding a new one, all of the callbacks have been canceled.
185 manual_callback_pending_
= false;
186 flush_pending_
= false;
187 callback_factory_
.CancelAll();
190 if (pending_device_scale_
!= 1.0)
191 graphics_
.SetScale(1.0 / pending_device_scale_
);
192 device_scale_
= pending_device_scale_
;
194 // This must be cleared before calling into the plugin since it may do
195 // additional invalidation or sizing operations.
196 has_pending_resize_
= false;
197 pending_size_
= pp::Size();
200 PaintAggregator::PaintUpdate update
= aggregator_
.GetPendingUpdate();
201 client_
->OnPaint(update
.paint_rects
, &ready
, &pending
);
203 if (ready
.empty() && pending
.empty()) {
205 return; // Nothing was painted, don't schedule a flush.
208 std::vector
<PaintAggregator::ReadyRect
> ready_now
;
209 if (pending
.empty()) {
210 std::vector
<PaintAggregator::ReadyRect
> temp_ready
;
211 for (size_t i
= 0; i
< ready
.size(); ++i
)
212 temp_ready
.push_back(ready
[i
]);
213 aggregator_
.SetIntermediateResults(temp_ready
, pending
);
214 ready_now
= aggregator_
.GetReadyRects();
215 aggregator_
.ClearPendingUpdate();
217 // Apply any scroll first.
218 if (update
.has_scroll
)
219 graphics_
.Scroll(update
.scroll_rect
, update
.scroll_delta
);
221 view_size_changed_waiting_for_paint_
= false;
223 std::vector
<PaintAggregator::ReadyRect
> ready_later
;
224 for (size_t i
= 0; i
< ready
.size(); ++i
) {
225 // Don't flush any part (i.e. scrollbars) if we're resizing the browser,
226 // as that'll lead to flashes. Until we flush, the browser will use the
227 // previous image, but if we flush, it'll revert to using the blank image.
228 // We make an exception for the first paint since we want to show the
229 // default background color instead of the pepper default of black.
230 if (ready
[i
].flush_now
&&
231 (!view_size_changed_waiting_for_paint_
|| first_paint_
)) {
232 ready_now
.push_back(ready
[i
]);
234 ready_later
.push_back(ready
[i
]);
237 // Take the rectangles, except the ones that need to be flushed right away,
238 // and save them so that everything is flushed at once.
239 aggregator_
.SetIntermediateResults(ready_later
, pending
);
241 if (ready_now
.empty()) {
243 EnsureCallbackPending();
248 for (size_t i
= 0; i
< ready_now
.size(); ++i
) {
249 graphics_
.PaintImageData(
250 ready_now
[i
].image_data
, ready_now
[i
].offset
, ready_now
[i
].rect
);
253 int32_t result
= graphics_
.Flush(
254 callback_factory_
.NewCallback(&PaintManager::OnFlushComplete
));
256 // If you trigger this assertion, then your plugin has called Flush()
257 // manually. When using the PaintManager, you should not call Flush, it will
258 // handle that for you because it needs to know when it can do the next paint
259 // by implementing the flush callback.
261 // Another possible cause of this assertion is re-using devices. If you
262 // use one device, swap it with another, then swap it back, we won't know
263 // that we've already scheduled a Flush on the first device. It's best to not
264 // re-use devices in this way.
265 DCHECK(result
!= PP_ERROR_INPROGRESS
);
267 if (result
== PP_OK_COMPLETIONPENDING
) {
268 flush_pending_
= true;
270 DCHECK(result
== PP_OK
); // Catch all other errors in debug mode.
274 first_paint_
= false;
276 if (graphics_need_to_be_bound_
) {
277 instance_
->BindGraphics(graphics_
);
278 graphics_need_to_be_bound_
= false;
282 void PaintManager::OnFlushComplete(int32_t) {
283 DCHECK(flush_pending_
);
284 flush_pending_
= false;
286 // If more paints were enqueued while we were waiting for the flush to
287 // complete, execute them now.
288 if (aggregator_
.HasPendingUpdate())
292 void PaintManager::OnManualCallbackComplete(int32_t) {
293 DCHECK(manual_callback_pending_
);
294 manual_callback_pending_
= false;
296 // Just because we have a manual callback doesn't mean there are actually any
297 // invalid regions. Even though we only schedule this callback when something
298 // is pending, a Flush callback could have come in before this callback was
299 // executed and that could have cleared the queue.
300 if (aggregator_
.HasPendingUpdate())