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 |