| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/plugins/ppapi/ppb_graphics_2d_impl.h" | |
| 6 | |
| 7 #include <iterator> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/debug/trace_event.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/message_loop.h" | |
| 13 #include "base/time.h" | |
| 14 #include "skia/ext/platform_canvas.h" | |
| 15 #include "ppapi/c/pp_errors.h" | |
| 16 #include "ppapi/c/pp_rect.h" | |
| 17 #include "ppapi/c/pp_resource.h" | |
| 18 #include "ppapi/c/ppb_graphics_2d.h" | |
| 19 #include "ppapi/thunk/enter.h" | |
| 20 #include "ppapi/thunk/thunk.h" | |
| 21 #include "third_party/skia/include/core/SkBitmap.h" | |
| 22 #include "ui/gfx/blit.h" | |
| 23 #include "ui/gfx/point.h" | |
| 24 #include "ui/gfx/point_conversions.h" | |
| 25 #include "ui/gfx/rect.h" | |
| 26 #include "ui/gfx/rect_conversions.h" | |
| 27 #include "ui/gfx/size_conversions.h" | |
| 28 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" | |
| 29 #include "ui/gfx/skia_util.h" | |
| 30 #include "webkit/plugins/ppapi/common.h" | |
| 31 #include "webkit/plugins/ppapi/gfx_conversion.h" | |
| 32 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" | |
| 33 #include "webkit/plugins/ppapi/ppb_image_data_impl.h" | |
| 34 #include "webkit/plugins/ppapi/resource_helper.h" | |
| 35 | |
| 36 #if defined(OS_MACOSX) | |
| 37 #include "base/mac/mac_util.h" | |
| 38 #include "base/mac/scoped_cftyperef.h" | |
| 39 #endif | |
| 40 | |
| 41 using ppapi::thunk::EnterResourceNoLock; | |
| 42 using ppapi::thunk::PPB_ImageData_API; | |
| 43 using ppapi::TrackedCallback; | |
| 44 | |
| 45 namespace webkit { | |
| 46 namespace ppapi { | |
| 47 | |
| 48 namespace { | |
| 49 | |
| 50 const int64 kOffscreenCallbackDelayMs = 1000 / 30; // 30 fps | |
| 51 | |
| 52 // Converts a rect inside an image of the given dimensions. The rect may be | |
| 53 // NULL to indicate it should be the entire image. If the rect is outside of | |
| 54 // the image, this will do nothing and return false. | |
| 55 bool ValidateAndConvertRect(const PP_Rect* rect, | |
| 56 int image_width, int image_height, | |
| 57 gfx::Rect* dest) { | |
| 58 if (!rect) { | |
| 59 // Use the entire image area. | |
| 60 *dest = gfx::Rect(0, 0, image_width, image_height); | |
| 61 } else { | |
| 62 // Validate the passed-in area. | |
| 63 if (rect->point.x < 0 || rect->point.y < 0 || | |
| 64 rect->size.width <= 0 || rect->size.height <= 0) | |
| 65 return false; | |
| 66 | |
| 67 // Check the max bounds, being careful of overflow. | |
| 68 if (static_cast<int64>(rect->point.x) + | |
| 69 static_cast<int64>(rect->size.width) > | |
| 70 static_cast<int64>(image_width)) | |
| 71 return false; | |
| 72 if (static_cast<int64>(rect->point.y) + | |
| 73 static_cast<int64>(rect->size.height) > | |
| 74 static_cast<int64>(image_height)) | |
| 75 return false; | |
| 76 | |
| 77 *dest = gfx::Rect(rect->point.x, rect->point.y, | |
| 78 rect->size.width, rect->size.height); | |
| 79 } | |
| 80 return true; | |
| 81 } | |
| 82 | |
| 83 // Converts BGRA <-> RGBA. | |
| 84 void ConvertBetweenBGRAandRGBA(const uint32_t* input, | |
| 85 int pixel_length, | |
| 86 uint32_t* output) { | |
| 87 for (int i = 0; i < pixel_length; i++) { | |
| 88 const unsigned char* pixel_in = | |
| 89 reinterpret_cast<const unsigned char*>(&input[i]); | |
| 90 unsigned char* pixel_out = reinterpret_cast<unsigned char*>(&output[i]); | |
| 91 pixel_out[0] = pixel_in[2]; | |
| 92 pixel_out[1] = pixel_in[1]; | |
| 93 pixel_out[2] = pixel_in[0]; | |
| 94 pixel_out[3] = pixel_in[3]; | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 // Converts ImageData from PP_IMAGEDATAFORMAT_BGRA_PREMUL to | |
| 99 // PP_IMAGEDATAFORMAT_RGBA_PREMUL, or reverse. It's assumed that the | |
| 100 // destination image is always mapped (so will have non-NULL data). | |
| 101 void ConvertImageData(PPB_ImageData_Impl* src_image, const SkIRect& src_rect, | |
| 102 PPB_ImageData_Impl* dest_image, const SkRect& dest_rect) { | |
| 103 ImageDataAutoMapper auto_mapper(src_image); | |
| 104 | |
| 105 DCHECK(src_image->format() != dest_image->format()); | |
| 106 DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(src_image->format())); | |
| 107 DCHECK(PPB_ImageData_Impl::IsImageDataFormatSupported(dest_image->format())); | |
| 108 | |
| 109 const SkBitmap* src_bitmap = src_image->GetMappedBitmap(); | |
| 110 const SkBitmap* dest_bitmap = dest_image->GetMappedBitmap(); | |
| 111 if (src_rect.width() == src_image->width() && | |
| 112 dest_rect.width() == dest_image->width()) { | |
| 113 // Fast path if the full line needs to be converted. | |
| 114 ConvertBetweenBGRAandRGBA( | |
| 115 src_bitmap->getAddr32(static_cast<int>(src_rect.fLeft), | |
| 116 static_cast<int>(src_rect.fTop)), | |
| 117 src_rect.width() * src_rect.height(), | |
| 118 dest_bitmap->getAddr32(static_cast<int>(dest_rect.fLeft), | |
| 119 static_cast<int>(dest_rect.fTop))); | |
| 120 } else { | |
| 121 // Slow path where we convert line by line. | |
| 122 for (int y = 0; y < src_rect.height(); y++) { | |
| 123 ConvertBetweenBGRAandRGBA( | |
| 124 src_bitmap->getAddr32(static_cast<int>(src_rect.fLeft), | |
| 125 static_cast<int>(src_rect.fTop + y)), | |
| 126 src_rect.width(), | |
| 127 dest_bitmap->getAddr32(static_cast<int>(dest_rect.fLeft), | |
| 128 static_cast<int>(dest_rect.fTop + y))); | |
| 129 } | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 } // namespace | |
| 134 | |
| 135 struct PPB_Graphics2D_Impl::QueuedOperation { | |
| 136 enum Type { | |
| 137 PAINT, | |
| 138 SCROLL, | |
| 139 REPLACE | |
| 140 }; | |
| 141 | |
| 142 QueuedOperation(Type t) | |
| 143 : type(t), | |
| 144 paint_x(0), | |
| 145 paint_y(0), | |
| 146 scroll_dx(0), | |
| 147 scroll_dy(0) { | |
| 148 } | |
| 149 | |
| 150 Type type; | |
| 151 | |
| 152 // Valid when type == PAINT. | |
| 153 scoped_refptr<PPB_ImageData_Impl> paint_image; | |
| 154 int paint_x, paint_y; | |
| 155 gfx::Rect paint_src_rect; | |
| 156 | |
| 157 // Valid when type == SCROLL. | |
| 158 gfx::Rect scroll_clip_rect; | |
| 159 int scroll_dx, scroll_dy; | |
| 160 | |
| 161 // Valid when type == REPLACE. | |
| 162 scoped_refptr<PPB_ImageData_Impl> replace_image; | |
| 163 }; | |
| 164 | |
| 165 PPB_Graphics2D_Impl::PPB_Graphics2D_Impl(PP_Instance instance) | |
| 166 : Resource(::ppapi::OBJECT_IS_IMPL, instance), | |
| 167 bound_instance_(NULL), | |
| 168 offscreen_flush_pending_(false), | |
| 169 is_always_opaque_(false), | |
| 170 scale_(1.0f), | |
| 171 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
| 172 } | |
| 173 | |
| 174 PPB_Graphics2D_Impl::~PPB_Graphics2D_Impl() { | |
| 175 // LastPluginRefWasDeleted should have aborted all pending callbacks. | |
| 176 DCHECK(painted_flush_callback_.is_null()); | |
| 177 DCHECK(unpainted_flush_callback_.is_null()); | |
| 178 } | |
| 179 | |
| 180 // static | |
| 181 PP_Resource PPB_Graphics2D_Impl::Create(PP_Instance instance, | |
| 182 const PP_Size& size, | |
| 183 PP_Bool is_always_opaque) { | |
| 184 scoped_refptr<PPB_Graphics2D_Impl> graphics_2d( | |
| 185 new PPB_Graphics2D_Impl(instance)); | |
| 186 if (!graphics_2d->Init(size.width, size.height, | |
| 187 PPBoolToBool(is_always_opaque))) { | |
| 188 return 0; | |
| 189 } | |
| 190 return graphics_2d->GetReference(); | |
| 191 } | |
| 192 | |
| 193 bool PPB_Graphics2D_Impl::Init(int width, int height, bool is_always_opaque) { | |
| 194 // The underlying PPB_ImageData_Impl will validate the dimensions. | |
| 195 image_data_ = new PPB_ImageData_Impl(pp_instance(), | |
| 196 PPB_ImageData_Impl::PLATFORM); | |
| 197 if (!image_data_->Init(PPB_ImageData_Impl::GetNativeImageDataFormat(), | |
| 198 width, height, true) || | |
| 199 !image_data_->Map()) { | |
| 200 image_data_ = NULL; | |
| 201 return false; | |
| 202 } | |
| 203 is_always_opaque_ = is_always_opaque; | |
| 204 scale_ = 1.0f; | |
| 205 return true; | |
| 206 } | |
| 207 | |
| 208 ::ppapi::thunk::PPB_Graphics2D_API* | |
| 209 PPB_Graphics2D_Impl::AsPPB_Graphics2D_API() { | |
| 210 return this; | |
| 211 } | |
| 212 | |
| 213 void PPB_Graphics2D_Impl::LastPluginRefWasDeleted() { | |
| 214 // Abort any pending callbacks. | |
| 215 unpainted_flush_callback_.PostAbort(); | |
| 216 painted_flush_callback_.PostAbort(); | |
| 217 } | |
| 218 | |
| 219 PP_Bool PPB_Graphics2D_Impl::Describe(PP_Size* size, | |
| 220 PP_Bool* is_always_opaque) { | |
| 221 size->width = image_data_->width(); | |
| 222 size->height = image_data_->height(); | |
| 223 *is_always_opaque = PP_FromBool(is_always_opaque_); | |
| 224 return PP_TRUE; | |
| 225 } | |
| 226 | |
| 227 void PPB_Graphics2D_Impl::PaintImageData(PP_Resource image_data, | |
| 228 const PP_Point* top_left, | |
| 229 const PP_Rect* src_rect) { | |
| 230 if (!top_left) | |
| 231 return; | |
| 232 | |
| 233 EnterResourceNoLock<PPB_ImageData_API> enter(image_data, true); | |
| 234 if (enter.failed()) { | |
| 235 Log(PP_LOGLEVEL_ERROR, | |
| 236 "PPB_Graphics2D.PaintImageData: Bad image resource."); | |
| 237 return; | |
| 238 } | |
| 239 PPB_ImageData_Impl* image_resource = | |
| 240 static_cast<PPB_ImageData_Impl*>(enter.object()); | |
| 241 | |
| 242 QueuedOperation operation(QueuedOperation::PAINT); | |
| 243 operation.paint_image = image_resource; | |
| 244 if (!ValidateAndConvertRect(src_rect, image_resource->width(), | |
| 245 image_resource->height(), | |
| 246 &operation.paint_src_rect)) | |
| 247 return; | |
| 248 | |
| 249 // Validate the bitmap position using the previously-validated rect, there | |
| 250 // should be no painted area outside of the image. | |
| 251 int64 x64 = static_cast<int64>(top_left->x); | |
| 252 int64 y64 = static_cast<int64>(top_left->y); | |
| 253 if (x64 + static_cast<int64>(operation.paint_src_rect.x()) < 0 || | |
| 254 x64 + static_cast<int64>(operation.paint_src_rect.right()) > | |
| 255 image_data_->width()) | |
| 256 return; | |
| 257 if (y64 + static_cast<int64>(operation.paint_src_rect.y()) < 0 || | |
| 258 y64 + static_cast<int64>(operation.paint_src_rect.bottom()) > | |
| 259 image_data_->height()) | |
| 260 return; | |
| 261 operation.paint_x = top_left->x; | |
| 262 operation.paint_y = top_left->y; | |
| 263 | |
| 264 queued_operations_.push_back(operation); | |
| 265 } | |
| 266 | |
| 267 void PPB_Graphics2D_Impl::Scroll(const PP_Rect* clip_rect, | |
| 268 const PP_Point* amount) { | |
| 269 QueuedOperation operation(QueuedOperation::SCROLL); | |
| 270 if (!ValidateAndConvertRect(clip_rect, | |
| 271 image_data_->width(), | |
| 272 image_data_->height(), | |
| 273 &operation.scroll_clip_rect)) | |
| 274 return; | |
| 275 | |
| 276 // If we're being asked to scroll by more than the clip rect size, just | |
| 277 // ignore this scroll command and say it worked. | |
| 278 int32 dx = amount->x; | |
| 279 int32 dy = amount->y; | |
| 280 if (dx <= -image_data_->width() || dx >= image_data_->width() || | |
| 281 dy <= -image_data_->height() || dy >= image_data_->height()) { | |
| 282 Log(PP_LOGLEVEL_ERROR, | |
| 283 "PPB_Graphics2D.Scroll: Scroll amount is larger than image size."); | |
| 284 return; | |
| 285 } | |
| 286 | |
| 287 operation.scroll_dx = dx; | |
| 288 operation.scroll_dy = dy; | |
| 289 | |
| 290 queued_operations_.push_back(operation); | |
| 291 } | |
| 292 | |
| 293 void PPB_Graphics2D_Impl::ReplaceContents(PP_Resource image_data) { | |
| 294 EnterResourceNoLock<PPB_ImageData_API> enter(image_data, true); | |
| 295 if (enter.failed()) | |
| 296 return; | |
| 297 PPB_ImageData_Impl* image_resource = | |
| 298 static_cast<PPB_ImageData_Impl*>(enter.object()); | |
| 299 | |
| 300 if (!PPB_ImageData_Impl::IsImageDataFormatSupported( | |
| 301 image_resource->format())) { | |
| 302 Log(PP_LOGLEVEL_ERROR, | |
| 303 "PPB_Graphics2D.ReplaceContents: Image data format is not supported."); | |
| 304 return; | |
| 305 } | |
| 306 | |
| 307 if (image_resource->width() != image_data_->width() || | |
| 308 image_resource->height() != image_data_->height()) { | |
| 309 Log(PP_LOGLEVEL_ERROR, | |
| 310 "PPB_Graphics2D.ReplaceContents: Image size doesn't match " | |
| 311 "Graphics2D size."); | |
| 312 return; | |
| 313 } | |
| 314 | |
| 315 QueuedOperation operation(QueuedOperation::REPLACE); | |
| 316 operation.replace_image = image_resource; | |
| 317 queued_operations_.push_back(operation); | |
| 318 } | |
| 319 | |
| 320 int32_t PPB_Graphics2D_Impl::Flush(scoped_refptr<TrackedCallback> callback, | |
| 321 PP_Resource* old_image_data) { | |
| 322 TRACE_EVENT0("pepper", "PPB_Graphics2D_Impl::Flush"); | |
| 323 // Don't allow more than one pending flush at a time. | |
| 324 if (HasPendingFlush()) | |
| 325 return PP_ERROR_INPROGRESS; | |
| 326 | |
| 327 bool done_replace_contents = false; | |
| 328 bool no_update_visible = true; | |
| 329 bool is_plugin_visible = true; | |
| 330 for (size_t i = 0; i < queued_operations_.size(); i++) { | |
| 331 QueuedOperation& operation = queued_operations_[i]; | |
| 332 gfx::Rect op_rect; | |
| 333 switch (operation.type) { | |
| 334 case QueuedOperation::PAINT: | |
| 335 ExecutePaintImageData(operation.paint_image, | |
| 336 operation.paint_x, operation.paint_y, | |
| 337 operation.paint_src_rect, | |
| 338 &op_rect); | |
| 339 break; | |
| 340 case QueuedOperation::SCROLL: | |
| 341 ExecuteScroll(operation.scroll_clip_rect, | |
| 342 operation.scroll_dx, operation.scroll_dy, | |
| 343 &op_rect); | |
| 344 break; | |
| 345 case QueuedOperation::REPLACE: | |
| 346 // Since the out parameter |old_image_data| takes ownership of the | |
| 347 // reference, if there are more than one ReplaceContents calls queued | |
| 348 // the first |old_image_data| will get overwritten and leaked. So we | |
| 349 // only supply this for the first call. | |
| 350 ExecuteReplaceContents(operation.replace_image, &op_rect, | |
| 351 done_replace_contents ? NULL : old_image_data); | |
| 352 done_replace_contents = true; | |
| 353 break; | |
| 354 } | |
| 355 | |
| 356 // For correctness with accelerated compositing, we must issue an invalidate | |
| 357 // on the full op_rect even if it is partially or completely off-screen. | |
| 358 // However, if we issue an invalidate for a clipped-out region, WebKit will | |
| 359 // do nothing and we won't get any ViewWillInitiatePaint/ViewFlushedPaint | |
| 360 // calls, leaving our callback stranded. So we still need to check whether | |
| 361 // the repainted area is visible to determine how to deal with the callback. | |
| 362 if (bound_instance_ && !op_rect.IsEmpty()) { | |
| 363 gfx::Point scroll_delta(operation.scroll_dx, operation.scroll_dy); | |
| 364 if (!ConvertToLogicalPixels(scale_, | |
| 365 &op_rect, | |
| 366 operation.type == QueuedOperation::SCROLL ? | |
| 367 &scroll_delta : NULL)) { | |
| 368 // Conversion requires falling back to InvalidateRect. | |
| 369 operation.type = QueuedOperation::PAINT; | |
| 370 } | |
| 371 | |
| 372 gfx::Rect clip = PP_ToGfxRect(bound_instance_->view_data().clip_rect); | |
| 373 is_plugin_visible = !clip.IsEmpty(); | |
| 374 | |
| 375 // Set |no_update_visible| to false if the change overlaps the visible | |
| 376 // area. | |
| 377 gfx::Rect visible_changed_rect = gfx::IntersectRects(clip, op_rect); | |
| 378 if (!visible_changed_rect.IsEmpty()) | |
| 379 no_update_visible = false; | |
| 380 | |
| 381 // Notify the plugin of the entire change (op_rect), even if it is | |
| 382 // partially or completely off-screen. | |
| 383 if (operation.type == QueuedOperation::SCROLL) { | |
| 384 bound_instance_->ScrollRect(scroll_delta.x(), scroll_delta.y(), | |
| 385 op_rect); | |
| 386 } else { | |
| 387 bound_instance_->InvalidateRect(op_rect); | |
| 388 } | |
| 389 } | |
| 390 } | |
| 391 queued_operations_.clear(); | |
| 392 | |
| 393 if (!bound_instance_) { | |
| 394 // As promised in the API, we always schedule callback when unbound. | |
| 395 ScheduleOffscreenCallback(FlushCallbackData(callback)); | |
| 396 } else if (no_update_visible && is_plugin_visible && | |
| 397 bound_instance_->view_data().is_page_visible) { | |
| 398 // There's nothing visible to invalidate so just schedule the callback to | |
| 399 // execute in the next round of the message loop. | |
| 400 ScheduleOffscreenCallback(FlushCallbackData(callback)); | |
| 401 } else { | |
| 402 unpainted_flush_callback_.Set(callback); | |
| 403 } | |
| 404 | |
| 405 return PP_OK_COMPLETIONPENDING; | |
| 406 } | |
| 407 | |
| 408 bool PPB_Graphics2D_Impl::SetScale(float scale) { | |
| 409 if (scale > 0.0f) { | |
| 410 scale_ = scale; | |
| 411 return true; | |
| 412 } | |
| 413 | |
| 414 return false; | |
| 415 } | |
| 416 | |
| 417 float PPB_Graphics2D_Impl::GetScale() { | |
| 418 return scale_; | |
| 419 } | |
| 420 | |
| 421 bool PPB_Graphics2D_Impl::ReadImageData(PP_Resource image, | |
| 422 const PP_Point* top_left) { | |
| 423 // Get and validate the image object to paint into. | |
| 424 EnterResourceNoLock<PPB_ImageData_API> enter(image, true); | |
| 425 if (enter.failed()) | |
| 426 return false; | |
| 427 PPB_ImageData_Impl* image_resource = | |
| 428 static_cast<PPB_ImageData_Impl*>(enter.object()); | |
| 429 if (!PPB_ImageData_Impl::IsImageDataFormatSupported( | |
| 430 image_resource->format())) | |
| 431 return false; // Must be in the right format. | |
| 432 | |
| 433 // Validate the bitmap position. | |
| 434 int x = top_left->x; | |
| 435 if (x < 0 || | |
| 436 static_cast<int64>(x) + static_cast<int64>(image_resource->width()) > | |
| 437 image_data_->width()) | |
| 438 return false; | |
| 439 int y = top_left->y; | |
| 440 if (y < 0 || | |
| 441 static_cast<int64>(y) + static_cast<int64>(image_resource->height()) > | |
| 442 image_data_->height()) | |
| 443 return false; | |
| 444 | |
| 445 ImageDataAutoMapper auto_mapper(image_resource); | |
| 446 if (!auto_mapper.is_valid()) | |
| 447 return false; | |
| 448 | |
| 449 SkIRect src_irect = { x, y, | |
| 450 x + image_resource->width(), | |
| 451 y + image_resource->height() }; | |
| 452 SkRect dest_rect = { SkIntToScalar(0), | |
| 453 SkIntToScalar(0), | |
| 454 SkIntToScalar(image_resource->width()), | |
| 455 SkIntToScalar(image_resource->height()) }; | |
| 456 | |
| 457 if (image_resource->format() != image_data_->format()) { | |
| 458 // Convert the image data if the format does not match. | |
| 459 ConvertImageData(image_data_, src_irect, image_resource, dest_rect); | |
| 460 } else { | |
| 461 SkCanvas* dest_canvas = image_resource->GetCanvas(); | |
| 462 | |
| 463 // We want to replace the contents of the bitmap rather than blend. | |
| 464 SkPaint paint; | |
| 465 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
| 466 dest_canvas->drawBitmapRect(*image_data_->GetMappedBitmap(), | |
| 467 &src_irect, dest_rect, &paint); | |
| 468 } | |
| 469 return true; | |
| 470 } | |
| 471 | |
| 472 bool PPB_Graphics2D_Impl::BindToInstance(PluginInstance* new_instance) { | |
| 473 if (bound_instance_ == new_instance) | |
| 474 return true; // Rebinding the same device, nothing to do. | |
| 475 if (bound_instance_ && new_instance) | |
| 476 return false; // Can't change a bound device. | |
| 477 | |
| 478 if (!new_instance) { | |
| 479 // When the device is detached, we'll not get any more paint callbacks so | |
| 480 // we need to clear the list, but we still want to issue any pending | |
| 481 // callbacks to the plugin. | |
| 482 if (!unpainted_flush_callback_.is_null()) { | |
| 483 FlushCallbackData callback; | |
| 484 std::swap(callback, unpainted_flush_callback_); | |
| 485 ScheduleOffscreenCallback(callback); | |
| 486 } | |
| 487 if (!painted_flush_callback_.is_null()) { | |
| 488 FlushCallbackData callback; | |
| 489 std::swap(callback, painted_flush_callback_); | |
| 490 ScheduleOffscreenCallback(callback); | |
| 491 } | |
| 492 } else { | |
| 493 // Devices being replaced, redraw the plugin. | |
| 494 new_instance->InvalidateRect(gfx::Rect()); | |
| 495 } | |
| 496 | |
| 497 bound_instance_ = new_instance; | |
| 498 return true; | |
| 499 } | |
| 500 | |
| 501 // The |backing_bitmap| must be clipped to the |plugin_rect| to avoid painting | |
| 502 // outside the plugin area. This can happen if the plugin has been resized since | |
| 503 // PaintImageData verified the image is within the plugin size. | |
| 504 void PPB_Graphics2D_Impl::Paint(WebKit::WebCanvas* canvas, | |
| 505 const gfx::Rect& plugin_rect, | |
| 506 const gfx::Rect& paint_rect) { | |
| 507 TRACE_EVENT0("pepper", "PPB_Graphics2D_Impl::Paint"); | |
| 508 ImageDataAutoMapper auto_mapper(image_data_); | |
| 509 const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap(); | |
| 510 | |
| 511 gfx::Rect invalidate_rect = gfx::IntersectRects(plugin_rect, paint_rect); | |
| 512 SkRect sk_invalidate_rect = gfx::RectToSkRect(invalidate_rect); | |
| 513 SkAutoCanvasRestore auto_restore(canvas, true); | |
| 514 canvas->clipRect(sk_invalidate_rect); | |
| 515 gfx::Size pixel_image_size(image_data_->width(), image_data_->height()); | |
| 516 gfx::Size image_size = gfx::ToFlooredSize( | |
| 517 gfx::ScaleSize(pixel_image_size, scale_)); | |
| 518 | |
| 519 PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
| 520 if (!plugin_instance) | |
| 521 return; | |
| 522 if (plugin_instance->IsFullPagePlugin()) { | |
| 523 // When we're resizing a window with a full-frame plugin, the plugin may | |
| 524 // not yet have bound a new device, which will leave parts of the | |
| 525 // background exposed if the window is getting larger. We want this to | |
| 526 // show white (typically less jarring) rather than black or uninitialized. | |
| 527 // We don't do this for non-full-frame plugins since we specifically want | |
| 528 // the page background to show through. | |
| 529 SkAutoCanvasRestore auto_restore(canvas, true); | |
| 530 SkRect image_data_rect = | |
| 531 gfx::RectToSkRect(gfx::Rect(plugin_rect.origin(), image_size)); | |
| 532 canvas->clipRect(image_data_rect, SkRegion::kDifference_Op); | |
| 533 | |
| 534 SkPaint paint; | |
| 535 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
| 536 paint.setColor(SK_ColorWHITE); | |
| 537 canvas->drawRect(sk_invalidate_rect, paint); | |
| 538 } | |
| 539 | |
| 540 SkBitmap image; | |
| 541 // Copy to device independent bitmap when target canvas doesn't support | |
| 542 // platform paint. | |
| 543 if (!skia::SupportsPlatformPaint(canvas)) | |
| 544 backing_bitmap.copyTo(&image, SkBitmap::kARGB_8888_Config); | |
| 545 else | |
| 546 image = backing_bitmap; | |
| 547 | |
| 548 SkPaint paint; | |
| 549 if (is_always_opaque_) { | |
| 550 // When we know the device is opaque, we can disable blending for slightly | |
| 551 // more optimized painting. | |
| 552 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
| 553 } | |
| 554 | |
| 555 SkPoint origin; | |
| 556 origin.set(SkIntToScalar(plugin_rect.x()), SkIntToScalar(plugin_rect.y())); | |
| 557 | |
| 558 SkPoint pixel_origin = origin; | |
| 559 if (scale_ != 1.0f && scale_ > 0.0f) { | |
| 560 float inverse_scale = 1.0f / scale_; | |
| 561 pixel_origin.scale(inverse_scale); | |
| 562 canvas->scale(scale_, scale_); | |
| 563 } | |
| 564 canvas->drawBitmap(image, pixel_origin.x(), pixel_origin.y(), &paint); | |
| 565 } | |
| 566 | |
| 567 void PPB_Graphics2D_Impl::ViewWillInitiatePaint() { | |
| 568 // Move any "unpainted" callback to the painted state. See | |
| 569 // |unpainted_flush_callback_| in the header for more. | |
| 570 if (!unpainted_flush_callback_.is_null()) { | |
| 571 DCHECK(painted_flush_callback_.is_null()); | |
| 572 std::swap(painted_flush_callback_, unpainted_flush_callback_); | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 void PPB_Graphics2D_Impl::ViewInitiatedPaint() { | |
| 577 } | |
| 578 | |
| 579 void PPB_Graphics2D_Impl::ViewFlushedPaint() { | |
| 580 TRACE_EVENT0("pepper", "PPB_Graphics2D_Impl::ViewFlushedPaint"); | |
| 581 // Notify any "painted" callback. See |unpainted_flush_callback_| in the | |
| 582 // header for more. | |
| 583 if (!painted_flush_callback_.is_null()) | |
| 584 painted_flush_callback_.Execute(PP_OK); | |
| 585 } | |
| 586 | |
| 587 // static | |
| 588 bool PPB_Graphics2D_Impl::ConvertToLogicalPixels(float scale, | |
| 589 gfx::Rect* op_rect, | |
| 590 gfx::Point* delta) { | |
| 591 if (scale == 1.0f || scale <= 0.0f) | |
| 592 return true; | |
| 593 | |
| 594 gfx::Rect original_rect = *op_rect; | |
| 595 // Take the enclosing rectangle after scaling so a rectangle scaled down then | |
| 596 // scaled back up by the inverse scale would fully contain the entire area | |
| 597 // affected by the original rectangle. | |
| 598 *op_rect = gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, scale)); | |
| 599 if (delta) { | |
| 600 gfx::Point original_delta = *delta; | |
| 601 float inverse_scale = 1.0f / scale; | |
| 602 *delta = gfx::ToFlooredPoint(gfx::ScalePoint(*delta, scale)); | |
| 603 | |
| 604 gfx::Rect inverse_scaled_rect = | |
| 605 gfx::ToEnclosingRect(gfx::ScaleRect(*op_rect, inverse_scale)); | |
| 606 if (original_rect != inverse_scaled_rect) | |
| 607 return false; | |
| 608 gfx::Point inverse_scaled_point = | |
| 609 gfx::ToFlooredPoint(gfx::ScalePoint(*delta, inverse_scale)); | |
| 610 if (original_delta != inverse_scaled_point) | |
| 611 return false; | |
| 612 } | |
| 613 | |
| 614 return true; | |
| 615 } | |
| 616 | |
| 617 void PPB_Graphics2D_Impl::ExecutePaintImageData(PPB_ImageData_Impl* image, | |
| 618 int x, int y, | |
| 619 const gfx::Rect& src_rect, | |
| 620 gfx::Rect* invalidated_rect) { | |
| 621 // Ensure the source image is mapped to read from it. | |
| 622 ImageDataAutoMapper auto_mapper(image); | |
| 623 if (!auto_mapper.is_valid()) | |
| 624 return; | |
| 625 | |
| 626 // Portion within the source image to cut out. | |
| 627 SkIRect src_irect = { src_rect.x(), src_rect.y(), | |
| 628 src_rect.right(), src_rect.bottom() }; | |
| 629 | |
| 630 // Location within the backing store to copy to. | |
| 631 *invalidated_rect = src_rect; | |
| 632 invalidated_rect->Offset(x, y); | |
| 633 SkRect dest_rect = { SkIntToScalar(invalidated_rect->x()), | |
| 634 SkIntToScalar(invalidated_rect->y()), | |
| 635 SkIntToScalar(invalidated_rect->right()), | |
| 636 SkIntToScalar(invalidated_rect->bottom()) }; | |
| 637 | |
| 638 if (image->format() != image_data_->format()) { | |
| 639 // Convert the image data if the format does not match. | |
| 640 ConvertImageData(image, src_irect, image_data_, dest_rect); | |
| 641 } else { | |
| 642 // We're guaranteed to have a mapped canvas since we mapped it in Init(). | |
| 643 SkCanvas* backing_canvas = image_data_->GetCanvas(); | |
| 644 | |
| 645 // We want to replace the contents of the bitmap rather than blend. | |
| 646 SkPaint paint; | |
| 647 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
| 648 backing_canvas->drawBitmapRect(*image->GetMappedBitmap(), | |
| 649 &src_irect, dest_rect, &paint); | |
| 650 } | |
| 651 } | |
| 652 | |
| 653 void PPB_Graphics2D_Impl::ExecuteScroll(const gfx::Rect& clip, | |
| 654 int dx, int dy, | |
| 655 gfx::Rect* invalidated_rect) { | |
| 656 gfx::ScrollCanvas(image_data_->GetCanvas(), clip, gfx::Vector2d(dx, dy)); | |
| 657 *invalidated_rect = clip; | |
| 658 } | |
| 659 | |
| 660 void PPB_Graphics2D_Impl::ExecuteReplaceContents(PPB_ImageData_Impl* image, | |
| 661 gfx::Rect* invalidated_rect, | |
| 662 PP_Resource* old_image_data) { | |
| 663 if (image->format() != image_data_->format()) { | |
| 664 DCHECK(image->width() == image_data_->width() && | |
| 665 image->height() == image_data_->height()); | |
| 666 // Convert the image data if the format does not match. | |
| 667 SkIRect src_irect = { 0, 0, image->width(), image->height() }; | |
| 668 SkRect dest_rect = { SkIntToScalar(0), | |
| 669 SkIntToScalar(0), | |
| 670 SkIntToScalar(image_data_->width()), | |
| 671 SkIntToScalar(image_data_->height()) }; | |
| 672 ConvertImageData(image, src_irect, image_data_, dest_rect); | |
| 673 } else { | |
| 674 // The passed-in image may not be mapped in our process, and we need to | |
| 675 // guarantee that the current backing store is always mapped. | |
| 676 if (!image->Map()) | |
| 677 return; | |
| 678 | |
| 679 if (old_image_data) | |
| 680 *old_image_data = image_data_->GetReference(); | |
| 681 image_data_ = image; | |
| 682 } | |
| 683 *invalidated_rect = gfx::Rect(0, 0, | |
| 684 image_data_->width(), image_data_->height()); | |
| 685 } | |
| 686 | |
| 687 void PPB_Graphics2D_Impl::ScheduleOffscreenCallback( | |
| 688 const FlushCallbackData& callback) { | |
| 689 DCHECK(!HasPendingFlush()); | |
| 690 offscreen_flush_pending_ = true; | |
| 691 MessageLoop::current()->PostDelayedTask( | |
| 692 FROM_HERE, | |
| 693 base::Bind(&PPB_Graphics2D_Impl::ExecuteOffscreenCallback, | |
| 694 weak_ptr_factory_.GetWeakPtr(), | |
| 695 callback), | |
| 696 base::TimeDelta::FromMilliseconds(kOffscreenCallbackDelayMs)); | |
| 697 } | |
| 698 | |
| 699 void PPB_Graphics2D_Impl::ExecuteOffscreenCallback(FlushCallbackData data) { | |
| 700 DCHECK(offscreen_flush_pending_); | |
| 701 | |
| 702 // We must clear this flag before issuing the callback. It will be | |
| 703 // common for the plugin to issue another invalidate in response to a flush | |
| 704 // callback, and we don't want to think that a callback is already pending. | |
| 705 offscreen_flush_pending_ = false; | |
| 706 data.Execute(PP_OK); | |
| 707 } | |
| 708 | |
| 709 bool PPB_Graphics2D_Impl::HasPendingFlush() const { | |
| 710 return !unpainted_flush_callback_.is_null() || | |
| 711 !painted_flush_callback_.is_null() || | |
| 712 offscreen_flush_pending_; | |
| 713 } | |
| 714 | |
| 715 } // namespace ppapi | |
| 716 } // namespace webkit | |
| OLD | NEW |