Upstream version 5.34.92.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/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   if (bound_instance_)
428     bound_instance_->UpdateLayerTransform();
429 }
430
431 void PepperGraphics2DHost::SetScale(float scale) {
432   scale_ = scale;
433 }
434
435 float PepperGraphics2DHost::GetScale() const {
436   return scale_;
437 }
438
439 bool PepperGraphics2DHost::IsAlwaysOpaque() const {
440   return is_always_opaque_;
441 }
442
443 PPB_ImageData_Impl* PepperGraphics2DHost::ImageData() {
444   return image_data_.get();
445 }
446
447 gfx::Size PepperGraphics2DHost::Size() const {
448   if (!image_data_)
449     return gfx::Size();
450   return gfx::Size(image_data_->width(), image_data_->height());
451 }
452
453 gfx::PointF PepperGraphics2DHost::GetResizeScale() const {
454   switch (resize_mode_) {
455     case PP_GRAPHICS2D_DEV_RESIZEMODE_DEFAULT:
456       return gfx::PointF(1.0f, 1.0f);
457     case PP_GRAPHICS2D_DEV_RESIZEMODE_STRETCH:
458       if (flushed_plugin_size_.IsEmpty())
459         return gfx::PointF(1.0f, 1.0f);
460       return gfx::PointF(
461           1.0f * current_plugin_size_.width() / flushed_plugin_size_.width(),
462           1.0f * current_plugin_size_.height() / flushed_plugin_size_.height());
463   }
464   NOTREACHED();
465   return gfx::PointF(1.0f, 1.0f);
466 }
467
468 int32_t PepperGraphics2DHost::OnHostMsgPaintImageData(
469     ppapi::host::HostMessageContext* context,
470     const ppapi::HostResource& image_data,
471     const PP_Point& top_left,
472     bool src_rect_specified,
473     const PP_Rect& src_rect) {
474   EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(),
475                                                true);
476   if (enter.failed())
477     return PP_ERROR_BADRESOURCE;
478   PPB_ImageData_Impl* image_resource =
479       static_cast<PPB_ImageData_Impl*>(enter.object());
480
481   QueuedOperation operation(QueuedOperation::PAINT);
482   operation.paint_image = image_resource;
483   if (!ValidateAndConvertRect(src_rect_specified ? &src_rect : NULL,
484                               image_resource->width(),
485                               image_resource->height(),
486                               &operation.paint_src_rect))
487     return PP_ERROR_BADARGUMENT;
488
489   // Validate the bitmap position using the previously-validated rect, there
490   // should be no painted area outside of the image.
491   int64 x64 = static_cast<int64>(top_left.x);
492   int64 y64 = static_cast<int64>(top_left.y);
493   if (x64 + static_cast<int64>(operation.paint_src_rect.x()) < 0 ||
494       x64 + static_cast<int64>(operation.paint_src_rect.right()) >
495       image_data_->width())
496     return PP_ERROR_BADARGUMENT;
497   if (y64 + static_cast<int64>(operation.paint_src_rect.y()) < 0 ||
498       y64 + static_cast<int64>(operation.paint_src_rect.bottom()) >
499       image_data_->height())
500     return PP_ERROR_BADARGUMENT;
501   operation.paint_x = top_left.x;
502   operation.paint_y = top_left.y;
503
504   queued_operations_.push_back(operation);
505   return PP_OK;
506 }
507
508 int32_t PepperGraphics2DHost::OnHostMsgScroll(
509     ppapi::host::HostMessageContext* context,
510     bool clip_specified,
511     const PP_Rect& clip,
512     const PP_Point& amount) {
513   QueuedOperation operation(QueuedOperation::SCROLL);
514   if (!ValidateAndConvertRect(clip_specified ? &clip : NULL,
515                               image_data_->width(),
516                               image_data_->height(),
517                               &operation.scroll_clip_rect))
518     return PP_ERROR_BADARGUMENT;
519
520   // If we're being asked to scroll by more than the clip rect size, just
521   // ignore this scroll command and say it worked.
522   int32 dx = amount.x;
523   int32 dy = amount.y;
524   if (dx <= -image_data_->width() || dx >= image_data_->width() ||
525       dy <= -image_data_->height() || dy >= image_data_->height())
526     return PP_ERROR_BADARGUMENT;
527
528   operation.scroll_dx = dx;
529   operation.scroll_dy = dy;
530
531   queued_operations_.push_back(operation);
532   return PP_OK;
533 }
534
535 int32_t PepperGraphics2DHost::OnHostMsgReplaceContents(
536     ppapi::host::HostMessageContext* context,
537     const ppapi::HostResource& image_data) {
538   EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(),
539                                                true);
540   if (enter.failed())
541     return PP_ERROR_BADRESOURCE;
542   PPB_ImageData_Impl* image_resource =
543       static_cast<PPB_ImageData_Impl*>(enter.object());
544
545   if (!PPB_ImageData_Impl::IsImageDataFormatSupported(
546           image_resource->format()))
547     return PP_ERROR_BADARGUMENT;
548
549   if (image_resource->width() != image_data_->width() ||
550       image_resource->height() != image_data_->height())
551     return PP_ERROR_BADARGUMENT;
552
553   QueuedOperation operation(QueuedOperation::REPLACE);
554   operation.replace_image = image_resource;
555   queued_operations_.push_back(operation);
556   return PP_OK;
557 }
558
559 int32_t PepperGraphics2DHost::OnHostMsgFlush(
560     ppapi::host::HostMessageContext* context,
561     const ppapi::ViewData& view_data) {
562   // Don't allow more than one pending flush at a time.
563   if (HasPendingFlush())
564     return PP_ERROR_INPROGRESS;
565
566   PP_Resource old_image_data = 0;
567   flush_reply_context_ = context->MakeReplyMessageContext();
568   if (is_running_in_process_)
569     return Flush(NULL, PP_ToGfxSize(view_data.rect.size));
570
571   // Reuse image data when running out of process.
572   int32_t result = Flush(&old_image_data, PP_ToGfxSize(view_data.rect.size));
573
574   if (old_image_data) {
575     // If the Graphics2D has an old image data it's not using any more, send
576     // it back to the plugin for possible re-use. See ppb_image_data_proxy.cc
577     // for a description how this process works.
578     ppapi::HostResource old_image_data_host_resource;
579     old_image_data_host_resource.SetHostResource(pp_instance(),
580                                                  old_image_data);
581     host()->Send(new PpapiMsg_PPBImageData_NotifyUnusedImageData(
582         ppapi::API_ID_PPB_IMAGE_DATA, old_image_data_host_resource));
583   }
584
585   return result;
586 }
587
588 int32_t PepperGraphics2DHost::OnHostMsgSetScale(
589     ppapi::host::HostMessageContext* context,
590     float scale) {
591   if (scale > 0.0f) {
592     scale_ = scale;
593     return PP_OK;
594   }
595   return PP_ERROR_BADARGUMENT;
596 }
597
598 int32_t PepperGraphics2DHost::OnHostMsgSetOffset(
599     ppapi::host::HostMessageContext* context,
600     const PP_Point& offset) {
601   QueuedOperation operation(QueuedOperation::SET_OFFSET);
602   operation.offset = PP_ToGfxPoint(offset);
603   queued_operations_.push_back(operation);
604   return PP_OK;
605 }
606
607 int32_t PepperGraphics2DHost::OnHostMsgSetResizeMode(
608     ppapi::host::HostMessageContext* context,
609     PP_Graphics2D_Dev_ResizeMode resize_mode) {
610   resize_mode_ = resize_mode;
611   return PP_OK;
612 }
613
614 int32_t PepperGraphics2DHost::OnHostMsgReadImageData(
615     ppapi::host::HostMessageContext* context,
616     PP_Resource image,
617     const PP_Point& top_left) {
618   context->reply_msg = PpapiPluginMsg_Graphics2D_ReadImageDataAck();
619   return ReadImageData(image, &top_left) ? PP_OK : PP_ERROR_FAILED;
620 }
621
622 void ReleaseCallback(scoped_ptr<base::SharedMemory> memory,
623                      unsigned sync_point,
624                      bool lost_resource) {}
625
626 bool PepperGraphics2DHost::PrepareTextureMailbox(
627     cc::TextureMailbox* mailbox,
628     scoped_ptr<cc::SingleReleaseCallback>* release_callback) {
629   if (!texture_mailbox_modified_)
630     return false;
631   // TODO(jbauman): Send image_data_ through mailbox to avoid copy.
632   gfx::Size pixel_image_size(image_data_->width(), image_data_->height());
633   int buffer_size = pixel_image_size.GetArea() * 4;
634   scoped_ptr<base::SharedMemory> memory =
635       RenderThread::Get()->HostAllocateSharedMemoryBuffer(buffer_size);
636   if (!memory || !memory->Map(buffer_size))
637     return false;
638   void* src = image_data_->Map();
639   memcpy(memory->memory(), src, buffer_size);
640   image_data_->Unmap();
641
642   *mailbox = cc::TextureMailbox(memory.get(), pixel_image_size);
643   *release_callback = cc::SingleReleaseCallback::Create(
644       base::Bind(&ReleaseCallback, base::Passed(&memory)));
645   texture_mailbox_modified_ = false;
646   return true;
647 }
648
649 void PepperGraphics2DHost::AttachedToNewLayer() {
650   texture_mailbox_modified_ = true;
651 }
652
653 int32_t PepperGraphics2DHost::Flush(PP_Resource* old_image_data,
654                                     const gfx::Size& flushed_plugin_size) {
655   bool done_replace_contents = false;
656   bool no_update_visible = true;
657   bool is_plugin_visible = true;
658   for (size_t i = 0; i < queued_operations_.size(); i++) {
659     QueuedOperation& operation = queued_operations_[i];
660     gfx::Rect op_rect;
661     gfx::Rect old_op_rect;
662     switch (operation.type) {
663       case QueuedOperation::PAINT:
664         ExecutePaintImageData(operation.paint_image.get(),
665                               operation.paint_x,
666                               operation.paint_y,
667                               operation.paint_src_rect,
668                               &op_rect);
669         break;
670       case QueuedOperation::SCROLL:
671         ExecuteScroll(operation.scroll_clip_rect,
672                       operation.scroll_dx, operation.scroll_dy,
673                       &op_rect);
674         break;
675       case QueuedOperation::REPLACE:
676         // Since the out parameter |old_image_data| takes ownership of the
677         // reference, if there are more than one ReplaceContents calls queued
678         // the first |old_image_data| will get overwritten and leaked. So we
679         // only supply this for the first call.
680         ExecuteReplaceContents(operation.replace_image.get(),
681                                &op_rect,
682                                done_replace_contents ? NULL : old_image_data);
683         done_replace_contents = true;
684         break;
685       case QueuedOperation::SET_OFFSET:
686         old_op_rect = gfx::Rect(plugin_offset_.x(), plugin_offset_.y(),
687             image_data_->width(), image_data_->height());
688         plugin_offset_ = operation.offset;
689         // The offset is applied below for |op_rect|.
690         op_rect = gfx::Rect(image_data_->width(), image_data_->height());
691         break;
692     }
693
694     op_rect.Offset(plugin_offset_.x(), plugin_offset_.y());
695
696     // For correctness with accelerated compositing, we must issue an invalidate
697     // on the full op_rect even if it is partially or completely off-screen.
698     // However, if we issue an invalidate for a clipped-out region, WebKit will
699     // do nothing and we won't get any ViewFlushedPaint calls, leaving our
700     // callback stranded. So we still need to check whether the repainted area
701     // is visible to determine how to deal with the callback.
702     if (bound_instance_ && (!op_rect.IsEmpty() || !old_op_rect.IsEmpty())) {
703       gfx::Point scroll_delta(operation.scroll_dx, operation.scroll_dy);
704       if (!ConvertToLogicalPixels(scale_, &old_op_rect, NULL)) {
705         NOTREACHED();
706       }
707       if (!ConvertToLogicalPixels(scale_,
708                                   &op_rect,
709                                   operation.type == QueuedOperation::SCROLL ?
710                                       &scroll_delta : NULL)) {
711         // Conversion requires falling back to InvalidateRect.
712         operation.type = QueuedOperation::PAINT;
713       }
714
715       gfx::Rect clip = PP_ToGfxRect(bound_instance_->view_data().clip_rect);
716       is_plugin_visible = !clip.IsEmpty();
717
718       // Set |no_update_visible| to false if the change overlaps the visible
719       // area.
720       if (!gfx::IntersectRects(clip, op_rect).IsEmpty() ||
721           !gfx::IntersectRects(clip, old_op_rect).IsEmpty()) {
722         no_update_visible = false;
723       }
724
725       // Notify the plugin of the entire change (op_rect), even if it is
726       // partially or completely off-screen.
727       if (operation.type == QueuedOperation::SCROLL) {
728         bound_instance_->ScrollRect(scroll_delta.x(), scroll_delta.y(),
729                                     op_rect);
730         DCHECK(old_op_rect.IsEmpty());
731       } else {
732         if (!op_rect.IsEmpty())
733           bound_instance_->InvalidateRect(op_rect);
734         if (!old_op_rect.IsEmpty())
735           bound_instance_->InvalidateRect(old_op_rect);
736       }
737       texture_mailbox_modified_ = true;
738     }
739   }
740   queued_operations_.clear();
741
742   flushed_plugin_size_ = flushed_plugin_size;
743   if (bound_instance_)
744     bound_instance_->UpdateLayerTransform();
745
746   if (!bound_instance_) {
747     // As promised in the API, we always schedule callback when unbound.
748     ScheduleOffscreenFlushAck();
749   } else if (no_update_visible && is_plugin_visible &&
750              bound_instance_->view_data().is_page_visible) {
751     // There's nothing visible to invalidate so just schedule the callback to
752     // execute in the next round of the message loop.
753     ScheduleOffscreenFlushAck();
754   } else {
755     need_flush_ack_ = true;
756   }
757
758   return PP_OK_COMPLETIONPENDING;
759 }
760
761 void PepperGraphics2DHost::ExecutePaintImageData(PPB_ImageData_Impl* image,
762                                                 int x, int y,
763                                                 const gfx::Rect& src_rect,
764                                                 gfx::Rect* invalidated_rect) {
765   // Ensure the source image is mapped to read from it.
766   ImageDataAutoMapper auto_mapper(image);
767   if (!auto_mapper.is_valid())
768     return;
769
770   // Portion within the source image to cut out.
771   SkIRect src_irect = { src_rect.x(), src_rect.y(),
772                         src_rect.right(), src_rect.bottom() };
773
774   // Location within the backing store to copy to.
775   *invalidated_rect = src_rect;
776   invalidated_rect->Offset(x, y);
777   SkRect dest_rect = { SkIntToScalar(invalidated_rect->x()),
778                        SkIntToScalar(invalidated_rect->y()),
779                        SkIntToScalar(invalidated_rect->right()),
780                        SkIntToScalar(invalidated_rect->bottom()) };
781
782   if (image->format() != image_data_->format()) {
783     // Convert the image data if the format does not match.
784     ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
785   } else {
786     // We're guaranteed to have a mapped canvas since we mapped it in Init().
787     SkCanvas* backing_canvas = image_data_->GetCanvas();
788
789     // We want to replace the contents of the bitmap rather than blend.
790     SkPaint paint;
791     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
792     backing_canvas->drawBitmapRect(*image->GetMappedBitmap(),
793                                    &src_irect, dest_rect, &paint);
794   }
795 }
796
797 void PepperGraphics2DHost::ExecuteScroll(const gfx::Rect& clip,
798                                         int dx, int dy,
799                                         gfx::Rect* invalidated_rect) {
800   gfx::ScrollCanvas(image_data_->GetCanvas(),
801                     clip, gfx::Vector2d(dx, dy));
802   *invalidated_rect = clip;
803 }
804
805 void PepperGraphics2DHost::ExecuteReplaceContents(PPB_ImageData_Impl* image,
806                                                  gfx::Rect* invalidated_rect,
807                                                  PP_Resource* old_image_data) {
808   if (image->format() != image_data_->format()) {
809     DCHECK(image->width() == image_data_->width() &&
810            image->height() == image_data_->height());
811     // Convert the image data if the format does not match.
812     SkIRect src_irect = { 0, 0, image->width(), image->height() };
813     SkRect dest_rect = { SkIntToScalar(0),
814                          SkIntToScalar(0),
815                          SkIntToScalar(image_data_->width()),
816                          SkIntToScalar(image_data_->height()) };
817     ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
818   } else {
819     // The passed-in image may not be mapped in our process, and we need to
820     // guarantee that the current backing store is always mapped.
821     if (!image->Map())
822       return;
823
824     if (old_image_data)
825       *old_image_data = image_data_->GetReference();
826     image_data_ = image;
827   }
828   *invalidated_rect = gfx::Rect(0, 0,
829                                 image_data_->width(), image_data_->height());
830 }
831
832 void PepperGraphics2DHost::SendFlushAck() {
833   host()->SendReply(flush_reply_context_,
834                     PpapiPluginMsg_Graphics2D_FlushAck());
835 }
836
837 void PepperGraphics2DHost::SendOffscreenFlushAck() {
838   DCHECK(offscreen_flush_pending_);
839
840   // We must clear this flag before issuing the callback. It will be
841   // common for the plugin to issue another invalidate in response to a flush
842   // callback, and we don't want to think that a callback is already pending.
843   offscreen_flush_pending_ = false;
844   SendFlushAck();
845 }
846
847 void PepperGraphics2DHost::ScheduleOffscreenFlushAck() {
848   offscreen_flush_pending_ = true;
849   base::MessageLoop::current()->PostDelayedTask(
850       FROM_HERE,
851       base::Bind(&PepperGraphics2DHost::SendOffscreenFlushAck,
852                  AsWeakPtr()),
853       base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs));
854 }
855
856 bool PepperGraphics2DHost::HasPendingFlush() const {
857   return need_flush_ack_ || offscreen_flush_pending_;
858 }
859
860 // static
861 bool PepperGraphics2DHost::ConvertToLogicalPixels(float scale,
862                                                  gfx::Rect* op_rect,
863                                                  gfx::Point* delta) {
864   if (scale == 1.0f || scale <= 0.0f)
865     return true;
866
867   gfx::Rect original_rect = *op_rect;
868   // Take the enclosing rectangle after scaling so a rectangle scaled down then
869   // scaled back up by the inverse scale would fully contain the entire area
870   // affected by the original rectangle.
871   *op_rect = gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, scale));
872   if (delta) {
873     gfx::Point original_delta = *delta;
874     float inverse_scale = 1.0f / scale;
875     *delta = gfx::ToFlooredPoint(gfx::ScalePoint(*delta, scale));
876
877     gfx::Rect inverse_scaled_rect =
878         gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, inverse_scale));
879     if (original_rect != inverse_scaled_rect)
880       return false;
881     gfx::Point inverse_scaled_point =
882         gfx::ToFlooredPoint(gfx::ScalePoint(*delta, inverse_scale));
883     if (original_delta != inverse_scaled_point)
884       return false;
885   }
886
887   return true;
888 }
889
890 }  // namespace content