Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / webkit / plugins / ppapi / ppb_graphics_2d_impl.cc
blob590a61d2d943a020a37aa64c9831649a845ab4b4
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"
7 #include <iterator>
9 #include "base/bind.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"
39 #endif
41 using ppapi::thunk::EnterResourceNoLock;
42 using ppapi::thunk::PPB_ImageData_API;
43 using ppapi::TrackedCallback;
45 namespace webkit {
46 namespace ppapi {
48 namespace {
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,
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;
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;
77 *dest = gfx::Rect(rect->point.x, rect->point.y,
78 rect->size.width, rect->size.height);
80 return true;
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];
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)));
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)));
133 } // namespace
135 struct PPB_Graphics2D_Impl::QueuedOperation {
136 enum Type {
137 PAINT,
138 SCROLL,
139 REPLACE
142 QueuedOperation(Type t)
143 : type(t),
144 paint_x(0),
145 paint_y(0),
146 scroll_dx(0),
147 scroll_dy(0) {
150 Type type;
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),
170 scale_(1.0f),
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());
180 // static
181 PP_Resource PPB_Graphics2D_Impl::Create(PP_Instance instance,
182 const PP_Size& size,
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))) {
188 return 0;
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()) {
200 image_data_ = NULL;
201 return false;
203 is_always_opaque_ = is_always_opaque;
204 scale_ = 1.0f;
205 return true;
208 ::ppapi::thunk::PPB_Graphics2D_API*
209 PPB_Graphics2D_Impl::AsPPB_Graphics2D_API() {
210 return this;
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_);
224 return PP_TRUE;
227 void PPB_Graphics2D_Impl::PaintImageData(PP_Resource image_data,
228 const PP_Point* top_left,
229 const PP_Rect* src_rect) {
230 if (!top_left)
231 return;
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.");
237 return;
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))
247 return;
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())
256 return;
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())
260 return;
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))
274 return;
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.");
284 return;
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);
295 if (enter.failed())
296 return;
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.");
304 return;
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 "
311 "Graphics2D size.");
312 return;
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];
332 gfx::Rect op_rect;
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,
338 &op_rect);
339 break;
340 case QueuedOperation::SCROLL:
341 ExecuteScroll(operation.scroll_clip_rect,
342 operation.scroll_dx, operation.scroll_dy,
343 &op_rect);
344 break;
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;
353 break;
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_,
365 &op_rect,
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
376 // area.
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(),
385 op_rect);
386 } else {
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));
401 } else {
402 unpainted_flush_callback_.Set(callback);
405 return PP_OK_COMPLETIONPENDING;
408 bool PPB_Graphics2D_Impl::SetScale(float scale) {
409 if (scale > 0.0f) {
410 scale_ = scale;
411 return true;
414 return false;
417 float PPB_Graphics2D_Impl::GetScale() {
418 return scale_;
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);
425 if (enter.failed())
426 return false;
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.
434 int x = top_left->x;
435 if (x < 0 ||
436 static_cast<int64>(x) + static_cast<int64>(image_resource->width()) >
437 image_data_->width())
438 return false;
439 int y = top_left->y;
440 if (y < 0 ||
441 static_cast<int64>(y) + static_cast<int64>(image_resource->height()) >
442 image_data_->height())
443 return false;
445 ImageDataAutoMapper auto_mapper(image_resource);
446 if (!auto_mapper.is_valid())
447 return false;
449 SkIRect src_irect = { x, y,
450 x + image_resource->width(),
451 y + image_resource->height() };
452 SkRect dest_rect = { SkIntToScalar(0),
453 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);
460 } else {
461 SkCanvas* dest_canvas = image_resource->GetCanvas();
463 // We want to replace the contents of the bitmap rather than blend.
464 SkPaint paint;
465 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
466 dest_canvas->drawBitmapRect(*image_data_->GetMappedBitmap(),
467 &src_irect, dest_rect, &paint);
469 return true;
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.
478 if (!new_instance) {
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);
492 } else {
493 // Devices being replaced, redraw the plugin.
494 new_instance->InvalidateRect(gfx::Rect());
497 bound_instance_ = new_instance;
498 return true;
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)
521 return;
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);
534 SkPaint paint;
535 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
536 paint.setColor(SK_ColorWHITE);
537 canvas->drawRect(sk_invalidate_rect, paint);
540 SkBitmap image;
541 // Copy to device independent bitmap when target canvas doesn't support
542 // platform paint.
543 if (!skia::SupportsPlatformPaint(canvas))
544 backing_bitmap.copyTo(&image, SkBitmap::kARGB_8888_Config);
545 else
546 image = backing_bitmap;
548 SkPaint paint;
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);
555 SkPoint origin;
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
582 // header for more.
583 if (!painted_flush_callback_.is_null())
584 painted_flush_callback_.Execute(PP_OK);
587 // static
588 bool PPB_Graphics2D_Impl::ConvertToLogicalPixels(float scale,
589 gfx::Rect* op_rect,
590 gfx::Point* delta) {
591 if (scale == 1.0f || scale <= 0.0f)
592 return true;
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));
599 if (delta) {
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)
607 return false;
608 gfx::Point inverse_scaled_point =
609 gfx::ToFlooredPoint(gfx::ScalePoint(*delta, inverse_scale));
610 if (original_delta != inverse_scaled_point)
611 return false;
614 return true;
617 void PPB_Graphics2D_Impl::ExecutePaintImageData(PPB_ImageData_Impl* image,
618 int x, int y,
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())
624 return;
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);
641 } else {
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.
646 SkPaint paint;
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,
654 int dx, int dy,
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),
669 SkIntToScalar(0),
670 SkIntToScalar(image_data_->width()),
671 SkIntToScalar(image_data_->height()) };
672 ConvertImageData(image, src_irect, image_data_, dest_rect);
673 } else {
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.
676 if (!image->Map())
677 return;
679 if (old_image_data)
680 *old_image_data = image_data_->GetReference();
681 image_data_ = image;
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(
692 FROM_HERE,
693 base::Bind(&PPB_Graphics2D_Impl::ExecuteOffscreenCallback,
694 weak_ptr_factory_.GetWeakPtr(),
695 callback),
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;
706 data.Execute(PP_OK);
709 bool PPB_Graphics2D_Impl::HasPendingFlush() const {
710 return !unpainted_flush_callback_.is_null() ||
711 !painted_flush_callback_.is_null() ||
712 offscreen_flush_pending_;
715 } // namespace ppapi
716 } // namespace webkit