• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ValidateAndConvertRect(const PP_Rect * rect,int image_width,int image_height,gfx::Rect * dest)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.
ConvertBetweenBGRAandRGBA(const uint32_t * input,int pixel_length,uint32_t * output)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).
ConvertImageData(PPB_ImageData_Impl * src_image,const SkIRect & src_rect,PPB_ImageData_Impl * dest_image,const SkRect & dest_rect)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 
QueuedOperationcontent::PepperGraphics2DHost::QueuedOperation143   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
Create(RendererPpapiHost * host,PP_Instance instance,PP_Resource resource,const PP_Size & size,PP_Bool is_always_opaque,scoped_refptr<PPB_ImageData_Impl> backing_store)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 
PepperGraphics2DHost(RendererPpapiHost * host,PP_Instance instance,PP_Resource resource)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 
~PepperGraphics2DHost()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 
Init(int width,int height,bool is_always_opaque,scoped_refptr<PPB_ImageData_Impl> backing_store)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 
OnResourceMessageReceived(const IPC::Message & msg,ppapi::host::HostMessageContext * context)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 
IsGraphics2DHost()258 bool PepperGraphics2DHost::IsGraphics2DHost() {
259   return true;
260 }
261 
ReadImageData(PP_Resource image,const PP_Point * top_left)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 
BindToInstance(PepperPluginInstanceImpl * new_instance)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.
Paint(blink::WebCanvas * canvas,const gfx::Rect & plugin_rect,const gfx::Rect & paint_rect)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 
ViewInitiatedPaint()413 void PepperGraphics2DHost::ViewInitiatedPaint() {
414 }
415 
ViewFlushedPaint()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 
DidChangeView(const ppapi::ViewData & view_data)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 
SetScale(float scale)429 void PepperGraphics2DHost::SetScale(float scale) {
430   scale_ = scale;
431 }
432 
GetScale() const433 float PepperGraphics2DHost::GetScale() const {
434   return scale_;
435 }
436 
IsAlwaysOpaque() const437 bool PepperGraphics2DHost::IsAlwaysOpaque() const {
438   return is_always_opaque_;
439 }
440 
ImageData()441 PPB_ImageData_Impl* PepperGraphics2DHost::ImageData() {
442   return image_data_.get();
443 }
444 
Size() const445 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 
GetResizeScale() const451 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 
OnHostMsgPaintImageData(ppapi::host::HostMessageContext * context,const ppapi::HostResource & image_data,const PP_Point & top_left,bool src_rect_specified,const PP_Rect & src_rect)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 
OnHostMsgScroll(ppapi::host::HostMessageContext * context,bool clip_specified,const PP_Rect & clip,const PP_Point & amount)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 
OnHostMsgReplaceContents(ppapi::host::HostMessageContext * context,const ppapi::HostResource & image_data)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 
OnHostMsgFlush(ppapi::host::HostMessageContext * context,const ppapi::ViewData & view_data)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 
OnHostMsgSetScale(ppapi::host::HostMessageContext * context,float scale)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 
OnHostMsgSetOffset(ppapi::host::HostMessageContext * context,const PP_Point & offset)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 
OnHostMsgSetResizeMode(ppapi::host::HostMessageContext * context,PP_Graphics2D_Dev_ResizeMode resize_mode)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 
OnHostMsgReadImageData(ppapi::host::HostMessageContext * context,PP_Resource image,const PP_Point & top_left)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 
ReleaseCallback(scoped_ptr<base::SharedMemory> memory,unsigned sync_point,bool lost_resource)620 void ReleaseCallback(scoped_ptr<base::SharedMemory> memory,
621                      unsigned sync_point,
622                      bool lost_resource) {}
623 
PrepareTextureMailbox(cc::TextureMailbox * mailbox,scoped_ptr<cc::SingleReleaseCallback> * release_callback)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 
AttachedToNewLayer()647 void PepperGraphics2DHost::AttachedToNewLayer() {
648   texture_mailbox_modified_ = true;
649 }
650 
Flush(PP_Resource * old_image_data,const gfx::Size & flushed_plugin_size)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 
ExecutePaintImageData(PPB_ImageData_Impl * image,int x,int y,const gfx::Rect & src_rect,gfx::Rect * invalidated_rect)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 
ExecuteScroll(const gfx::Rect & clip,int dx,int dy,gfx::Rect * invalidated_rect)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 
ExecuteReplaceContents(PPB_ImageData_Impl * image,gfx::Rect * invalidated_rect,PP_Resource * old_image_data)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 
SendFlushAck()827 void PepperGraphics2DHost::SendFlushAck() {
828   host()->SendReply(flush_reply_context_,
829                     PpapiPluginMsg_Graphics2D_FlushAck());
830 }
831 
SendOffscreenFlushAck()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 
ScheduleOffscreenFlushAck()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 
HasPendingFlush() const851 bool PepperGraphics2DHost::HasPendingFlush() const {
852   return need_flush_ack_ || offscreen_flush_pending_;
853 }
854 
855 // static
ConvertToLogicalPixels(float scale,gfx::Rect * op_rect,gfx::Point * delta)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