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.
5 #include "content/renderer/pepper/pepper_graphics_2d_host.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/trace_event/trace_event.h"
13 #include "cc/resources/shared_bitmap.h"
14 #include "cc/resources/texture_mailbox.h"
15 #include "content/child/child_shared_bitmap_manager.h"
16 #include "content/public/renderer/render_thread.h"
17 #include "content/public/renderer/renderer_ppapi_host.h"
18 #include "content/renderer/pepper/gfx_conversion.h"
19 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
20 #include "content/renderer/pepper/plugin_instance_throttler_impl.h"
21 #include "content/renderer/pepper/ppb_image_data_impl.h"
22 #include "content/renderer/render_thread_impl.h"
23 #include "ppapi/c/pp_bool.h"
24 #include "ppapi/c/pp_errors.h"
25 #include "ppapi/c/pp_rect.h"
26 #include "ppapi/c/pp_resource.h"
27 #include "ppapi/host/dispatch_host_message.h"
28 #include "ppapi/host/host_message_context.h"
29 #include "ppapi/host/ppapi_host.h"
30 #include "ppapi/proxy/ppapi_messages.h"
31 #include "ppapi/shared_impl/ppb_view_shared.h"
32 #include "ppapi/thunk/enter.h"
33 #include "skia/ext/platform_canvas.h"
34 #include "third_party/skia/include/core/SkBitmap.h"
35 #include "ui/gfx/blit.h"
36 #include "ui/gfx/geometry/point_conversions.h"
37 #include "ui/gfx/geometry/rect.h"
38 #include "ui/gfx/geometry/rect_conversions.h"
39 #include "ui/gfx/geometry/size_conversions.h"
40 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
41 #include "ui/gfx/skia_util.h"
43 #if defined(OS_MACOSX)
44 #include "base/mac/scoped_cftyperef.h"
47 using ppapi::thunk::EnterResourceNoLock
;
48 using ppapi::thunk::PPB_ImageData_API
;
54 const int64 kOffscreenCallbackDelayMs
= 1000 / 30; // 30 fps
56 // Converts a rect inside an image of the given dimensions. The rect may be
57 // NULL to indicate it should be the entire image. If the rect is outside of
58 // the image, this will do nothing and return false.
59 bool ValidateAndConvertRect(const PP_Rect
* rect
,
64 // Use the entire image area.
65 *dest
= gfx::Rect(0, 0, image_width
, image_height
);
67 // Validate the passed-in area.
68 if (rect
->point
.x
< 0 || rect
->point
.y
< 0 || rect
->size
.width
<= 0 ||
69 rect
->size
.height
<= 0)
72 // Check the max bounds, being careful of overflow.
73 if (static_cast<int64
>(rect
->point
.x
) +
74 static_cast<int64
>(rect
->size
.width
) >
75 static_cast<int64
>(image_width
))
77 if (static_cast<int64
>(rect
->point
.y
) +
78 static_cast<int64
>(rect
->size
.height
) >
79 static_cast<int64
>(image_height
))
83 rect
->point
.x
, rect
->point
.y
, rect
->size
.width
, rect
->size
.height
);
88 // Converts BGRA <-> RGBA.
89 void ConvertBetweenBGRAandRGBA(const uint32_t* input
,
92 for (int i
= 0; i
< pixel_length
; i
++) {
93 const unsigned char* pixel_in
=
94 reinterpret_cast<const unsigned char*>(&input
[i
]);
95 unsigned char* pixel_out
= reinterpret_cast<unsigned char*>(&output
[i
]);
96 pixel_out
[0] = pixel_in
[2];
97 pixel_out
[1] = pixel_in
[1];
98 pixel_out
[2] = pixel_in
[0];
99 pixel_out
[3] = pixel_in
[3];
103 // Converts ImageData from PP_IMAGEDATAFORMAT_BGRA_PREMUL to
104 // PP_IMAGEDATAFORMAT_RGBA_PREMUL, or reverse. It's assumed that the
105 // destination image is always mapped (so will have non-NULL data).
106 void ConvertImageData(PPB_ImageData_Impl
* src_image
,
107 const SkIRect
& src_rect
,
108 PPB_ImageData_Impl
* dest_image
,
109 const SkRect
& dest_rect
) {
110 ImageDataAutoMapper
auto_mapper(src_image
);
112 DCHECK(src_image
->format() != dest_image
->format());
113 DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(src_image
->format()));
114 DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(dest_image
->format()));
116 const SkBitmap
* src_bitmap
= src_image
->GetMappedBitmap();
117 const SkBitmap
* dest_bitmap
= dest_image
->GetMappedBitmap();
118 if (src_rect
.width() == src_image
->width() &&
119 dest_rect
.width() == dest_image
->width()) {
120 // Fast path if the full line needs to be converted.
121 ConvertBetweenBGRAandRGBA(
122 src_bitmap
->getAddr32(static_cast<int>(src_rect
.fLeft
),
123 static_cast<int>(src_rect
.fTop
)),
124 src_rect
.width() * src_rect
.height(),
125 dest_bitmap
->getAddr32(static_cast<int>(dest_rect
.fLeft
),
126 static_cast<int>(dest_rect
.fTop
)));
128 // Slow path where we convert line by line.
129 for (int y
= 0; y
< src_rect
.height(); y
++) {
130 ConvertBetweenBGRAandRGBA(
131 src_bitmap
->getAddr32(static_cast<int>(src_rect
.fLeft
),
132 static_cast<int>(src_rect
.fTop
+ y
)),
134 dest_bitmap
->getAddr32(static_cast<int>(dest_rect
.fLeft
),
135 static_cast<int>(dest_rect
.fTop
+ y
)));
142 struct PepperGraphics2DHost::QueuedOperation
{
143 enum Type
{ PAINT
, SCROLL
, REPLACE
, };
145 QueuedOperation(Type t
)
146 : type(t
), paint_x(0), paint_y(0), scroll_dx(0), scroll_dy(0) {}
150 // Valid when type == PAINT.
151 scoped_refptr
<PPB_ImageData_Impl
> paint_image
;
152 int paint_x
, paint_y
;
153 gfx::Rect paint_src_rect
;
155 // Valid when type == SCROLL.
156 gfx::Rect scroll_clip_rect
;
157 int scroll_dx
, scroll_dy
;
159 // Valid when type == REPLACE.
160 scoped_refptr
<PPB_ImageData_Impl
> replace_image
;
164 PepperGraphics2DHost
* PepperGraphics2DHost::Create(
165 RendererPpapiHost
* host
,
166 PP_Instance instance
,
167 PP_Resource resource
,
169 PP_Bool is_always_opaque
,
170 scoped_refptr
<PPB_ImageData_Impl
> backing_store
) {
171 PepperGraphics2DHost
* resource_host
=
172 new PepperGraphics2DHost(host
, instance
, resource
);
173 if (!resource_host
->Init(size
.width
,
175 PP_ToBool(is_always_opaque
),
177 delete resource_host
;
180 return resource_host
;
183 PepperGraphics2DHost::PepperGraphics2DHost(RendererPpapiHost
* host
,
184 PP_Instance instance
,
185 PP_Resource resource
)
186 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
187 renderer_ppapi_host_(host
),
188 bound_instance_(NULL
),
189 need_flush_ack_(false),
190 offscreen_flush_pending_(false),
191 is_always_opaque_(false),
193 is_running_in_process_(host
->IsRunningInProcess()),
194 texture_mailbox_modified_(true) {}
196 PepperGraphics2DHost::~PepperGraphics2DHost() {
197 // Unbind from the instance when destroyed if we're still bound.
199 bound_instance_
->BindGraphics(bound_instance_
->pp_instance(), 0);
202 bool PepperGraphics2DHost::Init(
205 bool is_always_opaque
,
206 scoped_refptr
<PPB_ImageData_Impl
> backing_store
) {
207 // The underlying PPB_ImageData_Impl will validate the dimensions.
208 image_data_
= backing_store
;
209 if (!image_data_
->Init(PPB_ImageData_Impl::GetNativeImageDataFormat(),
213 !image_data_
->Map()) {
217 is_always_opaque_
= is_always_opaque
;
222 int32_t PepperGraphics2DHost::OnResourceMessageReceived(
223 const IPC::Message
& msg
,
224 ppapi::host::HostMessageContext
* context
) {
225 PPAPI_BEGIN_MESSAGE_MAP(PepperGraphics2DHost
, msg
)
226 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_PaintImageData
,
227 OnHostMsgPaintImageData
)
228 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_Scroll
,
230 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_ReplaceContents
,
231 OnHostMsgReplaceContents
)
232 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_Flush
,
234 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_SetScale
,
236 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_ReadImageData
,
237 OnHostMsgReadImageData
)
238 PPAPI_END_MESSAGE_MAP()
239 return PP_ERROR_FAILED
;
242 bool PepperGraphics2DHost::IsGraphics2DHost() { return true; }
244 bool PepperGraphics2DHost::ReadImageData(PP_Resource image
,
245 const PP_Point
* top_left
) {
246 // Get and validate the image object to paint into.
247 EnterResourceNoLock
<PPB_ImageData_API
> enter(image
, true);
250 PPB_ImageData_Impl
* image_resource
=
251 static_cast<PPB_ImageData_Impl
*>(enter
.object());
252 if (!PPB_ImageData_Impl::IsImageDataFormatSupported(image_resource
->format()))
253 return false; // Must be in the right format.
255 // Validate the bitmap position.
258 static_cast<int64
>(x
) + static_cast<int64
>(image_resource
->width()) >
259 image_data_
->width())
263 static_cast<int64
>(y
) + static_cast<int64
>(image_resource
->height()) >
264 image_data_
->height())
267 ImageDataAutoMapper
auto_mapper(image_resource
);
268 if (!auto_mapper
.is_valid())
271 SkIRect src_irect
= {x
, y
, x
+ image_resource
->width(),
272 y
+ image_resource
->height()};
273 SkRect dest_rect
= {SkIntToScalar(0), SkIntToScalar(0),
274 SkIntToScalar(image_resource
->width()),
275 SkIntToScalar(image_resource
->height())};
277 if (image_resource
->format() != image_data_
->format()) {
278 // Convert the image data if the format does not match.
279 ConvertImageData(image_data_
.get(), src_irect
, image_resource
, dest_rect
);
281 SkCanvas
* dest_canvas
= image_resource
->GetCanvas();
283 // We want to replace the contents of the bitmap rather than blend.
285 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
286 dest_canvas
->drawBitmapRect(
287 *image_data_
->GetMappedBitmap(), src_irect
, dest_rect
, &paint
);
292 bool PepperGraphics2DHost::BindToInstance(
293 PepperPluginInstanceImpl
* new_instance
) {
294 if (new_instance
&& new_instance
->pp_instance() != pp_instance())
295 return false; // Can't bind other instance's contexts.
296 if (bound_instance_
== new_instance
)
297 return true; // Rebinding the same device, nothing to do.
298 if (bound_instance_
&& new_instance
)
299 return false; // Can't change a bound device.
302 // When the device is detached, we'll not get any more paint callbacks so
303 // we need to clear the list, but we still want to issue any pending
304 // callbacks to the plugin.
306 ScheduleOffscreenFlushAck();
308 // Devices being replaced, redraw the plugin.
309 new_instance
->InvalidateRect(gfx::Rect());
312 cached_bitmap_
.reset();
313 texture_mailbox_modified_
= true;
315 bound_instance_
= new_instance
;
319 // The |backing_bitmap| must be clipped to the |plugin_rect| to avoid painting
320 // outside the plugin area. This can happen if the plugin has been resized since
321 // PaintImageData verified the image is within the plugin size.
322 void PepperGraphics2DHost::Paint(blink::WebCanvas
* canvas
,
323 const gfx::Rect
& plugin_rect
,
324 const gfx::Rect
& paint_rect
) {
325 TRACE_EVENT0("pepper", "PepperGraphics2DHost::Paint");
326 ImageDataAutoMapper
auto_mapper(image_data_
.get());
327 const SkBitmap
& backing_bitmap
= *image_data_
->GetMappedBitmap();
329 gfx::Rect invalidate_rect
= plugin_rect
;
330 invalidate_rect
.Intersect(paint_rect
);
331 SkRect sk_invalidate_rect
= gfx::RectToSkRect(invalidate_rect
);
332 SkAutoCanvasRestore
auto_restore(canvas
, true);
333 canvas
->clipRect(sk_invalidate_rect
);
334 gfx::Size
pixel_image_size(image_data_
->width(), image_data_
->height());
335 gfx::Size image_size
=
336 gfx::ToFlooredSize(gfx::ScaleSize(pixel_image_size
, scale_
));
338 PepperPluginInstance
* plugin_instance
=
339 renderer_ppapi_host_
->GetPluginInstance(pp_instance());
340 if (!plugin_instance
)
342 if (plugin_instance
->IsFullPagePlugin()) {
343 // When we're resizing a window with a full-frame plugin, the plugin may
344 // not yet have bound a new device, which will leave parts of the
345 // background exposed if the window is getting larger. We want this to
346 // show white (typically less jarring) rather than black or uninitialized.
347 // We don't do this for non-full-frame plugins since we specifically want
348 // the page background to show through.
349 SkAutoCanvasRestore
auto_restore(canvas
, true);
350 SkRect image_data_rect
=
351 gfx::RectToSkRect(gfx::Rect(plugin_rect
.origin(), image_size
));
352 canvas
->clipRect(image_data_rect
, SkRegion::kDifference_Op
);
355 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
356 paint
.setColor(SK_ColorWHITE
);
357 canvas
->drawRect(sk_invalidate_rect
, paint
);
361 // Copy to device independent bitmap when target canvas doesn't support
363 if (!skia::SupportsPlatformPaint(canvas
))
364 backing_bitmap
.copyTo(&image
, kN32_SkColorType
);
366 image
= backing_bitmap
;
369 if (is_always_opaque_
) {
370 // When we know the device is opaque, we can disable blending for slightly
371 // more optimized painting.
372 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
376 origin
.set(SkIntToScalar(plugin_rect
.x()), SkIntToScalar(plugin_rect
.y()));
378 SkPoint pixel_origin
= origin
;
380 if (scale_
!= 1.0f
&& scale_
> 0.0f
) {
381 canvas
->scale(scale_
, scale_
);
382 pixel_origin
.set(pixel_origin
.x() * (1.0f
/ scale_
),
383 pixel_origin
.y() * (1.0f
/ scale_
));
385 canvas
->drawBitmap(image
, pixel_origin
.x(), pixel_origin
.y(), &paint
);
388 void PepperGraphics2DHost::ViewInitiatedPaint() {
389 TRACE_EVENT0("pepper", "PepperGraphics2DHost::ViewInitiatedPaint");
390 if (need_flush_ack_
) {
392 need_flush_ack_
= false;
396 void PepperGraphics2DHost::SetScale(float scale
) { scale_
= scale
; }
398 float PepperGraphics2DHost::GetScale() const { return scale_
; }
400 bool PepperGraphics2DHost::IsAlwaysOpaque() const { return is_always_opaque_
; }
402 PPB_ImageData_Impl
* PepperGraphics2DHost::ImageData() {
403 return image_data_
.get();
406 gfx::Size
PepperGraphics2DHost::Size() const {
407 if (!image_data_
.get())
409 return gfx::Size(image_data_
->width(), image_data_
->height());
412 void PepperGraphics2DHost::ClearCache() {
413 cached_bitmap_
.reset();
416 int32_t PepperGraphics2DHost::OnHostMsgPaintImageData(
417 ppapi::host::HostMessageContext
* context
,
418 const ppapi::HostResource
& image_data
,
419 const PP_Point
& top_left
,
420 bool src_rect_specified
,
421 const PP_Rect
& src_rect
) {
422 EnterResourceNoLock
<PPB_ImageData_API
> enter(image_data
.host_resource(),
425 return PP_ERROR_BADRESOURCE
;
426 PPB_ImageData_Impl
* image_resource
=
427 static_cast<PPB_ImageData_Impl
*>(enter
.object());
429 QueuedOperation
operation(QueuedOperation::PAINT
);
430 operation
.paint_image
= image_resource
;
431 if (!ValidateAndConvertRect(src_rect_specified
? &src_rect
: NULL
,
432 image_resource
->width(),
433 image_resource
->height(),
434 &operation
.paint_src_rect
))
435 return PP_ERROR_BADARGUMENT
;
437 // Validate the bitmap position using the previously-validated rect, there
438 // should be no painted area outside of the image.
439 int64 x64
= static_cast<int64
>(top_left
.x
);
440 int64 y64
= static_cast<int64
>(top_left
.y
);
441 if (x64
+ static_cast<int64
>(operation
.paint_src_rect
.x()) < 0 ||
442 x64
+ static_cast<int64
>(operation
.paint_src_rect
.right()) >
443 image_data_
->width())
444 return PP_ERROR_BADARGUMENT
;
445 if (y64
+ static_cast<int64
>(operation
.paint_src_rect
.y()) < 0 ||
446 y64
+ static_cast<int64
>(operation
.paint_src_rect
.bottom()) >
447 image_data_
->height())
448 return PP_ERROR_BADARGUMENT
;
449 operation
.paint_x
= top_left
.x
;
450 operation
.paint_y
= top_left
.y
;
452 queued_operations_
.push_back(operation
);
456 int32_t PepperGraphics2DHost::OnHostMsgScroll(
457 ppapi::host::HostMessageContext
* context
,
460 const PP_Point
& amount
) {
461 QueuedOperation
operation(QueuedOperation::SCROLL
);
462 if (!ValidateAndConvertRect(clip_specified
? &clip
: NULL
,
463 image_data_
->width(),
464 image_data_
->height(),
465 &operation
.scroll_clip_rect
))
466 return PP_ERROR_BADARGUMENT
;
468 // If we're being asked to scroll by more than the clip rect size, just
469 // ignore this scroll command and say it worked.
472 if (dx
<= -image_data_
->width() || dx
>= image_data_
->width() ||
473 dy
<= -image_data_
->height() || dy
>= image_data_
->height())
474 return PP_ERROR_BADARGUMENT
;
476 operation
.scroll_dx
= dx
;
477 operation
.scroll_dy
= dy
;
479 queued_operations_
.push_back(operation
);
483 int32_t PepperGraphics2DHost::OnHostMsgReplaceContents(
484 ppapi::host::HostMessageContext
* context
,
485 const ppapi::HostResource
& image_data
) {
486 EnterResourceNoLock
<PPB_ImageData_API
> enter(image_data
.host_resource(),
489 return PP_ERROR_BADRESOURCE
;
490 PPB_ImageData_Impl
* image_resource
=
491 static_cast<PPB_ImageData_Impl
*>(enter
.object());
493 if (!PPB_ImageData_Impl::IsImageDataFormatSupported(image_resource
->format()))
494 return PP_ERROR_BADARGUMENT
;
496 if (image_resource
->width() != image_data_
->width() ||
497 image_resource
->height() != image_data_
->height())
498 return PP_ERROR_BADARGUMENT
;
500 QueuedOperation
operation(QueuedOperation::REPLACE
);
501 operation
.replace_image
= image_resource
;
502 queued_operations_
.push_back(operation
);
506 int32_t PepperGraphics2DHost::OnHostMsgFlush(
507 ppapi::host::HostMessageContext
* context
,
508 const std::vector
<ui::LatencyInfo
>& latency_info
) {
509 // Don't allow more than one pending flush at a time.
510 if (HasPendingFlush())
511 return PP_ERROR_INPROGRESS
;
514 bound_instance_
->AddLatencyInfo(latency_info
);
516 PP_Resource old_image_data
= 0;
517 flush_reply_context_
= context
->MakeReplyMessageContext();
518 if (is_running_in_process_
)
521 // Reuse image data when running out of process.
522 int32_t result
= Flush(&old_image_data
);
524 if (old_image_data
) {
525 // If the Graphics2D has an old image data it's not using any more, send
526 // it back to the plugin for possible re-use. See ppb_image_data_proxy.cc
527 // for a description how this process works.
528 ppapi::HostResource old_image_data_host_resource
;
529 old_image_data_host_resource
.SetHostResource(pp_instance(), old_image_data
);
530 host()->Send(new PpapiMsg_PPBImageData_NotifyUnusedImageData(
531 ppapi::API_ID_PPB_IMAGE_DATA
, old_image_data_host_resource
));
537 int32_t PepperGraphics2DHost::OnHostMsgSetScale(
538 ppapi::host::HostMessageContext
* context
,
544 return PP_ERROR_BADARGUMENT
;
547 int32_t PepperGraphics2DHost::OnHostMsgReadImageData(
548 ppapi::host::HostMessageContext
* context
,
550 const PP_Point
& top_left
) {
551 context
->reply_msg
= PpapiPluginMsg_Graphics2D_ReadImageDataAck();
552 return ReadImageData(image
, &top_left
) ? PP_OK
: PP_ERROR_FAILED
;
555 void PepperGraphics2DHost::ReleaseCallback(scoped_ptr
<cc::SharedBitmap
> bitmap
,
556 const gfx::Size
& bitmap_size
,
558 bool lost_resource
) {
559 cached_bitmap_
.reset();
560 // Only keep around a cached bitmap if the plugin is currently drawing (has
561 // need_flush_ack_ set).
562 if (need_flush_ack_
&& bound_instance_
)
563 cached_bitmap_
= bitmap
.Pass();
564 cached_bitmap_size_
= bitmap_size
;
567 bool PepperGraphics2DHost::PrepareTextureMailbox(
568 cc::TextureMailbox
* mailbox
,
569 scoped_ptr
<cc::SingleReleaseCallback
>* release_callback
) {
570 if (!texture_mailbox_modified_
)
572 // TODO(jbauman): Send image_data_ through mailbox to avoid copy.
573 gfx::Size
pixel_image_size(image_data_
->width(), image_data_
->height());
574 scoped_ptr
<cc::SharedBitmap
> shared_bitmap
;
575 if (cached_bitmap_
) {
576 if (cached_bitmap_size_
== pixel_image_size
)
577 shared_bitmap
= cached_bitmap_
.Pass();
579 cached_bitmap_
.reset();
581 if (!shared_bitmap
) {
582 shared_bitmap
= RenderThreadImpl::current()
583 ->shared_bitmap_manager()
584 ->AllocateSharedBitmap(pixel_image_size
);
588 void* src
= image_data_
->Map();
589 memcpy(shared_bitmap
->pixels(),
591 cc::SharedBitmap::CheckedSizeInBytes(pixel_image_size
));
592 image_data_
->Unmap();
594 *mailbox
= cc::TextureMailbox(shared_bitmap
.get(), pixel_image_size
);
595 *release_callback
= cc::SingleReleaseCallback::Create(
596 base::Bind(&PepperGraphics2DHost::ReleaseCallback
,
598 base::Passed(&shared_bitmap
),
600 texture_mailbox_modified_
= false;
604 void PepperGraphics2DHost::AttachedToNewLayer() {
605 texture_mailbox_modified_
= true;
608 int32_t PepperGraphics2DHost::Flush(PP_Resource
* old_image_data
) {
609 bool done_replace_contents
= false;
610 bool no_update_visible
= true;
611 bool is_plugin_visible
= true;
612 for (size_t i
= 0; i
< queued_operations_
.size(); i
++) {
613 QueuedOperation
& operation
= queued_operations_
[i
];
615 switch (operation
.type
) {
616 case QueuedOperation::PAINT
:
617 ExecutePaintImageData(operation
.paint_image
.get(),
620 operation
.paint_src_rect
,
623 case QueuedOperation::SCROLL
:
624 ExecuteScroll(operation
.scroll_clip_rect
,
629 case QueuedOperation::REPLACE
:
630 // Since the out parameter |old_image_data| takes ownership of the
631 // reference, if there are more than one ReplaceContents calls queued
632 // the first |old_image_data| will get overwritten and leaked. So we
633 // only supply this for the first call.
634 ExecuteReplaceContents(operation
.replace_image
.get(),
636 done_replace_contents
? NULL
: old_image_data
);
637 done_replace_contents
= true;
641 // For correctness with accelerated compositing, we must issue an invalidate
642 // on the full op_rect even if it is partially or completely off-screen.
643 // However, if we issue an invalidate for a clipped-out region, WebKit will
644 // do nothing and we won't get any ViewFlushedPaint calls, leaving our
645 // callback stranded. So we still need to check whether the repainted area
646 // is visible to determine how to deal with the callback.
647 if (bound_instance_
&& !op_rect
.IsEmpty()) {
648 gfx::Point
scroll_delta(operation
.scroll_dx
, operation
.scroll_dy
);
649 if (!ConvertToLogicalPixels(scale_
,
651 operation
.type
== QueuedOperation::SCROLL
654 // Conversion requires falling back to InvalidateRect.
655 operation
.type
= QueuedOperation::PAINT
;
658 gfx::Rect clip
= PP_ToGfxRect(bound_instance_
->view_data().clip_rect
);
659 is_plugin_visible
= !clip
.IsEmpty();
661 // Set |no_update_visible| to false if the change overlaps the visible
663 if (!gfx::IntersectRects(clip
, op_rect
).IsEmpty()) {
664 no_update_visible
= false;
667 // Notify the plugin of the entire change (op_rect), even if it is
668 // partially or completely off-screen.
669 if (operation
.type
== QueuedOperation::SCROLL
) {
670 bound_instance_
->ScrollRect(
671 scroll_delta
.x(), scroll_delta
.y(), op_rect
);
673 if (!op_rect
.IsEmpty())
674 bound_instance_
->InvalidateRect(op_rect
);
676 texture_mailbox_modified_
= true;
679 queued_operations_
.clear();
681 if (!bound_instance_
) {
682 // As promised in the API, we always schedule callback when unbound.
683 ScheduleOffscreenFlushAck();
684 } else if (no_update_visible
&& is_plugin_visible
&&
685 bound_instance_
->view_data().is_page_visible
) {
686 // There's nothing visible to invalidate so just schedule the callback to
687 // execute in the next round of the message loop.
688 ScheduleOffscreenFlushAck();
690 need_flush_ack_
= true;
693 if (bound_instance_
&& bound_instance_
->throttler() &&
694 bound_instance_
->throttler()->needs_representative_keyframe()) {
695 bound_instance_
->throttler()->OnImageFlush(image_data_
->GetMappedBitmap());
698 return PP_OK_COMPLETIONPENDING
;
701 void PepperGraphics2DHost::ExecutePaintImageData(PPB_ImageData_Impl
* image
,
704 const gfx::Rect
& src_rect
,
705 gfx::Rect
* invalidated_rect
) {
706 // Ensure the source image is mapped to read from it.
707 ImageDataAutoMapper
auto_mapper(image
);
708 if (!auto_mapper
.is_valid())
711 // Portion within the source image to cut out.
712 SkIRect src_irect
= {src_rect
.x(), src_rect
.y(), src_rect
.right(),
715 // Location within the backing store to copy to.
716 *invalidated_rect
= src_rect
;
717 invalidated_rect
->Offset(x
, y
);
718 SkRect dest_rect
= {SkIntToScalar(invalidated_rect
->x()),
719 SkIntToScalar(invalidated_rect
->y()),
720 SkIntToScalar(invalidated_rect
->right()),
721 SkIntToScalar(invalidated_rect
->bottom())};
723 if (image
->format() != image_data_
->format()) {
724 // Convert the image data if the format does not match.
725 ConvertImageData(image
, src_irect
, image_data_
.get(), dest_rect
);
727 // We're guaranteed to have a mapped canvas since we mapped it in Init().
728 SkCanvas
* backing_canvas
= image_data_
->GetCanvas();
730 // We want to replace the contents of the bitmap rather than blend.
732 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
733 backing_canvas
->drawBitmapRect(
734 *image
->GetMappedBitmap(), src_irect
, dest_rect
, &paint
);
738 void PepperGraphics2DHost::ExecuteScroll(const gfx::Rect
& clip
,
741 gfx::Rect
* invalidated_rect
) {
742 gfx::ScrollCanvas(image_data_
->GetCanvas(), clip
, gfx::Vector2d(dx
, dy
));
743 *invalidated_rect
= clip
;
746 void PepperGraphics2DHost::ExecuteReplaceContents(PPB_ImageData_Impl
* image
,
747 gfx::Rect
* invalidated_rect
,
748 PP_Resource
* old_image_data
) {
749 if (image
->format() != image_data_
->format()) {
750 DCHECK(image
->width() == image_data_
->width() &&
751 image
->height() == image_data_
->height());
752 // Convert the image data if the format does not match.
753 SkIRect src_irect
= {0, 0, image
->width(), image
->height()};
754 SkRect dest_rect
= {SkIntToScalar(0), SkIntToScalar(0),
755 SkIntToScalar(image_data_
->width()),
756 SkIntToScalar(image_data_
->height())};
757 ConvertImageData(image
, src_irect
, image_data_
.get(), dest_rect
);
759 // The passed-in image may not be mapped in our process, and we need to
760 // guarantee that the current backing store is always mapped.
765 *old_image_data
= image_data_
->GetReference();
769 gfx::Rect(0, 0, image_data_
->width(), image_data_
->height());
772 void PepperGraphics2DHost::SendFlushAck() {
773 host()->SendReply(flush_reply_context_
, PpapiPluginMsg_Graphics2D_FlushAck());
776 void PepperGraphics2DHost::SendOffscreenFlushAck() {
777 DCHECK(offscreen_flush_pending_
);
779 // We must clear this flag before issuing the callback. It will be
780 // common for the plugin to issue another invalidate in response to a flush
781 // callback, and we don't want to think that a callback is already pending.
782 offscreen_flush_pending_
= false;
786 void PepperGraphics2DHost::ScheduleOffscreenFlushAck() {
787 offscreen_flush_pending_
= true;
788 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
790 base::Bind(&PepperGraphics2DHost::SendOffscreenFlushAck
, AsWeakPtr()),
791 base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs
));
794 bool PepperGraphics2DHost::HasPendingFlush() const {
795 return need_flush_ack_
|| offscreen_flush_pending_
;
799 bool PepperGraphics2DHost::ConvertToLogicalPixels(float scale
,
802 if (scale
== 1.0f
|| scale
<= 0.0f
)
805 gfx::Rect original_rect
= *op_rect
;
806 // Take the enclosing rectangle after scaling so a rectangle scaled down then
807 // scaled back up by the inverse scale would fully contain the entire area
808 // affected by the original rectangle.
809 *op_rect
= gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect
, scale
));
811 gfx::Point original_delta
= *delta
;
812 float inverse_scale
= 1.0f
/ scale
;
813 *delta
= gfx::ToFlooredPoint(gfx::ScalePoint(*delta
, scale
));
815 gfx::Rect inverse_scaled_rect
=
816 gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect
, inverse_scale
));
817 if (original_rect
!= inverse_scaled_rect
)
819 gfx::Point inverse_scaled_point
=
820 gfx::ToFlooredPoint(gfx::ScalePoint(*delta
, inverse_scale
));
821 if (original_delta
!= inverse_scaled_point
)
828 } // namespace content