| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "webkit/glue/plugins/pepper_device_context_2d.h" | |
| 6 | |
| 7 #include <iterator> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/message_loop.h" | |
| 11 #include "base/task.h" | |
| 12 #include "gfx/blit.h" | |
| 13 #include "gfx/point.h" | |
| 14 #include "gfx/rect.h" | |
| 15 #include "skia/ext/platform_canvas.h" | |
| 16 #include "third_party/ppapi/c/pp_errors.h" | |
| 17 #include "third_party/ppapi/c/pp_module.h" | |
| 18 #include "third_party/ppapi/c/pp_rect.h" | |
| 19 #include "third_party/ppapi/c/pp_resource.h" | |
| 20 #include "third_party/ppapi/c/ppb_device_context_2d.h" | |
| 21 #include "third_party/skia/include/core/SkBitmap.h" | |
| 22 #include "webkit/glue/plugins/pepper_image_data.h" | |
| 23 #include "webkit/glue/plugins/pepper_plugin_instance.h" | |
| 24 #include "webkit/glue/plugins/pepper_plugin_module.h" | |
| 25 | |
| 26 #if defined(OS_MACOSX) | |
| 27 #include "base/mac_util.h" | |
| 28 #include "base/scoped_cftyperef.h" | |
| 29 #endif | |
| 30 | |
| 31 namespace pepper { | |
| 32 | |
| 33 namespace { | |
| 34 | |
| 35 // Converts a rect inside an image of the given dimensions. The rect may be | |
| 36 // NULL to indicate it should be the entire image. If the rect is outside of | |
| 37 // the image, this will do nothing and return false. | |
| 38 bool ValidateAndConvertRect(const PP_Rect* rect, | |
| 39 int image_width, int image_height, | |
| 40 gfx::Rect* dest) { | |
| 41 if (!rect) { | |
| 42 // Use the entire image area. | |
| 43 *dest = gfx::Rect(0, 0, image_width, image_height); | |
| 44 } else { | |
| 45 // Validate the passed-in area. | |
| 46 if (rect->point.x < 0 || rect->point.y < 0 || | |
| 47 rect->size.width <= 0 || rect->size.height <= 0) | |
| 48 return false; | |
| 49 | |
| 50 // Check the max bounds, being careful of overflow. | |
| 51 if (static_cast<int64>(rect->point.x) + | |
| 52 static_cast<int64>(rect->size.width) > | |
| 53 static_cast<int64>(image_width)) | |
| 54 return false; | |
| 55 if (static_cast<int64>(rect->point.y) + | |
| 56 static_cast<int64>(rect->size.height) > | |
| 57 static_cast<int64>(image_height)) | |
| 58 return false; | |
| 59 | |
| 60 *dest = gfx::Rect(rect->point.x, rect->point.y, | |
| 61 rect->size.width, rect->size.height); | |
| 62 } | |
| 63 return true; | |
| 64 } | |
| 65 | |
| 66 PP_Resource Create(PP_Module module_id, | |
| 67 const PP_Size* size, | |
| 68 bool is_always_opaque) { | |
| 69 PluginModule* module = PluginModule::FromPPModule(module_id); | |
| 70 if (!module) | |
| 71 return 0; | |
| 72 | |
| 73 scoped_refptr<DeviceContext2D> context(new DeviceContext2D(module)); | |
| 74 if (!context->Init(size->width, size->height, is_always_opaque)) | |
| 75 return 0; | |
| 76 return context->GetReference(); | |
| 77 } | |
| 78 | |
| 79 bool IsDeviceContext2D(PP_Resource resource) { | |
| 80 return !!Resource::GetAs<DeviceContext2D>(resource); | |
| 81 } | |
| 82 | |
| 83 bool Describe(PP_Resource device_context, | |
| 84 PP_Size* size, | |
| 85 bool* is_always_opaque) { | |
| 86 scoped_refptr<DeviceContext2D> context( | |
| 87 Resource::GetAs<DeviceContext2D>(device_context)); | |
| 88 if (!context) | |
| 89 return false; | |
| 90 return context->Describe(size, is_always_opaque); | |
| 91 } | |
| 92 | |
| 93 bool PaintImageData(PP_Resource device_context, | |
| 94 PP_Resource image, | |
| 95 const PP_Point* top_left, | |
| 96 const PP_Rect* src_rect) { | |
| 97 scoped_refptr<DeviceContext2D> context( | |
| 98 Resource::GetAs<DeviceContext2D>(device_context)); | |
| 99 if (!context) | |
| 100 return false; | |
| 101 return context->PaintImageData(image, top_left, src_rect); | |
| 102 } | |
| 103 | |
| 104 bool Scroll(PP_Resource device_context, | |
| 105 const PP_Rect* clip_rect, | |
| 106 const PP_Point* amount) { | |
| 107 scoped_refptr<DeviceContext2D> context( | |
| 108 Resource::GetAs<DeviceContext2D>(device_context)); | |
| 109 if (!context) | |
| 110 return false; | |
| 111 return context->Scroll(clip_rect, amount); | |
| 112 } | |
| 113 | |
| 114 bool ReplaceContents(PP_Resource device_context, PP_Resource image) { | |
| 115 scoped_refptr<DeviceContext2D> context( | |
| 116 Resource::GetAs<DeviceContext2D>(device_context)); | |
| 117 if (!context) | |
| 118 return false; | |
| 119 return context->ReplaceContents(image); | |
| 120 } | |
| 121 | |
| 122 int32_t Flush(PP_Resource device_context, | |
| 123 PP_CompletionCallback callback) { | |
| 124 scoped_refptr<DeviceContext2D> context( | |
| 125 Resource::GetAs<DeviceContext2D>(device_context)); | |
| 126 if (!context) | |
| 127 return PP_ERROR_BADRESOURCE; | |
| 128 return context->Flush(callback); | |
| 129 } | |
| 130 | |
| 131 const PPB_DeviceContext2D ppb_devicecontext2d = { | |
| 132 &Create, | |
| 133 &IsDeviceContext2D, | |
| 134 &Describe, | |
| 135 &PaintImageData, | |
| 136 &Scroll, | |
| 137 &ReplaceContents, | |
| 138 &Flush | |
| 139 }; | |
| 140 | |
| 141 } // namespace | |
| 142 | |
| 143 struct DeviceContext2D::QueuedOperation { | |
| 144 enum Type { | |
| 145 PAINT, | |
| 146 SCROLL, | |
| 147 REPLACE | |
| 148 }; | |
| 149 | |
| 150 QueuedOperation(Type t) | |
| 151 : type(t), | |
| 152 paint_x(0), | |
| 153 paint_y(0), | |
| 154 scroll_dx(0), | |
| 155 scroll_dy(0) { | |
| 156 } | |
| 157 | |
| 158 Type type; | |
| 159 | |
| 160 // Valid when type == PAINT. | |
| 161 scoped_refptr<ImageData> paint_image; | |
| 162 int paint_x, paint_y; | |
| 163 gfx::Rect paint_src_rect; | |
| 164 | |
| 165 // Valid when type == SCROLL. | |
| 166 gfx::Rect scroll_clip_rect; | |
| 167 int scroll_dx, scroll_dy; | |
| 168 | |
| 169 // Valid when type == REPLACE. | |
| 170 scoped_refptr<ImageData> replace_image; | |
| 171 }; | |
| 172 | |
| 173 DeviceContext2D::DeviceContext2D(PluginModule* module) | |
| 174 : Resource(module), | |
| 175 bound_instance_(NULL), | |
| 176 flushed_any_data_(false), | |
| 177 offscreen_flush_pending_(false) { | |
| 178 } | |
| 179 | |
| 180 DeviceContext2D::~DeviceContext2D() { | |
| 181 } | |
| 182 | |
| 183 // static | |
| 184 const PPB_DeviceContext2D* DeviceContext2D::GetInterface() { | |
| 185 return &ppb_devicecontext2d; | |
| 186 } | |
| 187 | |
| 188 bool DeviceContext2D::Init(int width, int height, bool is_always_opaque) { | |
| 189 // The underlying ImageData will validate the dimensions. | |
| 190 image_data_ = new ImageData(module()); | |
| 191 if (!image_data_->Init(PP_IMAGEDATAFORMAT_BGRA_PREMUL, width, height, true) || | |
| 192 !image_data_->Map()) { | |
| 193 image_data_ = NULL; | |
| 194 return false; | |
| 195 } | |
| 196 | |
| 197 return true; | |
| 198 } | |
| 199 | |
| 200 bool DeviceContext2D::Describe(PP_Size* size, bool* is_always_opaque) { | |
| 201 size->width = image_data_->width(); | |
| 202 size->height = image_data_->height(); | |
| 203 *is_always_opaque = false; // TODO(brettw) implement this. | |
| 204 return true; | |
| 205 } | |
| 206 | |
| 207 bool DeviceContext2D::PaintImageData(PP_Resource image, | |
| 208 const PP_Point* top_left, | |
| 209 const PP_Rect* src_rect) { | |
| 210 if (!top_left) | |
| 211 return false; | |
| 212 | |
| 213 scoped_refptr<ImageData> image_resource(Resource::GetAs<ImageData>(image)); | |
| 214 if (!image_resource) | |
| 215 return false; | |
| 216 | |
| 217 QueuedOperation operation(QueuedOperation::PAINT); | |
| 218 operation.paint_image = image_resource; | |
| 219 if (!ValidateAndConvertRect(src_rect, image_resource->width(), | |
| 220 image_resource->height(), | |
| 221 &operation.paint_src_rect)) | |
| 222 return false; | |
| 223 | |
| 224 // Validate the bitmap position using the previously-validated rect, there | |
| 225 // should be no painted area outside of the image. | |
| 226 int64 x64 = static_cast<int64>(top_left->x); | |
| 227 int64 y64 = static_cast<int64>(top_left->y); | |
| 228 if (x64 + static_cast<int64>(operation.paint_src_rect.x()) < 0 || | |
| 229 x64 + static_cast<int64>(operation.paint_src_rect.right()) > | |
| 230 image_data_->width()) | |
| 231 return false; | |
| 232 if (y64 + static_cast<int64>(operation.paint_src_rect.y()) < 0 || | |
| 233 y64 + static_cast<int64>(operation.paint_src_rect.bottom()) > | |
| 234 image_data_->height()) | |
| 235 return false; | |
| 236 operation.paint_x = top_left->x; | |
| 237 operation.paint_y = top_left->y; | |
| 238 | |
| 239 queued_operations_.push_back(operation); | |
| 240 return true; | |
| 241 } | |
| 242 | |
| 243 bool DeviceContext2D::Scroll(const PP_Rect* clip_rect, | |
| 244 const PP_Point* amount) { | |
| 245 QueuedOperation operation(QueuedOperation::SCROLL); | |
| 246 if (!ValidateAndConvertRect(clip_rect, | |
| 247 image_data_->width(), | |
| 248 image_data_->height(), | |
| 249 &operation.scroll_clip_rect)) | |
| 250 return false; | |
| 251 | |
| 252 // If we're being asked to scroll by more than the clip rect size, just | |
| 253 // ignore this scroll command and say it worked. | |
| 254 int32 dx = amount->x; | |
| 255 int32 dy = amount->y; | |
| 256 if (dx <= -image_data_->width() || dx >= image_data_->width() || | |
| 257 dx <= -image_data_->height() || dy >= image_data_->height()) | |
| 258 return true; | |
| 259 | |
| 260 operation.scroll_dx = dx; | |
| 261 operation.scroll_dy = dy; | |
| 262 | |
| 263 queued_operations_.push_back(operation); | |
| 264 return false; | |
| 265 } | |
| 266 | |
| 267 bool DeviceContext2D::ReplaceContents(PP_Resource image) { | |
| 268 scoped_refptr<ImageData> image_resource(Resource::GetAs<ImageData>(image)); | |
| 269 if (!image_resource) | |
| 270 return false; | |
| 271 if (image_resource->format() != PP_IMAGEDATAFORMAT_BGRA_PREMUL) | |
| 272 return false; | |
| 273 | |
| 274 if (image_resource->width() != image_data_->width() || | |
| 275 image_resource->height() != image_data_->height()) | |
| 276 return false; | |
| 277 | |
| 278 QueuedOperation operation(QueuedOperation::REPLACE); | |
| 279 operation.replace_image = image_resource; | |
| 280 queued_operations_.push_back(operation); | |
| 281 | |
| 282 return true; | |
| 283 } | |
| 284 | |
| 285 int32_t DeviceContext2D::Flush(const PP_CompletionCallback& callback) { | |
| 286 // Don't allow more than one pending flush at a time. | |
| 287 if (HasPendingFlush()) | |
| 288 return PP_ERROR_INPROGRESS; | |
| 289 | |
| 290 // TODO(brettw) check that the current thread is not the main one and | |
| 291 // implement blocking flushes in this case. | |
| 292 if (!callback.func) | |
| 293 return PP_ERROR_BADARGUMENT; | |
| 294 | |
| 295 gfx::Rect changed_rect; | |
| 296 for (size_t i = 0; i < queued_operations_.size(); i++) { | |
| 297 QueuedOperation& operation = queued_operations_[i]; | |
| 298 gfx::Rect op_rect; | |
| 299 switch (operation.type) { | |
| 300 case QueuedOperation::PAINT: | |
| 301 ExecutePaintImageData(operation.paint_image, | |
| 302 operation.paint_x, operation.paint_y, | |
| 303 operation.paint_src_rect, | |
| 304 &op_rect); | |
| 305 break; | |
| 306 case QueuedOperation::SCROLL: | |
| 307 ExecuteScroll(operation.scroll_clip_rect, | |
| 308 operation.scroll_dx, operation.scroll_dy, | |
| 309 &op_rect); | |
| 310 break; | |
| 311 case QueuedOperation::REPLACE: | |
| 312 ExecuteReplaceContents(operation.replace_image, &op_rect); | |
| 313 break; | |
| 314 } | |
| 315 changed_rect = changed_rect.Union(op_rect); | |
| 316 } | |
| 317 queued_operations_.clear(); | |
| 318 flushed_any_data_ = true; | |
| 319 | |
| 320 // We need the rect to be in terms of the current clip rect of the plugin | |
| 321 // since that's what will actually be painted. If we issue an invalidate | |
| 322 // for a clipped-out region, WebKit will do nothing and we won't get any | |
| 323 // ViewInitiatedPaint/ViewFlushedPaint calls, leaving our callback stranded. | |
| 324 gfx::Rect visible_changed_rect; | |
| 325 if (bound_instance_ && !changed_rect.IsEmpty()) | |
| 326 visible_changed_rect = bound_instance_->clip().Intersect(changed_rect); | |
| 327 | |
| 328 if (bound_instance_ && !visible_changed_rect.IsEmpty()) { | |
| 329 unpainted_flush_callback_.Set(callback); | |
| 330 bound_instance_->InvalidateRect(visible_changed_rect); | |
| 331 } else { | |
| 332 // There's nothing visible to invalidate so just schedule the callback to | |
| 333 // execute in the next round of the message loop. | |
| 334 ScheduleOffscreenCallback(FlushCallbackData(callback)); | |
| 335 } | |
| 336 return PP_ERROR_WOULDBLOCK; | |
| 337 } | |
| 338 | |
| 339 bool DeviceContext2D::ReadImageData(PP_Resource image, | |
| 340 const PP_Point* top_left) { | |
| 341 // Get and validate the image object to paint into. | |
| 342 scoped_refptr<ImageData> image_resource(Resource::GetAs<ImageData>(image)); | |
| 343 if (!image_resource) | |
| 344 return false; | |
| 345 if (image_resource->format() != PP_IMAGEDATAFORMAT_BGRA_PREMUL) | |
| 346 return false; // Must be in the right format. | |
| 347 | |
| 348 // Validate the bitmap position. | |
| 349 int x = top_left->x; | |
| 350 if (x < 0 || | |
| 351 static_cast<int64>(x) + static_cast<int64>(image_resource->width()) > | |
| 352 image_data_->width()) | |
| 353 return false; | |
| 354 int y = top_left->y; | |
| 355 if (y < 0 || | |
| 356 static_cast<int64>(y) + static_cast<int64>(image_resource->height()) > | |
| 357 image_data_->height()) | |
| 358 return false; | |
| 359 | |
| 360 ImageDataAutoMapper auto_mapper(image_resource); | |
| 361 if (!auto_mapper.is_valid()) | |
| 362 return false; | |
| 363 skia::PlatformCanvas* dest_canvas = image_resource->mapped_canvas(); | |
| 364 | |
| 365 SkIRect src_irect = { x, y, | |
| 366 x + image_resource->width(), | |
| 367 y + image_resource->height() }; | |
| 368 SkRect dest_rect = { SkIntToScalar(0), | |
| 369 SkIntToScalar(0), | |
| 370 SkIntToScalar(image_resource->width()), | |
| 371 SkIntToScalar(image_resource->height()) }; | |
| 372 | |
| 373 // We want to replace the contents of the bitmap rather than blend. | |
| 374 SkPaint paint; | |
| 375 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
| 376 dest_canvas->drawBitmapRect(*image_data_->GetMappedBitmap(), | |
| 377 &src_irect, dest_rect, &paint); | |
| 378 return true; | |
| 379 } | |
| 380 | |
| 381 bool DeviceContext2D::BindToInstance(PluginInstance* new_instance) { | |
| 382 if (bound_instance_ == new_instance) | |
| 383 return true; // Rebinding the same device, nothing to do. | |
| 384 if (bound_instance_ && new_instance) | |
| 385 return false; // Can't change a bound device. | |
| 386 | |
| 387 if (!new_instance) { | |
| 388 // When the device is detached, we'll not get any more paint callbacks so | |
| 389 // we need to clear the list, but we still want to issue any pending | |
| 390 // callbacks to the plugin. | |
| 391 if (!unpainted_flush_callback_.is_null()) { | |
| 392 ScheduleOffscreenCallback(unpainted_flush_callback_); | |
| 393 unpainted_flush_callback_.Clear(); | |
| 394 } | |
| 395 if (!painted_flush_callback_.is_null()) { | |
| 396 ScheduleOffscreenCallback(painted_flush_callback_); | |
| 397 painted_flush_callback_.Clear(); | |
| 398 } | |
| 399 } else if (flushed_any_data_) { | |
| 400 // Only schedule a paint if this backing store has had any data flushed to | |
| 401 // it. This is an optimization. A "normal" plugin will first allocated a | |
| 402 // backing store, bind it, and then execute their normal painting and | |
| 403 // update loop. If binding a device always invalidated, it would mean we | |
| 404 // would get one paint for the bind, and one for the first time the plugin | |
| 405 // actually painted something. By not bothering to schedule an invalidate | |
| 406 // when an empty device is initially bound, we can save an extra paint for | |
| 407 // many plugins during the critical page initialization phase. | |
| 408 new_instance->InvalidateRect(gfx::Rect()); | |
| 409 } | |
| 410 | |
| 411 bound_instance_ = new_instance; | |
| 412 return true; | |
| 413 } | |
| 414 | |
| 415 void DeviceContext2D::Paint(WebKit::WebCanvas* canvas, | |
| 416 const gfx::Rect& plugin_rect, | |
| 417 const gfx::Rect& paint_rect) { | |
| 418 // We're guaranteed to have a mapped canvas since we mapped it in Init(). | |
| 419 const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap(); | |
| 420 | |
| 421 #if defined(OS_MACOSX) | |
| 422 SkAutoLockPixels lock(backing_bitmap); | |
| 423 | |
| 424 scoped_cftyperef<CGDataProviderRef> data_provider( | |
| 425 CGDataProviderCreateWithData( | |
| 426 NULL, backing_bitmap.getAddr32(0, 0), | |
| 427 backing_bitmap.rowBytes() * backing_bitmap.height(), NULL)); | |
| 428 scoped_cftyperef<CGImageRef> image( | |
| 429 CGImageCreate( | |
| 430 backing_bitmap.width(), backing_bitmap.height(), | |
| 431 8, 32, backing_bitmap.rowBytes(), | |
| 432 mac_util::GetSystemColorSpace(), | |
| 433 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, | |
| 434 data_provider, NULL, false, kCGRenderingIntentDefault)); | |
| 435 | |
| 436 // Flip the transform | |
| 437 CGContextSaveGState(canvas); | |
| 438 float window_height = static_cast<float>(CGBitmapContextGetHeight(canvas)); | |
| 439 CGContextTranslateCTM(canvas, 0, window_height); | |
| 440 CGContextScaleCTM(canvas, 1.0, -1.0); | |
| 441 | |
| 442 CGRect bounds; | |
| 443 bounds.origin.x = plugin_rect.origin().x(); | |
| 444 bounds.origin.y = window_height - plugin_rect.origin().y() - | |
| 445 backing_bitmap.height(); | |
| 446 bounds.size.width = backing_bitmap.width(); | |
| 447 bounds.size.height = backing_bitmap.height(); | |
| 448 | |
| 449 CGContextDrawImage(canvas, bounds, image); | |
| 450 CGContextRestoreGState(canvas); | |
| 451 #else | |
| 452 gfx::Point origin(plugin_rect.origin().x(), plugin_rect.origin().y()); | |
| 453 canvas->drawBitmap(backing_bitmap, | |
| 454 SkIntToScalar(plugin_rect.origin().x()), | |
| 455 SkIntToScalar(plugin_rect.origin().y())); | |
| 456 #endif | |
| 457 } | |
| 458 | |
| 459 void DeviceContext2D::ViewInitiatedPaint() { | |
| 460 // Move any "unpainted" callback to the painted state. See | |
| 461 // |unpainted_flush_callback_| in the header for more. | |
| 462 if (!unpainted_flush_callback_.is_null()) { | |
| 463 DCHECK(painted_flush_callback_.is_null()); | |
| 464 std::swap(painted_flush_callback_, unpainted_flush_callback_); | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 void DeviceContext2D::ViewFlushedPaint() { | |
| 469 // Notify any "painted" callback. See |unpainted_flush_callback_| in the | |
| 470 // header for more. | |
| 471 if (!painted_flush_callback_.is_null()) { | |
| 472 // We must clear this variable before issuing the callback. It will be | |
| 473 // common for the plugin to issue another invalidate in response to a flush | |
| 474 // callback, and we don't want to think that a callback is already pending. | |
| 475 FlushCallbackData callback; | |
| 476 std::swap(callback, painted_flush_callback_); | |
| 477 callback.Execute(PP_OK); | |
| 478 } | |
| 479 } | |
| 480 | |
| 481 void DeviceContext2D::ExecutePaintImageData(ImageData* image, | |
| 482 int x, int y, | |
| 483 const gfx::Rect& src_rect, | |
| 484 gfx::Rect* invalidated_rect) { | |
| 485 // Ensure the source image is mapped to read from it. | |
| 486 ImageDataAutoMapper auto_mapper(image); | |
| 487 if (!auto_mapper.is_valid()) | |
| 488 return; | |
| 489 | |
| 490 // Portion within the source image to cut out. | |
| 491 SkIRect src_irect = { src_rect.x(), src_rect.y(), | |
| 492 src_rect.right(), src_rect.bottom() }; | |
| 493 | |
| 494 // Location within the backing store to copy to. | |
| 495 *invalidated_rect = src_rect; | |
| 496 invalidated_rect->Offset(x, y); | |
| 497 SkRect dest_rect = { SkIntToScalar(invalidated_rect->x()), | |
| 498 SkIntToScalar(invalidated_rect->y()), | |
| 499 SkIntToScalar(invalidated_rect->right()), | |
| 500 SkIntToScalar(invalidated_rect->bottom()) }; | |
| 501 | |
| 502 // We're guaranteed to have a mapped canvas since we mapped it in Init(). | |
| 503 skia::PlatformCanvas* backing_canvas = image_data_->mapped_canvas(); | |
| 504 | |
| 505 // We want to replace the contents of the bitmap rather than blend. | |
| 506 SkPaint paint; | |
| 507 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
| 508 backing_canvas->drawBitmapRect(*image->GetMappedBitmap(), | |
| 509 &src_irect, dest_rect, &paint); | |
| 510 } | |
| 511 | |
| 512 void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy, | |
| 513 gfx::Rect* invalidated_rect) { | |
| 514 gfx::ScrollCanvas(image_data_->mapped_canvas(), | |
| 515 clip, gfx::Point(dx, dy)); | |
| 516 *invalidated_rect = clip; | |
| 517 } | |
| 518 | |
| 519 void DeviceContext2D::ExecuteReplaceContents(ImageData* image, | |
| 520 gfx::Rect* invalidated_rect) { | |
| 521 image_data_->Swap(image); | |
| 522 *invalidated_rect = gfx::Rect(0, 0, | |
| 523 image_data_->width(), image_data_->height()); | |
| 524 } | |
| 525 | |
| 526 void DeviceContext2D::ScheduleOffscreenCallback( | |
| 527 const FlushCallbackData& callback) { | |
| 528 DCHECK(!HasPendingFlush()); | |
| 529 offscreen_flush_pending_ = true; | |
| 530 MessageLoop::current()->PostTask( | |
| 531 FROM_HERE, | |
| 532 NewRunnableMethod(this, | |
| 533 &DeviceContext2D::ExecuteOffscreenCallback, | |
| 534 callback)); | |
| 535 } | |
| 536 | |
| 537 void DeviceContext2D::ExecuteOffscreenCallback(FlushCallbackData data) { | |
| 538 DCHECK(offscreen_flush_pending_); | |
| 539 | |
| 540 // We must clear this flag before issuing the callback. It will be | |
| 541 // common for the plugin to issue another invalidate in response to a flush | |
| 542 // callback, and we don't want to think that a callback is already pending. | |
| 543 offscreen_flush_pending_ = false; | |
| 544 data.Execute(PP_OK); | |
| 545 } | |
| 546 | |
| 547 bool DeviceContext2D::HasPendingFlush() const { | |
| 548 return !unpainted_flush_callback_.is_null() || | |
| 549 !painted_flush_callback_.is_null() || | |
| 550 offscreen_flush_pending_; | |
| 551 } | |
| 552 | |
| 553 } // namespace pepper | |
| OLD | NEW |