Index: webkit/glue/plugins/pepper_device_context_2d.cc |
=================================================================== |
--- webkit/glue/plugins/pepper_device_context_2d.cc (revision 49266) |
+++ webkit/glue/plugins/pepper_device_context_2d.cc (working copy) |
@@ -4,7 +4,11 @@ |
#include "webkit/glue/plugins/pepper_device_context_2d.h" |
+#include <iterator> |
+ |
#include "base/logging.h" |
+#include "base/message_loop.h" |
+#include "base/task.h" |
#include "gfx/point.h" |
#include "gfx/rect.h" |
#include "skia/ext/platform_canvas.h" |
@@ -14,6 +18,7 @@ |
#include "third_party/ppapi/c/ppb_device_context_2d.h" |
#include "third_party/skia/include/core/SkBitmap.h" |
#include "webkit/glue/plugins/pepper_image_data.h" |
+#include "webkit/glue/plugins/pepper_plugin_instance.h" |
#include "webkit/glue/plugins/pepper_plugin_module.h" |
#include "webkit/glue/plugins/pepper_resource_tracker.h" |
@@ -70,6 +75,12 @@ |
return context->GetResource(); |
} |
+bool IsDeviceContext2D(PP_Resource resource) { |
+ scoped_refptr<DeviceContext2D> context( |
+ ResourceTracker::Get()->GetAsDeviceContext2D(resource)); |
+ return !!context.get(); |
+} |
+ |
bool Describe(PP_Resource device_context, |
int32_t* width, int32_t* height, bool* is_always_opaque) { |
scoped_refptr<DeviceContext2D> context( |
@@ -120,6 +131,7 @@ |
const PPB_DeviceContext2D ppb_devicecontext2d = { |
&Create, |
+ &IsDeviceContext2D, |
&Describe, |
&PaintImageData, |
&Scroll, |
@@ -159,9 +171,23 @@ |
scoped_refptr<ImageData> replace_image; |
}; |
-DeviceContext2D::DeviceContext2D(PluginModule* module) : Resource(module) { |
+DeviceContext2D::FlushCallbackData::FlushCallbackData( |
+ PPB_DeviceContext2D_FlushCallback c, |
+ void* d) |
+ : callback_(c), |
+ callback_data_(d) { |
} |
+void DeviceContext2D::FlushCallbackData::Execute(PP_Resource device_context) { |
+ callback_(device_context, callback_data_); |
+} |
+ |
+DeviceContext2D::DeviceContext2D(PluginModule* module) |
+ : Resource(module), |
+ bound_instance_(NULL), |
+ flushed_any_data_(false) { |
+} |
+ |
DeviceContext2D::~DeviceContext2D() { |
} |
@@ -266,42 +292,57 @@ |
bool DeviceContext2D::Flush(PPB_DeviceContext2D_FlushCallback callback, |
void* callback_data) { |
+ // TODO(brettw) check that the current thread is not the main one and |
+ // implement blocking flushes in this case. |
+ if (!callback) |
+ return false; |
+ |
+ if (queued_operations_.empty()) |
+ return true; // Nothing to do. |
+ |
+ gfx::Rect changed_rect; |
for (size_t i = 0; i < queued_operations_.size(); i++) { |
QueuedOperation& operation = queued_operations_[i]; |
+ gfx::Rect op_rect; |
switch (operation.type) { |
case QueuedOperation::PAINT: |
ExecutePaintImageData(operation.paint_image.get(), |
operation.paint_x, operation.paint_y, |
- operation.paint_src_rect); |
+ operation.paint_src_rect, |
+ &op_rect); |
break; |
case QueuedOperation::SCROLL: |
ExecuteScroll(operation.scroll_clip_rect, |
- operation.scroll_dx, operation.scroll_dy); |
+ operation.scroll_dx, operation.scroll_dy, |
+ &op_rect); |
break; |
case QueuedOperation::REPLACE: |
- ExecuteReplaceContents(operation.replace_image.get()); |
+ ExecuteReplaceContents(operation.replace_image.get(), &op_rect); |
break; |
} |
+ changed_rect = changed_rect.Union(op_rect); |
} |
queued_operations_.clear(); |
- // TODO(brettw) implement invalidate and callbacks! |
+ flushed_any_data_ = true; |
- // Cause the updated part of the screen to be repainted. This will happen |
- // asynchronously. |
- /* |
- gfx::Rect dest_gfx_rect(src_rect->left, src_rect->top, |
- src_rect->right - src_rect->left, |
- src_rect->bottom - src_rect->top); |
+ // We need the rect to be in terms of the current clip rect of the plugin |
+ // since that's what will actually be painted. If we issue an invalidate |
+ // for a clipped-out region, WebKit will do nothing and we won't get any |
+ // ViewInitiatedPaint/ViewFlushedPaint calls, leaving our callback stranded. |
+ gfx::Rect visible_changed_rect; |
+ if (bound_instance_ && !changed_rect.IsEmpty()) |
+ visible_changed_rect = bound_instance_->clip().Intersect(changed_rect); |
- plugin_delegate_->instance()->webplugin()->InvalidateRect(dest_gfx_rect); |
- |
- // Save the callback to execute later. See |unpainted_flush_callbacks_| in |
- // the header file. |
- if (callback) { |
- unpainted_flush_callbacks_.push_back( |
- FlushCallbackData(callback, id, context, user_data)); |
+ if (bound_instance_ && !visible_changed_rect.IsEmpty()) { |
+ unpainted_flush_callbacks_.push_back(FlushCallbackData(callback, |
+ callback_data)); |
+ bound_instance_->InvalidateRect(visible_changed_rect); |
+ } else { |
+ // There's nothing visible to invalidate so just schedule the callback to |
+ // execute in the next round of the message loop. |
+ ScheduleOffscreenCallback( |
+ FlushCallbackData(callback, callback_data)); |
} |
-*/ |
return true; |
} |
@@ -345,6 +386,38 @@ |
return true; |
} |
+bool DeviceContext2D::BindToInstance(PluginInstance* new_instance) { |
+ if (bound_instance_ == new_instance) |
+ return true; // Rebinding the same device, nothing to do. |
+ if (bound_instance_ && new_instance) |
+ return false; // Can't change a bound device. |
+ |
+ if (!new_instance) { |
+ // When the device is detached, we'll not get any more paint callbacks so |
+ // we need to clear the list, but we still want to issue any pending |
+ // callbacks to the plugin. |
+ for (size_t i = 0; i < unpainted_flush_callbacks_.size(); i++) |
+ ScheduleOffscreenCallback(unpainted_flush_callbacks_[i]); |
+ for (size_t i = 0; i < painted_flush_callbacks_.size(); i++) |
+ ScheduleOffscreenCallback(painted_flush_callbacks_[i]); |
+ unpainted_flush_callbacks_.clear(); |
+ painted_flush_callbacks_.clear(); |
+ } else if (flushed_any_data_) { |
+ // Only schedule a paint if this backing store has had any data flushed to |
+ // it. This is an optimization. A "normal" plugin will first allocated a |
+ // backing store, bind it, and then execute their normal painting and |
+ // update loop. If binding a device always invalidated, it would mean we |
+ // would get one paint for the bind, and one for the first time the plugin |
+ // actually painted something. By not bothering to schedule an invalidate |
+ // when an empty device is initially bound, we can save an extra paint for |
+ // many plugins during the critical page initialization phase. |
+ new_instance->InvalidateRect(gfx::Rect()); |
+ } |
+ |
+ bound_instance_ = new_instance; |
+ return true; |
+} |
+ |
void DeviceContext2D::Paint(WebKit::WebCanvas* canvas, |
const gfx::Rect& plugin_rect, |
const gfx::Rect& paint_rect) { |
@@ -389,27 +462,47 @@ |
#endif |
} |
+void DeviceContext2D::ViewInitiatedPaint() { |
+ // Move all "unpainted" callbacks to the painted state. See |
+ // |unpainted_flush_callbacks_| in the header for more. |
+ std::copy(unpainted_flush_callbacks_.begin(), |
+ unpainted_flush_callbacks_.end(), |
+ std::back_inserter(painted_flush_callbacks_)); |
+ unpainted_flush_callbacks_.clear(); |
+} |
+ |
+void DeviceContext2D::ViewFlushedPaint() { |
+ // Notify all "painted" callbacks. See |unpainted_flush_callbacks_| in the |
+ // header for more. |
+ for (size_t i = 0; i < painted_flush_callbacks_.size(); i++) |
+ painted_flush_callbacks_[i].Execute(GetResource()); |
+ painted_flush_callbacks_.clear(); |
+} |
+ |
void DeviceContext2D::ExecutePaintImageData(ImageData* image, |
int x, int y, |
- const gfx::Rect& src_rect) { |
+ const gfx::Rect& src_rect, |
+ gfx::Rect* invalidated_rect) { |
+ // Ensure the source image is mapped to read from it. |
+ ImageDataAutoMapper auto_mapper(image); |
+ if (!auto_mapper.is_valid()) |
+ return; |
+ |
// Portion within the source image to cut out. |
SkIRect src_irect = { src_rect.x(), src_rect.y(), |
src_rect.right(), src_rect.bottom() }; |
// Location within the backing store to copy to. |
- SkRect dest_rect = { SkIntToScalar(x + src_rect.x()), |
- SkIntToScalar(y + src_rect.y()), |
- SkIntToScalar(x + src_rect.right()), |
- SkIntToScalar(y + src_rect.bottom()) }; |
+ *invalidated_rect = src_rect; |
+ invalidated_rect->Offset(x, y); |
+ SkRect dest_rect = { SkIntToScalar(invalidated_rect->x()), |
+ SkIntToScalar(invalidated_rect->y()), |
+ SkIntToScalar(invalidated_rect->right()), |
+ SkIntToScalar(invalidated_rect->bottom()) }; |
// We're guaranteed to have a mapped canvas since we mapped it in Init(). |
skia::PlatformCanvas* backing_canvas = image_data_->mapped_canvas(); |
- // Ensure the source image is mapped to read from it. |
- ImageDataAutoMapper auto_mapper(image); |
- if (!auto_mapper.is_valid()) |
- return; |
- |
// We want to replace the contents of the bitmap rather than blend. |
SkPaint paint; |
paint.setXfermodeMode(SkXfermode::kSrc_Mode); |
@@ -417,12 +510,29 @@ |
&src_irect, dest_rect, &paint); |
} |
-void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy) { |
+void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy, |
+ gfx::Rect* invalidated_rect) { |
// FIXME(brettw) |
} |
-void DeviceContext2D::ExecuteReplaceContents(ImageData* image) { |
+void DeviceContext2D::ExecuteReplaceContents(ImageData* image, |
+ gfx::Rect* invalidated_rect) { |
image_data_->Swap(image); |
+ *invalidated_rect = gfx::Rect(0, 0, |
+ image_data_->width(), image_data_->height()); |
} |
+void DeviceContext2D::ScheduleOffscreenCallback( |
+ const FlushCallbackData& callback) { |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &DeviceContext2D::ExecuteOffscreenCallback, |
+ callback)); |
+} |
+ |
+void DeviceContext2D::ExecuteOffscreenCallback(FlushCallbackData data) { |
+ data.Execute(GetResource()); |
+} |
+ |
} // namespace pepper |