• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
IsBrowserAllocated()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 
PPB_ImageData_Impl(PP_Instance instance,PPB_ImageData_Shared::ImageDataType type)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 
PPB_ImageData_Impl(PP_Instance instance,ForTest)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 
~PPB_ImageData_Impl()68 PPB_ImageData_Impl::~PPB_ImageData_Impl() {
69 }
70 
Init(PP_ImageDataFormat format,int width,int height,bool init_to_zero)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
Create(PP_Instance instance,PPB_ImageData_Shared::ImageDataType type,PP_ImageDataFormat format,const PP_Size & size,PP_Bool init_to_zero)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 
AsPPB_ImageData_API()102 PPB_ImageData_API* PPB_ImageData_Impl::AsPPB_ImageData_API() {
103   return this;
104 }
105 
IsMapped() const106 bool PPB_ImageData_Impl::IsMapped() const {
107   return backend_->IsMapped();
108 }
109 
GetTransportDIB() const110 TransportDIB* PPB_ImageData_Impl::GetTransportDIB() const {
111   return backend_->GetTransportDIB();
112 }
113 
Describe(PP_ImageDataDesc * desc)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 
Map()122 void* PPB_ImageData_Impl::Map() {
123   return backend_->Map();
124 }
125 
Unmap()126 void PPB_ImageData_Impl::Unmap() {
127   backend_->Unmap();
128 }
129 
GetSharedMemory(int * handle,uint32_t * byte_count)130 int32_t PPB_ImageData_Impl::GetSharedMemory(int* handle, uint32_t* byte_count) {
131   return backend_->GetSharedMemory(handle, byte_count);
132 }
133 
GetPlatformCanvas()134 skia::PlatformCanvas* PPB_ImageData_Impl::GetPlatformCanvas() {
135   return backend_->GetPlatformCanvas();
136 }
137 
GetCanvas()138 SkCanvas* PPB_ImageData_Impl::GetCanvas() {
139   return backend_->GetCanvas();
140 }
141 
SetIsCandidateForReuse()142 void PPB_ImageData_Impl::SetIsCandidateForReuse() {
143   // Nothing to do since we don't support image data re-use in-process.
144 }
145 
GetMappedBitmap() const146 const SkBitmap* PPB_ImageData_Impl::GetMappedBitmap() const {
147   return backend_->GetMappedBitmap();
148 }
149 
150 // ImageDataPlatformBackend ----------------------------------------------------
151 
ImageDataPlatformBackend(bool is_browser_allocated)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.
~ImageDataPlatformBackend()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 
Init(PPB_ImageData_Impl * impl,PP_ImageDataFormat format,int width,int height,bool init_to_zero)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 
IsMapped() const210 bool ImageDataPlatformBackend::IsMapped() const {
211   return !!mapped_canvas_.get();
212 }
213 
GetTransportDIB() const214 TransportDIB* ImageDataPlatformBackend::GetTransportDIB() const {
215   return dib_.get();
216 }
217 
Map()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 
Unmap()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 
GetSharedMemory(int * handle,uint32_t * byte_count)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 
GetPlatformCanvas()255 skia::PlatformCanvas* ImageDataPlatformBackend::GetPlatformCanvas() {
256   return mapped_canvas_.get();
257 }
258 
GetCanvas()259 SkCanvas* ImageDataPlatformBackend::GetCanvas() {
260   return mapped_canvas_.get();
261 }
262 
GetMappedBitmap() const263 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 
ImageDataSimpleBackend()271 ImageDataSimpleBackend::ImageDataSimpleBackend()
272     : map_count_(0) {
273 }
274 
~ImageDataSimpleBackend()275 ImageDataSimpleBackend::~ImageDataSimpleBackend() {
276 }
277 
Init(PPB_ImageData_Impl * impl,PP_ImageDataFormat format,int width,int height,bool init_to_zero)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 
IsMapped() const289 bool ImageDataSimpleBackend::IsMapped() const {
290   return map_count_ > 0;
291 }
292 
GetTransportDIB() const293 TransportDIB* ImageDataSimpleBackend::GetTransportDIB() const {
294   return NULL;
295 }
296 
Map()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 
Unmap()310 void ImageDataSimpleBackend::Unmap() {
311   if (--map_count_ == 0)
312     shared_memory_->Unmap();
313 }
314 
GetSharedMemory(int * handle,uint32_t * byte_count)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 
GetPlatformCanvas()328 skia::PlatformCanvas* ImageDataSimpleBackend::GetPlatformCanvas() {
329   return NULL;
330 }
331 
GetCanvas()332 SkCanvas* ImageDataSimpleBackend::GetCanvas() {
333   if (!IsMapped())
334     return NULL;
335   return skia_canvas_.get();
336 }
337 
GetMappedBitmap() const338 const SkBitmap* ImageDataSimpleBackend::GetMappedBitmap() const {
339   if (!IsMapped())
340     return NULL;
341   return &skia_bitmap_;
342 }
343 
344 }  // namespace content
345