Home | History | Annotate | Download | only in pdf
      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.
      4 
      5 #include "pdf/paint_manager.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 #include "ppapi/c/pp_errors.h"
     11 #include "ppapi/cpp/instance.h"
     12 #include "ppapi/cpp/module.h"
     13 
     14 PaintManager::PaintManager(pp::Instance* instance,
     15                            Client* client,
     16                            bool is_always_opaque)
     17     : instance_(instance),
     18       client_(client),
     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),
     26       device_scale_(1.0),
     27       in_paint_(false),
     28       first_paint_(true),
     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);
     33 
     34   // You can not use a NULL client pointer.
     35   DCHECK(client);
     36 }
     37 
     38 PaintManager::~PaintManager() {
     39 }
     40 
     41 // static
     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
     45   // the context.
     46   const int kBufferSize = 50;
     47 
     48   // Default to returning the same size.
     49   pp::Size result = current_context_size;
     50 
     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.
     55   pp::Size min_size(
     56       std::max(current_context_size.width() - 2 * kBufferSize, 0),
     57       std::max(current_context_size.height() - 2 * kBufferSize, 0));
     58 
     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);
     71   }
     72 
     73   return result;
     74 }
     75 
     76 void PaintManager::Initialize(pp::Instance* instance,
     77                               Client* client,
     78                               bool is_always_opaque) {
     79   DCHECK(!instance_ && !client_);  // Can't initialize twice.
     80   instance_ = instance;
     81   client_ = client;
     82   is_always_opaque_ = is_always_opaque;
     83 }
     84 
     85 void PaintManager::SetSize(const pp::Size& new_size, float device_scale) {
     86   if (GetEffectiveSize() == new_size &&
     87       GetEffectiveDeviceScale() == device_scale)
     88     return;
     89 
     90   has_pending_resize_ = true;
     91   pending_size_ = new_size;
     92   pending_device_scale_ = device_scale;
     93 
     94   view_size_changed_waiting_for_paint_ = true;
     95 
     96   Invalidate();
     97 }
     98 
     99 void PaintManager::Invalidate() {
    100   // You must call SetSize before using.
    101   DCHECK(!graphics_.is_null() || has_pending_resize_);
    102 
    103   EnsureCallbackPending();
    104   aggregator_.InvalidateRect(pp::Rect(GetEffectiveSize()));
    105 }
    106 
    107 void PaintManager::InvalidateRect(const pp::Rect& rect) {
    108   DCHECK(!in_paint_);
    109 
    110   // You must call SetSize before using.
    111   DCHECK(!graphics_.is_null() || has_pending_resize_);
    112 
    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.
    117 
    118   EnsureCallbackPending();
    119   aggregator_.InvalidateRect(clipped_rect);
    120 }
    121 
    122 void PaintManager::ScrollRect(const pp::Rect& clip_rect,
    123                               const pp::Point& amount) {
    124   DCHECK(!in_paint_);
    125 
    126   // You must call SetSize before using.
    127   DCHECK(!graphics_.is_null() || has_pending_resize_);
    128 
    129   EnsureCallbackPending();
    130 
    131   aggregator_.ScrollRect(clip_rect, amount);
    132 }
    133 
    134 pp::Size PaintManager::GetEffectiveSize() const {
    135   return has_pending_resize_ ? pending_size_ : plugin_size_;
    136 }
    137 
    138 float PaintManager::GetEffectiveDeviceScale() const {
    139   return has_pending_resize_ ? pending_device_scale_ : device_scale_;
    140 }
    141 
    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.
    146   if (flush_pending_)
    147     return;
    148 
    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_)
    152     return;
    153 
    154   pp::Module::Get()->core()->CallOnMainThread(
    155       0,
    156       callback_factory_.NewCallback(&PaintManager::OnManualCallbackComplete),
    157       0);
    158   manual_callback_pending_ = true;
    159 }
    160 
    161 void PaintManager::DoPaint() {
    162   in_paint_ = true;
    163 
    164   std::vector<ReadyRect> ready;
    165   std::vector<pp::Rect> pending;
    166 
    167   DCHECK(aggregator_.HasPendingUpdate());
    168 
    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
    173   // do this later.
    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;
    183 
    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();
    188     }
    189 
    190     if (pending_device_scale_ != 1.0)
    191       graphics_.SetScale(1.0 / pending_device_scale_);
    192     device_scale_ = pending_device_scale_;
    193 
    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();
    198   }
    199 
    200   PaintAggregator::PaintUpdate update = aggregator_.GetPendingUpdate();
    201   client_->OnPaint(update.paint_rects, &ready, &pending);
    202 
    203   if (ready.empty() && pending.empty()) {
    204     in_paint_ = false;
    205     return;  // Nothing was painted, don't schedule a flush.
    206   }
    207 
    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();
    216 
    217     // Apply any scroll first.
    218     if (update.has_scroll)
    219       graphics_.Scroll(update.scroll_rect, update.scroll_delta);
    220 
    221     view_size_changed_waiting_for_paint_ = false;
    222   } else {
    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]);
    233       } else {
    234         ready_later.push_back(ready[i]);
    235       }
    236     }
    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);
    240 
    241     if (ready_now.empty()) {
    242       in_paint_ = false;
    243       EnsureCallbackPending();
    244       return;
    245     }
    246   }
    247 
    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);
    251   }
    252 
    253   int32_t result = graphics_.Flush(
    254       callback_factory_.NewCallback(&PaintManager::OnFlushComplete));
    255 
    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.
    260   //
    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);
    266 
    267   if (result == PP_OK_COMPLETIONPENDING) {
    268     flush_pending_ = true;
    269   } else {
    270     DCHECK(result == PP_OK);  // Catch all other errors in debug mode.
    271   }
    272 
    273   in_paint_ = false;
    274   first_paint_ = false;
    275 
    276   if (graphics_need_to_be_bound_) {
    277     instance_->BindGraphics(graphics_);
    278     graphics_need_to_be_bound_ = false;
    279   }
    280 }
    281 
    282 void PaintManager::OnFlushComplete(int32_t) {
    283   DCHECK(flush_pending_);
    284   flush_pending_ = false;
    285 
    286   // If more paints were enqueued while we were waiting for the flush to
    287   // complete, execute them now.
    288   if (aggregator_.HasPendingUpdate())
    289     DoPaint();
    290 }
    291 
    292 void PaintManager::OnManualCallbackComplete(int32_t) {
    293   DCHECK(manual_callback_pending_);
    294   manual_callback_pending_ = false;
    295 
    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())
    301     DoPaint();
    302 }
    303