Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / pepper_graphics_2d_host.cc
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   texture_mailbox_modified_ = true;
311
312   bound_instance_ = new_instance;
313   return true;
314 }
315
316 // The |backing_bitmap| must be clipped to the |plugin_rect| to avoid painting
317 // outside the plugin area. This can happen if the plugin has been resized since
318 // PaintImageData verified the image is within the plugin size.
319 void PepperGraphics2DHost::Paint(blink::WebCanvas* canvas,
320                                  const gfx::Rect& plugin_rect,
321                                  const gfx::Rect& paint_rect) {
322   TRACE_EVENT0("pepper", "PepperGraphics2DHost::Paint");
323   ImageDataAutoMapper auto_mapper(image_data_.get());
324   const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap();
325
326   gfx::Rect invalidate_rect = plugin_rect;
327   invalidate_rect.Intersect(paint_rect);
328   SkRect sk_invalidate_rect = gfx::RectToSkRect(invalidate_rect);
329   SkAutoCanvasRestore auto_restore(canvas, true);
330   canvas->clipRect(sk_invalidate_rect);
331   gfx::Size pixel_image_size(image_data_->width(), image_data_->height());
332   gfx::Size image_size =
333       gfx::ToFlooredSize(gfx::ScaleSize(pixel_image_size, scale_));
334
335   PepperPluginInstance* plugin_instance =
336       renderer_ppapi_host_->GetPluginInstance(pp_instance());
337   if (!plugin_instance)
338     return;
339   if (plugin_instance->IsFullPagePlugin()) {
340     // When we're resizing a window with a full-frame plugin, the plugin may
341     // not yet have bound a new device, which will leave parts of the
342     // background exposed if the window is getting larger. We want this to
343     // show white (typically less jarring) rather than black or uninitialized.
344     // We don't do this for non-full-frame plugins since we specifically want
345     // the page background to show through.
346     SkAutoCanvasRestore auto_restore(canvas, true);
347     SkRect image_data_rect =
348         gfx::RectToSkRect(gfx::Rect(plugin_rect.origin(), image_size));
349     canvas->clipRect(image_data_rect, SkRegion::kDifference_Op);
350
351     SkPaint paint;
352     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
353     paint.setColor(SK_ColorWHITE);
354     canvas->drawRect(sk_invalidate_rect, paint);
355   }
356
357   SkBitmap image;
358   // Copy to device independent bitmap when target canvas doesn't support
359   // platform paint.
360   if (!skia::SupportsPlatformPaint(canvas))
361     backing_bitmap.copyTo(&image, kN32_SkColorType);
362   else
363     image = backing_bitmap;
364
365   SkPaint paint;
366   if (is_always_opaque_) {
367     // When we know the device is opaque, we can disable blending for slightly
368     // more optimized painting.
369     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
370   }
371
372   SkPoint origin;
373   origin.set(SkIntToScalar(plugin_rect.x()), SkIntToScalar(plugin_rect.y()));
374
375   SkPoint pixel_origin = origin;
376
377   if (scale_ != 1.0f && scale_ > 0.0f) {
378     canvas->scale(scale_, scale_);
379     pixel_origin.set(pixel_origin.x() * (1.0f / scale_),
380                      pixel_origin.y() * (1.0f / scale_));
381   }
382   canvas->drawBitmap(image, pixel_origin.x(), pixel_origin.y(), &paint);
383 }
384
385 void PepperGraphics2DHost::ViewInitiatedPaint() {}
386
387 void PepperGraphics2DHost::ViewFlushedPaint() {
388   TRACE_EVENT0("pepper", "PepperGraphics2DHost::ViewFlushedPaint");
389   if (need_flush_ack_) {
390     SendFlushAck();
391     need_flush_ack_ = false;
392   }
393 }
394
395 void PepperGraphics2DHost::SetScale(float scale) { scale_ = scale; }
396
397 float PepperGraphics2DHost::GetScale() const { return scale_; }
398
399 bool PepperGraphics2DHost::IsAlwaysOpaque() const { return is_always_opaque_; }
400
401 PPB_ImageData_Impl* PepperGraphics2DHost::ImageData() {
402   return image_data_.get();
403 }
404
405 gfx::Size PepperGraphics2DHost::Size() const {
406   if (!image_data_)
407     return gfx::Size();
408   return gfx::Size(image_data_->width(), image_data_->height());
409 }
410
411 int32_t PepperGraphics2DHost::OnHostMsgPaintImageData(
412     ppapi::host::HostMessageContext* context,
413     const ppapi::HostResource& image_data,
414     const PP_Point& top_left,
415     bool src_rect_specified,
416     const PP_Rect& src_rect) {
417   EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(),
418                                                true);
419   if (enter.failed())
420     return PP_ERROR_BADRESOURCE;
421   PPB_ImageData_Impl* image_resource =
422       static_cast<PPB_ImageData_Impl*>(enter.object());
423
424   QueuedOperation operation(QueuedOperation::PAINT);
425   operation.paint_image = image_resource;
426   if (!ValidateAndConvertRect(src_rect_specified ? &src_rect : NULL,
427                               image_resource->width(),
428                               image_resource->height(),
429                               &operation.paint_src_rect))
430     return PP_ERROR_BADARGUMENT;
431
432   // Validate the bitmap position using the previously-validated rect, there
433   // should be no painted area outside of the image.
434   int64 x64 = static_cast<int64>(top_left.x);
435   int64 y64 = static_cast<int64>(top_left.y);
436   if (x64 + static_cast<int64>(operation.paint_src_rect.x()) < 0 ||
437       x64 + static_cast<int64>(operation.paint_src_rect.right()) >
438           image_data_->width())
439     return PP_ERROR_BADARGUMENT;
440   if (y64 + static_cast<int64>(operation.paint_src_rect.y()) < 0 ||
441       y64 + static_cast<int64>(operation.paint_src_rect.bottom()) >
442           image_data_->height())
443     return PP_ERROR_BADARGUMENT;
444   operation.paint_x = top_left.x;
445   operation.paint_y = top_left.y;
446
447   queued_operations_.push_back(operation);
448   return PP_OK;
449 }
450
451 int32_t PepperGraphics2DHost::OnHostMsgScroll(
452     ppapi::host::HostMessageContext* context,
453     bool clip_specified,
454     const PP_Rect& clip,
455     const PP_Point& amount) {
456   QueuedOperation operation(QueuedOperation::SCROLL);
457   if (!ValidateAndConvertRect(clip_specified ? &clip : NULL,
458                               image_data_->width(),
459                               image_data_->height(),
460                               &operation.scroll_clip_rect))
461     return PP_ERROR_BADARGUMENT;
462
463   // If we're being asked to scroll by more than the clip rect size, just
464   // ignore this scroll command and say it worked.
465   int32 dx = amount.x;
466   int32 dy = amount.y;
467   if (dx <= -image_data_->width() || dx >= image_data_->width() ||
468       dy <= -image_data_->height() || dy >= image_data_->height())
469     return PP_ERROR_BADARGUMENT;
470
471   operation.scroll_dx = dx;
472   operation.scroll_dy = dy;
473
474   queued_operations_.push_back(operation);
475   return PP_OK;
476 }
477
478 int32_t PepperGraphics2DHost::OnHostMsgReplaceContents(
479     ppapi::host::HostMessageContext* context,
480     const ppapi::HostResource& image_data) {
481   EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(),
482                                                true);
483   if (enter.failed())
484     return PP_ERROR_BADRESOURCE;
485   PPB_ImageData_Impl* image_resource =
486       static_cast<PPB_ImageData_Impl*>(enter.object());
487
488   if (!PPB_ImageData_Impl::IsImageDataFormatSupported(image_resource->format()))
489     return PP_ERROR_BADARGUMENT;
490
491   if (image_resource->width() != image_data_->width() ||
492       image_resource->height() != image_data_->height())
493     return PP_ERROR_BADARGUMENT;
494
495   QueuedOperation operation(QueuedOperation::REPLACE);
496   operation.replace_image = image_resource;
497   queued_operations_.push_back(operation);
498   return PP_OK;
499 }
500
501 int32_t PepperGraphics2DHost::OnHostMsgFlush(
502     ppapi::host::HostMessageContext* context,
503     const std::vector<ui::LatencyInfo>& latency_info) {
504   // Don't allow more than one pending flush at a time.
505   if (HasPendingFlush())
506     return PP_ERROR_INPROGRESS;
507
508   if (bound_instance_)
509     bound_instance_->AddLatencyInfo(latency_info);
510
511   PP_Resource old_image_data = 0;
512   flush_reply_context_ = context->MakeReplyMessageContext();
513   if (is_running_in_process_)
514     return Flush(NULL);
515
516   // Reuse image data when running out of process.
517   int32_t result = Flush(&old_image_data);
518
519   if (old_image_data) {
520     // If the Graphics2D has an old image data it's not using any more, send
521     // it back to the plugin for possible re-use. See ppb_image_data_proxy.cc
522     // for a description how this process works.
523     ppapi::HostResource old_image_data_host_resource;
524     old_image_data_host_resource.SetHostResource(pp_instance(), old_image_data);
525     host()->Send(new PpapiMsg_PPBImageData_NotifyUnusedImageData(
526         ppapi::API_ID_PPB_IMAGE_DATA, old_image_data_host_resource));
527   }
528
529   return result;
530 }
531
532 int32_t PepperGraphics2DHost::OnHostMsgSetScale(
533     ppapi::host::HostMessageContext* context,
534     float scale) {
535   if (scale > 0.0f) {
536     scale_ = scale;
537     return PP_OK;
538   }
539   return PP_ERROR_BADARGUMENT;
540 }
541
542 int32_t PepperGraphics2DHost::OnHostMsgReadImageData(
543     ppapi::host::HostMessageContext* context,
544     PP_Resource image,
545     const PP_Point& top_left) {
546   context->reply_msg = PpapiPluginMsg_Graphics2D_ReadImageDataAck();
547   return ReadImageData(image, &top_left) ? PP_OK : PP_ERROR_FAILED;
548 }
549
550 void ReleaseCallback(scoped_ptr<cc::SharedBitmap> bitmap,
551                      uint32 sync_point,
552                      bool lost_resource) {}
553
554 bool PepperGraphics2DHost::PrepareTextureMailbox(
555     cc::TextureMailbox* mailbox,
556     scoped_ptr<cc::SingleReleaseCallback>* release_callback) {
557   if (!texture_mailbox_modified_)
558     return false;
559   // TODO(jbauman): Send image_data_ through mailbox to avoid copy.
560   gfx::Size pixel_image_size(image_data_->width(), image_data_->height());
561   scoped_ptr<cc::SharedBitmap> shared_bitmap =
562       RenderThreadImpl::current()
563           ->shared_bitmap_manager()
564           ->AllocateSharedBitmap(pixel_image_size);
565   if (!shared_bitmap)
566     return false;
567   void* src = image_data_->Map();
568   memcpy(shared_bitmap->pixels(),
569          src,
570          cc::SharedBitmap::CheckedSizeInBytes(pixel_image_size));
571   image_data_->Unmap();
572
573   *mailbox = cc::TextureMailbox(shared_bitmap->memory(), pixel_image_size);
574   *release_callback = cc::SingleReleaseCallback::Create(
575       base::Bind(&ReleaseCallback, base::Passed(&shared_bitmap)));
576   texture_mailbox_modified_ = false;
577   return true;
578 }
579
580 void PepperGraphics2DHost::AttachedToNewLayer() {
581   texture_mailbox_modified_ = true;
582 }
583
584 int32_t PepperGraphics2DHost::Flush(PP_Resource* old_image_data) {
585   bool done_replace_contents = false;
586   bool no_update_visible = true;
587   bool is_plugin_visible = true;
588   for (size_t i = 0; i < queued_operations_.size(); i++) {
589     QueuedOperation& operation = queued_operations_[i];
590     gfx::Rect op_rect;
591     switch (operation.type) {
592       case QueuedOperation::PAINT:
593         ExecutePaintImageData(operation.paint_image.get(),
594                               operation.paint_x,
595                               operation.paint_y,
596                               operation.paint_src_rect,
597                               &op_rect);
598         break;
599       case QueuedOperation::SCROLL:
600         ExecuteScroll(operation.scroll_clip_rect,
601                       operation.scroll_dx,
602                       operation.scroll_dy,
603                       &op_rect);
604         break;
605       case QueuedOperation::REPLACE:
606         // Since the out parameter |old_image_data| takes ownership of the
607         // reference, if there are more than one ReplaceContents calls queued
608         // the first |old_image_data| will get overwritten and leaked. So we
609         // only supply this for the first call.
610         ExecuteReplaceContents(operation.replace_image.get(),
611                                &op_rect,
612                                done_replace_contents ? NULL : old_image_data);
613         done_replace_contents = true;
614         break;
615     }
616
617     // For correctness with accelerated compositing, we must issue an invalidate
618     // on the full op_rect even if it is partially or completely off-screen.
619     // However, if we issue an invalidate for a clipped-out region, WebKit will
620     // do nothing and we won't get any ViewFlushedPaint calls, leaving our
621     // callback stranded. So we still need to check whether the repainted area
622     // is visible to determine how to deal with the callback.
623     if (bound_instance_ && !op_rect.IsEmpty()) {
624       gfx::Point scroll_delta(operation.scroll_dx, operation.scroll_dy);
625       if (!ConvertToLogicalPixels(scale_,
626                                   &op_rect,
627                                   operation.type == QueuedOperation::SCROLL
628                                       ? &scroll_delta
629                                       : NULL)) {
630         // Conversion requires falling back to InvalidateRect.
631         operation.type = QueuedOperation::PAINT;
632       }
633
634       gfx::Rect clip = PP_ToGfxRect(bound_instance_->view_data().clip_rect);
635       is_plugin_visible = !clip.IsEmpty();
636
637       // Set |no_update_visible| to false if the change overlaps the visible
638       // area.
639       if (!gfx::IntersectRects(clip, op_rect).IsEmpty()) {
640         no_update_visible = false;
641       }
642
643       // Notify the plugin of the entire change (op_rect), even if it is
644       // partially or completely off-screen.
645       if (operation.type == QueuedOperation::SCROLL) {
646         bound_instance_->ScrollRect(
647             scroll_delta.x(), scroll_delta.y(), op_rect);
648       } else {
649         if (!op_rect.IsEmpty())
650           bound_instance_->InvalidateRect(op_rect);
651       }
652       texture_mailbox_modified_ = true;
653     }
654   }
655   queued_operations_.clear();
656
657   if (!bound_instance_) {
658     // As promised in the API, we always schedule callback when unbound.
659     ScheduleOffscreenFlushAck();
660   } else if (no_update_visible && is_plugin_visible &&
661              bound_instance_->view_data().is_page_visible) {
662     // There's nothing visible to invalidate so just schedule the callback to
663     // execute in the next round of the message loop.
664     ScheduleOffscreenFlushAck();
665   } else {
666     need_flush_ack_ = true;
667   }
668
669   return PP_OK_COMPLETIONPENDING;
670 }
671
672 void PepperGraphics2DHost::ExecutePaintImageData(PPB_ImageData_Impl* image,
673                                                  int x,
674                                                  int y,
675                                                  const gfx::Rect& src_rect,
676                                                  gfx::Rect* invalidated_rect) {
677   // Ensure the source image is mapped to read from it.
678   ImageDataAutoMapper auto_mapper(image);
679   if (!auto_mapper.is_valid())
680     return;
681
682   // Portion within the source image to cut out.
683   SkIRect src_irect = {src_rect.x(), src_rect.y(), src_rect.right(),
684                        src_rect.bottom()};
685
686   // Location within the backing store to copy to.
687   *invalidated_rect = src_rect;
688   invalidated_rect->Offset(x, y);
689   SkRect dest_rect = {SkIntToScalar(invalidated_rect->x()),
690                       SkIntToScalar(invalidated_rect->y()),
691                       SkIntToScalar(invalidated_rect->right()),
692                       SkIntToScalar(invalidated_rect->bottom())};
693
694   if (image->format() != image_data_->format()) {
695     // Convert the image data if the format does not match.
696     ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
697   } else {
698     // We're guaranteed to have a mapped canvas since we mapped it in Init().
699     SkCanvas* backing_canvas = image_data_->GetCanvas();
700
701     // We want to replace the contents of the bitmap rather than blend.
702     SkPaint paint;
703     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
704     backing_canvas->drawBitmapRect(
705         *image->GetMappedBitmap(), &src_irect, dest_rect, &paint);
706   }
707 }
708
709 void PepperGraphics2DHost::ExecuteScroll(const gfx::Rect& clip,
710                                          int dx,
711                                          int dy,
712                                          gfx::Rect* invalidated_rect) {
713   gfx::ScrollCanvas(image_data_->GetCanvas(), clip, gfx::Vector2d(dx, dy));
714   *invalidated_rect = clip;
715 }
716
717 void PepperGraphics2DHost::ExecuteReplaceContents(PPB_ImageData_Impl* image,
718                                                   gfx::Rect* invalidated_rect,
719                                                   PP_Resource* old_image_data) {
720   if (image->format() != image_data_->format()) {
721     DCHECK(image->width() == image_data_->width() &&
722            image->height() == image_data_->height());
723     // Convert the image data if the format does not match.
724     SkIRect src_irect = {0, 0, image->width(), image->height()};
725     SkRect dest_rect = {SkIntToScalar(0), SkIntToScalar(0),
726                         SkIntToScalar(image_data_->width()),
727                         SkIntToScalar(image_data_->height())};
728     ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
729   } else {
730     // The passed-in image may not be mapped in our process, and we need to
731     // guarantee that the current backing store is always mapped.
732     if (!image->Map())
733       return;
734
735     if (old_image_data)
736       *old_image_data = image_data_->GetReference();
737     image_data_ = image;
738   }
739   *invalidated_rect =
740       gfx::Rect(0, 0, image_data_->width(), image_data_->height());
741 }
742
743 void PepperGraphics2DHost::SendFlushAck() {
744   host()->SendReply(flush_reply_context_, PpapiPluginMsg_Graphics2D_FlushAck());
745 }
746
747 void PepperGraphics2DHost::SendOffscreenFlushAck() {
748   DCHECK(offscreen_flush_pending_);
749
750   // We must clear this flag before issuing the callback. It will be
751   // common for the plugin to issue another invalidate in response to a flush
752   // callback, and we don't want to think that a callback is already pending.
753   offscreen_flush_pending_ = false;
754   SendFlushAck();
755 }
756
757 void PepperGraphics2DHost::ScheduleOffscreenFlushAck() {
758   offscreen_flush_pending_ = true;
759   base::MessageLoop::current()->PostDelayedTask(
760       FROM_HERE,
761       base::Bind(&PepperGraphics2DHost::SendOffscreenFlushAck, AsWeakPtr()),
762       base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs));
763 }
764
765 bool PepperGraphics2DHost::HasPendingFlush() const {
766   return need_flush_ack_ || offscreen_flush_pending_;
767 }
768
769 // static
770 bool PepperGraphics2DHost::ConvertToLogicalPixels(float scale,
771                                                   gfx::Rect* op_rect,
772                                                   gfx::Point* delta) {
773   if (scale == 1.0f || scale <= 0.0f)
774     return true;
775
776   gfx::Rect original_rect = *op_rect;
777   // Take the enclosing rectangle after scaling so a rectangle scaled down then
778   // scaled back up by the inverse scale would fully contain the entire area
779   // affected by the original rectangle.
780   *op_rect = gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, scale));
781   if (delta) {
782     gfx::Point original_delta = *delta;
783     float inverse_scale = 1.0f / scale;
784     *delta = gfx::ToFlooredPoint(gfx::ScalePoint(*delta, scale));
785
786     gfx::Rect inverse_scaled_rect =
787         gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, inverse_scale));
788     if (original_rect != inverse_scaled_rect)
789       return false;
790     gfx::Point inverse_scaled_point =
791         gfx::ToFlooredPoint(gfx::ScalePoint(*delta, inverse_scale));
792     if (original_delta != inverse_scaled_point)
793       return false;
794   }
795
796   return true;
797 }
798
799 }  // namespace content