1// Copyright 2014 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 "chrome/browser/chromeos/login/users/avatar/user_image_loader.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/files/file_path.h"
10#include "base/files/file_util.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "base/sequenced_task_runner.h"
14#include "chrome/browser/chromeos/login/helper.h"
15#include "components/user_manager/user_image/user_image.h"
16#include "skia/ext/image_operations.h"
17#include "third_party/skia/include/core/SkBitmap.h"
18#include "ui/gfx/codec/png_codec.h"
19#include "ui/gfx/skbitmap_operations.h"
20
21namespace chromeos {
22
23UserImageLoader::ImageInfo::ImageInfo(const std::string& file_path,
24                                      int pixels_per_side,
25                                      const LoadedCallback& loaded_cb)
26    : file_path(file_path),
27      pixels_per_side(pixels_per_side),
28      loaded_cb(loaded_cb) {
29}
30
31UserImageLoader::ImageInfo::~ImageInfo() {
32}
33
34UserImageLoader::UserImageLoader(
35    ImageDecoder::ImageCodec image_codec,
36    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
37    : foreground_task_runner_(base::MessageLoopProxy::current()),
38      background_task_runner_(background_task_runner),
39      image_codec_(image_codec) {
40}
41
42UserImageLoader::~UserImageLoader() {
43}
44
45void UserImageLoader::Start(const std::string& filepath,
46                            int pixels_per_side,
47                            const LoadedCallback& loaded_cb) {
48  background_task_runner_->PostTask(
49      FROM_HERE,
50      base::Bind(&UserImageLoader::ReadAndDecodeImage,
51                 this,
52                 ImageInfo(filepath, pixels_per_side, loaded_cb)));
53}
54
55void UserImageLoader::Start(scoped_ptr<std::string> data,
56                            int pixels_per_side,
57                            const LoadedCallback& loaded_cb) {
58  background_task_runner_->PostTask(
59      FROM_HERE,
60      base::Bind(&UserImageLoader::DecodeImage,
61                 this,
62                 base::Passed(&data),
63                 ImageInfo(std::string(), pixels_per_side, loaded_cb)));
64}
65
66void UserImageLoader::ReadAndDecodeImage(const ImageInfo& image_info) {
67  DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
68
69  scoped_ptr<std::string> data(new std::string);
70  if (!base::ReadFileToString(base::FilePath(image_info.file_path), data.get()))
71    LOG(ERROR) << "Failed to read image " << image_info.file_path;
72
73  // In case ReadFileToString() fails, |data| is empty and DecodeImage() calls
74  // back to OnDecodeImageFailed().
75  DecodeImage(data.Pass(), image_info);
76}
77
78void UserImageLoader::DecodeImage(const scoped_ptr<std::string> data,
79                                  const ImageInfo& image_info) {
80  DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
81
82  scoped_refptr<ImageDecoder> image_decoder =
83      new ImageDecoder(this, *data, image_codec_);
84  image_info_map_.insert(std::make_pair(image_decoder.get(), image_info));
85  image_decoder->Start(background_task_runner_);
86}
87
88void UserImageLoader::OnImageDecoded(const ImageDecoder* decoder,
89                                     const SkBitmap& decoded_image) {
90  DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
91
92  ImageInfoMap::iterator it = image_info_map_.find(decoder);
93  if (it == image_info_map_.end()) {
94    NOTREACHED();
95    return;
96  }
97  const std::string file_path = it->second.file_path;
98  const int target_size = it->second.pixels_per_side;
99  const LoadedCallback loaded_cb = it->second.loaded_cb;
100  image_info_map_.erase(it);
101
102  SkBitmap final_image = decoded_image;
103
104  if (target_size > 0) {
105    // Auto crop the image, taking the largest square in the center.
106    int pixels_per_side =
107        std::min(decoded_image.width(), decoded_image.height());
108    int x = (decoded_image.width() - pixels_per_side) / 2;
109    int y = (decoded_image.height() - pixels_per_side) / 2;
110    SkBitmap cropped_image = SkBitmapOperations::CreateTiledBitmap(
111        decoded_image, x, y, pixels_per_side, pixels_per_side);
112    if (pixels_per_side > target_size) {
113      // Also downsize the image to save space and memory.
114      final_image =
115          skia::ImageOperations::Resize(cropped_image,
116                                        skia::ImageOperations::RESIZE_LANCZOS3,
117                                        target_size,
118                                        target_size);
119    } else {
120      final_image = cropped_image;
121    }
122  }
123  // Make the SkBitmap immutable as we won't modify it. This is important
124  // because otherwise it gets duplicated during painting, wasting memory.
125  final_image.setImmutable();
126  gfx::ImageSkia final_image_skia =
127      gfx::ImageSkia::CreateFrom1xBitmap(final_image);
128  final_image_skia.MakeThreadSafe();
129  user_manager::UserImage user_image(final_image_skia,
130                                     decoder->get_image_data());
131  user_image.set_file_path(file_path);
132  if (image_codec_ == ImageDecoder::ROBUST_JPEG_CODEC)
133    user_image.MarkAsSafe();
134  foreground_task_runner_->PostTask(FROM_HERE,
135                                    base::Bind(loaded_cb, user_image));
136}
137
138void UserImageLoader::OnDecodeImageFailed(const ImageDecoder* decoder) {
139  DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
140
141  ImageInfoMap::iterator it = image_info_map_.find(decoder);
142  if (it == image_info_map_.end()) {
143    NOTREACHED();
144    return;
145  }
146  const LoadedCallback loaded_cb = it->second.loaded_cb;
147  image_info_map_.erase(it);
148
149  foreground_task_runner_->PostTask(
150      FROM_HERE, base::Bind(loaded_cb, user_manager::UserImage()));
151}
152
153}  // namespace chromeos
154