Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / content / renderer / pepper / pepper_graphics_2d_host.cc
blob3bc2296ce2f3d561802156d514cbaa159382cd34
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"
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/shared_bitmap.h"
12 #include "cc/resources/texture_mailbox.h"
13 #include "content/child/child_shared_bitmap_manager.h"
14 #include "content/public/renderer/render_thread.h"
15 #include "content/public/renderer/renderer_ppapi_host.h"
16 #include "content/renderer/pepper/gfx_conversion.h"
17 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
18 #include "content/renderer/pepper/ppb_image_data_impl.h"
19 #include "content/renderer/render_thread_impl.h"
20 #include "ppapi/c/pp_bool.h"
21 #include "ppapi/c/pp_errors.h"
22 #include "ppapi/c/pp_rect.h"
23 #include "ppapi/c/pp_resource.h"
24 #include "ppapi/host/dispatch_host_message.h"
25 #include "ppapi/host/host_message_context.h"
26 #include "ppapi/host/ppapi_host.h"
27 #include "ppapi/proxy/ppapi_messages.h"
28 #include "ppapi/shared_impl/ppb_view_shared.h"
29 #include "ppapi/thunk/enter.h"
30 #include "skia/ext/platform_canvas.h"
31 #include "third_party/skia/include/core/SkBitmap.h"
32 #include "ui/gfx/blit.h"
33 #include "ui/gfx/point_conversions.h"
34 #include "ui/gfx/rect.h"
35 #include "ui/gfx/rect_conversions.h"
36 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
37 #include "ui/gfx/size_conversions.h"
38 #include "ui/gfx/skia_util.h"
40 #if defined(OS_MACOSX)
41 #include "base/mac/mac_util.h"
42 #include "base/mac/scoped_cftyperef.h"
43 #endif
45 using ppapi::thunk::EnterResourceNoLock;
46 using ppapi::thunk::PPB_ImageData_API;
48 namespace content {
50 namespace {
52 const int64 kOffscreenCallbackDelayMs = 1000 / 30; // 30 fps
54 // Converts a rect inside an image of the given dimensions. The rect may be
55 // NULL to indicate it should be the entire image. If the rect is outside of
56 // the image, this will do nothing and return false.
57 bool ValidateAndConvertRect(const PP_Rect* rect,
58 int image_width,
59 int image_height,
60 gfx::Rect* dest) {
61 if (!rect) {
62 // Use the entire image area.
63 *dest = gfx::Rect(0, 0, image_width, image_height);
64 } else {
65 // Validate the passed-in area.
66 if (rect->point.x < 0 || rect->point.y < 0 || rect->size.width <= 0 ||
67 rect->size.height <= 0)
68 return false;
70 // Check the max bounds, being careful of overflow.
71 if (static_cast<int64>(rect->point.x) +
72 static_cast<int64>(rect->size.width) >
73 static_cast<int64>(image_width))
74 return false;
75 if (static_cast<int64>(rect->point.y) +
76 static_cast<int64>(rect->size.height) >
77 static_cast<int64>(image_height))
78 return false;
80 *dest = gfx::Rect(
81 rect->point.x, rect->point.y, rect->size.width, rect->size.height);
83 return true;
86 // Converts BGRA <-> RGBA.
87 void ConvertBetweenBGRAandRGBA(const uint32_t* input,
88 int pixel_length,
89 uint32_t* output) {
90 for (int i = 0; i < pixel_length; i++) {
91 const unsigned char* pixel_in =
92 reinterpret_cast<const unsigned char*>(&input[i]);
93 unsigned char* pixel_out = reinterpret_cast<unsigned char*>(&output[i]);
94 pixel_out[0] = pixel_in[2];
95 pixel_out[1] = pixel_in[1];
96 pixel_out[2] = pixel_in[0];
97 pixel_out[3] = pixel_in[3];
101 // Converts ImageData from PP_IMAGEDATAFORMAT_BGRA_PREMUL to
102 // PP_IMAGEDATAFORMAT_RGBA_PREMUL, or reverse. It's assumed that the
103 // destination image is always mapped (so will have non-NULL data).
104 void ConvertImageData(PPB_ImageData_Impl* src_image,
105 const SkIRect& src_rect,
106 PPB_ImageData_Impl* dest_image,
107 const SkRect& dest_rect) {
108 ImageDataAutoMapper auto_mapper(src_image);
110 DCHECK(src_image->format() != dest_image->format());
111 DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(src_image->format()));
112 DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(dest_image->format()));
114 const SkBitmap* src_bitmap = src_image->GetMappedBitmap();
115 const SkBitmap* dest_bitmap = dest_image->GetMappedBitmap();
116 if (src_rect.width() == src_image->width() &&
117 dest_rect.width() == dest_image->width()) {
118 // Fast path if the full line needs to be converted.
119 ConvertBetweenBGRAandRGBA(
120 src_bitmap->getAddr32(static_cast<int>(src_rect.fLeft),
121 static_cast<int>(src_rect.fTop)),
122 src_rect.width() * src_rect.height(),
123 dest_bitmap->getAddr32(static_cast<int>(dest_rect.fLeft),
124 static_cast<int>(dest_rect.fTop)));
125 } else {
126 // Slow path where we convert line by line.
127 for (int y = 0; y < src_rect.height(); y++) {
128 ConvertBetweenBGRAandRGBA(
129 src_bitmap->getAddr32(static_cast<int>(src_rect.fLeft),
130 static_cast<int>(src_rect.fTop + y)),
131 src_rect.width(),
132 dest_bitmap->getAddr32(static_cast<int>(dest_rect.fLeft),
133 static_cast<int>(dest_rect.fTop + y)));
138 } // namespace
140 struct PepperGraphics2DHost::QueuedOperation {
141 enum Type { PAINT, SCROLL, REPLACE, };
143 QueuedOperation(Type t)
144 : type(t), paint_x(0), paint_y(0), scroll_dx(0), scroll_dy(0) {}
146 Type type;
148 // Valid when type == PAINT.
149 scoped_refptr<PPB_ImageData_Impl> paint_image;
150 int paint_x, paint_y;
151 gfx::Rect paint_src_rect;
153 // Valid when type == SCROLL.
154 gfx::Rect scroll_clip_rect;
155 int scroll_dx, scroll_dy;
157 // Valid when type == REPLACE.
158 scoped_refptr<PPB_ImageData_Impl> replace_image;
161 // static
162 PepperGraphics2DHost* PepperGraphics2DHost::Create(
163 RendererPpapiHost* host,
164 PP_Instance instance,
165 PP_Resource resource,
166 const PP_Size& size,
167 PP_Bool is_always_opaque,
168 scoped_refptr<PPB_ImageData_Impl> backing_store) {
169 PepperGraphics2DHost* resource_host =
170 new PepperGraphics2DHost(host, instance, resource);
171 if (!resource_host->Init(size.width,
172 size.height,
173 PP_ToBool(is_always_opaque),
174 backing_store)) {
175 delete resource_host;
176 return NULL;
178 return resource_host;
181 PepperGraphics2DHost::PepperGraphics2DHost(RendererPpapiHost* host,
182 PP_Instance instance,
183 PP_Resource resource)
184 : ResourceHost(host->GetPpapiHost(), instance, resource),
185 renderer_ppapi_host_(host),
186 bound_instance_(NULL),
187 need_flush_ack_(false),
188 offscreen_flush_pending_(false),
189 is_always_opaque_(false),
190 scale_(1.0f),
191 is_running_in_process_(host->IsRunningInProcess()),
192 texture_mailbox_modified_(true) {}
194 PepperGraphics2DHost::~PepperGraphics2DHost() {
195 // Unbind from the instance when destroyed if we're still bound.
196 if (bound_instance_)
197 bound_instance_->BindGraphics(bound_instance_->pp_instance(), 0);
200 bool PepperGraphics2DHost::Init(
201 int width,
202 int height,
203 bool is_always_opaque,
204 scoped_refptr<PPB_ImageData_Impl> backing_store) {
205 // The underlying PPB_ImageData_Impl will validate the dimensions.
206 image_data_ = backing_store;
207 if (!image_data_->Init(PPB_ImageData_Impl::GetNativeImageDataFormat(),
208 width,
209 height,
210 true) ||
211 !image_data_->Map()) {
212 image_data_ = NULL;
213 return false;
215 is_always_opaque_ = is_always_opaque;
216 scale_ = 1.0f;
217 return true;
220 int32_t PepperGraphics2DHost::OnResourceMessageReceived(
221 const IPC::Message& msg,
222 ppapi::host::HostMessageContext* context) {
223 PPAPI_BEGIN_MESSAGE_MAP(PepperGraphics2DHost, msg)
224 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_PaintImageData,
225 OnHostMsgPaintImageData)
226 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_Scroll,
227 OnHostMsgScroll)
228 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_ReplaceContents,
229 OnHostMsgReplaceContents)
230 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_Flush,
231 OnHostMsgFlush)
232 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_SetScale,
233 OnHostMsgSetScale)
234 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_ReadImageData,
235 OnHostMsgReadImageData)
236 PPAPI_END_MESSAGE_MAP()
237 return PP_ERROR_FAILED;
240 bool PepperGraphics2DHost::IsGraphics2DHost() { return true; }
242 bool PepperGraphics2DHost::ReadImageData(PP_Resource image,
243 const PP_Point* top_left) {
244 // Get and validate the image object to paint into.
245 EnterResourceNoLock<PPB_ImageData_API> enter(image, true);
246 if (enter.failed())
247 return false;
248 PPB_ImageData_Impl* image_resource =
249 static_cast<PPB_ImageData_Impl*>(enter.object());
250 if (!PPB_ImageData_Impl::IsImageDataFormatSupported(image_resource->format()))
251 return false; // Must be in the right format.
253 // Validate the bitmap position.
254 int x = top_left->x;
255 if (x < 0 ||
256 static_cast<int64>(x) + static_cast<int64>(image_resource->width()) >
257 image_data_->width())
258 return false;
259 int y = top_left->y;
260 if (y < 0 ||
261 static_cast<int64>(y) + static_cast<int64>(image_resource->height()) >
262 image_data_->height())
263 return false;
265 ImageDataAutoMapper auto_mapper(image_resource);
266 if (!auto_mapper.is_valid())
267 return false;
269 SkIRect src_irect = {x, y, x + image_resource->width(),
270 y + image_resource->height()};
271 SkRect dest_rect = {SkIntToScalar(0), SkIntToScalar(0),
272 SkIntToScalar(image_resource->width()),
273 SkIntToScalar(image_resource->height())};
275 if (image_resource->format() != image_data_->format()) {
276 // Convert the image data if the format does not match.
277 ConvertImageData(image_data_.get(), src_irect, image_resource, dest_rect);
278 } else {
279 SkCanvas* dest_canvas = image_resource->GetCanvas();
281 // We want to replace the contents of the bitmap rather than blend.
282 SkPaint paint;
283 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
284 dest_canvas->drawBitmapRect(
285 *image_data_->GetMappedBitmap(), &src_irect, dest_rect, &paint);
287 return true;
290 bool PepperGraphics2DHost::BindToInstance(
291 PepperPluginInstanceImpl* new_instance) {
292 if (new_instance && new_instance->pp_instance() != pp_instance())
293 return false; // Can't bind other instance's contexts.
294 if (bound_instance_ == new_instance)
295 return true; // Rebinding the same device, nothing to do.
296 if (bound_instance_ && new_instance)
297 return false; // Can't change a bound device.
299 if (!new_instance) {
300 // When the device is detached, we'll not get any more paint callbacks so
301 // we need to clear the list, but we still want to issue any pending
302 // callbacks to the plugin.
303 if (need_flush_ack_)
304 ScheduleOffscreenFlushAck();
305 } else {
306 // Devices being replaced, redraw the plugin.
307 new_instance->InvalidateRect(gfx::Rect());
310 texture_mailbox_modified_ = true;
312 bound_instance_ = new_instance;
313 return true;
316 // The |backing_bitmap| must be clipped to the |plugin_rect| to avoid painting
317 // outside the plugin area. This can happen if the plugin has been resized since
318 // PaintImageData verified the image is within the plugin size.
319 void PepperGraphics2DHost::Paint(blink::WebCanvas* canvas,
320 const gfx::Rect& plugin_rect,
321 const gfx::Rect& paint_rect) {
322 TRACE_EVENT0("pepper", "PepperGraphics2DHost::Paint");
323 ImageDataAutoMapper auto_mapper(image_data_.get());
324 const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap();
326 gfx::Rect invalidate_rect = plugin_rect;
327 invalidate_rect.Intersect(paint_rect);
328 SkRect sk_invalidate_rect = gfx::RectToSkRect(invalidate_rect);
329 SkAutoCanvasRestore auto_restore(canvas, true);
330 canvas->clipRect(sk_invalidate_rect);
331 gfx::Size pixel_image_size(image_data_->width(), image_data_->height());
332 gfx::Size image_size =
333 gfx::ToFlooredSize(gfx::ScaleSize(pixel_image_size, scale_));
335 PepperPluginInstance* plugin_instance =
336 renderer_ppapi_host_->GetPluginInstance(pp_instance());
337 if (!plugin_instance)
338 return;
339 if (plugin_instance->IsFullPagePlugin()) {
340 // When we're resizing a window with a full-frame plugin, the plugin may
341 // not yet have bound a new device, which will leave parts of the
342 // background exposed if the window is getting larger. We want this to
343 // show white (typically less jarring) rather than black or uninitialized.
344 // We don't do this for non-full-frame plugins since we specifically want
345 // the page background to show through.
346 SkAutoCanvasRestore auto_restore(canvas, true);
347 SkRect image_data_rect =
348 gfx::RectToSkRect(gfx::Rect(plugin_rect.origin(), image_size));
349 canvas->clipRect(image_data_rect, SkRegion::kDifference_Op);
351 SkPaint paint;
352 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
353 paint.setColor(SK_ColorWHITE);
354 canvas->drawRect(sk_invalidate_rect, paint);
357 SkBitmap image;
358 // Copy to device independent bitmap when target canvas doesn't support
359 // platform paint.
360 if (!skia::SupportsPlatformPaint(canvas))
361 backing_bitmap.copyTo(&image, kN32_SkColorType);
362 else
363 image = backing_bitmap;
365 SkPaint paint;
366 if (is_always_opaque_) {
367 // When we know the device is opaque, we can disable blending for slightly
368 // more optimized painting.
369 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
372 SkPoint origin;
373 origin.set(SkIntToScalar(plugin_rect.x()), SkIntToScalar(plugin_rect.y()));
375 SkPoint pixel_origin = origin;
377 if (scale_ != 1.0f && scale_ > 0.0f) {
378 canvas->scale(scale_, scale_);
379 pixel_origin.set(pixel_origin.x() * (1.0f / scale_),
380 pixel_origin.y() * (1.0f / scale_));
382 canvas->drawBitmap(image, pixel_origin.x(), pixel_origin.y(), &paint);
385 void PepperGraphics2DHost::ViewInitiatedPaint() {}
387 void PepperGraphics2DHost::ViewFlushedPaint() {
388 TRACE_EVENT0("pepper", "PepperGraphics2DHost::ViewFlushedPaint");
389 if (need_flush_ack_) {
390 SendFlushAck();
391 need_flush_ack_ = false;
395 void PepperGraphics2DHost::SetScale(float scale) { scale_ = scale; }
397 float PepperGraphics2DHost::GetScale() const { return scale_; }
399 bool PepperGraphics2DHost::IsAlwaysOpaque() const { return is_always_opaque_; }
401 PPB_ImageData_Impl* PepperGraphics2DHost::ImageData() {
402 return image_data_.get();
405 gfx::Size PepperGraphics2DHost::Size() const {
406 if (!image_data_.get())
407 return gfx::Size();
408 return gfx::Size(image_data_->width(), image_data_->height());
411 int32_t PepperGraphics2DHost::OnHostMsgPaintImageData(
412 ppapi::host::HostMessageContext* context,
413 const ppapi::HostResource& image_data,
414 const PP_Point& top_left,
415 bool src_rect_specified,
416 const PP_Rect& src_rect) {
417 EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(),
418 true);
419 if (enter.failed())
420 return PP_ERROR_BADRESOURCE;
421 PPB_ImageData_Impl* image_resource =
422 static_cast<PPB_ImageData_Impl*>(enter.object());
424 QueuedOperation operation(QueuedOperation::PAINT);
425 operation.paint_image = image_resource;
426 if (!ValidateAndConvertRect(src_rect_specified ? &src_rect : NULL,
427 image_resource->width(),
428 image_resource->height(),
429 &operation.paint_src_rect))
430 return PP_ERROR_BADARGUMENT;
432 // Validate the bitmap position using the previously-validated rect, there
433 // should be no painted area outside of the image.
434 int64 x64 = static_cast<int64>(top_left.x);
435 int64 y64 = static_cast<int64>(top_left.y);
436 if (x64 + static_cast<int64>(operation.paint_src_rect.x()) < 0 ||
437 x64 + static_cast<int64>(operation.paint_src_rect.right()) >
438 image_data_->width())
439 return PP_ERROR_BADARGUMENT;
440 if (y64 + static_cast<int64>(operation.paint_src_rect.y()) < 0 ||
441 y64 + static_cast<int64>(operation.paint_src_rect.bottom()) >
442 image_data_->height())
443 return PP_ERROR_BADARGUMENT;
444 operation.paint_x = top_left.x;
445 operation.paint_y = top_left.y;
447 queued_operations_.push_back(operation);
448 return PP_OK;
451 int32_t PepperGraphics2DHost::OnHostMsgScroll(
452 ppapi::host::HostMessageContext* context,
453 bool clip_specified,
454 const PP_Rect& clip,
455 const PP_Point& amount) {
456 QueuedOperation operation(QueuedOperation::SCROLL);
457 if (!ValidateAndConvertRect(clip_specified ? &clip : NULL,
458 image_data_->width(),
459 image_data_->height(),
460 &operation.scroll_clip_rect))
461 return PP_ERROR_BADARGUMENT;
463 // If we're being asked to scroll by more than the clip rect size, just
464 // ignore this scroll command and say it worked.
465 int32 dx = amount.x;
466 int32 dy = amount.y;
467 if (dx <= -image_data_->width() || dx >= image_data_->width() ||
468 dy <= -image_data_->height() || dy >= image_data_->height())
469 return PP_ERROR_BADARGUMENT;
471 operation.scroll_dx = dx;
472 operation.scroll_dy = dy;
474 queued_operations_.push_back(operation);
475 return PP_OK;
478 int32_t PepperGraphics2DHost::OnHostMsgReplaceContents(
479 ppapi::host::HostMessageContext* context,
480 const ppapi::HostResource& image_data) {
481 EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(),
482 true);
483 if (enter.failed())
484 return PP_ERROR_BADRESOURCE;
485 PPB_ImageData_Impl* image_resource =
486 static_cast<PPB_ImageData_Impl*>(enter.object());
488 if (!PPB_ImageData_Impl::IsImageDataFormatSupported(image_resource->format()))
489 return PP_ERROR_BADARGUMENT;
491 if (image_resource->width() != image_data_->width() ||
492 image_resource->height() != image_data_->height())
493 return PP_ERROR_BADARGUMENT;
495 QueuedOperation operation(QueuedOperation::REPLACE);
496 operation.replace_image = image_resource;
497 queued_operations_.push_back(operation);
498 return PP_OK;
501 int32_t PepperGraphics2DHost::OnHostMsgFlush(
502 ppapi::host::HostMessageContext* context,
503 const std::vector<ui::LatencyInfo>& latency_info) {
504 // Don't allow more than one pending flush at a time.
505 if (HasPendingFlush())
506 return PP_ERROR_INPROGRESS;
508 if (bound_instance_)
509 bound_instance_->AddLatencyInfo(latency_info);
511 PP_Resource old_image_data = 0;
512 flush_reply_context_ = context->MakeReplyMessageContext();
513 if (is_running_in_process_)
514 return Flush(NULL);
516 // Reuse image data when running out of process.
517 int32_t result = Flush(&old_image_data);
519 if (old_image_data) {
520 // If the Graphics2D has an old image data it's not using any more, send
521 // it back to the plugin for possible re-use. See ppb_image_data_proxy.cc
522 // for a description how this process works.
523 ppapi::HostResource old_image_data_host_resource;
524 old_image_data_host_resource.SetHostResource(pp_instance(), old_image_data);
525 host()->Send(new PpapiMsg_PPBImageData_NotifyUnusedImageData(
526 ppapi::API_ID_PPB_IMAGE_DATA, old_image_data_host_resource));
529 return result;
532 int32_t PepperGraphics2DHost::OnHostMsgSetScale(
533 ppapi::host::HostMessageContext* context,
534 float scale) {
535 if (scale > 0.0f) {
536 scale_ = scale;
537 return PP_OK;
539 return PP_ERROR_BADARGUMENT;
542 int32_t PepperGraphics2DHost::OnHostMsgReadImageData(
543 ppapi::host::HostMessageContext* context,
544 PP_Resource image,
545 const PP_Point& top_left) {
546 context->reply_msg = PpapiPluginMsg_Graphics2D_ReadImageDataAck();
547 return ReadImageData(image, &top_left) ? PP_OK : PP_ERROR_FAILED;
550 void ReleaseCallback(scoped_ptr<cc::SharedBitmap> bitmap,
551 uint32 sync_point,
552 bool lost_resource) {}
554 bool PepperGraphics2DHost::PrepareTextureMailbox(
555 cc::TextureMailbox* mailbox,
556 scoped_ptr<cc::SingleReleaseCallback>* release_callback) {
557 if (!texture_mailbox_modified_)
558 return false;
559 // TODO(jbauman): Send image_data_ through mailbox to avoid copy.
560 gfx::Size pixel_image_size(image_data_->width(), image_data_->height());
561 scoped_ptr<cc::SharedBitmap> shared_bitmap =
562 RenderThreadImpl::current()
563 ->shared_bitmap_manager()
564 ->AllocateSharedBitmap(pixel_image_size);
565 if (!shared_bitmap)
566 return false;
567 void* src = image_data_->Map();
568 memcpy(shared_bitmap->pixels(),
569 src,
570 cc::SharedBitmap::CheckedSizeInBytes(pixel_image_size));
571 image_data_->Unmap();
573 *mailbox = cc::TextureMailbox(shared_bitmap->memory(), pixel_image_size);
574 *release_callback = cc::SingleReleaseCallback::Create(
575 base::Bind(&ReleaseCallback, base::Passed(&shared_bitmap)));
576 texture_mailbox_modified_ = false;
577 return true;
580 void PepperGraphics2DHost::AttachedToNewLayer() {
581 texture_mailbox_modified_ = true;
584 int32_t PepperGraphics2DHost::Flush(PP_Resource* old_image_data) {
585 bool done_replace_contents = false;
586 bool no_update_visible = true;
587 bool is_plugin_visible = true;
588 for (size_t i = 0; i < queued_operations_.size(); i++) {
589 QueuedOperation& operation = queued_operations_[i];
590 gfx::Rect op_rect;
591 switch (operation.type) {
592 case QueuedOperation::PAINT:
593 ExecutePaintImageData(operation.paint_image.get(),
594 operation.paint_x,
595 operation.paint_y,
596 operation.paint_src_rect,
597 &op_rect);
598 break;
599 case QueuedOperation::SCROLL:
600 ExecuteScroll(operation.scroll_clip_rect,
601 operation.scroll_dx,
602 operation.scroll_dy,
603 &op_rect);
604 break;
605 case QueuedOperation::REPLACE:
606 // Since the out parameter |old_image_data| takes ownership of the
607 // reference, if there are more than one ReplaceContents calls queued
608 // the first |old_image_data| will get overwritten and leaked. So we
609 // only supply this for the first call.
610 ExecuteReplaceContents(operation.replace_image.get(),
611 &op_rect,
612 done_replace_contents ? NULL : old_image_data);
613 done_replace_contents = true;
614 break;
617 // For correctness with accelerated compositing, we must issue an invalidate
618 // on the full op_rect even if it is partially or completely off-screen.
619 // However, if we issue an invalidate for a clipped-out region, WebKit will
620 // do nothing and we won't get any ViewFlushedPaint calls, leaving our
621 // callback stranded. So we still need to check whether the repainted area
622 // is visible to determine how to deal with the callback.
623 if (bound_instance_ && !op_rect.IsEmpty()) {
624 gfx::Point scroll_delta(operation.scroll_dx, operation.scroll_dy);
625 if (!ConvertToLogicalPixels(scale_,
626 &op_rect,
627 operation.type == QueuedOperation::SCROLL
628 ? &scroll_delta
629 : NULL)) {
630 // Conversion requires falling back to InvalidateRect.
631 operation.type = QueuedOperation::PAINT;
634 gfx::Rect clip = PP_ToGfxRect(bound_instance_->view_data().clip_rect);
635 is_plugin_visible = !clip.IsEmpty();
637 // Set |no_update_visible| to false if the change overlaps the visible
638 // area.
639 if (!gfx::IntersectRects(clip, op_rect).IsEmpty()) {
640 no_update_visible = false;
643 // Notify the plugin of the entire change (op_rect), even if it is
644 // partially or completely off-screen.
645 if (operation.type == QueuedOperation::SCROLL) {
646 bound_instance_->ScrollRect(
647 scroll_delta.x(), scroll_delta.y(), op_rect);
648 } else {
649 if (!op_rect.IsEmpty())
650 bound_instance_->InvalidateRect(op_rect);
652 texture_mailbox_modified_ = true;
655 queued_operations_.clear();
657 if (!bound_instance_) {
658 // As promised in the API, we always schedule callback when unbound.
659 ScheduleOffscreenFlushAck();
660 } else if (no_update_visible && is_plugin_visible &&
661 bound_instance_->view_data().is_page_visible) {
662 // There's nothing visible to invalidate so just schedule the callback to
663 // execute in the next round of the message loop.
664 ScheduleOffscreenFlushAck();
665 } else {
666 need_flush_ack_ = true;
669 return PP_OK_COMPLETIONPENDING;
672 void PepperGraphics2DHost::ExecutePaintImageData(PPB_ImageData_Impl* image,
673 int x,
674 int y,
675 const gfx::Rect& src_rect,
676 gfx::Rect* invalidated_rect) {
677 // Ensure the source image is mapped to read from it.
678 ImageDataAutoMapper auto_mapper(image);
679 if (!auto_mapper.is_valid())
680 return;
682 // Portion within the source image to cut out.
683 SkIRect src_irect = {src_rect.x(), src_rect.y(), src_rect.right(),
684 src_rect.bottom()};
686 // Location within the backing store to copy to.
687 *invalidated_rect = src_rect;
688 invalidated_rect->Offset(x, y);
689 SkRect dest_rect = {SkIntToScalar(invalidated_rect->x()),
690 SkIntToScalar(invalidated_rect->y()),
691 SkIntToScalar(invalidated_rect->right()),
692 SkIntToScalar(invalidated_rect->bottom())};
694 if (image->format() != image_data_->format()) {
695 // Convert the image data if the format does not match.
696 ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
697 } else {
698 // We're guaranteed to have a mapped canvas since we mapped it in Init().
699 SkCanvas* backing_canvas = image_data_->GetCanvas();
701 // We want to replace the contents of the bitmap rather than blend.
702 SkPaint paint;
703 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
704 backing_canvas->drawBitmapRect(
705 *image->GetMappedBitmap(), &src_irect, dest_rect, &paint);
709 void PepperGraphics2DHost::ExecuteScroll(const gfx::Rect& clip,
710 int dx,
711 int dy,
712 gfx::Rect* invalidated_rect) {
713 gfx::ScrollCanvas(image_data_->GetCanvas(), clip, gfx::Vector2d(dx, dy));
714 *invalidated_rect = clip;
717 void PepperGraphics2DHost::ExecuteReplaceContents(PPB_ImageData_Impl* image,
718 gfx::Rect* invalidated_rect,
719 PP_Resource* old_image_data) {
720 if (image->format() != image_data_->format()) {
721 DCHECK(image->width() == image_data_->width() &&
722 image->height() == image_data_->height());
723 // Convert the image data if the format does not match.
724 SkIRect src_irect = {0, 0, image->width(), image->height()};
725 SkRect dest_rect = {SkIntToScalar(0), SkIntToScalar(0),
726 SkIntToScalar(image_data_->width()),
727 SkIntToScalar(image_data_->height())};
728 ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
729 } else {
730 // The passed-in image may not be mapped in our process, and we need to
731 // guarantee that the current backing store is always mapped.
732 if (!image->Map())
733 return;
735 if (old_image_data)
736 *old_image_data = image_data_->GetReference();
737 image_data_ = image;
739 *invalidated_rect =
740 gfx::Rect(0, 0, image_data_->width(), image_data_->height());
743 void PepperGraphics2DHost::SendFlushAck() {
744 host()->SendReply(flush_reply_context_, PpapiPluginMsg_Graphics2D_FlushAck());
747 void PepperGraphics2DHost::SendOffscreenFlushAck() {
748 DCHECK(offscreen_flush_pending_);
750 // We must clear this flag before issuing the callback. It will be
751 // common for the plugin to issue another invalidate in response to a flush
752 // callback, and we don't want to think that a callback is already pending.
753 offscreen_flush_pending_ = false;
754 SendFlushAck();
757 void PepperGraphics2DHost::ScheduleOffscreenFlushAck() {
758 offscreen_flush_pending_ = true;
759 base::MessageLoop::current()->PostDelayedTask(
760 FROM_HERE,
761 base::Bind(&PepperGraphics2DHost::SendOffscreenFlushAck, AsWeakPtr()),
762 base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs));
765 bool PepperGraphics2DHost::HasPendingFlush() const {
766 return need_flush_ack_ || offscreen_flush_pending_;
769 // static
770 bool PepperGraphics2DHost::ConvertToLogicalPixels(float scale,
771 gfx::Rect* op_rect,
772 gfx::Point* delta) {
773 if (scale == 1.0f || scale <= 0.0f)
774 return true;
776 gfx::Rect original_rect = *op_rect;
777 // Take the enclosing rectangle after scaling so a rectangle scaled down then
778 // scaled back up by the inverse scale would fully contain the entire area
779 // affected by the original rectangle.
780 *op_rect = gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, scale));
781 if (delta) {
782 gfx::Point original_delta = *delta;
783 float inverse_scale = 1.0f / scale;
784 *delta = gfx::ToFlooredPoint(gfx::ScalePoint(*delta, scale));
786 gfx::Rect inverse_scaled_rect =
787 gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, inverse_scale));
788 if (original_rect != inverse_scaled_rect)
789 return false;
790 gfx::Point inverse_scaled_point =
791 gfx::ToFlooredPoint(gfx::ScalePoint(*delta, inverse_scale));
792 if (original_delta != inverse_scaled_point)
793 return false;
796 return true;
799 } // namespace content