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
17using content::BrowserThread;
18
19namespace ash {
20namespace {
21
22// For our scaling ratios we need to round positive numbers.
23int 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|.
29void 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
96uint32_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
101WallpaperResizer::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
112WallpaperResizer::~WallpaperResizer() {
113}
114
115void 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
130void WallpaperResizer::AddObserver(WallpaperResizerObserver* observer) {
131  observers_.AddObserver(observer);
132}
133
134void WallpaperResizer::RemoveObserver(WallpaperResizerObserver* observer) {
135  observers_.RemoveObserver(observer);
136}
137
138void 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