1 // Copyright (c) 2013 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 "ash/desktop_background/wallpaper_resizer.h" 6 7 #include "ash/desktop_background/wallpaper_resizer_observer.h" 8 #include "base/bind.h" 9 #include "base/logging.h" 10 #include "base/threading/sequenced_worker_pool.h" 11 #include "base/threading/worker_pool.h" 12 #include "content/public/browser/browser_thread.h" 13 #include "third_party/skia/include/core/SkImage.h" 14 #include "ui/base/resource/resource_bundle.h" 15 #include "ui/gfx/image/image_skia_rep.h" 16 #include "ui/gfx/skia_util.h" 17 18 using content::BrowserThread; 19 20 namespace ash { 21 namespace { 22 23 // For our scaling ratios we need to round positive numbers. 24 int RoundPositive(double x) { 25 return static_cast<int>(floor(x + 0.5)); 26 } 27 28 // Resizes |orig_bitmap| to |target_size| using |layout| and stores the 29 // resulting bitmap at |resized_bitmap_out|. 30 void Resize(SkBitmap orig_bitmap, 31 const gfx::Size& target_size, 32 WallpaperLayout layout, 33 SkBitmap* resized_bitmap_out) { 34 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 35 SkBitmap new_bitmap = orig_bitmap; 36 37 const int orig_width = orig_bitmap.width(); 38 const int orig_height = orig_bitmap.height(); 39 const int new_width = target_size.width(); 40 const int new_height = target_size.height(); 41 42 if (orig_width > new_width || orig_height > new_height) { 43 gfx::Rect wallpaper_rect(0, 0, orig_width, orig_height); 44 gfx::Size cropped_size = gfx::Size(std::min(new_width, orig_width), 45 std::min(new_height, orig_height)); 46 switch (layout) { 47 case WALLPAPER_LAYOUT_CENTER: 48 wallpaper_rect.ClampToCenteredSize(cropped_size); 49 orig_bitmap.extractSubset(&new_bitmap, 50 gfx::RectToSkIRect(wallpaper_rect)); 51 break; 52 case WALLPAPER_LAYOUT_TILE: 53 wallpaper_rect.set_size(cropped_size); 54 orig_bitmap.extractSubset(&new_bitmap, 55 gfx::RectToSkIRect(wallpaper_rect)); 56 break; 57 case WALLPAPER_LAYOUT_STRETCH: 58 new_bitmap = skia::ImageOperations::Resize( 59 orig_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, 60 new_width, new_height); 61 break; 62 case WALLPAPER_LAYOUT_CENTER_CROPPED: 63 if (orig_width > new_width && orig_height > new_height) { 64 // The dimension with the smallest ratio must be cropped, the other 65 // one is preserved. Both are set in gfx::Size cropped_size. 66 double horizontal_ratio = static_cast<double>(new_width) / 67 static_cast<double>(orig_width); 68 double vertical_ratio = static_cast<double>(new_height) / 69 static_cast<double>(orig_height); 70 71 if (vertical_ratio > horizontal_ratio) { 72 cropped_size = gfx::Size( 73 RoundPositive(static_cast<double>(new_width) / vertical_ratio), 74 orig_height); 75 } else { 76 cropped_size = gfx::Size(orig_width, RoundPositive( 77 static_cast<double>(new_height) / horizontal_ratio)); 78 } 79 wallpaper_rect.ClampToCenteredSize(cropped_size); 80 SkBitmap sub_image; 81 orig_bitmap.extractSubset(&sub_image, 82 gfx::RectToSkIRect(wallpaper_rect)); 83 new_bitmap = skia::ImageOperations::Resize( 84 sub_image, skia::ImageOperations::RESIZE_LANCZOS3, 85 new_width, new_height); 86 } 87 } 88 } 89 90 *resized_bitmap_out = new_bitmap; 91 resized_bitmap_out->setImmutable(); 92 } 93 94 } // namespace 95 96 // static 97 uint32_t WallpaperResizer::GetImageId(const gfx::ImageSkia& image) { 98 const gfx::ImageSkiaRep& image_rep = image.GetRepresentation(1.0f); 99 return image_rep.is_null() ? 0 : image_rep.sk_bitmap().getGenerationID(); 100 } 101 102 WallpaperResizer::WallpaperResizer(int image_resource_id, 103 const gfx::Size& target_size, 104 WallpaperLayout layout) 105 : image_(*(ui::ResourceBundle::GetSharedInstance(). 106 GetImageNamed(image_resource_id).ToImageSkia())), 107 original_image_id_(GetImageId(image_)), 108 target_size_(target_size), 109 layout_(layout), 110 weak_ptr_factory_(this) { 111 image_.MakeThreadSafe(); 112 } 113 114 WallpaperResizer::WallpaperResizer(const gfx::ImageSkia& image, 115 const gfx::Size& target_size, 116 WallpaperLayout layout) 117 : image_(image), 118 original_image_id_(GetImageId(image_)), 119 target_size_(target_size), 120 layout_(layout), 121 weak_ptr_factory_(this) { 122 image_.MakeThreadSafe(); 123 } 124 125 WallpaperResizer::~WallpaperResizer() { 126 } 127 128 void WallpaperResizer::StartResize() { 129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 130 SkBitmap* resized_bitmap = new SkBitmap; 131 if (!content::BrowserThread::PostBlockingPoolTaskAndReply( 132 FROM_HERE, 133 base::Bind(&Resize, *image_.bitmap(), target_size_, 134 layout_, resized_bitmap), 135 base::Bind(&WallpaperResizer::OnResizeFinished, 136 weak_ptr_factory_.GetWeakPtr(), 137 base::Owned(resized_bitmap)))) { 138 LOG(WARNING) << "PostSequencedWorkerTask failed. " 139 << "Wallpaper may not be resized."; 140 } 141 } 142 143 void WallpaperResizer::AddObserver(WallpaperResizerObserver* observer) { 144 observers_.AddObserver(observer); 145 } 146 147 void WallpaperResizer::RemoveObserver(WallpaperResizerObserver* observer) { 148 observers_.RemoveObserver(observer); 149 } 150 151 void WallpaperResizer::OnResizeFinished(SkBitmap* resized_bitmap) { 152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 153 image_ = gfx::ImageSkia::CreateFrom1xBitmap(*resized_bitmap); 154 FOR_EACH_OBSERVER(WallpaperResizerObserver, observers_, 155 OnWallpaperResized()); 156 } 157 158 } // namespace ash 159