// 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 "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* 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() { CEF_REQUIRE_UIT_RETURN(nullptr); return new CefImageImpl(); } CefImageImpl::CefImageImpl() { CEF_REQUIRE_UIT(); } CefImageImpl::CefImageImpl(const gfx::ImageSkia& image_skia) : image_(image_skia) { CEF_REQUIRE_UIT(); } CefImageImpl::~CefImageImpl() { CEF_REQUIRE_UIT(); } bool CefImageImpl::IsEmpty() { CEF_REQUIRE_UIT_RETURN(false); return image_.IsEmpty(); } bool CefImageImpl::IsSame(CefRefPtr that) { CEF_REQUIRE_UIT_RETURN(false); CefImageImpl* that_impl = static_cast(that.get()); if (!that_impl) return false; // Quick check for the same object. if (this == that_impl) return true; 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) { CEF_REQUIRE_UIT_RETURN(false); 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.getSize()); 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) { CEF_REQUIRE_UIT_RETURN(false); 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) { CEF_REQUIRE_UIT_RETURN(false); 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() { CEF_REQUIRE_UIT_RETURN(false); return image_.Width(); } size_t CefImageImpl::GetHeight() { CEF_REQUIRE_UIT_RETURN(false); return image_.Height(); } bool CefImageImpl::HasRepresentation(float scale_factor) { CEF_REQUIRE_UIT_RETURN(false); return image_.AsImageSkia().HasRepresentation(scale_factor); } bool CefImageImpl::RemoveRepresentation(float scale_factor) { CEF_REQUIRE_UIT_RETURN(false); 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) { CEF_REQUIRE_UIT_RETURN(false); 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.sk_bitmap().width(); pixel_height = rep.sk_bitmap().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) { CEF_REQUIRE_UIT_RETURN(nullptr); const SkColorType desired_ct = GetSkColorType(color_type); const SkAlphaType desired_at = GetSkAlphaType(alpha_type); 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->getSize()); } 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.getSize()); } } CefRefPtr CefImageImpl::GetAsPNG(float scale_factor, bool with_transparency, int& pixel_width, int& pixel_height) { CEF_REQUIRE_UIT_RETURN(nullptr); 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) { CEF_REQUIRE_UIT_RETURN(nullptr); 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 { 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; } 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); 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 { 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.sk_bitmap(); } // 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, 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* compressed, bool with_transparency) { return WriteCompressedFormat(bitmap, compressed, base::Bind(PNGMethod, with_transparency)); } // static bool CefImageImpl::WriteJPEG(const SkBitmap& bitmap, std::vector* compressed, int quality) { return WriteCompressedFormat(bitmap, compressed, base::Bind(JPEGMethod, quality)); }