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/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 
     45 using ppapi::thunk::EnterResourceNoLock;
     46 using ppapi::thunk::PPB_ImageData_API;
     47 
     48 namespace content {
     49 
     50 namespace {
     51 
     52 const 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.
     57 bool 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.
     87 void 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).
    104 void 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 
    140 struct 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
    162 PepperGraphics2DHost* 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 
    181 PepperGraphics2DHost::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 
    194 PepperGraphics2DHost::~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 
    200 bool 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 
    220 int32_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 
    240 bool PepperGraphics2DHost::IsGraphics2DHost() { return true; }
    241 
    242 bool 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 
    290 bool 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.
    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, 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 
    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_.get())
    408     return gfx::Size();
    409   return gfx::Size(image_data_->width(), image_data_->height());
    410 }
    411 
    412 void PepperGraphics2DHost::ClearCache() {
    413   cached_bitmap_.reset();
    414 }
    415 
    416 int32_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 
    456 int32_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 
    483 int32_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 
    506 int32_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 
    537 int32_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 
    547 int32_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 
    555 void 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 
    567 bool 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 
    604 void PepperGraphics2DHost::AttachedToNewLayer() {
    605   texture_mailbox_modified_ = true;
    606 }
    607 
    608 int32_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 
    696 void 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 
    733 void 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 
    741 void 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 
    767 void PepperGraphics2DHost::SendFlushAck() {
    768   host()->SendReply(flush_reply_context_, PpapiPluginMsg_Graphics2D_FlushAck());
    769 }
    770 
    771 void 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 
    781 void 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 
    789 bool PepperGraphics2DHost::HasPendingFlush() const {
    790   return need_flush_ack_ || offscreen_flush_pending_;
    791 }
    792 
    793 // static
    794 bool 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