blob: 1ce65c60f07a1a9a44bc23be39d170af5089e051 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <set>
#include <utility>
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_refptr.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/image/image_platform.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/gfx/image/image_skia_source.h"
namespace gfx {
namespace internal {
namespace {
// Returns a 16x16 red image to visually show error in decoding PNG.
ImageSkia GetErrorImageSkia() {
SkBitmap bitmap;
bitmap.allocN32Pixels(16, 16);
bitmap.eraseARGB(0xff, 0xff, 0, 0);
return ImageSkia(ImageSkiaRep(bitmap, 1.0f));
}
class PNGImageSource : public ImageSkiaSource {
public:
PNGImageSource() {}
PNGImageSource(const PNGImageSource&) = delete;
PNGImageSource& operator=(const PNGImageSource&) = delete;
~PNGImageSource() override {}
ImageSkiaRep GetImageForScale(float scale) override {
if (image_skia_reps_.empty())
return ImageSkiaRep();
const ImageSkiaRep* rep = nullptr;
// gfx::ImageSkia passes one of the resource scale factors. The source
// should return:
// 1) The ImageSkiaRep with the highest scale if all available
// scales are smaller than |scale|.
// 2) The ImageSkiaRep with the smallest one that is larger than |scale|.
// TODO(crbug.com/329953472): Use a predefined threshold.
for (auto iter = image_skia_reps_.begin(); iter != image_skia_reps_.end();
++iter) {
if ((*iter).scale() == scale)
return (*iter);
if (!rep || rep->scale() < (*iter).scale())
rep = &(*iter);
if (rep->scale() >= scale)
break;
}
return rep ? *rep : ImageSkiaRep();
}
const gfx::Size size() const { return size_; }
bool AddPNGData(const ImagePNGRep& png_rep) {
const gfx::ImageSkiaRep rep = ToImageSkiaRep(png_rep);
if (rep.is_null())
return false;
if (size_.IsEmpty())
size_ = gfx::Size(rep.GetWidth(), rep.GetHeight());
image_skia_reps_.insert(rep);
return true;
}
static ImageSkiaRep ToImageSkiaRep(const ImagePNGRep& png_rep) {
scoped_refptr<base::RefCountedMemory> raw_data = png_rep.raw_data;
CHECK(raw_data.get());
SkBitmap bitmap = PNGCodec::Decode(*raw_data);
if (bitmap.isNull()) {
LOG(ERROR) << "Unable to decode PNG for " << png_rep.scale << ".";
return ImageSkiaRep();
}
return ImageSkiaRep(bitmap, png_rep.scale);
}
private:
struct Compare {
bool operator()(const ImageSkiaRep& rep1, const ImageSkiaRep& rep2) const {
return rep1.scale() < rep2.scale();
}
};
typedef std::set<ImageSkiaRep, Compare> ImageSkiaRepSet;
ImageSkiaRepSet image_skia_reps_;
gfx::Size size_;
};
} // namespace
ImageSkia ImageSkiaFromPNG(const std::vector<ImagePNGRep>& image_png_reps) {
if (image_png_reps.empty())
return GetErrorImageSkia();
std::unique_ptr<PNGImageSource> image_source(new PNGImageSource);
for (size_t i = 0; i < image_png_reps.size(); ++i) {
if (!image_source->AddPNGData(image_png_reps[i]))
return GetErrorImageSkia();
}
const gfx::Size& size = image_source->size();
DCHECK(!size.IsEmpty());
if (size.IsEmpty())
return GetErrorImageSkia();
return ImageSkia(std::move(image_source), size);
}
scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia(
const ImageSkia* image_skia) {
ImageSkiaRep image_skia_rep = image_skia->GetRepresentation(1.0f);
if (image_skia_rep.scale() != 1.0f) {
return nullptr;
}
std::optional<std::vector<uint8_t>> result = PNGCodec::EncodeBGRASkBitmap(
image_skia_rep.GetBitmap(), /*discard_transparency=*/false);
if (!result) {
return nullptr;
}
return base::MakeRefCounted<base::RefCountedBytes>(std::move(result.value()));
}
} // namespace internal
} // namespace gfx