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(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