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