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