1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/renderer/pepper/ppb_image_data_impl.h" 6 7#include <algorithm> 8#include <limits> 9 10#include "base/logging.h" 11#include "base/memory/scoped_ptr.h" 12#include "content/common/view_messages.h" 13#include "content/renderer/pepper/common.h" 14#include "content/renderer/render_thread_impl.h" 15#include "ppapi/c/pp_errors.h" 16#include "ppapi/c/pp_instance.h" 17#include "ppapi/c/pp_resource.h" 18#include "ppapi/c/ppb_image_data.h" 19#include "ppapi/thunk/thunk.h" 20#include "skia/ext/platform_canvas.h" 21#include "third_party/skia/include/core/SkColorPriv.h" 22#include "ui/surface/transport_dib.h" 23 24using ppapi::thunk::PPB_ImageData_API; 25 26namespace content { 27 28PPB_ImageData_Impl::PPB_ImageData_Impl(PP_Instance instance, 29 PPB_ImageData_Shared::ImageDataType type) 30 : Resource(ppapi::OBJECT_IS_IMPL, instance), 31 format_(PP_IMAGEDATAFORMAT_BGRA_PREMUL), 32 width_(0), 33 height_(0) { 34 switch (type) { 35 case PPB_ImageData_Shared::PLATFORM: 36 backend_.reset(new ImageDataPlatformBackend); 37 return; 38 case PPB_ImageData_Shared::SIMPLE: 39 backend_.reset(new ImageDataSimpleBackend); 40 return; 41 // No default: so that we get a compiler warning if any types are added. 42 } 43 NOTREACHED(); 44} 45 46PPB_ImageData_Impl::~PPB_ImageData_Impl() { 47} 48 49bool PPB_ImageData_Impl::Init(PP_ImageDataFormat format, 50 int width, int height, 51 bool init_to_zero) { 52 // TODO(brettw) this should be called only on the main thread! 53 if (!IsImageDataFormatSupported(format)) 54 return false; // Only support this one format for now. 55 if (width <= 0 || height <= 0) 56 return false; 57 if (static_cast<int64>(width) * static_cast<int64>(height) >= 58 std::numeric_limits<int32>::max() / 4) 59 return false; // Prevent overflow of signed 32-bit ints. 60 61 format_ = format; 62 width_ = width; 63 height_ = height; 64 return backend_->Init(this, format, width, height, init_to_zero); 65} 66 67// static 68PP_Resource PPB_ImageData_Impl::Create(PP_Instance instance, 69 PPB_ImageData_Shared::ImageDataType type, 70 PP_ImageDataFormat format, 71 const PP_Size& size, 72 PP_Bool init_to_zero) { 73 scoped_refptr<PPB_ImageData_Impl> 74 data(new PPB_ImageData_Impl(instance, type)); 75 if (!data->Init(format, size.width, size.height, !!init_to_zero)) 76 return 0; 77 return data->GetReference(); 78} 79 80PPB_ImageData_API* PPB_ImageData_Impl::AsPPB_ImageData_API() { 81 return this; 82} 83 84bool PPB_ImageData_Impl::IsMapped() const { 85 return backend_->IsMapped(); 86} 87 88TransportDIB* PPB_ImageData_Impl::GetTransportDIB() const { 89 return backend_->GetTransportDIB(); 90} 91 92PP_Bool PPB_ImageData_Impl::Describe(PP_ImageDataDesc* desc) { 93 desc->format = format_; 94 desc->size.width = width_; 95 desc->size.height = height_; 96 desc->stride = width_ * 4; 97 return PP_TRUE; 98} 99 100void* PPB_ImageData_Impl::Map() { 101 return backend_->Map(); 102} 103 104void PPB_ImageData_Impl::Unmap() { 105 backend_->Unmap(); 106} 107 108int32_t PPB_ImageData_Impl::GetSharedMemory(int* handle, uint32_t* byte_count) { 109 return backend_->GetSharedMemory(handle, byte_count); 110} 111 112skia::PlatformCanvas* PPB_ImageData_Impl::GetPlatformCanvas() { 113 return backend_->GetPlatformCanvas(); 114} 115 116SkCanvas* PPB_ImageData_Impl::GetCanvas() { 117 return backend_->GetCanvas(); 118} 119 120void PPB_ImageData_Impl::SetIsCandidateForReuse() { 121 // Nothing to do since we don't support image data re-use in-process. 122} 123 124const SkBitmap* PPB_ImageData_Impl::GetMappedBitmap() const { 125 return backend_->GetMappedBitmap(); 126} 127 128// ImageDataPlatformBackend ---------------------------------------------------- 129 130ImageDataPlatformBackend::ImageDataPlatformBackend() 131 : width_(0), 132 height_(0) { 133} 134 135// On POSIX, we have to tell the browser to free the transport DIB. 136ImageDataPlatformBackend::~ImageDataPlatformBackend() { 137#if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID) 138 if (dib_) { 139 RenderThreadImpl::current()->Send( 140 new ViewHostMsg_FreeTransportDIB(dib_->id())); 141 } 142#endif 143} 144 145bool ImageDataPlatformBackend::Init(PPB_ImageData_Impl* impl, 146 PP_ImageDataFormat format, 147 int width, int height, 148 bool init_to_zero) { 149 // TODO(brettw) use init_to_zero when we implement caching. 150 width_ = width; 151 height_ = height; 152 uint32 buffer_size = width_ * height_ * 4; 153 154 // Allocate the transport DIB and the PlatformCanvas pointing to it. 155#if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID) 156 // On the Mac, shared memory has to be created in the browser in order to 157 // work in the sandbox. Do this by sending a message to the browser 158 // requesting a TransportDIB (see also 159 // chrome/renderer/webplugin_delegate_proxy.cc, method 160 // WebPluginDelegateProxy::CreateBitmap() for similar code). The TransportDIB 161 // is cached in the browser, and is freed (in typical cases) by the 162 // TransportDIB's destructor. 163 TransportDIB::Handle dib_handle; 164 IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(buffer_size, 165 true, 166 &dib_handle); 167 if (!RenderThreadImpl::current()->Send(msg)) 168 return false; 169 if (!TransportDIB::is_valid_handle(dib_handle)) 170 return false; 171 172 TransportDIB* dib = TransportDIB::CreateWithHandle(dib_handle); 173#else 174 static int next_dib_id = 0; 175 TransportDIB* dib = TransportDIB::Create(buffer_size, next_dib_id++); 176 if (!dib) 177 return false; 178#endif 179 dib_.reset(dib); 180 return true; 181} 182 183bool ImageDataPlatformBackend::IsMapped() const { 184 return !!mapped_canvas_.get(); 185} 186 187TransportDIB* ImageDataPlatformBackend::GetTransportDIB() const { 188 return dib_.get(); 189} 190 191void* ImageDataPlatformBackend::Map() { 192 if (!mapped_canvas_) { 193 mapped_canvas_.reset(dib_->GetPlatformCanvas(width_, height_)); 194 if (!mapped_canvas_) 195 return NULL; 196 } 197 const SkBitmap& bitmap = 198 skia::GetTopDevice(*mapped_canvas_)->accessBitmap(true); 199 200 // Our platform bitmaps are set to opaque by default, which we don't want. 201 const_cast<SkBitmap&>(bitmap).setIsOpaque(false); 202 203 bitmap.lockPixels(); 204 return bitmap.getAddr32(0, 0); 205} 206 207void ImageDataPlatformBackend::Unmap() { 208 // This is currently unimplemented, which is OK. The data will just always 209 // be around once it's mapped. Chrome's TransportDIB isn't currently 210 // unmappable without freeing it, but this may be something we want to support 211 // in the future to save some memory. 212} 213 214int32_t ImageDataPlatformBackend::GetSharedMemory(int* handle, 215 uint32_t* byte_count) { 216 *byte_count = dib_->size(); 217#if defined(OS_WIN) 218 *handle = reinterpret_cast<intptr_t>(dib_->handle()); 219#elif defined(TOOLKIT_GTK) 220 *handle = static_cast<intptr_t>(dib_->handle()); 221#else 222 *handle = static_cast<intptr_t>(dib_->handle().fd); 223#endif 224 225 return PP_OK; 226} 227 228skia::PlatformCanvas* ImageDataPlatformBackend::GetPlatformCanvas() { 229 return mapped_canvas_.get(); 230} 231 232SkCanvas* ImageDataPlatformBackend::GetCanvas() { 233 return mapped_canvas_.get(); 234} 235 236const SkBitmap* ImageDataPlatformBackend::GetMappedBitmap() const { 237 if (!mapped_canvas_) 238 return NULL; 239 return &skia::GetTopDevice(*mapped_canvas_)->accessBitmap(false); 240} 241 242// ImageDataSimpleBackend ------------------------------------------------------ 243 244ImageDataSimpleBackend::ImageDataSimpleBackend() 245 : map_count_(0) { 246} 247 248ImageDataSimpleBackend::~ImageDataSimpleBackend() { 249} 250 251bool ImageDataSimpleBackend::Init(PPB_ImageData_Impl* impl, 252 PP_ImageDataFormat format, 253 int width, int height, 254 bool init_to_zero) { 255 skia_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, 256 impl->width(), impl->height()); 257 shared_memory_.reset(RenderThread::Get()->HostAllocateSharedMemoryBuffer( 258 skia_bitmap_.getSize()).release()); 259 return !!shared_memory_.get(); 260} 261 262bool ImageDataSimpleBackend::IsMapped() const { 263 return map_count_ > 0; 264} 265 266TransportDIB* ImageDataSimpleBackend::GetTransportDIB() const { 267 return NULL; 268} 269 270void* ImageDataSimpleBackend::Map() { 271 DCHECK(shared_memory_.get()); 272 if (map_count_++ == 0) { 273 shared_memory_->Map(skia_bitmap_.getSize()); 274 skia_bitmap_.setPixels(shared_memory_->memory()); 275 // Our platform bitmaps are set to opaque by default, which we don't want. 276 skia_bitmap_.setIsOpaque(false); 277 skia_canvas_.reset(new SkCanvas(skia_bitmap_)); 278 return skia_bitmap_.getAddr32(0, 0); 279 } 280 return shared_memory_->memory(); 281} 282 283void ImageDataSimpleBackend::Unmap() { 284 if (--map_count_ == 0) 285 shared_memory_->Unmap(); 286} 287 288int32_t ImageDataSimpleBackend::GetSharedMemory(int* handle, 289 uint32_t* byte_count) { 290 *byte_count = skia_bitmap_.getSize(); 291#if defined(OS_POSIX) 292 *handle = shared_memory_->handle().fd; 293#elif defined(OS_WIN) 294 *handle = reinterpret_cast<int>(shared_memory_->handle()); 295#else 296#error "Platform not supported." 297#endif 298 return PP_OK; 299} 300 301skia::PlatformCanvas* ImageDataSimpleBackend::GetPlatformCanvas() { 302 return NULL; 303} 304 305SkCanvas* ImageDataSimpleBackend::GetCanvas() { 306 if (!IsMapped()) 307 return NULL; 308 return skia_canvas_.get(); 309} 310 311const SkBitmap* ImageDataSimpleBackend::GetMappedBitmap() const { 312 if (!IsMapped()) 313 return NULL; 314 return &skia_bitmap_; 315} 316 317} // namespace content 318