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