#include "ui/gfx/image/image_skia.h"
#include <stddef.h>
#include <cmath>
#include <limits>
#include <memory>
#include "base/check.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/no_destructor.h"
#include "base/sequence_checker.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/resource/resource_scale_factor.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/gfx/image/image_skia_source.h"
#include "ui/gfx/switches.h"
namespace gfx {
namespace {
gfx::ImageSkiaRep& NullImageRep() {
static base::NoDestructor<ImageSkiaRep> null_image_rep;
return *null_image_rep;
}
}
namespace internal {
namespace {
ImageSkiaRep ScaleImageSkiaRep(const ImageSkiaRep& rep, float target_scale) {
if (rep.is_null() || rep.scale() == target_scale)
return rep;
gfx::Size scaled_size =
gfx::ScaleToCeiledSize(rep.pixel_size(), target_scale / rep.scale());
return ImageSkiaRep(
skia::ImageOperations::Resize(rep.GetBitmap(),
skia::ImageOperations::RESIZE_LANCZOS3,
scaled_size.width(), scaled_size.height()),
target_scale);
}
}
class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage> {
public:
ImageSkiaStorage(std::unique_ptr<ImageSkiaSource> source,
const gfx::Size& size);
ImageSkiaStorage(std::unique_ptr<ImageSkiaSource> source, float scale);
ImageSkiaStorage(const ImageSkiaStorage&) = delete;
ImageSkiaStorage& operator=(const ImageSkiaStorage&) = delete;
bool has_source() const { return source_ != nullptr; }
std::vector<gfx::ImageSkiaRep>& image_reps() { return image_reps_; }
const gfx::Size& size() const { return size_; }
bool read_only() const { return read_only_; }
void DeleteSource();
void SetReadOnly();
void DetachFromSequence();
bool CanModify() const;
bool CanRead() const;
void AddRepresentation(const ImageSkiaRep& image);
bool HasRepresentationAtAllScales() const;
std::vector<ImageSkiaRep>::const_iterator FindRepresentation(
float scale,
bool fetch_new_image) const;
private:
friend class base::RefCountedThreadSafe<ImageSkiaStorage>;
virtual ~ImageSkiaStorage();
std::vector<gfx::ImageSkiaRep> image_reps_;
std::unique_ptr<ImageSkiaSource> source_;
gfx::Size size_;
bool read_only_;
base::SequenceChecker sequence_checker_;
};
ImageSkiaStorage::ImageSkiaStorage(std::unique_ptr<ImageSkiaSource> source,
const gfx::Size& size)
: source_(std::move(source)), size_(size), read_only_(false) {}
ImageSkiaStorage::ImageSkiaStorage(std::unique_ptr<ImageSkiaSource> source,
float scale)
: source_(std::move(source)), read_only_(false) {
DCHECK(source_);
auto it = FindRepresentation(scale, true);
if (it == image_reps_.end() || it->is_null())
source_.reset();
else
size_.SetSize(it->GetWidth(), it->GetHeight());
}
void ImageSkiaStorage::DeleteSource() {
source_.reset();
}
void ImageSkiaStorage::SetReadOnly() {
read_only_ = true;
}
void ImageSkiaStorage::DetachFromSequence() {
sequence_checker_.DetachFromSequence();
}
bool ImageSkiaStorage::CanModify() const {
return !read_only_ && sequence_checker_.CalledOnValidSequence();
}
bool ImageSkiaStorage::CanRead() const {
return (read_only_ && !source_) || sequence_checker_.CalledOnValidSequence();
}
void ImageSkiaStorage::AddRepresentation(const ImageSkiaRep& image) {
DCHECK(!HasRepresentationAtAllScales());
if (image.scale() != 1.0f) {
ImageSkia::ImageSkiaReps::iterator it;
for (it = image_reps_.begin(); it < image_reps_.end(); ++it) {
if (it->unscaled()) {
DCHECK_EQ(1.0f, it->scale());
*it = ImageSkiaRep(it->GetBitmap(), it->scale());
break;
}
}
}
image_reps_.push_back(image);
}
bool ImageSkiaStorage::HasRepresentationAtAllScales() const {
return source_ && source_->HasRepresentationAtAllScales();
}
std::vector<ImageSkiaRep>::const_iterator ImageSkiaStorage::FindRepresentation(
float scale,
bool fetch_new_image) const {
TRACE_EVENT0("ui", "ImageSkiaStorage::FindRepresentation");
auto exact_iter = image_reps_.end();
auto closest_downscale_iter = image_reps_.end();
auto closest_upscale_iter = image_reps_.end();
float smallest_downscale_diff = std::numeric_limits<float>::max();
float smallest_upscale_diff = std::numeric_limits<float>::max();
for (auto it = image_reps_.begin(); it != image_reps_.end(); ++it) {
if (it->scale() == scale) {
fetch_new_image = false;
if (it->is_null()) {
continue;
}
exact_iter = it;
break;
}
if (it->is_null()) {
continue;
}
if (it->scale() > scale) {
float diff = it->scale() - scale;
if (diff < smallest_downscale_diff) {
closest_downscale_iter = it;
smallest_downscale_diff = diff;
}
} else {
float diff = scale - it->scale();
if (diff < smallest_upscale_diff) {
closest_upscale_iter = it;
smallest_upscale_diff = diff;
}
}
}
if (fetch_new_image && source_) {
DCHECK(sequence_checker_.CalledOnValidSequence())
<< "An ImageSkia with the source must be accessed by the same "
"sequence.";
auto* mutable_this = const_cast<ImageSkiaStorage*>(this);
ImageSkiaRep image;
float resource_scale = scale;
if (!HasRepresentationAtAllScales()) {
resource_scale = ui::GetScaleForResourceScaleFactor(
ui::GetSupportedResourceScaleFactorForRescale(scale));
}
if (scale != resource_scale) {
auto iter = FindRepresentation(resource_scale, fetch_new_image);
CHECK(iter != image_reps_.end());
image = iter->unscaled() ? (*iter) : ScaleImageSkiaRep(*iter, scale);
} else {
image = source_->GetImageForScale(scale);
if (image.is_null() && scale != 1.0f)
image = ScaleImageSkiaRep(source_->GetImageForScale(1.0f), scale);
}
if (!image.is_null() &&
!base::Contains(image_reps_, image.scale(), &ImageSkiaRep::scale)) {
mutable_this->image_reps_.push_back(image);
}
return FindRepresentation(!image.is_null() ? image.scale() : scale, false);
}
if (exact_iter != image_reps_.end()) {
return exact_iter;
}
if (closest_downscale_iter != image_reps_.end()) {
return closest_downscale_iter;
}
return closest_upscale_iter;
}
ImageSkiaStorage::~ImageSkiaStorage() = default;
}
ImageSkia::ImageSkia() = default;
ImageSkia::ImageSkia(std::unique_ptr<ImageSkiaSource> source,
const gfx::Size& size)
: storage_(
base::MakeRefCounted<internal::ImageSkiaStorage>(std::move(source),
size)) {
DCHECK(storage_->has_source());
DetachStorageFromSequence();
}
ImageSkia::ImageSkia(std::unique_ptr<ImageSkiaSource> source, float scale)
: storage_(
base::MakeRefCounted<internal::ImageSkiaStorage>(std::move(source),
scale)) {
if (!storage_->has_source())
storage_ = nullptr;
DetachStorageFromSequence();
}
ImageSkia::ImageSkia(const ImageSkiaRep& image_rep) {
DCHECK(!image_rep.is_null());
Init(image_rep);
DetachStorageFromSequence();
}
ImageSkia::ImageSkia(const ImageSkia& other) : storage_(other.storage_) {
}
ImageSkia& ImageSkia::operator=(const ImageSkia& other) {
storage_ = other.storage_;
return *this;
}
ImageSkia::~ImageSkia() {
}
ImageSkia ImageSkia::CreateFromBitmap(const SkBitmap& bitmap, float scale) {
if (bitmap.drawsNothing())
return ImageSkia();
return ImageSkia(ImageSkiaRep(bitmap, scale));
}
ImageSkia ImageSkia::CreateFrom1xBitmap(const SkBitmap& bitmap) {
if (bitmap.drawsNothing())
return ImageSkia();
return ImageSkia(ImageSkiaRep(bitmap, 0.0f));
}
ImageSkia ImageSkia::DeepCopy() const {
TRACE_EVENT0("ui", "ImageSkia::DeepCopy");
ImageSkia copy;
if (isNull())
return copy;
CHECK(CanRead());
std::vector<gfx::ImageSkiaRep>& reps = storage_->image_reps();
for (auto iter = reps.begin(); iter != reps.end(); ++iter) {
copy.AddRepresentation(*iter);
}
if (!copy.isNull())
copy.storage_->DetachFromSequence();
return copy;
}
bool ImageSkia::BackedBySameObjectAs(const gfx::ImageSkia& other) const {
return storage_.get() == other.storage_.get();
}
const void* ImageSkia::GetBackingObject() const {
return storage_.get();
}
void ImageSkia::AddRepresentation(const ImageSkiaRep& image_rep) {
DCHECK(!image_rep.is_null());
if (isNull()) {
Init(image_rep);
} else {
CHECK(CanModify());
storage_->AddRepresentation(image_rep);
}
}
void ImageSkia::RemoveRepresentation(float scale) {
if (isNull())
return;
CHECK(CanModify());
ImageSkiaReps& image_reps = storage_->image_reps();
auto it = storage_->FindRepresentation(scale, false);
if (it != image_reps.end() && it->scale() == scale)
image_reps.erase(it);
}
bool ImageSkia::HasRepresentation(float scale) const {
if (isNull())
return false;
CHECK(CanRead());
if (storage_->HasRepresentationAtAllScales())
return true;
auto it = storage_->FindRepresentation(scale, false);
return (it != storage_->image_reps().end() && it->scale() == scale);
}
const ImageSkiaRep& ImageSkia::GetRepresentation(float scale) const {
TRACE_EVENT0("ui", "ImageSkia::GetRepresentation");
if (isNull())
return NullImageRep();
CHECK(CanRead());
auto it = storage_->FindRepresentation(scale, true);
if (it == storage_->image_reps().end())
return NullImageRep();
return *it;
}
void ImageSkia::SetReadOnly() {
CHECK(storage_.get());
storage_->SetReadOnly();
DetachStorageFromSequence();
}
void ImageSkia::MakeThreadSafe() {
TRACE_EVENT0("ui", "ImageSkia::MakeThreadSafe");
CHECK(storage_.get());
EnsureRepsForSupportedScales();
if (storage_.get())
storage_->DeleteSource();
storage_->SetReadOnly();
CHECK(IsThreadSafe());
}
bool ImageSkia::IsThreadSafe() const {
return !storage_.get() || (storage_->read_only() && !storage_->has_source());
}
int ImageSkia::width() const {
return isNull() ? 0 : storage_->size().width();
}
gfx::Size ImageSkia::size() const {
return gfx::Size(width(), height());
}
int ImageSkia::height() const {
return isNull() ? 0 : storage_->size().height();
}
std::vector<ImageSkiaRep> ImageSkia::image_reps() const {
if (isNull())
return std::vector<ImageSkiaRep>();
CHECK(CanRead());
ImageSkiaReps internal_image_reps = storage_->image_reps();
ImageSkiaReps image_reps;
for (auto it = internal_image_reps.begin(); it != internal_image_reps.end();
++it) {
if (!it->is_null())
image_reps.push_back(*it);
}
return image_reps;
}
void ImageSkia::EnsureRepsForSupportedScales() const {
TRACE_EVENT0("ui", "ImageSkia::EnsureRepsForSupportedScales");
const std::vector<ui::ResourceScaleFactor>& supported_scales =
ui::GetSupportedResourceScaleFactors();
if (storage_.get() && storage_->has_source()) {
for (const auto scale : supported_scales) {
storage_->FindRepresentation(ui::GetScaleForResourceScaleFactor(scale),
true);
}
}
}
void ImageSkia::RemoveUnsupportedRepresentationsForScale(float scale) {
for (const ImageSkiaRep& image_rep_to_test : image_reps()) {
const float test_scale = image_rep_to_test.scale();
if (test_scale != scale &&
ui::GetScaleForResourceScaleFactor(
ui::GetSupportedResourceScaleFactorForRescale(test_scale)) ==
scale) {
RemoveRepresentation(test_scale);
}
}
}
bool ImageSkia::IsUniquelyOwned() const {
return storage_->HasOneRef();
}
void ImageSkia::Init(const ImageSkiaRep& image_rep) {
DCHECK(!image_rep.is_null());
storage_ = new internal::ImageSkiaStorage(
nullptr, gfx::Size(image_rep.GetWidth(), image_rep.GetHeight()));
storage_->image_reps().push_back(image_rep);
}
const SkBitmap& ImageSkia::GetBitmap() const {
TRACE_EVENT0("ui", "ImageSkia::GetBitmap");
if (isNull()) {
return NullImageRep().GetBitmap();
}
#if !BUILDFLAG(IS_WIN)
CHECK(CanRead());
#endif
auto it = storage_->FindRepresentation(1.0f, true);
if (it != storage_->image_reps().end())
return it->GetBitmap();
return NullImageRep().GetBitmap();
}
bool ImageSkia::CanRead() const {
return !storage_.get() || storage_->CanRead();
}
bool ImageSkia::CanModify() const {
return !storage_.get() || storage_->CanModify();
}
void ImageSkia::DetachStorageFromSequence() {
if (storage_.get())
storage_->DetachFromSequence();
}
}