wallpaper_resizer.cc revision f2477e01787aa58f445919b809d89e252beef54f
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
18using content::BrowserThread;
19
20namespace ash {
21namespace {
22
23// For our scaling ratios we need to round positive numbers.
24int 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|.
30void 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
97uint32_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
102WallpaperResizer::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
114WallpaperResizer::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
125WallpaperResizer::~WallpaperResizer() {
126}
127
128void 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
143void WallpaperResizer::AddObserver(WallpaperResizerObserver* observer) {
144  observers_.AddObserver(observer);
145}
146
147void WallpaperResizer::RemoveObserver(WallpaperResizerObserver* observer) {
148  observers_.RemoveObserver(observer);
149}
150
151void 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