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.
5 #include "pdf/thumbnail_control.h"
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"
14 namespace chrome_pdf
{
16 const int kLeftBorderSize
= 52;
17 const int kBorderSize
= 12;
18 const int kHighlightBorderSize
= 2;
20 const uint32 kLeftColor
= 0x003F537B;
21 const uint32 kRightColor
= 0x990D1626;
23 const uint32 kTopHighlightColor
= 0xFF426DC9;
24 const uint32 kBottomHighlightColor
= 0xFF6391DE;
25 const uint32 kThumbnailBackgroundColor
= 0xFF000000;
27 const uint32 kSlidingTimeoutMs
= 50;
28 const int32 kSlidingShift
= 50;
30 const double kNonSelectedThumbnailAlpha
= 0.91;
32 ThumbnailControl::ThumbnailControl()
33 : engine_(NULL
), sliding_width_(0), sliding_shift_(kSlidingShift
),
34 sliding_timeout_(kSlidingTimeoutMs
), sliding_timer_id_(0) {
37 ThumbnailControl::~ThumbnailControl() {
41 bool ThumbnailControl::CreateThumbnailControl(
42 uint32 id
, const pp::Rect
& rc
,
43 bool visible
, Owner
* owner
, PDFEngine
* engine
,
44 NumberImageGenerator
* number_image_generator
) {
46 number_image_generator_
= number_image_generator
;
47 sliding_width_
= rc
.width();
49 return Control::Create(id
, rc
, visible
, owner
);
52 void ThumbnailControl::SetPosition(int position
, int total
, bool invalidate
) {
53 visible_rect_
= pp::Rect();
54 visible_pages_
.clear();
56 if (rect().width() < kLeftBorderSize
+ kBorderSize
) {
57 return; // control is too narrow to show thumbnails.
60 int num_pages
= engine_
->GetNumberOfPages();
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();
73 int max_thumbnail_width
= rect().width() - kLeftBorderSize
- kBorderSize
;
74 double thumbnail_ratio
=
75 max_thumbnail_width
/ static_cast<double>(max_doc_width
);
77 int total_thumbnail_height
= 0;
78 for (int i
= 0; i
< num_pages
; ++i
) {
79 total_thumbnail_height
+= kBorderSize
;
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;
86 pp::Rect(x
, total_thumbnail_height
, thumbnail_width
, thumbnail_height
);
87 total_thumbnail_height
+= thumbnail_height
;
89 total_thumbnail_height
+= kBorderSize
;
93 double range
= total_thumbnail_height
- rect().height();
96 visible_y
= static_cast<int>(range
* position
/ total
);
98 visible_rect_
= pp::Rect(0, visible_y
, max_thumbnail_width
, rect().height());
100 for (int i
= 0; i
< num_pages
; ++i
) {
101 if (page_sizes
[i
].Intersects(visible_rect_
)) {
104 page_info
.rect
= page_sizes
[i
];
105 page_info
.rect
.Offset(kLeftBorderSize
, -visible_rect_
.y());
106 visible_pages_
.push_back(page_info
);
111 owner()->Invalidate(id(), rect());
114 void ThumbnailControl::Show(bool visible
, bool invalidate
) {
115 if (!visible
|| invalidate
)
117 sliding_width_
= rect().width();
118 Control::Show(visible
, invalidate
);
121 void ThumbnailControl::SlideIn() {
127 sliding_shift_
= kSlidingShift
;
129 sliding_timer_id_
= owner()->ScheduleTimer(id(), sliding_timeout_
);
130 owner()->Invalidate(id(), rect());
133 void ThumbnailControl::SlideOut() {
136 sliding_shift_
= -kSlidingShift
;
137 sliding_timer_id_
= owner()->ScheduleTimer(id(), sliding_timeout_
);
140 void ThumbnailControl::Paint(pp::ImageData
* image_data
, const pp::Rect
& rc
) {
144 pp::Rect
control_rc(rect());
145 control_rc
.Offset(control_rc
.width() - sliding_width_
, 0);
146 control_rc
.set_width(sliding_width_
);
148 pp::Rect draw_rc
= rc
.Intersect(control_rc
);
149 if (draw_rc
.IsEmpty())
152 pp::Rect
gradient_rc(control_rc
.x(), draw_rc
.y(),
153 control_rc
.width(), draw_rc
.height());
154 GradientFill(owner()->GetInstance(),
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());
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(),
176 kBottomHighlightColor
,
181 pp::Rect draw_page_rc
= page_rc
.Intersect(draw_rc
);
182 if (draw_page_rc
.IsEmpty())
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
];
193 image_cache_
.erase(it
);
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
,
202 engine_
->PaintThumbnail(thumbnail
, visible_pages_
[i
].index
);
204 pp::ImageData page_number
;
205 number_image_generator_
->GenerateImage(
206 visible_pages_
[i
].index
+ 1, &page_number
);
208 (thumbnail
->size().width() - page_number
.size().width()) / 2,
209 (thumbnail
->size().height() - page_number
.size().height()) / 2);
211 if (origin
.x() > 0 && origin
.y() > 0) {
212 AlphaBlend(page_number
, pp::Rect(pp::Point(), page_number
.size()),
213 thumbnail
, origin
, kOpaqueAlpha
);
216 image_cache_
[visible_pages_
[i
].index
] = thumbnail
;
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
);
229 bool ThumbnailControl::HandleEvent(const pp::InputEvent
& event
) {
233 pp::MouseInputEvent
mouse_event(event
);
234 if (mouse_event
.is_null())
236 pp::Point pt
= mouse_event
.GetPosition();
237 if (!rect().Contains(pt
))
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
)) {
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
);
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
);
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.
277 } else if (sliding_width_
>= rect().width()) {
278 // We completely slided in. Make sliding width to full control width.
279 sliding_width_
= rect().width();
281 // We have not completed sliding yet. Keep sliding.
282 sliding_timer_id_
= owner()->ScheduleTimer(id(), sliding_timeout_
);
284 owner()->Invalidate(id(), rect());
288 void ThumbnailControl::ResetEngine(PDFEngine
* engine
) {
293 void ThumbnailControl::ClearCache() {
294 std::map
<int, pp::ImageData
*>::iterator it
;
295 for (it
= image_cache_
.begin(); it
!= image_cache_
.end(); ++it
) {
298 image_cache_
.clear();
301 } // namespace chrome_pdf