Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / renderer / pepper / pepper_graphics_2d_host.cc
blobbdb055274ceb9a770acd815e5dcfe70972daedb8
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/pepper/pepper_graphics_2d_host.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/trace_event/trace_event.h"
13 #include "cc/resources/shared_bitmap.h"
14 #include "cc/resources/texture_mailbox.h"
15 #include "content/child/child_shared_bitmap_manager.h"
16 #include "content/public/renderer/render_thread.h"
17 #include "content/public/renderer/renderer_ppapi_host.h"
18 #include "content/renderer/pepper/gfx_conversion.h"
19 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
20 #include "content/renderer/pepper/plugin_instance_throttler_impl.h"
21 #include "content/renderer/pepper/ppb_image_data_impl.h"
22 #include "content/renderer/render_thread_impl.h"
23 #include "ppapi/c/pp_bool.h"
24 #include "ppapi/c/pp_errors.h"
25 #include "ppapi/c/pp_rect.h"
26 #include "ppapi/c/pp_resource.h"
27 #include "ppapi/host/dispatch_host_message.h"
28 #include "ppapi/host/host_message_context.h"
29 #include "ppapi/host/ppapi_host.h"
30 #include "ppapi/proxy/ppapi_messages.h"
31 #include "ppapi/shared_impl/ppb_view_shared.h"
32 #include "ppapi/thunk/enter.h"
33 #include "skia/ext/platform_canvas.h"
34 #include "third_party/skia/include/core/SkBitmap.h"
35 #include "ui/gfx/blit.h"
36 #include "ui/gfx/geometry/point_conversions.h"
37 #include "ui/gfx/geometry/rect.h"
38 #include "ui/gfx/geometry/rect_conversions.h"
39 #include "ui/gfx/geometry/size_conversions.h"
40 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
41 #include "ui/gfx/skia_util.h"
43 #if defined(OS_MACOSX)
44 #include "base/mac/scoped_cftyperef.h"
45 #endif
47 using ppapi::thunk::EnterResourceNoLock;
48 using ppapi::thunk::PPB_ImageData_API;
50 namespace content {
52 namespace {
54 const int64 kOffscreenCallbackDelayMs = 1000 / 30; // 30 fps
56 // Converts a rect inside an image of the given dimensions. The rect may be
57 // NULL to indicate it should be the entire image. If the rect is outside of
58 // the image, this will do nothing and return false.
59 bool ValidateAndConvertRect(const PP_Rect* rect,
60 int image_width,
61 int image_height,
62 gfx::Rect* dest) {
63 if (!rect) {
64 // Use the entire image area.
65 *dest = gfx::Rect(0, 0, image_width, image_height);
66 } else {
67 // Validate the passed-in area.
68 if (rect->point.x < 0 || rect->point.y < 0 || rect->size.width <= 0 ||
69 rect->size.height <= 0)
70 return false;
72 // Check the max bounds, being careful of overflow.
73 if (static_cast<int64>(rect->point.x) +
74 static_cast<int64>(rect->size.width) >
75 static_cast<int64>(image_width))
76 return false;
77 if (static_cast<int64>(rect->point.y) +
78 static_cast<int64>(rect->size.height) >
79 static_cast<int64>(image_height))
80 return false;
82 *dest = gfx::Rect(
83 rect->point.x, rect->point.y, rect->size.width, rect->size.height);
85 return true;
88 // Converts BGRA <-> RGBA.
89 void ConvertBetweenBGRAandRGBA(const uint32_t* input,
90 int pixel_length,
91 uint32_t* output) {
92 for (int i = 0; i < pixel_length; i++) {
93 const unsigned char* pixel_in =
94 reinterpret_cast<const unsigned char*>(&input[i]);
95 unsigned char* pixel_out = reinterpret_cast<unsigned char*>(&output[i]);
96 pixel_out[0] = pixel_in[2];
97 pixel_out[1] = pixel_in[1];
98 pixel_out[2] = pixel_in[0];
99 pixel_out[3] = pixel_in[3];
103 // Converts ImageData from PP_IMAGEDATAFORMAT_BGRA_PREMUL to
104 // PP_IMAGEDATAFORMAT_RGBA_PREMUL, or reverse. It's assumed that the
105 // destination image is always mapped (so will have non-NULL data).
106 void ConvertImageData(PPB_ImageData_Impl* src_image,
107 const SkIRect& src_rect,
108 PPB_ImageData_Impl* dest_image,
109 const SkRect& dest_rect) {
110 ImageDataAutoMapper auto_mapper(src_image);
112 DCHECK(src_image->format() != dest_image->format());
113 DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(src_image->format()));
114 DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(dest_image->format()));
116 const SkBitmap* src_bitmap = src_image->GetMappedBitmap();
117 const SkBitmap* dest_bitmap = dest_image->GetMappedBitmap();
118 if (src_rect.width() == src_image->width() &&
119 dest_rect.width() == dest_image->width()) {
120 // Fast path if the full line needs to be converted.
121 ConvertBetweenBGRAandRGBA(
122 src_bitmap->getAddr32(static_cast<int>(src_rect.fLeft),
123 static_cast<int>(src_rect.fTop)),
124 src_rect.width() * src_rect.height(),
125 dest_bitmap->getAddr32(static_cast<int>(dest_rect.fLeft),
126 static_cast<int>(dest_rect.fTop)));
127 } else {
128 // Slow path where we convert line by line.
129 for (int y = 0; y < src_rect.height(); y++) {
130 ConvertBetweenBGRAandRGBA(
131 src_bitmap->getAddr32(static_cast<int>(src_rect.fLeft),
132 static_cast<int>(src_rect.fTop + y)),
133 src_rect.width(),
134 dest_bitmap->getAddr32(static_cast<int>(dest_rect.fLeft),
135 static_cast<int>(dest_rect.fTop + y)));
140 } // namespace
142 struct PepperGraphics2DHost::QueuedOperation {
143 enum Type { PAINT, SCROLL, REPLACE, };
145 QueuedOperation(Type t)
146 : type(t), paint_x(0), paint_y(0), scroll_dx(0), scroll_dy(0) {}
148 Type type;
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;
155 // Valid when type == SCROLL.
156 gfx::Rect scroll_clip_rect;
157 int scroll_dx, scroll_dy;
159 // Valid when type == REPLACE.
160 scoped_refptr<PPB_ImageData_Impl> replace_image;
163 // static
164 PepperGraphics2DHost* PepperGraphics2DHost::Create(
165 RendererPpapiHost* host,
166 PP_Instance instance,
167 PP_Resource resource,
168 const PP_Size& size,
169 PP_Bool is_always_opaque,
170 scoped_refptr<PPB_ImageData_Impl> backing_store) {
171 PepperGraphics2DHost* resource_host =
172 new PepperGraphics2DHost(host, instance, resource);
173 if (!resource_host->Init(size.width,
174 size.height,
175 PP_ToBool(is_always_opaque),
176 backing_store)) {
177 delete resource_host;
178 return NULL;
180 return resource_host;
183 PepperGraphics2DHost::PepperGraphics2DHost(RendererPpapiHost* host,
184 PP_Instance instance,
185 PP_Resource resource)
186 : ResourceHost(host->GetPpapiHost(), instance, resource),
187 renderer_ppapi_host_(host),
188 bound_instance_(NULL),
189 need_flush_ack_(false),
190 offscreen_flush_pending_(false),
191 is_always_opaque_(false),
192 scale_(1.0f),
193 is_running_in_process_(host->IsRunningInProcess()),
194 texture_mailbox_modified_(true) {}
196 PepperGraphics2DHost::~PepperGraphics2DHost() {
197 // Unbind from the instance when destroyed if we're still bound.
198 if (bound_instance_)
199 bound_instance_->BindGraphics(bound_instance_->pp_instance(), 0);
202 bool PepperGraphics2DHost::Init(
203 int width,
204 int height,
205 bool is_always_opaque,
206 scoped_refptr<PPB_ImageData_Impl> backing_store) {
207 // The underlying PPB_ImageData_Impl will validate the dimensions.
208 image_data_ = backing_store;
209 if (!image_data_->Init(PPB_ImageData_Impl::GetNativeImageDataFormat(),
210 width,
211 height,
212 true) ||
213 !image_data_->Map()) {
214 image_data_ = NULL;
215 return false;
217 is_always_opaque_ = is_always_opaque;
218 scale_ = 1.0f;
219 return true;
222 int32_t PepperGraphics2DHost::OnResourceMessageReceived(
223 const IPC::Message& msg,
224 ppapi::host::HostMessageContext* context) {
225 PPAPI_BEGIN_MESSAGE_MAP(PepperGraphics2DHost, msg)
226 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_PaintImageData,
227 OnHostMsgPaintImageData)
228 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_Scroll,
229 OnHostMsgScroll)
230 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_ReplaceContents,
231 OnHostMsgReplaceContents)
232 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_Flush,
233 OnHostMsgFlush)
234 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_SetScale,
235 OnHostMsgSetScale)
236 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Graphics2D_ReadImageData,
237 OnHostMsgReadImageData)
238 PPAPI_END_MESSAGE_MAP()
239 return PP_ERROR_FAILED;
242 bool PepperGraphics2DHost::IsGraphics2DHost() { return true; }
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(image_resource->format()))
253 return false; // Must be in the right format.
255 // Validate the bitmap position.
256 int x = top_left->x;
257 if (x < 0 ||
258 static_cast<int64>(x) + static_cast<int64>(image_resource->width()) >
259 image_data_->width())
260 return false;
261 int y = top_left->y;
262 if (y < 0 ||
263 static_cast<int64>(y) + static_cast<int64>(image_resource->height()) >
264 image_data_->height())
265 return false;
267 ImageDataAutoMapper auto_mapper(image_resource);
268 if (!auto_mapper.is_valid())
269 return false;
271 SkIRect src_irect = {x, y, x + image_resource->width(),
272 y + image_resource->height()};
273 SkRect dest_rect = {SkIntToScalar(0), SkIntToScalar(0),
274 SkIntToScalar(image_resource->width()),
275 SkIntToScalar(image_resource->height())};
277 if (image_resource->format() != image_data_->format()) {
278 // Convert the image data if the format does not match.
279 ConvertImageData(image_data_.get(), src_irect, image_resource, dest_rect);
280 } else {
281 SkCanvas* dest_canvas = image_resource->GetCanvas();
283 // We want to replace the contents of the bitmap rather than blend.
284 SkPaint paint;
285 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
286 dest_canvas->drawBitmapRect(
287 *image_data_->GetMappedBitmap(), src_irect, dest_rect, &paint);
289 return true;
292 bool PepperGraphics2DHost::BindToInstance(
293 PepperPluginInstanceImpl* new_instance) {
294 if (new_instance && new_instance->pp_instance() != pp_instance())
295 return false; // Can't bind other instance's contexts.
296 if (bound_instance_ == new_instance)
297 return true; // Rebinding the same device, nothing to do.
298 if (bound_instance_ && new_instance)
299 return false; // Can't change a bound device.
301 if (!new_instance) {
302 // When the device is detached, we'll not get any more paint callbacks so
303 // we need to clear the list, but we still want to issue any pending
304 // callbacks to the plugin.
305 if (need_flush_ack_)
306 ScheduleOffscreenFlushAck();
307 } else {
308 // Devices being replaced, redraw the plugin.
309 new_instance->InvalidateRect(gfx::Rect());
312 cached_bitmap_.reset();
313 texture_mailbox_modified_ = true;
315 bound_instance_ = new_instance;
316 return true;
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(blink::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();
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 =
336 gfx::ToFlooredSize(gfx::ScaleSize(pixel_image_size, scale_));
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);
354 SkPaint paint;
355 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
356 paint.setColor(SK_ColorWHITE);
357 canvas->drawRect(sk_invalidate_rect, paint);
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, kN32_SkColorType);
365 else
366 image = backing_bitmap;
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);
375 SkPoint origin;
376 origin.set(SkIntToScalar(plugin_rect.x()), SkIntToScalar(plugin_rect.y()));
378 SkPoint pixel_origin = origin;
380 if (scale_ != 1.0f && scale_ > 0.0f) {
381 canvas->scale(scale_, scale_);
382 pixel_origin.set(pixel_origin.x() * (1.0f / scale_),
383 pixel_origin.y() * (1.0f / scale_));
385 canvas->drawBitmap(image, pixel_origin.x(), pixel_origin.y(), &paint);
388 void PepperGraphics2DHost::ViewInitiatedPaint() {
389 TRACE_EVENT0("pepper", "PepperGraphics2DHost::ViewInitiatedPaint");
390 if (need_flush_ack_) {
391 SendFlushAck();
392 need_flush_ack_ = false;
396 void PepperGraphics2DHost::SetScale(float scale) { scale_ = scale; }
398 float PepperGraphics2DHost::GetScale() const { return scale_; }
400 bool PepperGraphics2DHost::IsAlwaysOpaque() const { return is_always_opaque_; }
402 PPB_ImageData_Impl* PepperGraphics2DHost::ImageData() {
403 return image_data_.get();
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());
412 void PepperGraphics2DHost::ClearCache() {
413 cached_bitmap_.reset();
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());
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;
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;
452 queued_operations_.push_back(operation);
453 return PP_OK;
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;
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;
476 operation.scroll_dx = dx;
477 operation.scroll_dy = dy;
479 queued_operations_.push_back(operation);
480 return PP_OK;
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());
493 if (!PPB_ImageData_Impl::IsImageDataFormatSupported(image_resource->format()))
494 return PP_ERROR_BADARGUMENT;
496 if (image_resource->width() != image_data_->width() ||
497 image_resource->height() != image_data_->height())
498 return PP_ERROR_BADARGUMENT;
500 QueuedOperation operation(QueuedOperation::REPLACE);
501 operation.replace_image = image_resource;
502 queued_operations_.push_back(operation);
503 return PP_OK;
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;
513 if (bound_instance_)
514 bound_instance_->AddLatencyInfo(latency_info);
516 PP_Resource old_image_data = 0;
517 flush_reply_context_ = context->MakeReplyMessageContext();
518 if (is_running_in_process_)
519 return Flush(NULL);
521 // Reuse image data when running out of process.
522 int32_t result = Flush(&old_image_data);
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));
534 return result;
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;
544 return PP_ERROR_BADARGUMENT;
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;
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;
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();
581 if (!shared_bitmap) {
582 shared_bitmap = RenderThreadImpl::current()
583 ->shared_bitmap_manager()
584 ->AllocateSharedBitmap(pixel_image_size);
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();
594 *mailbox = cc::TextureMailbox(shared_bitmap.get(), 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;
604 void PepperGraphics2DHost::AttachedToNewLayer() {
605 texture_mailbox_modified_ = true;
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;
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;
658 gfx::Rect clip = PP_ToGfxRect(bound_instance_->view_data().clip_rect);
659 is_plugin_visible = !clip.IsEmpty();
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;
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);
676 texture_mailbox_modified_ = true;
679 queued_operations_.clear();
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;
693 if (bound_instance_ && bound_instance_->throttler() &&
694 bound_instance_->throttler()->needs_representative_keyframe()) {
695 bound_instance_->throttler()->OnImageFlush(image_data_->GetMappedBitmap());
698 return PP_OK_COMPLETIONPENDING;
701 void PepperGraphics2DHost::ExecutePaintImageData(PPB_ImageData_Impl* image,
702 int x,
703 int y,
704 const gfx::Rect& src_rect,
705 gfx::Rect* invalidated_rect) {
706 // Ensure the source image is mapped to read from it.
707 ImageDataAutoMapper auto_mapper(image);
708 if (!auto_mapper.is_valid())
709 return;
711 // Portion within the source image to cut out.
712 SkIRect src_irect = {src_rect.x(), src_rect.y(), src_rect.right(),
713 src_rect.bottom()};
715 // Location within the backing store to copy to.
716 *invalidated_rect = src_rect;
717 invalidated_rect->Offset(x, y);
718 SkRect dest_rect = {SkIntToScalar(invalidated_rect->x()),
719 SkIntToScalar(invalidated_rect->y()),
720 SkIntToScalar(invalidated_rect->right()),
721 SkIntToScalar(invalidated_rect->bottom())};
723 if (image->format() != image_data_->format()) {
724 // Convert the image data if the format does not match.
725 ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
726 } else {
727 // We're guaranteed to have a mapped canvas since we mapped it in Init().
728 SkCanvas* backing_canvas = image_data_->GetCanvas();
730 // We want to replace the contents of the bitmap rather than blend.
731 SkPaint paint;
732 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
733 backing_canvas->drawBitmapRect(
734 *image->GetMappedBitmap(), src_irect, dest_rect, &paint);
738 void PepperGraphics2DHost::ExecuteScroll(const gfx::Rect& clip,
739 int dx,
740 int dy,
741 gfx::Rect* invalidated_rect) {
742 gfx::ScrollCanvas(image_data_->GetCanvas(), clip, gfx::Vector2d(dx, dy));
743 *invalidated_rect = clip;
746 void PepperGraphics2DHost::ExecuteReplaceContents(PPB_ImageData_Impl* image,
747 gfx::Rect* invalidated_rect,
748 PP_Resource* old_image_data) {
749 if (image->format() != image_data_->format()) {
750 DCHECK(image->width() == image_data_->width() &&
751 image->height() == image_data_->height());
752 // Convert the image data if the format does not match.
753 SkIRect src_irect = {0, 0, image->width(), image->height()};
754 SkRect dest_rect = {SkIntToScalar(0), SkIntToScalar(0),
755 SkIntToScalar(image_data_->width()),
756 SkIntToScalar(image_data_->height())};
757 ConvertImageData(image, src_irect, image_data_.get(), dest_rect);
758 } else {
759 // The passed-in image may not be mapped in our process, and we need to
760 // guarantee that the current backing store is always mapped.
761 if (!image->Map())
762 return;
764 if (old_image_data)
765 *old_image_data = image_data_->GetReference();
766 image_data_ = image;
768 *invalidated_rect =
769 gfx::Rect(0, 0, image_data_->width(), image_data_->height());
772 void PepperGraphics2DHost::SendFlushAck() {
773 host()->SendReply(flush_reply_context_, PpapiPluginMsg_Graphics2D_FlushAck());
776 void PepperGraphics2DHost::SendOffscreenFlushAck() {
777 DCHECK(offscreen_flush_pending_);
779 // We must clear this flag before issuing the callback. It will be
780 // common for the plugin to issue another invalidate in response to a flush
781 // callback, and we don't want to think that a callback is already pending.
782 offscreen_flush_pending_ = false;
783 SendFlushAck();
786 void PepperGraphics2DHost::ScheduleOffscreenFlushAck() {
787 offscreen_flush_pending_ = true;
788 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
789 FROM_HERE,
790 base::Bind(&PepperGraphics2DHost::SendOffscreenFlushAck, AsWeakPtr()),
791 base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs));
794 bool PepperGraphics2DHost::HasPendingFlush() const {
795 return need_flush_ack_ || offscreen_flush_pending_;
798 // static
799 bool PepperGraphics2DHost::ConvertToLogicalPixels(float scale,
800 gfx::Rect* op_rect,
801 gfx::Point* delta) {
802 if (scale == 1.0f || scale <= 0.0f)
803 return true;
805 gfx::Rect original_rect = *op_rect;
806 // Take the enclosing rectangle after scaling so a rectangle scaled down then
807 // scaled back up by the inverse scale would fully contain the entire area
808 // affected by the original rectangle.
809 *op_rect = gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, scale));
810 if (delta) {
811 gfx::Point original_delta = *delta;
812 float inverse_scale = 1.0f / scale;
813 *delta = gfx::ToFlooredPoint(gfx::ScalePoint(*delta, scale));
815 gfx::Rect inverse_scaled_rect =
816 gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, inverse_scale));
817 if (original_rect != inverse_scaled_rect)
818 return false;
819 gfx::Point inverse_scaled_point =
820 gfx::ToFlooredPoint(gfx::ScalePoint(*delta, inverse_scale));
821 if (original_delta != inverse_scaled_point)
822 return false;
825 return true;
828 } // namespace content