OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/glue/plugins/pepper_device_context_2d.h" | 5 #include "webkit/glue/plugins/pepper_device_context_2d.h" |
6 | 6 |
| 7 #include <iterator> |
| 8 |
7 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/message_loop.h" |
| 11 #include "base/task.h" |
8 #include "gfx/point.h" | 12 #include "gfx/point.h" |
9 #include "gfx/rect.h" | 13 #include "gfx/rect.h" |
10 #include "skia/ext/platform_canvas.h" | 14 #include "skia/ext/platform_canvas.h" |
11 #include "third_party/ppapi/c/pp_module.h" | 15 #include "third_party/ppapi/c/pp_module.h" |
12 #include "third_party/ppapi/c/pp_rect.h" | 16 #include "third_party/ppapi/c/pp_rect.h" |
13 #include "third_party/ppapi/c/pp_resource.h" | 17 #include "third_party/ppapi/c/pp_resource.h" |
14 #include "third_party/ppapi/c/ppb_device_context_2d.h" | 18 #include "third_party/ppapi/c/ppb_device_context_2d.h" |
15 #include "third_party/skia/include/core/SkBitmap.h" | 19 #include "third_party/skia/include/core/SkBitmap.h" |
16 #include "webkit/glue/plugins/pepper_image_data.h" | 20 #include "webkit/glue/plugins/pepper_image_data.h" |
| 21 #include "webkit/glue/plugins/pepper_plugin_instance.h" |
17 #include "webkit/glue/plugins/pepper_plugin_module.h" | 22 #include "webkit/glue/plugins/pepper_plugin_module.h" |
18 #include "webkit/glue/plugins/pepper_resource_tracker.h" | 23 #include "webkit/glue/plugins/pepper_resource_tracker.h" |
19 | 24 |
20 #if defined(OS_MACOSX) | 25 #if defined(OS_MACOSX) |
21 #include "base/mac_util.h" | 26 #include "base/mac_util.h" |
22 #include "base/scoped_cftyperef.h" | 27 #include "base/scoped_cftyperef.h" |
23 #endif | 28 #endif |
24 | 29 |
25 namespace pepper { | 30 namespace pepper { |
26 | 31 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 if (!module) | 68 if (!module) |
64 return NULL; | 69 return NULL; |
65 | 70 |
66 scoped_refptr<DeviceContext2D> context(new DeviceContext2D(module)); | 71 scoped_refptr<DeviceContext2D> context(new DeviceContext2D(module)); |
67 if (!context->Init(width, height, is_always_opaque)) | 72 if (!context->Init(width, height, is_always_opaque)) |
68 return NULL; | 73 return NULL; |
69 context->AddRef(); // AddRef for the caller. | 74 context->AddRef(); // AddRef for the caller. |
70 return context->GetResource(); | 75 return context->GetResource(); |
71 } | 76 } |
72 | 77 |
| 78 bool IsDeviceContext2D(PP_Resource resource) { |
| 79 scoped_refptr<DeviceContext2D> context( |
| 80 ResourceTracker::Get()->GetAsDeviceContext2D(resource)); |
| 81 return !!context.get(); |
| 82 } |
| 83 |
73 bool Describe(PP_Resource device_context, | 84 bool Describe(PP_Resource device_context, |
74 int32_t* width, int32_t* height, bool* is_always_opaque) { | 85 int32_t* width, int32_t* height, bool* is_always_opaque) { |
75 scoped_refptr<DeviceContext2D> context( | 86 scoped_refptr<DeviceContext2D> context( |
76 ResourceTracker::Get()->GetAsDeviceContext2D(device_context)); | 87 ResourceTracker::Get()->GetAsDeviceContext2D(device_context)); |
77 if (!context.get()) | 88 if (!context.get()) |
78 return false; | 89 return false; |
79 return context->Describe(width, height, is_always_opaque); | 90 return context->Describe(width, height, is_always_opaque); |
80 } | 91 } |
81 | 92 |
82 bool PaintImageData(PP_Resource device_context, | 93 bool PaintImageData(PP_Resource device_context, |
(...skipping 30 matching lines...) Expand all Loading... |
113 void* callback_data) { | 124 void* callback_data) { |
114 scoped_refptr<DeviceContext2D> context( | 125 scoped_refptr<DeviceContext2D> context( |
115 ResourceTracker::Get()->GetAsDeviceContext2D(device_context)); | 126 ResourceTracker::Get()->GetAsDeviceContext2D(device_context)); |
116 if (!context.get()) | 127 if (!context.get()) |
117 return false; | 128 return false; |
118 return context->Flush(callback, callback_data); | 129 return context->Flush(callback, callback_data); |
119 } | 130 } |
120 | 131 |
121 const PPB_DeviceContext2D ppb_devicecontext2d = { | 132 const PPB_DeviceContext2D ppb_devicecontext2d = { |
122 &Create, | 133 &Create, |
| 134 &IsDeviceContext2D, |
123 &Describe, | 135 &Describe, |
124 &PaintImageData, | 136 &PaintImageData, |
125 &Scroll, | 137 &Scroll, |
126 &ReplaceContents, | 138 &ReplaceContents, |
127 &Flush | 139 &Flush |
128 }; | 140 }; |
129 | 141 |
130 } // namespace | 142 } // namespace |
131 | 143 |
132 struct DeviceContext2D::QueuedOperation { | 144 struct DeviceContext2D::QueuedOperation { |
(...skipping 19 matching lines...) Expand all Loading... |
152 gfx::Rect paint_src_rect; | 164 gfx::Rect paint_src_rect; |
153 | 165 |
154 // Valid when type == SCROLL. | 166 // Valid when type == SCROLL. |
155 gfx::Rect scroll_clip_rect; | 167 gfx::Rect scroll_clip_rect; |
156 int scroll_dx, scroll_dy; | 168 int scroll_dx, scroll_dy; |
157 | 169 |
158 // Valid when type == REPLACE. | 170 // Valid when type == REPLACE. |
159 scoped_refptr<ImageData> replace_image; | 171 scoped_refptr<ImageData> replace_image; |
160 }; | 172 }; |
161 | 173 |
162 DeviceContext2D::DeviceContext2D(PluginModule* module) : Resource(module) { | 174 DeviceContext2D::FlushCallbackData::FlushCallbackData( |
| 175 PPB_DeviceContext2D_FlushCallback c, |
| 176 void* d) |
| 177 : callback_(c), |
| 178 callback_data_(d) { |
| 179 } |
| 180 |
| 181 void DeviceContext2D::FlushCallbackData::Execute(PP_Resource device_context) { |
| 182 callback_(device_context, callback_data_); |
| 183 } |
| 184 |
| 185 DeviceContext2D::DeviceContext2D(PluginModule* module) |
| 186 : Resource(module), |
| 187 bound_instance_(NULL), |
| 188 flushed_any_data_(false) { |
163 } | 189 } |
164 | 190 |
165 DeviceContext2D::~DeviceContext2D() { | 191 DeviceContext2D::~DeviceContext2D() { |
166 } | 192 } |
167 | 193 |
168 // static | 194 // static |
169 const PPB_DeviceContext2D* DeviceContext2D::GetInterface() { | 195 const PPB_DeviceContext2D* DeviceContext2D::GetInterface() { |
170 return &ppb_devicecontext2d; | 196 return &ppb_devicecontext2d; |
171 } | 197 } |
172 | 198 |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 | 285 |
260 QueuedOperation operation(QueuedOperation::REPLACE); | 286 QueuedOperation operation(QueuedOperation::REPLACE); |
261 operation.replace_image = image_resource; | 287 operation.replace_image = image_resource; |
262 queued_operations_.push_back(operation); | 288 queued_operations_.push_back(operation); |
263 | 289 |
264 return true; | 290 return true; |
265 } | 291 } |
266 | 292 |
267 bool DeviceContext2D::Flush(PPB_DeviceContext2D_FlushCallback callback, | 293 bool DeviceContext2D::Flush(PPB_DeviceContext2D_FlushCallback callback, |
268 void* callback_data) { | 294 void* callback_data) { |
| 295 // TODO(brettw) check that the current thread is not the main one and |
| 296 // implement blocking flushes in this case. |
| 297 if (!callback) |
| 298 return false; |
| 299 |
| 300 if (queued_operations_.empty()) |
| 301 return true; // Nothing to do. |
| 302 |
| 303 gfx::Rect changed_rect; |
269 for (size_t i = 0; i < queued_operations_.size(); i++) { | 304 for (size_t i = 0; i < queued_operations_.size(); i++) { |
270 QueuedOperation& operation = queued_operations_[i]; | 305 QueuedOperation& operation = queued_operations_[i]; |
| 306 gfx::Rect op_rect; |
271 switch (operation.type) { | 307 switch (operation.type) { |
272 case QueuedOperation::PAINT: | 308 case QueuedOperation::PAINT: |
273 ExecutePaintImageData(operation.paint_image.get(), | 309 ExecutePaintImageData(operation.paint_image.get(), |
274 operation.paint_x, operation.paint_y, | 310 operation.paint_x, operation.paint_y, |
275 operation.paint_src_rect); | 311 operation.paint_src_rect, |
| 312 &op_rect); |
276 break; | 313 break; |
277 case QueuedOperation::SCROLL: | 314 case QueuedOperation::SCROLL: |
278 ExecuteScroll(operation.scroll_clip_rect, | 315 ExecuteScroll(operation.scroll_clip_rect, |
279 operation.scroll_dx, operation.scroll_dy); | 316 operation.scroll_dx, operation.scroll_dy, |
| 317 &op_rect); |
280 break; | 318 break; |
281 case QueuedOperation::REPLACE: | 319 case QueuedOperation::REPLACE: |
282 ExecuteReplaceContents(operation.replace_image.get()); | 320 ExecuteReplaceContents(operation.replace_image.get(), &op_rect); |
283 break; | 321 break; |
284 } | 322 } |
| 323 changed_rect = changed_rect.Union(op_rect); |
285 } | 324 } |
286 queued_operations_.clear(); | 325 queued_operations_.clear(); |
287 // TODO(brettw) implement invalidate and callbacks! | 326 flushed_any_data_ = true; |
288 | 327 |
289 // Cause the updated part of the screen to be repainted. This will happen | 328 // We need the rect to be in terms of the current clip rect of the plugin |
290 // asynchronously. | 329 // since that's what will actually be painted. If we issue an invalidate |
291 /* | 330 // for a clipped-out region, WebKit will do nothing and we won't get any |
292 gfx::Rect dest_gfx_rect(src_rect->left, src_rect->top, | 331 // ViewInitiatedPaint/ViewFlushedPaint calls, leaving our callback stranded. |
293 src_rect->right - src_rect->left, | 332 gfx::Rect visible_changed_rect; |
294 src_rect->bottom - src_rect->top); | 333 if (bound_instance_ && !changed_rect.IsEmpty()) |
| 334 visible_changed_rect = bound_instance_->clip().Intersect(changed_rect); |
295 | 335 |
296 plugin_delegate_->instance()->webplugin()->InvalidateRect(dest_gfx_rect); | 336 if (bound_instance_ && !visible_changed_rect.IsEmpty()) { |
297 | 337 unpainted_flush_callbacks_.push_back(FlushCallbackData(callback, |
298 // Save the callback to execute later. See |unpainted_flush_callbacks_| in | 338 callback_data)); |
299 // the header file. | 339 bound_instance_->InvalidateRect(visible_changed_rect); |
300 if (callback) { | 340 } else { |
301 unpainted_flush_callbacks_.push_back( | 341 // There's nothing visible to invalidate so just schedule the callback to |
302 FlushCallbackData(callback, id, context, user_data)); | 342 // execute in the next round of the message loop. |
| 343 ScheduleOffscreenCallback( |
| 344 FlushCallbackData(callback, callback_data)); |
303 } | 345 } |
304 */ | |
305 return true; | 346 return true; |
306 } | 347 } |
307 | 348 |
308 bool DeviceContext2D::ReadImageData(PP_Resource image, int32_t x, int32_t y) { | 349 bool DeviceContext2D::ReadImageData(PP_Resource image, int32_t x, int32_t y) { |
309 // Get and validate the image object to paint into. | 350 // Get and validate the image object to paint into. |
310 scoped_refptr<ImageData> image_resource( | 351 scoped_refptr<ImageData> image_resource( |
311 ResourceTracker::Get()->GetAsImageData(image)); | 352 ResourceTracker::Get()->GetAsImageData(image)); |
312 if (!image_resource.get()) | 353 if (!image_resource.get()) |
313 return false; | 354 return false; |
314 if (image_resource->format() != PP_IMAGEDATAFORMAT_BGRA_PREMUL) | 355 if (image_resource->format() != PP_IMAGEDATAFORMAT_BGRA_PREMUL) |
(...skipping 23 matching lines...) Expand all Loading... |
338 SkIntToScalar(image_resource->height()) }; | 379 SkIntToScalar(image_resource->height()) }; |
339 | 380 |
340 // We want to replace the contents of the bitmap rather than blend. | 381 // We want to replace the contents of the bitmap rather than blend. |
341 SkPaint paint; | 382 SkPaint paint; |
342 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | 383 paint.setXfermodeMode(SkXfermode::kSrc_Mode); |
343 dest_canvas->drawBitmapRect(*image_data_->GetMappedBitmap(), | 384 dest_canvas->drawBitmapRect(*image_data_->GetMappedBitmap(), |
344 &src_irect, dest_rect, &paint); | 385 &src_irect, dest_rect, &paint); |
345 return true; | 386 return true; |
346 } | 387 } |
347 | 388 |
| 389 bool DeviceContext2D::BindToInstance(PluginInstance* new_instance) { |
| 390 if (bound_instance_ == new_instance) |
| 391 return true; // Rebinding the same device, nothing to do. |
| 392 if (bound_instance_ && new_instance) |
| 393 return false; // Can't change a bound device. |
| 394 |
| 395 if (!new_instance) { |
| 396 // When the device is detached, we'll not get any more paint callbacks so |
| 397 // we need to clear the list, but we still want to issue any pending |
| 398 // callbacks to the plugin. |
| 399 for (size_t i = 0; i < unpainted_flush_callbacks_.size(); i++) |
| 400 ScheduleOffscreenCallback(unpainted_flush_callbacks_[i]); |
| 401 for (size_t i = 0; i < painted_flush_callbacks_.size(); i++) |
| 402 ScheduleOffscreenCallback(painted_flush_callbacks_[i]); |
| 403 unpainted_flush_callbacks_.clear(); |
| 404 painted_flush_callbacks_.clear(); |
| 405 } else if (flushed_any_data_) { |
| 406 // Only schedule a paint if this backing store has had any data flushed to |
| 407 // it. This is an optimization. A "normal" plugin will first allocated a |
| 408 // backing store, bind it, and then execute their normal painting and |
| 409 // update loop. If binding a device always invalidated, it would mean we |
| 410 // would get one paint for the bind, and one for the first time the plugin |
| 411 // actually painted something. By not bothering to schedule an invalidate |
| 412 // when an empty device is initially bound, we can save an extra paint for |
| 413 // many plugins during the critical page initialization phase. |
| 414 new_instance->InvalidateRect(gfx::Rect()); |
| 415 } |
| 416 |
| 417 bound_instance_ = new_instance; |
| 418 return true; |
| 419 } |
| 420 |
348 void DeviceContext2D::Paint(WebKit::WebCanvas* canvas, | 421 void DeviceContext2D::Paint(WebKit::WebCanvas* canvas, |
349 const gfx::Rect& plugin_rect, | 422 const gfx::Rect& plugin_rect, |
350 const gfx::Rect& paint_rect) { | 423 const gfx::Rect& paint_rect) { |
351 // We're guaranteed to have a mapped canvas since we mapped it in Init(). | 424 // We're guaranteed to have a mapped canvas since we mapped it in Init(). |
352 const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap(); | 425 const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap(); |
353 | 426 |
354 #if defined(OS_MACOSX) | 427 #if defined(OS_MACOSX) |
355 SkAutoLockPixels lock(backing_bitmap); | 428 SkAutoLockPixels lock(backing_bitmap); |
356 | 429 |
357 scoped_cftyperef<CGDataProviderRef> data_provider( | 430 scoped_cftyperef<CGDataProviderRef> data_provider( |
(...skipping 24 matching lines...) Expand all Loading... |
382 CGContextDrawImage(canvas, bounds, image); | 455 CGContextDrawImage(canvas, bounds, image); |
383 CGContextRestoreGState(canvas); | 456 CGContextRestoreGState(canvas); |
384 #else | 457 #else |
385 gfx::Point origin(plugin_rect.origin().x(), plugin_rect.origin().y()); | 458 gfx::Point origin(plugin_rect.origin().x(), plugin_rect.origin().y()); |
386 canvas->drawBitmap(backing_bitmap, | 459 canvas->drawBitmap(backing_bitmap, |
387 SkIntToScalar(plugin_rect.origin().x()), | 460 SkIntToScalar(plugin_rect.origin().x()), |
388 SkIntToScalar(plugin_rect.origin().y())); | 461 SkIntToScalar(plugin_rect.origin().y())); |
389 #endif | 462 #endif |
390 } | 463 } |
391 | 464 |
| 465 void DeviceContext2D::ViewInitiatedPaint() { |
| 466 // Move all "unpainted" callbacks to the painted state. See |
| 467 // |unpainted_flush_callbacks_| in the header for more. |
| 468 std::copy(unpainted_flush_callbacks_.begin(), |
| 469 unpainted_flush_callbacks_.end(), |
| 470 std::back_inserter(painted_flush_callbacks_)); |
| 471 unpainted_flush_callbacks_.clear(); |
| 472 } |
| 473 |
| 474 void DeviceContext2D::ViewFlushedPaint() { |
| 475 // Notify all "painted" callbacks. See |unpainted_flush_callbacks_| in the |
| 476 // header for more. |
| 477 for (size_t i = 0; i < painted_flush_callbacks_.size(); i++) |
| 478 painted_flush_callbacks_[i].Execute(GetResource()); |
| 479 painted_flush_callbacks_.clear(); |
| 480 } |
| 481 |
392 void DeviceContext2D::ExecutePaintImageData(ImageData* image, | 482 void DeviceContext2D::ExecutePaintImageData(ImageData* image, |
393 int x, int y, | 483 int x, int y, |
394 const gfx::Rect& src_rect) { | 484 const gfx::Rect& src_rect, |
| 485 gfx::Rect* invalidated_rect) { |
| 486 // Ensure the source image is mapped to read from it. |
| 487 ImageDataAutoMapper auto_mapper(image); |
| 488 if (!auto_mapper.is_valid()) |
| 489 return; |
| 490 |
395 // Portion within the source image to cut out. | 491 // Portion within the source image to cut out. |
396 SkIRect src_irect = { src_rect.x(), src_rect.y(), | 492 SkIRect src_irect = { src_rect.x(), src_rect.y(), |
397 src_rect.right(), src_rect.bottom() }; | 493 src_rect.right(), src_rect.bottom() }; |
398 | 494 |
399 // Location within the backing store to copy to. | 495 // Location within the backing store to copy to. |
400 SkRect dest_rect = { SkIntToScalar(x + src_rect.x()), | 496 *invalidated_rect = src_rect; |
401 SkIntToScalar(y + src_rect.y()), | 497 invalidated_rect->Offset(x, y); |
402 SkIntToScalar(x + src_rect.right()), | 498 SkRect dest_rect = { SkIntToScalar(invalidated_rect->x()), |
403 SkIntToScalar(y + src_rect.bottom()) }; | 499 SkIntToScalar(invalidated_rect->y()), |
| 500 SkIntToScalar(invalidated_rect->right()), |
| 501 SkIntToScalar(invalidated_rect->bottom()) }; |
404 | 502 |
405 // We're guaranteed to have a mapped canvas since we mapped it in Init(). | 503 // We're guaranteed to have a mapped canvas since we mapped it in Init(). |
406 skia::PlatformCanvas* backing_canvas = image_data_->mapped_canvas(); | 504 skia::PlatformCanvas* backing_canvas = image_data_->mapped_canvas(); |
407 | 505 |
408 // Ensure the source image is mapped to read from it. | |
409 ImageDataAutoMapper auto_mapper(image); | |
410 if (!auto_mapper.is_valid()) | |
411 return; | |
412 | |
413 // We want to replace the contents of the bitmap rather than blend. | 506 // We want to replace the contents of the bitmap rather than blend. |
414 SkPaint paint; | 507 SkPaint paint; |
415 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | 508 paint.setXfermodeMode(SkXfermode::kSrc_Mode); |
416 backing_canvas->drawBitmapRect(*image->GetMappedBitmap(), | 509 backing_canvas->drawBitmapRect(*image->GetMappedBitmap(), |
417 &src_irect, dest_rect, &paint); | 510 &src_irect, dest_rect, &paint); |
418 } | 511 } |
419 | 512 |
420 void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy) { | 513 void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy, |
| 514 gfx::Rect* invalidated_rect) { |
421 // FIXME(brettw) | 515 // FIXME(brettw) |
422 } | 516 } |
423 | 517 |
424 void DeviceContext2D::ExecuteReplaceContents(ImageData* image) { | 518 void DeviceContext2D::ExecuteReplaceContents(ImageData* image, |
| 519 gfx::Rect* invalidated_rect) { |
425 image_data_->Swap(image); | 520 image_data_->Swap(image); |
| 521 *invalidated_rect = gfx::Rect(0, 0, |
| 522 image_data_->width(), image_data_->height()); |
| 523 } |
| 524 |
| 525 void DeviceContext2D::ScheduleOffscreenCallback( |
| 526 const FlushCallbackData& callback) { |
| 527 MessageLoop::current()->PostTask( |
| 528 FROM_HERE, |
| 529 NewRunnableMethod(this, |
| 530 &DeviceContext2D::ExecuteOffscreenCallback, |
| 531 callback)); |
| 532 } |
| 533 |
| 534 void DeviceContext2D::ExecuteOffscreenCallback(FlushCallbackData data) { |
| 535 data.Execute(GetResource()); |
426 } | 536 } |
427 | 537 |
428 } // namespace pepper | 538 } // namespace pepper |
OLD | NEW |