Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(541)

Side by Side Diff: webkit/glue/plugins/pepper_device_context_2d.cc

Issue 3255003: Pull new PPAPI, rename non-P0 interfaces to Dev, rename DeviceContext2D to Gr... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 10 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW
« no previous file with comments | « webkit/glue/plugins/pepper_device_context_2d.h ('k') | webkit/glue/plugins/pepper_directory_reader.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698