| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 | 8 |
| 9 #include "base/histogram.h" | 9 #include "base/histogram.h" |
| 10 #include "base/time.h" | 10 #include "base/time.h" |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 52 // Indicates the time that the RWH must be visible for us to update the | 52 // Indicates the time that the RWH must be visible for us to update the |
| 53 // thumbnail on it. If the user holds down control enter, there will be a lot | 53 // thumbnail on it. If the user holds down control enter, there will be a lot |
| 54 // of backing stores created and destroyed. WE don't want to interfere with | 54 // of backing stores created and destroyed. WE don't want to interfere with |
| 55 // that. | 55 // that. |
| 56 // | 56 // |
| 57 // Any operation that happens within this time of being shown is ignored. | 57 // Any operation that happens within this time of being shown is ignored. |
| 58 // This means we won't throw the thumbnail away when the backing store is | 58 // This means we won't throw the thumbnail away when the backing store is |
| 59 // painted in this time. | 59 // painted in this time. |
| 60 static const int kVisibilitySlopMS = 3000; | 60 static const int kVisibilitySlopMS = 3000; |
| 61 | 61 |
| 62 static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS"; |
| 63 |
| 62 struct WidgetThumbnail { | 64 struct WidgetThumbnail { |
| 63 SkBitmap thumbnail; | 65 SkBitmap thumbnail; |
| 64 | 66 |
| 65 // Indicates the last time the RWH was shown and hidden. | 67 // Indicates the last time the RWH was shown and hidden. |
| 66 base::TimeTicks last_shown; | 68 base::TimeTicks last_shown; |
| 67 base::TimeTicks last_hidden; | 69 base::TimeTicks last_hidden; |
| 68 }; | 70 }; |
| 69 | 71 |
| 70 PropertyAccessor<WidgetThumbnail>* GetThumbnailAccessor() { | 72 PropertyAccessor<WidgetThumbnail>* GetThumbnailAccessor() { |
| 71 static PropertyAccessor<WidgetThumbnail> accessor; | 73 static PropertyAccessor<WidgetThumbnail> accessor; |
| 72 return &accessor; | 74 return &accessor; |
| 73 } | 75 } |
| 74 | 76 |
| 75 // Returns the existing WidgetThumbnail for a RVH, or creates a new one and | 77 // Returns the existing WidgetThumbnail for a RVH, or creates a new one and |
| 76 // returns that if none exists. | 78 // returns that if none exists. |
| 77 WidgetThumbnail* GetDataForHost(RenderWidgetHost* host) { | 79 WidgetThumbnail* GetDataForHost(RenderWidgetHost* host) { |
| 78 WidgetThumbnail* wt = GetThumbnailAccessor()->GetProperty( | 80 WidgetThumbnail* wt = GetThumbnailAccessor()->GetProperty( |
| 79 host->property_bag()); | 81 host->property_bag()); |
| 80 if (wt) | 82 if (wt) |
| 81 return wt; | 83 return wt; |
| 82 | 84 |
| 83 GetThumbnailAccessor()->SetProperty(host->property_bag(), | 85 GetThumbnailAccessor()->SetProperty(host->property_bag(), |
| 84 WidgetThumbnail()); | 86 WidgetThumbnail()); |
| 85 return GetThumbnailAccessor()->GetProperty(host->property_bag()); | 87 return GetThumbnailAccessor()->GetProperty(host->property_bag()); |
| 86 } | 88 } |
| 87 | 89 |
| 90 #if defined(OS_WIN) |
| 91 |
| 92 // PlatformDevices/Canvases can't be copied like a regular SkBitmap (at least |
| 93 // on Windows). So the second parameter is the canvas to draw into. It should |
| 94 // be sized to the size of the backing store. |
| 95 void GetBitmapForBackingStore(BackingStore* backing_store, |
| 96 skia::PlatformCanvas* canvas) { |
| 97 HDC dc = canvas->beginPlatformPaint(); |
| 98 BitBlt(dc, 0, 0, |
| 99 backing_store->size().width(), backing_store->size().height(), |
| 100 backing_store->hdc(), 0, 0, SRCCOPY); |
| 101 canvas->endPlatformPaint(); |
| 102 } |
| 103 |
| 104 #endif |
| 105 |
| 88 // Creates a downsampled thumbnail for the given backing store. The returned | 106 // Creates a downsampled thumbnail for the given backing store. The returned |
| 89 // bitmap will be isNull if there was an error creating it. | 107 // bitmap will be isNull if there was an error creating it. |
| 90 SkBitmap GetThumbnailForBackingStore(BackingStore* backing_store) { | 108 SkBitmap GetThumbnailForBackingStore(BackingStore* backing_store) { |
| 109 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now(); |
| 110 |
| 91 SkBitmap result; | 111 SkBitmap result; |
| 92 | 112 |
| 93 // TODO(brettw) write this for other platforms. If you enable this, be sure | 113 // TODO(brettw) write this for other platforms. If you enable this, be sure |
| 94 // to also enable the unit tests for the same platform in | 114 // to also enable the unit tests for the same platform in |
| 95 // thumbnail_generator_unittest.cc | 115 // thumbnail_generator_unittest.cc |
| 96 #if defined(OS_WIN) | 116 #if defined(OS_WIN) |
| 97 // Get the bitmap as a Skia object so we can resample it. This is a large | 117 // Get the bitmap as a Skia object so we can resample it. This is a large |
| 98 // allocation and we can tolerate failure here, so give up if the allocation | 118 // allocation and we can tolerate failure here, so give up if the allocation |
| 99 // fails. | 119 // fails. |
| 100 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now(); | |
| 101 | |
| 102 skia::PlatformCanvas temp_canvas; | 120 skia::PlatformCanvas temp_canvas; |
| 103 if (!temp_canvas.initialize(backing_store->size().width(), | 121 if (!temp_canvas.initialize(backing_store->size().width(), |
| 104 backing_store->size().height(), true)) | 122 backing_store->size().height(), true)) |
| 105 return SkBitmap(); | 123 return result; |
| 106 HDC temp_dc = temp_canvas.beginPlatformPaint(); | 124 GetBitmapForBackingStore(backing_store, &temp_canvas); |
| 107 BitBlt(temp_dc, | |
| 108 0, 0, backing_store->size().width(), backing_store->size().height(), | |
| 109 backing_store->hdc(), 0, 0, SRCCOPY); | |
| 110 temp_canvas.endPlatformPaint(); | |
| 111 | 125 |
| 112 // Get the bitmap out of the canvas and resample it. | 126 // Get the bitmap out of the canvas and resample it. It would be nice if this |
| 127 // whole Windows-specific block could be put into a function, but the memory |
| 128 // management wouldn't work out because the bitmap is a PlatformDevice which |
| 129 // can't actually be copied. |
| 113 const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false); | 130 const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false); |
| 131 |
| 132 #elif defined(OS_LINUX) |
| 133 SkBitmap bmp = backing_store->PaintRectToBitmap( |
| 134 gfx::Rect(0, 0, |
| 135 backing_store->size().width(), backing_store->size().height())); |
| 136 #endif |
| 137 |
| 114 result = skia::ImageOperations::DownsampleByTwoUntilSize( | 138 result = skia::ImageOperations::DownsampleByTwoUntilSize( |
| 115 bmp, | 139 bmp, |
| 116 kThumbnailWidth, kThumbnailHeight); | 140 kThumbnailWidth, kThumbnailHeight); |
| 117 if (bmp.width() == result.width() && bmp.height() == result.height()) { | 141 |
| 118 // This is a bit subtle. SkBitmaps are refcounted, but the magic ones in | 142 #if defined(OS_WIN) |
| 119 // PlatformCanvas can't be ssigned to SkBitmap with proper refcounting. | 143 // This is a bit subtle. SkBitmaps are refcounted, but the magic ones in |
| 120 // If the bitmap doesn't change, then the downsampler will return the input | 144 // PlatformCanvas on Windows can't be ssigned to SkBitmap with proper |
| 121 // bitmap, which will be the reference to the weird PlatformCanvas one | 145 // refcounting. If the bitmap doesn't change, then the downsampler will |
| 122 // insetad of a regular one. To get a regular refcounted bitmap, we need to | 146 // return the input bitmap, which will be the reference to the weird |
| 123 // copy it. | 147 // PlatformCanvas one insetad of a regular one. To get a regular refcounted |
| 148 // bitmap, we need to copy it. |
| 149 if (bmp.width() == result.width() && bmp.height() == result.height()) |
| 124 bmp.copyTo(&result, SkBitmap::kARGB_8888_Config); | 150 bmp.copyTo(&result, SkBitmap::kARGB_8888_Config); |
| 125 } | |
| 126 | |
| 127 HISTOGRAM_TIMES("Thumbnail.ComputeOnDestroyMS", | |
| 128 base::TimeTicks::Now() - begin_compute_thumbnail); | |
| 129 #endif | 151 #endif |
| 130 | 152 |
| 153 HISTOGRAM_TIMES(kThumbnailHistogramName, |
| 154 base::TimeTicks::Now() - begin_compute_thumbnail); |
| 131 return result; | 155 return result; |
| 132 } | 156 } |
| 133 | 157 |
| 134 } // namespace | 158 } // namespace |
| 135 | 159 |
| 136 ThumbnailGenerator::ThumbnailGenerator() | 160 ThumbnailGenerator::ThumbnailGenerator() |
| 137 : no_timeout_(false) { | 161 : no_timeout_(false) { |
| 138 // The BrowserProcessImpl creates this non-lazily. If you add nontrivial | 162 // The BrowserProcessImpl creates this non-lazily. If you add nontrivial |
| 139 // stuff here, be sure to convert it to being lazily created. | 163 // stuff here, be sure to convert it to being lazily created. |
| 140 // | 164 // |
| (...skipping 17 matching lines...) Expand all Loading... |
| 158 NotificationService::AllSources()); | 182 NotificationService::AllSources()); |
| 159 registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DESTROYED, | 183 registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DESTROYED, |
| 160 NotificationService::AllSources()); | 184 NotificationService::AllSources()); |
| 161 } | 185 } |
| 162 } | 186 } |
| 163 | 187 |
| 164 SkBitmap ThumbnailGenerator::GetThumbnailForRenderer( | 188 SkBitmap ThumbnailGenerator::GetThumbnailForRenderer( |
| 165 RenderWidgetHost* renderer) const { | 189 RenderWidgetHost* renderer) const { |
| 166 // Return a cached one if we have it and it's still valid. This will only be | 190 // Return a cached one if we have it and it's still valid. This will only be |
| 167 // valid when there used to be a backing store, but there isn't now. | 191 // valid when there used to be a backing store, but there isn't now. |
| 168 WidgetThumbnail* wt = GetThumbnailAccessor()->GetProperty( | 192 WidgetThumbnail* wt = GetDataForHost(renderer); |
| 169 renderer->property_bag()); | 193 if (!wt->thumbnail.isNull() && |
| 170 if (wt && !wt->thumbnail.isNull() && | |
| 171 (no_timeout_ || | 194 (no_timeout_ || |
| 172 base::TimeTicks::Now() - | 195 base::TimeTicks::Now() - |
| 173 base::TimeDelta::FromMilliseconds(kVisibilitySlopMS) < wt->last_shown)) | 196 base::TimeDelta::FromMilliseconds(kVisibilitySlopMS) < wt->last_shown)) |
| 174 return wt->thumbnail; | 197 return wt->thumbnail; |
| 175 | 198 |
| 176 BackingStore* backing_store = renderer->GetBackingStore(false); | 199 BackingStore* backing_store = renderer->GetBackingStore(false); |
| 177 if (!backing_store) | 200 if (!backing_store) |
| 178 return SkBitmap(); | 201 return SkBitmap(); |
| 179 | 202 |
| 180 // Save this thumbnail in case we need to use it again soon. It will be | 203 // Save this thumbnail in case we need to use it again soon. It will be |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 &ThumbnailGenerator::ShownDelayHandler); | 345 &ThumbnailGenerator::ShownDelayHandler); |
| 323 } | 346 } |
| 324 } | 347 } |
| 325 | 348 |
| 326 void ThumbnailGenerator::EraseHostFromShownList(RenderWidgetHost* widget) { | 349 void ThumbnailGenerator::EraseHostFromShownList(RenderWidgetHost* widget) { |
| 327 std::vector<RenderWidgetHost*>::iterator found = | 350 std::vector<RenderWidgetHost*>::iterator found = |
| 328 std::find(shown_hosts_.begin(), shown_hosts_.end(), widget); | 351 std::find(shown_hosts_.begin(), shown_hosts_.end(), widget); |
| 329 if (found != shown_hosts_.end()) | 352 if (found != shown_hosts_.end()) |
| 330 shown_hosts_.erase(found); | 353 shown_hosts_.erase(found); |
| 331 } | 354 } |
| OLD | NEW |