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 43 using ppapi::thunk::EnterResourceNoLock; 44 using ppapi::thunk::PPB_ImageData_API; 45 46 namespace content { 47 48 namespace { 49 50 const 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. 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; 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. 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]; 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). 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); 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 135 struct PepperGraphics2DHost::QueuedOperation { 136 enum Type { 137 PAINT, 138 SCROLL, 139 REPLACE, 140 SET_OFFSET 141 }; 142 143 QueuedOperation(Type t) 144 : type(t), 145 paint_x(0), 146 paint_y(0), 147 scroll_dx(0), 148 scroll_dy(0) { 149 } 150 151 Type type; 152 153 // Valid when type == PAINT. 154 scoped_refptr<PPB_ImageData_Impl> paint_image; 155 int paint_x, paint_y; 156 gfx::Rect paint_src_rect; 157 158 // Valid when type == SCROLL. 159 gfx::Rect scroll_clip_rect; 160 int scroll_dx, scroll_dy; 161 162 // Valid when type == REPLACE. 163 scoped_refptr<PPB_ImageData_Impl> replace_image; 164 165 // Valid when type == SET_OFFSET. 166 gfx::Point offset; 167 }; 168 169 // static 170 PepperGraphics2DHost* PepperGraphics2DHost::Create( 171 RendererPpapiHost* host, 172 PP_Instance instance, 173 PP_Resource resource, 174 const PP_Size& size, 175 PP_Bool is_always_opaque, 176 scoped_refptr<PPB_ImageData_Impl> backing_store) { 177 PepperGraphics2DHost* resource_host = 178 new PepperGraphics2DHost(host, instance, resource); 179 if (!resource_host->Init(size.width, size.height, 180 PP_ToBool(is_always_opaque), 181 backing_store)) { 182 delete resource_host; 183 return NULL; 184 } 185 return resource_host; 186 } 187 188 PepperGraphics2DHost::PepperGraphics2DHost(RendererPpapiHost* host, 189 PP_Instance instance, 190 PP_Resource resource) 191 : ResourceHost(host->GetPpapiHost(), instance, resource), 192 renderer_ppapi_host_(host), 193 bound_instance_(NULL), 194 need_flush_ack_(false), 195 offscreen_flush_pending_(false), 196 is_always_opaque_(false), 197 scale_(1.0f), 198 is_running_in_process_(host->IsRunningInProcess()), 199 texture_mailbox_modified_(true), 200 resize_mode_(PP_GRAPHICS2D_DEV_RESIZEMODE_DEFAULT) {} 201 202 PepperGraphics2DHost::~PepperGraphics2DHost() { 203 // Unbind from the instance when destroyed if we're still bound. 204 if (bound_instance_) 205 bound_instance_->BindGraphics(bound_instance_->pp_instance(), 0); 206 } 207 208 bool PepperGraphics2DHost::Init( 209 int width, 210 int height, 211 bool is_always_opaque, 212 scoped_refptr<PPB_ImageData_Impl> backing_store) { 213 // The underlying PPB_ImageData_Impl will validate the dimensions. 214 image_data_ = backing_store; 215 if (!image_data_->Init(PPB_ImageData_Impl::GetNativeImageDataFormat(), 216 width, height, true) || 217 !image_data_->Map()) { 218 image_data_ = NULL; 219 return false; 220 } 221 is_always_opaque_ = is_always_opaque; 222 scale_ = 1.0f; 223 return true; 224 } 225 226 int32_t PepperGraphics2DHost::OnResourceMessageReceived( 227 const IPC::Message& msg, 228 ppapi::host::HostMessageContext* context) { 229 IPC_BEGIN_MESSAGE_MAP(PepperGraphics2DHost, msg) 230 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 231 PpapiHostMsg_Graphics2D_PaintImageData, 232 OnHostMsgPaintImageData) 233 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 234 PpapiHostMsg_Graphics2D_Scroll, 235 OnHostMsgScroll) 236 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 237 PpapiHostMsg_Graphics2D_ReplaceContents, 238 OnHostMsgReplaceContents) 239 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 240 PpapiHostMsg_Graphics2D_Flush, 241 OnHostMsgFlush) 242 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 243 PpapiHostMsg_Graphics2D_Dev_SetScale, 244 OnHostMsgSetScale) 245 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 246 PpapiHostMsg_Graphics2D_SetOffset, 247 OnHostMsgSetOffset) 248 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 249 PpapiHostMsg_Graphics2D_SetResizeMode, 250 OnHostMsgSetResizeMode) 251 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 252 PpapiHostMsg_Graphics2D_ReadImageData, 253 OnHostMsgReadImageData) 254 IPC_END_MESSAGE_MAP() 255 return PP_ERROR_FAILED; 256 } 257 258 bool PepperGraphics2DHost::IsGraphics2DHost() { 259 return true; 260 } 261 262 bool PepperGraphics2DHost::ReadImageData(PP_Resource image, 263 const PP_Point* top_left) { 264 // Get and validate the image object to paint into. 265 EnterResourceNoLock<PPB_ImageData_API> enter(image, true); 266 if (enter.failed()) 267 return false; 268 PPB_ImageData_Impl* image_resource = 269 static_cast<PPB_ImageData_Impl*>(enter.object()); 270 if (!PPB_ImageData_Impl::IsImageDataFormatSupported( 271 image_resource->format())) 272 return false; // Must be in the right format. 273 274 // Validate the bitmap position. 275 int x = top_left->x; 276 if (x < 0 || 277 static_cast<int64>(x) + static_cast<int64>(image_resource->width()) > 278 image_data_->width()) 279 return false; 280 int y = top_left->y; 281 if (y < 0 || 282 static_cast<int64>(y) + static_cast<int64>(image_resource->height()) > 283 image_data_->height()) 284 return false; 285 286 ImageDataAutoMapper auto_mapper(image_resource); 287 if (!auto_mapper.is_valid()) 288 return false; 289 290 SkIRect src_irect = { x, y, 291 x + image_resource->width(), 292 y + image_resource->height() }; 293 SkRect dest_rect = { SkIntToScalar(0), 294 SkIntToScalar(0), 295 SkIntToScalar(image_resource->width()), 296 SkIntToScalar(image_resource->height()) }; 297 298 if (image_resource->format() != image_data_->format()) { 299 // Convert the image data if the format does not match. 300 ConvertImageData(image_data_.get(), src_irect, image_resource, dest_rect); 301 } else { 302 SkCanvas* dest_canvas = image_resource->GetCanvas(); 303 304 // We want to replace the contents of the bitmap rather than blend. 305 SkPaint paint; 306 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 307 dest_canvas->drawBitmapRect(*image_data_->GetMappedBitmap(), 308 &src_irect, dest_rect, &paint); 309 } 310 return true; 311 } 312 313 bool PepperGraphics2DHost::BindToInstance( 314 PepperPluginInstanceImpl* new_instance) { 315 if (new_instance && new_instance->pp_instance() != pp_instance()) 316 return false; // Can't bind other instance's contexts. 317 if (bound_instance_ == new_instance) 318 return true; // Rebinding the same device, nothing to do. 319 if (bound_instance_ && new_instance) 320 return false; // Can't change a bound device. 321 322 if (!new_instance) { 323 // When the device is detached, we'll not get any more paint callbacks so 324 // we need to clear the list, but we still want to issue any pending 325 // callbacks to the plugin. 326 if (need_flush_ack_) 327 ScheduleOffscreenFlushAck(); 328 } else { 329 // Devices being replaced, redraw the plugin. 330 new_instance->InvalidateRect(gfx::Rect()); 331 } 332 333 texture_mailbox_modified_ = true; 334 335 bound_instance_ = new_instance; 336 return true; 337 } 338 339 // The |backing_bitmap| must be clipped to the |plugin_rect| to avoid painting 340 // outside the plugin area. This can happen if the plugin has been resized since 341 // PaintImageData verified the image is within the plugin size. 342 void PepperGraphics2DHost::Paint(blink::WebCanvas* canvas, 343 const gfx::Rect& plugin_rect, 344 const gfx::Rect& paint_rect) { 345 TRACE_EVENT0("pepper", "PepperGraphics2DHost::Paint"); 346 ImageDataAutoMapper auto_mapper(image_data_.get()); 347 const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap(); 348 349 gfx::Rect invalidate_rect = plugin_rect; 350 invalidate_rect.Intersect(paint_rect); 351 SkRect sk_invalidate_rect = gfx::RectToSkRect(invalidate_rect); 352 SkAutoCanvasRestore auto_restore(canvas, true); 353 canvas->clipRect(sk_invalidate_rect); 354 gfx::Size pixel_image_size(image_data_->width(), image_data_->height()); 355 gfx::Size image_size = gfx::ToFlooredSize( 356 gfx::ScaleSize(pixel_image_size, scale_)); 357 358 PepperPluginInstance* plugin_instance = 359 renderer_ppapi_host_->GetPluginInstance(pp_instance()); 360 if (!plugin_instance) 361 return; 362 if (plugin_instance->IsFullPagePlugin()) { 363 // When we're resizing a window with a full-frame plugin, the plugin may 364 // not yet have bound a new device, which will leave parts of the 365 // background exposed if the window is getting larger. We want this to 366 // show white (typically less jarring) rather than black or uninitialized. 367 // We don't do this for non-full-frame plugins since we specifically want 368 // the page background to show through. 369 SkAutoCanvasRestore auto_restore(canvas, true); 370 SkRect image_data_rect = 371 gfx::RectToSkRect(gfx::Rect(plugin_rect.origin(), image_size)); 372 canvas->clipRect(image_data_rect, SkRegion::kDifference_Op); 373 374 SkPaint paint; 375 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 376 paint.setColor(SK_ColorWHITE); 377 canvas->drawRect(sk_invalidate_rect, paint); 378 } 379 380 SkBitmap image; 381 // Copy to device independent bitmap when target canvas doesn't support 382 // platform paint. 383 if (!skia::SupportsPlatformPaint(canvas)) 384 backing_bitmap.copyTo(&image, SkBitmap::kARGB_8888_Config); 385 else 386 image = backing_bitmap; 387 388 SkPaint paint; 389 if (is_always_opaque_) { 390 // When we know the device is opaque, we can disable blending for slightly 391 // more optimized painting. 392 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 393 } 394 395 SkPoint origin; 396 origin.set(SkIntToScalar(plugin_rect.x()), SkIntToScalar(plugin_rect.y())); 397 398 SkPoint pixel_origin = origin; 399 400 gfx::PointF resize_scale(GetResizeScale()); 401 gfx::PointF scale(ScalePoint(resize_scale, scale_)); 402 if ((scale.x() != 1.0f || scale.y() != 1.0f) && 403 scale.x() > 0.0f && scale.y() > 0.0f) { 404 canvas->scale(scale.x(), scale.y()); 405 pixel_origin.set(pixel_origin.x() * (1.0f / scale.x()), 406 pixel_origin.y() * (1.0f / scale.y())); 407 } 408 pixel_origin.offset(SkIntToScalar(plugin_offset_.x()), 409 SkIntToScalar(plugin_offset_.y())); 410 canvas->drawBitmap(image, pixel_origin.x(), pixel_origin.y(), &paint); 411 } 412 413 void PepperGraphics2DHost::ViewInitiatedPaint() { 414 } 415 416 void PepperGraphics2DHost::ViewFlushedPaint() { 417 TRACE_EVENT0("pepper", "PepperGraphics2DHost::ViewFlushedPaint"); 418 if (need_flush_ack_) { 419 SendFlushAck(); 420 need_flush_ack_ = false; 421 } 422 } 423 424 void PepperGraphics2DHost::DidChangeView(const ppapi::ViewData& view_data) { 425 gfx::Size old_plugin_size = current_plugin_size_; 426 current_plugin_size_ = PP_ToGfxSize(view_data.rect.size); 427 } 428 429 void PepperGraphics2DHost::SetScale(float scale) { 430 scale_ = scale; 431 } 432 433 float PepperGraphics2DHost::GetScale() const { 434 return scale_; 435 } 436 437 bool PepperGraphics2DHost::IsAlwaysOpaque() const { 438 return is_always_opaque_; 439 } 440 441 PPB_ImageData_Impl* PepperGraphics2DHost::ImageData() { 442 return image_data_.get(); 443 } 444 445 gfx::Size PepperGraphics2DHost::Size() const { 446 if (!image_data_) 447 return gfx::Size(); 448 return gfx::Size(image_data_->width(), image_data_->height()); 449 } 450 451 gfx::PointF PepperGraphics2DHost::GetResizeScale() const { 452 switch (resize_mode_) { 453 case PP_GRAPHICS2D_DEV_RESIZEMODE_DEFAULT: 454 return gfx::PointF(1.0f, 1.0f); 455 case PP_GRAPHICS2D_DEV_RESIZEMODE_STRETCH: 456 if (flushed_plugin_size_.IsEmpty()) 457 return gfx::PointF(1.0f, 1.0f); 458 return gfx::PointF( 459 1.0f * current_plugin_size_.width() / flushed_plugin_size_.width(), 460 1.0f * current_plugin_size_.height() / flushed_plugin_size_.height()); 461 } 462 NOTREACHED(); 463 return gfx::PointF(1.0f, 1.0f); 464 } 465 466 int32_t PepperGraphics2DHost::OnHostMsgPaintImageData( 467 ppapi::host::HostMessageContext* context, 468 const ppapi::HostResource& image_data, 469 const PP_Point& top_left, 470 bool src_rect_specified, 471 const PP_Rect& src_rect) { 472 EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(), 473 true); 474 if (enter.failed()) 475 return PP_ERROR_BADRESOURCE; 476 PPB_ImageData_Impl* image_resource = 477 static_cast<PPB_ImageData_Impl*>(enter.object()); 478 479 QueuedOperation operation(QueuedOperation::PAINT); 480 operation.paint_image = image_resource; 481 if (!ValidateAndConvertRect(src_rect_specified ? &src_rect : NULL, 482 image_resource->width(), 483 image_resource->height(), 484 &operation.paint_src_rect)) 485 return PP_ERROR_BADARGUMENT; 486 487 // Validate the bitmap position using the previously-validated rect, there 488 // should be no painted area outside of the image. 489 int64 x64 = static_cast<int64>(top_left.x); 490 int64 y64 = static_cast<int64>(top_left.y); 491 if (x64 + static_cast<int64>(operation.paint_src_rect.x()) < 0 || 492 x64 + static_cast<int64>(operation.paint_src_rect.right()) > 493 image_data_->width()) 494 return PP_ERROR_BADARGUMENT; 495 if (y64 + static_cast<int64>(operation.paint_src_rect.y()) < 0 || 496 y64 + static_cast<int64>(operation.paint_src_rect.bottom()) > 497 image_data_->height()) 498 return PP_ERROR_BADARGUMENT; 499 operation.paint_x = top_left.x; 500 operation.paint_y = top_left.y; 501 502 queued_operations_.push_back(operation); 503 return PP_OK; 504 } 505 506 int32_t PepperGraphics2DHost::OnHostMsgScroll( 507 ppapi::host::HostMessageContext* context, 508 bool clip_specified, 509 const PP_Rect& clip, 510 const PP_Point& amount) { 511 QueuedOperation operation(QueuedOperation::SCROLL); 512 if (!ValidateAndConvertRect(clip_specified ? &clip : NULL, 513 image_data_->width(), 514 image_data_->height(), 515 &operation.scroll_clip_rect)) 516 return PP_ERROR_BADARGUMENT; 517 518 // If we're being asked to scroll by more than the clip rect size, just 519 // ignore this scroll command and say it worked. 520 int32 dx = amount.x; 521 int32 dy = amount.y; 522 if (dx <= -image_data_->width() || dx >= image_data_->width() || 523 dy <= -image_data_->height() || dy >= image_data_->height()) 524 return PP_ERROR_BADARGUMENT; 525 526 operation.scroll_dx = dx; 527 operation.scroll_dy = dy; 528 529 queued_operations_.push_back(operation); 530 return PP_OK; 531 } 532 533 int32_t PepperGraphics2DHost::OnHostMsgReplaceContents( 534 ppapi::host::HostMessageContext* context, 535 const ppapi::HostResource& image_data) { 536 EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(), 537 true); 538 if (enter.failed()) 539 return PP_ERROR_BADRESOURCE; 540 PPB_ImageData_Impl* image_resource = 541 static_cast<PPB_ImageData_Impl*>(enter.object()); 542 543 if (!PPB_ImageData_Impl::IsImageDataFormatSupported( 544 image_resource->format())) 545 return PP_ERROR_BADARGUMENT; 546 547 if (image_resource->width() != image_data_->width() || 548 image_resource->height() != image_data_->height()) 549 return PP_ERROR_BADARGUMENT; 550 551 QueuedOperation operation(QueuedOperation::REPLACE); 552 operation.replace_image = image_resource; 553 queued_operations_.push_back(operation); 554 return PP_OK; 555 } 556 557 int32_t PepperGraphics2DHost::OnHostMsgFlush( 558 ppapi::host::HostMessageContext* context, 559 const ppapi::ViewData& view_data) { 560 // Don't allow more than one pending flush at a time. 561 if (HasPendingFlush()) 562 return PP_ERROR_INPROGRESS; 563 564 PP_Resource old_image_data = 0; 565 flush_reply_context_ = context->MakeReplyMessageContext(); 566 if (is_running_in_process_) 567 return Flush(NULL, PP_ToGfxSize(view_data.rect.size)); 568 569 // Reuse image data when running out of process. 570 int32_t result = Flush(&old_image_data, PP_ToGfxSize(view_data.rect.size)); 571 572 if (old_image_data) { 573 // If the Graphics2D has an old image data it's not using any more, send 574 // it back to the plugin for possible re-use. See ppb_image_data_proxy.cc 575 // for a description how this process works. 576 ppapi::HostResource old_image_data_host_resource; 577 old_image_data_host_resource.SetHostResource(pp_instance(), 578 old_image_data); 579 host()->Send(new PpapiMsg_PPBImageData_NotifyUnusedImageData( 580 ppapi::API_ID_PPB_IMAGE_DATA, old_image_data_host_resource)); 581 } 582 583 return result; 584 } 585 586 int32_t PepperGraphics2DHost::OnHostMsgSetScale( 587 ppapi::host::HostMessageContext* context, 588 float scale) { 589 if (scale > 0.0f) { 590 scale_ = scale; 591 return PP_OK; 592 } 593 return PP_ERROR_BADARGUMENT; 594 } 595 596 int32_t PepperGraphics2DHost::OnHostMsgSetOffset( 597 ppapi::host::HostMessageContext* context, 598 const PP_Point& offset) { 599 QueuedOperation operation(QueuedOperation::SET_OFFSET); 600 operation.offset = PP_ToGfxPoint(offset); 601 queued_operations_.push_back(operation); 602 return PP_OK; 603 } 604 605 int32_t PepperGraphics2DHost::OnHostMsgSetResizeMode( 606 ppapi::host::HostMessageContext* context, 607 PP_Graphics2D_Dev_ResizeMode resize_mode) { 608 resize_mode_ = resize_mode; 609 return PP_OK; 610 } 611 612 int32_t PepperGraphics2DHost::OnHostMsgReadImageData( 613 ppapi::host::HostMessageContext* context, 614 PP_Resource image, 615 const PP_Point& top_left) { 616 context->reply_msg = PpapiPluginMsg_Graphics2D_ReadImageDataAck(); 617 return ReadImageData(image, &top_left) ? PP_OK : PP_ERROR_FAILED; 618 } 619 620 void ReleaseCallback(scoped_ptr<base::SharedMemory> memory, 621 unsigned sync_point, 622 bool lost_resource) {} 623 624 bool PepperGraphics2DHost::PrepareTextureMailbox( 625 cc::TextureMailbox* mailbox, 626 scoped_ptr<cc::SingleReleaseCallback>* release_callback) { 627 if (!texture_mailbox_modified_) 628 return false; 629 // TODO(jbauman): Send image_data_ through mailbox to avoid copy. 630 gfx::Size pixel_image_size(image_data_->width(), image_data_->height()); 631 int buffer_size = pixel_image_size.GetArea() * 4; 632 scoped_ptr<base::SharedMemory> memory = 633 RenderThread::Get()->HostAllocateSharedMemoryBuffer(buffer_size); 634 if (!memory || !memory->Map(buffer_size)) 635 return false; 636 void* src = image_data_->Map(); 637 memcpy(memory->memory(), src, buffer_size); 638 image_data_->Unmap(); 639 640 *mailbox = cc::TextureMailbox(memory.get(), pixel_image_size); 641 *release_callback = cc::SingleReleaseCallback::Create( 642 base::Bind(&ReleaseCallback, base::Passed(&memory))); 643 texture_mailbox_modified_ = false; 644 return true; 645 } 646 647 void PepperGraphics2DHost::AttachedToNewLayer() { 648 texture_mailbox_modified_ = true; 649 } 650 651 int32_t PepperGraphics2DHost::Flush(PP_Resource* old_image_data, 652 const gfx::Size& flushed_plugin_size) { 653 bool done_replace_contents = false; 654 bool no_update_visible = true; 655 bool is_plugin_visible = true; 656 for (size_t i = 0; i < queued_operations_.size(); i++) { 657 QueuedOperation& operation = queued_operations_[i]; 658 gfx::Rect op_rect; 659 gfx::Rect old_op_rect; 660 switch (operation.type) { 661 case QueuedOperation::PAINT: 662 ExecutePaintImageData(operation.paint_image.get(), 663 operation.paint_x, 664 operation.paint_y, 665 operation.paint_src_rect, 666 &op_rect); 667 break; 668 case QueuedOperation::SCROLL: 669 ExecuteScroll(operation.scroll_clip_rect, 670 operation.scroll_dx, operation.scroll_dy, 671 &op_rect); 672 break; 673 case QueuedOperation::REPLACE: 674 // Since the out parameter |old_image_data| takes ownership of the 675 // reference, if there are more than one ReplaceContents calls queued 676 // the first |old_image_data| will get overwritten and leaked. So we 677 // only supply this for the first call. 678 ExecuteReplaceContents(operation.replace_image.get(), 679 &op_rect, 680 done_replace_contents ? NULL : old_image_data); 681 done_replace_contents = true; 682 break; 683 case QueuedOperation::SET_OFFSET: 684 old_op_rect = gfx::Rect(plugin_offset_.x(), plugin_offset_.y(), 685 image_data_->width(), image_data_->height()); 686 plugin_offset_ = operation.offset; 687 // The offset is applied below for |op_rect|. 688 op_rect = gfx::Rect(image_data_->width(), image_data_->height()); 689 break; 690 } 691 692 op_rect.Offset(plugin_offset_.x(), plugin_offset_.y()); 693 694 // For correctness with accelerated compositing, we must issue an invalidate 695 // on the full op_rect even if it is partially or completely off-screen. 696 // However, if we issue an invalidate for a clipped-out region, WebKit will 697 // do nothing and we won't get any ViewFlushedPaint calls, leaving our 698 // callback stranded. So we still need to check whether the repainted area 699 // is visible to determine how to deal with the callback. 700 if (bound_instance_ && (!op_rect.IsEmpty() || !old_op_rect.IsEmpty())) { 701 gfx::Point scroll_delta(operation.scroll_dx, operation.scroll_dy); 702 if (!ConvertToLogicalPixels(scale_, &old_op_rect, NULL)) { 703 NOTREACHED(); 704 } 705 if (!ConvertToLogicalPixels(scale_, 706 &op_rect, 707 operation.type == QueuedOperation::SCROLL ? 708 &scroll_delta : NULL)) { 709 // Conversion requires falling back to InvalidateRect. 710 operation.type = QueuedOperation::PAINT; 711 } 712 713 gfx::Rect clip = PP_ToGfxRect(bound_instance_->view_data().clip_rect); 714 is_plugin_visible = !clip.IsEmpty(); 715 716 // Set |no_update_visible| to false if the change overlaps the visible 717 // area. 718 if (!gfx::IntersectRects(clip, op_rect).IsEmpty() || 719 !gfx::IntersectRects(clip, old_op_rect).IsEmpty()) { 720 no_update_visible = false; 721 } 722 723 // Notify the plugin of the entire change (op_rect), even if it is 724 // partially or completely off-screen. 725 if (operation.type == QueuedOperation::SCROLL) { 726 bound_instance_->ScrollRect(scroll_delta.x(), scroll_delta.y(), 727 op_rect); 728 DCHECK(old_op_rect.IsEmpty()); 729 } else { 730 if (!op_rect.IsEmpty()) 731 bound_instance_->InvalidateRect(op_rect); 732 if (!old_op_rect.IsEmpty()) 733 bound_instance_->InvalidateRect(old_op_rect); 734 } 735 texture_mailbox_modified_ = true; 736 } 737 } 738 queued_operations_.clear(); 739 740 flushed_plugin_size_ = flushed_plugin_size; 741 if (!bound_instance_) { 742 // As promised in the API, we always schedule callback when unbound. 743 ScheduleOffscreenFlushAck(); 744 } else if (no_update_visible && is_plugin_visible && 745 bound_instance_->view_data().is_page_visible) { 746 // There's nothing visible to invalidate so just schedule the callback to 747 // execute in the next round of the message loop. 748 ScheduleOffscreenFlushAck(); 749 } else { 750 need_flush_ack_ = true; 751 } 752 753 return PP_OK_COMPLETIONPENDING; 754 } 755 756 void PepperGraphics2DHost::ExecutePaintImageData(PPB_ImageData_Impl* image, 757 int x, int y, 758 const gfx::Rect& src_rect, 759 gfx::Rect* invalidated_rect) { 760 // Ensure the source image is mapped to read from it. 761 ImageDataAutoMapper auto_mapper(image); 762 if (!auto_mapper.is_valid()) 763 return; 764 765 // Portion within the source image to cut out. 766 SkIRect src_irect = { src_rect.x(), src_rect.y(), 767 src_rect.right(), src_rect.bottom() }; 768 769 // Location within the backing store to copy to. 770 *invalidated_rect = src_rect; 771 invalidated_rect->Offset(x, y); 772 SkRect dest_rect = { SkIntToScalar(invalidated_rect->x()), 773 SkIntToScalar(invalidated_rect->y()), 774 SkIntToScalar(invalidated_rect->right()), 775 SkIntToScalar(invalidated_rect->bottom()) }; 776 777 if (image->format() != image_data_->format()) { 778 // Convert the image data if the format does not match. 779 ConvertImageData(image, src_irect, image_data_.get(), dest_rect); 780 } else { 781 // We're guaranteed to have a mapped canvas since we mapped it in Init(). 782 SkCanvas* backing_canvas = image_data_->GetCanvas(); 783 784 // We want to replace the contents of the bitmap rather than blend. 785 SkPaint paint; 786 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 787 backing_canvas->drawBitmapRect(*image->GetMappedBitmap(), 788 &src_irect, dest_rect, &paint); 789 } 790 } 791 792 void PepperGraphics2DHost::ExecuteScroll(const gfx::Rect& clip, 793 int dx, int dy, 794 gfx::Rect* invalidated_rect) { 795 gfx::ScrollCanvas(image_data_->GetCanvas(), 796 clip, gfx::Vector2d(dx, dy)); 797 *invalidated_rect = clip; 798 } 799 800 void PepperGraphics2DHost::ExecuteReplaceContents(PPB_ImageData_Impl* image, 801 gfx::Rect* invalidated_rect, 802 PP_Resource* old_image_data) { 803 if (image->format() != image_data_->format()) { 804 DCHECK(image->width() == image_data_->width() && 805 image->height() == image_data_->height()); 806 // Convert the image data if the format does not match. 807 SkIRect src_irect = { 0, 0, image->width(), image->height() }; 808 SkRect dest_rect = { SkIntToScalar(0), 809 SkIntToScalar(0), 810 SkIntToScalar(image_data_->width()), 811 SkIntToScalar(image_data_->height()) }; 812 ConvertImageData(image, src_irect, image_data_.get(), dest_rect); 813 } else { 814 // The passed-in image may not be mapped in our process, and we need to 815 // guarantee that the current backing store is always mapped. 816 if (!image->Map()) 817 return; 818 819 if (old_image_data) 820 *old_image_data = image_data_->GetReference(); 821 image_data_ = image; 822 } 823 *invalidated_rect = gfx::Rect(0, 0, 824 image_data_->width(), image_data_->height()); 825 } 826 827 void PepperGraphics2DHost::SendFlushAck() { 828 host()->SendReply(flush_reply_context_, 829 PpapiPluginMsg_Graphics2D_FlushAck()); 830 } 831 832 void PepperGraphics2DHost::SendOffscreenFlushAck() { 833 DCHECK(offscreen_flush_pending_); 834 835 // We must clear this flag before issuing the callback. It will be 836 // common for the plugin to issue another invalidate in response to a flush 837 // callback, and we don't want to think that a callback is already pending. 838 offscreen_flush_pending_ = false; 839 SendFlushAck(); 840 } 841 842 void PepperGraphics2DHost::ScheduleOffscreenFlushAck() { 843 offscreen_flush_pending_ = true; 844 base::MessageLoop::current()->PostDelayedTask( 845 FROM_HERE, 846 base::Bind(&PepperGraphics2DHost::SendOffscreenFlushAck, 847 AsWeakPtr()), 848 base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs)); 849 } 850 851 bool PepperGraphics2DHost::HasPendingFlush() const { 852 return need_flush_ack_ || offscreen_flush_pending_; 853 } 854 855 // static 856 bool PepperGraphics2DHost::ConvertToLogicalPixels(float scale, 857 gfx::Rect* op_rect, 858 gfx::Point* delta) { 859 if (scale == 1.0f || scale <= 0.0f) 860 return true; 861 862 gfx::Rect original_rect = *op_rect; 863 // Take the enclosing rectangle after scaling so a rectangle scaled down then 864 // scaled back up by the inverse scale would fully contain the entire area 865 // affected by the original rectangle. 866 *op_rect = gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, scale)); 867 if (delta) { 868 gfx::Point original_delta = *delta; 869 float inverse_scale = 1.0f / scale; 870 *delta = gfx::ToFlooredPoint(gfx::ScalePoint(*delta, scale)); 871 872 gfx::Rect inverse_scaled_rect = 873 gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, inverse_scale)); 874 if (original_rect != inverse_scaled_rect) 875 return false; 876 gfx::Point inverse_scaled_point = 877 gfx::ToFlooredPoint(gfx::ScalePoint(*delta, inverse_scale)); 878 if (original_delta != inverse_scaled_point) 879 return false; 880 } 881 882 return true; 883 } 884 885 } // namespace content 886