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 "webkit/plugins/ppapi/ppb_graphics_2d_impl.h"
10 #include "base/debug/trace_event.h"
11 #include "base/logging.h"
12 #include "base/message_loop.h"
13 #include "base/time.h"
14 #include "skia/ext/platform_canvas.h"
15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/c/pp_rect.h"
17 #include "ppapi/c/pp_resource.h"
18 #include "ppapi/c/ppb_graphics_2d.h"
19 #include "ppapi/thunk/enter.h"
20 #include "ppapi/thunk/thunk.h"
21 #include "third_party/skia/include/core/SkBitmap.h"
22 #include "ui/gfx/blit.h"
23 #include "ui/gfx/point.h"
24 #include "ui/gfx/point_conversions.h"
25 #include "ui/gfx/rect.h"
26 #include "ui/gfx/rect_conversions.h"
27 #include "ui/gfx/size_conversions.h"
28 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
29 #include "ui/gfx/skia_util.h"
30 #include "webkit/plugins/ppapi/common.h"
31 #include "webkit/plugins/ppapi/gfx_conversion.h"
32 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
33 #include "webkit/plugins/ppapi/ppb_image_data_impl.h"
34 #include "webkit/plugins/ppapi/resource_helper.h"
36 #if defined(OS_MACOSX)
37 #include "base/mac/mac_util.h"
38 #include "base/mac/scoped_cftyperef.h"
41 using ppapi::thunk::EnterResourceNoLock
;
42 using ppapi::thunk::PPB_ImageData_API
;
43 using ppapi::TrackedCallback
;
50 const int64 kOffscreenCallbackDelayMs
= 1000 / 30; // 30 fps
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
,
59 // Use the entire image area.
60 *dest
= gfx::Rect(0, 0, image_width
, image_height
);
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)
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
))
72 if (static_cast<int64
>(rect
->point
.y
) +
73 static_cast<int64
>(rect
->size
.height
) >
74 static_cast<int64
>(image_height
))
77 *dest
= gfx::Rect(rect
->point
.x
, rect
->point
.y
,
78 rect
->size
.width
, rect
->size
.height
);
83 // Converts BGRA <-> RGBA.
84 void ConvertBetweenBGRAandRGBA(const uint32_t* input
,
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];
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
);
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()));
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
)));
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
)),
127 dest_bitmap
->getAddr32(static_cast<int>(dest_rect
.fLeft
),
128 static_cast<int>(dest_rect
.fTop
+ y
)));
135 struct PPB_Graphics2D_Impl::QueuedOperation
{
142 QueuedOperation(Type t
)
152 // Valid when type == PAINT.
153 scoped_refptr
<PPB_ImageData_Impl
> paint_image
;
154 int paint_x
, paint_y
;
155 gfx::Rect paint_src_rect
;
157 // Valid when type == SCROLL.
158 gfx::Rect scroll_clip_rect
;
159 int scroll_dx
, scroll_dy
;
161 // Valid when type == REPLACE.
162 scoped_refptr
<PPB_ImageData_Impl
> replace_image
;
165 PPB_Graphics2D_Impl::PPB_Graphics2D_Impl(PP_Instance instance
)
166 : Resource(::ppapi::OBJECT_IS_IMPL
, instance
),
167 bound_instance_(NULL
),
168 offscreen_flush_pending_(false),
169 is_always_opaque_(false),
171 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
174 PPB_Graphics2D_Impl::~PPB_Graphics2D_Impl() {
175 // LastPluginRefWasDeleted should have aborted all pending callbacks.
176 DCHECK(painted_flush_callback_
.is_null());
177 DCHECK(unpainted_flush_callback_
.is_null());
181 PP_Resource
PPB_Graphics2D_Impl::Create(PP_Instance instance
,
183 PP_Bool is_always_opaque
) {
184 scoped_refptr
<PPB_Graphics2D_Impl
> graphics_2d(
185 new PPB_Graphics2D_Impl(instance
));
186 if (!graphics_2d
->Init(size
.width
, size
.height
,
187 PPBoolToBool(is_always_opaque
))) {
190 return graphics_2d
->GetReference();
193 bool PPB_Graphics2D_Impl::Init(int width
, int height
, bool is_always_opaque
) {
194 // The underlying PPB_ImageData_Impl will validate the dimensions.
195 image_data_
= new PPB_ImageData_Impl(pp_instance(),
196 PPB_ImageData_Impl::PLATFORM
);
197 if (!image_data_
->Init(PPB_ImageData_Impl::GetNativeImageDataFormat(),
198 width
, height
, true) ||
199 !image_data_
->Map()) {
203 is_always_opaque_
= is_always_opaque
;
208 ::ppapi::thunk::PPB_Graphics2D_API
*
209 PPB_Graphics2D_Impl::AsPPB_Graphics2D_API() {
213 void PPB_Graphics2D_Impl::LastPluginRefWasDeleted() {
214 // Abort any pending callbacks.
215 unpainted_flush_callback_
.PostAbort();
216 painted_flush_callback_
.PostAbort();
219 PP_Bool
PPB_Graphics2D_Impl::Describe(PP_Size
* size
,
220 PP_Bool
* is_always_opaque
) {
221 size
->width
= image_data_
->width();
222 size
->height
= image_data_
->height();
223 *is_always_opaque
= PP_FromBool(is_always_opaque_
);
227 void PPB_Graphics2D_Impl::PaintImageData(PP_Resource image_data
,
228 const PP_Point
* top_left
,
229 const PP_Rect
* src_rect
) {
233 EnterResourceNoLock
<PPB_ImageData_API
> enter(image_data
, true);
234 if (enter
.failed()) {
235 Log(PP_LOGLEVEL_ERROR
,
236 "PPB_Graphics2D.PaintImageData: Bad image resource.");
239 PPB_ImageData_Impl
* image_resource
=
240 static_cast<PPB_ImageData_Impl
*>(enter
.object());
242 QueuedOperation
operation(QueuedOperation::PAINT
);
243 operation
.paint_image
= image_resource
;
244 if (!ValidateAndConvertRect(src_rect
, image_resource
->width(),
245 image_resource
->height(),
246 &operation
.paint_src_rect
))
249 // Validate the bitmap position using the previously-validated rect, there
250 // should be no painted area outside of the image.
251 int64 x64
= static_cast<int64
>(top_left
->x
);
252 int64 y64
= static_cast<int64
>(top_left
->y
);
253 if (x64
+ static_cast<int64
>(operation
.paint_src_rect
.x()) < 0 ||
254 x64
+ static_cast<int64
>(operation
.paint_src_rect
.right()) >
255 image_data_
->width())
257 if (y64
+ static_cast<int64
>(operation
.paint_src_rect
.y()) < 0 ||
258 y64
+ static_cast<int64
>(operation
.paint_src_rect
.bottom()) >
259 image_data_
->height())
261 operation
.paint_x
= top_left
->x
;
262 operation
.paint_y
= top_left
->y
;
264 queued_operations_
.push_back(operation
);
267 void PPB_Graphics2D_Impl::Scroll(const PP_Rect
* clip_rect
,
268 const PP_Point
* amount
) {
269 QueuedOperation
operation(QueuedOperation::SCROLL
);
270 if (!ValidateAndConvertRect(clip_rect
,
271 image_data_
->width(),
272 image_data_
->height(),
273 &operation
.scroll_clip_rect
))
276 // If we're being asked to scroll by more than the clip rect size, just
277 // ignore this scroll command and say it worked.
278 int32 dx
= amount
->x
;
279 int32 dy
= amount
->y
;
280 if (dx
<= -image_data_
->width() || dx
>= image_data_
->width() ||
281 dy
<= -image_data_
->height() || dy
>= image_data_
->height()) {
282 Log(PP_LOGLEVEL_ERROR
,
283 "PPB_Graphics2D.Scroll: Scroll amount is larger than image size.");
287 operation
.scroll_dx
= dx
;
288 operation
.scroll_dy
= dy
;
290 queued_operations_
.push_back(operation
);
293 void PPB_Graphics2D_Impl::ReplaceContents(PP_Resource image_data
) {
294 EnterResourceNoLock
<PPB_ImageData_API
> enter(image_data
, true);
297 PPB_ImageData_Impl
* image_resource
=
298 static_cast<PPB_ImageData_Impl
*>(enter
.object());
300 if (!PPB_ImageData_Impl::IsImageDataFormatSupported(
301 image_resource
->format())) {
302 Log(PP_LOGLEVEL_ERROR
,
303 "PPB_Graphics2D.ReplaceContents: Image data format is not supported.");
307 if (image_resource
->width() != image_data_
->width() ||
308 image_resource
->height() != image_data_
->height()) {
309 Log(PP_LOGLEVEL_ERROR
,
310 "PPB_Graphics2D.ReplaceContents: Image size doesn't match "
315 QueuedOperation
operation(QueuedOperation::REPLACE
);
316 operation
.replace_image
= image_resource
;
317 queued_operations_
.push_back(operation
);
320 int32_t PPB_Graphics2D_Impl::Flush(scoped_refptr
<TrackedCallback
> callback
,
321 PP_Resource
* old_image_data
) {
322 TRACE_EVENT0("pepper", "PPB_Graphics2D_Impl::Flush");
323 // Don't allow more than one pending flush at a time.
324 if (HasPendingFlush())
325 return PP_ERROR_INPROGRESS
;
327 bool done_replace_contents
= false;
328 bool no_update_visible
= true;
329 bool is_plugin_visible
= true;
330 for (size_t i
= 0; i
< queued_operations_
.size(); i
++) {
331 QueuedOperation
& operation
= queued_operations_
[i
];
333 switch (operation
.type
) {
334 case QueuedOperation::PAINT
:
335 ExecutePaintImageData(operation
.paint_image
,
336 operation
.paint_x
, operation
.paint_y
,
337 operation
.paint_src_rect
,
340 case QueuedOperation::SCROLL
:
341 ExecuteScroll(operation
.scroll_clip_rect
,
342 operation
.scroll_dx
, operation
.scroll_dy
,
345 case QueuedOperation::REPLACE
:
346 // Since the out parameter |old_image_data| takes ownership of the
347 // reference, if there are more than one ReplaceContents calls queued
348 // the first |old_image_data| will get overwritten and leaked. So we
349 // only supply this for the first call.
350 ExecuteReplaceContents(operation
.replace_image
, &op_rect
,
351 done_replace_contents
? NULL
: old_image_data
);
352 done_replace_contents
= true;
356 // For correctness with accelerated compositing, we must issue an invalidate
357 // on the full op_rect even if it is partially or completely off-screen.
358 // However, if we issue an invalidate for a clipped-out region, WebKit will
359 // do nothing and we won't get any ViewWillInitiatePaint/ViewFlushedPaint
360 // calls, leaving our callback stranded. So we still need to check whether
361 // the repainted area is visible to determine how to deal with the callback.
362 if (bound_instance_
&& !op_rect
.IsEmpty()) {
363 gfx::Point
scroll_delta(operation
.scroll_dx
, operation
.scroll_dy
);
364 if (!ConvertToLogicalPixels(scale_
,
366 operation
.type
== QueuedOperation::SCROLL
?
367 &scroll_delta
: NULL
)) {
368 // Conversion requires falling back to InvalidateRect.
369 operation
.type
= QueuedOperation::PAINT
;
372 gfx::Rect clip
= PP_ToGfxRect(bound_instance_
->view_data().clip_rect
);
373 is_plugin_visible
= !clip
.IsEmpty();
375 // Set |no_update_visible| to false if the change overlaps the visible
377 gfx::Rect visible_changed_rect
= gfx::IntersectRects(clip
, op_rect
);
378 if (!visible_changed_rect
.IsEmpty())
379 no_update_visible
= false;
381 // Notify the plugin of the entire change (op_rect), even if it is
382 // partially or completely off-screen.
383 if (operation
.type
== QueuedOperation::SCROLL
) {
384 bound_instance_
->ScrollRect(scroll_delta
.x(), scroll_delta
.y(),
387 bound_instance_
->InvalidateRect(op_rect
);
391 queued_operations_
.clear();
393 if (!bound_instance_
) {
394 // As promised in the API, we always schedule callback when unbound.
395 ScheduleOffscreenCallback(FlushCallbackData(callback
));
396 } else if (no_update_visible
&& is_plugin_visible
&&
397 bound_instance_
->view_data().is_page_visible
) {
398 // There's nothing visible to invalidate so just schedule the callback to
399 // execute in the next round of the message loop.
400 ScheduleOffscreenCallback(FlushCallbackData(callback
));
402 unpainted_flush_callback_
.Set(callback
);
405 return PP_OK_COMPLETIONPENDING
;
408 bool PPB_Graphics2D_Impl::SetScale(float scale
) {
417 float PPB_Graphics2D_Impl::GetScale() {
421 bool PPB_Graphics2D_Impl::ReadImageData(PP_Resource image
,
422 const PP_Point
* top_left
) {
423 // Get and validate the image object to paint into.
424 EnterResourceNoLock
<PPB_ImageData_API
> enter(image
, true);
427 PPB_ImageData_Impl
* image_resource
=
428 static_cast<PPB_ImageData_Impl
*>(enter
.object());
429 if (!PPB_ImageData_Impl::IsImageDataFormatSupported(
430 image_resource
->format()))
431 return false; // Must be in the right format.
433 // Validate the bitmap position.
436 static_cast<int64
>(x
) + static_cast<int64
>(image_resource
->width()) >
437 image_data_
->width())
441 static_cast<int64
>(y
) + static_cast<int64
>(image_resource
->height()) >
442 image_data_
->height())
445 ImageDataAutoMapper
auto_mapper(image_resource
);
446 if (!auto_mapper
.is_valid())
449 SkIRect src_irect
= { x
, y
,
450 x
+ image_resource
->width(),
451 y
+ image_resource
->height() };
452 SkRect dest_rect
= { SkIntToScalar(0),
454 SkIntToScalar(image_resource
->width()),
455 SkIntToScalar(image_resource
->height()) };
457 if (image_resource
->format() != image_data_
->format()) {
458 // Convert the image data if the format does not match.
459 ConvertImageData(image_data_
, src_irect
, image_resource
, dest_rect
);
461 SkCanvas
* dest_canvas
= image_resource
->GetCanvas();
463 // We want to replace the contents of the bitmap rather than blend.
465 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
466 dest_canvas
->drawBitmapRect(*image_data_
->GetMappedBitmap(),
467 &src_irect
, dest_rect
, &paint
);
472 bool PPB_Graphics2D_Impl::BindToInstance(PluginInstance
* new_instance
) {
473 if (bound_instance_
== new_instance
)
474 return true; // Rebinding the same device, nothing to do.
475 if (bound_instance_
&& new_instance
)
476 return false; // Can't change a bound device.
479 // When the device is detached, we'll not get any more paint callbacks so
480 // we need to clear the list, but we still want to issue any pending
481 // callbacks to the plugin.
482 if (!unpainted_flush_callback_
.is_null()) {
483 FlushCallbackData callback
;
484 std::swap(callback
, unpainted_flush_callback_
);
485 ScheduleOffscreenCallback(callback
);
487 if (!painted_flush_callback_
.is_null()) {
488 FlushCallbackData callback
;
489 std::swap(callback
, painted_flush_callback_
);
490 ScheduleOffscreenCallback(callback
);
493 // Devices being replaced, redraw the plugin.
494 new_instance
->InvalidateRect(gfx::Rect());
497 bound_instance_
= new_instance
;
501 // The |backing_bitmap| must be clipped to the |plugin_rect| to avoid painting
502 // outside the plugin area. This can happen if the plugin has been resized since
503 // PaintImageData verified the image is within the plugin size.
504 void PPB_Graphics2D_Impl::Paint(WebKit::WebCanvas
* canvas
,
505 const gfx::Rect
& plugin_rect
,
506 const gfx::Rect
& paint_rect
) {
507 TRACE_EVENT0("pepper", "PPB_Graphics2D_Impl::Paint");
508 ImageDataAutoMapper
auto_mapper(image_data_
);
509 const SkBitmap
& backing_bitmap
= *image_data_
->GetMappedBitmap();
511 gfx::Rect invalidate_rect
= gfx::IntersectRects(plugin_rect
, paint_rect
);
512 SkRect sk_invalidate_rect
= gfx::RectToSkRect(invalidate_rect
);
513 SkAutoCanvasRestore
auto_restore(canvas
, true);
514 canvas
->clipRect(sk_invalidate_rect
);
515 gfx::Size
pixel_image_size(image_data_
->width(), image_data_
->height());
516 gfx::Size image_size
= gfx::ToFlooredSize(
517 gfx::ScaleSize(pixel_image_size
, scale_
));
519 PluginInstance
* plugin_instance
= ResourceHelper::GetPluginInstance(this);
520 if (!plugin_instance
)
522 if (plugin_instance
->IsFullPagePlugin()) {
523 // When we're resizing a window with a full-frame plugin, the plugin may
524 // not yet have bound a new device, which will leave parts of the
525 // background exposed if the window is getting larger. We want this to
526 // show white (typically less jarring) rather than black or uninitialized.
527 // We don't do this for non-full-frame plugins since we specifically want
528 // the page background to show through.
529 SkAutoCanvasRestore
auto_restore(canvas
, true);
530 SkRect image_data_rect
=
531 gfx::RectToSkRect(gfx::Rect(plugin_rect
.origin(), image_size
));
532 canvas
->clipRect(image_data_rect
, SkRegion::kDifference_Op
);
535 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
536 paint
.setColor(SK_ColorWHITE
);
537 canvas
->drawRect(sk_invalidate_rect
, paint
);
541 // Copy to device independent bitmap when target canvas doesn't support
543 if (!skia::SupportsPlatformPaint(canvas
))
544 backing_bitmap
.copyTo(&image
, SkBitmap::kARGB_8888_Config
);
546 image
= backing_bitmap
;
549 if (is_always_opaque_
) {
550 // When we know the device is opaque, we can disable blending for slightly
551 // more optimized painting.
552 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
556 origin
.set(SkIntToScalar(plugin_rect
.x()), SkIntToScalar(plugin_rect
.y()));
558 SkPoint pixel_origin
= origin
;
559 if (scale_
!= 1.0f
&& scale_
> 0.0f
) {
560 float inverse_scale
= 1.0f
/ scale_
;
561 pixel_origin
.scale(inverse_scale
);
562 canvas
->scale(scale_
, scale_
);
564 canvas
->drawBitmap(image
, pixel_origin
.x(), pixel_origin
.y(), &paint
);
567 void PPB_Graphics2D_Impl::ViewWillInitiatePaint() {
568 // Move any "unpainted" callback to the painted state. See
569 // |unpainted_flush_callback_| in the header for more.
570 if (!unpainted_flush_callback_
.is_null()) {
571 DCHECK(painted_flush_callback_
.is_null());
572 std::swap(painted_flush_callback_
, unpainted_flush_callback_
);
576 void PPB_Graphics2D_Impl::ViewInitiatedPaint() {
579 void PPB_Graphics2D_Impl::ViewFlushedPaint() {
580 TRACE_EVENT0("pepper", "PPB_Graphics2D_Impl::ViewFlushedPaint");
581 // Notify any "painted" callback. See |unpainted_flush_callback_| in the
583 if (!painted_flush_callback_
.is_null())
584 painted_flush_callback_
.Execute(PP_OK
);
588 bool PPB_Graphics2D_Impl::ConvertToLogicalPixels(float scale
,
591 if (scale
== 1.0f
|| scale
<= 0.0f
)
594 gfx::Rect original_rect
= *op_rect
;
595 // Take the enclosing rectangle after scaling so a rectangle scaled down then
596 // scaled back up by the inverse scale would fully contain the entire area
597 // affected by the original rectangle.
598 *op_rect
= gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect
, scale
));
600 gfx::Point original_delta
= *delta
;
601 float inverse_scale
= 1.0f
/ scale
;
602 *delta
= gfx::ToFlooredPoint(gfx::ScalePoint(*delta
, scale
));
604 gfx::Rect inverse_scaled_rect
=
605 gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect
, inverse_scale
));
606 if (original_rect
!= inverse_scaled_rect
)
608 gfx::Point inverse_scaled_point
=
609 gfx::ToFlooredPoint(gfx::ScalePoint(*delta
, inverse_scale
));
610 if (original_delta
!= inverse_scaled_point
)
617 void PPB_Graphics2D_Impl::ExecutePaintImageData(PPB_ImageData_Impl
* image
,
619 const gfx::Rect
& src_rect
,
620 gfx::Rect
* invalidated_rect
) {
621 // Ensure the source image is mapped to read from it.
622 ImageDataAutoMapper
auto_mapper(image
);
623 if (!auto_mapper
.is_valid())
626 // Portion within the source image to cut out.
627 SkIRect src_irect
= { src_rect
.x(), src_rect
.y(),
628 src_rect
.right(), src_rect
.bottom() };
630 // Location within the backing store to copy to.
631 *invalidated_rect
= src_rect
;
632 invalidated_rect
->Offset(x
, y
);
633 SkRect dest_rect
= { SkIntToScalar(invalidated_rect
->x()),
634 SkIntToScalar(invalidated_rect
->y()),
635 SkIntToScalar(invalidated_rect
->right()),
636 SkIntToScalar(invalidated_rect
->bottom()) };
638 if (image
->format() != image_data_
->format()) {
639 // Convert the image data if the format does not match.
640 ConvertImageData(image
, src_irect
, image_data_
, dest_rect
);
642 // We're guaranteed to have a mapped canvas since we mapped it in Init().
643 SkCanvas
* backing_canvas
= image_data_
->GetCanvas();
645 // We want to replace the contents of the bitmap rather than blend.
647 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
648 backing_canvas
->drawBitmapRect(*image
->GetMappedBitmap(),
649 &src_irect
, dest_rect
, &paint
);
653 void PPB_Graphics2D_Impl::ExecuteScroll(const gfx::Rect
& clip
,
655 gfx::Rect
* invalidated_rect
) {
656 gfx::ScrollCanvas(image_data_
->GetCanvas(), clip
, gfx::Vector2d(dx
, dy
));
657 *invalidated_rect
= clip
;
660 void PPB_Graphics2D_Impl::ExecuteReplaceContents(PPB_ImageData_Impl
* image
,
661 gfx::Rect
* invalidated_rect
,
662 PP_Resource
* old_image_data
) {
663 if (image
->format() != image_data_
->format()) {
664 DCHECK(image
->width() == image_data_
->width() &&
665 image
->height() == image_data_
->height());
666 // Convert the image data if the format does not match.
667 SkIRect src_irect
= { 0, 0, image
->width(), image
->height() };
668 SkRect dest_rect
= { SkIntToScalar(0),
670 SkIntToScalar(image_data_
->width()),
671 SkIntToScalar(image_data_
->height()) };
672 ConvertImageData(image
, src_irect
, image_data_
, dest_rect
);
674 // The passed-in image may not be mapped in our process, and we need to
675 // guarantee that the current backing store is always mapped.
680 *old_image_data
= image_data_
->GetReference();
683 *invalidated_rect
= gfx::Rect(0, 0,
684 image_data_
->width(), image_data_
->height());
687 void PPB_Graphics2D_Impl::ScheduleOffscreenCallback(
688 const FlushCallbackData
& callback
) {
689 DCHECK(!HasPendingFlush());
690 offscreen_flush_pending_
= true;
691 MessageLoop::current()->PostDelayedTask(
693 base::Bind(&PPB_Graphics2D_Impl::ExecuteOffscreenCallback
,
694 weak_ptr_factory_
.GetWeakPtr(),
696 base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs
));
699 void PPB_Graphics2D_Impl::ExecuteOffscreenCallback(FlushCallbackData data
) {
700 DCHECK(offscreen_flush_pending_
);
702 // We must clear this flag before issuing the callback. It will be
703 // common for the plugin to issue another invalidate in response to a flush
704 // callback, and we don't want to think that a callback is already pending.
705 offscreen_flush_pending_
= false;
709 bool PPB_Graphics2D_Impl::HasPendingFlush() const {
710 return !unpainted_flush_callback_
.is_null() ||
711 !painted_flush_callback_
.is_null() ||
712 offscreen_flush_pending_
;
716 } // namespace webkit