• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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