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