OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/cpp/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 | |
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 | |
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 | |
45 PaintManager::~PaintManager() { | |
46 } | |
47 | |
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 | |
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 | |
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 | |
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 | |
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 | |
96 Size PaintManager::GetEffectiveSize() const { | |
97 return has_pending_resize_ ? pending_size_ : graphics_.size(); | |
98 } | |
99 | |
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 | |
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 | |
180 void PaintManager::OnFlushComplete(int32_t) { | |
181 PP_DCHECK(flush_pending_); | |
182 flush_pending_ = false; | |
183 | |
184 // If more paints were enqueued while we were waiting for the flush to | |
185 // complete, execute them now. | |
186 if (aggregator_.HasPendingUpdate()) | |
187 DoPaint(); | |
188 } | |
189 | |
190 void PaintManager::OnManualCallbackComplete(int32_t) { | |
191 PP_DCHECK(manual_callback_pending_); | |
192 manual_callback_pending_ = false; | |
193 | |
194 // Just because we have a manual callback doesn't mean there are actually any | |
195 // invalid regions. Even though we only schedule this callback when something | |
196 // is pending, a Flush callback could have come in before this callback was | |
197 // executed and that could have cleared the queue. | |
198 if (aggregator_.HasPendingUpdate() && !flush_pending_) | |
199 DoPaint(); | |
200 } | |
201 | |
202 | |
203 } // namespace pp | |
OLD | NEW |