// 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 #include "skia/ext/skia_utils_base.h" #include "ui/gfx/codec/jpeg_codec.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image_png_rep.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_rep.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; } DCHECK(false); 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; } DCHECK(false); return kUnknown_SkAlphaType; } // Compress as PNG. Requires post-multiplied alpha. bool PNGMethod(bool with_transparency, const SkBitmap& bitmap, std::vector* compressed) { return gfx::PNGCodec::Encode( reinterpret_cast(bitmap.getPixels()), bitmap.colorType() == kBGRA_8888_SkColorType ? gfx::PNGCodec::FORMAT_BGRA : gfx::PNGCodec::FORMAT_RGBA, gfx::Size(bitmap.width(), bitmap.height()), static_cast(bitmap.rowBytes()), bitmap.alphaType() == kOpaque_SkAlphaType || !with_transparency, std::vector(), 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* compressed) { return gfx::JPEGCodec::Encode(bitmap, quality, compressed); } } // namespace // static CefRefPtr 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 that) { CefImageImpl* that_impl = static_cast(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(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 bitmap(gfx::JPEGCodec::Decode( static_cast(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 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 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 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 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 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& bitmaps) { if (scale_1x_size == 0) { // Set the scale 1x size to the smallest bitmap pixel size. int32_t min_size = std::numeric_limits::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(size) / static_cast(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); // Convert to N32 (e.g. native encoding) format if not already in that format. // N32 is expected by the Views framework and this early conversion avoids // CHECKs in ImageSkiaRep and eventual conversion to N32 at some later point // in the compositing pipeline. SkBitmap n32_bitmap; if (!skia::SkBitmapToN32OpaqueOrPremul(bitmap, &n32_bitmap)) { return false; } gfx::ImageSkiaRep skia_rep(n32_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* compressed, 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 std::move(method).Run(*bitmap_ptr, compressed); } // static bool CefImageImpl::WritePNG(const SkBitmap& bitmap, std::vector* compressed, bool with_transparency) { return WriteCompressedFormat(bitmap, compressed, base::BindOnce(PNGMethod, with_transparency)); } // static bool CefImageImpl::WriteJPEG(const SkBitmap& bitmap, std::vector* compressed, int quality) { return WriteCompressedFormat(bitmap, compressed, base::BindOnce(JPEGMethod, quality)); }