1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/image/image_family.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <cmath>
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/image/image.h"
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/image/image_skia.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/size.h"
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace gfx {
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)ImageFamily::const_iterator::const_iterator() {}
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)ImageFamily::const_iterator::const_iterator(const const_iterator& other)
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : map_iterator_(other.map_iterator_) {}
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)ImageFamily::const_iterator::const_iterator(
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::map<MapKey, gfx::Image>::const_iterator& other)
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : map_iterator_(other) {}
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
24a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ImageFamily::const_iterator::~const_iterator() {}
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)ImageFamily::ImageFamily() {}
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)ImageFamily::~ImageFamily() {}
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ImageFamily::Add(const gfx::Image& image) {
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Size size = image.Size();
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (size.IsEmpty()) {
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    map_[MapKey(1.0f, 0)] = image;
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    float aspect = static_cast<float>(size.width()) / size.height();
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK_GT(aspect, 0.0f);
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    map_[MapKey(aspect, size.width())] = image;
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ImageFamily::Add(const gfx::ImageSkia& image_skia) {
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Add(gfx::Image(image_skia));
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const gfx::Image* ImageFamily::GetBest(int width, int height) const {
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (map_.empty())
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return NULL;
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If either |width| or |height| is 0, both are.
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  float desired_aspect;
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (height == 0 || width == 0) {
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    desired_aspect = 1.0f;
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    height = 0;
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    width = 0;
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    desired_aspect = static_cast<float>(width) / height;
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK_GT(desired_aspect, 0.0f);
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  float closest_aspect = GetClosestAspect(desired_aspect);
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If thinner than desired, search for images with width such that the
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // corresponding height is greater than or equal to the desired |height|.
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int desired_width = closest_aspect <= desired_aspect ?
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      width : static_cast<int>(ceilf(height * closest_aspect));
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Get the best-sized image with the aspect ratio.
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return GetWithExactAspect(closest_aspect, desired_width);
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)float ImageFamily::GetClosestAspect(float desired_aspect) const {
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Find the two aspect ratios on either side of |desired_aspect|.
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::map<MapKey, gfx::Image>::const_iterator greater_or_equal =
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      map_.lower_bound(MapKey(desired_aspect, 0));
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Early exit optimization if there is an exact match.
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (greater_or_equal != map_.end() &&
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      greater_or_equal->first.aspect() == desired_aspect) {
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return desired_aspect;
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // No exact match; |greater_or_equal| will point to the first image with
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // aspect ratio >= |desired_aspect|, and |less_than| will point to the last
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // image with aspect ratio < |desired_aspect|.
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (greater_or_equal != map_.begin()) {
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    std::map<MapKey, gfx::Image>::const_iterator less_than =
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        greater_or_equal;
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    --less_than;
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    float thinner_aspect = less_than->first.aspect();
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK_GT(thinner_aspect, 0.0f);
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK_LT(thinner_aspect, desired_aspect);
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (greater_or_equal != map_.end()) {
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      float wider_aspect = greater_or_equal->first.aspect();
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DCHECK_GT(wider_aspect, desired_aspect);
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if ((wider_aspect / desired_aspect) < (desired_aspect / thinner_aspect))
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return wider_aspect;
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return thinner_aspect;
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // No aspect ratio is less than or equal to |desired_aspect|.
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK(greater_or_equal != map_.end());
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    float wider_aspect = greater_or_equal->first.aspect();
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK_GT(wider_aspect, desired_aspect);
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return wider_aspect;
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const gfx::Image* ImageFamily::GetBest(const gfx::Size& size) const {
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return GetBest(size.width(), size.height());
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const gfx::Image* ImageFamily::GetWithExactAspect(float aspect,
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                                  int width) const {
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Find the two images of given aspect ratio on either side of |width|.
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::map<MapKey, gfx::Image>::const_iterator greater_or_equal =
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      map_.lower_bound(MapKey(aspect, width));
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (greater_or_equal != map_.end() &&
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      greater_or_equal->first.aspect() == aspect) {
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // We have found the smallest image of the same size or greater.
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return &greater_or_equal->second;
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(greater_or_equal != map_.begin());
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::map<MapKey, gfx::Image>::const_iterator less_than = greater_or_equal;
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  --less_than;
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // This must be true because there must be at least one image with |aspect|.
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK_EQ(less_than->first.aspect(), aspect);
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // We have found the largest image smaller than desired.
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return &less_than->second;
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace gfx
131