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