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 PPB_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 46 PPB_ImageData_Impl::~PPB_ImageData_Impl() { 47 } 48 49 bool 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 68 PP_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 80 PPB_ImageData_API* PPB_ImageData_Impl::AsPPB_ImageData_API() { 81 return this; 82 } 83 84 bool PPB_ImageData_Impl::IsMapped() const { 85 return backend_->IsMapped(); 86 } 87 88 TransportDIB* PPB_ImageData_Impl::GetTransportDIB() const { 89 return backend_->GetTransportDIB(); 90 } 91 92 PP_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 100 void* PPB_ImageData_Impl::Map() { 101 return backend_->Map(); 102 } 103 104 void PPB_ImageData_Impl::Unmap() { 105 backend_->Unmap(); 106 } 107 108 int32_t PPB_ImageData_Impl::GetSharedMemory(int* handle, uint32_t* byte_count) { 109 return backend_->GetSharedMemory(handle, byte_count); 110 } 111 112 skia::PlatformCanvas* PPB_ImageData_Impl::GetPlatformCanvas() { 113 return backend_->GetPlatformCanvas(); 114 } 115 116 SkCanvas* PPB_ImageData_Impl::GetCanvas() { 117 return backend_->GetCanvas(); 118 } 119 120 void PPB_ImageData_Impl::SetIsCandidateForReuse() { 121 // Nothing to do since we don't support image data re-use in-process. 122 } 123 124 const SkBitmap* PPB_ImageData_Impl::GetMappedBitmap() const { 125 return backend_->GetMappedBitmap(); 126 } 127 128 // ImageDataPlatformBackend ---------------------------------------------------- 129 130 ImageDataPlatformBackend::ImageDataPlatformBackend() 131 : width_(0), 132 height_(0) { 133 } 134 135 // On POSIX, we have to tell the browser to free the transport DIB. 136 ImageDataPlatformBackend::~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 145 bool 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 183 bool ImageDataPlatformBackend::IsMapped() const { 184 return !!mapped_canvas_.get(); 185 } 186 187 TransportDIB* ImageDataPlatformBackend::GetTransportDIB() const { 188 return dib_.get(); 189 } 190 191 void* 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 207 void 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 214 int32_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 228 skia::PlatformCanvas* ImageDataPlatformBackend::GetPlatformCanvas() { 229 return mapped_canvas_.get(); 230 } 231 232 SkCanvas* ImageDataPlatformBackend::GetCanvas() { 233 return mapped_canvas_.get(); 234 } 235 236 const SkBitmap* ImageDataPlatformBackend::GetMappedBitmap() const { 237 if (!mapped_canvas_) 238 return NULL; 239 return &skia::GetTopDevice(*mapped_canvas_)->accessBitmap(false); 240 } 241 242 // ImageDataSimpleBackend ------------------------------------------------------ 243 244 ImageDataSimpleBackend::ImageDataSimpleBackend() 245 : map_count_(0) { 246 } 247 248 ImageDataSimpleBackend::~ImageDataSimpleBackend() { 249 } 250 251 bool 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 262 bool ImageDataSimpleBackend::IsMapped() const { 263 return map_count_ > 0; 264 } 265 266 TransportDIB* ImageDataSimpleBackend::GetTransportDIB() const { 267 return NULL; 268 } 269 270 void* 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 283 void ImageDataSimpleBackend::Unmap() { 284 if (--map_count_ == 0) 285 shared_memory_->Unmap(); 286 } 287 288 int32_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 301 skia::PlatformCanvas* ImageDataSimpleBackend::GetPlatformCanvas() { 302 return NULL; 303 } 304 305 SkCanvas* ImageDataSimpleBackend::GetCanvas() { 306 if (!IsMapped()) 307 return NULL; 308 return skia_canvas_.get(); 309 } 310 311 const SkBitmap* ImageDataSimpleBackend::GetMappedBitmap() const { 312 if (!IsMapped()) 313 return NULL; 314 return &skia_bitmap_; 315 } 316 317 } // namespace content 318