1 // Copyright (c) 2012 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 "ppapi/utility/graphics/paint_manager.h"
6
7 #include "ppapi/c/pp_errors.h"
8 #include "ppapi/cpp/instance.h"
9 #include "ppapi/cpp/logging.h"
10 #include "ppapi/cpp/module.h"
11
12 namespace pp {
13
PaintManager()14 PaintManager::PaintManager()
15 : instance_(NULL),
16 client_(NULL),
17 is_always_opaque_(false),
18 callback_factory_(NULL),
19 manual_callback_pending_(false),
20 flush_pending_(false),
21 has_pending_resize_(false) {
22 // Set the callback object outside of the initializer list to avoid a
23 // compiler warning about using "this" in an initializer list.
24 callback_factory_.Initialize(this);
25 }
26
PaintManager(Instance * instance,Client * client,bool is_always_opaque)27 PaintManager::PaintManager(Instance* instance,
28 Client* client,
29 bool is_always_opaque)
30 : instance_(instance),
31 client_(client),
32 is_always_opaque_(is_always_opaque),
33 callback_factory_(NULL),
34 manual_callback_pending_(false),
35 flush_pending_(false),
36 has_pending_resize_(false) {
37 // Set the callback object outside of the initializer list to avoid a
38 // compiler warning about using "this" in an initializer list.
39 callback_factory_.Initialize(this);
40
41 // You can not use a NULL client pointer.
42 PP_DCHECK(client);
43 }
44
~PaintManager()45 PaintManager::~PaintManager() {
46 }
47
Initialize(Instance * instance,Client * client,bool is_always_opaque)48 void PaintManager::Initialize(Instance* instance,
49 Client* client,
50 bool is_always_opaque) {
51 PP_DCHECK(!instance_ && !client_); // Can't initialize twice.
52 instance_ = instance;
53 client_ = client;
54 is_always_opaque_ = is_always_opaque;
55 }
56
SetSize(const Size & new_size)57 void PaintManager::SetSize(const Size& new_size) {
58 if (GetEffectiveSize() == new_size)
59 return;
60
61 has_pending_resize_ = true;
62 pending_size_ = new_size;
63
64 Invalidate();
65 }
66
Invalidate()67 void PaintManager::Invalidate() {
68 // You must call SetSize before using.
69 PP_DCHECK(!graphics_.is_null() || has_pending_resize_);
70
71 EnsureCallbackPending();
72 aggregator_.InvalidateRect(Rect(GetEffectiveSize()));
73 }
74
InvalidateRect(const Rect & rect)75 void PaintManager::InvalidateRect(const Rect& rect) {
76 // You must call SetSize before using.
77 PP_DCHECK(!graphics_.is_null() || has_pending_resize_);
78
79 // Clip the rect to the device area.
80 Rect clipped_rect = rect.Intersect(Rect(GetEffectiveSize()));
81 if (clipped_rect.IsEmpty())
82 return; // Nothing to do.
83
84 EnsureCallbackPending();
85 aggregator_.InvalidateRect(clipped_rect);
86 }
87
ScrollRect(const Rect & clip_rect,const Point & amount)88 void PaintManager::ScrollRect(const Rect& clip_rect, const Point& amount) {
89 // You must call SetSize before using.
90 PP_DCHECK(!graphics_.is_null() || has_pending_resize_);
91
92 EnsureCallbackPending();
93 aggregator_.ScrollRect(clip_rect, amount);
94 }
95
GetEffectiveSize() const96 Size PaintManager::GetEffectiveSize() const {
97 return has_pending_resize_ ? pending_size_ : graphics_.size();
98 }
99
EnsureCallbackPending()100 void PaintManager::EnsureCallbackPending() {
101 // The best way for us to do the next update is to get a notification that
102 // a previous one has completed. So if we're already waiting for one, we
103 // don't have to do anything differently now.
104 if (flush_pending_)
105 return;
106
107 // If no flush is pending, we need to do a manual call to get back to the
108 // main thread. We may have one already pending, or we may need to schedule.
109 if (manual_callback_pending_)
110 return;
111
112 Module::Get()->core()->CallOnMainThread(
113 0,
114 callback_factory_.NewCallback(&PaintManager::OnManualCallbackComplete),
115 0);
116 manual_callback_pending_ = true;
117 }
118
DoPaint()119 void PaintManager::DoPaint() {
120 PP_DCHECK(aggregator_.HasPendingUpdate());
121
122 // Make a copy of the pending update and clear the pending update flag before
123 // actually painting. A plugin might cause invalidates in its Paint code, and
124 // we want those to go to the *next* paint.
125 PaintAggregator::PaintUpdate update = aggregator_.GetPendingUpdate();
126 aggregator_.ClearPendingUpdate();
127
128 // Apply any pending resize. Setting the graphics to this class must happen
129 // before asking the plugin to paint in case it requests the Graphics2D during
130 // painting. However, the bind must not happen until afterward since we don't
131 // want to have an unpainted device bound. The needs_binding flag tells us
132 // whether to do this later.
133 bool needs_binding = false;
134 if (has_pending_resize_) {
135 graphics_ = Graphics2D(instance_, pending_size_, is_always_opaque_);
136 needs_binding = true;
137
138 // Since we're binding a new one, all of the callbacks have been canceled.
139 manual_callback_pending_ = false;
140 flush_pending_ = false;
141 callback_factory_.CancelAll();
142
143 // This must be cleared before calling into the plugin since it may do
144 // additional invalidation or sizing operations.
145 has_pending_resize_ = false;
146 pending_size_ = Size();
147 }
148
149 // Apply any scroll before asking the client to paint.
150 if (update.has_scroll)
151 graphics_.Scroll(update.scroll_rect, update.scroll_delta);
152
153 if (client_->OnPaint(graphics_, update.paint_rects, update.paint_bounds)) {
154 // Something was painted, schedule a flush.
155 int32_t result = graphics_.Flush(
156 callback_factory_.NewOptionalCallback(&PaintManager::OnFlushComplete));
157
158 // If you trigger this assertion, then your plugin has called Flush()
159 // manually. When using the PaintManager, you should not call Flush, it
160 // will handle that for you because it needs to know when it can do the
161 // next paint by implementing the flush callback.
162 //
163 // Another possible cause of this assertion is re-using devices. If you
164 // use one device, swap it with another, then swap it back, we won't know
165 // that we've already scheduled a Flush on the first device. It's best to
166 // not re-use devices in this way.
167 PP_DCHECK(result != PP_ERROR_INPROGRESS);
168
169 if (result == PP_OK_COMPLETIONPENDING) {
170 flush_pending_ = true;
171 } else {
172 PP_DCHECK(result == PP_OK); // Catch all other errors in debug mode.
173 }
174 }
175
176 if (needs_binding)
177 instance_->BindGraphics(graphics_);
178 }
179
OnFlushComplete(int32_t result)180 void PaintManager::OnFlushComplete(int32_t result) {
181 PP_DCHECK(flush_pending_);
182 flush_pending_ = false;
183
184 // Theoretically this shouldn't fail unless we've made an error, but don't
185 // want to call into the client code to do more painting if something bad
186 // did happen.
187 if (result != PP_OK)
188 return;
189
190 // If more paints were enqueued while we were waiting for the flush to
191 // complete, execute them now.
192 if (aggregator_.HasPendingUpdate())
193 DoPaint();
194 }
195
OnManualCallbackComplete(int32_t)196 void PaintManager::OnManualCallbackComplete(int32_t) {
197 PP_DCHECK(manual_callback_pending_);
198 manual_callback_pending_ = false;
199
200 // Just because we have a manual callback doesn't mean there are actually any
201 // invalid regions. Even though we only schedule this callback when something
202 // is pending, a Flush callback could have come in before this callback was
203 // executed and that could have cleared the queue.
204 if (aggregator_.HasPendingUpdate() && !flush_pending_)
205 DoPaint();
206 }
207
208
209 } // namespace pp
210