Home | History | Annotate | Download | only in pepper
      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