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
ThumbnailControl()32 ThumbnailControl::ThumbnailControl()
33 : engine_(NULL), sliding_width_(0), sliding_shift_(kSlidingShift),
34 sliding_timeout_(kSlidingTimeoutMs), sliding_timer_id_(0) {
35 }
36
~ThumbnailControl()37 ThumbnailControl::~ThumbnailControl() {
38 ClearCache();
39 }
40
CreateThumbnailControl(uint32 id,const pp::Rect & rc,bool visible,Owner * owner,PDFEngine * engine,NumberImageGenerator * number_image_generator)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
SetPosition(int position,int total,bool invalidate)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
Show(bool visible,bool invalidate)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
SlideIn()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
SlideOut()133 void ThumbnailControl::SlideOut() {
134 if (!visible())
135 return;
136 sliding_shift_ = -kSlidingShift;
137 sliding_timer_id_ = owner()->ScheduleTimer(id(), sliding_timeout_);
138 }
139
Paint(pp::ImageData * image_data,const pp::Rect & rc)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
HandleEvent(const pp::InputEvent & event)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
OnTimerFired(uint32 timer_id)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
ResetEngine(PDFEngine * engine)288 void ThumbnailControl::ResetEngine(PDFEngine* engine) {
289 engine_ = engine;
290 ClearCache();
291 }
292
ClearCache()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