12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/desktop_background/wallpaper_resizer.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/desktop_background/wallpaper_resizer_observer.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/threading/sequenced_worker_pool.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/threading/worker_pool.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "third_party/skia/include/core/SkImage.h"
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/image/image_skia_rep.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/skia_util.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::BrowserThread;
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace ash {
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// For our scaling ratios we need to round positive numbers.
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int RoundPositive(double x) {
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return static_cast<int>(floor(x + 0.5));
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Resizes |orig_bitmap| to |target_size| using |layout| and stores the
28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// resulting bitmap at |resized_bitmap_out|.
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid Resize(SkBitmap orig_bitmap,
30868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)            const gfx::Size& target_size,
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            WallpaperLayout layout,
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            SkBitmap* resized_bitmap_out) {
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SkBitmap new_bitmap = orig_bitmap;
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const int orig_width = orig_bitmap.width();
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const int orig_height = orig_bitmap.height();
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const int new_width = target_size.width();
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const int new_height = target_size.height();
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (orig_width > new_width || orig_height > new_height) {
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    gfx::Rect wallpaper_rect(0, 0, orig_width, orig_height);
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    gfx::Size cropped_size = gfx::Size(std::min(new_width, orig_width),
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                       std::min(new_height, orig_height));
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    switch (layout) {
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case WALLPAPER_LAYOUT_CENTER:
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        wallpaper_rect.ClampToCenteredSize(cropped_size);
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        orig_bitmap.extractSubset(&new_bitmap,
49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  gfx::RectToSkIRect(wallpaper_rect));
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case WALLPAPER_LAYOUT_TILE:
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        wallpaper_rect.set_size(cropped_size);
53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        orig_bitmap.extractSubset(&new_bitmap,
54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  gfx::RectToSkIRect(wallpaper_rect));
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case WALLPAPER_LAYOUT_STRETCH:
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        new_bitmap = skia::ImageOperations::Resize(
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            orig_bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            new_width, new_height);
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case WALLPAPER_LAYOUT_CENTER_CROPPED:
62eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if (orig_width > new_width && orig_height > new_height) {
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // The dimension with the smallest ratio must be cropped, the other
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // one is preserved. Both are set in gfx::Size cropped_size.
65eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          double horizontal_ratio = static_cast<double>(new_width) /
66eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch              static_cast<double>(orig_width);
67eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          double vertical_ratio = static_cast<double>(new_height) /
68eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch              static_cast<double>(orig_height);
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (vertical_ratio > horizontal_ratio) {
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            cropped_size = gfx::Size(
72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                RoundPositive(static_cast<double>(new_width) / vertical_ratio),
73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                orig_height);
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else {
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            cropped_size = gfx::Size(orig_width, RoundPositive(
76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                static_cast<double>(new_height) / horizontal_ratio));
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
78868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          wallpaper_rect.ClampToCenteredSize(cropped_size);
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          SkBitmap sub_image;
80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          orig_bitmap.extractSubset(&sub_image,
81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                    gfx::RectToSkIRect(wallpaper_rect));
82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          new_bitmap = skia::ImageOperations::Resize(
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              sub_image, skia::ImageOperations::RESIZE_LANCZOS3,
84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch              new_width, new_height);
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
89eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  *resized_bitmap_out = new_bitmap;
90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  resized_bitmap_out->setImmutable();
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// static
96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)uint32_t WallpaperResizer::GetImageId(const gfx::ImageSkia& image) {
97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const gfx::ImageSkiaRep& image_rep = image.GetRepresentation(1.0f);
98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return image_rep.is_null() ? 0 : image_rep.sk_bitmap().getGenerationID();
99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochWallpaperResizer::WallpaperResizer(const gfx::ImageSkia& image,
102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                   const gfx::Size& target_size,
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   WallpaperLayout layout)
104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : image_(image),
105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      original_image_id_(GetImageId(image_)),
106868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      target_size_(target_size),
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      layout_(layout),
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {
109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  image_.MakeThreadSafe();
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)WallpaperResizer::~WallpaperResizer() {
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WallpaperResizer::StartResize() {
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
117eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SkBitmap* resized_bitmap = new SkBitmap;
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!content::BrowserThread::PostBlockingPoolTaskAndReply(
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          FROM_HERE,
120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          base::Bind(&Resize, *image_.bitmap(), target_size_,
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                     layout_, resized_bitmap),
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          base::Bind(&WallpaperResizer::OnResizeFinished,
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                     weak_ptr_factory_.GetWeakPtr(),
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                     base::Owned(resized_bitmap)))) {
125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    LOG(WARNING) << "PostSequencedWorkerTask failed. "
126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                 << "Wallpaper may not be resized.";
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WallpaperResizer::AddObserver(WallpaperResizerObserver* observer) {
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  observers_.AddObserver(observer);
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void WallpaperResizer::RemoveObserver(WallpaperResizerObserver* observer) {
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  observers_.RemoveObserver(observer);
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid WallpaperResizer::OnResizeFinished(SkBitmap* resized_bitmap) {
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  image_ = gfx::ImageSkia::CreateFrom1xBitmap(*resized_bitmap);
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(WallpaperResizerObserver, observers_,
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    OnWallpaperResized());
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} // namespace ash
146