Home | History | Annotate | Download | only in pdf
      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 "pdf/thumbnail_control.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 #include "base/strings/string_util.h"
     11 #include "pdf/draw_utils.h"
     12 #include "pdf/number_image_generator.h"
     13 
     14 namespace chrome_pdf {
     15 
     16 const int kLeftBorderSize = 52;
     17 const int kBorderSize = 12;
     18 const int kHighlightBorderSize = 2;
     19 
     20 const uint32 kLeftColor = 0x003F537B;
     21 const uint32 kRightColor = 0x990D1626;
     22 
     23 const uint32 kTopHighlightColor = 0xFF426DC9;
     24 const uint32 kBottomHighlightColor = 0xFF6391DE;
     25 const uint32 kThumbnailBackgroundColor = 0xFF000000;
     26 
     27 const uint32 kSlidingTimeoutMs = 50;
     28 const int32 kSlidingShift = 50;
     29 
     30 const double kNonSelectedThumbnailAlpha = 0.91;
     31 
     32 ThumbnailControl::ThumbnailControl()
     33     : engine_(NULL), sliding_width_(0), sliding_shift_(kSlidingShift),
     34       sliding_timeout_(kSlidingTimeoutMs), sliding_timer_id_(0) {
     35 }
     36 
     37 ThumbnailControl::~ThumbnailControl() {
     38   ClearCache();
     39 }
     40 
     41 bool ThumbnailControl::CreateThumbnailControl(
     42     uint32 id, const pp::Rect& rc,
     43     bool visible, Owner* owner, PDFEngine* engine,
     44     NumberImageGenerator* number_image_generator) {
     45   engine_ = engine;
     46   number_image_generator_ = number_image_generator;
     47   sliding_width_ = rc.width();
     48 
     49   return Control::Create(id, rc, visible, owner);
     50 }
     51 
     52 void ThumbnailControl::SetPosition(int position, int total, bool invalidate) {
     53   visible_rect_ = pp::Rect();
     54   visible_pages_.clear();
     55 
     56   if (rect().width() < kLeftBorderSize + kBorderSize) {
     57     return;  // control is too narrow to show thumbnails.
     58   }
     59 
     60   int num_pages = engine_->GetNumberOfPages();
     61 
     62   int max_doc_width = 0, total_doc_height = 0;
     63   std::vector<pp::Rect> page_sizes(num_pages);
     64   for (int i = 0; i < num_pages; ++i) {
     65     page_sizes[i] = engine_->GetPageRect(i);
     66     max_doc_width = std::max(max_doc_width, page_sizes[i].width());
     67     total_doc_height += page_sizes[i].height();
     68   }
     69 
     70   if (!max_doc_width)
     71     return;
     72 
     73   int max_thumbnail_width = rect().width() - kLeftBorderSize - kBorderSize;
     74   double thumbnail_ratio =
     75       max_thumbnail_width / static_cast<double>(max_doc_width);
     76 
     77   int total_thumbnail_height = 0;
     78   for (int i = 0; i < num_pages; ++i) {
     79     total_thumbnail_height += kBorderSize;
     80     int thumbnail_width =
     81         static_cast<int>(page_sizes[i].width() * thumbnail_ratio);
     82     int thumbnail_height =
     83         static_cast<int>(page_sizes[i].height() * thumbnail_ratio);
     84     int x = (max_thumbnail_width - thumbnail_width) / 2;
     85     page_sizes[i] =
     86         pp::Rect(x, total_thumbnail_height, thumbnail_width, thumbnail_height);
     87     total_thumbnail_height += thumbnail_height;
     88   }
     89   total_thumbnail_height += kBorderSize;
     90 
     91   int visible_y = 0;
     92   if (total > 0) {
     93     double range = total_thumbnail_height - rect().height();
     94     if (range < 0)
     95       range = 0;
     96     visible_y = static_cast<int>(range * position / total);
     97   }
     98   visible_rect_ = pp::Rect(0, visible_y, max_thumbnail_width, rect().height());
     99 
    100   for (int i = 0; i < num_pages; ++i) {
    101     if (page_sizes[i].Intersects(visible_rect_)) {
    102       PageInfo page_info;
    103       page_info.index = i;
    104       page_info.rect = page_sizes[i];
    105       page_info.rect.Offset(kLeftBorderSize, -visible_rect_.y());
    106       visible_pages_.push_back(page_info);
    107     }
    108   }
    109 
    110   if (invalidate)
    111     owner()->Invalidate(id(), rect());
    112 }
    113 
    114 void ThumbnailControl::Show(bool visible, bool invalidate) {
    115   if (!visible || invalidate)
    116     ClearCache();
    117   sliding_width_ = rect().width();
    118   Control::Show(visible, invalidate);
    119 }
    120 
    121 void ThumbnailControl::SlideIn() {
    122   if (visible())
    123     return;
    124 
    125   Show(true, false);
    126   sliding_width_ = 0;
    127   sliding_shift_ = kSlidingShift;
    128 
    129   sliding_timer_id_ = owner()->ScheduleTimer(id(), sliding_timeout_);
    130   owner()->Invalidate(id(), rect());
    131 }
    132 
    133 void ThumbnailControl::SlideOut() {
    134   if (!visible())
    135     return;
    136   sliding_shift_ = -kSlidingShift;
    137   sliding_timer_id_ = owner()->ScheduleTimer(id(), sliding_timeout_);
    138 }
    139 
    140 void ThumbnailControl::Paint(pp::ImageData* image_data, const pp::Rect& rc) {
    141   if (!visible())
    142     return;
    143 
    144   pp::Rect control_rc(rect());
    145   control_rc.Offset(control_rc.width() - sliding_width_, 0);
    146   control_rc.set_width(sliding_width_);
    147 
    148   pp::Rect draw_rc = rc.Intersect(control_rc);
    149   if (draw_rc.IsEmpty())
    150     return;
    151 
    152   pp::Rect gradient_rc(control_rc.x(), draw_rc.y(),
    153                        control_rc.width(), draw_rc.height());
    154   GradientFill(owner()->GetInstance(),
    155                image_data,
    156                draw_rc,
    157                gradient_rc,
    158                kLeftColor,
    159                kRightColor,
    160                true,
    161                transparency());
    162 
    163   int selected_page = engine_->GetMostVisiblePage();
    164   for (size_t i = 0; i < visible_pages_.size(); ++i) {
    165     pp::Rect page_rc = visible_pages_[i].rect;
    166     page_rc.Offset(control_rc.point());
    167 
    168     if (visible_pages_[i].index == selected_page) {
    169       pp::Rect highlight_rc = page_rc;
    170       highlight_rc.Inset(-kHighlightBorderSize, -kHighlightBorderSize);
    171       GradientFill(owner()->GetInstance(),
    172                    image_data,
    173                    draw_rc,
    174                    highlight_rc,
    175                    kTopHighlightColor,
    176                    kBottomHighlightColor,
    177                    false,
    178                    transparency());
    179     }
    180 
    181     pp::Rect draw_page_rc = page_rc.Intersect(draw_rc);
    182     if (draw_page_rc.IsEmpty())
    183       continue;
    184 
    185     // First search page image in the cache.
    186     pp::ImageData* thumbnail = NULL;
    187     std::map<int, pp::ImageData*>::iterator it =
    188         image_cache_.find(visible_pages_[i].index);
    189     if (it != image_cache_.end()) {
    190       if (it->second->size() == page_rc.size())
    191         thumbnail = image_cache_[visible_pages_[i].index];
    192       else
    193         image_cache_.erase(it);
    194     }
    195 
    196     // If page is not found in the cache, create new one.
    197     if (thumbnail == NULL) {
    198       thumbnail = new pp::ImageData(owner()->GetInstance(),
    199                                     PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    200                                     page_rc.size(),
    201                                     false);
    202       engine_->PaintThumbnail(thumbnail, visible_pages_[i].index);
    203 
    204       pp::ImageData page_number;
    205       number_image_generator_->GenerateImage(
    206           visible_pages_[i].index + 1, &page_number);
    207       pp::Point origin(
    208           (thumbnail->size().width() - page_number.size().width()) / 2,
    209           (thumbnail->size().height() - page_number.size().height()) / 2);
    210 
    211       if (origin.x() > 0 && origin.y() > 0) {
    212         AlphaBlend(page_number, pp::Rect(pp::Point(), page_number.size()),
    213                    thumbnail, origin, kOpaqueAlpha);
    214       }
    215 
    216       image_cache_[visible_pages_[i].index] = thumbnail;
    217     }
    218 
    219     uint8 alpha = transparency();
    220     if (visible_pages_[i].index != selected_page)
    221       alpha = static_cast<uint8>(alpha * kNonSelectedThumbnailAlpha);
    222     FillRect(image_data, draw_page_rc, kThumbnailBackgroundColor);
    223     draw_page_rc.Offset(-page_rc.x(), -page_rc.y());
    224     AlphaBlend(*thumbnail, draw_page_rc, image_data,
    225         draw_page_rc.point() + page_rc.point(), alpha);
    226   }
    227 }
    228 
    229 bool ThumbnailControl::HandleEvent(const pp::InputEvent& event) {
    230   if (!visible())
    231     return false;
    232 
    233   pp::MouseInputEvent mouse_event(event);
    234   if (mouse_event.is_null())
    235     return false;
    236   pp::Point pt = mouse_event.GetPosition();
    237   if (!rect().Contains(pt))
    238     return false;
    239 
    240   int over_page = -1;
    241   for (size_t i = 0; i < visible_pages_.size(); ++i) {
    242     pp::Rect page_rc = visible_pages_[i].rect;
    243     page_rc.Offset(rect().point());
    244     if (page_rc.Contains(pt)) {
    245       over_page = i;
    246       break;
    247     }
    248   }
    249 
    250   bool handled = false;
    251   switch (event.GetType()) {
    252     case PP_INPUTEVENT_TYPE_MOUSEMOVE:
    253       owner()->SetCursor(id(),
    254           over_page == -1 ? PP_CURSORTYPE_POINTER : PP_CURSORTYPE_HAND);
    255       break;
    256     case PP_INPUTEVENT_TYPE_MOUSEDOWN:
    257       if (over_page != -1) {
    258         owner()->Invalidate(id(), rect());
    259         owner()->OnEvent(id(), EVENT_ID_THUMBNAIL_SELECTED,
    260                          &visible_pages_[over_page].index);
    261       }
    262       handled = true;
    263       break;
    264     default:
    265       break;
    266   }
    267 
    268   return handled;
    269 }
    270 
    271 void ThumbnailControl::OnTimerFired(uint32 timer_id) {
    272   if (timer_id == sliding_timer_id_) {
    273     sliding_width_ += sliding_shift_;
    274     if (sliding_width_ <= 0) {
    275       // We completely slided out. Make control invisible now.
    276       Show(false, false);
    277     } else if (sliding_width_ >= rect().width()) {
    278       // We completely slided in. Make sliding width to full control width.
    279       sliding_width_ = rect().width();
    280     } else {
    281       // We have not completed sliding yet. Keep sliding.
    282       sliding_timer_id_ = owner()->ScheduleTimer(id(), sliding_timeout_);
    283     }
    284     owner()->Invalidate(id(), rect());
    285   }
    286 }
    287 
    288 void ThumbnailControl::ResetEngine(PDFEngine* engine) {
    289   engine_ = engine;
    290   ClearCache();
    291 }
    292 
    293 void ThumbnailControl::ClearCache() {
    294   std::map<int, pp::ImageData*>::iterator it;
    295   for (it = image_cache_.begin(); it != image_cache_.end(); ++it) {
    296     delete it->second;
    297   }
    298   image_cache_.clear();
    299 }
    300 
    301 }  // namespace chrome_pdf
    302