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