| 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand all 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand all 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 |