blob: a4a77c90d5a9e6ad6ca62760a4319ab18564bbb5 [file] [log] [blame]
// Copyright (c) 2016 The Chromium Embedded Framework 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 "libcef/browser/image_impl.h"
#include <algorithm>
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/image/image_png_rep.h"
#include "ui/gfx/image/image_skia.h"
namespace {
SkColorType GetSkColorType(cef_color_type_t color_type) {
switch (color_type) {
case CEF_COLOR_TYPE_RGBA_8888:
return kRGBA_8888_SkColorType;
case CEF_COLOR_TYPE_BGRA_8888:
return kBGRA_8888_SkColorType;
default:
break;
}
NOTREACHED();
return kUnknown_SkColorType;
}
SkAlphaType GetSkAlphaType(cef_alpha_type_t alpha_type) {
switch (alpha_type) {
case CEF_ALPHA_TYPE_OPAQUE:
return kOpaque_SkAlphaType;
case CEF_ALPHA_TYPE_PREMULTIPLIED:
return kPremul_SkAlphaType;
case CEF_ALPHA_TYPE_POSTMULTIPLIED:
return kUnpremul_SkAlphaType;
default:
break;
}
NOTREACHED();
return kUnknown_SkAlphaType;
}
// Compress as PNG. Requires post-multiplied alpha.
bool PNGMethod(bool with_transparency,
const SkBitmap& bitmap,
std::vector<unsigned char>* compressed) {
return gfx::PNGCodec::Encode(
reinterpret_cast<unsigned char*>(bitmap.getPixels()),
bitmap.colorType() == kBGRA_8888_SkColorType ? gfx::PNGCodec::FORMAT_BGRA
: gfx::PNGCodec::FORMAT_RGBA,
gfx::Size(bitmap.width(), bitmap.height()),
static_cast<int>(bitmap.rowBytes()),
bitmap.alphaType() == kOpaque_SkAlphaType || !with_transparency,
std::vector<gfx::PNGCodec::Comment>(), compressed);
}
// Compress as JPEG. This internally uses JCS_EXT_RGBX or JCS_EXT_BGRX which
// causes the alpha channel to be ignored. Requires post-multiplied alpha.
bool JPEGMethod(int quality,
const SkBitmap& bitmap,
std::vector<unsigned char>* compressed) {
return gfx::JPEGCodec::Encode(bitmap, quality, compressed);
}
} // namespace
// static
CefRefPtr<CefImage> CefImage::CreateImage() {
return new CefImageImpl();
}
CefImageImpl::CefImageImpl(const gfx::ImageSkia& image_skia)
: image_(image_skia) {}
bool CefImageImpl::IsEmpty() {
base::AutoLock lock_scope(lock_);
return image_.IsEmpty();
}
bool CefImageImpl::IsSame(CefRefPtr<CefImage> that) {
CefImageImpl* that_impl = static_cast<CefImageImpl*>(that.get());
if (!that_impl)
return false;
// Quick check for the same object.
if (this == that_impl)
return true;
base::AutoLock lock_scope(lock_);
return image_.AsImageSkia().BackedBySameObjectAs(
that_impl->image_.AsImageSkia());
}
bool CefImageImpl::AddBitmap(float scale_factor,
int pixel_width,
int pixel_height,
cef_color_type_t color_type,
cef_alpha_type_t alpha_type,
const void* pixel_data,
size_t pixel_data_size) {
const SkColorType ct = GetSkColorType(color_type);
const SkAlphaType at = GetSkAlphaType(alpha_type);
// Make sure the client passed in the expected values.
if (ct != kBGRA_8888_SkColorType && ct != kRGBA_8888_SkColorType)
return false;
if (pixel_data_size != pixel_width * pixel_height * 4U)
return false;
SkBitmap bitmap;
if (!bitmap.tryAllocPixels(
SkImageInfo::Make(pixel_width, pixel_height, ct, at))) {
return false;
}
DCHECK_EQ(pixel_data_size, bitmap.computeByteSize());
memcpy(bitmap.getPixels(), pixel_data, pixel_data_size);
return AddBitmap(scale_factor, bitmap);
}
bool CefImageImpl::AddPNG(float scale_factor,
const void* png_data,
size_t png_data_size) {
SkBitmap bitmap;
if (!gfx::PNGCodec::Decode(static_cast<const unsigned char*>(png_data),
png_data_size, &bitmap)) {
return false;
}
return AddBitmap(scale_factor, bitmap);
}
bool CefImageImpl::AddJPEG(float scale_factor,
const void* jpeg_data,
size_t jpeg_data_size) {
std::unique_ptr<SkBitmap> bitmap(gfx::JPEGCodec::Decode(
static_cast<const unsigned char*>(jpeg_data), jpeg_data_size));
if (!bitmap.get())
return false;
return AddBitmap(scale_factor, *bitmap);
}
size_t CefImageImpl::GetWidth() {
base::AutoLock lock_scope(lock_);
return image_.Width();
}
size_t CefImageImpl::GetHeight() {
base::AutoLock lock_scope(lock_);
return image_.Height();
}
bool CefImageImpl::HasRepresentation(float scale_factor) {
base::AutoLock lock_scope(lock_);
return image_.AsImageSkia().HasRepresentation(scale_factor);
}
bool CefImageImpl::RemoveRepresentation(float scale_factor) {
base::AutoLock lock_scope(lock_);
gfx::ImageSkia image_skia = image_.AsImageSkia();
if (image_skia.HasRepresentation(scale_factor)) {
image_skia.RemoveRepresentation(scale_factor);
return true;
}
return false;
}
bool CefImageImpl::GetRepresentationInfo(float scale_factor,
float& actual_scale_factor,
int& pixel_width,
int& pixel_height) {
base::AutoLock lock_scope(lock_);
gfx::ImageSkia image_skia = image_.AsImageSkia();
if (image_skia.isNull())
return false;
const gfx::ImageSkiaRep& rep = image_skia.GetRepresentation(scale_factor);
if (rep.is_null())
return false;
actual_scale_factor = rep.scale();
pixel_width = rep.GetBitmap().width();
pixel_height = rep.GetBitmap().height();
return true;
}
CefRefPtr<CefBinaryValue> CefImageImpl::GetAsBitmap(float scale_factor,
cef_color_type_t color_type,
cef_alpha_type_t alpha_type,
int& pixel_width,
int& pixel_height) {
const SkColorType desired_ct = GetSkColorType(color_type);
const SkAlphaType desired_at = GetSkAlphaType(alpha_type);
base::AutoLock lock_scope(lock_);
const SkBitmap* bitmap = GetBitmap(scale_factor);
if (!bitmap)
return nullptr;
DCHECK(bitmap->readyToDraw());
pixel_width = bitmap->width();
pixel_height = bitmap->height();
if (bitmap->colorType() == desired_ct && bitmap->alphaType() == desired_at) {
// No conversion necessary.
return CefBinaryValue::Create(bitmap->getPixels(),
bitmap->computeByteSize());
} else {
SkBitmap desired_bitmap;
if (!ConvertBitmap(*bitmap, &desired_bitmap, desired_ct, desired_at))
return nullptr;
DCHECK(desired_bitmap.readyToDraw());
return CefBinaryValue::Create(desired_bitmap.getPixels(),
desired_bitmap.computeByteSize());
}
}
CefRefPtr<CefBinaryValue> CefImageImpl::GetAsPNG(float scale_factor,
bool with_transparency,
int& pixel_width,
int& pixel_height) {
base::AutoLock lock_scope(lock_);
const SkBitmap* bitmap = GetBitmap(scale_factor);
if (!bitmap)
return nullptr;
std::vector<unsigned char> compressed;
if (!WritePNG(*bitmap, &compressed, with_transparency))
return nullptr;
pixel_width = bitmap->width();
pixel_height = bitmap->height();
return CefBinaryValue::Create(&compressed.front(), compressed.size());
}
CefRefPtr<CefBinaryValue> CefImageImpl::GetAsJPEG(float scale_factor,
int quality,
int& pixel_width,
int& pixel_height) {
base::AutoLock lock_scope(lock_);
const SkBitmap* bitmap = GetBitmap(scale_factor);
if (!bitmap)
return nullptr;
std::vector<unsigned char> compressed;
if (!WriteJPEG(*bitmap, &compressed, quality))
return nullptr;
pixel_width = bitmap->width();
pixel_height = bitmap->height();
return CefBinaryValue::Create(&compressed.front(), compressed.size());
}
void CefImageImpl::AddBitmaps(int32_t scale_1x_size,
const std::vector<SkBitmap>& bitmaps) {
if (scale_1x_size == 0) {
// Set the scale 1x size to the smallest bitmap pixel size.
int32_t min_size = std::numeric_limits<int32_t>::max();
for (const SkBitmap& bitmap : bitmaps) {
const int32_t size = std::max(bitmap.width(), bitmap.height());
if (size < min_size)
min_size = size;
}
scale_1x_size = min_size;
}
for (const SkBitmap& bitmap : bitmaps) {
const int32_t size = std::max(bitmap.width(), bitmap.height());
const float scale_factor =
static_cast<float>(size) / static_cast<float>(scale_1x_size);
AddBitmap(scale_factor, bitmap);
}
}
gfx::ImageSkia CefImageImpl::GetForced1xScaleRepresentation(
float scale_factor) const {
base::AutoLock lock_scope(lock_);
if (scale_factor == 1.0f) {
// We can use the existing image without modification.
return image_.AsImageSkia();
}
const SkBitmap* bitmap = GetBitmap(scale_factor);
gfx::ImageSkia image_skia;
if (bitmap)
image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap, 1.0f));
return image_skia;
}
gfx::ImageSkia CefImageImpl::AsImageSkia() const {
base::AutoLock lock_scope(lock_);
return image_.AsImageSkia();
}
bool CefImageImpl::AddBitmap(float scale_factor, const SkBitmap& bitmap) {
#if DCHECK_IS_ON()
DCHECK(bitmap.readyToDraw());
#endif
DCHECK(bitmap.colorType() == kBGRA_8888_SkColorType ||
bitmap.colorType() == kRGBA_8888_SkColorType);
gfx::ImageSkiaRep skia_rep(bitmap, scale_factor);
base::AutoLock lock_scope(lock_);
if (image_.IsEmpty()) {
image_ = gfx::Image(gfx::ImageSkia(skia_rep));
} else {
image_.AsImageSkia().AddRepresentation(skia_rep);
}
return true;
}
const SkBitmap* CefImageImpl::GetBitmap(float scale_factor) const {
lock_.AssertAcquired();
gfx::ImageSkia image_skia = image_.AsImageSkia();
if (image_skia.isNull())
return nullptr;
const gfx::ImageSkiaRep& rep = image_skia.GetRepresentation(scale_factor);
if (rep.is_null())
return nullptr;
return &rep.GetBitmap();
}
// static
bool CefImageImpl::ConvertBitmap(const SkBitmap& src_bitmap,
SkBitmap* target_bitmap,
SkColorType target_ct,
SkAlphaType target_at) {
DCHECK(src_bitmap.readyToDraw());
DCHECK(src_bitmap.colorType() != target_ct ||
src_bitmap.alphaType() != target_at);
DCHECK(target_bitmap);
SkImageInfo target_info = SkImageInfo::Make(
src_bitmap.width(), src_bitmap.height(), target_ct, target_at);
if (!target_bitmap->tryAllocPixels(target_info))
return false;
if (!src_bitmap.readPixels(target_info, target_bitmap->getPixels(),
target_bitmap->rowBytes(), 0, 0)) {
return false;
}
DCHECK(target_bitmap->readyToDraw());
return true;
}
// static
bool CefImageImpl::WriteCompressedFormat(const SkBitmap& bitmap,
std::vector<unsigned char>* compressed,
const CompressionMethod& method) {
const SkBitmap* bitmap_ptr = nullptr;
SkBitmap bitmap_postalpha;
if (bitmap.alphaType() == kPremul_SkAlphaType) {
// Compression methods require post-multiplied alpha values.
if (!ConvertBitmap(bitmap, &bitmap_postalpha, bitmap.colorType(),
kUnpremul_SkAlphaType)) {
return false;
}
bitmap_ptr = &bitmap_postalpha;
} else {
bitmap_ptr = &bitmap;
}
DCHECK(bitmap_ptr->readyToDraw());
DCHECK(bitmap_ptr->colorType() == kBGRA_8888_SkColorType ||
bitmap_ptr->colorType() == kRGBA_8888_SkColorType);
DCHECK(bitmap_ptr->alphaType() == kOpaque_SkAlphaType ||
bitmap_ptr->alphaType() == kUnpremul_SkAlphaType);
return method.Run(*bitmap_ptr, compressed);
}
// static
bool CefImageImpl::WritePNG(const SkBitmap& bitmap,
std::vector<unsigned char>* compressed,
bool with_transparency) {
return WriteCompressedFormat(bitmap, compressed,
base::Bind(PNGMethod, with_transparency));
}
// static
bool CefImageImpl::WriteJPEG(const SkBitmap& bitmap,
std::vector<unsigned char>* compressed,
int quality) {
return WriteCompressedFormat(bitmap, compressed,
base::Bind(JPEGMethod, quality));
}