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