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