Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(205)

Side by Side Diff: chrome/browser/tab_contents/thumbnail_generator.cc

Issue 10831207: Make ThumbnailGenerator support high DPI. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/tab_contents/thumbnail_generator.h" 5 #include "chrome/browser/tab_contents/thumbnail_generator.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath>
8 #include <map> 9 #include <map>
9 10
10 #include "base/bind.h" 11 #include "base/bind.h"
11 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
13 #include "base/time.h" 14 #include "base/time.h"
14 #include "build/build_config.h" 15 #include "build/build_config.h"
15 #include "chrome/browser/browser_process.h" 16 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/history/top_sites.h" 17 #include "chrome/browser/history/top_sites.h"
17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/thumbnail_score.h" 19 #include "chrome/common/thumbnail_score.h"
19 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/notification_details.h" 21 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_source.h" 22 #include "content/public/browser/notification_source.h"
22 #include "content/public/browser/notification_types.h" 23 #include "content/public/browser/notification_types.h"
23 #include "content/public/browser/render_process_host.h" 24 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h" 25 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/render_widget_host_view.h" 26 #include "content/public/browser/render_widget_host_view.h"
26 #include "content/public/browser/web_contents.h" 27 #include "content/public/browser/web_contents.h"
27 #include "googleurl/src/gurl.h" 28 #include "googleurl/src/gurl.h"
28 #include "skia/ext/image_operations.h" 29 #include "skia/ext/image_operations.h"
29 #include "skia/ext/platform_canvas.h" 30 #include "skia/ext/platform_canvas.h"
30 #include "third_party/skia/include/core/SkBitmap.h" 31 #include "third_party/skia/include/core/SkBitmap.h"
31 #include "ui/base/layout.h" 32 #include "ui/base/layout.h"
32 #include "ui/gfx/color_utils.h" 33 #include "ui/gfx/color_utils.h"
33 #include "ui/gfx/rect.h" 34 #include "ui/gfx/rect.h"
35 #include "ui/gfx/screen.h"
34 #include "ui/gfx/scrollbar_size.h" 36 #include "ui/gfx/scrollbar_size.h"
35 #include "ui/gfx/skbitmap_operations.h" 37 #include "ui/gfx/skbitmap_operations.h"
36 38
37 #if defined(OS_WIN) 39 #if defined(OS_WIN)
38 #include "base/win/windows_version.h" 40 #include "base/win/windows_version.h"
41 #include "ui/base/win/dpi.h"
39 #endif 42 #endif
40 43
41 // Overview 44 // Overview
42 // -------- 45 // --------
43 // This class provides current thumbnails for tabs. The simplest operation is 46 // This class provides current thumbnails for tabs. The simplest operation is
44 // when a request for a thumbnail comes in, to grab the backing store and make 47 // when a request for a thumbnail comes in, to grab the backing store and make
45 // a smaller version of that. Clients of the class can send such a request by 48 // a smaller version of that. Clients of the class can send such a request by
46 // AskForSnapshot(). 49 // AskForSnapshot().
47 // 50 //
48 // The class also provides a service for updating thumbnails to be used in 51 // The class also provides a service for updating thumbnails to be used in
49 // "Most visited" section of the new tab page. The service can be started 52 // "Most visited" section of the new tab page. The service can be started
50 // by StartThumbnailing(). The current algorithm of the service is as 53 // by StartThumbnailing(). The current algorithm of the service is as
51 // simple as follows: 54 // simple as follows:
52 // 55 //
53 // When a renderer is about to be hidden (this usually occurs when the 56 // When a renderer is about to be hidden (this usually occurs when the
54 // current tab is closed or another tab is clicked), update the 57 // current tab is closed or another tab is clicked), update the
55 // thumbnail for the tab rendered by the renderer, if needed. The 58 // thumbnail for the tab rendered by the renderer, if needed. The
56 // heuristics to judge whether or not to update the thumbnail is 59 // heuristics to judge whether or not to update the thumbnail is
57 // implemented in ShouldUpdateThumbnail(). 60 // implemented in ShouldUpdateThumbnail().
58 // 61 //
59 // We'll likely revise the algorithm to improve quality of thumbnails this 62 // We'll likely revise the algorithm to improve quality of thumbnails this
60 // service generates. 63 // service generates.
61 64
62 using content::RenderViewHost; 65 using content::RenderViewHost;
63 using content::RenderWidgetHost; 66 using content::RenderWidgetHost;
64 using content::WebContents; 67 using content::WebContents;
65 68
66 namespace { 69 namespace {
67 70
71 // The thumbnail size in DIP.
68 static const int kThumbnailWidth = 212; 72 static const int kThumbnailWidth = 212;
69 static const int kThumbnailHeight = 132; 73 static const int kThumbnailHeight = 132;
70 74
71 // This factor determines the number of pixels to be copied by
72 // RenderWidgetHost::CopyFromBackingStore for generating thumbnail.
73 // Smaller scale is good for performance, but too small scale causes aliasing
74 // because the resampling method is not good enough to retain the image quality.
75 // TODO(mazda): the Improve resampling method and use a smaller scale
76 // (http://crbug.com/118571).
77 static const double kThumbnailCopyScale = 2.0;
78
79 static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS"; 75 static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS";
80 76
81 // Calculates the size used by RenderWidgetHost::CopyFromBackingStore. 77 // Returns the size used by RenderWidgetHost::CopyFromBackingStore.
82 // The result is computed as the minimum size that satisfies the following 78 //
83 // conditions. 79 // The size is calculated in such a way that the copied size in pixel becomes
84 // result.width : result.height == view_size.width : view_size.height 80 // equal to (f * kThumbnailWidth, f * kThumbnailHeight), where f is the scale
85 // result.width >= kThumbnailCopyScale * desired_size.width 81 // of ui::SCALE_FACTOR_200P. Since RenderWidgetHost::CopyFromBackingStore takes
86 // result.height >= kThumbnailCopyScale * desired_size.height 82 // the size in DIP, we need to adjust the size based on |view|'s device scale
87 gfx::Size GetCopySizeForThumbnail(const gfx::Size& view_size, 83 // factor in order to copy the pixels with the size above.
oshima 2012/08/10 02:01:15 I'm a bit confused by this comment. It thought the
mazda 2012/08/17 06:10:51 Yes, that's correct. RenderWidgetHost::CopyFromBac
88 const gfx::Size& desired_size) { 84 //
89 const double scale = kThumbnailCopyScale * 85 // The copied size was chosen for the following reasons.
90 std::max(static_cast<double>(desired_size.width()) / view_size.width(), 86 //
91 static_cast<double>(desired_size.height()) / view_size.height()); 87 // 1. When the scale factor of the primary monitor is ui::SCALE_FACTOR_200P, the
92 return gfx::Size(static_cast<int>(scale * view_size.width()), 88 // generated thumbnail size is (f * kThumbnailWidth, f * kThumbnailHeight).
93 static_cast<int>(scale * view_size.height())); 89 // In order to avoid degrading the image quality by magnification, the size
90 // of the copied pixels should be equal to or larger than this thumbnail size.
91 //
92 // 2. RenderWidgetHost::CopyFromBackingStore can be costly especially when
93 // it is necessary to read back the web contents image data from GPU. As the
94 // cost is roughly propotional to the number of the copied pixels, the size of
95 // the copied pixels should be as small as possible.
96 //
97 // When the scale factor of the primary monitor is ui::SCALE_FACTOR_100P,
98 // we still copy the pixels with the same size as ui::SCALE_FACTOR_200P because
99 // the resampling method used in RenderWidgetHost::CopyFromBackingStore is not
100 // good enough for the resampled image to be used directly for the thumbnail
101 // (http://crbug.com/141235). We assume this is not an issue in case of
102 // ui::SCALE_FACTOR_200P because the high resolution thumbnail on high density
103 // display alleviates the aliasing.
104 gfx::Size GetCopySizeForThumbnail(content::RenderWidgetHostView* view) {
105 gfx::Size copy_size(kThumbnailWidth, kThumbnailHeight);
106 ui::ScaleFactor scale_factor =
107 ui::GetScaleFactorForNativeView(view->GetNativeView());
108 switch (scale_factor) {
109 case ui::SCALE_FACTOR_100P:
110 copy_size =
111 copy_size.Scale(ui::GetScaleFactorScale(ui::SCALE_FACTOR_200P));
112 break;
113 case ui::SCALE_FACTOR_200P:
114 // Use the size as-is.
115 break;
116 default:
117 LOG(WARNING) << "Unsupported scale factor. Use the same copy size as "
118 << "ui::SCALE_FACTOR_100P";
119 copy_size =
120 copy_size.Scale(ui::GetScaleFactorScale(ui::SCALE_FACTOR_200P));
121 break;
122 }
123 return copy_size;
124 }
125
126 // Returns the size of the thumbnail stored in the database in pixel.
127 gfx::Size GetThumbnailSizeInPixel() {
128 gfx::Size thumbnail_size(kThumbnailWidth, kThumbnailHeight);
129 // Determine the resolution of the thumbnail based on the primary monitor.
130 gfx::Display primary_display = gfx::Screen::GetPrimaryDisplay();
oshima 2012/08/09 02:11:02 Can you add comment TODO(oshima): Use device's de
mazda 2012/08/09 17:07:38 Done.
131 return thumbnail_size.Scale(primary_display.device_scale_factor());
132 }
133
134 // Returns the scrollbar size in DIP.
135 int GetScrollbarSize() {
136 #if defined(OS_WIN)
oshima 2012/08/09 02:11:02 Is it better to have "&& !defined(USE_AURA) "? I k
mazda 2012/08/09 17:07:38 Done. Even with Aura, gfx::scrollbar_size() return
137 // On windows gfx::scrollbar_size() returns the size taking into account DPI.
138 return static_cast<int>(std::ceil(gfx::scrollbar_size() / ui::GetDPIScale()));
139 #else
140 return gfx::scrollbar_size();
141 #endif
142 }
143
144 // Returns the scrollbar size in pixel.
145 int GetScrollbarSizeInPixel(content::RenderWidgetHostView* view) {
146 #if defined(OS_WIN)
147 // On windows gfx::scrollbar_size() returns the size taking into account DPI.
148 return gfx::scrollbar_size();
149 #else
150 float scale_factor = ui::GetScaleFactorScale(ui::GetScaleFactorForNativeView(
151 view->GetNativeView()));
152 return static_cast<int>(scale_factor * gfx::scrollbar_size());
153 #endif
94 } 154 }
95 155
96 // Returns the clipping rectangle that is used for creating a thumbnail with 156 // Returns the clipping rectangle that is used for creating a thumbnail with
97 // the size of |desired_size| from the bitmap with the size of |source_size|. 157 // the size of |desired_size| from the bitmap with the size of |source_size|.
98 // The type of clipping that needs to be done is assigned to |clip_result|. 158 // The type of clipping that needs to be done is assigned to |clip_result|.
99 gfx::Rect GetClippingRect(const gfx::Size& source_size, 159 gfx::Rect GetClippingRect(const gfx::Size& source_size,
100 const gfx::Size& desired_size, 160 const gfx::Size& desired_size,
101 ThumbnailGenerator::ClipResult* clip_result) { 161 ThumbnailGenerator::ClipResult* clip_result) {
102 DCHECK(clip_result); 162 DCHECK(clip_result);
103 163
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 *clip_result = ThumbnailGenerator::kNotClipped; 195 *clip_result = ThumbnailGenerator::kNotClipped;
136 } 196 }
137 } 197 }
138 return clipping_rect; 198 return clipping_rect;
139 } 199 }
140 200
141 // Creates a downsampled thumbnail from the given bitmap. 201 // Creates a downsampled thumbnail from the given bitmap.
142 // store. The returned bitmap will be isNull if there was an error creating it. 202 // store. The returned bitmap will be isNull if there was an error creating it.
143 SkBitmap CreateThumbnail( 203 SkBitmap CreateThumbnail(
144 const SkBitmap& bitmap, 204 const SkBitmap& bitmap,
145 int desired_width, 205 const gfx::Size& desired_size,
146 int desired_height, 206 int scrollbar_size,
147 ThumbnailGenerator::ClipResult* clip_result) { 207 ThumbnailGenerator::ClipResult* clip_result) {
148 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now(); 208 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
149 209
150 SkBitmap clipped_bitmap; 210 SkBitmap clipped_bitmap;
151 if (*clip_result == ThumbnailGenerator::kUnprocessed) { 211 if (*clip_result == ThumbnailGenerator::kUnprocessed) {
152 // Clip the pixels that will commonly hold a scrollbar, which looks bad in 212 // Clip the pixels that will commonly hold a scrollbar, which looks bad in
153 // thumbnails. 213 // thumbnails.
154 int scrollbar_size = gfx::scrollbar_size();
155 SkIRect scrollbarless_rect = 214 SkIRect scrollbarless_rect =
156 { 0, 0, 215 { 0, 0,
157 std::max(1, bitmap.width() - scrollbar_size), 216 std::max(1, bitmap.width() - scrollbar_size),
158 std::max(1, bitmap.height() - scrollbar_size) }; 217 std::max(1, bitmap.height() - scrollbar_size) };
159 SkBitmap bmp; 218 SkBitmap bmp;
160 bitmap.extractSubset(&bmp, scrollbarless_rect); 219 bitmap.extractSubset(&bmp, scrollbarless_rect);
161 220
162 clipped_bitmap = ThumbnailGenerator::GetClippedBitmap( 221 clipped_bitmap = ThumbnailGenerator::GetClippedBitmap(
163 bmp, desired_width, desired_height, clip_result); 222 bmp, desired_size.width(), desired_size.height(), clip_result);
164 } else { 223 } else {
165 clipped_bitmap = bitmap; 224 clipped_bitmap = bitmap;
166 } 225 }
167 226
168 // Need to resize it to the size we want, so downsample until it's 227 // Need to resize it to the size we want, so downsample until it's
169 // close, and let the caller make it the exact size if desired. 228 // close, and let the caller make it the exact size if desired.
170 SkBitmap result = SkBitmapOperations::DownsampleByTwoUntilSize( 229 SkBitmap result = SkBitmapOperations::DownsampleByTwoUntilSize(
171 clipped_bitmap, desired_width, desired_height); 230 clipped_bitmap, desired_size.width(), desired_size.height());
172 #if !defined(USE_AURA) 231 #if !defined(USE_AURA)
173 // This is a bit subtle. SkBitmaps are refcounted, but the magic 232 // This is a bit subtle. SkBitmaps are refcounted, but the magic
174 // ones in PlatformCanvas can't be assigned to SkBitmap with proper 233 // ones in PlatformCanvas can't be assigned to SkBitmap with proper
175 // refcounting. If the bitmap doesn't change, then the downsampler 234 // refcounting. If the bitmap doesn't change, then the downsampler
176 // will return the input bitmap, which will be the reference to the 235 // will return the input bitmap, which will be the reference to the
177 // weird PlatformCanvas one insetad of a regular one. To get a 236 // weird PlatformCanvas one insetad of a regular one. To get a
178 // regular refcounted bitmap, we need to copy it. 237 // regular refcounted bitmap, we need to copy it.
179 // 238 //
180 // On Aura, the PlatformCanvas is platform-independent and does not have 239 // On Aura, the PlatformCanvas is platform-independent and does not have
181 // any native platform resources that can't be refounted, so this issue does 240 // any native platform resources that can't be refounted, so this issue does
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after
510 view_size, 569 view_size,
511 view_size); 570 view_size);
512 } 571 }
513 #endif 572 #endif
514 return; 573 return;
515 } 574 }
516 575
517 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size()); 576 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size());
518 // Clip the pixels that will commonly hold a scrollbar, which looks bad in 577 // Clip the pixels that will commonly hold a scrollbar, which looks bad in
519 // thumbnails. 578 // thumbnails.
520 int scrollbar_size = gfx::scrollbar_size(); 579 int scrollbar_size = GetScrollbarSize();
521 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size); 580 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size);
522 ClipResult clip_result = ThumbnailGenerator::kUnprocessed; 581 ClipResult clip_result = ThumbnailGenerator::kUnprocessed;
523 copy_rect = GetClippingRect(copy_rect.size(), 582 copy_rect = GetClippingRect(copy_rect.size(),
524 gfx::Size(kThumbnailWidth, kThumbnailHeight), 583 gfx::Size(kThumbnailWidth, kThumbnailHeight),
525 &clip_result); 584 &clip_result);
526 gfx::Size copy_size = 585 gfx::Size copy_size = GetCopySizeForThumbnail(view);
527 gfx::Size(kThumbnailWidth, kThumbnailHeight).Scale(kThumbnailCopyScale);
528 skia::PlatformCanvas* temp_canvas = new skia::PlatformCanvas; 586 skia::PlatformCanvas* temp_canvas = new skia::PlatformCanvas;
529 render_widget_host->CopyFromBackingStore( 587 render_widget_host->CopyFromBackingStore(
530 copy_rect, 588 copy_rect,
531 copy_size, 589 copy_size,
532 base::Bind(&ThumbnailGenerator::UpdateThumbnailWithCanvas, 590 base::Bind(&ThumbnailGenerator::UpdateThumbnailWithCanvas,
533 weak_factory_.GetWeakPtr(), 591 weak_factory_.GetWeakPtr(),
534 web_contents, 592 web_contents,
535 clip_result, 593 clip_result,
536 base::Owned(temp_canvas)), 594 base::Owned(temp_canvas)),
537 temp_canvas); 595 temp_canvas);
538 } 596 }
539 597
540 void ThumbnailGenerator::UpdateThumbnailWithBitmap( 598 void ThumbnailGenerator::UpdateThumbnailWithBitmap(
541 WebContents* web_contents, 599 WebContents* web_contents,
542 ClipResult clip_result, 600 ClipResult clip_result,
543 const SkBitmap& bitmap) { 601 const SkBitmap& bitmap) {
544 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 602 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
545 if (bitmap.isNull() || bitmap.empty()) 603 if (bitmap.isNull() || bitmap.empty())
546 return; 604 return;
547 605
548 SkBitmap thumbnail = CreateThumbnail(bitmap, 606 SkBitmap thumbnail = CreateThumbnail(
549 kThumbnailWidth, 607 bitmap,
550 kThumbnailHeight, 608 GetThumbnailSizeInPixel(),
551 &clip_result); 609 GetScrollbarSizeInPixel(web_contents->GetRenderViewHost()->GetView()),
610 &clip_result);
552 UpdateThumbnail(web_contents, thumbnail, clip_result); 611 UpdateThumbnail(web_contents, thumbnail, clip_result);
553 } 612 }
554 613
555 void ThumbnailGenerator::UpdateThumbnailWithCanvas( 614 void ThumbnailGenerator::UpdateThumbnailWithCanvas(
556 WebContents* web_contents, 615 WebContents* web_contents,
557 ClipResult clip_result, 616 ClipResult clip_result,
558 skia::PlatformCanvas* temp_canvas, 617 skia::PlatformCanvas* temp_canvas,
559 bool succeeded) { 618 bool succeeded) {
560 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 619 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
561 if (!succeeded) 620 if (!succeeded)
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
597 void ThumbnailGenerator::DidStartLoading( 656 void ThumbnailGenerator::DidStartLoading(
598 content::RenderViewHost* render_view_host) { 657 content::RenderViewHost* render_view_host) {
599 load_interrupted_ = false; 658 load_interrupted_ = false;
600 } 659 }
601 660
602 void ThumbnailGenerator::StopNavigation() { 661 void ThumbnailGenerator::StopNavigation() {
603 // This function gets called when the page loading is interrupted by the 662 // This function gets called when the page loading is interrupted by the
604 // stop button. 663 // stop button.
605 load_interrupted_ = true; 664 load_interrupted_ = true;
606 } 665 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698