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/pepper_graphics_2d_host.h"
6
7#include "base/bind.h"
8#include "base/debug/trace_event.h"
9#include "base/logging.h"
10#include "base/message_loop/message_loop.h"
11#include "cc/resources/shared_bitmap.h"
12#include "cc/resources/texture_mailbox.h"
13#include "content/child/child_shared_bitmap_manager.h"
14#include "content/public/renderer/render_thread.h"
15#include "content/public/renderer/renderer_ppapi_host.h"
16#include "content/renderer/pepper/gfx_conversion.h"
17#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
18#include "content/renderer/pepper/ppb_image_data_impl.h"
19#include "content/renderer/render_thread_impl.h"
20#include "ppapi/c/pp_bool.h"
21#include "ppapi/c/pp_errors.h"
22#include "ppapi/c/pp_rect.h"
23#include "ppapi/c/pp_resource.h"
24#include "ppapi/host/dispatch_host_message.h"
25#include "ppapi/host/host_message_context.h"
26#include "ppapi/host/ppapi_host.h"
27#include "ppapi/proxy/ppapi_messages.h"
28#include "ppapi/shared_impl/ppb_view_shared.h"
29#include "ppapi/thunk/enter.h"
30#include "skia/ext/platform_canvas.h"
31#include "third_party/skia/include/core/SkBitmap.h"
32#include "ui/gfx/blit.h"
33#include "ui/gfx/point_conversions.h"
34#include "ui/gfx/rect.h"
35#include "ui/gfx/rect_conversions.h"
36#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
37#include "ui/gfx/size_conversions.h"
38#include "ui/gfx/skia_util.h"
39
40#if defined(OS_MACOSX)
41#include "base/mac/mac_util.h"
42#include "base/mac/scoped_cftyperef.h"
43#endif
44
45using ppapi::thunk::EnterResourceNoLock;
46using ppapi::thunk::PPB_ImageData_API;
47
48namespace content {
49
50namespace {
51
52const int64 kOffscreenCallbackDelayMs = 1000 / 30;  // 30 fps
53
54// Converts a rect inside an image of the given dimensions. The rect may be
55// NULL to indicate it should be the entire image. If the rect is outside of
56// the image, this will do nothing and return false.
57bool ValidateAndConvertRect(const PP_Rect* rect,
58                            int image_width,
59                            int image_height,
60                            gfx::Rect* dest) {
61  if (!rect) {
62    // Use the entire image area.
63    *dest = gfx::Rect(0, 0, image_width, image_height);
64  } else {
65    // Validate the passed-in area.
66    if (rect->point.x < 0 || rect->point.y < 0 || rect->size.width <= 0 ||
67        rect->size.height <= 0)
68      return false;
69
70    // Check the max bounds, being careful of overflow.
71    if (static_cast<int64>(rect->point.x) +
72            static_cast<int64>(rect->size.width) >
73        static_cast<int64>(image_width))
74      return false;
75    if (static_cast<int64>(rect->point.y) +
76            static_cast<int64>(rect->size.height) >
77        static_cast<int64>(image_height))
78      return false;
79
80    *dest = gfx::Rect(
81        rect->point.x, rect->point.y, rect->size.width, rect->size.height);
82  }
83  return true;
84}
85
86// Converts BGRA <-> RGBA.
87void ConvertBetweenBGRAandRGBA(const uint32_t* input,
88                               int pixel_length,
89                               uint32_t* output) {
90  for (int i = 0; i < pixel_length; i++) {
91    const unsigned char* pixel_in =
92        reinterpret_cast<const unsigned char*>(&input[i]);
93    unsigned char* pixel_out = reinterpret_cast<unsigned char*>(&output[i]);
94    pixel_out[0] = pixel_in[2];
95    pixel_out[1] = pixel_in[1];
96    pixel_out[2] = pixel_in[0];
97    pixel_out[3] = pixel_in[3];
98  }
99}
100
101// Converts ImageData from PP_IMAGEDATAFORMAT_BGRA_PREMUL to
102// PP_IMAGEDATAFORMAT_RGBA_PREMUL, or reverse. It's assumed that the
103// destination image is always mapped (so will have non-NULL data).
104void ConvertImageData(PPB_ImageData_Impl* src_image,
105                      const SkIRect& src_rect,
106                      PPB_ImageData_Impl* dest_image,
107                      const SkRect& dest_rect) {
108  ImageDataAutoMapper auto_mapper(src_image);
109
110  DCHECK(src_image->format() != dest_image->format());
111  DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(src_image->format()));
112  DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(dest_image->format()));
113
114  const SkBitmap* src_bitmap = src_image->GetMappedBitmap();
115  const SkBitmap* dest_bitmap = dest_image->GetMappedBitmap();
116  if (src_rect.width() == src_image->width() &&
117      dest_rect.width() == dest_image->width()) {
118    // Fast path if the full line needs to be converted.
119    ConvertBetweenBGRAandRGBA(
120        src_bitmap->getAddr32(static_cast<int>(src_rect.fLeft),
121                              static_cast<int>(src_rect.fTop)),
122        src_rect.width() * src_rect.height(),
123        dest_bitmap->getAddr32(static_cast<int>(dest_rect.fLeft),
124                               static_cast<int>(dest_rect.fTop)));
125  } else {
126    // Slow path where we convert line by line.
127    for (int y = 0; y < src_rect.height(); y++) {
128      ConvertBetweenBGRAandRGBA(
129          src_bitmap->getAddr32(static_cast<int>(src_rect.fLeft),
130                                static_cast<int>(src_rect.fTop + y)),
131          src_rect.width(),
132          dest_bitmap->getAddr32(static_cast<int>(dest_rect.fLeft),
133                                 static_cast<int>(dest_rect.fTop + y)));
134    }
135  }
136}
137
138}  // namespace
139
140struct PepperGraphics2DHost::QueuedOperation {
141  enum Type { PAINT, SCROLL, REPLACE, };
142
143  QueuedOperation(Type t)
144      : type(t), paint_x(0), paint_y(0), scroll_dx(0), scroll_dy(0) {}
145
146  Type type;
147
148  // Valid when type == PAINT.
149  scoped_refptr<PPB_ImageData_Impl> paint_image;
150  int paint_x, paint_y;
151  gfx::Rect paint_src_rect;
152
153  // Valid when type == SCROLL.
154  gfx::Rect scroll_clip_rect;
155  int scroll_dx, scroll_dy;
156
157  // Valid when type == REPLACE.
158  scoped_refptr<PPB_ImageData_Impl> replace_image;
159};
160
161// static
162PepperGraphics2DHost* PepperGraphics2DHost::Create(
163    RendererPpapiHost* host,
164    PP_Instance instance,
165    PP_Resource resource,
166    const PP_Size& size,
167    PP_Bool is_always_opaque,
168    scoped_refptr<PPB_ImageData_Impl> backing_store) {
169  PepperGraphics2DHost* resource_host =
170      new PepperGraphics2DHost(host, instance, resource);
171  if (!resource_host->Init(size.width,
172                           size.height,
173                           PP_ToBool(is_always_opaque),
174                           backing_store)) {
175    delete resource_host;
176    return NULL;
177  }
178  return resource_host;
179}
180
181PepperGraphics2DHost::PepperGraphics2DHost(RendererPpapiHost* host,
182                                           PP_Instance instance,
183                                           PP_Resource resource)
184    : ResourceHost(host->GetPpapiHost(), instance, resource),
185      renderer_ppapi_host_(host),
186      bound_instance_(NULL),
187      need_flush_ack_(false),
188      offscreen_flush_pending_(false),
189      is_always_opaque_(false),
190      scale_(1.0f),
191      is_running_in_process_(host->IsRunningInProcess()),
192      texture_mailbox_modified_(true) {}
193
194PepperGraphics2DHost::~PepperGraphics2DHost() {
195  // Unbind from the instance when destroyed if we're still bound.
196  if (bound_instance_)
197    bound_instance_->BindGraphics(bound_instance_->pp_instance(), 0);
198}
199
200bool PepperGraphics2DHost::Init(
201    int width,
202    int height,
203    bool is_always_opaque,
204    scoped_refptr<PPB_ImageData_Impl> backing_store) {
205  // The underlying PPB_ImageData_Impl will validate the dimensions.
206  image_data_ = backing_store;
207  if (!image_data_->Init(PPB_ImageData_Impl::GetNativeImageDataFormat(),
208                         width,
209                         height,
210                         true) ||
211      !image_data_->Map()) {
212    image_data_ = NULL;
213    return false;
214  }
215  is_always_opaque_ = is_always_opaque;
216  scale_ = 1.0f;
217  return true;
218}
219
220int32_t PepperGraphics2DHost::OnResourceMessageReceived(
221    const IPC::Message& msg,
222    ppapi::host::HostMessageContext* context) {
223  PPAPI_BEGIN_MESSAGE_MAP(PepperGraphics2DHost, msg)
224    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_PaintImageData,
225                                      OnHostMsgPaintImageData)
226    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_Scroll,
227                                      OnHostMsgScroll)
228    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_ReplaceContents,
229                                      OnHostMsgReplaceContents)
230    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_Flush,
231                                      OnHostMsgFlush)
232    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_SetScale,
233                                      OnHostMsgSetScale)
234    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_ReadImageData,
235                                      OnHostMsgReadImageData)
236  PPAPI_END_MESSAGE_MAP()
237  return PP_ERROR_FAILED;
238}
239
240bool PepperGraphics2DHost::IsGraphics2DHost() { return true; }
241
242bool PepperGraphics2DHost::ReadImageData(PP_Resource image,
243                                         const PP_Point* top_left) {
244  // Get and validate the image object to paint into.
245  EnterResourceNoLock<PPB_ImageData_API> enter(image, true);
246  if (enter.failed())
247    return false;
248  PPB_ImageData_Impl* image_resource =
249      static_cast<PPB_ImageData_Impl*>(enter.object());
250  if (!PPB_ImageData_Impl::IsImageDataFormatSupported(image_resource->format()))
251    return false;  // Must be in the right format.
252
253  // Validate the bitmap position.
254  int x = top_left->x;
255  if (x < 0 ||
256      static_cast<int64>(x) + static_cast<int64>(image_resource->width()) >
257          image_data_->width())
258    return false;
259  int y = top_left->y;
260  if (y < 0 ||
261      static_cast<int64>(y) + static_cast<int64>(image_resource->height()) >
262          image_data_->height())
263    return false;
264
265  ImageDataAutoMapper auto_mapper(image_resource);
266  if (!auto_mapper.is_valid())
267    return false;
268
269  SkIRect src_irect = {x, y, x + image_resource->width(),
270                       y + image_resource->height()};
271  SkRect dest_rect = {SkIntToScalar(0), SkIntToScalar(0),
272                      SkIntToScalar(image_resource->width()),
273                      SkIntToScalar(image_resource->height())};
274
275  if (image_resource->format() != image_data_->format()) {
276    // Convert the image data if the format does not match.
277    ConvertImageData(image_data_.get(), src_irect, image_resource, dest_rect);
278  } else {
279    SkCanvas* dest_canvas = image_resource->GetCanvas();
280
281    // We want to replace the contents of the bitmap rather than blend.
282    SkPaint paint;
283    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
284    dest_canvas->drawBitmapRect(
285        *image_data_->GetMappedBitmap(), &src_irect, dest_rect, &paint);
286  }
287  return true;
288}
289
290bool PepperGraphics2DHost::BindToInstance(
291    PepperPluginInstanceImpl* new_instance) {
292  if (new_instance && new_instance->pp_instance() != pp_instance())
293    return false;  // Can't bind other instance's contexts.
294  if (bound_instance_ == new_instance)
295    return true;  // Rebinding the same device, nothing to do.
296  if (bound_instance_ && new_instance)
297    return false;  // Can't change a bound device.
298
299  if (!new_instance) {
300    // When the device is detached, we'll not get any more paint callbacks so
301    // we need to clear the list, but we still want to issue any pending
302    // callbacks to the plugin.
303    if (need_flush_ack_)
304      ScheduleOffscreenFlushAck();
305  } else {
306    // Devices being replaced, redraw the plugin.
307    new_instance->InvalidateRect(gfx::Rect());
308  }
309
310  cached_bitmap_.reset();
311  texture_mailbox_modified_ = true;
312
313  bound_instance_ = new_instance;
314  return true;
315}
316
317// The |backing_bitmap| must be clipped to the |plugin_rect| to avoid painting
318// outside the plugin area. This can happen if the plugin has been resized since
319// PaintImageData verified the image is within the plugin size.
320void PepperGraphics2DHost::Paint(blink::WebCanvas* canvas,
321                                 const gfx::Rect& plugin_rect,
322                                 const gfx::Rect& paint_rect) {
323  TRACE_EVENT0("pepper", "PepperGraphics2DHost::Paint");
324  ImageDataAutoMapper auto_mapper(image_data_.get());
325  const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap();
326
327  gfx::Rect invalidate_rect = plugin_rect;
328  invalidate_rect.Intersect(paint_rect);
329  SkRect sk_invalidate_rect = gfx::RectToSkRect(invalidate_rect);
330  SkAutoCanvasRestore auto_restore(canvas, true);
331  canvas->clipRect(sk_invalidate_rect);
332  gfx::Size pixel_image_size(image_data_->width(), image_data_->height());
333  gfx::Size image_size =
334      gfx::ToFlooredSize(gfx::ScaleSize(pixel_image_size, scale_));
335
336  PepperPluginInstance* plugin_instance =
337      renderer_ppapi_host_->GetPluginInstance(pp_instance());
338  if (!plugin_instance)
339    return;
340  if (plugin_instance->IsFullPagePlugin()) {
341    // When we're resizing a window with a full-frame plugin, the plugin may
342    // not yet have bound a new device, which will leave parts of the
343    // background exposed if the window is getting larger. We want this to
344    // show white (typically less jarring) rather than black or uninitialized.
345    // We don't do this for non-full-frame plugins since we specifically want
346    // the page background to show through.
347    SkAutoCanvasRestore auto_restore(canvas, true);
348    SkRect image_data_rect =
349        gfx::RectToSkRect(gfx::Rect(plugin_rect.origin(), image_size));
350    canvas->clipRect(image_data_rect, SkRegion::kDifference_Op);
351
352    SkPaint paint;
353    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
354    paint.setColor(SK_ColorWHITE);
355    canvas->drawRect(sk_invalidate_rect, paint);
356  }
357
358  SkBitmap image;
359  // Copy to device independent bitmap when target canvas doesn't support
360  // platform paint.
361  if (!skia::SupportsPlatformPaint(canvas))
362    backing_bitmap.copyTo(&image, kN32_SkColorType);
363  else
364    image = backing_bitmap;
365
366  SkPaint paint;
367  if (is_always_opaque_) {
368    // When we know the device is opaque, we can disable blending for slightly
369    // more optimized painting.
370    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
371  }
372
373  SkPoint origin;
374  origin.set(SkIntToScalar(plugin_rect.x()), SkIntToScalar(plugin_rect.y()));
375
376  SkPoint pixel_origin = origin;
377
378  if (scale_ != 1.0f && scale_ > 0.0f) {
379    canvas->scale(scale_, scale_);
380    pixel_origin.set(pixel_origin.x() * (1.0f / scale_),
381                     pixel_origin.y() * (1.0f / scale_));
382  }
383  canvas->drawBitmap(image, pixel_origin.x(), pixel_origin.y(), &paint);
384}
385
386void PepperGraphics2DHost::ViewInitiatedPaint() {}
387
388void PepperGraphics2DHost::ViewFlushedPaint() {
389  TRACE_EVENT0("pepper", "PepperGraphics2DHost::ViewFlushedPaint");
390  if (need_flush_ack_) {
391    SendFlushAck();
392    need_flush_ack_ = false;
393  }
394}
395
396void PepperGraphics2DHost::SetScale(float scale) { scale_ = scale; }
397
398float PepperGraphics2DHost::GetScale() const { return scale_; }
399
400bool PepperGraphics2DHost::IsAlwaysOpaque() const { return is_always_opaque_; }
401
402PPB_ImageData_Impl* PepperGraphics2DHost::ImageData() {
403  return image_data_.get();
404}
405
406gfx::Size PepperGraphics2DHost::Size() const {
407  if (!image_data_.get())
408    return gfx::Size();
409  return gfx::Size(image_data_->width(), image_data_->height());
410}
411
412void PepperGraphics2DHost::ClearCache() {
413  cached_bitmap_.reset();
414}
415
416int32_t PepperGraphics2DHost::OnHostMsgPaintImageData(
417    ppapi::host::HostMessageContext* context,
418    const ppapi::HostResource& image_data,
419    const PP_Point& top_left,
420    bool src_rect_specified,
421    const PP_Rect& src_rect) {
422  EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(),
423                                               true);
424  if (enter.failed())
425    return PP_ERROR_BADRESOURCE;
426  PPB_ImageData_Impl* image_resource =
427      static_cast<PPB_ImageData_Impl*>(enter.object());
428
429  QueuedOperation operation(QueuedOperation::PAINT);
430  operation.paint_image = image_resource;
431  if (!ValidateAndConvertRect(src_rect_specified ? &src_rect : NULL,
432                              image_resource->width(),
433                              image_resource->height(),
434                              &operation.paint_src_rect))
435    return PP_ERROR_BADARGUMENT;
436
437  // Validate the bitmap position using the previously-validated rect, there
438  // should be no painted area outside of the image.
439  int64 x64 = static_cast<int64>(top_left.x);
440  int64 y64 = static_cast<int64>(top_left.y);
441  if (x64 + static_cast<int64>(operation.paint_src_rect.x()) < 0 ||
442      x64 + static_cast<int64>(operation.paint_src_rect.right()) >
443          image_data_->width())
444    return PP_ERROR_BADARGUMENT;
445  if (y64 + static_cast<int64>(operation.paint_src_rect.y()) < 0 ||
446      y64 + static_cast<int64>(operation.paint_src_rect.bottom()) >
447          image_data_->height())
448    return PP_ERROR_BADARGUMENT;
449  operation.paint_x = top_left.x;
450  operation.paint_y = top_left.y;
451
452  queued_operations_.push_back(operation);
453  return PP_OK;
454}
455
456int32_t PepperGraphics2DHost::OnHostMsgScroll(
457    ppapi::host::HostMessageContext* context,
458    bool clip_specified,
459    const PP_Rect& clip,
460    const PP_Point& amount) {
461  QueuedOperation operation(QueuedOperation::SCROLL);
462  if (!ValidateAndConvertRect(clip_specified ? &clip : NULL,
463                              image_data_->width(),
464                              image_data_->height(),
465                              &operation.scroll_clip_rect))
466    return PP_ERROR_BADARGUMENT;
467
468  // If we're being asked to scroll by more than the clip rect size, just
469  // ignore this scroll command and say it worked.
470  int32 dx = amount.x;
471  int32 dy = amount.y;
472  if (dx <= -image_data_->width() || dx >= image_data_->width() ||
473      dy <= -image_data_->height() || dy >= image_data_->height())
474    return PP_ERROR_BADARGUMENT;
475
476  operation.scroll_dx = dx;
477  operation.scroll_dy = dy;
478
479  queued_operations_.push_back(operation);
480  return PP_OK;
481}
482
483int32_t PepperGraphics2DHost::OnHostMsgReplaceContents(
484    ppapi::host::HostMessageContext* context,
485    const ppapi::HostResource& image_data) {
486  EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(),
487                                               true);
488  if (enter.failed())
489    return PP_ERROR_BADRESOURCE;
490  PPB_ImageData_Impl* image_resource =
491      static_cast<PPB_ImageData_Impl*>(enter.object());
492
493  if (!PPB_ImageData_Impl::IsImageDataFormatSupported(image_resource->format()))
494    return PP_ERROR_BADARGUMENT;
495
496  if (image_resource->width() != image_data_->width() ||
497      image_resource->height() != image_data_->height())
498    return PP_ERROR_BADARGUMENT;
499
500  QueuedOperation operation(QueuedOperation::REPLACE);
501  operation.replace_image = image_resource;
502  queued_operations_.push_back(operation);
503  return PP_OK;
504}
505
506int32_t PepperGraphics2DHost::OnHostMsgFlush(
507    ppapi::host::HostMessageContext* context,
508    const std::vector<ui::LatencyInfo>& latency_info) {
509  // Don't allow more than one pending flush at a time.
510  if (HasPendingFlush())
511    return PP_ERROR_INPROGRESS;
512
513  if (bound_instance_)
514    bound_instance_->AddLatencyInfo(latency_info);
515
516  PP_Resource old_image_data = 0;
517  flush_reply_context_ = context->MakeReplyMessageContext();
518  if (is_running_in_process_)
519    return Flush(NULL);
520
521  // Reuse image data when running out of process.
522  int32_t result = Flush(&old_image_data);
523
524  if (old_image_data) {
525    // If the Graphics2D has an old image data it's not using any more, send
526    // it back to the plugin for possible re-use. See ppb_image_data_proxy.cc
527    // for a description how this process works.
528    ppapi::HostResource old_image_data_host_resource;
529    old_image_data_host_resource.SetHostResource(pp_instance(), old_image_data);
530    host()->Send(new PpapiMsg_PPBImageData_NotifyUnusedImageData(
531        ppapi::API_ID_PPB_IMAGE_DATA, old_image_data_host_resource));
532  }
533
534  return result;
535}
536
537int32_t PepperGraphics2DHost::OnHostMsgSetScale(
538    ppapi::host::HostMessageContext* context,
539    float scale) {
540  if (scale > 0.0f) {
541    scale_ = scale;
542    return PP_OK;
543  }
544  return PP_ERROR_BADARGUMENT;
545}
546
547int32_t PepperGraphics2DHost::OnHostMsgReadImageData(
548    ppapi::host::HostMessageContext* context,
549    PP_Resource image,
550    const PP_Point& top_left) {
551  context->reply_msg = PpapiPluginMsg_Graphics2D_ReadImageDataAck();
552  return ReadImageData(image, &top_left) ? PP_OK : PP_ERROR_FAILED;
553}
554
555void PepperGraphics2DHost::ReleaseCallback(scoped_ptr<cc::SharedBitmap> bitmap,
556                                           const gfx::Size& bitmap_size,
557                                           uint32 sync_point,
558                                           bool lost_resource) {
559  cached_bitmap_.reset();
560  // Only keep around a cached bitmap if the plugin is currently drawing (has
561  // need_flush_ack_ set).
562  if (need_flush_ack_ && bound_instance_)
563    cached_bitmap_ = bitmap.Pass();
564  cached_bitmap_size_ = bitmap_size;
565}
566
567bool PepperGraphics2DHost::PrepareTextureMailbox(
568    cc::TextureMailbox* mailbox,
569    scoped_ptr<cc::SingleReleaseCallback>* release_callback) {
570  if (!texture_mailbox_modified_)
571    return false;
572  // TODO(jbauman): Send image_data_ through mailbox to avoid copy.
573  gfx::Size pixel_image_size(image_data_->width(), image_data_->height());
574  scoped_ptr<cc::SharedBitmap> shared_bitmap;
575  if (cached_bitmap_) {
576    if (cached_bitmap_size_ == pixel_image_size)
577      shared_bitmap = cached_bitmap_.Pass();
578    else
579      cached_bitmap_.reset();
580  }
581  if (!shared_bitmap) {
582    shared_bitmap = RenderThreadImpl::current()
583                        ->shared_bitmap_manager()
584                        ->AllocateSharedBitmap(pixel_image_size);
585  }
586  if (!shared_bitmap)
587    return false;
588  void* src = image_data_->Map();
589  memcpy(shared_bitmap->pixels(),
590         src,
591         cc::SharedBitmap::CheckedSizeInBytes(pixel_image_size));
592  image_data_->Unmap();
593
594  *mailbox = cc::TextureMailbox(shared_bitmap->memory(), pixel_image_size);
595  *release_callback = cc::SingleReleaseCallback::Create(
596      base::Bind(&PepperGraphics2DHost::ReleaseCallback,
597                 this->AsWeakPtr(),
598                 base::Passed(&shared_bitmap),
599                 pixel_image_size));
600  texture_mailbox_modified_ = false;
601  return true;
602}
603
604void PepperGraphics2DHost::AttachedToNewLayer() {
605  texture_mailbox_modified_ = true;
606}
607
608int32_t PepperGraphics2DHost::Flush(PP_Resource* old_image_data) {
609  bool done_replace_contents = false;
610  bool no_update_visible = true;
611  bool is_plugin_visible = true;
612  for (size_t i = 0; i < queued_operations_.size(); i++) {
613    QueuedOperation& operation = queued_operations_[i];
614    gfx::Rect op_rect;
615    switch (operation.type) {
616      case QueuedOperation::PAINT:
617        ExecutePaintImageData(operation.paint_image.get(),
618                              operation.paint_x,
619                              operation.paint_y,
620                              operation.paint_src_rect,
621                              &op_rect);
622        break;
623      case QueuedOperation::SCROLL:
624        ExecuteScroll(operation.scroll_clip_rect,
625                      operation.scroll_dx,
626                      operation.scroll_dy,
627                      &op_rect);
628        break;
629      case QueuedOperation::REPLACE:
630        // Since the out parameter |old_image_data| takes ownership of the
631        // reference, if there are more than one ReplaceContents calls queued
632        // the first |old_image_data| will get overwritten and leaked. So we
633        // only supply this for the first call.
634        ExecuteReplaceContents(operation.replace_image.get(),
635                               &op_rect,
636                               done_replace_contents ? NULL : old_image_data);
637        done_replace_contents = true;
638        break;
639    }
640
641    // For correctness with accelerated compositing, we must issue an invalidate
642    // on the full op_rect even if it is partially or completely off-screen.
643    // However, if we issue an invalidate for a clipped-out region, WebKit will
644    // do nothing and we won't get any ViewFlushedPaint calls, leaving our
645    // callback stranded. So we still need to check whether the repainted area
646    // is visible to determine how to deal with the callback.
647    if (bound_instance_ && !op_rect.IsEmpty()) {
648      gfx::Point scroll_delta(operation.scroll_dx, operation.scroll_dy);
649      if (!ConvertToLogicalPixels(scale_,
650                                  &op_rect,
651                                  operation.type == QueuedOperation::SCROLL
652                                      ? &scroll_delta
653                                      : NULL)) {
654        // Conversion requires falling back to InvalidateRect.
655        operation.type = QueuedOperation::PAINT;
656      }
657
658      gfx::Rect clip = PP_ToGfxRect(bound_instance_->view_data().clip_rect);
659      is_plugin_visible = !clip.IsEmpty();
660
661      // Set |no_update_visible| to false if the change overlaps the visible
662      // area.
663      if (!gfx::IntersectRects(clip, op_rect).IsEmpty()) {
664        no_update_visible = false;
665      }
666
667      // Notify the plugin of the entire change (op_rect), even if it is
668      // partially or completely off-screen.
669      if (operation.type == QueuedOperation::SCROLL) {
670        bound_instance_->ScrollRect(
671            scroll_delta.x(), scroll_delta.y(), op_rect);
672      } else {
673        if (!op_rect.IsEmpty())
674          bound_instance_->InvalidateRect(op_rect);
675      }
676      texture_mailbox_modified_ = true;
677    }
678  }
679  queued_operations_.clear();
680
681  if (!bound_instance_) {
682    // As promised in the API, we always schedule callback when unbound.
683    ScheduleOffscreenFlushAck();
684  } else if (no_update_visible && is_plugin_visible &&
685             bound_instance_->view_data().is_page_visible) {
686    // There's nothing visible to invalidate so just schedule the callback to
687    // execute in the next round of the message loop.
688    ScheduleOffscreenFlushAck();
689  } else {
690    need_flush_ack_ = true;
691  }
692
693  return PP_OK_COMPLETIONPENDING;
694}
695
696void PepperGraphics2DHost::ExecutePaintImageData(PPB_ImageData_Impl* image,
697                                                 int x,
698                                                 int y,
699                                                 const gfx::Rect& src_rect,
700                                                 gfx::Rect* invalidated_rect) {
701  // Ensure the source image is mapped to read from it.
702  ImageDataAutoMapper auto_mapper(image);
703  if (!auto_mapper.is_valid())
704    return;
705
706  // Portion within the source image to cut out.
707  SkIRect src_irect = {src_rect.x(), src_rect.y(), src_rect.right(),
708                       src_rect.bottom()};
709
710  // Location within the backing store to copy to.
711  *invalidated_rect = src_rect;
712  invalidated_rect->Offset(x, y);
713  SkRect dest_rect = {SkIntToScalar(invalidated_rect->x()),
714                      SkIntToScalar(invalidated_rect->y()),
715                      SkIntToScalar(invalidated_rect->right()),
716                      SkIntToScalar(invalidated_rect->bottom())};
717
718  if (image->format() != image_data_->format()) {
719    // Convert the image data if the format does not match.
720    ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
721  } else {
722    // We're guaranteed to have a mapped canvas since we mapped it in Init().
723    SkCanvas* backing_canvas = image_data_->GetCanvas();
724
725    // We want to replace the contents of the bitmap rather than blend.
726    SkPaint paint;
727    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
728    backing_canvas->drawBitmapRect(
729        *image->GetMappedBitmap(), &src_irect, dest_rect, &paint);
730  }
731}
732
733void PepperGraphics2DHost::ExecuteScroll(const gfx::Rect& clip,
734                                         int dx,
735                                         int dy,
736                                         gfx::Rect* invalidated_rect) {
737  gfx::ScrollCanvas(image_data_->GetCanvas(), clip, gfx::Vector2d(dx, dy));
738  *invalidated_rect = clip;
739}
740
741void PepperGraphics2DHost::ExecuteReplaceContents(PPB_ImageData_Impl* image,
742                                                  gfx::Rect* invalidated_rect,
743                                                  PP_Resource* old_image_data) {
744  if (image->format() != image_data_->format()) {
745    DCHECK(image->width() == image_data_->width() &&
746           image->height() == image_data_->height());
747    // Convert the image data if the format does not match.
748    SkIRect src_irect = {0, 0, image->width(), image->height()};
749    SkRect dest_rect = {SkIntToScalar(0), SkIntToScalar(0),
750                        SkIntToScalar(image_data_->width()),
751                        SkIntToScalar(image_data_->height())};
752    ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
753  } else {
754    // The passed-in image may not be mapped in our process, and we need to
755    // guarantee that the current backing store is always mapped.
756    if (!image->Map())
757      return;
758
759    if (old_image_data)
760      *old_image_data = image_data_->GetReference();
761    image_data_ = image;
762  }
763  *invalidated_rect =
764      gfx::Rect(0, 0, image_data_->width(), image_data_->height());
765}
766
767void PepperGraphics2DHost::SendFlushAck() {
768  host()->SendReply(flush_reply_context_, PpapiPluginMsg_Graphics2D_FlushAck());
769}
770
771void PepperGraphics2DHost::SendOffscreenFlushAck() {
772  DCHECK(offscreen_flush_pending_);
773
774  // We must clear this flag before issuing the callback. It will be
775  // common for the plugin to issue another invalidate in response to a flush
776  // callback, and we don't want to think that a callback is already pending.
777  offscreen_flush_pending_ = false;
778  SendFlushAck();
779}
780
781void PepperGraphics2DHost::ScheduleOffscreenFlushAck() {
782  offscreen_flush_pending_ = true;
783  base::MessageLoop::current()->PostDelayedTask(
784      FROM_HERE,
785      base::Bind(&PepperGraphics2DHost::SendOffscreenFlushAck, AsWeakPtr()),
786      base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs));
787}
788
789bool PepperGraphics2DHost::HasPendingFlush() const {
790  return need_flush_ack_ || offscreen_flush_pending_;
791}
792
793// static
794bool PepperGraphics2DHost::ConvertToLogicalPixels(float scale,
795                                                  gfx::Rect* op_rect,
796                                                  gfx::Point* delta) {
797  if (scale == 1.0f || scale <= 0.0f)
798    return true;
799
800  gfx::Rect original_rect = *op_rect;
801  // Take the enclosing rectangle after scaling so a rectangle scaled down then
802  // scaled back up by the inverse scale would fully contain the entire area
803  // affected by the original rectangle.
804  *op_rect = gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, scale));
805  if (delta) {
806    gfx::Point original_delta = *delta;
807    float inverse_scale = 1.0f / scale;
808    *delta = gfx::ToFlooredPoint(gfx::ScalePoint(*delta, scale));
809
810    gfx::Rect inverse_scaled_rect =
811        gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, inverse_scale));
812    if (original_rect != inverse_scaled_rect)
813      return false;
814    gfx::Point inverse_scaled_point =
815        gfx::ToFlooredPoint(gfx::ScalePoint(*delta, inverse_scale));
816    if (original_delta != inverse_scaled_point)
817      return false;
818  }
819
820  return true;
821}
822
823}  // namespace content
824