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