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