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/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" 39 40 #if defined(OS_MACOSX) 41 #include "base/mac/mac_util.h" 42 #include "base/mac/scoped_cftyperef.h" 43 #endif 44 45 using ppapi::thunk::EnterResourceNoLock; 46 using ppapi::thunk::PPB_ImageData_API; 47 48 namespace content { 49 50 namespace { 51 52 const int64 kOffscreenCallbackDelayMs = 1000 / 30; // 30 fps 53 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; 69 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; 79 80 *dest = gfx::Rect( 81 rect->point.x, rect->point.y, rect->size.width, rect->size.height); 82 } 83 return true; 84 } 85 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]; 98 } 99 } 100 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); 109 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())); 113 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))); 134 } 135 } 136 } 137 138 } // namespace 139 140 struct PepperGraphics2DHost::QueuedOperation { 141 enum Type { PAINT, SCROLL, REPLACE, }; 142 143 QueuedOperation(Type t) 144 : type(t), paint_x(0), paint_y(0), scroll_dx(0), scroll_dy(0) {} 145 146 Type type; 147 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; 152 153 // Valid when type == SCROLL. 154 gfx::Rect scroll_clip_rect; 155 int scroll_dx, scroll_dy; 156 157 // Valid when type == REPLACE. 158 scoped_refptr<PPB_ImageData_Impl> replace_image; 159 }; 160 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; 177 } 178 return resource_host; 179 } 180 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) {} 193 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); 198 } 199 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; 214 } 215 is_always_opaque_ = is_always_opaque; 216 scale_ = 1.0f; 217 return true; 218 } 219 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; 238 } 239 240 bool PepperGraphics2DHost::IsGraphics2DHost() { return true; } 241 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. 252 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; 264 265 ImageDataAutoMapper auto_mapper(image_resource); 266 if (!auto_mapper.is_valid()) 267 return false; 268 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())}; 274 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(); 280 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); 286 } 287 return true; 288 } 289 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. 298 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()); 308 } 309 310 cached_bitmap_.reset(); 311 texture_mailbox_modified_ = true; 312 313 bound_instance_ = new_instance; 314 return true; 315 } 316 317 // The |backing_bitmap| must be clipped to the |plugin_rect| to avoid painting 318 // outside the plugin area. This can happen if the plugin has been resized since 319 // PaintImageData verified the image is within the plugin size. 320 void PepperGraphics2DHost::Paint(blink::WebCanvas* canvas, 321 const gfx::Rect& plugin_rect, 322 const gfx::Rect& paint_rect) { 323 TRACE_EVENT0("pepper", "PepperGraphics2DHost::Paint"); 324 ImageDataAutoMapper auto_mapper(image_data_.get()); 325 const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap(); 326 327 gfx::Rect invalidate_rect = plugin_rect; 328 invalidate_rect.Intersect(paint_rect); 329 SkRect sk_invalidate_rect = gfx::RectToSkRect(invalidate_rect); 330 SkAutoCanvasRestore auto_restore(canvas, true); 331 canvas->clipRect(sk_invalidate_rect); 332 gfx::Size pixel_image_size(image_data_->width(), image_data_->height()); 333 gfx::Size image_size = 334 gfx::ToFlooredSize(gfx::ScaleSize(pixel_image_size, scale_)); 335 336 PepperPluginInstance* plugin_instance = 337 renderer_ppapi_host_->GetPluginInstance(pp_instance()); 338 if (!plugin_instance) 339 return; 340 if (plugin_instance->IsFullPagePlugin()) { 341 // When we're resizing a window with a full-frame plugin, the plugin may 342 // not yet have bound a new device, which will leave parts of the 343 // background exposed if the window is getting larger. We want this to 344 // show white (typically less jarring) rather than black or uninitialized. 345 // We don't do this for non-full-frame plugins since we specifically want 346 // the page background to show through. 347 SkAutoCanvasRestore auto_restore(canvas, true); 348 SkRect image_data_rect = 349 gfx::RectToSkRect(gfx::Rect(plugin_rect.origin(), image_size)); 350 canvas->clipRect(image_data_rect, SkRegion::kDifference_Op); 351 352 SkPaint paint; 353 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 354 paint.setColor(SK_ColorWHITE); 355 canvas->drawRect(sk_invalidate_rect, paint); 356 } 357 358 SkBitmap image; 359 // Copy to device independent bitmap when target canvas doesn't support 360 // platform paint. 361 if (!skia::SupportsPlatformPaint(canvas)) 362 backing_bitmap.copyTo(&image, kN32_SkColorType); 363 else 364 image = backing_bitmap; 365 366 SkPaint paint; 367 if (is_always_opaque_) { 368 // When we know the device is opaque, we can disable blending for slightly 369 // more optimized painting. 370 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 371 } 372 373 SkPoint origin; 374 origin.set(SkIntToScalar(plugin_rect.x()), SkIntToScalar(plugin_rect.y())); 375 376 SkPoint pixel_origin = origin; 377 378 if (scale_ != 1.0f && scale_ > 0.0f) { 379 canvas->scale(scale_, scale_); 380 pixel_origin.set(pixel_origin.x() * (1.0f / scale_), 381 pixel_origin.y() * (1.0f / scale_)); 382 } 383 canvas->drawBitmap(image, pixel_origin.x(), pixel_origin.y(), &paint); 384 } 385 386 void PepperGraphics2DHost::ViewInitiatedPaint() {} 387 388 void PepperGraphics2DHost::ViewFlushedPaint() { 389 TRACE_EVENT0("pepper", "PepperGraphics2DHost::ViewFlushedPaint"); 390 if (need_flush_ack_) { 391 SendFlushAck(); 392 need_flush_ack_ = false; 393 } 394 } 395 396 void PepperGraphics2DHost::SetScale(float scale) { scale_ = scale; } 397 398 float PepperGraphics2DHost::GetScale() const { return scale_; } 399 400 bool PepperGraphics2DHost::IsAlwaysOpaque() const { return is_always_opaque_; } 401 402 PPB_ImageData_Impl* PepperGraphics2DHost::ImageData() { 403 return image_data_.get(); 404 } 405 406 gfx::Size PepperGraphics2DHost::Size() const { 407 if (!image_data_.get()) 408 return gfx::Size(); 409 return gfx::Size(image_data_->width(), image_data_->height()); 410 } 411 412 void PepperGraphics2DHost::ClearCache() { 413 cached_bitmap_.reset(); 414 } 415 416 int32_t PepperGraphics2DHost::OnHostMsgPaintImageData( 417 ppapi::host::HostMessageContext* context, 418 const ppapi::HostResource& image_data, 419 const PP_Point& top_left, 420 bool src_rect_specified, 421 const PP_Rect& src_rect) { 422 EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(), 423 true); 424 if (enter.failed()) 425 return PP_ERROR_BADRESOURCE; 426 PPB_ImageData_Impl* image_resource = 427 static_cast<PPB_ImageData_Impl*>(enter.object()); 428 429 QueuedOperation operation(QueuedOperation::PAINT); 430 operation.paint_image = image_resource; 431 if (!ValidateAndConvertRect(src_rect_specified ? &src_rect : NULL, 432 image_resource->width(), 433 image_resource->height(), 434 &operation.paint_src_rect)) 435 return PP_ERROR_BADARGUMENT; 436 437 // Validate the bitmap position using the previously-validated rect, there 438 // should be no painted area outside of the image. 439 int64 x64 = static_cast<int64>(top_left.x); 440 int64 y64 = static_cast<int64>(top_left.y); 441 if (x64 + static_cast<int64>(operation.paint_src_rect.x()) < 0 || 442 x64 + static_cast<int64>(operation.paint_src_rect.right()) > 443 image_data_->width()) 444 return PP_ERROR_BADARGUMENT; 445 if (y64 + static_cast<int64>(operation.paint_src_rect.y()) < 0 || 446 y64 + static_cast<int64>(operation.paint_src_rect.bottom()) > 447 image_data_->height()) 448 return PP_ERROR_BADARGUMENT; 449 operation.paint_x = top_left.x; 450 operation.paint_y = top_left.y; 451 452 queued_operations_.push_back(operation); 453 return PP_OK; 454 } 455 456 int32_t PepperGraphics2DHost::OnHostMsgScroll( 457 ppapi::host::HostMessageContext* context, 458 bool clip_specified, 459 const PP_Rect& clip, 460 const PP_Point& amount) { 461 QueuedOperation operation(QueuedOperation::SCROLL); 462 if (!ValidateAndConvertRect(clip_specified ? &clip : NULL, 463 image_data_->width(), 464 image_data_->height(), 465 &operation.scroll_clip_rect)) 466 return PP_ERROR_BADARGUMENT; 467 468 // If we're being asked to scroll by more than the clip rect size, just 469 // ignore this scroll command and say it worked. 470 int32 dx = amount.x; 471 int32 dy = amount.y; 472 if (dx <= -image_data_->width() || dx >= image_data_->width() || 473 dy <= -image_data_->height() || dy >= image_data_->height()) 474 return PP_ERROR_BADARGUMENT; 475 476 operation.scroll_dx = dx; 477 operation.scroll_dy = dy; 478 479 queued_operations_.push_back(operation); 480 return PP_OK; 481 } 482 483 int32_t PepperGraphics2DHost::OnHostMsgReplaceContents( 484 ppapi::host::HostMessageContext* context, 485 const ppapi::HostResource& image_data) { 486 EnterResourceNoLock<PPB_ImageData_API> enter(image_data.host_resource(), 487 true); 488 if (enter.failed()) 489 return PP_ERROR_BADRESOURCE; 490 PPB_ImageData_Impl* image_resource = 491 static_cast<PPB_ImageData_Impl*>(enter.object()); 492 493 if (!PPB_ImageData_Impl::IsImageDataFormatSupported(image_resource->format())) 494 return PP_ERROR_BADARGUMENT; 495 496 if (image_resource->width() != image_data_->width() || 497 image_resource->height() != image_data_->height()) 498 return PP_ERROR_BADARGUMENT; 499 500 QueuedOperation operation(QueuedOperation::REPLACE); 501 operation.replace_image = image_resource; 502 queued_operations_.push_back(operation); 503 return PP_OK; 504 } 505 506 int32_t PepperGraphics2DHost::OnHostMsgFlush( 507 ppapi::host::HostMessageContext* context, 508 const std::vector<ui::LatencyInfo>& latency_info) { 509 // Don't allow more than one pending flush at a time. 510 if (HasPendingFlush()) 511 return PP_ERROR_INPROGRESS; 512 513 if (bound_instance_) 514 bound_instance_->AddLatencyInfo(latency_info); 515 516 PP_Resource old_image_data = 0; 517 flush_reply_context_ = context->MakeReplyMessageContext(); 518 if (is_running_in_process_) 519 return Flush(NULL); 520 521 // Reuse image data when running out of process. 522 int32_t result = Flush(&old_image_data); 523 524 if (old_image_data) { 525 // If the Graphics2D has an old image data it's not using any more, send 526 // it back to the plugin for possible re-use. See ppb_image_data_proxy.cc 527 // for a description how this process works. 528 ppapi::HostResource old_image_data_host_resource; 529 old_image_data_host_resource.SetHostResource(pp_instance(), old_image_data); 530 host()->Send(new PpapiMsg_PPBImageData_NotifyUnusedImageData( 531 ppapi::API_ID_PPB_IMAGE_DATA, old_image_data_host_resource)); 532 } 533 534 return result; 535 } 536 537 int32_t PepperGraphics2DHost::OnHostMsgSetScale( 538 ppapi::host::HostMessageContext* context, 539 float scale) { 540 if (scale > 0.0f) { 541 scale_ = scale; 542 return PP_OK; 543 } 544 return PP_ERROR_BADARGUMENT; 545 } 546 547 int32_t PepperGraphics2DHost::OnHostMsgReadImageData( 548 ppapi::host::HostMessageContext* context, 549 PP_Resource image, 550 const PP_Point& top_left) { 551 context->reply_msg = PpapiPluginMsg_Graphics2D_ReadImageDataAck(); 552 return ReadImageData(image, &top_left) ? PP_OK : PP_ERROR_FAILED; 553 } 554 555 void PepperGraphics2DHost::ReleaseCallback(scoped_ptr<cc::SharedBitmap> bitmap, 556 const gfx::Size& bitmap_size, 557 uint32 sync_point, 558 bool lost_resource) { 559 cached_bitmap_.reset(); 560 // Only keep around a cached bitmap if the plugin is currently drawing (has 561 // need_flush_ack_ set). 562 if (need_flush_ack_ && bound_instance_) 563 cached_bitmap_ = bitmap.Pass(); 564 cached_bitmap_size_ = bitmap_size; 565 } 566 567 bool PepperGraphics2DHost::PrepareTextureMailbox( 568 cc::TextureMailbox* mailbox, 569 scoped_ptr<cc::SingleReleaseCallback>* release_callback) { 570 if (!texture_mailbox_modified_) 571 return false; 572 // TODO(jbauman): Send image_data_ through mailbox to avoid copy. 573 gfx::Size pixel_image_size(image_data_->width(), image_data_->height()); 574 scoped_ptr<cc::SharedBitmap> shared_bitmap; 575 if (cached_bitmap_) { 576 if (cached_bitmap_size_ == pixel_image_size) 577 shared_bitmap = cached_bitmap_.Pass(); 578 else 579 cached_bitmap_.reset(); 580 } 581 if (!shared_bitmap) { 582 shared_bitmap = RenderThreadImpl::current() 583 ->shared_bitmap_manager() 584 ->AllocateSharedBitmap(pixel_image_size); 585 } 586 if (!shared_bitmap) 587 return false; 588 void* src = image_data_->Map(); 589 memcpy(shared_bitmap->pixels(), 590 src, 591 cc::SharedBitmap::CheckedSizeInBytes(pixel_image_size)); 592 image_data_->Unmap(); 593 594 *mailbox = cc::TextureMailbox(shared_bitmap->memory(), pixel_image_size); 595 *release_callback = cc::SingleReleaseCallback::Create( 596 base::Bind(&PepperGraphics2DHost::ReleaseCallback, 597 this->AsWeakPtr(), 598 base::Passed(&shared_bitmap), 599 pixel_image_size)); 600 texture_mailbox_modified_ = false; 601 return true; 602 } 603 604 void PepperGraphics2DHost::AttachedToNewLayer() { 605 texture_mailbox_modified_ = true; 606 } 607 608 int32_t PepperGraphics2DHost::Flush(PP_Resource* old_image_data) { 609 bool done_replace_contents = false; 610 bool no_update_visible = true; 611 bool is_plugin_visible = true; 612 for (size_t i = 0; i < queued_operations_.size(); i++) { 613 QueuedOperation& operation = queued_operations_[i]; 614 gfx::Rect op_rect; 615 switch (operation.type) { 616 case QueuedOperation::PAINT: 617 ExecutePaintImageData(operation.paint_image.get(), 618 operation.paint_x, 619 operation.paint_y, 620 operation.paint_src_rect, 621 &op_rect); 622 break; 623 case QueuedOperation::SCROLL: 624 ExecuteScroll(operation.scroll_clip_rect, 625 operation.scroll_dx, 626 operation.scroll_dy, 627 &op_rect); 628 break; 629 case QueuedOperation::REPLACE: 630 // Since the out parameter |old_image_data| takes ownership of the 631 // reference, if there are more than one ReplaceContents calls queued 632 // the first |old_image_data| will get overwritten and leaked. So we 633 // only supply this for the first call. 634 ExecuteReplaceContents(operation.replace_image.get(), 635 &op_rect, 636 done_replace_contents ? NULL : old_image_data); 637 done_replace_contents = true; 638 break; 639 } 640 641 // For correctness with accelerated compositing, we must issue an invalidate 642 // on the full op_rect even if it is partially or completely off-screen. 643 // However, if we issue an invalidate for a clipped-out region, WebKit will 644 // do nothing and we won't get any ViewFlushedPaint calls, leaving our 645 // callback stranded. So we still need to check whether the repainted area 646 // is visible to determine how to deal with the callback. 647 if (bound_instance_ && !op_rect.IsEmpty()) { 648 gfx::Point scroll_delta(operation.scroll_dx, operation.scroll_dy); 649 if (!ConvertToLogicalPixels(scale_, 650 &op_rect, 651 operation.type == QueuedOperation::SCROLL 652 ? &scroll_delta 653 : NULL)) { 654 // Conversion requires falling back to InvalidateRect. 655 operation.type = QueuedOperation::PAINT; 656 } 657 658 gfx::Rect clip = PP_ToGfxRect(bound_instance_->view_data().clip_rect); 659 is_plugin_visible = !clip.IsEmpty(); 660 661 // Set |no_update_visible| to false if the change overlaps the visible 662 // area. 663 if (!gfx::IntersectRects(clip, op_rect).IsEmpty()) { 664 no_update_visible = false; 665 } 666 667 // Notify the plugin of the entire change (op_rect), even if it is 668 // partially or completely off-screen. 669 if (operation.type == QueuedOperation::SCROLL) { 670 bound_instance_->ScrollRect( 671 scroll_delta.x(), scroll_delta.y(), op_rect); 672 } else { 673 if (!op_rect.IsEmpty()) 674 bound_instance_->InvalidateRect(op_rect); 675 } 676 texture_mailbox_modified_ = true; 677 } 678 } 679 queued_operations_.clear(); 680 681 if (!bound_instance_) { 682 // As promised in the API, we always schedule callback when unbound. 683 ScheduleOffscreenFlushAck(); 684 } else if (no_update_visible && is_plugin_visible && 685 bound_instance_->view_data().is_page_visible) { 686 // There's nothing visible to invalidate so just schedule the callback to 687 // execute in the next round of the message loop. 688 ScheduleOffscreenFlushAck(); 689 } else { 690 need_flush_ack_ = true; 691 } 692 693 return PP_OK_COMPLETIONPENDING; 694 } 695 696 void PepperGraphics2DHost::ExecutePaintImageData(PPB_ImageData_Impl* image, 697 int x, 698 int y, 699 const gfx::Rect& src_rect, 700 gfx::Rect* invalidated_rect) { 701 // Ensure the source image is mapped to read from it. 702 ImageDataAutoMapper auto_mapper(image); 703 if (!auto_mapper.is_valid()) 704 return; 705 706 // Portion within the source image to cut out. 707 SkIRect src_irect = {src_rect.x(), src_rect.y(), src_rect.right(), 708 src_rect.bottom()}; 709 710 // Location within the backing store to copy to. 711 *invalidated_rect = src_rect; 712 invalidated_rect->Offset(x, y); 713 SkRect dest_rect = {SkIntToScalar(invalidated_rect->x()), 714 SkIntToScalar(invalidated_rect->y()), 715 SkIntToScalar(invalidated_rect->right()), 716 SkIntToScalar(invalidated_rect->bottom())}; 717 718 if (image->format() != image_data_->format()) { 719 // Convert the image data if the format does not match. 720 ConvertImageData(image, src_irect, image_data_.get(), dest_rect); 721 } else { 722 // We're guaranteed to have a mapped canvas since we mapped it in Init(). 723 SkCanvas* backing_canvas = image_data_->GetCanvas(); 724 725 // We want to replace the contents of the bitmap rather than blend. 726 SkPaint paint; 727 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 728 backing_canvas->drawBitmapRect( 729 *image->GetMappedBitmap(), &src_irect, dest_rect, &paint); 730 } 731 } 732 733 void PepperGraphics2DHost::ExecuteScroll(const gfx::Rect& clip, 734 int dx, 735 int dy, 736 gfx::Rect* invalidated_rect) { 737 gfx::ScrollCanvas(image_data_->GetCanvas(), clip, gfx::Vector2d(dx, dy)); 738 *invalidated_rect = clip; 739 } 740 741 void PepperGraphics2DHost::ExecuteReplaceContents(PPB_ImageData_Impl* image, 742 gfx::Rect* invalidated_rect, 743 PP_Resource* old_image_data) { 744 if (image->format() != image_data_->format()) { 745 DCHECK(image->width() == image_data_->width() && 746 image->height() == image_data_->height()); 747 // Convert the image data if the format does not match. 748 SkIRect src_irect = {0, 0, image->width(), image->height()}; 749 SkRect dest_rect = {SkIntToScalar(0), SkIntToScalar(0), 750 SkIntToScalar(image_data_->width()), 751 SkIntToScalar(image_data_->height())}; 752 ConvertImageData(image, src_irect, image_data_.get(), dest_rect); 753 } else { 754 // The passed-in image may not be mapped in our process, and we need to 755 // guarantee that the current backing store is always mapped. 756 if (!image->Map()) 757 return; 758 759 if (old_image_data) 760 *old_image_data = image_data_->GetReference(); 761 image_data_ = image; 762 } 763 *invalidated_rect = 764 gfx::Rect(0, 0, image_data_->width(), image_data_->height()); 765 } 766 767 void PepperGraphics2DHost::SendFlushAck() { 768 host()->SendReply(flush_reply_context_, PpapiPluginMsg_Graphics2D_FlushAck()); 769 } 770 771 void PepperGraphics2DHost::SendOffscreenFlushAck() { 772 DCHECK(offscreen_flush_pending_); 773 774 // We must clear this flag before issuing the callback. It will be 775 // common for the plugin to issue another invalidate in response to a flush 776 // callback, and we don't want to think that a callback is already pending. 777 offscreen_flush_pending_ = false; 778 SendFlushAck(); 779 } 780 781 void PepperGraphics2DHost::ScheduleOffscreenFlushAck() { 782 offscreen_flush_pending_ = true; 783 base::MessageLoop::current()->PostDelayedTask( 784 FROM_HERE, 785 base::Bind(&PepperGraphics2DHost::SendOffscreenFlushAck, AsWeakPtr()), 786 base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs)); 787 } 788 789 bool PepperGraphics2DHost::HasPendingFlush() const { 790 return need_flush_ack_ || offscreen_flush_pending_; 791 } 792 793 // static 794 bool PepperGraphics2DHost::ConvertToLogicalPixels(float scale, 795 gfx::Rect* op_rect, 796 gfx::Point* delta) { 797 if (scale == 1.0f || scale <= 0.0f) 798 return true; 799 800 gfx::Rect original_rect = *op_rect; 801 // Take the enclosing rectangle after scaling so a rectangle scaled down then 802 // scaled back up by the inverse scale would fully contain the entire area 803 // affected by the original rectangle. 804 *op_rect = gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, scale)); 805 if (delta) { 806 gfx::Point original_delta = *delta; 807 float inverse_scale = 1.0f / scale; 808 *delta = gfx::ToFlooredPoint(gfx::ScalePoint(*delta, scale)); 809 810 gfx::Rect inverse_scaled_rect = 811 gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, inverse_scale)); 812 if (original_rect != inverse_scaled_rect) 813 return false; 814 gfx::Point inverse_scaled_point = 815 gfx::ToFlooredPoint(gfx::ScalePoint(*delta, inverse_scale)); 816 if (original_delta != inverse_scaled_point) 817 return false; 818 } 819 820 return true; 821 } 822 823 } // namespace content 824