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