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 160 161 162 163 164 165
|
// Copyright 2013 The Chromium Authors
// 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 "base/check_op.h"
#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() = default;
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() = default;
ImageFamily::ImageFamily() = default;
ImageFamily::ImageFamily(ImageFamily&& other) = default;
ImageFamily::~ImageFamily() = default;
ImageFamily& ImageFamily::operator=(ImageFamily&& other) = default;
ImageFamily ImageFamily::Clone() const {
ImageFamily clone;
clone.map_ = map_;
return clone;
}
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|.
auto 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()) {
auto 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.
return gfx::Image(image->AsImageSkia());
}
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|.
auto 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());
auto 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
|