blob: fa2b937dd1d7832487d0dd4b1402e11eb50b2eb7 [file] [log] [blame]
// Copyright (c) 2012 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 "content/renderer/pepper/ppb_image_data_impl.h"
#include <algorithm>
#include <limits>
#include <memory>
#include "base/logging.h"
#include "content/common/pepper_file_util.h"
#include "content/common/view_messages.h"
#include "content/renderer/render_thread_impl.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/ppb_image_data.h"
#include "ppapi/thunk/thunk.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColorPriv.h"
#include "third_party/skia/include/core/SkPixmap.h"
#include "ui/surface/transport_dib.h"
using ppapi::thunk::PPB_ImageData_API;
namespace content {
PPB_ImageData_Impl::PPB_ImageData_Impl(PP_Instance instance,
PPB_ImageData_Shared::ImageDataType type)
: Resource(ppapi::OBJECT_IS_IMPL, instance),
format_(PP_IMAGEDATAFORMAT_BGRA_PREMUL),
width_(0),
height_(0) {
switch (type) {
case PPB_ImageData_Shared::PLATFORM:
backend_.reset(new ImageDataPlatformBackend());
return;
case PPB_ImageData_Shared::SIMPLE:
backend_.reset(new ImageDataSimpleBackend);
return;
// No default: so that we get a compiler warning if any types are added.
}
NOTREACHED();
}
PPB_ImageData_Impl::PPB_ImageData_Impl(PP_Instance instance, ForTest)
: Resource(ppapi::OBJECT_IS_IMPL, instance),
format_(PP_IMAGEDATAFORMAT_BGRA_PREMUL),
width_(0),
height_(0) {
backend_.reset(new ImageDataPlatformBackend());
}
PPB_ImageData_Impl::~PPB_ImageData_Impl() {}
bool PPB_ImageData_Impl::Init(PP_ImageDataFormat format,
int width,
int height,
bool init_to_zero) {
// TODO(brettw) this should be called only on the main thread!
if (!IsImageDataFormatSupported(format))
return false; // Only support this one format for now.
if (width <= 0 || height <= 0)
return false;
if (static_cast<int64_t>(width) * static_cast<int64_t>(height) >=
std::numeric_limits<int32_t>::max() / 4)
return false; // Prevent overflow of signed 32-bit ints.
format_ = format;
width_ = width;
height_ = height;
return backend_->Init(this, format, width, height, init_to_zero);
}
// static
PP_Resource PPB_ImageData_Impl::Create(PP_Instance instance,
PPB_ImageData_Shared::ImageDataType type,
PP_ImageDataFormat format,
const PP_Size& size,
PP_Bool init_to_zero) {
scoped_refptr<PPB_ImageData_Impl> data(
new PPB_ImageData_Impl(instance, type));
if (!data->Init(format, size.width, size.height, !!init_to_zero))
return 0;
return data->GetReference();
}
PPB_ImageData_API* PPB_ImageData_Impl::AsPPB_ImageData_API() { return this; }
bool PPB_ImageData_Impl::IsMapped() const { return backend_->IsMapped(); }
TransportDIB* PPB_ImageData_Impl::GetTransportDIB() const {
return backend_->GetTransportDIB();
}
PP_Bool PPB_ImageData_Impl::Describe(PP_ImageDataDesc* desc) {
desc->format = format_;
desc->size.width = width_;
desc->size.height = height_;
desc->stride = width_ * 4;
return PP_TRUE;
}
void* PPB_ImageData_Impl::Map() { return backend_->Map(); }
void PPB_ImageData_Impl::Unmap() { backend_->Unmap(); }
int32_t PPB_ImageData_Impl::GetSharedMemory(base::SharedMemory** shm,
uint32_t* byte_count) {
return backend_->GetSharedMemory(shm, byte_count);
}
SkCanvas* PPB_ImageData_Impl::GetCanvas() { return backend_->GetCanvas(); }
void PPB_ImageData_Impl::SetIsCandidateForReuse() {
// Nothing to do since we don't support image data re-use in-process.
}
SkBitmap PPB_ImageData_Impl::GetMappedBitmap() const {
return backend_->GetMappedBitmap();
}
// ImageDataPlatformBackend ----------------------------------------------------
ImageDataPlatformBackend::ImageDataPlatformBackend() : width_(0), height_(0) {
}
ImageDataPlatformBackend::~ImageDataPlatformBackend() {
}
bool ImageDataPlatformBackend::Init(PPB_ImageData_Impl* impl,
PP_ImageDataFormat format,
int width,
int height,
bool init_to_zero) {
// TODO(brettw) use init_to_zero when we implement caching.
width_ = width;
height_ = height;
uint32_t buffer_size = width_ * height_ * 4;
std::unique_ptr<base::SharedMemory> shared_memory =
RenderThread::Get()->HostAllocateSharedMemoryBuffer(buffer_size);
if (!shared_memory)
return false;
// The TransportDIB is always backed by shared memory, so give the shared
// memory handle to it.
base::SharedMemoryHandle handle = shared_memory->handle().Duplicate();
shared_memory->Unmap();
shared_memory->Close();
if (!handle.IsValid())
return false;
dib_.reset(TransportDIB::CreateWithHandle(handle));
return !!dib_;
}
bool ImageDataPlatformBackend::IsMapped() const {
return !!mapped_canvas_.get();
}
TransportDIB* ImageDataPlatformBackend::GetTransportDIB() const {
return dib_.get();
}
void* ImageDataPlatformBackend::Map() {
if (!mapped_canvas_) {
const bool is_opaque = false;
mapped_canvas_ = dib_->GetPlatformCanvas(width_, height_, is_opaque);
if (!mapped_canvas_)
return nullptr;
}
SkPixmap pixmap;
skia::GetWritablePixels(mapped_canvas_.get(), &pixmap);
DCHECK(pixmap.addr());
// SkPixmap does not manage the lifetime of this pointer, so it remains
// valid after the object goes out of scope. It will become invalid if
// the canvas' backing is destroyed or a pending saveLayer() is resolved.
return pixmap.writable_addr32(0, 0);
}
void ImageDataPlatformBackend::Unmap() {
// This is currently unimplemented, which is OK. The data will just always
// be around once it's mapped. Chrome's TransportDIB isn't currently
// unmappable without freeing it, but this may be something we want to support
// in the future to save some memory.
}
int32_t ImageDataPlatformBackend::GetSharedMemory(base::SharedMemory** shm,
uint32_t* byte_count) {
*byte_count = dib_->size();
*shm = dib_->shared_memory();
return PP_OK;
}
SkCanvas* ImageDataPlatformBackend::GetCanvas() { return mapped_canvas_.get(); }
SkBitmap ImageDataPlatformBackend::GetMappedBitmap() const {
SkBitmap bitmap;
if (!mapped_canvas_)
return bitmap;
SkPixmap pixmap;
skia::GetWritablePixels(mapped_canvas_.get(), &pixmap);
// SkPixmap does not manage the lifetime of this pointer, so it remains
// valid after the object goes out of scope. It will become invalid if
// the canvas' backing is destroyed or a pending saveLayer() is resolved.
bitmap.installPixels(pixmap);
return bitmap;
}
// ImageDataSimpleBackend ------------------------------------------------------
ImageDataSimpleBackend::ImageDataSimpleBackend() : map_count_(0) {}
ImageDataSimpleBackend::~ImageDataSimpleBackend() {}
bool ImageDataSimpleBackend::Init(PPB_ImageData_Impl* impl,
PP_ImageDataFormat format,
int width,
int height,
bool init_to_zero) {
skia_bitmap_.setInfo(
SkImageInfo::MakeN32Premul(impl->width(), impl->height()));
shared_memory_.reset(
RenderThread::Get()
->HostAllocateSharedMemoryBuffer(skia_bitmap_.computeByteSize())
.release());
return !!shared_memory_.get();
}
bool ImageDataSimpleBackend::IsMapped() const { return map_count_ > 0; }
TransportDIB* ImageDataSimpleBackend::GetTransportDIB() const {
return nullptr;
}
void* ImageDataSimpleBackend::Map() {
DCHECK(shared_memory_.get());
if (map_count_++ == 0) {
shared_memory_->Map(skia_bitmap_.computeByteSize());
skia_bitmap_.setPixels(shared_memory_->memory());
// Our platform bitmaps are set to opaque by default, which we don't want.
skia_bitmap_.setAlphaType(kPremul_SkAlphaType);
skia_canvas_ = std::make_unique<SkCanvas>(skia_bitmap_);
return skia_bitmap_.getAddr32(0, 0);
}
return shared_memory_->memory();
}
void ImageDataSimpleBackend::Unmap() {
if (--map_count_ == 0)
shared_memory_->Unmap();
}
int32_t ImageDataSimpleBackend::GetSharedMemory(base::SharedMemory** shm,
uint32_t* byte_count) {
*byte_count = skia_bitmap_.computeByteSize();
*shm = shared_memory_.get();
return PP_OK;
}
SkCanvas* ImageDataSimpleBackend::GetCanvas() {
if (!IsMapped())
return nullptr;
return skia_canvas_.get();
}
SkBitmap ImageDataSimpleBackend::GetMappedBitmap() const {
if (!IsMapped())
return SkBitmap();
return skia_bitmap_;
}
} // namespace content