blob: f598390ccb80300c238dce21319fd5e9fdcaa13b [file] [log] [blame]
// Copyright 2017 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_platform.h"
#include <memory>
#include <set>
#include <utility>
#include "base/logging.h"
#include "ui/gfx/codec/png_codec.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|.
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;
if (!PNGCodec::Decode(raw_data->front(), raw_data->size(), &bitmap)) {
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);
scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes());
if (image_skia_rep.scale() != 1.0f ||
!PNGCodec::EncodeBGRASkBitmap(image_skia_rep.GetBitmap(), false,
&png_bytes->data())) {
return nullptr;
}
return png_bytes;
}
} // namespace internal
} // namespace gfx