Home | History | Annotate | Download | only in graphics
      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 
     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 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 
    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