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> | 7 #include <iterator> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
(...skipping 167 matching lines...) Loading... |
178 callback_data_(d) { | 178 callback_data_(d) { |
179 } | 179 } |
180 | 180 |
181 void DeviceContext2D::FlushCallbackData::Execute(PP_Resource device_context) { | 181 void DeviceContext2D::FlushCallbackData::Execute(PP_Resource device_context) { |
182 callback_(device_context, callback_data_); | 182 callback_(device_context, callback_data_); |
183 } | 183 } |
184 | 184 |
185 DeviceContext2D::DeviceContext2D(PluginModule* module) | 185 DeviceContext2D::DeviceContext2D(PluginModule* module) |
186 : Resource(module), | 186 : Resource(module), |
187 bound_instance_(NULL), | 187 bound_instance_(NULL), |
188 flushed_any_data_(false) { | 188 flushed_any_data_(false), |
| 189 offscreen_flush_pending_(false) { |
189 } | 190 } |
190 | 191 |
191 DeviceContext2D::~DeviceContext2D() { | 192 DeviceContext2D::~DeviceContext2D() { |
192 } | 193 } |
193 | 194 |
194 // static | 195 // static |
195 const PPB_DeviceContext2D* DeviceContext2D::GetInterface() { | 196 const PPB_DeviceContext2D* DeviceContext2D::GetInterface() { |
196 return &ppb_devicecontext2d; | 197 return &ppb_devicecontext2d; |
197 } | 198 } |
198 | 199 |
(...skipping 86 matching lines...) Loading... |
285 | 286 |
286 QueuedOperation operation(QueuedOperation::REPLACE); | 287 QueuedOperation operation(QueuedOperation::REPLACE); |
287 operation.replace_image = image_resource; | 288 operation.replace_image = image_resource; |
288 queued_operations_.push_back(operation); | 289 queued_operations_.push_back(operation); |
289 | 290 |
290 return true; | 291 return true; |
291 } | 292 } |
292 | 293 |
293 bool DeviceContext2D::Flush(PPB_DeviceContext2D_FlushCallback callback, | 294 bool DeviceContext2D::Flush(PPB_DeviceContext2D_FlushCallback callback, |
294 void* callback_data) { | 295 void* callback_data) { |
| 296 // Don't allow more than one pending flush at a time. |
| 297 if (HasPendingFlush()) |
| 298 return false; |
| 299 |
295 // TODO(brettw) check that the current thread is not the main one and | 300 // TODO(brettw) check that the current thread is not the main one and |
296 // implement blocking flushes in this case. | 301 // implement blocking flushes in this case. |
297 if (!callback) | 302 if (!callback) |
298 return false; | 303 return false; |
299 | 304 |
300 if (queued_operations_.empty()) | |
301 return true; // Nothing to do. | |
302 | |
303 gfx::Rect changed_rect; | 305 gfx::Rect changed_rect; |
304 for (size_t i = 0; i < queued_operations_.size(); i++) { | 306 for (size_t i = 0; i < queued_operations_.size(); i++) { |
305 QueuedOperation& operation = queued_operations_[i]; | 307 QueuedOperation& operation = queued_operations_[i]; |
306 gfx::Rect op_rect; | 308 gfx::Rect op_rect; |
307 switch (operation.type) { | 309 switch (operation.type) { |
308 case QueuedOperation::PAINT: | 310 case QueuedOperation::PAINT: |
309 ExecutePaintImageData(operation.paint_image.get(), | 311 ExecutePaintImageData(operation.paint_image.get(), |
310 operation.paint_x, operation.paint_y, | 312 operation.paint_x, operation.paint_y, |
311 operation.paint_src_rect, | 313 operation.paint_src_rect, |
312 &op_rect); | 314 &op_rect); |
(...skipping 14 matching lines...) Loading... |
327 | 329 |
328 // We need the rect to be in terms of the current clip rect of the plugin | 330 // We need the rect to be in terms of the current clip rect of the plugin |
329 // since that's what will actually be painted. If we issue an invalidate | 331 // since that's what will actually be painted. If we issue an invalidate |
330 // for a clipped-out region, WebKit will do nothing and we won't get any | 332 // for a clipped-out region, WebKit will do nothing and we won't get any |
331 // ViewInitiatedPaint/ViewFlushedPaint calls, leaving our callback stranded. | 333 // ViewInitiatedPaint/ViewFlushedPaint calls, leaving our callback stranded. |
332 gfx::Rect visible_changed_rect; | 334 gfx::Rect visible_changed_rect; |
333 if (bound_instance_ && !changed_rect.IsEmpty()) | 335 if (bound_instance_ && !changed_rect.IsEmpty()) |
334 visible_changed_rect = bound_instance_->clip().Intersect(changed_rect); | 336 visible_changed_rect = bound_instance_->clip().Intersect(changed_rect); |
335 | 337 |
336 if (bound_instance_ && !visible_changed_rect.IsEmpty()) { | 338 if (bound_instance_ && !visible_changed_rect.IsEmpty()) { |
337 unpainted_flush_callbacks_.push_back(FlushCallbackData(callback, | 339 unpainted_flush_callback_.reset(new FlushCallbackData(callback, |
338 callback_data)); | 340 callback_data)); |
339 bound_instance_->InvalidateRect(visible_changed_rect); | 341 bound_instance_->InvalidateRect(visible_changed_rect); |
340 } else { | 342 } else { |
341 // There's nothing visible to invalidate so just schedule the callback to | 343 // There's nothing visible to invalidate so just schedule the callback to |
342 // execute in the next round of the message loop. | 344 // execute in the next round of the message loop. |
343 ScheduleOffscreenCallback( | 345 ScheduleOffscreenCallback( |
344 FlushCallbackData(callback, callback_data)); | 346 FlushCallbackData(callback, callback_data)); |
345 } | 347 } |
346 return true; | 348 return true; |
347 } | 349 } |
348 | 350 |
(...skipping 40 matching lines...) Loading... |
389 bool DeviceContext2D::BindToInstance(PluginInstance* new_instance) { | 391 bool DeviceContext2D::BindToInstance(PluginInstance* new_instance) { |
390 if (bound_instance_ == new_instance) | 392 if (bound_instance_ == new_instance) |
391 return true; // Rebinding the same device, nothing to do. | 393 return true; // Rebinding the same device, nothing to do. |
392 if (bound_instance_ && new_instance) | 394 if (bound_instance_ && new_instance) |
393 return false; // Can't change a bound device. | 395 return false; // Can't change a bound device. |
394 | 396 |
395 if (!new_instance) { | 397 if (!new_instance) { |
396 // When the device is detached, we'll not get any more paint callbacks so | 398 // 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 | 399 // we need to clear the list, but we still want to issue any pending |
398 // callbacks to the plugin. | 400 // callbacks to the plugin. |
399 for (size_t i = 0; i < unpainted_flush_callbacks_.size(); i++) | 401 if (unpainted_flush_callback_.get()) { |
400 ScheduleOffscreenCallback(unpainted_flush_callbacks_[i]); | 402 ScheduleOffscreenCallback(*unpainted_flush_callback_.get()); |
401 for (size_t i = 0; i < painted_flush_callbacks_.size(); i++) | 403 unpainted_flush_callback_.reset(); |
402 ScheduleOffscreenCallback(painted_flush_callbacks_[i]); | 404 } |
403 unpainted_flush_callbacks_.clear(); | 405 if (painted_flush_callback_.get()) { |
404 painted_flush_callbacks_.clear(); | 406 ScheduleOffscreenCallback(*painted_flush_callback_.get()); |
| 407 painted_flush_callback_.reset(); |
| 408 } |
405 } else if (flushed_any_data_) { | 409 } else if (flushed_any_data_) { |
406 // Only schedule a paint if this backing store has had any data flushed to | 410 // 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 | 411 // it. This is an optimization. A "normal" plugin will first allocated a |
408 // backing store, bind it, and then execute their normal painting and | 412 // backing store, bind it, and then execute their normal painting and |
409 // update loop. If binding a device always invalidated, it would mean we | 413 // 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 | 414 // 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 | 415 // 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 | 416 // when an empty device is initially bound, we can save an extra paint for |
413 // many plugins during the critical page initialization phase. | 417 // many plugins during the critical page initialization phase. |
414 new_instance->InvalidateRect(gfx::Rect()); | 418 new_instance->InvalidateRect(gfx::Rect()); |
(...skipping 41 matching lines...) Loading... |
456 CGContextRestoreGState(canvas); | 460 CGContextRestoreGState(canvas); |
457 #else | 461 #else |
458 gfx::Point origin(plugin_rect.origin().x(), plugin_rect.origin().y()); | 462 gfx::Point origin(plugin_rect.origin().x(), plugin_rect.origin().y()); |
459 canvas->drawBitmap(backing_bitmap, | 463 canvas->drawBitmap(backing_bitmap, |
460 SkIntToScalar(plugin_rect.origin().x()), | 464 SkIntToScalar(plugin_rect.origin().x()), |
461 SkIntToScalar(plugin_rect.origin().y())); | 465 SkIntToScalar(plugin_rect.origin().y())); |
462 #endif | 466 #endif |
463 } | 467 } |
464 | 468 |
465 void DeviceContext2D::ViewInitiatedPaint() { | 469 void DeviceContext2D::ViewInitiatedPaint() { |
466 // Move all "unpainted" callbacks to the painted state. See | 470 // Move any "unpainted" callback to the painted state. See |
467 // |unpainted_flush_callbacks_| in the header for more. | 471 // |unpainted_flush_callback_| in the header for more. |
468 std::copy(unpainted_flush_callbacks_.begin(), | 472 if (unpainted_flush_callback_.get()) { |
469 unpainted_flush_callbacks_.end(), | 473 DCHECK(!painted_flush_callback_.get()); |
470 std::back_inserter(painted_flush_callbacks_)); | 474 painted_flush_callback_.swap(unpainted_flush_callback_); |
471 unpainted_flush_callbacks_.clear(); | 475 } |
472 } | 476 } |
473 | 477 |
474 void DeviceContext2D::ViewFlushedPaint() { | 478 void DeviceContext2D::ViewFlushedPaint() { |
475 // Notify all "painted" callbacks. See |unpainted_flush_callbacks_| in the | 479 // Notify any "painted" callback. See |unpainted_flush_callback_| in the |
476 // header for more. | 480 // header for more. |
477 for (size_t i = 0; i < painted_flush_callbacks_.size(); i++) | 481 if (painted_flush_callback_.get()) { |
478 painted_flush_callbacks_[i].Execute(GetResource()); | 482 // We must clear this variable before issuing the callback. It will be |
479 painted_flush_callbacks_.clear(); | 483 // common for the plugin to issue another invalidate in response to a flush |
| 484 // callback, and we don't want to think that a callback is already pending. |
| 485 scoped_ptr<FlushCallbackData> callback; |
| 486 callback.swap(painted_flush_callback_); |
| 487 callback->Execute(GetResource()); |
| 488 } |
480 } | 489 } |
481 | 490 |
482 void DeviceContext2D::ExecutePaintImageData(ImageData* image, | 491 void DeviceContext2D::ExecutePaintImageData(ImageData* image, |
483 int x, int y, | 492 int x, int y, |
484 const gfx::Rect& src_rect, | 493 const gfx::Rect& src_rect, |
485 gfx::Rect* invalidated_rect) { | 494 gfx::Rect* invalidated_rect) { |
486 // Ensure the source image is mapped to read from it. | 495 // Ensure the source image is mapped to read from it. |
487 ImageDataAutoMapper auto_mapper(image); | 496 ImageDataAutoMapper auto_mapper(image); |
488 if (!auto_mapper.is_valid()) | 497 if (!auto_mapper.is_valid()) |
489 return; | 498 return; |
(...skipping 15 matching lines...) Loading... |
505 | 514 |
506 // We want to replace the contents of the bitmap rather than blend. | 515 // We want to replace the contents of the bitmap rather than blend. |
507 SkPaint paint; | 516 SkPaint paint; |
508 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | 517 paint.setXfermodeMode(SkXfermode::kSrc_Mode); |
509 backing_canvas->drawBitmapRect(*image->GetMappedBitmap(), | 518 backing_canvas->drawBitmapRect(*image->GetMappedBitmap(), |
510 &src_irect, dest_rect, &paint); | 519 &src_irect, dest_rect, &paint); |
511 } | 520 } |
512 | 521 |
513 void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy, | 522 void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy, |
514 gfx::Rect* invalidated_rect) { | 523 gfx::Rect* invalidated_rect) { |
515 // FIXME(brettw) | 524 // TODO(brettw): implement this. |
516 } | 525 } |
517 | 526 |
518 void DeviceContext2D::ExecuteReplaceContents(ImageData* image, | 527 void DeviceContext2D::ExecuteReplaceContents(ImageData* image, |
519 gfx::Rect* invalidated_rect) { | 528 gfx::Rect* invalidated_rect) { |
520 image_data_->Swap(image); | 529 image_data_->Swap(image); |
521 *invalidated_rect = gfx::Rect(0, 0, | 530 *invalidated_rect = gfx::Rect(0, 0, |
522 image_data_->width(), image_data_->height()); | 531 image_data_->width(), image_data_->height()); |
523 } | 532 } |
524 | 533 |
525 void DeviceContext2D::ScheduleOffscreenCallback( | 534 void DeviceContext2D::ScheduleOffscreenCallback( |
526 const FlushCallbackData& callback) { | 535 const FlushCallbackData& callback) { |
| 536 DCHECK(!HasPendingFlush()); |
| 537 offscreen_flush_pending_ = true; |
527 MessageLoop::current()->PostTask( | 538 MessageLoop::current()->PostTask( |
528 FROM_HERE, | 539 FROM_HERE, |
529 NewRunnableMethod(this, | 540 NewRunnableMethod(this, |
530 &DeviceContext2D::ExecuteOffscreenCallback, | 541 &DeviceContext2D::ExecuteOffscreenCallback, |
531 callback)); | 542 callback)); |
532 } | 543 } |
533 | 544 |
534 void DeviceContext2D::ExecuteOffscreenCallback(FlushCallbackData data) { | 545 void DeviceContext2D::ExecuteOffscreenCallback(FlushCallbackData data) { |
| 546 DCHECK(offscreen_flush_pending_); |
| 547 |
| 548 // We must clear this flag before issuing the callback. It will be |
| 549 // common for the plugin to issue another invalidate in response to a flush |
| 550 // callback, and we don't want to think that a callback is already pending. |
| 551 offscreen_flush_pending_ = false; |
535 data.Execute(GetResource()); | 552 data.Execute(GetResource()); |
536 } | 553 } |
537 | 554 |
| 555 bool DeviceContext2D::HasPendingFlush() const { |
| 556 return unpainted_flush_callback_.get() || painted_flush_callback_.get() || |
| 557 offscreen_flush_pending_; |
| 558 } |
| 559 |
538 } // namespace pepper | 560 } // namespace pepper |
OLD | NEW |