summaryrefslogtreecommitdiff
path: root/chromium/ui/gfx/image/image_family.cc
blob: b9d28bddd9df387e8b7fbc99544379de6b95e79a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/image/image_family.h"

#include <cmath>

#include "skia/ext/image_operations.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"

namespace gfx {

ImageFamily::const_iterator::const_iterator() {}

ImageFamily::const_iterator::const_iterator(const const_iterator& other)
    : map_iterator_(other.map_iterator_) {}

ImageFamily::const_iterator::const_iterator(
    const std::map<MapKey, gfx::Image>::const_iterator& other)
    : map_iterator_(other) {}

ImageFamily::const_iterator::~const_iterator() {}

ImageFamily::ImageFamily() {}
ImageFamily::~ImageFamily() {}

void ImageFamily::Add(const gfx::Image& image) {
  gfx::Size size = image.Size();
  if (size.IsEmpty()) {
    map_[MapKey(1.0f, 0)] = image;
  } else {
    float aspect = static_cast<float>(size.width()) / size.height();
    DCHECK_GT(aspect, 0.0f);
    map_[MapKey(aspect, size.width())] = image;
  }
}

void ImageFamily::Add(const gfx::ImageSkia& image_skia) {
  Add(gfx::Image(image_skia));
}

const gfx::Image* ImageFamily::GetBest(int width, int height) const {
  if (map_.empty())
    return NULL;

  // If either |width| or |height| is 0, both are.
  float desired_aspect;
  if (height == 0 || width == 0) {
    desired_aspect = 1.0f;
    height = 0;
    width = 0;
  } else {
    desired_aspect = static_cast<float>(width) / height;
  }
  DCHECK_GT(desired_aspect, 0.0f);

  float closest_aspect = GetClosestAspect(desired_aspect);

  // If thinner than desired, search for images with width such that the
  // corresponding height is greater than or equal to the desired |height|.
  int desired_width = closest_aspect <= desired_aspect ?
      width : static_cast<int>(ceilf(height * closest_aspect));

  // Get the best-sized image with the aspect ratio.
  return GetWithExactAspect(closest_aspect, desired_width);
}

float ImageFamily::GetClosestAspect(float desired_aspect) const {
  // Find the two aspect ratios on either side of |desired_aspect|.
  std::map<MapKey, gfx::Image>::const_iterator greater_or_equal =
      map_.lower_bound(MapKey(desired_aspect, 0));
  // Early exit optimization if there is an exact match.
  if (greater_or_equal != map_.end() &&
      greater_or_equal->first.aspect() == desired_aspect) {
    return desired_aspect;
  }

  // No exact match; |greater_or_equal| will point to the first image with
  // aspect ratio >= |desired_aspect|, and |less_than| will point to the last
  // image with aspect ratio < |desired_aspect|.
  if (greater_or_equal != map_.begin()) {
    std::map<MapKey, gfx::Image>::const_iterator less_than =
        greater_or_equal;
    --less_than;
    float thinner_aspect = less_than->first.aspect();
    DCHECK_GT(thinner_aspect, 0.0f);
    DCHECK_LT(thinner_aspect, desired_aspect);
    if (greater_or_equal != map_.end()) {
      float wider_aspect = greater_or_equal->first.aspect();
      DCHECK_GT(wider_aspect, desired_aspect);
      if ((wider_aspect / desired_aspect) < (desired_aspect / thinner_aspect))
        return wider_aspect;
    }
    return thinner_aspect;
  } else {
    // No aspect ratio is less than or equal to |desired_aspect|.
    DCHECK(greater_or_equal != map_.end());
    float wider_aspect = greater_or_equal->first.aspect();
    DCHECK_GT(wider_aspect, desired_aspect);
    return wider_aspect;
  }
}

const gfx::Image* ImageFamily::GetBest(const gfx::Size& size) const {
  return GetBest(size.width(), size.height());
}

gfx::Image ImageFamily::CreateExact(int width, int height) const {
  // Resize crashes if width or height is 0, so just return an empty image.
  if (width == 0 || height == 0)
    return gfx::Image();

  const gfx::Image* image = GetBest(width, height);
  if (!image)
    return gfx::Image();

  if (image->Width() == width && image->Height() == height) {
    // Make a copy at gfx::ImageSkia level, so that resulting image's ref count
    // is not racy to |image|. Since this function can run on a different thread
    // than the thread |image| created on, we should not touch the
    // non-thread-safe ref count in gfx::Image here.
    std::unique_ptr<gfx::ImageSkia> image_skia(image->CopyImageSkia());
    return gfx::Image(*image_skia);
  }

  SkBitmap bitmap = image->AsBitmap();
  SkBitmap resized_bitmap = skia::ImageOperations::Resize(
      bitmap, skia::ImageOperations::RESIZE_LANCZOS3, width, height);
  return gfx::Image::CreateFrom1xBitmap(resized_bitmap);
}

gfx::Image ImageFamily::CreateExact(const gfx::Size& size) const {
  return CreateExact(size.width(), size.height());
}

const gfx::Image* ImageFamily::GetWithExactAspect(float aspect,
                                                  int width) const {
  // Find the two images of given aspect ratio on either side of |width|.
  std::map<MapKey, gfx::Image>::const_iterator greater_or_equal =
      map_.lower_bound(MapKey(aspect, width));
  if (greater_or_equal != map_.end() &&
      greater_or_equal->first.aspect() == aspect) {
    // We have found the smallest image of the same size or greater.
    return &greater_or_equal->second;
  }

  DCHECK(greater_or_equal != map_.begin());
  std::map<MapKey, gfx::Image>::const_iterator less_than = greater_or_equal;
  --less_than;
  // This must be true because there must be at least one image with |aspect|.
  DCHECK_EQ(less_than->first.aspect(), aspect);
  // We have found the largest image smaller than desired.
  return &less_than->second;
}

}  // namespace gfx