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